电子说
FIFO ,First In First Out,先入先出队列,顾名思义,即第一个到达的数据也将会是第一个离开。由于同步FIFO的操作速度非常快,并且能 降低系统的复杂性 ,因此在很多高性能系统中是非常理想的选择。且同步FIFO相比异步FIFO来说实现起来更简单。所以在实际项目中用得相对较多。
UART项目中也使用了同步FIFO进行数据的缓存,本文主要对此进行讲解。
同步fifo架构,取自《硬件架构的艺术》
根据系统时钟和响应速度,需要确定 FIFO深度 。本设计中深度设置为15,数据宽度8bit。同步FIFO设计的关键在于空满信号的产生。
设计中rptr为 读指针 ,指向下一个要读的地址;wptr为 写指针 ,同样指向下一个要写的地址。有效的读写使能使读写指针递增。
wfull为 写满信号 ,表示FIFO空间已经写满,不能再写入数据;rempty为 读空信号 ,表示FIFO内没有可供读写的有效数据。空满信号的产生是根据读写指针(读写地址)产生的。
此FIFO模块中有两个复位,一个是系统复位rst_,一个是FIFO复位fifo_rst。
系统复位为是整系统复位信号,该系统中所有寄存器会在此复位信号有效时有一个初始值,避免不定态的产生。
FIFO复位信号是同步FIFO的复位信号,只对此模块有效,该信号有效时读写指针会归0。
满足FIFO复位单独可控的设计要求。
当FIFO复位信号fifo_rst有效时,读写指针会归零,这时rempty信号会拉起,表示FIFO为空状态,此时往fifo中写数据;当fifo中没有空间可以写时,写地址是ram的深度即15,写指针指向下一个写地址会回到0,此时fifo为满状态,wfull信号拉起。
空满产生
可以发现,在读写指针相等时,FIFO要么空要么满。那么我们怎么对空满状态进行区分呢?
FIFO深度为15,正常地址应该为4bit[3:0],为了区分空满状态,我们将指针设置为5bit[4:0]。
根据上述的空满状态产生原理,可以发现:
1) 当FIFO为空时,读写指针完全相等;
2) 当FIFO为满时,读写指针的最高位是相反的,而低4位一定相等。
空满信号产生
由于设计要求FIFO数据量需要可查询,所以增加一个fifo_cnt,它的值为写指针与读指针的差值。表示FIFO中剩余的数据量,作为输出传递到寄存器配置模块供系统查询。
最后附上本项目中所用到的同步FIFO代码,可将FIFO数据位宽和深度参数化,减少改动方便重复调用。另外要养成良好的代码习惯,多加注释。
同步FIFO Verilog代码:
1`timescale 1ns/1ps
2
3module UART_FIFO(
4 //inputs
5 clk,
6 rst_,
7 fifo_rst,
8 rinc,
9 winc,
10 data_i,
11 //outputs
12 data_o,
13 wfull,
14 rempty,
15 fifo_cnt
16);
17
18input clk; // ARM clock
19input rst_; // ARM reset
20input fifo_rst; // FIFO reset control signal.high active
21input rinc; // FIFO read enable signal
22input winc; // FIFO write enable signal
23input [7:0] data_i; // in data line
24
25output wfull; // write full signal
26output rempty; // read empty signal
27output [7:0] data_o; // FIFO out data
28output [4:0] fifo_cnt; // FIFO statu register
29
30reg [7:0] data_o;
31reg [4:0] fifo_cnt;
32reg [4:0] wptr; // write pointer
33reg [4:0] rptr; // read pointer
34reg [7:0] ram[15:0]; // ram in FIFO
35
36// write data in ram
37always@(posedge clk or negedge rst_) begin
38 if(!rst_) begin
39 data_o <= 8'd0;
40 rptr <= 5'd0;
41 end
42 else begin
43 if(fifo_rst) begin
44 rptr <= 5'd0;
45 end
46 else begin
47 if(rinc && !rempty) begin
48 data_o <= ram[rptr[3:0]];
49 rptr <= rptr + 1'b1;
50 end
51 end
52 end
53end
54
55// read data from ram
56always@(posedge clk or negedge rst_) begin
57 if(!rst_) begin
58 wptr <= 5'd0;
59 end
60 else begin
61 if(fifo_rst) begin
62 wptr <= 5'd0;
63 end