Files
retroDE_ps2/rtl/gif_gs/gs_axi_r_regbuf.sv
T
thejayman77 6319d7ca85 Ch443d: registered AXI R buffer (texcache fill) + drop fill_data_q reset
Closes the last currently-visible EMIF-handshake -> FSM setup family the
Ch443c fit exposed (-0.043/-0.022/-0.005 ns), the read-response analogue
of the AW/W buffers.

- gs_axi_r_regbuf: one-entry FULLY-registered AXI R buffer (the R twin of
  gs_axi_w_regbuf). Buffers the complete {rdata,rresp,rlast}; u_rready =
  !full only (NO combinational dependence on the texture FSM's d_rready);
  captures on u_rvalid && u_rready; d_rvalid = full with the payload held
  stable until d_rvalid && d_rready; resets only . Inserted between
  read-arbiter s2 and gs_texture_cache (u_texf_rbuf). The arbiter is
  unchanged -- it completes its R transaction into the buffer, which then
  owns delivery to the fill FSM. Cuts EMIF rvalid/rdata -> fst.F_R.
- gs_texture_cache: drop the unobservable fill_data_q reset. F_DRAIN (its
  only reader) is reachable only after F_R loads it, so the reset value is
  never observed; removing it kills the separate lock_sync|dreg[1] ->
  fill_data_q[80] setup path (-0.005 ns).
- New tb_gs_axi_r_regbuf: exactly-once/in-order, randomized responses +
  stalls, full backpressure, the full && d_rready no-fall-through case,
  {rdata,rresp,rlast} stability, reset-while-empty AND reset-while-full.

All green: r-buffer TB, texture_cache, texture_psmt8_clut, scanout_lb,
scanout_restart, scanout_diag, ps2_hps_bridge, rd_arb, and the complete
f52 replay BYTE-IDENTICAL (Z 0/307200, COLOR 0/245760). No Quartus/board/
push from here.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 10:03:01 -04:00

79 lines
3.5 KiB
Systemverilog

// retroDE_ps2 — gs_axi_r_regbuf (Ch443d)
//
// Fully-registered ONE-ENTRY AXI R-channel (read-response) buffer. The R twin of
// gs_axi_w_regbuf / gs_axi_aw_regbuf. Inserted between the read arbiter's s2 R
// OUTPUT and the texture-cache fill FSM's R INPUT to cut the combinational path
// EMIF gen_p2c_ff[*] (rvalid/rdata) -> gs_texture_cache F_R next-state (`fst`)
// + fill_data_q capture — the -0.043 ns / -0.022 ns EMIF setup family.
//
// CONTRACT (per Codex review, identical shape to gs_axi_w_regbuf):
// - FULLY REGISTERED, not a fall-through skid: u_rready depends ONLY on the
// registered occupancy `full`, never on the texture FSM's downstream d_rready.
// EMIF RVALID therefore can never propagate combinationally into the fill FSM.
// (i.e. NOT `u_rready = !full || d_rready`.)
// - Buffers the COMPLETE {RDATA, RRESP, RLAST} payload and holds it stable on the
// downstream side until the texture FSM accepts it.
// - Capture only on u_rvalid && u_rready; exactly-once, in-order (one in flight —
// the texture fill issues single-beat reads, ARLEN=0/RLAST=1, but RRESP/RLAST
// are preserved regardless).
// - Downstream VALID is `full`; payload held stable until d_rvalid && d_rready.
// - Reset only `full`; payload registers deliberately unreset (qualified by full).
// - The arbiter is unchanged: it completes its R transaction when the response is
// accepted into this buffer (s2_rready = u_rready = !full); the buffer then owns
// delivery to the texture FSM.
`timescale 1ns/1ps
module gs_axi_r_regbuf #(
parameter int RDATA_W = 256,
parameter int RRESP_W = 2
) (
input logic clk,
input logic rst_n,
// upstream — from the read arbiter's s2 R output (EMIF read return)
input logic [RDATA_W-1:0] u_rdata,
input logic [RRESP_W-1:0] u_rresp,
input logic u_rlast,
input logic u_rvalid,
output logic u_rready,
// downstream — to the texture-cache fill FSM's R input
output logic [RDATA_W-1:0] d_rdata,
output logic [RRESP_W-1:0] d_rresp,
output logic d_rlast,
output logic d_rvalid,
input logic d_rready
);
logic full;
logic [RDATA_W-1:0] rdata_q;
logic [RRESP_W-1:0] rresp_q;
logic rlast_q;
// Upstream ready = registered occupancy ONLY (no d_rready term) -> EMIF RVALID
// never reaches the texture fill FSM combinationally.
assign u_rready = !full;
// Downstream presents the held response, stable until the texture FSM accepts it.
assign d_rvalid = full;
assign d_rdata = rdata_q;
assign d_rresp = rresp_q;
assign d_rlast = rlast_q;
// One-entry register. Accept an offered upstream response only while empty;
// release only when the texture FSM accepts the held response. When full and
// accepted in the same cycle, u_rready is still 0 (full is registered), so the
// next response waits one cycle -> a swap/drop/dup is impossible. Payload
// registers deliberately have no reset (qualified by `full`/d_rvalid).
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
full <= 1'b0;
end else if (!full) begin
if (u_rvalid) begin
full <= 1'b1;
rdata_q <= u_rdata;
rresp_q <= u_rresp;
rlast_q <= u_rlast;
end
end else begin
if (d_rready) full <= 1'b0;
end
end
endmodule : gs_axi_r_regbuf