Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
9 years ago

Question about True Dual Port RAM

Hello,

I'am using True dual port ram one port for write and the other one for read.

When i change the content of an address i get the output on the read port delayed by two cycles.

Is there a possibility to get the new value just one cycle after instead of two?

6 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Have you got registered output? You can remove this register to reduce latency at the cost of max clock speed

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    can you please explain to me what does it mean to register an output or an input ? i searched the net but i can't find an example. I though that all the outputs when we do the assignment are called registered but i don't think this what it means isn't it ?

    And before i could understand what did you mean i changed in the code below to get what i want :
    
    library ieee;
    use ieee.std_logic_1164.all;
    entity merger is
    	generic 
    	(
    		DATA_WIDTH : natural;
    		ADDR_WIDTH : natural 
    	);
    	port 
    	(
    		clk		: in std_logic;
    		addr_a	: in natural range 0 to 2**ADDR_WIDTH - 1;
    		addr_b	: in natural range 0 to 2**ADDR_WIDTH - 1;
    		data_a	: in std_logic_vector((DATA_WIDTH-1) downto 0);
    		data_b	: in std_logic_vector((DATA_WIDTH-1) downto 0);
    		we_a	: in std_logic :='1';
    		we_b	: in std_logic:='1';
    		q_a	: out std_logic_vector((DATA_WIDTH -1) downto 0);
    		q_b		: out std_logic_vector((DATA_WIDTH -1) downto 0)
    	);
    end merger;
    architecture rtl of merger is
    	-- Build a 2-D array type for the RAM
    	subtype word_t is std_logic_vector((DATA_WIDTH-1) downto 0);
    	type memory_t is array(2**ADDR_WIDTH-1 downto 0) of word_t;
    	-- Declare the RAM 
    	shared variable ram : memory_t;
    begin
    	-- Port A
    	process(clk)
    	begin
    	if(rising_edge(clk)) then 
    		if(we_a = '1') then
    			ram(addr_a) := data_a;
    			q_a <= data_a;
    		else
    			q_a<=data_b;
    		end if;
    			
    	end if;
    	
    	end process;
    	-- Port B 
    	process(clk)
    	begin
    	if(rising_edge(clk)) then 
    		if(we_b = '1') then
    			ram(addr_b) := data_b;
    		end if;
      	   q_b <= ram(addr_b);
    	end if;
    	end process;
     
    end rtl;
    

    Is it okay i wrote it like this ? i disable the we_a whenever i want to update the content of the address and read directly the new data ( data_b) (using flags from other modules )
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    To register input or output, you add registers to the pipeline in those input. That means putting the signals in a clocked process.

    Your code (lifted I see from the quartus handbook) registers the input and output. As long as you register the read address then the Q output doesnt have to be registered.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I tried too but i don't know why there's a cycle where i get a zero as output

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    If you post your code and your testbench, and give more details about the problem, maybe we can help. But we can only guess at the moment.

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I'm sorry for my delayed reply.

    Here's my code
    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    use ieee.std_logic_signed.all;
    use ieee.math_real.all;
    library std;
     
    entity association  is
        generic 
        (     DEPTH : natural:=2;
            LABEL_WIDTH : natural := 8;
            ADDR_WIDTH : natural := 6
        );
        port 
        (
            clk ,reset    , in_fv , in_dv    : in std_logic;
            in_data : in std_logic_vector((LABEL_WIDTH -1) downto 0);
            addr    : in natural range 0 to 2**ADDR_WIDTH - 1;
            q        : out std_logic_vector((LABEL_WIDTH -1) downto 0)
            
        );
    end association ;
    architecture rtl of association  is
     component row_buffer is 
        generic (
        PIPLINE_LENGHT    :    integer;
        WORD_SIZE    :    integer
        );
        
        port (
        clk_proc         :    in    std_logic;
        reset_n         :    in    std_logic;
        enable_i        :    in    std_logic;
        in_data            :    in    std_logic_vector (WORD_SIZE-1 downto 0);
        out_data        :    out    std_logic_vector (WORD_SIZE-1 downto 0)
        );
     end component;
     component merger is 
     
     generic 
        (
            DATA_WIDTH : natural := 8;
            ADDR_WIDTH : natural := 6
        );
        port 
        (
            clk        : in std_logic;
            addr_a    : in natural range 0 to 2**ADDR_WIDTH - 1;
            data_a    : in std_logic_vector((DATA_WIDTH-1) downto 0);
            we_a    : in std_logic := '1';
            q_a        : out std_logic_vector((DATA_WIDTH -1) downto 0)
            
        );
        
     end component;
     signal out1 , out2 : std_logic_vector((LABEL_WIDTH -1) downto 0);
     
    begin
     
     row_buffer_inst: row_buffer
     generic map (DEPTH,LABEL_WIDTH)
     port map (clk,reset,in_fv and in_dv, in_data,out1);
     merger_inst: merger 
     generic map (LABEL_WIDTH,ADDR_WIDTH)
     port map (clk,addr,out1,in_fv and in_dv,out2);
     
     q<=out2;
        
    end rtl;
    

    the row buffer is a simple shift register :
    library ieee;
    use ieee.std_logic_1164.all;
    entity row_buffer  is
        generic (
        PIPLINE_LENGHT    :    integer;
        WORD_SIZE    :    integer
        );
        
        port (
        clk_proc         :    in    std_logic;
        reset_n         :    in    std_logic;
        enable_i        :    in    std_logic;
        in_data            :    in    std_logic_vector (WORD_SIZE-1 downto 0);
        out_data        :    out    std_logic_vector (WORD_SIZE-1 downto 0)
        );
    end row_buffer;
        
        
    architecture arch of row_buffer is
        
        type cell_t is array (0 to (PIPLINE_LENGHT-1)) of std_logic_vector ( (WORD_SIZE-1) downto 0);
        signal cell    :    cell_t;
        
        begin
        process(clk_proc,reset_n)
        variable i        :    integer := 0;
            begin
            
            if ( reset_n = '1' )    then 
                cell <= (others =>(others => '0'));
                
            elsif    (rising_edge(clk_proc))    then
                if    (enable_i='1') then
                    cell(0)     <=     in_data;
                    for i in 1 to  (PIPLINE_LENGHT-1) loop
                        cell(i)    <=    cell(i-1);
                    end loop;
                    
                    out_data<=    cell(PIPLINE_LENGHT - 1);
                    
                end if;
            end if;    
        end process;
    end arch;
    

    and here's the merger (true dual port ram ) :
    
    library ieee;
    use ieee.std_logic_1164.all;
    entity merger is
        generic 
        (
            DATA_WIDTH : natural := 8;
            ADDR_WIDTH : natural := 6
        );
        port 
        (
            clk        : in std_logic;
            addr_a    : in natural range 0 to 2**ADDR_WIDTH - 1;
            addr_b    : in natural range 0 to 2**ADDR_WIDTH - 1;
            data_a    : in std_logic_vector((DATA_WIDTH-1) downto 0);
            data_b    : in std_logic_vector((DATA_WIDTH-1) downto 0);
            we_a    : in std_logic := '1';
            we_b    : in std_logic := '1';
            q_a        : out std_logic_vector((DATA_WIDTH -1) downto 0);
            q_b        : out std_logic_vector((DATA_WIDTH -1) downto 0)
        );
    end merger;
    architecture rtl of merger is
        -- Build a 2-D array type for the RAM
        subtype word_t is std_logic_vector((DATA_WIDTH-1) downto 0);
        type memory_t is array(2**ADDR_WIDTH-1 downto 0) of word_t;
        -- Declare the RAM 
        shared variable ram : memory_t;
    begin
        -- Port A
        process(clk)
        begin
        if(rising_edge(clk)) then 
            
            if(we_a = '1') then
                ram(addr_a) := data_a;
            end if;
            
        end if;
        end process;
        -- Port B 
        process(clk)
        begin
        if(rising_edge(clk)) then 
            if(we_b = '1') then
                ram(addr_b) := data_b;
            end if;
             
        end if;
        end process;
    q_a <= ram(addr_a);
     q_b <= ram(addr_b);
    end rtl;
    

    This is my testbench :http://www.alteraforum.com/forum/attachment.php?attachmentid=13045&stc=1