赞
踩
本文来源:https://blog.csdn.net/weixin_43067657/article/details/87790012
如上图所示的同步模块synchronize to write clk,其作用是把读时钟域的指针rptr采集到写时钟wr_clk域,然后和写时针wptr进行比较从而产生或撤销写写满标志wfull;类似地,同步模块synchronize to read clk的作用是把写时钟域的写指针wptr采集到读时钟域,然后和读指针rptr进行比较从而产生或撤销读空标志位rempty。
另外还有写指针wptr和写满标志位wfull产生模块读指针和读空标志位rempty产生模块 ,以及双端口存储RAM模块。
当FIFO为满或为空时,写入指针和读取指针都是相等的。但我们需要将“满”与“空”区分,当FIFO工作时,写指针在前,读指针紧跟写指针。当FIFO为满时,写指针往前移动,返回并等于后面紧跟的读指针,这就是所谓的套圈。这个时候我们再增加1bit给读写指针,可以通过这个bit为0还是1来显示“满”或“空”。
二进制计数
异步FIFO读写指针需要在数学上的操作和比较才能产生准确的空满标志位,但由于读写指针属于不同的时钟域及读写时钟相位关系的不确定性,同步模块采集另一时钟域的指针时,此指针有可能正处在跳变的过程中。
上图中,rd_ptr2sync3和4以及4和5之间的中间态是由于到各寄存器的时钟rd_clk存在偏差而引起的。二进制的递增操作,在大多数情况下都会有两位或者两位以上的bit位在同一个递增操作内发生变化,但由于实际电路中会存在时钟偏差和不同的路径延时,二进制计数器在自增时会不可避免地产生错误的中间结果。
格雷码计数
格雷码一个最大的特点就是在递增或递减的过程中,每次只变化一位,这是它最大的优点。同时它也有自己的局限性,那么就是循环计数深度必须是2的n次幂(这个例子中n=4),否则就失去了每次只变化一位的特性。通过观察格雷码相邻位每次只有1位发生变化,上下两部分,除最高位相反,其余镜像对称。
7——8,格雷码从0100——1100,只有最高位发生变化其余位相同;
6——9,格雷码从0101——1101,只有最高位发生变化其余位相同;
那么进行空满判断的时候,就不是看最高位了,因为7-8的最高位不同,而其他位相同,在之前的判断中就会被判断为“满”,这就出现误判了。所以,用格雷码来判断时,还要考虑次高位。
当最高位和次高位相同,其余位相同认为是读空。
当最高位和次高位不同,其余位相同认为是写满。
读时针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO的读写时钟域不同,是异步的。如果将rclk的读指针和wclk的写指针直接比较肯定是错误的,我们需要进行同步处理进行比较。
方案:格雷码+两级寄存器 同步
(1)将写时钟域的写指针同步到读时钟域,将同步后的写指针与读时钟域的读指针进行比较产生读空信号;
(2)将读时钟域的读指针同步到写时钟域,将同步后的读指针与写时钟域的写指针进行比较产生写满信号。
两点注意:
1、打两拍(两级寄存器同步):输入信号来自异步时钟域(比如FPGA芯片外部的输入),必须寄存两拍。第一拍将输入信号同步化,同步化后的输出可能带来建立/保持时间的冲突,产生亚稳态。需要再寄存一拍,减少(注意是减少)亚稳态带来的影响。
2、同步的指针和两者比较的指针都是格雷码指针!
二进制数 10110
二进制数右移1位,空位补0 01011
异或运算11101
这样就可以实现二进制到格雷码的转换了,总结就是移位并且异或,代码实现如下:
assign wgraynext=(wbinnext>>1)^wbinnext;assign rgraynext=(rbinnext>>1)^rbinnext;
异步FIFO,宽度8,深度16,地址4bit,多加1bit判断空满
module fifo#(parameter DSIZE=8, parameter ASIZE=4)( output [DSIZE-1:0] rdata, output wfull, output rempty, input [DSIZE-1:0] wdata, input wclk, wrst, wr_en, input rclk, rrst, rd_en);wire [ASIZE-1:0] waddr, raddr;wire [ASIZE:0] rptr, wptr, rq2_wptr, wq2_rptr;fifomem i1( .wdata(wdata), .waddr(waddr), .raddr(raddr), .wr_en(wr_en), .wclk(wclk), .wfull(wfull), .rdata(rdata));sync_r2w i2(.wclk(wclk),.wrst(wrst),.rptr(rptr),.wq2_rptr(wq2_rptr));sync_w2r i3(.rclk(rclk),.rrst(rrst),.wptr(wptr).rq2_wptr(rq2_wptr),);wptr_full i4( //将sync_r2w.v同步后的读指针与wclk时钟域的写指针进行比较生成写满信号.wclk(wclk),.wrst(wrst),.wr_en(wr_en),.waddr(waddr),.wfull(wfull),.wptr(wptr),.wq2_rptr(wq2_rptr));rptr_empty i5(.rd_en(rd_en),.rclk(rclk),.rrst(rrst),.rq2_wptr(rq2_wptr),.rempty(rempty),.raddr(raddr),.rptr(rptr));endmodule
module fifomem#(parameter DATASIZE=8, parameter ADDRSIZE=4)( input [DATASIZE-1:0] wdata,input [ADDRSIZE-1:0] waddr, raddr,input wr_en, wfull, wclk,output [DATASIZE-1:0] rdata);localparam DEPTH=1<reg [DATASIZE-1:0] ram [DEPTH-1:0];assign rdata = ram [raddr];always @(posedge wclk) begin if(wr_en && !wfull) ram[waddr]<=wdata; end endmodule
## sync_r2w.v
//将rclk中的格雷码读指针打两拍同步到wclkmodule sync_r2w#(parameter ADDRSIZE=4)( input [ADDRSIZE:0] rptr, //格雷码形式的读指针 input wclk, wrst, output reg [ADDRSIZE:0] wq2_rptr //同步到写时钟域的打两拍后的读指针);reg [ADDRSIZE:0] wq1_rptr; //同步到写时钟域的打一拍后的读指针always @(posedge wclk or negedge wrst) begin if(!wrst) begin wq1_rptr<=0; wq2_rptr<=0 end else begin wq1_rptr<=rptr; wq2_rptr<=wq1_rptr; endendendmodule
module sync_w2r#(parameter ADDRSIZE=4)( input [ADDRSIZE:0] wptr, //格雷码形式的写指针 input rclk, rrst, output reg[ADDRSIZE:0] rq2_wptr //同步到读时钟域的打两拍的写指针);reg [ADDRSIZE:0] rq1_wptr; //同步到读时钟域的打一拍的写指针always @(posedge rclk or negedge rrst) begin if(!rrst) begin rq1_wptr<=0; rq2_wptr<=0; end else begin rq1_wptr<=wptr; rq2_wptr<=rq1_wptr; end endendmodule
//产生写满信号module wptr_full#(parameter ADDRSIZE=4)( input [ADDRSIZE:0] wq2_rptr, //同步后的读指针 input wclk, wrst, wr_en, output reg wfull, output reg [ADDRSIZE:0] wptr, //格雷码形式写指针 output [ADDRSIZE-1:0] waddr //二进制写指针);wire [ADDRSIZE:0] wbinnext, wgraynext;reg [ADDRSIZE:0] wbin;//将二进制的写指针与格雷码的写指针同步always @(posedge wclk or negedge wrst) begin if (!wrst) begin wbin<=0; wptr<=0; else begin wbin<=wbinnext; //直接作为存储实体的地址 wptr<=wgraynext; //输出到sync_w2r.v模块,被同步到rdclk模块 end endassign waddr= wbin [ADDRSIZE-1:0]; //生成二进制写地址assign wbinnext= wbin+(wr_en & ~wfull); //wbinnext是由assign关键字指定的,必须是wire类型assign wgraynext= (wbinnext>>1)^wbinnext;assign wfull_val =(wgraynext=={~wq2_rptr[ADDRSIZE: ADDRSIZE-1], wq2_rptr[ADDRSIZE-2:0]});always @(posedge wclk or negedge wrst) begin if (!wrst) wfull<=1’b0; else wfull<=wfull_val; endendmodule
//将sync_w2r.v同步后的写指针与rclk时钟域的读指针进行比较生成读空信号module rptr_empty#(parameter ADDRSIZE=4)( input rd_en, rrst, rclk, input [ADDRSIZE:0] rq2_wptr, //同步后的写指针 output reg rempty, output [ADDRSIZE-1:0] raddr, output reg [ADDRSIZE:0] rptr);wire [ADDRSIZE:0] rbinnext, rgraynext;reg [ADDRSIZE:0] rbin;//将二进制的读指针与格雷码进制的读指针同步always @(posedge rclk or negedge rrst) begin if (!rrst) begin rbin<=0; rptr<=0; end else begin rbin<=rbinnext; rptr<=rgraynext; end endassign raddr=rbin [ADDRSIZE-1:0]; //生成二进制读地址assign rbinnext= rbin+(rd_en & ~rempty); //不空且有读请求的时候读指针加1assign rgraynext= (rbinnext>>1)^rbinnext;assign rempty_val=(rgraynext==rq2_wptr);always @(posedge rclk or negedge rrst) begin if (!rrst) rempty<=1’b1; else rempty<=rempty_val; endendmodule
1.异步FIFO的设计思路及verilog代码
https://blog.csdn.net/weixin_43067657/article/details/87790012
2.异步fifo的设计(FPGA)
https://www.cnblogs.com/aslmer/p/6114216.html
3.跨时钟域信号传输(二)
https://www.cnblogs.com/IClearner/p/6579754.html
4.FPGA基础知识20(FPGA设计异步时钟处理分类及百度文库资料)
https://blog.csdn.net/Times_poem/article/details/51915066
5.《Verilog HDL高级数字设计》中异步FIFO采用脉冲同步器的思路错误
https://blog.csdn.net/u014045393/article/details/100615272?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522158402494119726867857048%2522%252C%2522scm%2522%253A%252220140713.130056874..%2522%257D&request_id=158402494119726867857048&biz_id=0&utm_source=distribute.pc_search_result.none-task(FIFO非教条)
扫
码
关
注
一
起
畅
聊
深耕在FPGA 扎根于视频领域
卓越于神经网络
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。