Skip to content

Instantly share code, notes, and snippets.

@C47D
Last active December 6, 2023 07:52
Show Gist options
  • Save C47D/e299230c65b82a87d7fc83579d78b168 to your computer and use it in GitHub Desktop.
Save C47D/e299230c65b82a87d7fc83579d78b168 to your computer and use it in GitHub Desktop.
Generic FIFO implemented in verilog.
/**
* 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
@C47D
Copy link
Author

C47D commented Jul 4, 2017

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)

@eclipsevl
Copy link

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;

@C47D
Copy link
Author

C47D commented Jan 20, 2021

Hey, thanks, it's been quite a while since I coded this, I will update it with your comments. 😄

@eclipsevl
Copy link

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.

@eclipsevl
Copy link

Sorry, line 43 shoud be reg [WIDTH-1:0] memory [0:DEPTH-1];

@C47D
Copy link
Author

C47D commented Jan 20, 2021

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

@eclipsevl
Copy link

eclipsevl commented Jan 21, 2021

Do you mean something like this?

Correct.

Thanks!

@futurelink
Copy link

futurelink commented Sep 3, 2021

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).

@lenniea
Copy link

lenniea commented Sep 28, 2021

write_ptr & read_ptr don't wrap around to 0 and fifo_full is not implemented correctly either.

@C47D
Copy link
Author

C47D commented Sep 29, 2021

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.

@lenniea
Copy link

lenniea commented Sep 29, 2021 via email

@C47D
Copy link
Author

C47D commented Sep 29, 2021

That or posting it here should be good, so everyone can see it.

@lenniea
Copy link

lenniea commented Sep 29, 2021 via email

@eclipsevl
Copy link

Carlos, apparently this FIFO is the most googlable FIFO on the internet. Thank you for sharing and maintenance! :)

@C47D
Copy link
Author

C47D commented Nov 3, 2021

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.

@clint-lemire
Copy link

clint-lemire commented Jun 29, 2022

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment