一、同步FIFO
对于同步FIFO,需要注意读写地址的累加;读空和写满信号的产生
代码主要结构:
- 写地址产生
- 读地址产生
- 写入数据
- 读出数据
- 写满信号
- 读空信号
//Description:sync fifo
module sync_fifo
#(
parameter DATA_WIDTH=8, //数据位宽
parameter ADDR_WIDTH=4 //FIFO深度
)
(
input clk, //时钟
input rst_n,
input [DATA_WIDTH-1:0] wr_data, //待写入数据
input wr_en, //写使能
input rd_en, //读使能
output reg [DATA_WIDTH-1:0] rdata, //读出数据
output reg full, //写满信号
output reg empty //读空信号
);
reg [ADDR_WIDTH-1:0] wr_addr; //写地址
reg [ADDR_WIDTH-1:0] rd_addr; //读地址
reg [DATA_WIDTH-1:0] ram_data[2**ADDR_WIDTH-1:0]; //定义一个ram
//写地址
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
wr_addr<=0;
end
else if(wr_en&~full) //写使能信号来临且没有写满时地址自加
begin
wr_addr<=wr_addr+1'b1;
end
else
begin
wr_addr<=wr_addr;
end
//读地址
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
rd_addr<=0;
end
else if(rd_en&~empty) //读使能信号来临且没有读空时地址自加
begin
rd_addr<=rd_addr+1'b1;
end
else
begin
rd_addr<=rd_addr;
end
//写入数据
always@(posedge clk)
if(wr_en&~full)
ram_data[wr_addr]<=wr_data;
//读出数据
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
rdata<=0;
end
else if(rd_en&~empty)
begin
rdata<=ram_data[rd_addr];
end
else
begin
rdata<=rdata;
end
//判断写满
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
full<=1'b0;
end
//((写地址比读地址少1)或者(写地址为最大、读地址为0)(这两个条件不能合并,否则写地址最大、读地址为0时无法判断为写满)),且写使能来临,读使能为低;
else if(((wr_addr==rd_addr-1)|(wr_addr==2**ADDR_WIDTH-1&&rd_addr==0))&wr_en&~rd_en)
begin
full<=1'b1;
end
else if(full&rd_en) //写满后来一个读信号写满信号拉低
begin
full<=1'b0;
end
//判断读空
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
empty<=1'b0;
end
else if(((rd_addr+1==wr_addr)|(rd_addr==2**ADDR_WIDTH-1&&wr_addr==0))&rd_en&~wr_en)
begin
empty<=1'b1;
end
else if(empty&wr_en)
begin
empty<=1'b0;
end
endmodule
二、异步FIFO
- 异步FIFO存在两个不同的时钟域,因此读写地址不能直接比较,需要转换为格雷码后再进行比较(由于读写地址的增加是连续的,因此可以将其转换成格雷码,这样可以保证在每个source clock的沿只会有一个bit发生翻转