From 3416b8d3cfd909a226ab5b2c29a38dc73ffffe9e Mon Sep 17 00:00:00 2001 From: manvalan Date: Wed, 16 Sep 2026 15:38:12 +0200 Subject: [PATCH] exp: fix pulse-race hang in tb_layer_reuse_vs_zero_reuse.v (EXP-0058 follow-up); real remaining bug still open, 7.16x NOT yet re-verified Same class of testbench-vs-DUT scheduling race documented in EXP-0058 (tb_layer_prefetch_ctrl.v, tb_neural_processor_layer_reuse.v) was also present here on ctrl_req/fill_done/consume_done: clearing a one-cycle pulse on the very next @(posedge clk) let the clear land in the same active-region pass as the edge the DUT needed to sample it at, so the pulse could be silently missed. Confirmed via direct state tracing: sdram_controller_openrow.v sat in S_IDLE with busy=0 forever after the first burst, never latching req_pending for the second -- this is why the file hung indefinitely rather than completing. Fixed with the same #1-before-clear hardening as the other files. Honesty note: fixing this hang exposed a SECOND, still-unfixed bug in prefetch_layer's own fill_addr sequencing (real data-correctness failures once the run actually completes, not just a hang) -- so EXP-0057's own headline "7.16x real measured speedup" number is NOT re-verified by this commit and should not be treated as confirmed. Not pursued further -- see decisions.log for why (project paused). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC --- .../v2/sim/tb_layer_reuse_vs_zero_reuse.v | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/hardware/v2/sim/tb_layer_reuse_vs_zero_reuse.v b/hardware/v2/sim/tb_layer_reuse_vs_zero_reuse.v index f87520a..d396418 100644 --- a/hardware/v2/sim/tb_layer_reuse_vs_zero_reuse.v +++ b/hardware/v2/sim/tb_layer_reuse_vs_zero_reuse.v @@ -77,11 +77,25 @@ module tb; .ba(ba), .a(a), .dq(dq), .dqm(dqm) ); + // Pulse-hardening note (found while debugging this exact file, + // EXP-0058 follow-up): clearing ctrl_req on the very next + // @(posedge clk) after setting it puts the clear in the SAME + // active-region pass as the edge sdram_controller_openrow.v's own + // synchronous `if (req) req_pending <= 1'b1;` latch needs to sample + // it at -- their relative execution order is implementation- + // defined, so the one-cycle req pulse can be silently missed + // (confirmed via direct state/req tracing: the controller sat in + // S_IDLE with busy=0 forever after the first burst, never latching + // req_pending for the second). Same class of bug as the + // consume_done/pf_start races fixed in tb_layer_prefetch_ctrl.v + // and tb_neural_processor_layer_reuse.v -- fixed the same way, by + // holding the pulse past the edge with a real time delay (#1) + // before clearing. task automatic sdram_write_burst(input [ADDR_WIDTH-1:0] word_addr, input [16*BURST_LEN-1:0] data); begin @(posedge clk); while (ctrl_busy) @(posedge clk); ctrl_req = 1'b1; ctrl_wr = 1'b1; ctrl_addr = word_addr; ctrl_wdata = data; ctrl_wmask = {(2*BURST_LEN){1'b0}}; - @(posedge clk); ctrl_req = 1'b0; + @(posedge clk); #1; ctrl_req = 1'b0; while (!ctrl_ready) @(posedge clk); end endtask @@ -89,7 +103,7 @@ module tb; begin @(posedge clk); while (ctrl_busy) @(posedge clk); ctrl_req = 1'b1; ctrl_wr = 1'b0; ctrl_addr = word_addr; ctrl_wmask = {(2*BURST_LEN){1'b0}}; - @(posedge clk); ctrl_req = 1'b0; + @(posedge clk); #1; ctrl_req = 1'b0; while (!ctrl_ready) @(posedge clk); data = ctrl_rdata; end @@ -150,7 +164,7 @@ module tb; end end @(posedge clk); fill_we = 1'b0; - fill_done = 1'b1; @(posedge clk); fill_done = 1'b0; + fill_done = 1'b1; @(posedge clk); #1; fill_done = 1'b0; end endtask @@ -175,7 +189,7 @@ module tb; @(posedge clk); end end - consume_done = 1'b1; @(posedge clk); consume_done = 1'b0; + consume_done = 1'b1; @(posedge clk); #1; consume_done = 1'b0; end endtask @@ -213,7 +227,7 @@ module tb; $display("=== REUSE case correctness pass: %0d layers x %0d reuses, double-buffered background prefetch (data check only, not timed) ===", L, M); prefetch_layer(0); - consume_done = 1'b1; @(posedge clk); consume_done = 1'b0; // trigger initial swap + consume_done = 1'b1; @(posedge clk); #1; consume_done = 1'b0; // trigger initial swap for (li_i = 0; li_i < L; li_i = li_i + 1) begin fork consume_layer_check(li_i, errors, errors);