本文实现了异步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