fpga 级联fifo(VHDL)

本文介绍了在FIFO深度不足时采用级联FIFO的方法,以及在普通模式下可能出现的数据丢失问题。通过引入'almost_full'信号提前禁用读使能来避免错误。此外,讨论了快速读出模式的优势,即在读使能到来前预读数据,确保数据连续性。最后,提出在快速读出模式下,最好在'valid'和'rd_en'同时有效时读取数据,以确保正确性。
摘要由CSDN通过智能技术生成

有的时候,我们会遇到fifo深度不够用的时候,那么就会采用级联fifo,一般来说思路如下:(这个前提是用的是fifo ip核的普通模式)

 

 测试一:fifo为8bit*16



library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;


entity fifo_fifo is
 Port ( 
    clk : IN STD_LOGIC;
    rst_n : IN STD_LOGIC
 );
end fifo_fifo;

architecture Behavioral of fifo_fifo is

COMPONENT fifo_generator_0
  PORT (
    clk : IN STD_LOGIC;
    din : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
    wr_en : IN STD_LOGIC;
    rd_en : IN STD_LOGIC;
    dout : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
    full : OUT STD_LOGIC;
    empty : OUT STD_LOGIC;
    valid : OUT STD_LOGIC
  );
END COMPONENT;

signal    fifo0_data_in :  STD_LOGIC_VECTOR(7 DOWNTO 0);
signal    fifo0_wr_en :  STD_LOGIC;
signal    fifo0_rd_en :  STD_LOGIC;
signal    fifo0_data_out :  STD_LOGIC_VECTOR(7 DOWNTO 0);
signal    fifo0_full :  STD_LOGIC;
signal    fifo0_empty :  STD_LOGIC;
signal    fifo0_valid :  STD_LOGIC;

signal    fifo1_rd_en :  STD_LOGIC;
signal    fifo1_data_out :  STD_LOGIC_VECTOR(7 DOWNTO 0);
signal    fifo1_full :  STD_LOGIC;
signal    fifo1_empty :  STD_LOGIC;
signal    fifo1_valid :  STD_LOGIC;

signal    cnt0 :  STD_LOGIC_VECTOR(7 DOWNTO 0);
signal    cnt_2 :  STD_LOGIC_VECTOR(1 DOWNTO 0);

type type_state1 is (s0,s1);     --能量数据fifo使能
signal state: type_state1;

begin

fifo0_rd_en <= ((not fifo0_empty ) and  (not fifo1_full ));

process(rst_n,clk,state,fifo0_full,fifo0_data_in,fifo1_empty,cnt0,cnt_2)
begin
if (rst_n = '0') then
    fifo0_wr_en <= '0';
    fifo0_data_in  <=  (others => '0');
    cnt0  <=  (others => '0');
    fifo1_rd_en <= '0';
    state <= s0;
    cnt_2  <=  (others => '0');
elsif (rising_edge(clk)) then
    case state is
    when s0 =>
        fifo1_rd_en <= '0';
        if(fifo0_full = '1') then
            fifo0_wr_en <= '0';
            fifo0_data_in  <=  (others => '0');
            cnt0  <=  (others => '0');
            state <= s1;
        else
            fifo0_wr_en <= '1';
            fifo0_data_in <= fifo0_data_in + '1';   
            cnt0 <= cnt0 + '1';   
            state <= s0;
        end if;
    when s1 =>
        if(cnt_2 = "11") then
            fifo1_rd_en <= not fifo1_empty;
            cnt_2  <=  (others => '0');
        else
            fifo1_rd_en <= '0';
            cnt_2 <= cnt_2 + '1';
        end if;
    --    fifo1_rd_en <= not fifo1_empty;
        state <= s1;
    when others =>
        state  <=  s0;
    end case;
end if;
end process;

fifo0 : fifo_generator_0   --存放数据fifo0  32bit*131072
  PORT MAP (
    clk => clk,
    din => fifo0_data_in,
    wr_en => fifo0_wr_en,
    rd_en => fifo0_rd_en,
    dout => fifo0_data_out,
    full => fifo0_full,
    empty => fifo0_empty,
    valid => fifo0_valid
  );

fifo1 : fifo_generator_0  --存放数据fifo1 
  PORT MAP (
    clk => clk,
    din => fifo0_data_out,
    wr_en => fifo0_valid,
    rd_en => fifo1_rd_en,
    dout => fifo1_data_out,
    full => fifo1_full,
    empty => fifo1_empty,
    valid => fifo1_valid
  );
  
end Behavioral;

结果就会在后面那个fifo满了之后产生错误,因为上一个fifo读出数据之后,要过一两个周期数据写进去,full菜户反应过来满了,但这段时间,上一个fifo任然在读,就导致了数据读出来了,但是后面那个fifo已经满脸,就算wr为1,也没用了,就会导致数据的丢失。

所以在fifo ip核当中,新加入almost_full信号,可以在真正满之前,就不再使能rd了,其实最后是写满了的,大家可以仔细对比一下仿真图:

代码只是把上面的fifo0_rd_en <= ((not fifo0_empty ) and  (not fifo1_full ));换成fifo0_rd_en <= ((not fifo0_empty ) and  (not fifo1_almost_full ));


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;


entity fifo_fifo is
 Port ( 
    clk : IN STD_LOGIC;
    rst_n : IN STD_LOGIC
 );
end fifo_fifo;

architecture Behavioral of fifo_fifo is

COMPONENT fifo_generator_0
  PORT (
    clk : IN STD_LOGIC;
    din : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
    wr_en : IN STD_LOGIC;
    rd_en : IN STD_LOGIC;
    dout : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
    full : OUT STD_LOGIC;
    almost_full : OUT STD_LOGIC;
    empty : OUT STD_LOGIC;
    valid : OUT STD_LOGIC
  );
END COMPONENT;

signal    fifo0_data_in :  STD_LOGIC_VECTOR(7 DOWNTO 0);
signal    fifo0_wr_en :  STD_LOGIC;
signal    fifo0_rd_en :  STD_LOGIC;
signal    fifo0_data_out :  STD_LOGIC_VECTOR(7 DOWNTO 0);
signal    fifo0_full :  STD_LOGIC;
signal    fifo0_almost_full:  STD_LOGIC;
signal    fifo0_empty :  STD_LOGIC;
signal    fifo0_valid :  STD_LOGIC;

signal    fifo1_rd_en :  STD_LOGIC;
signal    fifo1_data_out :  STD_LOGIC_VECTOR(7 DOWNTO 0);
signal    fifo1_full :  STD_LOGIC;
signal    fifo1_empty :  STD_LOGIC;
signal    fifo1_valid :  STD_LOGIC;
signal    fifo1_almost_full:  STD_LOGIC;

signal    cnt0 :  STD_LOGIC_VECTOR(7 DOWNTO 0);
signal    cnt_2 :  STD_LOGIC_VECTOR(1 DOWNTO 0);

type type_state1 is (s0,s1);     --能量数据fifo使能
signal state: type_state1;

begin

fifo0_rd_en <= ((not fifo0_empty ) and  (not fifo1_almost_full ));

process(rst_n,clk,state,fifo0_full,fifo0_data_in,fifo1_empty,cnt0,cnt_2)
begin
if (rst_n = '0') then
    fifo0_wr_en <= '0';
    fifo0_data_in  <=  (others => '0');
    cnt0  <=  (others => '0');
    fifo1_rd_en <= '0';
    state <= s0;
    cnt_2  <=  (others => '0');
elsif (rising_edge(clk)) then
    case state is
    when s0 =>
        fifo1_rd_en <= '0';
        if(fifo0_full = '1') then
            fifo0_wr_en <= '0';
            fifo0_data_in  <=  (others => '0');
            cnt0  <=  (others => '0');
            state <= s1;
        else
            fifo0_wr_en <= '1';
            fifo0_data_in <= fifo0_data_in + '1';   
            cnt0 <= cnt0 + '1';   
            state <= s0;
        end if;
    when s1 =>
        if(cnt_2 = "11") then
            fifo1_rd_en <= not fifo1_empty;
            cnt_2  <=  (others => '0');
        else
            fifo1_rd_en <= '0';
            cnt_2 <= cnt_2 + '1';
        end if;
    --    fifo1_rd_en <= not fifo1_empty;
        state <= s1;
    when others =>
        state  <=  s0;
    end case;
end if;
end process;

fifo0 : fifo_generator_0
  PORT MAP (
    clk => clk,
    din => fifo0_data_in,
    wr_en => fifo0_wr_en,
    rd_en => fifo0_rd_en,
    dout => fifo0_data_out,
    full => fifo0_full,
    almost_full => fifo0_almost_full,
    empty => fifo0_empty,
    valid => fifo0_valid
  );

fifo1 : fifo_generator_0  --存放数据fifo1 
  PORT MAP (
    clk => clk,
    din => fifo0_data_out,
    wr_en => fifo0_valid,
    rd_en => fifo1_rd_en,
    dout => fifo1_data_out,
    full => fifo1_full,
    almost_full => fifo1_almost_full,
    empty => fifo1_empty,
    valid => fifo1_valid
  );
  
end Behavioral;

 

---------------------------------------------------------------------------------------------------------------------------------

20221125新增:其实对于这种级联可以使用快速读出的fifo就可以避免这种问题,快速读出的本质就是,先将一个数据放在数据线上,当rd_en来的时候,就可以马上读出

 

因为fifo会提前将一个数放在读数据线上 所以在rden第一次来之前一直为高,所以可以在rden和valid都为高时再讲数据读出 

所以写了一个测试文件 跟上面一样,fifo0和fifo1级联,在fifo0有数据fifo1还没写满的时候,使能fifo0的读信号,在fifo0没写满之前,一直写(因为这个写使能用的时序逻辑,在满信号为高时,但它前一个时刻为低,那么在下一个时刻,任然会使能一次写使能,但其实是写不进去的),然后在下一个状态,使能fifo1的读使能,在读空之前一直拔高。至于fifo1的写使能,之前是将它连在fifo0的valid信号上的,其实在这种情况下,最好是在fifo0的valid和rd_en信号同时有效时,再拉高,因为快速读出模式,会预先读出一个数在数据线上,那么这段时间的valid就是一直有效的。但是这里没改,任然可以就是因为虽然写使能连接的valid一直有效,但是这个时候的full信号已经满了,也写不进去了。

----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date: 2022/11/25 15:35:55
-- Design Name: 
-- Module Name: fifo_fifo1 - Behavioral
-- Project Name: 
-- Target Devices: 
-- Tool Versions: 
-- Description: 
-- 
-- Dependencies: 
-- 
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
-- 
----------------------------------------------------------------------------------


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity fifo_fifo1 is
 Port ( 
    clk : IN STD_LOGIC;
    rst_n : IN STD_LOGIC
 );
end fifo_fifo1;

architecture Behavioral of fifo_fifo1 is

COMPONENT fifo_generator_0
  PORT (
    clk : IN STD_LOGIC;
    din : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
    wr_en : IN STD_LOGIC;
    rd_en : IN STD_LOGIC;
    dout : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
    full : OUT STD_LOGIC;
        valid : OUT STD_LOGIC;
    empty : OUT STD_LOGIC
  );
END COMPONENT;

signal    fifo0_data_in :  STD_LOGIC_VECTOR(7 DOWNTO 0);
signal    fifo0_wr_en :  STD_LOGIC;
signal    fifo0_rd_en :  STD_LOGIC;
signal    fifo0_data_out :  STD_LOGIC_VECTOR(7 DOWNTO 0);
signal    fifo0_full :  STD_LOGIC;
signal    fifo0_empty :  STD_LOGIC;
signal    fifo0_valid :  STD_LOGIC;

signal    fifo1_rd_en :  STD_LOGIC;
signal    fifo1_data_out :  STD_LOGIC_VECTOR(7 DOWNTO 0);
signal    fifo1_full :  STD_LOGIC;
signal    fifo1_empty :  STD_LOGIC;
signal    fifo1_valid :  STD_LOGIC;

signal    cnt0 :  STD_LOGIC_VECTOR(7 DOWNTO 0);
signal    cnt_2 :  STD_LOGIC_VECTOR(1 DOWNTO 0);

type type_state1 is (s0,s1);     --能量数据fifo使能
signal state: type_state1;

begin

fifo0_rd_en <= ((not fifo0_empty ) and  (not fifo1_full ));

process(rst_n,clk)
begin
if (rst_n = '0') then
    fifo0_wr_en <= '0';
    fifo0_data_in  <=  (others => '0');
    cnt0  <=  (others => '0');
    fifo1_rd_en <= '0';
    state <= s0;
    cnt_2  <=  (others => '0');
elsif (rising_edge(clk)) then
    case state is
    when s0 =>
        fifo1_rd_en <= '0';
        if(fifo0_full = '1') then
            fifo0_wr_en <= '0';
            fifo0_data_in  <=  (others => '0');
            cnt0  <=  (others => '0');
            state <= s1;
        else
            fifo0_wr_en <= '1';
            fifo0_data_in <= fifo0_data_in + '1';   
            cnt0 <= cnt0 + '1';   
            state <= s0;
        end if;
    when s1 =>
        fifo1_rd_en <= not fifo1_empty;
        state <= s1;
    when others =>
        state  <=  s0;
    end case;
end if;
end process;



fifo0 : fifo_generator_0
  PORT MAP (
    clk => clk,
    din => fifo0_data_in,
    wr_en => fifo0_wr_en,
    rd_en => fifo0_rd_en,
    dout => fifo0_data_out,
    full => fifo0_full,
    empty => fifo0_empty,
    valid => fifo0_valid
  );
  
fifo1 : fifo_generator_0  --存放数据fifo1 
  PORT MAP (
    clk => clk,
    din => fifo0_data_out,
    wr_en => fifo0_valid,    --最好是在fifo0的valid和rd_en信号同时有效时,再拉高
    rd_en => fifo1_rd_en,
    dout => fifo1_data_out,
    full => fifo1_full,
    empty => fifo1_empty,
    valid => fifo1_valid
  );
  
end Behavioral;

 

 

 

 总结:这两种模式都是可以实现级联的,普通模式下要注意的就是接口的位置,由于读使能和读出的数据是相差一个时钟的,导致判断是否给读使能的时候,实际数据还没写进去,那么full信号就还没拉高,会导致fifo0读出了数据,但是fifo1却因为满了,收不到。那么快速读出模式就是要注意valid信号,会在没读数据的时候一直有效,因为有个数据一直预先读到了数据线上,那么判断的时候,最好是在rd_en和valid信号同时有效时,才判断读出的数据是正确的。

还有一个差别就是两个fifo的真实深度,对于普通模式的fifo,(前提是fifo深度都是16),两个fifo级联之后,能写入32个数据、但是快速读出模式的,深度是36。这一点在建立ip核的时候也有说

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值