Files
Mini-Nav/hw/sim/tests/top/write_noise/test_write_noise.py
SikongJueluo 8b4d4c1b57 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
2026-05-26 23:45:52 +08:00

134 lines
4.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
CAM 写入噪声Write Noise集成测试 —— 专用配置。
本文件测试 WRITE_NOISE_EN=1 配置下,
写入噪声模块的正确性。默认噪声率约 1%NUM=1, DEN=100
=== 测试列表 ===
1. default_noise_reproducible — 固定种子 = 确定性噪声,两次运行结果一致
2. exact_noise_model_match — RTL 存储的哈希与 ref_model.py 的 PRNG 掩码逐位匹配
3. zero_rate_noise — 写入噪声模块连接但 RATE_NUM=0 → 无翻转
4. full_rate_noise — 100% 写入噪声率,与 Python 模型对比
=== 架构背景 ===
写入噪声流水线位置Write Noise → Banked Core Storage → Match Engine
本测试覆盖完整的 cam_top 链路,写入噪声为唯一活跃噪声源。
=== Makefile 子目标 ===
test-zero-rate : make test-zero-rate (WRITE_NOISE_RATE_NUM=0)
test-full-rate : make test-full-rate (WRITE_NOISE_RATE_NUM=1, RATE_DEN=1, SIM_DEBUG)
"""
from __future__ import annotations
import cocotb
import numpy as np
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge
from model.ref_model import (
match_top1,
random_hashes,
)
from tests.top.utils import (
collect_topk,
dut_hash_bits,
dut_num_rows,
get_param,
query_once,
query_topk_once,
reset_dut,
wait_idle,
write_row,
write_rows,
)
# ═══════════════════════════════════════════════════════════════════════════════
# 测试 1默认噪声 ~1%、可复现
# ── 固定种子 → 两次相同写入产生相同结果
# ═══════════════════════════════════════════════════════════════════════════════
@cocotb.test()
async def default_noise_reproducible(dut):
"""可复现性测试:相同种子、相同数据 → 两次独立运行结果一致。
流程:
1. 写入全部行,查询 row 50 → 记录 top1_index 和 top1_score
2. 复位
3. 再次写入相同数据,查询相同行 → 记录结果
4. 断言两次结果完全一致
如果结果不一致,说明 RTL 的 PRNG 状态没有正确复位,
或存在跨运行的状态残留。
"""
cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start())
await reset_dut(dut)
num_rows = dut_num_rows(dut)
hash_bits = dut_hash_bits(dut)
rng = np.random.default_rng(42)
rows = random_hashes(rng, num_rows, width=hash_bits)
await write_rows(dut, rows)
query = rows[min(50, num_rows - 1)]
top1_index_1, top1_score_1, _ = await query_once(dut, query)
await reset_dut(dut)
await write_rows(dut, rows)
top1_index_2, top1_score_2, _ = await query_once(dut, query)
assert top1_index_1 == top1_index_2
assert top1_score_1 == top1_score_2
# ═══════════════════════════════════════════════════════════════════════════════
# 测试 2零噪声率WRITE_NOISE_EN=1, RATE_NUM=0
# ── 噪声模块已连接但翻转概率为 0 → 行为应与无噪声一致
# ═══════════════════════════════════════════════════════════════════════════════
@cocotb.test()
async def zero_rate_noise(dut):
"""零速率噪声WRITE_NOISE_RATE_NUM=0 时不应有任何位被翻转。
这是噪声模块的边界测试——验证 RATE_NUM=0 确实禁用了翻转,
而非产生「默认速率」的噪声。
"""
rate_num = get_param(dut, "WRITE_NOISE_RATE_NUM", 1)
if rate_num != 0:
dut._log.info(
"Skipping zero_rate_noise: requires WRITE_NOISE_RATE_NUM=0."
)
return
cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start())
await reset_dut(dut)
num_rows = dut_num_rows(dut)
hash_bits = dut_hash_bits(dut)
rng = np.random.default_rng(1)
rows = random_hashes(rng, num_rows, width=hash_bits)
query_index = min(123, num_rows - 1)
query = rows[query_index]
await write_rows(dut, rows)
top1_index, top1_score, score_debug = await query_once(dut, query)
expected = match_top1(query, rows, width=hash_bits)
assert top1_index == expected.top1_index
assert top1_score == expected.top1_score
assert top1_index == query_index
assert top1_score == hash_bits
if score_debug is not None:
assert np.array_equal(score_debug, expected.scores)