【verilog】同步FIFO与异步FIFO

一、同步FIFO
FIFO表示先进先出的意思。是基于RAM的存储模块,一般多是用于缓冲数据,令模块独立,调用方便、随性。学习FIFO最重要的如何建立先进先出的机制。
图1:先用一个简单的示意图说明:
图1
图1
左边有写入使能Write,然后写入数据 Data_Write,还有写满标志位full。右边则有读使能Read,读出数据Data_Read,还有读空标志位empty。Write必须拉高Data_Write才能写入,一旦FIFO写满,那么full就会拉高;Read必须拉高,数据才能经由Data_Read读出,一旦FIFO读空,empty就会拉高。
难点是如何判断写满和读空:
首先要知道FIFO的运作机制。建立一个4bit,深度为4的RAM,如图2:
在这里插入图片描述
图2
Wp表示下一个将要去写的地址,比如现在还没开始,它就会一直停在00的位置等待
Rp表示下一个将要去读的地址,

  1. 此时图2所示为空,可以推断当Wp==Rp时为空。
empty = ( Wp == Rp );
  1. 当有数据输入时,如图3
    在这里插入图片描述
    图3
    此时可以发现Wp会指向下一个地址,等待数据,而Rp则指向将要读取的数据,Wp和Rp不再相等,整个RAM不满也不空。如图4:
    在这里插入图片描述
    图4
    那么,什么时候会满,可以发现在数据写满整个RAM后,Wp会指向下一个地址,即就是在一个循环之后Wp会回到第一个位置,如图5:
    在这里插入图片描述
    图5
    此时,就无法得出full时的关系式。根据黑金原创教程(FPGA那些事儿)中提到,我们可以给Wp和Rp加一位,当一个循环后,给最高位加1,如图6
    在这里插入图片描述
    图6
    原本Wp和Rp只需2位即可(2^2=4),现在加一位就可以表现出一轮循环后的标志。所以当Wp和Rp需要N位时,我们要加一位高位,即Wp和Rp是N+1位。
full = ( Wp [2] ^ Rp [2] & Wp [1:0] == Rp [1:0] );

所以有full和empty的计算方式:

full = ( Wp [2] ^ Rp [2] & Wp [1:0] == Rp [1:0] );
empty = ( Wp == Rp );

full可以写成:

full = ( Wp [N+1] ^ Rp [N+1] & Wp [N:0] == Rp [N:0] )

2.以深度16,8bit为例的同步FIFO:

`timescale 1 ns/ 1 ps
//
// Company: 
// Engineer: ZHANG ZEKUN
// 
// Create Date: 2019/08/22 15:01:16
// Design Name: 
// Module Name: Synchronize FIFO
// Project Name:  
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

module on_clk_fifo(
	input CLK,
	input RSTn,
	input write,
	input read,
	input [7:0] iData,
	
	output [7:0] oData,
	output full,
	output empty
);

reg [4:0] wp;          //write point should add 1 bit(N+1) 
reg [4:0] rp;          //read point
reg [7:0] RAM [15:0];  //deep16,8 bit RAM
reg [7:0] oData_reg;   //regsiter of oData

always @ ( posedge CLK or negedge RSTn )
begin                  //write to RAM
	if (!RSTn)
	begin
		wp <= 5'b0;
	end
	else if ( write )
	begin
		RAM[wp[3:0]] <= iData;
		wp <= wp + 1'b1;
	end
end

always @ ( posedge CLK or negedge RSTn )
begin                  // read from RAM
	if (!RSTn)
	begin
		rp <= 5'b0;
		oData_reg <= 8'b0;
	end
	else if ( read  )
	begin
		oData_reg <= RAM[rp[3:0]];
		rp <= rp + 1'b1;
	end
end


assign full = ( wp[4] ^ rp[4] & wp[3:0] == rp[3:0] );
assign empty = ( wp == rp );
assign oData = oData_reg;


endmodule

简单测试:

module on_clk_fifo_tb();
	reg CLK;
	reg RSTn;
	reg write;
	reg read;
	reg [7:0] iData;
	
	wire [7:0] oData;
	wire full;
	wire empty;

initial
begin
	CLK = 0;
	forever #100 CLK = ~CLK;
end

initial 
begin
	RSTn = 0;
	iData = 0;
	#100 RSTn = 1;
end
	
always @ (posedge CLK or negedge RSTn)
begin
	iData <= iData + 1'b1;
end

always @ (posedge CLK or negedge RSTn)
begin
	if (!RSTn)
		write = 0;
	else if (!full)
	begin
		write = 1;
	end
	else 
		write = 0;
end

always @ (posedge CLK or negedge RSTn)
begin
	if (!RSTn)
		read = 0;
	else if (!empty)
		read = 1;
	else 
		read = 0;
end


on_clk_fifo fifo (.CLK(CLK),
				  .RSTn(RSTn),
				  .write(write),
				  .read(read),
				  .iData(iData),
				  .full(full),
				  .empty(empty),
				  .oData(oData));
				  
endmodule

3.仿真结果:
在这里插入图片描述
二、异步FIFO
1.实现方法:
异步FIFO简单的说就是读写的时钟不同。可以在两个不同的时钟域之间传输数据,起到跨时钟域处理的作用,多用于处理跨时钟域的问题。
详细内容请移步:https://blog.csdn.net/MaoChuangAn/article/details/88783320
依旧是如何判断满和空的状态:
在上面的同步FIFO中已经说明判断满空的方法,可是那是在同时钟下;显然在不同的时钟域下需要将读、写指针进行同步才能进行判断,下图中
r2w_sync:读指针同步到写时钟域
w2r_sync:写指针同步到读时钟域

各模块结构图:
在这里插入图片描述
同步后就可以在同一时钟域下判断空满状态
判满:格雷码的最高位和次高为不同,剩下的都同,就是满。

assign full_reg = ( wgray_next == { -rp2_wpt [ ADDR_WIDTH : ADDR_WIDTH - 1],rp2_wpt [ ADDR_WIDTH - 2:0]});

判空:格雷码完全相同,就是空。

assign empty_reg = ( rgray_next == wp2_rpt );

2.代码实现:

双口RAM
/**Double Port RAM module**/
module double_ram#(
	parameter DATA_WIDTH = 8,
	parameter ADDR_WIDTH = 4,
	parameter RAM_DEPTH = 16
)
(	
	input                        WCLK,   //write clk
	input 					     write,  //write en
	input  [ ADDR_WIDTH - 1:0 ]  waddr,  //write address from full.v
	input  [ DATA_WIDTH - 1:0 ]  wdata,  //write data
	input  [ ADDR_WIDTH - 1:0 ]  raddr,  //read address from empty.v
	output  [ DATA_WIDTH - 1:0 ] rdata   //read data
);

reg [ DATA_WIDTH - 1:0 ] RAM [ RAM_DEPTH - 1:0 ];  //double Port ram


always @ ( posedge WCLK )
begin
	if ( write == 1'b1 )
	begin
		RAM [ waddr ] <= wdata; 
	end
	else
	begin
		RAM [ waddr ] <= RAM [ waddr ];
	end
end

assign rdata = RAM [ raddr ];

endmodule
写指针同步到读时钟域
/**Read to Write Sync module**/
module r2w_sync#(
	parameter ADDR_WIDTH = 4
)
(
	input 						  WRSTn,    //write RSTn
	input 			    		  WCLK,     //write CLK
	input      [ ADDR_WIDTH : 0 ] rpt,      //output to write port gray
	output reg [ ADDR_WIDTH : 0 ] rp2_wpt   //D trigger sync with two levels,second level
);


reg [ ADDR_WIDTH : 0 ] rp1_wpt;         //frist level


always @ ( posedge WCLK or negedge WRSTn )
begin
	if ( !WRSTn )
	begin
		{ rp2_wpt,rp1_wpt } <= 0;
	end
	else 
	begin
		{ rp2_wpt,rp1_wpt } <= { rp1_wpt,rpt };
	end
end

endmodule
读指针同步到写时钟域
/**Write to Read Sync module**/
module w2r_sync#(
	parameter ADDR_WIDTH = 4
)
(
	input 						  RRSTn,    //read RSTn
	input 						  RCLK,     //reaf CLK
	input      [ ADDR_WIDTH : 0 ] wpt,      //output to read port gray
	output reg [ ADDR_WIDTH : 0 ] wp2_rpt   //D trigger sync with two levels,second level
);

reg [ ADDR_WIDTH : 0 ] wp1_rpt;         //frist level
  

always @ ( posedge RCLK or negedge RRSTn )
begin
	if ( !RRSTn )
	begin
		{ wp2_rpt,wp1_rpt } <= 0;
	end
	else 
	begin
		{ wp2_rpt,wp1_rpt } <= { wp1_rpt,wpt};
	end
end

endmodule 
写满模块
module full#(
	parameter ADDR_WIDTH = 4
)
(
	input                           WRSTn,
	input                           WCLK,
	input                           write,
	input      [ ADDR_WIDTH : 0 ]   rp2_wpt,
	output reg [ ADDR_WIDTH : 0 ]   wpt,
	output     [ ADDR_WIDTH - 1:0 ] waddr,
	output reg                      full
);

reg  [ ADDR_WIDTH :0 ] wbin;
wire [ ADDR_WIDTH :0 ] wbin_next;
wire [ ADDR_WIDTH :0 ] wgray_next;
wire full_reg;

always @ ( posedge WCLK or negedge WRSTn )
begin
	if (!WRSTn)
	begin
		wpt <= 0;
		wbin <= 0;
	end
	else 
	begin
		wpt <= wgray_next;
		wbin <= wbin_next;
	end
end

assign wbin_next = ( !full ) ? ( wbin + write ) : wbin;
assign wgray_next = ( wbin_next >> 1 ) ^ wbin_next;
assign waddr = wbin [ ADDR_WIDTH - 1:0 ];


always @ ( posedge WCLK or negedge WRSTn )
begin
	if (!WRSTn)
	begin
		full <= 0;
	end
	else
	begin
		full <= full_reg;
	end
end

assign full_reg = ( wgray_next == { -rp2_wpt [ ADDR_WIDTH : ADDR_WIDTH - 1],rp2_wpt [ ADDR_WIDTH - 2:0]});

endmodule 
读空模块
module empty#(
	parameter ADDR_WIDTH = 4
)
(
	input                           RRSTn,
	input                           RCLK,
	input                           read,
	input      [ ADDR_WIDTH : 0 ]   wp2_rpt,
	output reg [ ADDR_WIDTH : 0 ]   rpt,
	output reg [ ADDR_WIDTH - 1:0 ] raddr,
	output reg                      empty
);

reg  [ ADDR_WIDTH :0 ] rbin;
wire [ ADDR_WIDTH :0 ] rbin_next;
wire [ ADDR_WIDTH :0 ] rgray_next;
wire empty_reg;

always @ ( posedge RCLK or negedge RRSTn )
begin
	if (!RRSTn)
	begin
		rpt <= 0;
		rbin <= 0;
	end
	else 
	begin
		rpt <= rgray_next;
		rbin <= rbin_next;
	end
end

assign rbin_next = ( !empty ) ? ( rbin + read ) : rbin;
assign rgray_next = ( rbin_next >> 1 ) ^ rbin_next;


always @ ( posedge RCLK or negedge RRSTn )
begin
	if (read)
	begin
	   raddr <= rbin [ ADDR_WIDTH - 1:0 ];
	end
	
end

always @ ( posedge RCLK or negedge RRSTn )
begin
	if (!RRSTn)
	begin
		empty <= 0;
	end
	else
	begin
		empty <= empty_reg;
	end
end

assign empty_reg = ( rgray_next == wp2_rpt );

endmodule 
顶层模块
`timescale 1 ns/ 1 ps
//
// Company: 
// Engineer: ZHANG ZEKUN
// 
// Create Date: 2019/08/30 15:01:16
// Design Name: 
// Module Name: top
// Project Name:  Asynchronous FIFO
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 1.0 - 8.27 File Created 
// Revision 2.0 - 8.29 File Changed 
// Revision 3.0 - 8.30 Finish over
// Additional Comments:
// 
//
module asyn_fifo_top#(
	parameter DATA_WIDTH = 8,
	parameter ADDR_WIDTH = 4,
	parameter RAM_DEPTH = 16
)
(
	input 					      WCLK,
	input                         WRSTn,
	input                         RCLK,
	input                         RRSTn,
	input                         write,
	input                         read,
	input  [ DATA_WIDTH - 1 : 0 ] wdata,
	output [ DATA_WIDTH - 1 : 0 ] rdata,
	output                        full,
	output                        empty
);

wire [ ADDR_WIDTH : 0 ]    wpt;
wire [ ADDR_WIDTH : 0 ]    rpt;
wire [ ADDR_WIDTH : 0 ]    rp2_wpt;
wire [ ADDR_WIDTH : 0 ]    wp2_rpt;
wire [ ADDR_WIDTH - 1: 0 ] waddr;
wire [ ADDR_WIDTH - 1: 0 ] raddr;

double_ram#(.DATA_WIDTH(DATA_WIDTH),
			.ADDR_WIDTH(ADDR_WIDTH),
			.RAM_DEPTH(RAM_DEPTH)) 
	ram_module (.WCLK(WCLK),
	            .write(write),
				.waddr(waddr),
				.wdata(wdata),
				.raddr(raddr),
				.rdata(rdata));
r2w_sync #(.ADDR_WIDTH(ADDR_WIDTH))
	r2w_module (.WCLK(WCLK),
				.WRSTn(WRSTn),
				.rpt(rpt),
				.rp2_wpt(rp2_wpt));
w2r_sync #(.ADDR_WIDTH(ADDR_WIDTH))
	w2r_module (.RCLK(RCLK),
				.RRSTn(RRSTn),
				.wpt(wpt),
				.wp2_rpt(wp2_rpt));
full#(.ADDR_WIDTH(ADDR_WIDTH)) 
	full_module(.WCLK(WCLK),
				.WRSTn(WRSTn),
				.write(write),
				.rp2_wpt(rp2_wpt),
				.wpt(wpt),
				.waddr(waddr),
				.full(full));
empty #(.ADDR_WIDTH(ADDR_WIDTH))
	empty_module(.RCLK(RCLK),
				 .RRSTn(RRSTn),
				 .read(read),
				 .wp2_rpt(wp2_rpt),
				 .rpt(rpt),
				 .raddr(raddr),
				 .empty(empty));


endmodule

testbench:

module asyn_fifo_tb();

parameter DATA_WIDTH = 8;

	reg                         WCLK;
	reg                         WRSTn;
	reg                         RCLK;
	reg                         RRSTn;
	reg                         write;
	reg                         read;
	reg  [ DATA_WIDTH - 1 : 0 ] wdata;
	wire [ DATA_WIDTH - 1 : 0 ] rdata;
	wire                        full;
	wire                        empty;
	
initial 
begin
	WCLK <= 0;
	forever #100 WCLK = ~WCLK;
end

initial 
begin
	RCLK <= 0;
	forever #200 RCLK = ~RCLK;
end

initial 
begin
	WRSTn = 0;
	wdata = 0;
	#100 WRSTn = 1;
end

initial
begin
	RRSTn = 0;
	#100 RRSTn = 1;
end

always @ ( posedge WCLK or negedge WRSTn )
begin
	wdata <= wdata + 1'b1;
end

always @ ( full or WRSTn )
begin
	if (!WRSTn)
	begin
		write <= 0;
	end
	else if (!full)
	begin
		write <= 1;
	end
	else 
	begin
		write <= 0;
	end
end

always @ ( empty or RRSTn )
begin
	if (!RRSTn)
	begin
		read <= 0;
	end
	else if (!empty)
	begin
		read <= 1;
	end 
	else 
	begin
		read <= 0;
	end
end
asyn_fifo_top asyn_fifo(.WCLK(WCLK),
						.WRSTn(WRSTn),
						.RCLK(RCLK),
						.RRSTn(RRSTn),
						.write(write),
						.read(read),
						.wdata(wdata),
						.rdata(rdata),
						.full(full),
						.empty(empty));

endmodule

3.仿真结果:
在这里插入图片描述
感觉不太对劲啊。。。。。
reference:

https://www.cnblogs.com/alinx/p/4223450.html
https://blog.csdn.net/MaoChuangAn/article/details/88783320

越是憧憬,越要风雨兼程!

  • 8
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
异步FIFO是一种在读写时钟不一致的情况下进行数据传输的存储器。与同步FIFO不同,异步FIFO需要使用同步打拍逻辑来进行读写地址的比较,这会导致一定的延时。因此,在异步FIFO中,空状态的指示信号可能存在一定的延迟。如果在延迟期间有新的数据写入FIFO,就会出现空状态指示信号有效,但实际上FIFO中仍存在数据的情况。 在Verilog中,可以使用一些方法来实现异步FIFO的设计。异步FIFO的设计需要考虑以下几个方面: 1. FIFO的大小:确定FIFO所能容纳的数据量,包括数据位宽和深度。 2. 读写指针:使用两个指针来追踪读写操作的位置。读指针指向最早写入的数据,写指针指向最新写入的数据。 3. 读写逻辑:通过判断读写指针的状态来实现读写操作。读操作会使读指针向前移动,写操作会使写指针向前移动,并将数据写入相应位置。 4. 空状态指示信号:通过判断读写指针的状态来确定是否存在数据。当读写指针相等时,说明FIFO为空。 综上所述,异步FIFOVerilog设计需要考虑读写指针和读写逻辑,并且要注意空状态指示信号的延迟问题。根据具体的需求和设计要求,可以选择适合的设计方法进行实现。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [异步FIFO的Verilg实现方法](https://blog.csdn.net/wuzhikaidetb/article/details/121152844)[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_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [异步FIFO设计(Verilog)](https://blog.csdn.net/qq_21842097/article/details/118307227)[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_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值