mirror of
https://github.com/SikongJueluo/Mini-Nav.git
synced 2026-07-12 20:15:31 +08:00
feat(rtl): migrate CAM interface to handshake protocol with integrated noise generation
BREAKING CHANGE: CAM write and query interface replaced with standard valid/ready handshake. wr_en/wr_row/wr_hash → wr_valid/wr_ready/wr_addr/write_hash. External noise_mask_lanes_flat removed; noise generation now handled internally by cam_noisy module with configurable rate via parameters. - cam_top: add parameters (NOISE_EN, NOISE_RATE_NUM/DEN, NOISE_GEN/SAMPLE_BITS, NOISE_SEED) - cam_top: replace cam_core with cam_noisy (integrated noise generation) - match_engine: remove external noise_mask_lanes_flat input - hw/sim: update Makefile with noise parameters and compile args - hw/sim/model: add generate_write_flip_mask() and xorshift64() matching RTL behavior - hw/sim/tests: adapt testbench to new handshake protocol
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Iterable, Sequence
|
||||
from typing import Sequence
|
||||
import numpy as np
|
||||
|
||||
|
||||
@@ -25,25 +25,20 @@ def xnor_popcount_score(query: int, stored: int, width: int = 512) -> int:
|
||||
return popcount_int(same_bits)
|
||||
|
||||
|
||||
def apply_noise(stored: int, noise_mask: int) -> int:
|
||||
return stored ^ noise_mask
|
||||
|
||||
|
||||
def match_top1(
|
||||
query: int,
|
||||
rows: Sequence[int],
|
||||
*,
|
||||
width: int = 512,
|
||||
noise_masks: Sequence[int] | None = None,
|
||||
) -> MatchResult:
|
||||
"""Pure matching — noise is already baked into rows at write time."""
|
||||
scores = np.zeros(len(rows), dtype=np.int32)
|
||||
|
||||
best_index = 0
|
||||
best_score = -1
|
||||
|
||||
for idx, row in enumerate(rows):
|
||||
effective = row if noise_masks is None else apply_noise(row, int(noise_masks[idx]))
|
||||
score = xnor_popcount_score(int(query), int(effective), width)
|
||||
score = xnor_popcount_score(int(query), int(row), width)
|
||||
scores[idx] = score
|
||||
|
||||
# Tie-break: choose the smallest row index.
|
||||
@@ -58,6 +53,54 @@ def match_top1(
|
||||
)
|
||||
|
||||
|
||||
def xorshift64(state: int) -> int:
|
||||
"""64-bit XOR-shift PRNG, single step. Matches RTL behavior."""
|
||||
mask64 = (1 << 64) - 1
|
||||
s = state & mask64
|
||||
s ^= (s << 13) & mask64
|
||||
s ^= (s >> 7) & mask64
|
||||
s ^= (s << 17) & mask64
|
||||
return s
|
||||
|
||||
|
||||
def generate_write_flip_mask(
|
||||
prng_state: int,
|
||||
hash_bits: int,
|
||||
noise_gen_bits: int,
|
||||
noise_sample_bits: int,
|
||||
rate_num: int,
|
||||
rate_den: int,
|
||||
) -> tuple[int, int]:
|
||||
"""
|
||||
Generate write-noise flip mask.
|
||||
Returns (flip_mask, next_prng_state).
|
||||
Matches RTL multi-cycle GEN_MASK behavior.
|
||||
|
||||
Each cycle processes noise_gen_bits bit decisions:
|
||||
- Advance xorshift64 → 64-bit output
|
||||
- Split into noise_gen_bits x noise_sample_bits-bit samples
|
||||
- Each sample < THRESHOLD → that bit flips
|
||||
"""
|
||||
assert hash_bits % noise_gen_bits == 0
|
||||
assert noise_gen_bits * noise_sample_bits == 64
|
||||
mask = 0
|
||||
state = prng_state
|
||||
sample_range = 1 << noise_sample_bits
|
||||
threshold = (rate_num * sample_range) // rate_den
|
||||
|
||||
for bit_offset in range(0, hash_bits, noise_gen_bits):
|
||||
# Advance PRNG
|
||||
state = xorshift64(state)
|
||||
|
||||
# Split into noise_gen_bits independent samples
|
||||
for b in range(noise_gen_bits):
|
||||
sample_b = (state >> (b * noise_sample_bits)) & (sample_range - 1)
|
||||
if sample_b < threshold:
|
||||
mask |= (1 << (bit_offset + b))
|
||||
|
||||
return mask, state
|
||||
|
||||
|
||||
def random_hashes(
|
||||
rng: np.random.Generator,
|
||||
n: int,
|
||||
@@ -76,35 +119,6 @@ def random_hashes(
|
||||
return out
|
||||
|
||||
|
||||
def random_noise_masks(
|
||||
rng: np.random.Generator,
|
||||
n: int,
|
||||
*,
|
||||
width: int = 512,
|
||||
bit_flip_rate: float = 0.0,
|
||||
) -> list[int]:
|
||||
if not (0.0 <= bit_flip_rate <= 1.0):
|
||||
raise ValueError("bit_flip_rate must be in [0, 1]")
|
||||
|
||||
masks: list[int] = []
|
||||
for _ in range(n):
|
||||
bits = rng.random(width) < bit_flip_rate
|
||||
value = 0
|
||||
for i, bit in enumerate(bits):
|
||||
if bool(bit):
|
||||
value |= 1 << i
|
||||
masks.append(value)
|
||||
return masks
|
||||
|
||||
|
||||
def pack_lanes_flat(masks: Sequence[int], *, width: int = 512) -> int:
|
||||
flat = 0
|
||||
lane_mask = mask_width(width)
|
||||
for lane, mask in enumerate(masks):
|
||||
flat |= (int(mask) & lane_mask) << (lane * width)
|
||||
return flat
|
||||
|
||||
|
||||
def unpack_score_debug_flat(flat: int, num_rows: int, score_bits: int) -> np.ndarray:
|
||||
mask = (1 << score_bits) - 1
|
||||
return np.array(
|
||||
|
||||
Reference in New Issue
Block a user