Saturday, December 31, 2011

Memory Initialization Methods

Questions on how to initialize a ROM or RAM come up quite often on FPGA discussion boards and forums. Several methods can be used to initialize memory in RTL, during implementation, and post-implementation when targeting Xilinx FPGA devices. Some of the methods are covered in this blog.

Verilog "initial" block with $readmemb or $readmemh
Memories can be initialized using $readmemb or $readmemh system tasks with a memory initialization file (.mif) (download complete example here):

reg [DATA_WIDTH-1:0] mem[0:2**ADDR_WIDTH-1];

initial begin
    $readmemh("mem_init_vlog.mif", mem, 0, 255);

XST  has two different Verilog parsers depending on the target device. The old parser is used for Virtex5/Spartan3 and older devices. For Virtex6/Spartan6 and newer devices, the new parser will be used. The old parser has a few restrictions on the memory initialization:
  • The entire memory array must be initialized. For example, if the memory has 256 words, all 256 words must be explicitly initialized. Otherwise the initialization will be ignored with this warning message: WARNING:Xst:2319 - "rom_vlog.v" line 21: Signal mem in initial block is partially initialized. The initialization will be ignored.
  • Make sure the .mif file does NOT have any unnecessary white spaces (especially "tab") anywhere. Or it will issue some obscure error message like this: ERROR:Xst:2354 - "rom_vlog_mif.v" line 18: Value 262025260 found at line 256 is not hexadecimal in call of system task $readmemh.
  • Starting address (@hh), Verilog comments (// or /* */), and multiple values per line are not supported in the .mif file. Or as you guessed it, it will issue other weird errors.
It is very unlikely that these restrictions will removed from the old parser because workarounds exist by following the rules above. The good news is that the new Verilog parser supports all valid formats in a memory initialization file as describe in Verilog LRM. i.e. the restrictions no longer exist in the new Verilog parser. An even better news is that the switch "-use_new_parser yes" can be manually added to the XST command line to enable the new parser for older devices if you insist on using full features of the memory initialization file.

VHDL functions
Memory can be initialized in VHDL by using a function at signal declaration. Functions like sin(), cos()in the IEEE math_real library are supported by XST for initializing memories. The code snippet below shows how to generate a look up table with a cosine wave (download complete example here):

constant MEM_DEPTH : integer := 2**ADDR_WIDTH;
type mem_type is array (0 to MEM_DEPTH-1) of signed(DATA_WIDTH-1 downto 0);

function init_mem return mem_type is
    constant SCALE : real := 2**(real(DATA_WIDTH-2));
    constant STEP  : real := 1.0/real(MEM_DEPTH);
    variable temp_mem : mem_type;
    for i in 0 to MEM_DEPTH-1 loop
        temp_mem(i) := to_signed(integer(cos(2.0*MATH_PI*real(i)*STEP)*SCALE), DATA_WIDTH);
    end loop;
    return temp_mem;

constant mem : mem_type := init_mem;

VHDL with external data files
XST User Guide has VHDL examples of initializing block RAMs with external data files. It's listed here so that you are aware of this option. The XST UG should always be your first stop for coding styles and techniques. I would like to point out two things highlighted in red in the code snippet below (download complete example here):
  •  The "file" declaration in XST UG uses VHDL-87 syntax. The code snippet below uses VHDL-93 syntax because that is the default VHDL starndard XST uses.
  •   The second argument for the "read" function needs to be a bit_vector. std_logic_vector or signed types are not supported by the "read" function in IEEE std.textio package.

constant MEM_DEPTH : integer := 2**ADDR_WIDTH;
type mem_type is array (0 to MEM_DEPTH-1) of signed(DATA_WIDTH-1 downto 0);

impure function init_mem(mif_file_name : in string) return mem_type is
    file mif_file : text open read_mode is mif_file_name;
    variable mif_line : line;
    variable temp_bv : bit_vector(DATA_WIDTH-1 downto 0);
    variable temp_mem : mem_type;
    for i in mem_type'range loop
        readline(mif_file, mif_line);
        read(mif_line, temp_bv);
        temp_mem(i) := signed(to_stdlogicvector(temp_bv));
    end loop;
    return temp_mem;
end function;

constant mem : mem_type := init_mem("mem_init_vhd.mif");

Verify Memory Content in Synthesized Netlist

There are a couple of ways to verify that the memory is correctly initialized by the synthesis tool:
  • The values of INIT_xx attributes of the block RAM can be examined in PlanAhead by opening the netlist design in PlanAhead, selecting the instance for the memory, and selecting the "Attributes" tab of the "Instance Properties" window. 
  • A post-synthesis simulation model can be generated for the synthesized netlist in Project Navigator or by using netgen tool. This simulation model can be used in the existing testbench to check if the initialization data made into the netlist correctly. The waveform below shows the correct outputs from the ROMs created in the Verilog .mif (counter) and VHDL function (sine wave) methods.


  1. According to UG687, XST supports specifying initial memory contents using an external data file. Here is the corresponding VHDL code snippet:

    type RamType is array(0 to 127) of bit_vector(31 downto 0);

    impure function InitRamFromFile(RamFileName : in string) return RamType is
    file RamFile : text is in RamFileName;
    variable RamFileLine : line;
    variable RAM : RamType;
    for I in RamType'range loop
    readline(RamFile, RamFileLine);
    read(RamFileLine, RAM(I));
    end loop;
    return RAM;
    end function;

    signal RAM : RamType := InitRamFromFile("");

  2. @Guy Eschemann, thanks a lot for posting this code snippiet. My main intention is to talk about things not directly covered in the UGs, especially XST UG, which should be the first stop for coding styles and techniques. Having said that, I will add a complete example using external file in VHDL.

  3. hello jim,

    I have a simple question. Will this work. I dun want to initialize my memory. Basically zero values is fine for me.
    will this code work?

    module register_file_behav(clk,write,r1_addr,r2_addr,wr_addr,wr_data,r1_data,r2_data);

    input clk;
    input write;
    input [4:0] r1_addr;
    input [4:0] r2_addr;
    input [4:0] wr_addr;
    input [31:0] wr_data;
    output reg[31:0] r1_data;
    output reg[31:0] r2_data;
    integer i;
    reg [31:0]out_reg [31:0];

    always @(posedge clk)
    r1_data<= out_reg[r1_addr];
    r2_data<= out_reg[r2_addr];


    out_reg[wr_addr]<= wr_data;



  4. @sid If you only read a memory location after you write to it, your code looks fine to me.

  5. Hello Jim.

    Thank you for your wonderful post.

    I have used for my LUT to read values from a text file and assign them to the address in the LUT. I need to write a test bench for my VHDL code, but I am having difficulties due to the FOR loop and reading from a text file in the test bench itself.

    Can you please help me? or show me how I can use the FOR loop in my case (NB. my code is very similar to yours above).

    Here’s my VHDL code:

    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;

    Entity ROM_ent is
    ADDR: IN std_logic_VECTOR(7 downto 0);
    CLK: IN std_logic;
    DATA: OUT std_logic_VECTOR(7 downto 0)
    end ROM_ent;

    Architecture Behavioral of ROM_ent is

    type rom_type is array (255 downto 0) of std_logic_vector (7 downto 0);

    impure function InitRomFromFile (RomFileName : in string) return rom_type is

    FILE romfile : text is in RomFileName;
    variable RomFileLine : line;
    variable ROM : rom_type;

    for i in rom_type'range loop

    readline(romfile, RomFileLine);
    read(RomFileLine, ROM(i));

    end loop;
    return ROM;
    end function;

    Constant ROM : rom_type := InitRomFromFile("Doc1.Txt");


    process (CLK)
    if(CLK'event and CLK = '1') then
    DATA <= ROM(to_integer(unsigned(ADDR)));
    end if;
    end process;

    end Behavioral;

    Thank you !!!

    Kind Regards,


  6. *** I need to use a FOR loop in my test bench ***

  7. hello friends
    i am new to xilinx ise
    i have xilinx 12.3
    i am getting error in behavioural model simulation
    'INFO:ProjectMgmt - The selected process was not run because a prior process failed.'
    my rtl logic is also done,and implementaion view is fully done.plz help

  8. hello friends
    i am new to xilinx ise
    i have xilinx 12.3
    i am getting error in behavioural model simulation
    'INFO:ProjectMgmt - The selected process was not run because a prior process failed.'
    my rtl logic is also done,and implementaion view is fully done.plz help

  9. @Ashwani Since your question has nothing to do with the post, please open a webcase with Xilinx tech support or post the question to the simulation board on Xilinx forums:

  10. Hello friends,
    I have a question.I want to store fixed point numbers(integer+fraction) on fpga.I was unable to do with above discussed code by jim.Could u help me

  11. Thanks for this nice article.

    Could you please let me know if there has been any update regarding this or it is still the same.
    Moreover, is there any way to initialize memory with floating numbers?


  12. Thank you,
    Please any one help me how to read the mif file in VHDL , Itried the code here output still 0s

  13. Hi Jim, it is still not clear to me if this method is synthesisable or just good for simulation. Thanks, Yacine

  14. hello friends,
    i am trying to use vga interface with nexys4 board.
    but i am facing the problem with block ram .
    i have tried two ways to store the file into bram but not working very well , may be i am missing some steps but which steps , i don't know. can you please share the steps for storing the image hex codes into the block ram