3. 点亮你的LED灯

在前面的章节我们已经将FPGA开发软件安装完毕,这一章里我们将通过建立一个完整的工程来验证软件安装是否正确,是否可以实现软件的基本操作。同时,我们以点亮一个led的工程为例,为大家讲解一下完整的FPGA开发流程。

3.1. 正确的设计流程

正确的设计流程是开发项目的关键,大概主要分为以下几个步骤:

  1. 首先我们要进行设计前的规划,即对项目要有一个全局的考虑,分析项目的具体需求来设计系统的结构、划分系统的层次,确定各个子模块的结构关系和信号之间的相互关系,然后确定模块的端口信号有哪些;

  2. 根据每个模块的功能和自己的理解并结合芯片、接口的时序手册我们使用Visio画出该模块能正常工作的时序波形图;

  3. 根据所画的波形图严格设计代码,所谓的严格就是要保证设计代码的仿真结果要和所画的波形保持一致;

  4. 代码编写完成后对代码进行编译,目的是检查代码中的语法错误。若代码存在语法错误,对代码进行修改,再次编译,直至通过编译;

  5. 根据RTL代码设计合理的Testbench进行逻辑仿真(也称为前仿真、功能仿真);

  6. 使用仿真工具进行仿真(可以使用软件自带的仿真工具,也可以使用其他的第三方仿真工具,本教程中我们使用ModelSim),并将仿真出来的波形和用Visio画出的时序波形图进行对比,如果对比有差别则修改代码直至相同;

  7. 绑定管脚后进行分析综合、布局布线,然后下载到硬件板卡中,此时如果硬件板卡能够正常工作,则说明前期的设计和编写的代码都正确;如果硬件板卡不能够正常工作,查找并解决问题,反复迭代直至正确实现功能,并重复后面的流程,最终保证硬件板卡能够正常工作。

3.2. 工程文件夹的管理

在项目设计之前我们先做好准备工作,先给设计的工程建立清晰明了的文件体系,把不同的设计文件放到不同的文件夹中,养成这样一个好的习惯是为了日后更方便管理每一个项目。不同的设计文件是有类别差异的,如果不进行文件分类,而是将所有文件存放在一起,非常不易于后期文件的查找、管理和移植。

下面我们以点亮第一个led的项目为例进行演示。首先切记一点所有的工程路径中一定不能出现中文,否则会出现找不到文件路径的情况,因为我们使用的开发工具大都是国外的,对中文的支持性较差,所以推荐用“英文字母+数字或下划线”组合的方式来命名,且工程和文件夹的名字要有一定的意义,能够让阅读者看到名字就知道该工 程或文件大概的功能。这里我们将第一个项目的总文件夹命名为led。如图 9‑1所示:

LED002

图 9‑1 总文件夹命名

然后在led总文件夹下建立4个子文件夹,分别来存储不同的文件集,如图 9‑2所示:

LED003

图 9‑2 子文件命名

这四个文件夹的用途分别是:

doc:该文件夹主要放置一些文档资料,如数据手册,使用Visio画的波形图、自己写的文档等都可以存放到这个文件夹里面。

quartus_prj:该文件夹主要放置的是工程文件,使用Quartus II新建的工程就保存到这里,如果是使用的Xilinx的ISE开发工具,就可以命名为ise_prj,这样能很清晰的知道是用的什么开发软件进行开发的。

rtl:该文件夹主要放置可综合的代码,就是最后可以生成硬件电路的代码,因为这部分代码主要是寄存器描述的寄存器传输级的代码所以文件夹取名为rtl(register transport level),因为也是我们的设计文件,所以也可以取名为design。

sim::该文件夹放置对可综合代码的仿真文件,即不可综合的代码,也叫testbench,所以也可以将文件夹取名为testbench或者tb都可以。

主要的文件夹就是这四个,后期的一些项目有可能还会用到Matlab、IP核,届时可以再新建一个单独管理Matlab文件和IP核文件的文件夹,文件数量可以根据自己的需求进行分类管理。

3.3. 一个完整的设计过程

这一小节不详细讲解语法、代码编写和思想结构,主要目的是测试软件安装激活后能否正常使用以及软件的基本操作步骤,通过第一个例子介绍后面所有设计实例的规范流程和方法(软件基本操作后面不再单独讲解,直接按照标准的开发流程重点讲解设计思想)。

3.3.1. 功能简介

拿到一个项目后首先进行项目分析,分析实现的功能是什么、如何进行系统的结构和层次划分、每一部分用什么方法实现,由于我们是进行学习,不是开发产品,所以不考虑芯片选型的问题。

这里我们的第一个实例要求通过按键控制一个led灯的亮灭,按键未被按下时led灯处于熄灭状态,按键被按下时led灯处于点亮状态,实现一个最简单的有输入、输出的小工程。

3.3.2. 硬件资源

明确工程要实现的功能后,就要了解工程设计的硬件资源。在“点亮LED灯”的实验工程中,我们需要用到开发板上的按键和LED灯,我们要使用按键KEY1点亮LED灯D6,如图 9‑3所示。

LED004

图 9‑3 硬件资源

由原理图可知,征途Pro开发板的按键未按下时为高电平、按下后为低电平;LED灯则为低电平点亮。如图 9‑4、图 9‑5所示。

LED005

图 9‑4 按键部分原理图

LED006

图 9‑5 LED灯原理图

3.3.3. 新建一个Visio文件及其配置

接下来就是在doc文件夹中新建一个名为led的Visio图,用于绘制我们设计过程中的结构框图和时序波形图,如图 9‑6所示:

LED007

图 9‑6 Visio文件的建立及配置(一)

双击打开新建的Visio文件,不用选择绘图类型,直接点击“取消”,如图 9‑7所示:

LED008

图 9‑7 Visio文件的建立及配置(二)

如图 9‑8所示,我们添加和FPGA设计相关的工具组件,先依次点击图中的①、②、③,再依次点击④中的三个工具组件,即可将FPGA相关的三个工具组件添加进来。

LED009

图 9‑8 添加波形工具箱(一)

如图 9‑9所示,我们可以看到显示了已经添加的组件。

LED010

图 9‑9 添加波形工具箱(二)

点击图 9‑10所示“>”处将其展开。

LED011

图 9‑10 Visio文件的建立及配置(五)

展开后如图 9‑11所示,三个设计好的专门用于FPGA设计的工具插件已经添加进来了(该插件也可以进行修改,或者重新设计符合自己习惯的插件)。

LED012

图 9‑11 Visio文件的建立及配置(六)

为了画图时更好的对齐,我们可以把“视图”下的“网格”勾选上,如图 9‑12所示:

LED013

图 9‑12 Visio文件的建立及配置(七)

做好上面的基本设置后就可以进行FPGA框图和时序波形图的设计了,更多细节的操作都可以在网上查询获取,这里不再进行一一讲解。

3.3.4. 模块和端口信号划分

接下来我们要对该项目的系统结构和层次进行设计,即要实现该功能需要设计哪些独立的模块,以及分析每一个模块的输入输出信号应该有哪些,这部分的设计往往是方案上的设计也称为系统架构的设计,非常重要。在公司中我们会设计一些较为复杂功能的项目,往往具有多种功能,为了简化设计,我们要把复杂的功能进行拆分简单化,划 分成相对独立的小功能,这些小功能的实现由一个个独立的模块来完成,每个小模块的功能和运转都是独立的,这样子在公司中就可以让多个人同时进行设计和验证,加快了开发效率,当每个人所分配的模块都设计好了以后,再将每个人设计的模块根据各自的功能和关系连接到一起,实现整体的功能。但是如何进行模块的划分和整体的布局 以及信号的端口设计都是需要我们在日后的学习中要不断积累的,通过做项目、多了解各种方案才能够使我们在对整体的设计结构和层次上有一个很好的把握。

我们今天设计的这个工程比较简单,因为只具有一个独立的功能,自然结构和层次就很简单了,所以只需要一个模块就可以实现了,我们将模块取名为led,然后再分析模块的输入输出信号应该有哪些。一个1bit的输入信号连接到按键上,取名key_in,一个1bit输出信号连接到led上,取名led_out。

根据上面的分析设计出的Visio框图如图 9‑13所示,在此设计中我们并不关心输入和输出之间通过什么方式来实现这种关系(可能是通过线连接、寄存器连接或者更复杂的逻辑连接),而只关心最后的输出是不是实现了我们期望的结果。

LED014

图 9‑13 框图设计

为了使端口信号和功能更加清晰,我们将其整理为如表格 9‑1所示的表格。

表格 9‑1 端口信号描述

信号

位宽

类型

功能描述

key_in

1Bit

Input

按键的输入

led_out

1Bit

Output

输出控制led灯

3.3.5. 波形设计

框图结构和端口信号设计完毕后就需要设计框图结构下模块功能的实现,也就是输入和输出之间怎么样的影响关系。输入和输出满足信号与系统的关系,这种关系是一种时序的、逻辑的关系,即既有时间上的关系又有逻辑上的关系。这种关系不再是结构的关系,对于这种时序的、逻辑的关系,我们用波形图的方式表达最为清晰直观。因为F PGA本身就是并行执行的,当信号较多时,我们仅靠人脑的记忆和联想如此众多信号的并行时序关系的效果可能并不是非常好,所以我们要通过绘制波形图的方法来将这种关系表达出来。波形画出来后一切时序和逻辑关系就清晰明了,无论是代码实现,还是日后再次拿过来分析代码,只要有波形图作为辅助参照,一切都是如此简单。但是 如何根据数据手册、设计要求来绘制波形图,也是我们设计方法中重点,需要跟着本教程长久的练习并加以总结才能够熟练掌握并运用自如。

为了使绘制的波形图更加直观,在绘制波形图前我们先统一定义输入信号用绿色标注,输出信号用红色标注,中间变量信号用黄色标注表示(本章不涉及中间变量,后面章节会有体现),本教程所有波形设计均是按照这个规范作图的,当然如果学习者有自己的颜色喜好也可以自定义配置 ,只要能够有所区分就可以。绘制波形图时首先要设计完善的测试激励以模拟最接近真实情况的输入信号,然后根据输入信号的波形画出相应的输出信号的波形。在画波形的过程中,你会详细了解到各信号之间的关系,这样先画波形的好处是能够在写代码之前对一些细节的问题做到预先了解,方便后面编写代码和调试时做到胸有成竹,在大 型复杂的项目中其优点更为明显,我们还有一个写代码的诀窍,就是如何根据波形图快速的编写出的代码,如果波形图正确,我们编写代码的速度会又快又正确,如果你基本功扎实,画图认真准确,毫不夸张的讲,根据波形图编写完的代码直接就是正确的,几乎不用调试,这在项目开发过程中的效率是相当高的。

本章的示例功能具体为:按键按下时即key_in为低电平,此时控制led灯的管脚为低电平,板卡硬件电路设计的led为低电平点亮,所以此时led灯为点亮状态。对于组合逻辑我们常用真值表来列出输入与输出的这种对应关系最为直接、明确,所以我们先列出如表格 9‑2所示的真值表,然后再根据真值表的输入与输出的对应关系画波形图。其波形如图 9‑14所示,与真值表的关系一一对应。

表格 9‑2 输入输出信号真值表

输入(input)

输出(output)

key_in

led_out

0

0

1

1

LED015

图 9‑14 Visio绘制波形图

如果最后通过仿真得到的结果和Visio波形的输入输出的逻辑关系一致(因为是最简单的组合逻辑,所以还不考虑时序之间的关系),那么我们设计的代码一定是正确的。

3.3.6. 新建工程

首先点击图标打开QuartusII13.0软件,如图 9‑15所示:

LED016

图 9‑15 打开QuartusII13.0软件步骤(一)

紧接着跳出该界面,如图 9‑16所示:

LED017

图 9‑16 打开QuartusII13.0软件步骤(二)

软件完全打开后是这样的界面,如图 9‑17所示

LED018

图 9‑17 打开QuartusII13.0软件步骤(三)

软件完全打开后会有一个快捷向导存在,如图 9‑18所示:是创建一个新的工程,是打开已经存在的工程,是最近打开过的工程列表,用户可以根据自己需求进行选择也可以不选择,不选择时可以直接点击界面右上角的 X 号退出。这里我们选择直接退出。

LED019

图 9‑18 软件快捷向导

使用新工程向导新建工程,可以选择1,也可以选择2直接打开新工程向导界面。这里我们选择1,如图 9‑19所示:

LED020

图 9‑19 新建工程步骤(一)

选择“New QuartusII Project”后点击“OK”,如图 9‑20所示:

LED021

图 9‑20 新建工程步骤(二)

打开新工程向导首页,直接点击“Next”,如图 9‑21所示:

LED022

图 9‑21 新建工程步骤(三)

是这个新建的工程的位置,选择到led文件夹下的quartus_prj文件夹;是这个工程的名字,也取名为led;是整个工程设计顶层的文件名,这个名字保持和2中的命名一致即可。然后点击“Next”,如图 9‑22所示:

LED023

图 9‑22 新建工程步骤(四)

在“File name”栏选择添加已经写好的.v文件,如果有多个文件可以一次性全部添加进工程,因为我们还没有写好的.v文件,所以我们不进行添加,直接点击“Next”如图 9‑23所示:

LED024

图 9‑23 新建工程步骤(四)

框是选择使用哪个系列的芯片,咱们使用的是CycloneIV系类的芯片;

框是选择该芯片的封装类型,选择FBGA封装;

框是管脚数量,选择256;

框是速度等级,选择8。

这4个选项设置好以后就得到了框的筛选结果,我们使用的芯片具体型号——EP4CE10F17C8。全部选择好后,点击“Next”,如图 9‑24所示:

LED025

图 9‑24 新建工程步骤(五)

如图 9‑25所示:

是器件具体的名字;

是内核电压,我们使用的芯片内核电压是1.2v;

是Logic ELements(每个逻辑单元主要由一个四输出的查找表和一个寄存器构成,还包括其他的必要电路)的数量,逻辑单元数越多说明资源越多,能够实现更多的逻辑设计,芯片的价格也就越贵,设计逻辑代码主要使用的是这部分资源;

是用户可配置的I/O共180个,虽然该芯片有256个管脚但是有一些管脚是不可以随意配置的,如电源管脚、固定功能的管脚,除去具有固定功能的管脚,留给用户可任意配置的管脚只有180个,设计中使用比较多的内存时就可以使用这部分专用资源;

是存储器的容量,共423936bit,主要是指Block RAM(块RAM),423936bit即46个M9K(每一块中包含8192个存储位,加上校验位共9216位,故称M9K);

是嵌入式乘法器数目共46个,在进行数学运算时可以调用该部分的资源,以节省逻辑资源的开销;

是锁相环的数量,一共有两个,主要用于分频、倍频和时钟相位的调制;

是全局时钟管脚,一共有10个,全局时钟管脚是连接到全局时钟树上的,能够保证连接到全局时钟树上的时钟信号到达每个寄存器的时间都是相同的。

LED026

图 9‑25 新建工程步骤(六)

这里是选择一些三方的开发工具,我们暂时先不用管,直接点击“Next”。如图 9‑26所示:

LED027

图 9‑26 新建工程步骤(七)

最后生成整个新工程向导的一个总结,可以验证下是否和自己最初的选择有出入,如果没有问题点击“Finish”。如图 9‑27所示:

LED028

图 9‑27 新建工程步骤(八)

新建工程完毕后你会在左上角出发现有如下的变化,会显示器件名和顶层设计文件名。如图 9‑28所示:

LED029

图 9‑28 新建工程完毕

打开quartus_prj文件夹发现里面生成了一些文件夹和文件,led.qpf就是这个工程的工程文件;led.qsf就是工程的配置文件。如图 9‑29所示:

LED030

图 9‑29 工程文件

3.3.7. RTL代码的编写

在rtl文件夹下新建一个名为led的文本文件,然后重命名为led.v。如图 9‑30所示:

LED031

图 9‑30 新建代码文本文件

使用Notepad++打开该文件,准备RTL代码的编写。如图 9‑31所示:

LED032

图 9‑31 打开代码文件

开始RTL代码的编写,RTL代码编写出的模块叫RTL模块(后文中也称功能模块、可综合模块)。之所以叫RTL代码是因为用Verilog HDL在Resistances Transistors Logic(寄存器传输级逻辑)来描述硬件电路,RTL代码能够综合出真实的电路以实现我们设计的功能,区别于不可综 合的仿真代码。(本章的重点是熟悉整个开发流程和软件工具的使用,不进行代码的详细讲解)我们前面讲过,根据波形写代码,这种方法在组合逻辑中表达的不够明显,而且刚开始的例子都很简单,后面我们会用单独的章节来讲解如何根据波形图来实现代码,并在视频课中着重教授大家这种方法。

1
2
3
4
5
6
7
8
9
module led
(
input wire key_in , //输入按键
output wire led_out //输出控制led灯
);
//\* Main Code \//
//led_out:led灯输出的结果为key_in按键的输入值
assign led_out = key_in;
endmodule

代码编写好后,需要将led.v文件添加到工程中。点击“File”切换到文件模式,在该模式下可以看到所有属于该工程下的文件。如图 9‑32所示:

LED033

图 9‑32 添加代码文件步骤(一)

右键点击“File”,并选择“Add/Remove File in Project…”,如图 9‑33所示:

LED034

图 9‑33 添加代码文件步骤(二)

如图 9‑34所示,在“File name:”栏选择添加已经写好的.v文件,找到“ledrtl”路径下的“led.v”。

LED035

图 9‑34 添加代码文件步骤(三)

打开后如图所示,点击“Add”添加到工程中,如图 9‑35所示:

LED036

图 9‑35 添加代码文件步骤(四)

点击“OK”完成添加。如图 9‑36所示:

LED037

图 9‑36 添加代码文件步骤(五)

添加后如图 9‑37所示:

LED038

图 9‑37 添加代码文件步骤(六)

3.3.8. 代码的分析和综合

点击如图 9‑38所示的图标进行代码的分析和综合,该步骤的目的是首先检查语法是否有错,其次是综合器将代码解释为电路的形式。

LED039

图 9‑38 代码的分析和综合图(一)

如图 9‑39所示,只有红色框内为绿色,说明分析与综合完成,并没有语法错误产生。

LED040

图 9‑39 代码的分析和综合图(二)

3.3.9. RTL视图的查看

双击“Netlist Viewers”下的“RTL Viewer”,如图 9‑40所示:

LED041

图 9‑40 RTL视图的查看

打开后的界面如图所示,这就是我们设计的硬件电路结构,可见输入是key_in,然后通过一根线连接到输出led_out,和我们设计所表达的意思相同,如图 9‑41所示:

LED042

图 9‑41 RTL视图

3.3.10. Testbench的原理

Testbench是测试脚本,测试谁?当然是测试我们用硬件描述语言(HDL)设计的电路,测试设计电路的整体功能、部分性能是否与预期的目标相符。有些初学者没有养成编写Testbench的习惯,总以为这是繁琐的、无用的,或者他们习惯用在线逻辑分析仪器来调试,其实不然,当你在编写大型工程代码的时候,综合一 次所用的时间少则十几分钟,多则几个小时,这种无用时间的消耗是我们所不允许的,在线逻辑分析仪虽然好用但是每修改一次代码我们就看结果就要综合一次,并不能节省时间,而使用Testbench做仿真的速度就很快,修改后即可马上看到结果,从而节省了我们大量的开发时间,所以大家务必养成从学习FPGA的一开始就编写 Testbench的好习惯。

编写 Testbench 进行测试的过程如下:

  1. 产生模拟激励(输入波形);

  2. 将产生的激励加入到被测试模块并观察其输出响应;

  3. 将输出响应与期望进行比较,从而判断设计的正确性。

接下来就是仿真激励Testbench的编写,首先给大家介绍下Testbench的原理。

我们通常给Testbench的取名是在被测试的模块名前加个tb_(也可以在被测试的模块名后面加_tb),容易识别出具体验证的是哪个模块。如图 9‑42所示:蓝色的框中的区域就表示一个测试系统,我们要写的Testbench就是用代码实现该区域的功能,这个功能是只针对待测试led模块,如果换成其他的模块,需要在单独设计专门针对其他待测试系统的Testbench。

LED043

图 9‑42 Testbench框图(一)

第二步是将待测试led模块放到tb_led模块框架中,如图 9‑43所示:

LED044

图 9‑43 Testbench框图(二)

第三步是将两个模块进行连线,如图 9‑44所示:

LED045

图 9‑44 Testbench框图(三)

tb_led模块和led模块的关系就是上面这种关系,但是我们需要怎么设计tb_led模块呢?我们需要做的就是希望tb_led模块能够产生key_in信号,然后这个信号通过led模块从led_out输出,我们观察led_out输出的信号是不是和最初设计的波形一致,如果严格一致则说明设计正确,代码综合布 局布线后下载到上板中能够正常工作的可能性越高,为什么说是越高而不是100%能够正常工作,是因为在高速系统中有时候逻辑仿真是对的,但是下载到板上依然不可以正常工作,其原因是因为逻辑并没有问题,真正的问题是在高速系统中往往需要进行时许约束,否则会由于系统工作太快导致时序违例,同样不能够正常工作。

可能大家还听说过后仿真(也称为时序仿真),我们为什么没做这个呢,做了是不是就能够保证下板后功能是100%正确的呢?答案是依然不能,虽然正确率可以相比于只做了逻辑仿真提高了,但是还是会存在下板后有问题的可能,而且后仿真的速度特别慢,所以我们干脆就不做这一步了,如果做了逻辑仿真后下板后仍有问题,我们可以 使用在线逻辑分析仪实时的对信号抓取确定是不是时序的问题,如果是时序的问题还要对系统进行静态时序分析(SAT)针对具体违例的时序加以约束。这样一说后仿真是不是没什么用了,当然也不是,前面我们讲过FPGA可以用在数字IC领域中,在数字IC的设计和验证中,往往要加入各种仿真延时文件,进行后仿真,但是本教程 不涉及数字IC的内容,因为数字IC虽然和FPGA相关,但又是一个全新的世界,如果个人想了解数字IC设计和验证或者以后想从事数字IC相关行业的工作可以在学习本教程后再继续深入学习。

3.3.11. Testbench代码的编写

先在sim文件夹下新建一个名为tb_led的文本文件,然后重命名为tb_led.v。如图 9‑45所示:

LED046

图 9‑45 新建仿真文件

使用Notepad++打开该文件,准备Testbench代码的编写。如图 9‑46所示:

LED047

图 9‑46 仿真文件的编写

开始Testbench代码的编写(本章的重点是熟悉整个开发流程和软件工具的使用,不进行代码的详细讲解),此编写技巧和方法我们也会在后面的章节和视频课中详细讲解。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
\`timescale 1ns/1ns
module tb_led();

////
//\* Parameter and Internal Signal \//
////
//wire define
wire led_out ;
//reg define
reg key_in ;

////
//\* Main Code \//
////
//初始化输入信号
initial key_in <= 1'b0;

//key_in:产生输入随机数,模拟按键的输入情况
always #10 key_in <= {$random} % 2; /*取模求余数,产生非负随机数0、1
每隔10ns产生一次随机数*/

////
//\* Instantiate \//
////
//------------- led_inst -------------
led led_inst
(
.key_in (key_in ), //input key_in

.led_out(led_out) //output led_out
);

endmodule

代码编写好后,需要将tb_led.v文件添加到工程中。点击“File”切换到文件模式,在该模式下可以看到所有属于该工程下的文件。如图 9‑47所示:

LED048

图 9‑47 工程测试文件的添加(一)

右键点击“File”,并选择“Add/Remove File in Project…”。如图 9‑48所示:

LED049

图 9‑48 工程测试文件的添加(二)

如图 9‑49所示,在“File name:”栏选择添加已经写好的.v文件,找到“ledsim”路径下“tb_led.v”。

LED050

图 9‑49 工程测试文件的添加(三)

打开后如图所示,点击“Add”添加到工程中,如图 9‑50所示:

LED051

图 9‑50 工程测试文件的添加(四)

点击“OK”完成添加。如图 9‑51所示:

LED052

图 9‑51 工程测试文件的添加(五)

添加完成后如图 9‑52所示:

LED053

图 9‑52 工程测试文件的添加(六)

3.3.12. 仿真设置

首先打开“Assignments”下的“Setting…”,如图 9‑53所示:

LED054

图 9‑53 仿真设置(一)

然后选择“EDA Tool Settings”下的“Simulation”,如图 9‑54所示:

LED055

图 9‑54 仿真设置(二)

如图 9‑55所示:框中“Tool name:”栏选择“ModelSim”,“ModelSim”就是独立的ModelSim,而“ModelSim-Altera”是安装QuartusII时自带的ModelSim;框中“Format for output netlist:”栏是选择输出网表的语言格式,该网表主要用于后仿真,因为我们在逻辑设计中往往不做后仿真,所以这里默认即可;框中“Time scale:”栏是选择输出网表的时间单位,所以也默认即可;选择框中的“Output direction:”选择输出网表的位置,这里我们默认即可。默认后即可在工程目录下产生一个名为simulation的文件夹,里面还有一个名为modelsim的子文件夹,这里不仅存放输出的网表文件,还存放了后面当ModelSim运行仿真后所产生的相关文件。

LED056

图 9‑55 仿真设置(三)

3.3.14. 打开ModelSim观察波形

因为第一个led工程实例相对简单,使用到ModelSim中的也是一些基本操作,所以本节只会对ModelSim软件的部分功能的使用进行讲解。但后期随着工程实例复杂度的提高,我们将会用到ModelSim中更高级的功能,那时再做详细讲解。

点击“Toos”下的“Run Simulation Tool”下的“RTL Simulaton”打开ModelSim进行功能仿真(因为主要是验证逻辑的正确性也称逻辑仿真)。如图 9‑63所示:

LED064

图 9‑63 ModelSim仿真(一)

如果能够成功打开如下界面说明ModelSim破解和关联QuartusII都正确。如图 9‑64所示:

LED065

图 9‑64 ModelSim仿真(二)

完全打开后的界面如图所示,会发现在Wave窗口有波形显示出来,如图 9‑65所示:

LED066

图 9‑65 ModelSim仿真(三)

点击箭头处,把显示波形的界面单独打开,如图 9‑66所示:

LED067

图 9‑66 ModelSim仿真(四)

点击图 9‑67中的放大镜图标显示如图 9‑68所示的全部仿真时间的波形,波形最后结束的位置对应的时间如图 9‑69所示在1000ns处,也就是1us,和图 9‑58中设置的结果是一致的。

LED068

图 9‑67 ModelSim仿真(五)

LED069

图 9‑68 ModelSim仿真(六)

LED070

图 9‑69 ModelSim仿真(七)

如图 9‑70所示:点击框中第一个按钮即添加参考线(图 9‑71中黄色的线即是参考线),点击第二个按钮即去掉参考线。当鼠标在波形界面中点击时,参考线也会指定到该位置,如果有两条黄色参考线时,会两条参考线之间的显示时间差(如图 9‑72所示)。

LED071

图 9‑70 ModelSim仿真(八)

LED072

图 9‑71 ModelSim仿真(九)

LED073

图 9‑72 ModelSim仿真(十)

点击图 9‑73左下角处的“小锁”即可固定参考线,被固定的参考线显示红色(添加和删减参考线也可以通过“小锁”旁边的“+”、“-”号实现)

LED074

图 9‑73 ModelSim仿真(十一)

为了更好地观察分析ModelSim仿真出的波形有以下几处按钮会用到。如图 9‑74所示:其中框中的图标是波形复位,点击后会出现如图 9‑75所示的框,我们直接点击“OK”即可对界面的波形进行全部清空;

框是设置运行一次仿真的时间,根据实际观察波形的需要填写相应的时间。但仿真的时间越久所需要等待的时间也会越久,所生成的仿真文件也会越大;

框中的第一个图标是运行左边设置的时间值,图中显示的是100ps,那么点击一次就运行100ps,运行完100ps后就会自动停止,也可以手动输入其他的时间单位和时间值。框中的第二个和第三个图标点击一次后波形会一直运行下去,直到点击第四个和第五个按钮时才会停止;

框中的图标分别是波形的下降沿定位和上升沿定位,当选中要观察的信号时,再点击这两个按钮时参考线就可以立刻定位到该信号的下降沿或上升沿,再次点击即跳到下一个下降沿或上升沿;

框中的第一个和第二个图标分别是波形方大和缩小(波形的放大缩小也可以按住键盘上的“Ctrl”键同时滚动鼠标滚轮)。第三个图标是全局波形显示。第四个图标作用是将参考线位置处的波形进行放大。第五个图标的作用是将参考线处的波形移动到波形显示窗口的最开头处。

LED075

图 9‑74 ModelSim仿真(十二)

LED076

图 9‑75 ModelSim仿真(十三)

图 9‑76可以看出在参考线处key_in的值为16进制表示的1’h0,根据需要我们还可以设置为其他格式显示。如图 9‑77所示,鼠标右击信号名字“key_in”,选择“Radix”后会看到各种不同进制格式的显示方式,可以根据需要任意选择。

LED077

图 9‑76 ModelSim仿真(十四)

LED078

图 9‑77 ModelSim仿真(十五)

3.3.15. 仿真波形分析

我们使用ModelSim仿真出来波形后可以直接观察出波形和预期之间的正确性,因为这个项目工程比较简单,只有两个信号,也没有内部信号,也不容易出错,所以可以直接分析得出,但是试想一下,如果有上百个信号,然后最后的输出结果还出现了错误,那你凭着自己的理解和观察就很难进行判断了,所以我们一开始就养成良好的 学习习惯,将ModelSim仿真出来的波形图和之前用根据自己理解用Visio画出来的波形图进行对比,既可以发现之前对整体设计的理解错误又能够方便的进行改错。

通过对比图 9‑78和图 9‑79发现两个图输入输出之间的关系是一模一样的(这里的一样不是说Visio波形中的key_in信号和ModelSim波形中的key_in信号都是先高后低的关系,而是Visio波形中的key_in信号和led_out之间的关系对应与ModelSim波形中的key_in信号和 led_out之间的关系是否一致),都是输入信号key_in与输出信号led_out相同,说明代码的设计结果符合预期的想法,可以进行下一步操作了。

LED015

图 9‑78 ModelSim仿真(十六)

LED079

图 9‑79 ModelSim仿真(十七)

3.3.16. 引脚约束

仿真结束后即验证了代码设计的正确性,也就是说可以进行上板验证了,但是在上板之前还需要进行引脚约束,就是根据硬件原理图确定按键和led分别与FPGA芯片的哪个引脚对应。由原理图知上板上一共有四个普通按键和四个led灯我们选择其中一对按键和led灯,按键选择连接的FPGA管脚为M2,led灯选择连接的F PGA管脚为L7。

点击如图 9‑80所示按钮,打开管脚绑定界面。

LED080

图 9‑80 管脚分配图(一)

打开后的界面如图 9‑81所示:

LED081

图 9‑81 管脚分配图(二)

管脚绑定在数目比较少的情况下有两种方法(管脚数目特别多的时候用其他方法,后面章节会单独介绍),一种是在的“Location”列“key_in”行输入“M2”,在“led_out”行输入“L7”然后按回车键确定。

LED082

图 9‑82 管脚分配图(三)

另一种方法是直接鼠标左键按住图 9‑83信号的名字,将其拖动到图 9‑84需要绑定的管脚上即可。

LED083

图 9‑83 管脚分配图(四)

LED084

图 9‑84 管脚分配图(五)

绑定完后的管脚状态如图 9‑85所示,其中:

  1. Node Name:RTL代码中定义的端口名字。

  2. Direction:管脚的输入输出方向。

  3. Location:管脚绑定的位置。

  4. I/O Bank:用于支持对应不同的电平标准,即VCCIO。每个Bank只能有一种电压标准,一般情况下选择默认值就好。一种颜色下的I/O口代表一组Bank。当管脚的Location约束完成以后,I/O Bank会自动进行填充。

  5. VREF Group:Bank内部的细分区域,非修改属性,会自动填充。

(6) I/O Standard:对管脚内部的I/O进行不同的电平约束。FPGA I/O的电压由I/O Bank上的VCC引入,一个Bank上如果引入了3.3V的 TTL电平,那么此时整个Bank上输出3.3V的TTL电平。设置好以后工具可以结合Current Strength一起计算功率。如果没有特殊要求默认即可。

  1. Reserved:这个是对管脚内部的I/O逻辑进行约束的,这个约束的是FPGA在I/O端的输入输出区域的逻辑。无特殊要求可以为空。

  2. Current Strength:是驱动电流强度,一般选择默认值。如果需要驱动大功率的电路,可以在FPGA外围加驱动电路。

  3. Slew Rate:是电压转换速率,表示单位时间内电压升高的幅值,跟信号跳变时间有关,一般选择默认值。

  4. Differential Pair:差分管脚。

  5. Filter:通过过滤选项显示指定种类的管脚。

所有管脚配置完成后就可以关闭当前界面了。

LED085

图 9‑85 管脚状态

3.3.17. 全编译

全编译和分析综合的不同是全编译需要进行布局布线,管脚绑定后就可以对全局进行布局布线了。点击如图 9‑86所示的按钮进行全部的分析综合布局布线。

LED086

图 9‑86 编译过程(一)

如图 9‑87所示:框是综合后的资源使用率报告;

框是综合后提示的错误警告数,这里没有错误,只有警告和严重警告存在,一共7个;

框是编译时每一步详细的过程,包括执行每一个部分所使用的具体时间,从上到下顺序执行,第一个是之前进行过的分析与综合,第二个是布局和布线,第三个是生成程序文件,第四个是静态时序分析,第五个是写入网表,这5个步骤编译后都是自动完成的不需要进行干涉,如果后期自动完成不能满足需求是需要手动进行设置以实现优化;

框中从左往右依次是错误、严重警告、警告,只要不出现错误就暂时先不用管,当警告或者严重警告影响功能时再去分析;

框是编译的总进度;

框是在编译过程中实时显示的时间,该时间随着工程量对增大而增大,硬件的编译一次的时间是非常慢的,不像软件那样瞬间就完成了,所以这也是需要进行仿真的原因之一。

LED087

图 9‑87 编译过程(二)

我们故意设置个错误,将第8行key_in后面的分号去掉(如图 9‑88所示),再进行综合。

LED088

图 9‑88 编译过程(三)

编译后的结果如图 9‑89所示:框中提示有三个错误,其实我们只修改了一个地方,却提示三个错误,所以提示的错误数,并不是你真的有这么多个的错误,所以遇到提示很多错误的时候不到担心惧怕,很可能你修改了一处后错误一下子会减少很多。经验再久的工程师写代码时也难免会出现语法错误,初学者犯语法错误的更是常见的事 情,凡是这种由语法导致的错误都是最容易修改的;框处前会有个“×”号,因为有语法错误,所以分析和综合失败;框是具体的错误提示处,提示第9行附近有错误,然后双击该错误提示语句就会立刻定位到错误位置附近,我们发现第9行没有问题,那就是第9行之前有问题,我们在第7行发现key_in后缺少一个分号,所以添加上 分号再进行重新编译即可。

LED089

图 9‑89 编译过程(四)

3.3.18. 通过JTAG将网表下载到开发板

如图 9‑90所示,开发板连接12V直流电源,USB-Blaster下载器JTAG端连接开发板JTAG接口,另一端连接电脑USB接口。线路正确连接后,打开开关为板卡上电。

LED090

图 9‑90 程序下载连线图

点击图 9‑91所示的图标,打开下载界面。

LED091

图 9‑91 下载过程(一)

打开下载界面如图 9‑92所示:框是硬件连接设置,当前显示的是“No Hardware”即没有连接到硬件,我们需要选择连接的硬件是USB-Blaster,点击“Hardware Setup…”,在弹出的界面中(如图 9‑93所示)的“Currently selected hardware:”选择“USB-Blaster[USB-0]”,然后点击“Close”,此时会发现框处发生变化如图 9‑92所示“Hardware Setup…”处已经变成了“USB- Blaster[USB-0]”;框是选择下载的方式,我们选择“JTAG”下载模式;框是下载的进度表,下载成功后会在框内显示绿色并出现“100%(Successful)”;框是下载的文件,这里是“led.sof”文件。

LED092

图 9‑92 下载过程(二)

LED093

图 9‑93 下载过程(三)

LED094

图 9‑94 下载过程(四)

如图 9‑95所示若没有“.sof”文件,点击“Add File…”,选择“ledquartus_prjoutput_files”路径下的“led.sof”后点击“Open”,如图 9‑96所示。

LED095

图 9‑95 下载过程(五)

LED096

图 9‑96 下载过程(六)

当设置完成所有的下载设置后,即可点击图 9‑97所示的“Start”,然后等带下载过程,此过程十分快,下载完成后发现“Progress:”处显示绿色,并出现如图 9‑98所示的“100%(Successful)”,说明下载成功。

LED097

图 9‑97 下载过程(七)

LED098

图 9‑98 下载过程(八)

下载完成后即可按下管脚绑定的按键,会发现同时被绑定的led随着按键的按下会被点亮,而按键松开时又熄灭,实现了我们最初预想的设计,如图 9‑99所示。

LED099

图 9‑99 上板效果图

注意:尽量不要带电插拔 JTAG 口,否则容易烧坏 FPGA 的 JTAG 口。如果用万用表测到 JTAG 号 TDI TDO TMS TCK 任意一个与地短路了,那你的 FPGA 可能已经被烧坏了。并不是每次热插拔 JTAG 口都一定会烧坏,但是至少会有一定烧坏的可能性,为了能让开发板陪伴我们学习完本教程,所以最好谨慎行事!

3.3.19. 未使用管脚的默认设置

我们使用的开发板上面有很多功能,在做一个个例子的时候不可能同时全部用上所有的FPGA管脚,而在Quartus软件中默认未使用管脚的状态为弱上拉输入,所以未使用到的管脚上也是有电压的,只是驱动能力很弱,这往往会导致一些不安全的隐患,所以我们需要将未使用管脚的状态设置为三态输入。具体操作如下:

如图 9‑100所示,鼠标右击“Cyclone IV E: EP4CE10F17C8”选择“Device…”。

LED100

图 9‑100 管脚的默认设置(一)

在打开的界面中(如图 9‑101所示)点击“Device and Pin Options…” 。

LED101

图 9‑101 管脚的默认设置(二)

在打开的界面中(如图 9‑102所示)选择“Unused Pins”,在“Reserve all unused pins:”框中发现默认的设置是“As input tri-stated weak pull-up”即弱上拉输入,意思是所有没有定义的引脚,都作为输入引脚,并附加弱上拉电阻。

LED102

图 9‑102 管脚的默认设置(三)

我们在“Reserve all unused pins:”框中选择“As input tri-stated”即三态输入,然后点击“OK”完成设置,如图 9‑103、图 9‑104所示。在本例中,默认的选择 “As input tr-stated with week pull- up”是一个安全的选项,除此之外,“As input tri-stated”等也是安全的选项。但是“ As output driving ground ”是一个危险选项,意思是未用到的 FPGA 引脚会被下拉到地。危险在于如果这些引脚在被 PCB 上的外围电路上拉到高电位,则可能会产生一个强烈的灌入电流,可能会烧毁 FPGA的引脚。因此为了避免经济损失,无论如何请你注意一下这里的选择,不要莫名其妙地把 FPGA烧坏了。

LED103

图 9‑103 管脚的默认设置(四)

LED104

图 9‑104 管脚的默认设置(五)

未使用的管脚状态设置完成后需要重新编译才能够进行映射。点击全编译按钮,会弹出一个对话框,当下载界面被修改(如图 9‑105所示)或者关闭时(如图 9‑106所示)会提示是否保存原下载界面的设置,这里我们不保存,所以点击“No”,如果个人需要保存点击“Yes”后重命名备注好即可。

LED105

图 9‑105 管脚的默认设置(六)

LED106

图 9‑106 管脚的默认设置(七)

3.3.20. 程序的固化

为什么网表下载后还要再进行固化呢?当你把下载网表的上板断电后再重新上电发现之前的功能已经不存在了,也就是说下载后的网表消失了。为什么会这样子呢,很多人不禁问道,其实我们使用的这款FPGA芯片是基于SRAM的结构,即下载后的网表存储在FPGA内部的SRAM中,我们也知道SRAM有掉电易失的特性,这也就 是我们为什么掉电后功能就消失的原因。所以我们要想使网表重新上电后仍然存在就需要将网表存储到片外的flash中,flash芯片型号为WinBond 25Q16,存储容量为16Mbit(2M字节),采用SPI协议和FPGA进行通信,可做为FPGA的配置芯片(完全兼容EPCS16芯片),以保证FPGA在重新上电后仍能继续工作。具体操作如下:

点击“File”下的“Convert Programming Files…”,如图 9‑107所示:

LED107

图 9‑107 程序的固化(一)

打开后的界面如图 9‑108所示:

LED108

图 9‑108 程序的固化(二)

“Programming file type:”栏是选择输出文件的类型,我们选择“JTAG Indirect ConfIguration File(.jic)”,如图 9‑109所示:

LED109

图 9‑109 程序的固化(三)

“Configuration device:”栏是flash的型号,我们选择“EPCS16”,如图 9‑110所示:

LED110

图 9‑110 程序的固化(四)

“File name:”栏是选择输出.jic文件的位置,“ledquartus_prjoutput_files”,并重命名为“led.jic”,如图 9‑111所示:

LED111

图 9‑111 程序的固化(五)

如图 9‑112所示,点击“Input files to convert”框中的“Flash Loader”后会发现“Add Device…”从不能被选择的情况下可以被选择,然后点击“Add Device…”

LED112

图 9‑112 程序的固化(六)

如图 9‑113所示,选择我们使用的FPGA芯片所属的型号,在“Device family”框中将“Cyclone IV E”前的括号勾选上,在“Device name”框中将“EP4CE10”前的括号勾选上,然后点击“OK”。添加后如图 9‑114所示。

LED113

图 9‑113 程序的固化(七)

LED114

图 9‑114 程序的固化(八)

如图 9‑115所示,点击“Input files to convert”框中的“Sof Data”后会发现“Add File…”可以被选择,然后点击“Add File…”。

LED115

图 9‑115 程序的固化(九)

因为“led.jic”文件的生成需要“led.sof”文件的参与,所以选择路径“ledquaruts_prjoutput_files”下的“led.sof”文件后点击“Open”,如图 9‑116所示:

LED116

图 9‑116 程序的固化(十)

选择“led.sof”文件后如图 9‑117所示,然后点击“Generate”生成“led.jic”文件

LED117

图 9‑117 程序的固化(十一)

如图 9‑118所示,“led.jic”文件生成成功,点击“OK”。

LED118

图 9‑118 程序的固化(十二)

然后点击“Close”关闭该界面,如图 9‑119示:

LED119

图 9‑119 程序的固化(十三)

回到下载界面,右击“led.sof”,点击“Delete”将其删除。如图 9‑120所示:

LED120

图 9‑120 程序的固化(十四)

点击“Add File…”将“led.jic”文件添加进来,如图 9‑121所示:

LED121

图 9‑121 程序的固化(十五)

如图 9‑122所示,选择“ledquartus_prjoutput_files”路径下“led.jic”的文件,然后点击“OK”。

LED122

图 9‑122 程序的固化(十六)

如图 9‑123所示,先将“Program/Configure”下的方框打上钩,然后点击“Start”执行程序的固化。

LED123

图 9‑123 程序的固化(十七)

如图 9‑124所示,下载的进度较之前明显慢很多,所以我们在平时调试的时候可以不固化程序,等待所有的工作都完成后,最后的再把完成的程序固化到开发板中。

LED124

图 9‑124 程序的固化(十八)

3.4. 章末总结

本章通过led灯的例子让大家了解了一个完整的FPGA开发流程,其中有几个步骤是非常关键的,涉及到FPGA的开发方法,如:模块和端口信号划分、波形设计、RTL代码的编写、Testbench代码的编写、仿真波形分析。我们会在后面的章节和实例中重点对以上提到的关键步骤做详细的分析。只要大家根据我们的教程训 练,一定可以掌握这种方法。

3.5. 拓展训练

自己按照整个流程操作一遍后,在独立不看教程的情况下训练一遍,熟悉FPGA的开发流程和相关软件的操作,只有工具使用熟练了,才能够加快开发效率。