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>
231 lines
8.8 KiB
Systemverilog
231 lines
8.8 KiB
Systemverilog
// retroDE_ps2 — sif_mailbox_stub
|
|
//
|
|
// Minimal EE↔IOP subsystem-interface mailbox shell. First stub on the SIF
|
|
// track. Standalone unit — does not yet integrate with any live IOP core.
|
|
// Testbenches drive both the EE-side port and the IOP-side port directly,
|
|
// playing both roles, to prove the register semantics without requiring
|
|
// a full dual-CPU bring-up.
|
|
//
|
|
// Contract refs:
|
|
// docs/stub_module_plan.md (Wave 2, item 10)
|
|
// docs/contracts/sif.md (mailbox/flag-only stub is allowed here)
|
|
//
|
|
// Register surface (offsets within the SIF block):
|
|
// 0x00 MSCOM — 32-bit mailbox, conventionally EE→IOP
|
|
// 0x10 SMCOM — 32-bit mailbox, conventionally IOP→EE
|
|
// 0x20 MSFLG — 32-bit flag word, conventionally EE-owned for set,
|
|
// IOP-owned for clear (directional semantics deferred)
|
|
// 0x30 SMFLG — 32-bit flag word, conventionally IOP-owned for set,
|
|
// EE-owned for clear (directional semantics deferred)
|
|
//
|
|
// Wave 2 scope intentionally does NOT enforce direction or set/clear
|
|
// semantics. Both ports can read and write any register with plain
|
|
// replace-on-write. The trace records which side initiated each access
|
|
// (side_id in arg2) so future-wave work can layer directional rules on
|
|
// top without changing the storage model.
|
|
//
|
|
// Port semantics:
|
|
// Each side (EE / IOP) has an independent register port:
|
|
// wr_en, rd_en, addr[7:0], wr_data[31:0], rd_data[31:0], rd_valid
|
|
// Reads have 1-cycle latency to match the existing stub ecosystem.
|
|
//
|
|
// Write arbitration (per-register):
|
|
// - EE and IOP writes to *different* registers on the same cycle both
|
|
// land. Storage is not serialized across independent registers.
|
|
// - EE and IOP writes to the *same* register on the same cycle: EE
|
|
// wins, IOP write is dropped that cycle.
|
|
// - Trace is limited to one event per cycle by the shared trace bus
|
|
// (priority EE > IOP). An IOP write that lands silently when EE is
|
|
// driving a different register will not be traced this wave — future
|
|
// waves can add a second trace output port if that becomes a gap.
|
|
//
|
|
// Trace payload schema (SUBSYS_SIF, existing EV_READ/EV_WRITE codes):
|
|
// SIF WRITE arg0=offset arg1=data arg2=side_id arg3=0 flags[0]=1
|
|
// SIF READ arg0=offset arg1=data arg2=side_id arg3=0 flags[0]=0
|
|
// side_id: 0 = EE, 1 = IOP
|
|
//
|
|
// Trace priority on same cycle: EE write > IOP write > EE read > IOP read.
|
|
// In practice TBs drive at most one operation per cycle.
|
|
|
|
`timescale 1ns/1ps
|
|
|
|
module sif_mailbox_stub
|
|
import trace_pkg::*;
|
|
(
|
|
input logic clk,
|
|
input logic rst_n,
|
|
|
|
// EE-side register port
|
|
input logic ee_wr_en,
|
|
input logic ee_rd_en,
|
|
input logic [7:0] ee_addr,
|
|
input logic [31:0] ee_wr_data,
|
|
output logic [31:0] ee_rd_data,
|
|
output logic ee_rd_valid,
|
|
|
|
// IOP-side register port
|
|
input logic iop_wr_en,
|
|
input logic iop_rd_en,
|
|
input logic [7:0] iop_addr,
|
|
input logic [31:0] iop_wr_data,
|
|
output logic [31:0] iop_rd_data,
|
|
output logic iop_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 logic [7:0] MSCOM_OFF = 8'h00;
|
|
localparam logic [7:0] SMCOM_OFF = 8'h10;
|
|
localparam logic [7:0] MSFLG_OFF = 8'h20;
|
|
localparam logic [7:0] SMFLG_OFF = 8'h30;
|
|
|
|
localparam logic [63:0] SIDE_EE = 64'd0;
|
|
localparam logic [63:0] SIDE_IOP = 64'd1;
|
|
|
|
// ------------------------------------------------------------------
|
|
// Register file
|
|
// ------------------------------------------------------------------
|
|
|
|
logic [31:0] mscom;
|
|
logic [31:0] smcom;
|
|
logic [31:0] msflg;
|
|
logic [31:0] smflg;
|
|
|
|
function automatic logic [31:0] select_reg(input logic [7:0] offset,
|
|
input logic [31:0] mscom_v,
|
|
input logic [31:0] smcom_v,
|
|
input logic [31:0] msflg_v,
|
|
input logic [31:0] smflg_v);
|
|
case (offset)
|
|
MSCOM_OFF: select_reg = mscom_v;
|
|
SMCOM_OFF: select_reg = smcom_v;
|
|
MSFLG_OFF: select_reg = msflg_v;
|
|
SMFLG_OFF: select_reg = smflg_v;
|
|
default: select_reg = 32'hDEAD_BEEF;
|
|
endcase
|
|
endfunction
|
|
|
|
// Per-register write arbitration: EE wins on same-register collision,
|
|
// but writes to different registers land independently.
|
|
logic ee_hits_mscom, ee_hits_smcom, ee_hits_msflg, ee_hits_smflg;
|
|
logic iop_hits_mscom, iop_hits_smcom, iop_hits_msflg, iop_hits_smflg;
|
|
|
|
assign ee_hits_mscom = ee_wr_en && (ee_addr == MSCOM_OFF);
|
|
assign ee_hits_smcom = ee_wr_en && (ee_addr == SMCOM_OFF);
|
|
assign ee_hits_msflg = ee_wr_en && (ee_addr == MSFLG_OFF);
|
|
assign ee_hits_smflg = ee_wr_en && (ee_addr == SMFLG_OFF);
|
|
|
|
assign iop_hits_mscom = iop_wr_en && (iop_addr == MSCOM_OFF);
|
|
assign iop_hits_smcom = iop_wr_en && (iop_addr == SMCOM_OFF);
|
|
assign iop_hits_msflg = iop_wr_en && (iop_addr == MSFLG_OFF);
|
|
assign iop_hits_smflg = iop_wr_en && (iop_addr == SMFLG_OFF);
|
|
|
|
always_ff @(posedge clk) begin
|
|
if (!rst_n) begin
|
|
mscom <= 32'd0;
|
|
smcom <= 32'd0;
|
|
msflg <= 32'd0;
|
|
smflg <= 32'd0;
|
|
end else begin
|
|
if (ee_hits_mscom) mscom <= ee_wr_data;
|
|
else if (iop_hits_mscom) mscom <= iop_wr_data;
|
|
|
|
if (ee_hits_smcom) smcom <= ee_wr_data;
|
|
else if (iop_hits_smcom) smcom <= iop_wr_data;
|
|
|
|
if (ee_hits_msflg) msflg <= ee_wr_data;
|
|
else if (iop_hits_msflg) msflg <= iop_wr_data;
|
|
|
|
if (ee_hits_smflg) smflg <= ee_wr_data;
|
|
else if (iop_hits_smflg) smflg <= iop_wr_data;
|
|
end
|
|
end
|
|
|
|
// ------------------------------------------------------------------
|
|
// Reads (1-cycle latency, both ports independent)
|
|
// ------------------------------------------------------------------
|
|
|
|
always_ff @(posedge clk) begin
|
|
if (!rst_n) begin
|
|
ee_rd_data <= 32'd0;
|
|
ee_rd_valid <= 1'b0;
|
|
iop_rd_data <= 32'd0;
|
|
iop_rd_valid <= 1'b0;
|
|
end else begin
|
|
ee_rd_valid <= ee_rd_en;
|
|
if (ee_rd_en)
|
|
ee_rd_data <= select_reg(ee_addr, mscom, smcom, msflg, smflg);
|
|
|
|
iop_rd_valid <= iop_rd_en;
|
|
if (iop_rd_en)
|
|
iop_rd_data <= select_reg(iop_addr, mscom, smcom, msflg, smflg);
|
|
end
|
|
end
|
|
|
|
// ------------------------------------------------------------------
|
|
// Trace emission — priority EE_wr > IOP_wr > EE_rd > IOP_rd.
|
|
// Reads emit with the data that will be delivered next cycle, keeping
|
|
// the trace line self-consistent.
|
|
// ------------------------------------------------------------------
|
|
|
|
always_ff @(posedge clk) begin
|
|
if (!rst_n) begin
|
|
ev_valid <= 1'b0;
|
|
ev_subsys <= SUBSYS_SIF;
|
|
ev_event <= EV_READ;
|
|
ev_arg0 <= 64'd0;
|
|
ev_arg1 <= 64'd0;
|
|
ev_arg2 <= 64'd0;
|
|
ev_arg3 <= 64'd0;
|
|
ev_flags <= 32'd0;
|
|
end else if (ee_wr_en) begin
|
|
ev_valid <= 1'b1;
|
|
ev_subsys <= SUBSYS_SIF;
|
|
ev_event <= EV_WRITE;
|
|
ev_arg0 <= {56'd0, ee_addr};
|
|
ev_arg1 <= {32'd0, ee_wr_data};
|
|
ev_arg2 <= SIDE_EE;
|
|
ev_arg3 <= 64'd0;
|
|
ev_flags <= 32'h0000_0001;
|
|
end else if (iop_wr_en) begin
|
|
ev_valid <= 1'b1;
|
|
ev_subsys <= SUBSYS_SIF;
|
|
ev_event <= EV_WRITE;
|
|
ev_arg0 <= {56'd0, iop_addr};
|
|
ev_arg1 <= {32'd0, iop_wr_data};
|
|
ev_arg2 <= SIDE_IOP;
|
|
ev_arg3 <= 64'd0;
|
|
ev_flags <= 32'h0000_0001;
|
|
end else if (ee_rd_en) begin
|
|
ev_valid <= 1'b1;
|
|
ev_subsys <= SUBSYS_SIF;
|
|
ev_event <= EV_READ;
|
|
ev_arg0 <= {56'd0, ee_addr};
|
|
ev_arg1 <= {32'd0, select_reg(ee_addr, mscom, smcom, msflg, smflg)};
|
|
ev_arg2 <= SIDE_EE;
|
|
ev_arg3 <= 64'd0;
|
|
ev_flags <= 32'd0;
|
|
end else if (iop_rd_en) begin
|
|
ev_valid <= 1'b1;
|
|
ev_subsys <= SUBSYS_SIF;
|
|
ev_event <= EV_READ;
|
|
ev_arg0 <= {56'd0, iop_addr};
|
|
ev_arg1 <= {32'd0, select_reg(iop_addr, mscom, smcom, msflg, smflg)};
|
|
ev_arg2 <= SIDE_IOP;
|
|
ev_arg3 <= 64'd0;
|
|
ev_flags <= 32'd0;
|
|
end else begin
|
|
ev_valid <= 1'b0;
|
|
end
|
|
end
|
|
|
|
endmodule : sif_mailbox_stub
|