有的时候,我们会遇到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核的时候也有说