Verilog:同步FIFO使用指针写法

同步FIFO(First in first out),先入先出。

Deign Spec:

  • 输入信号:clk;
  • 输入信号:异步复位信号:rstn;
  • 输入信号:写使能:wr_en;
  • 输入信号:写数据:wr_data;//时序逻辑,先判断写使能 wr_en 是否有效,有效情况下如果没有写满,则写入数据,如果满了,判断读使能 rd_en ,如果读使能有效能可以继续写数据。
  • 输入信号:读使能:rd_en;
  • 输出信号:读数据:rd_data;//时序逻辑,先判断读使能 rd_en 是否有效,有效情况下如果内存非空,则取上一个周期指针位置的数据作为读数据,如果内存为空,则判断写使能,如果写使能有效则直接将写数据 wr_data 作为读数据输出,无效时输出0。
  • 输出信号:内存满信号:full;//如果读写指针最高位不同但是后几位均相同则为1
  • 输出信号:内存空信号:empty;//如果读写指针完全相同则为1
  • 读指针:rd_ptr;//时序逻辑,先判断写使能 rd_en 是否有效,有效情况如果内存非空则+1,如果内存空了,判断 wr_en 是否有效,如果有效则+1,否则不变。指针大小比内存深度多一位。
  • 写指针:wr_ptr;//时序逻辑,先判断写使能 wr_en 是否有效,有效情况如果内存非满则+1,如果内存满了,判断 rd_en 是否有效,如果有效则+1,否则不变。指针大小比内存深度多一位。
  • 内存:mem;//内存深度要为 2^n 大小,即指针可以通过+1自动循环。

RTL:

module sync_fifo#(
	parameter DEPTH = 16,	//DEPTH = 2^n
	parameter WIDTH = 8
)(
	input				clk,
	input				rstn,
	
	//write
	input				wr_en,		//write enable
	input [WIDTH-1:0]	wr_data,	//write data
	output				full,
	
	//read
	input				rd_en,		//read enable
	output reg [WIDTH-1:0]	rd_data,	//read data
	output				empty
	
	//output reg [$clog2(DEPTH):0] cnt
);

	localparam	ADDR = $clog2(DEPTH);
	reg		[$clog2(DEPTH):0] rd_ptr, wr_ptr;
	wire	[$clog2(DEPTH)-1:0] rd_addr, wr_addr;
	reg 	[WIDTH-1:0] mem [0:DEPTH-1];
	
	assign rd_addr = rd_ptr[ADDR-1:0];
	assign wr_addr = wr_ptr[ADDR-1:0];

	//********** read start **********//
	always@(posedge clk, negedge rstn) begin
		if(~rstn) begin
			rd_ptr <= 0;
		end
		else if(rd_en && ~empty) begin
			rd_ptr <= rd_ptr + 1'b1;
		end
		else if(rd_en && empty && wr_en) begin
			rd_ptr <= rd_ptr + 1'b1;
		end
	end
	
	always@(posedge clk, negedge rstn) begin
		if(~rstn) begin
			rd_data <= 0;
		end
		else if(rd_en && ~empty) begin
			rd_data <= mem[rd_addr];
		end
		else if(rd_en && empty && wr_en) begin
			rd_data <= wr_data;
		end
		else begin
			rd_data <= 0;
		end
	end
	//********** read end **********//
	
	//********** write start **********//
	always@(posedge clk, negedge rstn) begin
		if(~rstn) begin
			wr_ptr <= 0;
		end
		else if(wr_en && ~full) begin
			wr_ptr <= wr_ptr + 1'b1;
		end
		else if(wr_en && full && rd_en) begin
			wr_ptr <= wr_ptr + 1'b1;
		end
	end
	
	always@(posedge clk) begin
		if(wr_en && ~full) begin
			mem[wr_addr] <= wr_data;
		end
		else if(wr_en && full && rd_en) begin
			mem[wr_addr] <= wr_data;
		end
	end
	//********** write end **********//
	
	assign empty = (rd_ptr[ADDR:0] == wr_ptr[ADDR:0]);
	assign full = (rd_ptr[ADDR] != wr_ptr[ADDR]) ? ((rd_ptr[ADDR-1:0] == wr_ptr[ADDR-1:0])?1'b1:1'b0) : 1'b0;

endmodule

TB:

module test#(
	parameter DEPTH = 16,
	parameter WIDTH = 8
);

	reg		clk;
	reg 	rstn;
	reg 	wr_en;
	reg 	[WIDTH-1:0] wr_data;
	wire	full;
	reg 	rd_en;
	wire	[WIDTH-1:0]	rd_data;
	wire	empty;
	//wire	[$clog2(DEPTH):0]	cnt;
	
	sync_fifo 	u0(
		.clk(clk),
		.rstn(rstn),
		.wr_en(wr_en),
		.wr_data(wr_data),
		.full(full),
		.rd_en(rd_en),
		.rd_data(rd_data),
		.empty(empty)
		//.cnt(cnt)
	);
	
	initial begin
		clk = 0;
		rstn = 0;
		wr_en = 0;
		wr_data = 0;
		rd_en = 0;
		#30	rstn = 1;
	end
	
	always #10 clk = ~clk;
	
	always@(posedge clk) begin
		if(({$random()}%16)<10) wr_en <= 1;
		else wr_en <= 0;
		if(({$random()}%16)<7) rd_en <= 1;
		else rd_en <= 0;
	end
 	
	always@(posedge clk) begin
		wr_data <= {$random()} & 8'hff;
	end
	
	initial begin
		#4000 $finish;
	end
	
endmodule

波形图:

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王bf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值