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,142 @@
|
||||
// retroDE_ps2 — ee_biu_mmio_stub
|
||||
//
|
||||
// Narrow latched-register-file stub for the EE Bus Interface Unit /
|
||||
// cache-control window at virtual `0xFFFE_0000 - 0xFFFE_0FFF`
|
||||
// (physical `0x1FFE_0000 - 0x1FFE_0FFF` after kseg1-stripping).
|
||||
// Architecturally this is the R5900's privileged BIU/control
|
||||
// register space — the same place the BIOS writes CACHE-control
|
||||
// and BIU-config values during boot.
|
||||
//
|
||||
// Chapter 9: chapter 8 closed the 0x1F80_xxxx hole. The first-
|
||||
// unmapped observer in tb_ee_core_bios_smoke then showed the next
|
||||
// unmapped event was a WRITE at 0xFFFE_0130 (pc=0xBFC0_21BC,
|
||||
// cycle 808). Multiple more writes to that same offset fire later
|
||||
// with values 0xCC4, 0xCC0, 0x1E988, 0xC04, 0x3202_000F —
|
||||
// classic cache/BIU config dance. Without a stub, these writes
|
||||
// land as UNMAPPED events; the first one reads back to this stub
|
||||
// would return 0xDEADBEEF and re-poison the pointer chain chapter
|
||||
// 8 just cleaned up.
|
||||
//
|
||||
// Codex's call for chapter 9: give this its own dedicated stub
|
||||
// with its own region tag, NOT a broad "everything else" fallback.
|
||||
// Keep architecturally distinct surfaces distinct. If the BIOS
|
||||
// later touches 0x1FA0_0000 (next unmapped in the observer), that
|
||||
// will be its own chapter, not folded in here.
|
||||
//
|
||||
// Semantics (same shape as ee_bootstrap_mmio_stub):
|
||||
// - 4 KiB window = 1024 × 32-bit latched registers, zero-init.
|
||||
// - Writes latch per-byte: for each `wr_be[i]`, byte[i] of the
|
||||
// addressed register updates; untouched lanes preserve their
|
||||
// prior value. Makes SB/SH through the window safe.
|
||||
// - Reads return currently-latched value, one-cycle latency.
|
||||
// - No side effects. BIOS read-modify-write sequences stay
|
||||
// self-consistent.
|
||||
//
|
||||
// Size cost: 1024 × 32 bits = 4 KiB sim memory. Negligible.
|
||||
//
|
||||
// Trace: per-access event on SUBSYS_MEM with region tag
|
||||
// `REGION_EE_BIU = 10` (distinct from REGION_EE_MISC_MMIO=9 so
|
||||
// post-run analysis can separate the two windows).
|
||||
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module ee_biu_mmio_stub
|
||||
import trace_pkg::*;
|
||||
(
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
|
||||
// Write port — 12-bit offset within the 4 KiB window
|
||||
input logic reg_wr_en,
|
||||
input logic [11:0] reg_wr_addr,
|
||||
input logic [31:0] reg_wr_data,
|
||||
input logic [3:0] reg_wr_be,
|
||||
|
||||
// Read port — 1-cycle latency
|
||||
input logic reg_rd_en,
|
||||
input logic [11:0] reg_rd_addr,
|
||||
output logic [31:0] reg_rd_data,
|
||||
output logic reg_rd_valid,
|
||||
|
||||
// Trace
|
||||
output logic ev_valid,
|
||||
output subsys_e ev_subsys,
|
||||
output event_e ev_event,
|
||||
output logic [63:0] ev_arg0,
|
||||
output logic [63:0] ev_arg1,
|
||||
output logic [63:0] ev_arg2,
|
||||
output logic [63:0] ev_arg3,
|
||||
output logic [31:0] ev_flags
|
||||
);
|
||||
|
||||
localparam int WORDS = 1024; // 4 KiB / 4
|
||||
localparam logic [63:0] REGION_EE_BIU = 64'd10;
|
||||
|
||||
logic [31:0] regs [0:WORDS-1];
|
||||
|
||||
initial begin
|
||||
for (int i = 0; i < WORDS; i++) regs[i] = 32'd0;
|
||||
end
|
||||
|
||||
logic [9:0] wr_idx;
|
||||
logic [9:0] rd_idx;
|
||||
assign wr_idx = reg_wr_addr[11:2];
|
||||
assign rd_idx = reg_rd_addr[11:2];
|
||||
|
||||
// Per-byte write latch
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst_n && reg_wr_en) begin
|
||||
if (reg_wr_be[0]) regs[wr_idx][ 7: 0] <= reg_wr_data[ 7: 0];
|
||||
if (reg_wr_be[1]) regs[wr_idx][15: 8] <= reg_wr_data[15: 8];
|
||||
if (reg_wr_be[2]) regs[wr_idx][23:16] <= reg_wr_data[23:16];
|
||||
if (reg_wr_be[3]) regs[wr_idx][31:24] <= reg_wr_data[31:24];
|
||||
end
|
||||
end
|
||||
|
||||
// Read — 1-cycle latency
|
||||
always_ff @(posedge clk) begin
|
||||
if (!rst_n) begin
|
||||
reg_rd_data <= 32'd0;
|
||||
reg_rd_valid <= 1'b0;
|
||||
end else begin
|
||||
reg_rd_valid <= reg_rd_en;
|
||||
if (reg_rd_en) reg_rd_data <= regs[rd_idx];
|
||||
end
|
||||
end
|
||||
|
||||
// Trace — write wins same-cycle collision (defensive; map enforces
|
||||
// mutual exclusion)
|
||||
always_ff @(posedge clk) begin
|
||||
if (!rst_n) begin
|
||||
ev_valid <= 1'b0;
|
||||
ev_subsys <= SUBSYS_MEM;
|
||||
ev_event <= EV_WRITE;
|
||||
ev_arg0 <= 64'd0;
|
||||
ev_arg1 <= 64'd0;
|
||||
ev_arg2 <= 64'd0;
|
||||
ev_arg3 <= 64'd0;
|
||||
ev_flags <= 32'd0;
|
||||
end else if (reg_wr_en) begin
|
||||
ev_valid <= 1'b1;
|
||||
ev_subsys <= SUBSYS_MEM;
|
||||
ev_event <= EV_WRITE;
|
||||
ev_arg0 <= {52'd0, reg_wr_addr};
|
||||
ev_arg1 <= {32'd0, reg_wr_data};
|
||||
ev_arg2 <= {60'd0, reg_wr_be};
|
||||
ev_arg3 <= REGION_EE_BIU;
|
||||
ev_flags <= 32'h0000_0001;
|
||||
end else if (reg_rd_en) begin
|
||||
ev_valid <= 1'b1;
|
||||
ev_subsys <= SUBSYS_MEM;
|
||||
ev_event <= EV_READ;
|
||||
ev_arg0 <= {52'd0, reg_rd_addr};
|
||||
ev_arg1 <= {32'd0, regs[rd_idx]};
|
||||
ev_arg2 <= 64'd0;
|
||||
ev_arg3 <= REGION_EE_BIU;
|
||||
ev_flags <= 32'd0;
|
||||
end else begin
|
||||
ev_valid <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule : ee_biu_mmio_stub
|
||||
Reference in New Issue
Block a user