Uso de BRAM como búfer

Estoy tratando de implementar un búfer para una canalización de procesamiento de imágenes y necesito cargar datos en BRAM.

He estado siguiendo un tutorial en línea ( https://www.youtube.com/watch?v=n35zS__YEFQ ) para implementar diferentes partes del sistema, pero después de la síntesis y la implementación, puedo ver que mi código usa LUTRAM en lugar de BRAM. Intenté buscar en las plantillas de idioma proporcionadas por vivado para inferir BRAM de puerto único y para mí el código se ve muy similar, pero soy nuevo en FPGA y no sé mucho, así que no estoy completamente seguro de cómo puedo forzar BRAM para ser inferido en lugar de LUTRAM.

Aquí está mi código Verilog:

module buffer #(parameter NUM_ELEMENTS=10, ADDRESS_BITS=4, WIDTH=8) (
    input clk,
    input rst,
    input [WIDTH-1:0] dataIn,
    input validInput,
    input readReady,
    output wire [WIDTH-1:0] dataOut
    );
    
    reg [WIDTH-1:0] mem [NUM_ELEMENTS-1:0];
    reg [ADDRESS_BITS-1:0] wrPtr;
    reg [ADDRESS_BITS-1:0] rdPtr;
    
    always @ (posedge clk) begin
        if (rst) begin
            wrPtr <= 0;
        end else if (validInput) begin
            mem[wrPtr] <= dataIn;
            wrPtr <= wrPtr + 1;
        end
    end
    
    assign dataOut = mem[rdPtr];
    
    always @ (negedge clk) begin
        if (rst) begin
            rdPtr <= 0;
        end else if (readReady) begin   
            rdPtr <= rdPtr + 1;
        end
    end
    
endmodule

la simulación del módulo muestra que funciona correctamente como se esperaba (debo ocuparme de algunos casos extremos en los que el puntero de lectura pasa el final del búfer, más sobre eso más adelante).

mi código de banco de pruebas es:

`timescale 1ns / 1ps

module buffer_tb();
    reg clk;
    reg rst;
    reg [buffer.WIDTH-1:0] dataIn;
    reg validInput;
    reg readReady;
    wire [buffer.WIDTH-1:0] dataOut;
    
    integer i;
    
    buffer dut (clk,rst,dataIn,validInput,readReady,dataOut);
    
    initial clk = 1;
    always #5 clk = ~clk;
    
    initial begin
        rst = 1;
        dataIn = 0;
        validInput = 0;
        readReady = 0;
        #10;
        rst = 0;
        validInput = 1;
        for (i=0; i<10; i = i+1) begin
            dataIn = 2*i;
            #10;
        end
        validInput = 0;
        #40;
        readReady = 1;
        #100;
        readReady = 0;
        $stop;
    end
    
endmodule

y la salida de la simulación es:ingrese la descripción de la imagen aquí

pero al abrir el resumen del proyecto o el diseño elaborado se muestra el uso incorrecto de LUTRAM:

ingrese la descripción de la imagen aquí

Vuelvo a enfatizar que soy un principiante total y recién comencé a usar FPGA; Después de buscar un poco en las guías de xilinx, todavía no puedo encontrar ninguna forma de especificar el tipo de RAM, solo que se puede inferir al usar una sintaxis muy específica, lo que parece extraño y no he logrado que funcione.

Mis preguntas principales son:

  1. ¿Cómo puedes inferir BRAM en lugar de LUT? ¿Tengo que copiar el código de ejemplo sugerido línea por línea o se permiten cambios?
  2. si tengo que copiar los códigos de la plantilla directamente, ¿sería mejor usar el generador central de IP para instanciar BRAM en el diseño del bloque y escribir un controlador para él (el módulo de búfer actúa como un envoltorio), o si usar la plantilla de código real? ? para ser honesto, ni siquiera sé cuáles son las diferencias. según tengo entendido, el diseño del bloque se convierte en verilog de todos modos.
  3. Solo necesito escribir en los búferes hasta que estén llenos, luego leerlos hasta que estén vacíos. ¿Debería usar un FIFO en lugar de intentar implementar mi propio código de búfer? De la forma en que entendí el tutorial en línea para el procesamiento de imágenes, puedo crear más búferes de los necesarios y llenarlos mientras procesa otras partes de los datos, luego multiplexar los búferes, pero no estoy seguro de cuáles son las ventajas y desventajas de un bloque FIFO. , en comparación con un búfer escrito a mano.
Antes de eso, ¿calculó cuánto bram se necesita para un búfer de cuadro y cuánto hay disponible? Un típico FPGA xilinx de gama baja de la serie 7 no tiene suficiente bram para incorporar, por ejemplo, un marco VGA de 640x480.
Sí, periódicamente llenaré el búfer con datos de los módulos QSPI que me brindan suficiente capacidad para toda la entrada de datos. la imagen real se cargará a través de DMA desde un procesador ZYNQ, pero eso será en el futuro. Ni siquiera puedo hacer que el búfer funcione como quiero :(
DMA+DDR+Procesador es la forma en que normalmente se implementan los motores de video en un FPGA. Los BRAM se pueden usar para implementar búferes de línea o FIFO en la tubería de procesamiento de video/imagen. Pero no son lo suficientemente grandes en la mayoría de los FPGA para los búferes de cuadros, especialmente si se necesita al menos un búfer doble. Y sí, debe seguir las pautas y los atributos de codificación para inferir correctamente la BRAM usando RTL.

Respuestas (1)

Si solo necesita un búfer pequeño (<16 entradas de datos), no hay absolutamente ninguna razón para preferir BRAM a LUTRAM.

Los BRAM están ubicados en áreas específicas del chip, lo que puede significar que se requieren rutas relativamente largas para llegar a ellos desde su otra lógica. LUTRAM siempre se puede ubicar cerca de la lógica que lo usa.

Por otro lado, los búferes más grandes (>64 entradas de datos) casi siempre deducirán BRAM en lugar de LUTRAM. También hay directivas de síntesis que pueden forzar la elección de una forma u otra. Consulte la documentación de las herramientas que está utilizando.

Y sí, los FIFO se usan mucho más comúnmente en el procesamiento de video. He escrito módulos genéricos para FIFO de reloj único (sincrónico) y de reloj dual (asincrónico) que uso todo el tiempo en mis diseños de canalización de video. Permito que las herramientas de síntesis infieran el tipo apropiado de RAM para el tamaño FIFO especificado. Si necesita el máximo rendimiento absoluto, utilice el generador de IP del proveedor, que aprovechará al máximo cualquier lógica de soporte especializada que esté en el chip.

(* ram_style = "block" *) es la directiva en Verilog.
desafortunadamente, incluso usar la directiva y aumentar la cantidad de elementos a 1024 aún no da como resultado el uso de BRAM :( Volveré y comenzaré a modificar los ejemplos provistos línea por línea para ver dónde está mi error, pero tener que ejecutar la síntesis cada vez verificar dos veces lleva un poco de tiempo.