当前位置:首页 > 工业控制 > 电子设计自动化
[导读]设计一个FIFO是FPGA设计者遇到的最普遍的问题之一。本文着重介绍怎样设计FIFO—— 这是一个看似简单却很复杂的任务。 一开始,要注意,FIFO通常用于时钟域的过渡,是双时钟设计。换句话说,设计工程要处

设计一个FIFO是FPGA设计者遇到的最普遍的问题之一。本文着重介绍怎样设计FIFO——
这是一个看似简单却很复杂的任务。
一开始,要注意,FIFO通常用于时钟域的过渡,是双时钟设计。换句话说,设计工程要处理
(work off)两个时钟,因此在大多数情况下,FIFO工作于独立的两个时钟之间。然而,
我们不从这样的结构开始介绍—我们将从工作在单时钟的一个FIFO特例开始。
虽然工作在同一时钟的FIFO在实际应用中很少用到,但它为更多的复杂设计搭建一个平台,
这是非常有用的。然后再从特例推广到更为普通的FIFO,该系列文章包括以下内容:
1.单时钟结构
2.双时钟结构——双钟结构1
3.双时钟结构——双钟结构2
4.双时钟结构——双钟结构3
5.脉冲模式FIFO
单时钟FIFO特例
FIFO有很多种结构,包括波浪型(ripple)FIFO,移位寄存器型以及其他
一些我们并不关心的结构类型。我们将集中讨论包含RAM存储器的结构类型。
其结构如图1所示。


通过分析,我们看到图中有一个具有独立的读端口和独立的写端口的RAM存储器。
这样选择是为了分析方便。如果是一个单端口的存储器,还应包含一个仲裁器保证
同一时刻只能进行一项操作(读或写),我们选择双口RAM(无需真正的双口RAM,
因为我们只是希望有一个简单的相互独立的读写端口)是因为这些实例非常接近实际情况。
读、写端口拥有又两个计数器产生的宽度为log2(array_size)的互相独立的读、写地址。
数据宽度是一个非常重要的参数将在在稍后的结构选择时予以介绍,而现在我们不必
过分的关心它。为了一致,我们称这些计数器为“读指针”(read pointer)和“写指针”
(write pointer)。写指针指向下一个将要写入的位置,读指针指向下一个将要读取的位置。
每次写操作使写指针加1,读操作使读指针加1。
我们看到最下面的模块为“状态”(stauts) 模块。
这个模块的任务实给FIFO提供“空”(empty)和“满”(full)信号。
这些信号告诉外部电路FIFO已经达到了临界条件:如果出现“满”信号,
那么FIFO为写操作的临界状态,如果出现“空”信号,则FIFO为读操作的临界状态。
写操作的临界状态(“full is active”)表示FIFO已经没有空间来存储更多的数据,
读操作的临界表示FIFO没有更多的数据可以读出。status模块还可告诉FIFO中“满”或“空”位置的数值。这是由指针的算术运算来完成了。

实际的“满”或“空”位置计算并不是为FIFO自身提供的。它是作为一个
报告机构给外部电路用的。但是,“满”和“空”信号在FIFO中却扮演着
非常重要的角色,它为了能实现读与写操作各自的独立运行而阻塞性的管
理数据的存取。这种阻塞性管理的重要性不是将数据复写(或重读),而
是指针位置可以控制整个FIFO,并且使读、写操作改变着指针数值。如果
我们不阻止指针在临界状态下改变状态,FIFO还能都一边“吃”着数据一
边“产生”数据,这简直是不可能的。
进一步分析:DPRAM若能够寄存读出的信号,这意味着存储器的输出数据已
被寄存。如果这样的话,读指针将不得不设计成“read 并加1 ”,也就是说
在FIFO输出数据有效之前,必须提供一个明确的读信号。另一方面,如果
DPRAM没有寄存输出,一旦写入有效数据就可以读出;先读数据,然后
使指针加1。这将影响到从FIFO读出数据和实现空/满计算的逻辑。由于
简化的缘故,我们仅论述DPRAM没有提供索锁存输出的情况。同理,将其
推广到寄存输出的DPRAM并不是很复杂。
从功能上看,FIFO工作原理如下所述:复位时,读、写指针均为0。
这是FIFO的空状态,空标志为高电平,(我们用高电平表示空标志)
此时满标志为低电平。当FIFO出现空标志时,不允许读操作,只能允许写操作。
写操作写入到位置0,并使写指针加1。此时,空标志变为低电平。假设没有发生读
操作而且随后的一段时间FIFO中只有写操作。一定时间后,写指针的值等于array_size-1。
这就意味着在存储器中,要写入数据的最后一个位置就是下一个位置。
在这种情况下,写操作将写指针变为0,并将输出满标志。
注意,在这种情况下,写指针和读指针是相等的,但是FIFO已满,而不是空。
这意味着“满”或“空”的决定并不是仅仅基于指针的值,而是基于引起指
针值相等的操作。如果指针值相等的原因是复位或者读操作,FIFO认为是空;
如果原因是写操作,那么FIFO认为是满。
现在,假设我们开始一系列的读操作,每次读操作都将增加读指针的值,
直到读指针的位置等于array_size-1。在该点,从这个位置读出的FIFO输
出总线上的数据是有效的。随后的逻辑读取这些数据并提供一个读信号
(在一个时钟周期内有效)。这将导致读指针再次等于写指针
(在两个指针走完存储器一圈后)。然而,由于这次相等是由于
一个读操作,将会输出空标志。
因此,我们将得到如下的空标志:写操作无条件的清除空标志。
Read pointer=(array_size-1) , 读操作置空标志。
以及如下的满标志:读操作无条件的清除满标志,
Write pointer= (array_size-1), 写操作置满标志。
然而,这是一个特殊的例子,由于一般情况下,读操作在FIFO不是空的情
况下就开始了(读操作逻辑不需要等待FIFO变满),因此这些条件不得不修改来存储读指针和写指针的每一个值。

有这样一个想法,那就是我们可以将存储器组织成一个环形列表。
因此,如果写指针与读指针差值大于1或更多,就进行读操作,
FIFO为空,这种工作方式对于用无符号(n-bit)结构来描述的
临界状态非常适合。同样的,如果读指针与写指针的差值大于1,
就进行写操作,直到FIFO为满。
这将带来如下的条件:
写操作无条件的清除空标志。
write_pointer=(read_pointer+1),读操作置空。
读操作无条件的清除满标志,
read_pointer= (write_pointer+1),写操作置满。
注意,读操作和写操作同时都在使其指针增加,
但不改变空标志和满标志的状态。在空或满的临界状态同时读操作和写操作都是不允许的。
综上所述,我们现在能够定义FIFO的status模块,
这里提供了用VHDL编写的代码,由于是同步的,很容易转换成Verilog HDL代码。

library IEEE, STD;use IEEE.std_logic_1164.all;use IEEE.std_logic_arith.all;use IEEE.std_logic_unsigned.all;entity status isport (reset : in std_logic;clk : in std_logic;fifo_wr : in std_logic;fifo_rd : in std_logic;valid_rd : out std_logic;valid_wr : out std_logic;rd_ptr : out std_logic_vector(4 downto 0);wr_ptr : out std_logic_vector(4 downto 0);empty : out std_logic;full : out std_logic);end status;architecture status_A of status issignal rd_ptr_s : std_logic_vector(4 downto 0);signal wr_ptr_s : std_logic_vector(4 downto 0);signal valid_rd_s : std_logic;signal valid_wr_s : std_logic;beginempty_P : process(clk, reset)beginif (reset = '1') thenempty <= '1';elsif (clk'event and clk = '1') thenif (fifo_wr = '1' and fifo_rd = '1') then-- do nothingnull;elsif (fifo_wr = '1') then-- write unconditionally clears emptyempty <= '0';elsif (fifo_rd = '1' and (wr_ptr_s = rd_ptr_s + '1')) then-- set emptyempty <= '1';end if;end if;end process;full_P : process(clk, reset)beginif (reset = '1') thenfull <= '0';elsif (clk'event and clk = '1') thenif (fifo_rd = '1' and fifo_wr = '1') then-- do nothingnull;elsif (fifo_rd = '1') then-- read unconditionally clears fullfull <= '0';elsif (fifo_wr = '1' and (rd_ptr_s = wr_ptr_s + '1')) then-- set fullfull <= '1';end if;end if;end process;valid_rd_s <= '1' when (empty = '0' and fifo_rd = '1');valid_wr_s <= '1' when (full = '0' and fifo_wr = '1');wr_ptr_s_P : process(clk, reset)beginif (reset = '1') thenwr_ptr_s_P <= (others => '0');elsif (clk'event and clk = '1') thenif (valid_wr_s = '1') thenwr_ptr_s <= wr_ptr_s + '1';end if;end if;end process;rd_ptr_s_P : process(clk, reset)beginif (reset = '1') thenrd_ptr_s_P <= (others => '0');elsif (clk'event and clk = '1') thenif (valid_rd_s = '1') thenrd_ptr_s <= rd_ptr_s + '1';end if;end if;end process;rd_ptr <= rd_ptr_s;wr_ptr <= wr_ptr_s;end status_A;

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭