dma.dma_multiburst_write_test
MismatchError: 1 row mismatches
Attempts (2)
Attempt 1 of 2 Sent back for more evidence
- Searched the design graph (
dma) - Searched the design spec for expected behavior (
dma multiburst write) - Inspected the module instance hierarchy (
tpe_dma) - Searched the design spec for expected behavior (
DMA state machine ST_WR_DATA ST_WR_SRAM_RD) - Read the RTL source (
rtl) - Searched the design graph (
ST_WR_DATA) - Searched the design graph (
state) - Traced the signal's driver (
state_d) - Read the RTL source (
rtl/dma/tpe_dma.sv)
rtl/dma/tpe_dma.sv:150Insufficient evidence gathered yet — did not read the actual ST_WR_SRAM_RD/ST_WR_DATA state machine logic or the burst/last-beat counter logic before running out of investigation budget. The failure shows the last row (row 16, the last beat of a 17-row multiburst SRAM->DDR transfer) is written as all zeros instead of the expected data, while earlier rows apparently matched (only 1 mismatch of 17). This points to an off-by-one in the burst beat counter or a last-beat handling bug in the ST_WR_SRAM_RD/ST_WR_DATA loop (e.g., final SRAM read data not valid/latched before being pushed to the AXI write data on the last beat of the last burst), likely in the always block around tpe_dma.sv:150-210 that drives state_d and the write data mux. However, I have not verified this against actual source lines, so I cannot provide a grounded verbatim patch.
Retrieved evidence (9 steps)
-
Step 1: Searched the design graph (
dma)- Instance:u_dma (id=instance:tpe_top.u_dma, module=None) - Module:tpe_dma (id=module:tpe_dma, module=None) - Signal:dma_busy (id=signal:tpe_top.dma_busy, module=tpe_top) - Signal:dma_done (id=signal:tpe_top.dma_done, module=tpe_top) - Port:dma_wait (id=port:tpe_pmu.dma_wait, module=tpe_pmu) - Port:dma_busy (id=port:tpe_scheduler__R10_C10.dma_busy, module=tpe_scheduler__R10_C10) - Port:dma_done (id=port:tpe_scheduler__R10_C10.dma_done, module=tpe_scheduler__R10_C10) - Signal:dma_start (id=signal
-
Step 2: Searched the design spec for expected behavior (
dma multiburst write)(no spec sections matched 'dma multiburst write')
-
Step 3: Inspected the module instance hierarchy (
tpe_dma){"module": {"name": "tpe_dma", "orig_name": "tpe_dma", "level": 2, "is_top": false, "node_type": "Module", "id": "module:tpe_dma"}, "instances": []} -
Step 4: Searched the design spec for expected behavior (
DMA state machine ST_WR_DATA ST_WR_SRAM_RD)(no spec sections matched 'DMA state machine ST_WR_DATA ST_WR_SRAM_RD')
-
Step 5: Read the RTL source (
rtl)(denied) 'rtl' is outside rtl/ — only RTL source is readable
-
Step 6: Searched the design graph (
ST_WR_DATA)(no matches)
-
Step 7: Searched the design graph (
state)- Signal:state_d (id=signal:tpe_dma.state_d, module=tpe_dma) - Register:state_q (id=register:tpe_dma.state_q, module=tpe_dma) - Signal:state_d (id=signal:tpe_scheduler__R10_C10.state_d, module=tpe_scheduler__R10_C10) - Register:state_q (id=register:tpe_scheduler__R10_C10.state_q, module=tpe_scheduler__R10_C10) - Signal:state_d (id=signal:matrix_engine_ctrl__pi1.state_d, module=matrix_engine_ctrl__pi1) - Register:state_q (id=register:matrix_engine_ctrl__pi1.state_q, module=matrix_engine_ctrl__pi1
-
Step 8: Traced the signal's driver (
state_d)- Assignment:assignment:tpe_dma.assign_181 inside AlwaysBlock:always:tpe_dma.always_179 [rtl/dma/tpe_dma.sv:152:3] - Assignment:assignment:tpe_dma.assign_183 inside AlwaysBlock:always:tpe_dma.always_179 [rtl/dma/tpe_dma.sv:152:3] - Assignment:assignment:tpe_dma.assign_185 inside AlwaysBlock:always:tpe_dma.always_179 [rtl/dma/tpe_dma.sv:152:3] - Assignment:assignment:tpe_dma.assign_187 inside AlwaysBlock:always:tpe_dma.always_179 [rtl/dma/tpe_dma.sv:152:3] - Assignment:assignment:tpe_dma.assign_1
-
Step 9: Read the RTL source (
rtl/dma/tpe_dma.sv)--- rtl/dma/tpe_dma.sv (lines 1-40 of 253) --- 1: // DMA Engine: descriptor-based mover between DDR (AXI4 master) and the 2: // Local SRAM scratchpad, per docs/architecture/tpe_architecture_spec.md 3: // section 3.3. V1 scope: single channel, one descriptor in flight at a 4: // time, INCR bursts capped at MAX_BURST_BEATS beats, transfer length must 5: // be a whole number of AXI_DATA_WIDTH-wide rows (16 bytes) -- this DMA 6: // moves whole SRAM rows, it does not do sub-ro
Proposed patch
--- a/rtl/dma/tpe_dma.sv
+++ b/rtl/dma/tpe_dma.sv
@@ -147,6 +147,6 @@
default: ;
endcase
end
end
always_comb begin
Attempt 2 of 2 Approved
- Searched the design graph (
dma) - Searched the design spec for expected behavior (
dma multiburst write) - Inspected the module instance hierarchy (
tpe_dma) - Searched the design spec for expected behavior (
DMA state machine ST_WR_DATA ST_WR_SRAM_RD) - Read the RTL source (
rtl) - Searched the design graph (
ST_WR_DATA) - Searched the design graph (
state) - Traced the signal's driver (
state_d) - Read the RTL source (
rtl/dma/tpe_dma.sv) - Read the RTL source (
rtl/dma/tpe_dma.sv) - Read the RTL source (
rtl/dma/tpe_dma.sv) - Searched the design graph (
sram_rdata) - Traced the signal's driver (
sram_rdata) - Read the RTL source (
rtl/top/tpe_top.sv) - Read the RTL source (
rtl/top/tpe_top.sv)
rtl/dma/tpe_dma.sv:161The DMA write-path state machine transitions from ST_WR_SRAM_RD directly to ST_WR_DATA, relying on the SRAM's registered read-data output (sram_rdata) being valid one cycle after sram_en/sram_we are asserted in ST_WR_SRAM_RD. For the final beat of the final burst (row 16 of 17 in this multiburst test), the state machine reaches ST_WR_RESP based on burst_beats_left_q alone, without accounting for whether more full row transfers (spanning multiple AXI bursts, i.e. more descriptor beats_remaining_q) are still pending -- causing the last SRAM row's read data never to be issued to the AXI write channel due to premature transition, leaving m_wdata driving stale/zero data (since sram_rdata reverts to its default post-read state) for the last row. Root cause: the last-beat-of-last-burst transition condition in ST_WR_DATA does not correctly gate on beats_remaining_q, causing the DMA to terminate the burst one beat early relative to the SRAM read pipeline for descriptors whose total beat count isn't an exact multiple of MAX_BURST_BEATS, dropping the final row's data (all zeros observed in scoreboard).
Retrieved evidence (15 steps)
-
Step 1: Searched the design graph (
dma)- Instance:u_dma (id=instance:tpe_top.u_dma, module=None) - Module:tpe_dma (id=module:tpe_dma, module=None) - Signal:dma_busy (id=signal:tpe_top.dma_busy, module=tpe_top) - Signal:dma_done (id=signal:tpe_top.dma_done, module=tpe_top) - Port:dma_wait (id=port:tpe_pmu.dma_wait, module=tpe_pmu) - Port:dma_busy (id=port:tpe_scheduler__R10_C10.dma_busy, module=tpe_scheduler__R10_C10) - Port:dma_done (id=port:tpe_scheduler__R10_C10.dma_done, module=tpe_scheduler__R10_C10) - Signal:dma_start (id=signal
-
Step 2: Searched the design spec for expected behavior (
dma multiburst write)(no spec sections matched 'dma multiburst write')
-
Step 3: Inspected the module instance hierarchy (
tpe_dma){"module": {"name": "tpe_dma", "orig_name": "tpe_dma", "level": 2, "is_top": false, "node_type": "Module", "id": "module:tpe_dma"}, "instances": []} -
Step 4: Searched the design spec for expected behavior (
DMA state machine ST_WR_DATA ST_WR_SRAM_RD)(no spec sections matched 'DMA state machine ST_WR_DATA ST_WR_SRAM_RD')
-
Step 5: Read the RTL source (
rtl)(denied) 'rtl' is outside rtl/ — only RTL source is readable
-
Step 6: Searched the design graph (
ST_WR_DATA)(no matches)
-
Step 7: Searched the design graph (
state)- Signal:state_d (id=signal:tpe_dma.state_d, module=tpe_dma) - Register:state_q (id=register:tpe_dma.state_q, module=tpe_dma) - Signal:state_d (id=signal:tpe_scheduler__R10_C10.state_d, module=tpe_scheduler__R10_C10) - Register:state_q (id=register:tpe_scheduler__R10_C10.state_q, module=tpe_scheduler__R10_C10) - Signal:state_d (id=signal:matrix_engine_ctrl__pi1.state_d, module=matrix_engine_ctrl__pi1) - Register:state_q (id=register:matrix_engine_ctrl__pi1.state_q, module=matrix_engine_ctrl__pi1
-
Step 8: Traced the signal's driver (
state_d)- Assignment:assignment:tpe_dma.assign_181 inside AlwaysBlock:always:tpe_dma.always_179 [rtl/dma/tpe_dma.sv:152:3] - Assignment:assignment:tpe_dma.assign_183 inside AlwaysBlock:always:tpe_dma.always_179 [rtl/dma/tpe_dma.sv:152:3] - Assignment:assignment:tpe_dma.assign_185 inside AlwaysBlock:always:tpe_dma.always_179 [rtl/dma/tpe_dma.sv:152:3] - Assignment:assignment:tpe_dma.assign_187 inside AlwaysBlock:always:tpe_dma.always_179 [rtl/dma/tpe_dma.sv:152:3] - Assignment:assignment:tpe_dma.assign_1
-
Step 9: Read the RTL source (
rtl/dma/tpe_dma.sv)--- rtl/dma/tpe_dma.sv (lines 1-40 of 253) --- 1: // DMA Engine: descriptor-based mover between DDR (AXI4 master) and the 2: // Local SRAM scratchpad, per docs/architecture/tpe_architecture_spec.md 3: // section 3.3. V1 scope: single channel, one descriptor in flight at a 4: // time, INCR bursts capped at MAX_BURST_BEATS beats, transfer length must 5: // be a whole number of AXI_DATA_WIDTH-wide rows (16 bytes) -- this DMA 6: // moves whole SRAM rows, it does not do sub-ro -
Step 10: Read the RTL source (
rtl/dma/tpe_dma.sv)--- rtl/dma/tpe_dma.sv (lines 100-200 of 253) --- 100: wire [7:0] burst_beats_this = (beats_remaining_q > BeatsWidth'(MAX_BURST_BEATS)) 101: ? 8'(MAX_BURST_BEATS) : 8'(beats_remaining_q); 102: wire len_misaligned = |desc_len[BeatShift-1:0]; 103: 104: always_ff @(posedge clk or negedge rst_n) begin 105: if (!rst_n) begin 106: state_q <= ST_IDLE; 107: mem_addr_q <= '0; 108: sram_addr_q <= '0; 109: beats_remainin
-
Step 11: Read the RTL source (
rtl/dma/tpe_dma.sv)--- rtl/dma/tpe_dma.sv (lines 200-253 of 253) --- 200: end 201: if (state_d != state_q) begin 202: `TPE_LOG_HIGH("dma", $sformatf("state %0s -> %0s", state_q.name(), state_d.name())); 203: end 204: if ((state_q == ST_RD_DATA && m_rvalid && m_rready) 205: || (state_q == ST_WR_DATA && m_wvalid && m_wready)) begin 206: `TPE_LOG_DEBUG("dma", $sformatf("beat mem_addr=%0h sram_addr=%0h beats_left=%0d", 207: -
Step 12: Searched the design graph (
sram_rdata)- Port:sram_rdata (id=port:tpe_dma.sram_rdata, module=tpe_dma) - Signal:dma_sram_rdata (id=signal:tpe_top.dma_sram_rdata, module=tpe_top)
-
Step 13: Traced the signal's driver (
sram_rdata)- Signal:dma_sram_rdata [rtl/top/tpe_top.sv:265:32]
-
Step 14: Read the RTL source (
rtl/top/tpe_top.sv)--- rtl/top/tpe_top.sv (lines 260-280 of 404) --- 260: // ---- DMA <-> SRAM router (DMA's single port) ---------------------------- 261: logic dma_sram_en, dma_sram_we; 262: logic [SRAM_STRB_WIDTH-1:0] dma_sram_strb; 263: logic [SRAM_ADDR_WIDTH-1:0] dma_sram_addr; 264: logic [SRAM_DATA_WIDTH-1:0] dma_sram_wdata; 265: logic [SRAM_DATA_WIDTH-1:0] dma_sram_rdata; 266: 267: tpe_dma u_dma ( 268: .clk (clk), 269: .rst_n(rst_n),
-
Step 15: Read the RTL source (
rtl/top/tpe_top.sv)--- rtl/top/tpe_top.sv (lines 280-300 of 404) --- 280: .sram_en (dma_sram_en), 281: .sram_we (dma_sram_we), 282: .sram_strb (dma_sram_strb), 283: .sram_addr (dma_sram_addr), 284: .sram_wdata(dma_sram_wdata), 285: .sram_rdata(dma_sram_rdata) 286: ); 287: 288: // ---- Matrix Engine ------------------------------------------------------- 289: logic wbuf_a_en, wbuf_a_we; 290: logic [COLS-1:0] wbuf_a_strb; 291: logic [TILE_DIM
Proposed patch
--- a/rtl/dma/tpe_dma.sv
+++ b/rtl/dma/tpe_dma.sv
@@ -158,12 +158,13 @@
ST_RD_DATA: if (m_rvalid && m_rready && m_rlast) begin
state_d = (beats_remaining_q == BeatsWidth'(1)) ? ST_DONE : ST_DECODE;
end
- ST_WR_ADDR: if (m_awvalid && m_awready) state_d = ST_WR_SRAM_RD;
- ST_WR_SRAM_RD: state_d = ST_WR_DATA;
- ST_WR_DATA: if (m_wvalid && m_wready) begin
- if (burst_beats_left_q == 8'd1) state_d = ST_WR_RESP;
- else state_d = ST_WR_SRAM_RD;
- end
+ ST_WR_ADDR: if (m_awvalid && m_awready) state_d = ST_WR_SRAM_RD;
+ ST_WR_SRAM_RD: state_d = ST_WR_DATA;
+ ST_WR_DATA: if (m_wvalid && m_wready) begin
+ if (burst_beats_left_q == 8'd1 && beats_remaining_q == BeatsWidth'(1)) state_d = ST_WR_RESP;
+ else if (burst_beats_left_q == 8'd1) state_d = ST_WR_ADDR;
+ else state_d = ST_WR_SRAM_RD;
+ end
ST_WR_RESP: if (m_bvalid && m_bready) begin
state_d = (beats_remaining_q <= BeatsWidth'(1)) ? ST_DONE : ST_DECODE;
end