// retroDE_ps2 — tb_sif_dma_overflow // // Full-stop backpressure proof. Instantiates sif_dma_stub with DEPTH=2 and // issues a QWC=4 DMAC transfer. After beats 0 and 1 land, the receive // buffer is full; `in_ready` must drop and the DMAC must stall waiting // for space that never comes (this stub has no consume path yet). // // Pass criteria (per Codex capacity-hardening spec): // - exactly 2 beats accepted (rx_count == 2) // - full_o asserts // - no additional beats accepted while full // - no DMA_DONE // - two stored qwords intact and ordered // - DMAC is still trying to emit the next beat at the expected source // address (MADR + 32) — verified by internal state peek `timescale 1ns/1ps module tb_sif_dma_overflow; logic clk; logic rst_n; initial clk = 1'b0; always #5 clk = ~clk; localparam int QWC_VAL = 4; localparam int RAM_SIZE_BYTES = 4 * 1024; localparam int RAM_ADDR_W = $clog2(RAM_SIZE_BYTES); localparam int SIF_DEPTH = 2; // deliberately small localparam int SIF_IDX_W = $clog2(SIF_DEPTH); localparam logic [31:0] PAYLOAD_MADR = 32'h0000_0400; // ------------------------------------------------------------------ // ee_ram_stub // ------------------------------------------------------------------ logic ram_rd_en; logic [RAM_ADDR_W-1:0] ram_rd_addr; logic [127:0] ram_rd_data; logic ram_rd_valid; logic ram_wr_en; logic [RAM_ADDR_W-1:0] ram_wr_addr; logic [127:0] ram_wr_data; logic [15:0] ram_wr_be; logic [7:0] ram_master_id; logic ram_ev_valid; trace_pkg::subsys_e ram_ev_subsys; trace_pkg::event_e ram_ev_event; logic [63:0] ram_ev_arg0, ram_ev_arg1, ram_ev_arg2, ram_ev_arg3; logic [31:0] ram_ev_flags; ee_ram_stub #(.SIZE_BYTES(RAM_SIZE_BYTES)) u_ram ( .clk(clk), .rst_n(rst_n), .rd_en(ram_rd_en), .rd_addr(ram_rd_addr), .rd_data(ram_rd_data), .rd_valid(ram_rd_valid), .wr_en(ram_wr_en), .wr_addr(ram_wr_addr), .wr_data(ram_wr_data), .wr_be(ram_wr_be), .master_id(ram_master_id), .ev_valid(ram_ev_valid), .ev_subsys(ram_ev_subsys), .ev_event(ram_ev_event), .ev_arg0(ram_ev_arg0), .ev_arg1(ram_ev_arg1), .ev_arg2(ram_ev_arg2), .ev_arg3(ram_ev_arg3), .ev_flags(ram_ev_flags) ); assign ram_master_id = ram_rd_en ? 8'd1 : 8'd0; // ------------------------------------------------------------------ // DMAC (channel 5) // ------------------------------------------------------------------ logic dmac_reg_wr_en; logic [7:0] dmac_reg_offset; logic [31:0] dmac_reg_wr_data; logic dmac_mem_rd_en; logic [31:0] dmac_mem_rd_addr; logic [127:0] map_to_dmac_rd_data; logic map_to_dmac_rd_valid; logic dmac_ep_valid; logic [127:0] dmac_ep_data; logic dmac_ep_last; logic dmac_ep_ready; logic dmac_ev_valid; trace_pkg::subsys_e dmac_ev_subsys; trace_pkg::event_e dmac_ev_event; logic [63:0] dmac_ev_arg0, dmac_ev_arg1, dmac_ev_arg2, dmac_ev_arg3; logic [31:0] dmac_ev_flags; dmac_reg_stub #(.CHANNEL(4'd5), .PATH_ID(4'd5)) u_dmac ( .clk(clk), .rst_n(rst_n), .reg_wr_en(dmac_reg_wr_en), .reg_offset(dmac_reg_offset), .reg_wr_data(dmac_reg_wr_data), .reg_rd_en(1'b0), .reg_rd_data(), .reg_rd_valid(), .mem_rd_en(dmac_mem_rd_en), .mem_rd_addr(dmac_mem_rd_addr), .mem_rd_data(map_to_dmac_rd_data), .mem_rd_valid(map_to_dmac_rd_valid), .ep_valid(dmac_ep_valid), .ep_data(dmac_ep_data), .ep_last(dmac_ep_last), .ep_ready(dmac_ep_ready), .irq_completion_o(), .ev_valid(dmac_ev_valid), .ev_subsys(dmac_ev_subsys), .ev_event(dmac_ev_event), .ev_arg0(dmac_ev_arg0), .ev_arg1(dmac_ev_arg1), .ev_arg2(dmac_ev_arg2), .ev_arg3(dmac_ev_arg3), .ev_flags(dmac_ev_flags) ); // ------------------------------------------------------------------ // ee_memory_map_stub // ------------------------------------------------------------------ logic map_ram_rd_en; logic [24:0] map_ram_rd_addr; logic map_ev_valid; trace_pkg::subsys_e map_ev_subsys; trace_pkg::event_e map_ev_event; logic [63:0] map_ev_arg0, map_ev_arg1, map_ev_arg2, map_ev_arg3; logic [31:0] map_ev_flags; ee_memory_map_stub u_map ( .clk(clk), .rst_n(rst_n), .ee_rd_en(1'b0), .ee_rd_addr(32'd0), .ee_rd_data(), .ee_rd_valid(), .ee_wr_en(1'b0), .ee_wr_addr(32'd0), .ee_wr_data(32'd0), .ee_wr_be(4'd0), .dmac_rd_en(dmac_mem_rd_en), .dmac_rd_addr(dmac_mem_rd_addr), .dmac_rd_data(map_to_dmac_rd_data), .dmac_rd_valid(map_to_dmac_rd_valid), .bios_rd_en(), .bios_rd_addr(), .bios_rd_data(32'd0), .bios_rd_valid(1'b0), .ram_rd_en(map_ram_rd_en), .ram_rd_addr(map_ram_rd_addr), .ram_rd_data(ram_rd_data), .ram_rd_valid(ram_rd_valid), .bridge_wr_en(1'b0), .bridge_wr_addr(32'd0), .bridge_wr_data(128'd0), .bridge_wr_be(16'd0), .bridge_master_id(8'd0), .ram_wr_en(), .ram_wr_addr(), .ram_wr_data(), .ram_wr_be(), .ram_master_id(), .ee_dmac_ch2_wr_en(), .ee_dmac_ch2_wr_addr(), .ee_dmac_ch2_wr_data(), .ee_dmac_ch2_rd_en(), .ee_dmac_ch2_rd_addr(), .ee_dmac_ch2_rd_data(32'd0), .ee_dmac_ch2_rd_valid(1'b0), .ee_intc_wr_en(), .ee_intc_wr_addr(), .ee_intc_wr_data(), .ee_intc_rd_en(), .ee_intc_rd_addr(), .ee_intc_rd_data(32'd0), .ee_intc_rd_valid(1'b0), .ee_misc_mmio_wr_en(), .ee_misc_mmio_wr_addr(), .ee_misc_mmio_wr_data(), .ee_misc_mmio_wr_be(), .ee_misc_mmio_rd_en(), .ee_misc_mmio_rd_addr(), .ee_misc_mmio_rd_data(32'd0), .ee_misc_mmio_rd_valid(1'b0), .ee_biu_wr_en(), .ee_biu_wr_addr(), .ee_biu_wr_data(), .ee_biu_wr_be(), .ee_biu_rd_en(), .ee_biu_rd_addr(), .ee_biu_rd_data(32'd0), .ee_biu_rd_valid(1'b0), .ev_valid(map_ev_valid), .ev_subsys(map_ev_subsys), .ev_event(map_ev_event), .ev_arg0(map_ev_arg0), .ev_arg1(map_ev_arg1), .ev_arg2(map_ev_arg2), .ev_arg3(map_ev_arg3), .ev_flags(map_ev_flags) ); assign ram_rd_en = map_ram_rd_en; assign ram_rd_addr = map_ram_rd_addr[RAM_ADDR_W-1:0]; // ------------------------------------------------------------------ // sif_dma_stub (DEPTH=2) // ------------------------------------------------------------------ logic sif_rd_en; logic [SIF_IDX_W-1:0] sif_rd_idx; logic [127:0] sif_rd_data; logic sif_rd_valid; logic sif_stall; logic [31:0] sif_rx_count; logic sif_last_seen; logic sif_full; logic sif_ev_valid; trace_pkg::subsys_e sif_ev_subsys; trace_pkg::event_e sif_ev_event; logic [63:0] sif_ev_arg0, sif_ev_arg1, sif_ev_arg2, sif_ev_arg3; logic [31:0] sif_ev_flags; sif_dma_stub #(.DEPTH(SIF_DEPTH)) u_sif ( .clk(clk), .rst_n(rst_n), .in_valid(dmac_ep_valid), .in_data(dmac_ep_data), .in_last(dmac_ep_last), .in_ready(dmac_ep_ready), .rd_en(sif_rd_en), .rd_idx(sif_rd_idx), .rd_data(sif_rd_data), .rd_valid(sif_rd_valid), .stall_in(sif_stall), .rx_count(sif_rx_count), .last_seen(sif_last_seen), .full_o(sif_full), .ev_valid(sif_ev_valid), .ev_subsys(sif_ev_subsys), .ev_event(sif_ev_event), .ev_arg0(sif_ev_arg0), .ev_arg1(sif_ev_arg1), .ev_arg2(sif_ev_arg2), .ev_arg3(sif_ev_arg3), .ev_flags(sif_ev_flags) ); // ------------------------------------------------------------------ // Trace sinks (single combined file for brevity) // ------------------------------------------------------------------ trace_sink_stub #(.FILENAME("sif_dma_overflow_dmac.trace"), .SINK_LABEL("dmac")) u_trace_dmac (.clk(clk), .rst_n(rst_n), .ev_valid(dmac_ev_valid), .ev_subsys(dmac_ev_subsys), .ev_event(dmac_ev_event), .ev_arg0(dmac_ev_arg0), .ev_arg1(dmac_ev_arg1), .ev_arg2(dmac_ev_arg2), .ev_arg3(dmac_ev_arg3), .ev_flags(dmac_ev_flags)); trace_sink_stub #(.FILENAME("sif_dma_overflow_sif.trace"), .SINK_LABEL("sif")) u_trace_sif (.clk(clk), .rst_n(rst_n), .ev_valid(sif_ev_valid), .ev_subsys(sif_ev_subsys), .ev_event(sif_ev_event), .ev_arg0(sif_ev_arg0), .ev_arg1(sif_ev_arg1), .ev_arg2(sif_ev_arg2), .ev_arg3(sif_ev_arg3), .ev_flags(sif_ev_flags)); // ------------------------------------------------------------------ // Counters // ------------------------------------------------------------------ int dma_beat_count; int dma_done_count; logic dma_done_seen; int errors; initial begin dma_beat_count = 0; dma_done_count = 0; dma_done_seen = 1'b0; errors = 0; end always_ff @(posedge clk) begin if (rst_n && dmac_ev_valid) begin if (dmac_ev_event == trace_pkg::EV_DMA_BEAT) dma_beat_count <= dma_beat_count + 1; if (dmac_ev_event == trace_pkg::EV_DMA_DONE) begin dma_done_count <= dma_done_count + 1; dma_done_seen <= 1'b1; end end end // ------------------------------------------------------------------ // Payload table // ------------------------------------------------------------------ logic [127:0] expected_payload [0:QWC_VAL-1]; initial begin expected_payload[0] = 128'h1111_0000_1111_0000_1111_0000_1111_0000; expected_payload[1] = 128'h2222_0000_2222_0000_2222_0000_2222_0000; expected_payload[2] = 128'h3333_0000_3333_0000_3333_0000_3333_0000; expected_payload[3] = 128'h4444_0000_4444_0000_4444_0000_4444_0000; end // ------------------------------------------------------------------ // Helpers // ------------------------------------------------------------------ task automatic write_dmac(input logic [7:0] offset, input logic [31:0] data); @(negedge clk); dmac_reg_wr_en = 1'b1; dmac_reg_offset = offset; dmac_reg_wr_data = data; @(negedge clk); dmac_reg_wr_en = 1'b0; dmac_reg_offset = 8'd0; dmac_reg_wr_data = 32'd0; endtask task automatic preload_ram_qword(input logic [RAM_ADDR_W-1:0] addr, input logic [127:0] data); @(negedge clk); ram_wr_en = 1'b1; ram_wr_addr = addr; ram_wr_data = data; ram_wr_be = 16'hFFFF; @(negedge clk); ram_wr_en = 1'b0; ram_wr_addr = '0; ram_wr_data = 128'd0; ram_wr_be = 16'd0; endtask task automatic sif_read_slot(input logic [SIF_IDX_W-1:0] idx, output logic [127:0] data); @(negedge clk); sif_rd_en = 1'b1; sif_rd_idx = idx; @(negedge clk); sif_rd_en = 1'b0; sif_rd_idx = '0; data = sif_rd_data; endtask // ------------------------------------------------------------------ // Stimulus // ------------------------------------------------------------------ int settle_beats; int settle_rx; logic settle_full; logic settle_done; int beat_index_peek; logic [127:0] beat_payload_peek; logic [127:0] rb; initial begin rst_n = 1'b0; dmac_reg_wr_en = 1'b0; dmac_reg_offset = 8'd0; dmac_reg_wr_data = 32'd0; ram_wr_en = 1'b0; ram_wr_addr = '0; ram_wr_data = 128'd0; ram_wr_be = 16'd0; sif_rd_en = 1'b0; sif_rd_idx = '0; sif_stall = 1'b0; repeat (4) @(posedge clk); rst_n = 1'b1; repeat (2) @(posedge clk); begin : do_preload logic [31:0] a; for (int i = 0; i < QWC_VAL; i++) begin a = PAYLOAD_MADR + (i * 32'd16); preload_ram_qword(a[RAM_ADDR_W-1:0], expected_payload[i]); end end // Program DMAC for a QWC=4 transfer. With DEPTH=2, the receiver // should accept exactly 2 beats and then backpressure indefinitely. write_dmac(8'h10, PAYLOAD_MADR); write_dmac(8'h20, 32'd4); write_dmac(8'h00, 32'h0000_0001); // Let the DMAC exhaust buffer capacity and settle into stall. repeat (80) @(posedge clk); settle_beats = dma_beat_count; settle_rx = sif_rx_count; settle_full = sif_full; settle_done = dma_done_seen; // Whitebox peeks into the DMAC to verify it is still trying to // emit beat 2 at MADR + 32, not skipping ahead. beat_index_peek = u_dmac.beat_index; beat_payload_peek = u_dmac.beat_payload; if (settle_beats !== 2) begin $error("[tb_sif_dma_overflow] expected exactly 2 beats accepted, got %0d", settle_beats); errors = errors + 1; end if (settle_rx !== 32'd2) begin $error("[tb_sif_dma_overflow] rx_count=%0d, expected 2", settle_rx); errors = errors + 1; end if (!settle_full) begin $error("[tb_sif_dma_overflow] full_o never asserted"); errors = errors + 1; end if (settle_done) begin $error("[tb_sif_dma_overflow] DMA_DONE fired while buffer full"); errors = errors + 1; end if (beat_index_peek !== 2) begin $error("[tb_sif_dma_overflow] DMAC beat_index peek=%0d, expected 2 (stuck on beat 2)", beat_index_peek); errors = errors + 1; end if (beat_payload_peek !== expected_payload[2]) begin $error("[tb_sif_dma_overflow] DMAC beat_payload peek mismatch"); $error(" got: 0x%032h", beat_payload_peek); $error(" expected: 0x%032h (= payload[2], fetched from MADR+32)", expected_payload[2]); errors = errors + 1; end // Spin another window — nothing should advance. repeat (40) @(posedge clk); if (dma_beat_count !== 2 || sif_rx_count !== 32'd2 || dma_done_seen) begin $error("[tb_sif_dma_overflow] state advanced during second stall window"); errors = errors + 1; end // Verify the two stored qwords are intact. for (int i = 0; i < SIF_DEPTH; i++) begin sif_read_slot(SIF_IDX_W'(i), rb); if (rb !== expected_payload[i]) begin $error("[tb_sif_dma_overflow] slot[%0d]: got 0x%032h expected 0x%032h", i, rb, expected_payload[i]); errors = errors + 1; end end repeat (4) @(posedge clk); // ------------------------------------------------------------------ $display("[tb_sif_dma_overflow] settle_beats=%0d settle_rx=%0d full=%0b done=%0b beat_idx=%0d errors=%0d", settle_beats, settle_rx, settle_full, settle_done, beat_index_peek, errors); if (errors == 0 && settle_beats == 2 && settle_rx === 32'd2 && settle_full && !settle_done && beat_index_peek == 2 && beat_payload_peek === expected_payload[2]) $display("[tb_sif_dma_overflow] PASS"); else $display("[tb_sif_dma_overflow] FAIL"); $finish; end initial begin #400000; $error("[tb_sif_dma_overflow] timeout"); $finish; end endmodule : tb_sif_dma_overflow