Ch440: gs_async_fifo QUAD_WIDTH4_READ (2 depth x 4 width) for Z-req FIFO [READY FOR REVIEW]
Candidate 2 from the accepted structural proposal. Adds a QUAD_WIDTH4_READ generate branch to gs_async_fifo: like QUADRANT_READ (2 depth x 2 width) but splits the payload into FOUR width banks, so each preserved read-address launch register drives ~half the M20K load (targets the 310MHz raddr_hi1_q->mem_hi1 setup family, WNS -0.103). Keeps QUADRANT_READ's proven 2:1 depth OUTPUT selector unchanged (no new/deeper mux, per the doc's warning). Depth (8192), one-cycle read latency, ordering, CDC, capacity, and interface are identical. zc_emit u_req switched QUADRANT_READ->QUAD_WIDTH4_READ. New default-off param leaves the other two gs_async_fifo instances byte-identical. TB gains a TEST_QUAD_WIDTH4 variant + standalone Makefile target tb_gs_async_fifo_quad_width4. NO simulations or Quartus run (per authorization). Awaiting review before any sim. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,7 +28,14 @@ module gs_async_fifo #(
|
||||
// times two width banks leave each physical read-address copy driving
|
||||
// roughly one quarter of the original M20K tree. The registered outputs
|
||||
// need only a 2:1 depth-bank select; FIFO depth and latency are unchanged.
|
||||
parameter bit QUADRANT_READ = 1'b0
|
||||
parameter bit QUADRANT_READ = 1'b0,
|
||||
// Ch440: two depth banks x FOUR width banks (eight physical RAMs). Like
|
||||
// QUADRANT_READ but splits the payload into four width banks instead of two,
|
||||
// halving each preserved read-address register's M20K load AGAIN at the same
|
||||
// total M20K, while KEEPING QUADRANT_READ's 2:1 depth output selector (no
|
||||
// new/deeper output mux). Depth, one-cycle read latency, ordering, capacity,
|
||||
// and interface behaviour are identical to QUADRANT_READ.
|
||||
parameter bit QUAD_WIDTH4_READ = 1'b0
|
||||
) (
|
||||
// write domain
|
||||
input logic wclk,
|
||||
@@ -164,7 +171,89 @@ module gs_async_fifo #(
|
||||
end
|
||||
end
|
||||
generate
|
||||
if (QUADRANT_READ) begin : g_quadrant_storage
|
||||
if (QUAD_WIDTH4_READ) begin : g_quad_width4_storage
|
||||
// Ch440: 2 depth banks x 4 width banks = eight RAMs. Each read-address
|
||||
// register drives only a HALF_DEPTH x ~(WIDTH/4) RAM -> roughly half the
|
||||
// M20K load of QUADRANT_READ's hi/lo banks, at the SAME total M20K. The
|
||||
// depth-half selection stays a 2:1 OUTPUT mux, byte-for-byte the selector
|
||||
// QUADRANT_READ already uses -- no new/deeper output mux is introduced.
|
||||
localparam int W4B0 = WIDTH/4;
|
||||
localparam int W4B1 = WIDTH/4;
|
||||
localparam int W4B2 = WIDTH/4;
|
||||
localparam int W4B3 = WIDTH - 3*(WIDTH/4); // remainder bits
|
||||
localparam int W4O0 = 0;
|
||||
localparam int W4O1 = W4B0;
|
||||
localparam int W4O2 = W4B0 + W4B1;
|
||||
localparam int W4O3 = W4B0 + W4B1 + W4B2;
|
||||
logic [W4B0-1:0] m4_0_0 [0:HALF_DEPTH-1]; logic [W4B0-1:0] m4_0_1 [0:HALF_DEPTH-1];
|
||||
logic [W4B1-1:0] m4_1_0 [0:HALF_DEPTH-1]; logic [W4B1-1:0] m4_1_1 [0:HALF_DEPTH-1];
|
||||
logic [W4B2-1:0] m4_2_0 [0:HALF_DEPTH-1]; logic [W4B2-1:0] m4_2_1 [0:HALF_DEPTH-1];
|
||||
logic [W4B3-1:0] m4_3_0 [0:HALF_DEPTH-1]; logic [W4B3-1:0] m4_3_1 [0:HALF_DEPTH-1];
|
||||
// Atomic staged write; depth-half selected by waddr_q[AW-1], exactly as
|
||||
// QUADRANT_READ. Same wwrite_q / waddr_q / wdata_q pointer staging.
|
||||
always_ff @(posedge wclk) begin
|
||||
if (wwrite_q) begin
|
||||
if (waddr_q[AW-1]) begin
|
||||
m4_0_1[waddr_q[HALF_AW-1:0]] <= wdata_q[W4O0 +: W4B0];
|
||||
m4_1_1[waddr_q[HALF_AW-1:0]] <= wdata_q[W4O1 +: W4B1];
|
||||
m4_2_1[waddr_q[HALF_AW-1:0]] <= wdata_q[W4O2 +: W4B2];
|
||||
m4_3_1[waddr_q[HALF_AW-1:0]] <= wdata_q[W4O3 +: W4B3];
|
||||
end else begin
|
||||
m4_0_0[waddr_q[HALF_AW-1:0]] <= wdata_q[W4O0 +: W4B0];
|
||||
m4_1_0[waddr_q[HALF_AW-1:0]] <= wdata_q[W4O1 +: W4B1];
|
||||
m4_2_0[waddr_q[HALF_AW-1:0]] <= wdata_q[W4O2 +: W4B2];
|
||||
m4_3_0[waddr_q[HALF_AW-1:0]] <= wdata_q[W4O3 +: W4B3];
|
||||
end
|
||||
end
|
||||
end
|
||||
if (REGISTERED_READ) begin : g_registered_read
|
||||
// Eight preserved read-address launch copies, one per RAM, so no
|
||||
// copy drives more than one quadrant's physical address tree.
|
||||
(* dont_merge, preserve *) logic [HALF_AW-1:0] r4a_0_0 /* synthesis maxfan = 32 */;
|
||||
(* dont_merge, preserve *) logic [HALF_AW-1:0] r4a_1_0 /* synthesis maxfan = 32 */;
|
||||
(* dont_merge, preserve *) logic [HALF_AW-1:0] r4a_2_0 /* synthesis maxfan = 32 */;
|
||||
(* dont_merge, preserve *) logic [HALF_AW-1:0] r4a_3_0 /* synthesis maxfan = 32 */;
|
||||
(* dont_merge, preserve *) logic [HALF_AW-1:0] r4a_0_1 /* synthesis maxfan = 32 */;
|
||||
(* dont_merge, preserve *) logic [HALF_AW-1:0] r4a_1_1 /* synthesis maxfan = 32 */;
|
||||
(* dont_merge, preserve *) logic [HALF_AW-1:0] r4a_2_1 /* synthesis maxfan = 32 */;
|
||||
(* dont_merge, preserve *) logic [HALF_AW-1:0] r4a_3_1 /* synthesis maxfan = 32 */;
|
||||
logic [W4B0-1:0] r4d_0_0, r4d_0_1;
|
||||
logic [W4B1-1:0] r4d_1_0, r4d_1_1;
|
||||
logic [W4B2-1:0] r4d_2_0, r4d_2_1;
|
||||
logic [W4B3-1:0] r4d_3_0, r4d_3_1;
|
||||
// depth-half selector + its 1-cycle-trailing twin, aligned with the
|
||||
// registered RAM outputs -- identical timing to QUADRANT_READ.
|
||||
logic r4bank_addr_q, r4bank_data_q;
|
||||
always_ff @(posedge rclk) begin
|
||||
r4a_0_0 <= rbin_nxt[HALF_AW-1:0];
|
||||
r4a_1_0 <= rbin_nxt[HALF_AW-1:0];
|
||||
r4a_2_0 <= rbin_nxt[HALF_AW-1:0];
|
||||
r4a_3_0 <= rbin_nxt[HALF_AW-1:0];
|
||||
r4a_0_1 <= rbin_nxt[HALF_AW-1:0];
|
||||
r4a_1_1 <= rbin_nxt[HALF_AW-1:0];
|
||||
r4a_2_1 <= rbin_nxt[HALF_AW-1:0];
|
||||
r4a_3_1 <= rbin_nxt[HALF_AW-1:0];
|
||||
r4bank_addr_q <= rbin_nxt[AW-1];
|
||||
r4bank_data_q <= r4bank_addr_q;
|
||||
r4d_0_0 <= m4_0_0[r4a_0_0];
|
||||
r4d_1_0 <= m4_1_0[r4a_1_0];
|
||||
r4d_2_0 <= m4_2_0[r4a_2_0];
|
||||
r4d_3_0 <= m4_3_0[r4a_3_0];
|
||||
r4d_0_1 <= m4_0_1[r4a_0_1];
|
||||
r4d_1_1 <= m4_1_1[r4a_1_1];
|
||||
r4d_2_1 <= m4_2_1[r4a_2_1];
|
||||
r4d_3_1 <= m4_3_1[r4a_3_1];
|
||||
end
|
||||
// 2:1 depth-half select (unchanged from QUADRANT_READ); the four
|
||||
// width banks are concatenated back into the payload word.
|
||||
assign rdata = r4bank_data_q ? {r4d_3_1, r4d_2_1, r4d_1_1, r4d_0_1}
|
||||
: {r4d_3_0, r4d_2_0, r4d_1_0, r4d_0_0};
|
||||
end else begin : g_fwft_read
|
||||
assign rdata = rbin[AW-1]
|
||||
? {m4_3_1[rbin[HALF_AW-1:0]], m4_2_1[rbin[HALF_AW-1:0]], m4_1_1[rbin[HALF_AW-1:0]], m4_0_1[rbin[HALF_AW-1:0]]}
|
||||
: {m4_3_0[rbin[HALF_AW-1:0]], m4_2_0[rbin[HALF_AW-1:0]], m4_1_0[rbin[HALF_AW-1:0]], m4_0_0[rbin[HALF_AW-1:0]]};
|
||||
end
|
||||
end else if (QUADRANT_READ) begin : g_quadrant_storage
|
||||
// Four physical RAM quadrants: low/high payload width crossed with
|
||||
// lower/upper address half. Writes remain atomic and use the
|
||||
// staged allocation address exactly as the monolithic form does.
|
||||
|
||||
@@ -99,7 +99,11 @@ module gs_lpddr_zc_emit #(
|
||||
// Split that fixed capacity by both depth and width instead: each physical
|
||||
// address copy drives one quarter of the original deep/wide RAM tree.
|
||||
gs_async_fifo #(.WIDTH(PW), .DEPTH(REQ_DEPTH), .REGISTERED_READ(1'b1),
|
||||
.QUADRANT_READ(1'b1)) u_req (
|
||||
// Ch440: 2 depth x 4 width banks (was QUADRANT_READ = 2 depth x
|
||||
// 2 width) -> halves each read-address register's M20K load to
|
||||
// shorten the 310 MHz raddr->mem address routing, keeping the
|
||||
// proven 2:1 depth output selector, 8192 capacity, and latency.
|
||||
.QUAD_WIDTH4_READ(1'b1)) u_req (
|
||||
.wclk(gs_clk), .wrst_n(gs_rst_n), .wr(g_valid && g_ready), .wdata(req_wdata), .wfull(req_wfull),
|
||||
.rclk(axi_clk), .rrst_n(axi_rst_n), .rd(req_rd), .rdata(req_rdata), .rempty(req_rempty)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user