Inferir BRAM con direcciones no utilizadas de manera eficiente

¿Cuál es una forma correcta de inferir una RAM con algunas direcciones superiores no utilizadas (usando RAM de bloque)?
Usando el código a continuación (valores predeterminados para genéricos, sintetizador Xilinx y mapa) obtengo una RAM del mismo tamaño que si la profundidad estuviera configurada en 2**ADDRWIDTH:

entity foo is
    generic (
        DATAWIDTH : positive := 8;
        DATADEPTH : positive := 5000;
        ADDRWIDTH : positive := 13
    );
    port (
        clk_a  : in std_logic;
        we_a   : in std_logic;
        addr_a : in std_logic_vector(ADDRWIDTH-1 downto 0);
        di_a   : in std_logic_vector(DATAWIDTH-1 downto 0);
        do_a   : out std_logic_vector(DATAWIDTH-1 downto 0)
    );
end foo;

architecture bar of foo is
    type myram_type is array (DATADEPTH-1 downto 0) of std_logic_vector(DATAWIDTH-1 downto 0); --! type for ram content
    shared variable myram : myram_type; --! ram
begin
    process (clk_a)
    begin
        if rising_edge(clk_a) then
            if we_a = '1' then
                myram(conv_integer(addr_a)) := di_a;
            end if;
            do_a <= myram(conv_integer(addr_a));
        end if;
    end process;
end bar;

Por ejemplo, quiero una RAM con DATAWIDTH = 8y DATADEPTH = 5000, por lo que la dirección debe ser ADDRWIDTH = 13porque ADDRWIDTH = 12solo permitiría direccionar 4096 ubicaciones de RAM. Supongamos que un recurso de RAM de bloque en mi FPGA puede contener 8192 bits. Si codificaba esto a mano, necesitaba 5000*8/8192 redondeado hacia arriba = 5 bloques de recursos de RAM. Sin embargo, con el código anterior, la síntesis y el mapa de Xilinx dan como resultado el uso de 8 recursos RAM de bloque, porque eso es lo que se puede abordar con direcciones de 13 bits de ancho... Sin embargo, este no
es un uso realmente eficiente de los recursos ya que 3 de los 8 las memorias RAM de bloque nunca se utilizarán.
Traté de verificar si la dirección en la entrada es más grande que DATADEPTHy luego asignar no se preocupa por los datos, pero eso da como resultado que todo el ram se implemente como RAM / LUTRAM distribuido.
¿Me estoy perdiendo algo importante o tengo que usar una gran generación fea para esto?

Respuestas (3)

En realidad, usar 8 BRAM en una configuración de 8K×1, en lugar de 5 BRAM en una configuración de 1K×8, es más eficiente en varias formas importantes.

Con las 8 BRAM, simplemente puede conectar todas las líneas de dirección y control a todas las BRAM, y un bit de los buses de entrada y salida de datos a cada una de las BRAM. No se requiere ninguna otra lógica en absoluto.

Por otro lado, con la configuración 5-BRAM, necesitará lógica adicional para decodificar los 3 bits de dirección superiores para habilitar una BRAM a la vez, y también necesitará un multiplexor 5:1 en el bus de salida de datos para seleccione los datos de la BRAM correcta al leer. Esto utiliza recursos adicionales dentro de la FPGA y también afecta negativamente el tiempo, lo que reduce la frecuencia de reloj máxima que puede usar.

Si realmente necesita usar la capacidad BRAM de la manera más eficiente posible, y no le importan los problemas de tiempo y recursos, entonces tendrá que codificar explícitamente su memoria como un módulo que usa cinco memorias 1K×8 internamente.

Una mejor configuración de 5-BRAM en este caso sería cuatro 4Kx2 y una 1Kx8, con un multiplexor de salida 2:1 y un decodificador de bit de dirección única para escritura.

Inferir módulos duros (como bloques de RAM) del código es una propuesta bastante frágil, por lo que los sintetizadores generalmente brindan pautas de codificación, de las cuales supongo que usó las "Pautas de codificación del lenguaje de descripción de hardware (HDL) de RAM de Xilinx".

Al leer esas pautas y darse cuenta de que ciertos estilos de codificación que parecen perfectamente válidos (como el uso de señales en lugar de variables compartidas para una RAM de doble puerto) en realidad no funcionarán, tiene una idea de lo limitadas que son las técnicas de inferencia.

Por lo tanto, creo que existe tal limitación con Xilinx, o la herramienta elige no minimizar el uso de BRAM a favor de otras optimizaciones (como se menciona en la respuesta de Dave Tweed).

Así que parece que desafortunadamente estás limitado a:

  • Conexión en cascada explícita de las BRAM requeridas.
  • Múltiples recuerdos en cascada codificados en la forma simplista de 'guía'.
  • Usando el generador CORE.
  • Dejar que los BRAM adicionales se desperdicien y seguir adelante; tal vez cuando las condiciones sean las adecuadas, la herramienta hará lo que esperaba y usará 5 BRAM.

Si necesita usar 13 bits de dirección para su RAM, entonces esas 13 señales de dirección física para una BRAM deben asignarse a su RAM. Las líneas de dirección no se pueden compartir fácilmente con algún otro módulo, por lo que efectivamente obtiene toda la RAM física a la que acceden esas 13 líneas de dirección. Si necesita 5000 palabras, obtendrá 8192 palabras de BRAM. Como notó, si sintetiza la RAM a partir de LUT, puede hacer una RAM con solo 5000 palabras pero pierde la eficiencia de la BRAM.