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>
140 lines
5.2 KiB
Systemverilog
140 lines
5.2 KiB
Systemverilog
// 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
|