基于FPGA的异步FIFO设计

今天要介绍的异步FIFO,可以有不同的读写时钟,即不同的时钟域。由于异步FIFO没有外部地址端口,因此内部采用读写指针并顺序读写,即先写进FIFO的数据先读取(简称先进先出)。这里的读写指针是异步的,处理不同的时钟域,而异步FIFO的空满标志位是根据读写指针的情况得到的。为了得到正确的空满标志位,需要对读写指针进行同步。一般情况下,如果一个时钟域的信号直接给另一个时钟域采集,可能会产生亚稳态,亚稳态的产生对设计而言是致命的。为了减少不同时钟域间的亚稳态问题,我们先对它进行两拍寄存同步,如图1所示。当然,对异步信号的寄存越多,产生亚稳态的概率就越小,但延时越多。不过一般情况下,寄存两拍就够了。为了继续减少亚稳态产生的概率,在对异步信号同步之前,将其转换为格雷码,使其每个状态只有一个位在变化。例如,假设N位二进制变量产生的亚稳态概率为a,那么二进制转换成格雷码后其产生的亚稳态概率则为a/N。

对异步信号进行同步.jpg

图1  对异步信号用两级寄存器同步

    根据上述原理,设计了异步FIFO的架构,如图2所示。

异步FIFO设计框架.jpg

图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的读写//******************************************************************//  写RAMalways @(posedge wr_clk)beginif((wr_en & ~wr_full) == 1'b1)mem[wr_addr] <= wr_data;end//  读RAMassign 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)beginif(wr_rst_n == 1'b0)wr_bin_ptr <= {(C_FIFO_DEPTH_WIDTH+1){1'b0}};elsewr_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)beginif(wr_rst_n == 1'b0)beginwr_full <= 1'b0;wr_cnt  <= {(C_FIFO_DEPTH_WIDTH+1){1'b0}};endelsebeginwr_full <= wr_full_w;wr_cnt  <= wr_cnt_w;endend//******************************************************************//  二进制读指针的产生//******************************************************************assign next_rd_bin_ptr = rd_bin_ptr + (rd_en & ~rd_empty);always @(posedge rd_clk or negedge rd_rst_n)beginif(rd_rst_n == 1'b0)rd_bin_ptr <= {(C_FIFO_DEPTH_WIDTH+1){1'b0}};elserd_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)beginif(rd_rst_n == 1'b0)beginrd_empty <= 1'b0;rd_cnt   <= {(C_FIFO_DEPTH_WIDTH+1){1'b0}};endelsebeginrd_empty <= rd_empty_w;rd_cnt   <= rd_cnt_w;endendendmodule

    其中,模块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)beginif(rst_n == 1'b0){dout,data_r} <= {(2*C_DATA_WIDTH){1'b0}};else{dout,data_r} <= {data_r,din};endendmodule

   由于字数的限制,异步FIFO的功能验证放在下一篇博文中吧!!!

转载:http://blog.chinaaet.com/crazybird/p/5100000872

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/284283.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EJB

Enterprise JavaBean,企业级javabean,是J2EE的一部分&#xff0c;定义了一个用于 开发基于组件的企业多重应用程序的标准。其特点包括网络服务支持和核心开发工具(SDK)。 是Java的核心代码&#xff0c;分别是会话Bean&#xff08;Session Bean&#xff09;&#xff0c;实体Be…

WinForm(一):开始一个WinForm程序

WinForm程序只能运行在Windows上&#xff0c;即使是基于.NET5&#xff0c;6&#xff0c;7也一样。因为WinForm的UI层对接的底层API是基于Windows的。用VisualStudio创建一个WinForm应用很简单&#xff0c;建议使用非.NET Framework版&#xff0c;因为.NET Framework微软渐渐不支…

【ArcGIS微课1000例】0050:Geodatabase属性域操作全解

文章目录 1. 属性域的创建2. 属性域的查看3. 属性域的删除与修改4. 属性域的关联地理数据库按照面向对象的模型存储地理信息,也可以将其非空间信息保存在表中。对于要素和表可以设置一些规则进行限制,对属性的约束称为属性域。 属性域是描述字段合法值的规则,是一种增强数据…

『JavaScript』核心

为什么80%的码农都做不了架构师&#xff1f;>>> 弱类型语言 JavaScript是一种弱类型的语言。变量可以根据所赋的值改变类型。原始类型之间也可以进行类型转换。其弱类型的物质为其带来了极大的灵活性。 注意&#xff1a;原始类型使用值传递&#xff0c;复合类型使用…

优酷VIP会员周卡只需7.5元,看《沉香如屑》用优酷视频

由杨紫、成毅主演的《沉香如屑》已上线7天。站内热度值已经破万&#xff0c;也拿下了4次日冠的好成绩。追优酷视频最新热剧不能没有优酷VIP会员啊&#xff0c;优酷的会员&#xff0c;价格算是最便宜的了&#xff0c;下面是幻海优品优酷VIP会员特价充值的价格。优酷VIP会员特价充…

Solr6.1.0Windows安装步骤

一、 环境 solr 6.1.0 下载地址 http://archive.apache.org/dist/lucene/solr/6.1.0/ jdk 1.8 tomcat8 二、 安装solr到tomcat 1.解压solr&#xff0c;把 solr-6.1.0\solr-6.1.0\server 下的solr-webapp 文件夹拷贝到tomcat 的webapps下&#xff0c;重命名为solr&#xff1b;…

[转]Autofac 框架初识与应用

一、前言 这上一篇中&#xff0c;主要讲述了什么是IoC容器&#xff0c;以及了解到它是DI构造函注入的框架&#xff0c;它管理着依赖项的生命周期以及映射关系&#xff0c;同时也介绍实践了在ASP.Net Core中,默认提供的内置IoC容器&#xff0c;以及它的实例注册方式和相应的生命…

【ArcGIS微课1000例】0051:Geodatabase子类型操作全解

子类型是要素类中具有相同属性的要素的子集&#xff0c;或表中具有相同属性的对象的子集。可 通过它们对数据进行分类。 子类型是特征类(或对象类)中特征(或对象)的次级分类。例如一个公路线要素类可以根 据其字段类型的值细分为“高速公路”和“普通公路”两个子类型。 子类…

在Winform程序中设置管理员权限及为用户组添加写入权限

在我们一些Winform程序中&#xff0c;往往需要具有一些特殊的权限才能操作系统文件&#xff0c;我们可以设置运行程序具有管理员权限或者设置运行程序的目录具有写入的权限&#xff0c;如果是在操作系统里面&#xff0c;我们可以设置运行程序以管理员身份运行&#xff0c;或者设…

数据库性能系列之索引(上)

前言上一次&#xff0c;我们从优化子查询的角度&#xff0c;讲解了一些简单的数据库性能优化方面的知识。通过优化子查询的顺序&#xff0c;包括合理使用IN和EXISTS&#xff0c;可以起到部分查询的效率提升。但对于其他大多数场景&#xff0c;如单表记录很大&#xff0c;或多表…

【ArcGIS微课1000例】0052:创建地理数据库注记(标准注记、要素关联注记、尺寸注记)

本文讲述创建地理数据库注记(标准注记、要素关联注记、尺寸注记)的方法。 文章目录 一、创建标准注记二、创建与要素关联的注记三、创建尺寸注记一、创建标准注记 标准注记不与地理数据库中的要素关联。标准注记的一个例子是,地图上标记某山脉的文字,没有特定的要索代表该…

Lambda表达式超详解

目录 背景 Lambda表达式的用法 函数式接口 Lambda表达式的基本使用 语法精简 变量捕获 匿名内部类 匿名内部类中的变量捕获 Lambda的变量捕获 Lambda表达式在类集中的使用 Collection接口 List接口 Map接口 总结 背景 Lambda表达式是Java SE 8中的一个重要的新特性.…

【ArcGIS微课1000例】0043:ArcGIS绘制国界线的3种方法

本文讲解ArcGIS绘制国界线的3种方法。 文章目录 1. 直接修改国界线符号2. 缓冲区工具3. 制图表达1. 直接修改国界线符号 直接修改国界线/省界线的符号。点击“线要素”出现符号选择器,点击【编辑符号】按钮,编辑成下面右图的形式。缺点:只能在边界一侧出现缓冲样式,如下面…

C# 获取系统已安装的.NET版本

本文经原作者授权以原创方式二次分享&#xff0c;欢迎转载、分享。原文作者&#xff1a;唐宋元明清原文地址&#xff1a; https://www.cnblogs.com/kybs0/p/16478587.htmlC# 获取系统已安装的.NET版本获取系统已安装的.NET版本&#xff0c;来确定当前应用可运行的环境。获取系…

.NET 6 Minimal API 的经验分享

Minimal API 是 .NET 6 提供的最新功能 &#xff0c; 对比传统的 ASP.NET Core Web API 方式更加直接 , 你可以用几行代码编写好 REST API 。 没有了祖传的 Startup.cs 和 Controller &#xff0c;通过简单的代码就可以完成 API 的开发。在第二阶段的 .NET 挑战赛中就以 .NET 6…

JavaWeb之Filter过滤器

原本计划这一篇来总结JSP&#xff0c;由于JSP的内容比较多&#xff0c;又想着晚上跑跑步减减肥&#xff0c;所以今天先介绍Filter以及它的使用举例&#xff0c;这样的话还有些时间可以锻炼锻炼。言归正传&#xff0c;过滤器从字面理解她的话有拦网、过滤的功能&#xff0c;可以…

【ArcGIS微课1000例】0054:尺寸注记的创建与编辑

尺寸注记要素是一种特殊类型的文本,用于显示地图上的长度或距离,可以创建各种形状的尺寸注记要素,如对齐、简单对齐、水平线状、垂直线状和旋转线状等。 文章目录 一、创建尺寸注记1. 直接创建尺寸注记要素2. 通过已有尺寸注记要素创建二、编辑尺寸注记1. 删除尺寸注记要素2…

利用python实现批量查询ip地址归属地址

今天需要查询nginx访问的客户端ip是否和调度一样&#xff01;先是用shell把文件中的ip截取出来&#xff1a; python脚本如下&#xff1a;&#xff08;哈哈&#xff0c;新手写的很草率&#xff09;#!/usr/bin/env#-- coding: utf-8 - import jsonimport urllibimport socketimpo…

基于.NetCore开发博客项目 StarBlog - (16) 一些新功能 (监控/统计/配置/初始化)

系列文章基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客&#xff1f;基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目基于.NetCore开发博客项目 StarBlog - (3) 模型设计基于.NetCore开发博客项目 StarBlog - (4) markdown博客批量导入基于.N…