天天看点

verilog FPGA 同步FIFO设计及仿真verilog FPGA 同步FIFO设计及仿真

verilog FPGA 同步FIFO设计及仿真

一、核心思想

FIFO即First In First Out,是一种数据缓冲方式。同步FIFO的读写端在同一时钟下

二、时序

1、写操作

当写入一个数据,写有效拉高,写地址加一,数据存量加一

2、读数据

当读出一个数据,读有效拉高,读地址加一,数据存量减一

3、存数据

利用寄存器组来实现数据的存储

四、设计难点

1、计数器与一般的计数器不同,在这里没有用add_cnt 和end_cnt,因为计数器本身会有减的情况

2、寄存器数组置零,在这里首次使用for循环来对其循环置零

五、代码实现

fifo_sync.v

module fifo_sync#(parameter dwidth = 8,awidth = 8)(//fifo 宽度为8,深度为2^8=256
	input 				 		clk		,
	input 				 		rst_n	,
	input   			 		wr_req	,
	input 		[dwidth-1:0] 	wr_data	,
	input				 		rd_req	,
	output		[dwidth-1:0] 	rd_data	,
	output 	reg[awidth-1:0] 	cnt		,//数据量
	output  			 		full	,
	output  			 		empty
);
	
	reg	 [awidth-1:0]	wr_addr;//写地址
	wire				add_wr_addr;
	wire				end_wr_addr;
	reg	 [awidth-1:0]	rd_addr;//读地址
	wire				add_rd_addr;
	wire				end_rd_addr;
	reg  [dwidth-1:0]	fifo_ram[{(awidth){1'b1}}:0];//寄存器组
	
	integer				i;

	//cnt 当读信号有效,cnt-1,当写信号有效,cnt+1
	always @(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			cnt <= 1'b0;
		end
		else if(rd_req &&wr_req)begin//当同时读写cnt不变
			cnt <= cnt;
		end
		else if(wr_req && cnt<{(awidth){1'b1}})begin//当写信号有效且非满cnt加一
			cnt <= cnt+1;
		end
		else if(rd_req && cnt>0)begin//当读信号有效且非空cnt减一
			cnt <= cnt-1;
		end
		else 
			cnt <= cnt;
	end
	
	//wr_addr
	always @(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			wr_addr <= 0;
		end
		else if(add_wr_addr)begin
			if(end_wr_addr)
				wr_addr <= 0;
			else
				wr_addr <= wr_addr+1;
		end
	end
	assign add_wr_addr = wr_req;
	assign end_wr_addr = add_wr_addr && wr_addr == {(awidth){1'b1}};
	
	//rd_addr
	always @(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			rd_addr <= 0;
		end
		else if(add_rd_addr)begin
			if(end_rd_addr)
				rd_addr <= 0;
			else
				rd_addr <= rd_addr+1;
		end
	end
	assign add_rd_addr = rd_req;
	assign end_rd_addr = add_rd_addr && rd_addr == {(awidth){1'b1}};
	
	//empty
	assign empty=(cnt!=0)?0:1;
	
	//full
	assign full = (cnt == {(awidth){1'b1}})?1:0;
	
	//wr_data
	always @(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
				for(i=0;i<={(awidth){1'b1}};i=i+1)begin
					fifo_ram[i] <= {(dwidth){1'b0}};
				end
		end
		else begin
			fifo_ram[wr_addr] <= wr_data;
		end
	end
	//rd_data
	assign rd_data = fifo_ram[rd_addr];
endmodule
           

fifo_sync_tb.v

`timescale 1ns/1ps
module fifo_sync_tb();
	reg 			 tb_clk		;
	reg 			 tb_rst_n	;
	reg   			 tb_wr_req	;
	reg 	[7:0] 	 tb_wr_data	;
	reg				 tb_rd_req	;
	wire	[7:0] 	 tb_rd_data	;
	wire 	[4:0] 	 tb_cnt		;
	wire  			 tb_full	;
	wire  			 tb_empty  	;
	
	
	fifo_sync u_fifo_sync(
	.clk					(tb_clk		),
	.rst_n					(tb_rst_n	),
	.wr_req					(tb_wr_req	),
	.wr_data				(tb_wr_data	),
	.rd_req					(tb_rd_req	),
	.rd_data				(tb_rd_data	),
	.cnt					(tb_cnt		),//数据量
	.full					(tb_full	),
	.empty					(tb_empty  	)
);
	defparam u_fifo_sync.dwidth = 8,
			 u_fifo_sync.awidth = 8;
	
	parameter CLOCK_CYCLE=20;
	
	initial tb_clk = 1'b0;//初始化时钟
	always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;
	integer i=0,j=0;
	initial begin
		tb_rst_n = 1'b1;
		tb_wr_req = 1'b0;
		tb_rd_req = 1'b0;
		tb_wr_data = 8'd0;
		#(20*CLOCK_CYCLE);
		tb_rst_n = 1'b0;
		#(20*CLOCK_CYCLE);
		tb_rst_n = 1'b1;
		
		repeat(40)begin
			for(j=0;j<20;j = j+1)begin
				i = {$random}%30;
				#2;
				tb_wr_req = {$random};
				tb_rd_req = {$random};
				tb_wr_data = {$random}%255;
				#(CLOCK_CYCLE*i);
			end
		end
		
	$stop;
	end
endmodule