基本原理:
1.读写指针的工作原理
写指针:总是指向下一个将要被写入的单元,复位时,指向第1个单元(编号为0)。
读指针:总是指向当前要被读出的数据,复位时,指向第1个单元(编号为0).
2.FIFO的“空”/“满”检测
FIFO设计的关键:产生可靠的FIFO读写指针和生成FIFO“空”/“满”状态标志。
当读写指针相等时,表明FIFO为空,这种情况发生在复位操作时,或者当读指针读出FIFO中最后一个字后,追赶上了写指针时,如下图所示:
当读写指针再次相等时,表明FIFO为满,这种情况发生在,当写指针转了一圈,折回来(wrapped around)又追上了读指针,如下图:
为了区分到底是满状态还是空状态,可以采用以下方法:
方法1:在指针中添加一个额外的位(extra bit),当写指针增加并越过最后一个FIFO地址时,就将写指针这个未用的MSB加1,其它位回零。对读指针也进行同样的操作。此时,对于深度为2n的FIFO,需要的读/写指针位宽为(n+1)位,如对于深度为8的FIFO,需要采用4bit的计数器,0000~1000、1001~1111,MSB作为折回标志位,而低3位作为地址指针。
- 如果两个指针的MSB不同,说明写指针比读指针多折回了一次;如r_addr=0000,而w_addr = 1000,为满。
- 如果两个指针的MSB相同,则说明两个指针折回的次数相等。其余位相等,说明FIFO为空;
3. 二进制FIFO指针的考虑
将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出现问题,因为采用二进制计数器时所有位都可能同时变化,在同一个时钟沿同步多个信号的变化会产生亚稳态问题。而使用格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到gray码的转换电路,将地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行对比,作为空满状态的检测。
使用gray码解决了一个问题,但同时也带来另一个问题,即在格雷码域如何判断空与满。
对于“空”的判断依然依据二者完全相等(包括MSB);
而对于“满”的判断,如下图,由于gray码除了MSB外,具有镜像对称的特点,当读指针指向7,写指针指向8时,除了MSB,其余位皆相同,不能说它为满。因此不能单纯的只检测最高位了,在gray码上判断为满必须同时满足以下3条:
- wptr和同步过来的rptr的MSB不相等,因为wptr必须比rptr多折回一次。
- wptr与rptr的次高位不相等,如上图位置7和位置15,转化为二进制对应的是0111和1111,MSB不同说明多折回一次,111相同代表同一位置。
- 剩下的其余位完全相等。
5.总体实现
以上内容参考http://www.cnblogs.com/BitArt/archive/2013/04/10/3010073.html,非原创。
源码在原来的基础上进行改编,如下:
1 module Asyn_FIFO(data_out, full, empty, data_in, wen, wclk, wrst,ren, rclk, rrst); 2 parameter datasize = 8; 3 parameter addrsize = 4; 4 5 input [datasize-1:0]data_in; 6 input wen,ren,wclk,rclk,wrst,rrst; 7 output [datasize-1:0]data_out; 8 output empty,full; 9 10 reg empty,full; 11 wire [datasize-1:0]data_out; 12 13 wire [addrsize-1:0]raddr,waddr; // 14 wire [addrsize:0]rbinnext,wbinnext,rptrnext,wptrnext; 15 wire empty_val,full_val; 16 reg [addrsize:0]rbin,wbin,rptr,wptr,rptr1,rptr2,wptr1,wptr2; 17 reg [addrsize-1:0]memory[0:(addrsize<<1)-1]; 18 19 //双口RAM 20 assign data_out=memory[raddr]; 21 always @(posedge wclk) 22 begin if(wen&&!full) memory[waddr]<=data_in;end 23 24 //同步wptr指针 25 always @(posedge rclk or negedge rrst) 26 begin if(!rrst) {rptr2,rptr1}<=0; 27 else {rptr2,rptr1}<={rptr1,wptr}; 28 end 29 30 //同步rptr指针 31 always @(posedge wclk or negedge wrst) 32 begin if(!wrst) {wptr2,wptr1}<=0; 33 else {wptr2,wptr1}<={wptr1,rptr}; 34 end 35 36 //产生raddr信号和empty信号 37 always @(posedge rclk or negedge rrst) 38 begin if(!rrst) {rbin,rptr}<=0; 39 else {rbin,rptr}<={rbinnext,rptrnext}; 40 end 41 assign raddr=rbin[addrsize-1:0]; 42 assign rbinnext=rbin+(ren&&~empty); 43 assign rptrnext=(rbinnext>>1)^rbinnext; //生成raddr 44 assign empty_val=(rptrnext==rptr2); 45 always @(posedge rclk or negedge rrst) 46 begin if(!rrst) empty<=1; 47 else empty<=empty_val; 48 end 49 50 //产生waddr信号和full信号 51 always @(posedge wclk or negedge wrst) 52 begin if(!wrst) {wbin,wptr}<=0; 53 else {wbin,wptr}<={wbinnext,wptrnext}; 54 end 55 assign waddr=wbin[addrsize-1:0]; 56 assign wbinnext=wbin+(wen&&~full); 57 assign wptrnext=(wbinnext>>1)^wbinnext; //生成waddr 58 assign full_val=(wptrnext=={~wptr2[addrsize:addrsize-1],wptr2[addrsize-2:0]}); 59 always @(posedge wclk or negedge wrst) 60 begin if(!rrst) full<=0; 61 else full<=full_val; 62 end 63 endmodule