FPGA项目开发实战讲解(txt+pdf+epub+mobi电子书下载)


发布时间:2020-06-04 13:33:08

点击下载

作者:李宪强

出版社:电子工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

FPGA项目开发实战讲解

FPGA项目开发实战讲解试读:

前言

笔者发现市场上关于FPGA的书籍有以下几种:介绍语法、介绍软件安装和使用、介绍FPGA芯片本身、介绍FPGA小项目(如开发100例,这样的小项目不能使读者学会真正的FPGA开发)、介绍FPGA大项目(如以太网开发、PCI_E系统开发,但介绍太笼统,没有介绍读者关心的具体操作步骤,最终读者还是不能掌握实际的开发技巧),所以笔者立足于让读者真正能掌握FPGA的开发技巧,以中等难度的项目为例,详细介绍开发中的每个环节,读者读完后,完全可以亲自动手完成一个中等难度的项目,建立自信,真正学会FPGA开发。

本书涉及的所有技术点都是笔者在项目设计中亲自完成的。其中用7章从易到难介绍了7个开发项目,每个项目从以下几方面介绍:相关技术、FPGA方案设计、子模块设计、仿真和测试、项目开发过程中遇到的问题、定位和解决措施。在编写过程中,插入了大量的状态机转换图、仿真结果和关键代码,方便读者学习,同时还加上了笔者在开发过程中遇到的实际问题及解决问题的手段、机理分析。

本书的读者群是FPGA初学者,或者对FPGA设计有一定经验,但对复杂的项目经常感到束手无策的开发人员,以及想进一步提高的朋友。

在7个开发项目中,异步FIFO控制器是每个FPGA开发人员都需要面对的问题,一些从事FPGA开发多年的朋友仍然不能掌握其中的细节,虽然看似简单,但真正弄清楚后能提高很多。

串口通信控制器是比较常用的模块,80%以上的项目都需要用到串口进行通信、控制、参数配置等,所以串口的开发比较有代表性。

A/D采集控制器也是非常常用的,但不同A/D芯片的工作模式不一样,笔者在开发过程中遇到了一些障碍,所以拿出来和读者分享,希望对读者有一定的启发作用,遇到调试不顺的时候,需要慢慢分析,不能轻易打退堂鼓,也不能对技术指标打折扣,要100%完成才可以。

HDLC也是非常常用的技术,在商用网络通信及军工领域中应用非常广泛。

SDRAM应用也非常广泛,学会了这个模块,理解DDR SDRAM就容易很多了。

NAND FLASH作为大容量存储器,在开发中有一定的难度,第9章将告诉读者面对一个相对复杂的项目,如何一步步分解,把复杂的工程分解成多个相对简单的模块。

1553B控制器在航空航天领域中用得非常多,目前,在这个领域实现1553B控制器主要用进口ASIC芯片,但价格昂贵,国产化和FPGA化的趋势正在一步步加深,军工类院校和研究所对这项技术会非常感兴趣。

本书的编写重点不是介绍语法,而是讲解实际项目,每个项目的编写都有大量的状态转移图、时序仿真图和测试结果,异步FIFO、串口控制器、A/D和D/A控制器、HDLC控制器四部分给出了完整的源代码,后面几章限于篇幅只列出了实现的关键代码,但是读者根据比较详细的设计描述和状态机,可以轻易地将代码编写出来。对照代码,用心领悟,沉下心,集中精力搞定其中一两个项目,就会有质的提高。

我的联系方式:lxqiang_2008@aliyun.com,期待和读者进一步沟通交流。编著者第1章FPGA介绍1.1 FPGA的优势

• 运行速度快

FPGA内部集成锁相环,可以实现外部时钟倍频,核心频率可达几百兆,而单片机运行速度则低得多。在高速场合,单片机无法代替FPGA。FPGA运算速度快,编程简易,而且有些高端的FPGA芯片内部集成了很多有用的模块,如串行收发模块,如果不用FPGA而是另搭电路,结构将会比较复杂,用FPGA可以简化设计。

• FPGA引脚多,容易实现大规模系统

单片机IO口有限,而FPGA动辄数百个IO引脚,甚至上千个引脚,可以方便地连接外设,比如一个系统有多路AD、DA,单片机要仔细地进行资源分配与总线隔离,而FPGA由于有着丰富的IO资源,可以用不同IO引脚很容易地连接各外设。

• FPGA内部程序并行执行性

单片机程序是串行执行的,执行完一条才能执行下一条,在处理突发事件时只能调用有限的中断资源;而FPGA的不同逻辑可以并行执行,可以同时处理不同任务,这使得FPGA的工作效率更高。

利用硬件并行执行的优势,FPGA打破了顺序执行的模式,在每个时钟周期内完成更多的处理任务,超越了数字信号处理器(DSP)的运算能力。

• FPGA包含大量软核,可以方便地进行二次开发

FPGA甚至包含单片机和DSP软核,并且其IO数仅受FPGA自身IO限制,所以FPGA又是单片机和DSP的超集,也就是说,单片机和DSP能实现的功能,FPGA一般都能实现。

• FPGA设计灵活

FPGA最大的特点就是灵活,它能够实现你想实现的任何数字电路,可以定制各种电路。减少受制于专用芯片的束缚,真正为自己的产品量身定做,在设计的过程中可以灵活地更改设计。而且FPGA强大的逻辑资源和寄存器资源可以让用户轻松地发挥设计理念,其并行执行、硬件实现的方式可以满足设计中大量的高速电子线路设计需求。FPGA拥有比DSP更快的速度,可以实现非常复杂的高速逻辑,有着比ASIC(专用芯片)更短的设计周期和灵活性,免去昂贵的开版费用,而且可以随时裁减,增加用户想要的功能,达到规避设计风险、回避芯片厂商的限制的目的。另外,知识产权的概念不断涌现,仿制别人抄袭,FPGA能够防止别人抄袭,让设计的智慧得到充分保护,并让公司的利益在较长时间内得到保证。随着FPGA芯片供应商和第三方公司的重视,现在有现成的IP核,这进一步缩短了设计周期,减小了开发成本。现在很多免费的软IP核和硬IP核的出现更是压缩了成本。1.2 FPGA典型结构图

基于LUT的FPGA具有很高的集成度,其器件密度从数万门到数千万门不等,可以完成极其复杂的时序与逻辑组合的逻辑电路功能,因此其适用于高速、高密度的高端数字逻辑电路设计领域。其组成部分主要有可编程输入/输出单元、基本可编程逻辑单元、内嵌SRAM、丰富的布线资源、底层嵌入功能单元、内嵌专用单元等。FPGA的主要设计和生产厂家有赛灵思、Altera、Lattice、Actel、Atmel和QuickLogic等,其中最大的是美国赛灵思公司,在可编程市场中占有50%以上的份额,比其他所有竞争对手市场份额的总和还多。

FPGA是由存放在片内RAM中的程序来设置其工作状态的,因此,工作时需要对片内的RAM进行编程。用户可以根据不同的配置模式,采用不同的编程方式。

目前,FPGA市场占有率最高的两大公司——赛灵思公司和Altera生产的FPGA都是基于SRAM工艺的,需要在使用时外接一个片外存储器以保存程序。上电时,FPGA将外部存储器中的数据读入片内RAM,完成配置后,进入工作状态;掉电后SRAM存储的数据丢失,内部逻辑消失。这样,FPGA能反复使用,且无须专门的FPGA编程器,只需配备一片存储代码的FLASH存储器即可。

Actel、QuickLogic等公司还提供反熔丝技术的FPGA,它具有抗辐射、耐高低温、低功耗和速度快等优点,在军品和航空航天领域中应用较多,但这种FPGA不能重复擦写,开发初期比较麻烦,费用也比较昂贵。Lattice是ISP技术的发明者,在小规模PLD应用上有一定的特色。赛灵思公司的早期产品一般不涉及军品和宇航级市场,但目前已经有多款产品进入该领域。

目前主流的FPGA仍是基于查找表技术的,已经远远超出了先前版本的基本性能,并且整合了常用功能(如RAM、时钟管理和DSP)的硬核(ASIC型)模块。

此处,我们以XC2V3000为例,介绍典型的FPGA结构图。XC2V3000拥有300万门的超大系统容量,采用0.15 µm/0.12 µm CMOS 8层金属混合工艺设计,内核电压为1.5 V,根据输入/输出参考电压的不同设计可支持多种接口标准,内部时钟频率可达420 MHz。XC2V3000的内部结构如图1-1所示,它主要由可配置逻辑(CLB)、用户可编程I/O(IOB )、Block SelectRAM、数字时钟管理模块(DCM)、数字阻抗匹配模块(DCI )和硬件乘法器等组成。其中CLB用于实现FPGA的绝大部分逻辑功能;IOB用于提供封装引脚与内部逻辑之间的接口;BlockRAM用于实现FPGA内部的随机存取,它可配置RAM、双口RAM、FIFO等随机存储器;DCM用于提供灵活的时钟管理功能;硬件乘法器用于提高FPGA的数字信号处理能力。图1-1 Virtex系列产品XC2V3000的内部结构

1.可配置逻辑块(CLB)

如图1-2所示,XC2V3000的CLB模块由4个相同的Slice和附加逻辑构成,用于实现组合逻辑和时序逻辑。图1-2 Virtex系列产品XC2V3000的CLB结构

每个Slice由2个4输入函数发生器、进位逻辑、算术逻辑、存储逻辑和函数复用器组成。算术逻辑包括1个异或门(XORG)与1个专用与门(MULTAND),1个异或门可以使1个Slice实现2位全加操作,专用与门用于提高乘法器的效率。进位逻辑由专用进位信号和函数复用器(MUXC)组成,共同实现快速的算术加减法操作。具体结构详见图1-3和图1-4。图1-3 Virtex系列产品XC2V3000的SLICE结构图1-4 Virtex系列产品Slice结构

2.可编程输入/输出单元(IOB)

可编程输入/输出单元简称IOB,提供FPGA内部逻辑与外部封装引脚之间的接口,用于完成不同电气特性下对输入/输出信号的驱动和匹配。根据当前使用的I/O接口标准的不同,需要设置不同的接口电压Vcco和参考电压VREF。XC2V3000的I/O引脚分布在8个Bank中,同一个Bank的接口电压Vcco必须保持一致,不同Bank的接口电压Vcco允许不同。

如图1-5所示,XC2V3000的IOB模块含有6个存储单元,可以单独配置为边沿D触发器或锁存器,也可以成对实现DDR(Double-Data-Rate)输入和DDR输出。

DDR寄存器的结构见图1-6。

3.嵌入式块RAM(BRAM)

XC2V3000的Block SelectRAM资源丰富,其单位容量为18Kb双端口RAM,可以自由设定数据宽度和深度纵横比,并支持三种并发读/写(Read-During-Write)模式。图1-5 Virtex系列产品XC2V3000的IOB结构图1-6 DDR寄存器结构

4.丰富的布线资源

布线资源连通FPGA内部所有的单元,IOB、CLB、BRAM、DCM等都使用相同的内连阵列。XC2V3000内部共有16条全局时钟线,用于芯片内部全局时钟和全局复位/置位的布线。24条纵横交错的长线资源实现了芯片Bank间的高速通信和第二全局时钟信号的布线。

5.底层内嵌功能单元

XC2V3000集成了丰富的内嵌功能模块,如DCM(数字时钟管理器)、DCI(数字控制阻抗)、MicroBlaze软处理器核等,使其具有软、硬件联合设计的能力,可以用有限的资源完成系统级的设计任务。1.3 LUT原理

查找表(Look-Up-Table)简称为LUT,其本质就是一个RAM。目前FPGA中多使用4输入的LUT,所以每一个LUT可以看成一个有4位地址线的16×1的RAM。当用户通过原理图或HDL语言描述了一个逻辑电路以后,PLD/FPGA开发软件会自动计算逻辑电路的所有可能的结果,并把结果事先写入RAM。这样,每输入一个信号进行逻辑运算就相当于输入一个地址进行查表,找出地址对应的内容,然后将其输出即可。

我们知道,一个n输入的逻辑运算,不管是与或非运算还是异或运算,最多只可能存在2n种结果,如表1-1的4输入与门共有16种输出结果。这样就将实际逻辑电路转换成了LUT结构。表1-1 4输入与门的真值表

A,B,C,D由FPGA芯片的引脚输入后进入可编程连线,然后作为地址线连到LUT。LUT中已经事先写入了所有可能的逻辑结果,通过地址查找到相应的数据,然后将其输出,这样就实现了组合逻辑。该电路中D触发器是直接利用LUT后面的D触发器来实现的。时钟信号CLK由I/O脚输入后进入芯片内部的时钟专用通道,直接连接到触发器的时钟端。触发器的输出端与I/O脚相连,把结果输出到芯片引脚,这样PLD就完成了图1-7所示电路的功能(以上步骤都是由软件自动完成的,不需要人为干预)。图1-7 4输入与门的逻辑实现

这个电路是一个很简单的例子,只需要一个LUT加一个触发器就可以完成。对于一个LUT无法完成的电路,需要通过进位逻辑将多个单元相连,这样就可以用FPGA实现复杂的逻辑。

由于LUT主要适于SRAM工艺生产,所以目前大部分FPGA都是基于SRAM工艺的,而SRAM工艺的芯片在掉电后就会信息丢失,因此一定需要外加一片专用配置芯片,在上电的时候,由这个专用配置芯片把数据加载到FPGA中,然后FPGA就可以正常工作,由于配置时间很短,这不会影响系统正常工作。也有少数FPGA采用反熔丝或Flash工艺,这种FPGA就不需要外加专用的配置芯片。

由于需要反复烧写,FPGA实现组合逻辑的基本结构不可能像ASIC那样通过固定的与非门来完成,而只能采用一种易于反复配置的结构。查找表可以很好地满足这一要求,目前主流FPGA都采用了基于SRAM工艺或基于FLASH工艺的查找表结构,通过每次烧写改变查找表内容的方法实现对FPGA的重复配置。1.4 FPGA上电配置过程时序图

如前所述,FPGA是由存放在片内的RAM来设置其工作状态的,因此工作时需要对片内RAM进行编程。用户可根据不同的配置模式,采用不同的编程方式。Xilinx FPGA的常用配置模式有主串模式、从串模式、Select MAP模式等。

FPGA和CPLD不同,上电后不能直接工作,而是需要一个配置过程。Xilinx FPGA需要经过8个步骤才能正常地运行用户逻辑,整个流程如图1-8所示。图1-8 FPGA上电配置步骤

1.FPGA上电启动

FPGA工作的第一步就是给器件加电。Xilinx要求VCCINT(核心电压)先动,然后再是VCCO(I/O电压),最坏情况是它们之间不能相差1 s以上。在并行配置模式下,要求VCCO_2参考电压必须和FLASH参考电压相同。

在系统正常上电或者PROG-B是一个低脉冲时,FPGA开始配置寄存器空间。这段时间除定义好的配置引脚外,其他I/O引脚均被设置为高阻态(High-Z)。经多次测试,这个阶段需要30 ms左右的时间。

FPGA启动阶段最后一步就是配置启动模式。在PROG-B变高时,FPGA开始采集配置方式引脚(M3、M2、M1),并同时驱动CCLK输出。在这个阶段,有两种方法可以延迟FPGA的配置时序,一种是拉低INIT-B引脚,这是由于FPGA检测到自身还没有初始化完毕,不会进行接下来的操作步骤,这种状态一直保持到INIT-B引脚变高。另一种就是拉低PROG-B引脚,使FPGA处于等待配置状态。

2.FPGA数据加载

FPGA正常数据加载前,需要做一个器件与FLASH之间的同步检查。其方法是传输一个特殊的32位数值(0 x AA995566)到FPGA中,提示FPGA下面开始传输的是配置数据。这个步骤对用户来说是透明的,因为Xilinx ISE Bitstream Generator生成的.bit文件中已经自动加入了这个校验码。

在完成配置前的通信同步后,FPGA与FLASH之间还无法识别对方是什么器件,于是Xilinx就给每一个型号的FPGA设计了一个唯一的器件ID号,这个ID号可以在Xilinx配置手册中查到。如XC4VSX35,其ID号为0x02088093. FPGA需要从FLASH中读出这个器件号和自身比对,如果相同就继续下面的步骤,如果不同则配置失败,并打印出配置故障信息。

所有准备工作正常完成后,FPGA开始载入配置文件。这一步对大多数用户也是透明的,由器件自行完成。这也是配置过程中最耗时的步骤,时间从100 ms到几秒不等。在这个过程中,FPGA的所有可配置I/O根据HSWAPEN引脚的设置变为弱上拉(HSWAPE=1)或者高阻态(HSWAPE=0)。这个阶段的I/O引脚还没有变为用户需要的状态,也最有可能影响到其他外围电路的上电时序和运行。设计硬件电路时要特别注意并采取必要措施,如加入上下拉电阻或改变器件加电顺序来尽量避免或减少FPGA配置时对电路其他器件的影响。

配置文件载入完成后,为了验证数据的正确性,FPGA还自动设置了CRC校验(这个在ISE配置选项中也可以去掉,但是为了保证载入数据的正确性,这个是必须选择的)。如果CRC校验不正确,FPGA会自动把INIT-B拉低,放弃这次配置。用户必须把PROG-B引脚拉低,才能进行重新配置。

3.启动序列

CRC校验正确后,FPGA不会马上执行用户的逻辑,它还要进行一些自身内部电路的配置,如DCM锁定(DCMs to Lock)、全局写信号使能(Global Write Enable)等,这些信号的启动顺序也是在ISE配置选项中设置的。必须启动的序列为:释放DONE引脚;释放GTS信号,激活IO引脚;设置全局读/写使能、使能内部RAM和FIFOAssert,结束上电配置。

从上面论述可知,整个FPGA的上电配置正常时序如图1-9所示。

4.FPGA配置外围电路设计冲突与解决方法

FPGA上电配置整个过程大约需要200 ms~2 s,这段时间绝大多数其他外围电路器件都已经上电并正常工作了,而FPGA的通用I/O引脚还处于弱上拉(HSWAPEN=0)或者不定态(HSWAPEN=1),设计时就需要考虑这些器件上电初始化和FPGA通用I/O引脚有无时序冲突。例如,上电时,外围器件要求I/O引脚都为低电平,而FPGA默认是弱上拉,初始化电平产生了冲突;外围器件要求在上电复位后马上采集配置引脚进行初始化,而FPGA还处在配置状态,无法正确上拉或下拉I/O引脚,器件初始化错误;外围器件要求上电初始化前需要时钟锁相,FPGA配置时产生不了时钟,导致器件初始化失败等。解决这些冲突大致有3种方法,但前提都是要正确配置HSWAPEN引脚,这是因为在FPGA执行“器件上电”步骤后(5~30 ms内),它输出的I/O引脚状态是可以通过HSWAPEN设定的。图1-9 FPGA上电配置时序

方法1:在FPGA的I/O引脚外加上下拉电阻,阻值在1~10KΩ之间,根据实际需要确定。FPGA在上电后10 ms内能通过上下拉电阻把I/O引脚拉到用户需要的电平上,这样能满足上电较慢但是又要求固定电平的外围电路的要求,如功放发射开关和保护开关等。

方法2:FPGA可以在配置完成后产生一个全局复位信号,使外围电路硬件复位,再进行一次初始化操作。这样能解决PowrerPC、ARM这种上电初始化很快(在100ms内就能完成)、但由于FPGA没有配置完成而导致PowrerPC初始化错误的问题。而且这种方式不用外接多余的上下拉电阻,减少了电路设计复杂性。

方法3:FPGA可以在配置完成后产生一个Power Good信号,系统根据此标志再给外围其他有时序要求的器件上电,这样能满足DSP或者高速AD这种需要时钟锁定后再进行初始化的器件的要求。

这3种方法也可根据具体情况混合使用,能达到更好的效果。

5.结论

通过上述分析可知,FPGA上电是一个短暂而复杂的过程,设计时需要充分考虑FPGA上电配置时序和该过程中I/O引脚的各种状态对外围电路的影响。根据系统设计的具体情况,通过选用最合理的配置方式以及外围电路连接,达到了既不影响其他器件性能和整个系统功能,又简化和改善了整个电路设计的效果。1.5 FPGA基本开发流程

FPGA的设计流程就是利用EDA开发软件和编程工具对FPGA芯片进行开发的过程。FPGA的开发流程一般如图1-10所示,包括电路功能设计、设计输入、功能仿真、综合优化、综合后仿真、实现、布线后仿真、板级仿真以及芯片编程与调试等主要步骤。图1-10 FPGA开发的一般流程

1.电路功能设计

在系统设计之前,首先要进行的是方案论证、系统设计和FPGA芯片选择等准备工作。系统工程师根据任务要求,如系统的指标和复杂度,对工作速度和芯片本身的各种资源、成本等方面进行权衡,选择合理的设计方案和合适的器件类型。一般都采用自顶向下的设计方法,把系统分成若干个基本单元,然后再把每个基本单元划分为下一层次的基本单元,一直这样做下去,直到可以直接使用EDA元件库为止。

2.设计输入

设计输入是将所设计的系统或电路以开发软件要求的某种形式表示出来,并输入给EDA工具的过程。常用的方法为硬件描述语言(HDL)和原理图输入方法等。原理图输入方式是一种最直接的描述方式,在可编程芯片发展的早期应用比较广泛,它将所需的器件从元件库中调出来,画成原理图。这种方法虽然直观且易于仿真,但效率很低,不易维护,不利于模块构造和重用。其更主要的缺点是可移植性差,当芯片升级后,所有的原理图都需要做一定的改动。目前,在实际开发中应用最广的就是HDL语言输入法,利用文本描述设计,可以分为普通HDL和行为HDL。普通HDL有ABEL、CUR等,支持逻辑方程、真值表和状态机等表达方式,主要用于简单的小型设计。而在中大型工程中,主要使用行为HDL,其主流语言是Verilog HDL和VHDL。这两种语言都是美国电气与电子工程师协会(IEEE)的标准,其共同的突出特点是语言与芯片工艺无关,利于自顶向下设计,便于模块的划分与移植,可移植性好,具有很强的逻辑描述和仿真功能,而且输入效率很高。

3.功能仿真

功能仿真,也称为前仿真,是在编译之前对用户所设计的电路进行逻辑功能验证,此时的仿真没有延迟信息,仅对初步的功能进行检测。仿真前,要先利用波形编辑器和HDL等建立波形文件和测试向量(即将所关心的输入信号组合成序列),仿真结果将会生成报告文件并输出信号波形,从中便可以观察各个节点信号的变化。如果发现错误,则返回修改逻辑设计。常用的工具有Model Tech公司ModelSim、Sysnopsys公司的VCS和Cadence公司的NC-Verilog及NC-VHDL等软件。功能仿真虽然不是FPGA开发过程中的必需步骤,但却是系统设计中最关键的一步。

4.综合

所谓综合就是将较高级抽象层次的描述转化成较低层次的描述。综合优化根据目标与要求优化生成的逻辑连接,使层次设计平面化,以便用FPGA布局布线软件进行实现。就目前的层次来看,综合优化是指将设计输入编译成由与门、或门、非门、RAM、触发器等基本逻辑单元组成的逻辑连接网表,而并非真实的门级电路。真实具体的门级电路需要利用FPGA制造商的布局布线功能,根据综合后生成的标准门级结构网表来产生。为了能转换成标准的门级结构网表,HDL程序的编写必须符合特定综合器所要求的风格。由于门级结构、RTL级的HDL程序的综合是很成熟的技术,所有的综合器都可以支持这一级别的综合。常用的综合工具有Synplicity公司的Synplify/Synplify Pro软件以及各个FPGA厂家自己推出的综合开发工具。

5.综合后仿真

综合后仿真检查综合结果是否与原设计一致。在仿真时,把综合生成的标准延时文件反标注到综合仿真模型中,可估计门延时带来的影响。但这一步骤不能估计线延时,因此估计结果和布线后的实际情况还有一定的差距,并不十分准确。目前的综合工具较为成熟,一般的设计可以省略这一步,但如果在布局布线后发现电路结构和设计意图不符,则需要回溯到综合后仿真来确认问题所在。在功能仿真中介绍的软件工具一般都支持综合后仿真。

6.实现与布局布线

实现是将综合生成的逻辑网表配置到具体的FPGA芯片上,布局布线是其中最重要的过程。布局是指将逻辑网表中的硬件原语和底层单元合理地配置到芯片内部的固有硬件结构上,这往往需要在速度最优和面积最优之间进行选择。布线是指根据布局的拓扑结构,利用芯片内部的各种连线资源,合理正确地连接各个元件。目前,FPGA的结构非常复杂,特别是在有时序约束条件时,需要利用时序驱动的引擎进行布局布线。布线结束后,软件工具会自动生成报告,提供有关设计中各部分资源的使用情况。由于只有FPGA芯片生产商对芯片结构最为了解,所以布局布线必须采用芯片开发商提供的工具。

7.时序仿真与验证

时序仿真,也称为后仿真,是指将布局布线的延时信息反标注到设计网表中来检测有无时序违规(即不满足时序约束条件或器件固有的时序规则,如建立时间、保持时间等)现象。时序仿真包含的延迟信息最全,也最精确,能较好地反映芯片的实际工作情况。由于不同芯片的内部延时不一样,不同的布局布线方案也给延时带来不同的影响。因此在布局布线后,通过对系统和各个模块进行时序仿真,分析其时序关系,估计系统性能,以及检查和消除竞争冒险是非常有必要的。在功能仿真中介绍的软件工具一般都支持综合后仿真。

8.板级仿真与验证

板级仿真主要应用于高速电路设计中,对高速系统的信号完整性、电磁干扰等特征进行分析,一般都用第三方工具进行仿真和验证。

9.芯片编程与调试

设计的最后一步就是芯片编程与调试。芯片编程是指产生使用的数据文件(位数据流文件,Bitstream Generation),然后将编程数据下载到FPGA芯片中。其中,芯片编程需要满足一定的条件,如编程电压、编程时序和编程算法等方面。逻辑分析仪(Logic Analyzer,LA)是FPGA设计的主要调试工具,但需要引出大量的测试引脚,且其价格昂贵。目前,主流的FPGA芯片生产商都提供了内嵌的在线逻辑分析仪(如Xilinx ISE中的ChipScope、Altera QuartusII中的SignalTapII以及SignalProb)来解决上述矛盾,它们只需要占用芯片少量的逻辑资源,具有很高的实用价值。第2章Verilog语法介绍2.1 Verilog HDL语言简介2.1.1 什么是Verilog HDL

Verilog HDL是硬件描述语言的一种,用于数字电子系统设计。它允许设计者进行各种级别的逻辑设计,可以用它进行数字逻辑系统的仿真验证、时序分析、逻辑综合。它和VHDL是目前应用最广泛的硬件描述语言。2.1.2 Verilog HLC的历史

Verilog HDL在1983年由GDA(GateWay Design Automation)公司的Phil Moorby首创。Phil Moorby后来成为Verilog-XL的主要设计者和Cadence(Cadence Design System)公司的第一个合伙人。在1984—1985年,Moorby设计出了名为Verilog-XL的仿真器;1986年,他对Verilog HDL的发展做出了另一个巨大贡献,提出了用于快速门级仿真的XL算法。

随着Verilog-XL算法的成功,Verilog HDL语言得到迅速的发展。1989年,Cadence公司收购GDA公司,Verilog HDL语言成为Cadence公司的私有财产。1990年,Cadence公司决定公开Verilog HDL语言,于是成立了OVI(Open Verilog International)组织来负责促进Verilog HDL语言的发展。基于Verilog HDL的优越性,IEEE于1995年制定了Verilog HDL的IEEE标准,即Verilog HDL1364—1995,2001年发布了Verilog HDL1364—2001标准。

Verilog HDL可以进行各种级别的逻辑设计,也可以进行数字逻辑系统的仿真验证、时序分析与逻辑综合,是目前应用最广泛的硬件描述语言。据有关文献报道,目前在美国使用Verilog HDL进行设计的工程师大约有10多万人,全美国有200多所大学的教授使用Verilog硬件描述语言来进行设计。在我国台湾地区几乎所有著名大学的电子和计算机工程系都开设有与Verilog相关的课程。

Verilogs是由Gateway设计自动化公司的工程师于1983年末创立的。当时Gateway设计自动化公司还叫作自动集成设计系统(Automated Integrated Design Systems),1985年公司将名字改成了前者。该公司的Phil Moorby完成了Verilog的主要设计工作。1990年,Gateway设计自动化公司被Cadence公司收购。

20世纪90年代初,开放Verilog国际(Open Verilog International,OVI)组织(即现在的Accellera)成立,Verilog面向公有领域开放。1992年,该组织寻求将Verilog纳入电气电子工程师学会标准。最终,Verilog成为了电气电子工程师学会1364—1995标准,即通常所说的Verilog-95。

设计人员在使用这个版本的Verilog的过程中发现了一些可改进之处。为了解决用户在使用此版本Verilog过程中反映的问题,对Verilog进行了修正和扩展,这部分内容后来再次被提交给电气电子工程师学会。这个扩展后的版本后来成为了电气电子工程师学会1364—2001标准,即通常所说的Verilog—2001。Verilog—2001是对Verilog—95的一个重大改进版本,它具备一些新的实用功能,例如敏感列表、多维数组、生成语句块、命名端口连接等。目前,Verilog—2001是Verilog的最主流版本,被大多数商业电子设计自动化软件包支持。

2005年,Verilog再次进行了更新,即电气电子工程师学会1364—2005标准。该版本只对上一版本进行了细微修正。这个版本还包括一个相对独立的新部分,即Verilog-AMS。这个扩展使得传统的Verilog可以对集成的模拟和混合信号系统进行建模。易与电气电子工程师学会1364—2005标准混淆的是加强硬件验证语言特性的SystemVerilog(电气电子工程师学会1800-2005标准),它是Verilog—2005的一个超集,它是硬件描述语言、硬件验证语言(针对验证的需求,特别加强了面向对象特性)的一个集成。2.2 Verilog HDL和VHDL语言比较

Verilog HDL和VHDL都是用于逻辑设计的硬件描述语言,并且都已成为IEEE标准。VHDL在1987年成为IEEE标准,Verilog HDL则在1995年才正式成为IEEE标准。

之所以VHDL比Verilog HDL更早成为IEEE标准,是因为VHDL是美国军方组织开发的,而Verilog HDL来自于私人企业。

共同点: 能形象化地抽象表示电路的结构和行为。 支持逻辑设计中层次与领域的描述。 可借用高级语言的精巧结构来简化电路的描述。 具有电路仿真与验证机制以保证设计的正确性。 支持电路描述由高层到低层的综合转换。 硬件描述与实现工艺无关。 便于文档管理,易于理解和设计重用。

不同点: Verilog HDL早在1983年就已推出,因而拥有更广泛的设计群体,成熟的资源也比VHDL丰富。 与VHDL相比,Verilog HDL最大的优点在于它是一种非常容易掌握的硬件描述语言,只要有C语言的编程基础,可以很快掌握这门语言。而VHDL不直观,学习起来困难一些。 目前版本的Verilog HDL和VHDL在行为级抽象建模的覆盖范围方面也有所不同,一般认为Verilog HDL在系统级抽象方面比VHDL略差一些,而在门级开关方面则强得多。2.3 Verilog语法介绍2.3.1 模块

模块的定义以关键字module开始,模块名、端口列表、端口声明和可选的参数声明必须出现在其他部分的前面,endmodule语句必须为模块的最后一条语句。端口是模块与外部环境交互的通道,只有在模块有端口的情况下才需要有端口列表和端口声明。模块内部的5个组成部分是:变量声明、数据流语句、低层模块实例、行为语句块以及任务和函数。

端口是模块与外界环境交互的接口。对于外部环境来说,模块内部是不可见的,对模块的调用只能通过其端口进行。这种特点为设计者提供了很大的灵活性,只要接口保持不变,模块内部的修改并不会影响到外部环境。

在模块的定义中包括一个可选的端口列表。如果模块和外部环境没有交换任何信号,则可以没有端口列表。

端口列表中的所有端口必须在模块中进行声明,Verilog中的端口具有3种类型,即input、output与inout。在Verilog中,所有的端口隐含地声明为wire类型,因此如果希望端口具有wire数据类型,只要将其声明为3种类型之一即可。如果输出类型的端口需要保存数值,则必须将其显式地声明为reg数据类型。

不能将input和inout类型的端口声明为reg数据类型,这是因为reg类型的变量是用来保存数值的,而输入端口只反映与其相连的外部信号的变化,并不能保存这些信号的值。

注意,在Verilog中,也可以使用ANSI C风格进行端口声明。这种风格的优点是避免了端口名在端口列表和端口声明语句中的重复。如果声明中未指明端口的数据类型,则默认端口具有wire数据类型。

举例如下: module fulladd4 (output reg [3:0] sum, output reg c_out, input [3:0] a,b, input c_in); …… endmodule

端口连接规则:将一个端口看成由相互连接的两个部分组成,一部分位于模块内部,另一部分位于模块外部。当在一个模块中调用(实例引用)另一个模块时,端口之间的连接必须遵守一些规则。

输入端口:从模块内部来看,输入端口必须为线网数据类型;从模块外部来看,输入端口可以连接到线网或reg数据类型的变量。

输出端口:从模块内部来看,输出端口可以是线网或者reg数据类型;从模块外部来看,输出必须连接到线网类型的变量,而不能连接到reg类型的变量。

输入/输出端口(必须为wire):从模块内部来看,输入/输出端口必须为线网数据类型;从模块外部来看,输入/输出端口也必须连接到线网类型的变量。

位宽匹配:在对模块进行调用的时候,Verilog允许端口的内、外两个部分具有不同的位宽。一般情况下,Verilog仿真器会对此发出警告。

未连接端口:Verilog允许模块实例的端口保持未连接的状态。例如,如果模块的某些输出端口只用于调试,那么这些端口可以不与外部信号连接。

端口与外部信号的连接:在对模块进行调用的时候,可以使用两种方法将模块定义的端口与外部环境中的信号连接起来,即按顺序连接以及按名字连接。但两种方法不能混合在一起使用。

顺序端口连接:需要连接到模块实例的信号必须与模块声明时目标端口在端口列表中的位置保持一致。2.3.2 常量

任何一种计算机语言都离不开常量和变量。

Verilog HDL语言中描述电路的基本常量有4种,分为数值常量和非数值常量:(1)0:代表逻辑0或“假”;(2)1:代表逻辑1或“真”;(3)x:表示不确定;(4)z:表示高阻。

这4种值的解释都内置于Verilog HDL语言中。数值常量“0”和“1”在逻辑电路中被解释为低电位和高电位。如一个电路的值为z,则该电路处于高阻抗状态,也就是电路处于断开状态。一个电路的值为x,则该电路处于不确定的状态。在逻辑门电路的输入或一个逻辑表达式中,为“z”的值通常解释为电路断开,其他值都被解释为导通。此外,x值和z值不分大小写,也就是说,值x、z与值X、Z相同。

电路常量表示的是一条导线的状态,我们称其为导线的值。除了导线的基本电路常量之外,Verilog HDL程序设计中还使用整型数、实数和字符串型3类常量。这3类常量主要用于电路的辅助描述,在实际电路中并没有这3类数值。

整型数常用基数表示方法给出,这种表示法要求书写形式固定。书写整型常数时,前面要写出二进制的位数,跟着用“’”号将进制标识和数字隔开,接下来再书写该进制的数码。在整型数或实数的描述中可以使用下画线,符号“_”可以随意用在整型数或实数中,它们就数量本身而言没有意义。它们能用来提高易读性,唯一的限制是下画线符号不能用作为首字符。

例如,16’b0001001000111111和16’h1_23_f,它们分别表示16位的二进制数0001001000111111和16个二进制位的十六进制整数123f,用十进制实数表示就是4671。

Verilog HDL规定十进制实数与通常的写法一样。

HDL代码经常在表达式和数组的边界使用常量。这些值在模块内是固定的,不可修改。一个很好的设计惯例是用符号常量取代这些hard literal,这样做可使代码清晰,便于后续维持及修改。在Verilog中,可以使用localparam(本地参数)来声明常量。例如,我们可以声明一个数据总线的位宽及数据范围为: localparam DATA_WIDYH = 8, DATA_RANGE = 2*DATA_WIDYH - 1;

或者定义一个符号端口名称: localparam UART_PORT = 4’b0001, LCD_PORT = 4’b0010, MOUSE_PORT = 4’b0100;

声明中的表达式,如2*DATA_WIDTH-1,在预编译时计算,因此它不会引用任何物理电路。一般使用大写字母来表示常量。

常量的使用可用实例来说明。考虑一个带进位的加法器的代码。为了正确执行加法运算,可将输入的值手动扩展1位,并取和的最高位为进位。代码如下:

代码1 使用hard literal的加法器 module adder_carry_hard_lit ( input [3:0] a, input [3:0] b, output [3:0] sum, output cout // carry output ); // signal declaration wire [4:0] sum_ext; // body assign sum_ext = {1’b0, a} + {1’b0, b}; assign sum = sum_ext[3:0]; assign cout = sum_ext[4]; endmodule

代码所示为4位加法器。hard literal,即硬文字,如用来表示数据范围的3和4、wire [4:]及sum_ext[3:0]以及最高位sum_ext[4]。如果需要把它修改为8位的加法器,那么就需要手动修改这些hard literal。如果代码很复杂且多处都引用这些hard literal,那么修改起来将是件痛苦的事情,同时也有可能带来不必要的错误。

为了提高代码的可读性,我们可以使用符号常量,如用N来代表加法器的位数。修改后的代码如下所示:

代码2 使用常量的加法器 module adder_carry_local_par ( input [3:0] a, input [3:0] b, output [3:0] sum, output cout // carry output ); // constant declaration localparam N = 4, N1 = N - 1; // signal declaration wire [4:0] sum_ext; // body assign sum_ext = {1’b0, a} + {1’b0, b}; assign sum = sum_ext[N1:0]; assign cout= sum_ext[N]; endmodule

常量使代码更易于理解和维持。2.3.3 parameter与define的区别

程序中多次出现某些数字,如延迟时间或变量的宽度,有时可能要改变这些值,这种情况下经常要用到参数。参数一经声明,就视其为一个常量,在整个仿真过程中不再改变。 parameter LINELENGTH = 132, ALL_X_S = 16’bx; parameter BIT = 1, BYTE = 8, PI = 3.14; parameter STROBE_DELAY = (BYTE+BIT)/2;

使用参数可以提高程序的可读性,也利于修改。

1.语法定义 parameter xx = yy; `define xx yy (注:句尾无分号)

2.作用范围

parameter作用于声明的那个文件;`define从编译器读到这条指令开始到编译结束都有效,或者遇到`undef命令失效。

3.功能

状态机可以用parameter定义,但是不推荐使用`define宏定义的方式,因为'define宏定义在编译时自动替换整个设计中所定义的宏,而parameter仅仅定义模块内部的参数,定义的参数不会与模块外的其他状态混淆。例如一个工程里面有两个module各包含一个FSM,设计时都有IDLE这一名称的状态,如果使用'define宏定义就会发生混淆,如果使用parameter则不会造成任何不良影响。 `define

一旦`define指令被编译,其在整个编译过程中都有效。例如,通过一个文件中的`define指令,定义的常量可以在其他文件中被调用,直到遇到`undef。

parameter只在定义的文件中有效,在其他文件中无效,举例如下。 `define plus 3'd0 `define minuus 3'd1 `define band 3'd2 `define bor 3'd3 `define oppo 3'd4 module test( input[7:0] a, input[7:0] b, input[2:0] opcode, output[7:0]out); reg [7:0] rout; always @(a or b or opcode) case(opcode) `plus: rout=a+b; `minuus: rout=(a>b)?b:a; `band: rout=a & b; `bor: rout =a | b; `oppo: rout =~a; default: rout=8'hxx; endcase assign out=rout; endmodule 2.3.4 reg和wire

wire表示直通,即输入−有变化,输出马上无条件地反映(如与、非门的简单连接)。

reg表示一定要有触发,输出才会反映输入的状态。

reg相当于存储单元,wire相当于物理连线。reg表示一定要有触发,没有输入的时候可以保持原来的值,但不直接和实际的硬件电路对应。

两者的区别是:寄存器型数据保持最后一次的赋值,而线型数据需要持续的驱动。wire使用在连续赋值语句中,而reg使用在过程赋值语句(initial,always)中。wire若无驱动器连接,其值为z,reg默认初始值为不定值x。

在连续赋值语句中,表达式右侧的计算结果可以立即更新表达式的左侧。在理解上,相当于一个逻辑之后直接连了一条线,这个逻辑对应于表达式的右侧,而这条线就对应于wire。在过程赋值语句中,表达式右侧的计算结果在某种条件的触发下放到一个变量当中,而这个变量是可以声明成reg类型的。根据触发条件的不同,过程赋值语句可以为不同的硬件结构建立模型:如果这个条件是时钟的上升沿或下降沿,那么这个硬件模型就是一个触发器;如果这个条件是某一信号的高电平或低电平,那么这个硬件模型就是一个锁存器;如果这个条件是赋值语句右侧任意操作数的变化,那么这个硬件模型就是一个组合逻辑。

对组合逻辑输出变量,可以直接用assign。即如果不指定为reg类型,那么就默认为1位wire类型,故无须指定1位wire类型的变量。若专门指定出wire类型,则可能是多位或为使程序易读。wire只能被assign连续赋值,reg只能在initial和always中赋值。

输入端口可以由wire/reg驱动,但输入端口只能是wire;输出端口可以是wire/reg类型,输出端口只能驱动wire;若输出端口在过程块中赋值则为reg型,若在过程块外赋值则为net型(wire/tri )。用关键词inout声明一个双向端口,inout端口不能声明为reg类型,只能是wire类型。

默认信号是wire类型,reg类型要声明。这里所说的默认是指输出信号声明成output时为wire。如果是模块内部信号,必须声明成wire或者reg。

对于always语句而言,赋值要声明成reg,连续赋值assign的时候要用wire。

模块调用时信号类型确定方法总结如下:

• 信号可以分为端口信号和内部信号。出现在端口列表中的信号是端口信号,其他的信号为内部信号。

• 对于端口信号,其输入端口只能是net类型,输出端口可以是net类型,也可以是register类型。若输出端口在过程块中赋值则为register类型,若在过程块外赋值(包括实例化语句)则为net类型。

• 内部信号类型与输出端口相同,可以是net或register类型,判断方法也与输出端口相同。若在过程块中赋值,则为register类型;若在过程块外赋值,则为net类型。

• 有些时候信号既需要在过程块中赋值,又需要在过程块外赋值,这种情况是有可能出现的,如决断信号。这时需要一个中间信号转换。

下面所列是常出的错误及相应的错误信息(error message)。

• 用过程语句给一个net类型的或忘记声明类型的信号赋值。 信息: illegal …… assignment.

• 将实例的输出连接到声明为register类型的信号上。 信息: has illegal output port specification.

• 将模块的输入信号声明为register类型。 信息: incompatible declaration, …… 2.3.5 操作符

在电子电路的设计当中,从硬件的角度考虑,人们时常将运算符称作操作符。同其他程序设计语言一样,EDA硬件描述语言也要定义运算和操作符号。不论是哪一种运算或操作符,都应该有实际的电路与之对应,运算或操作的过程最终还要通过实际的电子电路来完成,这一工作是由硬件语言的设计者实现的。

Verilog HDL中的运算和操作符可以分为:算术类操作符、关系类操作符、全等类操作符、逻辑类操作符、按位操作符、归约操作符、移位操作符、条件操作符、连接和复制操作符等,具体如表2-1所示。表2-1 Verilog操作符

此表中操作符的优先级排列顺序是,列从上到下,最高优先级(顶行)到最低优先级(底行)排列,处在同一级别操作符优先级相同,在表达式中先完成前面的操作。例如,a–b+c是先“–”后“+”。2.3.6 assign和always语句

Verilog HDL语言的描述主要有数据流描述和行为描述两种方式。数据流描述方式主要用assign语句,建立元器件间的连接,因而assign语句就是“连接”语句。行为描述主要用always结构。Verilog HDL语言中规定每个描述语句以“;”结束。

• 连接语句assign

Verilog HDL中线路的直接连接使用assign语句描述。assign语句的格式如下: assign 网线变量 = 表达式;

连接语句只要在右端表达式的操作数上有事件(值的变化)发生时,表达式即被计算,如果表达式结果值有变化,新结果就赋给左边的网线变量。格式右面的表达式常为逻辑运算或条件操作表达式。右端的表达式变量可以是网线型,也可以是寄存器类型。

以“?: ”形式表示的条件操作表达式是用来表示分支情况的连接语句,具有依条件来变换连接的功能。这与程序语言中if语句的作用很相似。

需要强调的是,不论程序中有多少个assign连接语句,它们都是并行独立执行的。

• always语句

行为描述结构主要有初始化的initial结构和常态化的always结构。

一个程序中可以包含任意多个initial或always结构,这些结构都是相互并行执行的,即这些结构的执行顺序与其书写的顺序无关。执行一个initial结构或always结构,产生一个单独的控制流,所有的initial和always结构都在0时刻开始并行执行。

下面重点介绍always结构。

always结构是不断重复执行的过程,结构内部有多个语句的时候,一般是顺序执行的,除非用特殊的表达方式使语句并行执行。

always最简单的是由一个赋值语言组成的语句体,如下面的定义时钟例子。 always Clk = ~Clk ; //此语句将无限循环执行。

这里的语句是Clk= ~Clk,always是一个结构的关键字(保留字)。

在一行中的“//”符号表示后面是注释部分。如果要用多行进行注释,那么注释部分前端要用“/*”符号,注释的结束处要用“*/”符号。

此always结构有一个过程性赋值。因为always结构是重复执行的,并且在此例中没有延时控制,所以过程语句将在0时刻无限循环取值0和1。这种always结构体内的语句在执行时必须带有某种延时控制,不然无法达到所希望的效果。

语句的延时控制描述,规定在语句的前面加“#”和正整数。对于上面的always结构,如果加上5个单位的延时控制,表示如下。 always #5 Clk = ~Clk;

这个语句执行时将产生时钟周期为10个单位时间长度的波形,因为每隔5个单位时间,Clk的值就取反。语句前面不加“#”就表示没有延时,这个取反的变化只会在原地进行,不会出现带延长时间的

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载