赞
踩
FIFO即First In First Out,是一种先进先出数据存储、缓冲器,我们知道一般的存储器是用外部的读写地址来进行读写,而FIFO这种存储器的结构并不需要外部的读写地址而是通过自动的加一操作来控制读写,这也就决定了FIFO只能顺序的读写数据。下面我们就介绍一下 同步FIFO和异步FIFO。
同步FIFO,读和写应用同一个时钟。它的作用一般是做交互数据的一个缓冲,也就是说它的主要作用就是一个buffer。
异步FIFO,读写应用不同的时钟,它有两个主要的作用,一个是实现数据在不同时钟域进行传递,另一个作用就是实现不同数据宽度的数据接口。
同步FIFO和异步FIFO略有不同,下面的参数适用于两者。
同步FIFO和异步FIFO的最主要的不同就体现在空满标志产生的方式上,由此引出两者一些不同的参数。
同步FIFO
异步FIFO
FIFO主要的设计难点在于如何产生空满标志,在同步FIFO中,我们定义一个计数器,当计数器的值为0时,产生空标志,当计数器的值为FIFO的深度时,产生满标志。基于以上的思想,可以将同步FIFO划分为以下几个模块:write、read、count、RAM。
同步FIFO主要划分为四个模块,RAM模块是用来读取和写入数据;write模块是用来产生写地址;read模块是用来产生读地址;count模块是用来产生空满标志符,每写入一位数,count加一,每读出一位数,count减一。
下面是各个模块的连接框图:
源文件
- module FIFO_sync(
- clk,
- rst,
- w_en,
- r_en,
- data_in,
- data_out,
- count,
- full,
- empty
- );
-
- parameter FIFO_data_size=3,
- FIFO_addr_size=2;
-
-
- input clk,rst;
- input w_en,r_en;
- input[FIFO_data_size-1:0] data_in;
- output[FIFO_data_size-1:0] data_out;
- output full,empty;
- output[FIFO_addr_size:0]count;
-
- reg [FIFO_data_size-1:0] data_out;
- reg [FIFO_addr_size:0]count;
- reg [FIFO_addr_size-1:0]w_addr,r_addr;
- reg [FIFO_data_size-1:0]mem[{FIFO_addr_size{1'b1}}:0];
- integer i;
- //memory的初始化以及写操作
- always@(posedge clk or negedge rst)
- begin
- if(!rst)
- begin
- w_addr<=0;
- for(i=0;i<={FIFO_addr_size{1'b1}};i=i+1)
- mem[i]<={FIFO_data_size{1'b0}};
- end
- else if(w_en&(~full))
- begin
- mem[w_addr]<=data_in;
- w_addr<=w_addr+1;
- end
- end
- //读操作
- always@(posedge clk or negedge rst)
- begin
- if(!rst)
- begin
- data_out<={(FIFO_data_size-1){1'b0}};
- r_addr<=0;
- end
- else if(r_en&(~empty))
- begin
- data_out<=mem[r_addr];
- r_addr<=r_addr+1;
- end
- end
-
- //count产生空满标志符
- always@(posedge clk or negedge rst)
- begin
- if(!rst)
- count<=0;
- else if(((w_en)&(~full))&(~((r_en)&(~empty))))
- count<=count+1;
- else if(((r_en)&(~empty))&(~((w_en)&(~full))))
- count<=count-1;
- end
-
- assign empty=(count==0);
- assign full=(count=={FIFO_addr_size{1'b1}}+1);
- endmodule
测试代码
- `timescale 1ns/1ns
- module FIFO_sync_top;
-
- reg clk,rst,w_en,r_en;
- reg[2:0]data_in;
-
- wire[2:0]count;
- wire[2:0]dtat_out;
-
- reg[2:0]i;
-
- initial
- begin
- clk=0;
- rst=1;
- data_in=3'b000;
- w_en=0;
- r_en=0;
- #25
- rst=0;
- #50
- rst=1;
- #25
- w_en=1;
- #100
- r_en=1;
- #100
- w_en=0;
- r_en=0;
- #100
- w_en=1;
- #400
- r_en=1;
- end
-
- initial
- begin
- for(i=0;i<=50;i=i+1)
- #100 data_in=i;
- end
-
- always
- #50 clk=~clk;
- FIFO_sync #(.FIFO_data_size(3),.FIFO_addr_size(2)) ut(
- .clk(clk),
- .rst(rst),
- .data_in(data_in),
- .data_out(data_out),
- .w_en(w_en),
- .r_en(r_en),
- .count(count),
- .full(full),
- .empty(empty)
- );
-
-
- endmodule
异步FIFO的设计难点在于空满标志符的产生,由于异步FIFO的读写是用不同的时钟来控制的,所以不能采用计数器的方法来产生空满标志符,就好像同一个变量不能再两个always块里赋值一样,所以我们必须寻求新的方法来产生空满标志符。
我们知道FIFO的状态是满还是空,他们的相同的判断条件都是w_addr=r_addr,但到底是空还是满我们还不能确定。在这里介绍一种方法来判断空满状态。我们设定一个指针r_pointer_bin,w_pointer_bin,宽度为[FIFO_addr_size:0],也就是说比传统的地址多一位,我们就用这多出来的一位做空满判断。
如下例子所示:FIFO_addr_size=2;FIFO_data_size=3;
将一个时钟域上的指针r_pointer_bin/w_pointer_bin同步到另一个时钟域,如果数据用二进制的方式进行同步的话就会出现多位数据同时跳变的问题,比如3'b011到3'b100即3到4跳变会引起多位数据的改变,这样会大大增加出错的概率。Gray 码就很好的解决了上述问题,gray码相邻数据只有一位跳变,这样就大大降低了数据出错的概率。下面 以一个例子介绍一下二进制码向格雷码的转化的算法。
在不同时钟域进行数据交换的时候我们一般采用格雷码的数据形式进行数据传递,这样能很大程度上降低出错的概率。
引入格雷码同时也引入一个问题,就是数据空满标志的判断不再是二进制时候的判断标准。
同时由于格雷码的引入,使得FIFO的深度只能是2的幂次方。
我们知道满状态以后数据就不能进行写入,空状态以后数据就不能进行读出。由此,我们在write模块进行满状态的判断,在read模块进行空状态的判断。
两拍延时的数据同步对空满标志产生的影响
由此信号r_pointer_gray经过两级D触发器,就会有两拍的延时形成r_pointer_gray_sync信号,所以在进行比较的时候就不是实时的r_pointer_gray与w_pointer_gray进行比较,而是两拍之前的r_pointer_gray即r_pointer_gray_sync与此刻的w_pointer_gray进行比较。那么问题就来了这与我们的本意其实是不相符的,其实是这样的,这是一种最坏情况的考虑,将r_pointer_gray_sync与w_pointer_gray相比较是为了产生full信号,在用于数据同步的这两拍里面有可能再进行读操作,所以用于比较时的读地址一定小于或等于当前的读地址,就算此刻产生full信号,其实FIFO有可能还没有满。这也就为设计留了一些设计的余量。同理,就算有empty信号的产生,FIFO有可能还有数据。这种留余量的设计在实际的工程项目中是很常见的。
异步FIFO将模块划分为4个部分,RAM、write_full、read_empty、synchronization。RAM根据读写地址进行数据的写入和读出,write_full根据clk_w产生写地址和full信号,read_empty根据clk_r产生读地址和empty信号,synchronization用于同步w_pointer_gray到读时钟域或者同步r_pointer_gray到写时钟域。
下面是各个模块的连接图:
源文件
- module FIFO_async(
- clk_w,
- rst_w,
- w_en,
- clk_r,
- rst_r,
- r_en,
- data_in,
- data_out,
- empty,
- full
- );
- parameter FIFO_data_size=6;
- parameter FIFO_addr_size=5;
-
- input clk_w,rst_w,w_en;
- input clk_r,rst_r,r_en;
- input[FIFO_data_size-1:0]data_in;
- output[FIFO_data_size-1:0]data_out;
- output empty,full;
-
- wire[FIFO_addr_size:0]r_pointer_gray_sync,w_pointer_gray_sync;
- wire[FIFO_addr_size:0]r_pointer_gray,w_pointer_gray;
- wire[FIFO_addr_size-1:0]w_addr,r_addr;
-
- RAM #(FIFO_data_size,FIFO_addr_size)
- I0(.clk_w(clk_w),
- .rst_w(rst_w),
- .clk_r(clk_r),
- .rst_r(rst_r),
- .full(full),
- .empty(empty),
- .w_en(w_en),
- .r_en(r_en),
- .w_addr(w_addr),
- .r_addr(r_addr),
- .data_in(data_in),
- .data_out(data_out));
-
- synchronization #(FIFO_addr_size)
- I1(.clk(clk_r),
- .rst(rst_r),
- .din(w_pointer_gray),
- .dout(w_pointer_gray_sync));
-
- synchronization #(FIFO_addr_size)
- I2(.clk(clk_w),
- .rst(rst_w),
- .din(r_pointer_gray),
- .dout(r_pointer_gray_sync));
-
- write_full #(FIFO_addr_size)
- I3(.clk_w(clk_w),
- .rst_w(rst_w),
- .w_en(w_en),
- .r_pointer_gray_sync(r_pointer_gray_sync),
- .w_pointer_gray(w_pointer_gray),
- .w_addr(w_addr),
- .full(full));
-
- read_empty #(FIFO_addr_size)
- I4(.clk_r(clk_r),
- .rst_r(rst_r),
- .r_en(r_en),
- .w_pointer_gray_sync(w_pointer_gray_sync),
- .r_pointer_gray(r_pointer_gray),
- .r_addr(r_addr),
- .empty(empty));
-
- endmodule
RAM
- module RAM(
- clk_w,
- rst_w,
- clk_r,
- rst_r,
- full,
- empty,
- w_en,
- r_en,
- r_addr,
- w_addr,
- data_in,
- data_out
- );
-
- parameter FIFO_data_size=3,
- FIFO_addr_size=2;
-
- input clk_w,rst_w;
- input clk_r,rst_r;
- input w_en,r_en;
- input full,empty;
- input [FIFO_addr_size-1:0]w_addr,r_addr;
- input [FIFO_data_size-1:0]data_in;
- output[FIFO_data_size-1:0]data_out;
- reg[FIFO_data_size-1:0]data_out;
-
- reg[FIFO_data_size-1:0]mem[{FIFO_addr_size{1'b1}}:0];
- integer i;
- always@(posedge clk_w or negedge rst_w)
- begin
- if(!rst_w)
- for(i=1;i<=FIFO_data_size;i=i+1)
- mem[i]<={FIFO_data_size{1'b0}};
- else if((w_en==1)&&(full==0))
- mem[w_addr]<=data_in;
- end
-
- always@(posedge clk_r or negedge rst_r)
- begin
- if(!rst_r)
- data_out<={(FIFO_data_size-1){1'b0}};
- else if((r_en==1)&&(empty==0))
- data_out<=mem[r_addr];
- end
-
- endmodule
write_full
- module write_full(
- clk_w,
- rst_w,
- w_en,
- r_pointer_gray_sync,
- //w_pointer_bin,
- w_pointer_gray,
- w_addr,
- full
- );
-
- parameter FIFO_addr_size=2;
-
- input clk_w,rst_w,w_en;
- input [FIFO_addr_size:0]r_pointer_gray_sync;
- output full;
- output [FIFO_addr_size-1:0]w_addr;
- output [FIFO_addr_size:0]w_pointer_gray;
- reg [FIFO_addr_size:0]w_pointer_bin;
-
- wire [FIFO_addr_size:0]w_pointer_gray;
- wire [FIFO_addr_size-1:0]w_addr;
-
- always@(posedge clk_w or negedge rst_w)
- begin
- if(!rst_w)
- w_pointer_bin<={(FIFO_addr_size){1'b0}};
- else if((w_en==1)&&(full==0))
- w_pointer_bin<=w_pointer_bin+1;
- end
-
- assign w_pointer_gray=(w_pointer_bin>>1)^w_pointer_bin;
- assign w_addr=w_pointer_bin[FIFO_addr_size-1:0];
- assign full=w_pointer_gray=={~r_pointer_gray_sync[FIFO_addr_size:FIFO_addr_size-1],r_pointer_gray_sync[FIFO_addr_size-2:0]}? 1:0;
- endmodule
read_empty
- module read_empty(
- clk_r,
- rst_r,
- r_en,
- w_pointer_gray_sync,
- // r_pointer_bin,
- r_pointer_gray,
- r_addr,
- empty
- );
-
- parameter FIFO_addr_size=2;
-
- input clk_r,rst_r,r_en;
- input [FIFO_addr_size:0]w_pointer_gray_sync;
- output empty;
- output [FIFO_addr_size-1:0]r_addr;
- output [FIFO_addr_size:0]r_pointer_gray;
- reg [FIFO_addr_size:0]r_pointer_bin;
-
- wire [FIFO_addr_size:0]r_pointer_gray;
- wire [FIFO_addr_size-1:0]r_addr;
-
- always@(posedge clk_r or negedge rst_r)
- begin
- if(!rst_r)
- r_pointer_bin<={(FIFO_addr_size){1'b0}};
- else if((r_en==1)&&(empty==0))
- r_pointer_bin<=r_pointer_bin+1;
- end
-
- assign r_pointer_gray=(r_pointer_bin>>1)^r_pointer_bin;
- assign r_addr=r_pointer_bin[FIFO_addr_size-1:0];
- assign empty=r_pointer_gray==w_pointer_gray_sync?1:0;
- endmodule
synchroization
- module synchronization(
- clk,
- rst,
- din,
- dout
- );
-
- parameter FIFO_addr_size=2;
-
- input clk,rst;
- input[FIFO_addr_size:0] din;
- output[FIFO_addr_size:0] dout;
- reg[FIFO_addr_size:0] dout;
-
- reg [FIFO_addr_size:0] dout1;
-
- always@(posedge clk or negedge rst)
- begin
- if(!rst)
- begin
- dout<={(FIFO_addr_size+1){1'b0}};
- dout1<={(FIFO_addr_size+1){1'b0}};
- end
- else
- begin
- dout1<=din;
- dout<=dout1;
- end
- end
-
- endmodule
测试文件
- `timescale 1ns/1ns
- module FIFO_async_top;
-
- parameter FIFO_data_size=3,
- FIFO_addr_size=2;
-
- reg clk_r,rst_r,w_en,r_en,clk_w,rst_w;
- reg[FIFO_data_size-1:0]data_in;
- wire[FIFO_addr_size-1:0]data_out;
- wire empty,full;
-
- reg[FIFO_data_size-1:0]i;
-
- initial
- begin
- clk_w=0;
- rst_w=1;
- data_in={FIFO_data_size{1'b0}};
- #15
- rst_w=0;
- #20
- rst_w=1;
- end
- initial
- begin
- clk_r=0;
- rst_r=1;
- r_en=0;
- #25
- rst_r=0;
- #50
- rst_r=1;
- end
-
- initial
- begin
- w_en=0;
- #450
- w_en=1;
- #400
- w_en=0;
- #750
- w_en=1;
- end
-
- initial
- begin
- r_en=0;
- #900
- r_en=1;
- #400
- r_en=0;
- #300
- r_en=1;
- end
-
- initial
- begin
- for(i=0;i<=50;i=i+1)
- #100 data_in=i;
- end
-
- always
- #25 clk_w=~clk_w;
- always
- #50 clk_r=~clk_r;
- FIFO_async #(.FIFO_data_size(FIFO_data_size),.FIFO_addr_size(FIFO_addr_size))
- u1(.clk_w(clk_w),
- .rst_w(rst_w),
- .w_en(w_en),
- .clk_r(clk_r),
- .rst_r(rst_r),
- .r_en(r_en),
- .data_in(data_in),
- .data_out(data_out),
- .empty(empty),
- .full(full)
- );
-
- endmodule
其实FIFO的深度可大可小,并没有一个具体的公式能够精确计算出FIFO深度的大小。在FIFO实际工作中,其数据的满/空标志可以控制数据的继续写入或读出。在一个具体的应用中也不可能由一些参数算数精确的所需FIFO深度为多少,这在写速度大于读速度的理想状态下是可行的,但在实际中用到的FIFO深度往往要大于计算值。一般来说根据电路的具体情况,在兼顾系统性能和FIFO成本的情况下估算一个大概的宽度和深度就可以了。而对于写速度慢于读速度的应用,FIFO的深度要根据读出的数据结构和读出数据的由那些具体的要求来确定。下面我们以一道简单的题目来估算一下FIFO的深度。
一个8bit宽的异步FIFO,输入时钟为100MHz,输出时钟为95MHz,设一个package为4Kbit,且两个package之间的发送间距足够大。问异步FIFO的深度。
解答:8bit位宽的异步FIFO,一个package的大小为4※1024/8=512Word,100MHz的输入时钟,传送一个Word需要的时间为1/100MHz,则发送一个package需要的时间T=512/100MHz,95MHz的输出时钟,接受一个Word需要的时间为1/95MHz,发送一个package的时间所能接受的数据量为(512※95)/100word=486.4word,所以FIFO的深度至少为512-486.4=25.6=26。
原文作者:猪肉白菜_125
原文地址:FIFO - 猪肉白菜_125 - 博客园(版权归原文作者所有,侵权留言联系删除)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。