其他分享
首页 > 其他分享> > 异步FIFO设计

异步FIFO设计

作者:互联网

引言

异步FIFO被广泛使用在数字电路中,不论是作为数据buffer还是数据跨时钟域处理、不同位宽数据的缓冲。

本人以往的使用经验都是直接调用IP。但是异步FIFO的原理也是应该熟悉的,这样一种经典的电路设计十分巧妙,如何做到安全缓冲并有效避免了数据上溢或下溢?

本文参考了CE Cummings的经典论文“Simulation and Synthesis Techniques for Asynchronous FIFO Design”

设计框架

整体的框架如下所示:(ps.结构精妙,且以一种非协调的对称美深深地抓住了读者的眼球)

 

设计主要分为4个部分即:

  1. 写控制端
  2. 读控制端
  3. 读指针同步电路、写指针同步电路
  4. 双端口RAM

下面对各个模块作一个简单的介绍:

一、写控制端

winc即写使能,当它拉高时写控制端内部的写地址自增。在这模块中需要产生写满标志(后文以设计一个8位宽,深度为16的异步FIFO为例),判断是不是写满了,需要写指针与读指针进行比较,这里读指针是读控制端这个模块产生的,因此需要将这个处在读时钟下的信号同步到写时钟下的写控制端模块来,这就是sync_r2w模块的意义,一个二级同步电路对读指针打两拍。而写满标志的产生条件是:写指针赶上读指针——假设读写指针都从0开始,那么写指针爬完了地址然后回到原点,这时候就是赶上了读指针。

这里有两个关键点:

一是要使用格雷码作为比较的读写指针即wptr和rptr(然而waddr和raddr保持是二进制指针),因为FIFO读写指针是多位数据进行比较,因此地址变化时容易产生毛刺,但是如果采用格雷码一次只跳变一位,如果跳变正确就没出错,如果跳变错误,本来是000到001,跳变失败变成了000,由于异步FIFO的读写裕量即读写指针的一个延迟情况,一般来说也不会影响到空满信号的产生。另外也比较节能,翻转率降低了。

二是格雷码读写指针进行比较,那么满信号的产生要比二进制地址多一位,且以MSB前2位进行判断。多出来的一位,即地址是log2(16)=4位宽,这里需要设计5位的格雷码读写指针, 因为空信号的判断是读指针=写指针。那么满信号的判断就不能用这个条件,而是多一位代表了写指针多跑了一圈,即读指针是0,而写指针是1,或反之的情况时,代表了写指针赶上一圈达到读指针的位置。但是还存在一个情况,不能仅仅用第一位进行判断,如下图所示:

考虑写指针在7即0100而读指针在8即1100,这时候最高位不同但其他位相同,但并不是写满了的情况。

因此正确的方法是用高2位进行判断,高两位不相同而其他位相同时代表了写满。full信号拉高。

二、读控制端

这个模块分析类似,但是空信号的判断条件是经过同步的写指针=读指针。

三、读指针同步电路、写指针同步电路

打两拍消除亚稳态的影响。简单的两级同步。

四、双端口RAM

可以调用IP或者自己用寄存器设计。

巧妙之处

开篇说的巧妙就在于不同读写时钟频率下也能做到数据的正确缓冲,根据上文的框图可以注意到写指针同步到读时钟域与读指针进行比较,当写指针同步到读端时,在这一时刻与读指针相同,即空信号可以拉高,外部不能继续读数据,但是实际上这一时刻写端地址也同时增加了几次,即在这个同步的两拍内仍然写了几个数据进去。因此这个空并不是真正的空,这也保证外部端口如果继续请求FIFO的数据不会读空。反之亦然,满并不标志着真正的满,满信号拉高时读端已然读出了几个数据,因此外部在满信号拉高后如果继续写数据进去也能保证一定的正确写入。不会丢失掉这部分数据。

代码及仿真结果

1.顶层代码

 1 module asfifo(
 2     input                 wire            wclk,
 3     input                 wire            wrst_n,
 4     input                 wire            winc,
 5     input                 wire [7:0]        wdata,
 6     input                 wire             rclk,
 7     input                 wire            rrst_n,
 8     input                 wire            rinc,
 9     output                 wire [7:0]        rdata,
10     output                 wire            wfull,
11     output                 wire            rempty
12     );
13 
14 wire        [3:0]        waddr;
15 wire        [3:0]        raddr;
16 wire        [4:0]        wptr,rptr;
17 wire          [4:0]        rq2_wptr;
18 wire          [4:0]        wq2_rptr;
19 wire                    full;
20 wire                    empty;
21 wire        [7:0]        rdata_o;
22 
23 assign wfull = full;
24 assign rempty = empty;
25 assign rdata = rdata_o;
26 
27 //inst mem
28     fifomem inst_fifomem
29         (
30             .wclk   (wclk),
31             .wdata  (wdata),
32             .wclken (winc),
33             .waddr  (waddr),
34             .raddr  (raddr),
35             .rdata  (rdata_o),
36             .wfull  (full)
37         );
38 
39 //inst wptr_full
40     wptr_full inst_wptr_full
41         (
42             .wclk     (wclk),
43             .wrst_n   (wrst_n),
44             .winc     (winc),
45             .wq2_rptr (wq2_rptr),
46             .wfull     (full),
47             .waddr    (waddr),
48             .wptr     (wptr)
49         );
50 
51 //inst rptr_empty
52     rptr_empty inst_rptr_empty
53         (
54             .rclk     (rclk),
55             .rrst_n   (rrst_n),
56             .rinc     (rinc),
57             .rq2_wptr (rq2_wptr),
58             .raddr    (raddr),
59             .rptr     (rptr),
60             .rempty   (empty)
61         );
62 
63 
64 //inst sync_w2r
65     sync_w2r inst_sync_w2r (.rclk(rclk), .rrst_n(rrst_n), .wptr(wptr), .rq2_wptr(rq2_wptr));
66 
67 //inst sync_r2w
68     sync_r2w inst_sync_r2w (.wclk(wclk), .wrst_n(wrst_n), .rptr(rptr), .wq2_rptr(wq2_rptr));
69 
70 
71 
72 endmodule 
View Code

 2.写控制模块

 1 module wptr_full(
 2     input             wire            wclk,
 3     input             wire            wrst_n,
 4     input           wire             winc,
 5     input             wire [4:0]        wq2_rptr,
 6     output            reg                wfull,
 7     output            wire [3:0]        waddr,
 8     output             reg     [4:0]        wptr
 9 );
10 
11 reg         [4:0]            wbin;
12 wire    [4:0]            wgreynext,wbinnext;
13 wire                    wfullnext;
14 
15 assign wbinnext = wbin + (winc & ~wfull);
16 assign wgreynext = (wbinnext>>1) ^ wbinnext;
17 assign waddr = wbin[3:0];
18 
19 always @(posedge wclk) begin
20     if (wrst_n==1'b0) begin
21         // reset
22         wbin <= 'd0;
23         wptr <= 'd0;
24     end
25     else begin
26         wbin <= wbinnext;
27         wptr <= wgreynext;
28     end
29 end
30 
31 assign wfullnext = (wgreynext[4:3]!=wq2_rptr[4:3]) && (wgreynext[2:0]==wq2_rptr[2:0]);
32 
33 always @(posedge wclk) begin
34     if (wrst_n==1'b0) begin
35         // reset
36         wfull <= 1'b0;
37     end
38     else begin
39         wfull <= wfullnext;
40     end
41 end
42 
43 endmodule
View Code

 3.读控制模块

 1 module rptr_empty(
 2     input             wire            rclk,
 3     input             wire            rrst_n,
 4     input             wire            rinc,
 5     input             wire [4:0]        rq2_wptr,
 6     output            wire [3:0]        raddr,
 7     output            reg [4:0]        rptr,
 8     output             reg             rempty
 9     );
10 
11 reg        [4:0]        rbin;    
12 wire    [4:0]        rbinnext,rgreynext;
13 wire                remptynext;
14 
15 assign rbinnext = rbin + (rinc & ~rempty);
16 assign rgreynext = (rbinnext>>1) ^ rbinnext;
17 assign raddr = rbin[3:0];
18 
19 always @(posedge rclk) begin
20     if (rrst_n==1'b0) begin
21         // reset
22         rbin <= 'd0;
23         rptr <= 'd0;
24     end
25     else begin
26         rbin <= rbinnext;
27         rptr <= rgreynext;
28     end
29 end
30 
31 assign remptynext = rgreynext==rq2_wptr;
32 
33 always @(posedge rclk) begin
34     if (rrst_n==1'b0) begin
35         // reset
36         rempty <= 1'b1;
37     end
38     else begin
39         rempty <= remptynext;
40     end
41 end
42 
43 
44 endmodule
View Code

 4.读指针同步、写指针同步

 1 module sync_w2r(
 2     input         wire            rclk,
 3     input         wire            rrst_n,
 4     input         wire [4:0]        wptr,
 5     output        wire [4:0]        rq2_wptr
 6     );
 7 
 8 reg            [4:0]    rq1_wptr_r,rq2_wptr_r;
 9 
10 assign rq2_wptr = rq2_wptr_r;
11 
12 always @(posedge rclk) begin
13     if (rrst_n==1'b0) begin
14         // reset
15         rq1_wptr_r <= 'd0;
16         rq2_wptr_r <= 'd0;
17     end
18     else begin
19         rq1_wptr_r <= wptr;
20         rq2_wptr_r <= rq1_wptr_r;    
21     end
22 end
23 
24 
25 endmodule
View Code
 1 module sync_r2w(
 2     input         wire            wclk,
 3     input         wire            wrst_n,
 4     input         wire [4:0]        rptr,
 5     output        wire [4:0]        wq2_rptr
 6     );
 7 
 8 reg            [4:0]    wq1_rptr_r,wq2_rptr_r;
 9 
10 assign wq2_rptr = wq2_rptr_r;
11 
12 always @(posedge wclk) begin
13     if (wrst_n==1'b0) begin
14         // reset
15         wq1_rptr_r <= 'd0;
16         wq2_rptr_r <= 'd0;
17     end
18     else begin
19         wq1_rptr_r <= rptr;
20         wq2_rptr_r <= wq1_rptr_r;    
21     end
22 end
23 
24 
25 endmodule
View Code

5.双口ram

 1 module fifomem(
 2     input         wire                wclk,
 3     input         wire    [7:0]        wdata,
 4     input         wire                wclken,
 5     input         wire    [3:0]        waddr,
 6     input        wire    [3:0]        raddr,
 7     output        wire    [7:0]        rdata,
 8     input          wire                wfull
 9     );
10 
11 reg         [7:0]  mem [0:15];
12 
13 assign rdata = mem[raddr];
14 
15 always @(posedge wclk) begin
16     if (wclken==1'b1 && wfull!=1'b1) begin
17         mem[waddr] <= wdata;
18     end
19 end
20 
21 
22 
23 endmodule
View Code

 6.测试文件

 1 `timescale 1ns/1ps
 2 module tb_asfifo();
 3 
 4 reg         wclk;
 5 reg         wrst_n;
 6 reg            winc;
 7 reg [7:0]     wdata;
 8 reg         rclk;
 9 reg         rrst_n;
10 reg         rinc;
11 wire [7:0]    rdata;
12 wire         wfull;
13 wire        rempty;
14 
15 initial begin
16     wclk = 0;
17     rclk = 0;
18     wrst_n = 0;
19     rrst_n = 0;
20     winc = 0;
21     rinc = 0;
22     wdata = 0;
23     #100
24     rrst_n = 1;
25     wrst_n = 1;
26     #3000
27     rinc = 1;
28 end
29 
30 always #5 wclk = ~wclk;
31 always #10 rclk = ~rclk;
32 
33 initial begin
34     gen_data();
35 end
36 
37 task gen_data;
38 integer i;
39 begin
40     @(posedge wrst_n);
41     for(i=0;i<16;i=i+1)begin
42         @(posedge wclk);
43         wdata = i;
44         winc = 1;
45     end
46     @(posedge wclk);
47     winc = 0;
48     wdata = 0;
49 end
50 endtask 
51 
52 
53 //inst asfifo
54     asfifo inst_asfifo
55         (
56             .wclk   (wclk),
57             .wrst_n (wrst_n),
58             .winc   (winc),
59             .wdata  (wdata),
60             .rclk   (rclk),
61             .rrst_n (rrst_n),
62             .rinc   (rinc),
63             .rdata  (rdata),
64             .wfull  (wfull),
65             .rempty (rempty)
66         );
67 
68 
69 
70 endmodule 
View Code

Modelsim仿真结果如下图所示:

首先写入16个数据,这里可以看到空信号并不是一写入数据就拉低的,而是延迟了几个时钟周期。同样满信号提前了一拍就拉高了。因为写时钟是读时钟频率的2倍,因此两者的延迟也不相同。

接着读出这16个数据,同样满信号在读到第三个数据时才拉低,这里空信号正好在读到最后一个数据时拉高了。

 参考文献

Verilog E ,  Cummings C E . Simulation and Synthesis Techniques for Asynchronous FIFO Design[J]. Snug, 2002.

 

标签:异步,wire,rptr,wptr,FIFO,wclk,设计,input,指针
来源: https://www.cnblogs.com/Achilles7/p/15980826.html