matrix_engine.matmul_overflow_test
Fail signature (AssertionError)
AssertionError: [overflow_neg] 1 value mismatches
Attempt
Attempt 1 of 1 Approved
Investigation plan
- Searched the design spec for expected behavior (
overflow_neg accumulator) - Searched the design graph (
overflow_neg) - Searched the design spec for expected behavior (
matrix_engine overflow) - Searched the design graph (
overflow) - Searched the design graph (
matrix_engine) - Searched the design spec for expected behavior (
matrix_engine) - Inspected the module instance hierarchy (
matrix_engine__R10_C10_M100) - Read the RTL source (
rtl/matrix_engine) - Read the RTL source (
rtl/matrix_engine/matrix_engine.sv) - Read the RTL source (
rtl/matrix_engine/matrix_engine.sv) - Read the RTL source (
rtl/matrix_engine/matrix_engine.sv) - Read the RTL source (
rtl/matrix_engine/mac_array.sv) - Read the RTL source (
rtl/matrix_engine/mac_array.sv) - Read the RTL source (
rtl/matrix_engine/pe.sv) - Read the RTL source (
rtl/matrix_engine/pe.sv)
Hypothesis / root cause
rtl/matrix_engine/pe.sv:50Confidence
94%
The accumulator saturation mux in pe.sv incorrectly returns the raw (wrapped) `sum` when a negative-operand overflow (underflow) occurs, instead of saturating to `AccumMin` (0x80000000). This is why the overflow_pos test passes (positive overflow correctly saturates to AccumMax) but overflow_neg fails: the RTL reports rtl=0x7fff05e8 (a wrapped/corrupted value) instead of the expected golden=0x80000000. The fix corrects the ternary so that when `this_add_overflows` is true and the shared operand sign is negative (acc_in[ACCUM_WIDTH-1]==1), the result saturates to AccumMin rather than passing through the overflowed `sum`.
Retrieved evidence (15 steps)
-
Step 1: Searched the design spec for expected behavior (
overflow_neg accumulator)(no spec sections matched 'overflow_neg accumulator')
-
Step 2: Searched the design graph (
overflow_neg)(no matches)
-
Step 3: Searched the design spec for expected behavior (
matrix_engine overflow)(no spec sections matched 'matrix_engine overflow')
-
Step 4: Searched the design graph (
overflow)- Port:result_overflow (id=port:mac_array__R10_C10_O8_A20.result_overflow, module=mac_array__R10_C10_O8_A20) - Signal:arr_result_overflow (id=signal:matrix_engine__R10_C10_M100.arr_result_overflow, module=matrix_engine__R10_C10_M100) - Port:arr_result_overflow (id=port:matrix_engine_ctrl__pi1.arr_result_overflow, module=matrix_engine_ctrl__pi1) - Port:overflow_sticky (id=port:matrix_engine__R10_C10_M100.overflow_sticky, module=matrix_engine__R10_C10_M100) - Port:overflow_sticky (id=port:matrix_e
-
Step 5: Searched the design graph (
matrix_engine)- Instance:u_matrix_engine (id=instance:tpe_top.u_matrix_engine, module=None) - Module:matrix_engine_ctrl__pi1 (id=module:matrix_engine_ctrl__pi1, module=None) - Module:matrix_engine__R10_C10_M100 (id=module:matrix_engine__R10_C10_M100, module=None)
-
Step 6: Searched the design spec for expected behavior (
matrix_engine)### [docs/architecture/tpe_architecture_spec.md] 3.5 Matrix Compute Engine (`rtl/matrix_engine/`) Weight-stationary systolic array, default `MAC_ARRAY_ROWS` x `MAC_ARRAY_COLS` = 16x16 = 256 MACs, computing `C = A x B + C` over tiles up to `MAX_TILE_DIM` in each of M/K/N. Internally: input buffers -> MAC array (`rtl/matrix_engine/pe.sv` x N instances) -> accumulator (`rtl/matrix_engine/accumulator.sv`, int32, saturating) -> output buffer. ### [docs/architecture/tpe_architecture_spec.md] 4. Inter
-
Step 7: Inspected the module instance hierarchy (
matrix_engine__R10_C10_M100){"module": {"name": "matrix_engine__R10_C10_M100", "orig_name": "matrix_engine", "level": 2, "is_top": false, "node_type": "Module", "id": "module:matrix_engine__R10_C10_M100"}, "instances": [{"instance": {"id": "instance:matrix_engine__R10_C10_M100.u_weight_buf", "name": "u_weight_buf", "parent_module": "matrix_engine__R10_C10_M100", "module_type": "dp_ram__DB10_A4_S10", "loc": "rtl/matrix_engine/matrix_engine.sv:118:5", "node_type": "Instance"}, "child": {"module": {"name": "dp_ram__DB10_A4_S1 -
Step 8: Read the RTL source (
rtl/matrix_engine)(denied) 'rtl/matrix_engine' does not exist
-
Step 9: Read the RTL source (
rtl/matrix_engine/matrix_engine.sv)--- rtl/matrix_engine/matrix_engine.sv (lines 1-50 of 283) --- 1: // Matrix Compute Engine top level: input buffers (weight/activation/seed) 2: // -> mac_array -> matrix_engine_ctrl -> output buffer, per 3: // docs/architecture/tpe_architecture_spec.md section 3.5. 4: // 5: // The four buffers are plain rtl/common/dp_ram.sv instances, each exposing 6: // port A externally (named <buf>_a_* to match the reusable 7: // verif/cocotb_tb/env/SyncPortAgent convention from M1 -
Step 10: Read the RTL source (
rtl/matrix_engine/matrix_engine.sv)--- rtl/matrix_engine/matrix_engine.sv (lines 50-150 of 283) --- 50: 51: // seed_buf port A (TB/DMA loads C-in: one M-row per address) 52: input logic sbuf_a_en, 53: input logic sbuf_a_we, 54: input logic [COLS*ACCUM_BYTES-1:0] sbuf_a_strb, 55: input logic [M_ADDR_WIDTH-1:0] sbuf_a_addr, 56: input logic [COLS*ACCUM_WIDTH-1:0] sbuf_a_wdata, 57: output logic [CO
-
Step 11: Read the RTL source (
rtl/matrix_engine/matrix_engine.sv)--- rtl/matrix_engine/matrix_engine.sv (lines 200-283 of 283) --- 200: logic [ROWS-1:0] arr_weight_load_row; 201: logic signed [OPERAND_WIDTH-1:0] arr_weight_bus[COLS]; 202: logic [ROWS-1:0] arr_a_valid_in; 203: logic signed [OPERAND_WIDTH-1:0] arr_a_in[ROWS]; 204: logic signed [ACCUM_WIDTH-1:0] arr_acc_seed[COLS]; 205: logic [COLS-1:0] arr_result_valid; 206: logic signed [ACCUM_WIDTH-1:0] arr_result[COLS]; 207: logic [COLS-1:0] arr_result_overflow; 208: 209:
-
Step 12: Read the RTL source (
rtl/matrix_engine/mac_array.sv)--- rtl/matrix_engine/mac_array.sv (lines 1-40 of 105) --- 1: // ROWS x COLS weight-stationary systolic array of pe.sv instances. 2: // Pure fabric: activations enter each row's west edge (a_in[r]) and flow 3: // east; partial sums enter each column's north edge (acc_seed[c]) and flow 4: // south; weights load into row r when weight_load_row[r] is set, taking 5: // weight_bus[c] for column c. Getting the *timing* of a_in/acc_seed right 6: // (the systolic skew) is matrix_ -
Step 13: Read the RTL source (
rtl/matrix_engine/mac_array.sv)--- rtl/matrix_engine/mac_array.sv (lines 40-105 of 105) --- 40: // a_grid[r][c] / valid_grid[r][c]: activation and its valid flag arriving 41: // at PE[r][c]'s west edge. c=0 is fed by a_in[r]/a_valid_in[r]; c=COLS 42: // holds the last column's a_out/valid_out (discarded except for row 43: // ROWS-1, which becomes result_valid). 44: logic signed [OPERAND_WIDTH-1:0] a_grid[ROWS][COLS+1]; 45: logic valid_grid[ROWS][COLS+1]; 46: 47: // acc_grid[r][c] / ovf_g
-
Step 14: Read the RTL source (
rtl/matrix_engine/pe.sv)--- rtl/matrix_engine/pe.sv (lines 1-40 of 81) --- 1: // Systolic Processing Element (one MAC) -- the fundamental unit of 2: // rtl/matrix_engine/mac_array.sv. Weight-stationary: the weight is loaded 3: // once (weight_load) and held for the whole tile; activations stream east 4: // (a_in -> a_out, one cycle of pipeline delay) and partial sums stream 5: // south (acc_in -> acc_out, one cycle of pipeline delay), each PE adding 6: // its own a_in*weight contribution as the -
Step 15: Read the RTL source (
rtl/matrix_engine/pe.sv)--- rtl/matrix_engine/pe.sv (lines 40-81 of 81) --- 40: logic signed [ACCUM_WIDTH-1:0] sum; 41: logic signed [ACCUM_WIDTH-1:0] sum_saturated; 42: logic this_add_overflows; 43: 44: assign sum = acc_in + product; 45: // Signed-add overflow: operands share a sign but the result doesn't. 46: assign this_add_overflows = (acc_in[ACCUM_WIDTH-1] == product[ACCUM_WIDTH-1]) && 47: (sum[ACCUM_WIDTH-1] != acc_in[ACCUM_WIDTH-1]); 48: //
Proposed patch
--- a/rtl/matrix_engine/pe.sv
+++ b/rtl/matrix_engine/pe.sv
@@ -47,7 +47,7 @@
(sum[ACCUM_WIDTH-1] != acc_in[ACCUM_WIDTH-1]);
// On overflow, clamp rather than silently wrap: the shared operand sign
// tells us which rail to saturate to.
- assign sum_saturated = !this_add_overflows ? sum : (acc_in[ACCUM_WIDTH-1] ? sum : AccumMax);
+ assign sum_saturated = !this_add_overflows ? sum : (acc_in[ACCUM_WIDTH-1] ? AccumMin : AccumMax);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
Answer-key check (final attempt)Correct
expected bug #3 — rtl/matrix_engine/pe.sv:[50]
Rerun
PASS
agent-fix/matrix_engine-matmul_overflow_test-68e2f7e2
Tokens (all attempts)24,724 (~$0.0894)