前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >异步fifo的工作原理(netty异步方法)

异步fifo的工作原理(netty异步方法)

作者头像
全栈程序员站长
发布2022-07-28 21:18:17
9230
发布2022-07-28 21:18:17
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

目录

本次设计主要介绍异步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、源代码

代码语言:javascript
复制
`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就可以实现读写快慢切换

代码语言:javascript
复制
`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.cn/129007.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年4月1,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • 一、异步FIFO的重要参数及其作用
  • 二、设计要点
  • 三、源代码及仿真
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档