当前位置:   article > 正文

手写异步FIFO

手写异步fifo

以下内容摘自:《正点原子逻辑设计指南》

一、异步FIFO简介

异步 FIFO 有两个时钟信号,读和写接口分别采用不同时钟,这两个时钟可能时钟频率不同,也可能时钟相位不同,可能是同源时钟,也可能是不同源时钟。

在现代逻辑设计中,随着设计规模的不断扩大,一个系统中往往含有数个时钟,多时钟域带来的一个问题就是,如何设计异步时钟之间的接口电路。异步 FIFO 是这个问题的一种简便、快捷的解决方案,使用异步 FIFO 可以在两个不同时钟系统之间快速而方便地传输实时数据。

异步 FIFO 指针的考虑:

为什么异步 FIFO 的指针需要特殊处理呢?因为异步 FIFO 的空满指示需要使用指针进行判断,如果空满判断直接用两个时钟域的信号做逻辑判断,会导致逻辑判断错误,如下图所示:读逻辑的时钟采样写地址的时候,由于路径延迟和两个时钟相位都不同,就会导致读逻辑采样到的写地址完全错误(如果采样的时候刚好写地址信号跳变,还可能会导致亚稳态产生),导致空标记错误,导致 FIFO 不能使用。
在这里插入图片描述
一般异步 FIFO 的地址传递需要使用格雷码,每次地址变化格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到 gray 码的转换电路,将地址值转换为相应的 gray 码,然后将该 gray 码同步到另一个时钟域进行对比,作为空满状态的检测。

使用 gray 码进行对比,如何判断“空”与“满”?

使用 gray 码解决了指针采样错误的问题,但同时也带来另一个问题,即在格雷码域如何判断空与满。

  • 对于“空”的判断依然依据二者完全相等(包括 MSB);

  • 而对于“满”的判断,首先需要说明的是,下图中虽然有16个格雷码,但其实是一个深度为8的FIFO,这是因为在判满时,写指针必须绕一圈追上读指针才行,所以读写指针就必须扩展1位变成4位。例如,当写指针是15,读指针是7时,才能认为是写指针比读指针多了一圈,但此时读写的实际地址仍然是相同的,也即都是7。 由于 gray 码除了 MSB 外,具有镜像对称的特点,当读指针指向 7,写指针指向 8 时,除了 MSB,其余位皆相同,不能说它为满。因此不能单纯的只检测最高位了,在 gray 码上判断为满必须同时满足以下 3 条:
    1、wptr 和同步过来的 rptr 的 MSB 不相等,因为 wptr 必须比 rptr 多折回一次。
    2、wptr 与 rptr 的次高位不相等,如下图位置 7 和位置 15,转化为二进制对应的是 0111 和 1111,MSB不同说明多折回一次,111 相同代表同一位置。
    3、 剩下的其余位完全相等。
    在这里插入图片描述
    下图即为异步FIFO的系统框图:
    在这里插入图片描述

二、程序设计

1、RTL代码

module AsyncFIFO
#(parameter ASIZE = 4, //地址位宽
  parameter DSIZE = 8) //数据位宽
( 
    input [DSIZE-1:0] wdata, 
    input winc, wclk, wrst_n, //写请求信号,写时钟,写复位
    input rinc, rclk, rrst_n, //读请求信号,读时钟,读复位
    output [DSIZE-1:0] rdata, 
    output wfull,
    output rempty
 );
    wire [ASIZE-1:0] waddr ;
    wire [ASIZE-1:0] raddr ;
    wire [ASIZE:0] wptr ;
    wire [ASIZE:0] rptr ;
    wire [ASIZE:0] wq2_rptr ;
    wire [ASIZE:0] rq2_wptr ;
 /************************************************************
 * In order to perform FIFO full and FIFO empty tests using 
 * this FIFO style, the read and write pointers must be
 * passed to the opposite clock domain for pointer comparison
 *************************************************************/
 //在检测“满”或“空”状态之前,需要将指针同步到其它时钟域时,
 //使用格雷码,可以降低同步过程中亚稳态出现的概率
sync_r2w I1_sync_r2w(
    .wq2_rptr(wq2_rptr),
    .rptr(rptr),
    .wclk(wclk),
    .wrst_n(wrst_n));

sync_w2r I2_sync_w2r (
    .rq2_wptr(rq2_wptr),
    .wptr(wptr),
    .rclk(rclk),
    .rrst_n(rrst_n));

/*
* DualRAM 
*/
DualRAM #(DSIZE, ASIZE) I3_DualRAM(
    .rdata(rdata),
    .wdata(wdata),
    .waddr(waddr),
    .raddr(raddr),
    .wclken(winc),
    .wclk(wclk));
/*
* 空、满比较逻辑
*/
rptr_empty #(ASIZE) I4_rptr_empty(
    .rempty(rempty),
    .raddr(raddr),
    .rptr(rptr),
    .rq2_wptr(rq2_wptr),
    .rinc(rinc),
    .rclk(rclk),
    .rrst_n(rrst_n));
wptr_full #(ASIZE) I5_wptr_full(
    .wfull(wfull),
    .waddr(waddr),
    .wptr(wptr),
    .wq2_rptr(wq2_rptr),
    .winc(winc),
    .wclk(wclk),
    .wrst_n(wrst_n));

endmodule

module DualRAM
#(
parameter DATA_SIZE = 8, // 数据位宽
parameter ADDR_SIZE = 4 // 地址位宽
)
(
    input wclken,wclk,
    input [ADDR_SIZE-1:0] raddr, //RAM read address
    input [ADDR_SIZE-1:0] waddr, //RAM write address
    input [DATA_SIZE-1:0] wdata, //data input
    output [DATA_SIZE-1:0] rdata //data output
); 

localparam RAM_DEPTH = 1 << ADDR_SIZE; //RAM 深度 = 2^ADDR_WIDTH
reg [DATA_SIZE-1:0] Mem[RAM_DEPTH-1:0];

always@(posedge wclk) begin
    if(wclken)
        Mem[waddr] <= wdata;
end

assign rdata = Mem[raddr];

endmodule

module sync_r2w 
#(parameter ADDRSIZE = 4)
(
    output reg [ADDRSIZE:0] wq2_rptr,
    input [ADDRSIZE:0] rptr,
    input wclk, wrst_n
);
 
 reg [ADDRSIZE:0] wq1_rptr;
 
 always @(posedge wclk or negedge wrst_n) begin
    if (!wrst_n)
        {wq2_rptr,wq1_rptr} <= 0;
    else
        {wq2_rptr,wq1_rptr} <= {wq1_rptr,rptr};
 end
 
 endmodule
 
 module sync_w2r 
 #(parameter ADDRSIZE = 4)
 (
    output reg [ADDRSIZE:0] rq2_wptr,
    input [ADDRSIZE:0] wptr,
    input rclk, rrst_n
 ); 
 
 reg [ADDRSIZE:0] rq1_wptr;
 
 always @(posedge rclk or negedge rrst_n)
    if (!rrst_n)
        {rq2_wptr,rq1_wptr} <= 0;
    else
        {rq2_wptr,rq1_wptr} <= {rq1_wptr,wptr};
 endmodule
 
 module rptr_empty 
 #(parameter ADDRSIZE = 4)
 (
    output reg rempty,
    output [ADDRSIZE-1:0] raddr,
    output reg [ADDRSIZE :0] rptr,
    input [ADDRSIZE :0] rq2_wptr,
    input rinc, rclk, rrst_n);
 
    reg [ADDRSIZE:0] rbin;
    wire [ADDRSIZE:0] rgraynext, rbinnext;
    wire rempty_val;
 
 //-------------------
 // GRAYSTYLE2 pointer: gray 码读地址指针
 //-------------------
 always @(posedge rclk or negedge rrst_n) begin
    if (!rrst_n) begin
        rbin <= 0;
        rptr <= 0;
    end
    else begin
        rbin <= rbinnext ;
        rptr <= rgraynext;
    end
 end
 
 // gray 码计数逻辑
 assign rbinnext = !rempty ? (rbin + rinc) : rbin;
 assign rgraynext = (rbinnext>>1) ^ rbinnext; //二进制到 gray 码的转换
 assign raddr = rbin[ADDRSIZE-1:0];
 
 //---------------------------------------------------------------
 // FIFO empty when the next rptr == synchronized wptr or on reset
 //---------------------------------------------------------------
 /*
 * 读指针是一个 n 位的 gray 码计数器,比 FIFO 寻址所需的位宽大一位
 * 当读指针和同步过来的写指针完全相等时(包括 MSB),说明二者折回次数一致,FIFO 为空
 */
 assign rempty_val = (rgraynext == rq2_wptr);
 
 always @(posedge rclk or negedge rrst_n)
    if (!rrst_n)
        rempty <= 1'b1;
    else
        rempty <= rempty_val;
 endmodule
 
 module wptr_full 
 #(
 parameter ADDRSIZE = 4
 )
 (
    output reg wfull,
    output [ADDRSIZE-1:0] waddr,
    output reg [ADDRSIZE :0] wptr,
    input [ADDRSIZE :0] wq2_rptr,
    input winc, wclk, wrst_n); 
 
    reg [ADDRSIZE:0] wbin;
    wire [ADDRSIZE:0] wgraynext, wbinnext;
    wire wfull_val;
 
 // GRAYSTYLE2 pointer
 always @(posedge wclk or negedge wrst_n) begin
    if (!wrst_n) begin
        wbin <= 0;
        wptr <= 0;
    end
    else begin
        wbin <= wbinnext;
        wptr <= wgraynext;
    end
 end
 
 //gray 码计数逻辑 
 assign wbinnext = !wfull ? wbin + winc : wbin;
 assign wgraynext = (wbinnext>>1) ^ wbinnext;
 assign waddr = wbin[ADDRSIZE-1:0];
 /*由于满标志在写时钟域产生,因此比较安全的做法是将读指针同步到写时钟域*/
 
 /**/
 //------------------------------------------------------------------
 // Simplified version of the three necessary full-tests:
 // assign wfull_val=((wgnext[ADDRSIZE] !=wq2_rptr[ADDRSIZE] ) &&
 // (wgnext[ADDRSIZE-1] !=wq2_rptr[ADDRSIZE-1]) &&
 // (wgnext[ADDRSIZE-2:0]==wq2_rptr[ADDRSIZE-2:0]));
 //------------------------------------------------------------------
 assign wfull_val = (wgraynext== {~wq2_rptr[ADDRSIZE:ADDRSIZE- 1],wq2_rptr[ADDRSIZE-2:0]});
 
 always @(posedge wclk or negedge wrst_n) begin
    if (!wrst_n)
        wfull <= 1'b0;
    else
        wfull <= wfull_val;
end
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226

2、测试代码

`timescale 1 ns/ 1 ps
module TB();
    reg rclk; 
    reg rinc; 
    reg rrst_n; 
    reg wclk; 
    reg [7:0] wdata; 
    reg winc; 
    reg wrst_n; 
    wire [7:0] rdata;
    wire rempty;
    wire wfull; 
 
 initial
 begin
    rrst_n = 0;
    wrst_n = 0;
    rclk = 0;
    wclk = 0;
    winc = 0;
    rinc = 0;
    #20 rrst_n = 1;
    wrst_n =1 ;
 end
 
 initial begin
    rclk = 'b0 ;
    wclk = 'b0 ;
 
 end
 
 always #10 rclk = ~ rclk;
 always #10 wclk = ~ wclk;
 
 always @(posedge rclk)
    rinc <= {$random} % 2;
 always @(posedge wclk)
    winc <= {$random} % 2;
 
 always @(negedge wclk)
    wdata <= {$random} % 256;
 
 AsyncFIFO U0_AsyncFIFO (
    .rclk(rclk),
    .rdata(rdata),
    .rempty(rempty),
    .rinc(rinc),
    .rrst_n(rrst_n),
    .wclk(wclk),
    .wdata(wdata),
    .wfull(wfull),
    .winc(winc),
    .wrst_n(wrst_n)
 );
 
 endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

3、仿真结果
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/550285
推荐阅读
相关标签
  

闽ICP备14008679号