各种FIFO硬件设计(FIFO概念、异步、同步、非2次幂深度FIFO)
文章目录
- 一、FIFO概述
- 二、FIFO分类
- 三、FIFO重要信号与参数
- 3.1 信号
- 3.2 参数
- 3.2.1 data_depth的确定
- 四、FIFO存储原理
- 五、同步FIFO
- 5.1 空满信号判断
- 5.2 同步FIFO源码
- 5.3 测试源码
- 5.4 功能仿真结果
- 在这里插入图片描述
- 六、异步FIFO
- 6.1 异步FIFO架构
- 6.2 设计源码
- 6.2.1 二进制-格雷码转换器
- 6.2.2 信号同步器(dff)
- 6.2.3 异步FIFO顶层
- 6.2.4 测试源码
- 6.2.5 功能仿真结果
- 七、非2的次幂FIFO
- 7.1 设计思路
- 7.2 设计源码
- 7.3 测试程序
- 7.4 功能仿真
- 八、非2次幂FIFO(带将空将满信号与FIFO余量)
- 8.1 设计源码
- 8.2 测试源码
- 8.3 功能仿真结果
(码字不易、三连支持)
一、FIFO概述
FIFO(first in first out)是一种先进先出的存储器,与栈不同,栈对应的是一种先进后出的数据存储理念。
FIFO无论是在IC设计中、IP核设计中、SOC设计中都存在广泛的应用。特别是随着设计复杂度的提高,在一个系统中往往会引入多个时钟,这也就使得数据的跨时钟域处理显得尤为重要,而FIFO正是解决这一问题的有效方法。
FIFO的设计应用:
1.德州仪器tm4c123g单片机的UART模块使用FIFO做缓冲。
2.NXP单片机正交解码器缓冲。
3.摄像头的数据缓冲。
4.使用ADC配合FIFO与DMA实现高速采集。
5.提高状态机模块的数据吞吐率(软件不需要再读状态机的busy标志位,只需关心fifo是否空满)。
二、FIFO分类
FIFO根据读写时钟域的不同可分为同步FIFO与异步FIFO。
同步FIFO是指读写通道均在相同的时钟下进行信号采样的FIFO,主要用于设备之间数据传输速率的匹配。
异步FIFO是指读写通道在不同的时钟下进行信号采样的FIFO,主要处理跨时钟域之间的数据传输问题。
系统2如果直接去采样处于时钟域1的系统数据,很有可能会采样到处于亚稳态的数据。使用异步FIFO对数据进行缓冲一定程度上减少了亚稳态发生的概率。
(无论是同步还是异步的FIFO,都是面向数据流的一种数据存储器,都具有数据缓冲作用)。
三、FIFO重要信号与参数
3.1 信号
通道 | 信号 | 位宽 | 描述 |
---|---|---|---|
nrst | 1 | 复位信号,有效时将清空FIFO中已缓冲的数据 | |
写通道 | clk_w | 1 | 写数据时钟,上升沿采样写通道信号 |
写通道 | wr_en | 1 | 写有效信号,当其无效时,FIFO将忽略写通道传输的数据;也可视为写数据的同步帧;当其有效时,FIFO会写入当前数据; |
写通道 | wrdata | 任意(需与RAM匹配) | 写数据输入,位宽视需求任意; |
写通道 | full | 1 | FIFO满信号,当写满时,此信号有效,此时FIFO将阻塞数据的输入; |
写通道 | almost_full | 1 | 将满信号,当FIFO中存储的数据达到或超过设定的余量时,将有效;此时FIFO不会阻塞数据的写入,但设备可以通过此信号来决定是否继续写入数据; |
读通道 | clk_r | 1 | 读数据通道时钟;(在同步FIFO中,该信号必须与clk_w接至同一时钟) |
读通道 | rd_en | 1 | 读有效信号,当其无效时,FIFO停止数据的读出;也可视为读数据的同步帧;当其有效时,FIFO会读出数据; |
读通道 | rddata | 任意(需与RAM匹配) | 读数据输出,位宽视需求任意; |
读通道 | empty | 1 | 空信号,当FIFO中数据为空时有效;此时FIFO将阻塞数据的读取; |
读通道 | almost_empty | 1 | 将空信号,当FIFO中存储的数据达到或低于设定的最小余量时,将有效;此时FIFO不会阻塞数据的读出,但设备可以通过此信号来判断是否继续读出数据; |
data_count | 任意(与数据深度匹配) | 存储计数器,表示当前FIFO中存储数据的数量,设备可根据该信号来判断是否写入或读出数据; |
在FIFO实际的应用中,一般将empty与almost_full配合起来使用。为了减少FIFO上溢的风险,full信号很少使用。
3.2 参数
参数 | 描述 |
---|---|
data_depth | 数据深度,代表了FIFO缓冲数据的能力;一般为2的次幂;在支持非2次幂深度的FIFO中,可任意; |
data_width | 数据读写宽度,一般与内部RAM匹配数据宽度,在不匹配情况下需要对数据进行补位处理; |
addr_width | 地址宽度,与深度对应,其关系式:addr_width = log2(data_depth) |
data_depth是否越大越好?
否,data_depth应该根据读写数据方的速率进行合理确定。
过大的data_depth会消耗过多的资源(若RAM是LUT实现,则消耗大量的LUT,若是Block RAM 则消耗Block RAM资源)
3.2.1 data_depth的确定
深度的确定既要满足数据不丢失,且不能过多的浪费资源;
这里首先考虑极端情况:
如果写数据方的写入速率大于读数据方的读出速率,则FIFO的数据深度只有无穷大时,才能确保数据不溢出;显然无穷大深度的FIFO是不存在的;这种情况是无解的;
相较于上述的极端情况,更多的是考虑写入Burst数据的情况,虽然写入数据流是连续的,但写Burst之间数据往往存在时间间隔;
fw>fr,且读写之间没有空闲周期:
假设fw为100Mhz,fr为80Mhz,一个Burst传输长度为2400。
根据fw,可以知道写入一个数据需要10ns
同理根据fr,可知读出一个数据需要12.5ns
将2400个数据写入FIFO需要2400*10ns = 24000ns
而在这2400个数据被写入期间,FIFO实际被读出的数据为24000/12.5 = 1920个
则该情况下FIFO深度需要2400-1920 = 480
其他情况下的FIFO深度计算也可以参考上述的情况,无论情形如何,最终需要计算的都是写入数据与读出数据之差;
如写时钟大于读时钟,且连写入之间会有1时钟空闲,读出之间会有3时钟空闲;
这种情况下其实写入的频率变为了100Mhz/(1+1) = 50Mhz,读出频率变为了80Mhz/(3+1) = 20Mhz,后面的解法就和上面一样了;
还有些情况会给出读写使能信号的占空比,这其实也变相的给出了读写频率,如wr_en占空比50%,rd_en占空比25%则fw = 100Mhz*(50%) = 50Mhz,fr = 80Mhz*(25%) = 20Mhz;总之万变不离其宗;
四、FIFO存储原理
FIFO的存储地址是不需要设备给出的,每次读使能有效则地址自增1,每次写使能有效则地址自增1;
如果说RAM对应的是存储地址首尾不相接的数据空间,则FIFO的地址是首位相连的;
如下图在初始化后,写指针与读指针指向地址0,此时FIFO为空状态;此时若强行读出数据,r_ptr则增1,到地址1的位置,很显然此处并无数据写入这个数据是错误的,这种情况为为数据下溢;
当FIFO写入了7个数据后,w_ptr指向地址7,此时若再写入一个数据,写指针w_ptr将回到地址0,w_ptr与r_ptr相同此时FIFO写满,若继续写入数据,则会覆盖之前的数据,这就是数据的上溢。
在FIFO实际使用中,写和读操作往往同时进行,此时w_ptr与r_ptr就会在这个环式地址空间上赛跑;假设写入了5个数据,则此时w_ptr指向了地址5,此时读出了两个数据,则r_ptr指向了地址2,如下图(红色为写入数据,蓝色为已读出的数据)。
被读出的数据可以看作是被释放了出来,可以再次被写入数据;读写指针就这不断进行绕圈;
在绕圈情况中,如果r_ptr超过了w_ptr(蓝色部分完全覆盖了红色部分),则会读出错误数据;
若w_ptr超过r_ptr(红色部分覆盖了蓝色部分),则会出现覆盖数据的情况;
上述两种情况需要避免,可以通过空满信号来避免;
当r_ptr 赶上w_ptr时,则判断为空,此时FIFO阻止数据继续读出;
当w_ptr赶上r_ptr时,则判断为满,此时FIFO阻止数据继续写入;
五、同步FIFO
5.1 空满信号判断
同比FIFO设计中,空满信号的判断比较简单,我们只需要想办法描述上述的两种“赶上”情况就可以了;
描述方法有很多,这里给出一个最简单的方法:
假设深度为8,则地址宽度为3,我们在设计中对实际的地址进行1位扩位来存储“圈数”。
此时地址枚举出来如下:
我们可以看到在自增8之后,高位从0变为了1,此时就代表该指针已经开始跑第二圈了;若此时写指针追上了还在跑上一圈(高位不同)的读指针,则说明此时FIFO满;
空则是相同圈内(高位相同),读指针赶上了写指针;
上述逻辑则可以写为:
assign fifo_empty = ((w_ptr == r_ptr)||((w_ptr=='b0)&(r_ptr=='b0))) ? 1:0;
assign fifo_full = ((w_ptr[FIFO_ADDR_WIDTH] != r_ptr[FIFO_ADDR_WIDTH])&&(w_ptr[FIFO_ADDR_WIDTH-1:0] == r_ptr[FIFO_ADDR_WIDTH-1:0])) ? 1:0;
5.2 同步FIFO源码
module Synchronous_FIFO#(parameter integer RAM_ADDR_WIDTH = 5,parameter integer FIFO_DATA_DEPTH = 8,parameter integer FIFO_ADDR_WIDTH = $clog2(FIFO_DATA_DEPTH),parameter integer FIFO_DATA_WIDTH = 8)(input wire nrst,input wire clk_w,input wire wr_en,input wire [FIFO_DATA_WIDTH-1:0] wrdata,output wire fifo_full,input wire clk_r,input wire rd_en,output wire [FIFO_DATA_WIDTH-1:0] rddata,output wire fifo_empty);/
/*w_ptr : the write data pointer (RAM Write address)r_ptr : the read data pointer (RAM Read address)
*/
reg [FIFO_ADDR_WIDTH:0] w_ptr;reg [FIFO_ADDR_WIDTH:0] r_ptr;wire [RAM_ADDR_WIDTH-1:0] ram_wr_addr;wire [RAM_ADDR_WIDTH-1:0] ram_rd_addr;assign ram_wr_addr = {{(RAM_ADDR_WIDTH-FIFO_ADDR_WIDTH){1'b0}},w_ptr[FIFO_ADDR_WIDTH-1:0]};assign ram_rd_addr = {{(RAM_ADDR_WIDTH-FIFO_ADDR_WIDTH){1'b0}},r_ptr[FIFO_ADDR_WIDTH-1:0]};
/
/*fifo_wr : (RAM Write enable). valid only when fifo is not full and fifo data write enable fifo_rd : (RAM Read enable). valid only when fifo is not empty and fifo data read enable
*/
wire fifo_wr;wire fifo_rd;assign fifo_wr = (~fifo_full)&wr_en;assign fifo_rd = (~fifo_empty)&rd_en;/
/*fifo_empty : valid only when w_ptr is equal with r_ptr fifo_full : valid only when w_ptr's 1 MSB is equal with not( r_ptr's 1 MSB) and w_ptr's other bits equals r_ptr's
*/
assign fifo_empty = ((w_ptr == r_ptr)||((w_ptr=='b0)&(r_ptr=='b0))) ? 1:0;assign fifo_full = ((w_ptr[FIFO_ADDR_WIDTH] != r_ptr[FIFO_ADDR_WIDTH])&&(w_ptr[FIFO_ADDR_WIDTH-1:0] == r_ptr[FIFO_ADDR_WIDTH-1:0])) ? 1:0;always @(posedge clk_w,negedge nrst) beginif (~nrst) begin// resetw_ptr <= 'b0;endelse beginif(fifo_wr)w_ptr <= w_ptr + 'b1;elsew_ptr <= w_ptr ;endendalways @(posedge clk_r,negedge nrst) beginif (~nrst) begin// resetr_ptr <= 'b0;endelse beginif(fifo_rd)r_ptr <= r_ptr + 'b1;elser_ptr <= r_ptr ;endendblk_mem_gen_0 ram0(.addra (ram_wr_addr),.clka (clk_w),.dina (wrdata),.wea (1),.ena (fifo_wr),.addrb (ram_rd_addr),.clkb (clk_r),.doutb (rddata),.enb (fifo_rd));
endmodule
5.3 测试源码
module Synchronous_FIFO_tb();parameter integer FIFO_DATA_DEPTH = 8;parameter integer FIFO_DATA_WIDTH = 8;reg nrst;reg clk;reg wr_en;reg [FIFO_DATA_WIDTH-1:0] wrdata;wire fifo_full;reg rd_en;wire [FIFO_DATA_WIDTH-1:0] rddata;wire fifo_empty;integer i;initial beginclk = 0;forever begin#1 clk = ~clk;endendinitial beginnrst = 0;#2 nrst = 1;wr_en = 1;wrdata = 0;rd_en = 0;while(wrdata <= 8)begin#2 wr_en = 1;wrdata = wrdata + 1;endwr_en = 0;rd_en = 1;endSynchronous_FIFO#(. FIFO_DATA_DEPTH(FIFO_DATA_DEPTH),. FIFO_DATA_WIDTH(FIFO_DATA_WIDTH))Synchronous_FIFO_inist0(. nrst(nrst),. clk_w(clk),. wr_en(wr_en),. wrdata(wrdata),. fifo_full(fifo_full),. clk_r(clk),. rd_en(rd_en),. rddata(rddata),. fifo_empty(fifo_empty));
endmodule
5.4 功能仿真结果
六、异步FIFO
6.1 异步FIFO架构
异步FIFO设计则需要考虑同步问题;
此时同步FIFO中指针计数器会出现亚稳态问题,因为地址从上一个变化到下一个会产生多个比特位的变化,如0111到1000,变化的比特位为4位,由于布局布线的问题,每一比特位的变化速度不同,这将导致采集到不可预测的错误数据;
因此我们需要另一种比特位变化更少的编码,即格雷码;
*二进制码与格雷码对照表
如上表,相邻的两位变化的比特位仅为1;除此之外,格雷码还具有很重要的对称性,我们可以在上图的7到8之间画一条分界线,除了高位,其余为按照这条分界线对称;
另一方面,需要将转换后的格雷码进行时钟域同步,这里采用典型的二级同步器;
空逻辑判断:
将w_ptr对应的格雷码w2r_ptr_gray同步到读时钟域,再将同步后的w2r_ptr_gray与r_ptr对应的格雷码r_ptr_gray进行对比,若两者相等则判定为空;
assign fifo_empty = (r_ptr_gray == w2r_ptr_gray) ? 1:0;
满逻辑判断:
将r_ptr对应的格雷码r2w_ptr_gray同步到写时钟域,再将同步后的r2w_ptr_gray与w_ptr对应的格雷码w_ptr_gray进行对比,若两者高两位为取反关系,剩余位相同,则判断为满;
assign fifo_full = ((w_ptr_gray[FIFO_ADDR_WIDTH:FIFO_ADDR_WIDTH-1] == ~(r2w_ptr_gray[FIFO_ADDR_WIDTH:FIFO_ADDR_WIDTH-1]))&&(w_ptr_gray[FIFO_ADDR_WIDTH-2:0] == r2w_ptr_gray[FIFO_ADDR_WIDTH-2:0])) ? 1:0;
综上,异步FIFO的架构可以总结为下图所示:
6.2 设计源码
6.2.1 二进制-格雷码转换器
module Gray_encoder#(parameter integer DATA_WIDTH = 8)(input wire [DATA_WIDTH - 1 : 0] data_in,output wire [DATA_WIDTH - 1 : 0] data_out);assign data_out = (data_in >> 1) ^ data_in;endmodule
module Gray_decoder#(parameter integer DATA_WIDTH = 8)(input wire [DATA_WIDTH - 1 : 0] data_in,output wire [DATA_WIDTH - 1 : 0] data_out);reg [DATA_WIDTH - 1 : 0] data_out_r;assign data_out = data_out_r;integer index;always @(*) beginfor(index = 0; index < DATA_WIDTH; index = index + 1)data_out_r[index] <= ^(data_in>>index);endendmodule
6.2.2 信号同步器(dff)
module dff#(parameter integer DFF_LEVEL = 1,parameter integer DATA_WIDTH = 8
)(input wire clk,input wire [DATA_WIDTH - 1:0] din,input wire [DATA_WIDTH - 1:0] dout,input wire nrst);reg [DATA_WIDTH - 1:0] din_buff [DFF_LEVEL-1:0];assign dout = din_buff[DFF_LEVEL-1];integer i;always @(posedge clk or negedge nrst) beginif (~nrst) begin// resetfor(i=0;i<DFF_LEVEL;i=i+1)begindin_buff[i] <= 'b0;endendelse beginfor(i=1;i<DFF_LEVEL;i=i+1)begindin_buff[i] <= din_buff[i-1];enddin_buff[0] <= din;endendendmodule
6.2.3 异步FIFO顶层
module Asynchronous_FIFO#(parameter integer RAM_ADDR_WIDTH = 5,parameter integer FIFO_DATA_DEPTH = 8,parameter integer FIFO_ADDR_WIDTH = $clog2(FIFO_DATA_DEPTH),parameter integer FIFO_DATA_WIDTH = 8)(input wire nrst,input wire clk_w,input wire wr_en,input wire [FIFO_DATA_WIDTH-1:0] wrdata,output wire fifo_full,input wire clk_r,input wire rd_en,output wire [FIFO_DATA_WIDTH-1:0] rddata,output wire fifo_empty);/
/*w_ptr : the write data pointer (RAM Write address)r_ptr : the read data pointer (RAM Read address)
*/
reg [FIFO_ADDR_WIDTH:0] w_ptr;reg [FIFO_ADDR_WIDTH:0] r_ptr;wire [RAM_ADDR_WIDTH-1:0] ram_wr_addr;wire [RAM_ADDR_WIDTH-1:0] ram_rd_addr;assign ram_wr_addr = {{(RAM_ADDR_WIDTH-FIFO_ADDR_WIDTH){1'b0}},w_ptr[FIFO_ADDR_WIDTH-1:0]};assign ram_rd_addr = {{(RAM_ADDR_WIDTH-FIFO_ADDR_WIDTH){1'b0}},r_ptr[FIFO_ADDR_WIDTH-1:0]};wire fifo_wr;wire fifo_rd;/
/*fifo_wr : (RAM Write enable). valid only when fifo is not full and fifo data write enable fifo_rd : (RAM Read enable). valid only when fifo is not empty and fifo data read enable
*/
assign fifo_wr = (~fifo_full)&wr_en;assign fifo_rd = (~fifo_empty)&rd_en;wire [FIFO_ADDR_WIDTH : 0] w_ptr_gray;wire [FIFO_ADDR_WIDTH : 0] r_ptr_gray;wire [FIFO_ADDR_WIDTH : 0] w2r_ptr_gray;wire [FIFO_ADDR_WIDTH : 0] r2w_ptr_gray;
/
/*fifo_empty : valid only when r_ptr_gray(read ptr's gray code) is equal with w2r_ptr_gray(w_ptr's gray code Sync from write clock domain) fifo_full : valid only when w_ptr_gray's 2 MSB is equal with not( r2w_ptr_gray's 2 MSB) and w_ptr_gray's other bits equals r2w_ptr_gray's
*/
assign fifo_empty = (r_ptr_gray == w2r_ptr_gray) ? 1:0;assign fifo_full = ((w_ptr_gray[FIFO_ADDR_WIDTH:FIFO_ADDR_WIDTH-1] == ~(r2w_ptr_gray[FIFO_ADDR_WIDTH:FIFO_ADDR_WIDTH-1]))&&(w_ptr_gray[FIFO_ADDR_WIDTH-2:0] == r2w_ptr_gray[FIFO_ADDR_WIDTH-2:0])) ? 1:0;
/
/*Gray code exchange logic
*/
Gray_encoder#(. DATA_WIDTH(FIFO_ADDR_WIDTH+1))Gray_encoder_inist0(. data_in(w_ptr),. data_out(w_ptr_gray));Gray_encoder#(. DATA_WIDTH(FIFO_ADDR_WIDTH+1))Gray_encoder_inist1(. data_in(r_ptr),. data_out(r_ptr_gray));always @(posedge clk_w,negedge nrst) beginif (~nrst) begin// resetw_ptr <= 'b0;endelse beginif(fifo_wr)w_ptr <= w_ptr + 'b1;elsew_ptr <= w_ptr ;endendalways @(posedge clk_r,negedge nrst) beginif (~nrst) begin// resetr_ptr <= 'b0;endelse beginif(fifo_rd)r_ptr <= r_ptr + 'b1;elser_ptr <= r_ptr ;endend
/
/*Synchronize logic across clock domains
*/
dff#(.DFF_LEVEL(2),.DATA_WIDTH(FIFO_ADDR_WIDTH+1)
)dff_inist0(. clk(clk_r),. din(w_ptr_gray),. dout(w2r_ptr_gray),. nrst(nrst));dff#(.DFF_LEVEL(2),.DATA_WIDTH(FIFO_ADDR_WIDTH+1)
)dff_inist1(. clk(clk_w),. din(r_ptr_gray),. dout(r2w_ptr_gray),. nrst(nrst));blk_mem_gen_0 ram0(.addra (ram_wr_addr),.clka (clk_w),.dina (wrdata),.wea (1),.ena (fifo_wr),.addrb (ram_rd_addr),.clkb (clk_r),.doutb (rddata),.enb (fifo_rd));
endmodule
6.2.4 测试源码
module async_fifo_tb();parameter integer RAM_ADDR_WIDTH = 5;parameter integer FIFO_DATA_DEPTH = 8;parameter integer FIFO_DATA_WIDTH = 8;reg nrst;reg clk_w;reg wr_en;reg [FIFO_DATA_WIDTH-1:0] wrdata;wire fifo_full;reg clk_r;reg rd_en;wire [FIFO_DATA_WIDTH-1:0] rddata;wire fifo_empty;integer i;initial beginclk_w = 0;forever begin#2 clk_w = ~clk_w;endendinitial beginclk_r = 0;#1forever begin#2 clk_r = ~clk_r;endendinitial beginnrst = 0;#4 nrst = 1;wr_en = 1;wrdata = 0;rd_en = 0;while(wrdata <= 8)begin#4 wr_en = 1;wrdata = wrdata + 1;endwr_en = 0;rd_en = 1;endAsynchronous_FIFO#(. RAM_ADDR_WIDTH(RAM_ADDR_WIDTH),. FIFO_DATA_DEPTH(FIFO_DATA_DEPTH),. FIFO_DATA_WIDTH(FIFO_DATA_WIDTH))Asynchronous_FIFO_inist0(. nrst(nrst),. clk_w(clk_w),. wr_en(wr_en),. wrdata(wrdata),. fifo_full(fifo_full),. clk_r(clk_r),. rd_en(rd_en),. rddata(rddata),. fifo_empty(fifo_empty));
endmodule
6.2.5 功能仿真结果
我们观察上面的仿真图,fifo_empty在写入两个数据后才拉低,fifo_full在读出两个数据后才拉低,这就是假空假满现象;
本质上这个现象是由同步器造成的,空信号是在读时钟与将读指针与同步过来的写指针进行比较,由于同步器采用了两级,因此会有两个clk的滞后输出,即实际的写地址已经增至2了,但同步过来的地址仍为0;假满状态同理;
但这种现象并不会影响FIFO的使用,因为这样产生的空满信号实际上是悲观的,虽然FIFO中有数据,但空信号仍然会阻止数据的读出,因此只会在FIFO的效率上产生影响。
七、非2的次幂FIFO
7.1 设计思路
非2次幂FIFO实际上与2次幂异步FIFO差不多,只不过在编码格式上我们需要有所改变,对应的空满信号的判断也需要稍加改动;
假设是6深度的FIFO,使用顺序的格雷码,变化的比特位就不再是1位了;
如从6变化到0,共两个码位发生了变化;
针对这个问题,可以利用格雷码的对称性解决;如下图
由13到2,(1011)到(0011)只变化了1位;
将原来的顺序格雷码映射到对称格雷码也比较简单;
只需在指针与格雷码的转换逻辑上,每次转换的指针加上一个偏置项FIFO_DEPTH_COMPLE;
Gray_encoder#(. DATA_WIDTH(FIFO_ADDR_WIDTH+1))Gray_encoder_inist0(. data_in(w_ptr+FIFO_DEPTH_COMPLE),. data_out(w_ptr_gray));Gray_encoder#(. DATA_WIDTH(FIFO_ADDR_WIDTH+1))Gray_encoder_inist1(. data_in(r_ptr+FIFO_DEPTH_COMPLE),. data_out(r_ptr_gray));
FIFO_DEPTH_COMPLE同时也为地址的补偿比特位,其使得FIFO_DATA_DEPTH + FIFO_DEPTH_COMPLE 为大于FIFO_DATA_DEPTH的最小2的次幂整数;
如FIFO_DATA_DEPTH为6,则大于6的最小2次幂整数为8,则FIFO_DEPTH_COMPLE = 8 - FIFO_DATA_DEPTH = 2;
采用这种编码方式,满信号的逻辑无法再使用两格雷码高两位取反,其余位相同的逻辑来进行了;
这里采用将同步过来的格雷码形式的指针转换为二进制,再通过对两二进制指针作差取绝对值算出FIFO中当前写入的数据数,来判断是否空满;
7.2 设计源码
module Asynchronous_FIFO_npow2#(//BLOCK RAM address width parameter integer RAM_ADDR_WIDTH = 5,//FIFO true depthparameter integer FIFO_DATA_DEPTH = 6,//FIFO compensate depth,(FIFO_DATA_DEPTH+FIFO_DEPTH_COMPLE) must be the data of power 2parameter integer FIFO_DEPTH_COMPLE = 2,parameter integer FIFO_ADDR_WIDTH = $clog2(FIFO_DATA_DEPTH+FIFO_DEPTH_COMPLE),parameter integer FIFO_DATA_WIDTH = 8)(input wire nrst,input wire clk_w,input wire wr_en,input wire [FIFO_DATA_WIDTH-1:0] wrdata,output wire fifo_full,input wire clk_r,input wire rd_en,output wire [FIFO_DATA_WIDTH-1:0] rddata,output wire fifo_empty);/
/*w_ptr : the write data pointer (RAM Write address)r_ptr : the read data pointer (RAM Read address)
*/
reg [FIFO_ADDR_WIDTH:0] w_ptr;reg [FIFO_ADDR_WIDTH:0] r_ptr;wire [RAM_ADDR_WIDTH-1:0] ram_wr_addr;wire [RAM_ADDR_WIDTH-1:0] ram_rd_addr;assign ram_wr_addr = {{(RAM_ADDR_WIDTH-FIFO_ADDR_WIDTH){1'b0}},w_ptr[FIFO_ADDR_WIDTH-1:0]};assign ram_rd_addr = {{(RAM_ADDR_WIDTH-FIFO_ADDR_WIDTH){1'b0}},r_ptr[FIFO_ADDR_WIDTH-1:0]};wire fifo_wr;wire fifo_rd;/
/*fifo_wr : (RAM Write enable). valid only when fifo is not full and fifo data write enable fifo_rd : (RAM Read enable). valid only when fifo is not empty and fifo data read enable
*/
assign fifo_wr = (~fifo_full)&wr_en;assign fifo_rd = (~fifo_empty)&rd_en;wire [FIFO_ADDR_WIDTH : 0] w_ptr_gray;wire [FIFO_ADDR_WIDTH : 0] r_ptr_gray;wire [FIFO_ADDR_WIDTH : 0] w2r_ptr_gray;wire [FIFO_ADDR_WIDTH : 0] r2w_ptr_gray;wire [FIFO_ADDR_WIDTH : 0] w2r_ptr_bin;wire [FIFO_ADDR_WIDTH : 0] r2w_ptr_bin;wire [FIFO_ADDR_WIDTH : 0] w_fifo_data_count;wire [FIFO_ADDR_WIDTH : 0] r_fifo_data_count;assign r_fifo_data_count = ((w2r_ptr_bin - FIFO_DEPTH_COMPLE)> r_ptr) ? (w2r_ptr_bin - r_ptr - FIFO_DEPTH_COMPLE):(r_ptr - w2r_ptr_bin + FIFO_DEPTH_COMPLE);assign w_fifo_data_count = ((r2w_ptr_bin - FIFO_DEPTH_COMPLE)> w_ptr) ? (r2w_ptr_bin - w_ptr - FIFO_DEPTH_COMPLE):(w_ptr - r2w_ptr_bin + FIFO_DEPTH_COMPLE);
/
/*fifo_empty : valid only when r_ptr_gray(read ptr's gray code) is equal with w2r_ptr_gray(w_ptr's gray code Sync from write clock domain) fifo_full : valid only when w_ptr_gray's 2 MSB is equal with not( r2w_ptr_gray's 2 MSB) and w_ptr_gray's other bits equals r2w_ptr_gray's
*/
assign fifo_empty = (r_fifo_data_count == 'b0) ? 1:0;assign fifo_full = (w_fifo_data_count == FIFO_DATA_DEPTH) ? 1:0;
/
/*Gray code exchange logic
*/
Gray_encoder#(. DATA_WIDTH(FIFO_ADDR_WIDTH+1))Gray_encoder_inist0(. data_in(w_ptr+FIFO_DEPTH_COMPLE),. data_out(w_ptr_gray));Gray_encoder#(. DATA_WIDTH(FIFO_ADDR_WIDTH+1))Gray_encoder_inist1(. data_in(r_ptr+FIFO_DEPTH_COMPLE),. data_out(r_ptr_gray));always @(posedge clk_w,negedge nrst) beginif (~nrst) begin// resetw_ptr <= 'b0;endelse beginif(fifo_wr)w_ptr <= w_ptr + 'b1;elsew_ptr <= w_ptr ;endendalways @(posedge clk_r,negedge nrst) beginif (~nrst) begin// resetr_ptr <= 'b0;endelse beginif(fifo_rd)r_ptr <= r_ptr + 'b1;elser_ptr <= r_ptr ;endend
/
/*Synchronize logic across clock domains
*/
dff#(.DFF_LEVEL(2),.DATA_WIDTH(FIFO_ADDR_WIDTH+1)
)dff_inist0(. clk(clk_r),. din(w_ptr_gray),. dout(w2r_ptr_gray),. nrst(nrst));dff#(.DFF_LEVEL(2),.DATA_WIDTH(FIFO_ADDR_WIDTH+1)
)dff_inist1(. clk(clk_w),. din(r_ptr_gray),. dout(r2w_ptr_gray),. nrst(nrst));
/
/*read and write pointer decode logic
*/
Gray_decoder#(. DATA_WIDTH(FIFO_ADDR_WIDTH+1))Gray_decoder_inist0(. data_in(w2r_ptr_gray),. data_out(w2r_ptr_bin));Gray_decoder#(. DATA_WIDTH(FIFO_ADDR_WIDTH+1))Gray_decoder_inist1(. data_in(r2w_ptr_gray),. data_out(r2w_ptr_bin));blk_mem_gen_0 ram0(.addra (ram_wr_addr),.clka (clk_w),.dina (wrdata),.wea (1),.ena (fifo_wr),.addrb (ram_rd_addr),.clkb (clk_r),.doutb (rddata),.enb (fifo_rd));
endmodule
7.3 测试程序
module Asynchronous_FIFO_npow2_tb();parameter integer RAM_ADDR_WIDTH = 5;parameter integer FIFO_DATA_DEPTH = 3;parameter integer FIFO_DEPTH_COMPLE = 5;parameter integer FIFO_DATA_WIDTH = 8;reg nrst;reg clk_w;reg wr_en;reg [FIFO_DATA_WIDTH-1:0] wrdata;wire fifo_full;reg clk_r;reg rd_en;wire [FIFO_DATA_WIDTH-1:0] rddata;wire fifo_empty;integer i;initial beginclk_w = 0;forever begin#2 clk_w = ~clk_w;endendinitial beginclk_r = 0;#1forever begin#2 clk_r = ~clk_r;endendinitial beginnrst = 0;#4 nrst = 1;wr_en = 1;wrdata = 0;rd_en = 0;while(wrdata <= 8)begin#4 wr_en = 1;wrdata = wrdata + 1;endwr_en = 0;rd_en = 1;endAsynchronous_FIFO_npow2#(//BLOCK RAM address width . RAM_ADDR_WIDTH (RAM_ADDR_WIDTH),//FIFO true depth. FIFO_DATA_DEPTH (FIFO_DATA_DEPTH),//FIFO compensate depth,(FIFO_DATA_DEPTH+FIFO_DEPTH_COMPLE) must be the data of power 2. FIFO_DEPTH_COMPLE (FIFO_DEPTH_COMPLE),. FIFO_DATA_WIDTH (FIFO_DATA_WIDTH))Asynchronous_FIFO_npow2_inist(. nrst(nrst),. clk_w(clk_w),. wr_en(wr_en),. wrdata(wrdata),. fifo_full(fifo_full),. clk_r(clk_r),. rd_en(rd_en),. rddata(rddata),.fifo_empty(fifo_empty));
endmodule
7.4 功能仿真
八、非2次幂FIFO(带将空将满信号与FIFO余量)
进一步在第七章中的FIFO上进行将空将满信号的设计;
将空将满已经在之前介绍过了,可以视作一个阈值,当FIFO中的数据数大于等于这个阈值时将满信号拉高,反之将空信号拉高;
上一章已经介绍了r_fifo_data_count与w_fifo_data_count的计算方法,我们使用这两个计数值来增加将空将满逻辑,如下:
assign fifo_almost_full = (w_fifo_data_count >= (FIFO_DATA_DEPTH - FIFO_DATA_DEPTH_MARGIN)) ? 1'b1:1'b0;
assign fifo_almost_empty = ((r_fifo_data_count <= FIFO_DATA_DEPTH_MARGIN)) ? 1'b1:1'b0;
FIFO_DATA_DEPTH_MARGIN为余量值;
8.1 设计源码
module Asynchronous_FIFO_npow2#(//BLOCK RAM address width parameter integer RAM_ADDR_WIDTH = 5,//FIFO true depthparameter integer FIFO_DATA_DEPTH = 6,//FIFO degth's marginparameter integer FIFO_DATA_DEPTH_MARGIN = 2,//FIFO compensate depth,(FIFO_DATA_DEPTH+FIFO_DEPTH_COMPLE) must be the data of power 2parameter integer FIFO_DEPTH_COMPLE = 2,parameter integer FIFO_ADDR_WIDTH = $clog2(FIFO_DATA_DEPTH+FIFO_DEPTH_COMPLE),parameter integer FIFO_DATA_WIDTH = 8)(input wire nrst,input wire clk_w,input wire wr_en,input wire [FIFO_DATA_WIDTH-1:0] wrdata,output wire fifo_full,output wire fifo_almost_full,output wire [FIFO_ADDR_WIDTH : 0] fifo_count_wr,input wire clk_r,input wire rd_en,output wire [FIFO_DATA_WIDTH-1:0] rddata,output wire fifo_empty,output wire [FIFO_ADDR_WIDTH : 0] fifo_count_rd,output wire fifo_almost_empty);/
/*w_ptr : the write data pointer (RAM Write address)r_ptr : the read data pointer (RAM Read address)
*/
reg [FIFO_ADDR_WIDTH:0] w_ptr;reg [FIFO_ADDR_WIDTH:0] r_ptr;wire [RAM_ADDR_WIDTH-1:0] ram_wr_addr;wire [RAM_ADDR_WIDTH-1:0] ram_rd_addr;assign ram_wr_addr = {{(RAM_ADDR_WIDTH-FIFO_ADDR_WIDTH){1'b0}},w_ptr[FIFO_ADDR_WIDTH-1:0]};assign ram_rd_addr = {{(RAM_ADDR_WIDTH-FIFO_ADDR_WIDTH){1'b0}},r_ptr[FIFO_ADDR_WIDTH-1:0]};wire fifo_wr;wire fifo_rd;/
/*fifo_wr : (RAM Write enable). valid only when fifo is not full and fifo data write enable fifo_rd : (RAM Read enable). valid only when fifo is not empty and fifo data read enable
*/
assign fifo_wr = (~fifo_full)&wr_en;assign fifo_rd = (~fifo_empty)&rd_en;wire [FIFO_ADDR_WIDTH : 0] w_ptr_gray;wire [FIFO_ADDR_WIDTH : 0] r_ptr_gray;wire [FIFO_ADDR_WIDTH : 0] w2r_ptr_gray;wire [FIFO_ADDR_WIDTH : 0] r2w_ptr_gray;wire [FIFO_ADDR_WIDTH : 0] w2r_ptr_bin;wire [FIFO_ADDR_WIDTH : 0] r2w_ptr_bin;reg [FIFO_ADDR_WIDTH : 0] w_fifo_data_count;reg [FIFO_ADDR_WIDTH : 0] r_fifo_data_count;assign fifo_count_wr = w_fifo_data_count;assign fifo_count_rd = r_fifo_data_count;always@(*)beginif(~nrst)beginr_fifo_data_count <= 'b0;endelsebeginif(w2r_ptr_bin < FIFO_DEPTH_COMPLE)beginr_fifo_data_count <= 'b0;endelse beginif((w2r_ptr_bin - FIFO_DEPTH_COMPLE)> r_ptr)r_fifo_data_count <= (w2r_ptr_bin - r_ptr - FIFO_DEPTH_COMPLE);elser_fifo_data_count <= (r_ptr - w2r_ptr_bin + FIFO_DEPTH_COMPLE);endendendalways@(*)beginif(~nrst)beginw_fifo_data_count <= 'b0;endelsebeginif(r2w_ptr_bin < FIFO_DEPTH_COMPLE)beginw_fifo_data_count <= 'b0;endelse beginif((r2w_ptr_bin - FIFO_DEPTH_COMPLE)> w_ptr)w_fifo_data_count <= (r2w_ptr_bin - w_ptr - FIFO_DEPTH_COMPLE);elsew_fifo_data_count <= (w_ptr - r2w_ptr_bin + FIFO_DEPTH_COMPLE);endendend
/
/*fifo_empty : valid only when r_ptr_gray(read ptr's gray code) is equal with w2r_ptr_gray(w_ptr's gray code Sync from write clock domain) fifo_full : valid only when w_ptr_gray's 2 MSB is equal with not( r2w_ptr_gray's 2 MSB) and w_ptr_gray's other bits equals r2w_ptr_gray's
*/
assign fifo_empty = (r_fifo_data_count == 'b0) ? 1:0;assign fifo_full = (w_fifo_data_count == FIFO_DATA_DEPTH) ? 1:0;assign fifo_almost_full = (w_fifo_data_count >= (FIFO_DATA_DEPTH - FIFO_DATA_DEPTH_MARGIN)) ? 1'b1:1'b0;assign fifo_almost_empty = ((r_fifo_data_count <= FIFO_DATA_DEPTH_MARGIN)) ? 1'b1:1'b0;
/
/*Gray code exchange logic
*/
Gray_encoder#(. DATA_WIDTH(FIFO_ADDR_WIDTH+1))Gray_encoder_inist0(. data_in(w_ptr+FIFO_DEPTH_COMPLE),. data_out(w_ptr_gray));Gray_encoder#(. DATA_WIDTH(FIFO_ADDR_WIDTH+1))Gray_encoder_inist1(. data_in(r_ptr+FIFO_DEPTH_COMPLE),. data_out(r_ptr_gray));always @(posedge clk_w,negedge nrst) beginif (~nrst) begin// resetw_ptr <= 'b0;endelse beginif(fifo_wr)w_ptr <= w_ptr + 'b1;elsew_ptr <= w_ptr ;endendalways @(posedge clk_r,negedge nrst) beginif (~nrst) begin// resetr_ptr <= 'b0;endelse beginif(fifo_rd)r_ptr <= r_ptr + 'b1;elser_ptr <= r_ptr ;endend
/
/*Synchronize logic across clock domains
*/
dff#(.DFF_LEVEL(2),.DATA_WIDTH(FIFO_ADDR_WIDTH+1)
)dff_inist0(. clk(clk_r),. din(w_ptr_gray),. dout(w2r_ptr_gray),. nrst(nrst));dff#(.DFF_LEVEL(2),.DATA_WIDTH(FIFO_ADDR_WIDTH+1)
)dff_inist1(. clk(clk_w),. din(r_ptr_gray),. dout(r2w_ptr_gray),. nrst(nrst));
/
/*read and write pointer decode logic
*/
Gray_decoder#(. DATA_WIDTH(FIFO_ADDR_WIDTH+1))Gray_decoder_inist0(. data_in(w2r_ptr_gray),. data_out(w2r_ptr_bin));Gray_decoder#(. DATA_WIDTH(FIFO_ADDR_WIDTH+1))Gray_decoder_inist1(. data_in(r2w_ptr_gray),. data_out(r2w_ptr_bin));blk_mem_gen_0 ram0(.addra (ram_wr_addr),.clka (clk_w),.dina (wrdata),.wea (1),.ena (fifo_wr),.addrb (ram_rd_addr),.clkb (clk_r),.doutb (rddata),.enb (fifo_rd));
endmodule
8.2 测试源码
module Asynchronous_FIFO_npow2_tb();parameter integer RAM_ADDR_WIDTH = 5;parameter integer FIFO_DATA_DEPTH = 14;parameter integer FIFO_DATA_DEPTH_MARGIN = 2;parameter integer FIFO_DEPTH_COMPLE = 2;parameter integer FIFO_DATA_WIDTH = 8;parameter integer FIFO_ADDR_WIDTH = $clog2(FIFO_DATA_DEPTH+FIFO_DEPTH_COMPLE);reg nrst;reg clk_w;reg wr_en;reg [FIFO_DATA_WIDTH-1:0] wrdata;wire fifo_full;wire fifo_almost_full;wire [FIFO_ADDR_WIDTH : 0] fifo_count_wr;reg clk_r;reg rd_en;wire [FIFO_DATA_WIDTH-1:0] rddata;wire fifo_empty;wire [FIFO_ADDR_WIDTH : 0] fifo_count_rd;wire fifo_almost_empty;integer i;initial beginclk_w = 0;forever begin#2 clk_w = ~clk_w;endendinitial beginclk_r = 0;#1forever begin#2 clk_r = ~clk_r;endendinitial beginnrst = 0;#4 nrst = 1;wr_en = 1;wrdata = 0;rd_en = 0;while(wrdata <= 14)begin#4 wr_en = 1;wrdata = wrdata + 1;endwr_en = 0;rd_en = 1;endAsynchronous_FIFO_npow2#(//BLOCK RAM address width . RAM_ADDR_WIDTH (RAM_ADDR_WIDTH),//FIFO true depth. FIFO_DATA_DEPTH (FIFO_DATA_DEPTH),. FIFO_DATA_DEPTH_MARGIN (FIFO_DATA_DEPTH_MARGIN),//FIFO compensate depth,(FIFO_DATA_DEPTH+FIFO_DEPTH_COMPLE) must be the data of power 2. FIFO_DEPTH_COMPLE (FIFO_DEPTH_COMPLE),. FIFO_DATA_WIDTH (FIFO_DATA_WIDTH))Asynchronous_FIFO_npow2_inist(. nrst(nrst),. clk_w(clk_w),. wr_en(wr_en),. wrdata(wrdata),. fifo_full(fifo_full),. fifo_almost_full(fifo_almost_full),. fifo_count_wr(fifo_count_wr),. clk_r(clk_r),. rd_en(rd_en),. rddata(rddata),. fifo_empty(fifo_empty),. fifo_almost_empty(fifo_almost_empty),. fifo_count_rd(fifo_count_rd));
endmodule
8.3 功能仿真结果
这里观察almost_empty和almost_empty信号,其假将空与假将满产生的原因与empty和full的假空假满相同;
各种FIFO硬件设计(FIFO概念、异步、同步、非2次幂深度FIFO)相关推荐
- 1311_硬件设计_ICT概念、应用以及优缺点学习小结
全部学习汇总: GreyZhang/g_hardware_basic: You should learn some hardware design knowledge in case hardware ...
- 任意深度同步FIFO设计总结(非2次幂)
同步FIFO属于是最基本的模块之一了,但是对于任意深度(非2次幂与2次幂均可以)的同步FIFO可能有些难度,但是只要了解格雷码的性质就能解决.接下来将介绍整个设计的思考流程,不单单是给出解决方法. 对 ...
- Asynchronous FIFO with gray code(异步FIFO verilog设计理念)
代码来自asic world 和paper" Simulation and Synthesis Techniques for Asynchronous FIFO Design",文 ...
- 基于Montgomery算法的高速、可配置 RSA密码IP核硬件设计系列(五)——模幂模块(抵抗侧信道攻击)模块的设计实现方案
基于Montgomery算法的高速.可配置RSA密码IP核硬件设计系列(五) 2.2 模幂模块设计(抵抗测信道攻击模块) 2.2.1 模幂模块及内部模块的功能 2.2.3 模幂各模块的实现方案 2.2 ...
- 硬件架构的艺术:同步FIFO设计
目录 1. 概述 2. 同步FIFO设计 2.1 同步FIFO结构 2.2 同步FIFO空满信号产生 2.2.1 时序逻辑产生空满 2.2.1.1 fifo满信号产生 2.2.1.2 fifo空信号产 ...
- (87)FPGA面试题-同步FIFO与异步FIFO区别?异步FIFO代码设计
1.1 FPGA面试题-同步FIFO与异步FIFO区别?异步FIFO代码设计 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题-同步FIFO与异步FIFO区 ...
- 异步FIFO的设计详解(格雷码计数+两级DFF同步)
文章目录 一.异步FIFO介绍 1.1.空满判断 1.2.跨时钟域问题 1.3.格雷码转换 1.4.格雷码计数器 二.代码code 一.异步FIFO介绍 FIFO有同步和异步两种,同步即读写时钟相 ...
- 74ls390设计任意进制计数器_异步FIFO:设计原理及Verliog源码
1. 异步FIFO的概念 异步FIFO为读取与写入采用不同的时钟,使用异步FIFO用于在不同的时钟域传输数据,主要用于跨时钟域传输多bit数据. 2. 异步FIFO的设计难点 同步异步信号,避免亚 ...
- FPGA基础知识极简教程(3)从FIFO设计讲起之同步FIFO篇
博文目录 写在前面 正文 FPGA/ASIC中的FIFO 同步FIFO的设计 参考资料 交个朋友 写在前面 个人博客首页 注:学习交流使用! 正文 FPGA/ASIC中的FIFO FIFO缓冲区如何用 ...
最新文章
- 栈的应用——迷宫的非递归解法
- 【Alertmanager】腾讯企业邮箱配置
- [云炬创业基础笔记] 第四章测试15
- windows下利用sox批量将PCM转为WAV
- ld-linux.so.2 重定向,2-Linux重定向和管道、Shell编程.doc
- 关于numpy mean函数的axis参数
- Android 系统(89)---ART
- python交叉验证结合线性回归_Python数据分析-线性回归、逻辑回归
- sharepoint2013列表实现项目级权限控制
- 20155303 2016-2017-2 《Java程序设计》第九周学习总结
- 【双十一精选】史上最强的宝贝详情页设计思路以及操作流程
- 计算机报名照片像素大小,证件照尺寸怎么修改-三种方法搞定证件照要求,让你不用再为图像分辨率和大小发愁!...
- 微信小程序如何快速累计独立访客(UV)不低于 1000
- 自恋的人脑袋有啥不一样?| 自恋型人格特质和前额脑结构
- 解读txt文件中的乱码
- No signing certificate “iOS Distribution“ found No “iOS Distribution“ signing certificate matching
- Guide哥|我写了四年博客,收获了20w+读者。我为什么推荐你写博客?
- 获取某一年的起始时间和结束时间
- 时下火热的 NFT 究竟有什么用?
- STM32F1XX的GPIO的8种工作模式以及GPIO的寄存器简介
热门文章
- 关于sqoop抽取数据时显示ERROR :/QueryResult.java‘ already exists 解读
- JVM性能优化一些概念简介
- 美团和大众点评早期分别以交易和用户评价进军团购行业
- 行进中换轮胎——万字长文解析美团和大众点评两大数据平台是怎么融合的
- linux云计算架构师:搭建DHCP服务和NTP网络时间同步
- 3.25 使用钢笔工具选择平滑形状的叶子 [原创Ps教程]
- hdcp key校验流程
- 2021 年第十三届四川省 ACM-ICPC 大学生程序设计竞赛(A/B/D/H/E/K/M/L)
- 图的广度优先搜索(BFS)和深度优先搜索(DFS)算法解析
- 1号店两年即被资本俘获 创始人离开仅是时间问题