From 76bb460dc16b504822aa5d51d268a0ae1a9d7e18 Mon Sep 17 00:00:00 2001 From: Rupert Swarbrick Date: Thu, 28 Nov 2024 14:07:49 +0000 Subject: [PATCH] [prim_fifo_sync,rtl] Specialize for Depth == 1 This case is pretty simple to reason about, and avoids needing proper read and write addresses (because there is only one element). Signed-off-by: Rupert Swarbrick --- hw/ip/prim/rtl/prim_fifo_sync.sv | 68 ++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/hw/ip/prim/rtl/prim_fifo_sync.sv b/hw/ip/prim/rtl/prim_fifo_sync.sv index c4c1ac429fd46a..f4f02388e5d524 100644 --- a/hw/ip/prim/rtl/prim_fifo_sync.sv +++ b/hw/ip/prim/rtl/prim_fifo_sync.sv @@ -55,6 +55,49 @@ module prim_fifo_sync #( // No error assign err_o = 1'b 0; + // FIFO has space for a single element (and doesn't need proper counters) + end else if (Depth == 1) begin : gen_singleton_fifo + + // full_q is true if the (singleton) queue has data + logic full_d, full_q; + + assign full_o = full_q; + assign depth_o = full_q; + assign wready_o = ~full_q; + + // We can always read from the storage if it contains something, so rvalid_o is true if full_q + // is true. Enabling pass-through mode also allows data to flow through if wvalid_i is true. + assign rvalid_o = full_q || (Pass && wvalid_i); + + // For there to be data on the next cycle, there must either be new data coming in (so !rvalid_o + // && wvalid_i) or we must be keeping the current data (so rvalid_o && !rready_i). Using + // rvalid_o instead of full_q ensures we get the right behaviour with pass-through. + // + // In either case, any stored data will be forgotten if clr_i is true. + assign full_d = (rvalid_o ? !rready_i : wvalid_i) && !clr_i; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + full_q <= 1'b0; + end else begin + full_q <= full_d; + end + end + + logic [Width-1:0] storage; + always_ff @(posedge clk_i) begin + if (wvalid_i && wready_o) storage <= wdata_i; + end + + logic [Width-1:0] rdata_int; + assign rdata_int = (full_q || Pass == 1'b0) ? storage : wdata_i; + + assign rdata_o = (OutputZeroIfEmpty && !rvalid_o) ? Width'(0) : rdata_int; + + // The err_o signal is only used for larger depths, where there are redundant counters that get + // checked. + assign err_o = 1'b0; + // Normal FIFO construction end else begin : gen_normal_fifo @@ -100,30 +143,15 @@ module prim_fifo_sync #( assign fifo_incr_wptr = wvalid_i & wready_o & ~under_rst; assign fifo_incr_rptr = rvalid_o & rready_i & ~under_rst; - // the generate blocks below are needed to avoid lint errors due to array indexing - // in the where the fifo only has one storage element logic [Depth-1:0][Width-1:0] storage; logic [Width-1:0] storage_rdata; - if (Depth == 1) begin : gen_depth_eq1 - assign storage_rdata = storage[0]; - - always_ff @(posedge clk_i) - if (fifo_incr_wptr) begin - storage[0] <= wdata_i; - end - - logic unused_ptrs; - assign unused_ptrs = ^{fifo_wptr, fifo_rptr}; - // fifo with more than one storage element - end else begin : gen_depth_gt1 - assign storage_rdata = storage[fifo_rptr]; + assign storage_rdata = storage[fifo_rptr]; - always_ff @(posedge clk_i) - if (fifo_incr_wptr) begin - storage[fifo_wptr] <= wdata_i; - end - end + always_ff @(posedge clk_i) + if (fifo_incr_wptr) begin + storage[fifo_wptr] <= wdata_i; + end logic [Width-1:0] rdata_int; if (Pass == 1'b1) begin : gen_pass