Reputation: 247
I'm trying to simulate RAM type memory, the input and output are presented on the same lines and the functionality and selected via 3 control pins. ceN is for enabling the device (otherwise the output is high Z), weN is for enabling writing into the RAM and oeN is not enabling output to data_io vector. These controls are all active low type.
This passes compilation under Quartus II but does not simulate well with Modelsim or with the built in simulation of Quartus.
library ieee ;
use ieee.std_logic_1164.all ;
entity ram1 is
port ( address : in integer range 0 to 255 ;
ceN : in std_logic ;
weN : in std_logic ;
oeN : in std_logic ;
data_io : buffer std_logic_vector(7 downto 0) ) ;
end ram1 ;
architecture arc_ram1 of ram1 is
type mem_array is array (0 to 255) of std_logic_vector(7 downto 0) ;
signal ram : mem_array ;
signal data : std_logic_vector(7 downto 0) ;
begin
process ( address , ceN , weN , oeN , data_io )
begin
-- write to RAM
if ceN = '0' then
if weN = '0' then
ram(address) <= to_x01(data_io) ;
elsif oeN = '0' then
data_io <= ram(address) ;
end if ;
else
data_io <= (others => 'Z') ;
end if ;
end process ;
end arc_ram1 ;
Upvotes: 2
Views: 760
Reputation: 554
You do want to switch the type of the data_io signal to inout. When you do that you're implying a tri-state buffer. That means in your logic (including your testbench) you have to ensure that whenever one side is driving the interface, the other side is driving a 'Z' else the bus will have two drivers and the simulator will display an 'X' if both drivers have equal drive strengths.
Here's a simple test sequence that should work with your DUT to show that the RAM read/write logic is working correctly:
test: process is
begin
address <= 0;
data_io <= (others => 'Z');
ceN <= '0';
oeN <= '0';
weN <= '1';
wait for 20 ns;
address <= 0;
data_io <= (others => '1');
oeN <= '1';
weN <= '0';
wait for 20 ns;
address <= 0;
data_io <= (others => 'Z');
oeN <= '0';
weN <= '1';
wait for 20 ns;
address <= 1;
data_io <= (others => '0');
oeN <= '1';
weN <= '0';
wait for 20 ns;
address <= 1;
data_io <= (others => 'Z');
oeN <= '0';
weN <= '1';
wait for 20 ns;
address <= 0;
wait for 20 ns;
end process test;
I had to modify your logic to ensure that the RAM isn't driving the data_io bus when the testbench is trying to drive it during a memory write:
process ( address , ceN , oeN, weN , data_io )
begin
-- write to RAM
if ceN = '0' then
if weN = '0' then
ram(address) <= to_x01(data_io) ;
data_io <= (others => 'Z') ; -- *** Added this ***
elsif oeN = '0' then
data_io <= ram(address) ;
end if ;
else
data_io <= (others => 'Z') ;
end if ;
end process ;
The reason I had to add the line that tri-states the data_io bus during a write might be easier to understand if you code the process this way:
process ( address , ceN , oeN, weN , data_io )
begin
-- write to RAM
if ((ceN = '0') and (weN = '0')) then
ram(address) <= to_x01(data_io) ;
end if ;
-- read from RAM
if ((ceN = '0') and (oeN = '0')) then
data_io <= ram(address) ;
else
data_io <= (others => 'Z') ;
end if ;
end process ;
Upvotes: 1
Reputation: 326
You've created some transparent latches!
Make your processes synchronous, eg
process(clk)
begin
if rising_edge(clk) then
...
end if;
end process;
As an example, there's a big difference between the following:
process(en, d)
signal q : std_logic;
begin
if en = '1' then
q <= d;
end if;
end process;
and the following:
process(clk)
signal q : std_logic;
begin
if rising_edge(clk) then
if en = '1' then
q <= d;
end if;
end if;
end process;
The first of these examples is a transparent latch, the latter is a regular flip flop. The former will be vulnerable to brief errors in the input, and commonly fail to map to real hardware. The latter is the way to design proper FPGA logic. Quartus will probably let you get away with it with a warning, but it's certainly not what you want!
Also, check your warnings to see if Quartus inferred transparent latches instead of the RAM block you wanted. It can still build, even though this behaviour is almost never intentional.
Upvotes: 1