本文實現了異步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