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.
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.