赞
踩
目录
FIFO设计的关键:产生可靠的FIFO读写指针和生成FIFO“空”/“满”状态标志。
当读写指针相等时,表明FIFO为空,这种情况发生在复位操作时,或者当读指针读出FIFO中最后一个字后,追赶上了写指针时,如下图所示:
当读写指针再次相等时,表明FIFO为满,这种情况发生在,当写指针转了一圈,折回来(wrapped around)又追上了读指针,如下图:
为了区分到底是满状态还是空状态,可以采用以下方法:
方法1:在指针中添加一个额外的位(extra bit)
当写指针增加并越过最后一个FIFO地址时,就将写指针这个未用的MSB加1,其它位回零。对读指针也进行同样的操作。此时,对于深度为2的n次方的FIFO,需要的读/写指针位宽为(n+1)位,如对于深度为8的FIFO,需要采用4bit的计数器,0000~1000、1001~1111,MSB作为折回标志位,而低3位作为地址指针。
如果两个指针的MSB不同,说明写指针比读指针多折回了一次;如r_addr=0000,而w_addr = 1000,为满。
如果两个指针的MSB相同,则说明两个指针折回的次数相等,其余位也相等,即两个指针相等说明FIFO为空;
这个办法主要用于同步FIFO判断空满
将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出现问题,因为采用二进制计数器时所有位都可能同时变化,在同一个时钟沿同步多个信号的变化会产生亚稳态问题。而使用格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到gray码的转换电路,将地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行对比,作为空满状态的检测。
方法2:使用gray码进行对比,如何判断“空”与“满”
使用gray码解决了一个问题,但同时也带来另一个问题,即在格雷码域如何判断空与满。
对于“空”的判断依然依据二者完全相等(包括MSB);
而对于“满”的判断,如下图,由于gray码除了MSB外,具有镜像对称的特点,当读指针指向7,写指针指向8时,除了MSB,其余位皆相同,不能说它为满。因此不能单纯的只检测最高位了,在gray码上判断为满必须同时满足以下3条:
由于是异步FIFO的设计,读写时钟不一样,在产生读空信号和写满信号时,会涉及到跨时钟域的问题,如何解决?
跨时钟域的问题:由于读指针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO的读写时钟域不同,是异步的,要是将读时钟域的读指针与写时钟域的写指针不做任何处理直接比较肯定是错误的,因此我们需要进行同步处理以后仔进行比较
解决方法: 加两级寄存器同步 + 格雷码(目的都是消除亚稳态)
1.使用异步信号进行使用的时候,好的设计都会对异步信号进行同步处理,同步一般采用多级D触发器级联处理,如下图。这种模型大部分资料都说的是第一级寄存器产生亚稳态后,第二级寄存器稳定输出概率为90%,第三极寄存器稳定输出的概率为99%,如果亚稳态跟随电路一直传递下去,那就会另自我修护能力较弱的系统直接崩溃
2.将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出现问题,因为采用二进制计数器时所有位都可能同时变化,在同一个时钟沿同步多个信号的变化会产生亚稳态问题。而使用格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到gray码的转换电路,将地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行对比,作为空满状态的检测。
那么,多位二进制码如何转化为格雷码?
verilog代码实现就一句:assign gray_code = (bin_code>>1) ^ bin_code;
顺便提一下,多位格雷码如何转化为二进制码
格雷码域如何判断空与满?前面已经讨论过,简单来说就是:
读空时:需要读时钟域的格雷码rgray_next和被同步到读时钟域的写指针rd2_wp每一位完全相同;
写满时:需要写时钟域的格雷码wgray_next和被同步到写时钟域的读指针wr2_rp高两位不相同,其余各位完全相同;
assign full = (wr_addr_gray == {~(rd_addr_gray_d2[addr_width-:2]),rd_addr_gray_d2[addr_width-2:0]}) ;//高两位不同
assign empty = ( rd_addr_gray == wr_addr_gray_d2 );
- module asyn_fifo #(
- parameter DATA_WIDTH = 4'd8;
- parameter DATA_DEPTH = 5'd16;
- parameter ADDR_WIDTH = 4'd4;
- )(
- input [DATA_WIDTH-1:0] data_in;
- input wr_clk;
- input rst_n;
- input wr_en;
- output full;
- input rd_clk;
- input rd_en;
- output reg [DATA_WIDTH-1:0] data_out;
- output empty
- );
-
- wire [ADDR_WIDTH - 1'd1 : 0] wr_addr;
- reg [ADDR_WIDTH : 0] wr_ptr;
- wire [ADDR_WIDTH : 0] wr_gray;
- reg [ADDR_WIDTH : 0] wr_gray_d1;
- reg [ADDR_WIDTH : 0] wr_gray_d2;
-
- wire [ADDR_WIDTH - 1'd1 : 0] rd_addr;
- reg [ADDR_WIDTH : 0] rd_ptr;
- wire [ADDR_WIDTH : 0] rd_gray;
- reg [ADDR_WIDTH : 0] rd_gray_d1;
- reg [ADDR_WIDTH : 0] rd_gray_d2;
-
- reg [DATA_WIDTH - 1'd1 : 0] fifo_ram [DATA_DEPTH - 1'd1 : 0];
-
- assign wr_addr = wr_ptr[data_addr - 1'd1 : 0];
- assign rd_addr = rd_ptr[data_addr - 1'd1 : 0];
-
- //写指针
- always @(posedge wr_clk or negedge rst_n )
- begin
- if (!rst_n)
- wr_ptr <= 'd0;
- else if ( (wr_en) && (!full) )
- wr_ptr <= 'd1 + wr_ptr;
- else
- wr_ptr <= wr_ptr;
- end
- //读指针
- always @(posedge rd_clk or negedge rst_n )
- begin
- if (!rst_n)
- rd_ptr <= 'd0;
- else if ( (rd_en) && (!empty) )
- rd_ptr <= 'd1 + rd_ptr;
- else
- rd_ptr <= rd_ptr;
- end
- //---------------------------------------------------------------
- assign wr_gray = (wr_ptr >> 1'd1) ^ wr_ptr;
- assign rd_gray = (rd_ptr >> 1'd1) ^ rd_ptr;
- always @(posedge rd_clk )
- begin
- wr_gray_d1 <= wr_gray;
- wr_gray_d2 <= wr_gray_d1;
- end
-
- always @(posedge wr_clk )
- begin
- rd_gray_d1 <= rd_gray;
- rd_gray_d2 <= rd_gray_d1;
- end
-
-
- //------------------------------------------------------------------
- assign full = (wr_gray == {~(rd_gray_d2[ADDR_WIDTH :ADDR_WIDTH-1 ]),rd_gray_d2[ADDR_WIDTH-2:0]});
- assign empty = (rd_gray == wr_gray_d2);
-
- //-------------------------------------------------------------write operation
- always @(posedge wr_clk or negedge rst_n)
- begin
- if(!rst_n)
- begin
- fifo_ram [wr_addr] <= 'h0;
- end
- else if ( (wr_en) && (!full) )
- fifo_ram[wr_addr] <= data_in;
- else
- fifo_ram[wr_addr] <= fifo_ram[wr_addr];
- end
- //--------------------------------------------------------------read operation
- always @(posedge rd_clk or negedge rst_n)
- begin
- if(!rst_n)
- data_out <= 'h0;
-
- else if( (rd_en) && (!empty) )
- data_out <= fifo_ram[rd_addr];
- else
- data_out <= data_out;
- end
-
-
- endmodule
-
-
- module asyn_fifo_tb;
-
- parameter DATA_W = 8 ;
- parameter ADDR_W = 4 ;
- parameter data_hight = 16 ;
-
- reg wr_clk ;
- reg rd_clk ;
- reg rst_n ;
- reg wr_en ;
- reg [DATA_W-1:0] wr_data ;
- reg rd_en ;
-
- wire [DATA_W-1:0] rd_data ;
- wire empty ;
- wire full ;
-
-
- asyn_fifo u_FIFO_asyn
- (
- .data_out (rd_data),
- .data_in (wr_data),
- .wr_clk (wr_clk) ,
- .rst_n (rst_n) ,
- .full (full) ,
- .empty (empty) ,
- .rd_clk (rd_clk) ,
- .rd_en (rd_en) ,
- .wr_en (wr_en)
- );
-
- always #10 wr_clk = ~wr_clk;
- always #5 rd_clk = ~rd_clk;
-
- initial begin
-
- wr_clk = 1;
- rd_clk = 0;
- rst_n = 0;
- wr_en = 0;
- wr_data = 0;
- rd_en = 0;
-
- #101;
- rst_n = 1;
-
- #20;
- gen_data;
-
- @(posedge rd_clk);
- rd_en=1;
- #1000 $finish;
- end
-
- task gen_data;
- integer i;
- begin
- for(i=0;i<15;i=i+1)
- begin
- wr_en = 1;
- wr_data = i;
- #20;
- end
- wr_en = 0;
- wr_data = 0;
- end
- endtask
-
- /*initial
- begin
- `ifdef DUMP_VPD
- $vcdpluson();
- `endif
- end*/
-
- initial begin
- $fsdbDumpfile("asy_fifo.fsdb");
- $fsdbDumpvars(0);
-
- end
-
-
-
- endmodule
理论参考:
Clifford E. Cummings《Simulation and Synthesis Techniques for Asynchronous FIFO Design》
https://blog.csdn.net/alangaixiaoxiao/article/details/81432144
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。