FIFO学习

1、FIFO分类

同步FIFO,读和写应用同一个时钟。它的作用一般是做交互数据的一个缓冲,也就是说它的主要作用就是一个buffer。
异步FIFO,读写应用不同的时钟,它有两个主要的作用,一个是实现数据在不同时钟域进行传递另一个作用就是实现不同数据宽度的数据接口(ADC:8->16,16->8)。

2、同步FIFO

clk,rst,读和写共用一个时钟

r_en,w_en,读和写使能

read_empty,write_full,读空和写满标志,不需要格雷码

read_data,write_data

内部模块还需要定义,读地址和写地址 read_addr,write_addr

需要例化ram模块

表明FIFO深度和位宽

核心在判断读空与写满标志,有两种方法:1、计数器法;2、高位扩展法

1、计数器法

构建一个计数器,利用计数器(fifo_cnt)来表示FIFO中的数据个数,并且利用其来判断是否写满和读空。

(1)、当读使能(r_en)与写使能(w_en)同时有效时,FIFO同时进行读和写操作,即fifo_cnt的值不变;

(2)、当读使能(r_en)有效且read_empty=0时,即FIFO未读空时,继续读出,fifo_cnt-1;

(3)、当写使能(w_en)有效且write_full=0时,即FIFO未写满时,继续写入,fifo_cnt+1;

(4)、当fifo_cnt=0时,表明FIFO读空,将read_empty=1,当fifo_cnt=FIFO的深度时,表明FIFO写满,将write_full=1。

2、高位扩展法

多扩展1bit地址来判断读空和写满标志,如原地址[2:0],扩展1bit为[3:0]

当扩展后的读地址与写地址相等时,表明写地址追上读地址,即写满

当扩展后的读写地址最高位不同,其他位相同同时,表明读地址追上写地址,即读空

`timescale 1ns/1ns
/**********************************RAM************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
	,input [WIDTH-1:0] wdata      	//数据写入
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
	,output reg [WIDTH-1:0] rdata 		//数据输出
);

reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];

always @(posedge wclk) begin
	if(wenc)
		RAM_MEM[waddr] <= wdata;
end 

always @(posedge rclk) begin
	if(renc)
		rdata <= RAM_MEM[raddr];
end 

endmodule  

/**********************************SFIFO************************************/
module sfifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					clk		, 
	input 					rst_n	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output reg				wfull	,
	output reg				rempty	,
	output wire [WIDTH-1:0]	rdata
);
localparam	addwidth=$clog2(DEPTH);
reg	[addwidth:0]	waddr;
reg	[addwidth:0]	raddr;
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		waddr<=0;
	end
	else if(winc&&!wfull)begin
		waddr<=waddr+1;
	end
	else begin
		waddr<=waddr;
	end
end
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		raddr<=0;
	end
	else if(rinc&&!rempty)begin
		raddr<=raddr+1;
	end
	else begin
		raddr<=raddr;
	end
end
always@(posedge clk or negedge rst_n )begin
	if(!rst_n)begin
		wfull<=0;
		rempty<=0;
	end
	else begin
		rempty<=waddr==raddr;
		wfull<=(raddr=={~waddr[addwidth],waddr[addwidth-1:0]});
	end
end

dual_port_RAM #(. DEPTH(DEPTH),
					   . WIDTH (WIDTH))
					   ram_d(
	 . wclk(clk)
	,. wenc(winc&&!wfull)
	,.  waddr(waddr[addwidth-1:0])  //深度对2取对数,得到地址的位宽。
	,.  wdata(wdata)      	//数据写入
	,. rclk(clk)
	,. renc(rinc&&!rempty)
	,.  raddr(raddr[addwidth-1:0])  //深度对2取对数,得到地址的位宽。
	,.  rdata(rdata) 		//数据输出
);

endmodule

3、异步FIFO

由于读和写是不同时钟控制的,所以不能用计数器法来实现,可用高位扩展法,扩展一位地址

在同步FIFO中,读空时,读地址=写地址;写满时,写地址多跑一圈,所以读写地址最高位不同,其他位相同。

而在异步FIFO中,由于读或写地址都要同步到另一个时钟域中进行比较,此时如果仍使用二进制进行同步,就会出现多位bit数据同时进行跳变,此时就会增大出错概率;如果采用格雷码,相邻位仅有一位bit发生变化,就可以大幅度降低出错概率。

所以在异步FIFO中,在原二进制读写地址加1后,要先进行格雷码转换,再进行同步和判断读空、写满。

二进制转换为格雷码:最高位相同,然后最低位与次低位依次异或得到格雷码的每一位。

assign waddr_gray=waddr^(waddr>>1);
assign raddr_gray=raddr^(raddr>>1);

此时判断空满的标志不再是二进制时判断标准:

当读空时,仍是读地址与写地址相同

当写满时,读地址与写地址最高位、次高位不同,其余相同

在转换为格雷码后,先存储到寄存器中,然后再进行同步;

在同步中,采用打两拍的方式进行同步,即串行两个D触发器,防止亚稳态。

此时打两拍延时的同步对空满标志的影响

当读或写标志经过两级触发器,形成新的同步信号,在比较时不再是实时的比较,此时写地址格雷码或者读地址格雷码(格雷码转换后的,即两拍前的)与新同步后的信号进行比较,这其实是留有余量的设计,同于比较时读地址一定小于或等于当前的读地址(数据同步的这两拍中有可能再进行读操作),此时产生full信号,其实FIFO有可能还没有满,这也就为设计留了一些设计的余量。同理,就算有empty信号的产生,FIFO有可能还有数据。这种留余量的设计在实际的工程项目中是很常见的。

`timescale 1ns/1ns

/***************************************RAM*****************************************/
module dual_port_RAM  #(parameter WIDTH=8,
						parameter DEPTH = 16)
		(
			input	wclk,
			input	rclk,
			input	wenc,
			input	renc,
			input	[$clog2(DEPTH)-1:0]waddr,
			input	[$clog2(DEPTH)-1:0]raddr,
			input	[WIDTH-1:0]	wdata,
			output reg	[WIDTH-1:0] rdata
		);
		reg [WIDTH-1:0] ram_reg [0:DEPTH-1];
		always@(posedge wclk )begin
			if(wenc)
				ram_reg[waddr] <= wdata;
		end
		always@(posedge rclk )begin
			if(renc)
				rdata <= ram_reg[raddr];
		end

endmodule  

/***************************************AFIFO*****************************************/
module asyn_fifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					wclk	, 
	input 					rclk	,   
	input 					wrstn	,
	input					rrstn	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output wire				wfull	,
	output wire				rempty	,
	output wire [WIDTH-1:0]	rdata
);
localparam a_width = $clog2(DEPTH);
reg [a_width:0] waddr;
reg	[a_width:0]	raddr;

always@(posedge wclk or negedge wrstn)begin
	if(!wrstn)begin
		waddr <= 'd0;
	end
	else if(winc&&(~wfull))begin
		waddr <= waddr + 1'b1;
	end
	else begin
		waddr <= waddr ;
	end
end

always@(posedge rclk or negedge rrstn)begin
	if(!rrstn)begin
		raddr <= 'd0;
	end
	else if(rinc&&(~rempty))begin
		raddr <= raddr + 1'b1;
	end
	else begin
		raddr <= raddr;
	end
end

wire	[a_width:0] waddr_gray;
wire	[a_width:0] raddr_gray;
assign waddr_gray=waddr^(waddr>>1);
assign raddr_gray=raddr^(raddr>>1);

reg	[a_width:0] waddr_gray_reg;
reg	[a_width:0] raddr_gray_reg;
always@(posedge wclk or negedge wrstn)begin
	if(!wrstn)begin
		waddr_gray_reg<='d0;
	end
	else begin
		waddr_gray_reg <= waddr_gray;
	end
end
always@(posedge rclk or negedge rrstn )begin
	if(!rrstn)begin
		raddr_gray_reg <= 'd0;
	end
	else begin
		raddr_gray_reg <= raddr_gray;
	end

end

reg [a_width:0] waddr_1;
reg	[a_width:0] waddr_2;
reg	[a_width:0] raddr_1;
reg	[a_width:0] raddr_2;
always@(posedge wclk or negedge wrstn)begin
	if(!wrstn)begin
		raddr_1<= 'd0;
		raddr_2 <= 'd0;
	end
	else begin
		raddr_1 <= raddr_gray_reg;
		raddr_2 <= raddr_1;
	end
end

always@(posedge rclk or negedge rrstn)begin
	if(!rrstn)begin
		waddr_1 <= 'd0;
		waddr_2 <= 'd0;
	end
	else begin
		waddr_1 <= waddr_gray_reg;
		waddr_2 <= waddr_1;
	end
end

assign wfull = waddr_gray_reg=={~raddr_2[a_width:a_width-1],raddr_2[a_width-2:0]};
assign	rempty = raddr_gray_reg==waddr_2;
    

	dual_port_RAM  #(. WIDTH(WIDTH),
						. DEPTH(DEPTH))
	d_ram
		(
			.	wclk(wclk),
			.	rclk(rclk),
			.	wenc(winc&&(!wfull)),
			.	renc(rinc&&(!rempty)),
			.	waddr(waddr[a_width-1:0]),
			.	raddr(raddr[a_width-1:0]),
			.	wdata(wdata),
			.   rdata(rdata)
		);
endmodule

还有FIFO深度的计算,下一篇写

参考:同步FIFO的两种Verilog设计方法(计数器法、高位扩展法)_fifo的构建方式-CSDN博客

深入理解FIFO以及同步FIFO和异步FIFO的verilog实现代码_verilog的fifo代码-CSDN博客

  • 20
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FIFO Generator是一种IP核,用于生成FIFO(First-In-First-Out)缓冲区。它可以根据用户的需求生成具有不同宽度和深度的FIFOFIFO Generator可以通过使用或门结合FIFO或块RAM基元的sbiterr和dbiterr信号来生成输出信号。sbiterr和dbiterr信号用于指示FIFO中的错误情况,例如数据损坏或溢出。当标记了dbiterr时,用户逻辑需要采取适当的操作,例如停止FIFO操作,复位FIFO,并重新启动数据传输。\[1\] FIFO Generator还具有一些常见的参数,包括FIFO的宽度和深度。宽度是指FIFO一次读写操作的数据位数,而深度是指FIFO可以存储多少个数据。此外,FIFO还具有一些状态标志,如空标志和满标志,用于指示FIFO的状态。空标志表示FIFO已空,阻止读操作继续读取数据。满标志表示FIFO已满或将要写满,阻止写操作继续写入数据。FIFO读写操作是由读时钟和写时钟控制的,读时钟在每个时钟的上升沿触发读操作,写时钟在每个时钟的上升沿触发写操作。\[2\] 此外,FIFO Generator还支持FWFT(First Word Fall Through)FIFO。FWFT FIFO与标准FIFO相似,但它会预先将读数据加载到rdata端口上,并通过rd_en信号控制FIFO更新下一个读数据到rdata端口上。\[3\] #### 引用[.reference_title] - *1* [【PG057】FIFO Generator IP核学习](https://blog.csdn.net/lum250/article/details/120641213)[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^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Vivado18.3-IP核-FIFO 学习笔记](https://blog.csdn.net/weixin_42151221/article/details/103410556)[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^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Xilinx IP解析之FIFO Generator v13.2](https://blog.csdn.net/weixin_42837669/article/details/121734888)[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^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值