- 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
122 lines
3.6 KiB
Python
122 lines
3.6 KiB
Python
"""gen_vectors.py - Test vector generator for mod_add module.
|
|
|
|
Generates (a, b) input pairs and computes expected (a + b) mod Q.
|
|
"""
|
|
|
|
import os
|
|
import random
|
|
import sys
|
|
|
|
# Add test_framework/lib to path for VectorGenerator base class
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', 'lib'))
|
|
|
|
from vector_gen import VectorGenerator
|
|
|
|
Q = 3329 # ML-KEM prime modulus
|
|
|
|
|
|
class ModAddVectorGenerator(VectorGenerator):
|
|
"""Generates test vectors for the mod_add_sync module."""
|
|
|
|
def generate_one(self, params: dict) -> dict:
|
|
"""Generate a single (a, b) pair with expected modular sum.
|
|
|
|
Uses random values covering:
|
|
- No overflow: a + b < Q
|
|
- With overflow: a + b >= Q
|
|
- Edge cases: a=0, b=0, a=Q-1, b=Q-1
|
|
|
|
Args:
|
|
params: Unused for mod_add basic case.
|
|
|
|
Returns:
|
|
dict with 'input' and 'expected' keys.
|
|
"""
|
|
# Mix of random and edge cases for coverage
|
|
roll = random.random()
|
|
|
|
if roll < 0.05:
|
|
# Both zero
|
|
a, b = 0, 0
|
|
elif roll < 0.10:
|
|
# a=0, b random
|
|
a = 0
|
|
b = random.randint(0, Q - 1)
|
|
elif roll < 0.15:
|
|
# b=0, a random
|
|
a = random.randint(0, Q - 1)
|
|
b = 0
|
|
elif roll < 0.20:
|
|
# Both Q-1 (max overflow)
|
|
a, b = Q - 1, Q - 1
|
|
elif roll < 0.25:
|
|
# a random, b = Q-1
|
|
a = random.randint(0, Q - 1)
|
|
b = Q - 1
|
|
elif roll < 0.65:
|
|
# No overflow: both small
|
|
a = random.randint(0, Q // 2 - 1)
|
|
b = random.randint(0, Q // 2 - 1)
|
|
else:
|
|
# Potential overflow
|
|
a = random.randint(0, Q - 1)
|
|
b = random.randint(0, Q - 1)
|
|
|
|
# Compute expected: (a + b) mod Q
|
|
expected_sum = (a + b) % Q
|
|
|
|
return {
|
|
'input': {'a': a, 'b': b},
|
|
'expected': {'sum': expected_sum}
|
|
}
|
|
|
|
def write_hex_file(self, vectors: list[dict], filepath: str) -> None:
|
|
"""Write input vectors as "AAA BBB" hex format.
|
|
|
|
Args:
|
|
vectors: List of vector dicts from generate_one().
|
|
filepath: Path to write the hex file.
|
|
"""
|
|
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
|
with open(filepath, 'w') as f:
|
|
for v in vectors:
|
|
a = v['input']['a']
|
|
b = v['input']['b']
|
|
f.write(f'{a:03X} {b:03X}\n')
|
|
|
|
def write_expected_file(self, vectors: list[dict], filepath: str) -> None:
|
|
"""Write expected output as "CCC" hex format.
|
|
|
|
Args:
|
|
vectors: List of vector dicts from generate_one().
|
|
filepath: Path to write the expected hex file.
|
|
"""
|
|
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
|
with open(filepath, 'w') as f:
|
|
for v in vectors:
|
|
s = v['expected']['sum']
|
|
f.write(f'{s:03X}\n')
|
|
|
|
def compare_results(self, got: list[str], expected_file: str) -> bool:
|
|
"""Compare RTL output against expected values.
|
|
|
|
Args:
|
|
got: List of hex result strings from simulation.
|
|
expected_file: Path to expected hex file.
|
|
|
|
Returns:
|
|
bool: True if all results match.
|
|
"""
|
|
with open(expected_file, 'r') as f:
|
|
expected = [line.strip() for line in f
|
|
if line.strip() and not line.startswith('#')]
|
|
|
|
if len(got) != len(expected):
|
|
return False
|
|
|
|
for i, (g, e) in enumerate(zip(got, expected)):
|
|
if g.upper() != e.upper():
|
|
return False
|
|
|
|
return True
|