Files
mlkem-sync/test_framework/modules/mod_add/gen_vectors.py
FallenSigh 8fdf944555 feat: init mlkem project with Verilator test framework
- 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
2026-06-24 19:43:29 +08:00

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