异步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、仿真截图

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

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

在这里插入图片描述

在这里插入图片描述

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

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

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

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

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

(0)
blank

相关推荐

  • es6模板字符串_模板字符串如何实现

    es6模板字符串_模板字符串如何实现<spanclass=”fr”>${(function(){if(list[item].oStatus==0){return`待交易`;}elseif(list[item].oStatus==1){return’已交易’;}else{return’已过期’;}

  • SVM——支持向量回归(SVR)[通俗易懂]

    SVM——支持向量回归(SVR)[通俗易懂]1、支持向量回归的原始问题先来看看SVM线性支持向量机(软间隔)的原始问题:其中ξi是松弛变量,但它实际上是hinge(合页)损失函数,所以ξi也作为对应的点(xi,yi)的损失,如下图所示:当点(xi,yi)位于间隔面上或者间隔面之外(这两种都是正确分类的情况)则ξi=0,若点(xi,yi)位于分割面上或者正确分类且位于间隔面之内或者位于分错的那一侧,这三种情况都是有损失的,损失…

  • 用opencv的dnn模块做yolov5目标检测[通俗易懂]

    用opencv的dnn模块做yolov5目标检测[通俗易懂]最近在微信公众号里看到多篇讲解yolov5在openvino部署做目标检测文章,但是没看到过用opencv的dnn模块做yolov5目标检测的。于是,我就想着编写一套用opencv的dnn模块做yolov5目标检测的程序。在编写这套程序时,遇到的bug和解决办法,在这篇文章里讲述一下。在yolov5之前的yolov3和yolov4的官方代码都是基于darknet框架的实现的,因此opencv的dnn模块做目标检测时,读取的是.cfg和.weight文件,那时候编写程序很顺畅,没有遇到bug。但是yolo

    2022年10月13日
  • 如何利用计算机模拟分子生物学,分子生物学软件教学的经验浅谈.doc

    如何利用计算机模拟分子生物学,分子生物学软件教学的经验浅谈.doc分子生物学软件教学的经验浅谈分子生物学软件教学的经验浅谈摘要:分子生物学是生命科学和生物技术的基础学科,其教学尤其是实验教学得到了各个高等院校的普遍重视,但是对于应用性强的分子生物学软件的教学却长期以来受到忽视。笔者首次在厦门大学的夏季短学期中开设《分子生物学常用软件的应用》课程已有五余年,本文总结了分子生物学软件教学的经验,提出教学改进的建议,阐述了软件应用与实验设计的必要联系,为生物医学类学科…

  • 图片的放大与缩小_photoshop怎么放大图片

    图片的放大与缩小_photoshop怎么放大图片packagecom.school.util;importjava.awt.Graphics;importjava.awt.Image;importjava.awt.image.Buffere

  • whl文件下载「建议收藏」

    whl文件下载「建议收藏」到哪找.whl文件?http://www.lfd.uci.edu/~gohlke/pythonlibs/转载于:https://www.cnblogs.com/lhuser/p/8084734.html

发表回复

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

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