// 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