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>
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
// 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
|
||||
Reference in New Issue
Block a user