diff --git a/util/sim/simulate.py b/util/sim/simulate.py index 9387ab1916..a1466dc167 100755 --- a/util/sim/simulate.py +++ b/util/sim/simulate.py @@ -19,7 +19,7 @@ BANSHEE_CFG = 'src/banshee.yaml' # Tool settings -SIMULATORS = ['vsim', 'banshee', 'verilator', 'vcs'] +SIMULATORS = ['vsim', 'banshee', 'verilator', 'vcs', 'other'] DEFAULT_SIMULATOR = SIMULATORS[0] SIMULATOR_BINS = { 'vsim': 'bin/snitch_cluster.vsim', @@ -81,6 +81,51 @@ def check_exit_code(test, exit_code): return exit_code +def run_simulation(cmd, simulator, test): + # Defaults + result = 1 + + # Spawn simulation subprocess + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, universal_newlines=True) + + # Poll simulation subprocess and log its output + while p.poll() is None: + line = p.stdout.readline() + print(line, end='', flush=True) + + # When simulating with vsim or vcs, we need to parse the simulation + # log to catch the application's return code + if simulator in ['vsim', 'vcs']: + # Capture success + regex_success = r'\[SUCCESS\] Program finished successfully' + match_success = re.search(regex_success, line) + if match_success: + result = 0 + else: + regex_fail = r'\[FAILURE\] Finished with exit code\s+(\d+)' + match = re.search(regex_fail, line) + if match: + exit_code = match.group(1) + result = check_exit_code(test, exit_code) + + # Check if the subprocess terminated correctly + exit_code = p.poll() + # In Banshee and Verilator the exit code of the Snitch binary is returned + # through the exit code of the simulation command + if simulator in ['banshee', 'verilator']: + result = check_exit_code(test, exit_code) + # For custom commands the return code is that of the command + elif simulator == 'other': + result = exit_code + # For standard simulation commands the simulated Snitch binary exit + # code is overriden only if the simulator failed + else: + if exit_code != 0: + result = exit_code + + return result + + def run_test(test, args): # Extract args simulator = args.simulator @@ -94,59 +139,31 @@ def run_test(test, args): return 0 # Construct path to executable - elf = Path(testlist).absolute().parent / Path(test['elf']) + elf = Path(test['elf']) + if testlist: + elf = Path(testlist).absolute().parent / elf cprint(f'Run test {colored(elf, "cyan")}', attrs=["bold"]) # Construct simulation command (override only supported for RTL) if 'cmd' in test and simulator != 'banshee': cmd = test['cmd'] + cmd = cmd.format(sim_bin=sim_bin, elf=elf, simulator=simulator) + simulator = 'other' else: cmd = SIMULATOR_CMDS[simulator] - cmd = cmd.format(sim_bin=sim_bin, elf=elf) + cmd = cmd.format(sim_bin=sim_bin, elf=elf) print(f'$ {cmd}', flush=True) - # Run test + # Run simulation result = 0 if not dry_run: - result = 1 - - # When simulating with vsim or vcs, we need to parse the simulation - # log to catch the application's return code - if simulator in ['vsim', 'vcs']: - p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, - text=True) - - while p.poll() is None: - line = p.stdout.readline() - print(line, end='', flush=True) - - # Capture success - regex_success = r'\[SUCCESS\] Program finished successfully' - match_success = re.search(regex_success, line) - if match_success: - result = 0 - else: - regex_fail = r'\[FAILURE\] Finished with exit code\s+(\d+)' - match = re.search(regex_fail, line) - if match: - exit_code = match.group(1) - result = check_exit_code(test, exit_code) - - # Check if the subprocess terminated correctly - if p.poll() != 0: - result = p.poll() + result = run_simulation(cmd, simulator, test) - else: - p = subprocess.Popen(cmd, shell=True) - p.wait() - exit_code = p.returncode - result = check_exit_code(test, exit_code) - - # Report failure or success - if result != 0: - cprint(f'{elf} test failed', 'red', attrs=['bold'], flush=True) - else: - cprint(f'{elf} test passed', 'green', attrs=['bold'], flush=True) + # Report failure or success + if result != 0: + cprint(f'{elf} test failed', 'red', attrs=['bold'], flush=True) + else: + cprint(f'{elf} test passed', 'green', attrs=['bold'], flush=True) return result