verilogfpgaxilinxvivado

How to initialize contents of inferred Block RAM (BRAM) in Verilog


I am having trouble initializing the contents of an inferred ram in Verilog. The code for the ram is as below:

module ram(
        input clock, // System clock
        input we, // When high RAM sets data in input lines to given address
        input [13:0] data_in, // Data lines to write to memory
        input [10:0] addr_in, // Address lines for saving data to memory
        input [10:0] addr_out, // Address for reading from ram
        output reg data_out // Data out
);

reg [13:0] ram[2047:0];

// Initialize RAM from file
// WHAT SHOULD GO HERE?

always @(posedge clock) begin
    // Save data to RAM
    if (we) begin
        ram[addr_in] <= data_in;
    end

    // Place data from RAM
    data_out <= ram[addr_out];
end        
endmodule

I have run into the command $readmemh. However, documentation for it seems sparse. How should I format the file that contains the data? Also, how can I pass the file as argument when instantiating this module so that I can have different instances of this module load from different files?

I want the initialized content to be available for both simulation and actual implementation. So that the FPGA already boots with this content in RAM.

I am using Vivado 2015.4 to program a Kintex xc7k70 FPGA.


Solution

  • You are correct that you should use $readmemh inside an initial block. In order to make it so different instances of the module can have different initialization files, you should use a parameter like so:

    parameter MEM_INIT_FILE = "";
    ...
    initial begin
      if (MEM_INIT_FILE != "") begin
        $readmemh(MEM_INIT_FILE, ram);
      end
    end
    

    The format is described in Section 21.4 of the IEEE1800-2012 specification; typically the file is just a bunch of lines containing hex numbers of the correct bit-length, like so:

    0001
    1234
    3FFF
    1B34
    ...
    

    Note that there is no "0x" prefix and each line represents an adjacent address (or any separating whitespace). In the example above, $readmemh would put 14'h0001 into ram[0], 14'h1234 into ram[1], 14'h3FFF into ram[2] and so on. You can also include comments in the hex file using // or /* */. Finally, you can use the @ symbol to designate an address for the following numbers to be located at, like so:

    @0002
    0101
    0A0A
    ...
    

    In the above file, ram[0] and ram[1] would be uninitialized and ram[2] would get 14'h0101. Those are all the major constructs of the hex file format, though you can also use _, x and z as you would in other Verilog numbers and theres a few more rules you can read in the section sited above.