01signal.com

Using FIFOs on Versal APAC FPGAs

Where's the FIFO Generator?

When using Versal FPGAs, Xilinx provides no IP block that implements a plain FIFO which can be used in an instantiation in Verilog or VHDL. The well-known FIFO Generator IP is not available for these FPGAs. This is the case with Vivado 2022.1 and earlier, at least.

Therefore, if the FPGA part of an existing FPGA design is changed to a Versal FPGA, it's likely that some changes to the projects will be necessary. Almost all FPGA projects use the FIFO Generator.

The same goes for the Memory Generator: The IP that is sometimes used to create block RAMs has been eliminated on Versal FPGAs as well. This IP is however less used, because block RAMs are often created by virtue of inference. In other words, an array in Verilog (or VHDL) is usually declared for the purpose of creating a RAM.

Note that the FIFOs and RAMs that have an AXI interface are still available as IPs. The lack of support for Versal FPGAs relates only to FIFOs and block RAMs that have a "native" interface.

There are several options to work around this problem. I will suggest two possible ways.

For a general tutorial on FIFOs, refer to this series of pages.

Option #1: Use a Xilinx Parameterized Macros

Xilinx Parameterized Macros are available in Vivado for all FPGAs that are supported by this software (i.e. series-7 FPGAs and later). These macros can be used in instantiations, like any module in Verilog or VHDL. Their documentation can be found together with the FPGA's primitives (for example, in Versal Architecture Premium Series Libraries Guide, UG1485).

There is however an important difference between primitives and Parameterized Macros: When the synthesizer encounters a primitive, it usually produces only one logic element in the synthesized netlist. A Parameterized Macro, on the other hand, produces a whole lot of logic elements, just like a Verilog module.

There are several parameterized macros that produce FIFOs, in particular these two:

The FIFOs that are produced by these two macros have "native" interface, i.e. ports like wr_en and rd_en. Almost all options that were available with the FIFO Generator can be enabled by setting the macro's instantiation parameters. Among others, these FIFOs can be either "Standard FIFOs" or First Word Fall Through (FWFT).

There are also a few macros for implementing RAMs of different sorts. Regardless, there are also several AXI-Stream FIFOs of different kinds, but these can be created with IP blocks, even for Versal FPGAs.

For complete information on how to use these macros, refer to the relevant Libraries Guide, which is UG1485 for Versal FPGAs. It's also possible to obtain an instantiation template with Vivado: Select Tools > Language Templates and go to Verilog > Xilinx Parameterized Macros (XPM) > XPM > XPM_FIFO, and from there select the Synchronous FIFO or Asynchronous FIFO, as applicable.

This is an example of how to create a baseline synchronous FIFO, which does the same as the one that is shown on this page.

module fifo_8x2048
  #(parameter width = 8, depth = 2048)
   (
    input  clk,
    input  rst,
    input  wr_en,
    input [(width-1):0] din,
    input  rd_en,
    output [(width-1):0] dout,
    output full,
    output empty
    );

   xpm_fifo_sync #(
		   .DOUT_RESET_VALUE("0"),
		   .ECC_MODE("no_ecc"),
		   .FIFO_MEMORY_TYPE("auto"),
		   .FIFO_READ_LATENCY(1),
		   .FIFO_WRITE_DEPTH(depth),
		   .FULL_RESET_VALUE(1),
		   .READ_DATA_WIDTH(width),
		   .READ_MODE("std"),
		   .SIM_ASSERT_CHK(0),
		   .USE_ADV_FEATURES("0000"),
		   .WAKEUP_TIME(0),
		   .WRITE_DATA_WIDTH(width)
		   )
   xpm_fifo_sync_inst (
      .almost_empty(),
      .almost_full(),
      .data_valid(),
      .dbiterr(),
      .dout(dout),
      .empty(empty),
      .full(full),
      .overflow(),
      .prog_empty(),
      .prog_full(),
      .rd_data_count(),
      .rd_rst_busy(),
      .sbiterr(),
      .underflow(),
      .wr_ack(),
      .wr_data_count(),
      .wr_rst_busy(),
      .din(din),
      .injectdbiterr(1'b0),
      .injectsbiterr(1'b0),
      .rd_en(rd_en),
      .rst(rst),
      .sleep(1'b0),
      .wr_clk(clk),
      .wr_en(wr_en)
   );
endmodule

Option #2: The Embedded FIFO Generator

The Embedded FIFO Generator is practically the same as the well-known FIFO Generator, but the Embedded FIFO Generator can only be used inside a block design.

So the obvious solution is to create a block design and add an Embedded FIFO Generator. Then set up the parameters of this IP block exactly like the previous FIFO Generator. After that, connect all of the FIFO's ports to external ports. The result is a block design that has ports like a FIFO, and behaves like a FIFO, so it's a FIFO.

This solution is a bit ugly, in particular because the names of the FIFO's ports become longer than usual. For example, instead of wr_en, there name is FIFO_WRITE_0_wr_en.

Conclusion

Even though the disappearance of the FIFO Generator from Vivado's IP Catalog is sudden and unexplainable, it's usually not difficult to replace this IP with a Parameterized Macro or a block design.

In a broader context, it's maybe a good idea to stop using the FIFO Generator even for FPGAs that support it, regardless of how this affects the possibility to migrate the design to Versal FPGAs.

Copyright © 2021-2023. All rights reserved. (e7386b71)