参考博文:https://blog.csdn.net/alangaixiaoxiao/article/details/81432144

1、概述

  异步FIFO设计的关键是产生“写满”和“读空”信号,这两个信号的产生需要用到读指针rptr和写指针wptr构建组合逻辑进行判断,然而读指针属于读时钟域rclk,写指针属于写时钟域wclk,因此必须进行同步化处理以消除亚稳态。异步FIFO的设计一般采用2种手段进行同步化处理:

  (1)将读指针rptr打2拍到写时钟域,将写指针wptr打两拍到读时钟域,消除亚稳态;

  (2)由于读写指针都是多比特信号,直接对它们进行同步化容易产生亚稳态,且用组合逻辑进行判断容易产生毛刺,因此改用格雷码进行异步时钟域的传输。

2、代码

  设计思路有以下几点:

  (1)在指针中添加一个额外的位(extra bit),当写指针增加并越过最后一个FIFO地址时,就将写指针这个未用的MSB加1,其它位回零。对读指针也进行同样的操作。此时,对于深度为2^n的FIFO,需要的读/写指针位宽为(n+1)位,如对于深度为8的FIFO,需要采用4bit的计数器,0000~0111、1000~1111,MSB作为折回标志位,而低3位作为地址指针。如果两个指针的MSB不同,说明写指针比读指针多折回了一次;如r_addr=0000,而w_addr = 1000,为满。如果两个指针完全相同,为空。

  (2)使用gray码解决了一个问题,但同时也带来另一个问题,即在格雷码域如何判断空与满。空的判断标准仍是完全相同,满的判断标准需要满足:

    ■ 格雷码指针的最高位不同,因为wptr必须比rptr多折回一次。

    ■ wptr与rptr的次高位不相等,如下表的7(格雷码为0100)和15(格雷码为1000),转化为二进制对应的是0111和1111,MSB不同说明多折回一次,111相同代表同一位置。

    

    ■ 其余位完全相同

  (3)对双口RAM的寻址采用二进制码,异步时钟域的交互采用格雷码。

    

`timescale 1ns / 1ps

module asynchronous_fifo
#(parameter ASIZE=,DSIZE=)
(
// 读
input rclk,
input rrst_n,
input rinc,
output [DSIZE-:] rdata,
output rempty,
// 写
input wclk,
input wrst_n,
input winc,
input [DSIZE-:] wdata,
output wfull
); // 读写地址,由wbin、rbin的低位生成
wire [ASIZE-:] waddr,raddr;
reg [ASIZE:] wbin,rbin;
// 读写指针(比地址位宽大1)
reg [ASIZE:] wgray,rgray;
wire [ASIZE:] wbinnext,wgraynext,rbinnext,rgraynext;
// 经打拍后的读写指针
reg [ASIZE:] rq1_wgray,rq2_wgray,wq1_rgray,wq2_rgray; reg rempty_val,wfull_val; // 双端口存储器
reg [DSIZE-:] mem [:(<<ASIZE)-];
reg [DSIZE-:] rdata_reg;
//assign rdata = mem[raddr];
always @(posedge wclk) begin
if(!wfull && winc) mem[waddr] <= wdata;
end
always @(posedge rclk or negedge rrst_n) begin
if(!rrst_n) rdata_reg <= ;
else if(!rempty && rinc) rdata_reg <= mem[raddr];
else rdata_reg <= {DSIZE{'bz}}; // 设成z态方便看波形
end
assign rdata = rdata_reg; // 同步化处理
always @(posedge rclk or negedge rrst_n)begin
if(!rrst_n) {rq1_wgray,rq2_wgray} <= ;
else {rq1_wgray,rq2_wgray} <= {wgray,rq1_wgray};
end always @(posedge wclk or negedge wrst_n) begin
if(!wrst_n) {wq1_rgray,wq2_rgray} <= ;
else {wq1_rgray,wq2_rgray} <= {rgray,wq1_rgray};
end // 写地址和写满信号产生
always @(posedge wclk or negedge wrst_n) begin
if(!wrst_n) {wbin,wgray} <= ;
else {wbin,wgray} <= {wbinnext,wgraynext};
end
assign wbinnext = (~wfull & winc)? (wbin+'b1) : wbin;
assign waddr = wbin[ASIZE-:];
assign wgraynext = (wbinnext>>)^wbinnext; always @(posedge wclk or negedge wrst_n) begin
if(!wrst_n) wfull_val <= ; // 用同步到写时钟域的读指针与写指针进行比较,高两位不同其他位相同则满
else wfull_val <= (wgraynext=={~wq2_rgray[ASIZE:ASIZE-],wq2_rgray[ASIZE-:]});
end
assign wfull = wfull_val; // 读地址和读空信号
always @(posedge rclk or negedge rrst_n) begin
if(!rrst_n) {rbin,rgray} <= ;
else {rbin,rgray} <= {rbinnext,rgraynext};
end
assign rbinnext = (~rempty & rinc)? (rbin+'b1) : rbin;
assign raddr = rbin[ASIZE-:];
assign rgraynext = (rbinnext >> ) ^ rbinnext; always @(posedge rclk or negedge rrst_n) begin
if(!rrst_n) rempty_val <= ; // 用同步到读时钟域的写指针与读指针进行比较,各位都相同则满
else rempty_val <= (rgraynext==rq2_wgray);
end
assign rempty = rempty_val; endmodule

3、验证

  (1)testbench

  写8个数据(0-7)——触发wfull信号——读8个数据(0-7)——触发rempty信号——写8个数据(8-15)——触发wfull信号——读4个数据(8-11)——写4个数据(16-19)——触发wfull信号——读8个数据(12-19)——触发rempty信号

`timescale 1ns / 1ps

module asynchronous_fifo_tb;

    // Inputs
reg rclk;
reg rrst_n;
reg rinc;
reg wclk;
reg wrst_n;
reg winc;
reg [:] wdata; // Outputs
wire [:] rdata;
wire rempty;
wire wfull; integer i; // Instantiate the Unit Under Test (UUT)
asynchronous_fifo uut (
.rclk(rclk),
.rrst_n(rrst_n),
.rinc(rinc),
.rdata(rdata),
.rempty(rempty),
.wclk(wclk),
.wrst_n(wrst_n),
.winc(winc),
.wdata(wdata),
.wfull(wfull)
); initial begin
// Initialize Inputs
rclk = ;
rrst_n = ;
rinc = ;
wclk = ;
wrst_n = ;
winc = ;
wdata = ; // Wait 100 ns for global reset to finish
#;
rrst_n = ;
wrst_n = ;
@(negedge rclk); // Write
for(i=;i<;i=i+) begin
wdata = i;
winc = ;
@(negedge wclk);
end
winc = ; //写满
wdata = ; //Read
@(negedge rclk);
for(i=;i<;i=i+) begin
rinc = ;
@(negedge rclk);
end
rinc = ; //读空 // Write
@(negedge wclk);
for(i=;i<;i=i+) begin
wdata = i+;
winc = ;
@(negedge wclk);
end
winc = ;
wdata = ; // 写满 //Read
@(negedge rclk);
for(i=;i<;i=i+) begin
rinc = ;
@(negedge rclk);
end
rinc = ; // 读一半 // Write
@(negedge wclk);
for(i=;i<;i=i+) begin
wdata = i+;
winc = ;
@(negedge wclk);
end
winc = ;
wdata = ; // 写一半 //Read
@(negedge rclk);
for(i=;i<;i=i+) begin
rinc = ;
@(negedge rclk);
end
rinc = ; //读空 // Add stimulus here end always # wclk = ~wclk;
always # rclk = ~rclk; endmodule

  (2)读空信号(rempty)存在几个周期的置0延迟,即在空状态写,写入数据后rempty不会立即置0,而是会经过几个周期以后才会置0,这跟跨异步时钟域的打拍有关,因为写指针需要2个周期才能同步到读时钟域与读指针进行比较。rempty在读状态下的置1不存在延迟,因为当前同步到读时钟域的写指针已经是最新的了,读指针的变化立即就可以触发置1,置0延迟置1不延迟对于实际使用是没有太大影响的,反之则有。同样写满信号(wfull)的置0也存在延迟。

Verilog-异步FIFO的更多相关文章

  1. 同步fifo与异步fifo

    参考以下帖子: https://blog.csdn.net/hengzo/article/details/49683707 https://blog.csdn.net/Times_poem/artic ...

  2. 异步fifo的Verilog实现

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

  3. 异步FIFO总结+Verilog实现

    异步FIFO简介 异步FIFO(First In First Out)可以很好解决多比特数据跨时钟域的数据传输与同步问题.异步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原码

    这几天看了Clifford E. Cummings的两篇大作<Simulation and Synthesis Techniques for Asynchronous FIFO Design&g ...

  8. 异步fifo的设计

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

  9. 基于FPGA的异步FIFO设计

    今天要介绍的异步FIFO,可以有不同的读写时钟,即不同的时钟域.由于异步FIFO没有外部地址端口,因此内部采用读写指针并顺序读写,即先写进FIFO的数据先读取(简称先进先出).这里的读写指针是异步的, ...

  10. 异步fifo的设计(FPGA)

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

随机推荐

  1. ELK学习001:Elastic Stack简介

    ELK简介: ELK Stack:ELK是Elasticsearch.Logstash.Kibana的缩写简称,这三者都是开源软件.ELK是5.0版本前的统称:这是一套统一的日志收集分析系统.它能够方 ...

  2. C++ 实现string转BYTE

    用于将形如"0x1A"的string转成BYTE类型 代码如下, 有问题欢迎指出 bool str2byte(const std::string &str, BYTE &a ...

  3. 发ajax响应json格式数据

    1.maven依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="h ...

  4. kaks calculator批量计算多个基因的选择压力kaks值

    欢迎来到"bio生物信息"的世界 今天给大家带来"批量计算kaks值"的技能. 关于kaks的背景知识我就不介绍了,感兴趣的自行搜索,这里直接开始讲怎么批量计算 ...

  5. RPC远程过程调用(Remote Procedure Call)

    RPC,就是Remote Procedure Call,远程过程调用 远程过程调用,自然是相对于本地过程调用 本地过程调用,就好比你现在在家里,你要想洗碗,那你直接把碗放进洗碗机,打开洗碗机开关就可以 ...

  6. 基于element-ui 模仿微信聊天页面以及滚动条隐藏在chrome和其他浏览器的处理

    1.效果图 2.代码 <template> <div style=" overflow: hidden;"> <el-row> <el-c ...

  7. 数据库->神奇的concat_ws函数

    神奇的concat_ws函数 了解到concat_ws函数起因是因为:朋友给我发了一份面试题,其中有一句看似很简单的查询,但是我就是没做出来.如下图:   经查阅资料了解通过两个字段拼接并且用“_”进 ...

  8. Jenkins+robotframework持续集成环境(二)

    配置Jenkins上的robotframework环境 一.添加robot插件 需要导一个robot framework 的包,导包方式如下: 1.进入插件管理页面,选择“可选插件”,在右侧搜索栏搜索 ...

  9. 洛谷P4526 【模板】自适应辛普森法2

    P4526 [模板]自适应辛普森法2 洛谷传送门 题目描述 计算积分 保留至小数点后5位.若积分发散,请输出"orz". 输入格式 一行,包含一个实数,为a的值 输出格式 一行,积 ...

  10. Python三种注释及分行换行输出

    1.单行注释 以#开头,#右边的所有东西都被当成文字说明,而不是真正要执行的程序,只是起到辅助说明作用(#后需要先添加一个空格). 单行注释写到行内代码后边,或者单独一行均可. # 我是单行注释,#号 ...