Files
retroDE_ps2/rtl/gif_gs/gs_axi_w_regbuf.sv
T
thejayman77 dd7ca4fb8e Ch441: one-entry fully-registered W buffer, Z-RMW master -> wr_arb s2 [READY FOR REVIEW]
Cuts the lone remaining EMIF setup fail (-0.016 ns, -0.259 skew): the combinational
EMIF gen_p2c_ff -> wr_arb s2_wready -> gs_lpddr_z_rmw next-state (st, endpoint
labelled S_FILL_R via the shared encoded state register).

New gs_axi_w_regbuf: fully-registered one-entry W buffer (Option B per Codex).
- u_wready = !full ONLY (registered occupancy) -> EMIF WREADY never reaches the Z
  FSM combinationally. NOT a fall-through skid (no !full-OR-d_wready term).
- Buffers WDATA/WSTRB/WLAST; downstream held stable until accepted; exactly-once.
- AW/B untouched; arbiter bready_q unchanged (still arms on real EMIF W handshake).
- z_rmw may enter B-wait once the beat is buffered -- safe: EMIF cannot return B
  until the buffered beat reaches it. Single-beat writes -> the 1-beat/2-cycle
  buffer rate is far above the Z write rate (no new FIFO pressure).

Wired in zc_emit between u_z W output (zi_*) and the z_w* ports. New file in sim
Makefile RTL_SRCS + synth QSF (both). Focused tb_gs_axi_w_regbuf: exactly-once/order/
payload scoreboard + no-combinational-bypass check (u_wready===!full incl. full &&
d_wready) + downstream-stable check; standalone target + in make run. Texture-cache
+0.016 paths NOT touched. No simulations or Quartus run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 14:12:11 -04:00

79 lines
3.5 KiB
Systemverilog

// retroDE_ps2 — gs_axi_w_regbuf (Ch441)
//
// Fully-registered ONE-ENTRY AXI W-channel buffer. Inserted between the Z RMW
// master's (gs_lpddr_z_rmw) W OUTPUT and the write arbiter's s2 W INPUT to cut
// the combinational path EMIF gen_p2c_ff -> fbr wready -> wr_arb s2_wready ->
// gs_lpddr_z_rmw next-state (`st`) — the -0.016 ns / -0.259 ns-skew setup family.
//
// CONTRACT (per Codex review, must hold exactly):
// - FULLY REGISTERED, not a fall-through skid: u_wready depends ONLY on the
// registered occupancy `full`, never on d_wready. Downstream (EMIF) WREADY
// therefore can never propagate combinationally back into the upstream FSM.
// (i.e. NOT `u_wready = !full || d_wready`.)
// - Buffers the complete {WDATA, WSTRB, WLAST} payload and holds it stable on
// the downstream side until the arbiter accepts it.
// - Exactly-once: a beat accepted upstream is delivered downstream exactly once.
// One beat in flight (the Z RMW issues single-beat writes, AWLEN=0/WLAST=1).
// - AW and B channels are NOT touched (they pass straight through, outside this
// module). The write arbiter's bready_q is unaffected: it still arms only on
// the REAL downstream m_wvalid && m_wready && m_wlast, because this buffer
// sits UPSTREAM of the arbiter and presents a clean registered W to it.
// - The upstream FSM may enter its B-wait state once the beat is accepted here;
// that is safe because EMIF cannot return B until the buffered beat reaches it.
`timescale 1ns/1ps
module gs_axi_w_regbuf #(
parameter int WDATA_W = 256,
parameter int WSTRB_W = 32
) (
input logic clk,
input logic rst_n,
// upstream — from the Z RMW master's W output
input logic [WDATA_W-1:0] u_wdata,
input logic [WSTRB_W-1:0] u_wstrb,
input logic u_wlast,
input logic u_wvalid,
output logic u_wready,
// downstream — to the write arbiter's s2 W input
output logic [WDATA_W-1:0] d_wdata,
output logic [WSTRB_W-1:0] d_wstrb,
output logic d_wlast,
output logic d_wvalid,
input logic d_wready
);
logic full;
logic [WDATA_W-1:0] wdata_q;
logic [WSTRB_W-1:0] wstrb_q;
logic wlast_q;
// Upstream ready = registered occupancy ONLY (no d_wready term) -> EMIF WREADY
// never reaches the upstream FSM combinationally.
assign u_wready = !full;
// Downstream presents the held beat, stable until the arbiter accepts it.
assign d_wvalid = full;
assign d_wdata = wdata_q;
assign d_wstrb = wstrb_q;
assign d_wlast = wlast_q;
// One-entry register. Accept an offered upstream beat only while empty;
// release only when the arbiter accepts the held beat. When full and accepted
// in the same cycle, u_wready is still 0 (full is registered), so the next
// beat waits one cycle -> a swap/drop/dup is impossible. Payload registers
// deliberately have no reset (qualified by `full`/d_wvalid; the writer cannot
// present a beat until reset releases), avoiding a wide reset fanout.
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
full <= 1'b0;
end else if (!full) begin
if (u_wvalid) begin
full <= 1'b1;
wdata_q <= u_wdata;
wstrb_q <= u_wstrb;
wlast_q <= u_wlast;
end
end else begin
if (d_wready) full <= 1'b0;
end
end
endmodule : gs_axi_w_regbuf