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 ──────────────────────────────────────────────────────────
|
# ── 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)
|
# Run all CAM tests in one pass (model + top + all modules)
|
||||||
cam-test-full:
|
cam-test-full:
|
||||||
just remote "make -C hw/sim clean && make -C hw/sim test-all"
|
just remote "make -C hw/sim clean && make -C hw/sim test-all"
|
||||||
|
|||||||
@@ -15,3 +15,4 @@ outputs
|
|||||||
**/__pycache__
|
**/__pycache__
|
||||||
**/sim_build
|
**/sim_build
|
||||||
**/__marimo__
|
**/__marimo__
|
||||||
|
*.fst
|
||||||
|
|||||||
@@ -31,9 +31,10 @@ module match_engine_pipeline (
|
|||||||
//==========================================================================
|
//==========================================================================
|
||||||
// State encoding
|
// State encoding
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
typedef enum logic [2:0] {
|
typedef enum logic [3:0] {
|
||||||
S_IDLE,
|
S_IDLE,
|
||||||
S_ISSUE_READ,
|
S_ISSUE_READ,
|
||||||
|
S_WAIT_READ_RESP,
|
||||||
S_WAIT_SCORE,
|
S_WAIT_SCORE,
|
||||||
S_STAGE_CANDIDATES,
|
S_STAGE_CANDIDATES,
|
||||||
S_PUSH_CANDIDATES,
|
S_PUSH_CANDIDATES,
|
||||||
@@ -373,6 +374,13 @@ module match_engine_pipeline (
|
|||||||
end
|
end
|
||||||
|
|
||||||
S_ISSUE_READ: begin
|
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
|
if (rd_valid_i) begin
|
||||||
// Capture staging data for popcount pipeline
|
// Capture staging data for popcount pipeline
|
||||||
rd_stage_valid_q <= 1'b1;
|
rd_stage_valid_q <= 1'b1;
|
||||||
@@ -380,8 +388,7 @@ module match_engine_pipeline (
|
|||||||
rd_stage_hashes_q <= rd_hashes_i;
|
rd_stage_hashes_q <= rd_hashes_i;
|
||||||
rd_stage_lane_valid_q <= rd_lane_valid_i;
|
rd_stage_lane_valid_q <= rd_lane_valid_i;
|
||||||
// Remember the lane mask for this batch (used in S_WAIT_SCORE
|
// Remember the lane mask for this batch (used in S_WAIT_SCORE
|
||||||
// to determine batch_scores_done — survives until overwritten
|
// to determine batch_scores_done).
|
||||||
// by the next S_ISSUE_READ).
|
|
||||||
batch_lane_mask_q <= rd_lane_valid_i;
|
batch_lane_mask_q <= rd_lane_valid_i;
|
||||||
issue_base_q <= issue_base_q + `LANES;
|
issue_base_q <= issue_base_q + `LANES;
|
||||||
issued_batches_q <= issued_batches_q + 1;
|
issued_batches_q <= issued_batches_q + 1;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ TOP_CONFIGS := no_noise write_noise read_noise
|
|||||||
help:
|
help:
|
||||||
@echo "Available hw/sim targets:"
|
@echo "Available hw/sim targets:"
|
||||||
@echo " make test-model"
|
@echo " make test-model"
|
||||||
@echo " make test-top # 只运行默认顶层配置 (no_noise)"
|
@echo " make test-top # 默认运行所有顶层噪声配置"
|
||||||
@echo " make test-top-all # 运行所有顶层噪声配置"
|
@echo " make test-top-all # 运行所有顶层噪声配置"
|
||||||
@echo " make test-top-no_noise # 无噪声集成测试"
|
@echo " make test-top-no_noise # 无噪声集成测试"
|
||||||
@echo " make test-top-write_noise # 写入噪声集成测试"
|
@echo " make test-top-write_noise # 写入噪声集成测试"
|
||||||
@@ -20,7 +20,7 @@ help:
|
|||||||
|
|
||||||
test-all: test-model test-top-all test-modules
|
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-%)
|
test-top-all: $(TOP_CONFIGS:%=test-top-%)
|
||||||
|
|
||||||
|
|||||||
@@ -79,4 +79,4 @@ COMPILE_ARGS += -Wno-WIDTHEXPAND
|
|||||||
COMPILE_ARGS += -Wno-UNOPTFLAT
|
COMPILE_ARGS += -Wno-UNOPTFLAT
|
||||||
endif
|
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 (
|
from tests.top.utils import (
|
||||||
collect_topk,
|
collect_topk,
|
||||||
dut_hash_bits,
|
dut_hash_bits,
|
||||||
|
dut_lanes,
|
||||||
dut_num_rows,
|
dut_num_rows,
|
||||||
query_once,
|
query_once,
|
||||||
query_topk_once,
|
query_topk_once,
|
||||||
@@ -44,7 +45,6 @@ from tests.top.utils import (
|
|||||||
write_rows,
|
write_rows,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
# 编译冒烟测试 — 验证 cam_top 能正确 elaborate
|
# 编译冒烟测试 — 验证 cam_top 能正确 elaborate
|
||||||
# ═══════════════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
@@ -343,6 +343,8 @@ async def query_scan_blocks_writes_until_result_consumed(dut):
|
|||||||
dut.wr_valid.value = 0
|
dut.wr_valid.value = 0
|
||||||
|
|
||||||
# Consume full serial stream so the DUT returns idle
|
# 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 len(beats) > 0
|
||||||
assert beats[-1][3] == 1 # last asserted
|
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")
|
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 结果流。
|
"""发起一次查询并收集完整的串行 Top-K 结果流。
|
||||||
|
|
||||||
完整流程:
|
完整流程:
|
||||||
1. 等待 DUT 空闲
|
1. 等待 DUT 空闲
|
||||||
2. 通过 query_valid/query_ready 握手发送查询
|
2. 等待 query_ready 为高
|
||||||
3. 消费完整的结果流
|
3. 通过 query_valid/query_ready 握手发送查询
|
||||||
4. 读取 score_debug_flat(如果存在)
|
4. 消费完整的结果流
|
||||||
|
5. 读取 score_debug_flat(如果存在)
|
||||||
|
|
||||||
返回:(beats, top1_index, top1_score, score_debug)
|
返回:(beats, top1_index, top1_score, score_debug)
|
||||||
- beats: [(rank, row, score, last), ...]
|
- beats: [(rank, row, score, last), ...]
|
||||||
@@ -186,16 +205,21 @@ async def query_topk_once(dut, query, timeout_cycles=2000):
|
|||||||
await wait_idle(dut)
|
await wait_idle(dut)
|
||||||
|
|
||||||
dut.query_hash.value = int(query)
|
dut.query_hash.value = int(query)
|
||||||
dut.query_valid.value = 1
|
|
||||||
|
|
||||||
# 等待查询握手完成
|
# 等待 query_ready 为高(DUT 已就绪),避免组合逻辑下降沿导致的
|
||||||
while True:
|
# valid&&ready 握手丢失问题
|
||||||
|
while not int(dut.query_ready.value):
|
||||||
await RisingEdge(dut.clk)
|
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.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)
|
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
|
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)。
|
"""发起查询,返回 (top1_index, top1_score, score_debug)。
|
||||||
|
|
||||||
内部调用 query_topk_once 并消费完整结果流,仅保留 rank-0 数据。
|
内部调用 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
|
return top1_index, top1_score, score_debug
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user