Verilog RTL 设计:异步FIFO的设计与验证
之前的两篇博文讨论了同步FIFO的设计和验证,其读写时钟时相同的单一时钟,应用范围有限。
在实际的系统中,经常会遇到多个时钟域传输数据的情况,此时需要数据在跨时钟域上实现无缝传输,且不能有毛刺出现。异步FIFO读写时钟是不相同的,因此可以实现某个频率的写时钟写入再由另一个频率的读时钟读出,也就能够实现跨时钟的传输数据了。
异步FIFO的结构框图如下:
设计的关键:异步FIFO中如何产生空满信号。
根据同步FIFO方法二的设计经验。空满信号的产生可以根据读写的地址的比较来判断。但是,在异步FIFO中,处于两个不同时钟域的读写地址该如何来比较呢?
首先,对于空信号,只有在读的时候才会来看FIFO是否为空,如果FIFO为空就不能读数据了,因此空信号的产生是在读时钟域的。同理,对于满信号,只有在写的时候才会来看FIFO是否为满,如果FIFO满了就不能写数据了,因此满信号的产生是在写时钟域的。在进行读写地址的比较时,只能先对地址信号进行时钟域同步,然后再进行比较。采用常规的异步处理的方法:打两拍。
//wr_addr_g sample twice always @(posedge rclk or negedge rst_n) if(rst_n == 1'b0) {wr_addr_g_dly2,wr_addr_g_dly1} <= 'b0; else {wr_addr_g_dly2,wr_addr_g_dly1} <= {wr_addr_g_dly1,wr_addr_g}; //rd_addr_g sample twice always @(posedge wclk or negedge rst_n) if(rst_n == 1'b0) {rd_addr_g_dly2,rd_addr_g_dly1} <= 'b0; else {rd_addr_g_dly2,rd_addr_g_dly1} <= {rd_addr_g_dly1,rd_addr_g};
然后,我们知道地址是一位一位累加的多位宽数据,但在跨时钟采样时,多位宽的数据传输并被采样时,如果不做处理很容易发生亚稳态,从而影响数据正确性。比如六位地址111111会在下一个时刻变成000000,在实际电路中,这个变化过程要持续很长一段时间,会由111111经历6个状态转移到000000.比如111111 -> 101111 -> 100111 -> 100110 -> 100100 -> 000100 -> 000000。由于写时钟与读时钟域不同步,异步的写时钟很可能会在状态不稳定的中间某个状态采样,这样就会得到错误的读指针,进而做出错误的状态判断,导致系统异常(亚稳态问题)。而且由于多位同时突变,凭借概率论常识可知发生错误的可能性很大。因此,将二进制的地址编码转换成对应的格雷码,地址每次只有1个bit发生改变。其结果也不外乎两种:递增前原指针和递增后新指针。显然递增后新指针是最新情况的反映,如果采样到这个指针,那么和我们的设计预期是一致的,如果采样到递增前的原指针,假设现在采样读指针,那么最坏的情况就是把“不满”判断成了“满”,使得本来被允许的写操作被禁止了,但是这并不会对逻辑产生影响,只是带来了写操作的延迟。同样的,如果现在采样写指针,那么最坏的情况就是把“不空”判断成“空”,使得本来被允许的读操作被禁止了,但是这也不会对逻辑产生影响,只是带来了读操作的延迟。二进制码转换成格雷码,其法则是保留二进制码的最高位作为格雷码的最高位,而次高位格雷码为二进制码的高位与次高位相异或,而格雷码其余各位与次高位的求法相类似。这样就可以实现二进制到格雷码的转换了,总结就是移位并且异或,verilog代码实现就一句:assign wgraynext = (wbinnext>>1) ^ wbinnext。
//binary code to gray code assign wr_addr_g = (wr_addr_b>>1) ^ wr_addr_b; assign rd_addr_g = (rd_addr_b>>1) ^ rd_addr_b;
最后,需要解决的问题就是格雷码如何进行比较产生空满信号。空信号很简单,当读地址和写地址相同时,即产生空信号。对于满信号,参考下面的表格发现,格雷码的0-15、1-14、2-13...都是最高位相反其余低位相同,同时,为判断满信号,又会多出一个最高位,而在FIFO满时,读写地址的最高位也是相反的。所以总结如下:当读写地址的高2bit的相反,后几位的bit相同时,写满;当读写地址相等时,读空。
assign empty = (rd_addr_g == wr_addr_g_dly2); assign full = (wr_addr_g == {~rd_addr_g_dly2[ADDR_WIDTH:ADDR_WIDTH-1],rd_addr_g_dly2[ADDR_WIDTH-2:0]});
十进制数 |
二进制数 |
格雷码 |
|
十进制数 |
二进制数 |
格雷码 |
0 |
0000 |
0000 |
|
8 |
1000 |
1100 |
1 |
0001 |
0001 |
|
9 |
1001 |
1101 |
2 |
0010 |
0011 |
|
10 |
1010 |
1111 |
3 |
0011 |
0010 |
|
11 |
1011 |
1110 |
4 |
0100 |
0110 |
|
12 |
1100 |
1010 |
5 |
0101 |
0111 |
|
13 |
1101 |
1011 |
6 |
0110 |
0101 |
|
14 |
1110 |
1001 |
7 |
0111 |
0100 |
|
15 |
1111 |
1000 |
根据上述分析,异步FIFO的设计如下:
RTL:
1 module async_fifo #(parameter FIFO_WIDTH = 8, 2 FIFO_DEPTH = 16, 3 ADDR_WIDTH = 4) 4 ( 5 input wire rclk, 6 input wire wclk, 7 input wire rst_n, 8 input wire wr_en, 9 input wire rd_en, 10 input wire[FIFO_WIDTH-1:0] wr_data, 11 output reg [FIFO_WIDTH-1:0] rd_data, 12 output reg empty, 13 output reg full 14 ); 15 //memory 16 reg [FIFO_WIDTH-1:0] mem[FIFO_DEPTH-1:0]; 17 //address 18 reg [ADDR_WIDTH-1:0] wr_addr,rd_addr; 19 //binary adderss 20 reg [ADDR_WIDTH:0] wr_addr_b,rd_addr_b; 21 //gray address 22 wire[ADDR_WIDTH:0] wr_addr_g,rd_addr_g; 23 //sample gray address 24 reg [ADDR_WIDTH:0] wr_addr_g_dly1,wr_addr_g_dly2,rd_addr_g_dly1,rd_addr_g_dly2; 25 26 //binary code to gray code 27 assign wr_addr_g = (wr_addr_b>>1) ^ wr_addr_b; 28 assign rd_addr_g = (rd_addr_b>>1) ^ rd_addr_b; 29 //addr 30 assign wr_addr = wr_addr_b[ADDR_WIDTH-1:0]; 31 assign rd_addr = rd_addr_b[ADDR_WIDTH-1:0]; 32 33 //read & write allow 34 wire rd_allow = rd_en && !empty; 35 wire wr_allow = wr_en && !full; 36 37 //write data 38 always @(posedge wclk or negedge rst_n) 39 if(rst_n == 1'b0) 40 wr_addr_b <= 'b0; 41 else if(wr_allow) begin 42 mem[wr_addr] <= wr_data; 43 wr_addr_b <= wr_addr_b + 1'b1; 44 end 45 46 //read data 47 always @(posedge rclk or negedge rst_n) 48 if(rst_n == 1'b0) 49 rd_addr_b <= 'b0; 50 else if(rd_allow) begin 51 rd_data <= mem[rd_addr]; 52 rd_addr_b <= rd_addr_b + 1'b1; 53 end 54 55 //wr_addr_g sample twice 56 always @(posedge rclk or negedge rst_n) 57 if(rst_n == 1'b0) 58 {wr_addr_g_dly2,wr_addr_g_dly1} <= 'b0; 59 else 60 {wr_addr_g_dly2,wr_addr_g_dly1} <= {wr_addr_g_dly1,wr_addr_g}; 61 62 //rd_addr_g sample twice 63 always @(posedge wclk or negedge rst_n) 64 if(rst_n == 1'b0) 65 {rd_addr_g_dly2,rd_addr_g_dly1} <= 'b0; 66 else 67 {rd_addr_g_dly2,rd_addr_g_dly1} <= {rd_addr_g_dly1,rd_addr_g}; 68 69 assign empty = (rd_addr_g == wr_addr_g_dly2); 70 assign full = (wr_addr_g == {~rd_addr_g_dly2[ADDR_WIDTH:ADDR_WIDTH-1],rd_addr_g_dly2[ADDR_WIDTH-2:0]}); 71 72 endmodule
Testbench:
1 `timescale 1ns/1ps 2 module async_fifo_tb(); 3 4 parameter FIFO_WIDTH = 8, 5 FIFO_DEPTH = 16, 6 ADDR_WIDTH = 4; 7 8 reg rclk,wclk,rst_n,wr_en,rd_en; 9 reg [FIFO_WIDTH-1:0] wr_data; 10 wire[FIFO_WIDTH-1:0] rd_data; 11 wire empty,full; 12 13 async_fifo 14 #( .FIFO_WIDTH (FIFO_WIDTH), 15 .FIFO_DEPTH (FIFO_DEPTH), 16 .ADDR_WIDTH (ADDR_WIDTH) 17 ) 18 ( 19 .rclk (rclk ), 20 .wclk (wclk ), 21 .rst_n (rst_n ), 22 .wr_en (wr_en ), 23 .rd_en (rd_en ), 24 .wr_data (wr_data), 25 .rd_data (rd_data), 26 .empty (empty ), 27 .full (full ) 28 ); 29 30 initial begin 31 rst_n=0; 32 rclk=0; 33 wclk=0; 34 #5 35 rst_n=1; 36 end 37 38 always #30 rclk=~rclk; 39 always #20 wclk=~wclk; 40 41 initial begin 42 wr_en=0; 43 rd_en=0; 44 #50 45 wr_en=1; 46 wr_data=8'h0; 47 #40 wr_data=8'h1; 48 #40 wr_data=8'h2; 49 #40 wr_data=8'h3; 50 #40 wr_data=8'h4; 51 #40 wr_data=8'h5; 52 #40 wr_data=8'h6; 53 #40 wr_data=8'h7; 54 #40 wr_data=8'h8; 55 #40 wr_data=8'h9; 56 #40 wr_data=8'hA; 57 #40 wr_data=8'hB; 58 #40 wr_data=8'hC; 59 #40 wr_data=8'hD; 60 #40 wr_data=8'hE; 61 #40 wr_data=8'hF; 62 #60 63 wr_en=0; 64 rd_en=1; 65 #1000 66 $finish; 67 end 68 69 `include "vpd.inc" 70 71 endmodule
DVE波形:
在此记录异步FIFO的设计和验证。