cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
fvandesa
Contributor
Contributor
227 Views
Registered: ‎05-17-2018

initialization of inferred memories

Jump to solution

Hi, I'm trying to initialize inferred memories as follows:

 

 

    type RamType is array (0 to 2**15 - 1) of signed(31 downto 0);
    impure function InitRamFromFile(RamFileName : in string) return RamType is
        FILE RamFile         : text open read_mode is RamFileName;
        variable RamFileLine : line;
        variable RAM         : RamType;
    begin
        for I in RamType'range loop
            readline(RamFile, RamFileLine);
            hread(RamFileLine, RAM(I));
        end loop;
        return RAM;
    end function;
    signal rRAM_0     : RamType := InitRamFromFile("ram_0.data");

 

 

the ram_0.data file looks like this

 

 

...

f5555555
f2492492
f0000000
ee38e38e

...

 

 

The vivado GUI allows to me to add "Memory initialization files (.mif, mem)". 

Should I just rename ram_0,data to ram_0.mem? Or is .mem a different format? 

I gave it a try and renamed to ram_0.mem and got this

 

WARNING: [Synth 8-312] ignoring unsynthesizable construct: non-synthesizable procedure call

 

(this refers to hread  hread(RamFileLine, RAM(I));

Is this a warning that can be ignored, i.e. is the memory initialization taking place or not?

 

in general: 

  • How to initialize inferred memories in VHDL?
  • what file format and extension are needed?
  • how to add this to the project

 

thanks in advance,

Frank

 

0 Kudos
1 Solution

Accepted Solutions
maps-mpls
Mentor
Mentor
182 Views
Registered: ‎06-20-2017

What you posted looks good to me.  You can open the synthesized design, select the RAM in the netlist or on the schematic view (F4), and look at the properties to see if the memory was initialized.

Also, the error message makes me wonder if you also used a reset for your rRAM_0 signal.  If so, it will not be a RAM as so much as an array of resettable registers, and you cannot use a function to compute the value of reset in this way, only for initialization. 

The only other thing I can think of is I usually do this as a dual port, and so I use a shared variable.

The following is similar to what you did (maybe fancier, it was used to teach some concepts in VHDL).  File format is ADDR_IN_HEX DATA_IN_HEX.

Allows sparsely populated file, format is similar to:

000 7FFFFFFF
033 7FFFFFFE
088 7FFFFFFD

DP_memory.vhd

-- (C) 2020 Morgan Advanced Programmable Systems, Inc.
--
-- NOTES:
-- In order for assert statements to be processed during synthesis, you must enter the following
-- tcl command in Vivado:
--   o set_param synth.elaboration.rodinMoreOptions {rt::set_parameter ignoreVhdlAssertStmts false}
-- Most users oftend it more convient to just put this in the Vivado_init.tcl script in the
-- C:\Xilinx\Vivado\2020.1\scripts directory.  (Change path as necessary).  See
-- https://www.xilinx.com/support/answers/65415.html

use std.textio.all; -- 1993 version of VHDL standard
-- use std.textio.readline; -- procedure
-- use std.textio.read; -- procedure
-- use std.textio.line; -- type
library IEEE;
use IEEE.std_logic_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.std_logic_textio.all; -- std_logic_textio is squatter until 2008, required for reading std_logic_vector from text file
--use IEEE.std_logic_textio.hread;

use work.MAPS_utils_pkg.ALL;
-- require both to do file io with SLV

entity DP_memory is
generic (
  G_INIT_FILE : string  := string'("NULL");
  G_ADDR_BITS : integer := 10;
  G_DATA_BITS : integer := 32
);
port (
  -- Port A
  iClk_A  : IN  std_logic;
  iEN_A   : IN  std_logic;
  iWE_A   : IN  std_logic;
  iAddr_A : IN  std_logic_vector(G_ADDR_BITS-1 downto 0);
  iData_A : IN  std_logic_vector(G_DATA_BITS-1 downto 0);
  oData_A : OUT std_logic_vector(G_DATA_BITS-1 downto 0);
  -- Port B
  iClk_B  : IN  std_logic;
  iEN_B   : IN  std_logic;
  iWE_B   : IN  std_logic;
  iAddr_B : IN  std_logic_vector(G_ADDR_BITS-1 downto 0);
  iData_B : IN  std_logic_vector(G_DATA_BITS-1 downto 0);
  oData_B : OUT std_logic_vector(G_DATA_BITS-1 downto 0)
 );
end entity DP_memory;

architecture beh of DP_memory is
  type tMemArray is array (0 to 2**G_ADDR_BITS-1) of std_logic_vector(G_DATA_BITS-1 downto 0);

  -- funciton funcRMFF (Read Memory From File) can be used to initialize
  -- a memory of type tMemArray declared above, from the file specified.
  -- this is synthesizable only if used for initializtion.  Any other
  -- use is not synthesizable at the time of writing
  impure function funcRMFF(pFileName : string ; pA_bits : integer ; pD_bits : integer) return tMemArray is
    -- function declarative region
    file     fp           : text;    -- file pointer/handle
    variable vFOstatus    : file_open_status; -- see STD.STANARD package
    variable vRS          : boolean; -- value read successfully
    variable vLOT         : line;    -- line of text
    variable vData_SLV    : std_logic_vector(pD_bits-1 downto 0); -- data in std_logic_vector format
    variable vAddr_SLV    : std_logic_vector(((pA_bits+3)/4)*4-1 downto 0); -- addr in SLV format.
    variable vAddr        : integer; -- address as integer, for indexing memory
    variable vLineNum     : integer; -- keeps track of line number we're reading, for error messages
    -- initialize return value to all 0s, will be modified as directed by contents of memory initialization file
    variable vRetVal      : tMemArray := (others => (others => '0'));
  begin -- function

    -- NOTE assert statements in impure functions used to initialize shared variables do work in
    -- synthesis (if synthesis assert processing is enabled via tcl script) but do not work
    -- in simulation.  This seems to be a Vivado simulation bug.

    -- check for NULL string in pFileName, which is a name used to indicate no initialization
    -- of memory via file.
    if(pFileName = "NULL") then
      -- the following line is not necessary because the variable was already initilized to all 0s
      -- in the declaration above in the function declarative region.  Duplicated here for
      -- readability
      vRetVal := (others => (others => '0')); -- initialize the memory to all zeros
    else
      -- open the file with call to file_open using named parameter association
      file_open(
        status        => vFOstatus,
        f             => fp,
        external_name => pFileName,
        open_kind     => read_mode
      );
      --positional association call:  file_open(vFOstatus, fp, pFileName, read_mode);

      -- check the file_open_status return value from file_open
      CASE_check_status : case vFOstatus is
        -- see file_open_status in 92 version of STANDARD package of STD library
        when OPEN_OK =>
          assert false report pFileName & " opened successfully." severity note;
        when  STATUS_ERROR =>                    -- File object was already open.
          assert false report pFileName & " was already opened." severity warning;
        when NAME_ERROR =>
          assert false report "Could not open " & pFileName & " please check for existence and accuracy of path/filename." severity failure;
        when MODE_ERROR =>
          assert false report "Could not open " & pFileName & " in READ_MODE" severity failure;
      end case CASE_check_status;

      -- set line number to 1 before loop for error reporting
      vLineNum := 1;
      -- read each line of the file until we hit the end of file
      while(FALSE = endfile(fp)) loop
        -- read a line of text from the file pointer
        READLINE(fp, vLOT);

        -- hexread convert first field in line of text to the addr, in HEX format (upper case)
        -- we are using named association here
        hread(
          L     => vLOT,
          value => vAddr_SLV,
          good  => vRS
        );
        -- we could have used positional association
        -- alternative call:  hread(vLOT, vAddr_SLV, vRS );

        -- check status of reading hex value
        assert vRS report "Error reading hex addr from file " & pFileName & "on line " & integer'image(vLineNum) & DP_memory'instance_name severity failure;

        -- convert the slv addr to an integer
        vAddr := to_integer(unsigned(vAddr_SLV));

        -- addr is in hex, multiple of a nibble, but memory might be smaller so
        -- sanity check the addr to make sure it is in the addressable
        -- range of the memory
        assert vAddr < 2**G_ADDR_BITS report
          "Error:  addr 0x" & slv_to_hstring(vAddr_SLV) &
          "(" & integer'image(vAddr) & ")" &
          " too large in memory of " & integer'image(2**G_ADDR_BITS) & "x" & integer'image(G_DATA_BITS) &
          " in file " & pFileName &
          " at line number " & integer'image(vLineNum) severity failure;

        -- convert second field from vLOT to data
        -- todo:  if the number of bits are greater than 32, this might not work.  Test.
        HREAD(
          L     => vLOT,
          VALUE => vData_SLV,
          GOOD  => vRS
        );
        assert vRS report "Error reading data in file " & pFileName & " at line " & integer'image(vLineNum) severity failure;

        if(vRS) then
          assert not vRS report "Loading 0x" & slv_to_hstring(vData_SLV) & " to addr 0x" & slv_to_hstring(vAddr_SLV) severity note;
          --assert not vRS report "Loading " & integer'image(to_integer(unsigned(vData_SLV))) & " to addr " & integer'image(to_integer(unsigned(vAddr_SLV))) severity note;
          vRetVal(vAddr) := vData_SLV;
        end if;
        vLineNum := vLineNum + 1;
      end loop;

      file_close(fp);
    end if;

    return vRetVal;
  end function funcRMFF;

  -- specify the location of the file name
  -- todo:  Make kFileName a generic
  constant kFileName : string := G_INIT_FILE;
  -- declare the memory and initialize it from the file
  shared variable memArray : tMemArray := funcRMFF(pFileName => kFileName, pA_bits => G_ADDR_BITS, pD_bits => G_DATA_BITS );

  signal data_A : std_logic_vector(oData_A'range) := (others => '0');
  signal data_B : std_logic_vector(oData_B'range) := (others => '0');

begin -- architecture

  process
  begin
    assert false report "Hello there" severity note;

    -- Vivado synthesis doesn't support the folling VHDL 1993 do not work in Vivado 2020.1 during
    -- synthesis, but they do in simulation.  If it worked in synthesis the messages would be useful
    -- in the impure function used to initialize the shared variable.
    --
    -- assert false report "instance name : " & DP_memory'instance_name severity note;
    -- assert false report "    path name:  " & DP_memory'path_name severity note;
    wait;
  end process;

  procMemA: process(iClk_A)
    variable addr_A : integer range 0 to 2**G_ADDR_BITS-1 := 0;
  begin
    if rising_edge(iClk_A) then
      addr_A := to_integer(unsigned(iAddr_A));   --convert array to integer
      if(iEN_A = '1') then
        if (iWE_A = '1') then
          memArray(addr_A) := iData_A;
        end if;
        data_A  <= memArray(addr_A);
      end if;
    end if;
  end process procMemA;
  oData_A <= data_A;

  procMemB: process(iClk_B)
    variable addr_B : integer range 0 to 2**G_ADDR_BITS-1 := 0;
  begin
    if rising_edge(iClk_B) then
      addr_B := to_integer(unsigned(iAddr_B));   --convert array to integer
      if(iEN_B = '1') then
        if (iWE_B = '1') then
          memArray(addr_B) := iData_B;
        end if;
        data_B  <= memArray(addr_B);
      end if;
    end if;
  end process procMemB;
  oData_B <= data_B;

end architecture beh;
-- (C) 2020 Morgan Advanced Programmable Systems, Inc.
--
-- https://morgan-aps.com
--
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.all;
package MAPS_utils_pkg is
  constant kDEBUG_ASSERT : boolean := false;

  function slv_to_hstring (value : std_logic_vector) return string;
end package MAPS_utils_pkg;

package body MAPS_utils_pkg is


  function slv_to_hstring (value : std_logic_vector) return string is
    variable N      : integer;
    variable ivalue : std_logic_vector(((value'length+3)/4)*4 - 1 downto 0);
    variable result : string((value'length+3)/4 downto 1);
    variable nibble : std_logic_vector(3 downto 0);
  begin
    assert not (kDEBUG_ASSERT) report "N = " & integer'image((value'length+3)/4) severity note;
    if value'length < 1 then
      return string'(" ");
    else
      N                   := (value'length+3)/4;
      ivalue(value'range) := value;

      if(ivalue'length > value'length) then
        ivalue(ivalue'high downto value'high + 1) := (others => '0');  -- zero pad
      end if;

      for i in N-1 downto 0 loop
        nibble := To_X01Z(ivalue(4*i+3 downto 4*i));
        case nibble is
          when x"0"   => result(i+1) := '0';
          when x"1"   => result(i+1) := '1';
          when x"2"   => result(i+1) := '2';
          when x"3"   => result(i+1) := '3';
          when x"4"   => result(i+1) := '4';
          when x"5"   => result(i+1) := '5';
          when x"6"   => result(i+1) := '6';
          when x"7"   => result(i+1) := '7';
          when x"8"   => result(i+1) := '8';
          when x"9"   => result(i+1) := '9';
          when x"A"   => result(i+1) := 'A';
          when x"B"   => result(i+1) := 'B';
          when x"C"   => result(i+1) := 'C';
          when x"D"   => result(i+1) := 'D';
          when x"E"   => result(i+1) := 'E';
          when x"F"   => result(i+1) := 'F';
          when "ZZZZ" => result(i+1) := 'Z';
          when others => result(i+1) := 'X';
        end case;
      end loop;
      return result;
    end if;
  end function slv_to_hstring;
end package body MAPS_utils_pkg;

DP_memory_top.vhd

-- (C) 2020 Morgan Advanced Programmable Systems, Inc.
--
-- NOTES:
-- In order for assert statements to be processed during synthesis, you must enter the following
-- tcl command in Vivado:
--   o set_param synth.elaboration.rodinMoreOptions {rt::set_parameter ignoreVhdlAssertStmts false}
-- Most users oftend it more convient to just put this in the Vivado_init.tcl script in the
-- C:\Xilinx\Vivado\2020.1\scripts directory.  (Change path as necessary).
use std.textio.all;
library IEEE;
use IEEE.std_logic_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.std_logic_textio.all; -- squatter until 2008, required for reading std_logic_vector

use work.MAPS_utils_pkg.ALL;
-- require both to do file io with SLV

entity DP_memory_top is
  port (
    -- Port A
    iClk_A  : IN  std_logic;
    iSEL_A  : IN  std_logic;
    iEN_A   : IN  std_logic;
    iWE_A   : IN  std_logic;
    iAddr_A : IN  std_logic_vector(11 downto 0);
    iData_A : IN  std_logic_vector(31 downto 0);
    oData_A : OUT std_logic_vector(31 downto 0);
    -- Port B
    iClk_B  : IN  std_logic;
    iSEL_B  : IN  std_logic;
    iEN_B   : IN  std_logic;
    iWE_B   : IN  std_logic;
    iAddr_B : IN  std_logic_vector(11 downto 0);
    iData_B : IN  std_logic_vector(31 downto 0);
    oData_B : OUT std_logic_vector(31 downto 0)
  );
end entity DP_memory_top;

architecture beh of DP_memory_top is

  component DP_memory is
  generic (
    G_INIT_FILE : string  := string'("NULL");
    G_ADDR_BITS : integer := 10;
    G_DATA_BITS : integer := 32
  );
  port (
    -- Port A
    iClk_A  : IN  std_logic;
    iEN_A   : IN  std_logic;
    iWE_A   : IN  std_logic;
    iAddr_A : IN  std_logic_vector(G_ADDR_BITS-1 downto 0);
    iData_A : IN  std_logic_vector(G_DATA_BITS-1 downto 0);
    oData_A : OUT std_logic_vector(G_DATA_BITS-1 downto 0);
    -- Port B
    iClk_B  : IN  std_logic;
    iEN_B   : IN  std_logic;
    iWE_B   : IN  std_logic;
    iAddr_B : IN  std_logic_vector(G_ADDR_BITS-1 downto 0);
    iData_B : IN  std_logic_vector(G_DATA_BITS-1 downto 0);
    oData_B : OUT std_logic_vector(G_DATA_BITS-1 downto 0)
  );
  end component DP_memory;

  -- U1 is a 4kx32 memory
  constant kU1_ADDR_BITS : integer := 12;
  constant kU1_DATA_BITS : integer := 32;
  constant kU1_fileName  : string  := string'("D:\delme\memContents4kx32.txt");

  -- U2 is a 1kx32 memory
  constant kU2_ADDR_BITS : integer := 10;
  constant kU2_DATA_BITS : integer := 32;
  constant kU2_fileName  : string  := string'("D:\delme\memContents1kx32.txt");


  signal   wU1_EN_A      : std_logic;
  signal   wU1_WE_A      : std_logic;
  signal   wU1_EN_B      : std_logic;
  signal   wU1_WE_B      : std_logic;
  signal   wU2_EN_A      : std_logic;
  signal   wU2_WE_A      : std_logic;
  signal   wU2_EN_B      : std_logic;
  signal   wU2_WE_B      : std_logic;
  signal   wU1_Data_A    : std_logic_vector(kU1_DATA_BITS-1 downto 0);
  signal   wU2_Data_A    : std_logic_vector(kU2_DATA_BITS-1 downto 0);
  signal   wU1_Data_B    : std_logic_vector(kU1_DATA_BITS-1 downto 0);
  signal   wU2_Data_B    : std_logic_vector(kU2_DATA_BITS-1 downto 0);
begin -- architecture


  -- U1 is a 4kx32 memory
  wU1_EN_A <= iEN_A and not iSEL_A;
  wU1_WE_A <= iWE_A and not iSEL_A;
  wU1_EN_B <= iEN_B and not iSEL_B;
  wU1_WE_B <= iWE_B and not iSEL_B;
  DP_memory_U1 : DP_memory
  generic map(
    G_INIT_FILE => kU1_fileName,
    G_ADDR_BITS => kU1_ADDR_BITS,
    G_DATA_BITS => kU1_DATA_BITS
  )
  port map(
    -- Port A
    iClk_A  => iClk_A,
    iEN_A   => wU1_EN_A,
    iWE_A   => wU1_WE_A,
    iAddr_A => iAddr_A(kU1_ADDR_BITS -1 downto 0),
    iData_A => iData_A,
    oData_A => wU1_Data_A,
    -- Port B
    iClk_B  => iClk_B,
    iEN_B   => wU1_EN_B,
    iWE_B   => wU1_WE_B,
    iAddr_B => iAddr_B(kU1_ADDR_BITS -1 downto 0),
    iData_B => iData_B,
    oData_B => wU1_Data_B
  );

  -- U2 is a 1kx32 memory
  wU2_EN_A <= iEN_A and iSEL_A;
  wU2_WE_A <= iWE_A and iSEL_A;
  wU2_EN_B <= iEN_B and iSEL_B;
  wU2_WE_B <= iWE_B and iSEL_B;

  DP_memory_U2 : DP_memory
  generic map(
    G_INIT_FILE => kU2_fileName,
    G_ADDR_BITS => kU2_ADDR_BITS,
    G_DATA_BITS => kU2_DATA_BITS
  )
  port map(
    -- Port A
    iClk_A  => iClk_A,
    iEN_A   => wU2_EN_A,
    iWE_A   => wU2_WE_A,
    iAddr_A => iAddr_A(kU2_ADDR_BITS -1 downto 0),
    iData_A => iData_A,
    oData_A => wU2_Data_A,
    -- Port B
    iClk_B  => iClk_B,
    iEN_B   => wU2_EN_B,
    iWE_B   => wU2_WE_B,
    iAddr_B => iAddr_B(kU2_ADDR_BITS -1 downto 0),
    iData_B => iData_B,
    oData_B => wU2_Data_B
  );

 oData_A <= wU1_Data_A when iSEL_A = '0' else wU2_Data_A;
 oData_B <= wU1_Data_B when iSEL_B = '0' else wU2_Data_B;

end architecture beh;

You can enable the vivado synthesizer to parse assert statements (for debugging your synthesis) with:

set_param synth.elaboration.rodinMoreOptions {rt::set_parameter ignoreVhdlAssertStmts false}

 

*** Destination: Rapid design and development cycles ***

View solution in original post

1 Reply
maps-mpls
Mentor
Mentor
183 Views
Registered: ‎06-20-2017

What you posted looks good to me.  You can open the synthesized design, select the RAM in the netlist or on the schematic view (F4), and look at the properties to see if the memory was initialized.

Also, the error message makes me wonder if you also used a reset for your rRAM_0 signal.  If so, it will not be a RAM as so much as an array of resettable registers, and you cannot use a function to compute the value of reset in this way, only for initialization. 

The only other thing I can think of is I usually do this as a dual port, and so I use a shared variable.

The following is similar to what you did (maybe fancier, it was used to teach some concepts in VHDL).  File format is ADDR_IN_HEX DATA_IN_HEX.

Allows sparsely populated file, format is similar to:

000 7FFFFFFF
033 7FFFFFFE
088 7FFFFFFD

DP_memory.vhd

-- (C) 2020 Morgan Advanced Programmable Systems, Inc.
--
-- NOTES:
-- In order for assert statements to be processed during synthesis, you must enter the following
-- tcl command in Vivado:
--   o set_param synth.elaboration.rodinMoreOptions {rt::set_parameter ignoreVhdlAssertStmts false}
-- Most users oftend it more convient to just put this in the Vivado_init.tcl script in the
-- C:\Xilinx\Vivado\2020.1\scripts directory.  (Change path as necessary).  See
-- https://www.xilinx.com/support/answers/65415.html

use std.textio.all; -- 1993 version of VHDL standard
-- use std.textio.readline; -- procedure
-- use std.textio.read; -- procedure
-- use std.textio.line; -- type
library IEEE;
use IEEE.std_logic_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.std_logic_textio.all; -- std_logic_textio is squatter until 2008, required for reading std_logic_vector from text file
--use IEEE.std_logic_textio.hread;

use work.MAPS_utils_pkg.ALL;
-- require both to do file io with SLV

entity DP_memory is
generic (
  G_INIT_FILE : string  := string'("NULL");
  G_ADDR_BITS : integer := 10;
  G_DATA_BITS : integer := 32
);
port (
  -- Port A
  iClk_A  : IN  std_logic;
  iEN_A   : IN  std_logic;
  iWE_A   : IN  std_logic;
  iAddr_A : IN  std_logic_vector(G_ADDR_BITS-1 downto 0);
  iData_A : IN  std_logic_vector(G_DATA_BITS-1 downto 0);
  oData_A : OUT std_logic_vector(G_DATA_BITS-1 downto 0);
  -- Port B
  iClk_B  : IN  std_logic;
  iEN_B   : IN  std_logic;
  iWE_B   : IN  std_logic;
  iAddr_B : IN  std_logic_vector(G_ADDR_BITS-1 downto 0);
  iData_B : IN  std_logic_vector(G_DATA_BITS-1 downto 0);
  oData_B : OUT std_logic_vector(G_DATA_BITS-1 downto 0)
 );
end entity DP_memory;

architecture beh of DP_memory is
  type tMemArray is array (0 to 2**G_ADDR_BITS-1) of std_logic_vector(G_DATA_BITS-1 downto 0);

  -- funciton funcRMFF (Read Memory From File) can be used to initialize
  -- a memory of type tMemArray declared above, from the file specified.
  -- this is synthesizable only if used for initializtion.  Any other
  -- use is not synthesizable at the time of writing
  impure function funcRMFF(pFileName : string ; pA_bits : integer ; pD_bits : integer) return tMemArray is
    -- function declarative region
    file     fp           : text;    -- file pointer/handle
    variable vFOstatus    : file_open_status; -- see STD.STANARD package
    variable vRS          : boolean; -- value read successfully
    variable vLOT         : line;    -- line of text
    variable vData_SLV    : std_logic_vector(pD_bits-1 downto 0); -- data in std_logic_vector format
    variable vAddr_SLV    : std_logic_vector(((pA_bits+3)/4)*4-1 downto 0); -- addr in SLV format.
    variable vAddr        : integer; -- address as integer, for indexing memory
    variable vLineNum     : integer; -- keeps track of line number we're reading, for error messages
    -- initialize return value to all 0s, will be modified as directed by contents of memory initialization file
    variable vRetVal      : tMemArray := (others => (others => '0'));
  begin -- function

    -- NOTE assert statements in impure functions used to initialize shared variables do work in
    -- synthesis (if synthesis assert processing is enabled via tcl script) but do not work
    -- in simulation.  This seems to be a Vivado simulation bug.

    -- check for NULL string in pFileName, which is a name used to indicate no initialization
    -- of memory via file.
    if(pFileName = "NULL") then
      -- the following line is not necessary because the variable was already initilized to all 0s
      -- in the declaration above in the function declarative region.  Duplicated here for
      -- readability
      vRetVal := (others => (others => '0')); -- initialize the memory to all zeros
    else
      -- open the file with call to file_open using named parameter association
      file_open(
        status        => vFOstatus,
        f             => fp,
        external_name => pFileName,
        open_kind     => read_mode
      );
      --positional association call:  file_open(vFOstatus, fp, pFileName, read_mode);

      -- check the file_open_status return value from file_open
      CASE_check_status : case vFOstatus is
        -- see file_open_status in 92 version of STANDARD package of STD library
        when OPEN_OK =>
          assert false report pFileName & " opened successfully." severity note;
        when  STATUS_ERROR =>                    -- File object was already open.
          assert false report pFileName & " was already opened." severity warning;
        when NAME_ERROR =>
          assert false report "Could not open " & pFileName & " please check for existence and accuracy of path/filename." severity failure;
        when MODE_ERROR =>
          assert false report "Could not open " & pFileName & " in READ_MODE" severity failure;
      end case CASE_check_status;

      -- set line number to 1 before loop for error reporting
      vLineNum := 1;
      -- read each line of the file until we hit the end of file
      while(FALSE = endfile(fp)) loop
        -- read a line of text from the file pointer
        READLINE(fp, vLOT);

        -- hexread convert first field in line of text to the addr, in HEX format (upper case)
        -- we are using named association here
        hread(
          L     => vLOT,
          value => vAddr_SLV,
          good  => vRS
        );
        -- we could have used positional association
        -- alternative call:  hread(vLOT, vAddr_SLV, vRS );

        -- check status of reading hex value
        assert vRS report "Error reading hex addr from file " & pFileName & "on line " & integer'image(vLineNum) & DP_memory'instance_name severity failure;

        -- convert the slv addr to an integer
        vAddr := to_integer(unsigned(vAddr_SLV));

        -- addr is in hex, multiple of a nibble, but memory might be smaller so
        -- sanity check the addr to make sure it is in the addressable
        -- range of the memory
        assert vAddr < 2**G_ADDR_BITS report
          "Error:  addr 0x" & slv_to_hstring(vAddr_SLV) &
          "(" & integer'image(vAddr) & ")" &
          " too large in memory of " & integer'image(2**G_ADDR_BITS) & "x" & integer'image(G_DATA_BITS) &
          " in file " & pFileName &
          " at line number " & integer'image(vLineNum) severity failure;

        -- convert second field from vLOT to data
        -- todo:  if the number of bits are greater than 32, this might not work.  Test.
        HREAD(
          L     => vLOT,
          VALUE => vData_SLV,
          GOOD  => vRS
        );
        assert vRS report "Error reading data in file " & pFileName & " at line " & integer'image(vLineNum) severity failure;

        if(vRS) then
          assert not vRS report "Loading 0x" & slv_to_hstring(vData_SLV) & " to addr 0x" & slv_to_hstring(vAddr_SLV) severity note;
          --assert not vRS report "Loading " & integer'image(to_integer(unsigned(vData_SLV))) & " to addr " & integer'image(to_integer(unsigned(vAddr_SLV))) severity note;
          vRetVal(vAddr) := vData_SLV;
        end if;
        vLineNum := vLineNum + 1;
      end loop;

      file_close(fp);
    end if;

    return vRetVal;
  end function funcRMFF;

  -- specify the location of the file name
  -- todo:  Make kFileName a generic
  constant kFileName : string := G_INIT_FILE;
  -- declare the memory and initialize it from the file
  shared variable memArray : tMemArray := funcRMFF(pFileName => kFileName, pA_bits => G_ADDR_BITS, pD_bits => G_DATA_BITS );

  signal data_A : std_logic_vector(oData_A'range) := (others => '0');
  signal data_B : std_logic_vector(oData_B'range) := (others => '0');

begin -- architecture

  process
  begin
    assert false report "Hello there" severity note;

    -- Vivado synthesis doesn't support the folling VHDL 1993 do not work in Vivado 2020.1 during
    -- synthesis, but they do in simulation.  If it worked in synthesis the messages would be useful
    -- in the impure function used to initialize the shared variable.
    --
    -- assert false report "instance name : " & DP_memory'instance_name severity note;
    -- assert false report "    path name:  " & DP_memory'path_name severity note;
    wait;
  end process;

  procMemA: process(iClk_A)
    variable addr_A : integer range 0 to 2**G_ADDR_BITS-1 := 0;
  begin
    if rising_edge(iClk_A) then
      addr_A := to_integer(unsigned(iAddr_A));   --convert array to integer
      if(iEN_A = '1') then
        if (iWE_A = '1') then
          memArray(addr_A) := iData_A;
        end if;
        data_A  <= memArray(addr_A);
      end if;
    end if;
  end process procMemA;
  oData_A <= data_A;

  procMemB: process(iClk_B)
    variable addr_B : integer range 0 to 2**G_ADDR_BITS-1 := 0;
  begin
    if rising_edge(iClk_B) then
      addr_B := to_integer(unsigned(iAddr_B));   --convert array to integer
      if(iEN_B = '1') then
        if (iWE_B = '1') then
          memArray(addr_B) := iData_B;
        end if;
        data_B  <= memArray(addr_B);
      end if;
    end if;
  end process procMemB;
  oData_B <= data_B;

end architecture beh;
-- (C) 2020 Morgan Advanced Programmable Systems, Inc.
--
-- https://morgan-aps.com
--
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.all;
package MAPS_utils_pkg is
  constant kDEBUG_ASSERT : boolean := false;

  function slv_to_hstring (value : std_logic_vector) return string;
end package MAPS_utils_pkg;

package body MAPS_utils_pkg is


  function slv_to_hstring (value : std_logic_vector) return string is
    variable N      : integer;
    variable ivalue : std_logic_vector(((value'length+3)/4)*4 - 1 downto 0);
    variable result : string((value'length+3)/4 downto 1);
    variable nibble : std_logic_vector(3 downto 0);
  begin
    assert not (kDEBUG_ASSERT) report "N = " & integer'image((value'length+3)/4) severity note;
    if value'length < 1 then
      return string'(" ");
    else
      N                   := (value'length+3)/4;
      ivalue(value'range) := value;

      if(ivalue'length > value'length) then
        ivalue(ivalue'high downto value'high + 1) := (others => '0');  -- zero pad
      end if;

      for i in N-1 downto 0 loop
        nibble := To_X01Z(ivalue(4*i+3 downto 4*i));
        case nibble is
          when x"0"   => result(i+1) := '0';
          when x"1"   => result(i+1) := '1';
          when x"2"   => result(i+1) := '2';
          when x"3"   => result(i+1) := '3';
          when x"4"   => result(i+1) := '4';
          when x"5"   => result(i+1) := '5';
          when x"6"   => result(i+1) := '6';
          when x"7"   => result(i+1) := '7';
          when x"8"   => result(i+1) := '8';
          when x"9"   => result(i+1) := '9';
          when x"A"   => result(i+1) := 'A';
          when x"B"   => result(i+1) := 'B';
          when x"C"   => result(i+1) := 'C';
          when x"D"   => result(i+1) := 'D';
          when x"E"   => result(i+1) := 'E';
          when x"F"   => result(i+1) := 'F';
          when "ZZZZ" => result(i+1) := 'Z';
          when others => result(i+1) := 'X';
        end case;
      end loop;
      return result;
    end if;
  end function slv_to_hstring;
end package body MAPS_utils_pkg;

DP_memory_top.vhd

-- (C) 2020 Morgan Advanced Programmable Systems, Inc.
--
-- NOTES:
-- In order for assert statements to be processed during synthesis, you must enter the following
-- tcl command in Vivado:
--   o set_param synth.elaboration.rodinMoreOptions {rt::set_parameter ignoreVhdlAssertStmts false}
-- Most users oftend it more convient to just put this in the Vivado_init.tcl script in the
-- C:\Xilinx\Vivado\2020.1\scripts directory.  (Change path as necessary).
use std.textio.all;
library IEEE;
use IEEE.std_logic_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.std_logic_textio.all; -- squatter until 2008, required for reading std_logic_vector

use work.MAPS_utils_pkg.ALL;
-- require both to do file io with SLV

entity DP_memory_top is
  port (
    -- Port A
    iClk_A  : IN  std_logic;
    iSEL_A  : IN  std_logic;
    iEN_A   : IN  std_logic;
    iWE_A   : IN  std_logic;
    iAddr_A : IN  std_logic_vector(11 downto 0);
    iData_A : IN  std_logic_vector(31 downto 0);
    oData_A : OUT std_logic_vector(31 downto 0);
    -- Port B
    iClk_B  : IN  std_logic;
    iSEL_B  : IN  std_logic;
    iEN_B   : IN  std_logic;
    iWE_B   : IN  std_logic;
    iAddr_B : IN  std_logic_vector(11 downto 0);
    iData_B : IN  std_logic_vector(31 downto 0);
    oData_B : OUT std_logic_vector(31 downto 0)
  );
end entity DP_memory_top;

architecture beh of DP_memory_top is

  component DP_memory is
  generic (
    G_INIT_FILE : string  := string'("NULL");
    G_ADDR_BITS : integer := 10;
    G_DATA_BITS : integer := 32
  );
  port (
    -- Port A
    iClk_A  : IN  std_logic;
    iEN_A   : IN  std_logic;
    iWE_A   : IN  std_logic;
    iAddr_A : IN  std_logic_vector(G_ADDR_BITS-1 downto 0);
    iData_A : IN  std_logic_vector(G_DATA_BITS-1 downto 0);
    oData_A : OUT std_logic_vector(G_DATA_BITS-1 downto 0);
    -- Port B
    iClk_B  : IN  std_logic;
    iEN_B   : IN  std_logic;
    iWE_B   : IN  std_logic;
    iAddr_B : IN  std_logic_vector(G_ADDR_BITS-1 downto 0);
    iData_B : IN  std_logic_vector(G_DATA_BITS-1 downto 0);
    oData_B : OUT std_logic_vector(G_DATA_BITS-1 downto 0)
  );
  end component DP_memory;

  -- U1 is a 4kx32 memory
  constant kU1_ADDR_BITS : integer := 12;
  constant kU1_DATA_BITS : integer := 32;
  constant kU1_fileName  : string  := string'("D:\delme\memContents4kx32.txt");

  -- U2 is a 1kx32 memory
  constant kU2_ADDR_BITS : integer := 10;
  constant kU2_DATA_BITS : integer := 32;
  constant kU2_fileName  : string  := string'("D:\delme\memContents1kx32.txt");


  signal   wU1_EN_A      : std_logic;
  signal   wU1_WE_A      : std_logic;
  signal   wU1_EN_B      : std_logic;
  signal   wU1_WE_B      : std_logic;
  signal   wU2_EN_A      : std_logic;
  signal   wU2_WE_A      : std_logic;
  signal   wU2_EN_B      : std_logic;
  signal   wU2_WE_B      : std_logic;
  signal   wU1_Data_A    : std_logic_vector(kU1_DATA_BITS-1 downto 0);
  signal   wU2_Data_A    : std_logic_vector(kU2_DATA_BITS-1 downto 0);
  signal   wU1_Data_B    : std_logic_vector(kU1_DATA_BITS-1 downto 0);
  signal   wU2_Data_B    : std_logic_vector(kU2_DATA_BITS-1 downto 0);
begin -- architecture


  -- U1 is a 4kx32 memory
  wU1_EN_A <= iEN_A and not iSEL_A;
  wU1_WE_A <= iWE_A and not iSEL_A;
  wU1_EN_B <= iEN_B and not iSEL_B;
  wU1_WE_B <= iWE_B and not iSEL_B;
  DP_memory_U1 : DP_memory
  generic map(
    G_INIT_FILE => kU1_fileName,
    G_ADDR_BITS => kU1_ADDR_BITS,
    G_DATA_BITS => kU1_DATA_BITS
  )
  port map(
    -- Port A
    iClk_A  => iClk_A,
    iEN_A   => wU1_EN_A,
    iWE_A   => wU1_WE_A,
    iAddr_A => iAddr_A(kU1_ADDR_BITS -1 downto 0),
    iData_A => iData_A,
    oData_A => wU1_Data_A,
    -- Port B
    iClk_B  => iClk_B,
    iEN_B   => wU1_EN_B,
    iWE_B   => wU1_WE_B,
    iAddr_B => iAddr_B(kU1_ADDR_BITS -1 downto 0),
    iData_B => iData_B,
    oData_B => wU1_Data_B
  );

  -- U2 is a 1kx32 memory
  wU2_EN_A <= iEN_A and iSEL_A;
  wU2_WE_A <= iWE_A and iSEL_A;
  wU2_EN_B <= iEN_B and iSEL_B;
  wU2_WE_B <= iWE_B and iSEL_B;

  DP_memory_U2 : DP_memory
  generic map(
    G_INIT_FILE => kU2_fileName,
    G_ADDR_BITS => kU2_ADDR_BITS,
    G_DATA_BITS => kU2_DATA_BITS
  )
  port map(
    -- Port A
    iClk_A  => iClk_A,
    iEN_A   => wU2_EN_A,
    iWE_A   => wU2_WE_A,
    iAddr_A => iAddr_A(kU2_ADDR_BITS -1 downto 0),
    iData_A => iData_A,
    oData_A => wU2_Data_A,
    -- Port B
    iClk_B  => iClk_B,
    iEN_B   => wU2_EN_B,
    iWE_B   => wU2_WE_B,
    iAddr_B => iAddr_B(kU2_ADDR_BITS -1 downto 0),
    iData_B => iData_B,
    oData_B => wU2_Data_B
  );

 oData_A <= wU1_Data_A when iSEL_A = '0' else wU2_Data_A;
 oData_B <= wU1_Data_B when iSEL_B = '0' else wU2_Data_B;

end architecture beh;

You can enable the vivado synthesizer to parse assert statements (for debugging your synthesis) with:

set_param synth.elaboration.rodinMoreOptions {rt::set_parameter ignoreVhdlAssertStmts false}

 

*** Destination: Rapid design and development cycles ***

View solution in original post