-
-
Save C47D/e299230c65b82a87d7fc83579d78b168 to your computer and use it in GitHub Desktop.
/** | |
* Generic FIFO. | |
* Author: Carlos Diaz (2017) | |
* | |
* DISCLAIMER (2021): This implementation was done when I had a few days of | |
* experience using verilog, I was at school and not really sure what | |
* I was doing. I choose to make it public in case of me needing it | |
* in the future, but that was not the case, it's been a long time | |
* since I tried to learn any HDL. | |
* | |
* Parameters: | |
* WIDTH: Width of the data on the FIFO, default to 4. | |
* DEPTH: Depth of the FIFO, default to 4. | |
* | |
* Input signals: | |
* data_in: Data input, width controlled with WIDTH parameter. | |
* clk: Clock input. | |
* write: Enable writing into the FIFO. | |
* read: Enable reading from the FIFO. | |
* | |
* Output signals: | |
* data_out: Data output, witdh controlled with WIDTH parameter. | |
* fifo_full: 1bit signal, indicate when the FIFO is full. | |
* fifo_empty: 1bit signal, indicate when the FIFO is empty. | |
* fifo_not_empty: 1bit signal, indicate when the FIFO is not empty. | |
* fifo_not_full: 1bit signal, indicate when the FIFO is not full. | |
* | |
**/ | |
`timescale 1ns / 1ps | |
`default_nettype none | |
module fifo #( | |
parameter WIDTH = 4, | |
parameter DEPTH = 4 | |
)( | |
input [WIDTH-1:0] data_in, | |
input wire clk, | |
input wire write, | |
input wire read, | |
output reg [WIDTH-1:0] data_out, | |
output wire fifo_full, | |
output wire fifo_empty, | |
output wire fifo_not_empty, | |
output wire fifo_not_full | |
); | |
// memory will contain the FIFO data. | |
reg [WIDTH-1:0] memory [0:DEPTH-1]; | |
// $clog2(DEPTH+1)-2 to count from 0 to DEPTH | |
reg [$clog2(DEPTH)-1:0] write_ptr; | |
reg [$clog2(DEPTH)-1:0] read_ptr; | |
// Initialization | |
initial begin | |
// Init both write_cnt and read_cnt to 0 | |
write_ptr = 0; | |
read_ptr = 0; | |
// Display error if WIDTH is 0 or less. | |
if ( WIDTH <= 0 ) begin | |
$error("%m ** Illegal condition **, you used %d WIDTH", WIDTH); | |
end | |
// Display error if DEPTH is 0 or less. | |
if ( DEPTH <= 0) begin | |
$error("%m ** Illegal condition **, you used %d DEPTH", DEPTH); | |
end | |
end // end initial | |
assign fifo_empty = ( write_ptr == read_ptr ) ? 1'b1 : 1'b0; | |
assign fifo_full = ( write_ptr == (DEPTH-1) ) ? 1'b1 : 1'b0; | |
assign fifo_not_empty = ~fifo_empty; | |
assign fifo_not_full = ~fifo_full; | |
always @ (posedge clk) begin | |
if ( write ) begin | |
memory[write_ptr] <= data_in; | |
end | |
if ( read ) begin | |
data_out <= memory[read_ptr]; | |
end | |
end | |
always @ ( posedge clk ) begin | |
if ( write ) begin | |
write_ptr <= write_ptr + 1; | |
end | |
if ( read && fifo_not_empty ) begin | |
read_ptr <= read_ptr + 1; | |
end | |
end | |
endmodule |
There are several mistakes.
line : reg [WIDTH-1:0] memory [0:$clog2(DEPTH+1)];
Should be : reg [WIDTH-1:0] memory [0:DEPTH];
line: assign fifo_empty = ( write_ptr == 0 ) ? 1'b1 : 1'b0;
Should be: assign fifo_empty = ( write_ptr == read_prt ) ? 1'b1 : 1'b0;
Hey, thanks, it's been quite a while since I coded this, I will update it with your comments. 😄
Well, I needed a simple fifo, googled this one... And spent two hours debugging my code until I finally took a look at the fifo behavior.
Also it will be good to internally 'and' 'read' and 'fifo_not_empty' at line 88. Otherwise pointer value will be incremented even if the fifo is empty.
Sorry, line 43 shoud be reg [WIDTH-1:0] memory [0:DEPTH-1];
Well, I needed a simple fifo, googled this one... And spent two hours debugging my code until I finally took a look at the fifo behavior.
Thanks for letting me know about the issues, I've never touched the design after releasing it.
Also it will be good to internally 'and' 'read' and 'fifo_not_empty' at line 88. Otherwise pointer value will be incremented even if the fifo is empty.
Do you mean something like this?
if ( read && fifo_not_empty ) begin
Do you mean something like this?
Correct.
Thanks!
The code is not quite correct in calculating FULL flag. The MSB comparison method may be implemented to fix that.
For example, something like this:
parameter PTR_MSB = $clog2(DEPTH);
parameter ADDR_MSB = PTR_MSB - 1;
reg [PTR_MSB:0] READ_PTR;
reg [PTR_MSB:0] WRITE_PTR;
// Exclude both pointers MSB from memory address
wire [ADDR_MSB:0] WRITE_ADDR = WRITE_PTR[ADDR_MSB:0];
wire [ADDR_MSB:0] READ_ADDR = READ_PTR[ADDR_MSB:0];
/*
* FIFO is EMPTY when write pointer and read pointer are completely equal (including the MSB),
* but when their MSB is different but other parts are equal that tells us FIFO is FULL.
*/
assign EMPTY = (WRITE_PTR == READ_PTR) ? 1'b1 : 1'b0;
assign FULL =
(WRITE_PTR[ADDR_MSB:0] == READ_PTR[ADDR_MSB:0]) &
(WRITE_PTR[PTR_MSB] != READ_PTR[PTR_MSB])
? 1'b1 : 1'b0;
Obviously the memory access should use WRITE_ADDR and WRITE_ADDR. Also it worth taking into account the DEPTH is always a power of 2 (even if you define it, say 5 or 6, then 3-bit address registers and 4-bit pointer registers will be created, so the actual depth will be 8).
write_ptr & read_ptr don't wrap around to 0 and fifo_full is not implemented correctly either.
Hi @lennia and @futurelink,
Thanks for letting me know about this mistakes and improvement opportunities, this chunk of verilog was one of my first attempts (I was at school), as you already noticed. I will try to fix it, I no longer use verilog (or any other HDL for that matter), so I'm not sure on how to fix the write_ptr
, read_ptr
and fifo_full
tho.
That or posting it here should be good, so everyone can see it.
Carlos, apparently this FIFO is the most googlable FIFO on the internet. Thank you for sharing and maintenance! :)
It's nice to know it's been useful for others, but I did it at school when I didn't knew much, so it's been good to receive feedback to improve it.
Since this is one of the top google results for verilog fifo I'll post my hdl.
module RXFIFO #(
parameter PACKET_WIDTH = 248,
parameter FIFO_DEPTH = 256,
parameter PTR_MSB = 8,
parameter ADDR_MSB = 7
)
(
input zedClk100MHz_i,
input reset_ni,
input read_i,
input write_i,
input [PACKET_WIDTH-1:0] packet_i,
output logic [PACKET_WIDTH-1:0] packet_o,
output logic fifoFull_o,
output logic fifoEmpty_o
);
logic [PACKET_WIDTH-1:0] memory [0:FIFO_DEPTH-1];
logic [ PTR_MSB:0] readPtr, writePtr;
wire [ ADDR_MSB:0] writeAddr = writePtr[ADDR_MSB:0];
wire [ ADDR_MSB:0] readAddr = readPtr[ADDR_MSB:0];
always_ff@(posedge zedClk100MHz_i or negedge reset_ni)begin
if(~reset_ni)begin
readPtr <= '0;
writePtr <= '0;
end
else begin
if(write_i && ~fifoFull_o)begin
memory[writeAddr] <= packet_i;
writePtr <= writePtr + 1;
end
if(read_i && ~fifoEmpty_o)begin
packet_o <= memory[readAddr];
readPtr <= readPtr + 1;
end
end
end
assign fifoEmpty_o = (writePtr == readPtr) ? 1'b1: 1'b0;
assign fifoFull_o = ((writePtr[ADDR_MSB:0] == readPtr[ADDR_MSB:0])&(writePtr[PTR_MSB] != readPtr[PTR_MSB])) ? 1'b1 : 1'b0;
endmodule
WiP.
Verilator throws %Warning-WIDTH: fifo.v:69: Operator EQ expects 32 or 3 bits on the LHS, but LHS's VARREF 'write_ptr' generates 2 bits.
%Warning-WIDTH: Use "/* verilator lint_off WIDTH */" and lint_on around source to disable this message.
%Error: Exiting due to 1 warning(s)