inicialización de FPGA BRAM

1

Necesito crear muchos bloques BRAM en mi diseño (Altera). Cada uno tiene un contenido de memoria único, determinado a priori mediante un algoritmo.

Antes, estaba configurando un parámetro para que cada celda BRAM leyera desde un archivo .MIF, pero esto hizo que mi tiempo de compilación durara para siempre.

Otro enfoque que inventé fue permitir la población "dinámica" de la memoria; el controlador del host podría enviar símbolos al FPGA para rellenar sus bloques BRAM. Esto es un poco más complicado de lo que me gustaría.

Esperaba que hubiera una forma de inicializar los BRAM con un literal. Cada bloque tiene solo 1 bit x 256, por lo que el código HDL resultante ni siquiera se vería que horrible.

¿Alguien sabe cómo hacer esto con el BRAM IP de Altera, o quizás incluso el de Xilinx?

ACTUALIZACIÓN 31/08/2016:

Hola, chicos, en realidad encontré una solución casi fácil para la inicialización de BRAM en Altera. En Quartus, hay plantillas integradas de VHDL y Verilog que pueden inferir automáticamente BRAM. Estas plantillas tienen utilidades de inicialización de memoria integradas que el usuario puede modificar para rellenar con los datos que desee (como un vector de bits de un genérico). Consulte esta página de ayuda de Quartus .

    
pregunta Ted X

5 respuestas

2

Definitivamente es posible inferir RAM de bloque directamente desde HDL. Dependiendo del tamaño y la configuración, la cadena de herramientas podría ubicarlos en el bloque RAM o en el RAM distribuido.

Aquí hay un ejemplo muy simple de verilog:

module rom
(
    input clk,
    input [3:0] addr,
    output [7:0] data
);

reg [7:0] mem[2**4-1:0];
reg [7:0] output_reg = 8'd0;

initial begin
    mem[4'h0] = 8'h00;
    // ...
    mem[4'hF] = 8'h00;
end

assign data = output_reg;

always @(posedge clk) begin
    output_reg <= mem[addr];
end

endmodule

He utilizado código similar en las cadenas de herramientas de Xilinx y Altera, y en general ambas herramientas deducirán los componentes de RAM adecuados. Esto también funciona para RAMs. Y se puede usar para almacenar valores en una tabla de búsqueda que se calculan en el momento de la síntesis, aunque Quartus históricamente ha tenido algunos problemas serios con esto, a saber, hacer punto flotante / trigonometría en el momento de la síntesis.

Ejemplo de contenido de ROM precomputado en verilog: enlace

    
respondido por el alex.forencich
1

Qué gracioso debes preguntar. Recientemente he estado luchando con la cadena de herramientas Vivado de Xilinx, intentando migrar un viejo diseño ISE que incluye algunas ROM basadas en BRAM cuyo contenido está definido por archivos .coe (coeficiente).

Finalmente tuve que renunciar a él: las herramientas del generador IP de Vivado siguieron perdiendo el rastro de los archivos .coe, lo que provocó que la síntesis fallara por completo. Nunca encontré una solución que funcionara dos veces seguidas ...

Terminé escribiendo una secuencia de comandos de Perl relativamente simple que convierte un archivo .coe en una declaración case de RTL pura, y eso ha funcionado bien. Vivado infiere correctamente una ROM basada en BRAM y todo está bien. No tengo el script en esta máquina en este momento, pero si está interesado en él, lo adjuntaré más adelante. Sería fácil adaptarlo para tomar archivos .mif como entrada también.

Aquí hay un fragmento de la documentación del script que explica lo que hace:

# The standard .coe file contains two statements:
# =========
# memory_initialization_radix=2;
# memory_initialization_vector=
# 0000000000000000,
# 0000010100000010,
# ....
# 0000000000000000;
# =========
# The argument to memory_initialization_radix gives the radix (in decimal)
# used for the individual values of the memory_initialization_vector list.
#
# For now, we're just going to make the following assumptions:
# * The .coe file is laid out exactly as shown above, with one word per line.
# * The radix is either 2 or 16.
# * The number of words indicates the address size of the ROM.
# * The width of the words indicates the data size of the ROM.
#
# We will convert the .coe data into a Verilog source file with the following
# structure:
# 
# module <filename> (
#   input           [5:0] addra,
#   output reg     [15:0] douta,
#   input                 clka
# );
# 
#   always @(posedge clka) begin
#     case (addra)
#     6'h00: douta <= 16'b0000000000000000;
#     6'h01: douta <= 16'b0000010100000010;
#     ....
#     6'h3F: douta <= 16'b0000000000000000;
#     endcase
#   end
# endmodule

Tenga en cuenta que los nombres de los puertos son los mismos que los utilizados por el generador de IP, por lo que este módulo de código de comportamiento es un reemplazo directo del módulo generado.

    
respondido por el Dave Tweed
1

Siempre puede crear una instancia de BRAM utilizando el atributo 'bloque' para una declaración de memoria (Esto infiere BRAM en la arquitectura FPGA). No necesita ningún IP Core para hacer esto y puede inicializarlo con cualquier dato que desee, de la misma manera que lo asignaría a una señal o vector.

He estado usando esto para Xilinx FPGA desde hace algún tiempo y creo que esto también funcionará para el dominio Altera, ya que es un fragmento de código y no un parámetro central de IP.

    
respondido por el samjay
0

He utilizado los BRAM de Xilinx inicializados con constantes contenidas en el flujo de programación. Eso fue en los días en que acababa de salir el V4, pero espero que no se haya eliminado la instalación con las herramientas actuales.

Hay un indicador en Xilinx CoreGen en la memoria que configuró en 'ROM' en lugar de 'RAM', y luego coloca las tablas de inicialización en línea en la VHDL para compilar.

No sé nada de Altera, pero es una facilidad tan obvia para proporcionar que debe estar allí, solo necesito buscarlo.

    
respondido por el Neil_UK
0

Además de las otras respuestas, también puede hacer la inicialización desde un solo parámetro. Algo como este ejemplo de Verilog debería ser suficiente:

module paramInitialisedROM #(
    parameter WIDTH = 1,
    parameter DEPTH = 256,
    parameter MEM_INIT = 256'd120310230123  //Or whatever, key thing is to make it the correct size (WIDTH*DEPTH).
)(
    input                  clock,
    input      [DEPTH-1:0] address,
    output reg [WIDTH-1:0] data
);

localparam WORDS = 1<<DEPTH; //Number of words in ROM.

// Create an inferred ROM of the correct size
reg [WIDTH-1:0] rom [WORDS-1:0]; //It is possible to add an altera derective 
                                 //to this to specify BRAM or MLAB, but I forget
                                 //the syntax of that.

// ROM Initialisation
integer idx;
integer offset;
initial begin
    for (idx = 0; idx < WORDS; idx=idx+1) begin //Count through each word in the rom
        offset = idx * WIDTH; //The offset into the parameter is the current index times the with
        rom[idx] = MEM_INIT[offset+:WIDTH]; //Set the current rom word to the correct chunk in the parameter
    end
end

//Clocked Read from ROM
always @ (posedge clock) begin
    data <= rom[address];
end

endmodule

Básicamente, cuando crea una instancia del módulo, asegúrese de proporcionar un parámetro del tamaño correcto. Por ejemplo, si lo hace 16x4b ROM, necesitaría que el parámetro se especifique como 64 bits o más, por ejemplo:

paramInitialisedROM aRomInstance #(
    .WIDTH(4),
    .DEPTH(4),
    .MEM_INIT(64'd120310230123)
)(
    .clock(clock),
    .address(address),
    .data(data)
);

Los datos en el parámetro se organizan de manera que se utilicen los WIDTH LSBs para la primera palabra. La siguiente palabra WIDTH es la siguiente palabra, y así sucesivamente hasta que se llenen todas las palabras.

El compilador optimizará el bucle for durante la inicialización, por lo que no le costará ninguna lógica. De hecho, todo el bloque initial se convierte en el valor inicial de la memoria.

Como se trata de memoria inferida, debería funcionar perfectamente bien en las herramientas Altera y Xilinx.

Nota: no he probado compilado esto, pero el principio debería funcionar bien. Si encuentra algún error de sintaxis, no dude en editar las correcciones.

    
respondido por el Tom Carpenter

Lea otras preguntas en las etiquetas