同步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;//如果计数器 cnt 等于内存深度,则 full 为1
- 输出信号:内存空信号:empty;//如果计数器 cnt 为0,则 empty 为
- 读指针:rd_ptr;//时序逻辑,先判断写使能 rd_en 是否有效,有效情况如果内存非空则+1,如果内存空了,判断 wr_en 是否有效,如果有效则+1,否则不变。
- 写指针:wr_ptr;//时序逻辑,先判断写使能 wr_en 是否有效,有效情况如果内存非满则+1,如果内存满了,判断 rd_en 是否有效,如果有效则+1,否则不变。
- 内存:mem;//内存深度要为 2^n 大小,即指针可以通过+1自动循环。
- 计数器:cnt;//如果 wr_en 和 rd_en 同时有效,则无论内存空满,cnt不变。如果单纯是 wr_en 有效且非满的情况下,cnt+1,如果单纯是 rd_en 有效且非空的情况下 cnt-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
);
reg [$clog2(DEPTH)-1:0] rd_ptr, wr_ptr;
reg [WIDTH-1:0] mem [0:DEPTH-1];
//********** 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_ptr];
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_ptr] <= wr_data;
end
else if(wr_en && full && rd_en) begin
mem[wr_ptr] <= wr_data;
end
end
//********** write end **********//
//********** cnt **********//
always@(posedge clk, negedge rstn) begin
if(~rstn) begin
cnt <= 0;
end
else if(wr_en && rd_en) begin
cnt <= cnt;
end
else if(wr_en && ~full) begin
cnt <= cnt + 1'b1;
end
else if(rd_en && ~empty) begin
cnt <= cnt - 1'b1;
end
end
assign full = (cnt == DEPTH);
assign empty = (cnt == 0);
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
波形: