27dfd0b0cf
Ch442's fit exposed two failing setup families (plus a stale max-skew). Attack both structurally; no reseeding. Family 1 - AWREADY -> Z-FSM (-0.410): gen_p2c_ff[23] (EMIF AWREADY) reached u_zc_emit|u_z's S_SFLUSH_AW/S_FILL_R next-state combinationally. Ch441 registered only the W channel; add the AW twin: - new gs_axi_aw_regbuf (one-entry fully-registered AW buffer), inserted in zc_emit between u_z's AW output and the arbiter s2 AW port. The FSM now sees registered occupancy, never EMIF's combinational AWREADY. Family 2 - texcache drain_idx_q -> tex_mem (-0.370, x7): a single index fanned across the whole 65536x32, 128-M20K macro. Split by WIDTH into four 65536x8 banks, each with its own (* preserve, dont_merge *) write- address launch register; write the four byte lanes together in F_WRITE; reconstruct the sample word by concatenating four registered read bytes. Selector structure and 1-cycle read latency unchanged; total M20Ks unchanged (4x32 == 128); fill_crc still sums the full 32-bit word. Max-skew: relax ONLY the Ch357 tile-write CDC set_max_skew 2.0 -> 2.5 (quasi-static bundle, >=2 dclk stability window); retain set_net_delay 2.0 (the real arrival bound). SDC comment updated. Tests: new tb_gs_axi_aw_regbuf (AW scoreboard: exactly-once/order/no- combinational-AWREADY-bypass/stable-while-stalled); tb_gs_texture_cache strengthened to distinct-per-byte-bank data + per-bank + full-word checks. All pass: aw/w regbuf, texture_cache, texture_psmt8_clut, scanout_diag, ps2_hps_bridge, and the complete f52 replay BYTE-IDENTICAL (Z 0/307200, COLOR 0/245760). No Quartus, board, or push from here. Ready for one owner GUI fit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
89 lines
3.9 KiB
Systemverilog
89 lines
3.9 KiB
Systemverilog
// 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
|