参考以下帖子:
https://blog.csdn.net/hengzo/article/details/49683707
https://blog.csdn.net/Times_poem/article/details/51917648
https://www.cnblogs.com/aslmer/p/6114216.html
https://www.cnblogs.com/ylsm-kb/p/9068449.html
https://blog.csdn.net/Pieces_thinking/article/details/78026326
1.定义
FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,他与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据, 其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。
FIFO一般用于不同时钟域之间的数据传输,比如FIFO的一端是AD数据采集, 另一端是计算机的PCI总线,假设其AD采集的速率为16位 100K SPS,那么每秒的数据量为100K×16bit=1.6Mbps,而PCI总线的速度为33MHz,总线宽度32bit,其最大传输速率为 1056Mbps,在两个不同的时钟域间就可以采用FIFO来作为数据缓冲。另外对于不同宽度的数据接口也可以用FIFO,例如单片机位8位数据输出,而 DSP可能是16位数据输入,在单片机与DSP连接时就可以使用FIFO来达到数据匹配的目的。
FIFO的分类根据FIFO工作的时钟域,可以将FIFO分为同步FIFO和异步FIFO。同步FIFO是指读时钟和写时钟为同一个时钟。在时钟沿来临时同时发生读写操作。异步FIFO是指读写时钟不一致,读写时钟是互相独立的。
FIFO设计的难点在于怎样判断FIFO的空/满状态。为了保证数据正确的写入或读出,而不发生溢出或读空的状态出现,必须保证FIFO在满的情况下,不能进行写操作。在空的状态下不能进行读操作。怎样判断FIFO的满/空就成了FIFO设计的核心问题。
2.同步FIFO之Verilog实现
同步FIFO的意思是说FIFO的读写时钟频率相同,不同于异步FIFO,异步FIFO的读写时钟频率是不同的。同步FIFO的对外接口包括时钟,清零,读请求,写请求,数据输入总线,数据输出总线,空以及满信号。下面分别对同步FIFO的对外接口信号作一描述:
1. 时钟,输入,用于同步FIFO的读和写,上升沿有效;
2. 清零,输入,异步清零信号,低电平有效,该信号有效时,FIFO被清空;
3. 写请求,输入,高电平有效,该信号有效时,表明外部电路请求向FIFO写入数据;
4. 读请求,输入,高电平有效,该信号有效时,表明外部电路请求从FIFO中读取数据;
5. 数据输入总线,输入,当写信号有效时,数据输入总线上的数据被写入到FIFO中;
6. 数据输出总线,输出,当读信号有效时,数据从FIFO中被读出并放到数据输出总线上;
7. 空,输出,高电平有效,当该信号有效时,表明FIFO中没有任何数据,全部为空;
8. 满,输出,高电平有效,当该信号有效时,表明FIFO已经满了,没有空间可用来存贮数据。
下面的框图主要描述同步FIFO的内部结构 :
verilog 代码和波形文件如下:
fifo_sync.v
/*use a extra counter to calcuate current fifo filled number its bit is fifo width + 1, such as fifo depth is 8, then it is 0,...,7, we use 4 bits counter, if counter = 8, then express full, counter is 0, then it is empty */ module fifo_sync #( parameter FIFO_WIDTH = 32, //every fifo unit 's bit number, default is 32bits, a dword parameter ADDR_WIDTH = 3, //2^3=FIFO_DEPTH, so addr with is 3 bits if depth=8 parameter FIFO_DEPTH = 8 //fifo depth, first in, first out. ) ( input clk, input rst_n, input [FIFO_WIDTH-1:0] wr_data, input rq, //read request input wq, //write request output reg [FIFO_WIDTH-1:0] rd_data, output full, output empty ); //internal signal reg[FIFO_WIDTH-1:0] fifo_mem[FIFO_DEPTH-1:0]; reg[ADDR_WIDTH:0] counter; //extra one bit for counter reg[ADDR_WIDTH-1:0] rd_ptr; reg[ADDR_WIDTH-1:0] wr_ptr; //set full and empty assign full=(counter==FIFO_DEPTH); assign empty=(counter==0); //set current fifo counter value always @(posedge clk or negedge rst_n) begin if(!rst_n) counter<=0; else if((wq && !full)&&(rq && !empty)) // read and write meanwhile,counter keep no change counter <= counter; else if(rq&&!empty) counter <= counter - 1; else if(wq&&!full) counter <= counter + 1; else counter <= counter; //no read, no write, keep no change end //read data if no empty and read enable always @(posedge clk or negedge rst_n ) begin if(!rst_n) begin rd_data <= 0; end if(rq && !empty) rd_data <= fifo_mem[rd_ptr]; end //write data if no full and write enable always @(posedge clk) begin if(wq && !full) fifo_mem[wr_ptr] <= wr_data; end //update read and write ptr always @(posedge clk or negedge rst_n) begin if(!rst_n) begin wr_ptr <= 0; rd_ptr <= 0; end else begin if(!full && wq) begin wr_ptr <= wr_ptr + 1; // we can omit these two lines, for it will change to // 0 if overflow. //if(wr_ptr==(FIFO_DEPTH-1)) // wr_ptr<=0; end if(!empty && rq) begin rd_ptr <= rd_ptr + 1; //if(rd_ptr==(FIFO_DEPTH-1)) // rd_ptr<=0; end end end endmodule