赞
踩
异步FIFO的工作内容与同步FIFO类似,但是异步FIFO的控制并不像同步FIFO那么简单,因为异步FIFO工作在不同的时钟域,这将会带来一些问题,比如空满检测?是否还可以像同步FIFO通过计数器来判断?是否需要同步电路?这些问题将会在本章节一一讨论。
因为同步FIFO工作在一个时钟域,可以通过一个计数器来判断空满。
当同步FIFO写入一次数据,计数器加1.
当同步FIFO读出一次数据,计数器减1.
当同步FIFO同时进行一次读写操作,计数器的值不变。
这时就可以通过计数器的值来判断空满,当计数器的值为0,则表示同步FIFO中没有可读的数据,此时同步FIFO为空。
当计数器的值等于同步FIFO的深度时,则表示同步FIFO中已经写满了。
异步FIFO是否可以通过同步FIFO这种计数器的方式实现空满检测。假如可以,那么在异步FIFO中对计数器的描述
always@(posedge wclk or posedge rclk),这种代码综合过程中是没有实际的触发器与之对应的。
实际的触发器只有两种,一种是是对一个时钟边沿敏感—>always@(posedge clk)。另一种是除了对一个时钟边沿敏感,还有一个异步复位端—>always@(posedge clk or negedge rst_n)
可以通过比较写指针和读指针来判断空满,如下图所示,先对异步FIFO写入数据1,写两次,然后对异步FIFO读两次,把之前写进去的数据1都读出来,已经没有数据可读,所以此时异步FIFO处于空状态。注意此时写指针和读指针相等,都是指向地址为2的存储空间。
然后对异步FIFO写数据0,写6次,此时写指针重新指向地址0所对应的存储空间,此时的状态不是满状态,因为之前写入两次数据1已经被读出,此时地址0和地址1还是可以写入数据的,再对异步FIFO写数据0,写两次,此时异步FIFO中所有的存储空间都写入数据,并且没有一个数据是被读出过的,所以此时是满状态,此时也是写指针和读指针相等。
所以可以总结当读指针追上写指针时,异步FIFO处于空状态。
写指针追上读指针时,异步FIFO处于满状态。但是这种方法处理后,仍然有一个问题就是读写指针相等时,难以区分是空状态还是满状态。
针对上面指针比较空满出现的问题,有人提出,对指针最高位进行扩展,即指针加宽一位,当写指针超出FIFO深度时,这额外的一位就会发生改变。FIFO的深度决定了指针扩展前的宽度,而这扩展的一位与FIFO深度无关,是为了标志指针多转了一圈。
因此,当读写指针完全相同时,此时FIFO为空状态。
当读写指针最高位不同,其余位宽相同时,FIFO为满状态。
用扩展指针的方式分析上面的例子,如下图所示,空状态时,rptr=0010,wptr=0010,读写指针完全相同,为空状态。
满状态时,rptr=0010,wptr=1010,读写指针最高位不同,其余位宽相同,为满状态。
经过指针扩展,可以进行空满检测。但是异步FIFO读写时钟不同,而读写指针又是分属各自的时钟域,在进行空满检测的时候,是需要将读写指针进行跨时钟域处理的。
使用二进制的方式很容易出现错误,在跨时钟域的时候,相邻二进制地址变化时,不止一位发生变化。比如写指针从0111到1000跳变时,4位地址都可以发生改变,这样读时钟在进行写指针同步后得到的写指针可能是0000~1111中任意一个值,这些都是不可控的,出错的概率非常大。
直接扩展读写指针可能因为多位改变导致错误。因此采用格雷码的方式,利用格雷码每次只变化一位的特征,降低同步发生错误的概率。如下图所示,其中0~7为真实的FIFO地址,而8-15是指针多转一圈以后的地址,实际对应还是0-7对应的存储空间。此时应该注意,此时是按照格雷码方式编码,不能再用二进制的方式来判断空满。比如说位置6(0101)和位置9(1101),除最高位不同,其余位相同,应该是满状态,但是位置6(0101)对应的满状态应是14(1001)。
因此格雷码的检测标准为:
当最高位和次高位均相同,其余为相同,FIFO空
当最高位和次高位均相反,其余为相同,FIFO满
所以异步FIFO最终的空满检测方式为:将二进制指针转换为格雷码,用于另一时钟域接收,随后按照检测条件进行检测。
异步FIFO相比较同步FIFO外部端口引脚类似。
wclk为写时钟,rclk为读时钟。
wrst_n为写复位信号,rrst_n为读复位信号。
winc为写使能信号,rinc为读使能信号。
wdata[7:0]为输入数据。
rdata[7:0]为输出数据。
wfull为写满信号。
rempty为读空信号。
异步FIFO的内部电路主要分为5个部分,会在下面部分一一详细介绍。在这里主要介绍一下内部的信号。
wptr为写指针,写指针的位数比写地址多一位。
rptr为读指针,读指针的位数比读地址多一位。
waddr为写地址,为具体把数据写入FIFO_Memory的地址。
raddr为读地址,为具体读出FIFO_Memory中数据的地址。
wq2_rptr为将读指针同步到写时钟域的信号。
rq2_wptr为将写指针同步到读时钟域的信号。
第一部分就是异步FIFO的写指针、满信号和写地址产生的电路设计,详细电路图如下所示。
代码如下:
module wptr_wfull #( parameter ADDRSIZE=4) ( input winc, input wclk, input wrst_n, input [ADDRSIZE:0] wq2_rptr, output reg wfull, output reg [ADDRSIZE:0] wptr, output [ADDRSIZE-1:0] waddr ); wire full_value; wire [ADDRSIZE:0] wbinnext , wgraynext; reg [ADDRSIZE:0] wbin; //写地址产生 assign wbinnext=wbin+(winc&!wfull); always@(posedge wclk or negedge wrst_n) begin if(wrst_n==1’b0) wbin<=0; else wbin<=wbinnext; end assign waddr=wbin[ADDRSIZE-1:0]; //写指针产生 assign wgraynext=wbinnext^(wbinnext>>1); always @(posedge wclk or negedge wrst_n) begin if(wrst_n==1’b0) wptr <=0; else wptr<=wgraynext; end //满信号产生 assign full_value=wgraynext == ({~wq2_rptr[ADDRSIZE:ADDRSIZE-1],wq2_rptr[ADDRSIZE-2:0]}); always@(posedge wclk or negedge wrst_n) begin if(wrst_n==1’b0) wfull<=1’b0; else wfull<=full_value; end endmodule
第二部分就是异步FIFO的读指针、空信号和读地址产生的电路设计,电路图如第一部分类似,因此不再具体介绍。
代码如下:
module rptr_rempty #( parameter ADDRSIZE=4) ( input rinc, input rclk, input rrst_n, input [ADDRSIZE:0] rq2_wptr, output reg rempty, output reg [ADDRSIZE:0] rptr, output [ADDRSIZE-1:0] raddr ); wire empty_value; wire [ADDRSIZE:0] rbinnext , rgraynext; reg [ADDRSIZE:0] rbin; //读地址产生 assign rbinnext=rbin+(rinc&!rempty); always@(posedge rclk or negedge rrst_n) begin if(rrst_n ==1’b0) rbin <=0; else rbin <=rbinnext; end assign raddr=rbin[ADDRSIZE-1:0]; //读指针产生 assign rgraynext=rbinnext^(rbinnext>>1); always@(posedge rclk or negedge rrst_n) begin if(rrst_n ==1’b0) rptr <=0; else rptr<=rgraynext; end //空信号产生 assign empty_value=rgraynext == rq2_wptr; always@(posedge rclk or negedge rrst_n) begin if(rrst_n==1’b0) rempty<=1’b0; else rempty<=empty_value; end endmodule
第三部分是读指针的同步,将读指针打两拍同步到写时钟域去,具体电路如下图所示。
代码如下:
module sync_r2w #( parameter ADDRSIZE=4) ( input wclk, input wrst_n, input [ADDRSIZE:0] rptr, output reg [ADDRSIZE:0] wq2_rptr ); reg [ADDRSIZE:0] wq1_rptr; always@(posedge wclk or negedge wrst_n) begin if(wrst_n==1’b0) {wq2_rptr, wq1_rptr}<=2’b0; else {wq2_rptr, wq1_rptr}<= {wq1_rptr, rptr} ; end endmodule
第四部分是写指针的同步,将写指针打两拍同步到读时钟域去,电路图如第三部分类似,不再具体介绍。
代码如下:
module sync_w2r #( parameter ADDRSIZE=4) ( input rclk, input rrst_n, input [ADDRSIZE:0] wptr, output reg [ADDRSIZE:0] rq2_wptr ); reg [ADDRSIZE:0] rq1_wptr; always@(posedge rclk or negedge rrst_n) begin if(rrst_n==1’b0) {rq2_wptr, rq1_wptr}<=2’b0; else {rq2_wptr, rq1_wptr}<= {rq1_wptr, wptr}; end endmodule
第五部分是数据的读取,具体电路如2.2异步电路中标记为5的部分所示。
代码如下:
module fifomem #( parameter ADDRSIZE=4, parameter DATASIZE=8) ( input winc, input wclk, input wfull, input [ADDRSIZE-1:0] waddr , raddr, input [DATASIZE-1:0] wdata , output [DATASIZE-1:0] rdata ); localparam DEPTH=1<<ADDRSIZE; reg [DATASIZE-1:0] mem [0:DEPTH-1]; assign rdata=mem[raddr]; always@(posedge wclk) begin if(! wfull & winc) mem[waddr]<= wdata; end endmodule
将上述5个部分整合在一起就形成一个完整的异步FIFO。
module afifo #( parameter ADDRSIZE=4, parameter DATASIZE=8) ( input wclk, input rclk, input wrst_n, input rrst_n, input winc, input rinc, input [DATASIZE-1:0] wdata, output [DATASIZE-1:0] rdata, output rempty, output wfull ); wire [ ADDRSIZE:0] wq2_rptr; wire [ ADDRSIZE:0] wptr; wire [ ADDRSIZE-1:0] waddr; wire [ ADDRSIZE:0] rq2_wptr; wire [ ADDRSIZE:0] rptr; wire [ ADDRSIZE-1:0] raddr; wptr_wfull u1( .winc(winc), .wclk(wclk), .wrst_n(wrst_n), .wq2_rptr(wq2_rptr), .wfull(wfull), .wptr(wptr), .waddr(waddr) ); rptr_rempty u2( .rinc(rinc), .rclk(rclk), .rrst_n(rrst_n), .rq2_wptr(rq2_wptr), .rempty(rempty), .rptr(rptr), .raddr(raddr) ); sync_r2w u3( .wclk(wclk), .wrst_n(wrst_n), .rptr(rptr), .wq2_rptr(wq2_rptr) ); sync_w2r u4( .rclk(rclk), .rrst_n(rrst_n), .wptr(wptr), .rq2_wptr(rq2_wptr) ); fifomem u5( .winc(winc), .wclk(wclk), .wfull(wfull), .waddr(waddr), .raddr(raddr), .wdata(wdata), .rdata(rdata) ); endmodule
module afifo_tb(); reg wclk; reg rclk; reg wrst_n; reg rrst_n; reg winc; reg rinc; reg [7:0] wdata; wire [7:0] rdata; wire rempty; wire wfull; afifo u6( .wclk(wclk), .rclk(rclk), .wrst_n(wrst_n), .rrst_n(rrst_n), .winc(winc), .rinc(rinc), .wdata(wdata), .rdata(rdata), .rempty(rempty), .wfull(wfull) ); initial begin //设置写时钟,写周期是20ns,50Mhz wclk=0; forever #10 wclk=~wclk; end initial begin //设置读时钟,读周期是10ns,100Mhz rclk=0; forever #5 rclk=~rclk; end initial begin wrst_n=1'b0; //写复位 rrst_n=1'b0; //读复位 winc =1'b0; //写无效 rinc =1'b0; //读无效 wdata=0; //初始写数据为0 #28 wrst_n=1'b1; //松开写复位 rrst_n=1'b1; //松开读复位 winc =1'b1; //写有效 wdata=1; //输入数据为1 @(posedge wclk);//写入数据 repeat(7) //接着写入2,3,4,5,6,7,8这些数据 begin #18; wdata=wdata+1'b1; @(posedge wclk); end #18 wdata=wdata+1'b1; //此时异步FIFO已经写满了,在往同步FIFO中写数据8 //8这个数据不会被写进 @(posedge rclk); #8 rinc=1’b1; //读使能,写无效 winc=1'b0; @(posedge rclk); //第一个读出的数为1 repeat(7) //读取剩余的数 begin @(posedge rclk); end #2; rinc=1‘b0; //结束读操作 end endmodule
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。