Files
retroDE_ps2/rtl/ee/ee_biu_mmio_stub.sv
thejayman77 ec82764bef 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>
2026-06-29 20:10:50 -04:00

143 lines
5.0 KiB
Systemverilog
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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