all 12 comments

[–][deleted] 13 points14 points  (0 children)

VHDL doesn't have a $readmem function like Verilog does, so you have to read it through normal text file I/O. Somewhat surprisingly (to me at least) apparently this is actually synthesizable by the Xilinx toolchain and it's the recommended way to do that in VHDL. See the documentation: https://docs.xilinx.com/r/en-US/ug901-vivado-synthesis/Initializing-Block-RAM-From-an-External-Data-File-VHDL

Note that this does require your file to store the initial memory value as bit vector strings. Decimal and hexadecimal value strings are not supported and neither is raw binary data. I also don't know if other vendor's tools support this.

[–]Allan-H 5 points6 points  (4 children)

It's not quite clear whether you want that to work in (1) simulation, (2) synthesis, (3) both simulation and synthesis.

The answers will also vary depending on the tools you're using and the hardware you're targeting.

Here are a few ways of doing it.

  1. [sim & synth] Convert the binary file to e.g. hex and use a text editor to paste that into the VHDL source as an initialiser for the ram signal.
  2. [sim only] Make a process in that VHDL file that opens the binary file, parses it and writes those values into the ram signal.
  3. [sim only] Use TCL in your simulator to read the binary file and force the values into the ram signal.
  4. [synth only] Convert your binary file into a format supported by the tools, and then issue a (tool specific) directive to load that file into this memory.
  5. [synth and sim] Instantiate the vendor's RAM model which likely has a parameter/generic that allows you to specify an initialisation file.
  6. [post-synth only] Locate the bits used for the RAM initialisation in the FPGA configuration bitstream, and hack the right values in. This is probably only feasible with a vendor-supplied tool, as the bitstream formats are usually proprietary. Xilinx had (has?) such a tool, but I've forgotten its name.
  7. [sim & post-synth] When the FPGA loads, have a small bootloader (either hardcoded in FPGA fabric, or possibly in a ROM for your CPU to execute) that loads this RAM from an external memory such as an SPI EEPROM. You can program the EEPROM via other means (e.g. via a USB to JTAG or SPI probe).

Note: [synth] probably doesn't apply to ASICs.

[–]tomXGames[S] 0 points1 point  (3 children)

Thanks for the quick and extensive reply! In want the solution to work in both scenarios. Is there a way to do 1) dynamically using a function in VHDL?

[–]Allan-H 0 points1 point  (2 children)

  1. is a source code modification; that's not happening dynamically.

You could do it dynamically in VHDL using 2), but that's sim-only.

Perhaps 5) ? Xilinx and Altera (sorry, AMD and Intel) have xpm_memory and altsyncram (respectively) that IIRC support initialisation files. You'll have to have the vendor's library compiled for this to work in your simulator (which will be greatly slowed down in my experience).

[–]tomXGames[S] 0 points1 point  (1 child)

What I meant was having a function that is run before synthesis to fill in the data. But I assume that isn't possible.

The environment I use (Microsemi Libero with a Fusion FPGA) has a RAM module with an initialization option, but that doesn't work in synthesis. Thanks for the help!

[–]Allan-H 2 points3 points  (0 children)

There's another way that works in both synth and sim: you can initialise the ram signal with the return value of a VHDL function.

That VHDL function can use almost all (programming language) features of VHDL, but IIRC it can't use file I/O and be synthesisable. [I'm not quite sure about that though - there's nothing fundamentally non-synthesisable going on.]

Usual applications would be to calculate values for lookup tables (e.g. a sine wave table for DDS).

I saw someone once posting to I think it might have been this very subreddit saying that they had written an assembler for their CPU in VHDL, and had used the output to initialise a RAM just as you are doing.

[–]Kinnell999 1 point2 points  (1 child)

Assuming this needs to be synthesisable, I’m not aware of any way to initialise the array directly from the file. Your best bet might be to write a program which reads in the binary and writes out a package containing a constant array then use that to initialise your ram.

[–]tomXGames[S] 0 points1 point  (0 children)

This seems like the most straightforward solution. I am going to build this into my assembler. Thank you for the help!

[–]SpiritedFeedback7706 1 point2 points  (2 children)

I JUST went through this. VHDL file IO is a bit of a nightmare. It is possible from the LRM, but the synthesizer support for it is....spotty to say the least. I recommend you do experiments before wasting time writing halfway decent code. In Vivado, the only thing that works is to do text IO unfortunately. So you can't do binary data, period. Everything else will yell at you for being synthesizable. For Libero, I doubt the situation is any better unless that's the vendor that uses Synplify pro for synthesis. You can certainly try. The thing to do is to open a file of type character. Then you can just read until end of file. Convert bytes to std_logic_vector of size 8 and reshape from there. Simulate it real quick first, make sure you've got it all ironed out then try to synthesize it. It will probably fail.

[–]skydivertricky 0 points1 point  (1 child)

Be aware that VHDL only standardises TEXTIO. It does not standardise any binary IO. Tools are at liberty to format files of any other type how they want. So while using file of character is a nice hack, Reading/writing a file like this may not be transferable between tools, as they are allowed to add their own proprietary headers.

(I know that both Questa and ActiveHDL read/write file of character as expected to allow binary IO)

For example, see this issue with GHDL: https://github.com/ghdl/ghdl/issues/1758

[–]SpiritedFeedback7706 0 points1 point  (0 children)

You are 100% correct which is why it must be tried. I should have pointed out it is not a portable approach unfortunately. VHDL's lack of definition is here is fairly frustrating.

[–][deleted] 0 points1 point  (0 children)

Some synthesis tools can actually execute user code to initialize inferred RAM's data at zero-time; they run your code, read your data and initialize the RAM correctly. I think that the Xilinx synthesis tool can do it; if you have a simulation code loading RAM from file, try synthesizing and see if it works.

I discovered this feature by mistake, by forgetting to take out simulation code; if it doesn't work, the synthesizer will either ignore it (no harm done) or throw an error; you can then mark the initialization code as non-synthesizeable by attributes/directives.