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的设计和验证。

 关注公众号,回复“async_fifo”,可获得源码

 

posted @ 2021-03-15 17:33  辰风阆苑  阅读(1168)  评论(0编辑  收藏  举报