1.设计思想:
1.同步FIFO的写时钟和读时钟为同一个时钟,FIFO内部所有逻辑都是同步逻辑,常常用于交互数据缓冲。
2.典型同步FIFO有三部分组成:(1)FIFO写控制逻辑;(2)FIFO读控制逻辑;(3)FIFO存储实体(如Memory、Reg)。
3.FIFO写控制逻辑主要功能:产生FIFO写地址、写有效信号,同时产生FIFO写满、写错等状态信号;
4.FIFO读控制逻辑主要功能:产生FIFO读地址、读有效信号,同时产生FIFO读空、读错等状态信号。

2.基本概念
- 同步FIFO的“同步”是什么意思?
- FIFO是什么,有什么用?
- 接口都有什么

- 同步:时钟间有确定的倍数关系或确定的相位关系
- FIFO:Frist-in-first-out,先进先出,是一种数据缓存器,实现速率匹配。
- 既然是数据缓冲器,那么缓冲器的大小,存储深度,读写地址和存储器空满状态都需要确定。
一般FIFO使用循环指针(计数溢出自动归零)。一般可以称写指针为头head,读指针为尾tail。初始化时,读写指针指向同一数据地址。
·FIFO:先进先出(First-in-first-out)
·FIFO的深度:同一块数据内存的犬小
·FIFO的宽度
·写指针:Write-pointer
·读指针:Read-pointer
·写数据端口
·读数据端口
·FIFO空、FIFO满
·同步:数据写入FIFO的时钟和数据读出FIFO的时钟是同步的(synchronous)
·异步:数据写入FIFO的时钟和数据读出FIFO的时钟是异步的(asynchronous)
一般FIFO使用循环指针(计数溢出自动归零)。一般可以称写指针为头head,读指针为尾tail。
初始化时,读写指针指向同一数据地址。
下图可见,FIFO初始化时,WP和RP指针指向同一数据单元。
WP指向下一个将要写入的数据单元,RP指向将要读出的数据单元。

初始化时,WP和RP指针指向同一数据单元。WP指向下一个将要写入的数据单元,RP指向将要读出的数据单元,两者是一个追赶过程。可以设置一个计数器,
只写,来一个数据,写一个,写地址,+1,计数器+1,写满为止;
只读,来一个,读出一个数据,读地址+1,计数器-1;
同时读写,计数器值不变,读写地址均+1。+
使用fifo_counter记录FIFORAM中的数据个数:
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)
3.SPEC
(1) Function description
同步FIFO实现了对write/read的控制,其接口解决了接口两端数据速率不匹配的问题。
(2) Feature list
支持存储宽度、深度可配置
时钟工作频率为1MHz
(3) Block diagram

模块主要分为读/写接口、读/写指针、读写指针的比较逻辑和array存储阵列四部分。
- 读/写接口:为模块提供读写数据和读写使能信号;
- 读写指针:主要标志读写指针当前array的地址
- 比较逻辑:
- 使用element counter(elem_cnt)记录FIFO RAM 中的数据个数:▷ 等于0时,给出empty信号;等于BUF_LENGTH时,给出full信号
- elem _ cnt :
- ▷ 写而未满时增加1
- ▷ 读而未空时减1
- ▷ 同时发生读写操作时,elem_cnt不变
(4) Interface description

(5) Timing

分为三部分,写操作,读操作,读写操作。
DUT代码1:
`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,rden;
input[7:0]bufin;//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&&Ibuf_empty)
buf_out<=buf_mem[rd_ptr];
end
always@(posedge clk)begin
if(wr_en&&!buf full)
buf_memlwr_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
TB1:
`define BUF WIDTH 4//地址宽度为3+1,
`define BUF_SIZE(8)//数据个数,FIFO深度
module tb_fifo_counter;
reg clk,rst_n;
reg wr_en,rden;
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;
rstn=1;
push(1);
fork
push(2);
pop(tempdata);
join
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)
Sdisplay("---Cannot Pop:Buffer Empty---");
else begin
rd_en=1;
@(posedge clk);
#3rden=0; data=buf out;
$display ("-----Poped:",data);
end
endtask
endmodule
RTL1
module sync_fifo
#(
parameter DATA_WIDTH = 32,
parameter DATA_DEPTH = 8 ,
parameter PTR_WIDTH = 3
//parameter PTR_WIDTH = $clog2(DATA_DEPTH)
)
(
input wire clk_i ,
input wire rst_n_i ,
//write interface
input wire wr_en_i ,
input wire [DATA_WIDTH-1:0] wr_data_i,
//read interface
input wire rd_en_i ,
output reg [DATA_WIDTH-1:0] rd_data_o,
//Flags_o
output reg full_o ,
output reg empty_o
);
reg [DATA_WIDTH-1:0] regs_array [DATA_DEPTH-1:0];
reg [PTR_WIDTH-1 :0] wr_ptr ;
reg [PTR_WIDTH-1 :0] rd_ptr ;
reg [PTR_WIDTH :0] elem_cnt ;
reg [PTR_WIDTH :0] elem_cnt_nxt ;
//Flags
wire full_comb ;
wire empty_comb ;
/*---------------------------------------------------\
--------------- write poiter addr ----------------
\---------------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
wr_ptr <= 3'b0;
end
else if (wr_en_i && !full_o) begin
wr_ptr <= wr_ptr + 3'b1;
end
end
/*---------------------------------------------------\
-------------- read poiter addr ------------------
\---------------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
rd_ptr <= 3'b0;
end
else if (rd_en_i && !empty_o) begin
rd_ptr <= rd_ptr + 3'b1;
end
end
/*---------------------------------------------------\
--------------- element counter ------------------
\---------------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
elem_cnt <= 4'b0;
end
else if (wr_en_i && rd_en_i && !full_o && !empty_o) begin
elem_cnt <= elem_cnt;
end
else if(wr_en_i && !full_o) begin
elem_cnt <= elem_cnt + 1'b1;
end
else if(rd_en_i && !empty_o) begin
elem_cnt <= elem_cnt - 1'b1;
end
end
/*---------------------------------------------------\
------------- generate the flags -----------------
\---------------------------------------------------*/
always @(*) begin
if(!rst_n_i) begin
elem_cnt_nxt = 1'b0;
end
else if(elem_cnt != 4'd0 && rd_en_i && !empty_o) begin
elem_cnt_nxt = elem_cnt - 1'b1;
end
else if(elem_cnt != 4'd8 && wr_en_i && !full_o) begin
elem_cnt_nxt = elem_cnt + 1'b1;
end
else begin
elem_cnt_nxt = elem_cnt;
end
end
assign full_comb = (elem_cnt_nxt == 4'd8);
assign empty_comb = (elem_cnt_nxt == 4'd0);
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
full_o <= 1'b0;
end
else begin
full_o <= full_comb;
end
end
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
empty_o <= 1'b1;
end
else begin
empty_o <= empty_comb;
end
end
/*---------------------------------------------------\
-------------------- read data -------------------
\---------------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
rd_data_o <= 32'b0;
end
else if(rd_en_i && !empty_o) begin
rd_data_o <= regs_array[rd_ptr];
end
end
/*---------------------------------------------------\
------------------- write data -------------------
\---------------------------------------------------*/
reg [PTR_WIDTH:0] i;
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
for(i=0;i<DATA_DEPTH;i=i+1) begin
regs_array[i] <= 32'b0;
end
end
else if(wr_en_i && !full_o) begin
regs_array[wr_ptr] <= wr_data_i;
end
end
endmoduleTB2:
module tb_sync_fifo;
reg clk_i ;
reg rst_n_i ;
reg wr_en_i ;
reg [31:0] wr_data_i;
reg rd_en_i ;
reg [31:0] rd_data_o;
wire full_o ;
reg empty_o ;
initial begin
rst_n_i = 1 ;
clk_i = 0 ;
rd_en_i = 0 ;
wr_en_i = 0 ;
wr_data_i = 32'b0;
#2 rst_n_i = 0 ;
#5 rst_n_i = 1 ;
end
initial begin
#10 wr_en_i = 1;
rd_en_i = 0;
#10 wr_en_i = 0;
rd_en_i = 1;
#10 wr_en_i = 1;
rd_en_i = 0;
#3 rd_en_i = 1;
#10
repeat(100) begin
#5 wr_en_i = {$random}%2;
rd_en_i = {$random}%2;
end
end
initial #2000 $finish;
always #0.5 clk_i = ~clk_i ;
always #1 wr_data_i = {$random}%10;
sync_fifo u_sync_fifo
(
.clk_i (clk_i ),
.rst_n_i (rst_n_i ),
.wr_en_i (wr_en_i ),
.wr_data_i(wr_data_i),
.rd_en_i (rd_en_i ),
.rd_data_o(rd_data_o),
.full_o (full_o ),
.empty_o (empty_o )
);
initial begin
$fsdbDumpfile("sync_fifo.fsdb");
$fsdbDumpvars ;
$fsdbDumpMDA ;
end
endmodule5.波形分析
写阶段

- 复位之后,进行写操作,直至写满,产生满标志后,不再写入新数据。
- 读阶段

- 进行读操作,直至读空,产生空标志后,不再读出新数据。
- 同时读写阶段

- 先进行写操作,写入三个新数据之后,同时进行读写操作,期间写入新数据和读出数据,但是elem_cnt计数器不再变化,动态平衡。
(2)小结
设计思路:
先分析需求,定义接口,画出具体的实现框图;
按照协议和理解,画出相应时序图;
看图写程序,验证仿真波形是否与时序图对应。
同步FIFO设计要点
是什么时候产生空满标志位,即怎么衡量array被写满或者被读空。
在这里,我使用了4bit的elem_cnt表示,通过elem_cnt的值表示当前array存储阵列的资源使用情况。
0表示没有数据,即空状态;8表示写满,因为array的存储深度就是8。
在spec中提到实现FIFO可配置,在这里只实现了宽度为32bit,深度为8的同步fifo设计,初步验证仿真波形与时序图相对应。

