Return to previous page Advance to next page
Synthesis and Simulation Design Guide
Chapter 4: Designing FPGAs with HDL

Implementing Memory

XC4000E/EX/XL/XLA and Spartan FPGAs provide distributed on-chip RAM or ROM. CLB function generators can be configured as ROM (ROM16X1, ROM32X1); level-sensitive RAM (RAM16X1, RAM 32X1); edge-triggered, single-port (RAM16X1S, RAM32X1S); or dual-port (RAM16x1D) RAM. Level sensitive RAMs are not available for the Spartan family. The edge-triggered capability simplifies system timing and provides better performance for RAM-based designs. This distributed RAM can be used for status registers, index registers, counter storage, constant coefficient multipliers, distributed shift registers, LIFO stacks, latching, or any data storage operation. The dual-port RAM simplifies FIFO designs.

Note: For more information on XC4000 family RAM, refer to the Xilinx Web site (http://support.xilinx.com) or the current release of The Programmable Logic Data Book.

Implementing XC4000 and Spartan ROMs

ROMs can be implemented as follows.

VHDL and Verilog examples of an RTL description of a ROM follow.

RTL Description of a ROM VHDL Example

--
-- Behavioral 16x4 ROM Example
-- rom_rtl.vhd
--

library IEEE;
use IEEE.std_logic_1164.all;

entity rom_rtl is
port (ADDR: in INTEGER range 0 to 15;
DATA: out STD_LOGIC_VECTOR (3 downto 0));
end rom_rtl;

architecture XILINX of rom_rtl is

subtype ROM_WORD is STD_LOGIC_VECTOR (3 downto 0);
type ROM_TABLE is array (0 to 15) of ROM_WORD;
constant ROM: ROM_TABLE := ROM_TABLE'(
ROM_WORD'("0000"),
ROM_WORD'("0001"),
ROM_WORD'("0010"),
ROM_WORD'("0100"),
ROM_WORD'("1000"),
ROM_WORD'("1100"),
ROM_WORD'("1010"),
ROM_WORD'("1001"),
ROM_WORD'("1001"),
ROM_WORD'("1010"),
ROM_WORD'("1100"),
ROM_WORD'("1001"),
ROM_WORD'("1001"),
ROM_WORD'("1101"),
ROM_WORD'("1011"),
ROM_WORD'("1111"));

begin
DATA <= ROM(ADDR); -- Read from the ROM

end XILINX;

RTL Description of a ROM Verilog Example

/*
* ROM_RTL.V
* Behavioral Example of 16x4 ROM
*/

module rom_rtl(ADDR, DATA) ;
input [3:0] ADDR ;
output [3:0] DATA ;

reg [3:0] DATA ;

// A memory is implemented
// using a case statement

always @(ADDR)
begin
case (ADDR)
4'b0000 : DATA = 4'b0000 ;
4'b0001 : DATA = 4'b0001 ;
4'b0010 : DATA = 4'b0010 ;
4'b0011 : DATA = 4'b0100 ;
4'b0100 : DATA = 4'b1000 ;
4'b0101 : DATA = 4'b1000 ;
4'b0110 : DATA = 4'b1100 ;
4'b0111 : DATA = 4'b1010 ;
4'b1000 : DATA = 4'b1001 ;
4'b1001 : DATA = 4'b1001 ;
4'b1010 : DATA = 4'b1010 ;
4'b1011 : DATA = 4'b1100 ;
4'b1100 : DATA = 4'b1001 ;
4'b1101 : DATA = 4'b1001 ;
4'b1110 : DATA = 4'b1101 ;
4'b1111 : DATA = 4'b1111 ;
endcase
end

endmodule

When using an RTL description of a ROM, the synthesis tool creates ROMs from random logic gates that are implemented using function generators.

Another method for implementing ROMs is instantiating the 16x1 or 32x1 ROM primitives. To define the ROM value, use the Set Attribute or equivalent command to set the INIT property on the ROM component.

Note: Refer to your synthesis tool documentation for the correct syntax.

This type of command writes the ROM contents to the netlist file so the Xilinx tools can initialize the ROM. The INIT value should be specified in hexadecimal values. See the VHDL and Verilog RAM examples in the following section for examples of this property using a RAM primitive.

Implementing XC4000 Family RAMs

Do not use RTL descriptions of RAMs in your code because they do not compile efficiently and can cause combinatorial loops. The exception to this is if your synthesis tool can infer memory. In this case, you must follow a strict coding style. Refer to your vendor's documentation for more information.

You can implement RAMs as follows.

When implementing RAM in XC4000 and Spartan designs, Xilinx recommends using the synchronous write, edge-triggered RAM (RAM16X1S, RAM32X1S, or RAM16X1D) instead of the asynchronous-write RAM (RAM16X1 or RAM32X1) to simplify write timing and increase RAM performance.

Examples of an instantiation of edge-triggered RAM primitives are provided in the following VHDL and Verilog designs. As with ROMs, initial RAM values can be specified from the command line. The INIT property value is specified in hexadecimal values. Refer to your synthesis tool documentation for the correct command and syntax.

An Exemplarexample of a RAM inference (ram.vhd) is also included in this section. Check with your synthesis tool vendor for the availability of this feature.

Instantiating RAM VHDL Example

------------------------------------------
-- RAM_PRIMITIVE.VHD --
-- Example of instantiating 4 --
-- 16x1 synchronous RAMs --
-- HDL Synthesis Design Guide for FPGAs --
-- May 1997 --
------------------------------------------

library IEEE;
use IEEE.std_logic_1164.all;


entity ram_primitive is

port ( DATA_IN, ADDR : in STD_LOGIC_VECTOR(3 downto 0);
WE, CLOCK : in STD_LOGIC;
DATA_OUT : out STD_LOGIC_VECTOR(3 downto 0));

end ram_primitive;


architecture STRUCTURAL_RAM of ram_primitive is

component RAM16X1S
port (D, A3, A2, A1, A0, WE, WCLK : in STD_LOGIC;
O : out STD_LOGIC);
end component;

begin

RAM0 : RAM16X1S port map (O => DATA_OUT(0), D => DATA_IN(0),
A3 => ADDR(3), A2 => ADDR(2),
A1 => ADDR(1), A0 => ADDR(0),
WE => WE, WCLK => CLOCK);

RAM1 : RAM16X1S port map (O => DATA_OUT(1), D => DATA_IN(1),
A3 => ADDR(3), A2 => ADDR(2),
A1 => ADDR(1), A0 => ADDR(0),
WE => WE, WCLK => CLOCK);

RAM2 : RAM16X1S port map (O => DATA_OUT(2), D => DATA_IN(2),
A3 => ADDR(3), A2 => ADDR(2),
A1 => ADDR(1), A0 => ADDR(0),
WE => WE, WCLK => CLOCK);

RAM3 : RAM16X1S port map (O => DATA_OUT(3), D => DATA_IN(3),
A3 => ADDR(3), A2 => ADDR(2),
A1 => ADDR(1), A0 => ADDR(0),
WE => WE, WCLK => CLOCK);

end STRUCTURAL_RAM;

Instantiating RAM Verilog Example

      //////////////////////////////////////////
// RAM_PRIMITIVE.V //
// Example of instantiating 4 //
// 16x1 Synchronous RAMs //
// HDL Synthesis Design Guide for FPGAs //
// August 1997 //
//////////////////////////////////////////


module ram_primitive (DATA_IN, ADDR, WE, CLOCK, DATA_OUT);

input [3:0] DATA_IN, ADDR;
input WE, CLOCK;
output [3:0] DATA_OUT;

RAM16X1S RAM0 (.O(DATA_OUT[0]), .D(DATA_IN[0]), .A3(ADDR[3]),
.A2(ADDR[2]), .A1(ADDR[1]), .A0(ADDR[0]),
.WE(WE), .WCLK(CLOCK));

RAM16X1S RAM1 (.O(DATA_OUT[1]), .D(DATA_IN[1]), .A3(ADDR[3]),
.A2(ADDR[2]), .A1(ADDR[1]), .A0(ADDR[0]),
.WE(WE), .WCLK(CLOCK));

RAM16X1S RAM2 (.O(DATA_OUT[2]), .D(DATA_IN[2]), .A3(ADDR[3]),
.A2(ADDR[2]), .A1(ADDR[1]), .A0(ADDR[0]),
.WE(WE), .WCLK(CLOCK));

RAM16X1S RAM3 (.O(DATA_OUT[3]), .D(DATA_IN[3]), .A3(ADDR[3]),
.A2(ADDR[2]), .A1(ADDR[1]), .A0(ADDR[0]),
.WE(WE), .WCLK(CLOCK));

endmodule

Inferring RAM VHDL Example

library ieee;
use ieee.std_logic_1164.all;
library exemplar;
use exemplar.exemplar_1164.all;
library exemplar;
use exemplar.exemplar.all;

package my_pkg is
type MEM_WORD is array (6 downto 0) of elbit_vector (1 downto 0);
end my_pkg;
=20
library exemplar;
use exemplar.exemplar.all;
use work.my_pkg.all;

entity mem is
port (dio : inout elbit_vector (1 downto 0);=20
meme, we, inclk, outclk : in bit;
addr : integer range 6 downto 0;
ro : out bit);
attribute clock_node : boolean;
attribute clock_node of inclk : signal is TRUE;
attribute clock_node of outclk : signal is TRUE;

=20
end mem;

architecture behav of mem is
signal mem : MEM_WORD;
signal d_int : elbit_vector (1 downto 0);
begin

process (inclk)
begin
if (inclk'event and inclk =3D '1') then
if (meme =3D '1' and we =3D '1') then
mem(addr) <=3D dio;
end if;
end if;
end process;
process (outclk)
begin
if (outclk'event and outclk =3D '1') then
d_int <=3D mem (addr);
end if;
end process;
dio <=3D d_int when (meme =3D '1' and we =3D '0') else "ZZ";
end behav;

Using LogiBLOX to Implement Memory

If you must instantiate memory, use LogiBLOX to create a memory module larger than 32X1 (16X1 for Dual Port). Implementing memory with LogiBLOX is similar to implementing any module with LogiBLOX except for defining the Memory initialization file. Use the following steps to create a memory module.

Note: Refer to the “Using LogiBLOX in HDL Designs” section for more information on using LogiBLOX.

  1. Before using LogiBLOX, verify the following.

  2. To run LogiBLOX, enter the following command.

    lbgui

    The LogiBLOX Setup Window appears after the LogiBLOX module generator is loaded. This window allows you to name and customize the module you want to create.

  3. Select the Vendor tab in the Setup Window. Select your synthesis tool in the Vendor Name field to specify the correct bus notation for connecting your module.

    Select the Project Directory tab. Enter the directory location of your project in the LogiBLOX Project Directory field.

    Select the Device Family tab. Select the target device for your design in the Device Family field.

    Select the Options tab and select the applicable options for your design.

    Select OK.

  4. Enter a name in the Module Name field in the Module Selector Window.

    Select the Memories module type from the Module Type field to specify that you are creating a memory module.

    Select a width (any value from 1 to 64 bits) for the memory from the Data Bus Width field.

    In the Details field, select the type of memory you are creating (ROM, RAM, SYNC_RAM, or DP_RAM).

    Enter a value in the Memory Depth field for your memory module.

    Note: Xilinx recommends (this is not a requirement) that you select a memory depth value that is a multiple of 16 because this is the memory size of one lookup table.

  5. If you want the memory module initialized to all zeros on power up, you do not need to create a memory file (Mem File). However, if you want the contents of the memory initialized to a value other than zero, you must create and edit a memory file. Enter a memory file name in the Mem File field and click on the Edit button. Continue with the following steps.

    Note: Some memory modules can only be initialized to zero. Refer to the Xilinx Programmable Logic Data Book for more information.

    1. A memory template file in a text editor is displayed. This file does not contain valid data, and must be edited before you can use it. The data values specified in the memory file Data Section define the contents of the memory. Data values are specified sequentially, beginning with the lowest address in the memory, as defined.

    2. Specify the address of a data value. The default radix of the data values is 16. If more than one radix definition is listed in the memory file header section, the last definition is the radix used in the Data Section.

      The following definition defines a 16-word memory with the contents 6, 4, 5, 5, 2, 7, 5, 3, 5, 5, 5, 5, 5, 5, 5, 5, starting at address 0. Note that the contents of locations 2, 3, 6, and 8 through 15 are defined via the default definition. Two starting addresses, 4 and 7, are given.

               depth 16
      default 5
      data 6,4,
      4: 2, 7,
      7: 3


    3. After you have finished specifying the data for the memory module, save the file and exit the editor.

  6. Click the OK button. Selecting OK generates a component instantiation declaration, a behavioral model, and an implementation netlist.

  7. Copy the HDL module declaration/instantiation into your HDL design. The template file created by LogiBLOX is module_name.vhi for VHDL and module_name.vei for Verilog, and is saved in the project directory as specified in the LogiBLOX setup.

  8. Complete the signal connections of the instantiated LogiBLOX memory module to the rest of your HDL design, and complete initial design coding.

  9. Perform a behavioral simulation on your design. For more information on behavioral simulation, refer to the “Simulating Your Design” chapter.

  10. Create an implementation script. Add a Set Don't Touch or equivalent attribute to the instantiated LogiBLOX memory module, and compile your design.

    Also, if you have a Verilog design, use a remove design type of command before writing the .xnf or .edif netlist.

    Note: If you do not use this type of command, the netlist file may be empty. If this occurs, the Xilinx software will trim this module/component and all connected logic. Refer to your synthesis tool documentation for the correct syntax.

  11. Compile your design and create a .xnf or .edif file. You can safely ignore the following type of warning messages.

    Warning: Can't find the design in the library WORK. (LBR-1)

    Warning: Unable to resolve reference LogiBLOX_name in design_name. (LINK-5)

    Warning: Design design_name has 1 unresolved references. For more detailed information, use the “link” command. (UID-341)

  12. Implement your design with the Xilinx tools. Verify that the .ngc file created by LogiBLOX is in the same project directory as the netlist.

    You may get the following warnings during the NGDBuild and mapping steps. These messages are issued if the Xilinx software can not locate the corresponding .ngc file created by LogiBLOX.

    Warning: basnu - logical block LogiBLOX_instance_name of type LogiBLOX_name is unexpanded. Logical Design DRC complete with 1 warning(s).

    If you get this message, you will get the following message during mapping.

    ERROR:basnu - logical block LogiBLOX_instance_name of type LogiBLOX_name is unexpanded. Errors detected in general drc.

    If you get these messages, first verify that the .ngc file created by LogiBLOX is in the project directory. If the file is there, verify that the module is properly instantiated in the code.

  13. To simulate your post-layout design, convert your design to a timing netlist and use the back-annotation flow applicable to your synthesis tool.

    Note: For more information on simulation, refer to the “Simulating Your Design” chapter.