Verilog function module – asynchronous FIFO with different read and write bit widths

Table of contents of FIFO series articles:

Verilog function module – asynchronous FIFO-CSDN blog

Verilog function module – synchronous FIFO-CSDN blog

Verilog function module – asynchronous FIFO with different read and write bit widths-CSDN Blog

Verilog function module – synchronous FIFO with different read and write bit widths-CSDN Blog

Verilog function module – standard FIFO to FWFT FIFO-CSDN blog


Preface

The previous blog post has talked about asynchronous FIFO and synchronous FIFO, but it has not implemented the read-write bit width conversion function of FIFO. This function is one of the main functions of FIFO and is widely used. Therefore, based on the previous two modules, This article uses pure Verilog to implement FIFOs with different read and write bit widths, and simulates to verify the correctness of the design.


1. Implementation ideas

Idea:

  1. According to the relationship between the read and write data bit widths, there are two situations: when the read bit width > the write bit width, the data is combined; when the read bit width

  2. Synchronous FIFO is used as a buffer, the depth is fixed at 2, asynchronous FIFO is used as the main body, and the depth is the set depth.

  3. When the read bit width ≥ write bit width, the read port logic does not need to be concerned. The synchronous FIFO clock and the asynchronous FIFO write clock are the same clock. As long as there is data in the synchronous FIFO, it is read immediately, and the asynchronous FIFP is written after combination, so the write port The full signal will only be set high when two more data are written after the asynchronous FIFO is full. At this time, there is no need to worry about the synchronous FIFO because the depth of 2 will be filled quickly.

  4. When the read bit width

Notice:

  1. Because the main body of the module is still an asynchronous FIFO, the “false full” and “false empty” problems of the asynchronous FIFO still exist and do not affect the function.
  2. The actual capacity of the FIFO is always larger than the set capacity. The difference is two small bit widths (read/write) of data. This does not affect the function.


2. Module functional block diagram and signal description

Signal description:

Classification Signal name Input/Output Description
Parameter DIN_WIDTH Input data bit width
DOUT_WIDTH Output data bit width
WADDR_WIDTH Write address bit Width, FIFO depth=2**WADDR_WIDTH
FWFT_EN First word fall-through output mode is enabled, high level is active
MSB_FIFO 1 (default value) means high-order first-in-first-out, 0 means low first in first out
For example, if 4 bits are input and 8 bits are output, the first input is considered to be the high 4 bits of 8 bits.
The next input is considered to be the lower 4 bits of 8bit.
In the same way, if you input 8 bits and output 4 bits, the high 4 bits of the 8 bits will be output first.
What is output next is the lower 4 bits of 8bit.
Vivado FIFO only has high-order first-in-first-out
FIFO write port din input FIFO data input
full output FIFO full signal
wr_en input FIFO write enable
wr_clk input FIFO write clock
wr_rst input FIFO write reset
almost_full output FIFO almost full signal, set high when FIFO remaining capacity <=1
FIFO read port dout output FIFO data output
empty output FIFO empty signal
rd_en input FIFO read enable
rd_clk input FIFO read clock
rd_rst input FIFO read reset
almost_empty output FIFO fast empty signal, FIFO Set high when data amount <=1

Notice:

  1. The naming of the signals is exactly the same as the FIFO IP core in Vivado

  2. All resets are high-level resets, consistent with the FIFO IP core in Vivado

  3. Reset is an asynchronous reset. Write reset and read reset can share the same signal or can be separated.

  4. The FIFO depth is set by WADDR_WIDTH, so the FIFO depth must be an index of 2, such as 8, 16, 32, etc.

  5. The multiple relationship between DIN_WIDTH and DOUT_WIDTH must be 2 to the nth power, such as 2 times, 4 times, 8 times, and cannot be 3 times or 6 times.

  6. WADDR_WIDTH must be ≥ 3, and RADDR_WIDTH = WADDR_WIDTH + log2(DIN_WIDTH / DOUT_WIDTH) must also be ≥ 3

    A limit case, DIN_WIDTH = 4, DOUT_WIDTH=16, WADDR_WIDTH=5, RADDR_WIDTH =5 + log2(4/16)=3

  7. MSB_FIFO is used to set the high-bit/low-bit first-in-first-out. It is not the same concept as the generally talked about FIFO big-endian and little-endian modes.


3. Partial code display

//~ If the read bit width is greater than the write bit width, the data needs to be combined, and the combined data into one data is written to the read side FIFO.
if (DOUT_WIDTH >= DIN_WIDTH) begin
  wire clk = wr_clk;
  wire rst = wr_rst;
  wire wdata_almost_full;

  syncFIFO # (
    .DATA_WIDTH (DIN_WIDTH),
    .ADDR_WIDTH (1 ),
    .FWFT_EN (1)
  ) syncFIFO_inst (
    .din (din ),
    .wr_en (wr_en ),
    .full (full),
    .almost_full (wdata_almost_full),
    .dout(wdata),
    .rd_en (wdata_rd_en),
    .empty (wdata_empty),
    .almost_empty ( ),
    .clk (clk),
    .rst (rst)
  );

  assign almost_full = (wdata_almost_full & amp; & amp; rdata_full) || full;


  localparam RADDR_WIDTH = $clog2(2**WADDR_WIDTH * DIN_WIDTH / DOUT_WIDTH);

  asyncFIFO # (
    .DATA_WIDTH (DOUT_WIDTH ),
    .ADDR_WIDTH (RADDR_WIDTH),
    .FWFT_EN (FWFT_EN )
  ) asyncFIFO_inst (
    .din(rdata),
    .wr_en (rdata_wr_en ),
    .full (rdata_full ),
    .almost_full ( ),
    .wr_clk (clk),
    .wr_rst (rst),
    .dout (dout),
    .rd_en (rd_en ),
    .empty (empty),
    .almost_empty (almost_empty),
    .rd_clk (rd_clk),
    .rd_rst (rd_rst)
  );

  // Read the write-side FIFO when the read-side FIFO is not full and the write-side FIFO is not empty.
  assign wdata_rd_en = ~rdata_full & amp; & amp; ~wdata_empty;

  reg [DOUT_WIDTH-1:0] rdata_r;

  if (MSB_FIFO == 1) begin
    always @(posedge clk or posedge rst) begin
      if(rst)
        rdata_r <= 'd0;
      else if (wdata_rd_en)
        rdata_r <= {rdata_r[DOUT_WIDTH-DIN_WIDTH-1:0], wdata}; // The advanced bit is the high bit
      else
        rdata_r <= rdata_r;
    end
    assign rdata = {rdata_r[DOUT_WIDTH-DIN_WIDTH-1:0], wdata}; // The advanced bit is the high bit
  end
  else begin
    always @(posedge clk or posedge rst) begin
      if(rst)
        rdata_r <= 'd0;
      else if (wdata_rd_en)
        rdata_r <= {wdata, rdata_r[DOUT_WIDTH-1 : DIN_WIDTH]}; // The advanced one is the low bit
      else
        rdata_r <= rdata_r;
    end
    assign rdata = {wdata, rdata_r[DOUT_WIDTH-1 : DIN_WIDTH]}; // The advanced one is the low bit
  end

  localparam WDATA_RD_EN_CNT_MAX = DOUT_WIDTH / DIN_WIDTH - 1;
  reg [$clog2(WDATA_RD_EN_CNT_MAX + 1)-1 : 0] wdata_rd_en_cnt;
  always @(posedge clk or posedge rst) begin
    if(rst)
      wdata_rd_en_cnt <= 'd0;
    else if (wdata_rd_en)
      wdata_rd_en_cnt <= wdata_rd_en_cnt + 1'b1;
    else
      wdata_rd_en_cnt <= wdata_rd_en_cnt;
  end
  
  assign rdata_wr_en = wdata_rd_en & amp; & amp; wdata_rd_en_cnt == WDATA_RD_EN_CNT_MAX;
end


4. Functional simulation

The simulation is basically the same as the previous asynchronous FIFO situation, except that the read and write data bit widths are set to different values.

The testbench is basically the same as the previous asynchronous FIFO. There are also projects shared at the end of the article, which students can check by themselves.

Write 4bit, write depth 64, read 16bit, FWFT FIFO simulation, the waveform is as follows:

It can be seen that after writing four 4-bit data, empty is pulled low after delaying two rising edges of the read clock, and the data becomes 16’h0123. After reading, the data becomes 16’h4567, and the read port logic is normal. The Vivado FIFO IP’s empty signal has a large delay in pulling low, but it also ensures that the data is valid when empty is pulled low.

As can be seen from the above figure, the full signal and almost_full signal can be set high normally, and then read one data and delay the rising edges of two write clocks to pull them low together. Because reading data once is equivalent to 4 write data, almost_full It is pulled down together with full, which is also correct. The actual depth of the Vivado FIFO IP is larger than the set 64, which is why the full value of this IP is set later than the full value of the module.

It can be seen that the read data of the module is the same as the read data of Vivado FIFO IP. At the end, after the module FIFO reads 16’h0123, empty is set high. Because of the greater depth of Vivado IP, empty is set high later, and the read port can be seen. behavior is correct.

Due to space constraints, simulations under other conditions are no longer shown. Interested students can verify it themselves by changing the testbench.

  1. Change the frequency of the read and write clocks. The above simulation is that the write clock frequency is greater than the read clock frequency.
  2. Change FWFT_EN to 0, and be careful to modify the configuration of Vivado FIFO simultaneously.
  3. Verify that the write data bit width > the read data bit width


5. Project sharing

Verilog function module – asynchronous FIFO with different read and write bit widths, Vivado 2021.2 project.

Welcome everyone to follow my public account: Xu Xiaokang’s blog, and reply to the following four-digit number to get it.

8303

It is recommended to copy it to avoid typos!

Or get it from my code cloud warehouse, portal:

Xu Xiaokang/Verilog Function Module – Code Cloud – Open Source China (gitee.com)

Xu Xiaokang’s blog continues to share high-quality hardware, FPGA and embedded knowledge, software, tools and other content. Everyone is welcome to pay attention.