Initial commit: retroDE_ps2 — first-of-its-kind PS2 GS FPGA core (DE25-Nano / Agilex 5)
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>
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
// retroDE_ps2 — tb_ee_biu_mmio
|
||||
//
|
||||
// Chapter 9 targeted TB for ee_biu_mmio_stub. Same semantic shape
|
||||
// as tb_ee_bootstrap_mmio (latched register file, per-byte write
|
||||
// latching, 1-cycle read latency) but a separate module and a
|
||||
// narrower 4 KiB address space. Verifies:
|
||||
//
|
||||
// 1) Reset-init-to-zero for probed offsets.
|
||||
// 2) Write-then-read round-trip returns written value.
|
||||
// 3) Distinct offsets don't collide.
|
||||
// 4) Per-lane byte-enable preservation (SB-through-window, SH-
|
||||
// through-window, and be=0 as no-op). These matter for the
|
||||
// same reason as chapter 8 — the EE's sub-word store
|
||||
// opcodes (SB/SH) can target this window.
|
||||
//
|
||||
// Targets the concrete offset the real BIOS touches: 0x130 (maps
|
||||
// to word index 0x4C inside the 4 KiB window). That specific
|
||||
// offset gets its own case, so if someone later narrows the
|
||||
// decode and accidentally drops that offset, this TB catches it.
|
||||
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module tb_ee_biu_mmio;
|
||||
|
||||
logic clk;
|
||||
logic rst_n;
|
||||
|
||||
initial clk = 1'b0;
|
||||
always #5 clk = ~clk;
|
||||
|
||||
// DUT ports
|
||||
logic reg_wr_en;
|
||||
logic [11:0] reg_wr_addr;
|
||||
logic [31:0] reg_wr_data;
|
||||
logic [3:0] reg_wr_be;
|
||||
logic reg_rd_en;
|
||||
logic [11:0] reg_rd_addr;
|
||||
logic [31:0] reg_rd_data;
|
||||
logic reg_rd_valid;
|
||||
|
||||
logic ev_valid;
|
||||
trace_pkg::subsys_e ev_subsys;
|
||||
trace_pkg::event_e ev_event;
|
||||
logic [63:0] ev_arg0, ev_arg1, ev_arg2, ev_arg3;
|
||||
logic [31:0] ev_flags;
|
||||
|
||||
ee_biu_mmio_stub dut (
|
||||
.clk(clk), .rst_n(rst_n),
|
||||
.reg_wr_en(reg_wr_en), .reg_wr_addr(reg_wr_addr),
|
||||
.reg_wr_data(reg_wr_data), .reg_wr_be(reg_wr_be),
|
||||
.reg_rd_en(reg_rd_en), .reg_rd_addr(reg_rd_addr),
|
||||
.reg_rd_data(reg_rd_data), .reg_rd_valid(reg_rd_valid),
|
||||
.ev_valid(ev_valid), .ev_subsys(ev_subsys), .ev_event(ev_event),
|
||||
.ev_arg0(ev_arg0), .ev_arg1(ev_arg1),
|
||||
.ev_arg2(ev_arg2), .ev_arg3(ev_arg3),
|
||||
.ev_flags(ev_flags)
|
||||
);
|
||||
|
||||
int errors = 0;
|
||||
|
||||
task automatic do_write_be(input logic [11:0] addr,
|
||||
input logic [31:0] data,
|
||||
input logic [3:0] be);
|
||||
@(negedge clk);
|
||||
reg_wr_en = 1'b1;
|
||||
reg_wr_addr = addr;
|
||||
reg_wr_data = data;
|
||||
reg_wr_be = be;
|
||||
@(negedge clk);
|
||||
reg_wr_en = 1'b0;
|
||||
reg_wr_addr = 12'd0;
|
||||
reg_wr_data = 32'd0;
|
||||
reg_wr_be = 4'b0000;
|
||||
endtask
|
||||
|
||||
task automatic do_write(input logic [11:0] addr, input logic [31:0] data);
|
||||
do_write_be(addr, data, 4'b1111);
|
||||
endtask
|
||||
|
||||
task automatic do_read(input logic [11:0] addr,
|
||||
output logic [31:0] data_out);
|
||||
@(negedge clk);
|
||||
reg_rd_en = 1'b1;
|
||||
reg_rd_addr = addr;
|
||||
@(negedge clk);
|
||||
reg_rd_en = 1'b0;
|
||||
reg_rd_addr = 12'd0;
|
||||
@(posedge clk);
|
||||
data_out = reg_rd_data;
|
||||
endtask
|
||||
|
||||
task automatic check(input string tag,
|
||||
input logic [31:0] got,
|
||||
input logic [31:0] exp);
|
||||
if (got !== exp) begin
|
||||
$display("[tb_ee_biu_mmio] FAIL %s got=0x%08h exp=0x%08h",
|
||||
tag, got, exp);
|
||||
errors++;
|
||||
end else begin
|
||||
$display("[tb_ee_biu_mmio] ok %s = 0x%08h", tag, got);
|
||||
end
|
||||
endtask
|
||||
|
||||
logic [31:0] rd0;
|
||||
|
||||
initial begin
|
||||
rst_n = 1'b0;
|
||||
reg_wr_en = 1'b0;
|
||||
reg_wr_addr = 12'd0;
|
||||
reg_wr_data = 32'd0;
|
||||
reg_wr_be = 4'b0000;
|
||||
reg_rd_en = 1'b0;
|
||||
reg_rd_addr = 12'd0;
|
||||
|
||||
repeat (4) @(posedge clk);
|
||||
rst_n = 1'b1;
|
||||
@(posedge clk);
|
||||
|
||||
// 1) Reset-init: every probed offset reads 0.
|
||||
do_read(12'h000, rd0); check("reset_read_0x000", rd0, 32'd0);
|
||||
do_read(12'h130, rd0); check("reset_read_0x130_bios_touches_here",
|
||||
rd0, 32'd0);
|
||||
do_read(12'hFFC, rd0); check("reset_read_0xFFC", rd0, 32'd0);
|
||||
|
||||
// 2) Write-then-read at the BIOS-touched offset. Mirrors the
|
||||
// real observed write pattern (cache-control config values).
|
||||
do_write(12'h130, 32'h3202_000F);
|
||||
do_read (12'h130, rd0); check("bios_0x130_writeread",
|
||||
rd0, 32'h3202_000F);
|
||||
|
||||
// 3) Distinct offsets.
|
||||
do_write(12'h000, 32'hAAAA_AAAA);
|
||||
do_write(12'h140, 32'hBBBB_BBBB);
|
||||
do_write(12'hFFC, 32'hCCCC_CCCC);
|
||||
do_read (12'h000, rd0); check("distinct_0x000", rd0, 32'hAAAA_AAAA);
|
||||
do_read (12'h140, rd0); check("distinct_0x140", rd0, 32'hBBBB_BBBB);
|
||||
do_read (12'hFFC, rd0); check("distinct_0xFFC", rd0, 32'hCCCC_CCCC);
|
||||
do_read (12'h130, rd0); check("prior_wr_preserved",
|
||||
rd0, 32'h3202_000F);
|
||||
|
||||
// 4) Per-byte enables (SB).
|
||||
do_write(12'h200, 32'h1122_3344);
|
||||
do_write_be(12'h200, 32'h0000_00AA, 4'b0001);
|
||||
do_read(12'h200, rd0); check("sb_lane0", rd0, 32'h1122_33AA);
|
||||
|
||||
do_write(12'h204, 32'h1122_3344);
|
||||
do_write_be(12'h204, 32'hDD00_0000, 4'b1000);
|
||||
do_read(12'h204, rd0); check("sb_lane3", rd0, 32'hDD22_3344);
|
||||
|
||||
// 5) Halfword enables (SH).
|
||||
do_write(12'h300, 32'h1122_3344);
|
||||
do_write_be(12'h300, 32'h0000_AABB, 4'b0011);
|
||||
do_read(12'h300, rd0); check("sh_low", rd0, 32'h1122_AABB);
|
||||
|
||||
do_write(12'h304, 32'h1122_3344);
|
||||
do_write_be(12'h304, 32'hCCDD_0000, 4'b1100);
|
||||
do_read(12'h304, rd0); check("sh_high", rd0, 32'hCCDD_3344);
|
||||
|
||||
// 6) Zero-be no-op.
|
||||
do_write(12'h400, 32'hDEAD_BEEF);
|
||||
do_write_be(12'h400, 32'hFFFF_FFFF, 4'b0000);
|
||||
do_read(12'h400, rd0); check("be_zero_noop", rd0, 32'hDEAD_BEEF);
|
||||
|
||||
if (errors == 0)
|
||||
$display("[tb_ee_biu_mmio] PASS");
|
||||
else
|
||||
$display("[tb_ee_biu_mmio] FAIL errors=%0d", errors);
|
||||
|
||||
$finish;
|
||||
end
|
||||
|
||||
initial begin
|
||||
#5_000_000;
|
||||
$display("[tb_ee_biu_mmio] TIMEOUT");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule : tb_ee_biu_mmio
|
||||
@@ -0,0 +1,221 @@
|
||||
// retroDE_ps2 — tb_ee_bootstrap_mmio
|
||||
//
|
||||
// Chapter 8 targeted TB for ee_bootstrap_mmio_stub. Proves the three
|
||||
// semantics that tb_ee_core_bios_smoke relies on for chapter 7.99's
|
||||
// DEADBEEF-poisoning fix to hold:
|
||||
//
|
||||
// 1) Reset-init to zero. A read from any register immediately after
|
||||
// reset returns 0, not X. (The real BIOS does read-modify-write:
|
||||
// if a fresh read returns X, the OR-in-bit write corrupts the
|
||||
// register even though it "looks" like a normal bit-set.)
|
||||
//
|
||||
// 2) Write-then-read at the same offset returns the written value.
|
||||
// 1-cycle read latency to match the rest of the stub ecosystem.
|
||||
//
|
||||
// 3) Writes at different offsets don't collide. A sentinel test
|
||||
// pattern across a handful of offsets verifies the latch is a
|
||||
// real register file, not a single shared latch.
|
||||
//
|
||||
// Runs in sub-microseconds. No BIOS dump required.
|
||||
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module tb_ee_bootstrap_mmio;
|
||||
|
||||
logic clk;
|
||||
logic rst_n;
|
||||
|
||||
initial clk = 1'b0;
|
||||
always #5 clk = ~clk;
|
||||
|
||||
// DUT ports
|
||||
logic reg_wr_en;
|
||||
logic [15:0] reg_wr_addr;
|
||||
logic [31:0] reg_wr_data;
|
||||
logic [3:0] reg_wr_be;
|
||||
logic reg_rd_en;
|
||||
logic [15:0] reg_rd_addr;
|
||||
logic [31:0] reg_rd_data;
|
||||
logic reg_rd_valid;
|
||||
|
||||
logic ev_valid;
|
||||
trace_pkg::subsys_e ev_subsys;
|
||||
trace_pkg::event_e ev_event;
|
||||
logic [63:0] ev_arg0, ev_arg1, ev_arg2, ev_arg3;
|
||||
logic [31:0] ev_flags;
|
||||
|
||||
ee_bootstrap_mmio_stub dut (
|
||||
.clk(clk), .rst_n(rst_n),
|
||||
.reg_wr_en(reg_wr_en), .reg_wr_addr(reg_wr_addr),
|
||||
.reg_wr_data(reg_wr_data), .reg_wr_be(reg_wr_be),
|
||||
.reg_rd_en(reg_rd_en), .reg_rd_addr(reg_rd_addr),
|
||||
.reg_rd_data(reg_rd_data), .reg_rd_valid(reg_rd_valid),
|
||||
// Ch259 — no synthetic IOP INTC source in this targeted TB.
|
||||
.iop_intc_inject_src_i(16'd0),
|
||||
.ev_valid(ev_valid), .ev_subsys(ev_subsys), .ev_event(ev_event),
|
||||
.ev_arg0(ev_arg0), .ev_arg1(ev_arg1),
|
||||
.ev_arg2(ev_arg2), .ev_arg3(ev_arg3),
|
||||
.ev_flags(ev_flags)
|
||||
);
|
||||
|
||||
int errors = 0;
|
||||
|
||||
task automatic do_write(input logic [15:0] addr, input logic [31:0] data);
|
||||
do_write_be(addr, data, 4'b1111);
|
||||
endtask
|
||||
|
||||
task automatic do_write_be(input logic [15:0] addr,
|
||||
input logic [31:0] data,
|
||||
input logic [3:0] be);
|
||||
@(negedge clk);
|
||||
reg_wr_en = 1'b1;
|
||||
reg_wr_addr = addr;
|
||||
reg_wr_data = data;
|
||||
reg_wr_be = be;
|
||||
@(negedge clk);
|
||||
reg_wr_en = 1'b0;
|
||||
reg_wr_addr = 16'd0;
|
||||
reg_wr_data = 32'd0;
|
||||
reg_wr_be = 4'b0000;
|
||||
endtask
|
||||
|
||||
task automatic do_read(input logic [15:0] addr,
|
||||
output logic [31:0] data_out);
|
||||
@(negedge clk);
|
||||
reg_rd_en = 1'b1;
|
||||
reg_rd_addr = addr;
|
||||
@(negedge clk);
|
||||
reg_rd_en = 1'b0;
|
||||
reg_rd_addr = 16'd0;
|
||||
// reg_rd_data is registered; sample on the following posedge
|
||||
@(posedge clk);
|
||||
data_out = reg_rd_data;
|
||||
endtask
|
||||
|
||||
task automatic check(input string tag,
|
||||
input logic [31:0] got,
|
||||
input logic [31:0] exp);
|
||||
if (got !== exp) begin
|
||||
$display("[tb_ee_bootstrap_mmio] FAIL %s got=0x%08h exp=0x%08h",
|
||||
tag, got, exp);
|
||||
errors++;
|
||||
end else begin
|
||||
$display("[tb_ee_bootstrap_mmio] ok %s = 0x%08h", tag, got);
|
||||
end
|
||||
endtask
|
||||
|
||||
logic [31:0] rd0;
|
||||
|
||||
initial begin
|
||||
rst_n = 1'b0;
|
||||
reg_wr_en = 1'b0;
|
||||
reg_wr_addr = 16'd0;
|
||||
reg_wr_data = 32'd0;
|
||||
reg_wr_be = 4'b0000;
|
||||
reg_rd_en = 1'b0;
|
||||
reg_rd_addr = 16'd0;
|
||||
|
||||
repeat (4) @(posedge clk);
|
||||
rst_n = 1'b1;
|
||||
@(posedge clk);
|
||||
|
||||
// 1) Reset-init: every probed offset reads 0.
|
||||
do_read(16'h0010, rd0); check("reset_read_0x0010", rd0, 32'd0);
|
||||
do_read(16'h1000, rd0); check("reset_read_0x1000", rd0, 32'd0);
|
||||
do_read(16'hF008, rd0); check("reset_read_0xF008", rd0, 32'd0);
|
||||
|
||||
// 2) Write-then-read same offset returns the written value.
|
||||
do_write(16'h0010, 32'hAABB_CCDD);
|
||||
do_read (16'h0010, rd0); check("wr_rd_same_off", rd0, 32'hAABB_CCDD);
|
||||
|
||||
// 3) Distinct offsets do not collide.
|
||||
do_write(16'h0100, 32'h1111_1111);
|
||||
do_write(16'h0200, 32'h2222_2222);
|
||||
do_write(16'h0300, 32'h3333_3333);
|
||||
do_write(16'hFFFC, 32'hDEAD_F00D);
|
||||
|
||||
do_read(16'h0100, rd0); check("distinct_0x0100", rd0, 32'h1111_1111);
|
||||
do_read(16'h0200, rd0); check("distinct_0x0200", rd0, 32'h2222_2222);
|
||||
do_read(16'h0300, rd0); check("distinct_0x0300", rd0, 32'h3333_3333);
|
||||
do_read(16'hFFFC, rd0); check("distinct_0xFFFC", rd0, 32'hDEAD_F00D);
|
||||
do_read(16'h0010, rd0); check("prior_wr_preserved", rd0, 32'hAABB_CCDD);
|
||||
|
||||
// 4) Overwrite replaces prior value.
|
||||
do_write(16'h0010, 32'h5555_5555);
|
||||
do_read (16'h0010, rd0); check("overwrite", rd0, 32'h5555_5555);
|
||||
|
||||
// 5) Word-index aliasing — low 2 bits ignored (word-addressed).
|
||||
do_write(16'h0400, 32'hCAFE_BABE);
|
||||
do_read (16'h0401, rd0); check("byte_addr_aliases_to_word", rd0, 32'hCAFE_BABE);
|
||||
do_read (16'h0402, rd0); check("byte_addr_aliases_to_word2", rd0, 32'hCAFE_BABE);
|
||||
do_read (16'h0403, rd0); check("byte_addr_aliases_to_word3", rd0, 32'hCAFE_BABE);
|
||||
|
||||
// 6) Byte-enable preservation (SB-style). Seed word with a
|
||||
// known pattern via full-word write, then SB to each lane and
|
||||
// verify the other 3 lanes are preserved.
|
||||
do_write(16'h0500, 32'h11223344);
|
||||
do_write_be(16'h0500, 32'h000000AA, 4'b0001); // SB byte 0
|
||||
do_read(16'h0500, rd0); check("sb_lane0", rd0, 32'h112233AA);
|
||||
|
||||
do_write(16'h0504, 32'h11223344);
|
||||
do_write_be(16'h0504, 32'h0000BB00, 4'b0010); // SB byte 1
|
||||
do_read(16'h0504, rd0); check("sb_lane1", rd0, 32'h1122BB44);
|
||||
|
||||
do_write(16'h0508, 32'h11223344);
|
||||
do_write_be(16'h0508, 32'h00CC0000, 4'b0100); // SB byte 2
|
||||
do_read(16'h0508, rd0); check("sb_lane2", rd0, 32'h11CC3344);
|
||||
|
||||
do_write(16'h050C, 32'h11223344);
|
||||
do_write_be(16'h050C, 32'hDD000000, 4'b1000); // SB byte 3
|
||||
do_read(16'h050C, rd0); check("sb_lane3", rd0, 32'hDD223344);
|
||||
|
||||
// 7) Halfword-enable preservation (SH-style).
|
||||
do_write(16'h0600, 32'h11223344);
|
||||
do_write_be(16'h0600, 32'h0000AABB, 4'b0011); // SH low halfword
|
||||
do_read(16'h0600, rd0); check("sh_low_half", rd0, 32'h1122AABB);
|
||||
|
||||
do_write(16'h0604, 32'h11223344);
|
||||
do_write_be(16'h0604, 32'hCCDD0000, 4'b1100); // SH high halfword
|
||||
do_read(16'h0604, rd0); check("sh_high_half", rd0, 32'hCCDD3344);
|
||||
|
||||
// 8) Zero be == no change.
|
||||
do_write(16'h0700, 32'hDEAD_BEEF);
|
||||
do_write_be(16'h0700, 32'hFFFF_FFFF, 4'b0000); // nothing latched
|
||||
do_read(16'h0700, rd0); check("be_zero_noop", rd0, 32'hDEAD_BEEF);
|
||||
|
||||
// 9) Ch202 — offset 0x1814 returns MMIO_1814_RDY_VALUE
|
||||
// (defaulted to 0xFFFFFFFF in the stub) regardless of writes.
|
||||
// The BIOS at PC=0xbfc4fb04..fb30 polls this address waiting
|
||||
// for ($read & mask=0x10000000) != 0. The stub's narrow
|
||||
// read-only return satisfies the poll without enabling a
|
||||
// full-window all-ones default (which would risk side-effects
|
||||
// for other offsets that DO need writable semantics).
|
||||
do_read (16'h1814, rd0); check("ch202_rdy_reset", rd0, 32'hFFFF_FFFF);
|
||||
// BIOS-observed mask satisfied?
|
||||
check("ch202_mask_satisfied",
|
||||
(rd0 & 32'h1000_0000) != 32'd0 ? 32'd1 : 32'd0, 32'd1);
|
||||
// Even after a write, reads still return the ready value
|
||||
// (write latches into regs[] but the read intercept wins).
|
||||
do_write(16'h1814, 32'hDEAD_BEEF);
|
||||
do_read (16'h1814, rd0); check("ch202_rdy_after_wr", rd0, 32'hFFFF_FFFF);
|
||||
// Neighbor offsets are NOT special-cased and behave normally.
|
||||
do_write(16'h1810, 32'hAAAA_AAAA);
|
||||
do_read (16'h1810, rd0); check("ch202_neighbor_lo", rd0, 32'hAAAA_AAAA);
|
||||
do_write(16'h1818, 32'hBBBB_BBBB);
|
||||
do_read (16'h1818, rd0); check("ch202_neighbor_hi", rd0, 32'hBBBB_BBBB);
|
||||
|
||||
if (errors == 0)
|
||||
$display("[tb_ee_bootstrap_mmio] PASS");
|
||||
else
|
||||
$display("[tb_ee_bootstrap_mmio] FAIL errors=%0d", errors);
|
||||
|
||||
$finish;
|
||||
end
|
||||
|
||||
initial begin
|
||||
#5_000_000;
|
||||
$display("[tb_ee_bootstrap_mmio] TIMEOUT");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule : tb_ee_bootstrap_mmio
|
||||
@@ -0,0 +1,256 @@
|
||||
// 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
|
||||
Reference in New Issue
Block a user