HELLO FPGA 之 软件工具篇
FIFO(First Input First Output),即先进先出队列,在计算机中,先进先出队列是一种传统的按序执行方法,先进入的指令先完成并引退,跟着才执行第二条指令。就好比我们在超市购物,买好了东西之后,我们便会推着我们满满的购物车来到收银台排在结账队伍的最后面等待结账,前面先来排队的客户先得到结账,然后一个一个的结完帐离开,后面后来的客户需要等到前面的客户结完帐之后才能进行结账。这就是一种先进先出的机制,相信大家还是很容易就能理解的。
FIFO一般用于不同时钟域之间的数据传输,比如FIFO的一端是采样速率比较慢的一个接口,另一端是采样速度比较快的一个接口,假设采样慢的接口速度为1Mhz,而采样快的接口速度为100Mhz,如果我们直接将这两个接口相连接,那么将会出现各种问题,如何解决呢,我们可以在这两个不同的时钟域间采用FIFO来作为数据缓冲。另外对于不同宽度的数据接口也可以用FIFO,比如一端的接口输出数据是8位的,而另一端输出数据可能是16位的,我们就可以在这两个不同宽度的数据接口中使用FIFO来达到数据匹配的。
同我们之前讲的PLL、ROM和RAM IP核一样,Altera也提供了FIFO IP核,用于实现FIFO存储器。
介绍完了FIFO IP核,接下来我们来看一下FIFO IP核是如何进行配置的。大家看,如图7.1所示是FIFOIP核的配置页面。
图7.1 FIFO IP核的配置页面
从该图中我们可以看出,FIFO IP核的配置也是很简单的,我们想要配置一个FIFO IP核,也同ROM和RAMIP核一样,只需要完成以下三个步骤:
1、Parameter Settings(参数设置);
2、EDA(电子设计自动化);
3、Summary(总结);
首先我们先来介绍一下ParameterSettings(参数设置),在Parameter Settings(参数设置)配置中,我们可以看到它有4个标签,这4个标签分别是Width,Clks,Synchronization、SCFIFO Options、Rdreq Option,Blk Type和Optimization,Circuitry Protection。这里我们需要说明的是,它不止4个标签,它是有6个标签的,其中DCFIFO1、DCFIFO2两个标签页面隐藏了起来,我们需要通过选项来将这两个页面打开。下面我们将会进行介绍。
1、Width,Clks,Synchronization
从图7.1所示的页面中,我们可以看出,我们的FIFO IP核也是需要设置其存储大小的,下面我们就对该页面中的每个选项一一进行讲解:
● How wide should the FIFO be?:用于FIFO输入和输出的位宽,默认值为8;
● Use a different output widthand set to:用于指定不同的输入和输出数据位宽。
● How deep should the FIFO be?:用于指定FIFO的数据深度,默认值为256;
● DO you want a common clock forreading and writing the FIFO?:该选项有两个选择值,分别是Yse和No,默认值是Yes,如果选择Yes,此时的FIFO就是一个单时钟FIFO,简称(SCFIFO),SCFIFO的读信号和写信号与同一个时钟同步,如果选择No,此时的FIFO就是一个双时钟FIFO,简称(DCFIFO),DCFIFO的读信号和写信号分别与rdclk和wrclk同步。这里需要我们注意的是,当我们选择No的时候,上面的Use a different output width and set to便可以开启并使用,同时在我们的标签中我们可以看到,之前的SCFIFO Options变成了DCFIFO1和DCFIFO2。首先我们先来介绍的是SCFIFO,介绍完SCFIFO标签,之后我们再来介绍DCFIFO1和DCFIFO2标签。
2、SCFIFO Options
说完了Width,Clks,Synchronization页面,接下来我们再来看下SCFIFO Options页面,大家看,如图7.2所示是SCFIFOOptions配置页面。
图7.2 SCFIFO Options的配置页面
下面我们就对SCFIFO Options配置页面进行一个详细的介绍:
● full:FIFO满的标记信号,为高电平时表示FIFO已满,不能再进行写操作。
● empty:FIFO空的标记信号,为高电平时表示FIFO已空,不能在进行读操作。
● usedw[](number of words in theFIFO):显示存储在FIFO中数据个数的信号,Note:(可以使用最高位作为FIFO的半满指示信号。)
● almost full:接近满信号,当usedw信号的值大于或等与我们设置的almost full的值时,该信号置为高电平,是full信号的提前提示信号。
● almost empty:接近空信号,当usedw信号的值小于我们设置的almost empty的值时,该信号置为高电平,是empty信号的提前提示信号。
● Asynchronous clear:异步复位信号,用于复位所有输出状态端口。
● Synchronous clear(flush the FIFO):同步复位信号,用于复位所有输出状态端口。
3、DCFIFO1
说完了SCFIFO Options页面,接下来我们再来看下DCFIFO1页面,大家看,如图7.3所示是DCFIFO1配置页面。
图7.3DCFIFO1的配置页面
从该页面中,我们可以看出,该页面主要是用于对我们的DCFIFO进行优化的,下面我们就来简单的介绍一下这三种优化类型:
● Lowest latency but requiressynchronized clocks(最低延迟,但要求同步时钟):此选项使用一个同步阶段,没有亚稳态保护。它是最小尺寸,提供良好的Fmax。
● Minimal setting forunsynchronized clocks(不同步的时钟设置为最小):这个选项使用两个同步阶段,具有良好的亚稳态保护。它是中等尺寸,提供良好的Fmax。
● Best metastability protection, best fmax and unsynchronized clocks(最好的亚稳态的保护,最好的Fmax,时钟不同步):这个选项使用三个或更多的同步阶段,具有最好的亚稳态保护。它是最大尺寸,给出了最好的Fmax。
在使用过程中,通常我们选择的是默认的中等优化,具体的优化主要还是看你的工程的要求,假如你的工程对速度和稳定性要求很高,但同时资源又很多,那么你就可以使用第三个选项,加入你的工程资源很紧张,那么你只能选择使用第一个资源少的优化。
4、DCFIFO2
说完了DCFIFO1页面,接下来我们再来看下DCFIFO2页面,大家看,如图7.4所示是DCFIFO2配置页面。
图7.4DCFIFO2的配置页面
从该页面中,我们可以看到,该页面和我们的SCFIFOOptions页面还是很相似的,无非是把之前的一个full,一个empty和一个usedw[]分成了rdfull和wrfull,rdempty和wrempty,以及rdusedw[]和wrusedw[]。下面我们就来简单的介绍一下:
● rdfull和wrfull:FIFO满的标记信号,为高电平时表示FIFO已满,不能再进行写操作。
● rdempty和wrempty:FIFO空的标记信号,为高电平时表示FIFO已空,不能在进行读操作。
● rdusedw[]和wrusedw[]:显示存储在FIFO中数据个数的信号。
● Add an extra MSB to usedw ports:将rdusedw和wrusedw数据位宽增加1位,用于保护FIFO在写满时不会翻转到0。
● Asynchronous clear:异步复位信号,用于复位所有输出状态端口。
5、Rdreq Option,Blk Type
说完了DCFIFO2页面,接下来我们再来看下Rdreq Option,BlkType页面,大家看,如图7.5所示是RdreqOption,Blk Type配置页面。
图7.5Rdreq Option,Blk Type的配置页面
下面我们就对Rdreq Option,BlkType配置页面进行一个详细的介绍。
● Which kind of read access do you want with the rdreq signal?:指定FIFO是正常模式,还是前显模式,对于正常模式,FIFO将端口rdreq看做正常的读请求并在该端口信号为高电平进行读操作。对于前显模式,FIFO将端口rdreq看做读确认并自动输出FIFO中有效数据的第1个数据字(前提是empty或rdempty为低电平),而步需要将rdreq信号置为高电平,将rdreq信号置为高电平将输出FIFO中的下一个数据字(如果存在)。如果使用前显模式,将会使设计性能下降。
● What should the memory blocktype be?:用于指定实现存储器使用的存储块类型,具体可选值与使用的FPGA芯片有关,默认为Auto,我们一般使用默认值就可以了。
● Set the maximum block depth to auto words:用于指定存储器的存储深度,可选值有32、64、128等,默认值为Auto,同样我们一般使用默认值。
5、Optimization,Circuitry Protection
说完了Read During WriteOption页面,接下来我们再来看下Optimization,Circuitry Protection页面,大家看,如图7.6所示是Optimization,CircuitryProtection配置页面。
图7.6Optimization,Circuitry Protection的配置页面
下面我们就对Optimization,CircuitryProtection配置页面进行一个详细的介绍:
● Woud you like to register the output to maxmize performance but usemore area?:其实该选项和我们之前的DCFIFO1标签页面有点类似,我们在这里也需要做出一个速度与面积的一个权衡,这里默认的设置是最小面积,当然你也可以设置成最快速度。这个主要还是根据你的工程决定。
● Would you like to disable anycircuitry protection?:如果你不需要上溢检测和下溢检测保护电路,那么你可以通过下面的选项来禁止它们,以此来提高我们的FIFO性能。上溢检测保护电路主要是用于在FIFO满时禁止wrreq端口,下溢检测保护电路主要是用于在FIFO空时,禁止rdreq端口。它们默认的状态是打开的。
● Implement FIFO storage with logic cells only, even if the devicecontains memory blocks?:使用逻辑单元实现我们的FIFO存储器。
介绍完了Parameter Settings(参数设置)标签,接下来我们接着继续介绍第二个EDA(电子设计自动化)标签,如图6.5所示是EDA的配置页面。
图7.7 EDA的配置页面
从该页面中,我们可以看出,如果我们想要仿真FIFOIP核,那么我们就需要用到altera_mf这个仿真库。如果我们想要将此FIFO IP核用在其他的EDA工具上,我们可以通过选择Generate netlist这个选项来生成IP_syn.v文件,用于其他的EDA工具中。这里需要注意的是,并不是所有的第三方EDA工具都支持。
介绍完了EDA(电子设计自动化)标签,最后,我们再来看下Summary(总结)标签,如图7.8所示是Summary配置页面。
图7.8 Summary的配置页面
在该页面中,我们可以看到,该IP核能生成的所有文件都在该页面中,在这么多的文件中,我们只要选择FIFO_inst.v文件就可以了。至此,关于FIFO IP核的配置就讲解完了。接下来我们就来手把手带领大家创建一个FIFO IP核。
说完了FIFO IP核的配置,接下来我们就来创建FIFO IP核。首先,我们在E盘的Zircon_Verilog文件夹下创建一个用于存放我们FIFO的工程文件夹,这里我们起名叫Verilog_Ip_FIFO。然后我们打开Quartus II软件,新建一个工程,我们将工程路径设置在Verilog_Ip_FIFO中,然后我们给工程命名为Verilog_Ip_FIFO,接下来我们选择好器件,设置好仿真工具,完成QuartusII工程的创建。创建好了工程以后,我们在Quartus II软件的菜单栏中找到【Tools】→【MegaWizard Plug-In Manager】按钮并点击打开,在弹出的页面中,我们直接点击【Next>】进入IP核选择页面,如图7.9所示。
图7.9选择FIFO IP核页面
在该页面中,我们可以在MemoryCompiler下找到FIFO IP核,也可以直接在搜索框中直接搜索FIFO,我们找到FIFO IP核以后,我们选择它,然后我们在右侧的窗口中给FIFO IP核命名,这里我们命名为FIFO,并且选择创建的IP核代码为Verilog HDL。完成这些设置以后,我们点击【Next>】,进入如图7.10所示页面。
图7.10 FIFO IP核的配置页面
在该页面中,我们可以看到,我们将FIFO的深度设置成了32,这个大小和我们之前RAM IP核中的大小是一致的。这里我们需要注意的是,我们可以看到,我们使用的是单时钟FIFO,也就是SCFIFO。设置好了以后,接下来我们就直接进入SCFIFO页面,如图7.11所示。
图7.11SCFIFO的配置页面
在该页面中,我们可以看到,我们这里使用的是默认配置,默认配置是带有full、empty和usedw[]三个信号的,接下来我们就可以点击【Finish】按钮来完成FIFO IP核的定制,其余的页面我们使用默认设置即可。这里需要提醒大家的是,不要忘记勾选FIFO_inst.v初始化模版文件。创建好了FIFO IP核之后,我们需要在顶层模块中实例化我们的FIFO IP核,代码如图7.12所示。
图7.12在Quartus II软件中例化FIFO模块
从该图中我们可以看到,其实FIFO IP核和我们的RAM IP核使用方法是差不多的,在RAM IP核中我们需要写使能、写数据、读使能、读数据以及时钟和地址,在我们的FIFO IP核中,我们则需要写使能、写数据、读使能、读数据,时钟,空标识、满标识,以及使用量。如果我们去掉空标识、满标识和使用量这三个信号,我们可以看到我们的FIFO IP核剩下的信号是和RAM IP核一样的,唯一不同的是我们的RAM IP核读写操作需要地址,我们的FIFO IP核就不需要使用地址,只要使能信号打开,时钟到来,数据就可以读出和写入,比我们的RAM IP核使用起来简单。下面我们来看下FIFO IP核中的代码如代码7.1所示。
代码7.1Verilog_Ip_FIFO.v源代码
1 | moduleVerilog_Ip_FIFO |
2 | ( |
3 | CLK_50M,RST_N, |
4 | wrdata,rddata,wren,rden,time_cnt,usedw,full,empty |
5 | ); |
6 | |
7 | input CLK_50M; |
8 | input RST_N; |
9 | output reg[5:0] time_cnt; |
10 | output reg[7:0] wrdata; |
11 | output [7:0] rddata; |
12 | output wren; |
13 | output rden; |
14 | output [4:0] usedw; |
15 | output full; |
16 | output empty; |
17 | reg [5:0] time_cnt_n; |
18 | reg [7:0] wrdata_n; |
19 | |
20 | always@(posedgeCLK_50MornegedgeRST_N) |
21 | begin |
22 | if(!RST_N) |
23 | time_cnt<=1'b0; |
24 | else |
25 | time_cnt<=time_cnt_n; |
26 | end |
27 | |
28 | always@(*) |
29 | begin |
30 | if(time_cnt==6'd63) |
31 | time_cnt_n=1'b0; |
32 | else |
33 | time_cnt_n=time_cnt+1'b1; |
34 | end |
35 | |
36 | assignwren=(time_cnt>=1'b0&&time_cnt<=5'd31)?1'b1:1'b0; |
37 | |
38 | always@(negedgeCLK_50MornegedgeRST_N) |
39 | begin |
40 | if(!RST_N) |
41 | wrdata<=1'b0; |
42 | else |
43 | wrdata<=wrdata_n; |
44 | end |
45 | |
46 | always@(*) |
47 | begin |
48 | if(time_cnt>=1'b0&&time_cnt<=5'd31) |
49 | wrdata_n=time_cnt; |
50 | else |
51 | wrdata_n=wrdata; |
52 | end |
53 | |
54 | assignrden=(time_cnt>=6'd32&&time_cnt<=6'd63)?1'b1:1'b0; |
55 | |
56 | FIFO FIFO_inst |
57 | ( |
58 | .clock (CLK_50M ), |
59 | .wrreq (wren ), |
60 | .data (wrdata ), |
61 | .rdreq (rden ), |
62 | .q (rddata ), |
63 | .empty (empty ), |
64 | .full (full ), |
65 | .usedw (usedw ) |
66 | ); |
67 | |
68 | endmodule |
从代码中我们可以看到,其实FIFO IP核的代码和RAM IP核的代码差不多的,我们只是在RAM IP核代码的基础上去掉了地址信号,然后添加了空标识、满标识和使用量这三个信号,该代码实现的功能其实和我们的RAM IP核的代码是一样的,下面我们就来简单的给大家分析一下:代码的前19行代码主要是声明端口和定义端口类型。第20行至第35行,这里面包含两个always模块,一个always模块是用于时序电路,一个always模块是用于组合电路,这个时序电路和组合电路构成了一个计数器,该计数器从0计数到63,当计数器等于63时,计数器就会清零,从0开始继续计数。依次循环。下面我们再来看第36行,这个assign主要是用来生成写使能信号wren;第38至52行,这两个always模块用来生成我们写入到FIFO IP核中的数据。第54行,这个assign主要是用来生成读使能信号,最后56至66行是用来例化FIFO IP核的。介绍完了代码,下面我们再来总结一下该代码主要实现了什么功能,该代码主要实现了先往FIFO IP核中写入32数据,即0~31;写完了32个数据之后,我们在将写入的32个数据读出。说完了代码,接下来我们就可以编译工程,配置工程了,这里我们需要说明的是,FIFO IP核通过开发板也是看不出它的实验效果的。
完成了Quartus II软件工程的建立,下面我们就来使用ModelSim软件仿真我们的FIFO IP核,首先我们利用Quartus II软件生成Testbench模板代码,然后我们在Testbench模板代码的基础上进行修改,代码修改如图7.13所示。
图7.13 Testbench激励代码编写
从该图中我们可以看出,我们这里的Testbench和RAM IP核中的Testbench是一样的,由于我们,我们把代码写在了顶层模块中,所以我们的Testbench才不需要编写这么多代码。修改好了代码,我们还需要将Testbench添加至我们的Quartus II软件的仿真设置中,添加好以后,我们就可以点击Quartus II软件菜单栏中的找到【Tools】→【Run Simulation Tool】→【RTL Simulation】点击打开,弹出如图7.14所示页面。
图7.14 FIFO IP核写操作仿真波形图
从图中我们可以看出,写使能wren为高,读使能rden为低,也就是说,该操作是往FIFO IP核中写数据,由于FIFO IP核并没有地址信号,所以只要在写使能wren为高时,写数据wrdata就可以将数据写入到FIFO中。大家看Cursor1这条线,当0数据写入到了FIFO中后,这时,空标识empty就由高变低,这意味着FIFO中有数据了,同时使用量usedw信号也将变为1,表示该FIFOIP核中存有一个数据。大家再来看Cursor2这条线,此时,写数据wrdata为1,使用量usedw信号为2,表示该FIFO IP核中存有两个数据。Cursor3也是如此,依次类推我们将0~31,也就是32个数据依次写入。如图7.15所示。
图7.15 FIFO IP核读操作仿真波形图
从图中我们可以看出,在Cursor1线时,我们往FIFO IP核中写入了数据30,在Cursor2时,我们往FIFO IP核中写入了数据31,此时由于32个数据已经写完,我们可以看到写使能wren由高变低,满标识full由低变高,使用量usedw变成了0,写使能和满标识相信大家都能够理解,但是使用量也许就有的朋友不理解了,为什么此时变成了0,而不是32呢?这是因为我们声明的usedw只有5位,也就是最大只能存入31,当usedw为32时便有一位溢出,因此为0。写数据完成之后,我们接下来就开始读数据,此时,rden将由低变高。在Cursor3时,我们就可以看到,此时rddata为0,由于rddata前面我们一直都是0所以有点不容易分辨,不过我们可以从满标识full和使用量usedw的变化来看出确实从FIFO IP核中读出了数据。下面我们再来看下Cursor4,在Cursor4这条线上,我们可以清楚的看到,我们将FIFO IP核中的数据读了出来,rddata由0变为了1,后面的数据依次类推也是如此,我们就不进一步说明了。
完成了ModelSim软件仿真FIFO IP核,接下来我们就需要使用SignalTap II软件对FIFO IP核进行调试,首先我们在Quartus II软件中创建一个SignalTap II调试文件,在SignalTap II调试文件中,我们利用节点发现器将time_cnt、wren、wrdata、rden、rddata、full、empty和usedw这八个信号添加至节点列表中,添加完成后,我们再将wren设置为上升沿触发,触发条件设置好了之后,我们将采样时钟设置为CLK_50M,将采样深度设置为2K,如图7.16图6.14所示。
图7.16 SignalTap II软件调试界面
设置好了之后,我们重新编译工程,通过SignalTapII页面,我们将.sof文件下载至开发板,然后我们点击SignalTapII页面中的开始调试按钮,SignalTap II软件触发并弹出如图6.15所示页面。
图7.17 wren写使能信号触发采样的波形图
从该图中我们可以看出,当wren为上升沿的时候,我们的wrdata依次往FIFO中写入了32个数据,这和我们的ModelSim仿真是一致的,这里我们就不再进一步进行说明了。下面我们再来看一下读操作,如图6.16所示。
图7.18 rden读使能信号触发采样的波形图
从该图中我们可以看出,当我们rden为上升沿的时候,我们的rddata从FIFO中依次读出0~31数据,同样也是和我们仿真看到的波形是一致的,至此,我们的FIFO IP核的内容就讲解完了。
到了这里,我们的《软件工具篇》章节中的所有内容我们就讲解完了,在该篇中我们不仅讲解了如何使用Quartus II软件、ModelSim和SignalTap II软件,我们还讲解了PLL、ROM、RAM和FIFO IP核的使用。这里我们只讲解了比较常用的几个IP核,在后续的教程中我们会讲解更多IP核的使用教程。
联系客服