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.
Block RAMs (or BRAM) stands for Block Random Access Memory. Block RAMs are used for storing large amounts of data inside of your FPGA. They one of four commonly identified components on an FPGA datasheet. The other three are Flip-Flops, Look-Up Tables (LUTs), and Digital Signal Processors (DSPs).
Block RAM (BRAM) is a type of random access memory embedded throughout an FPGA for data storage. You can use BRAM to accomplish the following tasks: Transfer data between multiple clock domains by using local FIFOs. Transfer data between an FPGA target and a host processor by using a DMA FIFO.
Distributed ram is, as its name suggests, distributed throughout the FPGA. A single 6-input LUT can store 64 bits. Distributed ram is read asynchronously, but written to synchronously (requires a clock). Writes are limited to a single port, but you can read from up to four ports in some FPGAs.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With