异步FIFO

FIFO是一种现先进先出的数据缓冲器,特点是没有外部的读写地址。根据满和空信号设计写使能和读使能来写/读FIFO,当FIFO满的时候不可以往里面写、当FIFO空的时候不能读数据。
异步FIFO常用于跨时钟域传输。

常见信号

  1. wfull: 满标志, 表示FIFO已经满,不能再写入数据。
  2. rempty:空标志,表示FIFO已经空,不能再读取数据。
  3. wclk: 写时钟
  4. rclk: 读时钟
  5. winc: 写使能
  6. rinc: 读使能
  7. wdata:写数据
  8. rdata: 读数据
  9. wrst_n: 写复位
  10. rrst_n:读复位

空满标志产生

  • 判空:读指针追上写指针的时候,两者相等,为空。
  • 判满:写指针追上读指针的时候,两者相等,为满。

如何判断谁追上谁
表示读写指针的数据位宽上再加1位来区分是满还是空。比如FIFO的深度位8,那么需要3位二进制数来表地址,则需要再最高之前再加一位,变成4位。一开始读写都是0000,FIFO为空。当写指针增加并越过最后一个存储单元的时候,就将这个最高位取反,变成1000。这时是写地址追上了读地址,FIFO为满。同理,当读地址越过最后一个存储单元的时候把读地址的最高位也取反。
当最高位相同,并且剩下的位也相同时,FIFO为空;
当最高位不同,并且剩下的位相同时,FIFO为满。

读写指针加一位

注意只是读写指针比实际多一位,FIFO深度还是一样的。

异步时钟下判断空满
需要将读写指针进行同步化才可以进行判断。通过两级D触发器。

  • 在判断空的时候,需要将写地址同步到读时钟域下进行判断。
  • 在判断满的时候,需要将读时钟域中的读指针同步到写时钟域进行判断。

虚空
空标志比较逻辑检测到读地址和写地址相同后紧接着系统产生了写操作,写地址增加,FIFO内有了新数据,由于同步模块的滞后性,用于比较的写地址不能及时更新,这样,一个本不应该有的空标志信号就产生了,不过这种情况也不会导致错误的发生,像这种FIFO非空而产生空标志信号的情况称为“虚空”。

格雷码表示地址

多位变化

将读指针发送到写时钟域下进行同步时,如果仍然采用自然二进制,那么就会面临地址同时有多位变化的情况。如上图多位延时不一样,就会采到错误的值。因此采用格雷码来表示地址,每次只改变一位。

格雷码与二进制转换

相互转换
  • 二进制转格雷码
    gray = (bin >> 1) ^ bin 异或
  • 格雷码转二进制

格雷码记法:

  • 0位:0 1 1 0重复
  • 1位:00 11 11 00重复
  • 2位:0000 1111 1111 0000重复
  • 3位:00000000 11111111 11111111 00000000

格雷码判空满

  • 判满:格雷码的最高位和次高为不同,剩下的都同,就是满。
  • 判空:格雷码完全相同,就是空。

可以参考下面图片,隔8个为满,相同为空。可见前两位不同,其余相同就是满。


格雷码判空满

模块设计

异步FIFO

如上图所示的同步模块synchronize to write clk,其作用是把读时钟域的读指针rd_ptr采集到写时钟(wr_clk)域,然后和写指针wr_ptr进行比较从而产生或撤消写满标志位wr_full;类似地,同步模块synchronize to read clk的作用是把写时钟域的写指针wr_ptr采集到读时钟域,然后和读指针rd_ptr进行比较从而产生或撤消读空标志位rd_empty。

另外还有写指针wr_ptr和写满标志位wr_full产生模块,读指针rd_ptr和读空标志位rd_empty产生模块,以及双端口存储RAM模块。

代码实现

代码搬运于网络

// 顶层模块
module  AsyncFIFO#(
    parameter   ADDR_SIZE = 4,
    parameter   DATA_SIZE = 8
)
(
    input       [DATA_SIZE-1:0] wdata,
    input                       winc,
    input                       wclk,
    input                       wrst_n,
    input                       rinc,
    input                       rclk,
    input                       rrst_n,
    output      [DATA_SIZE-1:0] rdata,
    output                      wfull,
    output                      rempty
);

    wire    [ADDR_SIZE-1:0] waddr,raddr;
    wire    [ADDR_SIZE:0]   wptr,rptr,wq2_rptr,rq2_wptr;

    // 时钟同步
    sync_r2w #(
    .ADDR_SIZE(ADDR_SIZE)
    )
    I1_sync_r2w(
        .wq2_rptr(wq2_rptr),
        .rptr(rptr),
        .wclk(wclk),
        .wrst_n(wrst_n)
        );

    sync_w2r #(
    .ADDR_SIZE(ADDR_SIZE)

    )I2_sync_w2r(
        .rq2_wptr(rq2_wptr),
        .wptr(wptr),
        .rclk(rclk),
        .rrst_n(rrst_n)
        );
    // 双口RAM
    DualRAM #(
    .ADDR_SIZE(ADDR_SIZE),
    .DATA_SIZE(DATA_SIZE)
    )I3_DualRAM(
        .rdata(rdata),
        .wdata(wdata),
        .waddr(waddr),
        .raddr(raddr),
        .wclken(winc),
        .wclk(wclk)
        );
    // 空判断
    rptr_empty #(
    .ADDR_SIZE(ADDR_SIZE)
    )I4_rptr_empty(
        .rempty(rempty),
        .raddr(raddr),
        .rptr(rptr),
        .rq2_wptr(rq2_wptr),
        .rinc(rinc),
        .rclk(rclk),
        .rrst_n(rrst_n));
    // 满判断
    wptr_full #(
    .ADDR_SIZE(ADDR_SIZE)
    )I5_wptr_full(
        .wfull(wfull),
        .waddr(waddr),
        .wptr(wptr),
        .wq2_rptr(wq2_rptr),
        .winc(winc),
        .wclk(wclk),
        .wrst_n(wrst_n));
endmodule
// 双口RAM模块
module  DualRAM #(
    parameter       DATA_SIZE = 8,//数据位宽
    parameter       ADDR_SIZE = 4//FIFO地址宽度
    )(
    input       wclken,
    input       wclk,
    input   [ADDR_SIZE-1:0] raddr,
    input   [ADDR_SIZE-1:0] waddr,
    input   [DATA_SIZE-1:0] wdata,
    output  [DATA_SIZE-1:0] rdata
    );


    localparam  RAM_DEPTH = 1<<ADDR_SIZE;//RAM深度,1左移4位为16

    reg [DATA_SIZE-1:0] mem [0:RAM_DEPTH-1];//开辟内存

    always@(posedge wclk) begin
        if(wclken==1'b1) begin
            mem[waddr] <= wdata;
        end
        else begin
            mem[waddr] <= mem[waddr];//保持
        end
    end

    assign rdata = mem[raddr];//给地址直接出数据
endmodule
// 写指针同步到读时钟
module  sync_w2r#(
    parameter       ADDR_SIZE = 4
)
(
    input   [ADDR_SIZE:0] wptr,
    input                 rclk,
    input                 rrst_n,
    output  reg [ADDR_SIZE:0] rq2_wptr
    );

    reg     [ADDR_SIZE:0] rq1_wptr;
    
    //D触发器,两级同步
    always@(posedge rclk or negedge rrst_n) begin
        if(!rrst_n) begin
            {rq2_wptr,rq1_wptr} <=0;
        end
        else begin
            {rq2_wptr,rq1_wptr} <= {rq1_wptr,wptr};
        end
    end
endmodule
// 读指针同步到写时钟
module  sync_r2w#(
    parameter   ADDR_SIZE = 4)
    (
    input   [ADDR_SIZE:0] rptr,
    input                 wclk,
    input                 wrst_n,
    output  reg [ADDR_SIZE:0] wq2_rptr
    );


    reg     [ADDR_SIZE:0] wq1_rptr;
    //D触发器,两级同步
    always@(posedge wclk or negedge wrst_n) begin
        if(!wrst_n) begin
            {wq2_rptr,wq1_rptr} <= 0;
        end
        else begin
            {wq2_rptr,wq1_rptr} <= {wq1_rptr,rptr};
        end
    end
endmodule
// 判空模块
module  rptr_empty#(
    parameter   ADDR_SIZE = 4
    )
(
    output  reg rempty,
    output      [ADDR_SIZE-1:0] raddr,//输出到RAM的读地址
    output  reg [ADDR_SIZE:0]   rptr,//输出到写时钟域的格雷码
    input       [ADDR_SIZE:0]   rq2_wptr,
    input                       rinc,
    input                       rclk,
    input                       rrst_n
    );

    reg     [ADDR_SIZE:0]   rbin;//二进制地址
    wire    [ADDR_SIZE:0]   rgraynext,rbinnext;//二进制和格雷码地址
    wire    rempty_val;

//----------------------------
//地址逻辑
//----------------------------

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

//地址产生逻辑
assign rbinnext = !rempty ?(rbin+rinc):rbin;
assign rgraynext = (rbinnext>>1)^(rbinnext);
assign raddr = rbin[ADDR_SIZE-1:0];


//FIFO判空
assign rempty_val = (rgraynext==rq2_wptr) ;

always@(posedge rclk or negedge rrst_n)begin
    if(!rrst_n)
        rempty <= 1'b1;
    else begin
        rempty <= rempty_val;
    end
end
endmodule
// 判满模块

module wptr_full#(
    parameter   ADDR_SIZE = 4
    )
(
    output  reg                 wfull,
    output      [ADDR_SIZE-1:0] waddr,
    output  reg [ADDR_SIZE:0]   wptr,
    input       [ADDR_SIZE:0]   wq2_rptr,
    input                       winc,
    input                       wclk,
    input                       wrst_n
    );

    reg [ADDR_SIZE:0]   wbin;
    wire    [ADDR_SIZE:0]   wbinnext;
    wire    [ADDR_SIZE:0]   wgraynext;

    wire    wfull_val;

    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         

    //地址逻辑
    assign wbinnext = !wfull?(wbin + winc):wbin;
    assign wgraynext = (wbinnext>>1)^wbinnext;
    assign waddr = wbin[ADDR_SIZE-1:0];
    
    //判满
    assign wfull_val = (wgraynext=={~wq2_rptr[ADDR_SIZE:ADDR_SIZE-1],wq2_rptr[ADDR_SIZE-2:0]});//最高两位取反,然后再判断
    always@(posedge wclk or negedge wrst_n)begin
        if(!wrst_n)
            wfull <=0;
        else begin
            wfull <= wfull_val;
         end 
    end
endmodule

异步FIFO最小深度计算方法及原理

转载自https://blog.csdn.net/bleauchat/article/details/89103976

计算 FIFO 深度是设计 FIFO 中常遇到的问题。常识告诉我们,当读速率慢于写速率时(瞬时速率),FIFO 便可被用作系统中的缓冲元件或队列。FIFO 的大小取决于读写数据的速率,系统的数据速率取决于系统的负载能力,因此为了保证系统性能,我们需要考虑 FIFO 传输的最坏情况。所谓最坏的情况就是使得写速率最大,读速率最小的时候考虑突发传输

计算原理

FIFO用于缓存数据流,一般用在写快读慢突发传输的情况,遵循的规则如下:

即是确保对FIFO写数据时不存在 Overflow 。

例1:
A/D 采样率50MHz,DSP 读 A/D 的速率40MHz,要不丢失地将10万个采样数据送入 DSP,在 A/D 在和 DSP 之间至少设置多大容量的FIFO才行?
100,000/50MHz = 1/500s = 2ms (数据包传送时间)
(50MHz−40MHz)∗1/500 = 20k(50MHz−40MHz)∗1/500 = 20k,即是 FIFO 深度;

常用计算公式

这里假设读写 FIFO 是可以同时进行的,
写时钟频率 w_clk,
读时钟频率 r_clk,
写时钟周期里,每 B 个时钟周期会有 A 个数据写入 FIFO,
读时钟周期里,每 Y 个时钟周期会有 X 个数据读出 FIFO, (XY不给则默认每个时钟读一个数据)
则 FIFO 的最小深度的计算公式如下:

此公式可从上面原理推导而来;
写入速率为w_clk,这里我觉得应该是(A/B) * w_clk;
burst_length / w_clk为一次突发写入的时间;
读出速率为(X/Y) * r_clk,单位时间读取的数据;

例2:
如果100个写时钟周期可以写入80个数据,10个读时钟可以读出8个数据。令w_clk=r_clk ,考虑背靠背 (20个clk不发数据+80clk发数据+80clk发数据+20个clk不发数据的200个clk) ,代入公式可计算 FIFO 的深度,
fifo_depth = 160-160*80% = 160 - 128 = 32

这里100个写时钟可以写入80个数据,不是只A和B,我的理解是A/B还是1,100个时钟中有20个时钟空闲。所以这里的最极端情况,就是burst最长的情况,就是背靠背,第一次前20空,第二次后20空,连续发了160个。

如果令w_clk=200MHz,改为100个w_clk里写入40个,r_clk=100MHz,10个r_clk里读出8个,那么 FIFO 深度为48。计算如下,
fifo_depth = 80 - 80 * 80% * (100/200) = 80 - 32 = 48


两者相除自然就是这段时间读出的数据量。显然burst_length表示这段时间写入的数据量,两者的差为 FIFO 中残留的数据,这个也就是理论上的 FIFO 的最小深度;

读写 FIFO 不是同时进行的情况下

假如读写 FIFO 不是同时进行的,FIFO 深度就是写数据最大突发个数;因为FIFO有判满不让下一次写。

异步FIFO最小深度计算例子

例3:
如两个异步时钟域数据接口,假如读写是同时进行的,一般设置 FIFO 的深度就要对应两个时钟以及对应写最大的突发数据。假设写时钟频率是40MHz,读时钟为25MHz,在写端最大突发写数据个数为100个数据。对应深度计算:100-100/40*25=37.5,对应深度设置至少为38;

假如读写不是同时的,这就需要设置深度为写数据最大突发个数,如上例中,对应最大突发个数为100个,则深度设置为100;

例4:
一个8bit宽的异步 FIFO,输入时钟为100MHz,输出时钟为95MHz,设一个 frame 为4Kbit,且两个 frame 之间的发送间距足够大。求FIFO的最小深度?
burst_length = 4K/8 = 4*1024/8 = 4096/8 = 512。

因为X和Y的值没有给出,所以默认为1。
fifo_depth = 512 - 512*(95/100) = 25.6 ,所以fifo_depth最小取值是26;

流量平衡

为了保证输入数据(负载)全部通过,输出吞吐量要大于输入吞吐量;但也不要太大,以免设计过剩。
所以有,
(r_clk∗X/Y)=(w_clk∗A/B)

等式左边是系统设计吞吐量,右边是负载流量。
例5:
两个异步时钟域数据接口,写时钟频率是19MHz,读时钟为20MHz,读写是同时进行的,输入数据不间断。求FIFO的最小深度?
显然有,
( r_clk * X/Y ) > ( w_clk * A/B )
故理论上FIFO的最小深度是1;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容

  • 最近在研究200w像素的相机。这里需要30fps,像素2304*1440对于时钟的要求就达到了120Mhz 对于整...
    彩彩_cc阅读 1,059评论 0 0
  • 本文首发于个人博客 1.设计目标 设计一个参数可配置的异步FIFO,要求: FIFO深度从4开始在2的幂次方连续可...
    月见樽阅读 1,946评论 0 0
  • by yang 最近准备数字IC岗时复习整理的知识点,参考了比较火的fpga面试题,和一些相关知识。主要是写着自己...
    Yuhan尽量笑不露齿阅读 7,998评论 0 4
  • 异步FIFO通过比较读写地址进行满空判断,但是读写地址属于不同的时钟域,所以在比较之前需要先将读写地址进行同步处理...
    li_li_li_1202阅读 3,012评论 0 1
  • FIFO即First In First Out,是一种先进先出数据存储、缓冲器,我们知道一般的存储器是用外部的读写...
    li_li_li_1202阅读 4,996评论 0 5