// retroDE_ps2 — gs_axi_aw_regbuf (Ch443) // // Fully-registered ONE-ENTRY AXI AW-channel (write-address) buffer. The AW twin // of gs_axi_w_regbuf. Inserted between the Z RMW master's (gs_lpddr_z_rmw) AW // OUTPUT and the write arbiter's s2 AW INPUT to cut the combinational path // EMIF gen_p2c_ff[23] (AWREADY) -> wr_arb s2_awready -> gs_lpddr_z_rmw // next-state (`st`, S_SFLUSH_AW / S_FILL_R) — the -0.410 ns EMIF setup family. // // Ch441 registered the W channel (gs_axi_w_regbuf u_z_wbuf) but left AW running // straight from the FSM to the arbiter, so EMIF AWREADY still reached the FSM // combinationally. This buffer applies the identical structural cut to AW. // // CONTRACT (identical to gs_axi_w_regbuf, must hold exactly): // - FULLY REGISTERED, not a fall-through skid: u_awready depends ONLY on the // registered occupancy `full`, never on d_awready. Downstream (EMIF) AWREADY // therefore can never propagate combinationally back into the upstream FSM. // (i.e. NOT `u_awready = !full || d_awready`.) // - Buffers the complete {AWADDR, AWLEN, AWSIZE, AWBURST} payload and holds it // stable downstream until the arbiter accepts it. // - Exactly-once: an address accepted upstream is delivered downstream exactly // once. One in flight (the Z RMW issues single-beat writes, AWLEN=0). // - W and B channels are NOT touched here (W is separately buffered by // gs_axi_w_regbuf; B passes through). AXI permits AW and W in either order, // and each one-entry buffer holds its beat until the arbiter accepts it, so // the AW/W pair still reaches the slave together. `timescale 1ns/1ps module gs_axi_aw_regbuf #( parameter int ADDR_W = 32, parameter int LEN_W = 8, parameter int SIZE_W = 3, parameter int BURST_W = 2 ) ( input logic clk, input logic rst_n, // upstream — from the Z RMW master's AW output input logic [ADDR_W-1:0] u_awaddr, input logic [LEN_W-1:0] u_awlen, input logic [SIZE_W-1:0] u_awsize, input logic [BURST_W-1:0] u_awburst, input logic u_awvalid, output logic u_awready, // downstream — to the write arbiter's s2 AW input output logic [ADDR_W-1:0] d_awaddr, output logic [LEN_W-1:0] d_awlen, output logic [SIZE_W-1:0] d_awsize, output logic [BURST_W-1:0] d_awburst, output logic d_awvalid, input logic d_awready ); logic full; logic [ADDR_W-1:0] awaddr_q; logic [LEN_W-1:0] awlen_q; logic [SIZE_W-1:0] awsize_q; logic [BURST_W-1:0] awburst_q; // Upstream ready = registered occupancy ONLY (no d_awready term) -> EMIF AWREADY // never reaches the upstream FSM combinationally. assign u_awready = !full; // Downstream presents the held address, stable until the arbiter accepts it. assign d_awvalid = full; assign d_awaddr = awaddr_q; assign d_awlen = awlen_q; assign d_awsize = awsize_q; assign d_awburst = awburst_q; // One-entry register. Accept an offered upstream address only while empty; // release only when the arbiter accepts the held address. When full and // accepted in the same cycle, u_awready is still 0 (full is registered), so // the next address waits one cycle -> a swap/drop/dup is impossible. Payload // registers deliberately have no reset (qualified by `full`/d_awvalid; the // writer cannot present an address until reset releases). always_ff @(posedge clk or negedge rst_n) begin if (!rst_n) begin full <= 1'b0; end else if (!full) begin if (u_awvalid) begin full <= 1'b1; awaddr_q <= u_awaddr; awlen_q <= u_awlen; awsize_q <= u_awsize; awburst_q <= u_awburst; end end else begin if (d_awready) full <= 1'b0; end end endmodule : gs_axi_aw_regbuf