Verilog function module – synchronous 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, asynchronous FIFO with different read and write bit widths, and synchronous FIFO. This article uses pure Verilog to implement a synchronous FIFO with different read and write bit widths, and simulates to verify the correctness of the design.


1. Implementation ideas

The idea is exactly the same as the asynchronous FIFO with different read and write bit widths, except that the asynchronous FIFO submodule is replaced by the synchronous FIFO submodule.

Notice:

  1. Synchronous FIFO does not have “false full” or “false empty” problems
  2. The actual capacity of the FIFO is larger than the set capacity, and the difference is 2 small bits wide (read/write) data


2. Module functional block diagram and signal description

Signal description:

Classification Signal name Input/output Description
Parameters 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-order 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
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
almost_empty output FIFO fast empty signal, set high when the amount of data in FIFO <=1
Clock domain reset clk input FIFO clock
rst input FIFO reset

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 4, 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 ≥ 2, and RADDR_WIDTH = WADDR_WIDTH + log2(DIN_WIDTH / DOUT_WIDTH) must also be ≥ 2

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

  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 wdata_almost_full;
    syncFIFO # (
      .DATA_WIDTH (DIN_WIDTH),
      .ADDR_WIDTH (1 ),
      .FWFT_EN (1)
    ) syncFIFO_u0 (
      .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);
    syncFIFO # (
      .DATA_WIDTH (DOUT_WIDTH ),
      .ADDR_WIDTH (RADDR_WIDTH),
      .FWFT_EN (FWFT_EN )
    ) syncFIFO_u1 (
      .din(rdata),
      .wr_en (rdata_wr_en ),
      .full (rdata_full ),
      .almost_full ( ),
      .dout (dout),
      .rd_en (rd_en ),
      .empty (empty),
      .almost_empty (almost_empty),
      .clk (clk),
      .rst (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 situation of asynchronous FIFO with different read and write bit widths, except that the read and write clocks are the same.

testbench, there are also project sharing at the end of the article, students can check it out by themselves.

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

It can be seen that after writing two 4-bit data, empty is pulled low after delaying two rising edges of the read clock, and the data becomes 8’h01. After reading, empty is set high again, 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 2 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 32, 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 8’h23, empty is set high. Because of the greater depth of Vivado IP, empty is set high later. It can be seen that the read port 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 FWFT_EN to 0, and be careful to modify the configuration of Vivado FIFO simultaneously.
  2. Verify that the write data bit width > the read data bit width


5. Project sharing

Verilog functional module – synchronous 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.

8304

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.