cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Adventurer
Adventurer
7,498 Views
Registered: ‎11-10-2009

Inferred BRAM with initial value via generic (VHDL)

Jump to solution

Hello,

 

I am having a little trouble to add initialization data to my generic plain-hdl BRAM. The actual code looks as following:

 

LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;


ENTITY bram_plain_hdl IS
    GENERIC (
        G_DATA_WIDTH : integer := 16;
        G_ADDR_WIDTH : integer := 10
        );
    PORT (
        clk     : IN  std_logic;
        wr      : IN  std_logic;
        addr_wr : IN  std_logic_vector(G_ADDR_WIDTH - 1 DOWNTO 0);
        addr_rd : IN  std_logic_vector(G_ADDR_WIDTH - 1 DOWNTO 0);
        din     : IN  std_logic_vector(G_DATA_WIDTH - 1 DOWNTO 0);
        dout    : OUT std_logic_vector(G_DATA_WIDTH - 1 DOWNTO 0)
        );
END bram_plain_hdl;


ARCHITECTURE rtl OF bram_plain_hdl IS

    TYPE lookup_memory_array_t IS
        ARRAY (0 TO ((2**G_ADDR_WIDTH) - 1)) OF std_logic_vector(G_DATA_WIDTH - 1 DOWNTO 0);

    -- Shared memory
    SHARED VARIABLE mem : lookup_memory_array_t := (OTHERS => (OTHERS => '0'));
    SIGNAL memo : lookup_memory_array_t := (OTHERS => (OTHERS => '0'));

    SIGNAL dout_r : std_logic_vector(G_DATA_WIDTH - 1 DOWNTO 0) := (OTHERS => '0');

    ATTRIBUTE ram_style        : string;
    ATTRIBUTE ram_style OF mem : VARIABLE IS "block";

BEGIN

    PROCESS (clk)
    BEGIN
        IF (rising_edge(clk)) THEN
            IF (wr = '1') THEN
                mem(to_integer(unsigned(addr_wr))) := din;
                -- readable by ISIM:
                memo(to_integer(unsigned(addr_wr))) <= din;
            END IF;
            dout_r <= mem(to_integer(unsigned(addr_rd)));
        END IF;
    END PROCESS;

    dout <= dout_r;

END rtl;

I want that SHARED VARIABLE mem : lookup_memory_array_t := (OTHERS => (OTHERS => '0')); becomes something like SHARED VARIABLE mem : lookup_memory_array_t := G_BRAM_INIT

The problem I am facing is that I cannot declare a two dimensional array in the generics using the same values of the generics (G_DATA_WIDTH and G_ADDRESS_WIDTH) to describe its dimensions. A wrong way to describe what I want to do would be:

 

LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;

 

PACKAGE bram_plain_hdl_pkg IS

    TYPE init_data_t IS ARRAY (NATURAL RANGE <>) OF std_logic_vector;

END PACKAGE bram_plain_hdl_pkg;


--------------------------------------------------------------------------------
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;


ENTITY bram_plain_hdl IS
    GENERIC (
        G_DATA_WIDTH : integer := 16;
        G_ADDR_WIDTH : integer := 10;
        G_BRAM_INIT : init_data_t(0 to 2**G_ADDR_WIDTH - 1)(G_DATA_WIDTH - 1 DOWNTO 0) := (OTHERS => (OTHERS => '0'))
        );
    PORT (
        clk     : IN  std_logic;
        wr      : IN  std_logic;
        addr_wr : IN  std_logic_vector(G_ADDR_WIDTH - 1 DOWNTO 0);
        addr_rd : IN  std_logic_vector(G_ADDR_WIDTH - 1 DOWNTO 0);
        din     : IN  std_logic_vector(G_DATA_WIDTH - 1 DOWNTO 0);
        dout    : OUT std_logic_vector(G_DATA_WIDTH - 1 DOWNTO 0)
        );
END bram_plain_hdl;


ARCHITECTURE rtl OF bram_plain_hdl IS

    TYPE lookup_memory_array_t IS
        ARRAY (0 TO ((2**G_ADDR_WIDTH) - 1)) OF std_logic_vector(G_DATA_WIDTH - 1 DOWNTO 0);

    -- Shared memory
    SHARED VARIABLE mem : lookup_memory_array_t := G_BRAM_INIT;
    SIGNAL memo : lookup_memory_array_t := G_BRAM_INIT;

    SIGNAL dout_r : std_logic_vector(G_DATA_WIDTH - 1 DOWNTO 0) := (OTHERS => '0');

    ATTRIBUTE ram_style        : string;
    ATTRIBUTE ram_style OF mem : VARIABLE IS "block";

BEGIN

    PROCESS (clk)
    BEGIN
        IF (rising_edge(clk)) THEN
            IF (wr = '1') THEN
                mem(to_integer(unsigned(addr_wr))) := din;
                -- readable by ISIM:
                memo(to_integer(unsigned(addr_wr))) <= din;
            END IF;
            dout_r <= mem(to_integer(unsigned(addr_rd)));
        END IF;
    END PROCESS;

    dout <= dout_r;

END rtl;

 

I know this does not work but I wonder if there is a way to implement the same idea correctly. Of course the initial values I want to describe them when I instantiate my generic BRAM.

 

I thought about a less generic solution in which I hardcode the addres with, data width and initialization data dimensions but I plan to implement this component in various places in my design and the component will then not be suitable.

 

Thank you in advance

0 Kudos
Reply
1 Solution

Accepted Solutions
Adventurer
Adventurer
12,054 Views
Registered: ‎11-10-2009

Thank you for your idea! Yesterday I came with another solution which works better with my inner structure. I have quite some processing of those initial values in the VHDL sources and it matches better with that design if I pass those initializations through the generic port, as I initially intended.

 

I have come with the following workaround:

 

LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;

 

PACKAGE bram_plain_hdl_pkg IS

    --overdimensioned
    TYPE bram_init_t IS ARRAY(0 TO 4095) OF std_logic_vector(31 DOWNTO 0);

END PACKAGE bram_plain_hdl_pkg;

--------------------------------------------------------------------------------
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;

USE work.bram_plain_hdl_pkg.ALL;

ENTITY bram_plain_hdl IS
    GENERIC (
        G_DATA_WIDTH : integer := 16;
        G_ADDR_WIDTH : integer := 10;
        G_BRAM_INIT  : bram_init_t := (OTHERS => (OTHERS => '0'))
        );
    PORT (
        clk        : IN  std_logic;
        wr       : IN  std_logic;
        addr_wr : IN  std_logic_vector(G_ADDR_WIDTH - 1 DOWNTO 0);
        addr_rd : IN  std_logic_vector(G_ADDR_WIDTH - 1 DOWNTO 0);
        din     : IN  std_logic_vector(G_DATA_WIDTH - 1 DOWNTO 0);
        dout    : OUT std_logic_vector(G_DATA_WIDTH - 1 DOWNTO 0)
        );
END bram_plain_hdl;


ARCHITECTURE rtl OF bram_plain_hdl IS

    TYPE lookup_memory_array_t IS
        ARRAY (0 TO ((2**G_ADDR_WIDTH) - 1)) OF std_logic_vector(G_DATA_WIDTH - 1 DOWNTO 0);

 

    FUNCTION init_bram (bram_init_data &colon; IN bram_init_t) RETURN lookup_memory_array_t IS
        VARIABLE return_arg : lookup_memory_array_t := (OTHERS => (OTHERS => '0'));
    BEGIN
        FOR i IN 0 TO 2**G_ADDR_WIDTH - 1 LOOP
            return_arg(i) := bram_init_data(i)(G_DATA_WIDTH - 1 DOWNTO 0);
        END LOOP;
       
        RETURN return_arg;
    END FUNCTION init_bram;
   
    -- Shared memory
    SHARED VARIABLE mem : lookup_memory_array_t := init_bram(G_BRAM_INIT);
    SIGNAL memo : lookup_memory_array_t := init_bram(G_BRAM_INIT);

    SIGNAL dout_r : std_logic_vector(G_DATA_WIDTH - 1 DOWNTO 0) := (OTHERS => '0');

    ATTRIBUTE ram_style        : string;
    ATTRIBUTE ram_style OF mem : VARIABLE IS "block";
   
BEGIN

    PROCESS (clk)
    BEGIN
        IF (rising_edge(clk)) THEN
            IF (wr = '1') THEN
                mem(to_integer(unsigned(addr_wr))) := din;
                -- readable by ISIM:
                memo(to_integer(unsigned(addr_wr))) <= din;
            END IF;
            dout_r <= mem(to_integer(unsigned(addr_rd)));
        END IF;
    END PROCESS;

    dout <= dout_r;

END rtl;

 

Of course the "overdimension" of the initialization data is always very relative but it could be changed to higher values (with a certain tradeof in compilation time for simulation and synthesis time)

View solution in original post

0 Kudos
Reply
2 Replies
Professor
Professor
7,478 Views
Registered: ‎08-14-2007

When I do essentially the same thing in Verilog, I generally use an external text file to initialize the memory.  Then you only need a parameter (generic) for the initialization file name rather than the actual array values.  In Verilog, the $readmemh or $readmemb function will work on any array size as long as the supplied text file contains the correct number of values to fill the array.  I'm not that versed on VHDL, but I imagine there should be a similar solution using file I/O.

-- Gabor
Adventurer
Adventurer
12,055 Views
Registered: ‎11-10-2009

Thank you for your idea! Yesterday I came with another solution which works better with my inner structure. I have quite some processing of those initial values in the VHDL sources and it matches better with that design if I pass those initializations through the generic port, as I initially intended.

 

I have come with the following workaround:

 

LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;

 

PACKAGE bram_plain_hdl_pkg IS

    --overdimensioned
    TYPE bram_init_t IS ARRAY(0 TO 4095) OF std_logic_vector(31 DOWNTO 0);

END PACKAGE bram_plain_hdl_pkg;

--------------------------------------------------------------------------------
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;

USE work.bram_plain_hdl_pkg.ALL;

ENTITY bram_plain_hdl IS
    GENERIC (
        G_DATA_WIDTH : integer := 16;
        G_ADDR_WIDTH : integer := 10;
        G_BRAM_INIT  : bram_init_t := (OTHERS => (OTHERS => '0'))
        );
    PORT (
        clk        : IN  std_logic;
        wr       : IN  std_logic;
        addr_wr : IN  std_logic_vector(G_ADDR_WIDTH - 1 DOWNTO 0);
        addr_rd : IN  std_logic_vector(G_ADDR_WIDTH - 1 DOWNTO 0);
        din     : IN  std_logic_vector(G_DATA_WIDTH - 1 DOWNTO 0);
        dout    : OUT std_logic_vector(G_DATA_WIDTH - 1 DOWNTO 0)
        );
END bram_plain_hdl;


ARCHITECTURE rtl OF bram_plain_hdl IS

    TYPE lookup_memory_array_t IS
        ARRAY (0 TO ((2**G_ADDR_WIDTH) - 1)) OF std_logic_vector(G_DATA_WIDTH - 1 DOWNTO 0);

 

    FUNCTION init_bram (bram_init_data &colon; IN bram_init_t) RETURN lookup_memory_array_t IS
        VARIABLE return_arg : lookup_memory_array_t := (OTHERS => (OTHERS => '0'));
    BEGIN
        FOR i IN 0 TO 2**G_ADDR_WIDTH - 1 LOOP
            return_arg(i) := bram_init_data(i)(G_DATA_WIDTH - 1 DOWNTO 0);
        END LOOP;
       
        RETURN return_arg;
    END FUNCTION init_bram;
   
    -- Shared memory
    SHARED VARIABLE mem : lookup_memory_array_t := init_bram(G_BRAM_INIT);
    SIGNAL memo : lookup_memory_array_t := init_bram(G_BRAM_INIT);

    SIGNAL dout_r : std_logic_vector(G_DATA_WIDTH - 1 DOWNTO 0) := (OTHERS => '0');

    ATTRIBUTE ram_style        : string;
    ATTRIBUTE ram_style OF mem : VARIABLE IS "block";
   
BEGIN

    PROCESS (clk)
    BEGIN
        IF (rising_edge(clk)) THEN
            IF (wr = '1') THEN
                mem(to_integer(unsigned(addr_wr))) := din;
                -- readable by ISIM:
                memo(to_integer(unsigned(addr_wr))) <= din;
            END IF;
            dout_r <= mem(to_integer(unsigned(addr_rd)));
        END IF;
    END PROCESS;

    dout <= dout_r;

END rtl;

 

Of course the "overdimension" of the initialization data is always very relative but it could be changed to higher values (with a certain tradeof in compilation time for simulation and synthesis time)

View solution in original post

0 Kudos
Reply