异步fifo深度计算_异步fifo verilog

异步fifo深度计算_异步fifo verilog文章目录一、异步FIFO介绍1.1.空满判断1.2.跨时钟域问题1.3.格雷码转换1.4.格雷码计数器二、代码code一、异步FIFO介绍  FIFO有同步和异步两种,同步即读写时钟相同,同步FIFO用的少,可以作为数据缓存;异步即读写时钟不相同,异步FIFO可以解决跨时钟域的问题,在应用时需根据实际情况考虑好fifo深度即可。  与同步FIFO相同,异步FIFO也主要由五大模块组成,不同…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

一、异步FIFO介绍

  FIFO有同步和异步两种,同步即读写时钟相同,同步FIFO用的少,可以作为数据缓存;异步即读写时钟不相同,异步FIFO可以 解决跨时钟域的问题,在应用时需根据实际情况考虑好fifo深度即可。
  与同步FIFO相同,异步FIFO也主要由五大模块组成,不同的是,异步FIFO的读写逻辑控制还包括了格雷码转换和时钟同步部分:
    (1)、 FIFO写逻辑控制——产生FIFO写地址、写有效信号,同时产生FIFO写满、写错等状态信号;
    (2)、 FIFO读逻辑控制——产生FIFO读地址、读有效信号,同时产生FIFO读空、读错等状态信号;
    (3)、 时钟同步逻辑——通过两级DFF分别将写时钟域的写指针同步到读时钟域,将读时钟域的读指针同步到写时钟域;
    (4)、 格雷码计数器——格雷码计数器中二进制计数器的低(n-1)位可以直接作为FIFO存储单元的地址指针;
    (3)、 FIFO存储体(如Memory,reg等)。
    
其逻辑结构如下所示:
在这里插入图片描述

1.1.空满判断

对于异步FIFO采用地址扩展一位的方式对FIFO进行读写计数,进而判断空满。  
   读空信号:复位的时候,读指针和写指针相等,读空信号有效(这里所说的指针其实就是读地址、写地址)
       当读指针赶上写指针的时候,写指针等于读指针意味着最后一个数据被读完,此时读空信号有效。
   写满信号:当写指针比读指针多一圈时,写指针等于读指针意味着写满了,此时写满信号有效

当最高位相同,其余位相同认为是读空
当最高位不同,其余位相同认为是写满

该方法试用的是二进制数之间的空满比较判断,详情参见同步FIFO设计方法2。

因为异步FIFO采用格雷码计数,而格雷码是镜像对称的,若只根据最高位是否相同来区分是读空还是写满是有问题的。如下如图所示:
在这里插入图片描述
因此用格雷码判断是否为读空或写满时应使用理论 2,看最高位和次高位是否相等,具体如下:
当最高位和次高位相同,其余位相同认为是读空
当最高位和次高位不同,其余位相同认为是写满

  通俗的讲:当FIFO空时即读指针赶上写指针,此时两个格雷码完全相同(包括扩展位)。当FIFO写满时候需要考虑如下3个条件

  • 写指针的格雷码与同步到写时钟域的读指针格雷码的最高位不同
  • 写指针的格雷码与同步到写时钟域的读指针格雷码的次高位不相等
  • 写指针的格雷码与同步到写时钟域的读指针格雷码的其余位都相等

1.2.跨时钟域问题

  由于是异步FIFO的设计,读写时钟不一样,在产生读空信号和写满信号时,会涉及到跨时钟域的问题,如何解决?

  跨时钟域的问题:上面我们已经提到要通过比较读写指针来判断产生读空和写满信号,但是读指针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO的读写时钟域不同,是异步的,要是将读时钟域的读指针与写时钟域的写指针不做任何处理直接比较肯定是错误的,因此我们需要进行同步处理以后仔进行比较。

解决方法:两级寄存器同步 + 格雷码
  (1)将写时钟域的写指针同步到读时钟域,将同步后的写指针与读时钟域的读指针进行比较产生读空信号
  (2)将读时钟域的读指针同步到写时钟域,将同步后的读指针与写时钟域的写指针进行比较产生写满信号

  如果直接用二进制编码的读写指针去完成上述的两种同步是不行的,使用格雷码更合适,为什么呢?

  因为二进制编码的指针在跳变的时候有可能是多位数据一起变化,如二进制的7–>8 即 0111 –> 1000 ,在跳变的过程中 4 位全部发生了改变,这样很容易产生毛刺,造成读写过程中数据出错。比如写指针在从0111到1000跳变时4位同时改变,这样读时钟在进行写指针同步后得到的写指针可能是0000-1111的某个值,一共有2^4个可能的情况,而这些都是不可控制的,你并不能确定会出现哪个值,那出错的概率非常大,而格雷码的编码特点是相邻位每次只有 1 位发生变化, 这样在进行指针同步的时候,就可以避免多个bit位同时跳变的情况。

  设计的时候读写指针用了至少两级寄存器同步,同步会消耗至少两个时钟周期,势必会使得判断空或满有所延迟,这会不会导致设计出错呢?

  异步FIFO通过比较读写指针进行满空判断,但是读写指针属于不同的时钟域,所以在比较之前需要先将读写指针进行同步处理,将写指针同步到读时钟域再和读指针比较进行FIFO空状态判断,因为在同步写指针时需要时间,而在这个同步的时间内有可能还会写入新的数据,因此同步后的写指针一定是小于或者等于当前实际的写指针,所以此时判断FIFO为空不一定是真空,这样更加保守,一共不会出现空读的情况,虽然会影响FIFO的性能,但是并不会出错,同理将读指针同步到写时钟域再和写指针比较进行FIFO满状态判断,同步后的读指针一定是小于或者等于当前的读指针,所以此时判断FIFO为满不一定是真满,这样更保守,这样可以保证FIFO的特性:FIFO空之后不能继续读取,FIFO满之后不能继续写入。总结来说异步逻辑转到同步逻辑不可避免需要额外的时钟开销,这会导致满空趋于保守,但是保守并不等于错误,这么写会稍微有性能损失,但是不会出错。

1.3.格雷码转换

  二进制码转换成二进制格雷码,其法则是保留二进制码的最高位作为格雷码的最高位,而次高位格雷码为二进制码的高位与次高位相异或,而格雷码其余各位与次高位的求法相类似。

二进制B[n:0]转化为格雷码G[n:0]
G[n] = B[n]//保留最高位作为格雷码的最高位
G[n-1:0] = B[n-1:0]^B[n:1]//次高位格雷码为二进制码的高位与次高位相异或其余类似
在这里插入图片描述
我再换种更简单的描述
二进制数            1 0 1 1 0
二进制数右移1位,空位补0    0 1 0 1 1
异或运算            1 1 1 0 1
这样就可以实现二进制到格雷码的转换了,总结就是移位并且异或,verilog代码实现就一句:
assign wgraynext = ( wbinnext >> 1 ) ^ wbinnext;

格雷码G[n:0]转化为二进制B[n:0]
B[n] = G[n]//保留最高位作为二进制码的最高位
B[n-1:0] = G[n-1:0]^B[n:1]//次高位格雷码为二进制码的高位与次高位相异或其余类似
在这里插入图片描述

1.4.格雷码计数器

图中所示的格雷码计数器中二进制计数器的低(n-1)位可以直接作为FIFO存储单元的地址指针,将二进制数转化为格雷码传输给另外一个时钟域。
在这里插入图片描述

二、代码code

/*异步fifo 参考文献 Simulation and Synthesis Techniques for Asynchronous FIFO Design*/
//源码:https://github.com/DeamonYang/FPGA_SYNC_ASYNC_FIFO

module async_fifo(
		rst_n			,
		fifo_wr_clk	,
		fifo_wr_en	,
		r_fifo_full	,
		fifo_wr_data,
		
		fifo_rd_clk	,
		fifo_rd_en	,
		fifo_rd_data,
		r_fifo_empty	
		
// fifo_wr_err,
// fifo_rd_err
		
	);

		input          rst_n			;
		input          fifo_wr_en	;
		input	[15:0] fifo_wr_data;
		input          fifo_rd_en	;
		input          fifo_rd_clk;
		input          fifo_wr_clk;
		output reg     r_fifo_full	;
		output  [15:0] fifo_rd_data;
		output reg     r_fifo_empty	;
		
		
// output reg fifo_wr_err;
// output reg fifo_rd_err;

//中间信号internal singles 
		reg	[9:0]  rdaddress;  //RAM地址为9位地址 扩展一位用于同步
		reg	[9:0]  wraddress;  //RAM写地址
	
		wire	[9:0]	gray_rdaddress;      //格雷码读写地址
		wire	[9:0]	gray_wraddress;
		
		/*同步寄存器*/
		reg	[9:0] sync_w2r_r1,sync_w2r_r2;
		reg	[9:0] sync_r2w_r1,sync_r2w_r2;
		
		wire fifo_empty;
		wire fifo_full;
		
		/*二进制转化为格雷码计数器*/
		assign gray_rdaddress = (rdaddress >>1) ^ rdaddress;//(({1'b0,rdaddress[9:1]}) ^ rdaddress);
		
		/*二进制转化为格雷码计数器*/
		assign gray_wraddress = (({ 
   1'b0,wraddress[9:1]}) ^ wraddress);
		
		assign fifo_empty = (gray_rdaddress == sync_w2r_r2);  //格雷码所有bit位均相同
		
		assign fifo_full = (gray_wraddress == { 
   ~sync_r2w_r2[9:8],sync_r2w_r2[7:0]});  //高两位不同,其余位相同
// 
// assign fifo_wr_err = (w_fifo_full && fifo_wr_en);
// assign fifo_rd_err = (fifo_empty && fifo_rd_en);
	
		ram  ram(
			.data		(fifo_wr_data		),
			.rdaddress(rdaddress[8:0]),
			.rdclock	(fifo_rd_clk	),
			
			.wraddress(wraddress[8:0]),
			.wrclock	(fifo_wr_clk	),
			.wren		(fifo_wr_en	),
			.q			(fifo_rd_data)
			);	
		
		/*在读时钟域同步FIFO空 sync_w2r_r2 为同步的写指针地址 延迟两拍非实际写指针值 但是确保不会发生未写入数据就读取*/	
		always@(posedge fifo_rd_clk or negedge rst_n)
		if(!rst_n)
			r_fifo_empty <= 1'b0;
		else 
			r_fifo_empty <= fifo_empty;


			/*在写时钟域判断FIFO满 sync_r2w_r2 实际延迟两个节拍 可能存在非满判断为满 但不会导致覆盖*/
		always@(posedge fifo_wr_clk or negedge rst_n)
		if(!rst_n)
			r_fifo_full <= 1'b0;
		else 									
			r_fifo_full <= fifo_full;//格雷码判断追及问题 
			
			
		/*读数据地址生成*/
		always@(posedge fifo_rd_clk or negedge rst_n)
		if(!rst_n)
			rdaddress <= 10'b0;
		else if(fifo_rd_en && ~fifo_empty)begin
			rdaddress <= rdaddress + 1'b1;
		end
		
		/*写数据地址生成*/
		always@(posedge fifo_wr_clk or negedge rst_n)
		if(!rst_n)
			wraddress <= 10'b0;
		else if(fifo_wr_en && ~r_fifo_full)begin
			wraddress <= wraddress + 1'b1;
		end
		
		/*同步读地址到写时钟域*/
		always@(posedge fifo_wr_clk or negedge rst_n)
		if(!rst_n)begin
			sync_r2w_r1 <= 10'd0;
			sync_r2w_r2 <= 10'd0;
		end else begin
			sync_r2w_r1 <= gray_rdaddress;
			sync_r2w_r2 <= sync_r2w_r1;		
		end

		/*同步写地址到读时钟域, 同步以后 存在延迟两个节拍*/
		always@(posedge fifo_rd_clk or negedge rst_n)
		if(!rst_n)begin
			sync_w2r_r1 <= 10'd0;
			sync_w2r_r2 <= 10'd0;
		end else begin
			sync_w2r_r1 <= gray_wraddress ;
			sync_w2r_r2 <= sync_w2r_r1;		
		end		
endmodule

参考:https://blog.csdn.net/u014070258/article/details/90052281
https://www.cnblogs.com/aslmer/p/6114216.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/170198.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 如何修改手机IP地址

    如何修改手机IP地址说起手机换IP大家可能没有对电脑换IP那么熟悉,但是现在智能手机能做到事情越来越多,手机换IP也成为许多工作需要,一部分人还不知道怎么操作,就跟着小编一起来看看手机换IP的几种方法。一、手动换IP这个适合偶尔换IP,时间富裕的朋友,我们使用手机进行开关飞行模式,这样就可以进行换IP。也可以找到手机设置点进去先进入WiFi热点的列表,点击所连接的WiFi热点的名字。选择“修改网度络”,然后勾选“显示高级选项版”,就可以进行IP设置了。还有一种比较简单,就是用软件辅助换IP,这里以芝麻代理为例

  • 首先看K一个难看的数字

    首先看K一个难看的数字

  • 在CMD命令行中切换到管理员权限模式「建议收藏」

    在CMD命令行中切换到管理员权限模式「建议收藏」1、打开CMD2、输入:runas/noprofile/user:Administratorcmd3、输入Administrator账户的密码,必须设置密码PS:可以不是Administrator账户,只要是具有管理员权限的账号都可,例如ASUS等。runas/noprofile/user:ASUScmd然后输入ASUS账号的密码run…

    2022年10月16日
  • 无显示屏 配置树莓派教程[通俗易懂]

    无显示屏 配置树莓派教程[通俗易懂]<无显示屏>配置树莓派教程0.相关链接#SDCardFormatter官网下载链接https://www.sdcard.org/downloads/formatter/eula_windows/index.html#Win32DiskImager下载链接https://sourceforge.net/projects/win32diskimager/#树莓派…

  • 什么是publickeytoken及publickeytoken的作用

    什么是publickeytoken及publickeytoken的作用什么是publickeytoken及publickeytoken的作用dll的publickeytoken的作用。

  • uboot启动过程详解

    uboot启动过程详解

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号