- sync_rtl/common/: skid_buffer, pipeline_reg, defines (valid/ready) - sync_rtl/mod_add/: modular adder example with Verilator C++ TB - test_framework/: Python-driven Verilator compile/sim/compare pipeline - test_framework/modules/mod_add/: 50-vector test plan, full鏈路 PASS - .trellis/spec/: RTL and test_framework conventions documented
135 lines
4.4 KiB
Python
135 lines
4.4 KiB
Python
"""sim_controller.py - Verilator simulation controller.
|
|
|
|
Handles Verilator compilation and execution of RTL testbenches.
|
|
"""
|
|
|
|
import subprocess
|
|
import os
|
|
import time
|
|
|
|
|
|
class SimController:
|
|
"""Controls Verilator compilation and simulation."""
|
|
|
|
def __init__(self, config: dict):
|
|
"""Initialize with test framework configuration.
|
|
|
|
Args:
|
|
config: The loaded config.json dict.
|
|
"""
|
|
self.config = config
|
|
self.verilator_path = config.get('verilator', {}).get('path', 'verilator')
|
|
self.compile_args = config.get('verilator', {}).get('compile_args', [])
|
|
|
|
def compile(self, top_module: str, rtl_files: list[str], tb_cpp: str,
|
|
work_dir: str, inc_dirs: list[str] = None) -> str:
|
|
"""Compile RTL with Verilator and return path to executable.
|
|
|
|
Args:
|
|
top_module: Name of the top-level Verilog module.
|
|
rtl_files: List of Verilog source file paths.
|
|
tb_cpp: Path to C++ testbench file.
|
|
work_dir: Working directory for compilation output (-Mdir).
|
|
inc_dirs: List of include directories for `include directives.
|
|
|
|
Returns:
|
|
Path to the compiled executable.
|
|
|
|
Raises:
|
|
RuntimeError: If Verilator is not found or compilation fails.
|
|
"""
|
|
os.makedirs(work_dir, exist_ok=True)
|
|
|
|
# Build verilator command
|
|
cmd = [self.verilator_path]
|
|
cmd.extend(self.compile_args)
|
|
cmd.extend(['--top-module', top_module])
|
|
# No explicit -std= flag; let Verilator use its default (C++14+)
|
|
if inc_dirs:
|
|
for d in inc_dirs:
|
|
cmd.append(f'+incdir+{d}')
|
|
cmd.extend(rtl_files)
|
|
cmd.append(tb_cpp)
|
|
cmd.extend(['-Mdir', work_dir])
|
|
|
|
try:
|
|
result = subprocess.run(
|
|
cmd,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=120,
|
|
cwd=os.getcwd()
|
|
)
|
|
except subprocess.TimeoutExpired:
|
|
raise RuntimeError("Verilator compilation timed out.")
|
|
except FileNotFoundError:
|
|
raise RuntimeError(
|
|
f"Verilator not found at '{self.verilator_path}'."
|
|
)
|
|
|
|
if result.returncode != 0:
|
|
tail = result.stderr[-800:] if result.stderr else '(no stderr)'
|
|
raise RuntimeError(f"Verilator compilation failed:\n{tail}")
|
|
|
|
# Executable is at <work_dir>/V<top_module>
|
|
executable = os.path.join(work_dir, f'V{top_module}')
|
|
if not os.path.exists(executable):
|
|
raise RuntimeError(
|
|
f"Compilation succeeded but executable not found: {executable}"
|
|
)
|
|
return executable
|
|
|
|
def run(self, executable: str, vector_file: str,
|
|
timeout_s: int = 30) -> dict:
|
|
"""Run the compiled simulation executable.
|
|
|
|
Args:
|
|
executable: Path to the compiled executable.
|
|
vector_file: Path to the hex vector file.
|
|
timeout_s: Timeout in seconds.
|
|
|
|
Returns:
|
|
dict with keys: success, results, elapsed, stderr.
|
|
"""
|
|
if not os.path.exists(executable):
|
|
return {
|
|
'success': False,
|
|
'results': [],
|
|
'elapsed': 0.0,
|
|
'stderr': f'Executable not found: {executable}'
|
|
}
|
|
|
|
start_time = time.time()
|
|
try:
|
|
result = subprocess.run(
|
|
[executable, f'+VECTOR_FILE={vector_file}'],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=timeout_s
|
|
)
|
|
elapsed = time.time() - start_time
|
|
|
|
# Parse stdout for "RESULT: XXX" lines
|
|
results = []
|
|
for line in result.stdout.splitlines():
|
|
line = line.strip()
|
|
if line.startswith('RESULT:'):
|
|
hex_val = line.split(':', 1)[1].strip()
|
|
results.append(hex_val)
|
|
|
|
success = result.returncode == 0 and len(results) > 0
|
|
return {
|
|
'success': success,
|
|
'results': results,
|
|
'elapsed': elapsed,
|
|
'stderr': result.stderr
|
|
}
|
|
except subprocess.TimeoutExpired:
|
|
elapsed = time.time() - start_time
|
|
return {
|
|
'success': False,
|
|
'results': [],
|
|
'elapsed': elapsed,
|
|
'stderr': 'Simulation timed out.'
|
|
}
|