I have been trying to implement a simple dual port block RAM in VHDL but it does not yield the expected results in simulation. Here is the code:
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;
entity rams is
generic ( g_adress_width: integer:= 18;
g_ram_size: integer:= 1000
);
port(
clka : in std_logic;
clkb : in std_logic;
wea : in std_logic;
web : in std_logic;
addra : in std_logic_vector(g_adress_width-1 downto 0);
addrb : in std_logic_vector(g_adress_width-1 downto 0);
dia : in std_logic_vector(15 downto 0);
dib : in std_logic_vector(15 downto 0);
doa : out std_logic_vector(15 downto 0);
dob : out std_logic_vector(15 downto 0));
end rams;
architecture syn of rams is
type ram_type is array (g_ram_size-1 downto 0) of std_logic_vector(15 downto 0);
signal RAM : ram_type;
begin
process (CLKA)
begin
if CLKA'event and CLKA = '1' then
DOA <= RAM(conv_integer(ADDRA));
if WEA = '1' then --always 0
RAM(conv_integer(ADDRA)) <= DIA; --does not execute
end if;
end if;
end process;
process (CLKB)
begin
if CLKB'event and CLKB = '1' then
DOB <= RAM(conv_integer(ADDRB));
if WEB = '1' then
RAM(conv_integer(ADDRB)) <= DIB;
end if;
end if;
end process;
end syn;
And here is the simulation:
Both clka and clkb are connected to the same clock. I give some arbitrary values (unsigned 300 and 355) to dib.
Basically, doa is always reading so I expect it to be undefined until something has been written to those block ram addresses with dib, but it is showing undefined values always.
What I expect to happen is for doa to read 300 when addra is at 0 again and to read 355 when addra is at 15. Something like this (pardon my paint skills):
Would appreciate it if someone could point me in the right direction of what I am doing wrong. Thanks.
EDIT: Code is modified to this and it works now (thanks to Paebbels solution):
signal RAM : ram_type;
begin
process (CLKA)
begin
if CLKA'event and CLKA = '1' then
DOA <= RAM(to_integer(unsigned(ADDRA)));
if WEA = '1' then --always 0
RAM(to_integer(unsigned(ADDRA))) <= DIA; --does not happen
end if;
end if;
if CLKA'event and CLKA = '1' then
DOB <= RAM(to_integer(unsigned(ADDRB)));
if WEB = '1' then
RAM(to_integer(unsigned(ADDRB))) <= DIB;
end if;
end if;
end process;
end syn;
This description of a dual-clock RAM is wrong. You need to use either:
Using one signal and two processes ist not correct. It creates multiple drives on a signal. This in turn creates a multiple source problem. While your simulation will work, because of the resolved type std_logic_vector
in your user defined array type, synthesis will fail.
In addition, to allow inference of BlockRAMs, you need to represent the internal structure of BlockRAMs in you VHDL code. This means you need to add pipeline registers on the address path.
You should read UG901 - Vivado Synthesis Guide and search for "RAM HDL Coding Techniques".
Further more, you should use package numeric_std
instead of std_logic_unsigned
, which is not an official IEEE package.
A working true-dual-port (TDP) BlockRAM implementation can be found in the PoC-Library: PoC.mem.ocram.tdp. This implementation will also work on Altera/Intel FPGAs and on Lattice FPGAs.
-- RAM can be inferred correctly only if '-use_new_parser yes' is enabled in XST options
subtype word_t is std_logic_vector(D_BITS - 1 downto 0);
type ram_t is array(0 to DEPTH - 1) of word_t;
signal ram : ram_t;
signal a1_reg : unsigned(A_BITS-1 downto 0);
signal a2_reg : unsigned(A_BITS-1 downto 0);
begin
process (clk1, clk2)
begin -- process
if rising_edge(clk1) then
if ce1 = '1' then
if we1 = '1' then
ram(to_integer(a1)) <= d1;
end if;
a1_reg <= a1;
end if;
end if;
if rising_edge(clk2) then
if ce2 = '1' then
if we2 = '1' then
ram(to_integer(a2)) <= d2;
end if;
a2_reg <= a2;
end if;
end if;
end process;
q1 <= (others => 'X') when SIMULATION and is_x(std_logic_vector(a1_reg)) else
ram(to_integer(a1_reg)); -- returns new data
q2 <= (others => 'X') when SIMULATION and is_x(std_logic_vector(a2_reg)) else
ram(to_integer(a2_reg)); -- returns new data