Verilog功能模块——同步FIFO

前言
FIFO功能模块分两篇文章,本篇为同步FIFO,另一篇为异步FIFO,传送门:
Verilog功能模块——异步FIFO-CSDN博客
同步FIFO实现起来是异步FIFO的简化版,所以,本博文不再介绍FIFO实现原理,感兴趣的同学可以去看我异步FIFO的文章,基本看懂了异步FIFO,同步FIFO自然就懂了。
二. 模块功能框图与信号说明

信号说明:
分类 | 信号名称 | 输入/输出 | 说明 |
---|---|---|---|
参数 | DATA_WIDTH | – | 数据位宽 |
ADDR_WIDTH | – | 地址位宽,FIFO深度=2**ADDR_WIDTH | |
FWFT_EN | – | First word fall-through输出模式使能,高电平有效 | |
FIFO写端口 | din | input | FIFO数据输入 |
wr_en | input | FIFO写使能 | |
full | output | FIFO满信号 | |
almost_full | output | FIFO快满信号,FIFO剩余容量<=1时置高 | |
FIFO读端口 | dout | output | FIFO数据输出 |
rd_en | input | FIFO读使能 | |
empty | output | FIFO空信号 | |
almost_empty | output | FIFO快空信号,FIFO内数据量<=1时置高 | |
时钟与复位 | clk | input | FIFO读时钟 |
rst | input | FIFO读复位 |
注意:
- 信号的命名与Vivado中的FIFO IP核完全一致
- 复位均为高电平复位,与Vivado中的FIFO IP核保持一致
- 复位为异步复位
- FIFO深度通过ADDR_WIDTH来设置,所以FIFO的深度必然是2的指数,如2、4、8、16等
三. 部分代码展示
//++ 生成读写指针 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
reg [ADDR_WIDTH:0] rptr;
always @(posedge clk or posedge rst) beginif (rst)rptr <= 0;else if (rd_en & ~empty)rptr <= rptr + 1'b1;
endreg [ADDR_WIDTH:0] wptr;
always @(posedge clk or posedge rst) beginif (rst)wptr <= 0;else if (wr_en & ~full)wptr <= wptr + 1'b1;
endwire [ADDR_WIDTH-1:0] raddr = rptr[ADDR_WIDTH-1:0];
wire [ADDR_WIDTH-1:0] waddr = wptr[ADDR_WIDTH-1:0];wire [ADDR_WIDTH:0] rptr_p1 = rptr + 1'b1;
wire [ADDR_WIDTH:0] wptr_p1 = wptr + 1'b1;
//-- 生成读写指针 ------------------------------------------------------------//++ 生成empty与almost_empty信号 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
always @(*) beginif (rst)empty <= 1'b1;else if (rptr == wptr)empty <= 1'b1;elseempty <= 1'b0;
endalways @(*) beginif (rst)almost_empty <= 1'b1;else if (rptr_p1 == wptr || empty)almost_empty <= 1'b1;elsealmost_empty <= 1'b0;
end
//-- 生成empty与almost_empty信号 ------------------------------------------------------------//++ 生成full与almost_full信号 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
always @(*) beginif (rst)full <= 1'b1;else if ((wptr[ADDR_WIDTH] != rptr[ADDR_WIDTH])&& (wptr[ADDR_WIDTH-1:0] == rptr[ADDR_WIDTH-1:0]))full <= 1'b1;elsefull <= 1'b0;
endalways @(*) beginif (rst)almost_full <= 1'b1;else if (((wptr_p1[ADDR_WIDTH] != rptr[ADDR_WIDTH])&& (wptr_p1[ADDR_WIDTH-1:0] == rptr[ADDR_WIDTH-1:0]))|| full)almost_full <= 1'b1;elsealmost_full <= 1'b0;
end
//-- 生成full与almost_full信号 ------------------------------------------------------------
三. 功能仿真
比较以下情形中的fifo行为是否与FIFO IP核一致,
情形一:单次写单次读
情形二:写满后再读空
情形三:在读的过程中写,在写的过程中读
判断模块功能正常的依据:
- 写入数据是否按顺序正常读出
- 空信号和满信号是否正常输出。
为方便比较,编写了顶层文件,实例化了FIFO IP核与自编模块,部分代码如下:
vivado_sync_fifo vivado_sync_fifo_u0 (.clk (clk ), // input wire clk.rst (rst ), // input wire rst.din (din ), // input wire [7 : 0] din.wr_en (wr_en ), // input wire wr_en.rd_en (rd_en ), // input wire rd_en.dout (vivado_fifo_dout ), // output wire [7: 0] dout.full (vivado_fifo_full ), // output wire full.almost_full (vivado_fifo_almost_full ), // output wire almost_full.empty (vivado_fifo_empty ), // output wire empty.almost_empty (vivado_fifo_almost_empty)// output wire almost_empty
);syncFIFO # (.DATA_WIDTH (DATA_WIDTH),.ADDR_WIDTH (ADDR_WIDTH),.FWFT_EN (FWFT_EN )
) syncFIFO_inst (.din (din ),.wr_en (wr_en ),.full (full ),.almost_full (almost_full ),.dout (dout ),.rd_en (rd_en ),.empty (empty ),.almost_empty (almost_empty),.clk (clk ),.rst (rst )
);
testbench部分代码如下:
// 生成时钟
localparam CLKT = 2;
initial beginclk = 0;forever #(CLKT / 2) clk = ~clk;
end// 读写使能控制
initial beginrst = 1;#(CLKT * 2)rst = 0;wr_en = 0;rd_en = 0;#(CLKT * 2)wait(~full && ~vivado_fifo_full); // 两个FIFO都从复位态恢复时开始写// 写入一个数据wr_en = 1;#(CLKT * 1)wr_en = 0;// 读出一个数据wait(~empty && ~vivado_fifo_empty);// 两个FIFO都非空时开始读,比较读数据和empty信号是否有差异rd_en = 1;#(CLKT * 1)rd_en = 0;// 写满wr_en = 1;wait(full && vivado_fifo_full); // 两个FIFO都满时停止写,如果两者不同时满,则先满的一方会有写满的情况发生,但对功能无影响// vivado FIFO IP在FWFT模式时, 设定深度16时实际深度为17, 但仿真显示full会在写入15个数据后置高, 过几个时钟后后拉低,// 再写入一个数据, full又置高; 然后过几个时钟又拉低, 再写入一个数据置高, 如此才能写入17个数据// 所以这里多等待12个wclk周期, 就是为了能真正写满vivado FWFT FIFO#(CLKT * 12)wr_en = 0;// 读空wait(~empty && ~vivado_fifo_empty);rd_en = 1;wait(empty && vivado_fifo_empty); // 两个FIFO都空时停止读,如果两者不同时空,则先空的一方会有读空的情况发生,但对功能无影响rd_en = 0;#(CLKT * 10)$stop;
end// 使用以下代码时,先注释掉上面的读写使能控制initial
// 同时读写
// initial begin
// #(CLKT * 30)
// $stop;
// end// assign wr_en = ~full || ~vivado_fifo_full; // 未满就一直写
// assign rd_en = ~empty || ~vivado_fifo_empty; // 未空就一直读always @(posedge clk) beginif (rst)din <= 0;else if (wr_en && ~full && ~vivado_fifo_full)din <= din + 1;
endendmodule
8bit,16深度,FWFT FIFO仿真,波形如下:

可以看到模块输出的自编fifo与vivado fwft fifo的写端口和读端口行为是一致的,只是可能会超前或滞后一定的clk周期。

可以看到empty拉低时,数据已经有效了,所以自编模块实现了FWFT功能,Vivado FIFO的实际深度为17,所以它多读出了一个数据,空信号更晚拉高。
因篇幅问题,其它条件下的仿真不再展示,感兴趣的同学可通过更改testbench自行验证。
- FWFT_EN改为0,注意同步修改Vivado FIFO的配置
四. 工程分享
Verilog功能模块——同步FIFO,Vivado 2021.2工程。
欢迎大家关注我的公众号:徐晓康的博客,回复以下四位数字获取。
8302
建议复制过去不会码错字!
或者在我的码云仓库获取,传送门:
徐晓康/Verilog功能模块 - 码云 - 开源中国 (gitee.com)

徐晓康的博客持续分享高质量硬件、FPGA与嵌入式知识,软件,工具等内容,欢迎大家关注。
相关文章:

Verilog功能模块——同步FIFO
前言 FIFO功能模块分两篇文章,本篇为同步FIFO,另一篇为异步FIFO,传送门: Verilog功能模块——异步FIFO-CSDN博客 同步FIFO实现起来是异步FIFO的简化版,所以,本博文不再介绍FIFO实现原理,感兴趣…...

Unity ToLua热更框架使用教程(1)
从本篇开始将为大家讲解ToLua在unity当中的使用教程。 Tolua的框架叫LuaFramework,首先附上下载链接: https://github.com/jarjin/LuaFramework_UGUI_V2 这个地址的是UGUI的。 下载完之后导入项目,首先,我们要先让这个项目跑起…...

车载相关名词--车载数据中心方案
车载数据中心方案 参考链接:https://zhuanlan.zhihu.com/p/600031042?utm_id=0 下面这张图是小鹏汽车嵌入式系统高级专家 唐黾 在同ARM一起的一个演讲稿中发布的,是一张未来车载数据中心单芯片方案构想图。主要针对的是智驾域和座舱域融合方案,下面对如上图的内外部组件及…...

helm使用
前言 类似于 Linux 的 YUM、APT,Helm 是 K8S 的包管理工具。 Helm, 一个二进制工具,用来安装、升级、卸载 K8S 中的应用程序。 Helm Chart,一个 tgz 包,类似安卓的 APK。 K8S 应用打包成 Chart,通过 He…...

Python in Visual Studio Code 2023年10月发布
排版:Alan Wang 我们很高兴地宣布 Visual Studio Code 的 Python 和 Jupyter 扩展于 2023 年 10 月发布! 此版本包括以下公告: Python 调试器扩展更新弃用 Python 3.7 支持Pylint 扩展更换时的 Lint 选项Mypy 扩展报告的范围和守护程序模式G…...

Webmin远程命令执行漏洞复现报告
漏洞编号 CVE-2019-15107 漏洞描述 Webmin是一个基于Web的系统配置工具,用于类Unix系统。密码重置页面中存在此漏洞,允许未经身份验证的用户通过简单的 POST 请求执行任意命令。 影响版本 Webmin<1.920 漏洞评级 严重 利用方法(利…...

webstorm自定义文件模板(Vue + Scss)
最终效果如下: 具体配置如下: 新增文件代码如下: <!--* Description: ${COMPONENT_NAME} 页面* Author: mhf* Date: ${DATE} --> <template><div>${COMPONENT_NAME} </div> </template><script&g…...

楔子-写在之前
最近一年都在忙着一个项目,并且需要学习另一个领域的知识,从单片机过渡到了LINUX嵌入式,倒静不下心去写点东西。看了下之前写的东西,感觉已经过去了很久很久。现在项目快忙完了,准备把最近的心得给大家分享下。 前言 …...

第 5 章 数组和广义表(稀疏矩阵的三元组顺序表存储实现)
1. 背景说明 为了节省存储空间,可以对这类矩阵进行压缩存储。所谓压缩存储是指:为多个值相同的元只分配一个存储空间,对零元不分配空间。 2. 示例代码 1)status.h /* DataStructure 预定义常量和类型头文件 */ #include <string.h>#i…...

【RabbitMQ 实战】11 队列的结构和惰性队列
一、 队列的结构 队列的组成: 队列由 rabbit_amgqueue_process 和 backing_queue两部分组成。rabbit_amqqueue_process负责协议相关的消息处理,即接收生产者发布的消息、向消费者交付消息、处理消息的确认 (包括生产端的 confirm 和消费端的 ack) 等。…...

Python3-批量重命名指定目录中的一组文件,更改其扩展名
Python3-批量重命名指定目录中的一组文件,更改其扩展名 1.argparse模块2.vars内置函数3.os.listdir(path)4.os.path.splitext(filepath)5.os.path.join6.os.rename7.os.path.isfile8.批量重命名指定目录中的一组文件,更改其扩展名 1.argparse模块 argpa…...

渗透测试KAILI系统的安装环境(第八课)
KAILI系统的安装环境(第八课) Kaili是一款基于PHP7的高性能微服务框架,其核心思想是面向服务的架构(SOA),支持http、websocket、tcp等多种通信协议,同时还提供了RPC、Service Mesh、OAuth2等功能。Kaili框架非常适合构…...

如何正确方便的理解双指针?力扣102 (二叉树的层序遍历)
双指针,顾名思义就是指针的指针。 在此之前我们需要先理解单指针 (简称为指针)。指针很简单,直接上例子:例:现有两个变量,a10,b20. 要求:交换他们的值,输出的结果应为a20…...

Vue或uniapp引入自定义字体
一、为什么引入字体 对于大部分APP或网站而言,字体是很重要的一部分。在前端开发中,选用合适的字体往往会极大地提升网站的视觉体验。然而,网页中默认字体的种类和风格有限,且在不同的设备、浏览器上渲染效果不尽相同。因此&…...

力扣:LCR 122. 路径加密 题目:剑指Offer 05.替换空格(c++)
本文章代码以c为例! 力扣:LCR 122. 路径加密 题目: 代码: class Solution { public:string pathEncryption(string path) {for(int i0;i<path.size();i){if(path[i].){path[i] ;}}return path;} }; 难度升级(原…...

cJson堆内存释放问题
cJSON_Delete(),是用来释放json对象的,释放父JSON对象后,子JSON对象也会被释放。 CJSON_free(),是用来释放其他对象的。 int main(void) {cJSON* cjson_test NULL;cJSON* cjson_address NULL;cJSON* cjson_skill NULL;char* s…...

论文阅读/写作扫盲
第一节:期刊科普 JCR分区和中科院分区是用于对期刊进行分类和评估的两种常见方法。它们的存在是为了帮助学术界和研究人员更好地了解期刊的学术质量、影响力和地位。 JCR分区(Journal Citation Reports):JCR分区是由Clarivate Ana…...

一文拿捏对象内存布局及JMM(JAVA内存模型)
1 JMM(Java Memory Model) 1 概述 Java内存模型(Java Memory Model简称JMM)是一种抽象的概念,并不真实存在,它描述的一组规则或者规范。通过这些规则、规范定义了程序中各个变量的访问方式。jvm运行的程序的实体是线程,而每个线程运行时&am…...

Android组件通信——ActivityGroup(二十五)
1. ActivityGroup 1.1 知识点 (1)了解ActivityGroup的作用; (2)使用ActivityGroup进行复杂标签菜单的实现; (3)使用PopupWindow组件实现弹出菜单组件开发; 1.2 具体…...

js的继承的方式
1.对象冒充继承 使用 bind,call,apply 解决构造函数属性的继承 缺点:不能继承原型上的属性和方法 //-------------父类-------------function Person(name, age, sex) {this.name name;this.age age;this.sex sex;}Person.prototype.run function () {console.log(我${this…...

聊聊HttpClient的重试机制
序 本文主要研究一下HttpClient的重试机制 HttpRequestRetryHandler org/apache/http/client/HttpRequestRetryHandler.java public interface HttpRequestRetryHandler {/*** Determines if a method should be retried after an IOException* occurs during execution.**…...

北邮22级信通院数电:Verilog-FPGA(4)第三周实验:按键消抖、呼吸灯、流水灯 操作流程注意事项
北邮22信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章,请访问专栏: 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 目录 一.注意事项 二.按键消抖 2.1 LED_deboun…...

Ghidra101再入门(上?)-Ghidra架构介绍
Ghidra101再入门(上?)-Ghidra架构介绍 最近有群友问我,说:“用了很多年的IDA,最近想看看Ghidra,这应该怎么进行入门?“这可难到我了。。 我发现,市面上虽然介绍Ghidra怎么用的文章和书籍很多&…...

Vue3路由引入报错解决:无法找到模块“xxx.vue”的声明文件 xxx隐式拥有 “any“ 类型。
这类情况应该遇见过吧,这是因为 TypeScript只能理解 .ts 文件,无法理解 .vue 文件。 解决方法:在项目的根目录或者src文件夹下创建一个后辍为 文件名.d.ts 的文件,并写入一下内容: declare module *.vue {import { …...

基于若依ruoyi-nbcio支持flowable流程分类里增加流程应用类型
更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码: https://gitee.com/nbacheng/ruoyi-nbcio 演示地址:RuoYi-Nbcio后台管理系统 主要考虑到流程分很多种,普通的是OA流程,还有自定义业务流程,钉钉流程等…...

JS之同步异步promise、async、await
promise异步操作 Promise是异步编程的一种解决方案 JavaScript异步与同步解析 学习promise前我们先来了解下什么是异步? 基本概念: 消息队列中的任务分为宏任务与微任务;调用栈也可以称为主线程 首先我们要知道js是单线程语言,也就是说…...

【OpenCV • c++】自定义直方图 | 灰度直方图均衡 | 彩色直方图均衡
文章目录 一、什么是直方图二、自定义直方图三、灰度直方图均衡四、彩色直方图均衡一、什么是直方图 直方图广泛应用于很多计算机视觉处理当中。通过标记帧与帧之间显著的边缘和颜色的变化,可以检测视频中的场景变化。在每个兴趣点设置一个有相似特征的直方图所构成的“标签”…...

el-tree目录和el-table实现搜索定位高亮方法
需求:el-tree目录实现搜索查询el-table表格项,双击表格项根据yiZhuMLID||muLuID定位el-tree目录,并且高亮展示在可视化区域内,再重新根据el-tree目录的yiZhuMLID搜索刷新el-table表格,定位且高亮展示相对应的yiZhuMLID…...

linux常用指令
基础命令 cd:用于切换目录。例如,要从当前目录切换到/home/user目录,可以使用命令“cd /home/user”。ls:用于列出目录内容。例如,要列出当前目录的内容,可以使用命令“ls”。mkdir:用于创建目…...

C语言,指针的一些运算
若创建一个数组:int arr[10] 0; 用指针变量来储存数组首元素的地址:int* p arr,这里arr是数组名,表示首元素地址。 若p p 1或者p之后p本来指向数组首元素地址,就变成了指向第二个元素的地址,p n即指向第n 1个地…...