// 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