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>
182 lines
6.8 KiB
Systemverilog
182 lines
6.8 KiB
Systemverilog
// retroDE_ps2 — ee_dmac_passive_chan_stub
|
||
//
|
||
// Ch288 — Lightweight per-channel register surface for the EE DMAC
|
||
// channels NOT covered by a dedicated transfer-FSM stub. Hosts the
|
||
// four standard per-channel registers (CHCR/MADR/QWC/TADR) for each
|
||
// covered channel; reset to zero, writes latch, reads return the
|
||
// latched value. NO transfer FSM, NO start-bit side effects, NO
|
||
// D_STAT interaction. This is the "init-time channel-clear / quiet
|
||
// register surface" Codex framed for Ch288.
|
||
//
|
||
// Channels covered (4 KiB window each, starting at 0x1000_8000):
|
||
// ch0 (VIF0) 0x1000_8000-0x1000_8FFF
|
||
// ch1 (VIF1) 0x1000_9000-0x1000_9FFF
|
||
// ch3 (IPU_FROM) 0x1000_B000-0x1000_BFFF
|
||
// ch4 (IPU_TO) 0x1000_C000-0x1000_CFFF ← qbert's first hit
|
||
// ch5 (SIF0) 0x1000_D000-0x1000_DFFF
|
||
//
|
||
// SKIPPED:
|
||
// ch2 (GIF) 0x1000_A000-0x1000_AFFF — already routed
|
||
// externally to dmac_reg_stub via the map's
|
||
// ee_dmac_ch2_* ports. Do NOT shadow it here.
|
||
//
|
||
// Channel index extracted from chan_addr[15:12]:
|
||
// 0x8 → ch0, 0x9 → ch1, 0xB → ch3, 0xC → ch4, 0xD → ch5
|
||
// (0xA / ch2 is filtered by the caller; if chan_addr[15:12]==0xA
|
||
// arrives here the module silently drops it.)
|
||
//
|
||
// Register offsets (chan_addr[11:0], matches dmac_reg_stub layout):
|
||
// 0x00 CHCR — control (start bit at [0]); latched, no FSM
|
||
// 0x10 MADR — main address
|
||
// 0x20 QWC — quadword count
|
||
// 0x30 TADR — tag address
|
||
// any other offset: read = 0, write dropped + traced
|
||
|
||
`timescale 1ns/1ps
|
||
|
||
module ee_dmac_passive_chan_stub
|
||
import trace_pkg::*;
|
||
(
|
||
input logic clk,
|
||
input logic rst_n,
|
||
|
||
// Write port. chan_addr is the 16-bit offset into the entire
|
||
// 0x1000_8000-base window: chan_addr[15:12] = channel selector,
|
||
// chan_addr[11:0] = register offset within that channel.
|
||
input logic reg_wr_en,
|
||
input logic [15:0] chan_addr,
|
||
input logic [31:0] reg_wr_data,
|
||
|
||
// Read port (1-cycle latency).
|
||
input logic reg_rd_en,
|
||
output logic [31:0] reg_rd_data,
|
||
output logic reg_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 [11:0] CHCR_OFFSET = 12'h000;
|
||
localparam logic [11:0] MADR_OFFSET = 12'h010;
|
||
localparam logic [11:0] QWC_OFFSET = 12'h020;
|
||
localparam logic [11:0] TADR_OFFSET = 12'h030;
|
||
|
||
// Channel index from the high nibble of chan_addr. Out-of-range
|
||
// nibbles (anything outside 0x8/0x9/0xB/0xC/0xD) get
|
||
// chan_valid=0 and the access is dropped.
|
||
logic [3:0] chan_nibble;
|
||
logic [2:0] chan_idx; // 0..4 packed: 0=ch0, 1=ch1, 2=ch3, 3=ch4, 4=ch5
|
||
logic chan_valid;
|
||
always_comb begin
|
||
chan_nibble = chan_addr[15:12];
|
||
chan_idx = 3'd0;
|
||
chan_valid = 1'b0;
|
||
unique case (chan_nibble)
|
||
4'h8: begin chan_idx = 3'd0; chan_valid = 1'b1; end // ch0
|
||
4'h9: begin chan_idx = 3'd1; chan_valid = 1'b1; end // ch1
|
||
4'hB: begin chan_idx = 3'd2; chan_valid = 1'b1; end // ch3
|
||
4'hC: begin chan_idx = 3'd3; chan_valid = 1'b1; end // ch4
|
||
4'hD: begin chan_idx = 3'd4; chan_valid = 1'b1; end // ch5
|
||
default: ;
|
||
endcase
|
||
end
|
||
|
||
logic [11:0] reg_offset;
|
||
assign reg_offset = chan_addr[11:0];
|
||
|
||
// ------------------------------------------------------------------
|
||
// Register file: 5 channels × 4 registers
|
||
// ------------------------------------------------------------------
|
||
logic [31:0] chcr [0:4];
|
||
logic [31:0] madr [0:4];
|
||
logic [31:0] qwc [0:4];
|
||
logic [31:0] tadr [0:4];
|
||
|
||
always_ff @(posedge clk) begin
|
||
if (!rst_n) begin
|
||
for (int i = 0; i < 5; i++) begin
|
||
chcr[i] <= 32'd0;
|
||
madr[i] <= 32'd0;
|
||
qwc[i] <= 32'd0;
|
||
tadr[i] <= 32'd0;
|
||
end
|
||
end else if (reg_wr_en && chan_valid) begin
|
||
unique case (reg_offset)
|
||
CHCR_OFFSET: chcr[chan_idx] <= reg_wr_data;
|
||
MADR_OFFSET: madr[chan_idx] <= reg_wr_data;
|
||
QWC_OFFSET: qwc[chan_idx] <= reg_wr_data;
|
||
TADR_OFFSET: tadr[chan_idx] <= reg_wr_data;
|
||
default: ;
|
||
endcase
|
||
end
|
||
end
|
||
|
||
// Read mux (1-cycle latency). Returns 0 for invalid channel /
|
||
// unknown offset.
|
||
always_ff @(posedge clk) begin
|
||
if (!rst_n) begin
|
||
reg_rd_data <= 32'd0;
|
||
reg_rd_valid <= 1'b0;
|
||
end else begin
|
||
reg_rd_valid <= reg_rd_en;
|
||
if (reg_rd_en && chan_valid) begin
|
||
unique case (reg_offset)
|
||
CHCR_OFFSET: reg_rd_data <= chcr[chan_idx];
|
||
MADR_OFFSET: reg_rd_data <= madr[chan_idx];
|
||
QWC_OFFSET: reg_rd_data <= qwc[chan_idx];
|
||
TADR_OFFSET: reg_rd_data <= tadr[chan_idx];
|
||
default: reg_rd_data <= 32'd0;
|
||
endcase
|
||
end else if (reg_rd_en) begin
|
||
reg_rd_data <= 32'd0; // invalid channel
|
||
end
|
||
end
|
||
end
|
||
|
||
// ------------------------------------------------------------------
|
||
// Trace — write priority over read; tagged SUBSYS_DMAC with
|
||
// arg0 = chan_nibble (0x8/0x9/0xB/0xC/0xD = phys channel), arg1
|
||
// = data, arg2 = reg_offset, arg3 = chan_idx (packed 0..4).
|
||
// ------------------------------------------------------------------
|
||
always_ff @(posedge clk) begin
|
||
if (!rst_n) begin
|
||
ev_valid <= 1'b0;
|
||
ev_subsys <= SUBSYS_DMAC;
|
||
ev_event <= EV_WRITE;
|
||
ev_arg0 <= 64'd0;
|
||
ev_arg1 <= 64'd0;
|
||
ev_arg2 <= 64'd0;
|
||
ev_arg3 <= 64'd0;
|
||
ev_flags <= 32'd0;
|
||
end else if (reg_wr_en) begin
|
||
ev_valid <= 1'b1;
|
||
ev_subsys <= SUBSYS_DMAC;
|
||
ev_event <= EV_WRITE;
|
||
ev_arg0 <= {60'd0, chan_nibble};
|
||
ev_arg1 <= {32'd0, reg_wr_data};
|
||
ev_arg2 <= {52'd0, reg_offset};
|
||
ev_arg3 <= {61'd0, chan_idx};
|
||
ev_flags <= {31'd0, chan_valid};
|
||
end else if (reg_rd_en) begin
|
||
ev_valid <= 1'b1;
|
||
ev_subsys <= SUBSYS_DMAC;
|
||
ev_event <= EV_READ;
|
||
ev_arg0 <= {60'd0, chan_nibble};
|
||
ev_arg1 <= 64'd0;
|
||
ev_arg2 <= {52'd0, reg_offset};
|
||
ev_arg3 <= {61'd0, chan_idx};
|
||
ev_flags <= {31'd0, chan_valid};
|
||
end else begin
|
||
ev_valid <= 1'b0;
|
||
end
|
||
end
|
||
|
||
endmodule : ee_dmac_passive_chan_stub
|