Files
retroDE_ps2/rtl/sif/boot_install_agent_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

218 lines
8.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 — boot_install_agent_stub (Ch55 / Ch56)
//
// Minimal external producer that streams a coordinated low-RAM handler
// image into EE RAM through the SIF EE-RAM bridge. Emits 32-bit beats
// on a ready/valid handshake compatible with sif_dma_ee_ram_bridge_stub.
//
// NOT an IOP, NOT a full boot firmware. This is the thinnest possible
// stand-in for "whatever on real PS2 populates EE useg [0x80..0x1FF]
// with exception-entry + safe-return stubs before the EE starts
// faulting" (IOP→EE SIF DMA, BootROM/CDVD handoff, etc.). The point
// is to validate the transport path and the coordinated-install
// thesis, not to model the producer's identity.
//
// Payload source (Ch56):
// USE_IMAGE_FILE=0 (default) — built-in Ch54 image, hardcoded below
// USE_IMAGE_FILE=1 — $readmemh(IMAGE_FILE, payload) once
// at sim start, expects TOTAL_WORDS
// hex words
// Transport (timing, handshake, trace) is identical across both modes.
//
// Built-in image (USE_IMAGE_FILE=0):
// word[0..3] → AdES handler at useg 0x80..0x8C:
// MFC0 $26, $14 (32'h401A7000)
// ADDIU $26, $26, 4 (32'h275A0004)
// JR $26 (32'h03400008)
// RFE (32'h42000010)
// word[4..95] → 46× (JR $31; NOP) safe-return pairs covering
// useg 0x90..0x1FC.
//
// Downstream contract (matches sif_dma_ee_ram_bridge_stub upstream):
// out_valid / out_data[31:0] / out_last / out_ready
// out_last asserted on the final word. One-beat-per-cycle while
// out_ready is high.
//
// Trace:
// SUBSYS_SIF / EV_DMA_START once on go.
// SUBSYS_SIF / EV_DMA_BEAT per accepted beat.
// arg0 = word index, arg1 = word data, arg2 = MASTER_ID,
// arg3 = TOTAL_WORDS, flags bit0 = out_last.
// SUBSYS_SIF / EV_DMA_DONE once on completion.
`timescale 1ns/1ps
module boot_install_agent_stub
import trace_pkg::*;
#(
parameter int TOTAL_WORDS = 96,
parameter logic [7:0] MASTER_ID = 8'd6, // install agent
parameter bit USE_IMAGE_FILE = 1'b0, // 0: built-in ROM, 1: $readmemh
parameter string IMAGE_FILE = ""
) (
input logic clk,
input logic rst_n,
input logic go_i,
output logic out_valid,
output logic [31:0] out_data,
output logic out_last,
input logic out_ready,
output logic busy_o,
output logic done_o,
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
);
// ------------------------------------------------------------------
// Payload ROM
// ------------------------------------------------------------------
// The 4-word exception-return stub at [0..3] plus (JR $31; NOP)
// pairs filling the rest requires TOTAL_WORDS to be even and at
// least 4. Guard explicitly (Ch55 audit low-1): odd values would
// walk past the array end in the pair loop.
initial begin
if (TOTAL_WORDS < 4 || (TOTAL_WORDS & 1) != 0) begin
$fatal(1, "boot_install_agent_stub: TOTAL_WORDS must be even and >= 4, got %0d",
TOTAL_WORDS);
end
end
logic [31:0] payload [0:TOTAL_WORDS-1];
initial begin
if (USE_IMAGE_FILE) begin
if (IMAGE_FILE == "") begin
$fatal(1, "boot_install_agent_stub: USE_IMAGE_FILE=1 but IMAGE_FILE is empty");
end
$readmemh(IMAGE_FILE, payload);
end else begin
payload[0] = 32'h401A7000; // MFC0 $26, $14
payload[1] = 32'h275A0004; // ADDIU $26, $26, 4
payload[2] = 32'h03400008; // JR $26
payload[3] = 32'h42000010; // RFE (delay slot)
for (int i = 4; i < TOTAL_WORDS; i = i + 2) begin
payload[i] = 32'h03E00008; // JR $31
payload[i + 1] = 32'h00000000; // NOP
end
end
end
// ------------------------------------------------------------------
// Streaming FSM
// ------------------------------------------------------------------
typedef enum logic [1:0] {
S_IDLE = 2'd0,
S_STREAM = 2'd1,
S_DONE = 2'd2
} state_e;
state_e state;
logic [31:0] idx; // next word to emit
logic accept_beat;
assign accept_beat = out_valid && out_ready;
assign out_valid = (state == S_STREAM);
assign out_data = (state == S_STREAM) ? payload[idx[$clog2(TOTAL_WORDS)-1:0]]
: 32'd0;
assign out_last = (state == S_STREAM) && (idx == TOTAL_WORDS - 1);
assign busy_o = (state == S_STREAM);
assign done_o = (state == S_DONE);
always_ff @(posedge clk) begin
if (!rst_n) begin
state <= S_IDLE;
idx <= 32'd0;
end else begin
unique case (state)
S_IDLE: begin
if (go_i) begin
state <= S_STREAM;
idx <= 32'd0;
end
end
S_STREAM: begin
if (accept_beat) begin
if (idx == TOTAL_WORDS - 1) state <= S_DONE;
else idx <= idx + 32'd1;
end
end
S_DONE: ; // terminal
default: state <= S_IDLE;
endcase
end
end
// ------------------------------------------------------------------
// Trace
// ------------------------------------------------------------------
// START fires combinationally on the cycle the caller pulses go_i
// while we're still in S_IDLE. That cycle has out_valid=0 and
// accept_beat=0, so the event doesn't compete with a BEAT event
// in the priority if-else below (the bug pre-fix: flopping
// go_latched delayed START onto the same cycle as beat 0, dropping
// one of the two).
logic go_pulse;
assign go_pulse = (state == S_IDLE) && go_i;
logic done_edge;
state_e state_prev;
always_ff @(posedge clk) begin
if (!rst_n) state_prev <= S_IDLE;
else state_prev <= state;
end
assign done_edge = (state == S_DONE) && (state_prev != S_DONE);
always_ff @(posedge clk) begin
if (!rst_n) begin
ev_valid <= 1'b0;
ev_subsys <= SUBSYS_SIF;
ev_event <= EV_DMA_START;
ev_arg0 <= 64'd0;
ev_arg1 <= 64'd0;
ev_arg2 <= 64'd0;
ev_arg3 <= 64'd0;
ev_flags <= 32'd0;
end else if (go_pulse) begin
ev_valid <= 1'b1;
ev_subsys <= SUBSYS_SIF;
ev_event <= EV_DMA_START;
ev_arg0 <= 64'd0;
ev_arg1 <= 64'd0;
ev_arg2 <= {56'd0, MASTER_ID};
ev_arg3 <= 64'(TOTAL_WORDS);
ev_flags <= 32'd0;
end else if (accept_beat) begin
ev_valid <= 1'b1;
ev_subsys <= SUBSYS_SIF;
ev_event <= EV_DMA_BEAT;
ev_arg0 <= {32'd0, idx};
ev_arg1 <= {32'd0, out_data};
ev_arg2 <= {56'd0, MASTER_ID};
ev_arg3 <= 64'(TOTAL_WORDS);
ev_flags <= {31'd0, out_last};
end else if (done_edge) begin
ev_valid <= 1'b1;
ev_subsys <= SUBSYS_SIF;
ev_event <= EV_DMA_DONE;
ev_arg0 <= 64'(TOTAL_WORDS);
ev_arg1 <= 64'd0;
ev_arg2 <= {56'd0, MASTER_ID};
ev_arg3 <= 64'(TOTAL_WORDS);
ev_flags <= 32'd0;
end else begin
ev_valid <= 1'b0;
end
end
endmodule : boot_install_agent_stub