异步fifo读写同步的设计分析.doc
课程论文(设计)题 目 异步缓冲fifo的设计院 系 电子与信息工程学院专 业 电子与通信工程学生姓名 卢兴森 学 号 20132281370指导教师 刘建成二 一 四 年 元 月 三 日一 异步FIFO的介绍 11.1 指针以及满空信号的产生 11.2 二进制指针和格雷码指针的同步 21.3 保守的满空判断 31.4 二进制指针和格雷码指针的比较 4二 程序及其介绍 4三 仿真结果图 13四 总结 14一 异步FIFO的介绍使用FIFO同步源自不同时钟域的数据是在数字IC设计中经常使用的方法,设计功能正确的FIFO会遇到很多问题,探讨了两种不同的异步FIFO的设计思路。两种思路都能够实现功能正确的FIFO。本文所研究的FIFO,从硬件的观点来看,就是一块数据内存。它有两个端口,一个用来写数据,就是将数据存入FIFO;另一个用来读数据,也就是将数据从FIFO当中取出。与FIFO操作相关的有两个指针,写指针指向要写的内存部分,读指针指向要读的内存部分。FIFO控制器通过外部的读写信号控制这两个指针移动,并由此产生FIFO空信号或满信号。对于异步FIFO而言,数据是由某一个时钟域的控制信号写入FIFO,而由另一个时钟域的控制信号将数据读出FIFO。也就是说,读写指针的变化动作是由不同的时钟产生的。因此,对FIFO空或满的判断是跨时钟域的。如何根据异步的指针信号对FIFO的满状态或空状态进行正确的判断是本文研究的重点。此外,设计过程中的一些细节问题也将在文中涉及到。1.1 指针以及满空信号的产生为了更好地说明问题,先探讨一下同步FIFO指针移动以及满空信号的产生过程。对于同步FIFO,读写指针都指向一个内存的初始位置,每进行一次读写操作,相应的指针就递增一次,指向下一个内存位置。当指针移动到了内存的最后一个位置时,它又重新跳回初始位置。在FIFO非满或非空的情况下,这个过程将随着读写控制信号的变化一直进行下去。如果FIFO处于空的状态,下一个读动作将会导致向下溢出(underflow),一个无效的数据被读人;同样,对于一个满了的FIFO,进行写动作将会导致向上溢出(overflow),一个有用的数据被新写入的数据覆盖。这两种情况都属于误动作,因此需要设置满和空两个信号,对满信号置位表示FIFO处于满状态,对满信号复位表示FIFO非满,还有空间可以写入数据;对空信号置位表示FIFO处于空状态,对空信号复位表示FIFO非空,还有有效的数据可以读出。当读指针和写指针相等也就是指向同一个内存位置的时候,FIFO可能处于满或空两种状态。可以通过不同的方法判断或区分FIFO究竟是处于满状态还是空状态,也就是究竟是写指针从后赶上了读指针,还是读指针从后赶上了写指针。本文所应用的方法是设置一个额外的状态位,指针由它的地址位以及状态位组成。地址位随着相应的操作递增,指针由内存的最后位置返回到初始位置的时候状态位取反。因此,当读写指针的地址位和状态位全部吻合的时候,读写指针经历了相同次数的循环移动,也就是说,FIFO处于空状态;如果读写指针的地址位相同而状态位相反,写指针比读指针多循环一次,标志FIFO处于满状态。 (2)二进制指针可以用于任意大小的FIFO;格雷码指针只能用于大小为2的幂的FIFO。1.2 二进制指针和格雷码指针的同步二进制指针(如图1)是由一位状态位和若干位二进制编码的地址位组成的(例如由三位地址和一位状态位构成的指针的变化:0000000100100011010001010110011110001001101010111100110111101111)。如果在不同的时钟域内直接同步二进制指针,有可能产生问题。例如,当读指针从0111向1000变化的时侯,指针所有的位都要变化,如果写时钟恰好在读指针的变化时刻采样,写者得到的读指针值有可能是从0000到1111中的任何一个。所以二进制指针不宜被直接同步,但可以通过一对握手信号同步二进制指针。图1例如,读指针被读者存人一个寄存器时,读者就发出一个就绪信号。当写者看到就绪信号时,读取读指针,发出一个收到数据的确认信号。当读者看到确认信号时,就收回就绪信号,然后用当前的读指针值刷新寄存器。在收到确认信号前,存放读指针的寄存器内容保持不变,确保了被读取的指针的正确性。为了能够在不同的时钟域内直接同步指针,可以对指针使用格雷码的编码方式,也就是指针每次移动只变化一位,这样就避免了由于指针多位同时变化而无法直接同步的问题。图3表示了一个三位地址的内存用于格雷码编码的FIFO。n位地址的内存需要n+1位的格雷码。使用最高位(也就是第n-1位)作为状态位,从第n2到第0位作为地址的第n-2位到第0位;对状态位(也就是第n-1位)和第n-2位进行位异或运算产生地址的第n-2位(也就是地址的最高位)。通过对图3的观察可以得知,四位格雷码编码的指针和三位二进制加状态位编码的指针具有同样的功能:指针产生的地址循环遍历8个内存地址,每一次遍历后状态位取反。值得注意的一点,是当指针从0100变化到1100的时候,相应的地址由100变为000,状态位和一位地址同时发生了变化,所以不能同步由另一个时钟域格雷码指针产生的状态位和地址位,此时应该直接由另一个时钟域同步指针来完成,由同步后的指针产生相应的状态位和地址位。1.3 保守的满空判断对于异步FIFO设计,无论是采用握手还是直接同步的方法来获取对方时钟域的指针,对满空信号的判断总是“保守”的。(1)满空信号的复位(满信号复位表示FIFO非满,空信号复位表示FIFO非空)和实际FIFO的情况相比有一定的延迟。例如,空信号是由读者用读指针与同步或握手后得到的写指针进行比较产生的。由于同步或握手需要一定的时间,在这段时间,写者可能向FIFO写入新的数据,写指针发生了变化,此时FIFO已经非空,但此刻空信号仍然没有复位。对于写者而言,满信号的复位也会遇到相同的问题。不过,在通常情况下,FIFO只要确保不会向下溢出或向上溢出,复位的延迟就不会导致向下溢出或向上溢出,是可以接受的。(2)满空信号的置位(满信号置位表示FIFO满,空信号置位表示FIFO空)和FIFO的实际情况相比没有延迟。例如,同样考虑FIFO为空的情况,读者使用读指针和同步或握手后的写指针进行比较,由于FIFO为空,写动作不会发生,相应的写指针也保持不变,因此读者获得的就是当时的写指针值。这样就能马上对空信号置位。同样地,在FIFO满的情况下,由于读指针不发生变化,写者得到的是当前的读指针值,能够马上判断FIFO为满。从以上两点的讨论可以得出结论,FIFO满空判断是保守的,写者可能在FIFO还有一定空间时停止写数据,但不会在FIFO已经满了的情况下继续写数据;读者可能在FIFO还有一些有效的数据时停止读数据,但是不会在FIFO已经空的情况下继续读数据。保守的满空判断能够满足FIFO的功能要求。1.4 二进制指针和格雷码指针的比较二进制指针和格雷码指针两者各有优缺点:(1)由于通过握手同步,指针可以有多位同时变化,二进制指针每次移动可以跳跃过任意的长度,这样给FIFO的某些功能的实现带来了方便(例如,硬件直接控制FIFO从缓存的数据流中丢弃一个出错的包);而格雷码指针一般只能做递增或递减的移动。(2) 与直接同步相比,通过握手同步需要多时钟周期,因此二进制指针的满空判断比格雷码指针的满空判断更“保守”;对于设计一个容量很大且由内存构成的FIFO来说,由于保守判断而损失的空间可以忽略;但对于一个容量较小的FIFO而言,这种“保守”可能是无法接受的。例如,一个由8个寄存器组成的FIFO,对于格雷码编码的指针,最坏情况下,FIFO被写者判断为满时,里面实际只存有五个数据(使用两级同步器,可能需要三个周期同步,而在这三个周期内都有读动作发生);而对与二进制编码的指针,FIFO被判为满时,FIFO里可能只有三个数据或更少(使用两级同步器,最好的情况下,需要五个周期握手,而在这五个周期里都有读动作发生)。设计者在两种方法的比较选择上应该考虑到以上的几点。本文介绍的异步FIFO的设计方法的两种不同尽路都能够实现功能正确的异步FIFO。对这两种方法设计的FIFO的优缺点所做的简要分析,对FIFO的设计具有指导意义。二 程序及其介绍该fifo缓冲器输入信号为10 khz ,输出信号为20khz,可实现输入结束时输出同时结束;程序分为二部分,顶层文件fifo4080为控制部分,fifo为异步缓冲fifo程序具体如下图2;图2其fifo系统总体设计框图图3如下:图3本程序的使用xilinx ise11.4软件进行设计,仿真使用其自带的testbench;Fifo4080系统顶层框图图4如下:图4Fifo4080程序如下library IEEE;use IEEE.STD_LOGIC_1164.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 primitives in this code.-library UNISIM;-use UNISIM.VComponents.all;entity fifo4080 isgeneric (w:integer:=1024;k:integer:=8);port( rst,wrclk,rdclk,wren: in std_logic;-复位高电平有效,clk上升沿有效,使能端低电平有效din:in std_logic_vector(k-1 downto 0);dout:out std_logic_vector(k-1 downto 0);full,empty:out std_logic);end fifo4080;architecture Behavioral of fifo4080 issignal count:integer range 0 to w-1;signal rden:std_logic:='1'component fifo generic (w:integer:=1024;k:integer:=8);port( rst,wrclk,rdclk,wren: in std_logic;-复位高电平有效,clk上升沿有效,使能端低电平有效din:in std_logic_vector(k-1 downto 0);dout:out std_logic_vector(k-1 downto 0);full,empty:out std_logic;rden:out std_logic;count:out integer range 0 to w-1);end component;beginu1:fifogeneric map(w=>w,k=>k)port map (rst=>rst,wrclk=>wrclk,rdclk=>rdclk,wren=>wren,din=>din,dout=>dout,full=>full,empty=>empty,rden=>rden,count=>count);process(count)begin if(count>=512) then rden<='0' else rden<='1' end if;end process; end Behavioral;异步缓冲器fifo文件程序如下entity fifo isgeneric (w:integer:=1024;k:integer:=8);port( rst,wrclk,rdclk,wren: in std_logic;-复位高电平有效,clk上升沿有效,使能端低电平有效din:in std_logic_vector(k-1 downto 0);dout:out std_logic_vector(k-1 downto 0);full,empty:out std_logic;rden:out std_logic;count:out integer range 0 to w-1);end fifo;architecture Behavioral of fifo istype memory is array (0 to w-1) of std_logic_vector (k-1 downto 0);signal ram:memory;signal wradd,rdadd: integer range 0 to w-1;signal infull,inempty:std_logic;signal rdens:std_logic; -signal cnen:std_logic;beginprocess(wrclk) -写数据beginif (wrclk'event and wrclk='1') thenif (wren='0' and infull='0') thenram(wradd) <= din;end if;end if;end process;process(rst,wrclk)-写地址修改beginif (rst='1') then wradd<=0; elsif (wrclk'event and wrclk='1') thenif (wren='0' and infull='0') thenif(wradd=w-1) thenwradd<=0;else wradd<=wradd+1;end if;end if;end if;end process;process (rst,rdclk)-读地址修改beginif(rst='1') thenrdadd<=w-1;elsif (rdclk'event and rdclk='1') thenif (rdens='0' and inempty='0') thenif(rdadd=w-1) thenrdadd<=0;else rdadd<=rdadd+1;end if;end if; end if;end process;process (rst,rdclk)-empty 产生beginif(rst='1') then inempty<='1' elsif (rdclk'event and rdclk='1') thenif (rdadd=wradd-2 or(rdadd=w-1 and wradd=1)or(rdadd=w-2 and wradd=0)and(rdens='0') theninempty<='1'elseinempty<='0'end if;end if;end process;process (rst,wrclk)-full 产生beginif(rst='1') theninfull<='0'elsif (wrclk'event and wrclk='1') thenif (rdadd=wradd and wren='0') theninfull<='1'elseinfull<='0'end if;end if;end process;process(wrclk) -计数variable counter:integer range 0 to w-1;beginif (wrclk'event and wrclk='1') and rst='0') thenif (wren='0' and infull='0') thencounter:=counter+1;end if;end if;if(counter>=512) then rdens<='0'end if;if(counter=1024) or (rst='1') thencounter:=0;rdens<='1'end if;count<=counter;end process;dout<=ram(rdadd);full<=infull;empty<=inempty;rden<=rdens;end Behavioral;在上述程序综合成功后,可在ise中新建testbench程序,在新建程序test的末端输入下列仿真时序程序即可进行仿真。仿真testbench程序如下u1 :process beginwrclk <= '0'wait for 50 ns;wrclk <= '1'wait for 50 ns; end process; u2 :process beginrdclk <= '0'wait for 25 ns;rdclk <= '1'wait for 25 ns; end process;u3:processbeginrst<='1'wait for 20 ns;rst<='0'wait for 100000 ms;end process;u4:processbeginwren<='1'wait for 20 ns;wren<='0'wait for 100 ms;end process;u5:processbegindin<="00001111"wait for 100 ns;din<="11110000"wait for 100 ns;end process;三 仿真结果图图5为1ms间距下观察的仿真波形图,其中空白部分这段时间为读使能处于关闭状态。图5图6为放大后观察到的仿真波形图此时可观察到读取速率刚好为写入速率一倍。图6图7为再次放大后观察到的仿真波形图,此时可观察到最后一个读数据和写数据刚好相等。图7四 总结通过这次fifo的设计,感觉自己eda的实战水平有了很大的提高,了解了eda模块的设计流程,对vhdl语言有了更深的了解,最后感谢老师的细心指导。