🔍 dma.dma_multiburst_write_test

Individual debug report — smoke regression.

← Back to regression report

dma.dma_multiburst_write_test

Fix failed
Fail signature (MismatchError)
MismatchError: 1 row mismatches

Attempts (2)

Attempt 1 of 2 Sent back for more evidence
Investigation plan
  1. Searched the design graph (dma)
  2. Searched the design spec for expected behavior (dma multiburst write)
  3. Inspected the module instance hierarchy (tpe_dma)
  4. Searched the design spec for expected behavior (DMA state machine ST_WR_DATA ST_WR_SRAM_RD)
  5. Read the RTL source (rtl)
  6. Searched the design graph (ST_WR_DATA)
  7. Searched the design graph (state)
  8. Traced the signal's driver (state_d)
  9. Read the RTL source (rtl/dma/tpe_dma.sv)
Hypothesis / root causertl/dma/tpe_dma.sv:150
Confidence
43%

Insufficient 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)
  1. 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
  2. Step 2: Searched the design spec for expected behavior (dma multiburst write)
    (no spec sections matched 'dma multiburst write')
  3. 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": []}
  4. 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')
  5. Step 5: Read the RTL source (rtl)
    (denied) 'rtl' is outside rtl/ — only RTL source is readable
  6. Step 6: Searched the design graph (ST_WR_DATA)
    (no matches)
  7. 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
  8. 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
  9. 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
Investigation plan
  1. Searched the design graph (dma)
  2. Searched the design spec for expected behavior (dma multiburst write)
  3. Inspected the module instance hierarchy (tpe_dma)
  4. Searched the design spec for expected behavior (DMA state machine ST_WR_DATA ST_WR_SRAM_RD)
  5. Read the RTL source (rtl)
  6. Searched the design graph (ST_WR_DATA)
  7. Searched the design graph (state)
  8. Traced the signal's driver (state_d)
  9. Read the RTL source (rtl/dma/tpe_dma.sv)
  10. Read the RTL source (rtl/dma/tpe_dma.sv)
  11. Read the RTL source (rtl/dma/tpe_dma.sv)
  12. Searched the design graph (sram_rdata)
  13. Traced the signal's driver (sram_rdata)
  14. Read the RTL source (rtl/top/tpe_top.sv)
  15. Read the RTL source (rtl/top/tpe_top.sv)
Hypothesis / root causertl/dma/tpe_dma.sv:161
Confidence
55%

The 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)
  1. 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
  2. Step 2: Searched the design spec for expected behavior (dma multiburst write)
    (no spec sections matched 'dma multiburst write')
  3. 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": []}
  4. 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')
  5. Step 5: Read the RTL source (rtl)
    (denied) 'rtl' is outside rtl/ — only RTL source is readable
  6. Step 6: Searched the design graph (ST_WR_DATA)
    (no matches)
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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:                              
  12. 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)
  13. Step 13: Traced the signal's driver (sram_rdata)
    - Signal:dma_sram_rdata [rtl/top/tpe_top.sv:265:32]
  14. 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),
  15. 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
Answer-key check (final attempt)Correct expected bug #4 — rtl/dma/tpe_dma.sv:[161]
Rerun FAIL agent-fix/dma-dma_multiburst_write_test-3f0b38b8
Tokens (all attempts)17,662 (~$0.0699)