3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-08-04 02:10:24 +00:00
This commit is contained in:
Gus Smith 2025-07-31 13:22:05 +02:00 committed by GitHub
commit 851ce302dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 180 additions and 42 deletions

View file

@ -31,4 +31,4 @@ def pytest_generate_tests(metafunc):
seed1 = metafunc.config.getoption("seed")
rnd = lambda seed2: random.Random('{}-{}'.format(seed1, seed2))
names, cases = generate_test_cases(per_cell, rnd)
metafunc.parametrize("cell,parameters", cases, ids=names)
metafunc.parametrize("name,cell,parameters", cases, ids=names)

View file

@ -43,21 +43,37 @@ def write_vcd(filename: Path, signals: SignalStepMap, timescale='1 ns', date='to
if change_time == time:
f.write(f"{value} {signal_name}\n")
def simulate_rosette(rkt_file_path: Path, vcd_path: Path, num_steps: int, rnd: Random):
def simulate_rosette(
rkt_file_path: Path,
vcd_path: Path,
num_steps: int,
rnd: Random,
use_assoc_list_helpers: bool = False,
):
"""
Args:
- use_assoc_list_helpers: If True, will use the association list helpers
in the Racket file. The file should have been generated with the
-assoc-list-helpers flag in the yosys command.
"""
signals: dict[str, list[str]] = {}
inputs: SignalWidthMap = {}
outputs: SignalWidthMap = {}
current_struct_name: str = ""
with open(rkt_file_path, 'r') as rkt_file:
with open(rkt_file_path, "r") as rkt_file:
for line in rkt_file:
m = re.search(r'gold_(Inputs|Outputs|State)', line)
m = re.search(r"gold_(Inputs|Outputs|State)", line)
if m:
current_struct_name = m.group(1)
if current_struct_name == "State": break
elif not current_struct_name: continue # skip lines before structs
m = re.search(r'; (.+?)\b \(bitvector (\d+)\)', line)
if not m: continue # skip non matching lines (probably closing the struct)
if current_struct_name == "State":
break
elif not current_struct_name:
continue # skip lines before structs
m = re.search(r"; (.+?)\b \(bitvector (\d+)\)", line)
if not m:
continue # skip non matching lines (probably closing the struct)
signal = m.group(1)
width = int(m.group(2))
if current_struct_name == "Inputs":
@ -69,43 +85,86 @@ def simulate_rosette(rkt_file_path: Path, vcd_path: Path, num_steps: int, rnd: R
step_list: list[int] = []
for step in range(num_steps):
value = rnd.getrandbits(width)
binary_string = format(value, '0{}b'.format(width))
binary_string = format(value, "0{}b".format(width))
step_list.append(binary_string)
signals[signal] = step_list
test_rkt_file_path = rkt_file_path.with_suffix('.tst.rkt')
with open(test_rkt_file_path, 'w') as test_rkt_file:
test_rkt_file.writelines([
'#lang rosette\n',
f'(require "{rkt_file_path.name}")\n',
])
test_rkt_file_path = rkt_file_path.with_suffix(".tst.rkt")
with open(test_rkt_file_path, "w") as test_rkt_file:
test_rkt_file.writelines(
[
"#lang rosette\n",
f'(require "{rkt_file_path.name}")\n',
]
)
for step in range(num_steps):
this_step = f"step_{step}"
value_list: list[str] = []
for signal, width in inputs.items():
value = signals[signal][step]
value_list.append(f"(bv #b{value} {width})")
gold_Inputs = f"(gold_Inputs {' '.join(value_list)})"
if use_assoc_list_helpers:
# Generate inputs as a list of cons pairs making up the
# association list.
for signal, width in inputs.items():
value = signals[signal][step]
value_list.append(f'(cons "{signal}" (bv #b{value} {width}))')
else:
# Otherwise, we generate the inputs as a list of bitvectors.
for signal, width in inputs.items():
value = signals[signal][step]
value_list.append(f"(bv #b{value} {width})")
gold_Inputs = (
f"(gold_inputs_helper (list {' '.join(value_list)}))"
if use_assoc_list_helpers
else f"(gold_Inputs {' '.join(value_list)})"
)
gold_State = f"(cdr step_{step-1})" if step else "gold_initial"
test_rkt_file.write(f"(define {this_step} (gold {gold_Inputs} {gold_State})) (car {this_step})\n")
get_value_expr = (
f"(gold_outputs_helper (car {this_step}))"
if use_assoc_list_helpers
else f"(car {this_step})"
)
test_rkt_file.write(
f"(define {this_step} (gold {gold_Inputs} {gold_State})) {get_value_expr}\n"
)
cmd = ["racket", test_rkt_file_path]
status = subprocess.run(cmd, capture_output=True)
assert status.returncode == 0, f"{cmd[0]} failed"
try:
status = subprocess.run(cmd, capture_output=True, check=True)
except subprocess.CalledProcessError as e:
raise RuntimeError(
f"Racket simulation failed with command: {cmd}\n"
f"Error: {e.stderr.decode()}"
) from e
for signal in outputs.keys():
signals[signal] = []
for line in status.stdout.decode().splitlines():
m = re.match(r'\(gold_Outputs( \(bv \S+ \d+\))+\)', line)
m = (
re.match(r"\(list( \(cons \"\S+\" \(bv \S+ \d+\)\))+\)", line)
if use_assoc_list_helpers
else re.match(r"\(gold_Outputs( \(bv \S+ \d+\))+\)", line)
)
assert m, f"Incomplete output definition {line!r}"
for output, (value, width) in zip(outputs.keys(), re.findall(r'\(bv (\S+) (\d+)\)', line)):
outputs_values_and_widths = (
{
output: re.findall(
r"\(cons \"" + output + r"\" \(bv (\S+) (\d+)\)\)", line
)[0]
for output in outputs.keys()
}.items()
if use_assoc_list_helpers
else zip(outputs.keys(), re.findall(r"\(bv (\S+) (\d+)\)", line))
)
for output, (value, width) in outputs_values_and_widths:
assert isinstance(value, str), f"Bad value {value!r}"
assert value.startswith(('#b', '#x')), f"Non-binary value {value!r}"
assert int(width) == outputs[output], f"Width mismatch for output {output!r} (got {width}, expected {outputs[output]})"
int_value = int(value[2:], 16 if value.startswith('#x') else 2)
binary_string = format(int_value, '0{}b'.format(width))
assert value.startswith(("#b", "#x")), f"Non-binary value {value!r}"
assert (
int(width) == outputs[output]
), f"Width mismatch for output {output!r} (got {width}, expected {outputs[output]})"
int_value = int(value[2:], 16 if value.startswith("#x") else 2)
binary_string = format(int_value, "0{}b".format(width))
signals[output].append(binary_string)
vcd_signals: SignalStepMap = {}

View file

@ -374,8 +374,9 @@ def generate_test_cases(per_cell, rnd):
for (name, parameters) in cell.generate_tests(rnd):
if not name in seen_names:
seen_names.add(name)
tests.append((cell, parameters))
names.append(f'{cell.name}-{name}' if name != '' else cell.name)
full_name = f'{cell.name}-{name}' if name != '' else cell.name
tests.append((full_name, cell, parameters))
names.append(full_name)
if per_cell is not None and len(seen_names) >= per_cell:
break
return (names, tests)

View file

@ -1,2 +1,2 @@
#!/usr/bin/env bash
pytest -v -m "not smt and not rkt" "$@"
pytest -v -n auto "$@" --steps 100

View file

@ -0,0 +1 @@
"""Python utilities for simulating Rosette code."""

View file

@ -40,12 +40,12 @@ def yosys_sim(rtlil_file, vcd_reference_file, vcd_out_file, preprocessing = ""):
capture_output=True, check=False)
raise
def test_cxx(cell, parameters, tmp_path, num_steps, rnd):
def test_cxx(name, cell, parameters, tmp_path, num_steps, rnd):
rtlil_file = tmp_path / 'rtlil.il'
vcdharness_cc_file = base_path / 'tests/functional/vcd_harness.cc'
cc_file = tmp_path / 'my_module_functional_cxx.cc'
vcdharness_exe_file = tmp_path / 'a.out'
vcd_functional_file = tmp_path / 'functional.vcd'
vcd_functional_file = tmp_path / f'{name}.vcd'
vcd_yosys_sim_file = tmp_path / 'yosys.vcd'
cell.write_rtlil_file(rtlil_file, parameters)
@ -56,12 +56,12 @@ def test_cxx(cell, parameters, tmp_path, num_steps, rnd):
yosys_sim(rtlil_file, vcd_functional_file, vcd_yosys_sim_file, getattr(cell, 'sim_preprocessing', ''))
@pytest.mark.smt
def test_smt(cell, parameters, tmp_path, num_steps, rnd):
def test_smt(name, cell, parameters, tmp_path, num_steps, rnd):
import smt_vcd
rtlil_file = tmp_path / 'rtlil.il'
smt_file = tmp_path / 'smtlib.smt'
vcd_functional_file = tmp_path / 'functional.vcd'
vcd_functional_file = tmp_path / f'{name}.vcd'
vcd_yosys_sim_file = tmp_path / 'yosys.vcd'
if hasattr(cell, 'smt_max_steps'):
@ -74,17 +74,19 @@ def test_smt(cell, parameters, tmp_path, num_steps, rnd):
yosys_sim(rtlil_file, vcd_functional_file, vcd_yosys_sim_file, getattr(cell, 'sim_preprocessing', ''))
@pytest.mark.rkt
def test_rkt(cell, parameters, tmp_path, num_steps, rnd):
@pytest.mark.parametrize("use_assoc_list_helpers", [True, False])
def test_rkt(name, cell, parameters, tmp_path, num_steps, rnd, use_assoc_list_helpers):
import rkt_vcd
rtlil_file = tmp_path / 'rtlil.il'
rkt_file = tmp_path / 'smtlib.rkt'
vcd_functional_file = tmp_path / 'functional.vcd'
vcd_functional_file = tmp_path / f'{name}.vcd'
vcd_yosys_sim_file = tmp_path / 'yosys.vcd'
cell.write_rtlil_file(rtlil_file, parameters)
yosys(f"read_rtlil {quote(rtlil_file)} ; clk2fflogic ; write_functional_rosette -provides {quote(rkt_file)}")
rkt_vcd.simulate_rosette(rkt_file, vcd_functional_file, num_steps, rnd(cell.name + "-rkt"))
use_assoc_helpers_flag = '-assoc-list-helpers' if use_assoc_list_helpers else ''
yosys(f"read_rtlil {quote(rtlil_file)} ; clk2fflogic ; write_functional_rosette -provides {use_assoc_helpers_flag} {quote(rkt_file)}")
rkt_vcd.simulate_rosette(rkt_file, vcd_functional_file, num_steps, rnd(cell.name + "-rkt"), use_assoc_list_helpers=use_assoc_list_helpers)
yosys_sim(rtlil_file, vcd_functional_file, vcd_yosys_sim_file, getattr(cell, 'sim_preprocessing', ''))
def test_print_graph(tmp_path):