[prim/fifo_async] Add support for Depth <= 2
Move dec2gray and gray2dec functions into a generate context.
Generate simplified versions of pointers and depth output for
Depth <= 2.
Signed-off-by: Tom Roberts <tomroberts@lowrisc.org>
diff --git a/hw/ip/prim/rtl/prim_fifo_async.sv b/hw/ip/prim/rtl/prim_fifo_async.sv
index d69b754..d6ff80c 100644
--- a/hw/ip/prim/rtl/prim_fifo_async.sv
+++ b/hw/ip/prim/rtl/prim_fifo_async.sv
@@ -29,10 +29,10 @@
);
// Depth must be a power of 2 for the gray code pointers to work
- `ASSERT_INIT(ParamCheckDepth_A, (Depth > 2) && (Depth == 2**$clog2(Depth)))
+ `ASSERT_INIT(ParamCheckDepth_A, (Depth == 2**$clog2(Depth)))
- localparam int unsigned PTRV_W = $clog2(Depth);
- localparam int unsigned PTR_WIDTH = PTRV_W+1;
+ localparam int unsigned PTRV_W = (Depth == 1) ? 1 : $clog2(Depth);
+ localparam int unsigned PTR_WIDTH = (Depth == 1) ? 1 : PTRV_W+1;
logic [PTR_WIDTH-1:0] fifo_wptr_q, fifo_wptr_d;
logic [PTR_WIDTH-1:0] fifo_rptr_q, fifo_rptr_d;
@@ -62,8 +62,6 @@
end
// gray-coded version
- assign fifo_wptr_gray_d = dec2gray(fifo_wptr_d);
-
always_ff @(posedge clk_wr_i or negedge rst_wr_ni) begin
if (!rst_wr_ni) begin
fifo_wptr_gray_q <= '0;
@@ -79,9 +77,6 @@
.d_i (fifo_wptr_gray_q),
.q_o (fifo_wptr_gray_sync));
- // decimal version of write pointer in read domain
- assign fifo_wptr_sync_combi = gray2dec(fifo_wptr_gray_sync);
-
//////////////////
// Read Pointer //
//////////////////
@@ -100,8 +95,6 @@
end
// gray-coded version
- assign fifo_rptr_gray_d = dec2gray(fifo_rptr_d);
-
always_ff @(posedge clk_rd_i or negedge rst_rd_ni) begin
if (!rst_rd_ni) begin
fifo_rptr_gray_q <= '0;
@@ -117,9 +110,6 @@
.d_i (fifo_rptr_gray_q),
.q_o (fifo_rptr_gray_sync));
- // decimal version of read pointer in write domain
- assign fifo_rptr_sync_combi = gray2dec(fifo_rptr_gray_sync);
-
// Registered version of synced read pointer
always_ff @(posedge clk_wr_i or negedge rst_wr_ni) begin
if (!rst_wr_ni) begin
@@ -137,33 +127,42 @@
assign full_rclk = (fifo_wptr_sync_combi == (fifo_rptr_q ^ {1'b1,{(PTR_WIDTH-1){1'b0}}}));
assign empty_rclk = (fifo_wptr_sync_combi == fifo_rptr_q);
- // Current depth in the write clock side
- logic wptr_msb;
- logic rptr_sync_msb;
- logic [PTRV_W-1:0] wptr_value;
- logic [PTRV_W-1:0] rptr_sync_value;
+ if (Depth > 1) begin : g_depth_calc
- assign wptr_msb = fifo_wptr_q[PTR_WIDTH-1];
- assign rptr_sync_msb = fifo_rptr_sync_q[PTR_WIDTH-1];
- assign wptr_value = fifo_wptr_q[0+:PTRV_W];
- assign rptr_sync_value = fifo_rptr_sync_q[0+:PTRV_W];
- assign wdepth_o = (full_wclk) ? DepthW'(Depth) :
- (wptr_msb == rptr_sync_msb) ? DepthW'(wptr_value) - DepthW'(rptr_sync_value) :
- (DepthW'(Depth) - DepthW'(rptr_sync_value) + DepthW'(wptr_value)) ;
+ // Current depth in the write clock side
+ logic wptr_msb;
+ logic rptr_sync_msb;
+ logic [PTRV_W-1:0] wptr_value;
+ logic [PTRV_W-1:0] rptr_sync_value;
- // Current depth in the read clock side
- logic rptr_msb;
- logic wptr_sync_msb;
- logic [PTRV_W-1:0] rptr_value;
- logic [PTRV_W-1:0] wptr_sync_value;
+ assign wptr_msb = fifo_wptr_q[PTR_WIDTH-1];
+ assign rptr_sync_msb = fifo_rptr_sync_q[PTR_WIDTH-1];
+ assign wptr_value = fifo_wptr_q[0+:PTRV_W];
+ assign rptr_sync_value = fifo_rptr_sync_q[0+:PTRV_W];
+ assign wdepth_o = (full_wclk) ? DepthW'(Depth) :
+ (wptr_msb == rptr_sync_msb) ? DepthW'(wptr_value) - DepthW'(rptr_sync_value) :
+ (DepthW'(Depth) - DepthW'(rptr_sync_value) + DepthW'(wptr_value)) ;
- assign wptr_sync_msb = fifo_wptr_sync_combi[PTR_WIDTH-1];
- assign rptr_msb = fifo_rptr_q[PTR_WIDTH-1];
- assign wptr_sync_value = fifo_wptr_sync_combi[0+:PTRV_W];
- assign rptr_value = fifo_rptr_q[0+:PTRV_W];
- assign rdepth_o = (full_rclk) ? DepthW'(Depth) :
- (wptr_sync_msb == rptr_msb) ? DepthW'(wptr_sync_value) - DepthW'(rptr_value) :
- (DepthW'(Depth) - DepthW'(rptr_value) + DepthW'(wptr_sync_value)) ;
+ // Current depth in the read clock side
+ logic rptr_msb;
+ logic wptr_sync_msb;
+ logic [PTRV_W-1:0] rptr_value;
+ logic [PTRV_W-1:0] wptr_sync_value;
+
+ assign wptr_sync_msb = fifo_wptr_sync_combi[PTR_WIDTH-1];
+ assign rptr_msb = fifo_rptr_q[PTR_WIDTH-1];
+ assign wptr_sync_value = fifo_wptr_sync_combi[0+:PTRV_W];
+ assign rptr_value = fifo_rptr_q[0+:PTRV_W];
+ assign rdepth_o = (full_rclk) ? DepthW'(Depth) :
+ (wptr_sync_msb == rptr_msb) ? DepthW'(wptr_sync_value) - DepthW'(rptr_value) :
+ (DepthW'(Depth) - DepthW'(rptr_value) + DepthW'(wptr_sync_value)) ;
+
+ end else begin : g_no_depth_calc
+
+ assign rdepth_o = full_rclk;
+ assign wdepth_o = full_wclk;
+
+ end
assign wready_o = !full_wclk;
assign rvalid_o = !empty_rclk;
@@ -172,48 +171,91 @@
// Storage //
/////////////
- always_ff @(posedge clk_wr_i) begin
- if (fifo_incr_wptr) begin
- storage[fifo_wptr_q[PTRV_W-1:0]] <= wdata_i;
- end
- end
+ if (Depth > 1) begin : g_storage_mux
- assign rdata_o = storage[fifo_rptr_q[PTRV_W-1:0]];
+ always_ff @(posedge clk_wr_i) begin
+ if (fifo_incr_wptr) begin
+ storage[fifo_wptr_q[PTRV_W-1:0]] <= wdata_i;
+ end
+ end
+
+ assign rdata_o = storage[fifo_rptr_q[PTRV_W-1:0]];
+
+ end else begin : g_storage_simple
+
+ always_ff @(posedge clk_wr_i) begin
+ if (fifo_incr_wptr) begin
+ storage[0] <= wdata_i;
+ end
+ end
+
+ assign rdata_o = storage[0];
+
+ end
//////////////////////////////////////
// Decimal <-> Gray-code Conversion //
//////////////////////////////////////
- function automatic [PTR_WIDTH-1:0] dec2gray(input logic [PTR_WIDTH-1:0] decval);
- logic [PTR_WIDTH-1:0] decval_sub;
- logic [PTR_WIDTH-2:0] decval_in;
- logic unused_decval_msb;
+ // This code is all in a generate context to avoid lint errors when Depth <= 2
+ if (Depth > 2) begin : g_full_gray_conversion
- decval_sub = (PTR_WIDTH)'(Depth) - {1'b0, decval[PTR_WIDTH-2:0]} - 1'b1;
+ function automatic [PTR_WIDTH-1:0] dec2gray(input logic [PTR_WIDTH-1:0] decval);
+ logic [PTR_WIDTH-1:0] decval_sub;
+ logic [PTR_WIDTH-2:0] decval_in;
+ logic unused_decval_msb;
- {unused_decval_msb, decval_in} = decval[PTR_WIDTH-1] ? decval_sub : decval;
- // Was done in two assigns for low bits and top bit
- // but that generates a (bogus) verilator warning, so do in one assign
- dec2gray = {decval[PTR_WIDTH-1],
- {1'b0,decval_in[PTR_WIDTH-2:1]} ^ decval_in[PTR_WIDTH-2:0]};
- endfunction
+ decval_sub = (PTR_WIDTH)'(Depth) - {1'b0, decval[PTR_WIDTH-2:0]} - 1'b1;
- // Algorithm walks up from 0..N-1 then flips the upper bit and walks down from N-1 to 0.
- function automatic [PTR_WIDTH-1:0] gray2dec(input logic [PTR_WIDTH-1:0] grayval);
- logic [PTR_WIDTH-2:0] dec_tmp, dec_tmp_sub;
- logic unused_decsub_msb;
+ {unused_decval_msb, decval_in} = decval[PTR_WIDTH-1] ? decval_sub : decval;
+ // Was done in two assigns for low bits and top bit
+ // but that generates a (bogus) verilator warning, so do in one assign
+ dec2gray = {decval[PTR_WIDTH-1],
+ {1'b0,decval_in[PTR_WIDTH-2:1]} ^ decval_in[PTR_WIDTH-2:0]};
+ endfunction
- dec_tmp[PTR_WIDTH-2] = grayval[PTR_WIDTH-2];
- for (int i = PTR_WIDTH-3; i >= 0; i--) begin
- dec_tmp[i] = dec_tmp[i+1] ^ grayval[i];
- end
- {unused_decsub_msb, dec_tmp_sub} = (PTR_WIDTH-1)'(Depth) - {1'b0, dec_tmp} - 1'b1;
- if (grayval[PTR_WIDTH-1]) begin
- gray2dec = {1'b1, dec_tmp_sub};
- end else begin
- gray2dec = {1'b0, dec_tmp};
- end
- endfunction
+ // Algorithm walks up from 0..N-1 then flips the upper bit and walks down from N-1 to 0.
+ function automatic [PTR_WIDTH-1:0] gray2dec(input logic [PTR_WIDTH-1:0] grayval);
+ logic [PTR_WIDTH-2:0] dec_tmp, dec_tmp_sub;
+ logic unused_decsub_msb;
+
+ dec_tmp[PTR_WIDTH-2] = grayval[PTR_WIDTH-2];
+ for (int i = PTR_WIDTH-3; i >= 0; i--) begin
+ dec_tmp[i] = dec_tmp[i+1] ^ grayval[i];
+ end
+ {unused_decsub_msb, dec_tmp_sub} = (PTR_WIDTH-1)'(Depth) - {1'b0, dec_tmp} - 1'b1;
+ if (grayval[PTR_WIDTH-1]) begin
+ gray2dec = {1'b1, dec_tmp_sub};
+ end else begin
+ gray2dec = {1'b0, dec_tmp};
+ end
+ endfunction
+
+ // decimal version of read pointer in write domain
+ assign fifo_rptr_sync_combi = gray2dec(fifo_rptr_gray_sync);
+ // decimal version of write pointer in read domain
+ assign fifo_wptr_sync_combi = gray2dec(fifo_wptr_gray_sync);
+
+ assign fifo_rptr_gray_d = dec2gray(fifo_rptr_d);
+ assign fifo_wptr_gray_d = dec2gray(fifo_wptr_d);
+
+ end else if (Depth == 2) begin : g_simple_gray_conversion
+
+ assign fifo_rptr_sync_combi = {fifo_rptr_gray_sync[PTR_WIDTH-1], ^fifo_rptr_gray_sync};
+ assign fifo_wptr_sync_combi = {fifo_wptr_gray_sync[PTR_WIDTH-1], ^fifo_rptr_gray_sync};
+
+ assign fifo_rptr_gray_d = {fifo_rptr_d[PTR_WIDTH-1], ^fifo_rptr_d};
+ assign fifo_wptr_gray_d = {fifo_wptr_d[PTR_WIDTH-1], ^fifo_rptr_d};
+
+ end else begin : g_no_gray_conversion
+
+ assign fifo_rptr_sync_combi = fifo_rptr_gray_sync;
+ assign fifo_wptr_sync_combi = fifo_wptr_gray_sync;
+
+ assign fifo_rptr_gray_d = fifo_rptr_d;
+ assign fifo_wptr_gray_d = fifo_rptr_d;
+
+ end
// TODO: assertions on full, empty, gray transitions