Verilog | FIFO简单实现

news2024/11/18 20:29:09

FIFO( First Input First Output)简单说就是指先进先出,也是缓存机制的一种,下面是我总结的 FIFO 的三大用途:
1)提高传输效率,增加 DDR 带宽的利用率。比如我们有 4 路视频数据缓存到 DDR 中去,比较笨的方法是,每个通道视频数据对应一颗 DDR。现在对于 DDR 来说非常浪费,因为现在的 DDR3 可以跑 1600Mbps DDR4 可以跑到2400Mbps,如果你还是把一路视频数据对应一颗 DDR 显然严重浪费了带宽。加入 FIFO 后,只要把 4 路数据先缓存进入 DDR,在缓存的过程中,快速得把数据从 FIFO 取出并且写入到 DDR 中,只要 FIFO 没有满就不会出现数据丢失。现在我们带宽够用,FIFO 给的足够大就可以确保数据不丢失。
2)数据位宽转换,比如我们有 32bit 的数据需要转换成 128bit 或者 32bit 的数据需要转换成 8bit,那么用 FIFO 来转换也是非常方便的。
3)跨时钟域的应用,比如数据是 2 个不同步的时钟,那么我们就可以用 FIFO 实现跨时钟域的传输。
以上总计的三点,很多时候是混合使用的。

FIFO的重点和难点是空满状态的判断。

同步FIFO

同步FIFO是指读写数据使用的是同一个时钟,所以不用进行跨时钟域处理。有两种设计方法:高位扩展法和计数器法

本程序设置了统计FIFO内部数据数量的计数器cnt,并根据计数器的大小判断空满。设FIFO的深度是DEPTH,如果cnt==0,说明FIFO内没有数据;如果cnt==DEPTH,说明FIFO已存满。

计数器根据读写信号自增或者自减。当读写同时进行时,计数器数值不变;当有效写入时计数器减1;当有效读取时,计数器加1。

`timescale 1ns/1ns
/**********************************RAM************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
	,input [WIDTH-1:0] wdata      	//数据写入
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
	,output reg [WIDTH-1:0] rdata 		//数据输出
);

reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];

always @(posedge wclk) begin
	if(wenc)
		RAM_MEM[waddr] <= wdata;
end 

always @(posedge rclk) begin
	if(renc)
		rdata <= RAM_MEM[raddr];
end 

endmodule  

/**********************************SFIFO************************************/
module sfifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					clk		, 
	input 					rst_n	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output reg				wfull	,
	output reg				rempty	,
	output wire [WIDTH-1:0]	rdata
);
    reg [$clog2(DEPTH)-1:0] waddr, raddr;
    reg [$clog2(DEPTH)  :0] cnt;
    
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            waddr <= 0;
        else
            waddr <= winc&~wfull? waddr+1: waddr;
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            raddr <= 0;
        else
            raddr <= rinc&~rempty? raddr+1:raddr;
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            cnt <= 0;
        else if(rinc&~rempty&winc&~wfull)
            cnt <= cnt;
        else if(winc&~wfull)
            cnt <= cnt + 1;
        else if(rinc&~rempty)
            cnt <= cnt - 1;
        else
            cnt <= cnt;
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            wfull  = 0;
            rempty = 0;
        end
        else begin
            wfull  = cnt == DEPTH;
            rempty = cnt == 0;
        end
    end
    
    dual_port_RAM #(
        .DEPTH(DEPTH       ),
        .WIDTH(WIDTH       )
    )
    myRAM(
        .wclk (clk         ), 
        .wenc (winc&~wfull ), 
        .waddr(waddr       ), 
        .wdata(wdata       ), 
        .rclk (clk         ), 
        .renc (rinc&~rempty), 
        .raddr(raddr       ), 
        .rdata(rdata       )
    );
endmodule

异步FIFO

异步FIFO的与同步FIFO的核心区别是它的读时钟和写时钟是不同步的。所以用对比读写地址的方法产生空满信号时,要进行跨时钟域处理。为了降低亚稳态可能性,异步FIFO还引入了格雷码。同时,格雷码也更方便产生空满信号。

二进制写地址读地址格雷码写地址读地址
空FIFO0 00000 00000 00000 0000
写满1 00000 00001 10000 0000
读空1 00001 00001 10001 1000
写满0 00001 00000 00001 1000
读空0 00000 00000 00000 0000

FIFO深度为16时,地址位宽位5,当最高位和次高位不相同,其余位相同认为是写满;当所有位相同认为是读空。

异步FIFO主要包含四部分:读写地址发生器、格雷码的产生与打拍、空满信号发生器以及RAM。

`timescale 1ns/1ns

/***************************************RAM*****************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
	,input [WIDTH-1:0] wdata      	//数据写入
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
	,output reg [WIDTH-1:0] rdata 		//数据输出
);

reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];

always @(posedge wclk) begin
	if(wenc)
		RAM_MEM[waddr] <= wdata;
end 

always @(posedge rclk) begin
	if(renc)
		rdata <= RAM_MEM[raddr];
end 

endmodule  

/***************************************AFIFO*****************************************/
module asyn_fifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					wclk	, 
	input 					rclk	,   
	input 					wrstn	,
	input					rrstn	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output wire				wfull	,
	output wire				rempty	,
	output wire [WIDTH-1:0]	rdata
);
	parameter addr_width = $clog2(DEPTH);
    //写指针--二进制
	reg [addr_width:0]wptr_bin,rptr_bin;
	always@(posedge wclk or negedge wrstn)
	begin
		if(!wrstn) wptr_bin <= 0;
		else if(winc && !wfull) wptr_bin <= wptr_bin +1;
		else ;
	end
	//读指针--二进制
	always@(posedge rclk or negedge rrstn)
	begin
		if(!rrstn) rptr_bin <= 0;
		else if(rinc && !rempty) rptr_bin <= rptr_bin +1;
		else ;
	end

	// 指针二进制转格雷码
	wire [addr_width:0]wptr_gray,rptr_gray;
	assign wptr_gray = wptr_bin ^ wptr_bin>>1;
	assign rptr_gray = rptr_bin ^ rptr_bin>>1;
	
	//
	reg [addr_width:0]wptr,rptr;
	always @(posedge wclk or negedge wrstn)
	begin
		if(!wrstn) wptr <= 0;
		else wptr <= wptr_gray;
	end
	// 
	always @(posedge rclk or negedge rrstn)
	begin
		if(!rrstn) rptr <= 0;
		else rptr <= rptr_gray;
	end

	// 经两级锁存器进行时钟同步
	reg [addr_width:0]sync_r2w,rptr_temp,sync_w2r,wptr_temp;
	// 写时针同步
	always @(posedge wclk or negedge wrstn)
	begin
		if(!wrstn) 
		begin
			sync_r2w <= 0 ;
			rptr_temp <= 0;
		end
		else 
		begin
			rptr_temp <= rptr;
			sync_r2w <= rptr_temp;
		end
	end
	// 读时针同步
	always @(posedge rclk or negedge rrstn)
	begin
		if(!rrstn) 
		begin
		sync_w2r <= 0 ;
		wptr_temp <= 0;
		end
		else 
		begin
			wptr_temp <= wptr;
			sync_w2r <= wptr_temp;
		end
	end

	// 判断写满读空状态
	assign wfull = wptr == {~sync_r2w[addr_width:addr_width-1],sync_r2w[addr_width-2:0]};
	assign rempty = rptr == sync_w2r;
	
	// 读数据(调用ram)
	dual_port_RAM #(
		.DEPTH(DEPTH),
		.WIDTH(WIDTH)
	)
	 dual_port_RAM
	(
		.wclk(wclk),
		.wenc(winc && !wfull),
		.waddr(wptr_bin[addr_width-1:0]),
		.wdata(wdata),
		.rclk(rclk),
		.renc(rinc && !rempty),
		.raddr(rptr_bin[addr_width-1:0]),
		.rdata(rdata)
	);

endmodule

补充

  1. 空和满时,读写指针末尾不一定全是0哦。换句话说,fifo的工作过程不一定是:先写满再读空再写满再读空这样的,也可能是边读边写,甚至可能同时读写。因此假设读指针为10011,写指针为00011(二进制),这也是fifo满。
  2. 亚稳态是在时钟跳变时,寄存器采样到一个逻辑0和逻辑1参考电压的中间值,这是亚稳态的概念。而亚稳态经过一段时间逐渐恢复成逻辑0或1,而具体会成为0还是1这件事是无法预测的。说回来,出现亚稳态的原因根源是被采样信号在时钟沿发生了跳变。一般情况下,同步时钟在保证setup和hold的情况下不会出现亚稳态(这也同步时钟不需要转格雷码的原因),而异步时钟相位关系无法设定,有可能同步前的信号正好在目标时钟沿跳变,有概率出现亚稳态,使用格雷码降低这种概率。一旦格雷码在跳变时也出现亚稳态,因为亚稳态最终也会恢复成逻辑0或1嘛,所以亚稳态后的格雷码相比于跳变前也可能会出现两种情况:正常跳变或者没有跳变。对于正常跳变,当然不会对结果产生任何影响;对于非正常跳变也就是格雷码没有跳变,会使被同步的指针更加保守,而可能加剧假空或者假满的程度,但不会造成功能错误,这也是选择用格雷码跨时钟的重要原因。

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

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

相关文章

SpringBoot毕业设计40个项目分享(源码+论文)(一)

文章目录 前言 课题1 : 基于SSM与VUE的旅游信息分享管理平台 <br /> 课题2&#xff1a;基于SSMVUE的中医商城管理系统 <br /> 课题3 : 基于SSM的汽车租赁系统<br /> 课题4 : 基于SSM与VUE的汉服销售论坛系统 <br /> 课题5 : 基于SSM的疫情校园师生登记…

java boot项目配置方式优先级

java boot项目认识一下三种格式的配置文件 中 我们说的 boot项目中支持三种配置文件格式 分别是 application.properties application.yml application.yaml 其中 我们也说推荐大家用application.yml格式的 那么 问题就来了 如果三个文件都存在于 resources目录下 系统会听谁的…

继电器相关知识

这个就是继电器&#xff0c;左边有三个接口&#xff0c;VCC(3.3v),GND,IO右面有COM,NO,NC。左侧的IO口如果接受到低电平&#xff0c;继电器内部线圈就会工作&#xff0c;然后供电&#xff0c;开关由NC端闭合到NO端&#xff0c;NO开始闭合&#xff0c;例如&#xff1a;可以将喇叭…

Real-Time C++ 嵌入式C++ 程序设计(三)

翻译自 Real-Time C Efficient Object-Oriented and Template Microcontroller Programming 4th Edition - Kormanyos, Christopher&#xff0c;这书涉及了从C11 到C20 的内容&#xff0c;主要介绍使用C 的模板、面向对象等特性设计嵌入式程序。书里的示例代码都是公开的&#…

ChatGPT报错:Sorry, you have been blocked解决方法

今天打开ChatGPT&#xff0c;发现再一次报错了&#xff01; 又一次出问题了。。。。。。。无语&#xff01; 原因分析 1、内容过滤&#xff1a;某些平台或网站可能使用内容过滤系统&#xff0c;该系统可能将AlI语言模型视为潜在的风险&#xff0c;从而对其进行封锁或限制。这…

传染病学模型 | Matlab实现基于SIS传染病模型模拟城市内人口的互相感染及城市人口流动所造成的传染

文章目录 效果一览基本描述模型介绍程序设计参考资料效果一览 基本描述 传染病学模型 | Matlab实现基于SIS传染病模型模拟城市内人口的互相感染及城市人口流动所造成的传染 模型介绍 SIS模型是一种基本的传染病学模型,用于描述一个人群中某种传染病的传播情况。SIS模型假设每个…

jsonschema networknt json-schema-validator 高级能力 自定义类校验

入参校验产品化 schema_个人渣记录仅为自己搜索用的博客-CSDN博客 自定义的string format可以使用. 详见 fpe的 addFormatValidator ajv 的 addFormat能力 借鉴自chatgpt , 谷歌了半天没答案. Q: "networknt JsonSchemaFactory Keyword " A: 如下 <dependenc…

windows下cplex20.1.0的下载、安装、IDE编程及相关问题解决

其他文章&#xff1a; 通过0-1背包问题看穷举法、贪心算法、启发式算法&#xff08;JAVA) 模拟退火(SA)算法实例介绍&#xff08;JAVA) 遗传算法&#xff08;GA&#xff09;实例介绍&#xff08;JAVA) CPLEX求解器入门案例 java集成Cplex&#xff1a;Cplex下载、IDEA环境搭…

css面试复习

目录 css常用网址: css三种书写样式 css属性 color(如字体颜色) text-decoration(如下划线) text-align(文字对齐) 字体属性 font-size font-family(字体名称) font-weight(字体粗细) font-style(斜体) line-height font缩写属性 css常见选择器 通用选择器 简单…

小黑子—MySQL数据库:第一章 -基础篇

MySQL数据库入门1.0 MySQL基础篇1. MySQL概述1.1 MySQL相关概念1.2 MySQL的安装及启动1.3 数据模型 2. SQL2.1 SQL的通用语法2.2 SQL语句的分类2.3 DDL语句2.3.1 DDL - 数据库操作2.3.2 DDL - 表操作 - 查询2.3.3 DDL - 表操作 - 创建2.3.4 DDL - 表操作 - 数据类型2.3.5 DDL -…

搭建stm32电机控制代码框架(三)——Stm32CubeMx配置ADC采样

电机控制另一个关键的模块就是ADC采样&#xff0c;这个模块配置的好坏决定了采样电流和电压的精准度&#xff0c;因此有必要对其进行深入学习。 简介&#xff1a; STM32 在片上集成的ADC 外设非常强大。STM32F103xC、STM32F103xD 和STM32F103xE增强型产品内嵌3个12位的ADC&am…

JDK8-JDK17中的新特性(var类型推断、模式匹配、Record、密封类)

文章目录 1. 新语法结构1.1 Java的REPL工具&#xff1a; jShell命令1.2 异常处理之try-catch资源关闭1.3 局部变量类型推断1.4 instanceof的模式匹配1.5 switch表达式1.6 文本块1.7 Record1.8 密封类 2. API的变化2.1 Optional类2.2 String存储结构和API变更2.3 JDK17&#xff…

vue-element-admin实践系列(二)初始化系统的页面元素

vue-element-admin实践系列 vue-element-admin实践系列(一)代码部署及运行demovue-element-admin实践系列(二)初始化系统的页面元素 文章目录 vue-element-admin实践系列1、修改默认参数1.1 修改启动端口1.2 修改网页title1.3 修改网站 ico1.4 效果如下 2、自定义左侧导航栏2.…

Fourier分析入门——第9章——Fourier系数的假设检测

目录 第9章 Fourier系数的假设检验 9.1 引言 9.2 回归分析(Regression analysis) 9.3 带限信号(Band-limited signals) 9.4 可信区间(Confidence intervals) 9.5 Fourier系数的多元(或多变量)统计分析(Mulitvariate statistical analysis of Fourier coefficients) 第9章 …

Three.js--》实现3d球形机器人模型展示

目录 项目搭建 初始化three.js基础代码 设置环境纹理 加载机器人模型 添加光阵 今天简单实现一个three.js的小Demo&#xff0c;加强自己对three知识的掌握与学习&#xff0c;只有在项目中才能灵活将所学知识运用起来&#xff0c;话不多说直接开始。 项目搭建 本案例还是…

(学习日记)AD学习 #4

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

C4D R26 渲染学习笔记 建模篇(0):建模常识

往期文章 C4D R26 渲染学习笔记&#xff08;1&#xff09;&#xff1a;C4D版本选择和初始UI框介绍 C4D R26 渲染学习笔记&#xff08;2&#xff09;&#xff1a;渲染流程介绍 C4D R26 渲染学习笔记&#xff08;3&#xff09;&#xff1a;物体基本操作快捷键 C4D如何建模 默认…

TiDB安装简介

文章目录 一、TiDB概述1、简介2、OLAP和OLTP3、与MySQL兼容性 二、架构三、安装1、本地版安装2、单机版集群安装2.1 概述2.2 安装2.3 访问集群 3、配置文件地址 四、使用方式1、基础SQL2、历史数据查询 一、TiDB概述 官网地址 https://docs.pingcap.com/zh/tidb/stable/quick…

(浙大陈越版)数据结构 第三章 树(上) 3.2 二叉树及存储结构

目录 3.2.1 二叉树的定义及性质 定义: 二叉树五种基本形态&#xff1a; 特殊二叉树 二叉树的几个重要性质 二叉树的抽象数据类型定义 操作集&#xff1a; 常用遍历&#xff1a; 3.2.2 二叉树的存储结构 顺序存储结构 数组实现 链表实现 3.2.1 二叉树的定义及性质 …

RK3588平台开发系列讲解(项目篇)YOLOv5部署测试

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、YOLOv5环境安装二、YOLOv5简单使用2.1、获取预训练权重文2.2、YOLOv5简单测试2.3、转换为rknn模型2.4、部署到 RK 板卡三、airockchip/yolov5简单测试3.1、转换成rknn模型并部署到板卡沉淀、分享、成长,让自己和他…