mirror of
https://github.com/SikongJueluo/Mini-Nav.git
synced 2026-07-12 20:15:31 +08:00
feat(cam): migrate noise generation from xorshift64 to xorshift128
- Replace NOISE_GEN_BITS/NOISE_SAMPLE_BITS parameters with unified NOISE_BITS
- Use xorshift128 (random128) instead of xorshift64 for PRNG
- Add flip_mask_next combinational helper for single-cycle mask computation
- Add random_enable signal to advance PRNG only on accepted noisy writes
- Simplify FSM by removing mask_group_idx counter
- Update parameter validation: GROUP_BITS (= HASH_BITS/NOISE_BITS) must equal 64
- Update ref_model.py and tests to match new seed convention: {seed, seed}
- Update Makefile and sweep_noise.py with renamed parameters
This commit is contained in:
@@ -9,7 +9,6 @@ from model.ref_model import ( # noqa: E402
|
||||
match_top1,
|
||||
random_hashes,
|
||||
unpack_score_debug_flat,
|
||||
xorshift64,
|
||||
)
|
||||
|
||||
NUM_ROWS = 512
|
||||
@@ -205,19 +204,45 @@ async def zero_rate_noise(dut):
|
||||
|
||||
@cocotb.test()
|
||||
async def full_rate_noise(dut):
|
||||
"""THRESHOLD=256 → all bits flip. stored == ~written."""
|
||||
"""NOISE_RATE_NUM=1, NOISE_RATE_DEN=1 → every group flips its selected bit per write.
|
||||
|
||||
At full rate with default NOISE_BITS=8, exactly 8 deterministic bits flip per write
|
||||
(one selected bit per group), not all 512 bits. We use ref_model.py PRNG to predict
|
||||
the exact stored rows.
|
||||
"""
|
||||
noise_en = _get_param(dut, "NOISE_EN", 1)
|
||||
rate_num = _get_param(dut, "NOISE_RATE_NUM", 1)
|
||||
rate_den = _get_param(dut, "NOISE_RATE_DEN", 100)
|
||||
if not noise_en or rate_num != 1 or rate_den != 1:
|
||||
dut._log.info("Skipping full_rate_noise: requires NOISE_EN=1, RATE_NUM=1, RATE_DEN=1.")
|
||||
return
|
||||
if not hasattr(dut, "score_debug_flat"):
|
||||
dut._log.info("Skipping full_rate_noise: requires SIM_DEBUG (score_debug_flat).")
|
||||
return
|
||||
|
||||
cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start())
|
||||
await reset_dut(dut)
|
||||
|
||||
noise_bits = _get_param(dut, "NOISE_BITS", 8)
|
||||
all_zero = 0
|
||||
all_one = (1 << HASH_BITS) - 1
|
||||
|
||||
# Predict stored rows using the same RTL seed convention as exact_noise_model_match.
|
||||
RTL_SEED = 0xB504_F32D_B504_F32D
|
||||
prng_state = (RTL_SEED << 64) | RTL_SEED
|
||||
|
||||
# Flip mask for row 0 (all-zero written)
|
||||
flip0, prng_state = generate_write_flip_mask(
|
||||
prng_state, HASH_BITS, noise_bits, rate_num, rate_den,
|
||||
)
|
||||
expected_row0 = all_zero ^ flip0 # stored value after noise
|
||||
|
||||
# Flip mask for row 1 (all-one written)
|
||||
flip1, prng_state = generate_write_flip_mask(
|
||||
prng_state, HASH_BITS, noise_bits, rate_num, rate_den,
|
||||
)
|
||||
expected_row1 = all_one ^ flip1 # stored value after noise
|
||||
|
||||
# Write all-zero to row 0, all-one to row 1, rest zero
|
||||
rows = [0] * NUM_ROWS
|
||||
rows[0] = all_zero
|
||||
@@ -225,20 +250,21 @@ async def full_rate_noise(dut):
|
||||
|
||||
await write_rows(dut, rows)
|
||||
|
||||
# After 100% flip: row 0 stored = ~0 = all_one, row 1 stored = ~all_one = all_zero
|
||||
# Query all-zero → matches row 1 (stored all_zero) with score = HASH_BITS
|
||||
top1_index, top1_score, _ = await query_once(dut, all_zero)
|
||||
assert top1_index == 1
|
||||
assert top1_score == HASH_BITS
|
||||
# Query expected_row0 → should exactly match row 0's stored value
|
||||
top1_index, top1_score, score_debug = await query_once(dut, expected_row0)
|
||||
assert score_debug is not None, "score_debug required for full_rate_noise"
|
||||
assert int(score_debug[0]) == HASH_BITS, (
|
||||
f"Row 0: expected exact match for predicted stored value, "
|
||||
f"score={score_debug[0]} != {HASH_BITS}"
|
||||
)
|
||||
|
||||
# Query all-one → matches row 0 (stored all_one) with score = HASH_BITS
|
||||
top1_index, top1_score, _ = await query_once(dut, all_one)
|
||||
assert top1_index == 0
|
||||
assert top1_score == HASH_BITS
|
||||
|
||||
# Query all-zero → score against row 0 (stored all_one) = 0
|
||||
# Re-query to verify: row 0 stored is all_one, query all_zero → score 0
|
||||
# But top1 picks row 1 with score HASH_BITS, so top1_index=1
|
||||
# Query expected_row1 → should exactly match row 1's stored value
|
||||
top1_index, top1_score, score_debug = await query_once(dut, expected_row1)
|
||||
assert score_debug is not None, "score_debug required for full_rate_noise"
|
||||
assert int(score_debug[1]) == HASH_BITS, (
|
||||
f"Row 1: expected exact match for predicted stored value, "
|
||||
f"score={score_debug[1]} != {HASH_BITS}"
|
||||
)
|
||||
|
||||
|
||||
# ── Test D: Default ~1% noise, reproducible ────────────────────────────────
|
||||
@@ -378,22 +404,21 @@ async def exact_noise_model_match(dut):
|
||||
cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start())
|
||||
await reset_dut(dut)
|
||||
|
||||
noise_gen_bits = _get_param(dut, "NOISE_GEN_BITS", 8)
|
||||
noise_sample_bits = _get_param(dut, "NOISE_SAMPLE_BITS", 8)
|
||||
noise_bits = _get_param(dut, "NOISE_BITS", 8)
|
||||
|
||||
# Use a small subset to keep test fast
|
||||
n_test_rows = 4
|
||||
rng = np.random.default_rng(99)
|
||||
rows = random_hashes(rng, n_test_rows, width=HASH_BITS)
|
||||
|
||||
# Predict stored hashes with Python model using the same seed
|
||||
# RTL default seed: 64'hB504_F32D_B504_F32D
|
||||
# Predict stored hashes with Python model using the same seed.
|
||||
# RTL random128 seed: {NOISE_SEED, NOISE_SEED}, default 64'hB504_F32D_B504_F32D.
|
||||
RTL_SEED = 0xB504_F32D_B504_F32D
|
||||
prng_state = RTL_SEED
|
||||
prng_state = (RTL_SEED << 64) | RTL_SEED
|
||||
expected_stored = []
|
||||
for row in rows:
|
||||
flip, prng_state = generate_write_flip_mask(
|
||||
prng_state, HASH_BITS, noise_gen_bits, noise_sample_bits, rate_num, rate_den,
|
||||
prng_state, HASH_BITS, noise_bits, rate_num, rate_den,
|
||||
)
|
||||
expected_stored.append(row ^ flip)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user