Designing a Synchronous FIFO in Verilog and SystemVerilog

A synchronous FIFO (First-In-First-Out) buffer is a type of buffer that temporarily stores data and retrieves it in the order it was received. A synchronous FIFO buffer is a critical component in many digital systems that require data to be processed sequentially, ensuring efficient data processing and transmission.

Why is a Synchronous FIFO Buffer Important?

A synchronous FIFO buffer is essential for managing data flow in digital systems that operate synchronously. It ensures that data is processed in the correct order, preventing data loss or data corruption. Synchronous FIFO buffers are commonly used in communication protocols, data processing systems, and memory systems.

Basic Design of a Synchronous FIFO Buffer

The basic design of a synchronous FIFO buffer includes a read pointer, a write pointer, and a storage array. The read and write pointers are used to access data from the storage array in a FIFO order. The read pointer points to the oldest data in the buffer, while the write pointer points to the next available empty space in the buffer.

The synchronous FIFO buffer design also includes control logic to manage the read and write pointers and ensure that data is processed in the correct order. The control logic is implemented using synchronous logic, which means that it operates in sync with the clock of the digital system.

Designing a Synchronous FIFO Buffer in Verilog and SystemVerilog

Here is an example Verilog and SystemVerilog design for a synchronous FIFO buffer that can store up to eight 32-bit words. This design includes the read pointer, write pointer, storage array, and control logic, as well as features for indicating when the buffer is full or empty.

module FIFO #(
  parameter  DataWidth = 32,
  parameter  Depth     = 8,
  localparam PtrWidth  = $clog2(Depth)
) (
  input  logic                 clk,
  input  logic                 rstN,
  input  logic                 writeEn,
  input  logic [DataWidth-1:0] writeData,
  input  logic                 readEn,
  output logic [DataWidth-1:0] readData,
  output logic                 full,
  output logic                 empty
);

  logic [DataWidth-1:0] mem[Depth];
  logic [PtrWidth:0] wrPtr, wrPtrNext;
  logic [PtrWidth:0] rdPtr, rdPtrNext;

  always_comb begin
    wrPtrNext = wrPtr;
    rdPtrNext = rdPtr;
    if (writeEn) begin
      wrPtrNext = wrPtr + 1;
    end
    if (readEn) begin
      rdPtrNext = rdPtr + 1;
    end
  end

  always_ff @(posedge clk or negedge rstN) begin
    if (!rstN) begin
      wrPtr <= '0;
      rdPtr <= '0;
    end else begin
      wrPtr <= wrPtrNext;
      rdPtr <= rdPtrNext;
    end

    mem[wrPtr[PtrWidth-1:0]] <= writeData;
  end

  assign readData = mem[rdPtr[PtrWidth-1:0]];

  assign empty = (wrPtr[PtrWidth] == rdPtr[PtrWidth]) && (wrPtr[PtrWidth-1:0] == rdPtr[PtrWidth-1:0]);
  assign full  = (wrPtr[PtrWidth] != rdPtr[PtrWidth]) && (wrPtr[PtrWidth-1:0] == rdPtr[PtrWidth-1:0]);

endmodule

This Verilog and SystemVerilog code defines a FIFO (First-In, First-Out) memory module. The module takes in several inputs to control the reading and writing of data. The inputs include a clock signal, a reset signal, a write enable signal, the data to be written, and a read enable signal. The outputs of the module include the data read from the FIFO, a signal indicating whether the FIFO is full, and a signal indicating whether the FIFO is empty.

Inside the module, there is an array of memory elements that stores the data in the FIFO. There are two pointer variables, wrPtr and rdPtr, that keep track of the write and read positions in the FIFO, respectively. These pointers have one more bit than the PtrWidth parameter, known as the wrap bit. This is because when the pointer increments and reaches the maximum value, it wraps around to the beginning of the FIFO. The always_comb block updates the next values of the write and read pointers based on the current values and the write and read enable signals. If the write enable signal is high, the write pointer is incremented by one. If the read enable signal is high, the read pointer is incremented by one.

The always_ff block updates the write and read pointers on the rising edge of the clock. If the reset signal is low, the pointers are set to 0. Otherwise, the pointers are updated to their next values. Additionally, the writeData input is written to the memory element at the position specified by the write pointer.

The assign statements are used to compute the outputs of the module. The readData output is assigned to the memory element at the position specified by the read pointer. The empty output is computed by checking if the write and read pointers are equal. The full output is computed by checking if the write and read pointers differ in their most significant bit (the wrap bit) but are equal in their lower bits. However, it should be noted that if the FIFO is full and an external signal still writes data to it, it might break the design or system.

Conclusion

A synchronous FIFO buffer is an essential component in many digital systems that require data to be processed sequentially. By implementing a synchronous FIFO buffer in Verilog and SystemVerilog, you can ensure efficient data processing and transmission in your digital system. With the basic design elements of a read pointer, write pointer, and storage array, as well as control logic and advanced features such as almost full and almost empty indicators, a synchronous FIFO buffer can be customized to fit the requirements of your specific digital system.