mirror of
https://github.com/SikongJueluo/Mini-Nav.git
synced 2026-07-12 20:15:31 +08:00
refactor(cam): remove read noise from noise architecture (Phase 2)
- Make cam_read_noise a pass-through module, removing all noise injection logic - Switch write noise to use noise_mask_bernoulli instead of noise_mask_grouped - Add state machine to cam_write_noise for mask generation timing - Remove noise_mask_grouped.sv (no longer needed) - Remove read noise parameters from cam_noisy and cam_top - Update simulation and benchmark code to reflect read noise removal - Sync documentation to reflect Phase 2 architecture
This commit is contained in:
@@ -7,9 +7,5 @@ COCOTB_TEST_MODULES := tests.modules.cam_read_noise.test_cam_read_noise
|
||||
VERILOG_SOURCES := $(RTL_READ_NOISE)
|
||||
|
||||
HASH_BITS ?= 512
|
||||
READ_NOISE_EN ?= 0
|
||||
READ_NOISE_RATE_NUM ?= 0
|
||||
READ_NOISE_RATE_DEN ?= 100
|
||||
READ_NOISE_BITS ?= $(shell echo $$(( $(HASH_BITS) / 64 )))
|
||||
|
||||
include $(SIM_ROOT)/mk/cocotb-common.mk
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.triggers import RisingEdge, Timer
|
||||
|
||||
|
||||
async def reset_read_noise(dut):
|
||||
@@ -38,11 +38,45 @@ async def read_noise_disabled_forwards_hashes_after_one_stage(dut):
|
||||
dut.row_ids_i.value = rows
|
||||
dut.lane_valid_i.value = all_lanes_valid
|
||||
dut.valid_i.value = 1
|
||||
await RisingEdge(dut.clk)
|
||||
await Timer(1, unit="step")
|
||||
await RisingEdge(dut.clk) # valid_o ← valid_i=1 internally
|
||||
await Timer(1, unit="step")
|
||||
dut.valid_i.value = 0
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
# One-stage pass-through: valid_o holds the latched value for this cycle
|
||||
assert int(dut.valid_o.value) == 1
|
||||
assert int(dut.hashes_noisy_o.value) == hashes
|
||||
assert int(dut.row_ids_o.value) == rows
|
||||
assert int(dut.lane_valid_o.value) == all_lanes_valid
|
||||
|
||||
|
||||
@cocotb.test()
|
||||
async def read_noise_enabled_still_forwards_hashes_unmodified(dut):
|
||||
"""With READ_NOISE_EN=1, the pass-through still forwards hashes unmodified."""
|
||||
cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start())
|
||||
await reset_read_noise(dut)
|
||||
|
||||
LANES = len(dut.lane_valid_i)
|
||||
ROW_BITS = len(dut.row_ids_i) // LANES
|
||||
HASH_BITS_PER_LANE = len(dut.hashes_i) // LANES
|
||||
all_lanes_valid = (1 << LANES) - 1
|
||||
|
||||
hashes = 0
|
||||
rows = 0
|
||||
for lane in range(LANES):
|
||||
hashes |= (lane + 0x55) << (lane * HASH_BITS_PER_LANE)
|
||||
rows |= lane << (lane * ROW_BITS)
|
||||
|
||||
dut.hashes_i.value = hashes
|
||||
dut.row_ids_i.value = rows
|
||||
dut.lane_valid_i.value = all_lanes_valid
|
||||
dut.valid_i.value = 1
|
||||
await Timer(1, unit="step")
|
||||
await RisingEdge(dut.clk) # valid_o ← valid_i=1 internally
|
||||
await Timer(1, unit="step")
|
||||
dut.valid_i.value = 0
|
||||
|
||||
# One-stage pass-through: valid_o holds latched value from previous cycle
|
||||
assert int(dut.valid_o.value) == 1
|
||||
assert int(dut.hashes_noisy_o.value) == hashes
|
||||
assert int(dut.row_ids_o.value) == rows
|
||||
|
||||
@@ -10,6 +10,4 @@ HASH_BITS ?= 512
|
||||
WRITE_NOISE_EN ?= 1
|
||||
WRITE_NOISE_RATE_NUM ?= 1
|
||||
WRITE_NOISE_RATE_DEN ?= 100
|
||||
WRITE_NOISE_BITS ?= $(shell echo $$(( $(HASH_BITS) / 64 )))
|
||||
|
||||
include $(SIM_ROOT)/mk/cocotb-common.mk
|
||||
|
||||
@@ -2,8 +2,32 @@ from __future__ import annotations
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from model.ref_model import generate_write_flip_mask
|
||||
from cocotb.triggers import RisingEdge, Timer
|
||||
|
||||
# Bernoulli: 1 PRIME + 16 RUN = 17 cycles internal
|
||||
# + 1 cycle for mask_start propagation + 1 cycle for core_wr_valid output = 19
|
||||
DEFAULT_WRITE_NOISE_LATENCY = 19
|
||||
|
||||
|
||||
async def pulse_write(dut, row: int, value: int):
|
||||
dut.wr_row.value = row
|
||||
dut.wr_hash.value = value
|
||||
dut.wr_valid.value = 1
|
||||
await Timer(1, unit="step")
|
||||
assert int(dut.wr_ready.value) == 1
|
||||
await RisingEdge(dut.clk)
|
||||
await Timer(1, unit="step")
|
||||
dut.wr_valid.value = 0
|
||||
|
||||
|
||||
async def wait_core_write(dut, max_cycles: int = 128) -> int:
|
||||
cycles = 0
|
||||
while int(dut.core_wr_valid.value) == 0:
|
||||
assert cycles < max_cycles, "timed out waiting for core_wr_valid"
|
||||
await RisingEdge(dut.clk)
|
||||
await Timer(1, unit="step")
|
||||
cycles += 1
|
||||
return cycles
|
||||
|
||||
|
||||
async def reset_write_noise(dut):
|
||||
@@ -19,23 +43,52 @@ async def reset_write_noise(dut):
|
||||
|
||||
|
||||
@cocotb.test()
|
||||
async def write_noise_outputs_grouped_noisy_hash(dut):
|
||||
async def write_noise_enabled_applies_bernoulli_mask_after_generation(dut):
|
||||
"""Noise active: FSM enters WAIT_MASK, core_wr_hash deterministic across reset."""
|
||||
cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start())
|
||||
await reset_write_noise(dut)
|
||||
|
||||
value = 0x123456789ABCDEF
|
||||
dut.wr_row.value = 3
|
||||
dut.wr_hash.value = value
|
||||
dut.wr_valid.value = 1
|
||||
value = (1 << 512) - 1 # all-ones: even low-rate Bernoulli may flip some bits
|
||||
await pulse_write(dut, row=3, value=value)
|
||||
await Timer(1, unit="step")
|
||||
assert int(dut.wr_ready.value) == 0
|
||||
|
||||
cycles = await wait_core_write(dut)
|
||||
assert cycles == DEFAULT_WRITE_NOISE_LATENCY
|
||||
|
||||
assert int(dut.core_wr_row.value) == 3
|
||||
hash_after_first = int(dut.core_wr_hash.value)
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await Timer(1, unit="step")
|
||||
assert int(dut.core_wr_valid.value) == 0
|
||||
assert int(dut.wr_ready.value) == 1
|
||||
|
||||
# Deterministic across reset: same seed → same mask → same noisy hash
|
||||
await reset_write_noise(dut)
|
||||
await pulse_write(dut, row=3, value=value)
|
||||
await wait_core_write(dut)
|
||||
assert int(dut.core_wr_hash.value) == hash_after_first
|
||||
|
||||
|
||||
@cocotb.test()
|
||||
async def write_noise_backpressures_second_write_until_done(dut):
|
||||
cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start())
|
||||
await reset_write_noise(dut)
|
||||
|
||||
await pulse_write(dut, row=1, value=0xAA55)
|
||||
|
||||
dut.wr_row.value = 2
|
||||
dut.wr_hash.value = 0x55AA
|
||||
dut.wr_valid.value = 1
|
||||
await Timer(1, unit="step")
|
||||
for _ in range(4):
|
||||
assert int(dut.wr_ready.value) == 0
|
||||
assert int(dut.core_wr_valid.value) == 0
|
||||
await RisingEdge(dut.clk)
|
||||
await Timer(1, unit="step")
|
||||
dut.wr_valid.value = 0
|
||||
|
||||
while int(dut.core_wr_valid.value) == 0:
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
seed = 0xB504_F32D_B504_F32D
|
||||
hash_bits = len(dut.wr_hash)
|
||||
noise_bits = hash_bits // 64
|
||||
flip, _ = generate_write_flip_mask((seed << 64) | seed, hash_bits, noise_bits, 1, 100)
|
||||
assert int(dut.core_wr_row.value) == 3
|
||||
assert int(dut.core_wr_hash.value) == (value ^ flip)
|
||||
cycles = await wait_core_write(dut)
|
||||
assert cycles == DEFAULT_WRITE_NOISE_LATENCY - 4 # 19 - 4 = 15
|
||||
assert int(dut.core_wr_row.value) == 1
|
||||
|
||||
Reference in New Issue
Block a user