Forum Discussion
Inferring a ROM
Has anyone used a HEX (Intel format) file to infer a ROM? I'd like to build a look-up table for waveform generation using a 32768 x 16 ROM and would prefer not to type in all of the values manually.
Is there a better way to do this? Thanks, Brian12 Replies
- Altera_Forum
Honored Contributor
What's the nature of the values being stored by the ROM? Are they random or do they fall naturally from some simple formula? You can always write a script to save you the typing. I avoid using MIF/HEX files unless I need to update my memory contents without recompiling the design. Instead, I describe the RAM and initial contents purely in HDL. Quartus II will infer initial contents from the HDL source, e.g.
Look at the Verilog and VHDL templates in the Quartus II GUI for more examples.signal rom : my_rom_t := initialize_rom(); - Altera_Forum
Honored Contributor
Thanks for your reply. Its a quarter wavelength sinusiod. Currently I'm using MATLAB to output a file with the appropriate VHDL formatting.
I did use Quartus' genmem command, but I'd rather write my own model and have Quartus infer the ROM. - Altera_Forum
Honored Contributor
I'd use ieee.math_real and generate the table automatically in VHDL using a function. Then you only need to make sure you use an appropriate template to get the ROM.
- Altera_Forum
Honored Contributor
Oh, that would be great! I've never used a function before. So using the math library and a function will still produce synthesizable code?
I'll do some internet searches for a function example. Thanks! - Altera_Forum
Honored Contributor
So long as you only do operations on constant real values, you're fine, i.e. don't declare an input to an entity as a real. :)
- Altera_Forum
Honored Contributor
Hello,
here's an example for ROM inferred from HDL, just a few snippets --- Quote Start --- GENERIC ( NNCO : INTEGER := 10; -- output amplitude resolution NAKKUI : INTEGER := 10; -- akku integer bits = phase resolution ); -- -- ARCHITECTURE rtl OF nco IS CONSTANT ROMSIZE : INTEGER := 2**(NAKKUI-1); CONSTANT ROMMAX : INTEGER := 2**(NNCO-1)-1; TYPE SINTAB IS ARRAY(0 TO ROMSIZE-1) OF STD_LOGIC_VECTOR (NNCO-2 DOWNTO 0); SIGNAL SINROM: SINTAB; BEGIN -- For FPGA devices, this construct infers ROM sine table, -- for RAMless devices (MAX II) an LE based table instead -- only the 0..pi positive half is represented GENROM: FOR idx in 0 TO ROMSIZE-1 GENERATE CONSTANT x: REAL := SIN(real(idx)*MATH_PI/real(ROMSIZE)); CONSTANT xn: UNSIGNED (NNCO-2 DOWNTO 0) := CONV_UNSIGNED(INTEGER(x*real(ROMMAX)),NNCO-1); BEGIN SINROM(idx) <= STD_LOGIC_VECTOR(xn); END GENERATE; --- Quote End --- I hope the method can be seen from the snippets. Regards, Frank - Altera_Forum
Honored Contributor
Hello,
wondering if the posted code would be understandable, I found some points that should be clarified: 1. To enable the real math in in preparation of the sine table, the math library must be imported. The example uses this libraries
2. The generate statement uses a feature, that may be unknown, cause it's ignored by most VHDL manuals, although defined in VHDL standard and (as far as I know) supported by all VHDL tools. Optionally a generate statement can contain a declarative part. If so, an additional begin preceedes the statement part. 3. The table in the example uses STD_LOGIC_VECTOR type cause it has been a replacement for an alt_ram megafunction in the original design. It could also use any numerical type. 4. Another way to code an inferred ROM table is to define an init function that returns an array. In the function, a sequential for loop sets all table values. The table is then instantiated as CONSTANT in the declarative part of the architecture. This technique moves the table generation completely to the declarative part, showing clearly that no "runtime" arithmetic shall be generated from this code.LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; USE ieee.math_real.all;
Regards, FrankTYPE ROM IS ARRAY(0 TO 511) OF UNSIGNED(8 DOWNTO 0); FUNCTION INIT_ROM RETURN ROM IS VARIABLE romvar: ROM; begin --for .. loop -- romvar() := --end loop return romvar; end; CONSTANT rom1: ROM := INIT_ROM; - Altera_Forum
Honored Contributor
Hi Frank, thanks for the info. I like the init function to infer the ROM, but I have one question;
Do you use the same commands in the init version's for loop that was used in the generate version's for loop? Specifically ... FOR idx in 0 TO ROMSIZE-1 CONSTANT x: REAL := SIN(real(idx)*MATH_PI/real(ROMSIZE)); CONSTANT xn: UNSIGNED (NNCO-2 DOWNTO 0) := CONV_UNSIGNED(INTEGER(x*real(ROMMAX)),NNCO-1); Thanks, Brian - Altera_Forum
Honored Contributor
Hello Brian,
I wanted to leave some problems for you... No, iit's not exactly the same code, it's easier. In the generate version, no VARIABLE object can be used, thus you need these long-winded CONSTANT constructs. With functions, all calculations can use VARIABLE objects. If you like, you could do the calculation in a single expression without variables except romvar. But with variables, readability is improved, I think.
Regards, FrankTYPE ROM IS ARRAY(0 TO 511) OF UNSIGNED(8 DOWNTO 0); FUNCTION INIT_ROM RETURN ROM IS VARIABLE romvar: ROM; VARIABLE x: REAL; begin for I in 0 TO 511 loop x:= SIN(real(i)*MATH_PI/real(511)); romvar(i):=CONV_UNSIGNED(INTEGER(x*real(511)),9); end loop; return romvar; end; - Altera_Forum
Honored Contributor
Ok, I understand the differences now. Thank you very much for you help!
Brian