FPGA设计——从电路到系统(txt+pdf+epub+mobi电子书下载)


发布时间:2020-07-03 10:10:08

点击下载

作者:蔡述庭,陈平,棠潮,吴泽雄

出版社:清华大学出版社

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

FPGA设计——从电路到系统

FPGA设计——从电路到系统试读:

前言

现场可编程逻辑阵列(Field Programmable Gate Array,FPGA)器件广泛应用于汽车电子、多媒体广播、计算机和存储、消费电子、工业、医疗、军事、航空航天、通信、测量等领域。FPGA既广泛使用在通信基站、大型路由器等高端网络设备中,也使用在显示器(电视)、投影仪等日常家用电器里,FPGA已经从最早的只应用于辅助功能和胶合逻辑(连接各种功能块以及集成电路的逻辑电路)的简单器件,发展到现今成为众多产品的核心器件。使用FPGA器件,可以使开发时间缩短1/2~1/3,因此FPGA成为实现“少量多品种”和“产品周期短”市场中不可缺少的器件之一。

针对FPGA器件的使用和开发离不开EDA工具的支持,因此,一般FPGA器件厂商也会提供相应的EDA开发工具,比如Altera公司的Quartus Ⅱ/Qsys开发平台、Xilinx公司的ISE/Vivado开发平台。其他公司器件都有自己的开发工具,但开发流程基本保持一致。熟悉这些工具对FPGA的开发无疑是基础和重要的,因此本书的结尾部分主要侧重于通过一些实验来让大家了解和熟悉Quartus与Vivado工具的使用。

一般而言,使用FPGA有两个层次:电路级和系统级。简单来说,电路级侧重于在FPGA上实现某些功能电路,或者实现信号处理算法,如FFT、CORDIC、OFDM,或者实现通信系统中部分通信协议,如基站与射频覆盖设备中用得最广泛的通用公众无线接口协议(CPRI)。系统级这里主要是指FPGA中使用CPU,如Nios Ⅱ、MicroBlaze、ARM、PowerPC等软核、硬核,构成一个比较大的SoC系统,在此基础上可以进一步进行软件应用开发。

因此,本书的编写也主要遵从这个思路,系统描述了从电路到系统的FPGA设计方法、流程、技巧以及工具使用。

第1章简要介绍了FPGA设计概论。

第2章对硬件描述语言Verilog HDL进行了简单介绍。

第3章对使用Verilog HDL对包括加法器在内的基础电路设计进行了概述。

第4章主要介绍了逻辑综合的概念以及Synplify、DC工具。

第5章主要介绍了测试平台的撰写以及行为级仿真工具ModelSim的使用。

第6章描述了在通信调制中使用较多的CORDIC算法。

第7章对无线通信系统中的CPRI等协议进行了阐述,并详述了其FPGA实现。

第8章描述了通信系统直放站中数字接口电路部分的FPGA设计。

第9章通过一个电机控制实例来描述FPGA系统级的设计过程,以Nios Ⅱ为核心处理器来搭建一个SoC系统。

由于FPGA器件的广泛应用,对于FPGA器件组成的系统的测试与认证也成了非常重要的一个课题,故第10章介绍了航空航天领域的一个硬件系统设计标准DO-254,侧重介绍其适应于FPGA器件的流程与准则。

为了便于读者实践操作,第11章给出了丰富的FPGA设计实验,包括基础实验和基于Qsys、SOPC的综合实验,如μClinux在Nios Ⅱ上的移植;还特别介绍了Vivado HLS工具的使用。HLS作为一种目前商业化的高层次综合工具,对系统级设计人员及算法设计人员都具有非常大的吸引力。书中给出的源代码都经过了实际项目的检验,读者可在清华大学出版社网站下载相关的源代码。

本书可作为电子、通信、自动化、计算机科学与技术等相关专业的高年级本科生及研究生的教学用书,也可作为从事FPGA设计工作的工程师的参考图书。

在编写本书的过程中,引用了大量参考文献,对此表示感谢。书中不少资料来自Altera和Xilinx公司,Altera大学计划负责人陈卫中博士和Xilinx大学计划负责人谢凯年博士均为本书的编写提供了大量资料与帮助,对两位致以谢意。感谢清华大学出版社的工作人员为本书顺利出版所付出的辛勤劳动。本科生张骏盛对Vivado HLS实验进行了验证,研究生李静园、林卓胜为本书提供了相关协助,谢志才、林燕云同学对本书亦有贡献,一并表示感谢。

由于作者水平有限,书中难免会有纰漏之处,敬请专家和读者批评指正,有兴趣的读者可发送邮件到liux@tup.tsinghua.edu.cn。蔡述庭2014年5月第1章 FPGA设计概论

集成电路一般可以分为全定制ASIC(Application Specific Integrated Circuit)、半定制ASIC以及用户可编程器件。而用户可编程器件已经由初始的一些小集成规模的PLD(Programmable Logic Device)发展到集成度更高的FPGA(Field Programmable Gate Array)器件。

ASIC器件具有以下特征:设计必须送到晶圆厂(foundry)进行流片,费用高,从行为级到最下面的物理版图都要自己设计,但设计出来的芯片具有更好的性能和更低的功耗。

FPGA器件具有以下特点:产品响应速度快,可以小批量生产,可编程,不需要流片,不需要进行后端设计,易升级,而且可以适用于一些特殊应用,如可重构计算等。具体而言,二者的特征比较如图1.1所示。图1.1 FPGA与ASIC的特征比较1.1 FPGA芯片结构与特点

FPGA芯片内部结构主要包括以下部分。

1. 可编程输入/输出单元(IOB(Xilinx),IOE(Altera))

可编程输入/输出单元简称I/O单元,是芯片为连接外围电路所提供的接口单元,完成对输入/输出信号在不同电气特性下的驱动与匹配要求。FPGA内的I/O按组(BANK)分类,每组都分别独立地提供不同的I/O电平。通过软件的灵活配置,可适配不同的电气标准与I/O物理特性,可以调整驱动电流的大小,可以改变上、下拉电阻。

2. 可配置逻辑块(CLB(Xilinx))

CLB是用户实现自定义逻辑电路的基本逻辑单元。CLB的数量会根据FPGA芯片型号的不同而有所不同,但是每个CLB都包含一个可配置开关矩阵,此矩阵由一个4输入的多路复用器(MUX)和触发器组成。此开关矩阵具有较高的灵活性,可以对其进行配置来实现组合逻辑、移位寄存器或RAM。在Altera器件中,与CLB相类似的模块为逻辑阵列块(LAB)。

3. 数字时钟管理模块(DCM)

由于FPGA应用开发都需要涉及高速电路的时序分析,因此FPGA对系统内部以及其外围设备的时钟信号源都有非常严格的要求。目前,大多数厂商的FPGA芯片均提供数字时钟管理以及相位环路锁定(PLL),其中PLL能够提供精确的时钟综合,降低时钟抖动,并实现过滤功能。

4. 嵌入式块RAM(BRAM)

现今市场上的FPGA芯片大都具有内嵌的块RAM(Block RAM),这种Block RAM是芯片内部独立的资源,用户通过调用这部分资源可以更好地节约FF触发器、4-LUT等底层可编程单元,最大限度地发挥器件功能;此外,Block RAM是一种可配置的硬件结构,可以被综合成ROM、单端口RAM、双端口RAM以及FIFO等常用存储结构,而且这些存储器的位宽和深度都可以根据具体需要进行灵活配置。

5. 丰富的布线资源

FPGA内部的逻辑门单元通过布线连通,信号的传输速度与输入/输出驱动能力受布线的工艺和长度的影响。FPGA内部丰富的布线资源可以大致分为4类:全局布线资源、长线资源、短线资源和分布式资源。可以根据设定的约束条件,使用FPGA开发软件的布局布线器来连通各个单元。实际上,布线资源直接影响着设计和结果。

6. 底层内嵌功能单元

DLL、PLL、DSP和CPU等软处理器就是FPGA内嵌功能模块的主要组成部分。如今越来越丰富的底层内嵌功能单元,使得FPGA芯片不仅具备了软件和硬件协同设计的能力,而且也使FPGA逐步成为系统级的设计工具,向SoC(System on Chip)方向发展。此外,DLL与PLL模块也大大地简化了提高时钟精度、降低时钟抖动以及时钟倍频和分频的过程。

7. 内嵌专用硬核

相对于底层嵌入的软核而言,FPGA内嵌专用硬核相当于ASIC电路。目前FPGA集成了一些专用DSP乘法器硬核,从而快速地完成复杂乘法运算。Xilinx的高端芯片不仅集成了PowerPC、Microblaze、Picoblaze、ARM系列CPU,还内嵌了DSP Core模块,通过利用这些硬核,用户可以二次开发标准的DSP处理器。Altera器件集成的CPU为NIOS Ⅱ、ARM等。

从上述的7个结构组成部分可以看出,FPGA内部采用的设计结构与传统的CPLD还是有很大的不同。具体来说,多个逻辑单元组成了FPGA中的逻辑阵列块,而逻辑单元中又含有多个查找表与寄存器。FPGA与CPLD工作原理上的区别就在于查找表的运用,CPLD主要用行列式的乘积去实现。而FPGA的特点如下:(1)FPGA内部具有丰富的FF触发器结构,因此FPGA更适合于完成时序逻辑电路的设计;(2)FPGA中存在分段式布线结构,这决定了其信号在布线上的延时是不可预测的;(3)FPGA具有较大的编程灵活性,主要通过改变内部连线的布线来编程,其可在逻辑门下完成编程;(4)FPGA的集成度比较高,具有更复杂的布线结构和逻辑实现;(5)FPGA是基于SRAM编程的,而非EEPROM或Flash存储器,因此可以进行任意次编程,可在工作中快速编程,从而实现板级和系统级的动态配置。1.2 FPGA工作原理

如上所述,FPGA是在PAL(Programmable Array Logic)、GAL(Generic Array Logic)、EPLD(Erasable Programmable Logic Device)、CPLD(Complex Programmable Logic Device)等可编程器件的基础上发展的产物。FPGA作为ASIC领域中的一种半定制电路而出现,在目前IC设计领域中主要负责前端逻辑电路设计综合和验证,其反复可编程的特点很好地解决了ASIC中定制电路流片后就不可修改的不足,并且很好地克服了像PLD、CPLD等可编程器件门电路有限的缺点,做到了很高的集成度。

由于可以对FPGA进行反复烧写,因此它不可能采用像ASIC那样固定的与非门来完成组合逻辑结构,而要采用一些便于反复配置的结构。因此,FPGA厂商提出了查找表(Look-Up-Table,LUT)的结构,这种查找表能够很好地实现这一要求。目前基于SRAM工艺的查找表结构已成为主流FPGA的烧写结构,这一点与传统的采用Flash或熔丝与反熔丝工艺的CPLD有很大的不同。基于这种查找表结构,可以通过烧写文件改变查找表内容对FPGA进行重复配置。

数字电路知识中,如果有一个n输入的逻辑运算,当经过“与或非”运算或者“异或”运算,其结果最多只可能存在2n个。FPGA原理是:当用户通过原理图或HDL(Hardware Description Language)语言描述了一个逻辑电路以后,FPGA开发软件会通过逻辑综合(Logic Synthesis)实现该逻辑电路RTL,并将结果实现写入SRAM中,每一组输入信号就等同于需要进行查表的地址值,随后开发软件会寻找出对应地址的内容,然后输出。根据这种原理,可以通过配置查找表的内容在相同电路的情况下实现不同的逻辑功能。

上述的FPGA可反复编程,从本质上来解释是因为存在查找表(LUT)。LUT实质上就是一种RAM,目前FPGA中LUT都可以看成一个有4位地址线的RAM。LUT和一般的逻辑电路具有相同的功能,但是相比较而言,LUT具有更快的执行速度和更大的规模。同时,正是具有LUT这种硬件结构使得FPGA器件集成度达到了数万门到数千万门不等,能够完成复杂的时序电路以及状态机的设计,所以FPGA适用于高速数字逻辑电路设计领域。

FPGA在实际工作的时候要求通过编程将数据写入片内RAM来配置芯片工作模式,开发人员依照自己所需要的配置模式选用不同的编程方式。FPGA配置模式有如下4种。(1)串行模式:PROM串行配置FPGA;(2)并行模式:Flash、PROM并行配置FPGA;(3)主从模式:一片PROM配置多片FPGA;(4)外设模式:将FPGA作为微处理器的外设,由微处理器对其编程。

现今最大的两家FPGA生产厂商Xilinx和Altera生产的FPGA芯片都是基于SRAM工艺,当需要保存程序的时候就在使用时外接一个片外存储器。上电时,FPGA将外部存储器中的程序数据读入片内RAM以完成相关配置,随后芯片进入工作状态;掉电后,由于SRAM的掉电丢失数据的特性,FPGA内部的逻辑消失。尽管具有掉电易失的不足,但是FPGA却能够反复编程使用,还无须专门的FPGA编程器。

FPGA可编程技术可以分为两大类:一次可编程与反复可编程。一次可编程技术包括熔丝、反熔丝以及PROM。熔丝技术主要通过电流来移除指定内部连接,反熔丝技术通过电流来增加内部连接。反复可编程包括EPROM、EEPROM、Flash、SRAM等。典型的FPGA结构如图1.2所示,主要包括可配置逻辑块(CLB)、输入/输出块(IOB)、块RAM等。图1.2 Xilinx FPGA结构

目前世界上主要的FPGA供应商包括Xilinx、Altera、Atmel、Lattice,这4家都以提供SRAM型FPGA为主,其中Xilinx与Altera两家占据了全球60%以上的FPGA市场。另外,还有两家以提供Flash和反熔丝FPGA为主的厂商:Actel与Quick Logic,尤其是Actel反熔丝技术的FPGA占据了航空领域等特定市场的较大份额。1.3 FPGA主要器件1.3.1 Xilinx

Xilinx公司主要提供可编程逻辑器件以及相应的设计软件ISE和新版的Vivado,公司总部位于San Jose,CA,是一家无生产线的半导体公司,其产品主要由UMC及TSMC代工。

Xilinx公司的主流器件有Spartan-6、Virtex-6、Artix-7、Kintex-7、Virtex-7等系列,其中-7系列是最新的器件。表1.1为各个系列的资源配置。表1.1 Xilinx系列FPGA资源对比

从表1.1可以看出,Spartan-6的资源最少,市场定位为低成本的解决方案。Virtex-7的资源最丰富,市场定位为高密度、高性能的解决方案,当然成本也比较高。其他几个为折中性能和成本的器件,大家可以根据实际需要进行选择。

除此之外,Xilinx公司新近推出了Zynq-7000 All Programmable SoC系列解决方案,该方案将两个ARM Cortex-A9硬核、各种外设(如UART、USB、SPI等)和FPGA可编程逻辑资源(包括DSP、RAM、LE等)集成在一个芯片中,通过硬件、软件和I/O可编程性实现了扩展式系统级差异、集成和灵活性。通过Zynq-7000平台,用户可以设计更智能的系统,控制和分析部分则利用灵活的软件、紧密配合擅长实时处理的硬件,并辅之以优化的系统接口。这样,BOM成本可大幅削减、NRE成本更低、设计风险减少、加快上市时间。

Zynq-7000平台包含由众多软件解决方案、工具、IP、开发板以TM及业界领先的Vivado HLS高层次综合工具组成的综合生态系统,这种高层次综合工具使设计团队能够利用C、C++或SystemC编写的描述代码直接创建硬件,从而显著提高设计生产力。

ARM处理系统与片上可编程逻辑之间的紧密集成可为设计人员带来无限机遇,使他们能够添加几乎所有的外设或创建定制加速器,从而扩展系统性能并充分满足各种独特的应用要求。Zynq-7000 All Programmable SoC系列是一款面向各种应用的理想解决方案,可满足各种市场的要求,如表1.2所示。表1.2 Zynq关键应用实例

Xilinx的高性能系列FPGA主要是Virtex系列,如Virtex-Ⅱ、Virtex-Ⅴ等;低成本系列主要是Spartan系列。图1.3是Spartan-Ⅱ的结构图,主要由CLB、IOB和RAM块组成。图1.4是CLB的模块图。图1.3 Spartan-Ⅱ的结构图图1.4 CLB的模块图

CLB中主要包括2个带进位逻辑的LUT-FF对和2个全局三态缓冲(BUFT)。单个LUT-FF包括有2个4输入LUT、2个控制和进位逻辑以及2个存储单元。

LUT(Look-Up-Table,查找表)是逻辑实现的主要元素,每个LUT能实现任何4输入的函数,如图1.5所示。图1.5 LUT结构

块RAM是最有效的存储器实现,可以满足绝大部分存储需求,并以双口RAM实现。Spartan-Ⅱ系列FPGA具有4~14个存储块,每个块有4096位,若有更大的存储需求可用多个块来实现。

IOB提供引脚与CLB之间的接口,有单向、双向I/O,输出可强制为高阻态,可被延时,如图1.6和图1.7所示。图1.6 Virtex Ⅱ的IOB图1.7 基本I/O结构

除此之外,FPGA中还有大量的布线资源,如图1.8所示,其中PSM(Programmable Switch Matrix)为可编程开关矩阵。图1.8 布线资源1.3.2 Altera

Altera主要器件包括Cyclone系列与Stratix系列,主要开发软件工具包括Quartus Ⅱ及相应的SOPC开发工具。

低成本的CPLD器件MAX Ⅱ以及FPGA器件Cylone Ⅱ都是Altera公司非常有性价比的产品。其高性能产品主要是Stratix系列。其主要设计工具是Quartus Ⅱ。

Cyclone Ⅲ系列FPGA的逻辑单元(LE)如图1.9所示。LE作为Cyclone Ⅲ器件的最小单元,能够用于各种逻辑表达。每个LE包括:一个4输入LUT(可以实现任何4个输入变量的各种函数)、一个可编程寄存器、一个进位链和一个寄存器链。

Cyclone Ⅲ系列FPGA的逻辑阵列块(LAB)如图1.10所示。LAB包括一组逻辑单元LE。每个LAB具有以下特性:16个LE、LAB控制信号、LE进位链、寄存器链、本地互连资源。图1.9 Cyclone Ⅲ LE结构图1.10 Cyclone Ⅲ LAB结构

图1.11是Stratix的结构。图1.11 Stratix结构

除此之外,现代FPGA还根据不同应用具有更多的附加元件,如RAM块、特定乘法器、三态缓冲、接收器、处理器核、DSP块等。比如Vertex Ⅱ Pro上的PowerPC硬核(300MHz),Excalibur上的工业标准ARM922T 32bit RISC处理器,最高频率200MHz。

Altera公司也非常重视ARM IP核在业界的影响,2014年起主推以ARM核为主的SoC解决方案,优点为提高系统性能、降低功耗,减小板级尺寸,降低系统成本。其主要包括Arria V SoC与Cyclone V SoC系列FPGA,主要系统架构如图1.12所示。

处理器:双核ARM Corte-A9 MPCore;4000 MIPS(单核最高800MHz);带双浮点精度FPU的NEON协处理器;每个核32KB/32KB L1 缓存;512KB共享L2缓存。

多端口SDRAM控制器:DDR3或LPDDR2,最高533MHz; DDR2,最高400MHz;集成ECC支持。

高带宽在片接口:>125Gbps,硬件处理器系统(HPS)到FPGA接口;>125Gbps,FPGA到SDRAM接口。

优化成本与功耗的FPGA fabric:低功耗收发器;高达1600GMAC,300GFLOP;高达25Mb在片RAM;更多硬IP核:PCIe和存储控制器。

对于ARM SoC FPGA芯片,其开发流程如图1.13所示,包括硬件设计部分和软件设计部分。硬件设计工具主要涉及Quartus Ⅱ、Qsys以及其他第三方工具,软件设计工具主要包括ARM Design Studio 5、GNU Tools、Lauterbach TRACE32、Wind River Workbench、ENEA Optima等。Altera的大学计划合作伙伴友晶科技Terasic从2013年已经开始供应Cyclone V SoC Development Kit and SoC Embedded Design Suite,并提供相应的辅助设计代码资源。图1.12 SoC FPGA系统架构图1.13 SoC FPGA系统开发流程1.4 FPGA设计流程

FPGA设计流程主要包括需求分析、HDL编码、逻辑综合、映射布局布线、配置下载,如图1.14所示。图1.14 FPGA设计流程(1)需求分析:在设计系统之前,首先要对需要实现的逻辑功能进行分析,提出设计方案。系统工程师根据任务要求、系统成本和一些相关技术指标对系统所需要的资源进行权衡,选择并完善设计方案。在设计方案的开始,一般是采用自顶向下的设计思路,先构造好系统的整体布局,然后再把系统分成若干个小模块,如此类推,直到可以直接使用一些EDA元件库为止。(2)各个子模块HDL设计:在将系统合理地分成若干个小模块后,可以对每个模块进行行为级的描述。常用的方法主要有硬件描述语言(HDL)和原理图输入方法。原理图输入方法是一种最直接的描述方式,主要是通过数字电路中的最基本单元——与或非门,对需要实现的逻辑功能进行描述,这种方式在FPGA发展的早期应用广泛,但是随着数字电路的复杂度和集成度越来越高,这种直观并易于仿真的描述方式出现了一些不可忽视的缺点——效率不高,维护升级困难,难于对设计的模块进行移植。基于上述的不足,目前,在实际开发流程中进行前端逻辑设计比较普遍使用的是HDL语言输入法,采用文本描述的方法,使用一系列赋值和状态机表达方式,完成模块逻辑功能的实现。通过这种HDL语言进行设计的模块具有良好的可移植性,不仅如此,设计的模块具有易读易改的优点,便于设计者对模块的理解和修改、维护。(3)各个子模块前仿真:前仿真,也称为功能仿真,是在对模块进行逻辑功能的验证,此时的仿真没有延时时间信息,是进行综合前的重要步骤。仿真前,需要建立Testbench文件,当仿真过后,仿真工具就会生成输出信号波形和一系列报告文件。目前常用的工具有Mentor的ModelSim、Synopsys公司的VCS和Cadence公司的NC-Verilog和NC-VHDL等软件。对各个模块进行前仿真可以非常有效地判断出各个模块是否能够实现所需的逻辑功能,前仿真从总体设计流程来说,可以大大地减少开发流程中进行反复纠错的时间和次数,为后续进行系统整合和后仿真打好了基础。当在功能仿真中发现了错误,需要回过头对子模块进行修改甚至重新设计。(4)各个模块整合成系统进行仿真:在完成各个子模块的设计和仿真,确定各个模块实现的逻辑功能正确后,需要将各个模块进行整合,即将各个子模块的相关引脚连接在一起。在实际的开发过程中,尽管各个模块经过仿真时确认没有逻辑错误,但当整合成一个系统的时候,也有可能会产生一些不可预测的逻辑错误。因此整合系统后必须对整个系统进行相关功能仿真,对于在仿真中发现的错误要及时回到各个子模块进行检查和修改。(5)系统逻辑综合:所谓综合就是将较高级的抽象层的描述转成较低级的门级描述的过程。综合就是根据目标和要求优化生成的逻辑连接,平面化层次设计,以供FPGA实现布局布线。从层次的角度来看,综合就是将采用HDL语言描述的逻辑模块编译为由RAM和触发器等的一些基本逻辑单元组成的逻辑连接网表,构成RTL级网表,最后将RAM和触发器等单元转换为由更底层的非门、或门、与门等基本门级单元构成的逻辑电路。目前,基于RTL级和门级的HDL程序的综合已经十分成熟,常用的综合工具有Synopsys公司的Synplify软件以及各个FPGA厂家自己推出的综合开发工具。(6)布局布线:经过系统综合后,开发软件将会生成逻辑网表,设计者依据这个网表对于具体的FPGA芯片以及其外设进行布局布线。布局主要是将芯片内部的逻辑单元进行合理的配置,已达到速度或者面积的一系列要求。布线主要是根据布局的几何结构,合理正确地利用芯片内各种连线资源进行元件连接。在实际的工程应用中,布局布线的合理与否直接影响元件之间走线延时的大小,也即影响了整个系统的最大工作频率。由于FPGA芯片生产商对自己设计的芯片以及外围结构最了解,因此目前都会使用FPGA芯片生产商提供的专门工具进行布局布线设计。(7)后仿真:也称为时序仿真,将FPGA芯片和走线延时信息标注到网表中,判断是否有时序违规现象。本质而言,判断时序是否违规就是判断是否满足每一个触发器建立时间和保持时间的要求。与功能仿真不同,时序仿真反映的是整个系统的实际工作情况,在进行时序仿真之前,设计者需要将走线延时参数和芯片内部建立时间与保持时间参数输入到时序仿真工具中,通过时序仿真,可以知道系统的一些关键路径是否满足时序要求。在整个系统设计过程中,对系统进行时序仿真,分析其时序关系,估计系统性能,检查和消除竞争冒险是提高优化系统速度和稳定性的必要步骤。(8)程序下载调试:这是整个系统设计的最后一步。所谓程序下载就是将软件生成的位数据流文件(Quartus Ⅱ中的.sof文件)通过USB Blaster或者JTAG线下载到FPGA芯片中。其中,芯片编程需要满足一定的条件,如编程电压、编程时序和编程算法等方面。通过程序下载可以观察设计的程序在开发板上运行的真实情况,甚至可以分析出程序中的逻辑错误和时序错误。当发现实验现象有问题时,应该进行程序的修改,直到正确为止。从系统设计的总体情况来看,程序下载调试主要流程是:首先进行程序下载,经行调试测试后判断出程序的错误,然后对源程序进行修改仿真,最后再将程序下载到板子上,如此反复直到程序正确为止。1.5 FPGA开发工具

这里简要介绍一些常用的EDA工具。根据FPGA开发流程,目前许多公司都根据设计流程与功能划分提供了各自独立的开发工具,主要有设计输入工具、综合工具、仿真工具、布局布线工具、验证与调试工具这几类。在本节中将重点介绍设计输入工具、综合工具、仿真工具以及布局布线工具这几种最经常使用的工具。

1. 设计输入工具

设计输入工具提供了常用的HDL语言输入、原理图输入、IP Core输入等方法,让设计者可以根据设计系统的要求灵活地利用不同方法进行设计。

HDL语言易读易改的优点使其成为应用最为广泛的一种输入方式,在HDL语言中最为流行的是Verilog HDL和VHDL语言,其余还有SystemVerilog和AHDL语言。Altera公司提供的Quartus Ⅱ开发软件有一个内嵌的文本编辑器,它可以对一些关键字用蓝色显示加以区分。原理图设计输入方式主要是利用与门、或门、非门等一些基本单元库进行设计,由于其输入方式简单明了,因此在FPGA发展的初期广泛使用。但随着系统设计复杂度的提升,这种用基本单元库的设计方法已经相形见绌,只是在设计一些简单的数字电路和查看顶层设计结构时才使用。

IP Core输入方式现在已经成为FPGA设计中一种非常重要的辅助设计方式。所谓的IP Core,就是一些具有特定功能且经过完善并满足严格时序要求的受知识产权保护的标准模块。目前,主流的FPGA生产商都提供了一系列的IP Core,其功能从基本的外设控制器到数字信号处理需要的滤波器等算法模块,甚至是功能更为复杂的处理器模块。当需要使用这些IP Core的时候,只要购买相应的License后就可以直接调用。如果能够合理地利用这些IP Core来进行系统设计,不仅能够大大地降低设计难度,加快设计进程,更重要的是可以提高整体系统的稳定性。

其他的设计输入方式有状态机输入、波形输入及真值表输入等。StateCAD是目前比较常用的状态机输入工具,可以根据状态转换图生成相应的VHDL、ABEL或Verilog HDL语言代码。真值表输入方法:设计者通过完全填充由输入/输出组成的真值表即可得到HDL代码。使用波形输入方法时,设计者需要画出输入波形以及输入的激励波形,随后符合输入/输出关系的功能代码就可以自动生成。比起HDL语言、原理图输入和IP Core输入这3种流行的输入方式,以上辅助性的设计输入方式已经逐渐被淘汰。

2. 综合工具

目前有3种主流的综合工具,分别是Synopsys公司的Synplify/Synplify Pro、Leonardo Spectrum以及Xilinx自身的XST等。当然,Altera公司的Quartus Ⅱ中拥有自带的综合工具。

Synplify/Synplify Pro由于其先进的时序驱动(Timing Driver)和行为级综合提取技术(BEST)算法引擎,使其具有较小的综合面积和较快的综合速度,在众多综合工具中使用较广泛。逻辑综合主要是将HDL转换为电路并优化。图1.15是逻辑综合的示意图。

3. 仿真工具

目前最为人所熟悉和使用的仿真工具是ModelSim。仿真速度快、仿真精度高是ModelSim工具的主要特点。此外,还有一些颇具影响力的仿真工具,例如Cadence Verilog-XL和Synopsys VCS。ActiveHDL也是一款很有特色的仿真工具,它可以利用状态机分析视图进行状态机调试。

4. 布局布线工具

综合完成后的实现工具主要由ISE及Quartus Ⅱ自身完成映射及布局布线,如图1.16所示。

一般还会有静态时序分析、主要报告关键路径以及最大时钟频率。配置下载主要是根据不同工具生成位流文件,并下载到FPGA器件中。1.6 FPGA应用

FPGA最初主要是一些接口应用,因此也有“电子创可贴”一说。这些接口有PCI、ITU656等,同时也完成各种电平转换。随着FPGA规模的上升,其可以进行大规模控制,如2008年北京奥运会LED卷轴控制系统,用到了大约4000片Cyclone II芯片。

另外一个很重要的应用是在数字信号处理方面。由于现代的FPGA带有很多硬件乘法器和硬件DSP单元,并具有优越的并行计算结构,使得FPGA在现代通信方面得到大量应用,如雷达接收机,如图1.17所示。图1.15 逻辑综合图1.16 映射与布局布线图1.17 雷达接收机

另一个应用是SOPC方面,由于FPGA集成了包括ARM在内的高性能处理器,使得系统的集成度可以做得更高。

FPGA还用于军事和民用航空航天领域,如战斗机、民用航空器、火星车等。第2章 硬件描述语言Verilog HDL

随着EDA技术的发展,使用硬件语言设计PLD/FPGA成为一种趋势。目前最主要的硬件描述语言是VHDL和Verilog HDL。

究竟选择VHDL还是Verilog HDL,这是一个初学者最常见的问题。其实两种语言的差别并不大,它们的描述能力也是类似的。掌握其中一种语言以后,可以通过短期的学习,较快地学会另一种语言。选择何种语言主要还是看自己的使用习惯。如果是集成电路(ASIC)设计人员,则必须首先掌握Verilog HDL,因为在IC设计领域,90%以上的公司都是采用Verilog HDL进行IC设计。对于PLD/FPGA设计者而言,两种语言可以自由选择。

目前,据了解,国内大部分公司使用的是Verilog HDL语言,因此本书将详细介绍Verilog HDL语言。

1. 简介

Verilog HDL是一种硬件描述语言,用于从算法级、门级到开关级的多种抽象设计层次的数字系统建模。被建模的数字系统对象的复杂性可以介于简单的门和完整的电子数字系统之间。数字系统能够按层次描述,并可在相同描述中显式地进行时序建模。

Verilog HDL语言具有下述描述能力:设计的行为特性、设计的数据流特性、设计的结构组成以及包含响应监控和设计验证方面的时延和波形产生机制。所有这些都使用同一种建模语言。此外,Verilog HDL语言提供了编程语言接口,通过该接口可以在模拟、验证期间从设计外部访问设计,包括模拟的具体控制和运行。

Verilog HDL语言不仅定义了语法,而且对每个语法结构都定义了清晰的模拟、仿真语义。因此,用这种语言编写的模型能够使用Verilog仿真器进行验证。该语言从C编程语言中继承了多种操作符和结构。Verilog HDL提供了扩展的建模能力,其中许多扩展最初很难理解。但是,Verilog HDL语言的核心子集非常易于学习和使用,这对大多数建模应用来说已经足够。当然,完整的硬件描述语言足以对从最复杂的芯片到完整的电子系统进行描述。

2. 历史

1995年Verilog硬件描述语言(HDL)成为一个IEEE标准IEEE STD 1364-1995。它具有简单、直观、有效、多层次的抽象,包括模拟验证、时序分析、测试分析与合成。正是因为这些丰富的功能而被集成电路(IC)设计人员所接受。Verilog HDL包含了一套丰富的内置原语,包括逻辑门、用户自定义的原语、开关和线型逻辑。

1990年,Cadence公司将Verilog HDL推向公共领域并成立Open Verilog International(OVI)组织,以发展Verilog HDL语言。1992年,OVI组织开始致力于将Verilog HDL语言发展为IEEE标准。1993年,第一个IEEE工作组成立,经过18个月的努力,Verilog成为IEEE标准1364-1995。标准化进程完成后,IEEE P1364工作组开始在全球范围内接受用户的反馈,以对该标准进行改进。通过5年的努力,得到一个更好的Verilog标准IEEE STD 1364-2001。随着IEEE标准1364-2001完成,工作组继续在较大的Verilog社区寻找悬而未决的问题并开始着手SystemVerilog的标准化。2001年,可能导致1364的Verilog和SystemVerilog之间的不兼容性问题被确定。IEEE P1364工作组作为SystemVerilog P1800工作组的小组委员会帮助解决这些问题。最终在2005年完成了最新的标准IEEE STD 1364-2005。

下面的章节将通过一些例子,重点介绍常用的语法内容,对于不常用的内容,如果读者感兴趣可以参阅IEEE Standard 1364-2005 for Verilog Hardware Description Language。2.1 Verilog HDL的基本规范2.1.1 标识符

标识符是程序代码中给对象起的独一无二的名字。标识符可以是普通的标识符,也可以是转义标识符。普通标识符可以由任意顺序的字母、数字、美元符号和下划线组成。标识符的第一个字符不能是数字或美元符号,只能是字符或下划线,这是因为以美元符开始的标识符和系统任务的保留字冲突。注意,标识符是区分大小写的,如:shiftreg_abusa_indexerror_conditionmerge_ab_bus32.1.2 转义标识符

转义标识符,是由“\”开始,以空白符(空格、制表符、换行)结束的一种特殊编程语言结构。其提供一种可以表示任何可以打印的ASCII字符(十进制的33~126或十六进制的21~7E)的方法。除此之外,在反斜线之后也可以加上字符的ASCII,这种转义标识符相当于一个字符。常用的转义标识符有换行(\n)、制表位(\t)、空格(\b)、反斜杠(\\)和英文的双引号(\")等。2.1.3 空白符

空白符是指代码中的空格、制表符和换行或分页符。如果这些空白符出现在字符串里,那么它们不可忽略。除此之外,代码中的其他空白符在编译的时候都将会被视为分隔标识符,使用2个空格或者1个空格并无影响。不过,在代码中使用恰当数量的空格,可以让上下行代码对齐,从而提高代码的可读性。2.1.4 注释

Verilog HDL有两种方式书写注释。一种为单行注释,注释从“//”开始,从这里到这一行末尾的内容会被系统识别为注释。另一种为多行注释,注释从“/*”开始,直到“*/”结束。注意,多行注释不能嵌套。2.2 数据类型2.2.1 逻辑值

在Verilog HDL中,有4种逻辑值,它们表达的意义为:0,代表逻辑0,条件为假;1,代表逻辑1,条件为真;X,代表逻辑值不确定;Z,代表高阻,浮动状态。

除了逻辑值外,Verilog HDL还用强度值来解决数字电路中不同强度的驱动源之间的赋值冲突,如表2.1所示。表2.1 Verilog强度值

如果两个具有不同强度的信号驱动同一个线网,则竞争结果为高强度信号的值;如果两个强度相同的信号之间发生竞争,则结果为不确定值。2.2.2 线网

Verilog HDL中有几种不同的线网,如wrie、tri、tri0、supply0、wand、triand、tril、supply1、wor、trior、trireg、uwire。wire作为一般的电路连线使用最为普遍,而其他几种用于构建总线,即多个驱动源连接到一条线网的情况,或搭建电源、接地等。当进行模块的端口声明时,如果没有明确指出其类型,那么这个端口会被隐含地声明为wire类型。

线网与实际使用的电线类似,它的数值一般只能通过连续赋值(continuous assignment),由赋值符右侧连接的驱动源决定。线网在初始化之前的值为x(trireg类型的线网是一个例外,它相当于能够储存电荷的电容器)。如果未连接驱动源,则该线网变量的当前数值为z,即高阻态。2.2.3 寄存器

与网线不同,寄存器可以保存当前的数值,直到另一个数值被赋值给它。在保持当前数值的过程中,不需要驱动源对它进行作用。给寄存器赋值必须在一个过程(initial过程或always过程)里面进行。如果未对寄存器变量赋值,它的初始值则为x。Verilog HDL中所说的寄存器类型变量与真实的硬件寄存器是不同的,它是指一个储存数值的变量。

关于选择线网类型还是寄存器类型,需要符合一定的规定。模块的输入端口可以与外界的线网或寄存器类型的变量连接,但是这个模块输出端口只能连接到外界的线网。再简单点,就是在两个模块的信号连接点,提供信号的一方可以是寄存器或者线网,但是接收信号的一方只能是线网。此外,在initial、always过程代码块中赋值的变量必须是寄存器类型,而连续赋值的对象只能是线网类型的变量。2.2.4 数字的表示

Verilog HDL中数字通常有4种进制表示形式:二进制(b或B);八进制(o或O);十进制(d或D);十六进制(h或H)。

语句:o_result<=9'd0,就是表示9位的十进制数0。

在Verilog HDL语言中,数字又分正整数和负整数,其中正整数数字表达方式有以下3种。(1)<位宽>'<进制><数值>:这是一种最全面的描述方式。如:(2)'<进制><数值>:这种表达式位宽按机器的系统位数,一般为32位。如:(3)<数值>:这种描述方式Verilog HDL会将其识别为十进制数,位宽按机器系统位数,一般为32位。如:12   //32位十进制数12

负整数数字的表示是在位宽的数字前面增加一个减号来表示它是一个负数。如:

在某些需要用二进制数来表示而且位宽又比较宽的场合,可以在数字中间加下划线,除了第一个字符,下划线“_”可以出现在数字中的任何位置,它的作用只是提高可读性,在编译阶段会被忽略掉。

如:

在数字的表示中还经常会用到问号“?”,它是z的另一种表示,使用问号的目的在于增强casex和casez语句的可读性。在这两条语句中,“?”表示不必关心的情况。

如:4'b10//  相当于4'b10zz

在表示不确定值时用“x”表示,高阻用“z”值表示。在二进制中代表1位,在八进制数中代表3位,十六进制中代表4位。在十进制中一般不用这个来表示。2.2.5 向量

在Verilog HDL中,没有声明位宽的网线或寄存器类型的量将被识别为标量。而声明中带有位宽范围的,即位宽大于1的网线或寄存器类型的量则被识别为标量。向量的表示需要使用方括号,方括号里的第1个数字为向量第1个分量的序号;第2个数字为向量最后一个分量的序号,中间用冒号隔开。向量分量的序号不像C语言的数组一样必须从0开始,不过为了和数字电路里二进制数高低位的表示方法一致,常常让最低位为0(即对于4位二进制数,其最高位为第3位,次高位为第2位,次低位为第1位,最低位为第0位),当然这只是一种习惯。如:

上面的向量声明之后,就可以方便地选择其中的某几个分量进行操作。请注意,用于域选的方括号的位置在向量名称之后,方括号内的数字为所需的位数。例如可以进行以下操作:

当对向量进行赋值时,如果右边的数值位宽大于左边的变量,则多出来的位被丢弃;如果右边的数值位宽小于左边的变量,则不够的位用0填补。2.2.6 数组

Verilog HDL 中允许声明reg、integer、time、real、realtime及其向量类型的数组,对数组的维数没有限制,即可声明任意维数的数组。线网数组也可用于连接实例的端口,数组中的每个元素都可以作为一个标量或者向量,以同样的方式来使用,形如<数组名》[<标>]。注意:不要把数组和线网或寄存器向量混淆起来。向量是一个单独的元件,它的位宽是n,数组由多个元件组成,其中每个元件的位宽为n或1。2.2.7 参数

Verilog HDL使用关键字parameter和localparam在模块内定义常数。localparam是局部参数,但它不能被重定义,也就是说在实例化的时候不能通过层次引用进行重定义。例如,parameter可以通过“#”(参数)来进行重新定义,但是localparam不可以,只能通过源代码来改变。参数代表常数,不能像变量那样赋值,但是每个模块实例的参数值可以在编译阶段被重载。通过参数重载使得用户可以对模块实例进行定制。除此之外,还可以对参数的类型和范围进行定义。

定义的语法:parameter[signed][range],localparam[signed][range]。2.2.8 字符串

字符串是双引号括起来的一个字符队列。对于字符串的限制是,它必须在一行中书写完,不可书写在多行中,也不能包含回车符。Verilog HDL将字符串当作一个单字节的ASCII字符队列。2.3 运算符

1. 算术运算符

Verilog HDL的算术操作符有5种。● 加(+):2个操作数相加;● 减(-):2个操作数相减或取1个操作数的负数;● 乘(*):2个操作数相乘;● 除(/):2个操作数相除;● 求幂(**):2个操作数求幂,前一个操作数为底数,后一个操作

数为指数。

2. 逻辑操作符

Verilog的操作符有3种:逻辑与(&&),逻辑或(||),逻辑非(!)。其中“&&”和“||”是双目操作符,它需要两个操作数;“!”是单目操作符,只要求一个操作数。逻辑运算的真值表如表2.2所示。表2.2 逻辑操作符真值表

3. 关系运算符

Verilog HDL的关系运算符有8种。● 大于(>):比较2个操作数,如果前者大于后者,结果为真。● 小于(<):比较2个操作数,如果前者小于后者,结果为真。● 大于或等于(>=):比较2个操作数,如果前者大于或等于后者,

结果为真。● 小于或等于(<=):比较2个操作数,如果前者小于或等于后者,

结果为真。● 逻辑相等(==):2个操作数比较,如果各位均相等,结果为

真。如果其中任何一个操作数中含有x或z,则结果为x。● 逻辑不等(!=):2个操作数比较,如果各位不完全相等,结果为

真。如果其中任何一个操作数中含有x或z,则结果为x。● case相等(===):2个操作数比较,如果各位(包括x和z位)均

相等,结果为真。● case不等(!==):2个操作数比较,如果各位(包括x和z位)不

完全相等,结果为真。

4. 按位操作符

按位取反(~):1个多位操作数按位取反。例如:a=4'b1011,则~a的结果为4'b0100。按位取反的真值表如表2.3所示。表2.3 按位取反真值表

按位与(&):2个多位操作数按位进行与运算,各位的结果按顺序组成一个新的多位数。例如:a=2'b10,b=2'b11,则a&b的结果为2'b10。按位与的真值表如表2.4所示。表2.4 按位与真值表

按位或(|):2个多位操作数按位进行或运算,各位的结果按顺序组成一个新的多位数。例如:a=2'b10,b=2'b11,则a|b的结果为2'b11。按位或的真值表如表2.5所示。表2.5 按位或真值表

按位或(^):2个多位操作数按位进行或运算,各位的结果按顺序组成一个新的多位数。例如:a=2'b10,b=2'b11,则a^b的结果为2'b11。按位或的真值表如表2.5所示。表2.6 按位异或真值表

按位或(~^或^~):2个多位操作数按位进行或运算,各位的结果按顺序组成一个新的多位数。例如:a=2'b10,b=2'b11,则a~^b的结果为2'b11。按位或的真值表如表2.5所示。表2.7 按位同或真值表

5. 缩减操作符

Verilog HDL的缩减操作符有6种。● 缩减与(&):对一个多位操作数进行缩减与操作,先将它最高

位与次高位进行与操作,其结果再与第二次高位进行与操作,直

到最低位。例如:&(4'b1011)的结果为0。● 缩减与非(~&):对一个多位操作数进行缩减与非操作,先将

它最高位与次高位进行与非操作,其结果再与第二次高位进行与

非操作,直到最低位。例如:~&(4'b1011)的结果为1。● 缩减或(|):对一个多位操作数进行缩减或操作,先将它最高位

与次高位进行或操作,其结果再与第二次高位进行或操作,直到

最低位。例如:|(4'b1011)的结果为1。● 缩减或非(~|):对一个多位操作数进行缩减或非操作,先将它

最高位与次高位进行或非操作,其结果再与第二次高位进行或非

操作,直到最低位。例如:|(4'b1011)的结果为0。● 缩减或非(^):对一个多位操作数进行缩减或非操作,先将它

最高位与次高位进行或非操作,其结果再与第二次高位进行或非

操作,直到最低位。例如:^(4'b1011)的结果为0。● 缩减或非(~^或^~):对一个多位操作数进行缩减或非操作,

先将它最高位与次高位进行或非操作,其结果再与第二次高位进

行或非操作,直到最低位。例如:~^(4'b1011)的结果为0。

6. 移位操作符

Verilog HDL的移位操作符有4种。● 逻辑右移(>>):1个操作数向右移位,产生的空位用0填充。● 逻辑左移(<<):1个操作数向左移位,产生的空位用0填充。● 算术右移(>>>):1个操作数向右移位。如果是无符号数,

则产生的空位用0填充;有符号数则用其符号位填充。● 算术左移(<<<):1个操作数向左移位,产生的空位用0填充。

7. 拼接操作符

拼接操作符({,}):2个操作数分别作为高低位进行拼接。例如:{2'b10,2'b11}的结果是4'b1011。

8. 重复操作符

重复操作符({n{m}}):将操作数m重复n次,拼接成一个多位的数。例如:A=2'b01,则{2{A}}的结果是4'b0101。

9. 条件操作符

条件(?:):根据“?”前的表达式是否为真,选择执行后面位于“:”左右两个语句。例如:(a>b)?(a=a-1):(b=b-2),如果a大于b,则将a-1的值赋给a,否则将b-2的值赋给b。

10. 操作符的优先级

上述所讲的各种操作符优先级如表2.8所示。表2.8 操作符的优先级2.4 模块2.4.1 模块的基本概念

在Verilog HDL编程中,设计人员总是将复杂的功能划分为简单的功能,模块是提供每个简单功能的基本结构。设计人员可以采取“自顶向下”的思路,将复杂的功能模块划分为低层次的模块。下面通过一个8位加减法器介绍一下模块的定义,让大家对模块有一个初步的认识。【例2-1】 8位加减法器。

上面模块实现的是一个8bit有符号数的加减法。从结构上看,模块(module)总是以关键字module开始,以endmodule结束。关键字module后面跟着的是该模块的模块名、参数声明(可选,该例子没有列出,后面的章节再做介绍)、端口列表、端口声明和实现功能的行为语句。下面将针对该例子进行详细分析。(1)模块名:add_sub。模块名在命名的时候没有特殊要求,可以以字母、下划线、数字组成,但不能用关键字作为模块名,而且在Verilog HDL中是区分大小写的。特别要注意的是在一个工程中,如果有多个模块,每一个模块的名字要是唯一的。模块的命名最好要有意义,但是尽量不要太长。如例2-1中用add_sub作为模块名,以加法(addition)和减法(substruction)的缩写来表示,既简洁又能表示该模块实现的功能。(2)端口列表:在老的标准里面,端口列表和端口声明是分开的,先列出一个端口的列表,然后再定义它们的属性。用旧的标准写法如下:

现在很多图书还是用这种表达方式。在IEEE Standard 1364-2005标准里面,允许端口列表和端口声明合在一起。这种风格的声明的优点是避免了端口名在端口列表和端口声明语句中的重复,这样看起来更直观易懂。(3)端口声明:端口是模块与外界环境交互的接口。对外部环境来讲,模块内部是不可见的,对模块的调用只能通过其端口进行。这种特点为设计者提供了很大的灵活性:只要接口保持不变,模块内部的修改并不会影响到外部的连接关系。声明端口类型,Verilog HDL中端口具有3种类型:input(输入),output(输出),inout(输入/输出)。然后声明该端口的数据类型和位宽,在Verilog HDL中,所有端口的数据类型隐含地声明为wire类型,因此如果希望端口具有wire数据类型,可以不做特殊声明。但是如果输出的端口需要保存数值,则必须将其声明为reg数据类型。但是要注意不能将input和inout类型的端口声明为reg数据类型,这是因为reg类型的变量是用于保存数值的,而输入端口只反映与其相连的外部信号的变化,并不能保存这些信号的值。如:

该例中出现了关键字signed,该关键字表示声明的端口数据类型是有符号数,在Verilog HDL中,定义端口或信号的时候,默认为unsigned(无符号)类型,所以如果定义的信号是无符号数,可以不用特别声明,但是如果是有符号数,在定义时加上signed关键字。

上文在模块的构成里面提到一个可选的参数声明,下面通过一个加减法器来进行讲解。【例2-2】 带参数的加减法器。

可以看到该例子与例2-1的不同是,在端口列表前面多了一句#(parameter DATAWIDTH=8),有了这个参数的定义,在模块被例化的时候可以通过该参数重新定义加减法器的位宽而不用修改模块代码,这种方法应用在一个工程中有不同位宽的加减法时非常灵活。关于模块的例化在下面的章节中会仔细讲解。2.4.2 模块的例化

1. 端口连接规则

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

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

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

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

2. 位宽匹配

在对模块进行调用的时候,Verilog HDL允许端口的内、外两个部分具有不同的位宽。一般情况下,Verilog HDL仿真器会对此警告。一般不建议直接将内外两个端口定义成不同位宽,如果出现需要将位宽不一致的信号进行连接,最好自己先将位宽较宽的信号进行截位之后再进行连接。

3. 未连接端口

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

4. 端口与外部信号的连接

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

顺序端口连接:需要连接到模块实例的信号必须与模块声明时目标端口在端口列表中的位置保持一致。如例2-1所定义的模块,调用时必须按照定义的顺序书写,这样,内部端口就可以省略不写,只写外部端口。如:

按名字连接:按名字连接对信号的顺序没有要求,可根据需要按任意顺序书写,如:

不过为了增强代码的可读性,如无特殊要求,一般还是按照定义时的顺序来写。注意,如果在一个顶层模块中,多次调用同一个底层模块,模块例化的名字应该区别开,不能用同一个例化名字。如上面已经例化了一个add_sub_02,再例化时可以例化成add_sub_03,根据实际需要进行命名,只要好辨认就可以。如:

用这种方式来例化,代码的可读性比较强,如无特殊要求,一般建议用该方法进行例化。

上文讲到带参数的模块,这里再讲一下带参数的模块应如何来例化。以例2-2的模块为例,例化如下:

与不带参数的模块例化的不同在于:例化的名字前面多了一个参数例化#(.DATAWIDTH(8)),如果定义的模块有多个参数,可以一次列出,与例化模块的端口是一样的。这里将DATAWIDTH参数的值定义为8,就相当于例化了一个8位的加减法器。如果在同一个工程中有另外一个地方需要用到一个16位的加减法器,那么只要如下例化就可以实现了:

这样就可以完全不用修改加减法器的内部代码,轻松实现位宽的改变,是不是非常灵活?所以,在编写一些通用性的模块时,若该模块在不同的应用中会有一些参数不一样,这个时候就可以将这些参数列出来定义成带参数的模块,以方便例化时可以灵活改变参数。2.4.3 模块的测试

设计人员可以使用一个顶层模块,通过实例调用这个模块来进行测试。这个顶层模块常被称为“测试平台(Testbench)”。为了最大限度地对电路的逻辑进行功能验证,测试代码需要尽可能多地覆盖系统所涉及的语句、分支、条件、路径、触发、状态机状态,验证人员需要在测试平台里创建足够多的输入激励,并连接到被测模块的输入端,然后检测其输出端的表现是否符合预期。这个顶层模块由于不需要再被外界调用,因此没有输入/输出端口,如例2-3所示。【例2-3】 测试平台实例。

在这个测试平台模块里,设计人员可以设定仿真时的输入信号以及信号监视程序,然后观察仿真时的输出情况是否符合要求,这样就可以了解设计是否达到了预期。

上面所述的情况是,测试平台顶层模块的测试变量直接连接了所设计的功能模块。测试平台还可以是另一种形式,即测试平台并不直接连接所设计的功能模块,而是在这个测试平台之下,将激励模块和功能模块以相同的抽象级别通过线网相互连接。这两种形式的测试平台都可以完成对功能模块的测试。大型的电路系统,正是通过各个不同层次模块之间的连接、调用来实现复杂的功能。2.5 过程语句2.5.1 两个过程

在Verilog HDL中,可以声明两种不同的过程:always过程和initial过程。过程可以是包含时序的过程描述,而不包含时序的过程还可以表达组合逻辑。always过程从关键字always开始,可以连续多次运行,当过程的最后一行代码执行完成后,再次从第一行代码开始执行。如果没有使用系统任务$finish,always过程将不断循环执行。initial过程从关键字initial开始,它只能执行一次。

一个模块中可以包含多个过程,各个过程相互之间是并发执行的。不过,过程不能够嵌套使用。如果过程中有多个语句,则需要使用关键字begin、end或fork、join将它们组成一个代码块。这两种关键字组合代表着顺序代码块和并行代码块,后面会讲述这两种结构。

例如,利用always过程循环执行的特点,可以为模块提供一个时间脉冲(注意,第一个initial过程为时钟的初始化,这个过程只需要进行一次):2.5.2 寄存器变量的过程赋值

虽然,always代码块和while语句、forever语句都能提供循环功能,但是alway代码块的循环更侧重过程的循环执行,而后两者更侧重代码的循环执行。因此,为了使代码更具条理,过程的循环应当用always语句描述。当然,在实际使用过程中,强制使用其中的某一种在功能实现上都是可行的。

在Verilog HDL中,有两种赋值运算:一种是阻塞赋值(blocking assignment),其运算符为“=”;另一种是非阻塞赋值(non-blocking assignment),其运算符为“<=”。在顺序代码块中使用阻塞赋值语句,如果这一语句没有执行完成,那么后面的语句不会执行;如果在顺序代码块中使用非阻塞赋值,则执行这一语句的同时,并不会阻碍下一语句的执行。而且,如果后一个语句涉及前面一个非阻塞赋值语句中的变量,由于这两个语句“同时”执行,因此后一个语句所用到的是前面一个语句执行前变化的数值。非阻塞赋值是Verilog HDL作为硬件描述语言与普通编程语言的一个重大区别。

带有两个触发器输出端的简单示例如下:

上面的例子如果没有使用非阻塞赋值,而使用了阻塞赋值,那么flop1和flop2的数值就不能被交换。flop1和flop2执行完毕后的数值都与之前flop2的数值相同。在传统的编程语言中,可能需要一个临时的变量,或者使用指针,才能够达到交换两个变量的目的。这里使用了非阻塞赋值,相当于引入了一个隐含的临时变量。第二个非阻塞赋值右边的a是第一句赋值之前的数值,变量交换的目的得以实现。信号边缘敏感的过程语句块内常使用非阻塞赋值,使语句块的诸赋值语句同时进行,虽然功能上似乎可以用阻塞赋值实现,但是仿真时会产生不正常的结果。

通常过程中,赋值语句往往只有在触发或循环等情况下,即赋值语句被执行到时候,才会使左边的寄存器变量改变一次;而线网变量的连续赋值则一直“监视”右边表达式的变化,一旦其结果发生变化,立即会将左边的线网变量更新为此结果。如果需要对寄存器变量进行过程连续赋值,则可以使用Verilog HDL提供的assign或force关键字“强制地”将赋值运算符右边表达式的结果连续不断地施加在左边的寄存器变量上。2.5.3 线网变量的连续赋值

连续赋值可以将数值驱动给网线,包括向量和标量。当右边的值有变化时,马上就将值赋给左边的变量。线网不能够像寄存器那样储存当前数值,它需要驱动源提供信号,这种驱动是连续不断的,因此线网变量的赋值称为连续赋值,这与寄存器变量在过程中的单次赋值不同,而且所用的运算符也有区别。在Verilog HDL里,线网连续赋值的关键字为assgin,如:module andwire y;wire a,b;assign y=a & b;

在这个例子中,线网变量y在系统运行过程中总为两个输入线网变量a和b逻辑与的结果。2.5.4 时序控制

Verilog HDL能够描述过程中的时序特性,这也是硬件描述语言与普通计算机编程语言的重要差别之一。过程的时序控制可以通过3种方式实现:延时时序控制、事件时序控制以及电平敏感时序控制。

过程中的时序控制可以控制代码的执行时间。在Verilog HDL中,除了过程中的时序控制,还可以定义元件、路径的延时。

1. 延时时序控制

在代码中使用关键字(#)和延时的时间,就可以通过延时来进行时序控制。延时的时间可以是数字、变量或者表达式。延时时序控制又分为两种:常规延时和内嵌延时。

常规延时在赋值语句的左边,系统执行到这一行代码时,系统先进行延时,延时完成后再计算表达式,并将结果赋值给左边的变量;而内嵌延时在赋值语句的右边,执行到这一行代码时,先立即计算表达式,再进行延时,最后把表达式的结果赋值给左边的变量。在上述两种延时方式中,设计人员需要注意表达式的自变量在延时过程中可能发生变化。常规延时是先延时再计算表达式,这时表达式的自变量可能已经发生了变化;而内嵌延时在延时前就已经进行了计算,表达式的自变量在延时过程中发生的变化,对已经计算的表达式结果没有影响,延时只是指这个结果需要等待一段时间再赋值给左边的变量。

下面的代码片段分别展示了常规延时和内嵌延时:

在顺序语句块(begin…end)中,由于语句是从上到下一行一行地执行,而所有常规延时时间都是实际执行时间相对于这一句本来应该开始执行的时间(也是上一句执行完成之时)的延时值。因此,在上面的代码示例中,对变量y的赋值时间相对于上一句结束延时了8个系统周期,而上一句相对系统零时刻已经延时了5个系统周期,因此对y的赋值发生在第13个系统周期。不过,如果顺序语句块中存在非阻塞赋值,由于这个结构有着类似并行语句块的特点,因此需要特别考虑。

在并行语句块(fork…join)中,由于所有语句都是并发执行的,而所有常规延时时间都是实际执行时间相对于这一句本来应该开始执行的时间(也是上一句执行完成之时)的延时值,因此各个常规延时所指的时间都是相对于系统零时刻。

2. 事件时序控制

事件时序控制的意思是,如果指定的事件发生,则代码被触发执行。它的关键字为@,后面可以加变量或者事件名称。参见下面的例子:

一种经典的用法结构如下,可以理解为“在整个仿真过程中,一旦某变量发生变化,就执行某操作”:

另一种用法称为OR事件时序控制,其代码结构为@(a or b)或@(a,b),即当a或b其中任意一个变量发生变化时,代码或代码块才被触发执行。监视的变量如果有3个,则其代码结构变为@(a or b or c)或@(a,b,c),以此类推。如果需要监视的变量很多,则可以使用@*或@(*),它表示对之后代码块中的所有输入变量敏感。此外,敏感列表中除了变量,还可以是前面所提到过的常规事件、命名事件。

3. 电平敏感时序控制

Verilog HDL中还有一种电平敏感时序控制方式,即使用wait(a),当变量a为真,则执行后面的代码块。2.5.5 顺序代码块与并行代码块

begin、end组合代表了这个代码块的各行代码是顺序执行的,这种代码块称为顺序代码块;后面的fork、join代表了这个代码块的各行代码是并发执行的,这种代码块称为并行代码块。与模块、过程不同,两种代码块可以嵌套,即顺序代码块中可以包含并行代码块。下面的例子展示了这两种代码块嵌套使用的效果。

由于这个initial过程使用了关键字fork、join,其中x、y、z的赋值同时在系统零时刻发生,而z和w由于位于一个顺序代码块中,因此w的赋值在z的赋值后才进行。2.6 条件语句

为了使设计人员方便地使用寄存器传输级描述,Verilog HDL提供了多种流程控制结构,包括if、if…else、if…else if…else等形式的条件结构,case分支结构,for、while循环结构。这些流程控制结构与C语言有着相似的用法。不同的循环结构可能造成不同的逻辑综合结果。Verilog HDL也提供了一些C语言中没有的流程控制结构以适应硬件描述语言的需要,例如casex、casez两种选择结构,前者将条件数值中的x、z均作为无关值,后者仅将z作为无关值;此外还提供了forever、repeat两种循环结构,分别用于无限循环和指定次数循环。数字电路的逻辑功能描述常常使用到这些流程控制结构,例如,case结构可以清晰地描述一个数据选择器。

下面再来看看例2-1中if…else if…else的语句。

在这段语句里面用了关键字if…else if…else,这几个关键字的用法和C语言是一样的。该段代码的含义是:如果rst_n信号为“0”,将一个9bit数“0”赋给o_result。该判断语句是为了在模块复位的时候让输出result清零;否则如果i_add_sub为“1”,将i_a - i_b的值赋给输出o_result;否则将i_a + i_b的值赋给输出o_result。

例2-4描述了case语句的用法。【例2-4】 四选一多路复用器。2.7 任务和函数

如果某部分代码需要在不同地方多次使用,则可以在模块中定义任务或函数。

任务通过关键字task来声明。任务可以有零个或者多个输入变量,但是没有输出返回值。调用任务时,将按照任务内指定的方式处理这些变量。由于它相当于一个子过程,因此任务中赋值的变量只能是寄存器类型的,而且只能使用过程赋值语句。任务可以具有时序结构,例如延时、非阻塞赋值等。任务中可以调用任务和函数。与模块的声明不同,任务的声明没有类似模块端口列表的输入变量列表。尽管如此,调用任务的时候,还是需要在括号里按照任务声明时的顺序罗列输入变量,在某种程度上,任务和C语言中没有返回值的函数有些类似。

函数通过关键字function来声明。任务不仅有输入变量,还有一个返回值作为输出变量,这个返回值的名称与函数的名称相同。函数与任务不同,它是一个只有逻辑功能的部分,不能包含时序结构。函数中只能调用函数。Verilog HDL中的函数与C语言中有返回值的函数有些类似。通常将函数放在赋值运算符的右边,它的返回值被赋值给左边的变量。

如果任务或函数同时在多个地方被调用,则需要使用automatic关键字声明,这样系统可以为不同地方的调用分配独立的内存空间。2.8 系统任务

系统任务可以被用来执行一些系统设计所需的输入、输出、时序检查、仿真控制操作。所有的系统任务名称前都带有美元符号“$”使之与用户定义的任务和函数相区分。例如,$display用于显示指定的字符串,然后自动换行(用法类似C语言中的printf函数);$monitor用于监视变量,一旦被监视的变量发生变化,会显示指定的字符串;而$time可以提取当前的仿真时间。完整的列表请查阅参考工具、Verilog HDL手册或标准文档。2.9 编译指令

Verilog HDL具有一些编译指令,它们的基本格式为`<keyword>,注意第一个符号不是单引号,而是键盘上数字1左边那个键对应的撇号。常用的编译指令有文本宏预定义`define、`include,它们的功能与C语言中类似,分别提供文本替换、文件包含的功能。Verilog HDL还提供了`ifdef、`ifndef等一系列条件编译指令,设计人员可以使得代码在满足一定条件的情况下才进行编译。此外,`timescale指令可以对时间单位进行定义。详细的编译指令清单请参阅相关书籍。2.10 阻塞赋值与非阻塞

特别地,有关阻塞赋值与非阻塞赋值的区别需要重点解释一下。阻塞与非阻塞都属于过程性赋值,都必须在initial或者always语句内赋值。先看一个阻塞赋值的例子的综合结果。【例2-5】 阻塞赋值。

上述代码用Synplify综合后,得到如图2.1所示的结果。说明always语句块中是一句一句执行的。在执行语句Q2=Q1之前,语句Q1=D就已经执行完成,所以数据D被打入了Q1和Q2。图2.1 阻塞赋值综合结果

再看一个非阻塞的例子。【例2-6】 非阻塞赋值。

上述代码用Synplify综合后,得到如图2.2所示的结果。Q1和Q2是不同的数据。非阻塞式赋值的赋值对象总是在当前仿真时刻结束时被赋值,所以,当在对语句Q2<=Q1中的Q2赋值这一时刻,Q1值还没有得到语句Q1<=D中的新值,而是原来的值(即上一个时刻的值)。图2.2 非阻塞赋值综合结果

对于阻塞与非阻塞,一般建议组合逻辑使用阻塞语句、时序逻辑使用非阻塞语句;而且在同一个模块里,同一个变量不能既有阻塞赋值,又有非阻塞赋值。第3章 FPGA基础电路设计

现在FPGA技术在工业设计方面的应用已经越来越广泛。本章提供了一些基础的电路设计,使读者对FPGA应用技术有一个基础的认识。在设计中,通常采用同步设计的方法去实现,这是因为同步设计比异步设计有着更稳定可靠的优点。下面简单介绍一些基础的同步设计电路,以做参考。3.1 组合电路

基本组合电路包括反相器、缓冲器、NAND、NOR等门电路,如表3.1所示。表3.1 基本组合电路

1. 优势门与级联、移位

优势逻辑门是当且仅当超过一半的输入为“真”时,输出为“真”。常用于全加器等电路的设计,如图3.1所示。级联可以从不同的矢量中选择位并用它们组成一个新的矢量,其用于位的重组和矢量构造。级联时不限定操作数的数目。在操作符符号{ }中,用逗号将操作数分开,如图3.2所示。移位操作中右移将会丢失最低位,如图3.3所示;左移丢失最高位,如图3.4所示。图3.1 优势逻辑门图3.2 级联操作图3.3 右移1位图3.4 左移操作【例3-1】 组合电路。

注意,此处赋值使用的是阻塞赋值。

2. 多路选择器【例3-2】 两路选择器。

两路选择器如图3.5所示。对于多于两个输入的多路选择器,建议使用case语句,而只有两输入的MUX可以使用assign语句。case语句中的每条分支都可以是一组或一条语句,多条语句时必须使用begin…end语句。根据判断条件按先后顺序逐行运行,如全都不满足时,则执行default语句。当case语句不充分时,可能会生成锁存(Latch)。

ABC为级联信号,用于选择8个输入中的一个,也即是8路选择器,如图3.6所示。在代码中有always模块,敏感变量包括A、B、C与8个输入信号,其中任何一个的变换都会触发执行always模块。注意,敏感变量列表中的or并非逻辑运算,不能使用“|”等符号代替。代码中3'b代表该数据是3位二进制数据。具体Verilog HDL实现如下所示。图3.5 2路选择器图3.6 8路选择器【例3-3】 8路选择器。

3. 信号分离器

信号分离器与多路选择器相反,根据选择的通道,将输入信号分离到相应通道,如图3.7所示。【例3-4】 信号分离器。图3.7 信号分离器

4. 译码器

译码器也是比较常用的器件,下面以3-8译码器为例。【例3-5】 3-8译码器。3.2 时序电路

在介绍时序电路之前,必须将锁存器、寄存器、触发器的概念搞清楚。锁存器和寄存器如图3.8所示。图3.8 高电平锁存器与正边沿触发寄存器

锁存器:电平敏感器件,比如正向锁存器,当时钟处于高电平时,输入信号通过锁存器进入输出端;为低电平时,保持原有输出值。

寄存器:边沿触发器件。

触发器:任何具有两个稳定状态的元件都可以称为触发器,在很多场合,也用触发器表示寄存器。

锁存器一般用于构成寄存器,在标准数字设计中几乎不单独使用,因此系统中插入的锁存器几乎都是由于错误使用而导致的。

锁存器与寄存器的区别:两者都是基本存储单元,锁存器是电平触发的存储器,寄存器是边沿触发的存储器。本质上两者的基本功能是一样的,都可以存储数据,但锁存器有下列3个缺点:(1)对毛刺敏感,不能异步复位,所以上电以后处于不确定的状态;(2)Latch会使静态时序分析变得非常复杂;(3)在FPGA中,基本的单元是由查找表和触发器组成的,若生成锁存器反而需要更多的资源。

因此在电路设计中,要对Latch特别谨慎,如果综合出和设计意图不一致的Latch,会导致设计错误,包括仿真和综合。因此,要避免产生意外的Latch。当然对于一些必需的场合,如总线、地址锁存等,Latch还是必要的。

一般而言,产生意外Latch的原因主要是if语句和case语句不完整,如if语句缺少必要的else语句,case语句缺少default语句。【例3-6】 不含意外锁存器的时序元件。

图3.9是综合后的正常结果,不包含Latch。图3.9 不含意外锁存器综合结果【例3-7】 含意外锁存器的时序元件。

图3.10是Synplify综合后的原理图。图3.10 含意外锁存器综合结果

可以看到如果缺少必要的else逻辑,综合生成了意外的Latch。

对于case语句也是同样情况,如果case逻辑不完备,也会导致生成意外的Latch。【例3-8】 完备case语句实例。

正常完备的case语句并不会生成Latch,如图3.11所示。图3.11 逻辑完备的case语句综合结果【例3-9】 不完备case语句实例。

对于不完备的case语句,Synplify综合出来了意外的Latch,如图3.12所示。图3.12 逻辑不完备的case语句综合结果

而JK、SR、D触发器通常用于组成存储元件,但使用最多的还是D触发器,如表3.2所示。表3.2 异步与同步D触发器

状态机主要包括Mealy和Moore型。Moore型的输出仅为当前输入状态的函数,这类状态机在输入发生变化时还必须等到时钟的到来,时钟状态发生变化了才导致输出的变化,所以比Mealy型要多等待一个时钟周期。Mealy型状态机的输出是当前状态和所有输入信号的函数,它的输出是在输入变化后立即发生的,不依赖时钟的同步。因此Moore型属于同步输出状态机,而Mealy型属于异步输出状态机。简单来讲,Mealy型,状态的转变不仅和当前状态有关,而且与各输入信号有关;Moore型,状态的转变只和当前状态有关。

对同一电路,使用Moore状态机设计可能会比使用Mealy状态机多出一些状态。根据它们的特征和要设计的电路的具体情况,就可以确定使用哪种状态机来实现功能。一旦确定状态机,接下来就要构造状态转换图。

现在还没有一个成熟的系统化状态图构造算法,所以,对于实现同一功能,可以构造出不同的状态转换图,但一定要遵循结构化设计。在构造电路的状态转换图时,使用互补原则可以来帮助检查设计过程中是否出现了错误。互补原则是指离开状态图节点的所有支路的条件必须是互补的。同一节点的任何2个或多个支路的条件不能同时为真。同时为真是设计不允许的。在检查无冗余状态和错误条件后,就可以开始设计电路了。

图3.13为Mealy型状态机,其和Moore型状态图的描述是不一样的。Mealy状态机的译码写在箭头旁边,而Moore型译码输出写在圆圈内。比如对S0状态,“0/0”表示输入数据0时,输出数图3.13 Mealy型FSM据0,同时状态跳转为S0,也就是保持S0。同样在S0状态,“1/0”表示输入数据为1时,输出数据为0,同时状态跳转为S1。以下是其Verilog HDL代码。【例3-10】 Mealy型状态机。

图3.14是Moore型状态机,其译码输出写在圆圈内。“S0/0”表示状态S0的译码输出为0。对于S0状态而言,当输入为1时,下一个状态跳转为S1;当输入为0时,跳转为S0,也即是保持S0状态。以下是其Verilog HDL代码。图3.14 Moore型状态机【例3-11】 Moore型状态机。【例3-12】 Mealy型状态机的Testbench。

图3.15和图3.16分别为Moore型与Mealy型状态机的测试输出波形。Moore型状态机的输出仅为当前输入状态的函数,为同步状态机,时钟状态发生变化了才导致输出的变化,从图3.15可以看出,Moore型状态机比Mealy型要多等待一个时钟周期。Mealy型状态机的输出不依赖时钟的同步,属于异步输出状态机。图3.15 Moore型状态机测试波形图3.16 Mealy型状态机测试波形3.3 数据通路3.3.1 加法器基础理论

加法器有半加器和全加器之分。半加器中A和B为输入的两个加数,S为输出的和,C为产生的进位,如图3.17所示。其逻辑表达out式:S=AB,C=A·B。out

对于多位的加法器,必须考虑低位进位问题,因此有图3.18所示的全加器。图3.17 半加器图3.18 全加器

相比半加器,全加器考虑增加了低位进位位C,其逻辑表达式如下:,C=MAJ(A,B,C)。out

为了更方便地分析和设计加法器的结构和性能,一般会定义G、P、K三个中间参数。定义G为进位生成位,也就是当输入为“真”时,全加器产生进位,即G=A·B。P为进位传播位,也就是当只有一个输入为“真”时,只有接收到输入进位,才能产生输出进位,即P=AB。K为进位消除位,也就是输入为“假”时,全加器消除进位,即。

1. 传播进位加法器(CPA)

N位加法器将{A,…,A},{B,…,B}和进位输入C作为输入,N1N1in和{S,…,S}与最高位的进位输出C为输出,由于每一位的输入N1out进位都会影响接下来的进位,所以称为CPA。为了加快速度,将所有位分成多个位组。在求和之前预测多位组的输出进位。通常而言需要计算多位组的PG信号,PG信号主要表示多位组是传播进位输入还是产生进位输出。而如果想要得到更快的运行速度,必须采用多级超前进位加法器(CLA)。

传播进位加法器中,最简单的就是行波进位加法器。级联N个全加器就可以构造N位加法器,图3.19所示是4级的加法器,也就是行波进位加法器。

从图3.19中可以看到,计算延时主要在于进位位C到C的传播inout延时。一个自然而然的减小延时的方法是将所有全加器的输出端的反相器去掉,得到图3.20。图3.19 4级行波进位加法器

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

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载