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>
156 lines
7.7 KiB
Systemverilog
156 lines
7.7 KiB
Systemverilog
// ============================================================================
|
|
// gs_lpddr_rd_arb.sv (Ch320 Brick 2; Ch322 extended 2:1 -> 3:1)
|
|
//
|
|
// 3:1 AXI4 READ-channel arbiter for the FPGA-private LPDDR4B EMIF user port.
|
|
// Lets the Ch320 scanout reader (port 0, priority), the Ch319 HPS read-probe
|
|
// (port 1), and the Ch322 texture-cache fill (port 2, lowest priority) share the
|
|
// single EMIF read channel. The write channel is arbitrated separately
|
|
// (gs_lpddr_wr_arb). Adapted from ao486 axi_fb_arbiter (read half): grant held
|
|
// for a whole transaction, watchdog force-release, idle-drain rready so a late
|
|
// response can't wedge the bus. All single-clock (emif_clk).
|
|
//
|
|
// Port 2 (texture fill) is a ONE-SHOT prefill before raster; scanout (port 0)
|
|
// keeps priority. Leave s2_* unconnected (arvalid=0) on builds without a texture
|
|
// cache — the arbiter is then bit-for-bit the old 2:1 behavior.
|
|
//
|
|
// Single-beat transactions (ARLEN=0), so a response completes on rvalid&rlast.
|
|
// ============================================================================
|
|
`timescale 1ns/1ps
|
|
|
|
module gs_lpddr_rd_arb (
|
|
input logic clk,
|
|
input logic rst_n,
|
|
|
|
// ---- Port 0: scanout reader (priority) ----
|
|
input logic [29:0] s0_araddr,
|
|
input logic [1:0] s0_arburst,
|
|
input logic [6:0] s0_arid,
|
|
input logic [7:0] s0_arlen,
|
|
input logic [2:0] s0_arsize,
|
|
input logic s0_arvalid,
|
|
output logic s0_arready,
|
|
output logic [255:0] s0_rdata,
|
|
output logic [1:0] s0_rresp,
|
|
output logic s0_rlast,
|
|
output logic s0_rvalid,
|
|
input logic s0_rready,
|
|
|
|
// ---- Port 1: HPS read-probe ----
|
|
input logic [29:0] s1_araddr,
|
|
input logic [1:0] s1_arburst,
|
|
input logic [6:0] s1_arid,
|
|
input logic [7:0] s1_arlen,
|
|
input logic [2:0] s1_arsize,
|
|
input logic s1_arvalid,
|
|
output logic s1_arready,
|
|
output logic [255:0] s1_rdata,
|
|
output logic [1:0] s1_rresp,
|
|
output logic s1_rlast,
|
|
output logic s1_rvalid,
|
|
input logic s1_rready,
|
|
|
|
// ---- Port 2: texture-cache fill (lowest priority; Ch322) ----
|
|
input logic [29:0] s2_araddr,
|
|
input logic [1:0] s2_arburst,
|
|
input logic [6:0] s2_arid,
|
|
input logic [7:0] s2_arlen,
|
|
input logic [2:0] s2_arsize,
|
|
input logic s2_arvalid,
|
|
output logic s2_arready,
|
|
output logic [255:0] s2_rdata,
|
|
output logic [1:0] s2_rresp,
|
|
output logic s2_rlast,
|
|
output logic s2_rvalid,
|
|
input logic s2_rready,
|
|
|
|
// ---- Port 3: tile-reload fill (Ch323; priority ABOVE probe/texfill, below scanout) ----
|
|
input logic [29:0] s3_araddr,
|
|
input logic [1:0] s3_arburst,
|
|
input logic [6:0] s3_arid,
|
|
input logic [7:0] s3_arlen,
|
|
input logic [2:0] s3_arsize,
|
|
input logic s3_arvalid,
|
|
output logic s3_arready,
|
|
output logic [255:0] s3_rdata,
|
|
output logic [1:0] s3_rresp,
|
|
output logic s3_rlast,
|
|
output logic s3_rvalid,
|
|
input logic s3_rready,
|
|
|
|
// ---- Master out: EMIF read channel ----
|
|
output logic [29:0] m_araddr,
|
|
output logic [1:0] m_arburst,
|
|
output logic [6:0] m_arid,
|
|
output logic [7:0] m_arlen,
|
|
output logic [2:0] m_arsize,
|
|
output logic m_arvalid,
|
|
input logic m_arready,
|
|
input logic [255:0] m_rdata,
|
|
input logic [1:0] m_rresp,
|
|
input logic m_rlast,
|
|
input logic m_rvalid,
|
|
output logic m_rready
|
|
);
|
|
// grant: 0=idle, 1=s0 scanout, 2=s1 probe, 3=s2 texfill, 4=s3 tile-reload.
|
|
// EXPLICIT priority (Ch323, Codex): scanout > tile_reload > probe > texture_fill — i.e.
|
|
// s0 > s3 > s1 > s2. Render-display (scanout) highest; the render-prep tile reload above
|
|
// the debug read-probe so a debug read can never starve a render's Z/color reload.
|
|
reg [2:0] grant;
|
|
// Ch326 — NON-ABORTING ARBITER (Codex). The OLD design force-released the grant on a
|
|
// watchdog (was 2^10 ~3.3us) at ANY point in the transaction; when it fired AFTER the AR had
|
|
// handshaked, the idle state's m_rready=1 drained the now-orphaned response and the requester
|
|
// hung forever (blank HDMI + stuck HPS probe under the always-on-scanout traffic). Once
|
|
// m_arvalid && m_arready, the read is COMMITTED and its response BELONGS to that requester —
|
|
// there is no AXI-legal way to abandon it. So: the watchdog gates ONLY the pre-AR wait (no
|
|
// transaction committed yet — safe to drop); after AR acceptance the grant is held until
|
|
// m_rvalid && m_rlast && selected_rready, regardless of how long the read takes.
|
|
reg ar_done; // AR handshake captured for the active grant -> never abort past here
|
|
reg [21:0] watchdog; // pre-AR only (waiting for m_arready); ~6.7 ms @ 310 MHz dead-bus backstop
|
|
wire wd_expired = watchdog[21];
|
|
wire sel_rready = (grant==3'd1)?s0_rready:(grant==3'd2)?s1_rready:
|
|
(grant==3'd3)?s2_rready:(grant==3'd4)?s3_rready:1'b1;
|
|
|
|
always_ff @(posedge clk or negedge rst_n) begin
|
|
if (!rst_n) begin
|
|
grant <= 3'd0; ar_done <= 1'b0; watchdog <= '0;
|
|
end else if (grant == 3'd0) begin
|
|
ar_done <= 1'b0; watchdog <= '0;
|
|
if (s0_arvalid) grant <= 3'd1; // scanout (highest)
|
|
else if (s3_arvalid) grant <= 3'd4; // tile reload (render-prep)
|
|
else if (s1_arvalid) grant <= 3'd2; // read probe (debug)
|
|
else if (s2_arvalid) grant <= 3'd3; // texture fill (lowest)
|
|
end else begin
|
|
if (m_arvalid && m_arready) ar_done <= 1'b1; // AR accepted -> COMMITTED
|
|
if (m_rvalid && m_rlast && sel_rready) begin
|
|
grant <= 3'd0; ar_done <= 1'b0; watchdog <= '0; // response delivered -> release
|
|
end else if (!ar_done) begin // still waiting for AR (nothing owed)
|
|
if (wd_expired) begin grant <= 3'd0; ar_done <= 1'b0; watchdog <= '0; end
|
|
else watchdog <= watchdog + 22'd1;
|
|
end
|
|
// ar_done && response not yet complete: HOLD the grant, never abort.
|
|
end
|
|
end
|
|
|
|
// AR mux
|
|
assign m_araddr = (grant==3'd4)?s3_araddr :(grant==3'd3)?s2_araddr :(grant==3'd2)?s1_araddr :s0_araddr;
|
|
assign m_arburst = (grant==3'd4)?s3_arburst:(grant==3'd3)?s2_arburst:(grant==3'd2)?s1_arburst:s0_arburst;
|
|
assign m_arid = (grant==3'd4)?s3_arid :(grant==3'd3)?s2_arid :(grant==3'd2)?s1_arid :s0_arid;
|
|
assign m_arlen = (grant==3'd4)?s3_arlen :(grant==3'd3)?s2_arlen :(grant==3'd2)?s1_arlen :s0_arlen;
|
|
assign m_arsize = (grant==3'd4)?s3_arsize :(grant==3'd3)?s2_arsize :(grant==3'd2)?s1_arsize :s0_arsize;
|
|
assign m_arvalid = (grant==3'd1)?s0_arvalid:(grant==3'd2)?s1_arvalid:(grant==3'd3)?s2_arvalid:(grant==3'd4)?s3_arvalid:1'b0;
|
|
assign s0_arready = (grant==3'd1)?m_arready:1'b0;
|
|
assign s1_arready = (grant==3'd2)?m_arready:1'b0;
|
|
assign s2_arready = (grant==3'd3)?m_arready:1'b0;
|
|
assign s3_arready = (grant==3'd4)?m_arready:1'b0;
|
|
|
|
// R demux (idle: rready=1 drains any stale/late response)
|
|
assign s0_rdata=m_rdata; assign s1_rdata=m_rdata; assign s2_rdata=m_rdata; assign s3_rdata=m_rdata;
|
|
assign s0_rresp=m_rresp; assign s1_rresp=m_rresp; assign s2_rresp=m_rresp; assign s3_rresp=m_rresp;
|
|
assign s0_rlast=m_rlast; assign s1_rlast=m_rlast; assign s2_rlast=m_rlast; assign s3_rlast=m_rlast;
|
|
assign s0_rvalid = (grant==3'd1)?m_rvalid:1'b0;
|
|
assign s1_rvalid = (grant==3'd2)?m_rvalid:1'b0;
|
|
assign s2_rvalid = (grant==3'd3)?m_rvalid:1'b0;
|
|
assign s3_rvalid = (grant==3'd4)?m_rvalid:1'b0;
|
|
assign m_rready = (grant==3'd1)?s0_rready:(grant==3'd2)?s1_rready:(grant==3'd3)?s2_rready:(grant==3'd4)?s3_rready:1'b1;
|
|
endmodule
|