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,139 @@
|
||||
// retroDE_ps2 — sif_dma_iop_ram_bridge_stub
|
||||
//
|
||||
// Width-adapting bridge from a 128-bit SIF DMA endpoint to the 32-bit
|
||||
// IOP memory map. Splits each incoming qword into four 32-bit writes at
|
||||
// consecutive physical addresses starting from DEST_BASE_ADDR.
|
||||
//
|
||||
// First real coupling between the SIF data-plane and the IOP side. NOT an
|
||||
// IOP, not a DMAC, not a peer — just a width/ordering adapter.
|
||||
//
|
||||
// Contract refs:
|
||||
// docs/contracts/sif.md (DMA-linked data movement endpoints)
|
||||
// docs/contracts/iop.md (IOP-local RAM/I/O decode; writes land
|
||||
// through the IOP memory map)
|
||||
//
|
||||
// Handshake (upstream, from DMAC ep_* port or equivalent):
|
||||
// in_valid / in_data[127:0] / in_last / in_ready
|
||||
// Bridge asserts in_ready only while idle. During the four-write
|
||||
// expansion of a qword, in_ready drops — natural backpressure onto
|
||||
// whatever's producing qwords.
|
||||
//
|
||||
// Handshake (downstream, to iop_memory_map_stub's bridge-write port):
|
||||
// bridge_wr_en / bridge_wr_addr[31:0] / bridge_wr_data[31:0] /
|
||||
// bridge_wr_be[3:0] / bridge_master_id[7:0]
|
||||
// Addresses are physical (no kseg stripping) — the IOP map must treat
|
||||
// this port's addresses differently from its CPU-side port.
|
||||
//
|
||||
// Data layout:
|
||||
// Little-endian unpacking: in_data[31:0] -> DEST_BASE+0
|
||||
// in_data[63:32] -> DEST_BASE+4
|
||||
// in_data[95:64] -> DEST_BASE+8
|
||||
// in_data[127:96] -> DEST_BASE+12
|
||||
// Subsequent qwords append: DEST_BASE+16, +20, +24, +28, ...
|
||||
//
|
||||
// Parameters:
|
||||
// DEST_BASE_ADDR — where the bridge starts writing. Persistent across
|
||||
// the life of the transfer; would become a register in
|
||||
// a later wave where software programs the target.
|
||||
// MASTER_ID — bridge's identity in MEM / IOP traces (default 3,
|
||||
// distinct from EE IFETCH=0, DMAC=1, IOP_CPU=2).
|
||||
//
|
||||
// Non-goals:
|
||||
// - multiple in-flight qwords
|
||||
// - ack back upstream beyond in_ready / in_last observation
|
||||
// - byte-enable variation per write (all writes are full 32-bit)
|
||||
// - arbitration against other masters on the map's write path
|
||||
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module sif_dma_iop_ram_bridge_stub
|
||||
#(
|
||||
parameter logic [31:0] DEST_BASE_ADDR = 32'h0000_0000,
|
||||
parameter logic [7:0] MASTER_ID = 8'd3
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
|
||||
// Upstream (DMAC endpoint side)
|
||||
input logic in_valid,
|
||||
input logic [127:0] in_data,
|
||||
input logic in_last,
|
||||
output logic in_ready,
|
||||
|
||||
// Downstream (IOP map bridge-write port)
|
||||
output logic bridge_wr_en,
|
||||
output logic [31:0] bridge_wr_addr,
|
||||
output logic [31:0] bridge_wr_data,
|
||||
output logic [3:0] bridge_wr_be,
|
||||
output logic [7:0] bridge_master_id
|
||||
);
|
||||
|
||||
typedef enum logic [1:0] {
|
||||
S_IDLE = 2'd0,
|
||||
S_WRITE = 2'd1
|
||||
} state_e;
|
||||
|
||||
state_e state;
|
||||
logic [127:0] latched_qword;
|
||||
logic [1:0] beat_index; // 0..3 across the 4 writes
|
||||
logic [31:0] wr_offset; // running byte offset
|
||||
|
||||
assign in_ready = (state == S_IDLE);
|
||||
assign bridge_master_id = MASTER_ID;
|
||||
|
||||
logic accept_new_qword;
|
||||
assign accept_new_qword = in_valid && in_ready;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// State machine
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (!rst_n) begin
|
||||
state <= S_IDLE;
|
||||
latched_qword <= 128'd0;
|
||||
beat_index <= 2'd0;
|
||||
wr_offset <= 32'd0;
|
||||
end else begin
|
||||
unique case (state)
|
||||
S_IDLE: begin
|
||||
if (accept_new_qword) begin
|
||||
latched_qword <= in_data;
|
||||
beat_index <= 2'd0;
|
||||
state <= S_WRITE;
|
||||
end
|
||||
end
|
||||
|
||||
S_WRITE: begin
|
||||
// Each cycle here drives one 32-bit write. After the
|
||||
// fourth, go idle. wr_offset advances per write.
|
||||
wr_offset <= wr_offset + 32'd4;
|
||||
if (beat_index == 2'd3) begin
|
||||
state <= S_IDLE;
|
||||
beat_index <= 2'd0;
|
||||
end else begin
|
||||
beat_index <= beat_index + 2'd1;
|
||||
end
|
||||
end
|
||||
|
||||
default: state <= S_IDLE;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Downstream write-port drive (combinational on state)
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
// Indexed part-select picks the 32-bit slice for the current beat.
|
||||
// Avoids the constant-select-in-always_comb pattern that trips
|
||||
// portability warnings on some simulators.
|
||||
logic [31:0] beat_data;
|
||||
assign beat_data = latched_qword[beat_index*32 +: 32];
|
||||
|
||||
assign bridge_wr_en = (state == S_WRITE);
|
||||
assign bridge_wr_addr = DEST_BASE_ADDR + wr_offset;
|
||||
assign bridge_wr_data = beat_data;
|
||||
assign bridge_wr_be = 4'b1111; // full-word writes only
|
||||
|
||||
endmodule : sif_dma_iop_ram_bridge_stub
|
||||
Reference in New Issue
Block a user