⬆️ ⬇️

Description of VHDL Memory Blocks

This article shows the basic principles of the description of the modules ROM and RAM in the language of VHDL. The article is aimed at beginners. Its purpose is to give a general idea of ​​the description of memory modules in the VHDL language. Examples and illustrations are provided for the Quartus II v. Package. 9.1. It is assumed that the reader knows how to create a project in the Quartus II package, to compile and simulate it.



General provisions



Memory in the VHDL language is described as an array ( array ) of vectors. The width of the vector is determined by the bit depth of the memory cell, and the number of vectors is determined by the number of cells in the memory module.



For example, for a memory module of 32 cells, each of which contains 8 bits, you must declare an array containing 32 vectors, each of which is eight-bit.



 type mem is array (0 to 31) of std_logic_vector (7 downto 0); 


Next, it is necessary to describe the address inputs, data inputs and outputs, control signals. The type of data ports must match the data type of the individual cell. For the example above, this is std_logic_vector (7 downto 0) .

')

 data_in: in std_logic_vector (7 downto 0); data_out: out std_logic_vector (7 downto 0); 


The data type for an address is integer or types based on it. The integer type is necessary because the address is used as an index of the memory array.



 addr: in integer range 0 to 31; 


Memory description is best done using parameterized modules. This permits the reuse of written code. The following is an example of a parameterized 32 Γ— 8 module. In the example, the parameters addr_width and data_width are used to describe the memory module, which specify the width of the address and data buses, respectively. The number of cells in the memory block in this case is defined as 2**addr_width , and their width is equal to data_width .



 generic (addr_width: natural:= 5; data_width: natural:=8); port ( addr: in integer range 0 to 2**addr_width - 1; data_in: in std_logic_vector (data_width-1 downto 0); data_out: out std_logic_vector (data_width-1 downto 0) ); type mem is array (2**addr_width-1 downto 0) of std_logic_vector (7 downto 0); 


Description of permanent storage devices in the VHDL language


When describing permanent storage devices, the contents of cells must be determined when writing a program. You can use several options for determining the contents of the memory:

  1. creating a constant or array type signal;
  2. use of the case ;
  3. use of * .mif file and attributes of synthesis.


Of the three options, the first two can be implemented on FPGA chips of any manufacturer, and the third is possible only in the Quartus II package.



Determining the contents of memory using a constant or array.


When using this option, you must first declare a type that will correspond to the size of the memory block. Then a constant of this type is declared and the contents of all the cells of the array are determined.

For example, let's declare a new type of ROM, which is an array of 8 cells, each of which has a size of 8 bits. Then we define a Content constant of the ROM type.



 type ROM is array (0 to 7) of std_logic_vector (7 downto 0); constant Content: ROM := ( 0 => "00000001", 1 => "00000010", 2 => "00000011", 3 => "00000100", 4 => "00000101", 5 => "00000110", 6 => "00000111", 7 => "00001000", ); 


To use such a constant, you simply need to address the desired cell in the array using input data from the address lines. The source data port must be of the same type as the cell type of the memory block. For the example above, the source port Data_out must be of type std_logic_vector (7 downto 0) . Access to the contents of the memory will look like this:



 Data_out <= Content (Addr); 


Example 1. Consider an example of a complete description of a memory block using a constant. The memory block that meets this description is shown in Figure 1.





Figure 1 - The memory block described in example 1



Lines 13 and 14 declare an array type of 32 cells, each of which contains 8 bits.

Lines 15 through 23 set the value of the cells in the array. The values ​​for the first 16 cells are determined separately - lines 16 through 22. All other cells are filled with the same value β€œ1111 1111” using the word others β€” line 23.

The operation of the memory module is described using the process operator, the initialization list of which includes the signals clk , cs - clock and chip selection, respectively. If the signal cs equals one, the original ROM lines go to the Z-state (lines 27 and 28). If the signal cs is equal to zero, then the outputs go to the working state and the operation of the chip occurs.

Line 29 checks for the leading edge of the clock signal clk .

Lines 30-35 describe the process of reading information from the ROM. If the signal rd equals one, then the information is allowed to read, if it equals zero, the original lines go to the Z-state (line 32). To access a specific cell in the memory module, line 30 is used. The content constant has the data type of the std_logic_vector cell, which corresponds to the type of the original data_out signal. The address signal in the memory module is also of the std_logic_vector type, so for addressing the cell in the content array, the necessary conversion is of the std_logic_vector type to the integer type, which is done using the to_integer (unsigned (address) structure. only later - to the integer type. More clearly about the type conversion here .



 1 library ieee; 2 use ieee.std_logic_1164.all; 3 use ieee.numeric_std.all; 4 entity ROM is 5 port (clk : in std_logic; 6 cs : in std_logic; 7 rd : in std_logic; 8 address : in std_logic_vector(4 downto 0); 9 data_out: out std_logic_vector(7 downto 0)); 10 end ROM; 11 architecture behav of ROM is 12 type ROM_array is array (0 to 31) 13 of std_logic_vector(7 downto 0); 14 constant content: ROM_array := ( 15 0 => "00000001", 16 1 => "00000010", 17 2 => "00000011", 18 . . . 19 12 => "00001101", 20 13 => "00001110", 21 14 => "00001111", 22 others => "11111111"); 23 begin 24 process(clk, cs) 25 begin 26 if(cs = '1' ) then 27 data_out <= "ZZZZZZZZ"; 28 elsif (clk'event and clk = '1') then 29 if rd = '1' then 30 data_out <= content(to_integer (unsigned (address))); 31 else 32 data_out <= "ZZZZZZZZ"; 33 end if; 34 end if; 35 end process; 36 end behav; 


The simulation results are shown in Figure 2.



Figure 2 - The results of the simulation of the memory block



Determining memory contents with a case .


When using the case statement, you must declare the ports, and the definition of the contents of the memory takes place in the architectural body. Each value of the address corresponds to the contents of this memory cell. The design will look like this:



 when  => _ <= _; 


Example 2. As an example, consider the description of a 256 Γ— 6 memory module. The address is described by an eight-bit vector of type std_logic , the data output is described by a six-bit vector of type std_logic . With the help of the case statement, the contents of the first ten cells of the memory block are separately determined, all others are defined together with the help of when others operators.



 library ieee; use ieee.std_logic_1164.all; entity mem is port ( clock : in std_logic; address : in std_logic_vector (7 downto 0); data_out : out std_logic_vector (5 downto 0)); end mem; architecture rtl of mem is begin process (clock) begin if rising_edge (clock) then case address is when "00000000" => data_out <= "000111"; when "00000001" => data_out <= "000110"; when "00000010" => data_out <= "000010"; when "00000011" => data_out <= "100000"; when "00000100" => data_out <= "100010"; when "00000101" => data_out <= "001110"; when "00000110" => data_out <= "111100"; when "00000111" => data_out <= "110111"; when "00001000" => data_out <= "111000"; when "00001001" => data_out <= "100110"; when others => data_out <= "101111"; end case; end if; end process; end rtl; 


The results of the simulation of the memory block from Example 2 are shown in Figure 3.



Figure 3 - Simulation of the memory block from example 2



Determining the contents of memory using the mif file.


This version of determining the contents of the memory works only with Altera products, but also allows you to quickly change the contents of the memory. For the description, it is necessary to connect the Altera altera_syn_attributes synthesis attribute library of the company and use the ram_init_file attribute. By default, the library is located in the folder _quartus\libraries\vhdl\altera . This attribute specifies a mif file that contains information about the contents of the memory.

To use this attribute, you must declare the synthesis attribute as a lowercase type:



 attribute ram_init_file : string; 


Create a link between the ram_init_file attribute and a signal that describes a block of memory. The attribute value must match the * .mif file name:



 attribute ram_init_file of rom : signal is "mem.mif"; 


Example 3. The example describes the description of a 256 Γ— 8 memory block. Lines 1-4 describe the libraries and modules of these libraries. It can be seen that in lines 1 and 4 the synthesis attribute library is described.

Lines 5-9 describe the interface part of the module.

In lines 11 and 12, the mem_t type and the rom signal of this type are entered, which describe the memory module.

In line 13, the ram_init_file attribute of the string type is specified, and in line 14 this attribute links to the rom signal and a link is made to the mem.mif file, in which the contents of the memory module are listed.

The use of the memory module is described in line 19 and represents only the output to the source port of the contents of the cell, which is determined by the address signal.



 1 library ieee, altera; 2 use ieee.std_logic_1164.all; 3 use ieee.numeric_std.all; 4 use altera.altera_syn_attributes.all; 5 entity mem is 6 port (clk: in std_logic; 7 addr: in natural range 0 to 255; 8 q: out std_logic_vector (7 downto 0)); 9 end entity; 10 architecture rtl of mem is 11 type mem_t is array (255 downto 0) of std_logic_vector(7 downto 0); 12 signal rom: mem_t; 13 attribute ram_init_file: string; 14 attribute ram_init_file of rom: signal is "mem.mif"; 15 begin 16 process(clk) 17 begin 18 if(rising_edge(clk)) then 19 q <= rom(addr); 20 end if; 21 end process; 22 end rtl; 


The contents of the mif file is determined by the help of the mif file editor. For the given example, the window with the contents of the memory is shown in Figure 4, and the modulation results are shown in Figure 5.





Figure 4 - The contents of the memory module





Figure 5 - Time diagrams of the memory module operation from Example 3



Description of online storage devices


The description of the operational storage devices (RAM) differs from the description of the permanent storage devices only in that it can be recorded in the RAM.

As an example, consider synchronous RAM. His work is described in the following table:

Wn_RCSnDo [3..0]Operation mode
00ZzzzzRecord
one0Initial dataReading
Γ—oneZzzzzSaving information


As in the previous examples for working with memory, we define a new type as an array that has the size of the necessary memory module.



 library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; use ieee.numeric_std.all; entity mem is port (clk : in std_logic; Wn_R : in std_logic; CSn : in std_logic; addr : in std_logic_vector(4 downto 0); Di : in std_logic_vector(3 downto 0); Do : out std_logic_vector(3 downto 0)); end mem; architecture syn of mem is type ram_type is array (31 downto 0) of std_logic_vector (3 downto 0); signal RAM : ram_type; begin process (clk, CSn) begin if CSn = '0' then if (clk'event and clk = '1') then if (Wn_R = '0') then RAM(to_integer(unsigned(addr))) <= Di; Do <= "ZZZZ"; else Do <= RAM(to_integer(unsigned(addr))); end if; end if; else Do <= "ZZZZ"; end if; end process; end syn; 


The results of the memory module stimulator are shown in Figure 6. Here it is necessary to pay attention to the fact that the initial content of all memory cells is zero. This can be seen while reading cells 6-8.



Figure 6 - Temporary diagrams of the RAM



In more detail in Quartus II Hahdbook any version. For example, the current one is 13 . Section 6 - Recommended HDL Coding Style.

Source: https://habr.com/ru/post/197750/



All Articles