dd7ca4fb8e
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>
79 lines
3.5 KiB
Systemverilog
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
|