同步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
文章来源:https://www.toymoban.com/news/detail-650685.html
文章来源地址https://www.toymoban.com/news/detail-650685.html
到了这里,关于Verilog同步FIFO设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!