3
\$\begingroup\$

I am looking for a implementation of ROM using VHDL code. I´m looking for a piece of code to make this easy example in order to make a generalization afterwards. I want to put in the ROM these values: 14,45,67,32,12,64,89,34,54,22 so when the for the address 'x' (the input of the system) you get 'y' (the output of the system):

x  y
1  14
2  45
3  67
4  32
5  12
6  64
7  89
8  34
9  54
10 22

Thank you to all possible references, articles or comments.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ While they are not exact duplicates, I do not think there is much to be gained by asking a number of closely related questions at the same time, such as this question and a similar question for the Xilinx system generator. See also the questions about floating point formats/negation. \$\endgroup\$
    – drxzcl
    Feb 11, 2011 at 9:43
  • \$\begingroup\$ @Ranieri: I do that because there is always several ways to do one thing. \$\endgroup\$
    – Peterstone
    Feb 11, 2011 at 10:52

4 Answers 4

8
\$\begingroup\$

It would be better to use a vendor independent (and therefore portable) pure VHDL approach. Any good synthesis tool will infer a ROM from this code:

case address is
    when 1 =>      result <= 14;
    when 2 =>      result <= 45;
    when 3 =>      result <= 67;
    when 4 =>      result <= 32;
    when 5 =>      result <= 12;
    when 6 =>      result <= 64;
    ...
    when others => result <= 0;  -- Default
end case;
\$\endgroup\$
1
  • \$\begingroup\$ To infer a ROM this case block should be wrapped into an if/then block that tests a clock signal. \$\endgroup\$
    – Brian Jack
    Nov 22, 2017 at 1:21
2
\$\begingroup\$

This isn't in VHDL, but does provide an answer to the general problem that others may find useful.

You can use a Block Memory Generator IP core to do what you want. This is created by adding a coregen type source file to your project and choosing Block Memory Generator from the list of available cores. You can customise the type of memory, you'd want to choose ROM in this case of course, the size and also provide a memory initialisation file. This .coe file is described at http://www.xilinx.com/itp/xilinx4/data/docs/cgn/ch3-using20.html

You'd want something like this:

Memory_Initialization_Radix=16;
Memory_Initialization_Vector=
0E,2D,43,20,0C,40,59,22,36,16;
\$\endgroup\$
2
\$\begingroup\$

here's a VHDL example of doing it in the source code - some data omitted for clarity:

 entity cgrom is
 port (clkin : in std_logic;
      adr : in std_logic_vector(13 downto 0);
     data:out std_logic_vector(3 downto 0));
     end cgrom; 

 architecture a_cgrom of cgrom is

 attribute INIT_00 :string;attribute INIT_01 :string;attribute INIT_02 :string;attribute INIT_03 :string; 
attribute INIT_04 :string;attribute INIT_05 :string;attribute INIT_06 :string;attribute INIT_07 :string;

attribute INIT_00 of chargen0: label is "0000000000000000000000000000000000000000000000000000000000000000";
attribute INIT_01 of chargen0: label is "00000000000000E000E00060000000200040006000E000E000E000E000E00000";
attribute INIT_02 of chargen0: label is "00000000000000000000000000000000000000000000011C011C011C011C0000";
attribute INIT_03 of chargen0: label is "0000000000000048007C005C00CC01D800B800B801B007FC0330017007700260";
attribute INIT_04 of chargen0: label is "00000020002000DC03FC01080120035000F0005C0028030C01FC035800200000";
attribute INIT_05 of chargen0: label is "00000000000001800600006002C0039C0384038C0398006A0076003600300000";
attribute INIT_06 of chargen0: label is "00000000002002F803BA059E03CC03E60654007800700030001C00F800F80020";
attribute INIT_07 of chargen0: label is "0000000000000000000000000000000000000000000000600060006000600000";
\$\endgroup\$
2
  • \$\begingroup\$ I don´t understand why there are so large words between quotes like:"00000000002002F803BA059E03CC03E60654007800700030001C00F800F80020" any port of signa in your example code has so many bits. \$\endgroup\$
    – Peterstone
    Feb 11, 2011 at 10:56
  • \$\begingroup\$ The length of the strings are based more on the implementation of the memory cells than on the ports chosen for plugging it into the rest of the design. \$\endgroup\$ Nov 22, 2011 at 7:18
1
\$\begingroup\$

I'll turn your example code into something that infers a ROM:

Somewhere in your architecture section create the signals for accessing your ROM (ROM inference requires a clocked result) for simplicity (avoiding the verbosity of VHDL type casting cluttering the example) I've converted your numbers to hex:

subtype nybble is std_logic_vector(3 downto 0);
subtype byte is std_logic_vector(7 downto 0);

signal romClock   : std_logic;
signal romAddress : nybble;
signal romResult  : byte;

Then in the behavior section a process such as the following infers a ROM:

myROM: process(romClock,romAddress) is
begin
    if (rising_edge(romClock)) then
        case romAddress is
            when x"1"   => romResult <= x"0E"; -- 14
            when x"2"   => romResult <= x"2D"; -- 45
            when x"3"   => romResult <= x"43"; -- 67
            when x"4"   => romResult <= x"20"; -- 32
            when x"5"   => romResult <= x"0C"; -- 12
            when x"6"   => romResult <= x"40"; -- 64
            when others => romResult <= x"00"; -- 0
        end case;
    end if;
end process myROM;

I've had to do this a few times on my Commodore-64-on-Zybo design via python scripts to turn the ROM binary data (Character, Basic and Kernal ROMs) into VHDL sources containing the huge 4K/8K line case blocks within them (if I need to write a converter anyways may as well convert to generic VHDL rather than the vendor-specific textual memory contents description files).

With just the CASE block you get a combinational constant selector implemented in LUTs rather than a ROM. To infer a ROM the result signal assignment has to be staged to a clock edge.

Repeat this pattern to generate additional ROMs.

One gotcha: you cannot generate more than one output per clock per ROM (this will prevent inference of a ROM if it occurs in your design). For multiple output you must either partition the data into separate ROMs for each output (interleaving solution) or use a small cyclic FSM with two states per result required and clock this FSM on both edges of its input clock (deserializing solution). Generate the ROM address on the - edge states (assuming the ROM outputs on + edges as it does in the above example) and assign the signal from the ROM's output on the + edge states. Ensure that any sync or async reset state and initialized/starting states are one of the - edge states.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.