在这个界面中,我们会建立新工程的名字、文件所在位置、工作目录以及顶层文件的文件类型。
在复杂设计时,会将电路分成各个小模块去做设计,最终还需要一个大模块将这些小模块包括进来,对外呈现都是大模块的接口。此时,这个大模块就是顶层实体(TOP level entity)。如果设计中只有一个模块,那么这个模块就是顶层实体。
工程的名称就是采用之前我们做的设计文件夹的名字,这个名字可以是任意的,笔者建议和文件夹保持一致,因为当初建立文件夹时,就是选择用工程的名字。直接输入工程名称and_gate2_1即可。
顶层实体的名字会自动出现,与工程的名字保持一致。我们也可以重新指定一个新名字,笔者建议与工程名字保持一致。
Top-level source type选择HDL。
点击Next。
FPGA 设计最终是要落实到芯片内部,在这里要选择对应的芯片(自己手里开发板的FPGA芯片)。芯片的型号在FPAG的芯片上有描述,如果芯片上看不清楚,或者芯片在被其他东西挡住,可以查看开发板的资料,一般都有介绍。笔者手中开发板的FPGA的型号为XC6SLX9-2TQG144的型号进行设计和选择,读者不相同的,请自行改动。如果暂时还没有开发板的读者,可以跟着笔者选择继续下面的步骤(没有开发板的话,后面有一些步骤是做不了的)。
选择时,首先选择对应的系列。
选择对应的系列后,可以看到下面的器件列表(此列表的窗口是可以拉大的,可以直接扩大整个界面)中。
然后我们选择Device,我们的开发板型号为XC6SLX9,所以我们选中跟我们型号对应的一项。
接下来是选择我们的封装,我们的FPGA芯片的封装。我们的芯片是TQFP封装,144个管脚,所以我们选择TQG144。G意思为无铅。
然后就是选择芯片的速度等级,我们的芯片速度等级是-2。选择好之后点击NEXT。
笔者手中的芯片是Spartan6系列的芯片,接下来介绍一下命名规则。我们以XC3S50-4PQ208C为例。
如果是其他系列,请自行参考所对应的芯片手册。
XC6SLX9-2TQG144C当中的C与内部结构没有任何关系,C表示商业温度。
点击Next。
这个总结显示出在新工程向导中,我们所做的所有的设置。大家可以检查一下,如果发现那一项和自己的要求不一致,就需要点击back,修改后,在回到此步骤。
点击Finsh,完成工程创建。
工程建立完成后,工程向导界面显示出选择的器件和指定的顶层实体。
打开and_gate2_1文件夹。
.xise,ISE的工程文件。如果此时将ISE关闭了,双击此文件就可以打开工程。
打开工程的方式,不建议采用双击.xise文件。有时间一个PC上面,会有多个quaruts软件的情况,如果直接双击,就会采用某一个版本打开,这不一定使我们想要的。
建议打开工程的方式,首先查看应该选择的版本,启动对应的版本ISE软件,选择Open project(不要点击Open),找到工程,启动。
3. 输入设计
当建立完工程后,就可以输入设计。输入设计的方法有三种:原理图输入、HDL代码输入、原理图和HDL代码混入输入。
本小节只讲HDL代码输入。
用计算机语言设计一个数字电路系统,其实质就是用一种语言描述一个硬件模型,因此这样的语言又称为硬件描述语言(Hardware Description Language),或使用缩写HDL。虽然现在HDL已经有多种语言版本,而且还在发展中。但是在本书讨论的HDL仅包括现在最常使用的Verilog HDL和VHDL两种语言系统。
目前在国内做FPGA设计的公司中,使用Verilog HDL占据大多数,故而本教程以Verilog HDL为主。
选择顶层,右键选择New Source...,出现如下界面:
在这个界面中,我们需要选择代码文件类型,即Verilog Module,然后在右侧给文件起一个名字,这里我们还是使用and_gate2_1。
然后点击Next。
这个界面我们可以定义我们的Module。在我们的设计中,整个模块有三个端口,两个输入,一个输出。那么我们可以事先把端口进行一下设置:在Port Name这一列,填入我们的端口名,在Direction这一栏选择好端口类型。因为我们的输入和输出都是1Bit数据,所以,BUS不用勾选。如图:
点击Next。
点击Finish,可以看到ISE会自动生成一个代码文件,而且,文件中还有一部分代码:
Verilog 语法和C很相似,学习起来比较容易。下面我们按照做电路的方式讲解verilog语言。
做电路的话,首先需要拿出一块打的面包板,剪出合适大小的一块。相当于圈了一个地方,做设计只能在这块区域内。
对于verilog语言来说,需要用module和endmodule圈出一个区域,设计代码只能在这块区域中。Verilog语言区分大小写,我们一律采用小写。module和endmodule是verilog的关键字,在综合器中会变蓝。如果endmodule没有变蓝,请多打一个回车或者空格。
当剪出合适大小的面包板后,需要其上面写一个名字。后面应用也好,说起来也好,好歹有个名字。
在verilog中,也需要有一个module name。
在verilog中命名的话,需要遵从一定的规则。由字母、数字、下划线构成;建议字母开头;不能够与verilog的关键字相同;命名是要有一定的意义。
对于module name来说,一般还有一个要求,与文件名称保持一致。那么此时我们要做二输入与门,文件名称是and_gate2_1。要求module name也写成and_gate2_1。
当对面包板命名后,需要给它添加输入和输出的端口(合理的布局接口)。
二输入与门有两个输入,一个为a,另外一个为b;一个输出为s。在verilog中,布置接口的方式有两种。
在verilog中,module name(and_gate2_1)之后的那个括号中的内容成为端口列表。
Verilog布置接口的第一种方式为1995标准,第二种方式为2003标准。目前大多数平台都可以支持这两种方式。笔者建议用2003标准。
端口列表中,描述端口时,用逗号隔开,最后一个端口后面不加逗号。在端口列表的括号后面有一个分号。
对于描述端口来说,有最基本的四项:方向、类型、位宽、名称。
input表示输入,output表示输出,inout表示输入输出。
类型中比较常用的有两种:一种是wire,另外一种reg。wire类型时,wire可以省略不写。另外input必须是wire类型。笔者建议wire不省略,都写上。
在做电路时,位宽表示有几根线。有时候为了方便,会将同一种类的线进行同时命名,此时就需要用到位宽。例如:5位的地址线。可以单独命名5次,但是比较麻烦。位宽用中括号括起来,例:[3:0],[3:1],[2:5]。如果位宽为1的话,省略不写。笔者建议位宽的右侧为0,左侧为位宽减一。
名称就是为这个输入命名了一个名字。命名时要遵从verilog命名规则。
在做完端口后,需要在面包板上做出符合功能的设计,然后用连接线将设计和输入输出管脚相连接。
二输入与门的设计是需要在中间放一个组合逻辑电路二输入与门。
Verilog中,描述组合逻辑的第一种方式是利用assign语句进行描述。
assign语句要求被赋值变量(Y)为wire类型,中间采用阻塞赋值(=)的方式,最后面是赋值表达式,在verilog中,算术与用&来表示(后续介绍算术运算和逻辑运算的区别)。
至此,二输入与门的HDL输入就完成了。
4. 综合分析
当设计输入完成后,需要对设计进行综合分析,同时也检查一下其中是否存在错误。
进行综合分析时,有时会提出一个提示:
出现上述提示,就证明我们在设计时,修改了某些文件后,没有点击保存。此时点击Yes即可。但是这是一个非常不好的习惯,建议大家修改完任何设计都要及时保存。
出现上述提示,就证明我们在设计时,修改了某些文件后,没有点击保存。此时点击Yes即可。但是这是一个非常不好的习惯,建议大家修改完任何设计都要及时保存。
如果中间有错误的话,请参考输入设计,查看自己的输入是否正确,然后重新综合分析即可。
综合分析成功。点开XST左边的+号,可以看到有四个选项,第一个即为我们的RTL视图,双击查看视图。
这里我们会看到有两种查看方式,第一种是使用资源管理器向导,第二种是使用顶层模块原理图查看。这里我们选择第一种,点击OK。
这时我们会看到出现了一个选择元素的界面,这里我们可以直接选择顶层进行查看。选中顶层之后点击Create Schematic。
在RTL视图中,综合出来的电路图,只是电路模型而已。在FPGA中是没有与门的,有的只是LUT等效的二输入与门电路。
综合分析成功后,会产生一个报告。
在报告中,可以看出综合状态、软件信息、工程版本信息、顶层实体、器件系列、目标器件、时序模型、逻辑单元数量、寄存器数量、管脚数量、虚拟管脚数量、存储器大小、嵌入式乘法器的使用个数、锁相环使用个数。
本设计中只是设计了一个二输入的与门,所以使用一个逻辑单元,3个管脚,其他都没有涉及到。
5. RTL仿真
在综合分析完成后,对于简单的设计,通过查看RTL视图中综合出来的电路模型,就能够知道所做设计是否正确。但是对于复杂的设计,电路模型比较复杂,无法直接判断是否设计正确。
如果直接将不知道正确与否的设计加载到板卡中,很多时候无法通过结果直接看出来是否设计比较严谨。所以要求设计者在软件环境下对所做设计进行一定的测试。
仿真是利用模型复现实际系统中发生的本质过程,并通过对系统模型的实验来研究存在的或设计中的系统。
当所研究的系统造价昂贵、实验的危害性大或需要很长的时间才能了解系统参数变化所引起的后果时,仿真是一种特别有效的研究手段。
仿真其实就是模拟实际情况。对于电路来说,就是给予合适的输入,观测输出是否和设计时所预想的相同。
电路的输入、中间过程和输出,都是数字信号,用波形来表示比较直观。
在真正的电路中,是存在电路延迟的。在仿真时,如果加载的综合出来的电路模型,那么此时验证的内容主要是测试模型的逻辑功能是否正确,不考虑延时信息。这种仿真被称为功能仿真、RTL仿真、前仿真、前仿。
在开发中用的比较多的方式是利用HDL的方式进行充当激励,ISIM会自动抓取HDL代码中的信号进行绘制波形,用于设计者的观测。
我们这里需要新建一个仿真文件,同样的方式,右键选择New Source。
这次我们选择Verilog Text Fixture,给仿真文件起一个名字,比如and_gate2_1_tb。因为我们仿真的是and_gate2_1,所以起名字的时候,建议在and_gate2_1后面加上_tb,这样便于分辨文件功能,然后点击Next。
这个界面是选择一个关联的源文件,也就是要我们选择被仿真的文件,这里就是我们的and_gate2_1。
选中点击Next。然后会出现一个新建信息。
点击完成,软件会生成一个仿真的代码文件。
`timescale是verilog中定义时间标度的关键字。1ns/1ps中的1ns表示时间的单位,在veirlog中不支持直接写出单位,例:5 ns,ns等时间单位是不被识别的。当定义了1ns为时间单位后,表示时间时,可以直接写出,例:表示10ns时,可以直接写10即可。1ns/1ps中的1ps表示时间的精度,由于精度的存在我们可以写小数。例:表示5.5ns时,可以直接写5.5。但是也正是由于精度的存在,小数不能无限制向下描述。例:表示5.523ns时,可以成5.523,如果表示5.258963ns,那目前的精度是到不了这么精确的。精度是1ps,因此小数的位数最多能有三位。在设计中,很少用到比ps还要精确的单位,所以一般的时间标度都是1ns/1ps。
Testbench文件也是verilog文件,所以也必须遵从verilog的标准。
在tb文件中,是没有端口的。在测试时,输入的信号都由内部产生,输出信号只要引出到内部即可,仿真器会自动捕获。所以tb的模块是没有端口的。
在测试文件中,需要将被测试元件例化进来。例化的方式如下:
在例化时,首先是模块名称(and_gate2_1),后面是例化名称,这个名字可以任意名字,笔者建议例化名称要和模块名称有一定的关系,笔者采用模块名称后加上_inst,表示例化的意思。后面的括号是端口列表,每一个端口的前面加上一个“.”,后面加上一个“()”,此时表示这个端口可以连接线了,连接线放到“()”里面就是连接上了。
对于连接线来说,命名也是任意的,笔者建议连接线的名字和要连接的端口的名字要有一定的关系,笔者采用端口名字前面加tb_,表示tb中的连接线。
所有的连接线都需要提前定义。在定义时,都可以采用“wire”类型(后续会有更改)。
当例化完成,连接线定义和连接完成后,就需要开始测试了。而测试就是给模块的输入赋值,观测输出是否正确。
在测试时,我们需要顺序性的给出激励,verilog提供了一种比较简单的方式“initial”语句。在这个语句中,我们只需要顺序性的给出激励就可以了。“#”表示延时,延时在verilog中是不可综合的,但是在仿真中,是存在的。
“1’b0”中的1表示位宽为1,’b表示后面的数码为2进制,0表示数码为0。
在赋值时,建议采用位宽+进制+数码的方式进行赋值。
Verilog中,begin end表示中间的语句是一个整体,verilog和C类似,每一个函数或者语句下只能包含一个语句块。C语言中采用大括号,verilog中采用begin end。
上述的initial语句块中,描述了tb_A和tb_B被先赋值00,延迟20ns,被赋值为01,延迟20ns,被赋值为10,延迟20ns,被赋值为11。
Verilog语法规定,在initial语句中被赋值的变量,应该定义为reg类型。
在写完testbench后,可以综合分析一下。保证没有任何的语法错误。点击Simulation,选中仿真文件and_gate2_1_tb。
点开Isim Simulator左边的+。会看到有两项,第一个是检查仿真代码的语法,第二个是打开Isim查看仿真波形。那么我们在查看波形之前,首先要检查一下语法错误。在确保没有错误的情况下再打开仿真波形。那么我们先双击Behavioral Cheak Syntax。Console窗口信息没有错误的情况下,我们即可双击Simulate Behavioral Model来查看波形。
由图中可以看到,三个端口都有波形,但是三条线都是直线。这是由于我们只看到了波形的一小部分,我们需要查看全部波形来观察仿真结果。在上方有快捷键:第一个是放大,第二个是缩小,第三个是全局,第四个是查看光标线位置。这里我们点击第三个查看全局波形。
由波形图我们可以看出,我们的仿真结果跟我们的理论实际是一致的。
有一个细节需要大家注意,在我们的仿真波形里面,波形运行到了1000ns才停止,而我们的代码里面只运行400ns。那这是怎么回事呢?原因是因为Isim设置的默认时间是1us。
所以我们在波形在400ns之后会一直保持很长一段的时间。
此时就可以关闭ISIM软件。
点击“Yes”即可关闭。
6. 锁定管脚
输入设计后,经过综合和分析以及RTL仿真后,证明设计的逻辑功能是没有任何错误的。但是设计是在芯片内部进行实现的,如果想要发挥功能,势必要与外部的逻辑电路进行相连接。
在上述例子中,设计了二输入与门。我们可以将两个输入绑定到任意的两个管脚上,将输出绑定到任意一个管脚上。经过下载后,可以在输入的管脚上加载电平,测量输出管脚的电平,验证设计是否正确。
在FPGA学习开发板上,大部分都会有一些按键和LED,这些按键就可以为输入提供高低电平,LED就可以检测输出的电平值。
所以最好的验证方法是,将输入的管脚分配到连接有按键的管脚上,将输出分配到带有LED的管脚上。
自己制作或者购买的开发板,都会有原理图。
经过分析,key0的网络是直接连接到FPGA芯片上的;按键释放时,key0网络为高电平,按下时,key0网络为低电平。其他按键原理相同。
经过分析,LED0的网络是直接连接到FPGA芯片上的;当FPGA将LED0网络置成低电平时,LED是熄灭的;当FPGA将LED0网络置成高电平时,LED是点亮的。其他的LED原理类似。
不同的电路原理,后续会得出不同的结果。请认真分析原理。
通过查看各个网络与FPGA的芯片连接关系,就可以得出按键、LED电路与FPGA的线连接的IO。
经过查看,两个按键分别选择83和82。LED选择74。
点击Tools -> PlanAhead -> I/O Pin planning(PlanAhead)-Pre Synthesis... 。打开管脚规划器。
这时候会出现一个提示框,提示这个进程要求UCF约束文件需要与模块关联,是否要自动创建UCF文件并添加到工程当中。选择“No”,则在进行此进程之前需要创建或添加现有的UCF文件。这里需要说明的是,UCF文件是管脚约束文件,我们进行到这个地方的时候,并没有新建过UCF文件,所以,这里我们点击“Yes”新建一个UCF文件。这时我们可以看到在我们的代码文件下面多了一个UCF文件。
而且此时,还会自动打开PlanAhead。
这个是我们配置管脚的,首先先把欢迎界面关掉。然后将下方的Scalar Ports,我们会看到我们的三个端口,在site的这一列填入管脚。
输入对应的管脚号。
输入完成后,点击保存然后关闭即可。
右键XST,点击Rerun,等待综合分析完成。
7. 布局布线
综合分析只是将外部的输入转换成为对应的电路模型或者对应FPGA的电路模型,我们可以对电路模型进行RTL仿真,来排除逻辑功能的错误。
在FPGA片内实现的话,就需要对模型进行“实地”布置,利用FPGA片内的资源来实现模型,并且要固定好位置和连接线。这部分操作称为适配,也被称为布局布线。
双击Implement Design即可进行布局布线。
布局布线后形成的报告有可能和之前综合分析形成的报告中的资源利用有所出入,最终我们以布局布线后的资源为准。
8. 生成配置文件并下载
在布局布线后,需要将对应的结果下载到FPGA片内。对于模型来说是无法下载的,只有通过编译,形成某种文件才可以下载。
双击Generate programming files,产生配置文件。
利用下载电缆连接FPGA开发板和PC。将FPGA开发板通电。
连接好之后,在设备管理器里面会看到我们的驱动。
就绪之后,等待下板文件生成,我们点开Configure Target Device。
点开之后,会有两个选项,我们选择Manage Configuration Project(iMPACT),然后双击。
我们可以看到这个界面全是空的,首先我们双击Boundary Scan。
双击之后会看到右边空白区域有一个提示:右键点击去添加设备或者初始化JTAG。我们在空白区域点击右键,然后选择第一个Add Xilinx Device(ctrl+D)。这是需要我们添加下板文件,则在文件夹中找到我们的下板文件.bit。
选中之后点击打开。会看到有Xilinx标志的芯片。
我们在芯片上右键点击一下弹出菜单,选择第一个Program。
这个界面我们点击Cancel,然后就可以看到下板界面提示下板成功。
下载完成后,此界面就可以关闭。询问是否保存时,选择“No”即可。
当配置完成后,我们就可以进行验证。按下按键,分析LED的灯的状态。我们做的是二输入与门,它的真值表如下:
通过记录按键和LED的状态,也会得到一组真值表。
通过分析按键和LED的工作原理,可以化简真值表。
通过分析两个真值表,确认功能正确。
不同的按键和LED原理,可以对应去分析。
大多数FPGA的内部实现是用SRAM等效出来的电路,SRAM是一种掉电丢失的器件。所以FPGA下载成功后可以正常运行,但是掉电后,FPGA会丢失之前配置的所有信息。
这种情况非常不利于产品的研发,设备断电时常有的事情,而断电后再上电,还是希望FPGA能够正常工作的。
正是由于FPGA掉电丢失所有信息,所以在FPGA的周边会配置一块掉电不丢失的存储器(flash),可以将配置信息存储到存储器中,FPGA每次上电后读取存储器的内容即可。
向flash中存储信息,下面简单介绍一下固化流程。
在Implementation窗口选中顶层文件,然后右键点击Generate Programming File。在菜单里选择最后一个Process Properties。
对生成FLASH文件进行设置:配置速度为6,位宽选择最大1。以上是为了加快烧写FLASH的速度。
设置成功之后,双击Manage Configuration Project(iMPACT)。
双击Creat PRom File (PRom File Formatter)。
在这个界面,在SPI Flash下,选择Configure Single FPGA,然后点击绿色箭头进入第二步。因为我们的开发板中Flash型号为M25P16。内存大小为16M,所以在Storage Device (bits)选择16M,然后点击Add Storage Device。点击后在下面的空白框内,会出现16M字样。然后我们继续点击绿色箭头。进入第三步,在第三步中,我们需要给Flash的烧写文件起一个名字,并且选择存放的路径,如下:
点击OK,如下所示:
此提示是让添加我们的器件文件,即我们的下板文件.bit。点击OK,选择我们的下板文件。
此提示说是否添加另外的文件,这里我们不需要,点击NO即可。
提示已经编译了器件文件,点击OK继续。然后双击左侧的Generate File。
双击之后会有提示说生成成功。然后双击Boundary Scan 。以同样的方式,鼠标右键点击空白区域,选择Add Xilinx Device ... ,我们先添加.bit文件,然后右键点击芯片,选择Add Spi/Bpi Flash,这次我们选择Flash烧录文件.mcs,点击打开。
芯片选择我们所用的M25P16,点击OK。
右键点击Flash芯片,选择第一个Program。
点击Cancel,界面会提示Program succeeded。
下载此文件速度比较慢,请耐心等待。
下载后,FPGA不能够正常工作,需要断电后上电,FPGA就可以正常工作了。
以后每次断电再上电,都可以正常工作。
9. 设计流程总结在设计时,上述的8个步骤是最基本的开发流程。
时序仿真一般做的不太多,大多数都会使用静态时序分析来替代。