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
波形
