mirror of
https://github.com/SikongJueluo/Mini-Nav.git
synced 2026-07-12 20:15:31 +08:00
fix(pipeline): add S_WAIT_READ_RESP state to fix read-noise PRNG timing
- Emit rd_valid_o for exactly one combinational cycle before waiting for read response, ensuring the read-noise PRNG advances once per batch issue - Fix query handshake: wait for query_ready before asserting query_valid to avoid valid&&ready handshake drops on clock edges - Add dynamic timeout estimation in test utilities based on DUT parameters - Update test-top Makefile to run all noise configurations by default - Remove uv run prefix from cocotb-config Makefile invocation
This commit is contained in:
@@ -50,13 +50,6 @@ remote-dry cmd:
|
||||
|
||||
# ── CAM verification ──────────────────────────────────────────────────────────
|
||||
|
||||
# Run all 4 integrated CAM configurations (no-noise, write-only, read-only, combined)
|
||||
cam-test-all:
|
||||
just remote "make -C hw/sim clean && make -C hw/sim test-top NUM_ROWS=64 LANES=8 HASH_BITS=512 WRITE_NOISE_EN=0 READ_NOISE_EN=0"
|
||||
just remote "make -C hw/sim clean && make -C hw/sim test-top NUM_ROWS=64 LANES=8 HASH_BITS=512 WRITE_NOISE_EN=1 WRITE_NOISE_RATE_NUM=1 WRITE_NOISE_RATE_DEN=100 READ_NOISE_EN=0"
|
||||
just remote "make -C hw/sim clean && make -C hw/sim test-top NUM_ROWS=64 LANES=8 HASH_BITS=512 WRITE_NOISE_EN=0 READ_NOISE_EN=1 READ_NOISE_RATE_NUM=1 READ_NOISE_RATE_DEN=100"
|
||||
just remote "make -C hw/sim clean && make -C hw/sim test-top NUM_ROWS=64 LANES=8 HASH_BITS=512 WRITE_NOISE_EN=1 WRITE_NOISE_RATE_NUM=1 WRITE_NOISE_RATE_DEN=100 READ_NOISE_EN=1 READ_NOISE_RATE_NUM=1 READ_NOISE_RATE_DEN=100"
|
||||
|
||||
# Run all CAM tests in one pass (model + top + all modules)
|
||||
cam-test-full:
|
||||
just remote "make -C hw/sim clean && make -C hw/sim test-all"
|
||||
|
||||
@@ -31,9 +31,10 @@ module match_engine_pipeline (
|
||||
//==========================================================================
|
||||
// State encoding
|
||||
//==========================================================================
|
||||
typedef enum logic [2:0] {
|
||||
typedef enum logic [3:0] {
|
||||
S_IDLE,
|
||||
S_ISSUE_READ,
|
||||
S_WAIT_READ_RESP,
|
||||
S_WAIT_SCORE,
|
||||
S_STAGE_CANDIDATES,
|
||||
S_PUSH_CANDIDATES,
|
||||
@@ -373,6 +374,13 @@ module match_engine_pipeline (
|
||||
end
|
||||
|
||||
S_ISSUE_READ: begin
|
||||
// Emit rd_valid_o for exactly one cycle (combinational), then
|
||||
// transition to S_WAIT_READ_RESP. This ensures the read-noise
|
||||
// PRNG in cam_read_noise only advances once per batch issue.
|
||||
state_q <= S_WAIT_READ_RESP;
|
||||
end
|
||||
|
||||
S_WAIT_READ_RESP: begin
|
||||
if (rd_valid_i) begin
|
||||
// Capture staging data for popcount pipeline
|
||||
rd_stage_valid_q <= 1'b1;
|
||||
@@ -380,8 +388,7 @@ module match_engine_pipeline (
|
||||
rd_stage_hashes_q <= rd_hashes_i;
|
||||
rd_stage_lane_valid_q <= rd_lane_valid_i;
|
||||
// Remember the lane mask for this batch (used in S_WAIT_SCORE
|
||||
// to determine batch_scores_done — survives until overwritten
|
||||
// by the next S_ISSUE_READ).
|
||||
// to determine batch_scores_done).
|
||||
batch_lane_mask_q <= rd_lane_valid_i;
|
||||
issue_base_q <= issue_base_q + `LANES;
|
||||
issued_batches_q <= issued_batches_q + 1;
|
||||
|
||||
@@ -7,7 +7,7 @@ TOP_CONFIGS := no_noise write_noise read_noise
|
||||
help:
|
||||
@echo "Available hw/sim targets:"
|
||||
@echo " make test-model"
|
||||
@echo " make test-top # 只运行默认顶层配置 (no_noise)"
|
||||
@echo " make test-top # 默认运行所有顶层噪声配置"
|
||||
@echo " make test-top-all # 运行所有顶层噪声配置"
|
||||
@echo " make test-top-no_noise # 无噪声集成测试"
|
||||
@echo " make test-top-write_noise # 写入噪声集成测试"
|
||||
@@ -20,7 +20,7 @@ help:
|
||||
|
||||
test-all: test-model test-top-all test-modules
|
||||
|
||||
test-top: test-top-no_noise
|
||||
test-top: test-top-all
|
||||
|
||||
test-top-all: $(TOP_CONFIGS:%=test-top-%)
|
||||
|
||||
|
||||
@@ -79,4 +79,4 @@ COMPILE_ARGS += -Wno-WIDTHEXPAND
|
||||
COMPILE_ARGS += -Wno-UNOPTFLAT
|
||||
endif
|
||||
|
||||
include $(shell uv run cocotb-config --makefiles)/Makefile.sim
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
@@ -35,6 +35,7 @@ from model.ref_model import (
|
||||
from tests.top.utils import (
|
||||
collect_topk,
|
||||
dut_hash_bits,
|
||||
dut_lanes,
|
||||
dut_num_rows,
|
||||
query_once,
|
||||
query_topk_once,
|
||||
@@ -44,7 +45,6 @@ from tests.top.utils import (
|
||||
write_rows,
|
||||
)
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# 编译冒烟测试 — 验证 cam_top 能正确 elaborate
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
@@ -343,6 +343,8 @@ async def query_scan_blocks_writes_until_result_consumed(dut):
|
||||
dut.wr_valid.value = 0
|
||||
|
||||
# Consume full serial stream so the DUT returns idle
|
||||
beats = await collect_topk(dut, timeout_cycles=2000)
|
||||
beats = await collect_topk(
|
||||
dut, timeout_cycles=max(2000, (dut_num_rows(dut) // dut_lanes(dut)) * 24 + 100)
|
||||
)
|
||||
assert len(beats) > 0
|
||||
assert beats[-1][3] == 1 # last asserted
|
||||
|
||||
@@ -170,14 +170,33 @@ async def collect_topk(dut, timeout_cycles: int = 2000):
|
||||
raise AssertionError("Top-K result stream did not finish")
|
||||
|
||||
|
||||
async def query_topk_once(dut, query, timeout_cycles=2000):
|
||||
# ── 默认超时估算 ──────────────────────────────────────────────────
|
||||
|
||||
|
||||
def dut_query_timeout_cycles(dut):
|
||||
"""基于 DUT 参数估算完整查询(扫描 + 串行结果输出)的超时周期数。
|
||||
|
||||
各通道串行输出一个 beat 需要约 24 个流水线周期;
|
||||
总超时 = ceil(全部行数 / 通道数) * 24 + 固定裕量 2000 周期。
|
||||
至少返回 2000 周期以防止极小配置下的不合理值。
|
||||
|
||||
Example: 4096 rows / 8 lanes → ceil(4096/8) * 24 + 2000 = 14288 cycles.
|
||||
"""
|
||||
num_rows = dut_num_rows(dut)
|
||||
lanes = dut_lanes(dut)
|
||||
batches = (num_rows + lanes - 1) // lanes # ceil division, no math import
|
||||
return max(2000, batches * 24 + 2000)
|
||||
|
||||
|
||||
async def query_topk_once(dut, query, timeout_cycles=None):
|
||||
"""发起一次查询并收集完整的串行 Top-K 结果流。
|
||||
|
||||
完整流程:
|
||||
1. 等待 DUT 空闲
|
||||
2. 通过 query_valid/query_ready 握手发送查询
|
||||
3. 消费完整的结果流
|
||||
4. 读取 score_debug_flat(如果存在)
|
||||
2. 等待 query_ready 为高
|
||||
3. 通过 query_valid/query_ready 握手发送查询
|
||||
4. 消费完整的结果流
|
||||
5. 读取 score_debug_flat(如果存在)
|
||||
|
||||
返回:(beats, top1_index, top1_score, score_debug)
|
||||
- beats: [(rank, row, score, last), ...]
|
||||
@@ -186,16 +205,21 @@ async def query_topk_once(dut, query, timeout_cycles=2000):
|
||||
await wait_idle(dut)
|
||||
|
||||
dut.query_hash.value = int(query)
|
||||
dut.query_valid.value = 1
|
||||
|
||||
# 等待查询握手完成
|
||||
while True:
|
||||
# 等待 query_ready 为高(DUT 已就绪),避免组合逻辑下降沿导致的
|
||||
# valid&&ready 握手丢失问题
|
||||
while not int(dut.query_ready.value):
|
||||
await RisingEdge(dut.clk)
|
||||
if int(dut.query_ready.value):
|
||||
break
|
||||
|
||||
# assert query_valid 覆盖一个上升沿完成握手
|
||||
dut.query_valid.value = 1
|
||||
await RisingEdge(dut.clk)
|
||||
dut.query_valid.value = 0
|
||||
|
||||
# 若调用者未指定超时,根据 DUT 参数动态估算
|
||||
if timeout_cycles is None:
|
||||
timeout_cycles = dut_query_timeout_cycles(dut)
|
||||
|
||||
# 消费完整串行结果流
|
||||
beats = await collect_topk(dut, timeout_cycles=timeout_cycles)
|
||||
|
||||
@@ -213,12 +237,19 @@ async def query_topk_once(dut, query, timeout_cycles=2000):
|
||||
return beats, beats[0][1], beats[0][2], score_debug
|
||||
|
||||
|
||||
async def query_once(dut, query):
|
||||
async def query_once(dut, query, timeout_cycles=None):
|
||||
"""发起查询,返回 (top1_index, top1_score, score_debug)。
|
||||
|
||||
内部调用 query_topk_once 并消费完整结果流,仅保留 rank-0 数据。
|
||||
|
||||
Parameters
|
||||
----------
|
||||
timeout_cycles : int or None
|
||||
传递给 query_topk_once 的超时周期数。None 表示根据 DUT 参数动态估算。
|
||||
"""
|
||||
_, top1_index, top1_score, score_debug = await query_topk_once(dut, query)
|
||||
_, top1_index, top1_score, score_debug = await query_topk_once(
|
||||
dut, query, timeout_cycles=timeout_cycles,
|
||||
)
|
||||
return top1_index, top1_score, score_debug
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user