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>
180 lines
8.6 KiB
Systemverilog
180 lines
8.6 KiB
Systemverilog
// ============================================================================
|
|
// gs_lpddr_wr_arb.sv (Ch322 Brick 3; Ch323 extended 2:1 -> 3:1)
|
|
//
|
|
// 3:1 AXI4 WRITE-channel arbiter for the FPGA-private LPDDR4B EMIF user port.
|
|
// The write twin of gs_lpddr_rd_arb. Lets the GS framebuffer writer
|
|
// (gs_lpddr_axi_master, port 0, PRIORITY), the Ch323 tile Z-flush writer
|
|
// (gs_z_flush_writer, port 2) and the Ch322 HPS write-probe
|
|
// (gs_lpddr_wr_probe, port 1) share the single EMIF write channel.
|
|
//
|
|
// EXPLICIT priority (Ch323, Codex): FB-writer > Z-writer > wr-probe — i.e.
|
|
// s0 > s2 > s1. The active render's color (FB) and Z spill outrank the debug
|
|
// write-probe so a debug write can never starve a render flush. Leave s2_*
|
|
// unconnected (awvalid=0) on builds without a Z writer — the arbiter is then
|
|
// bit-for-bit the old 2:1 behavior.
|
|
//
|
|
// Per-transaction grant held AW->W->B (single-beat writes, AWLEN=0, so B
|
|
// completes one transaction). Watchdog force-release guards a lost B.
|
|
// All single-clock (emif_clk).
|
|
// ============================================================================
|
|
`timescale 1ns/1ps
|
|
|
|
module gs_lpddr_wr_arb (
|
|
input logic clk,
|
|
input logic rst_n,
|
|
|
|
// ---- Port 0: GS framebuffer writer (priority) ----
|
|
input logic [29:0] s0_awaddr,
|
|
input logic [1:0] s0_awburst,
|
|
input logic [6:0] s0_awid,
|
|
input logic [7:0] s0_awlen,
|
|
input logic [2:0] s0_awsize,
|
|
input logic s0_awvalid,
|
|
output logic s0_awready,
|
|
input logic [255:0] s0_wdata,
|
|
input logic [31:0] s0_wstrb,
|
|
input logic s0_wlast,
|
|
input logic s0_wvalid,
|
|
output logic s0_wready,
|
|
output logic [1:0] s0_bresp,
|
|
output logic s0_bvalid,
|
|
input logic s0_bready,
|
|
|
|
// ---- Port 1: HPS write-probe ----
|
|
input logic [29:0] s1_awaddr,
|
|
input logic [1:0] s1_awburst,
|
|
input logic [6:0] s1_awid,
|
|
input logic [7:0] s1_awlen,
|
|
input logic [2:0] s1_awsize,
|
|
input logic s1_awvalid,
|
|
output logic s1_awready,
|
|
input logic [255:0] s1_wdata,
|
|
input logic [31:0] s1_wstrb,
|
|
input logic s1_wlast,
|
|
input logic s1_wvalid,
|
|
output logic s1_wready,
|
|
output logic [1:0] s1_bresp,
|
|
output logic s1_bvalid,
|
|
input logic s1_bready,
|
|
|
|
// ---- Port 2: tile Z-flush writer (Ch323; priority ABOVE probe, below FB writer) ----
|
|
input logic [29:0] s2_awaddr,
|
|
input logic [1:0] s2_awburst,
|
|
input logic [6:0] s2_awid,
|
|
input logic [7:0] s2_awlen,
|
|
input logic [2:0] s2_awsize,
|
|
input logic s2_awvalid,
|
|
output logic s2_awready,
|
|
input logic [255:0] s2_wdata,
|
|
input logic [31:0] s2_wstrb,
|
|
input logic s2_wlast,
|
|
input logic s2_wvalid,
|
|
output logic s2_wready,
|
|
output logic [1:0] s2_bresp,
|
|
output logic s2_bvalid,
|
|
input logic s2_bready,
|
|
|
|
// ---- Port 3: HPS write-probe (Ch323 diag; LOWEST priority — debug staging) ----
|
|
input logic [29:0] s3_awaddr,
|
|
input logic [1:0] s3_awburst,
|
|
input logic [6:0] s3_awid,
|
|
input logic [7:0] s3_awlen,
|
|
input logic [2:0] s3_awsize,
|
|
input logic s3_awvalid,
|
|
output logic s3_awready,
|
|
input logic [255:0] s3_wdata,
|
|
input logic [31:0] s3_wstrb,
|
|
input logic s3_wlast,
|
|
input logic s3_wvalid,
|
|
output logic s3_wready,
|
|
output logic [1:0] s3_bresp,
|
|
output logic s3_bvalid,
|
|
input logic s3_bready,
|
|
|
|
// ---- Master out: EMIF write channel ----
|
|
output logic [29:0] m_awaddr,
|
|
output logic [1:0] m_awburst,
|
|
output logic [6:0] m_awid,
|
|
output logic [7:0] m_awlen,
|
|
output logic [2:0] m_awsize,
|
|
output logic m_awvalid,
|
|
input logic m_awready,
|
|
output logic [255:0] m_wdata,
|
|
output logic [31:0] m_wstrb,
|
|
output logic m_wlast,
|
|
output logic m_wvalid,
|
|
input logic m_wready,
|
|
input logic [1:0] m_bresp,
|
|
input logic m_bvalid,
|
|
output logic m_bready
|
|
);
|
|
// grant: 0=idle, 1=s0 FB writer, 2=s1 color spill, 3=s2 Z spill, 4=s3 HPS write-probe.
|
|
// EXPLICIT priority: FB-writer > Z-spill > color-spill > wr-probe — i.e. s0 > s2 > s1 > s3.
|
|
reg [2:0] grant;
|
|
// Ch326 — NON-ABORTING ARBITER (Codex), same protocol fix as gs_lpddr_rd_arb. Once
|
|
// m_awvalid && m_awready, the write is COMMITTED (the slave will return B); abandoning it on
|
|
// a watchdog would orphan the B / leave the slave mid-write. So the watchdog gates ONLY the
|
|
// pre-AW wait; after AW acceptance the grant is held until m_bvalid && selected_bready. (The
|
|
// FB/spill writers never tripped the old 2^10 watchdog in practice, but the latent bug is the
|
|
// same — fixed for safety.)
|
|
// "committed" = EITHER the AW or a W beat has handshaked. The current writers send AW-then-W
|
|
// so AW sets it first, but tracking either makes this a GENERAL AXI write arbiter that never
|
|
// abandons a transaction regardless of AW/W ordering (Codex audit note).
|
|
reg aw_done; // a write beat/addr accepted for the active grant -> never abort past here
|
|
reg [21:0] watchdog; // pre-commit only; ~6.7 ms @ 310 MHz dead-bus backstop
|
|
wire wd_expired = watchdog[21];
|
|
wire sel_bready = (grant==3'd1)?s0_bready:(grant==3'd2)?s1_bready:
|
|
(grant==3'd3)?s2_bready:(grant==3'd4)?s3_bready:1'b1;
|
|
|
|
always_ff @(posedge clk or negedge rst_n) begin
|
|
if (!rst_n) begin
|
|
grant <= 3'd0; aw_done <= 1'b0; watchdog <= '0;
|
|
end else if (grant == 3'd0) begin
|
|
aw_done <= 1'b0; watchdog <= '0;
|
|
if (s0_awvalid) grant <= 3'd1; // FB writer (highest)
|
|
else if (s2_awvalid) grant <= 3'd3; // Z spill (render-flush)
|
|
else if (s1_awvalid) grant <= 3'd2; // color spill (render-flush)
|
|
else if (s3_awvalid) grant <= 3'd4; // HPS write-probe (debug, lowest)
|
|
end else begin
|
|
if ((m_awvalid && m_awready) || (m_wvalid && m_wready)) aw_done <= 1'b1; // AW or W accepted -> COMMITTED
|
|
if (m_bvalid && sel_bready) begin
|
|
grant <= 3'd0; aw_done <= 1'b0; watchdog <= '0; // B delivered -> release
|
|
end else if (!aw_done) begin // still waiting for AW (nothing owed)
|
|
if (wd_expired) begin grant <= 3'd0; aw_done <= 1'b0; watchdog <= '0; end
|
|
else watchdog <= watchdog + 22'd1;
|
|
end
|
|
// aw_done && B not yet seen: HOLD the grant, never abort.
|
|
end
|
|
end
|
|
|
|
// AW mux
|
|
assign m_awaddr = (grant==3'd4)?s3_awaddr :(grant==3'd3)?s2_awaddr :(grant==3'd2)?s1_awaddr :s0_awaddr;
|
|
assign m_awburst = (grant==3'd4)?s3_awburst:(grant==3'd3)?s2_awburst:(grant==3'd2)?s1_awburst:s0_awburst;
|
|
assign m_awid = (grant==3'd4)?s3_awid :(grant==3'd3)?s2_awid :(grant==3'd2)?s1_awid :s0_awid;
|
|
assign m_awlen = (grant==3'd4)?s3_awlen :(grant==3'd3)?s2_awlen :(grant==3'd2)?s1_awlen :s0_awlen;
|
|
assign m_awsize = (grant==3'd4)?s3_awsize :(grant==3'd3)?s2_awsize :(grant==3'd2)?s1_awsize :s0_awsize;
|
|
assign m_awvalid = (grant==3'd1)?s0_awvalid:(grant==3'd2)?s1_awvalid:(grant==3'd3)?s2_awvalid:(grant==3'd4)?s3_awvalid:1'b0;
|
|
assign s0_awready = (grant==3'd1)?m_awready:1'b0;
|
|
assign s1_awready = (grant==3'd2)?m_awready:1'b0;
|
|
assign s2_awready = (grant==3'd3)?m_awready:1'b0;
|
|
assign s3_awready = (grant==3'd4)?m_awready:1'b0;
|
|
|
|
// W mux
|
|
assign m_wdata = (grant==3'd4)?s3_wdata:(grant==3'd3)?s2_wdata:(grant==3'd2)?s1_wdata:s0_wdata;
|
|
assign m_wstrb = (grant==3'd4)?s3_wstrb:(grant==3'd3)?s2_wstrb:(grant==3'd2)?s1_wstrb:s0_wstrb;
|
|
assign m_wlast = (grant==3'd4)?s3_wlast:(grant==3'd3)?s2_wlast:(grant==3'd2)?s1_wlast:s0_wlast;
|
|
assign m_wvalid = (grant==3'd1)?s0_wvalid:(grant==3'd2)?s1_wvalid:(grant==3'd3)?s2_wvalid:(grant==3'd4)?s3_wvalid:1'b0;
|
|
assign s0_wready = (grant==3'd1)?m_wready:1'b0;
|
|
assign s1_wready = (grant==3'd2)?m_wready:1'b0;
|
|
assign s2_wready = (grant==3'd3)?m_wready:1'b0;
|
|
assign s3_wready = (grant==3'd4)?m_wready:1'b0;
|
|
|
|
// B demux (idle: bready=1 drains any stale/late response)
|
|
assign s0_bresp = m_bresp; assign s1_bresp = m_bresp; assign s2_bresp = m_bresp; assign s3_bresp = m_bresp;
|
|
assign s0_bvalid = (grant==3'd1)?m_bvalid:1'b0;
|
|
assign s1_bvalid = (grant==3'd2)?m_bvalid:1'b0;
|
|
assign s2_bvalid = (grant==3'd3)?m_bvalid:1'b0;
|
|
assign s3_bvalid = (grant==3'd4)?m_bvalid:1'b0;
|
|
assign m_bready = (grant==3'd1)?s0_bready:(grant==3'd2)?s1_bready:(grant==3'd3)?s2_bready:(grant==3'd4)?s3_bready:1'b1;
|
|
endmodule
|