异步fifo的工作原理_netty的异步实现原理

异步fifo的工作原理_netty的异步实现原理FPGA(一):异步FIFO实现(包含源码和仿真文件)一、异步FIFO的重要参数及其作用1、FIFO:FirstInputFirstOutput,即先入先出队列,本质是RAM。FIFO有几个最重要的参数:2、wr_clk:写时钟,所有与写有关的操作都是基于写时钟;3、rd_clk:读时钟,所有与读有关的操作都是基于读时钟;4、FIFO_WIDTH:FIFO的位宽,即FIFO中每个地址对应的数据的位宽;5、FIFO_DEPTH:FIFO的深度,即FIFO中能存入多少个(位宽为FIFO_

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

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


本次设计主要介绍异步FIFO中读写指针和格雷码的原理及其实现,最后会有代码和仿真文件

一、异步FIFO的重要参数及其作用

FIFO有几个最重要的参数:
1、FIFO:First Input First Output,即先入先出队列,本质是RAM。
2、wr_clk:写时钟,所有与写有关的操作都是基于写时钟;
3、rd_clk:读时钟,所有与读有关的操作都是基于读时钟;
4、FIFO_WIDTH: FIFO的位宽,即FIFO中每个地址对应的数据的位宽;
5、FIFO_DEPTH: FIFO的深度,即FIFO中能存入多少个(位宽为FIFO_WIDTH的)数据;
6、full:FIFO发出的满信号,当FIFO满了之后,将full拉高;
7、empty:FIFO发出的空信号,当FIFO空了之后,将empty拉高;
8、wr_en:主机发送给FIFO的写使能,一般受制于FIFO发出的full信号,若full信号为高,一般主机会拉低写使能信号,防止新数据覆盖原来的数据;
9、rd_en:主机发送给FIFO的读使能,一般受制于FIFO发出的empty信号,若empty信号为高,一般主机会拉低读使能信号,防止从FIFO中读出不确定的数据。

异步FIFO主要用作跨时钟域的数据缓存。

二、设计要点

异步FIFO设计中,最重要的就是空满判断,格雷码是现在使用最多用于判断空满的一种码制,虽然都知道用格雷码,那为什么要用格雷码?

先说一说空满判断:
:读快于写时,读指针追上写指针时,写入的数据被读完,读指针和读指针相同,FIFO为空。
:当写快于读时,当写指针追上读指针,写入的数据比读出的数据多FIFO_DEPTH个,即写指针比读指针大一个FIFO_DEPTH时,此时FIFO为满。

还有就是为什么读写指针要比FIFO_DEPTH多一位,以及格雷码在这之中起到的重要作用,这个在说完格雷码之后会有解释。

格雷码:因1953年公开的弗兰克·格雷专利 “Pulse Code Communication”而得名。
格雷码是一种安全码,因为相邻的格雷码只有一位不同,和二进制不同,二进制一般相邻的都有多位不同。格雷码在传输中,因为相邻只有一位不同,所以其误码率比二进制低得多。
在同步时,出现亚稳态的概率也比二进制低。

二进制转换为格雷码
在这里插入图片描述
Verilog代码描述:gray = binary ^ (binary >> 1)

在这里插入图片描述
注:以上两幅关于格雷码的图片均不是原创,不知道出处,若有侵权,请联系删除

1、首先是读写指针为什么可以多一位,对读写地址有没有影响

答案是,没有影响
如上图,假如FIFO_DEPTH为8,那么指针就有16个值
普通的三位的地址从000写到111,然后再写入的话,地址又为000,一直写到111,重复上述操作。
因为我们取指针的低三位作为读写地址,如图,可以看出,即使是四位的指针,因为取的低三位,所以也是在000-111中往复循环,不会出现地址溢出的情况。

2、其次是为什么要用格雷码

由于以上原因,指针多一位对读写地址没有影响,而多一位又可以很好地利用格雷码的特性进行空满判断。
如上图,3和4, 5和6, 7和8之间有什么关系呢
可以看出,3的高两位与4的高两位相反低两位相同;5和6,7和8也有这种关系。
其实格雷码还是一种对称码,比如说3位的格雷码,可以先写出一位的格雷码,0 和 1;然后将这两位对称一下,写出 0 1 1 0,然后在前两个前面填上0,后两个前面添上1,得到两位格雷码 00 01 11 10,然后再对称得到 00 01 11 10 10 11 01 00,再在前四个前添上0,后四个 前面添上1,得到三位的格雷码,000 001 011 010 110 111 101 100.

而3和4之间还有什么关系呢,那就是他们的数值之间相差8,即一个FIFO_DEPTH,所以可以用这个来判断满。
空的判断很简单,格雷码一样就是空。

假设FIFO_DEPTH == 8
空的判断:empty =(wr_gray == rd_gray)?1:0;
满的判断:full = ({~wr_gray[3:2],wr_gray[1:0]} == rd_gray)?1:0;

3、空满信号的同步
因为空信号是对读操作有影响,所以,将空信号在rd_clk下同步(多采用两级寄存器)
因为满信号是对写操作有影响,所以,将满信号在wr_clk下同步(多采用两级寄存器)

三、源代码及仿真

1、源代码


`timescale 1ns / 1ns
module asynchronous_fifo #(	
	parameter 						FIFO_WIDTH = 8,
	parameter 						FIFO_DEPTH = 8
)(
	input							wr_clk,				//写时钟
	input							rd_clk,				//读时钟
	input							wr_en,				//写使能
	input							rd_en,				//读使能
	input							wr_rst_n,			//写复位
	input							rd_rst_n,			//读复位
	
	input		[FIFO_WIDTH-1:0]	wr_data,			//写入的数据		
	
	output	reg	[FIFO_WIDTH-1:0]	rd_data,			//输出的数据
	output	reg						wr_full,
    output	reg						rd_empty,
    output									wr_fifo_en,
	output									rd_fifo_en,
	output	reg		[$clog2(FIFO_DEPTH):0]	wr_gray_g2,		// 写指针
    output	reg		[$clog2(FIFO_DEPTH):0]	rd_gray_g2
    );
    
    
    // reg define
    reg		[$clog2(FIFO_DEPTH):0]		wr_pointer = 0;		// 写指针
	reg		[$clog2(FIFO_DEPTH):0]		rd_pointer = 0;		// 读指针
	reg		[$clog2(FIFO_DEPTH):0]		wr_gray_g1;  		// 写格雷码,用格雷码来判断空满
	reg		[$clog2(FIFO_DEPTH):0]		rd_gray_g1; 
	

    wire	[$clog2(FIFO_DEPTH):0]		wr_gray;  		// 写格雷码,用格雷码来判断空满
	wire	[$clog2(FIFO_DEPTH):0]		rd_gray;  		// 读格雷码,用格雷码来判断空满 
	wire	[$clog2(FIFO_DEPTH)-1:0]  	wr_addr;
	wire	[$clog2(FIFO_DEPTH)-1:0]  	rd_addr;
	wire								full_1;
	wire								empty_1;
    
    
     //ram define
   //(*ram_style = "distributed"*) reg [FIFO_WIDTH - 1 : 0] 	fifo_buffer	[FIFO_DEPTH - 1:0];			//寄存器组当FIFO
    reg [FIFO_WIDTH - 1 : 0] 	fifo_buffer	[FIFO_DEPTH - 1:0];			//寄存器组当FIFO
    
   	assign	wr_fifo_en = wr_en && !full_1;
    assign	rd_fifo_en = rd_en && !empty_1;
    
    assign	wr_gray = wr_pointer^(wr_pointer>>1);
    assign	rd_gray = rd_pointer^(rd_pointer>>1);
    
    assign	wr_addr = wr_pointer[$clog2(FIFO_DEPTH)-1:0]; 
    assign	rd_addr = rd_pointer[$clog2(FIFO_DEPTH)-1:0];
    
    assign	full_1 = ({~rd_gray_g2[$clog2(FIFO_DEPTH):$clog2(FIFO_DEPTH)-1], rd_gray_g2[$clog2(FIFO_DEPTH)-2:0]} == wr_gray)?1:0;
    assign	empty_1 = (wr_gray_g2 == rd_gray)?1:0;

   
    //将rd_gray在写时钟域与wr_gray比较,如果wr_gray与rd_gray高两位相反,低位相等,则写满
    always@(posedge wr_clk or negedge wr_rst_n) begin
    	if(!wr_rst_n) begin
    		rd_gray_g1 <= 0;
    		rd_gray_g2 <= 0;
    	end
    	else begin
    		rd_gray_g1 <= rd_gray;
    		rd_gray_g2 <= rd_gray_g1;
    	end
    end
    
     always@(posedge wr_clk or negedge wr_rst_n) begin
    	if(!wr_rst_n)
    		wr_full <= 1'b0;
    	else if(wr_full && wr_en)
    		wr_full <= 1'b1;
    	else 
    		wr_full <= full_1;
    end
    
    //将wr_gray在读时钟域与rd_gray比较,相等为FIFO空
     always@(posedge rd_clk or negedge rd_rst_n) begin
    	if(!rd_rst_n) begin
    		wr_gray_g1 <= 0;
    		wr_gray_g2 <= 0;
    	end
    	else begin
    		wr_gray_g1 <= wr_gray;
    		wr_gray_g2 <= wr_gray_g1;
    	end
    end
    
    always@(posedge rd_clk or negedge rd_rst_n) begin
    	if(!rd_rst_n)
    		rd_empty <= 1'b0;
    	else if(rd_empty && rd_en)
    		rd_empty <= 1'b1;
    	else 
    		rd_empty <= empty_1;
    end
    
    
    // 写数据指针计数
   	always@(posedge wr_clk or negedge wr_rst_n) begin
    	if(!wr_rst_n) begin
    		fifo_buffer[wr_addr] <= 'bz;
    		wr_pointer <= 0;
    	end
    	else if(wr_fifo_en) begin
    		fifo_buffer[wr_addr] <= wr_data;
    		wr_pointer <= wr_pointer + 1'b1;
    	end
    	else begin
    		fifo_buffer[wr_addr] <= fifo_buffer[wr_addr];
    		wr_pointer <= wr_pointer;
    	end
    end
    
    // 读数据指针计数
   	always@(posedge rd_clk or negedge rd_rst_n) begin
    	if(!rd_rst_n) begin
    		rd_data <= 'bz;
    		rd_pointer <= 0;
    	end
    	else if(rd_fifo_en) begin
    		rd_data <= fifo_buffer[rd_addr];
    		rd_pointer <= rd_pointer + 1'b1;
    	end
    	else begin
    		rd_data <= fifo_buffer[rd_addr];
    		rd_pointer <= rd_pointer;
    	end
    end

    
endmodule

2、仿真文件

改变clk_period_wr和clk_period_rd就可以实现读写快慢切换


`timescale 1ns / 1ns
`define	 clk_period_wr 50
`define	 clk_period_rd 20


module asynchronous_fifo_tb();


parameter		FIFO_WIDTH = 8;
parameter		FIFO_DEPTH = 16;


reg							wr_clk;				
reg							rd_clk;				
reg							wr_en;			
reg							rd_en;				
reg							wr_rst_n;
reg							rd_rst_n;
reg		[FIFO_WIDTH-1:0]	wr_data;

wire	[FIFO_WIDTH-1:0]	rd_data;			
wire						wr_full;    
wire						rd_empty;
wire						wr_fifo_en;
wire						rd_fifo_en;
wire	[$clog2(FIFO_DEPTH):0]		wr_gray_g2;
wire	[$clog2(FIFO_DEPTH):0]		rd_gray_g2;

assign wr_fifo_en = wr_en && !wr_full;
assign rd_fifo_en = rd_en && !rd_empty;

initial begin
	wr_clk = 0;
	forever begin
		#(`clk_period_wr/2) wr_clk = ~wr_clk;
	end
end
initial begin
	rd_clk = 0;
	forever begin
		#(`clk_period_rd/2) rd_clk = ~rd_clk;
	end
end

initial begin
	wr_rst_n = 1;
	rd_rst_n = 1;
	wr_en = 0;
	rd_en = 0;
	#5;
	wr_rst_n = 0;
	rd_rst_n = 0;
	#5;
	wr_rst_n = 1;
	rd_rst_n = 1;
end


initial begin
	//第一段仿真
	@(negedge wr_clk) wr_en = 1;wr_data = $random;
	repeat(7) begin   		
		@(negedge wr_clk)
		wr_data = $random;
	end
	@(negedge wr_clk) wr_en = 0;
	@(negedge rd_clk) rd_en = 1;
	
	repeat(8) begin
		@(negedge rd_clk); 
	end 
	@(negedge rd_clk) rd_en = 0;
	# 150
	
	//第二段仿真
	@(negedge wr_clk)
	wr_en = 1;
	wr_data = $random;
	repeat(18) begin
	@(negedge wr_clk)
		wr_data = $random;
	end
	@(negedge wr_clk) wr_en = 0;	
	@(negedge rd_clk) rd_en = 1;
	repeat(18) begin
		@(negedge rd_clk);
	end
	rd_en = 0;
	
	// 第三段仿真
	repeat(9) begin
	@(negedge wr_clk)
		wr_data = $random;wr_en = 1;
	end
	rd_en = 1;
	repeat(19) begin
	@(negedge wr_clk)
		wr_data = $random;
	end
	@(negedge wr_clk) wr_en = 0;	
	@(negedge rd_clk) rd_en = 0;


	#20 $finish;	


end
	
asynchronous_fifo #(
		.FIFO_WIDTH	(FIFO_WIDTH),
		.FIFO_DEPTH	(FIFO_DEPTH)
		)
	asynchronous_fifo (	
		.wr_clk			(wr_clk),
		.rd_clk			(rd_clk),
		.wr_rst_n		(wr_rst_n),
		.rd_rst_n		(rd_rst_n),
		.wr_en			(wr_fifo_en),
		.rd_en			(rd_fifo_en),
		.wr_data		(wr_data),
		
		.wr_full		(wr_full),
		.rd_empty		(rd_empty),
		.rd_data		(rd_data)
	);

endmodule

3、仿真截图

读比写快
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

写比读快
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

这个代码也还有些问题,希望大家多多包容并且指出错误或者可以改善的地方,一起进步。

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

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/170180.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 基于C#的WinForm窗体美化(无须美工功底)「建议收藏」

    基于C#的WinForm窗体美化(无须美工功底)「建议收藏」基于C#的WinForm窗体美化(无须美工功底)在近期的实训中,学习到了许多精髓的小知识。接下来我会发布几篇博客,分享我的开发经验。在设计面向对象的UI层界面时,常常因为WinForm自身的窗体界面不够美观而去反复修改各种控件的位置。当然在不同的Windos中,WinForm所具有的主题也是不同的,但对于颜控的我们,这些也是无法满足的。所以今天我分享一个关于简单的对窗体进行美化的一个小技巧。

    2022年5月28日
    36
  • sae wpa3加密方式_WPA3:四大安全新特性技术分析

    sae wpa3加密方式_WPA3:四大安全新特性技术分析周一晚些时候,包括苹果、思科、英特尔、高通和微软等科技巨头在内的Wi-Fi联盟正式推出了新的Wi-Fi安全标准WPA3。这个标准将解决所有已知的、会影响重要标准的安全问题,同时还针对KRACK和DEAUTH等无线攻击给出缓解措施。WPA3为支持Wi-Fi的设备带来重要改进,旨在增强配置、加强身份验证和加密等问题。重要改进主要包括:防范暴力攻击、WAP3正向保密、加强公共和…

    2022年5月24日
    47
  • ubuntu 20.04 lts安装_vmware如何安装

    ubuntu 20.04 lts安装_vmware如何安装ubuntu22.04lts安装步骤

    2022年9月8日
    0
  • c语言getchar()的用法_c=getchar()

    c语言getchar()的用法_c=getchar()文章目录getchar()函数定义函数返回值注意区分getchar和scanfgetchar的使用实例getchar()函数定义getchar()-字符输入函数,没有参数,从输入缓冲区里面读取一个字符-「一次只能读取一个字符」EOF(-1)-endoffile文件结束标志-键盘上用ctrl+z实现先查一下文档函数返回值该函数以无符号char强制转换为int的形式返回读取的字符,如果到达文件末尾或发生读取错误,则返回EOF(-1

    2022年10月18日
    0
  • Ubuntu中搭建ICE服务器(Coturn)

    Ubuntu中搭建ICE服务器(Coturn)1.WebRTC的P2P穿透WebRTC的P2P穿透部分是由libjingle实现的.步骤顺序大概是这样的:尝试直连.通过STUN服务器进行穿透无法穿透则通过TURN服务器中转STUN服务器比较简单.网上也有很多公开的STUN服务器可以用于测试,例如:stun.ideasip.com在WebRTC的P2P应用中,使用公开的STUN服务器时,有时响应比较慢,这就需要自己搭一个…

    2022年6月14日
    31
  • projecteuler—-&gt;problem=14—-Longest Collatz sequence

    projecteuler—-&gt;problem=14—-Longest Collatz sequence

    2022年1月2日
    44

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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