| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| // Generic synchronous fifo for use in a variety of devices. |
| |
| `include "prim_assert.sv" |
| |
| module prim_fifo_sync #( |
| parameter int unsigned Width = 16, |
| parameter bit Pass = 1'b1, // if == 1 allow requests to pass through empty FIFO |
| parameter int unsigned Depth = 4, |
| parameter bit OutputZeroIfEmpty = 1'b1, // if == 1 always output 0 when FIFO is empty |
| parameter bit Secure = 1'b0, // use prim count for pointers |
| // derived parameter |
| localparam int DepthW = prim_util_pkg::vbits(Depth+1) |
| ) ( |
| input clk_i, |
| input rst_ni, |
| // synchronous clear / flush port |
| input clr_i, |
| // write port |
| input wvalid_i, |
| output wready_o, |
| input [Width-1:0] wdata_i, |
| // read port |
| output rvalid_o, |
| input rready_i, |
| output [Width-1:0] rdata_o, |
| // occupancy |
| output full_o, |
| output [DepthW-1:0] depth_o, |
| output err_o |
| ); |
| |
| |
| // FIFO is in complete passthrough mode |
| if (Depth == 0) begin : gen_passthru_fifo |
| `ASSERT_INIT(paramCheckPass, Pass == 1) |
| |
| assign depth_o = 1'b0; //output is meaningless |
| |
| // devie facing |
| assign rvalid_o = wvalid_i; |
| assign rdata_o = wdata_i; |
| |
| // host facing |
| assign wready_o = rready_i; |
| assign full_o = rready_i; |
| |
| // this avoids lint warnings |
| logic unused_clr; |
| assign unused_clr = clr_i; |
| |
| // No error |
| assign err_o = 1'b 0; |
| |
| // Normal FIFO construction |
| end else begin : gen_normal_fifo |
| |
| localparam int unsigned PTRV_W = prim_util_pkg::vbits(Depth); |
| localparam int unsigned PTR_WIDTH = PTRV_W+1; |
| |
| logic [PTR_WIDTH-1:0] fifo_wptr, fifo_rptr; |
| logic fifo_incr_wptr, fifo_incr_rptr, fifo_empty; |
| |
| // module under reset flag |
| logic under_rst; |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| under_rst <= 1'b1; |
| end else if (under_rst) begin |
| under_rst <= ~under_rst; |
| end |
| end |
| |
| // create the write and read pointers |
| logic full, empty; |
| logic wptr_msb; |
| logic rptr_msb; |
| logic [PTRV_W-1:0] wptr_value; |
| logic [PTRV_W-1:0] rptr_value; |
| |
| assign wptr_msb = fifo_wptr[PTR_WIDTH-1]; |
| assign rptr_msb = fifo_rptr[PTR_WIDTH-1]; |
| assign wptr_value = fifo_wptr[0+:PTRV_W]; |
| assign rptr_value = fifo_rptr[0+:PTRV_W]; |
| assign depth_o = (full) ? DepthW'(Depth) : |
| (wptr_msb == rptr_msb) ? DepthW'(wptr_value) - DepthW'(rptr_value) : |
| (DepthW'(Depth) - DepthW'(rptr_value) + DepthW'(wptr_value)) ; |
| |
| assign fifo_incr_wptr = wvalid_i & wready_o & ~under_rst; |
| assign fifo_incr_rptr = rvalid_o & rready_i & ~under_rst; |
| |
| // full and not ready for write are two different concepts. |
| // The latter can be '0' when under reset, while the former is an indication that no more |
| // entries can be written. |
| assign wready_o = ~full & ~under_rst; |
| assign full_o = full; |
| assign rvalid_o = ~empty & ~under_rst; |
| |
| prim_fifo_sync_cnt #( |
| .Width(PTR_WIDTH), |
| .Depth(Depth), |
| .Secure(Secure) |
| ) u_fifo_cnt ( |
| .clk_i, |
| .rst_ni, |
| .clr_i, |
| .incr_wptr_i(fifo_incr_wptr), |
| .incr_rptr_i(fifo_incr_rptr), |
| .wptr_o(fifo_wptr), |
| .rptr_o(fifo_rptr), |
| .err_o |
| ); |
| |
| //always_ff @(posedge clk_i or negedge rst_ni) begin |
| // if (!rst_ni) begin |
| // fifo_wptr <= {(PTR_WIDTH){1'b0}}; |
| // end else if (clr_i) begin |
| // fifo_wptr <= {(PTR_WIDTH){1'b0}}; |
| // end else if (fifo_incr_wptr) begin |
| // if (fifo_wptr[PTR_WIDTH-2:0] == (PTR_WIDTH-1)'(Depth-1)) begin |
| // fifo_wptr <= {~fifo_wptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}}; |
| // end else begin |
| // fifo_wptr <= fifo_wptr + {{(PTR_WIDTH-1){1'b0}},1'b1}; |
| // end |
| // end |
| //end |
| // |
| //always_ff @(posedge clk_i or negedge rst_ni) begin |
| // if (!rst_ni) begin |
| // fifo_rptr <= {(PTR_WIDTH){1'b0}}; |
| // end else if (clr_i) begin |
| // fifo_rptr <= {(PTR_WIDTH){1'b0}}; |
| // end else if (fifo_incr_rptr) begin |
| // if (fifo_rptr[PTR_WIDTH-2:0] == (PTR_WIDTH-1)'(Depth-1)) begin |
| // fifo_rptr <= {~fifo_rptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}}; |
| // end else begin |
| // fifo_rptr <= fifo_rptr + {{(PTR_WIDTH-1){1'b0}},1'b1}; |
| // end |
| // end |
| //end |
| |
| assign full = (fifo_wptr == (fifo_rptr ^ {1'b1,{(PTR_WIDTH-1){1'b0}}})); |
| assign fifo_empty = (fifo_wptr == fifo_rptr); |
| |
| |
| // 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 |
| // fifo with more than one storage element |
| end else begin : gen_depth_gt1 |
| assign storage_rdata = storage[fifo_rptr[PTR_WIDTH-2:0]]; |
| |
| always_ff @(posedge clk_i) |
| if (fifo_incr_wptr) begin |
| storage[fifo_wptr[PTR_WIDTH-2:0]] <= wdata_i; |
| end |
| end |
| |
| logic [Width-1:0] rdata_int; |
| if (Pass == 1'b1) begin : gen_pass |
| assign rdata_int = (fifo_empty && wvalid_i) ? wdata_i : storage_rdata; |
| assign empty = fifo_empty & ~wvalid_i; |
| end else begin : gen_nopass |
| assign rdata_int = storage_rdata; |
| assign empty = fifo_empty; |
| end |
| |
| if (OutputZeroIfEmpty == 1'b1) begin : gen_output_zero |
| assign rdata_o = empty ? 'b0 : rdata_int; |
| end else begin : gen_no_output_zero |
| assign rdata_o = rdata_int; |
| end |
| |
| `ASSERT(depthShallNotExceedParamDepth, !empty |-> depth_o <= DepthW'(Depth)) |
| end // block: gen_normal_fifo |
| |
| |
| ////////////////////// |
| // Known Assertions // |
| ////////////////////// |
| |
| `ASSERT(DataKnown_A, rvalid_o |-> !$isunknown(rdata_o)) |
| `ASSERT_KNOWN(DepthKnown_A, depth_o) |
| `ASSERT_KNOWN(RvalidKnown_A, rvalid_o) |
| `ASSERT_KNOWN(WreadyKnown_A, wready_o) |
| |
| endmodule |