feat(hw/sim): distinguish query-only and end-to-end performance cycles in retrieval benchmark

Add explicit separation between query-only cycles (accept→last) and end-to-end cycles
(load + write + noise + queries) in hardware retrieval benchmarks.

- Add query_only_cycles_per_query, load_write_noise_cycles, end_to_end_cycles metrics
- Refactor summarize_query_timings() to use accept_to_last_result_cycles as query-only base
- Add build_hardware_performance() to compute end-to-end performance separately
- Add current_sim_cycle() helper using cocotb get_sim_time
- Update CSV/Markdown outputs and RETRIEVAL_PERF_RESULT log format
- Update documentation to clarify cycle-counting methodology
- Update tests to cover new performance measurement logic
This commit is contained in:
2026-05-28 13:46:09 +08:00
parent 42d4a9728d
commit 97e53d44f8
4 changed files with 195 additions and 56 deletions

View File

@@ -216,7 +216,7 @@ async def query_topk_once_with_latency(dut, query, timeout_cycles=None):
``timing`` 字段:
- accept_to_first_result_cycles: query 接受到首个 result_valid beat
- accept_to_last_result_cycles: query 接受到 result_last beatTop-K 完成)
- total_query_cycles: 从拉高 query_valid 到 Top-K 完成的总上升沿数
- total_query_cycles: 纯查询事务周期,等于 accept_to_last_result_cycles
``query_ready`` 是组合信号,握手周期在上升沿前采样;结果信号在
ReadOnly settled phase 采样,避免重新引入 query_ready 采样时序问题。
@@ -236,8 +236,10 @@ async def query_topk_once_with_latency(dut, query, timeout_cycles=None):
await RisingEdge(dut.clk)
edge_count += 1
q_valid = int(dut.query_valid.value)
q_ready = int(dut.query_ready.value)
assert q_ready, "Query accept handshake was missed despite query_ready pre-wait"
assert q_valid, "Query valid deasserted before accept handshake"
accept_edge = edge_count
dut.query_valid.value = 0
@@ -252,7 +254,9 @@ async def query_topk_once_with_latency(dut, query, timeout_cycles=None):
await RisingEdge(dut.clk)
edge_count += 1
await ReadOnly()
if int(dut.result_valid.value):
result_valid = int(dut.result_valid.value)
result_ready = int(dut.result_ready.value)
if result_valid and result_ready:
if first_result_edge is None:
first_result_edge = edge_count
rank = int(dut.result_rank.value)
@@ -282,7 +286,7 @@ async def query_topk_once_with_latency(dut, query, timeout_cycles=None):
timing = {
"accept_to_first_result_cycles": int(first_result_edge - accept_edge),
"accept_to_last_result_cycles": int(last_result_edge - accept_edge),
"total_query_cycles": int(edge_count),
"total_query_cycles": int(last_result_edge - accept_edge),
}
return beats, beats[0][1], beats[0][2], score_debug, timing