基于FPGA的异步FIFO设计

异步FIFO的结构如下图所示:

具体的逻辑如下图所示:

空满标志逻辑如下图所示:

fifo_async.v代码如下:


`timescale 1ns / 1ps

module fifo_async 
#(parameter ADDR_WIDTH=5,BIT_WIDTH=8) 		// DEPTH = 32,ADDR_WIDTH=ADDR_WIDTH
(	
	 input iResetN

	,input iWriteClk,iWriteEnable			// write part
	,input [BIT_WIDTH-1:0] iWriteData
	,output oWriteFull

	,input iReadClk,iReadEnable				// read part
	,output [BIT_WIDTH-1:0] oReadData
	,output oReadEmpty
);

wire wClear;
reg [ADDR_WIDTH:0] rWritePointer='b0,rReadPointer='b0;	// gray code
reg [ADDR_WIDTH:0] rWriteCnt='b0,rReadCnt='b0;	// natual binary code
reg [ADDR_WIDTH:0] rW2rPointerFront='b0,rR2wPointerFront='b0;
reg [ADDR_WIDTH:0] rW2rPointerNext='b0,rR2wPointerNext='b0;

assign wClear = ~ iResetN;
// this dual ram content is 32(depth) * 8(bit width) ;
dual_ram dual_ram (
	 .rd_aclr 	( wClear 						)
	,.rdclock 	( iReadClk 						)
	,.rden 		( iReadEnable 					)
	,.rdaddress ( rReadPointer[ADDR_WIDTH-1:0] 	)
	,.q 		( oReadData 					)

	,.wrclock 	( iWriteClk 					)
	,.wren 		( iWriteEnable 					)
	,.wraddress ( rWritePointer[ADDR_WIDTH-1:0] )
	,.data 		( iWriteData 					)
);

// output write addreass 
always@(negedge iResetN or posedge iWriteClk) begin
	if(!iResetN)begin
		rWritePointer <= 'b0;
		rWriteCnt <= 'b0;
	end
	else begin
		if(iWriteEnable)begin
			rWritePointer <= {1'b0 , rWriteCnt[ADDR_WIDTH:1]} ^ rWriteCnt;	// convert natual binary code to gray code
			rWriteCnt <= rWriteCnt + (1'b1 & ~oWriteFull);
		end
	end
end

// output read addreass 
always@(negedge iResetN or posedge iReadClk) begin
	if(!iResetN)begin
		rReadPointer <= 'b0;
		rReadCnt <= 'b0;
	end
	else begin
		if(iReadEnable)begin
			rReadPointer <= {1'b0 , rReadCnt[ADDR_WIDTH:1]} ^ rReadCnt;	// convert natual binary code to gray code
			rReadCnt <= rReadCnt + (1'b1 & ~oReadEmpty);
		end
	end
end

always @(negedge iResetN or posedge iWriteClk) begin
	if (!iResetN) begin	
		rR2wPointerFront <= 'b0;
		rR2wPointerNext  <= 'b0;
	end
	else begin
		rR2wPointerFront<= rReadPointer;
		rR2wPointerNext <= rR2wPointerFront;
	end
end

always @(negedge iResetN or posedge iReadClk) begin
	if (!iResetN) begin		
		rW2rPointerFront <= 'b0;
		rW2rPointerNext  <= 'b0;
	end
	else begin
		rW2rPointerFront <= rWritePointer;
		rW2rPointerNext  <= rW2rPointerFront;
	end
end

assign oReadEmpty = (rW2rPointerNext == rReadPointer)?1'b1:1'b0;
assign oWriteFull = (rR2wPointerNext[ADDR_WIDTH]=={~rWritePointer[ADDR_WIDTH],rWritePointer[ADDR_WIDTH-1:0]})?1'b1:1'b0;

endmodule 

dual_ram.v可由quartus生成,代码如下:

`timescale 1 ps / 1 ps
// synopsys translate_on
module dual_ram (
	data,
	rd_aclr,
	rdaddress,
	rdclock,
	rden,
	wraddress,
	wrclock,
	wren,
	q);

	input	[7:0]  data;
	input	  rd_aclr;
	input	[4:0]  rdaddress;
	input	  rdclock;
	input	  rden;
	input	[4:0]  wraddress;
	input	  wrclock;
	input	  wren;
	output	[7:0]  q;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_off
`endif
	tri0	  rd_aclr;
	tri1	  rden;
	tri1	  wrclock;
	tri0	  wren;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_on
`endif

	wire [7:0] sub_wire0;
	wire [7:0] q = sub_wire0[7:0];

	altsyncram	altsyncram_component (
				.clock0 (wrclock),
				.wren_a (wren),
				.aclr1 (rd_aclr),
				.address_b (rdaddress),
				.clock1 (rdclock),
				.address_a (wraddress),
				.data_a (data),
				.rden_b (rden),
				.q_b (sub_wire0),
				.aclr0 (1'b0),
				.addressstall_a (1'b0),
				.addressstall_b (1'b0),
				.byteena_a (1'b1),
				.byteena_b (1'b1),
				.clocken0 (1'b1),
				.clocken1 (1'b1),
				.clocken2 (1'b1),
				.clocken3 (1'b1),
				.data_b ({8{1'b1}}),
				.eccstatus (),
				.q_a (),
				.rden_a (1'b1),
				.wren_b (1'b0));
	defparam
		altsyncram_component.address_reg_b = "CLOCK1",
		altsyncram_component.clock_enable_input_a = "BYPASS",
		altsyncram_component.clock_enable_input_b = "BYPASS",
		altsyncram_component.clock_enable_output_a = "BYPASS",
		altsyncram_component.clock_enable_output_b = "BYPASS",
		altsyncram_component.intended_device_family = "Cyclone II",
		altsyncram_component.lpm_type = "altsyncram",
		altsyncram_component.numwords_a = 32,
		altsyncram_component.numwords_b = 32,
		altsyncram_component.operation_mode = "DUAL_PORT",
		altsyncram_component.outdata_aclr_b = "CLEAR1",
		altsyncram_component.outdata_reg_b = "CLOCK1",
		altsyncram_component.power_up_uninitialized = "FALSE",
		altsyncram_component.rdcontrol_reg_b = "CLOCK1",
		altsyncram_component.widthad_a = 5,
		altsyncram_component.widthad_b = 5,
		altsyncram_component.width_a = 8,
		altsyncram_component.width_b = 8,
		altsyncram_component.width_byteena_a = 1;


endmodule

fifo_async_tb.v代码如下:


`timescale 1 ns/ 1 ps

module fifo_async_tb();
reg 		iResetN;
reg 		iReadClk;
reg 		iReadEnable;
reg 		iWriteClk;
reg [7:0] 	iWriteData;
reg 		iWriteEnable;

wire [7:0] 	oReadData;
wire 		oReadEmpty;
wire 		oWriteFull;

fifo_async fifo_async (
	 .iResetN		( iResetN		)
	,.iReadClk		( iReadClk		)
	,.oReadData		( oReadData		)
	,.oReadEmpty	( oReadEmpty	)
	,.iReadEnable	( iReadEnable	)
	,.iWriteClk		( iWriteClk		)
	,.iWriteData	( iWriteData	)
	,.iWriteEnable	( iWriteEnable	)
	,.oWriteFull	( oWriteFull	)
);

always @ (*) begin
	iResetN = 1; 
	iReadEnable = (oReadEmpty == 1'b0)?1'b1:1'b0;
	iWriteEnable = (oWriteFull == 1'b0)?1'b1:1'b0;
end // always

// fast writting and slow reading
initial begin
	forever begin
		iWriteClk = 0;
		#10 iWriteClk = 1;
		#10;
	end
end     

initial begin
	iWriteData = 0;
	#20;
	forever begin
		iWriteData = (oWriteFull == 1'b0)?$random:8'b0;
		#20;
	end
end     

initial begin
	forever begin
		iReadClk = 0;
		#80 iReadClk = 1;
		#80;
	end
end                                                

// fast reading and slow writting
// initial begin
// 	forever begin
// 		iWriteClk = 0;
// 		#40 iWriteClk = 1;
// 		#40;
// 	end
// end     

// initial begin
// 	iWriteData = 0;
// 	#80;
// 	forever begin
// 		iWriteData = (oWriteFull == 1'b0)?$random:8'b0;
// 		#80;
// 	end
// end     

// initial begin
// 	forever begin
// 		iReadClk = 0;
// 		#10 iReadClk = 1;
// 		#10;
// 	end
// end                                                

endmodule

快读慢写,会产生读空(oReadEmpty),仿真结果如下:

快写慢读,会产生写满(oWriteFull),仿真结果如下:

其它问题:

1、仿真时需要添加quartus的库,以使用双口RAM。

2、FIFO与双口RAM不同,在于FIFO不能指定地址,其地址由计数器(自然二进制码)转Gray码来产生,Gray码翻转的bit较少,从而减少亚稳态。此外,FIFO可以用于同步两个异频/异相时钟。

3、这里的FIFO使用四象限法来设计,将RAM中的地址以最高两位来区分象限,

4、当写指针比读指针落后一个象限时,意味着写指针即将从追上读指针,FIFO处于“可能满”的状态。

5、当读指针比写指针落后一个象限时,意味着读指针即将从追上写指针,FIFO处于“可能空”的状态。

6、利用写时钟给读指针打两拍,以实现同步读指针;同理,利用读时钟给写指针打两拍,以实现同步写指针。

7、在实际应用中,很少使用full和empty标志,而是使用almost full和almost empty,将要满和将要空标志,给RAM的空间留有一定的余量。

8、在实际应用中,往往使用厂家提供的FIFO的IP核,但是我们需要学习异步FIFO的同步思想,所以自己写一下还是很有必要的。

 

 

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值