文章目录

  • FIFO简介
  • 使用场景
  • 类别
    • 同步FIFO
    • 异步FIFO
  • 参数
    • FIFO宽度
    • FIFO深度
    • 满指标
    • 空指标
    • 读时钟
    • 写时钟
  • 实现
    • 同步FIFO实现
      • 1、计数器法
        • 计数器法verilog代码
      • 2、高位扩展法
        • 高位扩展法verilog代码
      • 实验仿真
        • TestBench代码
        • 波形示意图
    • 异步FIFO实现
      • 异步产生亚稳态问题
      • gray码判断空满问题
        • 空判断
        • 满判断
      • 设计总体实现
        • 顶层设计
          • RAM存储器模块
          • FIFO写地址以及写满判断模块
          • FIFO读地址以及读空判断模块
          • FIFOF写时钟同步到读时钟模块
          • FIFO读时钟同步到写时钟模块
      • 实验仿真
      • 总结

FIFO简介

先进先出的数据缓存器,与普通存储器的区别是没有外部读写地址线,使用方便,缺点是只能顺序读写,不能随机读写。其数据地址由内部读写指针自动加 1 完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。

使用场景

1、数据缓冲:也就是数据写入过快,并且间隔时间长,也就是突发写入。那么通过设置一定深度的FIFO,可以起到数据暂存的功能,且使得后续处理流程平滑。

2、时钟域的隔离:主要用异步FIFO。对于不同时钟域的数据传输,可以通过FIFO进行隔离,避免跨时钟域的数据传输带来的设计和约束上的复杂度。比如FIFO的一端是AD,另一端是PCI;AD的采集速率是16位100KSPS,每秒的数据量是1.6Mbps。而PCI总线的速度是33MHz,总线宽度是32位

3、用于不同宽度的数据接口

类别

同步FIFO

读写时钟为同一个时钟

异步FIFO

读写时钟不为同一个时钟

参数

FIFO宽度

FIFO一次读写操作的数据位

FIFO深度

指FIFO可以存储多少个N位的数据(如果宽度为N)

满指标

FIFO已满或将要满时由FIFO的状态电路送出的一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成溢出(overflow)

空指标

FIFO已空或将要空时由FIFO的状态电路送出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出(underflow)

读时钟

读操作所遵循的时钟,在每个时钟沿来临时读数据

写时钟

写操作所遵循的时钟,在每个时钟沿来临时写数据

实现

同步FIFO实现

FIFO 读写指针(读写指针就是读写地址)的工作原理:

  • 写指针:总是指向下一个将要被写入的单元,复位时,指向第 1 个单元(编号为 0)
  • 读指针:总是指向当前要被读出的数据,复位时,指向第 1 个单元(编号为 0) FIFO 的“空”/“满”检测

FIFO 设计的关键:产生可靠的 FIFO 读写指针和生成 FIFO“空”/“满”状态标志。

当读写指针相等时,表明 FIFO 为空,这种情况发生在复位操作时,或者当读指针读出 FIFO 中最后一 个字后,追赶上了写指针时。

当读写指针再次相等时,表明 FIFO 为满,这种情况发生在,当写指针转了一圈,折回来(wrapped around) 又追上了读指针。

1、计数器法

构建一个计数器,该计数器(fifo_cnt)用于指示当前 FIFO 中数据的个数:

  • 复位时,该计数器为0,FIFO中的数据个数为0
  • 当读写使能信号均有效时,说明又读又写,计数器不变,FIFO中的数据个数无变化
  • 当写使能有效且 full=0,则 fifo_cnt +1;表示写操作且 FIFO 未满时候,FIFO 中的数据个数增加了 1
  • 当读使能有效且 empty=0,则 fifo_cnt -1;表示读操作且 FIFO 未满时候,FIFO 中的数据个数减少了 1
  • fifo_cnt =0 的时候,表示 FIFO 空,需要设置 empty=1;fifo_cnt = fifo的深度 的时候,表示 FIFO 现在已经满,需要设置 full=1

计数器法verilog代码

//计数器法实现同步FIFO
module  sync_fifo_cnt
#(parameter   DATA_WIDTH = 8  ,                            //FIFO位宽parameter   DATA_DEPTH = 16                            //FIFO深度
)
(input                                  clk     ,       //系统时钟input                                 rst_n   ,       //低电平有效的复位信号input   [DATA_WIDTH-1:0]                data_in ,       //写入的数据input                                    rd_en   ,       //读使能信号,高电平有效input                                   wr_en   ,       //写使能信号,高电平有效output  reg [DATA_WIDTH-1:0]            data_out,       //输出的数据output                                   empty   ,       //空标志,高电平表示当前FIFO已被写满output                                  full    ,       //满标志,高电平表示当前FIFO已被读空output  reg [$clog2(DATA_DEPTH) : 0]    fifo_cnt        //$clog2是以2为底取对数
);//reg define
reg [DATA_WIDTH - 1 : 0] fifo_buffer[DATA_DEPTH - 1 : 0];   //用二维数组实现RAM
reg [$clog2(DATA_DEPTH) - 1 : 0]    wr_addr;                //写地址
reg [$clog2(DATA_DEPTH) - 1 : 0]    rd_addr;                //读地址//读操作,更新读地址
always @ (posedge clk or negedge rst_n) beginif (!rst_n)rd_addr <= 0;else if (!empty && rd_en)begin                            //读使能有效且非空rd_addr <= rd_addr + 1'd1;data_out <= fifo_buffer[rd_addr];end
end
//写操作,更新写地址
always @ (posedge clk or negedge rst_n) beginif (!rst_n)wr_addr <= 0;else if (!full && wr_en)begin                         //写使能有效且非满wr_addr <= wr_addr + 1'd1;fifo_buffer[wr_addr]<=data_in;end
end
//更新计数器
always @ (posedge clk or negedge rst_n) beginif (!rst_n)fifo_cnt <= 0;else begincase({wr_en,rd_en})                                    //拼接读写使能信号进行判断2'b00:fifo_cnt <= fifo_cnt;                      //不读不写2'b01:                                           //仅仅读if(fifo_cnt != 0)                         //fifo没有被读空fifo_cnt <= fifo_cnt - 1'b1;            //fifo个数-12'b10:                                           //仅仅写if(fifo_cnt != DATA_DEPTH)                    //fifo没有被写满fifo_cnt <= fifo_cnt + 1'b1;               //fifo个数+12'b11:fifo_cnt <= fifo_cnt;                     //读写同时default:;                                 endcaseend
end
//依据计数器状态更新指示信号
//依据不同阈值还可以设计半空、半满 、几乎空、几乎满
assign full  = (fifo_cnt == DATA_DEPTH) ? 1'b1 : 1'b0;     //空信号
assign empty = (fifo_cnt == 0)? 1'b1 : 1'b0;               //满信号endmodule

其中计数器和读写地址以为为底去对数的原因在于用多少位的二进制表示深度。

2、高位扩展法

采取扩展最高位与读写地址位共同判断是为满或为空。

  • 当最高位不同,且其他位相同,则表示读指针或者写指针多跑了一圈,而显然不会让读指针多跑一圈(多跑一圈读啥?),所以可能出现的情况只能是写指针多跑了一圈,与就意味着FIFO被写满了
  • 当最高位相同,且其他位相同,则表示读指针追到了写指针或者写指针追到了读指针,而显然不会让写指针追读指针(这种情况只能是写指针超过读指针一圈),所以可能出现的情况只能是读指针追到了写指针,也就意味着FIFO被读空了

高位扩展法示意图:

高位扩展法verilog代码

module   sync_fifo_ptr
#(parameter   DATA_WIDTH = 'd8  ,                             //FIFO位宽parameter   DATA_DEPTH = 'd16                                 //FIFO深度
)
(input                                      clk     ,       //系统时钟input                                     rst_n   ,       //低电平有效的复位信号input   [DATA_WIDTH-1:0]                    data_in ,       //写入的数据input                                        rd_en   ,       //读使能信号,高电平有效input                                       wr_en   ,       //写使能信号,高电平有效output  reg [DATA_WIDTH-1:0]                data_out,       //输出的数据output                                       empty   ,       //空标志,高电平表示当前FIFO已被写满output                                      full            //满标志,高电平表示当前FIFO已被读空
);                                                              //reg define
//用二维数组实现RAM
reg [DATA_WIDTH - 1 : 0]            fifo_buffer[DATA_DEPTH - 1 : 0];
reg [$clog2(DATA_DEPTH) : 0]        wr_ptr;                     //写地址指针,位宽多一位
reg [$clog2(DATA_DEPTH) : 0]        rd_ptr;                     //读地址指针,位宽多一位    //wire define
wire [$clog2(DATA_DEPTH) - 1 : 0]   wr_ptr_true;                //真实写地址指针
wire [$clog2(DATA_DEPTH) - 1 : 0]   rd_ptr_true;                //真实读地址指针
wire                                wr_ptr_msb;                 //写地址指针地址最高位
wire                                rd_ptr_msb;                 //读地址指针地址最高位assign {wr_ptr_msb,wr_ptr_true} = wr_ptr;                      //将最高位与其他位拼接
assign {rd_ptr_msb,rd_ptr_true} = rd_ptr;                      //将最高位与其他位拼接//读操作,更新读地址
always @ (posedge clk or negedge rst_n) beginif (rst_n == 1'b0)rd_ptr <= 'd0;else if (rd_en && !empty)begin                                //读使能有效且非空data_out <= fifo_buffer[rd_ptr_true];rd_ptr <= rd_ptr + 1'd1;end
end
//写操作,更新写地址
always @ (posedge clk or negedge rst_n) beginif (!rst_n)wr_ptr <= 0;else if (!full && wr_en)begin                              //写使能有效且非满wr_ptr <= wr_ptr + 1'd1;fifo_buffer[wr_ptr_true] <= data_in;end
end//更新指示信号
//当所有位相等时,读指针追到到了写指针,FIFO被读空
assign  empty = ( wr_ptr == rd_ptr ) ? 1'b1 : 1'b10;
//当最高位不同但是其他位相等时,写指针超过读指针一圈,FIFO被写满
assign  full  = ( (wr_ptr_msb != rd_ptr_msb ) && ( wr_ptr_true == rd_ptr_true ) )? 1'b1 : 1'b0;endmodule

实验仿真

TestBench代码

`timescale  1ns / 1psmodule tb_sync_fifo_ptr;// sync_fifo_ptr Parameters
parameter PERIOD      = 5  ;
parameter DATA_WIDTH  = 'd8 ;
parameter DATA_DEPTH  = 'd16;// sync_fifo_ptr Inputs
reg   clk                                  = 0 ;
reg   rst_n                                = 0 ;
reg   [DATA_WIDTH-1:0]  data_in            = 0 ;
reg   rd_en                                = 0 ;
reg   wr_en                                = 0 ;// sync_fifo_ptr Outputs
wire  [DATA_WIDTH-1:0]  data_out           ;
wire  empty                                ;
wire  full                                 ;initial
beginforever #(PERIOD/2)  clk=~clk;
endinitial
begin#(PERIOD*2) rst_n  =  1;
endsync_fifo_ptr #(.DATA_WIDTH ( DATA_WIDTH ),.DATA_DEPTH ( DATA_DEPTH ))u_sync_fifo_ptr (.clk                     ( clk                        ),.rst_n                   ( rst_n                      ),.data_in                 ( data_in   [DATA_WIDTH-1:0] ),.rd_en                   ( rd_en                      ),.wr_en                   ( wr_en                      ),.data_out                ( data_out  [DATA_WIDTH-1:0] ),.empty                   ( empty                      ),.full                    ( full                       )
);
reg [7:0] tempdata = 0;
initial
begin$dumpfile("wave.vcd");        //生成的vcd文件名称$dumpvars(0, tb_sync_fifo_ptr);    //tb模块名称clk = 0;rst_n = 0;wr_en = 0;rd_en = 0;data_in = 0;#10rst_n = 1;push(1);forkpush(2);pop(tempdata);join              //push and pop together   push(10);push(20);push(30);push(40);push(50);push(60);push(70);push(80);push(90);push(100);push(110);push(120);push(130);pop(tempdata);push(tempdata);pop(tempdata);pop(tempdata);pop(tempdata);pop(tempdata);push(140);pop(tempdata);push(tempdata);//pop(tempdata);pop(tempdata);pop(tempdata);pop(tempdata);pop(tempdata);pop(tempdata);pop(tempdata);pop(tempdata);pop(tempdata);pop(tempdata);pop(tempdata);push(5);pop(tempdata);$finish;
endtask push (input [7:0] data);if(full)$display("---Cannot push %d: Buffer Full---",data);else begin$display("Push",data);data_in = data;wr_en = 1;@(posedge clk);#5 wr_en = 0;endendtasktask pop(output[7:0] data);if(empty)$display("---Cannot Pop: Buffer Empty---");else beginrd_en = 1;@(posedge clk);#3 rd_en = 0;data = data_out;$display("------Poped:",data);end      endtask
endmodule

波形示意图

异步FIFO实现

异步产生亚稳态问题

异步FIFO需要考虑的是亚稳态的问题,如果采用二进制数的计数值从一个时钟域到另一个时钟域的时候就会出问题,因为二进制计数器的所有位都有可能发生变化。使用格雷码的话只有一位变化,在两个时钟域间的同步多个位不会产生问题。所以需要一个二进制到gray码的转换电路,将地址值转换成相应的gray码,然后将gray码同步到另一个时钟域进行对比,作为空满状态的检测。

gray码判断空满问题

空判断

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

满判断

因为gray码的特性,与同步不同的是MSB最高位不同时,其他位相同,不能作为FIFO满的判断。

在gray码上判断为满必须同时满足以下3条:

  • wptr和同步过来的rptr的MSB不相等,因为wptr必须比rptr多折回一次。
  • wptr与rptr的次高位不相等,如上图位置7和位置15,转化为二进制对应的是0111和1111,MSB不同说明多折回一次,111相同代表同一位置。
  • 剩下的其余位完全相等。

设计总体实现

代码:

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, raddr;wire [ASIZE:0]   wptr, rptr, wq2_rptr, rq2_wptr; /*在检测“满”或“空”状态之前,需要将指针同步到其它时钟域时,使用格雷码,可以降低同步过程中亚稳态出现的概率*/sync_r2w#(.ADDRSIZE ( 4 ))u_sync_r2w(.wq2_rptr ( wq2_rptr ), //out.rptr     ( rptr     ),.wclk     ( wclk     ),.wrst_n   ( wrst_n   ));sync_w2r#(.ADDRSIZE ( 4 ))u_sync_w2r(.rq2_wptr ( rq2_wptr ),.wptr     ( wptr     ),.rclk     ( rclk     ),.rrst_n   ( rrst_n   ));fifomem#(.DATASIZE ( 8 ),.ADDRSIZE ( 4 ))u_fifomem(.wclken   ( wclken   ),.wfull    ( wfull    ),.wclk     ( wclk     ),.wdata    ( wdata    ),.waddr    ( waddr    ),.raddr    ( raddr    ),.rdata    ( rdata    ));rptr_empty#(.ADDRSIZE ( 4 ))u_rptr_empty(.rempty   ( rempty   ),.raddr    ( raddr    ),.rptr     ( rptr     ),.rq2_wptr ( rq2_wptr ),.rinc     ( rinc     ),.rclk     ( rclk     ),.rrst_n   ( rrst_n   ));wptr_full#(.ADDRSIZE ( 4 ))u_wptr_full(.wfull    ( wfull    ),.waddr    ( waddr    ),.wptr     ( wptr     ),.wq2_rptr ( wq2_rptr ),.winc     ( winc     ),.wclk     ( wclk     ),.wrst_n   ( wrst_n   ));endmodule

顶层设计

信号描述

信号名称 位宽 信号描述
Wdata 数据位宽 写入数据
Wfull 1 写满信号
Winc 1 写请求信号(写使能信号)
Wclk 1 写时钟
Wrst_n 1 写复位信号(低电平有效
Rdata 数据位宽 读出数据
Rempty 1 读空信号
Rinc 1 读请求信号(读使能信号)
Rrst_n 1 读复位信号(低电平有效)
RAM存储器模块

信号描述:

信号名称 位宽 信号描述
wclken 1 写使能信号
wclk 1 写时钟信号
raddr 地址位宽 读地址
waddr 地址位宽 写地址
wdata 数据位宽 写入的数据
rdata 数据位宽 读数据
wfull 1 写满信号

代码:

根据上面RAM的总概图,定义的一个宽度为8位,深度为8的RAM(即定义了8个寄存器,每个寄存器的宽度为8)

module fifomem
#(parameter  DATASIZE = 8, // Memory data word width               parameter  ADDRSIZE = 4  // 指针地址位宽设置为4,因为8=2^3,应该加一判断写满或读空
) // Number of mem address bits
(input                 wclken, wfull, wclk,input  [DATASIZE-1:0] wdata, //write datainput  [ADDRSIZE-1:0] waddr, raddr, output [DATASIZE-1:0] rdata //read data
);// RTL Verilog memory modellocalparam DEPTH = 1<<ADDRSIZE;   // leftshift is equal divide tworeg [DATASIZE-1:0] mem [0:DEPTH-1];always @(posedge wclk) begin //当使能信号有效且还未写满的时候将数据写入实体中,与wclk时钟信号同步if (wclken && !wfull)mem[waddr] <= wdata;endassign rdata = mem[raddr];endmodule
FIFO写地址以及写满判断模块

主要是控制是否可以写入数据,写指针与写满的顶层模块图如图所示:

信号描述:

信号名称 位宽 信号描述
Winc 1 写请求信号
Wclk 1 写时钟信号
Wrst_n 1 写复位信号
Wq2_rptr 地址位宽+1 同步之后的读指针(格雷码形式)
Wfull 1 写满信号
Waddr 地址位宽 二进制形式的写地址
Wptr 地址位宽+1 格雷码形式的写指针

模块作用:当时钟信号来临时且写请求信号有效时,写一组数据,并且同时写地址向下加一位,然后讲写地址转化为格雷码,判断是否写满

写满的判断:写地址指针再次追上读地址指针

二进制转格雷码:

​ 二进制的最右一位(最低位)起,依次将每一位与左边一为进行异或操作,作为格雷码该位的值,而最左一位不变。

代码:

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 pointeralways @(posedge wclk or negedge wrst_n) begin if (!wrst_n){wbin, wptr} <= 0;   else         {wbin, wptr} <= {wbinnext, wgraynext};end// Memory write-address pointer (okay to use binary to address memory) assign waddr      = wbin[ADDRSIZE-1:0]; assign wbinnext   = wbin + (winc & ~wfull);//写请求且没写满时,地址加一assign wgraynext  = (wbinnext>>1) ^ wbinnext; //将二进制码转换为格雷码//最高位表示多这一次,但由于格雷码特性,次高位也需要不同,其余位需要相同assign wfull_val = (wgraynext=={~wq2_rptr[ADDRSIZE:ADDRSIZE-1],wq2_rptr[ADDRSIZE-2:0]}); always @(posedge wclk or negedge wrst_n) beginif (!wrst_n)wfull  <= 1'b0;   else     wfull  <= wfull_val;endendmodule
FIFO读地址以及读空判断模块

读控制端口主要用于是否可以读取数据,读指针与读空的顶层模块图如下图:

信号描述:

信号名称 位宽 信号描述
Rinc 1 读请求信号
Rclk 1 读时钟信号
Rrst_n 1 读复位信号
Rq2_rptr 地址位宽+1 同步之后的写指针(格雷码形式)
Rempty 1 读空信号
Raddr 地址位宽 二进制形式的读地址
Rptr 地址位宽+1 格雷码形式的读指针

**作用:**当时钟信号来且读请求信号有效时,读出一组数据,并且同时读地址向下加一位。然后将读地址转换为格雷码,判断是否为读空。

代码:

1、寄存二进制地址,方便转换成格雷码

2、根据读使能以及是否为空,赋值下一地址

3、每到上升沿采样将下一拍地址传给当前地址变量

4、当同步之后的写指针与读指针(格雷码形式)相同时说明为空

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) beginif (!rrst_n) begin rbin <= 0;rptr <= 0;endelsebeginrbin <= rbinnext ; rptr <= rgraynext;endend
// gray码计数逻辑assign raddr = rbin[ADDRSIZE-1:0];assign rbinnext  = rbin + (rinc & ~rempty); //不空且有读请求的时候读地址加1assign rgraynext = (rbinnext>>1) ^ rbinnext;      //二进制到gray码的转换
//---------------------------------------------------------------
// 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
FIFOF写时钟同步到读时钟模块

示意图如下:

信号名称 位宽 信号描述
Rclk 1 读时钟信号
Rrst_n 1 读复位信号
Wptr 地址位宽+1 写指针地址
Rq2_wptr 地址位宽+1 同步写指针地址

代码:

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) beginif (!rrst_n){rq2_wptr,rq1_wptr} <= 0;else begin // {rq2_wptr,rq1_wptr} <= {rq1_wptr,wptr};rq1_wptr <= wptr;rq2_wptr <= rq1_wptr;endendendmodule
FIFO读时钟同步到写时钟模块

示意图如下:

信号名称 位宽 信号描述
Wclk 1 写时钟信号
Wrst_n 1 写复位信号
Rptr 地址位宽+1 读指针地址
Wq2_rptr 地址位宽+1 同步读指针地址

代码:

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) beginif (!wrst_n) {wq2_rptr,wq1_rptr} <= 0;else {wq2_rptr,wq1_rptr} <= {wq1_rptr,rptr};end
endmodule

实验仿真

1、输入信号初始化

2、FIFO复位

3、写读时钟

4、读写控制(写数据、读数据)

TestBench代码:

`timescale  1ns / 1psmodule tb_AsyncFIFO;// AsyncFIFO Parametersparameter ASIZE  = 4;
parameter DSIZE  = 8;// AsyncFIFO Inputs
reg   [DSIZE-1:0]  wdata                   = 0 ;
reg   winc                                 = 0 ;
reg   wclk                                 = 0 ;
reg   wrst_n                               = 0 ;
reg   rinc                                 = 0 ;
reg   rclk                                 = 0 ;
reg   rrst_n                               = 0 ;// AsyncFIFO Outputs
wire  [DSIZE-1:0]  rdata                   ;
wire  wfull                                ;
wire  rempty                               ;initial
begin//输入信号初始化wrst_n = 1;rrst_n = 1;wclk = 0;rclk = 0;winc = 0;rinc = 0;wdata = 0;//raddr = 0;//复位信号# 30 wrst_n = 0;rrst_n = 0;#30wrst_n = 1;rrst_n = 1;end
always //写时钟#2 wclk = ~wclk;
always //读时钟#4 rclk = ~rclk;always @(*) beginif(!wfull) beginwinc = 1;endelse beginwinc =0;end
endalways @(*) beginif(!rempty) beginrinc = 1;endelse beginrinc =0;end
endalways @(posedge wclk) beginif(!wfull) beginwdata <= wdata + 1;end else beginwdata <= wdata;end
endAsyncFIFO #(.ASIZE ( ASIZE ),.DSIZE ( DSIZE ))u_AsyncFIFO (.wdata                   ( wdata   ),.winc                    ( winc                ),.wclk                    ( wclk                ),.wrst_n                  ( wrst_n              ),.rinc                    ( rinc                ),.rclk                    ( rclk                ),.rrst_n                  ( rrst_n              ),.rdata                   ( rdata ),.wfull                   ( wfull               ),.rempty                  ( rempty              )
);initial
begin$dumpfile("wave_test.vcd");        //生成的vcd文件名称$dumpvars(0, tb_AsyncFIFO);    //tb模块名称#1000 $stop;//$finish;
endendmodule

仿真的波形图:

总结

异步fifo的重点,第一是对读写信号的同步,主要用两级缓存对读写信号的同步,第二是对写满信号的判断需要二进制到格雷码之间的转换

IC基础——FIFO相关推荐

  1. IC基础知识(4)电源管理简介:稳压器IC

    文章目录 写在前面 正文 线性与开关 线性稳压器 开关稳压器 其他类型的功率IC 结论 写在前面 原文链接:Introduction to Power Management: Voltage Regu ...

  2. IC基础知识(3)通用模拟,数字和混合信号集成电路

    文章目录 写在前面 正文 数字IC 逻辑 内存 处理器 模拟IC 混合信号IC 结论 交个朋友 写在前面 原文链接:Common Analog, Digital, and Mixed-Signal I ...

  3. 【IC基础】集成电路设计领域术语缩写及名词解释(字母索引版)

    前言: 笔者在大三上学习学习<SoC设计导论>时整理的有关集成电路设计领域的常见有英文缩写和对应的名词解释,文中标注的页码均出自<SoC设计方法与实现>这本参考书: 目录 目录 ...

  4. FPGA/IC基础知识

    1.简述建立时间和保持时间 建立时间Tsu(setup):触发器在时钟上升沿到来之前,其数据输入端的数据必须保持不变的最小时间. 保持时间Th(hold):触发器在时钟上升沿到来之后,其数据输入端的数 ...

  5. IC基础——CDC(单bit)

    CDC基础 0.亚稳态概念 1.CDC(clock domain crossing)基本概念 2.两种典型的跨时钟域处理 3.时序分析 4.总结 参考资料 0.亚稳态概念 亚稳态:亚稳态是设计正常运行 ...

  6. IC基础知识(2)模拟和数字电子学导论

    文章目录 写在前面 正文 模拟世界 数字系统 模拟和数字IC 概括 交个朋友 写在前面 该教程探讨了重要的主题,这些主题使我们可以将集成电路分为两大类. 承接上一篇:集成电路简介 原文地址:Intro ...

  7. IC基础知识(1)集成电路(IC)简介

    文章目录 写在前面 正文 什么是集成电路? IC中有什么? IC封装类型 结论 写在前面 在最前面还是分享下一个英文网站吧,挺不错的教程网站,觉得一些知识讲解的还算吸引人,为了阅读起来没那么障碍,这里 ...

  8. IC基础知识(六)SV中default input #1 output #1的解释

    目录 1. clocking-endclocking块 2. clocking shew的含义 3. 实例代码 4. clocking event 5. clock cycle延时 '##' 6. d ...

  9. IC基础知识(十一)时钟周期、状态周期、机器周期、指令周期和总线周期的定义及关系

    目录 ​ ​1.时钟周期.振荡周期.节拍周期 2.状态周期 3.机器周期.CPU周期 4.指令周期 5.总线周期 总结 微信公众号 ​1.时钟周期.振荡周期.节拍周期 时钟周期又叫做振荡周期.节拍周期 ...

最新文章

  1. 中国电子学会青少年编程能力等级测试图形化四级编程题:打篮球
  2. bzoj 1233: [Usaco2009Open]干草堆tower【dp+单调栈】
  3. c语言学习之用筛选法求100之内的素数。
  4. HDU - 5459 Jesus Is Here(思维+非线性递推)
  5. 一文搞懂IT基础知识,讲通HTTP、TCP、IP、以太网
  6. HTTP缓存解释为何页面响应的数据和服务端的不一致
  7. [圣诞记]HULK七周年庆
  8. 东哥再见!我打算 6 月份离职
  9. python 代码片段24
  10. 计算机应用及发展,计算机应用及发展趋势.docx
  11. authorware学习
  12. Mac VM 虚拟机固定IP
  13. Connect Four四子棋c++程序 - 用户交互(1)
  14. 使用jQuery easyui和Springdata JPI进行数据的查询
  15. 大厂技术实现 | 爱奇艺短视频推荐业务中的多目标优化实践 @推荐与计算广告系列
  16. 计算机大专物联网专业学什么好,物联网应用技术专业介绍(专科)
  17. 使用STM32C103C8T6的注意点
  18. Shell 中的真与假
  19. 模数转换,你必须知道的8个经典ADC转换电路方案
  20. pythonjson安装_安装pip和json

热门文章

  1. matlab 变分贝叶斯,变分法和变分贝叶斯推断
  2. MES生产调度管理系统源码 MES系统源码
  3. java火焰纹章攻略女神之剑_火焰纹章晓之女神图文攻略(4)
  4. 搜狗输入法中文状态下开启和关闭英文自动提示
  5. 学习Chrome Devtools 调试
  6. 安利一个程序员学习法
  7. python修改文件格式为jpg_利用python将webp文件转化为jpg文件,将png文件修改为jpg,图像重命名...
  8. 深度学习 端对端的车牌检测与识别 LPDR算法 License Plate Detection and Recognition CCPD
  9. Word 打字技巧——U模式和V模式
  10. 步进电机加速的c语言编程,关于步进电机S型加速程序编写