feat(phase1): implement RNG, SampleCBD, SampleNTT modules + xsim TBs

Phase 1 complete — all 4 leaf modules verified:
- rng_sync.v: 256-bit Galois LFSR PRNG (10/10 PASS)
- sample_cbd_sync.v: CBD sampler with keccak_core PRF (2560/2560 PASS)
- sample_ntt_sync.v: SHAKE-128 rejection sampling for A matrix (1536/1536 PASS)
- xsim Verilog TBs for sha3 module (tb_sha3_xsim.v, tb_sha3_xsim_simple.v, tb_keccak_core_xsim.v)
This commit is contained in:
2026-06-24 21:32:53 +08:00
parent 453bc899fc
commit 5941fee980
16 changed files with 2398 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
"""gen_vectors.py - Test vector generator for rng module.
Generates expected 256-bit LFSR output values using the same Galois LFSR
polynomial as the RTL: x^256 + x^255 + x^253 + x^252 + x^247 + 1
Taps: [255, 253, 252, 247, 0]
"""
import os
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
# Default seed matches RTL SEED parameter
DEFAULT_SEED = 0xDEADBEEFCAFEBABEFEEDFACEDECAFBAD1234567887654321ABCDEF010FEDCBA9
# 256-bit mask
MASK_256 = (1 << 256) - 1
class _LFSR256:
"""256-bit Galois LFSR matching rng_sync.v implementation.
Polynomial: x^256 + x^255 + x^253 + x^252 + x^247 + 1
Taps: [255, 253, 252, 247, 0]
"""
def __init__(self, seed: int):
self.state = seed & MASK_256
def next(self) -> int:
"""Advance LFSR one step and return the new state."""
feedback = self.state & 1 # LSB = state[0]
# Shift right by 1, feedback into MSB (position 255)
new_state = (self.state >> 1) | (feedback << 255)
# XOR feedback into tap positions (shifted)
if feedback:
new_state ^= (1 << 254) # tap 255 -> position 254
new_state ^= (1 << 252) # tap 253 -> position 252
new_state ^= (1 << 251) # tap 252 -> position 251
new_state ^= (1 << 246) # tap 247 -> position 246
self.state = new_state & MASK_256
return self.state
class RngVectorGenerator(VectorGenerator):
"""Generates test vectors for the rng_sync module."""
def __init__(self):
super().__init__()
self._lfsr = None
def _ensure_lfsr(self, params: dict) -> None:
"""Initialize LFSR on first call or when params change seed."""
seed = params.get('seed', DEFAULT_SEED)
if self._lfsr is None:
self._lfsr = _LFSR256(seed)
def generate_one(self, params: dict) -> dict:
"""Generate one LFSR output value.
On each call, advances the LFSR from its current state and returns
the new state. This matches the RTL behavior: valid_i advances the
LFSR, and the new value appears on data_o.
Args:
params: Optional dict with 'seed' key to override default seed.
Returns:
dict with 'input' (empty) and 'expected' {'data': lfsr_state_hex}.
"""
self._ensure_lfsr(params)
new_state = self._lfsr.next()
return {
'input': {},
'expected': {'data': new_state}
}
def write_hex_file(self, vectors: list[dict], filepath: str) -> None:
"""Write input file with one dummy line per vector.
The RNG has no input signals, but the testbench reads the hex file
to determine the number of vectors to generate. Each line acts as
a pulse trigger.
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 _ in vectors:
f.write('0\n')
def write_expected_file(self, vectors: list[dict], filepath: str) -> None:
"""Write expected output as 64-char hex strings, one per line.
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:
data = v['expected']['data']
# Format as 64-character uppercase hex string
f.write(f'{data:064X}\n')

View File

@@ -0,0 +1,17 @@
{
"module": "rng",
"rtl_top": "sync_rtl/rng/rng_sync.v",
"rtl_deps": [],
"tb_cpp": "sync_rtl/rng/TB/tb_rng.cpp",
"simulator": "verilator",
"timeout_s": 30,
"cases": [
{
"id": "basic",
"description": "Generate 10 pseudo-random 256-bit values with fixed-seed LFSR",
"params": {},
"num_vectors": 10,
"tolerance": "bit_exact"
}
]
}