diff --git a/rtl/gif_gs/gs_async_fifo.sv b/rtl/gif_gs/gs_async_fifo.sv index f23daa0..5e566bc 100644 --- a/rtl/gif_gs/gs_async_fifo.sv +++ b/rtl/gif_gs/gs_async_fifo.sv @@ -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. diff --git a/rtl/gif_gs/gs_lpddr_zc_emit.sv b/rtl/gif_gs/gs_lpddr_zc_emit.sv index 3ca3458..ce39767 100644 --- a/rtl/gif_gs/gs_lpddr_zc_emit.sv +++ b/rtl/gif_gs/gs_lpddr_zc_emit.sv @@ -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) ); diff --git a/sim/Makefile b/sim/Makefile index ba8879d..a97758d 100644 --- a/sim/Makefile +++ b/sim/Makefile @@ -1103,6 +1103,16 @@ tb_gs_async_fifo_quadrant: dirs @echo "=== run tb_gs_async_fifo_quadrant ===" @cd $(TRACE_DIR) && $(VVP) $(BUILD_DIR)/tb_gs_async_fifo_quadrant.vvp +tb_gs_async_fifo_quad_width4: dirs + @echo "=== build tb_gs_async_fifo_quad_width4 ===" + $(IVERILOG) -g2012 -Wall \ + -P tb_gs_async_fifo.TEST_QUAD_WIDTH4=1 \ + -o $(BUILD_DIR)/tb_gs_async_fifo_quad_width4.vvp \ + -s tb_gs_async_fifo \ + $(RTL_SRCS) $(TB_ROOT)/gif_gs/tb_gs_async_fifo.sv + @echo "=== run tb_gs_async_fifo_quad_width4 ===" + @cd $(TRACE_DIR) && $(VVP) $(BUILD_DIR)/tb_gs_async_fifo_quad_width4.vvp + tb_gs_async_fifo_registered: dirs @echo "=== build tb_gs_async_fifo_registered ===" $(IVERILOG) $(IVERILOG_FLGS) \ diff --git a/sim/tb/gif_gs/tb_gs_async_fifo.sv b/sim/tb/gif_gs/tb_gs_async_fifo.sv index 149d323..614ae61 100644 --- a/sim/tb/gif_gs/tb_gs_async_fifo.sv +++ b/sim/tb/gif_gs/tb_gs_async_fifo.sv @@ -11,7 +11,8 @@ module tb_gs_async_fifo #( parameter bit TEST_BANKED = 1'b0, parameter bit TEST_QUADRANT = 1'b0, - parameter bit TEST_REGISTERED = TEST_BANKED || TEST_QUADRANT + parameter bit TEST_QUAD_WIDTH4 = 1'b0, // Ch440: 2 depth x 4 width banks + parameter bit TEST_REGISTERED = TEST_BANKED || TEST_QUADRANT || TEST_QUAD_WIDTH4 ); localparam int WIDTH = 32; localparam int DEPTH = 8; @@ -31,7 +32,7 @@ module tb_gs_async_fifo #( gs_async_fifo #(.WIDTH(WIDTH), .DEPTH(DEPTH), .REGISTERED_READ(TEST_REGISTERED), .BANKED_READ(TEST_BANKED), - .QUADRANT_READ(TEST_QUADRANT)) dut ( + .QUADRANT_READ(TEST_QUADRANT), .QUAD_WIDTH4_READ(TEST_QUAD_WIDTH4)) dut ( .wclk(wclk), .wrst_n(wrst_n), .wr(wr), .wdata(wdata), .wfull(wfull), .rclk(rclk), .rrst_n(rrst_n), .rd(dut_rd), .rdata(rdata), .rempty(rempty) );