用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

 

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