VHDL coding tips and tricks: November 2017

Thursday, November 2, 2017

VHDL code for a Dual Port RAM with Testbench

There are many types RAM. These differ in terms of the number of ports, synchronous or asynchronous modes of operation etc.

Here in this post, I have written the VHDL code for a simple Dual port RAM, with two ports 0 and 1. The writing is allowed to only one port, on the positive edge the clock. The reading is done from both the ports asynchronously, that means we don't have to wait for the clock signal to read from the memory.

I have fixed the RAM size as 16*8 bits, meaning 16 elements of 8 bits each. These specifications can be changed easily by altering few lines in the code.

VHDL code:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity dual_port_ram is
port(   clk: in std_logic; --clock
        wr_en : in std_logic;   --write enable for port 0
        data_in : in std_logic_vector(7 downto 0);  --Input data to port 0.
        addr_in_0 : in std_logic_vector(3 downto 0);    --address for port 0
        addr_in_1 : in std_logic_vector(3 downto 0);    --address for port 1
        port_en_0 : in std_logic;   --enable port 0.
        port_en_1 : in std_logic;   --enable port 1.
        data_out_0 : out std_logic_vector(7 downto 0);  --output data from port 0.
        data_out_1 : out std_logic_vector(7 downto 0)   --output data from port 1.
    );
end dual_port_ram;

architecture Behavioral of dual_port_ram is

--type and signal declaration for RAM.
type ram_type is array(0 to 15) of std_logic_vector(7 downto 0);
signal ram : ram_type := (others => (others => '0'));

begin

process(clk)
begin
    if(rising_edge(clk)) then
        --For port 0. Writing.
        if(port_en_0 = '1') then    --check enable signal
            if(wr_en = '1') then    --see if write enable is ON.
                ram(conv_integer(addr_in_0)) <= data_in;
            end if;
        end if;
    end if;
end process;

--always read when port is enabled.
data_out_0 <= ram(conv_integer(addr_in_0)) when (port_en_0 = '1') else
            (others => 'Z');
data_out_1 <= ram(conv_integer(addr_in_1)) when (port_en_1 = '1') else
            (others => 'Z');
            
end Behavioral;

Testbench code:

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
 
ENTITY tb IS
END tb;
 
ARCHITECTURE behavior OF tb IS 
 
    -- Component Declaration for the Unit Under Test (UUT)
    COMPONENT dual_port_ram
    PORT(
         clk : IN  std_logic;
         wr_en : IN  std_logic;
         data_in : IN  std_logic_vector(7 downto 0);
         addr_in_0 : IN  std_logic_vector(3 downto 0);
         addr_in_1 : IN  std_logic_vector(3 downto 0);
         port_en_0 : IN  std_logic;
         port_en_1 : IN  std_logic;
         data_out_0 : OUT  std_logic_vector(7 downto 0);
         data_out_1 : OUT  std_logic_vector(7 downto 0)
        );
    END COMPONENT;
    

   --Inputs
   signal clk : std_logic := '0';
   signal wr_en : std_logic := '0';
   signal data_in : std_logic_vector(7 downto 0) := (others => '0');
   signal addr_in_0 : std_logic_vector(3 downto 0) := (others => '0');
   signal addr_in_1 : std_logic_vector(3 downto 0) := (others => '0');
   signal port_en_0 : std_logic := '0';
   signal port_en_1 : std_logic := '0';
    --Outputs
   signal data_out_0 : std_logic_vector(7 downto 0);
   signal data_out_1 : std_logic_vector(7 downto 0);
   -- Clock period definitions
   constant clk_period : time := 10 ns;
 
BEGIN
 
    -- Instantiate the Unit Under Test (UUT)
   uut: dual_port_ram PORT MAP (
          clk => clk,
          wr_en => wr_en,
          data_in => data_in,
          addr_in_0 => addr_in_0,
          addr_in_1 => addr_in_1,
          port_en_0 => port_en_0,
          port_en_1 => port_en_1,
          data_out_0 => data_out_0,
          data_out_1 => data_out_1
        );

   -- Clock process definitions
   clk_process :process
   begin
        clk <= '1';
        wait for clk_period/2;
        clk <= '0';
        wait for clk_period/2;
   end process;

   -- Stimulus process
   stim_proc: process
   begin        
        --these 4 lines shows that when port is not enabled, we cannot perform write or read operation.
        port_en_0 <= '0';
        wr_en <= '1';
        data_in <= X"FF";
        addr_in_0 <= X"1";  
        wait for 20 ns;
        --Write all the locations of RAM
        port_en_0 <= '1';   
      for i in 1 to 16 loop
            data_in <= conv_std_logic_vector(i,8);
            addr_in_0 <= conv_std_logic_vector(i-1,4);
            wait for 10 ns;
        end loop;
        wr_en <= '0';
        port_en_0 <= '0';   
        --Read from port 1, all the locations of RAM.
        port_en_1 <= '1';   
        for i in 1 to 16 loop
            addr_in_1 <= conv_std_logic_vector(i-1,4);
            wait for 10 ns;
        end loop;
        port_en_1 <= '0';   
        --Wait eternally.
      wait;
   end process;

END;

The code synthesised and simulated using Xilinx ISE 14.6 tool.

Simulation waveform:


Synthesis:

The synthesis was done for a Virtex 6 device. Note that, for this particular device the inbuilt BRAM's were not used to implement this RAM because of asynchronous read operations. Making it synchronous might use the available BRAM's.