1. 同步FIFO的写时钟和读时钟为同一个时钟,FIFO内部所有逻辑都是同步逻辑,常常用于交互数据缓冲。
2. 典型同步FIFO有三部分组成: (1) FIFO写控制逻辑; (2)FIFO读控制逻辑;(3)FIFO 存储实体(如Memory、Reg)。
3. FIFO写控制逻辑主要功能:产生FIFO写地址、写有效信号,同时产生FIFO写满、写错等状态信号;
4. FIFO读控制逻辑主要功能:产生FIFO读地址、读有效信号,同时产生FIFO读空、读错等状态信号。
FIFO基本概念:
• FIFO:先进先出(First-in-first-out)
• FIFO的深度(如下), 同一块数据内存的大小
• FIFO的宽度
• 写指针:Write-pointer
• 读指针:Read-pointer
• 写数据端口
• 读数据端口
• FIFO空、FIFO满
• 同步FIFO/异步FIFO
• 同步:数据写入FIFO的时钟和数据读出FIFO的时钟是同步的(synchronous)• 异步:数据写入FIFO的时钟和数据读出FIFO的时钟是异步的(asynchronous)
一般FIFO使用循环指针(计数溢出自动归零)。一般可以称写指针为头head,读指针为尾tail。初始化时,读写指针指向同一数据地址。
下图可见,FIFO初始化时,WP和RP指针指向同一数据单元。WP指向下一个将要写入的数据单元,RP指向将要读出的数据单元
使用fifo_counter记录FIFO RAM中的数据个数:
1. 等于0时,给出empty信号
2. 等于BUF_LENGTH时,给出full信号fifo_counter:
1. 写而未满时增加1
2. 读而未空时减1
3. 同时发生读写操作时,fifo_counter不变读写指针宽度
1. 与地址宽度相当
2. 地址增加而溢出后,自动变成0
3. 循环指针(此处地址变化:0-7-0-7-0)
代码实现
RTL
`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时,FIFO为满
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);
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))//同时读写,counter不变
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;
else 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
rd_ptr<=0;
wr_ptr<=0;
end
else begin
if(!buf_empty&&rd_en)
rd_ptr<=rd_ptr+1;
if(!buf_full&&wr_en)
wr_ptr<=wr_ptr+1;
end
end
endmodule
TB:
//fifi.v TB
`define BUF_WINTH 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 reg [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
//当写入数据个数为8时,FIFO为满
fifo_counter init(clk,rst_n,buf_in,buf_out,wr_en,rd_en,buf_empty,buf_full,fifo_cnt);
always begin
#10 clk=~clk;
end
reg[7:0] tempdata;
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(10);
push(20);
push(30);
push(40);
push(50);
push(60);
pop(tempdata);
push(70);
push(80);
push(90);
push(100);
push(110);
pop(tempdata);
push(120);
push(130);
push(140);
push(150);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
push(160);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
push(170);
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 %d:buffer empty", data);
else begin
rd_en=1;
@(posedge clk);
#3 rd_en=0;
data=buf_out;
$display("poped",,data);
end
endtask
endmodule
波形