同步FIFO(First in first out),先入先出。
Deign Spec:
- 输入信号:clk;
- 输入信号:异步复位信号:rstn;
- 输入信号:写使能:wr_en;
- 输入信号:写数据:wr_data;//时序逻辑,先判断写使能 wr_en 是否有效,有效情况下如果没有写满,则写入数据,如果满了,判断读使能 rd_en ,如果读使能有效能可以继续写数据。
- 输入信号:读使能:rd_en;
- 输出信号:读数据:rd_data;//时序逻辑,先判断读使能 rd_en 是否有效,有效情况下如果内存非空,则取上一个周期指针位置的数据作为读数据,如果内存为空,则判断写使能,如果写使能有效则直接将写数据 wr_data 作为读数据输出,无效时输出0。
- 输出信号:内存满信号:full;//如果读写指针最高位不同但是后几位均相同则为1
- 输出信号:内存空信号:empty;//如果读写指针完全相同则为1
- 读指针:rd_ptr;//时序逻辑,先判断写使能 rd_en 是否有效,有效情况如果内存非空则+1,如果内存空了,判断 wr_en 是否有效,如果有效则+1,否则不变。指针大小比内存深度多一位。
- 写指针:wr_ptr;//时序逻辑,先判断写使能 wr_en 是否有效,有效情况如果内存非满则+1,如果内存满了,判断 rd_en 是否有效,如果有效则+1,否则不变。指针大小比内存深度多一位。
- 内存:mem;//内存深度要为 2^n 大小,即指针可以通过+1自动循环。
RTL:
module sync_fifo#(
parameter DEPTH = 16, //DEPTH = 2^n
parameter WIDTH = 8
)(
input clk,
input rstn,
//write
input wr_en, //write enable
input [WIDTH-1:0] wr_data, //write data
output full,
//read
input rd_en, //read enable
output reg [WIDTH-1:0] rd_data, //read data
output empty
//output reg [$clog2(DEPTH):0] cnt
);
localparam ADDR = $clog2(DEPTH);
reg [$clog2(DEPTH):0] rd_ptr, wr_ptr;
wire [$clog2(DEPTH)-1:0] rd_addr, wr_addr;
reg [WIDTH-1:0] mem [0:DEPTH-1];
assign rd_addr = rd_ptr[ADDR-1:0];
assign wr_addr = wr_ptr[ADDR-1:0];
//********** read start **********//
always@(posedge clk, negedge rstn) begin
if(~rstn) begin
rd_ptr <= 0;
end
else if(rd_en && ~empty) begin
rd_ptr <= rd_ptr + 1'b1;
end
else if(rd_en && empty && wr_en) begin
rd_ptr <= rd_ptr + 1'b1;
end
end
always@(posedge clk, negedge rstn) begin
if(~rstn) begin
rd_data <= 0;
end
else if(rd_en && ~empty) begin
rd_data <= mem[rd_addr];
end
else if(rd_en && empty && wr_en) begin
rd_data <= wr_data;
end
else begin
rd_data <= 0;
end
end
//********** read end **********//
//********** write start **********//
always@(posedge clk, negedge rstn) begin
if(~rstn) begin
wr_ptr <= 0;
end
else if(wr_en && ~full) begin
wr_ptr <= wr_ptr + 1'b1;
end
else if(wr_en && full && rd_en) begin
wr_ptr <= wr_ptr + 1'b1;
end
end
always@(posedge clk) begin
if(wr_en && ~full) begin
mem[wr_addr] <= wr_data;
end
else if(wr_en && full && rd_en) begin
mem[wr_addr] <= wr_data;
end
end
//********** write end **********//
assign empty = (rd_ptr[ADDR:0] == wr_ptr[ADDR:0]);
assign full = (rd_ptr[ADDR] != wr_ptr[ADDR]) ? ((rd_ptr[ADDR-1:0] == wr_ptr[ADDR-1:0])?1'b1:1'b0) : 1'b0;
endmodule
TB:
module test#(
parameter DEPTH = 16,
parameter WIDTH = 8
);
reg clk;
reg rstn;
reg wr_en;
reg [WIDTH-1:0] wr_data;
wire full;
reg rd_en;
wire [WIDTH-1:0] rd_data;
wire empty;
//wire [$clog2(DEPTH):0] cnt;
sync_fifo u0(
.clk(clk),
.rstn(rstn),
.wr_en(wr_en),
.wr_data(wr_data),
.full(full),
.rd_en(rd_en),
.rd_data(rd_data),
.empty(empty)
//.cnt(cnt)
);
initial begin
clk = 0;
rstn = 0;
wr_en = 0;
wr_data = 0;
rd_en = 0;
#30 rstn = 1;
end
always #10 clk = ~clk;
always@(posedge clk) begin
if(({$random()}%16)<10) wr_en <= 1;
else wr_en <= 0;
if(({$random()}%16)<7) rd_en <= 1;
else rd_en <= 0;
end
always@(posedge clk) begin
wr_data <= {$random()} & 8'hff;
end
initial begin
#4000 $finish;
end
endmodule
波形图: