异步FIFO总结

异步FIFO的基本概念


异步FIFO读写分别采用相互异步的不同时钟,使用异步FIFO可以在两个不同时钟系统之间快速而方便地传输实时数据

FIFO的常见参数


FIFO的宽度:即FIFO一次读写操作的数据位;

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

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

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

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

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

异步FIFO的设计难点


如何同步异步信号,使触发器不产生亚稳态

如何正确地设计空、满以及几乎满等信号的控制电路

亚稳态问题的解决


对写地址/读地址采用格雷码

采用触发器来同步异步输入信号

空/满标志的产生


空/满标志产生的原则是:写满不溢出,读空不多读

FIFO的缓冲空间是有限的,在设计中需要考虑缓冲区的溢出和空情况。

缓冲区满,产生FULL信号,以阻塞数据继续向FIFO写入;

缓冲区空,产生EMPTY信号,以阻塞数据继续从FIFO读出。

方法:

1.采用握手协议

2.读时钟采样写指针,写时钟采样读指针,采样的写指针和读指针判断是否为EMPTY,采样的读指针和写指针判断是否为FULL

Gray code counter


FIFO 框图




VCS仿真结果


异步FIFO代码(verilog)


RTL描述


fifoif.v

module fifoif
(
fifo_flush,
data_out,
full_out,
empty_out,
data_in,
wren_in, wclk, wclr_in,
rden_in, rclk, rclr_in,
fifo_waddr, fifo_raddr,
ram_douta,
ram_dinb,
ram_ada,
ram_adb,
ram_cena,
ram_cenb,
ram_clka,
ram_clkb
); parameter DSIZE = 32;
parameter ASIZE = 7; input fifo_flush;
output [DSIZE-1:0] data_out;
output full_out;
output empty_out;
input [DSIZE-1:0] data_in;
input wren_in, wclk, wclr_in;
input rden_in, rclk, rclr_in;
output [ASIZE-1:0] fifo_waddr, fifo_raddr; input [DSIZE-1:0] ram_douta;
output [DSIZE-1:0] ram_dinb;
output [ASIZE-1:0] ram_ada;
output [ASIZE-1:0] ram_adb;
output ram_cena;
output ram_cenb;
output ram_clka;
output ram_clkb; wire [DSIZE-1:0] ram_douta;
wire [DSIZE-1:0] ram_dinb;
wire [ASIZE-1:0] ram_ada;
wire [ASIZE-1:0] ram_adb;
wire ram_cena;
wire ram_cenb;
wire ram_clka;
wire ram_clkb; wire [DSIZE-1:0] data_out;
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;
wire [ASIZE-1:0] fifo_waddr = waddr;
wire [ASIZE-1:0] fifo_raddr = raddr; sync_r2w #(ASIZE) u_sync_r2w(
.wq2_rptr (wq2_rptr),
.rptr (rptr),
.wclk (wclk),
.wrst_n (wclr_in),
.wflush (fifo_flush)); sync_w2r #(ASIZE) u_sync_w2r(
.rq2_wptr (rq2_wptr),
.wptr (wptr),
.rclk (rclk),
.rrst_n (rclr_in),
.rflush (fifo_flush)); fifomem #(DSIZE, ASIZE) u_fifomem(
.rdata (data_out),
.wdata (data_in),
.waddr (waddr),
.raddr (raddr),
.wren (wren_in),
.wclk (wclk),
.rden (rden_in),
.rclk (rclk),
.ram_douta (ram_douta),
.ram_dinb (ram_dinb),
.ram_ada (ram_ada),
.ram_adb (ram_adb),
.ram_cena (ram_cena),
.ram_cenb (ram_cenb),
.CLKA (ram_clka),
.CLKB (ram_clkb)
); rptr_empty #(ASIZE) u_rptr_empty(
.rempty (empty_out),
.raddr (raddr),
.rptr (rptr),
.rq2_wptr (rq2_wptr),
.rinc (rden_in),
.rclk (rclk),
.rrst_n (rclr_in),
.rflush (fifo_flush)
); wptr_full #(ASIZE) u_wptr_full(
.wfull (full_out),
.waddr (waddr),
.wptr (wptr),
.wq2_rptr (wq2_rptr),
.winc (wren_in),
.wclk (wclk),
.wrst_n (wclr_in),
.wflush (fifo_flush)
); endmodule

fifomem.v

module fifomem(
rdata, wdata, waddr, raddr, wren, wclk, rden, rclk, //memory interface
ram_douta, ram_dinb, ram_ada, ram_adb, ram_cena, ram_cenb,
CLKA, CLKB
);
parameter DATASIZE = 32; // Memory data word width
parameter ADDRSIZE = 7; // Number of mem address bits output [DATASIZE-1:0] rdata;
input [DATASIZE-1:0] wdata;
input [ADDRSIZE-1:0] waddr, raddr;
input wren, wclk;
input rden, rclk; input [DATASIZE-1:0] ram_douta;
output [DATASIZE-1:0] ram_dinb;
output [ADDRSIZE-1:0] ram_ada;
output [ADDRSIZE-1:0] ram_adb;
output ram_cena;
output ram_cenb;
output CLKA;
output CLKB; wire ram_cena,ram_cenb;
wire CLKA;
wire CLKB;
wire [DATASIZE-1:0] ram_dinb,ram_douta;
wire [ADDRSIZE-1:0] ram_ada, ram_adb;
wire [DATASIZE-1:0] rdata,wdata; assign ram_cena = ~rden;
assign ram_ada = raddr;
assign ram_cenb = ~wren;
assign ram_adb = waddr;
assign ram_dinb = wdata;
assign CLKA = rclk;
assign CLKB = wclk;
assign rdata = ram_douta; endmodule

ram_dp.v

module ram_dp #(
parameter DATA_WIDTH = 32,
parameter ADDR_WIDTH = 7,
parameter RAM_DEPTH = 1 << ADDR_WIDTH
)
(
output reg [DATA_WIDTH-1:0] data_out ,
input [DATA_WIDTH-1:0] data_in ,
input [ADDR_WIDTH-1:0] addr_a ,
input [ADDR_WIDTH-1:0] addr_b ,
input web , //Write Enable/Read Enable,addr_b write enable, low active
input clka , // write Clock Input
input clkb , // read Clock Input
input cena , //Chip Select
input cenb //Chip Select
); //--------------Internal variables----------------
reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1];
reg oe_r; //--------------Code Starts Here------------------ // Memory Write Block
// Write Operation : When web = 0, cenb = 1
always @ (posedge clkb)
begin : MEM_WRITE
if ((~cenb) && (~web) ) begin
mem[addr_b] = data_in;
end
end // Memory Read Block
// Read Operation : When web = 1, cena = 1
always @ (posedge clka)
begin : MEM_READ
if ((~cena) && web) begin
data_out = mem[addr_a];
end
end endmodule // End of Module ram_dp

sync_r2w.v

module sync_r2w
#(parameter ADDRSIZE = 4)
(
output reg [ADDRSIZE:0] wq2_rptr,
input [ADDRSIZE:0] rptr,
input wclk,
input wrst_n,
input wflush
); reg [ADDRSIZE:0] wq1_rptr; always @(posedge wclk or negedge wrst_n)
if (!wrst_n)
{wq2_rptr,wq1_rptr} <= 0;
else if(wflush)
{wq2_rptr,wq1_rptr} <= 0;
else
{wq2_rptr,wq1_rptr} <= {wq1_rptr,rptr}; endmodule

sync_w2r.v

module sync_w2r
#(parameter ADDRSIZE = 4)
(
output reg [ADDRSIZE:0] rq2_wptr,
input [ADDRSIZE:0] wptr,
input rclk,
input rrst_n,
input rflush
); reg [ADDRSIZE:0] rq1_wptr; always @(posedge rclk or negedge rrst_n)
if (!rrst_n)
{rq2_wptr,rq1_wptr} <= 0;
else if(rflush)
{rq2_wptr,rq1_wptr} <= 0;
else
{rq2_wptr,rq1_wptr} <= {rq1_wptr,wptr}; endmodule

rptr_empty.v

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,
input rclk,
input rrst_n,
input rflush
); reg [ADDRSIZE:0] rbin;
wire [ADDRSIZE:0] rgraynext, rbinnext; //-------------------
// GRAYSTYLE2 pointer
//-------------------
always @(posedge rclk or negedge rrst_n)
if (!rrst_n)
{rbin, rptr} <= 0;
else if(rflush)
{rbin, rptr} <= 0;
else
{rbin, rptr} <= {rbinnext, rgraynext}; // Memory read-address pointer (okay to use binary to address memory)
assign raddr = rbin[ADDRSIZE-1:0]; assign rbinnext = rbin + (rinc & ~rempty);
assign rgraynext = (rbinnext>>1) ^ rbinnext; //---------------------------------------------------------------
// FIFO empty when the next rptr == synchronized wptr or on reset
//---------------------------------------------------------------
wire rempty_val = (rgraynext == rq2_wptr); always @(posedge rclk or negedge rrst_n)
if (!rrst_n)
rempty <= 1'b1;
else
rempty <= rempty_val; endmodule

wptr_full.v

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,
input wclk,
input wrst_n,
input wflush
); reg [ADDRSIZE:0] wbin;
wire [ADDRSIZE:0] wgraynext, wbinnext; // GRAYSTYLE2 pointer
always @(posedge wclk or negedge wrst_n)
if (!wrst_n)
{wbin, wptr} <= 0;
else if(wflush)
{wbin, wptr} <= 0;
else
{wbin, wptr} <= {wbinnext, wgraynext}; // 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; //------------------------------------------------------------------
// 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]));
//------------------------------------------------------------------ wire wfull_val = (wgraynext=={~wq2_rptr[ADDRSIZE:ADDRSIZE-1],
wq2_rptr[ADDRSIZE-2:0]}); always @(posedge wclk or negedge wrst_n)
if (!wrst_n)
wfull <= 1'b0;
else
wfull <= wfull_val; endmodule

testbench


fifo_tb.v

module fifo_tb;

reg                          fifo_flush;
wire [31:0] data_out;
wire full_out;
wire empty_out;
wire [31:0] data_in;
wire wren_in;
wire wclk;
wire wr_rst_n;
wire rd_rst_n;
wire rden_in;
wire rclk;
wire [6:0] fifo_waddr;
wire [6:0] fifo_raddr; // memory interface signal
wire [31:0] if_ram_douta;
wire [31:0] if_ram_dinb;
wire [6:0] if_ram_ada;
wire [6:0] if_ram_adb;
wire if_ram_cena;
wire if_ram_cenb;
wire if_ram_clka;
wire if_ram_clkb; //******************************************************************
// generate clk
//******************************************************************
clock #(.CLK_FREQ(100.0))
u_clock_wr (
.clk ( wclk )
); clock #(.CLK_FREQ(70.0))
u_clock_rd (
.clk ( rclk )
); assign if_ram_clka = rclk;
assign if_ram_clkb = wclk; //******************************************************************
// generate read and write data
//****************************************************************** fifo_data #(.DATA_WIDTH(8))
u_fifo_data (
.wr_rst_n ( wr_rst_n ),
.wr_clk ( wclk ),
.wr_en ( wren_in ),
.wr_data ( data_in ),
.wr_full ( full_out ),
.rd_rst_n ( rd_rst_n ),
.rd_clk ( rclk ),
.rd_en ( rden_in ),
.rd_empty ( empty_out )
); //******************************************************************
// creat FSDB
//******************************************************************
initial begin
$fsdbDumpfile("tb.fsdb");
$fsdbDumpvars();
end //===================================================
// u_fifoif from rtl/fifoif.v
//=================================================== fifoif u_fifoif (
.fifo_flush ( 1'b0 ),
.data_out ( data_out ),
.full_out ( full_out ),
.empty_out ( empty_out ),
.data_in ( data_in ),
.wren_in ( wren_in ),
.wclk ( wclk ),
.wclr_in ( wr_rst_n ),
.rden_in ( rden_in ),
.rclk ( rclk ),
.rclr_in ( rd_rst_n ),
.fifo_waddr ( fifo_waddr ),
.fifo_raddr ( fifo_raddr ), // Memory interface
.ram_douta ( if_ram_douta ),
.ram_dinb ( if_ram_dinb ),
.ram_ada ( if_ram_ada ),
.ram_adb ( if_ram_adb ),
.ram_cena ( if_ram_cena ),
.ram_cenb ( if_ram_cenb ),
.ram_clka ( if_ram_clka ),
.ram_clkb ( if_ram_clkb )
); ram_dp u_ram_dp(
.data_out (if_ram_douta[31:0]), //A data output, 32 bits
.data_in (if_ram_dinb[31:0]), //B data input , 32 bits
.addr_a (if_ram_ada[6:0]), //A adress, 7 bits
.addr_b (if_ram_adb[6:0]), //B adress, 7 bits
.web (if_ram_cenb), //Write Enable/Read Enable,addr_b write enable, low active
.clka (if_ram_clka), // write Clock Input
.clkb (if_ram_clkb), // read Clock Input
.cena (if_ram_cena), //Chip Select ,low active
.cenb (if_ram_cenb) //Chip Select ,low active
); endmodule

clock.v

module clock #(parameter   CLK_FREQ = 100.0)  //MHz
(
output reg clk
); localparam CLK_CYCLE = 1000.0 / CLK_FREQ; initial
begin
clk = 0;
forever #(CLK_CYCLE/2)
clk = ~clk;
end endmodule

fifo_data.v

module fifo_data #(parameter DATA_WIDTH = 8)
(
output reg wr_rst_n,
input wr_clk,
output reg wr_en,
output reg [DATA_WIDTH-1:0] wr_data,
input wr_full,
output reg rd_rst_n,
input rd_clk,
output reg rd_en,
input rd_empty
); reg normal_wr;
reg normal_rd; initial
begin
wr_rst_n = 1'b0;
rd_rst_n = 1'b0;
normal_wr = 1'b0;
normal_rd = 1'b0;
#492;
wr_rst_n = 1'b1;
rd_rst_n = 1'b1;
#100; //only write FIFO
normal_rd = 1'b0;
normal_wr = 1'b1;
repeat(500) @(negedge wr_clk); //only read FIFO
normal_wr = 1'b0;
normal_rd = 1'b1;
repeat(500) @(negedge rd_clk); //read and write FIFO
normal_rd = 1'b0;
normal_wr = 1'b0; normal_wr = 1'b1;
normal_rd = 1'b1;
repeat(1000) @(negedge wr_clk); normal_wr = 1'b0;
normal_rd = 1'b0;
repeat(50) @(negedge rd_clk);
$finish;
end //******************************************************************
// write FIFO data generate
//******************************************************************
always @(negedge wr_clk or negedge wr_rst_n)
begin
if(wr_rst_n == 1'b0)
begin
wr_en <= 1'b0;
wr_data <= {(DATA_WIDTH){1'b0}};
end
else if(normal_wr == 1'b1)
begin
if(wr_full == 1'b0)
begin
wr_en <= 1'b1;
wr_data <= {$random%((1 << (DATA_WIDTH-1)))};
//wr_data <= $random;
end
else
begin
wr_en <= 1'b0;
wr_data <= {(DATA_WIDTH){1'b0}};
end
end
else
begin
wr_en <= 1'b0;
wr_data <= {(DATA_WIDTH){1'b0}};
end
end //******************************************************************
// read FIFO data generate
//******************************************************************
always @(negedge wr_clk or negedge wr_rst_n)
begin
if(wr_rst_n == 1'b0)
rd_en <= 1'b0;
else if(normal_rd == 1'b1)
begin
if(rd_empty == 1'b0)
rd_en <= 1'b1;
else
rd_en <= 1'b0;
end
else
rd_en <= 1'b0;
end endmodule

参考资料


[0].Simulation and Synthesis Techniques for Asynchronous FIFO Design

[1].Memories

[2].异步FIFO的FPGA实现

[3].异步FIFO的设计

[4].基于FPGA的异步FIFO设计

[5].基于FPGA的异步FIFO验证

异步FIFO总结的更多相关文章

  1. 异步fifo的设计

    本文首先对异步 FIFO 设计的重点难点进行分析 最后给出详细代码 一.FIFO简单讲解 FIFO的本质是RAM, 先进先出 重要参数:fifo深度(简单来说就是需要存多少个数据)           ...

  2. 【iCore、iCore2、iBoard例程】【异步FIFO跨时钟域通信(通过ARM 读FPGA FIFO)】

    欢迎访问电子工程师学堂,以便了解更多内容:http://www.eeschool.org 一.本实验基于iCore2 完成,通过简单改动,即可用在 iCore 核心板.iBoard 电子学堂上. iC ...

  3. 异步FIFO为什么用格雷码

    异步FIFO通过比较读写地址进行满空判断,但是读写地址属于不同的时钟域,所以在比较之前需要先将读写地址进行同步处理,将写地址同步到读时钟域再和读地址比较进行FIFO空状态判断(同步后的写地址一定是小于 ...

  4. 异步FIFO的verilog实现与简单验证(调试成功)

    最近在写一个异步FIFO的时候,从网上找了许多资料,文章都写的相当不错,只是附在后面的代码都多多少少有些小错误. 于是自己写了一个调试成功的代码,放上来供大家参考. 非原创 原理参考下面: 原文 ht ...

  5. 怎么用Verilog语言描述同步FIFO和异步FIFO

    感谢 知乎龚大佬 打杂大佬 网上几个nice的博客(忘了是哪个了....) 前言 虽然FIFO都有IP可以使用,但理解原理还是自己写一个来得透彻. 什么是FIFO? Fist in first out ...

  6. Verilog设计异步FIFO

    转自http://ninghechuan.com 异步FIFO有两个异步时钟,一个端口写入数据,一个端口读出数据.通常被用于数据的跨时钟域的传输. 同步FIFO的设计.一个时钟控制一个计数器,计数器增 ...

  7. 异步fifo的Verilog实现

     一.分析 由于是异步FIFO的设计,读写时钟不一样,在产生读空信号和写满信号时,会涉及到跨时钟域的问题,如何解决? 跨时钟域的问题:由于读指针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO ...

  8. 异步FIFO空满设计延迟问题

    由于设计的时候读写指针用了至少两级寄存器同步,同步会消耗至少两个时钟周期,势必会使得判断空或满有所延迟,这会不会导致设计出错呢? 异步FIFO通过比较读写指针进行满空判断,但是读写指针属于不同的时钟域 ...

  9. 异步FIFO跨时钟域亚稳态如何解决?

    跨时钟域的问题:前一篇已经提到要通过比较读写指针来判断产生读空和写满信号,但是读指针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO的读写时钟域不同,是异步的,要是将读时钟域的读指针与写时钟域 ...

  10. 异步fifo with 读控制

    之前做LDPC编码器时,学习了一下异步FIFO的相关知识,主要参考了http://www.cnblogs.com/aslmer/p/6114216.html,并在此基础上根据项目需求,添加了一个读控制 ...

随机推荐

  1. 热点共享SS网络

    # 测试系统: Ubuntu 16.04 LTS-lxde-ARM # ***-libev 安装脚本源于 秋水逸冰: https://teddysun.com/358.html # ss-tproxy ...

  2. actionbar-去掉背景的阴影

    今天发现一个问题,就是actionbar跟界面的交界处,会有一个阴影,通过调查发现,这个阴影是actionbar的.然后通过在网上找资料,完美解决了问题.解决方法如下 1.在这个actionbar所在 ...

  3. 转:Java的一道面试题----静态变量初始化过程

    public class Test{ private static Test tester = new Test(); //step 1 private static int count1; //st ...

  4. c++运算符重载笔记

    运算符重载的概念:给原有的运算符赋予新的功能: 比如:+ 不仅可以做算术运算也可以连接俩个字符串 一元运算符:只与一个操作数进行运算 比如 正负号 运算符重载的本质是:函数重载. <<与& ...

  5. 72.挖掘CSDN密码到链表并统计密码出现次数生成密码库

    list.h #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include & ...

  6. Linux平台下使用AdventNet ManageEngine OpUtils监控网络

    AdventNet ManageEngine OpUtils 是一套系统和网络监视工具,它有Linux/Windows系统平台的免费版和企业版,该软件是一款用于监视诸如路由器,交换机,服务器或者桌面这 ...

  7. using可以用于释放操作,相当于Dispose()

    using可以用于释放操作,相当于Dispose()

  8. CISP/CISA 每日一题 11

    CISA 每日一题(答) 一个合理建造的数据仓库应当支持下列三种基本的查询格式: 1.向上溯源和向下溯源——向上溯源是对数据进行总计:向下溯源是将数据进行细化: 2.交叉溯源——通过通用属性访问数据仓 ...

  9. 设计模式六大原则(一):单一职责原则(Single Responsibility Principle)

    单一职责(SRP)定义: 不要存在多于一个导致类变更的原因,通俗的说,即一个类只负责一项职责. 问题由来: 类T负责两个不同的职责:职责P1,职责P2.当由于职责P1需求发生改变而需要修改类T时,有可 ...

  10. 自定义HTML标签属性

    为HTML元素添加一自定义的属性非常方便,只须将其加到尖括号中即可,与内置属性地位相等. 如我们要为TextBox元素添加属性idvalue: <input type="text&qu ...