Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

RAM description in vhdl

Status
Not open for further replies.

Binome

Full Member level 3
Joined
Nov 16, 2009
Messages
152
Helped
2
Reputation
4
Reaction score
2
Trophy points
1,298
Location
Lyon, France
Activity points
2,405
Hi,
I just want to write a simple RAM description in VHDL. Here's my code:
Code:
library ieee; 
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity ram_mem is 
  generic (
    data_size : integer:=12;
    address_size : integer:=2 
    );
  port(
    clock : in std_logic;
    enable_write : in std_logic; 
    address : in std_logic_vector (address_size-1 downto 0);
    data_in : in std_logic_vector(data_size-1 downto 0);
    data_out : out std_logic_vector(data_size-1 downto 0)
    );
end ram_mem;

architecture RTL of ram_mem is 

  type data_ram is array (0 to 2**address_size-1) of std_logic_vector(data_size-1 downto 0);
  signal signal_ram : data_ram := (others => (others => '0'));

begin

  process(clock) 
  begin 
    if rising_edge(clock) then 
      if (enable_write = '1') then 
        data_out <= signal_ram(to_integer(unsigned(address)));
        signal_ram(to_integer(unsigned(address))) <= data_in;
      end if; 
    end if;
  end process;

end RTL;
Data_out is turning to 0 for one clock cycle between correct values. Where is my code false?
Thank you.
 

I can only assume thats a function of how you write to the ram or read from the ram. The ram code itself looks correct..
 

Your description seems flawed. You have to set enable_write (writing) to read (data_out) from the RAM, that doesn't make much sense to me.

- - - Updated - - -

You might want to look at this blog for inspiration and insight into inferring RAMs that can be used in both Altera and Xilinx.
 

You don't give us quite enough information, e.g., a waveform or what you're actually expecting. But as pointed out by ads-ee, you are enabling read with the write enable, is that really what you want? We really need to see what your write_enable looks like, but I suspect that's your problem.
 

Show us your testbench, if you are driving data_in and expect data_out to be updated in the same clock cycle then that's the problem. The flip flops will get updated on the first rising edge when you see data_out as 0.
 

Without actually trying to run this code in a simulator, I suspect these three lines is the problem.

Code:
signal signal_ram : data_ram := (others => (others => '0'));
data_out <= signal_ram(to_integer(unsigned(address)));
signal_ram(to_integer(unsigned(address))) <= data_in;

First you initialize the ram to 0's
Second you read the ram at address
Third you write to the ram at address.

So everytime you read the empty address data and then write data, which then shows up in the next clock cycle due to the read side being in a clocked process.

The base ram blocks in Altera/Xilinx both have registered inputs (addr,wrena,rdena,din,etc) and combinational outputs. You can add registers to the output, but the basic ram blocks usually have them disabled by default.
 

Just a guess, but the link that ads-ee gave apparently clarifyed the issue.
There, the equivalent signal_ram(to_integer(unsigned(address))) <= data_in is placed after the end if.
Seems like it was the condition to keep data stored at output.

- - - Updated - - -

Just say your last reply, I'm not sure of that anymore...
 

The description does not represent a ram I suspect the result through synthesis might end up as FFs.

The enable_write is not a ram write signal but is describe as a clock enable.
 

How do you see said "Data_out is turning to 0 for one clock cycle"? RTL simulation?

- - - Updated - - -

Quartus does understand the VHDL code as RAM description (with suitable memory size).
 

This should create a ram with the write port read mode set to read first. At least for xilinx. The enable and write ports are connected to the same signal.

The issue with zero data probably comes from an accidental write of zero to any address.
 

OK, I've corrected the code for the enable signal and I give the code of everything:
component:
Code:
library ieee; 
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity ram_mem is 
  generic (
    data_size : integer:=12;
    address_size : integer:=2 
    );
  port(
    clock : in std_logic;
    enable_write : in std_logic; 
    address : in std_logic_vector (address_size-1 downto 0);
    data_in : in std_logic_vector(data_size-1 downto 0);
    data_out : out std_logic_vector(data_size-1 downto 0)
    );
end ram_mem;

architecture RTL of ram_mem is 

  type data_ram is array (0 to 2**address_size-1) of std_logic_vector(data_size-1 downto 0);
  signal signal_ram : data_ram := (others => (others => '0'));

begin

  process(clock) 
  begin
    if rising_edge(clock) then
      data_out <= signal_ram(to_integer(unsigned(address)));
      if (enable_write = '1') then 
        signal_ram(to_integer(unsigned(address))) <= data_in;
      end if; 
    end if;
  end process;

end RTL;

testbench:
Code:
library ieee; 
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity tb_mem is
end tb_mem;

architecture test of tb_mem is
	component ram_mem is
  generic(
    data_size : integer := 12;  
    address_size : integer := 2 
    );
  port(
    clock : in std_logic;
    enable_write : in std_logic; 
    address : in std_logic_vector (address_size-1 downto 0);
    data_in : in std_logic_vector(data_size-1 downto 0);
    data_out : out std_logic_vector(data_size-1 downto 0)
    );
	end component ram_mem;
	
  signal s_clock: std_logic := '0';
  signal s_enable_write : std_logic := '0';
  signal s_data_in : std_logic_vector(11 downto 0) := (others => '0');
  signal s_data_out : std_logic_vector(11 downto 0) := (others => '0');
  signal s_address : std_logic_vector (1 downto 0);
  
begin
  
	comp : ram_mem
  generic map(
    data_size => 12,      -- pixel size
    address_size  => 2    -- row size
  )
	port map(	
    clock => s_clock,
    enable_write => s_enable_write,
    address => s_address,
    data_in => s_data_in,
    data_out => s_data_out
  );
  
  clock : process
  begin
    wait for 1 ns;
    s_clock <= not s_clock;
  end process;
  
  start : process
  begin
    wait for 8 ns;
    s_enable_write <= not s_enable_write;
    wait;
  end process;
  
  test_vectors : process
  begin
    wait for 14 ns;
    for i in 0 to 15 loop
      s_address <= std_logic_vector(to_unsigned(15-i, 2));
      s_data_in <= std_logic_vector(to_unsigned(15-i, 12));
      wait for 8 ns;
    end loop;
    wait;
  end process;
  
end test;
 
I do wonder if your problem is a function of you using absolute time in your test_vectors process rather than waiting clock edges.
 

Yeah the transfers are not aligned to the clock edges and the 0 output is caused by the read before write behavior along with that extra delay of the output register pipeline. See the waveforms.
Capture.JPG

- - - Updated - - -

Sorry, the signals are aligned to the falling clock edges, but they persist for multiple clock cycles, which pipelines the 0's out.
 
I've written a new testbench!
Code:
library ieee; 
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity tb_mem is
end tb_mem;

architecture test of tb_mem is
	component ram_mem is
  generic(
    data_size : integer := 12;  
    address_size : integer := 2 
    );
  port(
    clock : in std_logic;
    enable_write : in std_logic; 
    address : in std_logic_vector (address_size-1 downto 0);
    data_in : in std_logic_vector(data_size-1 downto 0);
    data_out : out std_logic_vector(data_size-1 downto 0)
    );
	end component ram_mem;
	
  signal s_clock: std_logic := '0';
  signal s_enable_write : std_logic := '0';
  signal s_data_in : std_logic_vector(11 downto 0) := (others => '0');
  signal s_data_out : std_logic_vector(11 downto 0) := (others => '0');
  signal s_address : std_logic_vector (1 downto 0);
  
begin
  
	comp : ram_mem
  generic map(
    data_size => 12,      -- pixel size
    address_size  => 2    -- row size
  )
	port map(	
    clock => s_clock,
    enable_write => s_enable_write,
    address => s_address,
    data_in => s_data_in,
    data_out => s_data_out
  );
  
  clock : process
  begin
    wait for 1 ns;
    s_clock <= not s_clock;
  end process;
  
  start : process
  begin
    wait for 8 ns;
    s_enable_write <= not s_enable_write;
    wait;
  end process;
  
  test_vectors : process
  begin
    wait for 14 ns;
    wait until s_clock'event and s_clock = '1';
    for i in 0 to 15 loop
      s_address <= std_logic_vector(to_unsigned(15-i, 2));
      s_data_in <= std_logic_vector(to_unsigned(15-i, 12));
      wait for 8 ns;
      wait until s_clock'event and s_clock = '1';
    end loop;
    wait;
  end process;
  
end test;
But the result is the same.
 

I haven't run the new tsstbench but I know it has the same behavior, because that is the way you wrote your RAM code's behavior. If you want different behavior then change the RAM code.
 


Code Verilog - [expand]
1
2
3
4
5
6
7
8
9
process(clock) 
  begin
    if rising_edge(clock) then
      data_out <= signal_ram(to_integer(unsigned(address)));
      if (enable_write = '1') then 
        signal_ram(to_integer(unsigned(address))) <= data_in;
      end if; 
    end if;
  end process;



Clearly, you don't understand what changes in the hardware with this change in code. Nothing at all ! You got to visualize the component that's created with the statements you write in the code.
Whatever is you testbench, you cannot expect the data_out to be visible in the same clock cycle you drive data_in.
 
Last edited by a moderator:

I don't understand anything.
What should I change exactly?
 

Hope this helps:


Code Verilog - [expand]
1
2
3
4
5
6
7
data_out <= signal_ram(to_integer(unsigned(address)));
 
if rising_edge(clock) then
if (enable_write = '1') then
signal_ram(to_integer(unsigned(address))) <= data_in;
end if;
end if;

 

process(clock)
begin
if rising_edge(clock) then
data_out <= signal_ram(to_integer(unsigned(address)));
if (enable_write = '1') then
signal_ram(to_integer(unsigned(address))) <= data_in;
end if;
end if;
end process;

Clearly, you don't understand what changes in the hardware with this change in code. Nothing at all ! You got to visualize the component that's created with the statements you write in the code.
Whatever is you testbench, you cannot expect the data_out to be visible in the same clock cycle you drive data_in.

The code generates a ram with a registered data_out.
Your new code just removes that register.
Why are you recommending such a change? you're advocate an asynchronous read ram (which will not infer a ram).
 

In any case this is not RAM. Just for the improper data he's reading, i proposed that code. If you need synchronous read he should have another clocked read and enable_read - where he should assign signal to data_out .
BTW, the code doesn't generate RAM, just few FFs.
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top