blob: 5e9ee9c3e59695c79015553b3f62262cd934173f [file] [log] [blame]
// 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