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>
257 lines
9.6 KiB
Systemverilog
257 lines
9.6 KiB
Systemverilog
// retroDE_ps2 — tb_ee_fetch_stub
|
|
//
|
|
// Milestone B integration test: ee_fetch_stub → ee_memory_map_stub →
|
|
// bios_rom_stub. Verifies reset vector, BIOS-window fetches against the
|
|
// synthetic fixture, unmapped access handling, and trace emission on all
|
|
// three stubs.
|
|
//
|
|
// Plan ref: docs/stub_module_plan.md (Initial testbench plan).
|
|
|
|
`timescale 1ns/1ps
|
|
|
|
module tb_ee_fetch_stub;
|
|
|
|
logic clk;
|
|
logic rst_n;
|
|
logic enable;
|
|
|
|
// --- clock ---
|
|
initial clk = 1'b0;
|
|
always #5 clk = ~clk; // 100 MHz sim clock
|
|
|
|
// ------------------------------------------------------------------
|
|
// DUTs: EE fetch → memory map → BIOS ROM
|
|
// ------------------------------------------------------------------
|
|
|
|
// EE <-> memory map
|
|
logic ee_rd_en;
|
|
logic [31:0] ee_rd_addr;
|
|
logic [31:0] ee_rd_data;
|
|
logic ee_rd_valid;
|
|
|
|
// TB-driven write channel into the map, used to exercise the UNMAPPED
|
|
// path without disturbing the ee_fetch_stub → bios chain. In Wave 1 no
|
|
// writable region exists, so any write lands as UNMAPPED.
|
|
logic tb_wr_en;
|
|
logic [31:0] tb_wr_addr;
|
|
logic [31:0] tb_wr_data;
|
|
logic [3:0] tb_wr_be;
|
|
|
|
// Memory map <-> BIOS ROM
|
|
logic bios_rd_en;
|
|
logic [21:0] bios_rd_addr;
|
|
logic [31:0] bios_rd_data;
|
|
logic bios_rd_valid;
|
|
|
|
// Trace ports per stub
|
|
logic ee_ev_valid;
|
|
trace_pkg::subsys_e ee_ev_subsys;
|
|
trace_pkg::event_e ee_ev_event;
|
|
logic [63:0] ee_ev_arg0, ee_ev_arg1, ee_ev_arg2, ee_ev_arg3;
|
|
logic [31:0] ee_ev_flags;
|
|
|
|
logic mem_ev_valid;
|
|
trace_pkg::subsys_e mem_ev_subsys;
|
|
trace_pkg::event_e mem_ev_event;
|
|
logic [63:0] mem_ev_arg0, mem_ev_arg1, mem_ev_arg2, mem_ev_arg3;
|
|
logic [31:0] mem_ev_flags;
|
|
|
|
logic bios_ev_valid;
|
|
trace_pkg::subsys_e bios_ev_subsys;
|
|
trace_pkg::event_e bios_ev_event;
|
|
logic [63:0] bios_ev_arg0, bios_ev_arg1, bios_ev_arg2, bios_ev_arg3;
|
|
logic [31:0] bios_ev_flags;
|
|
|
|
ee_fetch_stub u_ee (
|
|
.clk(clk), .rst_n(rst_n), .enable(enable),
|
|
.rd_en(ee_rd_en), .rd_addr(ee_rd_addr),
|
|
.rd_data(ee_rd_data), .rd_valid(ee_rd_valid),
|
|
.ev_valid(ee_ev_valid), .ev_subsys(ee_ev_subsys), .ev_event(ee_ev_event),
|
|
.ev_arg0(ee_ev_arg0), .ev_arg1(ee_ev_arg1),
|
|
.ev_arg2(ee_ev_arg2), .ev_arg3(ee_ev_arg3),
|
|
.ev_flags(ee_ev_flags)
|
|
);
|
|
|
|
// Wave 2.7 added DMAC + RAM ports on the map. Not exercised by this TB —
|
|
// tie inputs to inactive values, leave outputs unconnected.
|
|
logic [127:0] tb_dmac_rd_data_sink;
|
|
logic tb_dmac_rd_valid_sink;
|
|
logic tb_ram_rd_en_sink;
|
|
logic [24:0] tb_ram_rd_addr_sink;
|
|
|
|
ee_memory_map_stub u_map (
|
|
.clk(clk), .rst_n(rst_n),
|
|
.ee_rd_en(ee_rd_en), .ee_rd_addr(ee_rd_addr),
|
|
.ee_rd_data(ee_rd_data), .ee_rd_valid(ee_rd_valid),
|
|
.ee_wr_en(tb_wr_en), .ee_wr_addr(tb_wr_addr),
|
|
.ee_wr_data(tb_wr_data), .ee_wr_be(tb_wr_be),
|
|
.dmac_rd_en(1'b0), .dmac_rd_addr(32'd0),
|
|
.dmac_rd_data(tb_dmac_rd_data_sink),
|
|
.dmac_rd_valid(tb_dmac_rd_valid_sink),
|
|
.bios_rd_en(bios_rd_en), .bios_rd_addr(bios_rd_addr),
|
|
.bios_rd_data(bios_rd_data), .bios_rd_valid(bios_rd_valid),
|
|
.ram_rd_en(tb_ram_rd_en_sink), .ram_rd_addr(tb_ram_rd_addr_sink),
|
|
.ram_rd_data(128'd0), .ram_rd_valid(1'b0),
|
|
.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(mem_ev_valid), .ev_subsys(mem_ev_subsys), .ev_event(mem_ev_event),
|
|
.ev_arg0(mem_ev_arg0), .ev_arg1(mem_ev_arg1),
|
|
.ev_arg2(mem_ev_arg2), .ev_arg3(mem_ev_arg3),
|
|
.ev_flags(mem_ev_flags)
|
|
);
|
|
|
|
bios_rom_stub u_bios (
|
|
.clk(clk), .rst_n(rst_n),
|
|
.rd_en(bios_rd_en), .rd_addr(bios_rd_addr),
|
|
.rd_data(bios_rd_data), .rd_valid(bios_rd_valid),
|
|
.ev_valid(bios_ev_valid), .ev_subsys(bios_ev_subsys), .ev_event(bios_ev_event),
|
|
.ev_arg0(bios_ev_arg0), .ev_arg1(bios_ev_arg1),
|
|
.ev_arg2(bios_ev_arg2), .ev_arg3(bios_ev_arg3),
|
|
.ev_flags(bios_ev_flags)
|
|
);
|
|
|
|
// ------------------------------------------------------------------
|
|
// Trace sinks (one per stub)
|
|
// ------------------------------------------------------------------
|
|
|
|
trace_sink_stub #(.FILENAME("ee_fetch.trace"), .SINK_LABEL("ee"))
|
|
u_trace_ee (
|
|
.clk(clk), .rst_n(rst_n),
|
|
.ev_valid(ee_ev_valid), .ev_subsys(ee_ev_subsys), .ev_event(ee_ev_event),
|
|
.ev_arg0(ee_ev_arg0), .ev_arg1(ee_ev_arg1),
|
|
.ev_arg2(ee_ev_arg2), .ev_arg3(ee_ev_arg3),
|
|
.ev_flags(ee_ev_flags)
|
|
);
|
|
|
|
trace_sink_stub #(.FILENAME("ee_mem.trace"), .SINK_LABEL("mem"))
|
|
u_trace_mem (
|
|
.clk(clk), .rst_n(rst_n),
|
|
.ev_valid(mem_ev_valid), .ev_subsys(mem_ev_subsys), .ev_event(mem_ev_event),
|
|
.ev_arg0(mem_ev_arg0), .ev_arg1(mem_ev_arg1),
|
|
.ev_arg2(mem_ev_arg2), .ev_arg3(mem_ev_arg3),
|
|
.ev_flags(mem_ev_flags)
|
|
);
|
|
|
|
trace_sink_stub #(.FILENAME("ee_bios.trace"), .SINK_LABEL("bios"))
|
|
u_trace_bios (
|
|
.clk(clk), .rst_n(rst_n),
|
|
.ev_valid(bios_ev_valid), .ev_subsys(bios_ev_subsys), .ev_event(bios_ev_event),
|
|
.ev_arg0(bios_ev_arg0), .ev_arg1(bios_ev_arg1),
|
|
.ev_arg2(bios_ev_arg2), .ev_arg3(bios_ev_arg3),
|
|
.ev_flags(bios_ev_flags)
|
|
);
|
|
|
|
// ------------------------------------------------------------------
|
|
// Checkers
|
|
// ------------------------------------------------------------------
|
|
|
|
int ifetch_count;
|
|
int unmapped_count;
|
|
int errors;
|
|
|
|
initial begin
|
|
ifetch_count = 0;
|
|
unmapped_count = 0;
|
|
errors = 0;
|
|
end
|
|
|
|
// Count IFETCHes observed on the EE trace bus and verify each response
|
|
// matches the synthetic NOP-sled fixture: data == 0x00000000 (MIPS NOP).
|
|
// See sim/golden/trace_compare_spec.md for why the fixture is NOPs.
|
|
wire [31:0] obs_addr = ee_ev_arg0[31:0];
|
|
wire [31:0] obs_data = ee_ev_arg1[31:0];
|
|
wire [31:0] obs_expected = 32'h00000000;
|
|
|
|
always_ff @(posedge clk) begin
|
|
if (rst_n && ee_ev_valid && ee_ev_event == trace_pkg::EV_IFETCH) begin
|
|
ifetch_count <= ifetch_count + 1;
|
|
if (obs_data !== obs_expected) begin
|
|
$error("[tb_ee_fetch_stub] IFETCH mismatch: addr=0x%08h data=0x%08h expected=0x%08h",
|
|
obs_addr, obs_data, obs_expected);
|
|
errors <= errors + 1;
|
|
end
|
|
end
|
|
|
|
if (rst_n && mem_ev_valid && mem_ev_event == trace_pkg::EV_UNMAPPED) begin
|
|
unmapped_count <= unmapped_count + 1;
|
|
end
|
|
end
|
|
|
|
// ------------------------------------------------------------------
|
|
// Stimulus
|
|
// ------------------------------------------------------------------
|
|
|
|
initial begin
|
|
rst_n = 1'b0;
|
|
enable = 1'b0;
|
|
tb_wr_en = 1'b0;
|
|
tb_wr_addr = 32'd0;
|
|
tb_wr_data = 32'd0;
|
|
tb_wr_be = 4'd0;
|
|
|
|
repeat (4) @(posedge clk);
|
|
rst_n = 1'b1;
|
|
|
|
// Phase 1: let ee_fetch run for a window of cycles.
|
|
enable = 1'b1;
|
|
repeat (32) @(posedge clk);
|
|
enable = 1'b0;
|
|
repeat (4) @(posedge clk);
|
|
|
|
// Phase 2: drive a write to an unmapped region. Wave 1 has no
|
|
// writable backing, so this must trace as MEM UNMAPPED with
|
|
// flags bit 0 (write) set.
|
|
@(negedge clk);
|
|
tb_wr_en = 1'b1;
|
|
tb_wr_addr = 32'h1000_0000;
|
|
tb_wr_data = 32'hDEAD_BEEF;
|
|
tb_wr_be = 4'hF;
|
|
@(negedge clk);
|
|
tb_wr_en = 1'b0;
|
|
tb_wr_addr = 32'd0;
|
|
tb_wr_data = 32'd0;
|
|
tb_wr_be = 4'd0;
|
|
repeat (4) @(posedge clk);
|
|
|
|
// ------------------------------------------------------------------
|
|
// Summary
|
|
// ------------------------------------------------------------------
|
|
$display("[tb_ee_fetch_stub] ifetch_count=%0d unmapped_count=%0d errors=%0d",
|
|
ifetch_count, unmapped_count, errors);
|
|
|
|
if (ifetch_count < 20)
|
|
$error("[tb_ee_fetch_stub] expected at least 20 IFETCHes, got %0d", ifetch_count);
|
|
if (unmapped_count < 1)
|
|
$error("[tb_ee_fetch_stub] expected at least 1 UNMAPPED, got %0d", unmapped_count);
|
|
|
|
if (errors == 0 && ifetch_count >= 20 && unmapped_count >= 1)
|
|
$display("[tb_ee_fetch_stub] PASS");
|
|
else
|
|
$display("[tb_ee_fetch_stub] FAIL");
|
|
|
|
$finish;
|
|
end
|
|
|
|
initial begin
|
|
#100000;
|
|
$error("[tb_ee_fetch_stub] timeout");
|
|
$finish;
|
|
end
|
|
|
|
endmodule : tb_ee_fetch_stub
|