用verilog实现异步fifo

本文实现了异步FIFO

代码思路参考了博客 https://blog.csdn.net/u014070258/article/details/90052281 

之后可能会学习一下怎么利用状态机实现FIFO

`define FIFO_DEPTH 108//FIFO深度
`define FIFO_WIDTH 16//FIFO宽度

module asynfifo(
input rst_n,//复位信号
input [`FIFO_WIDTH - 1:0] data_in,//输入信号 

input wr_en,//写使能
input wr_clk,//写时钟
output reg wr_full,//写满信号

input re_en,//读使能
input re_clk,//读时钟
output reg re_empty,//读空信号

output reg [`FIFO_WIDTH - 1:0] data_out//输出信号
);

wire wr_full_temp;//内部写满状态记录
wire re_empty_temp;//内部读空状态记录

reg [`FIFO_WIDTH-1:0] ram[`FIFO_DEPTH-1:0];//ram

reg [`FIFO_DEPTH:0] wr_addr;//2进制写地址
reg [`FIFO_DEPTH:0] re_addr;//2进制读地址

wire [`FIFO_DEPTH:0] grey_wr_addr;//格雷码写地址
wire [`FIFO_DEPTH:0] grey_re_addr;//格雷码读地址

//不直接使用grey_wr_addr和grey_re_addr来判断写满和读空状态,而用延迟了两拍的信号来判断
reg [`FIFO_DEPTH:0] re_addr_temp1,re_addr_temp2;
reg [`FIFO_DEPTH:0] wr_addr_temp1,wr_addr_temp2;

assign grey_wr_addr = (wr_addr>>1)^wr_addr;//2进制转换为格雷码
assign grey_re_addr = (re_addr>>1)^re_addr;//2进制转换为格雷码

//建议自己在纸上推一遍写满的条件判断(要保证格雷码的前两位相反,后面所有的位数相同,举个例子,假如深度是16bit,就是拿格雷码的‘0’和‘8’比较、‘1和9’比较,以此类推)
assign wr_full_temp = (grey_wr_addr == {~re_addr_temp2[`FIFO_DEPTH:`FIFO_DEPTH-1],re_addr_temp2[`FIFO_DEPTH-2:0]});
assign re_empty_temp = (grey_re_addr == wr_addr_temp2);

//读时钟域,更改读空信号
always@(posedge re_clk, negedge rst_n)
begin
	if(!rst_n)re_empty <= 1;
	else re_empty <= re_empty_temp;
end

//写时钟域,更改写满信号
always@(posedge wr_clk, negedge rst_n)
begin
	if(!rst_n)wr_full <= 0;
	else wr_full <= wr_full_temp;
end

//读时钟域,读取数据,更改读地址
always@(posedge re_clk, negedge rst_n)
begin
	if(!rst_n)re_addr <= 0;
	else
	begin
		if(re_en && (!re_empty_temp))
		begin
			data_out <= ram[re_addr];
			re_addr <= re_addr + 1;
		end
		
		else re_addr <= re_addr;//do nothing
	end
end

//写时钟域,写数据,更改写地址
always@(posedge wr_clk, negedge rst_n)
begin
	if(!rst_n)wr_addr <= 0;
	else
	begin
		if(wr_en && (!wr_full_temp))
		begin
			ram[wr_addr] <= data_in;
			wr_addr <= wr_addr + 1;
		end		
		else wr_addr <= wr_addr;//do nothing
	end
end


//读时钟域,同步写时钟域的格雷码数据地址
always@(posedge re_clk, negedge rst_n)
begin
	if(!rst_n)
	begin
		wr_addr_temp1 <= 0;
		wr_addr_temp2 <= 0;
	end
	else 
	begin
		wr_addr_temp1 <= grey_wr_addr;
		wr_addr_temp2 <= wr_addr_temp1;
	end
end

//写时钟域,同步读时钟域的格雷码数据地址
always@(posedge wr_clk, negedge rst_n)
begin
	if(!rst_n)
	begin
		re_addr_temp1 <= 0;
		re_addr_temp2 <= 0;
	end
	else 
	begin
		re_addr_temp1 <= grey_re_addr;
		re_addr_temp2 <= re_addr_temp1;
	end
end

endmodule

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章