作者:李西锐 校对:陆辉
大侠好,欢迎来到FPGA技术江湖。本系列将带来FPGA的系统性学习,从最基本的数字电路基础开始,最详细操作步骤,最直白的言语描述,手把手的“傻瓜式”讲解,让电子、信息、通信类专业学生、初入职场小白及打算进阶提升的职业开发者都可以有系统性学习的机会。
系统性的掌握技术开发以及相关要求,对个人就业以及职业发展都有着潜在的帮助,希望对大家有所帮助。本次带来Vivado系列,FIFO使用教程。话不多说,上货。
FIFO使用教程
FIFO的英文全称叫做First in First out,即先进先出。这也就决定了这个IP核的特殊性,先写进去的数据优先被读出,所以,FIFO是不需要地址信号线的,这也是它的一大特点,通常用来做数据的缓存,或者用来解决高速异步数据的交互,即解决了跨时钟域的问题。此外,FIFO还有一个特点,就是数据被读出之后就不存在了,不像RAM和ROM一样,数据被读出后还存在。所以我们如果想进行多次的读,那么就需要进行同样次数的写。
FIFO分为同步时钟和异步时钟,同步FIFO指的是读写使用同一个时钟,在时钟沿信号来的时候进行读写。异步FIFO是指读写在不同时钟下进行,这样我们可以实现读写不同速度。
那么接下来,我们就来实现一下异步FIFO的读写过程。
上图为选择异步FIFO之后的图示,在这个图示中,我们给大家解释一下每个信号的含义。
FIFO_WRITE:
full:FIFO的满信号,当FIFO的存储空间写满了之后,此信号拉高,否则为低。此信号为FIFO的输出信号。
din[7:0]:FIFO的数据输入,写进FIFO的数据通过此信号线进入FIFO。
wr_en:FIFO的写使能,当我们要往FIFO里面写入数据时,拉高此信号。此信号为FIFO的输入。
FIFO_READ:
empty:FIFO的空信号,当FIFO的存储空间空了之后,此信号拉高,否则为低。此信号为FIFO的输出信号。
dout:FIFO的数据输出,读出FIFO的数据通过此信号线输出
rd_en:FIFO的读使能,当我们要从FIFO里面读出数据时,拉高此信号。此信号为FIFO的输出。
rst:FIFO复位,默认高电平有效。
wr_clk:写时钟
rd_clk: 读时钟
wr_rst_busy:写复位忙信号
rd_rst_busy:读复位忙信号
在了解了FIFO的端口之后,我们来实现一个应用实例。比如,我们以10MHz的速度往FIFO里面写数据,写满之后,在20MHz的时钟下将数据读出,一直读空。当然,在显示应用中,FIFO的读写是可以同步进行的。
首先,我们先来新建工程。
新建好之后,我们先调用一下IP核:
在IP核管理器界面,搜索FIFO,然后选中图示所选项双击打开。
在FIFO类型选项,我们选择异步FIFO。刚打开默认的选项为同步FIFO。
在数据端口配置界面,我们将数据位宽改为8bit,深度使用1024。
复位端口在这就不再使用了,所以勾选位置取消掉。
在此界面出现了almost full flag和almost empty flag。这两个信号是几乎满或空的标志信号,在此实验中,我们不使用。
Data count是FIFO数据用量计数器,代表了此时FIFO的内部存储被使用的情况。假设我们写进去了10个数,那么两个计数器都为10。
此界面为IP核的信息,在此界面可以看出,我们的读写深度发生了变化,我们在前面设置的深度为1024,但是在此处显示的却是1023。原因是因为FIFO结构的特殊性,并不是我们设置的有问题。所以,在我们这个异步FIFO中,深度为1023。
点击OK直接生成。在点击Generate。
此外,我们还需要两个不同时钟,在这里我们使用锁相环生成。
在管理界面搜索clock。配置过程我们在此前已经讲过,就不在过多叙述。
接下来我们写一下fifo的写控制器,代码如下:
1 module fifo_wr(
2
3 input wire clk,
4 input wire rst_n,
5 input wire empty,
6 input wire full,
7 output reg fifo_wr_en,
8 output reg [7:0] fifo_data_in
9 );
10
11 reg state;
12
13 always @ (posedge clk, negedge rst_n)
14 begin
15 if(rst_n == 1'b0)
16 begin
17 fifo_wr_en <= 1'b0;
18 fifo_data_in <= 8'd0;
19 state <= 1'b0;
20 end
21 else
22 case(state)
23 1'b0 : begin
24 if(empty)
25 state <= 1'b1;
26 else
27 state <= 1'b0;
28 end
29 1'b1 : begin
30 if(full)
31 begin
32 fifo_wr_en <= 1'b0;
33 fifo_data_in <= 8'd0;
34 state <= 1'b0;
35 end
36 else
37 begin
38 fifo_wr_en <= 1'b1;
39 fifo_data_in <= fifo_data_in + 1'b1;
40 state <= 1'b1;
41 end
42 end
43 endcase
44 end
45
46 endmodule
因为我们的实验是读空了才写,所以我们用状态机来做,先判断FIFO是否为空。读控制器代码如下:
1 module fifo_rd(
2
3 input wire clk,
4 input wire rst_n,
5 input wire empty,
6 input wire full,
7 output reg fifo_rd_en
8 );
9
10 reg state;
11
12 always @ (posedge clk, negedge rst_n)
13 begin
14 if(rst_n == 1'b0)
15 begin
16 fifo_rd_en <= 1'b0;
17 state <= 1'b0;
18 end
19 else
20 case(state)
21 1'b0 : begin
22 if(full)
23 state <= 1'b1;
24 else
25 state <= 1'b0;
26 end
27 1'b1 : begin
28 if(empty)
29 begin
30 fifo_rd_en <= 1'b0;
31 state <= 1'b0;
32 end
33 else
34 begin
35 fifo_rd_en <= 1'b1;
36 state <= 1'b1;
37 end
38 end
39 endcase
40 end
41
42 endmodule
顶层代码如下:
1 module fifo(
2
3 input wire clk,
4 input wire rst_n,
5 output wire [7:0] q
6 );
7
8 wire fifo_wr_clk;
9 wire fifo_rd_clk;
10 wire locked;
11 wire empty;
12 wire full;
13 wire fifo_wr_en;
14 wire [7:0] fifo_data_in;
15 wire fifo_rd_en;
16
17 clk_wiz_0 clk_wiz_0_inst
18 (
19 // Clock out ports
20 .clk_out1(fifo_wr_clk), // output clk_out1
21 .clk_out2(fifo_rd_clk), // output clk_out2
22 // Status and control signals
23 .reset(~rst_n), // input reset
24 .locked(locked), // output locked
25 // Clock in ports
26 .clk_in1(clk)); // input clk_in1
27
28 fifo_wr fifo_wr_inst(
29
30 .clk (fifo_wr_clk),
31 .rst_n (locked ),
32 .empty (empty ),
33 .full (full ),
34 .fifo_wr_en (fifo_wr_en ),
35 .fifo_data_in (fifo_data_in)
36 );
37
38 fifo_generator_0 fifo_generator_0_inst (
39 .wr_clk(fifo_wr_clk), // input wire wr_clk
40 .rd_clk(fifo_rd_clk), // input wire rd_clk
41 .din(fifo_data_in), // input wire [7 : 0] din
42 .wr_en(fifo_wr_en), // input wire wr_en
43 .rd_en(fifo_rd_en), // input wire rd_en
44 .dout(q), // output wire [7 : 0] dout
45 .full(full), // output wire full
46 .empty(empty) // output wire empty
47 );
48
49 fifo_rd fifo_rd_inst(
50
51 .clk (fifo_rd_clk),
52 .rst_n (locked ),
53 .empty (empty ),
54 .full (full ),
55 .fifo_rd_en (fifo_rd_en)
56 );
57
58 endmodule
代码写完之后,我们写个仿真验证一下波形,代码如下:
1 `timescale 1ns / 1ps
2
3 module fifo_tb;
4
5 reg clk;
6 reg rst_n;
7 wire [7:0] q;
8
9 initial begin
10 clk = 0;
11 rst_n = 0;
12 #105;
13 rst_n = 1;
14 #10000;
15 $stop;
16 end
17
18 always #10 clk = ~clk;
19
20 fifo fifo_inst(
21
22 .clk (clk),
23 .rst_n (rst_n),
24 .q (q)
25 );
26
27 endmodule
打开波形之后,我们将读写控制模块的信号全部添加到波形窗口:
添加好之后,点击restart和run-all
由于波形默认运行10us,我们观察不到全部波形,所以,在此我们继续点击run-all,然后点击break,让仿真停止。
然后,我们观察波形:
在波形里面可以清楚的看到我们的fifo_data_in和q的波形,一长一短。这是因为读的速度快,所以波形维持的时间短。写数据的时间长度是读数据时间长度的两倍。
然后放大波形观察其他信号:
在黄色光标位置,可以看到满信号拉高了,然后写使能就拉低了,状态开始进入到读。在读使能拉高之后,输出q就有了数据,但是我们的empty信号维持了一段时间才拉低,这是因为fifo的特殊结构导致的,在此我们就不再过多讨论。
结论:异步FIFO控制正确,仿真波形输入和输出信号正常。