Initialize Memory in Verilog

Welcome to the FPGA Cookbook.

This is part of a new series of handy recipes to solve common FPGA development problems. Look out for more FPGA cookbook posts soon.

You want to initialize memory from a file using Verilog.

It's common for a simulation or firmware to need data loading into a memory array, ram, or rom. Fortunately Verilog provides the $readmemh and $readmemb functions for this very purposes. Unfortunately, there is a dearth of good Verilog documentation online, so using them can be harder than it should be. This article explains the syntax and provides plenty of examples, including how to do this in Xilinx Vivado.

Feedback to @WillFlux is most welcome. Last updated February 2018.

Verilog Syntax

Verilog allows you to initialize memory from a text file with either hex or binary values:

  • $readmemh("hex_memory_file.mem", memory_array, [start_address], [end_address])
  • $readmemb("bin_memory_file.mem", memory_array, [start_address], [end_address])

Function Arguments

  • hex_memory_file.mem - a text file containing hex values separated by whitespace (see below)
  • bin_memory_file.mem - a text file containing binary values separated by whitespace (see below)
  • memory_array - the name of Verilog memory array of the form: reg [n:0] memory_array [0:m]
  • start_address - where in the memory array to start loading data (optional)
  • end_address - where in the memory array to stop loading data (optional)

The following shows a very simple simulation module using $readmemh:

module readmemh_tb();
    reg [7:0] test_memory [0:15];
    initial begin
        $display("Loading rom.");
        $readmemh("rom_image.mem", test_memory);
    end
endmodule

Memory File Syntax

The hex_memory_file.mem or bin_memory_file.mem consist of text hex/binary values separated by whitespace: space, tab, and newline all work. You can mix the whitespace types in one file. Comments work in the same way as normal Verilog files: // begins a comment.

The width of a data value in the file mustn't be wider than the data width of the array, otherwise that value will be truncated. In Vivado you'll see a warning in the log "WARNING: Data truncated while reading Datafile:".

Verilog Examples

1) Four 16-bit data values in hex:

Verilog:

reg [15:0] ex1_memory [0:3];
$readmemh("ex1.mem", ex1_memory);

Memory file contents:

dead
beef
0a0a
1234

2) Sixteen 8-bit data values in hex (mixing spaces and newlines):

Verilog:

reg [7:0] ex2_memory [0:15];
$readmemh("ex2.mem", ex2_memory);

Memory file contents (including comment):

ab cd ef 01  // this is a comment
ef 22 1e 00
9f ff 13 e6
ce b7 28 8f

3) Six 3-bit values in binary:

Verilog:

reg [2:0] ex3_memory [0:5];
$readmemb("ex3.mem", ex3_memory);

Memory file contents:

001 101 111 111 101 001

4) Six 16-bit values in hex starting at array position 4:

Verilog:

reg [15:0] ex4_memory [0:255];
$readmemh("ex4.mem", ex4_memory, 4);

Memory file contents:

dead beef 0a0a 1234 abab 987e

NB. As demonstrated by this example the memory array can have more entries than the data file.

Vivado

The easy way to get memory files working with Vivado is to give them the .mem extension then add them to your project. You do this as you would for a design or simulation source using "Add Sources" then selecting "Files of type: Memory Initialization Files". Vivado will automatically identify them as memory files and place them in the current working directory during simulation etc.

If you just reference a file but don't add it to the project you'll get an error of the form:

WARNING: File rom_image.mem referenced on acme.v at line 42 cannot be opened for reading. Please ensure that this file is available in the current working directory.

Once you've added the files to your project they will show up in the Sources view under Design or Simulation Sources:

©2018 Will Green.

Image Credit: The wonderful image of the Micron MT4C1024 DRAM at the top of this article comes from Zeptobars and is licensed under a Creative Commons licence.