¿Dónde está el FIFO Generator?
Cuando se utilizan FPGAs Versal, AMD no ofrece ningún bloque de IP que implemente una FIFO sencilla que se pueda utilizar en una instanciación (instantiation) en Verilog o VHDL. La conocida IP FIFO Generator no está disponible para estas FPGAs. Esto es así, al menos, con Vivado 2022.1 y versiones anteriores.
Por lo tanto, si se cambia la FPGA de un diseño FPGA existente por una FPGA Versal, es muy probable que haya que hacer algunos cambios en los proyectos. Casi todos los proyectos con FPGA utilizan el FIFO Generator.
Lo mismo ocurre con el Memory Generator: la IP que a veces se utilizaba para crear RAMs de bloque (block RAMs) también ha desaparecido en las FPGAs Versal. Esta IP se usa, sin embargo, con menos frecuencia, porque las RAMs de bloque suelen crearse por inferencia. En otras palabras, normalmente se declara un array en Verilog (o VHDL) con el propósito de crear una RAM.
Ten en cuenta que las FIFOs y las RAMs con interfaz AXI siguen estando disponibles como bloques de IP. La falta de soporte para las FPGAs Versal afecta únicamente a las FIFOs y a las RAMs de bloque con interfaz “nativa”.
Hay varias opciones para solucionar este problema. Voy a proponer dos vías posibles.
Puedes consultar esta serie de páginas para un tutorial general sobre FIFOs.
Opción 1: Utilizar las macros parametrizadas de AMD
Las macros parametrizadas de AMD (AMD Parameterized Macros) están disponibles en Vivado para todas las FPGAs compatibles con este software (es decir, las FPGAs de la serie 7 y posteriores). Estas macros pueden utilizarse en instanciaciones (instantiation) como cualquier módulo en Verilog o VHDL. Su documentación se encuentra junto con las primitivas (primitive) de la FPGA (por ejemplo, en Versal Architecture Premium Series Libraries Guide, UG1485).
Hay, no obstante, una diferencia importante entre las primitivas y las macros parametrizadas: cuando el sintetizador (synthesizer) se encuentra con una primitiva, normalmente produce un único elemento lógico en la netlist sintetizada. Una macro parametrizada, en cambio, produce un montón de elementos lógicos, igual que un módulo Verilog.
Existen varias macros parametrizadas que generan FIFOs; en particular, estas dos:
- xpm_fifo_sync, que genera una FIFO síncrona (con un único reloj).
- xpm_fifo_async, que genera una FIFO asíncrona (de dos relojes).
Las FIFOs generadas por estas dos macros tienen interfaz “nativa”, es decir, puertos como wr_en y rd_en. Casi todas las opciones que estaban disponibles con el FIFO Generator pueden activarse mediante los parámetros de instanciación de la macro. Entre otras cosas, estas FIFOs pueden ser “FIFOs estándar” (Standard FIFO) o First Word Fall Through (FWFT).
También hay algunas macros más para implementar RAMs de distinto tipo. En cualquier caso, existen además varias FIFOs AXI-Stream de diferentes clases, pero estas sí pueden crearse con bloques de IP, incluso para FPGAs Versal.
Para obtener información completa sobre cómo utilizar estas macros, consulta la correspondiente Libraries Guide, que para las FPGAs Versal es UG1485. También es posible obtener una plantilla de instanciación (instantiation template) con Vivado: selecciona Tools > Language Templates y ve a Verilog > AMD Parameterized Macros (XPM) > XPM > XPM_FIFO; a continuación, elige Synchronous FIFO o Asynchronous FIFO, según corresponda.
Este es un ejemplo de cómo crear una FIFO síncrona básica, que hace lo mismo que la que se muestra en esta página.
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
Opción 2: El Embedded FIFO Generator
El Embedded FIFO Generator es prácticamente lo mismo que el conocido FIFO Generator, pero el Embedded FIFO Generator solo se puede utilizar dentro de un diseño de bloques (block design).
La solución evidente es, por tanto, crear un diseño de bloques y añadir un Embedded FIFO Generator. Después, ajusta los parámetros de este bloque de IP exactamente igual que con el anterior FIFO Generator. A continuación, conecta todos los puertos de la FIFO a puertos externos. El resultado es un diseño de bloques que tiene puertos como los de una FIFO y se comporta como una FIFO; por tanto, es una FIFO.
Esta solución es un poco fea, sobre todo porque los nombres de los puertos de la FIFO se vuelven más largos de lo habitual. Por ejemplo, en lugar de wr_en, el nombre del puerto es FIFO_WRITE_0_wr_en.
Conclusión
Aunque la desaparición del FIFO Generator del catálogo de IP (IP Catalog) de Vivado haya sido repentina e inexplicable, normalmente no es difícil sustituir esta IP por una macro parametrizada o por un diseño de bloques.
En un contexto más amplio, quizá sea buena idea dejar de utilizar el FIFO Generator, incluso en las FPGAs que lo soportan, independientemente de cómo afecte esto a la posibilidad de migrar el diseño a FPGAs Versal.