"""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')