基于FPGA的异步FIFO设计
今天要介绍的异步FIFO,可以有不同的读写时钟,即不同的时钟域。由于异步FIFO没有外部地址端口,因此内部采用读写指针并顺序读写,即先写进FIFO的数据先读取(简称先进先出)。这里的读写指针是异步的,处理不同的时钟域,而异步FIFO的空满标志位是根据读写指针的情况得到的。为了得到正确的空满标志位,需要对读写指针进行同步。一般情况下,如果一个时钟域的信号直接给另一个时钟域采集,可能会产生亚稳态,亚稳态的产生对设计而言是致命的。为了减少不同时钟域间的亚稳态问题,我们先对它进行两拍寄存同步,如图1所示。当然,对异步信号的寄存越多,产生亚稳态的概率就越小,但延时越多。不过一般情况下,寄存两拍就够了。为了继续减少亚稳态产生的概率,在对异步信号同步之前,将其转换为格雷码,使其每个状态只有一个位在变化。例如,假设N位二进制变量产生的亚稳态概率为a,那么二进制转换成格雷码后其产生的亚稳态概率则为a/N。
图1 对异步信号用两级寄存器同步
根据上述原理,设计了异步FIFO的架构,如图2所示。
图2 异步FIFO设计架构
根据异步FIFO的设计架构,归纳以下设计步骤:
写时钟域:
(1)根据写使能wr_en和写满标志位wr_full产生二进制写指针
(2)根据二进制写指针产生双端口RAM的写地址
(3)由二进制写指针转换成格雷码写指针
(4)对格雷码读指针在写时钟域中进行两级同步得同步后格雷码读指针
(5)同步后格雷码读指针转化成同步后二进制读指针
(6)步骤(3)与步骤(4)比较得写满标志位wr_full
(7)步骤(1)与步骤(5)相减得指示写FIFO的数据量
读时钟域:
(8)根据读使能rd_en和读空标志位rd_empty产生二进制读指针
(9)根据二进制读指针产生双端口RAM的读地址
(10)由二进制读指针转换成格雷码读指针
(11)对格雷码写指针在读时钟域中进行两级同步得同步后格雷码写指针
(12)同步后格雷码写指针转化成同步后二进制写指针
(13)步骤(10)与步骤(11)比较得读空标志位rd_empty
(14)步骤(8)与步骤(12)相减得指示读FIFO的数据量
Verilog HDL设计电路,如下所示:
/*******************************版权申明********************************
** 电子技术应用网站, CrazyBird
** http://www.chinaaet.com, http://blog.chinaaet.com/crazybird
**
**------------------------------文件信息--------------------------------
** 文件名: asyn_fifo.v
** 创建者: CrazyBird
** 创建日期: 2016-1-16
** 版本号: v1.0
** 功能描述: 异步FIFO,用于处理不同的时钟域
**
***********************************************************************/
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module asyn_fifo(
wr_rst_n,
wr_clk,
wr_en,
wr_data,
wr_full,
wr_cnt,
rd_rst_n,
rd_clk,
rd_en,
rd_data,
rd_empty,
rd_cnt
);
//******************************************************************
// 参数定义
//******************************************************************
parameter C_DATA_WIDTH = 8;
parameter C_FIFO_DEPTH_WIDTH = 4;
//******************************************************************
// 端口定义
//******************************************************************
input wr_rst_n;
input wr_clk;
input wr_en;
input [C_DATA_WIDTH-1:0] wr_data;
output reg wr_full;
output reg [C_FIFO_DEPTH_WIDTH:0] wr_cnt;
input rd_rst_n;
input rd_clk;
input rd_en;
output [C_DATA_WIDTH-1:0] rd_data;
output reg rd_empty;
output reg [C_FIFO_DEPTH_WIDTH:0] rd_cnt;
//******************************************************************
// 内部变量定义
//******************************************************************
reg [C_DATA_WIDTH-1:0] mem [0:(1 << C_FIFO_DEPTH_WIDTH)-1];
wire [C_FIFO_DEPTH_WIDTH-1:0] wr_addr;
wire [C_FIFO_DEPTH_WIDTH-1:0] rd_addr;
wire [C_FIFO_DEPTH_WIDTH:0] next_wr_bin_ptr;
wire [C_FIFO_DEPTH_WIDTH:0] next_rd_bin_ptr;
reg [C_FIFO_DEPTH_WIDTH:0] wr_bin_ptr;
reg [C_FIFO_DEPTH_WIDTH:0] rd_bin_ptr;
wire [C_FIFO_DEPTH_WIDTH:0] next_wr_gray_ptr;
wire [C_FIFO_DEPTH_WIDTH:0] next_rd_gray_ptr;
wire [C_FIFO_DEPTH_WIDTH:0] syn_wr_bin_ptr_rd_clk;
wire [C_FIFO_DEPTH_WIDTH:0] syn_rd_bin_ptr_wr_clk;
wire [C_FIFO_DEPTH_WIDTH:0] syn_wr_gray_ptr_rd_clk;
wire [C_FIFO_DEPTH_WIDTH:0] syn_rd_gray_ptr_wr_clk;
wire [C_FIFO_DEPTH_WIDTH:0] wr_cnt_w;
wire [C_FIFO_DEPTH_WIDTH:0] rd_cnt_w;
wire wr_full_w;
wire rd_empty_w;
//******************************************************************
// 双端口RAM的读写
//******************************************************************
// 写RAM
always @(posedge wr_clk)
begin
if((wr_en & ~wr_full) == 1'b1)
mem[wr_addr] <= wr_data;
end
// 读RAM
assign rd_data = mem[rd_addr];
//******************************************************************
// 二进制写指针的产生
//******************************************************************
assign next_wr_bin_ptr = wr_bin_ptr + (wr_en & ~wr_full);
always @(posedge wr_clk or negedge wr_rst_n)
begin
if(wr_rst_n == 1'b0)
wr_bin_ptr <= {(C_FIFO_DEPTH_WIDTH+1){1'b0}};
else
wr_bin_ptr <= next_wr_bin_ptr;
end
//******************************************************************
// RAM写地址的产生
//******************************************************************
assign wr_addr = wr_bin_ptr[C_FIFO_DEPTH_WIDTH-1:0];
//******************************************************************
// 二进制写指针转换成格雷码写指针
//******************************************************************
bin2gray #(
.C_DATA_WIDTH(C_FIFO_DEPTH_WIDTH+1)
)
u_bin2gray_wr (
.bin ( next_wr_bin_ptr ),
.gray ( next_wr_gray_ptr )
);
//******************************************************************
// 对格雷码读指针在写时钟域中进行两级同步
//******************************************************************
double_syn_ff #(
.C_DATA_WIDTH(C_FIFO_DEPTH_WIDTH+1)
)
u_double_syn_ff_wr (
.rst_n ( wr_rst_n ),
.clk ( wr_clk ),
.din ( next_rd_gray_ptr ),
.dout ( syn_rd_gray_ptr_wr_clk )
);
//******************************************************************
// 同步后的格雷码读指针转换成同步后的二进制读指针
//******************************************************************
gray2bin #(
.C_DATA_WIDTH(C_FIFO_DEPTH_WIDTH+1)
)
u_gray2bin_wr (
.gray ( syn_rd_gray_ptr_wr_clk ),
.bin ( syn_rd_bin_ptr_wr_clk )
);
//******************************************************************
// FIFO写满标志位的产生和写FIFO数据量的计数
//******************************************************************
assign wr_full_w = (next_wr_gray_ptr == ({~syn_rd_gray_ptr_wr_clk[C_FIFO_DEPTH_WIDTH:C_FIFO_DEPTH_WIDTH-1],
syn_rd_gray_ptr_wr_clk[C_FIFO_DEPTH_WIDTH-2:0]}));
assign wr_cnt_w = next_wr_bin_ptr - syn_rd_bin_ptr_wr_clk;
always @(posedge wr_clk or negedge wr_rst_n)
begin
if(wr_rst_n == 1'b0)
begin
wr_full <= 1'b0;
wr_cnt <= {(C_FIFO_DEPTH_WIDTH+1){1'b0}};
end
else
begin
wr_full <= wr_full_w;
wr_cnt <= wr_cnt_w;
end
end
//******************************************************************
// 二进制读指针的产生
//******************************************************************
assign next_rd_bin_ptr = rd_bin_ptr + (rd_en & ~rd_empty);
always @(posedge rd_clk or negedge rd_rst_n)
begin
if(rd_rst_n == 1'b0)
rd_bin_ptr <= {(C_FIFO_DEPTH_WIDTH+1){1'b0}};
else
rd_bin_ptr <= next_rd_bin_ptr;
end
//******************************************************************
// RAM读地址的产生
//******************************************************************
assign rd_addr = rd_bin_ptr[C_FIFO_DEPTH_WIDTH-1:0];
//******************************************************************
// 二进制读指针转换成格雷码读指针
//******************************************************************
bin2gray #(
.C_DATA_WIDTH(C_FIFO_DEPTH_WIDTH+1)
)
u_bin2gray_rd (
.bin ( next_rd_bin_ptr ),
.gray ( next_rd_gray_ptr )
);
//******************************************************************
// 对格雷码写指针在读时钟域中进行两级同步
//******************************************************************
double_syn_ff #(
.C_DATA_WIDTH(C_FIFO_DEPTH_WIDTH+1)
)
u_double_syn_ff_rd (
.rst_n ( rd_rst_n ),
.clk ( rd_clk ),
.din ( next_wr_gray_ptr ),
.dout ( syn_wr_gray_ptr_rd_clk )
);
//******************************************************************
// 同步后的格雷码写指针转换成同步后的二进制写指针
//******************************************************************
gray2bin #(
.C_DATA_WIDTH(C_FIFO_DEPTH_WIDTH+1)
)
u_gray2bin_rd (
.gray ( syn_wr_gray_ptr_rd_clk ),
.bin ( syn_wr_bin_ptr_rd_clk )
);
//******************************************************************
// FIFO读空标志位的产生和读FIFO数据量的计数
//******************************************************************
assign rd_empty_w = (next_rd_gray_ptr == syn_wr_gray_ptr_rd_clk);
assign rd_cnt_w = syn_wr_bin_ptr_rd_clk - next_rd_bin_ptr;
always @(posedge rd_clk or negedge rd_rst_n)
begin
if(rd_rst_n == 1'b0)
begin
rd_empty <= 1'b0;
rd_cnt <= {(C_FIFO_DEPTH_WIDTH+1){1'b0}};
end
else
begin
rd_empty <= rd_empty_w;
rd_cnt <= rd_cnt_w;
end
end
endmodule
其中,模块gray2bin是格雷码转二进制码,模块bin2gray是二进制码转格雷码,详情见上一篇博客,地址:http://blog.chinaaet.com/crazybird/p/5100000866 。模块double_syn_ff是两级寄存器,用于同步信号,对应的Verilog HDL实现如下所示:
/*******************************版权申明********************************
** 电子技术应用网站, CrazyBird
** http://www.chinaaet.com, http://blog.chinaaet.com/crazybird
**
**------------------------------文件信息--------------------------------
** 文件名: double_syn_ff.v
** 创建者: CrazyBird
** 创建日期: 2016-1-16
** 版本号: v1.0
** 功能描述: 对输入信号进行两级同步后输出
**
***********************************************************************/
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module double_syn_ff(
rst_n,
clk,
din,
dout
);
//******************************************************************
// 参数定义
//******************************************************************
parameter C_DATA_WIDTH = 8;
//******************************************************************
// 端口定义
//******************************************************************
input rst_n;
input clk;
input [C_DATA_WIDTH-1:0] din;
output reg [C_DATA_WIDTH-1:0] dout;
//******************************************************************
// 内部变量定义
//******************************************************************
reg [C_DATA_WIDTH-1:0] data_r;
//******************************************************************
// 对输入信号进行两级同步后输出
//******************************************************************
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
{dout,data_r} <= {(2*C_DATA_WIDTH){1'b0}};
else
{dout,data_r} <= {data_r,din};
end
endmodule
由于字数的限制,异步FIFO的功能验证放在下一篇博文中吧!!!
转载:http://blog.chinaaet.com/crazybird/p/5100000872
基于FPGA的异步FIFO设计的更多相关文章
- 基于FPGA的异步FIFO验证
现在开始对上一篇博文介绍的异步FIFO进行功能验证,上一篇博文地址:http://blog.chinaaet.com/crazybird/p/5100000872 .对异步FIFO验证的平台如图1所示 ...
- 基于FPGA的HDMI显示设计(三)
上一篇:基于FPGA的VGA显示设计(二) 10月10日 ~ 20日期间实习,令我万万没想到的是实习题目是 “便携式高清电视显示屏测试系统原型设计” 也就是 “基于FPGA的视频显示”. 实习要求用 ...
- 基于FPGA的VGA显示设计(二)
上一篇:基于FPGA的VGA显示设计(一) 参照 CrazyBingo 的 基于FPGA的VGA可移植模块终极设计代码 的工程代码风格,模块化处理了上一篇的代码,并增加了一点其它图形. 顶层 ...
- 基于SEDA的异步框架设计与实现
基于SEDA的异步框架设计与实现 二.为什么使用SEDA 目前,面对并发环境,主流互联网服务器编程模型有两种:多线程模型以及事件驱动模型.但是这两个模型都不足以解决这个问题.我们来首先看一下这两种编程 ...
- 基于FPGA的VGA显示设计(一)
前言 FPGA主要运用于芯片验证.通信.图像处理.显示VGA接口的显示器是最基本的要求了. 原理 首先需要了解 : (1)VGA接口协议:VGA端子_维基百科 .VGA视频传输标准_百度 引脚1 RE ...
- 异步FIFO设计
参考http://www.cnblogs.com/BitArt/archive/2013/04/10/3010073.html http://blog.sina.com.cn/s/blog_6d30f ...
- 异步FIFO总结
异步FIFO总结 异步FIFO的基本概念 异步FIFO读写分别采用相互异步的不同时钟,使用异步FIFO可以在两个不同时钟系统之间快速而方便地传输实时数据 FIFO的常见参数 FIFO的宽度:即FIFO ...
- 异步fifo的设计(FPGA)
本文首先对异步 FIFO 设计的重点难点进行分析 最后给出详细代码 一.FIFO简单讲解 FIFO的本质是RAM, 先进先出 重要参数:fifo深度(简单来说就是需要存多少个数据) ...
- 优化基于FPGA的深度卷积神经网络的加速器设计
英文论文链接:http://cadlab.cs.ucla.edu/~cong/slides/fpga2015_chen.pdf 翻译:卜居 转载请注明出处:http://blog.csdn.net/k ...
随机推荐
- 如何使用Replace Pioneer批量查找和替换并提取指定字符串
1 我们查看源代码之后获得的网页文件如下图所示,一般都是href="/p-286018571.html"我们只要能提取到所有的"/p-XXXXXXXX.html" ...
- 使用Draw rect 绘制圆角矩形
- (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); UIGraphicsPush ...
- iPhone调用ffmpeg2.0.2解码h264视频的示例代码
iPhone调用ffmpeg2.0.2解码h264视频的示例代码 h264demo.zip 关于怎么在MAC下编译iOS下的ffmpeg请看 编译最新ffmpeg2.0.1(ffmpeg2.0.2)到 ...
- Java实现根据输入的日期以及天数,获取此日期之后的天数的工作日
public static void main(String[] args) { List<String> list = new ArrayList<String>();//节 ...
- 王立平--include在Android中的应用
一个布局中包括还有一个布局 1.在layout下定义activity_other.xml布局 2.代码中的包括例如以下: <LinearLayout xmlns:android="ht ...
- Linux学习笔记——Ubuntu更新软件源
0.前言 通过改动ubuntu软件源可提高apt命令下载安装软件的速度. 參考资料 [官方资料]--配置文件改动方法 [Ubuntu如何改动软件源地址]--使用ubun ...
- Codevs3008 加工生产调度
题目大意:某工厂收到了n个产品的订单,这n个产品分别在A.B两个车间加工,而且必须先在A车间加工后才干够到B车间加工. 求如何安排这n个产品的加工顺序.才干使总的加工时间最短. 这里所说的加工时间是指 ...
- KNOCKOUTJS DOCUMENTATION-绑定(BINDINGS)-自定义绑定
除了ko内建的绑定,还可以自定义绑定,灵活地封装复杂的行为使之可重用. 自定义绑定 注册自定义绑定 向 ko.bindingHandles添加一个子属性来注册一个绑定. ko.bindingHandl ...
- centos7 卸载 gitlab
标黑的就是关键命令,先停止gitlab服务,然后rpm -e卸载,然后查看剩余gitlab进程,然后杀死主进程,然后删除所有相关目录 1 [liuyx@MiWiFi-R3L-srv ~]$ sudo ...
- springmvc json结合
获取json数据 名字一样就获取了 user @RequestMapping("/addUser") public String addUser(User user,Htt ...