-
Notifications
You must be signed in to change notification settings - Fork 888
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
eb609cc
commit 35deec5
Showing
3 changed files
with
130 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,4 @@ my_module_functional_cxx.cc | |
vcd_harness | ||
*.vcd | ||
*.smt2 | ||
*.rkt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
from __future__ import annotations | ||
from pathlib import Path | ||
import re | ||
import subprocess | ||
from typing import Dict, List, Tuple | ||
from random import Random | ||
|
||
vcd_version = "Yosys/tests/functional/rkt_vcd.py" | ||
StepList = List[Tuple[int, str]] | ||
SignalStepMap = Dict[str, StepList] | ||
SignalWidthMap = Dict[str, int] | ||
|
||
def write_vcd(filename: Path, signals: SignalStepMap, timescale='1 ns', date='today'): | ||
with open(filename, 'w') as f: | ||
# Write the header | ||
f.write(f"$date\n {date}\n$end\n") | ||
f.write(f"$timescale {timescale} $end\n") | ||
|
||
# Declare signals | ||
f.write("$scope module gold $end\n") | ||
for signal_name, changes in signals.items(): | ||
signal_size = len(changes[0][1]) | ||
f.write(f"$var wire {signal_size - 1} {signal_name} {signal_name} $end\n") | ||
f.write("$upscope $end\n") | ||
f.write("$enddefinitions $end\n") | ||
|
||
# Collect all unique timestamps | ||
timestamps = sorted(set(time for changes in signals.values() for time, _ in changes)) | ||
|
||
# Write initial values | ||
f.write("#0\n") | ||
for signal_name, changes in signals.items(): | ||
for time, value in changes: | ||
if time == 0: | ||
f.write(f"{value} {signal_name}\n") | ||
|
||
# Write value changes | ||
for time in timestamps: | ||
if time != 0: | ||
f.write(f"#{time}\n") | ||
for signal_name, changes in signals.items(): | ||
for change_time, value in changes: | ||
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): | ||
signals: dict[str, list[str]] = {} | ||
inputs: SignalWidthMap = {} | ||
outputs: SignalWidthMap = {} | ||
|
||
current_struct_name: str = "" | ||
with open(rkt_file_path, 'r') as rkt_file: | ||
for line in rkt_file: | ||
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) | ||
signal = m.group(1) | ||
width = int(m.group(2)) | ||
if current_struct_name == "Inputs": | ||
inputs[signal] = width | ||
elif current_struct_name == "Outputs": | ||
outputs[signal] = width | ||
|
||
for signal, width in inputs.items(): | ||
step_list: list[int] = [] | ||
for step in range(num_steps): | ||
value = rnd.getrandbits(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', | ||
]) | ||
|
||
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)})" | ||
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") | ||
|
||
cmd = ["racket", test_rkt_file_path] | ||
status = subprocess.run(cmd, capture_output=True) | ||
assert status.returncode == 0, f"{cmd[0]} failed" | ||
|
||
for signal in outputs.keys(): | ||
signals[signal] = [] | ||
|
||
for line in status.stdout.decode().splitlines(): | ||
m = 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)): | ||
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)) | ||
signals[output].append(binary_string) | ||
|
||
vcd_signals: SignalStepMap = {} | ||
for signal, steps in signals.items(): | ||
vcd_signals[signal] = [(time, f"b{value}") for time, value in enumerate(steps)] | ||
|
||
write_vcd(vcd_path, vcd_signals) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters