同步FIFO(synchronous)的写时钟和读时钟为同一个时钟,FIFO内部所有逻辑都是同步逻辑,常常用于交互数据缓冲。
异步FIFO:数据写入FIFO的时钟和数据读出FIFO的时钟是异步的(asynchronous)
典型同步FIFO有三部分组成:
(1) FIFO写控制逻辑;
(2)FIFO读控制逻辑;
(3)FIFO 存储实体(如Memory、Reg)。
FIFO写控制逻辑主要功能:产生FIFO写地址、写有效信号,同时产生FIFO写 满、写错等状态信号;
FIFO读控制逻辑主要功能:产生FIFO读地址、读有效信号,同时产生FIFO读 空、读错等状态信号。
基本概念
FIFO:先进先出(First-in-first-out) FIFO的深度 同一块数据内存的大小
FIFO的宽度:写指针:Write-pointer
读指针:Read-pointer
一般FIFO使用循环指针(计数溢出自动归零)。一般可以称写指针为头head,读指针为尾tail。 初始化时,读写指针指向同一数据地址。下图可见,FIFO初始化时,WP和RP指针指向同一数据单元。WP指向下一个将要写入的数据单元,RP指向将要读出的数据单元
2种方法判断空满:
counter计数器:判断有效数据是否等于FIFO的深度,为0就表示空
使用fifo_counter记录FIFO RAM中的数据个数,等于0时,给出empty信号,等于BUF_LENGTH时,给出full信号。
写而未满时增加1 读而未空时减1 同时发生读写操作时,fifo_counter不变
pointer:如果深度为8,那么3bit就可以表示8个数,但是为了判断空满,会多定义一位,也即4bit,WP为1000,RP为0000,我们使用最高位去判断是否在同一单元,用高位判断空满,如果高位相异,就表示满,如果相同表示空。
程序代码
`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; // 写数据
output reg [7:0] buf_out; // 读数据
output wire buf_empty,buf_full; // 空满两个状态信号
output reg [`BUF_WIDTH-1:0] fifo_cnt; //判断空满计数器
// 读写指针:数据指针3位宽度,0-7索引,8个数据深度,循环指针0-7-0-7
reg [`BUF_WIDTH-2:0] rd_ptr,wr_ptr;
// 读写容器
reg [7:0] buf_mem[0:`BUF_SIZE-1];
//判断空满 方式1
assign buf_empty = (fifo_cnt == 0); //buf_empty若是reg类型则错,不能使用assign持续赋值
assign buf_full = (fifo_cnt == `BUF_SIZE);// fifo_cnt = 8就是满的
//判断空满 方式2
assign buf_empty = (rd_ptr[3] == wr_ptr[3])&&(rd_ptr[2:0] == wr_ptr[2:0]);
assign buf_full = (rd_ptr[3] != wr_ptr[3])&&(rd_ptr[2:0] == wr_ptr[2:0]); // 前后必须同时为1
//读数据
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
// 满足写的条件,就把写指针+1
if(!buf_full && wr_en)
wr_ptr <= wr_ptr + 1;
// 满足读的条件,就把读指针+1
if(!buf_empty && rd_en)
rd_ptr <= rd_ptr + 1;
end
end
// 监控fifo_cnt
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) // 写数据:写而未满增加1
fifo_cnt <= fifo_cnt + 1;
else if(!buf_empty && rd_en) // 读数据:读而未空减1
fifo_cnt <= fifo_cnt-1;
else
fifo_cnt <= fifo_cnt; // 维持
end
endmodule
TestBench
`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(clk),
.rst_n(rst_n),
.buf_in(buf_in),
.buf_out(buf_out),
.wr_en(wr_en),
.rd_en(rd_en),
.buf_empty(buf_empty),
.buf_full(buf_full),
.fifo_cnt(fifo_cnt));
fifo_counter dut(
.clk (clk),
.rst_n (rst_n),
.buf_in (buf_in),
.buf_out (buf_out),
.wr_en (wr_en),
.rd_en (rd_en),
.buf_empty (buf_empty),
.buf_full (buf_full),
.fifo_cnt (fifo_cnt));
always #10 clk = ~clk;
// 定义一个临时的数据,将读出来的数据暂存
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); // 读取tempdata = 1
join
push(10);
push(20);
push(30);
push(40);
push(50);
push(60);
push(70);
// 70push 就会满
push(80);
push(90);
push(100);
push(110);
push(120);
push(130);
pop(tempdata); // 读取tempdata = 2
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);// 读取tempdata = 5
#50 $finish;
end
// 将data写入fifo
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
// 将data读取出来
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
find -name "*.v" > file.list
makefile文件:
all:clean com sim
SEED=1
com:
vcs -full64 -R -sverilog -debug_all -f file.list -l comp.log +ntb_random_seed=$(SEED) \
-cm line+cond+fsm+branch+tgl -cm_name simv -cm_dir ./covdir.vdb
sim:
./simv -l sim.log
rung:
./simv -gui -l sim.log
cov:
dve -full64 -covdir *.vdb &
clean:
rm -rf ./csrc *.daidir *.log *.vpd *.vdb simv* *.key *race.out*
rm -rf AN.DB
rm -rf novas*
rm -rf DVEfiles
rm -rf urgReport
VCS Coverage Metrics Release O-2018.09-1_Full64 Copyright (c) 1991-2018 by Synopsys Inc.
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
查看波形:make rung