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

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.


Apart from @Unn's excellent ans, I want to add that, If you just want to initialize your memory with either all bits to 1'b1 or 1'b0, then you can just put following code,

integer j;
initial 
  for(j = 0; j < DEPTH; j = j+1) 
    ram[j] = {WIDTH{MEM_INIT_VAL}};

For your case, WIDTH=14, and MEM_INIT_VAL may be 1'b1 or 1'b0.