异步FIFO的原理以及可综合的Verilog代码

一、FIFO的定义

_ First In First Out 【FIFO】先进先出的缓存器,它与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,并顺序读出先写入的数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器可以根据地址对读取或写入某个指定的地址位上的数据信息。

二、FIFO的应用场景

— 数据缓存
比如当数据发送侧写入数据后,接收侧不能实时的对数据进行读取就可以对数据暂存到FIFO内,当接收侧准备完毕后,就可以顺序读取FIFO内相应的数据。

— 位宽转换
比如单片机位8位数据输出,而DSP可能是16位数据输入,在单片机与DSP连接时就可以使用FIFO来达到数据匹配的目的。

— 速率匹配\异步处理
当数据的读写时钟相互异步,不一致时,而这种情况在集成芯片内对这种跨时钟域处理数据的情况非常多,而异步FIFO是一个可以很好解决这个问题的方案。

三、FIFO的分类

根据FIFO工作的时钟域,可以将FIFO分为同步FIFO和异步FIFO。

  • 同步FIFO是指读时钟和写时钟为同一个时钟。在时钟沿来临时同时发生读写操作。
  • 异步FIFO是指读写时钟不一致,读写时钟是互相独立的。

FIFO的参数

  • FIFO的位宽:表示一次性读取数据位的宽度(N),与通常所表示的32位、64位单片机含义是一样的。
  • 读时针:读数据时所采用的时钟,在时钟上升沿进行从FIFO内读取读指针所对应地址的数据。
  • 写时钟:写数据时所采用的时钟,在时钟上升沿时将数据写入写指针所对应地址的FIFO内存内。
  • FIFO的深度(Deep):表示的FIFO可以存取多少个N位数据。如果FIFO的位宽为N=8bit,FIFO的深度为Deep=16,就表示此FIFO可以存储16个8位的数据。也就是说此FIFO的内存大小为8*16bit。
  • 读指针:指向下一个要读的数据地址,读完后地址指针自动加一。
  • 写指针:指向下一个要写的数据地址,写完后地址指针自动加一。
  • 空标致(empty):如果将读、写指针类似成两个人在圆形操场内跑圈,如果读写指针跑的圈数一样(写指针没有领先读指针一圈的情况下),当读指针追上了写指针,表示FIFO内没有数据了,已经被读指针全部读出并送出数据, FIFO就会发出空标致信号,禁止再进行读数据操作。(可以想像成写指针一直领先读指针,因为只有先在FIFO内写入数据,才能进行相应的读数据操作
  • 满标致:表示FIFO已经将最后一个8位内存写入数据,也就是写指针已经超过读指针一圈,并追上了读指针,此时FIFO会发出满标致信号,禁止再进行写操作。

FIFO的设计难点

  • 空满信号的判断:对于一个FIFO的设计其难点在于对其空、满状态的准确判断。当进行读操作时,根据FIFO发出的空信号,不至于产生在FIFO已空状态下,进行读操作,造成读空情况的发生; 当进行写操作时,根据FIFO发出的满信号,不至于产生在FIFO已满的状态下,进行写入数据的操作,造成数据溢出的情况发生。所以对FIFO进行正确的空、满状态判断是进行FIFO设计的重点及难点。
BinaryGray
0_0000_000
0_0010_001
0_0100_011
0_0110_010
0_1000_110
0_1010_111
0_1100_101
0_1110_100
1_0001_100
1_0011_101
1_0101_111
1_0111_110
1_1001_010
1_1011_011
1_1101_001
1_1111_000
  • 在同步FIFO中,读写时钟为同一时钟,在实际硬件电路设计中,用的到比较少,而且空满判断比较简单,这里不做介绍。而异步FIFO芯片以及FPGA硬件电路设计中是一种常用的数据缓存方式。异步在产生满信号时,需要将读时钟域内的读指针同步到写时钟域内。如果采用Binary(二进制)做为地址指针,那么相邻位码元之间变化会有多个Bit位发生变化(例如:1到2,二进制0_001变为0_010有两bit位发生变化),如上表所示:(为二进制与格雷码变化对应表),每一时钟地址指针发生多bit位变化,会大大增加亚稳态发生的概率,且增大功耗。而格雷码是相邻两个码元之间只有一Bit位发生变化,而在芯片IC设计及FPGA设计异步FIFO中通常采用格雷码做为指针地址,这样大大降低亚稳态情况的发生。
    在实际设计中通常采用N+1位格雷码做为地址指针,在读写时钟进行指针同步,以产生空满信号。用N位二进制地址进行对FIFO内数据进行读写。如果感兴趣的可以深入看一下Clifford E的文章,看完之后会对异步FIFO有深入的理解。相应文章链接如下。
    超经典的异步 FIFO文章讲解,如果你读懂了异步你肯定懂了
    对于Verilog的讲解一定要看程序 ,不然不会明白 了,大家有什么想知道的,不明白的可以评论说明,大家一直学习。
  • 采用Gray2的形式的代码如下:
`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date:    22:30:20 08/04/2020 
// Design Name: 
// Module Name:    async_fifo 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//
module async_fifo #(parameter DATASIZE = 8,
						  parameter ADDRSIZE = 4
						  //parameter DEEP  = 32
						 )
(
input  wire					wclk,
input  wire					rclk,
input  wire					wrst_n,
input  wire					rrst_n,
input  wire					din_vld,
input  wire	[DATASIZE-1:0]	din_data,
output reg					rempty,
output reg					ral_empty,

output wire					dout_vld,
output reg					wfull,
output reg					wal_full,
output wire	[DATASIZE-1:0] dout_data
					
);
wire	[ADDRSIZE-1:0]		waddr			;
wire	[ADDRSIZE-1:0]		raddr			;
reg	[ADDRSIZE  :0]    	waddr_bin	;
reg	[ADDRSIZE  :0]		raddr_bin	;
wire                    wfull_val	;
wire 							rempty_val	;
reg   [ADDRSIZE  :0]     rptr			;
reg   [ADDRSIZE  :0]		wq1_rptr		;
reg   [ADDRSIZE  :0]		wq2_rptr		;

reg 	[ADDRSIZE  :0]     wptr			;
reg 	[ADDRSIZE  :0]		rq1_wptr		;
reg	[ADDRSIZE  :0]		rq2_wptr		;

wire	[ADDRSIZE  :0]     rgray_next	;
wire	[ADDRSIZE  :0]     wgray_next	;

wire	[ADDRSIZE  :0]		wbin_next		;
wire	[ADDRSIZE  :0]		rbin_next		;


localparam DEPTH = 1 << ADDRSIZE;
reg   [DATASIZE-1 :0] ram [0: DEPTH - 1];

//write and read data from ram
assign dout_data = ram[raddr];

always @(posedge wclk)
	if(din_vld && !wfull) ram[waddr] <= din_data;


//radr_gary syco to write clk
always @(posedge wclk or negedge wrst_n)
begin
if(!wrst_n)
begin
	wq1_rptr <= 0;
	wq2_rptr <= 0;
end
else 
begin
	wq1_rptr <= rptr;
	wq2_rptr <= wq1_rptr;
end
end

//wadr_gray syco to read clk
always @(posedge rclk or negedge rrst_n)
begin
if(!rrst_n)
begin
	rq1_wptr <= 0;
	rq2_wptr <= 0;
end
else 
begin
	rq1_wptr <= wptr;
	rq2_wptr <= rq1_wptr;
end
end

//generating empty 
assign rempty_val = (rgray_next == rq2_wptr);
always @(posedge rclk or negedge rrst_n)
	if(!rrst_n) rempty <= 1'b1;
	else			rempty <= rempty_val;
	
//generating  full 
//assign wfull_val = ((wgnext[DATASIZE : DATASIZE-1] != wq2_rptr[DATASIZE : DATASIZE-1]) &&
//						 (wgnext[DATASIZE-2 : 0]     == wq2_rptr[DATASIZE-2 : 0]));
assign wfull_val = (wgray_next == {~wq2_rptr[ADDRSIZE : ADDRSIZE-1],
						  wq2_rptr[ADDRSIZE-2 : 0]});
always @(posedge wclk or negedge wrst_n)
	if(!wrst_n) wfull <= 1'b0;
	else			wfull <= wfull_val;


//read point
always @(posedge rclk or negedge rrst_n)
begin
	if(!rrst_n) begin
	raddr_bin <= 0;
	rptr      <= 0;
	end
	else begin
	raddr_bin <= rbin_next;
	rptr      <= rgray_next;
	end
end

assign raddr = raddr_bin[ADDRSIZE-1 : 0];

assign rbin_next  = raddr_bin + (din_vld && ~rempty);
assign rgray_next = (rbin_next>>1) ^ rbin_next; 

//write point
always @(posedge wclk or negedge wrst_n)
begin
	if(!wrst_n)	begin
	waddr_bin <= 0;
	wptr      <= 0;
	end
	else begin
	waddr_bin <= wbin_next;
	wptr		 <= wgray_next;
	end
end

assign waddr     = waddr_bin[ADDRSIZE-1 : 0]			;

	
assign wbin_next  = waddr_bin + (din_vld && ~wfull) ;
//bin to gray
assign wgray_next = (wbin_next >>1 ) ^ wbin_next      ;





endmodule

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
异步FIFO寄存器是一种通过异步时钟域实现的先进先出(FIFO)数据结构,它包括RAM存储区域构建、读地址同步到写时钟域进行写满判断、写地址同步到读时钟域进行读空判断和跨时钟域地址指针同步等四个部分。 在Verilog中,我们可以使用代码来实现异步FIFO寄存器。具体的实现包括FIFO原理简介、FIFO空、满信号的检测、二进制至格雷码的转换以及异步FIFOVerilog代码实现等。 异步FIFO的基础是一个双时钟双端口RAM,读端口和写端口的时钟不一致。而其设计重点是跨时钟域的地址同步问题,由于读时钟域与写时钟域不同,只有在相同的时钟频率下才能进行地址的比较。在异步FIFO中,使用格雷码进行跨时钟域读写指针的同步,通过格雷码的转换实现地址的同步传递。 因此,我们可以根据上述原理和方法,在Verilog综合实现异步FIFO寄存器,并通过测试bench来验证其功能,并使用仿真波形来观察其工作情况。 总结起来,异步FIFO寄存器是一种通过异步时钟域实现的FIFO数据结构,其实现需要考虑到RAM存储区域构建、读地址同步到写时钟域、写地址同步到读时钟域和跨时钟域地址指针同步等方面。在Verilog中,我们可以使用代码来实现异步FIFO寄存器,并通过测试bench和仿真波形来验证其功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [异步FIFO的设计 verilog](https://blog.csdn.net/qq_24287711/article/details/129960090)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [异步FIFO_Verilog实现](https://blog.csdn.net/qq_40147893/article/details/117000168)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值