关于异步FIFO的知识点–详细代码解释(很干)[通俗易懂]

大家好,又见面了,我是你们的朋友全栈君。

异步FIFO的空满判断也是通过地址位扩展来实现的,这一点与同步FIFO是相同的。然而,异步FIFO无法通过计数器来判断空满,且将地址转换为格雷码所带来的问题与同步FIFO的判断算法不同,这是异步FIFO的一个难点。关于格雷码的空满判断,请继续阅读下文。

异步FIFO的框架结构:

关于异步FIFO的知识点–详细代码解释(很干)[通俗易懂]

顶层代码:

module asyn_fifo(w_clk,rst_n,w_req,w_data,r_clk,r_req,r_data,w_full,r_empty);    parameter DEPTH=256; //设置FIFO深度为256    parameter WIDTH_A=8; //根据FIFO深度进行地址编码    parameter WIDTH_D=16;//数据位宽为16    input w_clk; //写时钟    input rst_n; //复位信号    input w_req; // 写使能信号    input [WIDTH_D-1:0]w_data; // 写数据    input r_clk; // 读时钟    input r_req; // 读使能    output w_full; // 输出FIFO满信号    output r_empty; // 输出FIFO空信号    output [WIDTH_D-1:0]r_data; // 读数据    wire [WIDTH_A:0]w_addr; //写地址    wire [WIDTH_A:0]w_gaddr;//将地址转换成格雷码    wire [WIDTH_A:0]w_gaddr_syn;//转换成格雷码后的写地址同步到读时钟域去    wire [WIDTH_A:0]r_addr;// 读地址    wire [WIDTH_A:0]r_gaddr;//    wire [WIDTH_A:0]r_gaddr_syn;//    asyn_fifo_write_part write_control( //写控制        .rst_n(rst_n),        .w_clk(w_clk),        .w_req(w_req),        .r_gaddr_syn(r_gaddr_syn),        .w_full(w_full),        .w_addr(w_addr),        .w_gaddr(w_gaddr)    );    asyn_fifo_syn syn_w_2_r( // 写地址同步到读时钟域        .rst_n(rst_n),        .w_r_clk(r_clk),        .w_r_gaddr(w_gaddr),        .w_r_gaddr_syn(w_gaddr_syn)    );    asyn_fifo_read_part read_control( // 读控制        .rst_n(rst_n),        .r_clk(r_clk),        .r_req(r_req),        .w_gaddr_syn(w_gaddr_syn),        .r_empty(r_empty),        .r_addr(r_addr),        .r_gaddr(r_gaddr)    );    asyn_fifo_syn syn_r_2_w( // 读地址同步到 写时钟域        .rst_n(rst_n),        .w_r_clk(w_clk),        .w_r_gaddr(r_gaddr),        .w_r_gaddr_syn(r_gaddr_syn)    );    asyn_fifo_RAM_1 ram( // RAM存储        .rst_n(rst_n),        .w_clk(w_clk),        .r_clk(r_clk),        .w_en(w_req & (!w_full)),        .r_en(r_req & (!r_empty)),        .w_data(w_data),        .w_addr(w_addr[WIDTH_A-1:0]),        .r_addr(r_addr[WIDTH_A-1:0]),        .r_data(r_data)    );endmodule

各部分代码:

(1)写控制模块

module asyn_fifo_write_part(rst_n,w_clk,w_req,w_gaddr,w_addr,w_full,r_gaddr_syn);    parameter WIDTH_A=8;    input rst_n;    input w_clk;    input w_req; //写使能    input [WIDTH_A:0]r_gaddr_syn; //同步后的读地址作为输入作用到写控制,进行写满判断    output [WIDTH_A:0]w_gaddr; //调用bin_to_gray模块,将二进制转换成格雷码    output [WIDTH_A:0]w_addr;    output w_full;    reg [WIDTH_A:0]w_addr;    always @(posedge w_clk or negedge rst_n) begin        if(!rst_n)            w_addr <= 0;        else if(w_req & (!w_full))            w_addr <= w_addr + 1'b1;    end    //假设地址位是3bit,所以FIFO深度为8,扩展一位后为4bit。先进行写操作,首先写入4个数据,此时的地址二进制表示为0100,格雷码表示为0110。然后数据被读取,//读地址二进制,为0100,格雷码也为0110,此时在读时钟域可判读为空。后连续写入8个数据,此时地址的二进制表示为1100,格雷码为1010,此时在写时钟域可判断////为写满。所以将二进制转换成格雷码带来的问题:通过格雷码进行空满判断就不能只考虑最高位的不同,需要进行最高位和次高位的取反,其余位保持即可判断。    assign w_full=({~w_gaddr[WIDTH_A],~w_gaddr[WIDTH_A-1],w_gaddr[WIDTH_A-2:0]}==r_gaddr_syn)?1'b1:1'b0;    asyn_fifo_bin_to_gray bin_to_gray(        .bin_c(w_addr),        .gray_c(w_gaddr)    );endmodule

(2)不同时钟域的两级同步模块:这一块没什么好解释的,就是打两拍同步,防止亚稳态传播

通义灵码 通义灵码

阿里云出品的一款基于通义大模型的智能编码辅助工具,提供代码智能生成、研发智能问答能力

通义灵码 31 查看详情 通义灵码

module asyn_fifo_syn(rst_n,w_r_clk,w_r_gaddr,w_r_gaddr_syn);    parameter WIDTH_D=8;    input rst_n;    input w_r_clk;    input [WIDTH_D:0]w_r_gaddr;    output [WIDTH_D:0]w_r_gaddr_syn;    reg [WIDTH_D:0]w_r_gaddr_syn_1,w_r_gaddr_syn_2;    always @(posedge w_r_clk or negedge rst_n) begin        if(!rst_n) begin            w_r_gaddr_syn_1 <= 0;            w_r_gaddr_syn_2 <= 0;        end        else begin            w_r_gaddr_syn_1 <= w_r_gaddr;            w_r_gaddr_syn_2 <= w_r_gaddr_syn_1;        end    end    assign w_r_gaddr_syn = w_r_gaddr_syn_2;endmodule

(3)读控制模块:空信号较易判断,地址相同即为空

module asyn_fifo_read_part(rst_n,r_clk,r_req,w_gaddr_syn,r_empty,r_addr,r_gaddr);    parameter WIDTH_A=8;    input rst_n;    input r_clk;    input r_req;    input [WIDTH_A:0]w_gaddr_syn;    output [WIDTH_A:0]r_addr;    output [WIDTH_A:0]r_gaddr;    output r_empty;    reg [WIDTH_A:0]r_addr;    always @(posedge r_clk or negedge rst_n) begin        if(!rst_n)            r_addr <= 0;        else if(r_req & (!r_empty))            r_addr <= r_addr + 1'b1;    end    assign r_empty=(w_gaddr_syn==r_gaddr)?1'b1:1'b0;    asyn_fifo_bin_to_gray bin_to_gray_2(        .bin_c(r_addr),        .gray_c(r_gaddr)    );endmodule

(4)RAM模块

module asyn_fifo_RAM_1 #(    parameter DEPTH = 256,    parameter WIDTH_A = 8, //addr bit    parameter WIDTH_D = 16 //data bit)(    input r_clk,    input w_clk,    input rst_n,    input [WIDTH_A-1:0] w_addr,    input [WIDTH_D-1:0] w_data,    input w_en,    input [WIDTH_A-1:0] r_addr,    input r_en,    output reg[WIDTH_D-1:0] r_data);    reg [15:0] mem[0:DEPTH-1];    integer i;    always @( posedge w_clk ) begin        if( !rst_n )            for(i=0;i<DEPTH;i=i+1)                mem[i] <= 0;        else if(w_en)            mem[w_addr] <= w_data;    end    always @( posedge r_clk ) begin        if( !rst_n )            r_data <= 0;        else if(r_en)            r_data <= mem[r_addr];    endendmodule

(5)二进制转换成格雷码bin_to_gray模块

module asyn_fifo_bin_to_gray(bin_c,gray_c);    parameter WIDTH_D=8;    input [WIDTH_D:0]bin_c;    output [WIDTH_D:0]gray_c;    wire h_b;    assign h_b=bin_c[WIDTH_D];    reg [WIDTH_D-1:0]gray_c_d;    integer i;    always @(*) begin        for(i=0;i<WIDTH_D;i=i+1)            gray_c_d[i] = bin_c[i] ^ bin_c[i+1];        gray_c = {h_b,gray_c_d};    endendmodule

tb文件:

`timescale 1ns/1psmodule asyn_fifo_top_tb;    reg rst_n;    reg w_clk;    reg r_clk;    reg w_req;    reg r_req;    reg [15:0]w_data;    wire [15:0]r_data;    wire w_full;    wire r_empty;    always #2 w_clk=~w_clk;    always #8 r_clk=~r_clk;    asyn_fifo u1(        .w_clk(w_clk),        .rst_n(rst_n),        .w_req(w_req),        .w_data(w_data),        .r_clk(r_clk),        .r_req(r_req),        .r_data(r_data),        .w_full(w_full),        .r_empty(r_empty)    );    initial begin        $vcdpluson();    end    initial begin        w_req=0;        w_data=0;        r_req=0;        w_clk=0;        r_clk=0;        rst_n=0;        #10;        rst_n=1;        #2;        w_req=1;        #10;        r_req=1;        forever begin            @(posedge w_clk)            if(!w_full) begin                w_data = w_data + 1'b1;            end        end    endendmodule

关于异步FIFO的知识点–详细代码解释(很干)[通俗易懂]关于异步FIFO的知识点–详细代码解释(很干)[通俗易懂]

在仿真中验证了逻辑的正确性。

发布者:全栈程序员栈长,转载请注明出处:https://www.php.cn/link/2f75f02d15ae1f6ed96adc5761351562原文链接:https://www.php.cn/link/c8377ad2a50fb65de28b11cfc628d75c

以上就是关于异步FIFO的知识点–详细代码解释(很干)[通俗易懂]的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/456780.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月8日 01:44:33
下一篇 2025年11月8日 01:49:10

相关推荐

  • 机器学习领域中最受欢迎的20个R语言包

    我们通过对下载次数进行分析,筛选出了在机器学习领域中最受欢迎的前20个r语言包。 这些R包不仅受到Kaggle高手们的青睐,也得到了资深用户的认可。它们的使用率和评价不仅仅取决于其他包的依赖关系。接下来,我们将逐一介绍这20个R包。 1. e1071 提供了潜类分析、短时傅里叶变换、模糊聚类、支持向…

    2025年12月1日
    000
  • Qt编写的项目作品4-输入法V2019

    一、功能特点 本输入法未采用Qt系统层输入法框架,而是采用了独创的输入切换机制。完全基于QWidget编写,适用于任何目标平台(已测试Windows、Linux、嵌入式Linux等),兼容任意Qt版本(已测试从Qt4.6.0到Qt5.13),支持任意编译器(已测试mingw、gcc、msvc等),并…

    2025年11月27日
    000
  • H5 图像识别

    识别对比 1、百度识别 发现百度的图片搜索识别率不是特别,下面为测试图片跟测试后的结果: 测试图片: 下面为测试后的结果: 2、采用 tesseract.js 后结果 H5 图像识别 (采用Tesseract.js 进行识别) 简单的文案之类的,识别的还算可以,但是稍微复杂点的,准确率就不是那么好了…

    2025年11月27日 运维
    000
  • 10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

    网络上虽然有许多项目,但详细教程并不多见。今天分享的项目从安装部署到代码功能都有详细说明。 eladmin 是一个基于 Spring Boot 2.1.0、Jpa、Spring Security、redis 和 Vue 的前后端分离的后台管理系统。项目采用模块化开发,权限控制采用 RBAC,支持数据…

    2025年11月27日 运维
    000
  • MyEclipse、eclipse代码自动补全(联想输入)「建议收藏」

    大家好,很高兴再次见到你们,我是你们的朋友全栈君。myeclipse(eclipse)的自动补全(联想输入)功能有两种方式,一种是直接提示,另一种是通过快捷键提示,设置方法如下: 一、每次输入都自动提示通过Window->preferences->Java->Editor->…

    2025年11月26日 系统教程
    000
  • linux 目录结构及用途

    深入了解linux的树状文件目录结构至关重要,只有熟记这些目录,你才能在命令行中自由切换,畅行无阻。 在Linux的世界中,一切皆文件。对于Linux来说,所有的资源都是以文件的形式存在的。 例如,CPU会被映射到 /dev 目录下的 cpu 文件夹中。 再比如,硬盘(disk)会被映射到 /dev…

    2025年11月26日 系统教程
    000
  • Selenium之文件上传、下载

    在文件上传功能中,通常需要点击上传按钮,然后通过windows窗口选择文件进行上传。然而,由于windows的弹窗不属于浏览器元素,webdriver无法直接操作这些控件。那么,如何模拟文件上传操作呢?以下是两种方法的详细介绍。 方法一:通过元素定位,使用send_keys()方法输入文件路径。 这…

    2025年11月25日 系统教程
    000
  • 哈希传递(Pass The Hash)攻击与利用

    哈希传递攻击详解 哈希传递攻击(Pass The Hash)是一种通过获取与账户相关的密码散列值(通常是 NTLM Hash)来实施的攻击方法。 在域环境中,用户通常使用域账号登录计算机。由于许多计算机在安装时使用相同的本地管理员账户密码,如果这些计算机的本地管理员账户和密码一致,攻击者便可利用哈希…

    2025年11月23日 系统教程
    200
  • 小案例-01-字符串转计算公式

    系统:windows 7语言版本:anaconda3-4.3.0.1-windows-x86_64编辑器:pycharm-community-2016.3.2python:3.6.0 本系列介绍一些小案例,综合运用所学,但又不会很复杂今天介绍字符串转计算公式 Part 1:示例 已知一个已字符串存储…

    2025年11月19日 系统教程
    000
  • [译]C++17, 语言核心层变化的更多细节

    在之前的文章中,我介绍了一些c++++17语言核心层的变化。这次我将详细探讨更多相关的细节,涉及的主题包括:内联变量(inline variables)、模板、auto相关的自动类型推导以及属性(attributes)。 C++标准整体的特性时间线 上图列出了C++17的主要特性,而本文将介绍一些不…

    2025年11月11日
    000
  • 跨平台代码的3种组织方式

    一、起源 在前一篇文章中,我分享了一个跨平台头文件的示例,该示例在 Windows 平台上更为重要,因为它需要处理库函数的导入和导出声明(dllexport、dllimport)。基于这个头文件,我们可以进一步扩展,以实现更细粒度的控制,比如对编译器和其版本的判断。 在源代码中,我们同样会遇到一些跨…

    2025年11月10日 系统教程
    000
  • Mac 键盘上的 Windows 按键映射

    我再次回到了mac平台,这里记录一下windows与mac下的键盘映射关系,方便大家参考。 Mac 键盘上的 Windows 按键Mac 键盘上的许多按键与 Windows 键盘按键的功能相同。以下是其中的一些按键: Windows 按键 Mac 按键 用途 向前删除键 ⌦Fn-Delete 删除右…

    2025年11月10日 系统教程
    000
  • JDBC的概述「建议收藏」

    大家好,我们又见面了,我是你们的朋友全栈君。 ————————————————–JDBC概述———————————————————— 1 JDBC概述1什么是JDBCJDBC(JavaDataBase Connectivity),也就是Java数据库连接,简单来说,就是使用Java语言来操作数据库。…

    2025年11月10日 运维
    000
  • GDB调试

    约定:在gdb的命令中,如果有缩写形式,会在第一次出现时用小括号标注,例如运行命令写为run(r);本文中使用尖括号表示一类实体,例如表示程序的位置;中括号[]表示内容可选,例如[=]表示“=”可以有也可以没有(本身表示一种实体);“|”表示或的关系。 GDB简介GDB是GNU开源组织的一款强大代码…

    2025年11月8日 运维
    000
  • 一文带你学会Linux系统的环境变量

    背景 由于 Linux 命令行没有图形化界面,因此,所有的设置都采用文本的方式来完成,而其中最重要的用户设置则为 bashrc 文件,bashrc 配置文件非常重要,相当于图形化界面的设置选项。很多时候,命令找不到,缺少 XXX,都可能与这个有关。如果 bashrc 修改错误,会造成严重的影响。下面…

    2025年11月8日 运维
    000
  • Python Qt GUI设计:窗口之间数据传递(拓展篇—5)

    目录 1、单一窗口数据传输 2、多窗口数据传输:调用属性 3、多窗口数据传输:信号与槽 在开发程序时,如果这个程序只有一个窗口,则应该关心这个窗口里面的各个控件之间是如何传递数据的。如果这个程序有多个窗口,那么还应该关心不同的窗口之间是如何传递数据的。 本篇博文首先给出一个例子,说明在一个窗口中不同…

    2025年11月8日 运维
    200
  • STM32MP157启动程序 ROM Code 详解

    一、stm32mp157启动流程 两张图展示了STM32MP157的完整启动过程,分为五个步骤: ROM Code:启动时初始化基本时钟,从指定的启动介质中加载FSBL,并对其进行验证和启动;FSBL:执行TF-A程序,完成时钟树初始化,初始化DDR,从选定的介质中加载SSBL,并启动SSBL;SS…

    2025年11月8日 运维
    300
  • 13K点赞都基于 Vue+Spring 前后端分离管理系统ELAdmin,大爱

    虽然网络上有许多项目,但详细教程的项目却不多见。今天分享的这个项目从安装部署到代码具体功能都有非常详细的说明。 ELAdmin 是一款基于 Spring Boot 2.1.0、Jpa、Spring Security、Redis、Vue 的前后端分离的后台管理系统。该项目采用分模块开发方式,权限控制采…

    2025年11月8日 运维
    000
  • 图的应用详解-数据结构

    概述 最小生成树——无向连通图的所有生成树中有一棵边的权值总和最小的生成树 拓扑排序 ——由偏序定义得到拓扑有序的操作便是拓扑排序。建立模型是AOV网 关键路径——在AOE-网中有些活动可以并行地进行,所以完成工程的最短时间是从开始点到完成点的最长路径的长度,路径长度最长的路径叫做关键路径(Crit…

    2025年11月8日 运维
    000
  • 从零开始学习UCOSII操作系统15–总结篇[通俗易懂]

    大家好,又见面了,我是你们的朋友全栈君。 从零开始学习UCOSII操作系统15–总结篇 前言:在大学的时候,我们班级上面都有很多人觉得学习UCOSII(包括UCOSIII)是没什么厉害的,因为很多人都喜欢去学习Linux操作系统,但是,但是,真实的对整个UCOSII操作系统进行学习,我可以保证,如果…

    2025年11月8日 运维
    000

发表回复

登录后才能评论
关注微信