同步FIFO的Verilog实现

引言

       在前面的文章中,我们已经对FIFO的基本设计思路做了简单的介绍,并且对异步FIFO进行了简单的Verilog实现,具体介绍见链接:同步FIFO与异步FIFO的基本原理异步FIFO的Verilog实现,下面我们基于如前所述的FIFO基本原理,利用Verilog代码对同步FIFO进行了一个简单的代码实现,并进行了简单的仿真测试。
1、实现过程

      同步FIFO的设计过程中主要有如下几个注意事项:

  • FIFO深度的计算
  • FIFO满空标志的产生

     解决好如上所述的几个问题后,同步FIFO的实现就非常简单了,下面直接贴代码。

Sync_FIFO.v

`timescale 1ns/1ps
module Sync_FIFO #(
	//FIFO参数定义
	 parameter   data_width = 16,// FIFO宽度
     parameter   data_depth = 256,// FIFO深度
     parameter   address_width = 8 // 地址宽度,对于深度为2^n的FIFO,需要的读/写指针位宽为(n+1)位,多的一位作为折返标志位
	)(
	//system signals
	input						clk,
	input						rst_n,

	input       				write_en,// 写使能,高有效
	input 	[data_width-1:0] 	data_in,// 写数据
	output 		 	  			full,//写满标志,高有效
	
	input 						read_en,// 读使能,高有效
	output reg [data_width-1:0] data_out,// 读数据
	output 		 	  			empty//读空标志,高有效
);

reg    [data_width-1:0] FIFO_RAM [data_depth-1:0];// RAM定义,用于存放FIFO数据

reg    [address_width:0]    wr_addr_p;//写地址指针
reg    [address_width:0]    rd_addr_p;//读地址指针

wire   [address_width-1:0]  wr_addr;//写RAM 地址
wire   [address_width-1:0]  rd_addr;//读RAM 地址

assign wr_addr = wr_addr_p[address_width-1:0];// 读写RAM地址等于读写指针的低address_width位
assign rd_addr = rd_addr_p[address_width-1:0];// 读写RAM地址等于读写指针的低address_width位

always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		wr_addr_p <= 'h0;
		rd_addr_p <= 'h0;
		data_out <= 'h0;
	end
	else begin
		case({write_en,read_en})
			2'b00:begin// 不读不写
				wr_addr_p <= wr_addr_p;
				rd_addr_p <= rd_addr_p;
			end
			2'b10:if(~full) begin // 非满只写不读
				FIFO_RAM[wr_addr] <= data_in;
				wr_addr_p <= wr_addr_p + 1'b1;
			end
			2'b01:if(~empty) begin // 非空只读不写
				data_out <= FIFO_RAM[rd_addr];
				rd_addr_p <= rd_addr_p + 1'b1;
			end
			2'b11:if(~full && ~empty)begin // 非满非空又读又写
				FIFO_RAM[wr_addr] <= data_in;
				wr_addr_p <= wr_addr_p + 1'b1;

				data_out <= FIFO_RAM[rd_addr];
				rd_addr_p <= rd_addr_p + 1'b1;
			end
		endcase
	end
end

// 判断空满
assign full = (wr_addr_p == {~rd_addr_p[address_width],rd_addr_p[address_width-1:0]});// 最高位不同,其余各位相同
assign empty = (wr_addr_p == rd_addr_p);// 所有位都相同
endmodule

 Testbench.v

`timescale 1ns/1ps
module  Testbench();

//FIFO参数定义
parameter   data_width = 16;// FIFO宽度
parameter   data_depth = 256;// FIFO深度
parameter   address_width = 8;// 地址宽度,对于深度为2^n的FIFO,需要的读/写指针位宽为(n+1)位,多的一位作为折返标志位

reg clk;
reg rst_n;

reg write_en;
reg [data_width-1:0] data_in;
wire full;

reg read_en;
wire [data_width-1:0] data_out;
wire empty;

// 例化模块
Sync_FIFO  #(
	.data_width(data_width),
	.data_depth(data_depth),
	.address_width(address_width)
	)demo(
	  .clk(clk),
	  .rst_n(rst_n),

	  .write_en(write_en),
	  .data_in(data_in),
	  .full(full),

	  .read_en(read_en),
	  .data_out(data_out),
	  .empty(empty)
	);

initial
begin
	clk = 1'b0;

	rst_n = 1'b1;
	#5 rst_n = 1'b0;
	#5 rst_n = 1'b1;

	#20 write_en = 1'b1;// 先写满
		read_en = 1'b0;

	#5000 read_en = 1'b1;// 再读空
	write_en = 1'b0;

	#5000 read_en = 1'b1;// 然后边写边读
	write_en = 1'b1;

end

always #5 clk = ~clk;

always @ (posedge clk ) begin
	if(write_en)
		data_in <= $random % 1024;
end

endmodule

 2、仿真结果


版权声明:本文为qq_40230112原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。