Modeling memory in VHDL
As a VHDL user, you might be tempted to model a memory using signals. Two common simulator problems are the likely result:
- You may get a "memory allocation error" message, which typically means the simulator ran out of memory and failed to allocate more storage.
- Or, you may get very long load, elaboration or run times.
These problems are usually explained by the fact that signals consume a substantial amount of memory (many dozens of bytes per bit), all of which needs to be loaded or initialized before your simulation starts.
A simple alternative implementation provides some excellent performance benefits:
- storage required to model the memory can be reduced by 1-2 orders of magnitude
- startup and run times are reduced
- associated memory allocation errors are eliminated
The trick is to model memory using variables instead of signals.
In the example below, we illustrate three alternative architectures for entity "memory". Architecture "style_87_bad" uses a vhdl signal to store the ram data. Architecture "style_87" uses variables in the "memory" process, and architecture "style_93" uses variables in the architecture.
For large memories, architecture "style_87_bad" runs many times longer than the other two, and uses much more memory. This style should be avoided.
Both architectures "style_87" and "style_93" work with equal efficiently. You'll find some additional flexibility with the VHDL 1993 style, however, because the ram storage can be shared between multiple processes. For example, a second process is shown that initializes the memory; you could add other processes to create a multi-ported memory.
To implement this model, you will need functions that convert vectors to integers. To use it you will probably need to convert integers to vectors.
Example functions are provided below in package "conversions".
use std.standard.all; library ieee; use ieee.std_logic_1164.all; use work.conversions.all; entity memory is generic(add_bits : integer := 12; data_bits : integer := 32); port(add_in : in std_ulogic_vector(add_bits-1 downto 0); data_in : in std_ulogic_vector(data_bits-1 downto 0); data_out : out std_ulogic_vector(data_bits-1 downto 0); cs, mwrite : in std_ulogic; do_init : in std_ulogic); subtype word is std_ulogic_vector(data_bits-1 downto 0); constant nwords : integer := 2 ** add_bits; type ram_type is array(0 to nwords-1) of word; end; architecture style_93 of memory is ------------------------------ shared variable ram : ram_type; ------------------------------ begin memory: process (cs) variable address : natural; begin if rising_edge(cs) then address := sulv_to_natural(add_in); if (mwrite = '1') then ram(address) := data_in; data_out <= ram(address); else data_out <= ram(address); end if; end if; end process memory; -- illustrates a second process using the shared variable initialize: process (do_init) variable address : natural; begin if rising_edge(do_init) then for address in 0 to nwords-1 loop ram(address) := data_in; end loop; end if; end process initialize; end architecture style_93; architecture style_87 of memory is begin memory: process (cs) ----------------------- variable ram : ram_type; ----------------------- variable address : natural; begin if rising_edge(cs) then address := sulv_to_natural(add_in); if (mwrite = '1') then ram(address) := data_in; data_out <= ram(address); else data_out <= ram(address); end if; end if; end process; end style_87; architecture bad_style_87 of memory is ---------------------- signal ram : ram_type; ---------------------- begin memory: process (cs) variable address : natural := 0; begin if rising_edge(cs) then address := sulv_to_natural(add_in); if (mwrite = '1') then ram(address) <= data_in; data_out <= data_in; else data_out <= ram(address); end if; end if; end process; end bad_style_87; ------------------------------------------------------------ ------------------------------------------------------------ use std.standard.all; library ieee; use ieee.std_logic_1164.all; package conversions is function sulv_to_natural(x : std_ulogic_vector) return natural; function natural_to_sulv(n, bits : natural) return std_ulogic_vector; end conversions; package body conversions is function sulv_to_natural(x : std_ulogic_vector) return natural is variable n : natural := 0; variable failure : boolean := false; begin assert (x'high - x'low + 1) <= 31 report "Range of sulv_to_natural argument exceeds natural range" severity error; for i in x'range loop n := n * 2; case x(i) is when '1' | 'H' => n := n + 1; when '0' | 'L' => null; when others => failure := true; end case; end loop; assert not failure report "sulv_to_natural cannot convert indefinite std_ulogic_vector" severity error; if failure then return 0; else return n; end if; end sulv_to_natural; function natural_to_sulv(n, bits : natural) return std_ulogic_vector is variable x : std_ulogic_vector(bits-1 downto 0) := (others => '0'); variable tempn : natural := n; begin for i in x'reverse_range loop if (tempn mod 2) = 1 then x(i) := '1'; end if; tempn := tempn / 2; end loop; return x; end natural_to_sulv; end conversions;
Model Technology Incorporated Voice: (503) 641-1340 Fax: (503)526-5410 www.model.com sales@model.com |