user34920
user34920

Reputation: 247

RAM simulation code does not simulate well in VHDL

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

Answers (2)

Ciano
Ciano

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

Jeff Taylor
Jeff Taylor

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

Related Questions