ec82764bef
RTL (GS rasterizer, EE core stub, platform bridge, LPDDR4B path), sim regression (272 TBs), docs, and tooling. Copyrighted PS2 content (BIOS, game code, GS dumps, and all dump-derived textures/traces) is excluded via .gitignore and stays local. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
445 lines
16 KiB
Systemverilog
445 lines
16 KiB
Systemverilog
// retroDE_ps2 — tb_sif_dma_smoke
|
|
//
|
|
// First SIF DMA smoke test. One-direction bounded-payload transfer from
|
|
// ee_ram_stub, through ee_memory_map_stub, through dmac_reg_stub (channel
|
|
// 5, SIF0 in real PS2), into sif_dma_stub's receive buffer. TB preloads
|
|
// the payload, programs the DMAC, waits for completion, and reads back
|
|
// the received qwords to verify payload integrity.
|
|
//
|
|
// Combines positive-path and negative-path in one bench:
|
|
// Phase A (stall at start): TB asserts stall_in, programs DMAC, verifies
|
|
// that DMA_DONE does NOT fire and rx_count stays at 0. Then releases
|
|
// stall_in; the transfer must complete cleanly.
|
|
// Phase B (payload verify): after completion, TB reads each receive
|
|
// slot and checks against preloaded values.
|
|
//
|
|
// Scope (per Codex Wave 3 plan):
|
|
// - one direction (EE→SIF)
|
|
// - one DMAC channel (ch5)
|
|
// - fixed 4-qword payload
|
|
// - no arbitration
|
|
// - no interrupts
|
|
// - no real IOP
|
|
|
|
`timescale 1ns/1ps
|
|
|
|
module tb_sif_dma_smoke;
|
|
|
|
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 = 8;
|
|
localparam int SIF_IDX_W = $clog2(SIF_DEPTH);
|
|
|
|
localparam logic [31:0] PAYLOAD_MADR = 32'h0000_0200;
|
|
|
|
// ------------------------------------------------------------------
|
|
// 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 = SIF0 in real PS2)
|
|
// ------------------------------------------------------------------
|
|
|
|
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 (receive side)
|
|
// ------------------------------------------------------------------
|
|
|
|
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_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),
|
|
.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
|
|
// ------------------------------------------------------------------
|
|
|
|
trace_sink_stub #(.FILENAME("sif_dma_ram.trace"), .SINK_LABEL("ee_ram"))
|
|
u_trace_ram (.clk(clk), .rst_n(rst_n),
|
|
.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));
|
|
|
|
trace_sink_stub #(.FILENAME("sif_dma_map.trace"), .SINK_LABEL("map"))
|
|
u_trace_map (.clk(clk), .rst_n(rst_n),
|
|
.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));
|
|
|
|
trace_sink_stub #(.FILENAME("sif_dma_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_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 / state
|
|
// ------------------------------------------------------------------
|
|
|
|
int dma_done_count;
|
|
int dma_beat_count;
|
|
int sif_write_count;
|
|
int errors;
|
|
logic dma_done_seen;
|
|
|
|
initial begin
|
|
dma_done_count = 0;
|
|
dma_beat_count = 0;
|
|
sif_write_count = 0;
|
|
errors = 0;
|
|
dma_done_seen = 1'b0;
|
|
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
|
|
if (rst_n && sif_ev_valid && sif_ev_event == trace_pkg::EV_WRITE)
|
|
sif_write_count <= sif_write_count + 1;
|
|
end
|
|
|
|
// ------------------------------------------------------------------
|
|
// Payload table (what we preload and expect to receive)
|
|
// ------------------------------------------------------------------
|
|
|
|
logic [127:0] expected_payload [0:QWC_VAL-1];
|
|
|
|
initial begin
|
|
expected_payload[0] = 128'hAAAAAAAA_AAAAAAAA_11111111_11111111;
|
|
expected_payload[1] = 128'hBBBBBBBB_BBBBBBBB_22222222_22222222;
|
|
expected_payload[2] = 128'hCCCCCCCC_CCCCCCCC_33333333_33333333;
|
|
expected_payload[3] = 128'hDDDDDDDD_DDDDDDDD_44444444_44444444;
|
|
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 stall_observed_rx;
|
|
int stall_observed_done;
|
|
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);
|
|
|
|
// Preload the RAM with 4 distinct qwords at MADR, MADR+16, ...
|
|
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
|
|
|
|
// ---- Phase A: stall the receiver, start DMA, verify no completion.
|
|
sif_stall = 1'b1;
|
|
write_dmac(8'h10, PAYLOAD_MADR); // MADR
|
|
write_dmac(8'h20, 32'd4); // QWC = 4
|
|
write_dmac(8'h00, 32'h0000_0001); // CHCR start
|
|
|
|
// Spin for a window while stalled — no beats should be accepted,
|
|
// and DMA_DONE must not fire.
|
|
repeat (60) @(posedge clk);
|
|
stall_observed_rx = sif_rx_count;
|
|
stall_observed_done = dma_done_seen ? 1 : 0;
|
|
|
|
if (sif_rx_count !== 32'd0) begin
|
|
$error("[tb_sif_dma_smoke] stall window: rx_count=%0d (expected 0)", sif_rx_count);
|
|
errors = errors + 1;
|
|
end
|
|
if (dma_done_seen) begin
|
|
$error("[tb_sif_dma_smoke] stall window: DMA_DONE fired while stalled");
|
|
errors = errors + 1;
|
|
end
|
|
|
|
// Release the stall; transfer must now complete.
|
|
@(negedge clk);
|
|
sif_stall = 1'b0;
|
|
|
|
// Wait for DMA_DONE.
|
|
begin : wait_done
|
|
int spin;
|
|
spin = 0;
|
|
while (!dma_done_seen && spin < 200) begin
|
|
@(posedge clk);
|
|
spin = spin + 1;
|
|
end
|
|
if (!dma_done_seen) begin
|
|
$error("[tb_sif_dma_smoke] DMA_DONE never fired after stall release");
|
|
errors = errors + 1;
|
|
end
|
|
end
|
|
|
|
repeat (4) @(posedge clk);
|
|
|
|
// ---- Phase B: payload verification.
|
|
if (sif_rx_count !== 32'd4) begin
|
|
$error("[tb_sif_dma_smoke] rx_count=%0d expected 4", sif_rx_count);
|
|
errors = errors + 1;
|
|
end
|
|
if (!sif_last_seen) begin
|
|
$error("[tb_sif_dma_smoke] last_seen never asserted");
|
|
errors = errors + 1;
|
|
end
|
|
|
|
for (int i = 0; i < QWC_VAL; i++) begin
|
|
sif_read_slot(SIF_IDX_W'(i), rb);
|
|
if (rb !== expected_payload[i]) begin
|
|
$error("[tb_sif_dma_smoke] 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_smoke] stall_rx=%0d stall_done=%0d rx_count=%0d dma_beats=%0d dma_done=%0d sif_writes=%0d last_seen=%0b errors=%0d",
|
|
stall_observed_rx, stall_observed_done, sif_rx_count,
|
|
dma_beat_count, dma_done_count, sif_write_count,
|
|
sif_last_seen, errors);
|
|
|
|
if (errors == 0 &&
|
|
stall_observed_rx == 0 && stall_observed_done == 0 &&
|
|
sif_rx_count === 32'd4 && dma_beat_count == 4 &&
|
|
dma_done_count == 1 && sif_write_count == 4 && sif_last_seen)
|
|
$display("[tb_sif_dma_smoke] PASS");
|
|
else
|
|
$display("[tb_sif_dma_smoke] FAIL");
|
|
|
|
$finish;
|
|
end
|
|
|
|
initial begin
|
|
#400000;
|
|
$error("[tb_sif_dma_smoke] timeout");
|
|
$finish;
|
|
end
|
|
|
|
endmodule : tb_sif_dma_smoke
|