09-11-2014 06:04 AM
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
09-12-2014 12:53 AM
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 : 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)
09-11-2014 01:02 PM
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.
09-12-2014 12:53 AM
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 : 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)