关键:本文简单说明了FIFO的实现方式,同步FIFO,判断full或者empty方法使用FIFO内容计数器,设置一个变量fifo_cnt记录存储器中数据个数:
//判断空满
assign buf_empty = (fifo_cnt == 0); //buf_empty若是reg类型则错,不能使用assign持续赋值
assign buf_full = (fifo_cnt == `BUF_SIZE);
Implementing a FIFO using Verilog
FIFO uses a dual port memory and there will be two pointers to point read and write addresses. Here is a generalized block diagram of FIFO. 简单来说,FIFO就是一个双口RAM加上两个读写指针。
Generally fifos are implementedusing rotating pointers. We can call write and read pointers of a FIFO as headand tail of data area. Initially read and write pointers of the FIFO will pointto the same location
一般FIFO使用循环指针(计数溢出自动归零)。一般可以称写指针为头head,读指针为尾tail。初始化时,读写指针指向同一数据地址。
Here is an example to explain howFIFO uses the memory. This is a fifo of length 8, WP and RP are the locationswhere write pointer and read pointer points. Shaded area in the diagram isfilled with data.
下图可见,FIFO初始化时,WP和RP指针指向同一数据单元。WP指向下一个将要写入的数据单元,RP指向将要读出的数据单元
When ever FIFO counter becomes zeroor BUF_LENGTH, empty or full flags will beset.
使用fifo_counter记录FIFO RAM中的数据个数,等于0时,给出empty信号,等于BUF_LENGTH时,给出full信号
fifo_counter is incremented ifwrite takes place and buffer is not full and will be decremented id read takesplace and buffer is not empty. If both read and write takes place, counter willremain the same.
fifo_counter写而未满时增加1,读而未空时减1。同时发生读写操作时,fifo_counter不变。
rd_ptr and wr_ptr are read andwrite pointers. Since we selected the bits in these registers same as addresswidth of buffer, when buffer overflows, values will overflow and become 0.
读写指针宽度与地址宽度相当,地址增加而溢出后,自动变成0。循环指针,此处地质变化:0-7-0-7-0。
程序代码:
`define BUF_WIDTH 4 //地址宽度为3+1,
`define BUF_SIZE 8 //数据个数,FIFO深度
module fifo_counter( clk,rst_n,buf_in,buf_out,wr_en,rd_en,buf_empty,buf_full,fifo_cnt);
input clk,rst_n;
input wr_en,rd_en;
input [7:0] buf_in; // data input to be pushed to buffer
output reg [7:0] buf_out; // port to output the data using pop.
output wire buf_empty,buf_full; // buffer empty and full indication
output reg [`BUF_WIDTH-1:0] fifo_cnt; // number of data pushed in to buffer
//写入数据等于8时,满
reg [`BUF_WIDTH-2:0] rd_ptr,wr_ptr; //这个很重要,数据指针3位宽度,0-7索引,8个数据深度,循环指针0-7-0-7
reg [7:0] buf_mem[0:`BUF_SIZE-1];
//判断空满
assign buf_empty = (fifo_cnt == 0); //buf_empty若是reg类型则错,不能使用assign持续赋值
assign buf_full = (fifo_cnt == `BUF_SIZE);
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
fifo_cnt <= 0;
else if((!buf_full&&wr_en)&&(!buf_empty&&rd_en)) //同时读写,数量不变
fifo_cnt <= fifo_cnt;
else if(!buf_full && wr_en) //写数据
fifo_cnt <= fifo_cnt + 1;
else if(!buf_empty && rd_en) //读数据
fifo_cnt <= fifo_cnt-1;
else
fifo_cnt <= fifo_cnt;
end
always @(posedge clk or negedge rst_n) begin //读数据
if(!rst_n)
buf_out <= 0;
if(rd_en && !buf_empty)
buf_out <= buf_mem[rd_ptr];
end
always @(posedge clk) begin
if(wr_en && !buf_full)
buf_mem[wr_ptr] <= buf_in;
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
wr_ptr <= 0;
rd_ptr <= 0;
end
else begin
if(!buf_full && wr_en)
wr_ptr <= wr_ptr + 1;
if(!buf_empty && rd_en)
rd_ptr <= rd_ptr + 1;
end
end
endmodule
使用Modelsim仿真的程序:
`define BUF_WIDTH 4 //地址宽度为3+1,
`define BUF_SIZE (8) //数据个数,FIFO深度
module tb_fifo_counter;
reg clk,rst_n;
reg wr_en,rd_en;
reg [7:0] buf_in; // data input to be pushed to buffer
wire [7:0] buf_out; // port to output the data using pop.
wire buf_empty,buf_full; // buffer empty and full indication
wire [`BUF_WIDTH-1:0] fifo_cnt; // number of data pushed in to buffer
fifo_counter dut(clk,rst_n,buf_in,buf_out,wr_en,rd_en,buf_empty,buf_full,fifo_cnt);
always #10 clk = ~clk;
reg [7:0] tempdata = 0;
initial begin
clk = 0;
rst_n = 0;
wr_en = 0;
rd_en = 0;
buf_in = 0;
#15;
rst_n = 1;
push(1);
fork
push(2);
pop(tempdata);
join //push and pop together
push(10);
push(20);
push(30);
push(40);
push(50);
push(60);
push(70);
push(80);
push(90);
push(100);
push(110);
push(120);
push(130);
pop(tempdata);
push(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
push(140);
pop(tempdata);
push(tempdata);//
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
push(5);
pop(tempdata);
end
task push (input [7:0] data);
if(buf_full)
$display("---Cannot push %d: Buffer Full---",data);
else begin
$display("Push",,data);
buf_in = data;
wr_en = 1;
@(posedge clk);
#5 wr_en = 0;
end
endtask
task pop(output[7:0] data);
if(buf_empty)
$display("---Cannot Pop: Buffer Empty---");
else begin
rd_en = 1;
@(posedge clk);
#3 rd_en = 0;
data = buf_out;
$display("------Poped:",,data);
end
endtask
endmodule
程序仿真打印结果:
# Push 1
# Push 2
# ------Poped: 1
# Push 10
# Push 20
# Push 30
# Push 40
# Push 50
# Push 60
# Push 70
# ---Cannot push 80: Buffer Full---
# ---Cannot push 90: Buffer Full---
# ---Cannot push 100: Buffer Full---
# ---Cannot push 110: Buffer Full---
# ---Cannot push 120: Buffer Full---
# ---Cannot push 130: Buffer Full---
# ------Poped: 2
# Push 2
# ------Poped: 10
# ------Poped: 20
# ------Poped: 30
# ------Poped: 40
# Push 140
# ------Poped: 50
# Push 50
# ------Poped: 60
# ------Poped: 70
# ------Poped: 2
# ------Poped: 140
# ------Poped: 50
# ---Cannot Pop: Buffer Empty---
# ---Cannot Pop: Buffer Empty---
# ---Cannot Pop: Buffer Empty---
# ---Cannot Pop: Buffer Empty---
# ---Cannot Pop: Buffer Empty---
# ---Cannot Pop: Buffer Empty---
# Push 5
# ------Poped: 5
程序仿真图: