绪论 0. 1 数字系统设计的基本概念 目前, 数字技术已渗透到科研、 生产和人们日常生活的各个领域。 从计算机到家用电 器, 从手机到数字电话, 以及绝大部分新研制的医用设备、 军用设备等, 无不尽可能地采 用了数字技术。 数字系统是对数字信息进行存储、传输、处理的电子系统。 通常把门电路、触发器等称为逻辑器件,将由逻辑器件构成,能执行某单一功能 的 电路, 如计数器、 译码器、 加法器等, 称为逻辑功能部件, 把由逻辑功能部件组成的能 实 现复杂功能的数字电路称数字系统。 复杂的数字系统可以分割成若干个子系统, 例如计算 机就是一个内部结构相当复杂的数字系统。 不论数字系统的复杂程度如何,规模大小怎样,就其实质而言皆为逻辑问题,从 组 成上说是由许多能够进行各种逻辑操作的功能部件组成的, 这类功能部件, 可以是 SSI 逻 辑部件,也可以是各种 MSI、 LSI 逻 辑部件,甚至可以是 CPU 芯片。由 于各功能部件之 间的有机配合, 协调工作, 使数字电路成为统一的数字信息存储、 传输、 处理的电子电路。 与数字系统相对应的是模拟系统,和模拟系统相比,数字系统具有工作稳定可靠 , 抗干扰能力强,便于大规模集成,易于实现小型化、模块化等优点。 数字系统一般由控制电路、 多个受控电路、 输入 /输出电路、 时基电路等几部分构成, 如图 0-1 所示。 图 0-1 数字系统框图 图中,输入电路将外部信号(开关信号、时钟信号等)引入数字系统,经控制电 路 逻辑处理后,或控制受控电路,或经输出电路产生外部执行机构(发光二极管、数码管、 扬声器等) 所需的信号。 数字系统通常是一个时序电路, 时基电路产生各种时钟信号, 保 证整个系统在时钟作用下协调工作。 数字系统和功能部件之间的区别之一在于功能是否单一,一个存储器,尽管规模 很 大,可以达到数兆甚至 G 字节,但 因其功能单一,只能算是逻辑部件,而由几片 MSI 构 成的交通灯控制器却应称为系统。 数字系统和功能部件之间的区别之二是是否包含控制电路,一个数字电路,无论 其 规模大小,只有在具有控制电路的情况下才能称之为系统。控制电路根据外部输入信号、 各受控电路的反馈信号、 控制电路的当前状态, 决定系统的下一步动作。 控制电路的逻辑 关系最为复杂,是数字系统设计中的关键。 0. 2 数字系统设计方法简介 数字系统的设计的一般流程为 1.明确设计要求,确定系统的输入 /输出 90 在具体设计之前, 详细分析设计要求、 确定系统输入 /输出信号是必要的。 例如, 要设 计一个交通灯控制器,必须明确系统的输入信号有哪些(由传感器得到的车辆到来信号, 时钟信号) , 输出要求是什么 (红、 黄、 绿交通灯正确显示和时间显示) , 只有在明确设计 要求的基础上,才能使系统设计有序地进行。 2.确定整体设计方案 对于一个具体的设计可能有多种不同的方案, 确定方案时, 应对不同方案的性能、 成 本、可靠性等方面进行综合考虑,最终确定设计方案。 3.自顶向下( top-down)的模块化设计方法 数字系统的设计通常有两种设计方法, 一种是自底向上的设计方法, 一种是自顶向下 的设计方法。 自底向上( Bottom-up)的设计过程从最底层设计开始。设计系统硬件时,首先选择 具体的元器件, 用这些元器件通过逻辑电路设计, 完成系统中各独立功能模块的设计, 再 把这些功能模块连接起来,总装成完整的硬件系统。 这种设计过程在进行传统的手工电路设计时经常用到, 优点是符合硬件设计工程师传 统的设计习惯; 缺点是在进行底层设计时, 缺乏对整个电子系统总体性能的把握, 在整个 系统设计完成后,如果发现性能尚待改进,修改起来比较困难,因而设计周期长。 随着集成电路设计规模的不断扩大, 复杂度的不断提高, 传统的电路原理图输入法已 经无法满足设计的要求。 EDA 工具和 HDL 语言的产生使自顶向下 ( Top-Down) 的设计 方 法得以实现。 自顶向下 ( Top-down) 的 设计方法是在顶层设计中, 把整个系统看成是包含输入输出 端 口的单个模块, 对系统级进行仿真、 纠错, 然后 对顶层进行功能方框图和结构的划分, 即 从整个系统的功能出发, 按一定原则将系统分成若干子系统, 再将每个子系统分成若干个 功能模块, 再将每个模块分成若干小的模块 ……直至分成许多基本模块实现。 这样将系统 模块划分为各个子功能模块,并对其进行行为描述,在行为级进行验证。 例如,交通灯控制器的设计,可以把整个系统分为主控电路、定时电路,译码驱 动 显示等, 而定时电路可以由计数器功能模块构成, 译码驱动显示可由 SSI 构成组合逻辑电 路构成, 这两部分都是设计者所熟悉的各种功能电路, 设计起来并不困难, 这样交通灯控 制器的设计的主要问题就是控制电路的设计了, 而这是一个规模不大的时序电路, 这样就 把一个复杂的数字系统的设计变成了一个较小规模的时序电路的设计, 从而大大简化了设 计的难度, 缩短了设计周期, 由于设计调试都可以针对这些子模块进行, 使修改设计也变 得非常方便。 模块分割的一般要求为 ( 1) 各模块之间的逻辑关系明确; ( 2) 各模块内部逻辑功能集中,且易于实现; ( 3) 各模块之间的接口线尽量少。 模块化的设 计最能体现设计者的思想, 分割合适与否对系统设计的方便与否有着至关 重要的影响, 4.数字系统的设计 数字系统的设计可以在以下几个层次上进行: ( 1)选用通用集成电路芯片构成数字系统; ( 2)应用可编程逻辑器件实现数字系统; ( 3)设计专用集成电路 (单片系统 )。 91 用通用集成电路构成数字系统即采用 SSI、 MSI、 LSI(如 74 系列芯片, 计数器芯片、 存储器芯片 等) ,根据系统的设计要 求,构成所 需数字系统 。早期的数 字系统的设 计 , 都 是在这个层次上进行的, 电子工程师设计电子系统的过程一般是: 根据设计要求进行书面 设计——选择器件——电路搭建调试——样机制作, 这样完成的系统设计由于芯片之间的 众多连接造成系统可靠性不高, 也使系统体积相对较大, 集成度低。 当数字系统大到一定 规模时,搭建调试会变得非常困难甚至不可行。 随着数字集成技术和电子设计自动化 (Electronic Design Automation EDA)技术的迅速 发展,数 字 系统设计 的 理论和方 法 也在相应 地 变化和发 展 着 。 EDA 技术是从计 算 机 辅 助 设计 CAD、 计算机辅助制造 CAM、 计算机辅助测试 CAT 和 计算机辅助工程 CAE 等技 术 发展而来的。它以计算机为工具,设计者只需对系统功能进行描述,就可在 EDA 工具的 帮助下完成系统设计。 应用可编程逻辑器件( Programmable Logic Device PLD)实现 数字系统设计和单片系 统的设计,是目前利用 EDA 技术设 计数字系统的潮流。这种设计方法以数字系统设计软 件为工具, 将传统数字系统设计中的搭建调试用软件仿真取代, 对计算机上建立的系统模 型,用测试码或测试序列测试验证后,将系统实现在 PLD 芯片或专用集成电路上,这样 最大程度地缩短了设计和开发时间,降低了成本,提高了系统的可靠性。 高速发展的可编程逻辑器件为 EDA 技术的不断进步奠定了坚实的物理基础。大规模 可编程逻辑器件不但具有微处理器和单片机的特点, 而且随着微电子技术和半导体制造工 艺的进步,集成度不断提高,与微处理器、 DSP、 A/D、 D/A、 RAM 和 ROM 等独立器件 之间的物理与功能界限正日趋模糊, 嵌入式系统和片上系统 ( SOC) 得以实现。 以大规模 可编程集成电路为物质基础的 EDA 技术打破了软硬件之间的设计界限,使硬件系统软件 化。这已成为现代电子设计技术的发展趋势。 0. 3 可编程逻辑器件简介 数字集成电路从它的产生到现在, 经过了早期的电子管、 晶体管、 小中规模集成电路, 到大规模 、 超大规模 集 成电路 (VLSIC,几 万门以 上 )以及许 多 具有特定 功 能的专用 集 成 电 路的发展过程。 但是, 随着微电子技术的发展, 设计与制造集成电路的任务已不完全由半 导体厂商来独立承担。系统设计师们更愿意自己设计专用集成电路 (Application Specific Integrated Circuit ASIC)芯 片,而且希望 ASIC 的设计周期尽可能短,最好是在实验室里 就 能设计出合适的 ASIC 芯片, 并且立即投入实际应用之中, 因而出现了现场可编程逻辑器 件 (Field Programmable Logic Device FPLD),其中应用最广泛的当属 CPLD 和 FPGA。 CPLD 是复杂 可编程逻辑器件(Complex Programmable Logic Device)的简称 ,FPGA 是 现场可编程 门阵列(Field Pr ogrammable Gate Array)的简称,两者的 功能基本相 同, 只是实现原理略有不同, 所以我们有时可以忽略这两者的区别, 统称为可编程逻辑器件或 CPLD/FPGA。 可编程逻辑器件是电子设计领域中最具活力和发展前途的一项技术, 它的影响丝毫不 亚于 70 年代单片机的发明和使用。 可编程逻辑器件能完成任何数字器件的功能, 上至高性能 CPU, 下至简单 的 74 电路, 都可以用可编程逻辑器件来实现。 可编程逻辑器件如同一张白纸或是一堆积木, 工程师可 以通过传统的原理图输入法, 或是硬件描述语言自由地设计一个数字系统, 通过软件仿真, 我们可以事先验证设计的正确性,还可以利用 PLD 的在线修改能力,随时修改设计。 使用可编程逻辑器件来开发数字电路, 可以大大缩短设计时间, 减少芯片面积, 提高 系统的可靠性。可编程逻辑器件的这些优点使得可编程逻辑器件技术在 90 年代以 后得到 飞速的发展 ,同时也大 大推动了 EDA 软件和硬 件描述语言 ( Hard Description Language HDL)的进步。 92 早期的 可编 程逻辑 器件 只有可 编程 只读存 贮器 (PROM)、紫 外线可 按除 只读存 贮器 (EPROM)和电可擦除只读存贮器 (EEPROM)三种, 它们由全译码的与阵列和可编程的或阵 列组成,由于阵列规模大,速度低,主要用途是作存储器。 70 年代 中 期,出现 了 一类结构 上 稍复杂的 可 编程芯片 , 称可编程 逻 辑 阵 列 (Pro grammable Logic array PLA) , 它由可编程 的与阵列和 可编程的或 阵列组成, 虽然 其阵列规模大为减小,提高了芯片的利用率,但由于编程复杂,支持 PLA 的开发软件有 一定难度,因而也没有得到广泛应用。 70 年代末 , 美 国 MMI 公司 ( Monolithic Memories Inc, 单片机存 储器公司) 率先推出 了可编程阵列逻辑器件(Programmable array Logic PAL),PAL 由可编程的 与阵列和固 定 的或阵列构成, 采用熔丝编程方式, 双极型工艺制造。 PAL在器件的工作速度、 输出结构 种类上较早期的可编程逻辑器件有了很大进步,但由于其输出方式的固定不能重新组态, 所以编程灵活性较差,又由于采用的是 PROM 工 艺,只能一次性编程,使用者仍要承担一 定风险。 80 年代中期 , Altera公司 发明了通用阵列逻辑 GAL(Generic Array Logic),它 和 PAL的 区别在于 GAL的输出电路可以组态, 而且它大多采用 UVCMOS或 E 2 CMOS工艺, 实现了重 复编程, 通常可擦写百次以上, 甚至上千次。 GAL由于其设计具有很强的灵活性, 设计风 险为零,可以取代大部分 SSI、 MSI、和 PAL器件,所以在 20 世纪 80 年代 得到广泛使用 。 这些早期的可编程逻辑器件的一个共同特点是都属于低密度 PLD, 结构简单, 设计灵 活,但规模小,难以实现复杂的逻辑功能。 其后, 随着集成电路工艺水平的不断提高, PLD 突破了传统的单一结构, 向着高密度、 高速度、 低功耗以及结构体系更灵活、 适用范围更宽的方向发展, 因而相继出现了各种不 同结构的高密度 PLD。 80 年代中期, Altera 公司推 出了一种新型的可擦除、 可编程逻辑器 件( Erasable Programmable Logic Device EPLD) ,它采用 CMOS 和 UVEPROM 工艺制作 , 集成度比 PAL 和 GAL 高得多,设计也更加灵活,但内部互联能力比较弱。 1985 年 Xilinx 公司首家推 出了现场可编程门阵列器件 ( Field Programmable Gate Array FPGA) , 它是一种新型的高密度 PLD, 采用 CMOS-SRAM 工艺制作, 其结构 和阵列型 PLD 不同, 内部由许多独立的可编程逻辑模块组成, 逻辑模块之间可以灵活地相互连接, 具有 密度高、 编程速度快、 设计灵活和可再配置设计能力等许多优点。 FPGA 出现后立即受到 世界范围内电子工程师的普遍欢迎,并得到迅速发展。 80 年代末 Lattice公司提出 在系统可编程 ( In System Programmable ISP) 技 术后, 相继 出现了一系列具备在系统可编程能力的复杂可编程逻辑器件 (Complex Programmab1e Logic Device CPLD), CPLD是在 EPLD的基础上发展起来的,它采用 E 2 CMOS工艺制作 , 增加了内部联线,改进了内部结构体系,因而比 EPLD性能更好,设计更加灵活,其发展 也非常迅速。 不同厂家对可编程逻辑器件的叫法也不尽相同。 Xilinx 公司 把基于查找表技术, SRAM 工艺, 要外挂配置用的 EEPROM 的可编 程逻辑器件叫 FPGA; 把 基于乘积项技术, Flash( 类 似 EEPROM 工 艺 ) 工艺的可编程逻辑器件叫 CPLD。 Altera 公司把 自己的可编程逻辑器件产 品中的 MAX 系列(乘积项技术,EEPROM 工艺 ) 、FLEX 系列(查 找表技术,SRAM 工艺)都 叫作 CPLD,而 把也是 SRAM工艺,基于查 找表技术, 要外挂配置用的 FLEX 系列 的 EPROM, 叫 做 FPGA。 90 年代以后 ,高密度 PLD 在生产工艺、器件的编程和测试技术等方面都有了飞速发 展, CPLD的 集成度一般可达数千甚至上万门。 Altera公司的 EPM9560, 其单 密度达到 12000 个可用门,包含多达 50 个宏单元, 216 个用户 I/O 引脚,并能提供 15nS 的脚至脚延时 , 16 位计数的 最高工作频率为 118MHz。 目前 CPLD 的集成度最多可达 25 万 个等效门以上 , 93 最高工作速度已超过 180MHz, FPGA 的门延时已小于 3nS。在系统可编程技术、边界扫 描技术的出现也使器件在编程技术和测试技术及系统可重构技术方面有了很快的发展。 0. 4 EDA 软件种类及各自特点 计算机技 术的进步推动了 EDA 技术的普及和发展, EDA 工具层出不穷, 目前在我国各 大专院校教学中具有的广泛影响的 EDA 软件有 PSpice、 OrCad、 Electrical Workbench、 Protel 等。 Pspice是美国 MicroSim 公司于 20 世纪 80 年 代开发的电路模拟分析软件,可以进行 模拟分忻、模拟数字混合分析、参数优化等,该公司还开发了 PCB、 CPLD 的设计软件。 该软件现已并入 OrCad。 OrCad 是一 个大型的电子线路 EDA 软件包, OrCad 公司的产品 包括原理图设计、 PCB 设计、 PLD Tools 等设计 软件工具。 OrCad 被 Cadence 公司收购 后, 其产品功能更加强大。 Electronic Workbench 软 件是加拿大 Interactive Image Technologies 公司 于 20 世纪 80 年代末、 90 年代初推出的专门用于电子线路仿真的“虚拟电子工作台”软件,可以将 不 同类型的电路组合成混合电路进行仿真。 它不仅可以完成电路的瞬态分析和稳态分析、 时 域和领域分析、 器件的线性和非线性分析、 电路的噪声分析和失真分析等常规电路的分析, 而且还提供了离散傅立叶分析、 电路零极点分析、 交直流灵敏度分析和电路容差分析等共 计 14 种电路 分析方法, 并具有故障模拟和数据储存等功能。 其升级版 本 Multisim 2001 除 具备上述功能外,还支持 VHDL 和 Verilog HDL 文本地输入。 Protel软 件包是 20 世纪 90 年代初 由澳大利亚 Protel Technology 公司研制 开发的电路 EDA 软件, 它在我国电子行业中知名度很高, 普及程度较广。 Protel 98 是 应用于 windows95 / 98 和 windows NT 下的 EDA 设计软件,它包括 5 大组件 :原理图设计系统 Advanced Schematic 98、印制电路 扳设计系统 Advanced PCB 98、可编 程逻辑器件 (PLD)设 计系统 Advanced PLD 98、 电路仿 真系统 Advanced SIM 98 以及自动布线系统 Advanced Route 98。 它可以完成电路原理图的设计和绘制、 电路仿真、 印制电路板设计、 可编程逻辑器件 (PLD) 设计和自动布线等。 在 Protel 98 的基 础上, Protel 经历了 Protel 99、 Protel 99se、 Protel DXP 的发展过程,功能也愈来愈完善。 除此之外, 专门用于开发 FPGA 和 CPLD 的 EDA 工具也很多, 它们大致可以分为五 个模块: 1.设计输入编辑器; 2.仿真器; 3. HDL 综合器; 4.适配器(或部局布线器) ; 5.下载器。 设计输入编辑器可以接受不同的设计输入方式, 如原理图输入方式、 状态图输入方式、 波形图输入方式以及 HDL 文本输入方式。 各 PLD 器件厂商一般都有自己的设计输入编辑 器,如 xilinx 的 Foundation、 Altera 的 MAX+plus2 等。 仿真器以基于 HDL 的仿 真器应用广泛。数字系统的设计中,行为模型的表达、电子 系统的建模、 逻辑电路的验证以及门级系统的测试, 都离不开仿真器的模拟检测。 按处理 的硬件描述语言,仿真器可分为 VHDL 仿真器、 Verilog 仿真 器等,按仿真的电路描述级 别的不同, HDL 仿真器可以独立或综合完成系统级仿真、行为级仿真、 RTL 级仿真和门 级时序仿真。 各 EDA厂商都提供基于 VHDL/Verilog的仿真器, 如 Mentor公司 的 ModelSim, Cadence 公司的 Verilog-XL, NC-Verilog, Synopsys 公司的 VCS, Aldec 公司的 Active-HDL 等。 HDL 综合器可以把 VHDL/Verilog HDL 描述的系统转化成硬件电路, 这样使硬件描述 94 语言不 仅适 用于电 路逻 辑的建 模和 仿真, 还可 以直接 用于 电路的 设计 。目前 常 用 的 FPGA/CPLD 设计的 HDL 综合器为: 1. Synopsys 公司的 FPGA Compiler、 FPGA Express; 2. Synplicity 公司的 Synplify Pro 综合器; 3. Mentor 子公司 Exemplar Logic 的 Leonardo Spectrum 综合器; 综合器综合电路时,首先对 VHDL/Verilog 进行 分析处理,并将其转换成相应的电路 结构或模块, 这是一个通用电路原理图的形成过程, 与硬件无关。 然后才对实际实现的目 标器件的结构进行优化,并使之满足各种约束条件,优化关键路径等。 综合器一般输出网表文件,如 EDIF 格式( Electronic Design Interchange Format) ,文 件后缀是 .edf, 或是直接用 VHDL/Verilog 语言表达的 标准格式的网表文件, 或 是对应 FPGA 器件厂商的网表文件,如 Xilinx 的 XNF 网表文件。 适配器又称布局布线器, 其任务是完成系统在器件上的布局布线。 适配器输出的是厂 商自己定义的下载文件,用于下载到器件中以实现设计。布局布线通常由 PLD 厂商提供 的专门针对器件开发的软件完成,这些软件可以嵌在 EDA 开发环境中,也可以是专用的 适配器, 例如 Lattice 公司 的 ispEXPERT、 Altera 公司的 MAX+plus2 和 Quartus、 Xilinx 公 司的 Foundation 和 ISE 中都有各自的适配器。 下载器 又称 编程器 ,它 把设计 下载 到对应 的实 际器件 中, 实现硬 件设 计,一般 PLD 厂商都提供专门针对器件的下载或编程软件。 0.5 硬件描述语言简介 数字系统的设计输入方式有多种, 通常是由线信号和表示基本设计单元的符号连在一 起组成线路图, 符号取自器件库, 符号通过信号 (或网线) 连接在一起, 信号使符号互连, 这样设计的系统所形成的设计文件是若干张电路原理结构图, 在图中详细标注了各逻辑单 元、 器件的名称和相互间的信号连接关系。 对于小的系统, 这种原理电路图只要几十张至 几百张就可以了,但如果系统比较大,硬件比较复杂,这样的原理电路图可能要几千张、 几万张甚至更多,这样就给设计归档、阅读、修改等都带来了不便。这一点在 IC 设计领 域表现得尤为突出,从而导致了采用硬件描述语言进行硬件电路设计方法的兴起。 硬件描述语言( HDL, Hardwhare Description Language)是用 文本形式来描述数字电 路的内部结构和信号连接关系的一类语言, 类似于一般的计算机高级语言的语言形式和结 构形式。 设计者可以利用 HDL 描述设计的电路, 然后利用 EDA 工具进行综合和仿真, 最 后形成目标文件,再用 ASIC 或 PLD 等器件实现。 硬件描述语言的发展至今约有 20 多 年的历史,并成功地应用于数字系统开发的各个 阶段: 设计、 综合、 仿 真和验证等, 使设计过程达到高度自动化。 硬件描述语言有多种类 型,最具代表性的、使用最广泛的是 VHDL( Very High Speed Intergated Circuit Hardware Description Language)语言和 Verilog HDL 语言。 VHDL 语言 于八十年代初由美国国防部( The United States Department of Defense ) 发起创建,当时制订了一个名为 VHSIC( Very High Speed Integrated Circuit)的计划,其 目的是为了能制定一个标准的文件格式和语法, 要求各武器承包商遵循该标准描述其设计 的电路, 以便于保存和重复使用电子电路设计。 VHDL 语言的全称为 “超高速集成电路硬 件描述语言” ( VHSIC Hardware Description Language) ,于 1982 年正式诞生, VHDL 吸取 了计算机高级语言语法严谨的优点,采用了模块化的设计方法,于 1987 年被国际电 气电 子工程协会( International Electrical & Electronic Engineering , IEEE)收纳为标准;文件编 号为 IEEE standard 1076。 1993 年, IEEE 对 VHDL 进行了修订,从更高的抽象层次和系 统描述能力上扩展了 VHDL 的内容,公布了新版本的 VHDL,即 IEEE 标准的 1076-1993 版本。 95 Verilog HDL 语言最初是于 1983 年由 Gateway Design Automation( GDA)公 司 的 Phil Moorby 为其模拟器产品开发的硬件描述语言,那时它只是一种专用语言,最初只设计了 一个仿真与验证工具, 之 后又陆续开发了相关的故障模拟与时序分析工具。 1985 年 Moorby 推出它的第三个商用仿真器 Verilog-XL, 获得了 巨大的成功, 由于他们的模拟、 仿真器产 品的广泛使 用, Verilog HDL 作为一种便于实用 的语言逐步 为设计者所 接受。 1989 年 CADENCE 公司收购了 GDA 公司, 使得 Verilog HDL 成为该 公司的专有技术。 1990 年 CADENCE 公司公开发表了 Verilog HDL,并成 立 OVI(Open Verilon International)来促 进 Verilog HDL 的发展,致力于推广 Verilog HDL 成为 IEEE 标准 ,这一努力最后获得成功, Verilog 语言于 1995 年成为 IEEE 标准,称为 IEEE Std 1364-1995。 本书将在介绍数字系统的设计方法及基本步骤的基础上,介绍 MAX+plus2 的使用 方 法,介绍硬件描述语言 VHDL 和 Verilog HDL, 并给出若干数字系统设计问题, ,期 望通 过实例和练习,把数字系统设计的基本理论、 基本方法和设计课题紧密结合,使读者在 MAX+plus2 的设计平台下, 学会用原 理电路图输入或硬件描述语言输入 ( VHDL 或 Verilog HDL) 进 行 电路设计、 编译 ( Compiler) 、仿 真( (Simulator)、 底层编辑 ( Floorplan Editor) 及 PLD 器件编程校验( Programmer 或 Configure) ,对功能测试向量( Waveform Editor) 、 逻辑综合与试配( Logic Synthesize) 等涉及不多,以求提高读者利用 MAX+plus2 进行 数 字系统设计的能力。 本书除第三章使用 Mentor 公司的 Modelsim 仿真 器、 Synplicity 公司的 Synplify 综合 器进行仿真和综合外,其余章节的仿真结果都是用 Altera 公司的 MAX+plus2 得到的。 第一章 MAX+plus2 使用练习 1. 1 MAX+plus2 简介 MAX+plus2(Multiple array matrix and programmable logic user system)是一个完 全集成 化、 易学易用的可编程逻辑设计环境, 它可以在多种平台上运行, 其图形界面丰富, 加上 完整的、可即时访问的在线文档,使设计人员可以轻松地掌握软件的使用。 MAX+plus2 开发系统有很多特点: 1.界面开放 MAX+plus2 是 Altera 公司 的 EDA 软件, 但它可以与其它工业标准的设计输入、 综合 与 校验工具相连接,设计人员可以使用 Altera 或标 准 EDA 工具设计输入工具来建立逻辑设 计,用 MAX+plus2 编译 器( Compiler)对 Altera 器件设计进行编译,并使用 Altera 或其 它 EDA校验工具进行器件或板级仿真。 目前, MAX+plus2 支持 与 Candence、 Exemplarlogic、 Metor Graphics、 Synopsys、 Synplicity、 Viewlogic 等公司所提供的 EDA 工具接口。 2.与结构无关 MAX+plus2 系统的核心 Compiler 支持 Altera 公司的 FLEX10K、 FLEX8000、 FLEX6000、 MAX9000、 MAX7000、 MAX5000 和 Classic 可遍 程逻辑器件系列, 提供了与 结构无关的可编程逻辑环境。 MAX+plus2 的编译 器还提供了强大的逻辑综合与优化功能, 使用户可以容易地把设计集成到器件中。 3.丰富的设计库 MAX+plus2 提供丰富的库单元供设计者调用, 其中包括 74 系 列的全部器件和其它多 种 96 逻辑功能部件,调用库元件进行设计,可以大大减轻设计人员的工作量,缩短设计周期。 4.模块化工具 设计人员可以从各种设计输入、处理和校验选项中进行选择,从而使 MAX+plus2 可 以 满足不同用户的需求, 根据需要, 还可以添加新功能。 例如, 在本教材中, 侧重点在于 用 MAX+plus2 进行各种设计输入 (图形或 HDL 输入) 、编 译( Compiler) 、仿 真 (Simulator)、 底层编辑 ( Floorplan Editor)及 PLD 器件编程校验 ( Programmer 或 Configure) , 并不过多 涉及功能测试向量( Waveform Editor) 、逻辑综合与试配( Logic Synthesize)等。 5.硬件描述语言 MAX+plus2 软件支持各种 HDL 设计 输入选项,包括 VHDL、 Verilog HDL 和 Altera 公 司的 AHDL。 MAX+plus2 软件的启始界面如图 1-1-1 所示。 本章将 通 过 一 些例子来说 明 利 用 MAX+plus2 进行数字功能模块或数字系统设计的过程。 图 1-1-1 MAX+plus2 界面 1. 2 基于 MAX+plus2 的电路设计过程 在进入 设计之前, 需要先建立一个文件夹为用户存放设计文件用, 即用户库。 用户库 的名称用英文字母表示,例如 E:\maxplus2\lgl。 例 1-2-1 用原理图输入法设计一个 3 线 -8 线译码器。 步骤 1、进入 Windows 操作系统,打开 MAX+plus2; 步骤 2、启 动 File \ Project Name 菜单 , 输入设计文件的名称 (原理图文件的扩展名为 .gdf) 如图 1-2-1 所示; 97 步骤 3、点 击 Assign \ Device 菜单, 选择器件 (例如 EPF10K10LC84-3) , OK,如 图 1-2-2 所示; 图 1-2-2 选择器件 步骤 4、启 动 File \ New 菜单, 如图 1-2-3 所示, 选择 Graphic Editor File, OK, 打开原 理 图编辑器。 图 1-2-3 选择原理图编辑文件 步骤 5、原理图设计输入 (1) 元器件放置 在空白处双击鼠标左键,弹出器件选择界面如图 1-2-4 所 示,图中 Symbol Libraries 98 中列出文件目录分别的是用户库、 基本元器件库、 宏功能库和可调参数库。 选择其中任一 库, 如基本元器件库, 则此库中所有元器件的符号名称列在 Symbol Files 中, 点击所需的 元件或在 Symbol Name 中输入元件名, 如 and3(三输入与门) 、 not(非 门) 、 input(输 入 端口) 、 output(输出端口)等, OK 即可。 若要安放相同的元件,只要按住 Ctrl 键,同时用鼠标拖动该元件。 用户库 基本元器件 宏功能库 可调参数库 图 1-2-4 器件选择界面 (2) 在器件之间添加连线 把鼠标移到元件引脚附近, 则鼠标光标自动由箭头变为十字, 按住鼠标左键拖动, 即 可画出连线。如图 1-2-5。 图 1-2-5 在器件之间添加连线 99 步骤 6、给输入、输出引脚命名 电路图绘制完成后, 给输入、 输出引脚命名, 即将 PIN_NAME改为: a,b,c和 y 0 ,y 1 ,……y 7 , 如图 1-2-6 所示。 图 1-2-6 给输入、输出引脚命名 步骤 7、保存原理图 在菜单 File \Save 下 保存原理图,对于第一次输入的新原理图,出现类似文件管理的 对话框,将文件存入用户库、文件名为 L38.gdf。 步骤 8、编译 启动 MAX+plusII \Compiler 菜单 , 按 Start 开 始编译, 如图 1-2-7, 若 电路中有错, 则 显示出错提示。若电路无错,则编译通过,生成 .sof 和 .pof 文件,以备硬件下载或编程时 用,同时生成 .rpt 文件,可详细查看编译结果。 图 1-2-7 编译 步骤 9、时序模拟仿真 编译通过以后就可以进行时序模拟仿真。 1.建立波形输入文件 100 ( 1)在菜单 File/New 下选择 Waveform Editor File .scf,如图 1-2-8 所示。 图 1-2-8 打 开波形输入编 ( 2)波形编 辑器窗口如图 1-2-9 所示 ,在 Name 下方单击鼠标右键,出现浮动菜单, 选择 Enter Nodes from SNF……,进入对话框如图 1-2-10 所示。 图 1-2-9 波形编辑器窗口 图 1-2-10 添 加输入、 输出节点 ( 3)在 Type 区选择 Inputs 和 outputs,单 击 List 按钮, 则在 Available nodes &Groups 101 中出现所有输入、 输出节点名称, 选择需要仿真的节点名称, 单击 =>按钮, 则它们被添加 到 Selected Nodes & Groups 中, OK。 2.编辑输入节点波形 ( 1) 在图 1-2-11 所示的界面中对输入节点波形进行编辑, 可以设置的项有: 在菜单 Options 中选择 Snap to Grid(画线时网格对齐) 、 Show Grid(显示网格) ,并打开 Grid Size….., 设 置网格大小 (如 20.0ns) , 并点击 file/End Time 设置 模拟时间的长短 (如 2μs) 。 ( 2) 用波形绘制工具条绘制输入节点 a,b,c 的波形。 ( 3) 选 File\save 保存文件,生成扩展名为 .scf 的波形文件。 图 1-2-11 波形编辑选项 波形绘制 工具条 3.时序模拟仿真 ( 1) 选择菜单 Max+plusII\Simulator,界面如图 1-2-12 所示; ( 2) 单击 Start,开始模拟仿真; ( 3) 仿真完毕后, 单击 Open SCF, 打开仿真结果, 如图 1-2-13 所 示 。 分析仿真结 果可见,波形图满足表 1-2-1 所列的 3 线 -8 线译码器的真值表(注意有仿真 延时和竞争冒险现象) 。 102 图 1-2-12 时序模拟仿真界面 图 1-2-13 仿真结果 表 1-2-1 3 线 -8 线译码器真值表 a b c y 0 y 1 y 2 y 3 y 4 y 5 y 6 y 7 000 1 0 0 0 0 0 0 0 001 0 1 0 0 0 0 0 0 010 0 0 1 0 0 0 0 0 011 0 0 0 1 0 0 0 0 100 0 0 0 0 1 0 0 0 101 0 0 0 0 0 1 0 0 110 0 0 0 0 0 0 1 0 111 0 0 0 0 0 0 0 1 步骤 10、管脚分配 仿真模 拟正确后,就可以准备下载至 PLD 芯片进行硬件验证了。在下载前需要进行 管脚分配, 启动 MAX+plusII\ Floorplan Editor 菜单 , 则显示该设计项目的信号列表和目 标 芯片的管脚 图 , 如 图 1-2-14 所示。 在 MAX+ plus2 中, 管脚分配有自动和手动两 种方式,自 动方式由软 件自动完成 管脚分配, 手动方式则 由用户完成 管脚分配。 过程如下: ( 1)按键,所有的输入、输出口出现在 Unassigned Nodes 栏内,用户可以用 手动方式分配管脚。 ( 2)用 鼠标左键按住某输入 /输出口名称,拖到下面芯片的某一管脚上,松开鼠标, 便完成一个管脚的分配。 ( 3) 按 键,软件自动分配管脚。 注意: 芯片上有一些特定功能的管脚, 进行管脚编辑时一定要注意不能使用它们。 另 外,在实验步骤 3 中若选择器件为 Auto,则不允许对管脚进行再分配。 103 管脚分配时应注意实验箱的实际情况, 选择自动或手动分配管脚。 如图 1-2-15 所示 , 北京理工达盛科技有限公司生产的以 EPM7128SLC84-6 为目 标芯片的实验箱, 其 PLD芯片 的引脚与外部的开关、 LED显示、 数 码管显示等没有连接在一起, 要求程序设计员在数字 系统下载后手工连接以验证设计, 则管脚分配为自动分配和手动分配皆可。 若实验箱本身 已经将 PLD芯片的引脚与外部的开关、 LED显示、数码管显示等锁定在一起,不能改变, 则管脚分配时只能根据管脚与外部器件的对应表用手动方式分配, 如图 1-2-16 所示 , 东 南 大学东迅通公司生产的以 EPF10K110LC84-3 为 目标芯片的实验箱,管脚与外部器件的 锁 定关系如表 1-2-2 所列, 则分配管脚时, 输入逻辑信号 a,b,c只能分配给 28、 29、 30……中 的任三个管脚与信号开关相连, 输出电平 y 0 ,y 1 ,……y 7 只能分配给 25、 24、 23……中的 任 8 个管脚与发光管相连。 图 1-2-14 管脚分配 尽管自动分配管脚操作简单, 但由于自动分配管脚的任意性, 会给用户在连线和验证 设计时带来很多不便,所以建议使用手动方式分配管脚。 ( 4)管脚分配结束后,再进行一次编译。 表 1-2-2 F10K10 与外部器件管脚锁定表 104 外部器件名称 兼容器件名称 引脚 外部器件名称 引脚 发光管 L12G DATA/IN 25 数据开关 K1 28 发光管 L12R PS2/233 CLK/OUT 24 数据开关 K2 29 发光管 L11 WR 23 数据开关 K3 30 发光管 L10 D7 22 数据开关 K4 35 发光管 L9 D6 21 数据开关 K5 36 发光管 L8 D5 19 数据开关 K6 37 发光管 L7 D4 18 数据开关 K7 38 发光管 L6 D3 65 数据开关 K8 39 发光管 L5 D2 64 数据开关 K9 47 发光管 L4 D1 62 数据开关 K10 48 发光管 L3 TCL7528 D0 61 数据开关 K11 49 发光管 L2 OUT 60 数据开关 K12 50 发光管 L1R CLK 59 数据开关 K13 51 发光管 L1G TLC549 CS 58 数据开关 K14 52 M4A a 17 数据开关 K15 53 M4B b 16 数据开关 K16 54 M4C c 11 扬声器 SP 27 数码管 M4 M4D d 10 键盘 V1 44 M3A e 9 键盘 V2 84 M3B f 8 键盘 V3 72 M3C g 7 键盘 V4 71 数码管 M3 M3D dot 6 键盘 H1 70 M2A MS5 5 键盘 H2 69 M2B MS6 3 键盘 H3 67 M2C MS7 83 键盘 H4 66 数码管 M2 M2D MS8 81 时钟信号 CP1 1 M1A MS4 80 时钟信号 CP2 43 M1B MS1-MS8 8段显示信 号 MS3 79 M1C MS2 78 数码管 M1 M1D MS1 73 105 图 1-2-15 PLD 引脚与外部人工连接 图 1-2-16 PLD 引脚与外部器件连接锁定 步骤 11、下载 ( 1)用专用连接线将计算机并口与下载箱相连,打开实验箱电源; ( 2)启动 MAX+plusII \ programmer 菜单,出现图 1-2-17 的对话框。 ( 3) 如果是第一次使用该软件,启动 Options \Hardware Setup 菜单 (见图 1-2-18) , 在 Hardware Type 中选择 ByteBlaster( MV) 。 ( 4) 如 果是以 EPF10K10LC84-4 为目标芯片, 选中下载文件 L38.sof, Configure 即可 完成下载。 ( 5) 如果是以 EPM7128SLC84-6 为 目标芯片, 选中下载文件 L38.pof, Program 键完 成下载。 注意: 若下载后提示下载不成功信息, 按以上各步检查是否设置正确, 并检查计算机 与实验箱硬件连接,排除故障,再次尝试下载。 步骤 12、设 计文件下载至 PLD 芯片 后,根据步骤9管脚分配的结果,改变数据开关的电 平,验证发光管的状态是否满足表 1-2-1,硬件系统示意图如图 1-2-19 所示。 图 1-2-17 下载 1-2-18 硬件设置 图 1-2-19 三线 -八线译码器硬件系统示意图 106 例 1-2-2 用 Verilog HDL 设计一个 3 线 -8 线译码器。 步骤 1-3 与原理图输入法时相同。 步骤 4、启动 File \ New 菜单,如图 1-2-20,选择 Text Editor File, OK。 图 1-2-20 步骤 5、在硬件描述语言输入界面下,键入 Verilog HDL 代码如下。 module decoder(out,in); output[7:0] out; input[2:0] in; reg[7:0] out; always@(in) begin case(in) 3'd0:out=8'b00000001; 3'd1:out=8'b00000010; 3'd2:out=8'b00000100; 3'd3:out=8'b00001000; 3'd4:out=8'b00010000; 3'd5:out=8'b00100000; 3'd6:out=8'b01000000; 3'd7:out=8'b10000000; endcase end endmodule 步骤 6、保存设计文件 在菜单 File \Save as 下保存文本文件,生成 decoder.v。 编译、仿真、管脚分配、下载等步骤与图形输入法相同。 注意: .v 文 件描述的功能模块可以生成一个符号, 放在用户库中, 供其它原理图输入 文件调用, 调用的方法与从器件库中取元器件的方法相同。 生成功能模块的方法为: 选择 菜单 file\create default symbol, 经过与 编译相同的过程后, 生成的功能模块被放入用户库, 如图 1-2-21 所示。 107 生成的功能 模块 图 1-2-21 生成功能模块及调用 1.3计数器设计 例 1-3-1 用D触发器设计一个异步4位二进制加法计数器。 原理图输入法时, 参考电路如图 1-3-1 所示。 图中, CLK 接时钟信号源, reset 接数 据 开关给出复位信号, q3、 q2、 q1、 q0 接发光管,下载后,在时钟信号作用下,由发光 管 的状态验证满足异步4位二进制加法计数器的加法规律,硬件示意图如图 1-3-2 所示。 图 1-3-1 异步4位二进制加法计数器 图 1-3-2 异步4位二进制加法计数器硬件系统示意图 108 文本文件输入时, Verilog HDL 代码为: module jsq16(q,clk,reset); output[3:0] q; input clk,reset; reg [3:0] q; always @(posedge clk or negedge reset) begin if(!reset)q[3:0]<=4'b0000; else q[3:0]<=q[3:0]+4'b0001; end endmodule 例 1-3-2 设计一个十进制加法计数器。 实现十进制计数器的方法有很多种。用 74LS161 实现时,参 考电路如图 1-3-3 所示。 图 1-3-3 用 74LS161 实现十进制加法计数器 用 Verilog HDL 实现时,代码为: module jsq10(q,clk,reset); output[3:0]q; reg [3:0]q; input clk,reset; always @(posedge clk or negedge reset) begin if(~reset)q<=0; else begin if(q[3:0]==4'b1001)q<=0; else 109 q<=q+1; end end endmodule 例 1-3-3 设计一个六十进制加法计数器。 原理图输入法实现时,参考电路如图 1-3-4 所示,硬件系统示意图如图 1-3-5 所示。 图 1-3-4 六十进制计数器 图 1-3-5 六十进制计数器硬件系统示意图 用 Verilog HDL 实现时,代码为: module jsq60(qh,ql,clk,reset); output[3:0]qh; output[3:0]ql; reg [3:0] qh; reg [3:0] ql; input clk,reset; always @(posedge clk or negedge reset) 110 begin if(~reset){qh,ql}<=0; else begin if({qh,ql}==8'h59){qh,ql}<=0; else begin if(ql==9)begin ql<=0;qh<=qh+1;end else ql<=ql+1; end end end endmodule 1. 4 扫描显示电路 以上的设 计中, 设计验证采取的方式是将设计文件下载至 PLD 芯片后, 根据管脚分配 的结果, 将输出管脚接至发光管, 改变数据开关的电平, 验证发光管的状态是否满足设计 要求。 在设计译码器时, 这种验证方式是很直观的, 但在计数器设计时, 这样的验证方式 就显得很不 直观,尤其 当计数器的 位数增加时 (如百进制 计数) ,太多的发光管将 使 结 果 的读出非常困难。 数码管显示是计数器等电路的最好选择。半导体数码管的外形和等效电路如图 1-4-1 所示,其每一个字段都是一个发光二极管( Light Emitting Diode,简 称 LED) ,因而也把 它叫做 LED 数码管或 LED 七段(带小数点时为八段)显示器。 图 1-4-1 数码管及其等效电路 数码管可以用 TTL 或 CMOS 集成电路直接驱动,其驱动方式有两种: 1、 BCD 码驱动方式 这种驱动方式由 BCD-七段显示驱动器 7448 给出 数码管所需的驱动电平,硬件连接 电路如图 1-4-2 所示, 当 7448 的输入端 四位二进制数时, 数码管根据表 1-4-1 的关系显 示 相应的字形。 D C B A 图 1-4-2 用 7448 驱动数码管的连接方式 111 表 1-4-1 BCD-七段显示译码器的真值表 输入 输出 D C B A Y a Y b Y c Y d Y e Y f Y g 字形 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 1 0 1 0 0 0 1 0 1 0 1 1 0 0 1 1 1 1 0 0 0 1 0 0 1 1 0 1 0 1 0 1 1 1 1 0 0 1 1 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 0 1 1 0 0 0 0 1 1 0 1 1 0 1 1 1 1 1 0 0 1 0 1 1 0 0 1 1 1 0 1 1 0 1 1 1 0 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 0 1 1 1 0 0 1 1 1 1 1 1 0 0 1 1 1 0 0 1 1 1 1 0 1 1 0 0 1 1 1 1 1 0 0 0 1 1 1 0 1 2 3 4 5 6 7 8 9 A b C d E F 2、直接驱动方式 这种驱 动 方式直接对 数码管相应 的字段给出 驱动电平, 显示字形。 真值表如表 1-4-2 所示。 表 1-4-2 直接驱动显示真值表 输入 a b c d e f g 输出字形 1 1 1 1 1 1 0 0 1 1 0 0 0 0 1 1 0 1 1 0 1 1 1 1 1 0 0 1 0 1 1 0 0 1 1 1 0 1 1 0 1 1 1 0 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 0 0 1 1 1 1 1 1 0 0 1 1 1 0 0 1 1 1 1 0 1 1 0 0 1 1 1 1 1 0 0 0 1 1 1 0 1 2 3 4 5 6 7 8 9 A B C D E F 实际 PLD 器件验证设计结果时,以上两种驱动数码管的方式都可以使用,例如,图 112 1-2-16 的连 接中, M1、 M2、 M3、 M4 可以用 BCD 码驱动, 表 1-2-2 中 F10K10 的管 脚 10、 11、 16、 17 就是数码管 M4 的 BCD 输入信号。 当采用直接驱动方式时, 驱动一个数码管需要七个电平信号, 如果系统用来显示结果 的数码管较多, 应考虑数字系统输出信号占用 PLD 芯片管脚的问题, 因为 PLD 芯片的管 脚总数是有限的, 例如 F10K10, 它 的 管脚总数是 84, 其中还有一些特定功能的管脚不能 给用户使用, 能使用的管脚不到 60 个, 所以直接驱动时, 必须设法减少占用 PLD 芯片的 管脚的数量。 解决的方法是采用动态扫描显示,如图 1-4-3 所 示,将所有数码管( 8 个 )的相应字 段并联在一起, 由 FPGA/CPLD 的输出信号 a、 b…..g 直接驱动相 应字段, 由片选信号 MS1、 MS2…..MS8 决定选中的是哪个数码管,数码管显示何种字形由表 1-4-2 决定。 例 1-4-1 设计 一个电路, 使八个数码管依次同时显示 0、 1、 2、 ……A、 B、 C、 E、 F。 此电路包含一个动态扫描信号产生模块和一个十六进制计数器,参考电路如图 1-4-4 所示。 动态扫描信号产生模块可以由计数器和译码器构成。 上面的一片 74161 构成三位二进 113 图 1-4-4 动态扫描信号产生电路 图 1-4-3 动态扫描显示原理图 动态扫描 字形显示 十六进 制计数 制加法计数器,其输出 Q C 、 Q B 、 B A 送例 1-2-2 设计 的 3 线 -8 线译码器 decoder,则在时钟 脉冲 clk作用 下, MS1 到 MS8 依次产 生 “ 1” 信号, 连接到八个数码管的片选端后, 即依次 选中八个数 码管,当时 钟 clk足够快时,人眼就 不能分辨数 码管的选中 次序了,看 到的 将 是八个数码管同时被选中。 下面的一片 74161 是十六 进制加法计数器, led 是 一个字形显示模块, 它是由 Verilog HDL 文件产 生的符号体。在 clk1 作 用下, D[3..0]从 0, 1, ….F 变化时,由 led 模块输 出 的字段驱动电平使八个数码管显示正确的字形,代码如下: module led(a,b,c,d,e,f,g,D); output a,b,c,d,e,f,g; input[3:0] D; reg a,b,c,d,e,f,g; always @(D) begin case(D) 4'd0:{a,b,c,d,e,f,g}=7'b1111110; 4'd1:{a,b,c,d,e,f,g}=7'b0110000; 4'd2:{a,b,c,d,e,f,g}=7'b1101101; 4'd3:{a,b,c,d,e,f,g}=7'b1111001; 4'd4:{a,b,c,d,e,f,g}=7'b0110011; 4'd5:{a,b,c,d,e,f,g}=7'b1011011; 4'd6:{a,b,c,d,e,f,g}=7'b1011111; 4'd7:{a,b,c,d,e,f,g}=7'b1110000; 4'd8:{a,b,c,d,e,f,g}=7'b1111111; 4'd9:{a,b,c,d,e,f,g}=7'b1111011; default:{a,b,c,d,e,f,g}=7'bx; endcase end endmodule 硬件系统示意图如图 1-4-5 所示, 则 当 clk 的频率 较大, clk1 选择 1HZ 时 , 会看到八 个数码管同时依此显示 0、 1、 2、 ……A、 B、 C、 D、 E、 F。 114 图 1-4-5 例 1-4-1 硬件系统示意图 此例也可以完全由 Verilog HDL 文件完成,参考代码为: module saomiao(reset,clk,clk1,ms1,ms2,ms3,ms4,ms5,ms6,ms7,ms8,a,b,c,d,e,f,g); input clk,reset,clk1; reg [3:0] in1; output ms1,ms2,ms3,ms4,ms5,ms6,ms7,ms8,a,b,c,d,e,f,g; reg ms1,ms2,ms3,ms4,ms5,ms6,ms7,ms8,a,b,c,d,e,f,g; reg [3:0] temp,flag; always@(posedge clk) begin {ms1,ms2,ms3,ms4,ms5,ms6,ms7,ms8}=8'b00000000; flag=flag+1; case (flag) 0:begin temp=in1;ms1=1;end 1:begin temp=in1;ms2=1;end 2:begin temp=in1;ms3=1;end 3:begin temp=in1;ms4=1;end 4:begin temp=in1;ms5=1;end 5:begin temp=in1;ms6=1;end 6:begin temp=in1;ms7=1;end 7:begin temp=in1;ms8=1;end endcase case(temp) 4'd0:{a,b,c,d,e,f,g}=7'b1111110; 4'd1:{a,b,c,d,e,f,g}=7'b0110000; 4'd2:{a,b,c,d,e,f,g}=7'b1101101; 4'd3:{a,b,c,d,e,f,g}=7'b1111001; 4'd4:{a,b,c,d,e,f,g}=7'b0110011; 4'd5:{a,b,c,d,e,f,g}=7'b1011011; 4'd6:{a,b,c,d,e,f,g}=7'b1011111; 4'd7:{a,b,c,d,e,f,g}=7'b1110000; 4'd8:{a,b,c,d,e,f,g}=7'b1111111; 4'd9:{a,b,c,d,e,f,g}=7'b1111011; 4'hA:{a,b,c,d,e,f,g}=7'b1110111; 4'hB:{a,b,c,d,e,f,g}=7'b0011111; 4'hC:{a,b,c,d,e,f,g}=7'b1001110; 4'hD:{a,b,c,d,e,f,g}=7'b0111101; 4'hE:{a,b,c,d,e,f,g}=7'b1001111; 4'hF:{a,b,c,d,e,f,g}=7'b1000111; default:{a,b,c,d,e,f,g}=7'b1111110; endcase end always@(posedge clk1) begin if(!reset) in1=4'b0000; else begin in1=in1+1; 115 end end endmodule 例 1-4-2 设 计一个电路,使两个数码管显示 1~12 的十二进 制计数,两个数码管显示 0~59 的六十进制计数。 六十进制 计数器在例 1-3-3 中设计 过了, 十二进制计数器也可以仿照完成, 现在的问题 是要把两个计数器输出的个位和十位数分别显示在不同的数码管上, 相应的数码管选择显 示模块可以用 Verilog HDL 文件完成, 参考代码如下。 Sel 模 块 的原理是: in1, in2…… in8 为八个数码管的 BCD 码 输入端数据,MS1 有效时 ,in1 的数据 送 MS1 显示 ;MS2 有效时, in2 的数据 送 MS2 显示 ;……MS8 有 效时,in8 的 数据送 MS8 显示。八组数据可以不全 部 都有, 如此例中, 十二进制数的低四位送 in1,高四 位 送 in2, 六 十进制数的低四位送 in3, 高四位送 in4,其余数据端为空。 module sel(in1,in2,in3,in4,in5,in6,in7,in8,clk,ms1,ms2,ms3,ms4,ms5,ms6,ms7,ms8,a,b,c,d,e,f,g); input clk; input [3:0] in1,in2,in3,in4,in5,in6,in7,in8; output ms1,ms2,ms3,ms4,ms5,ms6,ms7,ms8,a,b,c,d,e,f,g; reg ms1,ms2,ms3,ms4,ms5,ms6,ms7,ms8,a,b,c,d,e,f,g; reg [3:0] temp,flag; always@(posedge clk) begin {ms1,ms2,ms3,ms4,ms5,ms6,ms7,ms8}=8'b00000000; flag=flag+1; case (flag) 0:begin temp=in1;ms1=1;end 1:begin temp=in2;ms2=1;end 2:begin temp=in3;ms3=1;end 3:begin temp=in4;ms4=1;end 4:begin temp=in5;ms5=1;end 5:begin temp=in6;ms6=1;end 6:begin temp=in7;ms7=1;end 7:begin temp=in8;ms8=1;end endcase case(temp) 4'd0:{a,b,c,d,e,f,g}=7'b1111110; 4'd1:{a,b,c,d,e,f,g}=7'b0110000; 4'd2:{a,b,c,d,e,f,g}=7'b1101101; 4'd3:{a,b,c,d,e,f,g}=7'b1111001; 4'd4:{a,b,c,d,e,f,g}=7'b0110011; 4'd5:{a,b,c,d,e,f,g}=7'b1011011; 4'd6:{a,b,c,d,e,f,g}=7'b1011111; 4'd7:{a,b,c,d,e,f,g}=7'b1110000; 4'd8:{a,b,c,d,e,f,g}=7'b1111111; 4'd9:{a,b,c,d,e,f,g}=7'b1111011; 116 4'hA:{a,b,c,d,e,f,g}=7'b1110111; 4'hB:{a,b,c,d,e,f,g}=7'b0011111; 4'hC:{a,b,c,d,e,f,g}=7'b1001110; 4'hD:{a,b,c,d,e,f,g}=7'b0111101; 4'hE:{a,b,c,d,e,f,g}=7'b1001111; 4'hF:{a,b,c,d,e,f,g}=7'b1000111; default:{a,b,c,d,e,f,g}=7'b1111110; endcase end endmodule 生成符号体后,参考电路如图 1-4-6 所示。 图 1-4-6 例 1-4-2 参考电路图 图中, jsq60 为六十进制计数器, jsq12 为十二进制 计数器, 硬件系统示意图如图 1-4-7 所示。 117 图 1-4-7 例 1-4-2 硬件系统示意图 1. 5 数字系统设计例题 设计一个具有三种信号灯的交通灯控制器。 设计要求是: 由一条主干道和一条支干 道汇合成十字路口, 在每个入口处设置红、 绿、 黄三色信号灯, 红灯亮禁止通行, 绿灯 亮 允许通行,黄灯亮则给行驶中的车辆有时间停在禁行线外。用传感器检测车辆是否到来。 主干道处于常允许通行的状态, 支干道有车来时才允许通行, 主、 支干 道均有车时, 两者 交替允许通行,主干道每次放行 45S,支干道每次放行 25S, 在每次由绿灯亮到红灯亮的 转换过程中,要亮 5S 黄灯作为过渡,使行驶中的车辆有时间停到禁行线外,设立 45S、 25S、 5S 计时、显示电路。 这是一个数字系统设计问题, 按自顶向下 ( top-down) 的 设 计方法, 整个系统可分为 主控电路、 定时电路, 译码驱动显示等几个模块, 而定时电路可以由 45S、 25S、 5S计数 器 功能模块构 成,这是我 们已经熟悉 的功能模块 ,译码驱动 显示可由 SSI构成组合逻 辑电路 构成,系统框图如图 1-5-1所示。 图 1-5-1 交通灯控制系统框图 118 在这个设计问题中,主控电路是核心,这是一个时序电路,其输入信号为: (1) 车辆检测信号,设为 A、 B(模拟传感器输出信号)。 (2) 45S、 25S、 5S定时信号,设为 C、 D、 E。 其状态表如表 1-5-1 所列。 表 1-5-1 状态转换表 状态 主干道 支干道 时间 /S S 0 S 1 S 2 S 3 绿灯亮, 允许通行 黄灯亮,停车 红灯亮, 禁止通行 红灯亮, 禁止通行 红灯亮, 禁止通行 红灯亮, 禁止通行 绿灯亮, 允许通行 黄灯亮,停车 45 5 25 5 对逻辑变量逻辑赋值为: A=0 主干道无车,A=1 主干道有车(可始终设为 1,使主干道处于常允许通行状态) ; B=0 支干道无车,B=1 支干道有车; C=0 45 秒定时未到,C=1 45 秒定时已到; D=0 25 秒定时未到,D=1 25 秒定时已到; E=0 5 秒定时未到,E=1 5 秒定时已到。状态编码为: S0=00 S1=01 S2=11 S3=10 若选JK触发器,其输出为Q 2 Q1 逻辑赋值后的状态表如表 1-5-2 所列,逻辑赋值后的状态转换图如图 1-5-2 所示。 表 1-5-2 逻辑赋值后状态表 A B C D E Q n 2 Q n 1 Q2 n+1 Q1 n+1 说明 0 0 0 0 X 0 X X X 1 1 0 X X 0 1 X X X 1 1 1 X X X X X X 0 X X X X 1 1 1 X 0 X 0 1 X X X X 0 X X X 1 1 X 1 X X X X X 0 X X X X 1 0 0 0 0 0 1 0 1 1 1 1 1 1 1 1 1 1 0 1 0 0 0 0 0 0 1 0 1 0 1 1 1 1 1 1 1 1 0 1 0 1 0 0 0 维持S 0 由S 0→S1 维持S 1 由S 1→S2 维持S 2 由S 2→S3 维持S 3 由S 3→S0 119 S 0 S 1 S 2 S 3 1=+ CAB 0=E 1=E 1)( =+DAB 0)( =+DAB 0=E 1=E 0=+ CAB 图 1-5-2 状态转换图 将表 1-5-2 中Q 2 n+1 、 Q 1 n+1 的“1”项按最小项之和的形式写出,并化简即得状态方程为 nnnnn QEQEQQQ 2112 1 2 )( ++= + )]([)( 2121 1 1 DABQQCABQQQ nnnnn ++++= + 所以,两 JK 触发器的驱动方程为 )( 21 CABQJ n += , )( 21 DABQK n ++= n EQJ 12 = , EQK n += 12 45S、 25S、 5S 定时器 CP 脉冲驱动方程为 脉冲CPEQQBAQQCP ])([ 121245 ++= 脉冲CPEQQBQQCP ][ 121225 += 脉冲CPQQCP ][ 215 ⊕= 则主控电路和各定时电路如图 1-5-3 所示。 120 图 1-5-3 交通灯控制器主控电路 图 1-5-3 中,各计数器驱动脉冲如图 1-5-4 所示。 设主干道红、 黄、 绿三 色灯为 R 1 、 Y 1 、 G 1 , 支 干 道红、 黄、绿三色灯为 R 2 、 Y 2 、 G 2 , 信号灯显示的真值表如表 1-4-5 所列。 121 图 1-5-4 计数器驱动脉冲电路图 表 1-5-3 译码驱动电路真值表 Q 2 Q 1 R 1 Y 1 G 1 R 2 Y 2 G 2 0 0 1 1 0 0 0 1 0 1 0 0 0 0 0 1 1 1 1 0 1 0 0 0 0 1 1 0 0 0 1 0 所以,表达式为 21 QR = , 22 QR = 211 QQY = , 212 QQY = 211 QQG = , 122 QQG = 译码驱动电路如图 1-5-5 所示。 122 图 1-5-5 交通灯译码驱动电路 此例也可以完全由 Verilog HDL 文件完成,参考代码为: module traffic(clk,enb,ared,agreen,ayellow,bred,bgreen,byellow,acounth,acountl,bcounth,bcountl); output[3:0] acounth,acountl,bcounth,bcountl; output ared,agreen,ayellow,bred,bgreen,byellow; input clk,enb; reg ared,agreen,ayellow,bred,bgreen,byellow; reg[3:0] acounth,acountl,bcounth,bcountl; always@(posedge clk) begin if({acounth,acountl}==0||{bcounth,bcountl}==0) // case({ared,agreen,ayellow,bred,bgreen,byellow}) 6'b010100:begin{ared,agreen,ayellow,bred,bgreen,byellow}<=6'b001100;acountl=5;acount h=0;bcountl=5;bcounth=0; end 6'b001100:if (enb)begin{ared,agreen,ayellow,bred,bgreen,byellow}<=6'b100010;acountl=0;acounth=3;bcoun tl=5;bcounth=2;end else begin{ared,agreen,ayellow,bred,bgreen,byellow}<=6'b010100;acountl=5;acounth=4;bcountl=0; bcounth=5; end 6'b100010:begin{ared,agreen,ayellow,bred,bgreen,byellow}<=6'b100001;acountl=5;acount 123 h=0;bcountl=5;bcounth=0;end 6'b100001:begin{ared,agreen,ayellow,bred,bgreen,byellow}<=6'b010100; acountl=5;acounth=4;bcountl=0;bcounth=5; end default:begin{ared,agreen,ayellow,bred,bgreen,byellow}<=6'b010100;acountl=5;acounth= 4;bcountl=0;bcounth=5; end endcase // else begin if(acountl==0) begin acounth=acounth-1; acountl=4'b1001; end else acountl=acountl-1; if(bcountl==0) begin bcounth=bcounth-1; bcountl=4'b1001; end else bcountl=bcountl-1; end end endmodule 顶层 gdf 文件 如图 1-5-6 所示 , 其 中 sel 为扫描显示模块, 电路下载后, 硬件系统示意 图如图 1-5-7 所示。 124 图 1-5 6 具有三种信号灯的交通灯控制器顶层 gdf 文件图 图 1-5-7 具有三种信号灯的交通灯控制器硬件系统示意图 第二章 VHDL 硬件描述语言 2.1 VHDL概述 2.1.1 VHDL 的特点 VHDL 语言 作为一种标准的硬件描述语言, 具有结构严谨、 描述能力强的特点, 支持 从系统级到门级所有层次的设计,在使用 VHDL 语言进行逻辑电路设计时,不需要考虑 特定电路制造工艺的影响, 其设计覆盖所有的逻辑电路形式。 其语法结构以严谨著称, 适 合于复杂逻辑电路的设计。 由于 VHDL 语言来源于 C、 Fortran 等计算机高级语言, 在 VHDL 语言中保留了部分高级语言的原语句, 如 if 语 句 、 子程序和函数等, 便于阅读和应用。 具 体特点如下: 1. 支持从系统级到门级电路的描述,既支持自底向上( bottom-up)的设计也支持从 顶向下( top-down)的设计,同时也支持结构、行为和数据流三种形式的混合描 述。 2. VHDL 的设计单元的基本组成部分是实体( entity)和结构体( architecture),实 体包含设计系统单元的输入和输出端口信息,结构体描述设计单元的组成和行 为, 便于各模块之间数据传送。 利用单元 ( componet) 、 块 ( block) 、 过程 ( procure) 和函数 ( function) 等语句 , 用结构化层次化的描述方法, 使复杂电路的设计更加 简便。采用包的概念,便于标准设计文档资料的保存和广泛使用。 3. VHDL 语言有常数、信号和变量三种数据对象,每一个数据对象都要指定数据 类型, VHDL 的数据类型丰富, 有数值数据类型和逻辑数据类型, 有位型和位向 量型。 既支持预定义的数据类型, 又支持自定义的数据类型, 其定义的数据类型 具有明确的物理意义, VHDL 是强类型语言。 4. 数字系 统有组合电路和时序电路, 时序电路又分为同步和异步, 电路的动作行为 有并行和串行动作, VHDL 语言常用语句分为并行语句和顺序语句, 完全能够描 述复杂的电路结构和行为状态。 2.1.2 VHDL 语言的基本结构 VHDL 语言 是数字电路的硬件描述语言,在语句结构上吸取了 Fortran 和 C 等计算 机 高级语言的语句,如 IF 语句、循环语句、函数和子程序等,只要具备高级语言的编程技 能和数字逻辑电路的设计基础,就可以在较短的时间内学会 VHDL 语 言。但是 VHDL 毕 竟是一种描述数字电路的工业标准语言, 该种语言的标识符号、 数据类型、 数据对象以及 描述各种电路的语句形式和程序结构等方面具有特殊的规定, 如果一开始就介绍它的语法 规定, 会使初学者感到枯草无味, 不得要领。 较好的办法是选取几个具有代表性的 VHDL 程序实例,先介绍整体的程序结构,再逐步介绍程序中的语法概念。 一个 VHDL 语言的设计程序描述的是一个电路单元,这个电路单元可以是一个 门 电 路,或者是一个计数器,也可以是一个 CPU。一般情况下,一个完整的 VHDL 语言程序 至少要包含实体、 结构体和程序包三个部分。 实体给出电路单元的外部输入输出接口信号 和引脚信息, 结构体给出了电路单元的内部结构和信号的行为特点 , 程 序包定义在设计结 构体和实体中将用到的常数、数据类型、子程序和设计好的电路单元等。 例 2-1-1 用 VHDL 语言描述一位全加器。一位全加器的输入信号是 A, B, Ci,输出信 号是 S 和 Co。全加器的真值表如表 2-1-1 所列。 表 2-1-1 全加器的真值表 输入信号 输出信号 A B Ci S Co 0 0 0 0 0 0 0 1 1 0 125 0 1 0 1 0 0 1 1 0 1 1 0 0 1 0 1 0 1 0 1 1 1 0 0 1 1 1 1 1 1 一位全加器的逻辑表达式是: S=A⊕ B⊕ Ci Co=AB+ACi+BCi 一位全加器的电路如图 2-1-1 所示 图 2-1-1 一位全加器电路图 Ci A B S Co 全加器的 VHDL 程序的文 件名称是 fulladder.VHD,其 中 VHD 是 VHDL 程序的文件 扩展名,程序如下: LIBRARY IEEE; --IEEE 标准库 USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY fulladder IS -- fulladder 是实体名称 PORT( A, B, Ci : IN STD_LOGIC; --定义输入 /输出信号 Co, S : OUT STD_LOGIC ); END fulladder; ARCHITECTURE addstr OF fulladder IS --addstr 是结构体名 BEGIN S <= A XOR B XOR Ci; Co <= (A AND B) OR (A AND Ci) OR (B AND Ci); 程序包 实体 结构体 END addstr; 从这个例子中可以看出,一段完整的 VHDL 代码主要由以下几部分组成: 第一部分是程序包,程序包是用 VHDL 语言编 写的共享文件,定义在设计结构体和 实体中将用到的常数、数据类型、子程序和设 计好的电路单元等,放在文件目录名称为 IEEE 的程序包库中。 第二部 分是 程序的 实体 ,定义 电路 单元的 输入 /输出引 脚信 号。程 序的 实体名 称 fulladder 是 任意取的, 但是必须与 VHDL 程序的文件名称相同。 实体的标识符是 ENTITY, 实体以 ENTITY 开头,以 END 结束 。其中,定义 A、 B、 Ci 是输入信号引脚,定义 Co 和 S 是输出信号引脚。 第三部分是程序的结构体, 具体描述电路的内部结构和逻辑功能。 结构体有三种描述 126 方式, 分别是行为 ( BEHAVIOR) 描述、 数据流 ( DATAFLOW) 描述方式和结构 (STRUCTURE) 描述方 式, 其中数 据流 ( DATAFLOW) 描述方 式又称 为寄 存器( RTL)描述 方式 ,例中 结构体的描述方式属于数据流描述方式。 结构体以标识符 ARCHITECTURE 开头, 以 END 结尾。结构体的名称 addstr 是任意取的。 2.1.3 VHDL 语言的实体( ENTITY)说明语句 实体是 VHDL 程序设计中最基本的组成部分,在实体中定义了该设计芯片中所需要 的输入 /输 出 信号引 脚。 端口信 号名 称表示 芯片 的输入 /输 出 信号的 引脚 名,这 种端 口信 号 通常被称为外部信号,信号的输入 /输出状态被称为端口模式,在实体中还定义信号的 数 据类型。 实体说明语句的格式为: ENTITY 实体名称 IS PORT( 端口信号名称 1:输入 /输出状态 数据类型; 端口信号名称 2:输入 /输出状态 数据类型; … 端口信号名称 N:输入 /输出状态 数据类型 ) ; END 实体名称; 例 2-1-2 一个同步十六进制加法计数器,带有计数控制、异步清零、和进位输出等功 能。计数器电路图如图 2-1-2 所示,电路有三个输入端和五个输出端,分别是时钟脉冲输 入端 CLK, 计数器状态控制端 EN, 异步清零控制端 Rd, 四 位计数输出端 Q0, Q1, Q2, Q3 和一个进位输出端 Co。 当计数器输出 0000~ 1110 时, Co=0, 只有当计数器输出 1111 时, Co=1。电路的功能表如表 2-1-2 所示。 Q0 Q1 Q2 Q3 EN Co Rd CLK 图 2-1-2 同 步十六进制加法计数器 表 2-1-2 同步十六进制加法计数器的功能表 控制端 CLK EN Rd 工作状态 × × 0 异步清零 上升沿 1 1 计数 × 0 1 保持 该设计的实体部分如下: ENTITY cntm16 IS PORT ( EN : IN STD_LOGIC; 127 Rd : IN STD_LOGIC; CLK : IN STD_LOGIC; Co : OUT STD_LOGIC; Q : BUFFER STD_LOGIC_VECTOR(3 DOWNTO 0) ); END cntm16; 1. 实体名称 表 示所设计 电 路的电路 名 称,必须与 VHDL 文件 名相同, 实 体名称是 “cntm16”,所存的 VHDL 文件名必须是 “cntm16.VHD”。 2. 端口信号名称表示芯片的输入 /输出 信号的引脚名,这种端口信号通常被称为外部 信 号, 端口信号名称可以表示一个信号, 也可以表示一组信号 ( BUS) , 由数据类型定义, 如 EN, Rd, CLK, Co 分 别表示计数允许信号, 异步清零信号, 时钟输入信号和进 位 输出信号, Q 是一组输出信号, 用来表示四位同步二进制计数器的四位计数输出信号。 3. 端口信号输入 /输出状态有以下几种状态: IN 信号进入电路单元。 OUT 信号从电路单元输出。 INOUT 信号是双向的,既可以进入电路单元也可以从电路单元输出。 BUFFER 信号从电路单元输出,同时在电路单元内部可以使用该输出信号。 4. 端口数据类型( TYPE)定义端口信号的数据类型,在 VHDL 中,常用 的端口信号数 据类型如下: ( 1) 位( BIT) 型 : 表示一位信号的值, 可以取值 ’0’和 ’1’, 放在单引号里面表示, 如 X < =‘1’, Y <=‘0’。 ( 2) 位向量( BIT_VECTOR)型:表示一 组位型信号 值,在使用 时必须标明 位向量的宽 度(个数)和位向量的排列顺序,例如: Q : OUT BIT_VECTOR(3 downto 0),表 示 Q3, Q2, Q1, Q0 四个 位型信号。 位向量的信号值放在双引号里面表示, 例如 Q <= “0000”; ( 3) 标准逻辑位( STD_LOGIC)型: IEEE 标准的逻辑类型,它是 BIT 型数 据类型的扩 展,可以取值 ’U’, ’X’, ’0’, ’1’, ’Z’, ’W’, ’L’, ’H’, ’-’等。 ( 4) 标准逻辑位向量 ( STD_LOGIC_VECTOR)型 : IEEE 标准的逻辑向量, 表示一组标 准逻辑位型信号值。 VHDL 是与 类型高度相关的语言, 不允许将一种数据类型的信号赋予另一种数据类型 的信号。除了上述介绍的数据类型外,还有其他多种数据类型用于定义内部信号和变量, 请参见 2-2 节。 2.1.4 VHDL 语言的结构体( ARCHITECTURE) 结构体是 VHDL 程序设计中的最主要组成部分, 是描述设计单元的具体结构和功能, 在程序中, 结构体放在实体的后面。 每一个结构体都有名称, 结构体的名称是由设计者任 取的,结构体是以标识符 ARCHITECTURE 开头,以 END 结 尾。结构体可以有三种描述 方式, 分别 是行为 ( BEAVHER)描述方式 、数 据流( DATAFLOW)描 述方式 和结 构 (STRUCTURE)描述方式, 其中数据流 ( DATAFLOW) 描述方 式又称为寄存器 ( RTL)描 述方式。不同的结构体采用不同的描述语句。 结构体的一般格式为: ARCHITECTURE 结构体名 OF 实体名称 IS 说明语句 BEGIN 电路描述语句 128 END 结构体名; 结构体 说明 语句是 对结 构体中 用到 的数据 对象 的数据 类型 、元件 和子 程序等 加以 说 明。 电路描述语句用并行语句来描述电路的各种功能, 这些并行语句包括并行信号赋值语 句、 条件赋值 (WHEN-ELSE)语句、 进程 ( PROCESS) 语句、 元件例化 (COMPONET MAP) 语句和子程序调用语句等。 例 2-1-2 设计程序的结构体部分如下: ARCHITECTURE counstr OF cntm16 IS BEGIN Co <= ‘1’ WHEN (Q =”1111” AND EN =‘1’) ELSE ‘0’; --条件赋值语句 PROCESS (CLK, Rd) --PROCESS语句 BEGIN IF (Rd=‘0’) THEN --IF 语句 Q <= ”0000”; ELSIF (CLK’ EVENT AND CLK=‘1’) THEN --CLK 上升沿计数 IF(EN=‘1’) then Q <= Q+1; END IF; END IF; END PROCESS; END counstr; 结构体的 名 称 是 counstr,该结构 体 属于行为 描 述方式, 采 用多种描 述 语句,如 进程 ( PROCRESS) 语句, 条 件赋值 语句 ( WHEN-ELSE) ,顺 序语 句( IF-ELSE )等, 这些 语句的具体用法参见 2-3 节相关内容。 2.1.5 程序包( PACKAGE) 、库( LIBRARY)和 USE 语句 程序包定义了一组标准的数据类型说明、 常量说明、 元件说明、 子程序说明和函数说 明等,它是一个用 VHDL 语言描写的一段程序,可以供其他设计单元调用。它如同 C 语 言中的 *.H 文 件一样, 定义了一些数据类型说明和函数说明。 在一个设计单元中, 在实 体 部分所定义 的数据类型 、常数和子 程序在相应 的结构体中 是可以被使 用的(可见 的 ) ,但 是在一个实体的说明部分和结构体部分中定义的数据类型、 常量及子程序却不能被其它设 计单元的实 体和结构体 使用(不可 见) 。程序包就是为了使 一组类型说 明、常量说 明 和 子 程序说明对多个设计单元都可以使用而提供的一种结构。程序包分为两大类,即 VHDL 预定义标准程序包和用户定义的程序包。 VHDL 设计中常用的标准程序包的名称和内容如 见表 2-1-3 所列。用户定义的程序包是设计者把预先设计好的电路单元设计定义在一个程 序包中, 放在指定的库中, 以供其它设计单元调用, 如果在设计中要使用某个程序包中的 内容时,可以用 USE 语句打开该程序包。有关程序包的设计方法参见 2-4-5 节的内容。 库( LIBRARY)是专门用于存放预先编译好的程序包的地方,它实际上对应一个文 件目录, 程序包的文件就存放在此目录中。 库名与目录名的对应关系可以在编译程序中指 定,库的说明总是放在设计单元的最前面。例如,对 IEEE 标准库的调用格式为: LIBRARY IEEE; 表 2-1-3 IEEE 两个标准库 STD 和 IEEE 中的程序包 库名 程序包名 定义的内容 STD STANDARD TEXTIO 定义 VHDL 的数据类型,如 BIT, BIT_VECTOR 等 TEXT 读写控制数据类型和子程序等 IEEE STD_LOGIC_1164 定义 STD_LOG, STD_LOGIC_VECTOR 等 129 STD_LOGIC_ARITH 定义有符号与无符号数据类型, 基于这些数据类型的算 术运算符,如 “+”, “-”, “?”, “/”SHL, SHR 等 STD_LOGIC_SIGNED 定义基于 STD_LOGIC与 STD_LOGIC_VECTOR数据类 型上的有符号的算术运算 STD_LOGIC_UNSIGNED 定义基于 STD_LOGIC 与 STD_LOGIC_VECTOR 类型 上的无符号的算术运算 1. 常用的库和包的种类 VHDL 程序 中常用的库有 STD 库、 IEEE 库和 WORK 等。其 中 STD 和 IEEE 库中的 标准程序包是由提供 EDA 工具的厂商提供的, 用户在设计程序时可以用相应的语句调用。 ( 1) STD 库 STD 库是 VHDL 语言标准库, 库中定义 了 STANDARD 和 TEXTIO 两个标准程序包 。 STANDARD 程序包中定义了 VHDL 的基本的数据类型,如字符( CHARACTER) 、整 数 ( INTEGER) 、实数 ( REAL) 、 位型( BIT)和布 尔量( BOOLEAN)等。 用户在 程序 中 可以随时调用 STANDARD 包中的内容, 不需要 任何说明。 TEXTIO 程序 包中定义了对文 本文件的读和写控制的数据类型和子程序。 用户在程序中调用 TEXTIO 包中的内容, 需要 USE 语句加以说明。 ( 2) IEEE 库 IEEE 标准库是存放用 VHDL 语言编写的多个标准程序包的目录, IEEE 库中的程序 包有 STD_LOGIC_1164, STD_LOGIC_ARITH, STD_LOGIC_UNSIGNED 和 STD_LOGI C_SIGNED 等程序包。 其中 STD_LOGIC_1164 是 IEEE 标准 的程序包, 定义了 STD_LOG IC 和 STD_LOGIC_VECTOR 等多种数据类型,以及多种逻辑运算符子程序和数据类型转 换子程序等。 STD_LOGIC_ARITH 和 STD_LOGIC_UNSINGED 等程序包是 SYNOPSYS 公司提供的, 包中定义了 SIGNED 和 UNSIGNED 数据类型以及基于这些数据类型的运算 符子程序。用户使用包中的内容,需要用 USE 语句加以说明。 ( 3) WORK 库 WORK 库是 用户进行 VHDL 设计的当前目录,用于存放用户设计好的设计单元和程 序包。在使用该库中的内容时不需要进行任何说明。 2.库、包和 USE 语句的格式 用户在用到标准程序包中内容时, 除了 STANDARD 程序包以外, 都要在设计程序中 加以说明,首先用 LIBRARY 语句说 明程序包所在的库名,再用 USE 语 句说明具体使用 哪一个程序包和具体的子程序名。 各种标准程序包中的内容太多, 初学者一时之间难以全 面了解,可以用下面的格式,以免出现不必要的错误。 库和包的调用格式: LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH..ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; 2.2 VHDL的数据类型和数据对象 VHDL 语言 和其它 高级 语言一 样, 除了具 有一 定的语 法结 构外, 还定 义了常 数、 变 量和信号等三种数据对象, 每个数据对象要求指定数据类型, 每一种数据类型具有特定的 物理意义。由于 VHDL 语言是强类型语言,不同的语句类型的数据之间不能进行运算和 赋值,我们有必要详细了解 VHDL 语言的数据类型和数据对象。 2.2.1 VHDL 的标记 一个完整的 VHDL 语句 可以有下列几个部分组成: 标识符、 保留字 ( Reserved Words) 、 130 界符、常数、赋值符号和注释( 1. 标识符 标识符是程序员为了书写程序所规定的一些词, 变量、 信号、 子程序、 结构体和实体等名称。 VHDL 基本的标识符组成的规则如下: ( 1) 标识符由 26 个英文字母、数字 ( 2) 标识符必须是以英文字母开头; ( 3) 标识符中不能有两个连续的下划线 ( 4) 标识符中的英文字母不区分大小写; ( 5) 标识符字符最长可以是 32 例如: CLK, QO, DAT1, SX_1, 3DA, _QD, NA__C, DB-A 2. 保留字 VHDL 中的 保留字是具有特殊含义的标识符号, 留字作为标识符。 比如 ENTITY, 等。 VHDL 保留字如表 2-2-1 所列。 abs access and architectu begin block case componen downto else exit file generic group in inertial library linkage mod nand not null or others postponed procedure record register return rol signal shared srl subtype type unaffected variable wait xnor xor 3. VHDL 中的界符 界符是作为 VHDL 语言 中两个部分的分隔符用的。 用双减号 “-”开头的部分 是注释内容 符号是 “:=” 等。 在 VHDL 中,常用的界符如表 Comments) ,所有这些统称为标记。 用来表示常数、 0, 1, 2, …, 9 及下划线 “_”组成; “_”,标识符的最后一个字符不能是下划线; 个字符。 NOT_Q 是合法的标识符。 , DB_等是非法的标识符。 只能作为固定的用途, 用户不能用保 ARCHITECTURE, PROCESS, BLOCK, BEGIN 和 END 表 2-2-1 VHDL 保留字 after alias all re array assert attribute body buffer bus t configuration constant disconnect elsif end entity for function generate guarded if impure inout is label literal loop map new next nor of on open out package port process pure range reject rem report ror select severity sla sll sra then to transport units until use when while with 如每个完整的语句均以 “;” 结尾, ,不参加程 序编译。信 号赋值符号 是 “<=”,变 量赋值 2-2-2 所列 表 2-2-2 VHDL 中的界符 , : ; > < = + ? * / & <> -- _ ( ) <= := 131 4. 注释符 在 VHDL 中, 为了便于理解和阅读程序, 常常加上注释, 注释符用双减号 “?”表示。 注释语句以 注释符打头 ,到行尾结 束。注释可 以加在语句 结束符 “;”之后,也可以 加在空 行处。 2.2.2 VHDL 的数据类型 在 VHDL 中 ,定义了三种数据对象,即信号、变量和常数,每一个数据对象都必须 具有确定的数据类型,只有相同的数据类型的 两个数据对象才能进行运算和赋值,为此 VHDL 定义 了多种标准的数据类型,而且每一种数据类型都具有特定的物理意义。例如, BIT 型、 STD_LOGIC 型、 INTEGER 型和 REAL 型等数据类型。 VHDL 的数 据类型较多, 根据数据用途分类可分为标量型、 复合型、 存取型和文件型 。 标量型 包括 整数类 型、 实数类 型、 枚举类 型和 时间类 型, 其中位 ( BIT)型和 标准 逻辑位 ( STD_LOGIC) 型属于枚举类型。 复合型主要包括数组 ( ARRAY) 型和记录 ( RECORD) 型,存取类型和文件类型提供数据和文件的存取方式。 这些数据类型又可以分为两大类:即在 VHDL 程序包中预定义的数据类型和用户自 定义的数据类型。 预定义的数据类型是最基本的数据类型, 这些数据类型都定义在标准程 序包 STANARD、 STD_LOGIC_1164 和其它标准的程序包中,这些程序包放在 EDA 软件 中 IEEE 和 STD 目录中, 供用户随时调用。 在预定义的各种数据类型的基础上, 用户可以 根据实际需要自己定义数据类型和子类型, 如标量型和数组型。 使用用户定义的数据类型 和子类型可以使设计程序的语句简练易于阅读,简化设计电路硬件结构。 值得注意的是, 各种 EDA 工具不能完全支持 VHDL 的所有数据类型, 只支持 VHDL 的子集。 1. STANDARD 程序包中预定义的数据类型 (1) 整数( INTEGER)数据类型 整数数据类型与数学中整数的定义是相同的,整数类型的数据代表正整数、负整数 和 零。 VHDL 整数类型定义格式为: TYPE INTEGER IS RANGE -2147483648 TO 2147483647 ; 实际上一个整数是由 32 位二进制码表示的带符号数的范围。所以,数值范围也可以 表示为 - ~ -1。 31 2 31 2 正整数( POSITIVE)和自然数( NATURAL)是整数的子类型,定义格式为: SUBTYPE POSITIVE IS INTEGER RANGE 0 TO INTEGER’HIGH ; SUBTYPE NATURE IS INTEGER RANGE 1 TO INTEGER’HIGH ; 其中 INTEGER’HIGH 是 数值类属性,代表整数上限的数值,也即 -1。所以正整数 表示的数值范围是 0~ -1, 自然数表示的数值范围是 1~ -1。 实际使用过程中为了节 省硬件组件,常用 RANGER… TO… 限制整数的范围。例如: 31 2 31 2 31 2 SIGNAL A :INTEGER; --信号 A 是整数数据类型 VARIABLE B :INTEGER RANGE 0 TO 15; --变量 B 是整数数据类型,变化范围是 0 到 15。 SIGNAL C :INTEGER RANGE 1 TO 7; --信号 C 是整数数据类型,变化范围是 1 到 7。 (2) 实数( REAL)数据类型 VHDL 实数 数据类型与数学上的实数相似, VHDL 的实数就是带小数点的数, 分为正 数和小数。实数有两种书写形式即小数形式和 科学计数形式,不能写成整数形式。例如 132 1.0, 1.0E4, -5.2 等实数是合法的。实数数据类型的定义格式为: TYPE REAL is range -1.7e38 to 1.7e38; 例如: SIGNAL A, B, C :REAL ; A <= 5.0; B <= 3.5E5; C <= -4.5; (3) 位( BIT)数据类型 位数据类型的位值用字符 ‘0’和 ‘1’表示,将值放在单引号中,表示二值逻辑的 0 和 1。 这里的 0 和 1 与整数型 的 0 和 1 不 同 , 可以进行算术运算和逻辑运算, 而整数类型只能进 行算术运算。位数据类型的定义格式为: TYPE BIT is ( '0', '1' ); 例如: RESULT : OUT BIT; RESULT <= ‘1’; 将 RESULT 引脚设置为高电平。 (4) 位向量( BIT_VECTOR)数据类型 位向量是基于 BIT 数据类型的数组。 VHDL 位向量的定义格式为: TYPE BIT_VECTOR is array (NATURAL range <>) of BIT; 使用位向量必须注明位宽, 即数组的个数和排列顺序, 位向量的数据要用双引号括起 来。 例如 “1010”, X “A8”。其 中 1010 是四位二进制数, 用 X 表示双引号里的数是十六进 制数。 例如: SIGNAL A :BIT_VECTOR (3 DOWNTO 0 ); A <= “1110” ; 表示 A 是四 个 BIT 型元素组成的一维数组, 数组 元素的排列顺序是 A3=1, A2=1, A1=1, A0=0。 (5) 布尔( BOOLEAN)数据类型 一个布尔量 具有真( TRUE)和假( FALSE)两 种状态。布 尔量没有数 值的含义, 不 能用于数值运算,它的数值只能通过关系运算产生。例如,在 IF 语句中, A>B 是关系运 算,如果 A=3, B=2,则 A>B 关系成立,结果是布尔量 TRUE,否则结果为 FALSE。 VHDL 中,布尔数据类型的定义格式为 : TYPE BOOLEAN IS (FALSE, TRUE); (6) 字符( CHARACTER)数据类型 在 STANDARD 程序包中预定义了 128 个 ASCII 码字符类型, 字符类型用单引号括起 来, 如 ‘A’, ‘b’, ’1’等, 与 VHDL 标识符不区分大小写不同, 字符类型中的字符大小写 是 不同的,如‘ B’和‘ b’不同。 (7) 字符串( STRING) 在 STANDARD 程序包中,字符串的定义是 : TYPE STRING is array (POSITIVE range <>) of CHARACTER; 字符串数据类型是由字符型数据组成的数组,字符串必须用双引号括起来。例如: CONSTANT STR1 :STRING := “Hellow world”; 定义常数 ST1 是字符串,初值是 “Hellow world ”。 (8) 时间( TIME)数据类型 表示时间的数据类型, 一个完整的时间类型包括整数表示的数值部分和时间单位两个 部分,数值和单位之间至少留一个空格,如 1 ms, 20 ns 等。 STANDARD 程序包中定义时间格式为: 133 TYPE TIME is range -9223372036854775808 to 9223372036854775807 UNITS fs; -- 飞秒 ps = 1000 fs; -- 皮秒 ns = 1000 ps; -- 纳秒 us = 1000 ns; -- 微秒 ms = 1000 us; -- 毫秒 sec = 1000 ms; -- 秒 min = 60 sec; -- 分 hr = 60 min; -- 小时 END UNITS; 2. IEEE 预定义的标准逻辑位和标准逻辑位向量 (1) 标准逻辑位( STD_LOGIC)数据类型 STD_LOGIC 是位( BIT)数据类型的扩展,是 STD_ULOGIC 数据类型的子类型。它 是一个逻辑型的数据类型, 其取值取代 BIT 数据 类型的取值 0 和 1 两种数 值, 扩展定义了 九种值, 在 IEEE STD1164 程序包中, STD_ULOGIC 和 STD_LOGIC 数据类型定义格式为: TYPE std_ulogic IS ( 'U', -- Uninitialized 'X', -- Forcing Unknown '0', -- Forcing 0 '1', -- Forcing 1 'Z', -- High Impedance 'W', -- Weak Unknown 'L', -- Weak 0 'H', -- Weak 1 '-' -- Don't care ); FUNCTION resolved ( s : std_ulogic_vector ) RETURN std_ulogic; SUBTYPE std_logic IS resolved std_ulogic; STD_LOGIC 和 STD_ULOGIC数据类 型的区别在于 STD_LOGIC数据类型是经过重新 定义的,可以用来描述多路驱动的三态总线,而 STD_ULOGIC 数据类 型只能用于描述单 路驱动的三态总线。 (2) 标准逻辑位向量( STD_LOGIC_VECTOR)数据类型 STD_LOGIC_VECTOR 是基于 STD_LOGIC 数据类型的标 准逻辑一维 数组, 和 BIT_VECTOR 数组 一样 ,使用 标准 逻辑位 向量 必须注 明位 宽和排 列顺 序,数 据要 用双引 号括起来。例如: SIGNAL SA1 :STD_LOGIC_VECTOR (3 DOWNTO 0 ); SA1 <= “0110” ; 在 IEEE_STD_1164 程序包中, STD_LOGIC_VECTOR 数据类型定义格式为: TYPE std_logic_vector IS ARRAY ( NATURAL RANGE <>) OF std_logic; 3. 其它预定义的数据类型 在 STD_LOGIC_ARITH 程序包中定义了无符号( UNSIGNED) 和带符号( SIGNED) 数据类型,这两种数据类型主要用来进行算术运算。定义格式为: TYPE UNSIGNED is array (NATURAL range <>) of STD_LOGIC; TYPE SIGNED is array (NATURAL range <>) of STD_LOGIC; (1) 无符号( UNSIGNED)数据类型 134 无符号数据类型是由 STD_LOGIC 数据类型构成的一维数组,它表示一个自然数。在 一个结构体中,当一个数据除了执行算术运算 之外,还要执行逻辑运算,就必须定义成 UNSIGNED,而不能是 SIGNED 或 INTEGER 类型。例如: SIGNAL DAT1 :UNSIGNED( 3 DOWNTO 0) ; DAT1 <= “1001”; 定义信号 DAT1 是四位二进制码表示的无符号数据,数值是 9。 (2) 带符号( SIGNED)数据类型 带符号( SIGNED)数据 类型表示一 个带符号的 整数,其最 高位用来表 示符号位, 用 补码表示数值的大小。 当一个数据的最高位是 0 时, 这个数 表示正整数, 当一个数据的最 高位是 1 时,这个数表示负整数,例如: VARIABLE DB1, DB2 : SIGNED( 3 DOWNTO 0) ; DB1 <= “0110” ; DB2 <= “ 1001” ; 定义变量 DB1 是 6,变量 DB2 是 -7。 4. 用户自定义的数据类型 在 VHDL 中 ,用户可以根据设计需要,自己定义数据的类型,称为用户自定义的数 据类型。 利用用户自己定义数据类型可以使设计程序便于阅读。 用户自定义的数据类型可 以通过两种途径来实现, 一种方法是通过对预定义的数据类型作一些范围限定而形成的一 种新的数据类型。这种定义数据类型的方法有如下几种格式: TYPE 数据类型名称 IS 数据类型名 RANGE 数据范围; 例如: TYPE DATA IS INTEGER RANGER 0 TO 9 ; 定义 DATA 是 INTEGER 数据类型的子集,数据范围是 0~ 9。 SUBTYPE 数据类型名称 IS 数据类型名 RANGE 数据范围; 例如: SUBTYPE DB IS STD_LOGIC_VECTOR( 7 DOWNTO 0) ; 定义 DB 是 STD_LOGIC_VECTOR 数据类型的子集,位宽 8 位。 另一种方法是在数据类型定义中直接列出新的数据类型的所有取值, 称为枚举数据类 型。定义该种数据类型的格式为: TYPE 数据类型名称 IS(取值 1,取值 2,…) ; 例如 TYPE BIT IS( ’0’,’1’) ; TYPE STATE_M IS (STAT0, STAT1, STAT2, STAT3); 定义 BIT 数 据类型, 取值 0 和 1。定 义 STATE_M 是数据类型, 表示状态变量 STAT0, STAT1, STAT2, STAT3。 在 VHDL 中, 为了便于阅读程序, 可以用符号名来代替具体的数值, 前例中 STATE_M 是状态变量, 用符号 STAT0, STAT1, STAT2, STAT3 表示四种 不同的状态取值是 00, 01, 10, 11。例如定义一个 “WEEK” 的数据类型用来表示一个星期的七天,定义格式为: TYPE WEEK IS (SUN, MON, TUE, WED, THU, FRI, SAT); 5.数组( ARRAY)的定义 数组是将相同类型的单个数据元素集合在一起所形成的一个新的数据类型。 它可以是 一维数组( 一个下标) 和多维数组 (多个下标) ,下标的数 据类型必须 是整数。前 面 介 绍 的位向量 ( BIT_VECTOR) 和标准逻辑位向量 ( STD_LOGIC_VECTOR) 数据类型都属于 一维数组类型。数组定义的格式为: TYPE 数据类型名称 IS ARRAY 数组下标的范围 OF 数组元素的数据类型; 根据数 组元 素下标 的范 围是否 指定 ,把数 组分 为非限 定性 数组和 限定 性数组 两 种 类 型。 非限定性数组不具体指定数组元素下标的范围, 而是用 NATURAL RANGER <> 表示 , 135 当用到该数组时, 再定义具体的下标范围。 如前面介绍的位向量 ( BIT_VECTOR) 和标准 逻辑位向量 ( STD_LOGIC_VECTOR) 数据类型等在程序包中预定义的数组属于非限定性 数组。 例如,在 IEEE 程序包中定义 STD_LOGIC_VECTOR 数据类型的语句是 TYPE std_logic_vector IS ARRAY ( NATURAL RANGE <>) OF std_logic; 没有具体指出数组元素的下标范围,在程序中用信号说明语句指定。 例如: SIGNAL DAT : STD_LOGIC_VECTOR( 3 DOWNTO 0) ; 限定性数组的下标的范围用整数指定, 数组元素的下标可以是由低到高, 如 0 TO 3, 也可以是由高到低,如 7 DOWNTO 0,表示数组元素的个数和在数组中的排列方式。 例如: TYPE D IS ARRAY( 0 TO 3) OF STD_LOGIC; TYPE A IS ARRAY( 4 DOWNTO 1) OF BIT; 定义数组 D 是一维数组,由四个 STD_LOGIC 型元素组成,数组元素的排列顺序是 D(0), D(1), D(2), D(3)。 A 数组是由 四个元素组成的 BIT 数据类型,数组元素的排列顺 序是 A(4), A(3), A(2), A(1)。 6.数据类型的转换 在 VHDL 语言中,数据类型的定义是相当严格的,不同类型的数据是不能进行运算 和赋值的。 为了实现不同类型的数据赋值, 就要进行数据类型的变换。 变换函数在 VHDL 语言程序包中定义。 在程序包 STD_LOGIC_1164、 STD_LOGITH_ARITH 和 STD_LOGIC_ UNSIGNED 中提供的数据类型变换函数如表 2-2-3 所列。 例如把 INTEGER 数据类型 的信 号转换为 STD_LOGIC_VECTOR 数据类型的方法是: 定义 A, B 为: SIGNAL A :INTEGER RANGER 0 TO 15; SIGNAL B : STD_LOGIC_VECTOR(3 DOWNTO 0); 需要调用 STD_LOGIC_ARITH 程序包中的函数 CONV_STD_LOGIC_VECTOR 调用的格式是: B <= CONV_STD_LOGIC_VECTOR(A); 表 2-2-3 数据类型变换函数 程序包名称 函数名称 功能 STD_LOGIC_1164 TO_BIT TO_BITVECTOR TO_STDULOGIC TO_STDULOGICVECTER 由 STD_LOGIC 转换为 BIT 由 STD_LOGIC_VECTOR 转换 为 BIT_VECTOR 由 BIT 转换为 STD_LOGIC 由 BIT_VECTOR 转换为 STD_LOGIC_VECTOR STD_LOGIC_ARITH CONV_INTEGER CONV_UNSIGNED CONV_STD_LOGIC_ VECTOR 由 UNSIGNED, SIGNED 转换为 INTEGER 由 SIGNED, INTEGER 转换为 UNSIGNED 由 INTEGER, UNSDGNED, SIGNED 转换为 STD_LOGIC_VECTOR STD_LOGIC_UNSIGNED CONV_INTEGER 由 STD_LOGIC_VECTOT 转换为 INTEGER 2.2.3 VHDL 的运算符 与高级语言一样, VHDL 语言的表达式也是由运算符和操作数组成的。 VHDL 标准预 定义了四种运算符, 即逻辑运算符、 算术运算符、 关系运算符、 移位运算符和连接运算符, 并且定义了与运算符相应的操作数的数据类型。 各种运算符之间是有优先级的, 例如在所 有运算符中,逻辑运算符 NOT 的优 先级别最高。表 2-2-1 列出了所有运算符的优先级顺 序。 136 表 2-2-1 VHDL 运算符列表 运算符类型 运算符 功能 优先级 AND 逻辑与 OR 逻辑或 NAND 逻辑与非 NOR 逻辑或非 XOR 逻辑异或 逻辑运算符 NXOR 逻辑异或非 = 等于 /= 不等于 < 小于 > 大于 <= 小于等于 关系运算符 >= 大于等于 SLL 逻辑左移 SLA 算术左移 SRL 逻辑右移 SRA 算术右移 ROL 逻辑循环左移 移位运算符 ROR 逻辑循环右移 + 正 符号运算符 - 负 连接运算符 & 位合并 + 加 - 减 ? 乘 / 除 MOD 求模 REM 求余 ?? 乘方 算术运算符 ABS 求绝对值 逻辑非运算符 NOT 逻辑非 最低 最高 1.逻辑运算符 在 VHDL 语言中定义了七种基本的逻辑运算符,它们分别是: AND(与) 、 OR(或) 、 NOT(非) 、 NAND(与非) 、 NOR(或 非) 、 XOR( 异或) 和 NXOR(异或非)等。 由逻辑运算符和操作数组成了逻辑表达式。在 VHDL 语言中,逻辑表达式中的操作 数的数据类型可以是 BIT 和 STD_LOGIC 数据类型, 也可以是 一维数组类型 BIT_VECTOR 和 STD_LOGIC_VECTOR, 要求运算符两边的操作数的数据类型相同、 位宽相同。 逻辑运 算是按位进行的,运算的结果的数据类型与操作数的数据类型相同。 例如用 VHDL 描述逻辑表达式是 Y=AB, Z=A+B+C 的程序如下: ENTITY loga IS PORT( A, B, C : IN STD_LOGIC; Y, Z : OUT STD_LOGIC 137 ); END loga; ARCHITECTURE stra OF loga IS BEGIN Y<= A AND B; Z <= A OR B OR C; END stra; 例如用 VHDL 描述两个位向量的逻辑运算的程序如下: ENTITY logb IS PORT ( A, B : IN BIT_VECTOR( 0 TO 3) ; Y : OUT BIT_VECTOR( 0 TO 3) ) ; END logb; ARCHITECTURE strb OF logb IS BEGIN Y <=A AND B; END strb; 如果 A=1011, B=1101,则程序仿真的结果是 Y=1001。 在一个逻辑表达式中有两个以上的运算符时, 需要用括号对这些运算进行分组。 例如 语句 X1 <=(A AND B ) OR (C AND B); X2 <=( A OR B) AND C; 是正确的。 如果一个逻辑表达式中只有 AND、 OR 和 XOR 三种运算符中的一种运算符,那么改 变运算顺序不会影响电路的逻辑关系, 表达式中的括号是可以省略的。 例如下列语句是正 确的。 Y1 <=A AND B AND C; Y2 <= A OR B OR D; 2.算术运算符 VHDL 语言定义了五种常用的算术运算符,分别是 + 加或正, A+B, +A - 减或负, A-B, -B ? 乘, A?B / 除, A/B ?? 指数, N??2 以及 MOD(求模) 、 REN(取余) 、 ABS(求绝对值) 等算术运算符。 在算术运算表达 式中, 两个操作数必须具有相同的数据类型, 加法和减法的操作数的数据类型可以是整数、 实数或物理量, 乘除法的操作数可以是整数或实数。 为了节约硬件资源, 除法和乘法的操 作数应该选用 INTEGER、 STD_LOGIC_VECTOR 或 BIT_VECTOR 等数据类型。 例如 X<=A+B; Y<=C?D; Z<=A-C??2 3.关系运算符 关系运算符是将两个相同类型的操作数进行数值比较或关系比较, 关系 运算的结果的 138 数据类型是 TRUE 或 FALSE,即 BOOLEAN 类型 。 VHDL 语言中定义了六种关系运算符, 分别是: = 等于 /= 不等于 > 大于 < 小于 >= 大于或等于 <= 小于或等于 在 VHDL 中, 关系运算符的数据类型根据不同的运算符有不同的要求。 其中 “=”(等 于)和 “/=”(不等于) 操作数的数 据类型可以 是所有类型 的数据,其 他关系运算 符可以 使用整数类型、 实数类型、 枚举类型和数组。 整数和实数的大小排序方法与数学中的比较 大小方法相同。 枚举型数据的大小排序方法与它们的定义顺序一致, 例如 BIT 型数 据 1>0, BOOLEAN 型数据 TRUE>FALSE。 在利用关系运算符对位向量数据进行比较时, 比较过程是从左到右的顺序按位进行比 较的,操作数的位宽可以不同,但有时会产生错误的结果。 如果 A、 B 是 STD_LOGIC_VECTOR 数据类型, A = “1110”, B= “10110”, 关系表达 式 A>B 的 比较结果是 TRUE,也就是说 A>B。对于以上出现的 错误可以利 用 STD_LOGIC_ARITH 程序包中定义的数据类型 UNSIGNED 来 解决, 把要比较的操作数定 义成 UNSIGNED 数据类型。 4.移位运算符 VHDL93 标准中增加了六个移位运算符, 分别是 SLL 逻辑左移, SRL 逻辑右移, SLA 算术左移, SRA 算术右移, ROL 逻辑 循环左移, ROR 逻辑循环右移。 移位运算符的格式 是: 操作数名称 移位运算符 移位位数; 操作数的数据类型可以是 BIT_VECTOR、 STD_LOGIC_VECTOR 等一维数组,也可 以是 INTEGER 型,移位位数必须是 INTEGER 型常数。 六条移位运算符所执行的操作如图 2-2-1 所示。 其中 SLL 是 将位向量左移,右边移空位补零。 SLA 是将位向 量左移,右边第一位的 数值保持原值不变。 SRL 是将位向量右移, 左边移空位补零。 SRA 是将位向量右移, 左 边第一位的数值保持原值不变。 ROR 和 ROL 是自循环移位方式。 例如 A <= “0101”; B <= A SL 1; 仿真的结果是 B = 1010。 0 SRL SRA ROR ROL 图 2-2-1 移位运算符操作示意图 0 SLL SLA 5.连接运算符( &) 用连接运算符可以将多个数据对象合并成一个新的一维数组, 也可以将两个一维数组 139 中的元素分解合并成新的一维数组。 连接两个操作符产生新的一维数组的位宽等于两个操 作数的位宽 之和,新的 数组元素的 顺序是由操 作数的位置 决定的,连 接符 “&”左边 的操作 数的元素在左, 连接符 “&”右边的操作数的元素在右。 操作数可以是 BIT 或 STD_LOGIC 数据类型。 如果 1011,0010 A :BIT_VECTOR( 0 TO 7) 0001,0110 B :BIT_VECTOR( 0 TO 7) C <=B(0 TO 3) & A( 5 TO 7)&’1’; 则 C=1011,1101 2.2.4 VHDL 的数据对象 在算法语言中, 定义了多种数据对象, 如常数、 变量和数组等, 用来存放不同类型的 数据,如整数、实数、复数、逻辑常数和逻辑变量等。在 VHDL 程序 中,常用的数据对 象分为三种类型, 即常数 ( CONSTANT) 、变 量( VA R I A B L E) 和信号 ( SIGNAL) ,在 使 用过程中, 这三种数据对象除了具有一定的数据功能外, 还赋予了不同的物理意义, 在应 用时要特别注意。 1. 常数( CONSTANT) 常数被赋值后就保持某一固定的值不变。在 VHDL 中,常 数通常用来表示计数器的 模的大小、 数组数据的位宽和循环计数次数等, 也可以表示电源电压值的大小。 常数的使 用范围与其在设计程序中的位置有关, 如果常数在结构体中赋值, 则这个常数可供整个设 计单元使用, 属于全局量, 如果常数在 PROCESS 语句或子程序中赋值, 只能供进程或子 程序使用, 属于局部量。 程序设计中使用常数有利于提高程序的可读性和方便对程序进行 修改。 通常常数的赋值在程序开始前进行, 其数据类型在常数说明语句中指明, 赋值符号 为 “:=”。 常数定义语句的格式为: CONSTANT 常数名称:数据类型 := 表达式 例如: CONSTANT Vcc :REAL := 5.0; CONSTANT DALY : TIME := 20ns; CONSTANT KN : INTEGER := 60; 在上面的例子中, Vcc 的数据类型是实数,被赋值为 5.0, DALY 被赋值为时间常数 20ns, KN 被赋值为 60 的整数。 注意: 常数所赋的值的数据类型必须与定义的数据类型一致, 在程序中常数被赋值后 不能再改变。 2.变量(VARIABLE) 在 VHDL 程 序中,变量只能在进程和子程序中定义和使用,不能在进程外部定义使 用, 变量属于局部量, 在进程内部主要用来暂存数据。 对变量操作有变量定义语句和变量 赋值语句, 变量在赋值前必须通过定义, 可以在变量定义语句中赋初值, 变量初值不是必 需的,变量初值的赋值的符号是 “ :=”。 变量定义语句的格式为: VARABLE 变量名称:数据类型 := 初值 ; 例如: VARABLE S1 :INTEGER := 0 ; VARABLE S2, S3 :INTEGER ; VARABLE CON1 :INTEGER RANGER 0 TO 20 ; 140 VARABLE D1, D2 :STD_LOGIC ; S1 是整数型变量、初值是 0; CON1 是整数型变量,其变化范围是 0 到 20; D1, D2 是一位标准逻辑位型变量。 变量赋值语句的格式为: 变量名称 := 表达式 ; 在对变 量进 行赋值 时, 要求表 达式 的数据 类型 必须与 变量 定义语 句中 的数据 类 型 一 致, 表达式的数据对象可以是常数、 变量和信号。 变量赋值是立即发生的, 没有任何时间 延迟的, 所以变量只有当前值, 并且对同一个变量可以多次赋予新值。 多个变量的赋值是 根据赋值语句在程序中的书写位置, 按照自上而下顺序进行的, 所以变量赋值语句属于顺 序执行语句。变量不能放在进程的敏感信号表中。 例如: PROCESS( D, E) VARIABLE AV, BV, CV :INTEGER := 0 ; BEGIN AV := 1 ; BV := AV + D ; AV := E + 2 ; CV := AV * 2 ; A <= AV ; B <= BV ; C <= CV ; END PROCESS ; 这是一个进程语句,定义 AV, BV, CV 是整数型变量,当敏感信号 D, E 只要有 一 个发生变化,放在进程中的语句就要全部执行一次,如 D = 1, E 变化 为 2,则这 段程序 的执行结果是: A = 4, B =2, C = 8。 3. 信号( SIGNAL) 在 VHDL 中 ,信号分为外部端口信号和内部信号,外部端口信号是设计单元电路的 引脚,在程序实体中定义,外部信号对应四种 I/O 状态是 IN, OUT, INOUT, BUFFER 等, 其作用是在设计单元电路之间起互连作用, 外部信号可以供整个设计单元使用属于全 局量。 例如在结构体中, 外部信号可以直接使用, 不需要加以说明, 可以通过信号赋值语 句给外部输出信号赋值。 内部信 号是 用来描 述设 计单元 内部 的传输 信号 ,它除 了没 有外部 信号 的流动 方 向 之 外, 其它性质与外部信号一致。 内部信号的使用范围 (可见性) 与其在设计程序中的位置 有关, 内部信号可以在包体、 结构体和块语句中定义, 如果信号在结构体中定义, 则可以 在供整个结构体中使用, 如果在块语句中定义的信号, 只能供块内使用, 不能在进程和子 程序中定义内部信号。信号在状态机中表示状态变量。 对内部信号操作有信号定义语句和信号赋值语句,内部信号在赋值前必须通过定义, 可以在信号定义语句中赋初值, 内部信号初值不是必需的, 内部信号的定义格式与变量的 定义格式基本相同,只要将变量定义中的保留字 VA R I A B L E 换成 SIGNAL 即可。 内部信号定义语句的格式为: SIGNAL 信号名称:数据类型 := 初值 ; 例如: SIGNAL S1 :STD_LOGIC := ‘0’ ; SIGNAL D1 :STD_LOGIC _VECTOR( 3 DOWNTO 0) := “1001” ; 定义信号 S1 是标准逻辑位型,初值是逻辑 0 ;信 号 D1 是标 准逻辑位向量,初值是 141 逻辑向量 1001。 信号赋值符号与变量赋值符号不同,信号赋值符号为 “ <=”。 信号赋值语句的格式为: 信号名称 <= 表达式 ; 在对信号进行赋值时, 表达式的数据对象可以是常数、 变量和信号, 但是要求表达式 的数据类型必须与信号定义语句中的数据类型一致。 在结构体中信号的赋值可以在进程中 也可以在进程外, 但两者的赋值方式是不同的。 在进程外, 信号的赋值是并行执行的, 所 以被称之为并行信号赋值语句。在进程内,信号的赋值方式具有特殊性。 信号不能在进程中定义, 但可以在进程中赋值。 在进程中, 变量赋值是立即起作用的, 信号只有在进程被激活 (敏感信号发生变化) 后, 在进程结束时才能赋予新的值。 信号具 有时间特性, 信号赋值不是立即发生的, 需要经过固有的时间延迟, 所以信号具有过去值 和当前发生值, 这与实际电路的特性是一致的。 信号的赋值过程分为顺序处理和并行赋值 两个阶段。顺序处理是按照自上而下的顺序,用信号原来的值对所有的表达式进行运算, 运算结果不影响下一个表达式的运算, 直到处理好进程中的最后一个表达式。 并行赋值是 把表达式的值并行同时赋给信号。 整个过程是一个无限循环的过程, 循环停止的条件是敏 感信号保持不变, 所以在进程中的信号赋值语句属于顺序执行语句。 在进程之外的信号赋 值语句属于并行同时语句。 在进程中, 允许对同一个信号多次赋值, 只有最后一次赋值是有效的。 用下面两个进 程来说明信号与变量的赋值过程是不同的。 PROCESS( A,B,C,D) BEGIN D <= A ; X <= B + D ; D <= C ; Y <= B + D ; END PROCESS ; 执行的结果是: D <= C ; X <= B + C ; Y <= B + C ; PROCESS( A,B,C) VARIABLE D :INTEGER ; BEGIN D := A ; X <= B + D ; D := C ; Y <= B+ D ; END PROCESS ; 执行的结果是: X <= B + A ; Y <= B + C ; 例 2-2-3 通过一位 BCD 码的加法器的程序,比较信号、常量、变量的赋值及使用方法。 ENTITY bcdadd IS PORT( op1, op2 :IN INTEGER RANGE 0 TO 9 ; result :OUT INTEGER RANGE 0 TO 31 142 ) ; END bcdadder; ARCHITECTURE a OF bcdadder IS CONSTANT adj :INTEGER := 6 ; --定义常数 adj=6 SIGNAL binadd :INTEGER RANGE 0 TO 18 ; --定义信号 binadd 的取值范围是 0~ 18 BEGIN binadd <=op1+op2; --求 op1+op2 和运算 PROCESS (binadd) VARIABLE tmp : INTEGER:=0; --定义变量 tmp 是整数型,初值是 0 BEGIN IF binadd > 9 THEN --如果 binadd 大于 9,结果要调整 tmp := adj ; --方法是和加 6,否则,结果加 0。 ELSE tmp := 0 ; END IF ; result <=binadd+tmp ; --给外部信号赋值 END PROCESS; END a; 2.3 VHD设计的基本语句 VHDL 常用 语句可以分为两大类并行语句和顺序语句, 在数字系统的设计中, 这些语 句用来描述系统的内部硬件结构和动作行为, 以及信号之间的基本逻辑关系。 顺序语句必 须放在进程中, 因此可以把顺序语句称作为进程中的语句。 顺序语句的执行方式类似于普 通计算机语言的程序执行方式, 都是按照语句的前后排列的方式顺序执行的, 一次执行一 条语句, 并且从仿真的角度来看是顺序执行的。 结构体中的并行语句总是处于进程的外部, 所有并行语句都是一次同时执行的,与他们在程序中排列的先后次序无关。 常用的并行语句有: ( 1) 并行信号赋值语句,用 “<=” 运算符 ( 2) 条件赋值语句, WHEN-ELSE ( 3) 选择信号赋值语句, WITH-SELECT ( 4) 方块语句, BLOCK 常用的顺序语句有: ( 1) 信号赋值语句和变量赋值语句 ( 2) IF- ELSE 语句 ( 3) CASE-WHEN 语句 ( 4) FOR-LOOP 2.3.1 并行信号赋值语句 信号赋值语句的功能是将一个数据或一个表达式的运算结果传送给一个数据对象, 这 个数据对象可以是内部信号, 也可以是预定义的端口信号。 值得一提的是, 在进程中的信 号赋值语句属于顺序语句,而在结构体中进程外的信号赋值语句则属于并行语句。 例 2-3-1 用并行信号赋值语句描述逻辑表达式是 Y=AB+C⊕D 的电路。 ENTITY loga IS PORT ( A, B, C, D : IN BIT; 143 Y : OUT BIT ); END loga; --定义 A, B, C, D 是输入端口信号, Y 是输出端口信号 ARCHITECTURE stra OF loga IS SIGNAL E : BIT; --定义 E 是内部信号 BEGIN Y <=(A AND B) OR E; --以下两条并行语句与顺序无关 E <=C XOR D; END stra; 2.3.2 条件赋值语句, WHEN-ELSE 语法格式为: 信号 Y<= 信号 A WHEN 条件表达式 1 ELSE 信号 B WHEN 条件表达式 2 ELSE ... 信号 N; 在执行 WHEN-ELSE 语句时, 先判断条件表达式 1 是否为 TRUE, 若为真, Y<=信号 A,否则判断条件表达式 2 是否为 TRUE,若为 TRUE, Y<=信号 B,依 次类推,只有当 所列的条件表达式都为假时, Y<=信号 N。 例 2-3-2 用条件赋值语句 WHEN-ELSE 实现的四选一数据选择器 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY mux4 IS PORT( a0, a1, a2, a3 :IN STD_LOGIC; s :IN STD_LOGIC_VECTOR (1 DOWNTO 0); y :OUT STD_LOGIC ); END mux4; ARCHITECTURE archmux OF mux4 IS BEGIN y <= a0 WHEN s = “00” else --当 s=00 时, y=a0 a1 WHEN s = “01” else --当 s=01 时, y=a1 a2 WHEN s = “10” else --当 s=10 时, y=a2 a3; --当 s 取其它值时, y=a2 END archmux; 2.3.3 选择信号赋值语句, WITH-SELECT 语法格式为: WITH 选择信号 X SELECT 信号 Y<= 信号 A WHEN 选择信号值 1, 信号 B WHEN 选择信号值 2, 信号 C WHEN 选择信号值 3, ... 144 信号 Z WHEN OTHERS; WITH-SELECT 语句不能 在进程中应用, 通过选择信号 X 的值的变化来选择相应的操 作。当选择信号 X 的值与选择信号值 1 相同时 ,执行 Y<=信号 A,当选择信号 X 的值与 选择信号值 2 相同时,执行 Y<=信号 B,只有当选择信号 X 的值与所列的值都不同时, 才执行 Y<=信号 Z。 采用选择信号赋值语句 WITH-SELECT 实现的四选一数据选择器结构体: ARCHITECTURE archmux OF mux4 IS BEGIN WITH s SELECT y <= a0 WHEN “00”, a1 WHEN “01”, a2 WHEN “10”, a3 WHEN OTHERS; END archmux; 注意: WITH-SLECT语句 必须指 明所 有互斥 条件 ,即 “s”的 所 有取值 组合 ,因为 “s”的 类型为 “STD_LOGIC_VECTOR”, 其取值组合除了 00, 01, 10, 11 外还有 0x, 0z, x1,… 等。 虽然这些取值组合在实际电路中不出现, 但也应列出。 为避免麻烦可以用 OTHERS代 替其他各种组合。 2.3.4 块( BLOCK)语句 为了实现复杂数字电路的程序设计, 常常采用层次化设计和功能模块化设计方法, 在 VHDL 语句 中, 实现这些功能的语句有: 块语句 ( BLOCK) ,元 件( COMPONENT)定 义 语句和元件例化 ( PORT MAP) 语句, 子程序 (过程和函数 ), 以及包和库 ( LIBRARY)等 。 块语句可以看作是结构体中的子模块, 它把实现 某一特定功能的一些并发语句组合在 一起形成一个语句模块。 利用多个块语句可以把一个复杂的结构体划分成多个不同功能的 模块, 使复杂的结构体结构分明, 功能明确, 提高了结构体的可读性, 块与块语句之间的 关系是并行执行的, 这种结构体的划分方法仅仅只是形式上的, 处于一个设计层次, 块与 块之间是不透明的, 每个块都可以定义共块内使用的数据对象和数据类型, 并且这种说明 对其它块是无效的。另外,利用块语句中的保护表达式可以控制方块语句的执行。 块语句的格式为: 块标号: BLOCK 说明语句 BEGIN 并行语句区 END BLOCK 块标号; 在块语句说明部分中定义块内局部信号、 数据类型、 元件和子程序, 在块内并行语句 区可以使用 VHDL 中的所有并行语句。 例 2-3-3 设计 一个电路, 包含一个半加器和一个半减器, 分别计算出 A+B 和 A-B 的 结果。 表 2-3-1 半加器和半减器的真值表 输入值 半加器输出 半减法器输出 A B SUM Co SUB Bo 0 0 0 0 0 0 0 1 1 0 1 1 1 0 1 0 1 0 1 1 0 1 0 0 145 逻辑表达式: 半加器: SUM=A⊕ B Co=AB 半减法器: SUB=A⊕ B Bo= BA 把加法和减法分成两个功能模块, 分别用两个 BLOCK 方块语句来表示, 设计的程序 如下所示: LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY adsu is PORT( a, b : IN STD_LOGIC; co, sum, bo, sub : OUT STD_LOGIC ); END adsu ; ARCHITECTURE a OF adsu IS BEGIN half_adder : BLOCK -- half_adder BEGIN sum <= A XOR B; co <= A AND B; END BLOCK half_adder; half_subtractor: BLOCK -- half_subtractor Begin sub<= a XOR b; bo <= NOT a AND b; END BLOCK half_subtractor; END a; 2.3.5 IF-ELSE 语句 IF-ELSE 语 句是最常用的顺序语句, 其用法和语句格式与普通的计算机高级语言类似, 在 VHDL 语言中, 它只在进程中使用, 根据一个或一组条件来选择某一特定的执行通道。 其常用的格式为: 格式一 : IF 条件表达式 1 THEN 语句方块 A ELSIF 条件表达式 2 THEN 语句方块 B ELSIF 条件表达式 3 THEN 语句方块 C : ELSE 语句方块 N 146 END IF 格式二: IF 条件表达式 THEN 语句方块 A END IF; 格式三: IF 条件表达式 THEN 语句方块 A ELSE 语句方块 B END IF 格式四: PROCESS (CLK) BEGIN IF CLK'event AND CLK='1' THEN 语句方块 END IF; END PROCESS; 语句格式一是 IF 语句的完整形式,格式二和格式三是 IF 语句的简化形式,格式四是 IF 语句的一种特例, 它用于描述带有时钟信号 CLK 上升沿触发的时序逻辑电路。 IF 语句 可以嵌套使用。 IF 语句中至少应包含一个条件表达式,先判断条件表达式的结果是否为真,若为真, 则执行 THEN 后面的语句方块的语句, 执行完以后就跳转到 END IF 之后的语句。 若条件 条件表达式的结果为假,则执行 ELSE 之后的语句方块。 例如采用 IF-ELSE 实现的四选一数据选择器结构体如下: ARCHITECTURE archmux OF mux4 IS BEGIN PROCESS(s, a0, a1, a2, a3) BEGIN IF s= “00” THEN y <= a0 ; ELSIF s= “01” THEN y <= a1 ; ELSIF s= “10” THEN y <= a2; ELSE y <= a3; END IF; END PROCESS; END archmux; 每一个 IF 语句都必须有一个对应的 END IF 语句, IF 语句可以嵌套使用,即在一 个 IF 语句中可以调用另一个 IF 语句。 ELSEIF 允许在 IF 语句中出现多次。 例 2-3-4 用格 式四描述一般的 D 触发器程序如下, D 触发器的电路符号如图 2-3-1 所 示。 147 LIBRARY IEEE; D Q CLK 图 2-3-1 D 触发器 USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY dff1 IS PORT( CLK, D : IN STD_LOGIC; Q : OUT STD_LOGIC ); END dff1; ARCHITECTURE a OF dff1 IS BEGIN PROCESS (CLK) BEGIN IF CLK'EVENT AND CLK='1' THEN Q <= D; END IF; END PROCESS; END a; 程序中,时钟信号( CLK)是敏感信号,用表达式 CLK’EVENT AND CLK=’1’判断 CLK 是否产生上升沿(由低电平变成高电平) ,若 CLK 产生 上升沿,则执行 Q <= D,否 则, Q 保持不变。 如果要判断时钟信号产生下降沿,可以用表达式 CLK’EVENT AND CLK=’0’。 2.3.6 CASE-WHEN 语句 CASE-WHEN 语句属于顺序语句, 只能在进程中使用, 常用来选择有明确描述的信号。 语法格式: CASE 选择信号 X IS WHEN 信号值 1 => 语句方块 1 WHEN 信号值 2 => 语句方块 2 WHEN 信号值 3 => : WHEN OTHERS => 语句方块 N CASE-WHEN 语句的功能与 WITH-SELECT 语 句的功能相似,都是通过选择信号 X 的值的变化来选择相应的操作。但两者之间不同的是: ( 1) CASE-WHEN 语句必须放在进程中,而 WITH-SELECT 语句是并行语句必须放 在进程外; ( 2) CASE-WHEN 语句根据选择信号的值,执行不同的语句方块,完成不同的功能。 而 WITH-SELECT 语句根据选择信号的值只能执行一个操作。 (3) 使用 CASE-WHEN 语句时, WHEN 语句中的信号值必须在选择信号的取值范围 内,如果 WHEN 语句中列举的信号值不能覆盖选择信号 S 的所有取值,就用关键 字 OTHERS 表示未能列出的其他可能的取值。 148 例如采用 CASE-WHEN 实现的四选一数据选择器结构体: ARCHITECTURE archmux OF mux4 IS BEGIN PROCESS(S, A0, A1, A2, A3) BEGIN CASE S IS WHEN “00” => y <= A0 ; WHEN “01” => y<= A1 ; WHEN “10” => y <= A2 ; WHEN OTHERS => y <= A3; END CASE; END PROCESS; END archmux; 该结构体的功能是:通过 PROCESS 对信号 S 进行感测,当 S= “00”时, Y=A0,当 S= “01”时, Y=A1,当 S= “10”时, Y=A2,当 S= “11”时, Y=A3, 程序中用关键字 OTHERS 表示 S= “11”。 2.3.7 FOR-LOOP 语句 FOR-LOOP 语句是一种循环执行语句, 它可以使包含的一组顺序语句被循环执行, 其 执行的次数可由设定的循环参数决定, 只要设计到重复的动作需求时, 就可以考虑使用循 环语句。 FOR-LOOP 语句分为递减方式和递增方式,两种语法格式为: (1) 递减方式 FOR I IN 起始值 DOWNTO 结束值 LOOP 顺序语句 END LOOP; (2) 递增方式 FOR I IN 起始值 TO 结束值 LOOP 顺序语句 END LOOP; 在循环语句 中, I 是循 环变量决定 循环次数, 循环变量的 变化范围由 起始值和结 束 值 的大小确定, 起始值和结束值都应该取整数。 当采用 DOWNTO 递减方式时, 取起始值大 于结束值, I 从起始值开始, 每执行 一次循环后 I 递减 1, 直 到 结束值为止; 当采用 TO 递 增方式时,取结束值大于起始值, I 也是从起始值开始执行,每次循环增加 1。 例 2-3-5 用 FOR-LOOP 语句描述奇偶校验器中的奇校验。输入四位二进制数,当检测到 数据中 1 的位数为奇数时,输出 Y=1,否则, Y=0。 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY loop1 IS PORT( D : IN STD_LOGIC_VECTOR(0 TO 7); --输入 D 是八位二进制数 Y : OUT STD_LOGIC ); END loop1; 149 ARCHITECTURE a OF loop1 IS BEGIN PROCESS (D) VARIABLE tmp : STD_LOGIC ; --定义临时变量 tmp BEGIN tmp := ‘0’; FOR I IN 0 TO 7 LOOP tmp := tmp XOR D(I); --变量赋值语句是立即赋值, tmp=tmp⊕D(I) END LOOP; Y <= tmp ; END PROCESS; END a; 2.4 VHDL高级语句 前面详细地介绍的 VHDL 常用的并行语句和顺序语句, 在此基础上进一步介绍 VHDL 中用于结构化和模块化的设计语句, 包括: 进程语句 ( PROCESS) 、 元件和元件例化语句、 生成语句、子程序和程序包等。 2.4.1 进程( PROCESS)语句 进程语句是在结构体中用来描述特定电路功能的程序模块。 进程语句的内部主要是由 一组顺序语句组成的。 进程中的语句具有顺序处理和并行执行的特点。 在一个结构体中可 以包含多个进程语句, 多个进程语句之间的是并行同时执行的, 所以并行语句本身属于并 行语句。 进程语句即可以用来描述组合逻辑电路, 也可以描述时序逻辑电路。 进程语句的 语法结构格式为: <进程名称 > : PROCESS <敏感信号表 > 进程说明区:说明用于该进程的常数,变量和子程序。 BEGIN 变量和信号赋值语句 顺序语句 END PROCESS <进程名称 >; ( 1)每个进 程语句结构都可以取一个进程名称,但进程语句的名称是可以选用的。 进程语句从 PROCESS 开始至 END PROCESS 结束。进程中的 敏感信号表( sensitivity list)只能是进程中使用 的一些信号 ,而不能是 进程中的变 量。当敏感 信号表中的 某个 信 号的值发生变化时, 立即启动进程语句, 将进程中的顺序语句按顺序循环执行, 直到敏感 信号表中的信号值稳定不变为止。也可以用 WAIT 语句来启动进程。 ( 2)在进程 说明部分能定义常数、变量和子程序等,但不能在进程内部定义信号, 信号只能在结构体说明部分定义。 ( 3) 在进程 中的语句是顺序语句, 包 括信号赋值语句、 变量赋 值语句、 IF 语句、 CASE 语句和 LOOP 语句等 用 PROCESS 语句描述的计数器的程序如下: PROCESS(CLK, Rd) --进程(敏感信号表) BEGIN IF (Rd=‘0’) THEN Q <= “0000”; ELSIF (CLK’ EVENT AND CLK=‘1’) THEN IF(en=‘1’) then 150 Q <= Q+1; end if; END IF; END PROCESS; 在敏感信号表中,信号 Rd, CLK 被 列为敏感信号,当此两个信号只要有一个发生变 化时,此进程就被执行。注意 EN 并没有被列入敏感表,这是因为 EN 起作用必须发生在 时钟的上升沿这时 CLK 必定发生变化,引起进程的执行。同样,若为同步清零,敏感表 中可无 Rd 信号,此时进程如下: PROCESS ( CLK ) --进程(敏感信号表) BEGIN IF (CLK’ EVENT AND CLK=‘1’) THEN IF (Rd=‘0’) THEN Q <= “0000”; ELSIF (EN=‘1’) then Q <= Q+1; END IF; END IF; END PROCESS; 2.4.2 元件(COMPONET)定义语句和元件例化(PORT MAP)语句 在 VHDL 程序设计中, 一个完整的 VHDL 设计程序包括实体和结构体, 实体提供设计 单元的端口信息, 结构体描述设计单元的结构和功能, 设计程序通过综合、 仿真等一系列 操作后, 其最终的目的是得到一个具有特定功能的电路元件, 因此, 把这种设计好的程序 定义为一个元件。 这种元件可以是一个描述简单门电路的程序, 也可以是一个描述一位全 加器的程序, 或者是其他复杂电路的描述。 这些元件设计好后保存在当前工作目录中, 其 他设计体可以通过元件例化的方法调用这些元件。 元件 ( COMPONET) 定义语句和元件例化 ( PORT MAP) 语句就是用于在一个结构体 中定义元件和实现元件调用的两条语句,两条语句分别放在一个结构体中的不同的位置, 元件定义 ( COMPONET) 语句放在结构体的 ARCHITECTURE 和 BEGIN 之间, 指出该结 构体调用哪一个具体的元件, 元件调用时必须要进行数据交换, 元件例化语句中的 PORT MAP 是端口映射的意思 ,表示结构 体与元件端 口之间交换 数据的方式 。其语法结 构格式 为: ( 1)元件定义( COMPONET)语句的格式为: COMPONENT 元件名称 IS PORT 元件端口信息(同该元件源程序实体中的 PORT 部分) END COMPONET; ( 2)元件例化( PORT MAP)语句的格式为: 例化名:元件名称 PORT MAP 元件端口列表 例 2-4-1 用 元件定义( COMPONET) 和元件例化语句实现四位全加器的程序设计 ,调 用的元件是一位全加器,元件名称是 fulladder,用 VHDL 描述的程序的文件名是 fulladder.VHD,四位全加器电路图如图 2-4-1 所示。 151 Ci A B S Co Ci A B S Co Ci A B S Co Ci A B S Co CI A0 B0 A1 B1 A2 B2 A3 B3 S0 C1 S1 C2 S2 C3 S3 C4 四位全加器的程序文件名为 adder4.VHD,内容如下: LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY adder4 IS PORT ( A, B : IN STD_LOGIC_VECTOR(3 DOWNTO 0); CI : IN STD_LOGIC; S : OUT STD_LOGIC_VECTOR(3 DOWNTO 0); C : BUFFER STD_LOGIC_VECTOR(4 DOWNTO 1) ); END adder4; ARCHITECTURE a OF adder4 IS COMPONENT fulladder --元件定义, fulladder 是元件名称 PORT ( -端口名表 A, B, Ci : IN STD_LOGIC; Co, S : OUT STD_LOGIC ); END COMPONENT; BEGIN U0: fulladder PORT MAP (A(0),B(0),CI,C(1),S(0)); --元件例化 U1: fulladder PORT MAP (A(1),B(1),C(1),C(2),S(1)); U2: fulladder PORT MAP (A(2),B(2),C(2),C(3),S(2)); U3: fulladder PORT MAP (A(3),B(3),C(3),C(4),S(3)); END a; 在上面程 序 的元件定 义 ( COMPONET)语句中 , COMPONET 语句后面 的元件名称 是 fulladder,其在 PORT 中的端口信号信息与描述一位全加器的程序名 fulladder.VHD 在 实体中 PORT 部分的端 口信号信息必须相同,包括端口信号名称、端口信号的输入 /输出 状态和端口信号的数据类型等。 COMPONET 语 句放在结构 体的 ARCHITECTURE 和 BEGIN 之间。 在 PORT MAP 部分,元件名称 fulladder 必须与 COMPONT 中的元件名称一致, U0、 U1、 U2、 U3 是四个元件例化名, 表明在这个结构体中对 fulladder 单元的四 次不同的调用。 PORT MAP 中所列的端口信号列表表示当前设计单元与元件的端口连接方式, 在 VHDL 设计中有两种连接方式,一种是位置关联方式,如 152 U0: Fulladder PORT MAP (A(0),B(0),Ci,C(1),S(0)); PORT MAP 列出的端口信号名与 COMPONET 中的 PORT 端口信号名称在顺序、 端口 状态和数据类型上必须一致, 各个端口信号的意义取决于它的位置而不是它的名称; 另一 种是端口信号名称关联方式,在这种关联方式下,用符号 “=>”连接元件端口和设计电路 的端口,如上例 U1 中, PORT MAP 列出的端口信号名称是 A(1), B(1), C(0), C(1)和 S(1), PORT MAP 语句可以写成如下形式: U1: Fulladder PORT MAP (A=>A(1), B=>B(1), Ci=>C(1), Co=>C(2), S=>S(1)); 这时,各个端口的意义取决于端口的名称,与位置无关。 元件例化语句与 BLOCK 语句一样属于并行语句, 但是元件和元件例化在设计项目中 是分层次的, 每个元件就是一个独立的设计实体, 这样就可以把一个复杂的设计实体划分 成多个简单的元件来设计。 2.4.3 生成 (GENERATE)语句 生成语句是一种循环语句, 具有复制电路的功能。 当设计一个由多个相同单元模块组 成的电路时,就可以用生成语句来描述。生成语句有 FOR-GENERATE 和 IF-GENERATE 两种形式,分别说明如下: ( 1) FOR-GENERATE 语句格式为: 标号: FOR 循环变量 IN 取值范围 GENERATE 并行语句 END GENERATE [标号 ]; FOR-GENERATE 语句与 FOR-LOOP 语句不同, FOR-GENERATE 中所列的语句是并 行信号赋值语句、 元件例化、 块语句和子程序等并行语句。 循环变量是一个局部变量, 其 取值范围可以选择递增和递减两种形式 ,如 0 TO 5 和 3 DOWNTO 1 等。生成语句所复 制的单元模块是按照一定的顺序排列的, 而单元模块之间的关系却是并行的。 所以生成语 句属于并行语句。 例 2-4-2 用 FOR-GENERATE 语句实现四位全加器的程序设计如下: LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY adder4 IS PORT( A, B : IN STD_LOGIC_VECTOR(3 DOWNTO 0); Ci : IN STD_LOGIC; S : OUT STD_LOGIC_VECTOR(3 DOWNTO 0); C : BUFFER STD_LOGIC_VECTOR(4 DOWNTO 0) ); END adder4; ARCHITECTURE a OF adder4 IS COMPONENT fulladder PORT ( A, B, Ci : IN STD_LOGIC; Co, S : OUT STD_LOGIC); END COMPONENT; BEGIN C(0) <= Ci; 153 gen1: FOR I IN 0 TO 3 GENERATE addx: fulladder PORT MAP (A(I),B(I),C(I),C(I+1),S(I)); --产生四位串行全加器 END GENERATE ; END a; 例 2-4-3 用生 成语句描述用四个 D触发器组成一个四位移位寄存器, 电路 图如图 2-4-2 所示。 D 触发器用元件定义和元件例化语句调用。元件名称是 dff1,用 VHDL 描述 的 D 触发器的程序参见例 2-3-4,程序的文件名是 dff1.VHD。 D Q CLK 图 2-4-2 四位移位寄存器 D Q CLK D Q CLK D Q CLK DIN CLK Q0 Q1 Q2 Q3 四位移位寄存器的文件名为 shift.VHD,内容如下: LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY shift IS PORT( DIN, CLK : IN STD_LOGIC; DOUT : OUT STD_LOGIC; Q : BUFFER STD_LOGIC_VECTOR(3 DOWNTO 0) ); END shift; ARCHITECTURE B OF shift IS COMPONENT dff1 PORT ( D, CLK : IN STD_LOGIC; Q : OUT STD_LOGIC ); END COMPONENT; SIGNAL D : STD_LOGIC_VECTOR(0 TO 4); BEGIN D( 0) <= DIN; gen2: FOR I IN 0 TO 3 GENERATE fx: dff1 PORT MAP (D(I),CLK,D(I+1)); END GENERATE ; Q(0) <= D(1); 154 Q(1) <= D(2); Q(2) <= D(3); Q(3) <= D(4); DOUT<=D(4); END B; 从上例可以看出, FOR-GENERATE 用 来处理规则的单元模块, 对于不规则的单元 模块用 IF-GENERATE 格式。 ( 2) IF-GENERATE 语句带有条件选择项,其格式为: 标号: IF 条件 GENERATE 并行语句 END GENERATE [标号 ]; 例 2-4-4 用 IF-GENERATE 语句描述的四位移位寄存器,其结构体部分程序如下: ARCHITECTURE C OF shift IS COMPONENT DFF1 PORT ( DIN, CLK : IN STD_LOGIC; Q : OUT STD_LOGIC ); END COMPONENT; SIGNAL D : STD_LOGIC_VECTOR(0 TO 4); BEGIN gen3: FOR I IN 0 TO 3 GENERATE IF I = 0 GENERATE fx: dff1 PORT MAP (DIN,CLK,D(I+1)); END GENERATE ; IF I /= 0 GENERATE fx: dff1 PORT MAP (D(I),CLK, D(I+1)); END GENERATE ; END GENERATE ; Q(0) <= D(1); Q(1) <= D(2); Q(2) <= D(3); Q(3) <= D(4); END B; 2.4.4 子程序( SUBPROGRAM) 子程序是一个 VHDL 程序模块, 它是由一组顺序语句组成的。 主程序调用子程序, 子 程序将处理结果返回给主程序, 其含义与其它高级计算机语言中的子程序相同。 子程序可 以在程序包、 结构体和进程中定义, 子程序必须在被定义后才能被调用, 主程序和子程序 之间通过端口参数列表位置关联方式进行数据传送, 子程序可以被多次调用完成重复性的 任务。在 VHDL 中的子程序有两种类型:过程( Procedure)和 函数( Function) ,他们 在 被调用后返回数据的方式不同。 1. 过程语句 过程语句的格式为: 155 PROCEDURE 过程名称 参数列表 --过程首 PROCEDURE 过程名称 参数列表 IS --过程体 说明部分 BEGIN 顺序语句 END 过程名称; 调用过程语句的格式为: 过程名称 参数列表; 在 VHDL 中 ,过程定义由两部分组成,即过程首和过程体,在进程或结构体中过程 首可以省略, 过程体放在结构体的说明部分。 而在程序包中必须定义过程首, 把过程首放 在程序包的包首部分,而过程体放在包体部分。 在 PROCEDURE 结构中,参数列表中的参数可以是输入也可以是输出,在参数表中 可以对常数、变量和信号三类数据对象作出说明,用 IN、 OUT 和 INOUT 定义这些参数 的端口模式, 在没有特别的指定时, 端 口模式 IN 的 参数默认常数, 端口模式 OUT 和 INOUT 看作变量。 在子程序调用时, IN 和 INOUT 的参 数传送数据至子程序, 子程序调用结束返 回时, OUT 和 INOUT 的参数返回数据。 例 2-4-5 用一个过程语句来实现数据求和运算程序。 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY PADD IS PORT( A, B, C : IN STD_LOGIC_VECTOR(3 DOWNTO 0); CLK, SET : IN STD_LOGIC; D : OUT STD_LOGIC_VECTOR(3 DOWNTO 0) ); END PADD; ARCHITECTURE a OF PADD IS --定义过程体 PROCEDURE ADD1 (DATAA, DATAB, DATAC : IN STD_LOGIC_VECTOR; DATAOUT : OUT STD_LOGIC_VECTOR ) IS BEGIN DATAOUT := DATAA+DATAB+DATAC; END ADD1; BEGIN PROCESS (CLK) VARIABLE TMP :STD_LOGIC_VECTOR (3 DOWNTO 0); BEGIN IF (CLK'EVENT AND CLK ='1') THEN IF(SET = '1') THEN TMP := "0000"; ELSE ADD1 (A, B, C, TMP); --过程调用 END IF; 156 END IF; D <= TMP; END PROCESS; END a; 2. 函数语句 函数语句分为两个部分,函数首和函数体。 ( 1)函数首的格式为: FUNCTION 函数名称(参数列表) RETURN 数据类型名; ( 2)函数体的格式为: FUNCTION 函数名称(参数列表) RETURN 数据类型名 IS 说明部分 BEGIN 顺序语句 RETURN 返回变量 END 函数名称; 在进程或结构体中函数首可以省略, 而在程序包中必须定义函数首, 放在程序包的包 首部分,而函数体放在包体部分。 函数语句中参数列表列出的参数都是输入参数, 在参数表中可以对常数、 变量和信号 三类数据 对 象作出说 明 ,默认的 端 口模式是 IN,在函数 语 句中如果 参 数没有定 义 数 据 类 型就看作常数处理。调用函数语句的返回数据和返回数据的数据类型分别是由 RETURN 后的返回变量和返回变量的数据类型决定的。 调用函数语句的格式为: Y<= 函数名称(参数列表) ; 例 2-4-6 用 FUNCTION 语 句描述在两个数中找出最大值, 并 用函数调用方式求出最大值。 这段程序放在一个程序包( PACKAGE)中。 ( 1)在程序 包名称为 BF1 的程序包 中定义函数名称为 MAX1 的函数, 程序包文件名为 bf1.VHD,放在当前的 WORK 库中。 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; PACKAGE BF1 IS --定义程序包的包头, BF1 是程序包名称 FUNCTION MAX1(A : STD_LOGIC_VECTOR; --定义函数首,函数名称是 MAX B : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; --定义函数返回值的类型 END BF1; PACKAGE BODY BF1 IS --定义程序包体 FUNCTION MAX1 (A : STD_LOGIC_VECTOR; --定义函数体 B : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS VARIABLE TMP : STD_LOGIC_VECTOR (A’RANGE); --属性 A’RANGE 表示 A 的数组范围 , 该例中 A’RANGE=3 DOWNTO 0 --MAX +PLUS2 不支持该种属性表示 BEGIN 157 IF (A>B) THEN TMP :=A; ELSE TMP :=B; END IF; RETURN TMP; --TMP 是函数返回变量 END MAX1; END BF1; ( 2)调用函数 MAX1 的程序,文件名是 SMAX.VHD LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; LIBRARY WORK; --用户当前工作库,可以不列出 USE WORK.BF1.ALL; ENTITY SMAX IS PORT( D1, DA, DB : IN STD_LOGIC_VECTOR(3 DOWNTO 0); CLK, SET : IN STD_LOGIC; Do : OUT STD_LOGIC_VECTOR(3 DOWNTO 0) ); END SMAX; ARCHITECTURE a OF SMAX IS BEGIN PROCESS (CLK) BEGIN IF (CLK’EVENT AND CLK =‘1’) THEN IF(SET = ‘1’) THEN --SET=1,同步置数 DO <= D1; ELSE DO <= MAX1(DA, DB); --调用 MAX1 函数 END IF; END IF; END PROCESS; END a; 用 MAX +PLUS2 仿真的结果如图 2-4-3 所示。 图 2-4-3 求两个数中最大值的程序仿真波形图 158 2.4.5 程序包的设计 在 VHDL 中 ,为了使已定义的数据类型、子程序和元件等被其他设计程序所利用, 用户可以自己设计一个程序包, 将它们收集在程序包中。 程序包分为两个部分, 即包首和 包体两个部分,结构为: ( 1)包首部分 PACKAGE 程序包名称 IS 包首说明 END 程序包名称; ( 2)包体部分 PACKAGE BODY 程序包名称 IS 包体说明语句 END 程序包名称; 包首说明部分定义数据类型、 元件和子程序等。 包体说明语句部分具体描述元件和子 程序的内容。 在程序包结构中, 程序包体不是必需的, 因为在程序包首也可以具体定义元 件和子程序的内容。 在例 2-4-6 中,定义一个 MAX 函数在程序包文件 bf1.VHD 中, 在 smax.VHD 程序中 用函数调用的方式直接调用包中的内容。 2.5 VHDL设计实例 前面详细介绍了 VHDL 的基本语法、基本语句和 VHDL 程 序的组成结构。本节重点 介绍用 VHDL 设计常用的组合逻辑电路、时序逻辑电路和有限状态机。 2.5.1 常见的组合逻辑电路设计 根据逻辑功能的不同特点, 可以把数字电路分成组合逻辑电路和时序逻辑电路。 常用 的组合逻辑电路有 3 线 -8 线译码器、 8 线 -3 线编码 器、 七段显示译码器、 数据选择器、 数 据分配器、加法器和数值比较器等。 1. 3 线 -8 线译码器 译码器的功能是将输入的二进制代码翻译成对应的高低电平信号。 3 线 -8 线译码器 输 入 A2A1A0 三位二进制代码,输出 Y7~ Y0 八个输出信号, EN 是控制输入端,当 EN=1 时, 译码器工作, 当 EN=0 时, 译码 器输出全部是高电平。 3 线 -8 线译码 器的示意图如 图 2-5-1 所示,其真值表如见表 1-2-1 所列。 用 CASE-WHEN 语句、 WHEN-ELSE 语句和 WITH-SLECT 语句可以实现查表功能, 应注意的是 CASE-WHEN 语句是顺序语句,使用时应放在 PROCESS 语句中。 Y0 Y1 Y2 Y3 Y4 Y5 Y6 Y7 EN A2 A1 A0 图 2-5-1 3 线 -8 线译码器 例 2-5-1 3 线 -8 线译码器的 VHDL 程序名是 DECODER.VHD,程序描述如下。 LIBRARY IEEE ; USE IEEE.STD_LOGIC_1164.ALL ; 159 ENTITY DECODER IS PORT ( A : IN STD_LOGIC_VECTOR( 2 DOWNTO 0) ; EN : IN STD_LOGIC ; Y : OUT STD_LOGIC_VECTOR( 7 DOWNTO 0) ) ; END DECODER ; ARCHITECTURE A OF DECODER IS SIGNAL SEL : STD_LOGIC_VECTOR( 3 DOWNTO 0) ; BEGIN SEL(0) <= EN ; SEL(1) <= A(0) ; SEL(2) <= A(1) ; SEL(3) <= A(2) ; WITH SEL SELECT Y<= "00000001" WHEN "0001", "00000010" WHEN "0011", "00000100" WHEN "0101", "00001000" WHEN "0111", "00010000" WHEN "1001", "00100000" WHEN "1011", "01000000" WHEN "1101", "10000000" WHEN "1111", "00000000" WHEN OTHERS ; END A ; 2. 8 线 -3 线编码器 编码器的功能是将输入的一组高低电平信号翻译成对应的二进制代码。 8 线 -3 线编 码 器输入 I7~ I0 八路信号, 输出是 Y2Y1Y0 三位二进制代码, S 是控制输入端, 当 S=1 时, 编码器工作,当 S=0 时 ,编码器输出 “000”。 8线 -3 线编码 器的示意图如图 2-5-2 所示 , 其真值表如表 2-5-2 所列。 表 2-5-1 8 线 -3 线编码器真值表 Y2 Y1 Y0 S I0 I1 I2 I3 I4 I5 I6 I7 图 2-5-2 8 线 -3 线编码器 输入信号 输出信号 S I0 I1 I2 I3 I4 I5 I6 I7 Y2 Y1Y0 10000000 000 01000000 001 1 00100000 010 160 00010000 011 00001000 100 00000100 101 00000010 110 00000001 111 0 00000000 000 例 2-5-2 8 线 -3 线编码器的 VHDL 程序名是 CODER.VHD,程序描述如下。 LIBRARY IEEE ; USE IEEE.STD_LOGIC_1164.ALL ; ENTITY CODER IS PORT ( I : IN STD_LOGIC_VECTOR(7 DOWNTO 0) ; S : IN STD_LOGIC ; Y : OUT STD_LOGIC_VECTOR( 2 DOWNTO 0) ) ; END CODER ; ARCHITECTURE B OF CODER IS SIGNAL SEL : STD_LOGIC_VECTOR( 8 DOWNTO 0) ; BEGIN SEL <= S & I ; --S 和 I 合成 9 位标准逻辑位向量 WITH SEL SELECT Y<= "000" WHEN "100000001" , "001" WHEN "100000010" , "010" WHEN "100000100" , "011" WHEN "100001000" , "100" WHEN "100010000" , "101" WHEN "100100000" , "110" WHEN "101000000" , "111" WHEN "110000000" , "000" WHEN OTHERS ; END B ; 3. BCD-七段显示译码器 BCD-七段显示译码器的功能是将用四位二进制代码所表示的十进制数翻译成对应 的 七段显示码。 译码器输入信号是 D3D2D1D0 四位 BCD 码, 输出 是 ABCDEFG 七个高低电 平信号, 用输出的信号去驱动七段显示器中的七只发光二极管。 七段显示器有共阴极和共 阳极两种类型,选用不同的七段显示器对应的七段显示译码器的真值表不同,表 1-4-1 列 出的真值表 中的七段显 示码是共阴 极型的。 BCD-七段显示 译码器的示 意图如图 2-5-3 所 示。 A B C D E F G D3 D2 D1 D0 图 2-5-3 BCD-七段显示译码器 161 例 2-5-3 BCD-七段显示译码器的 VHDL 程序名是 DECODER47.VHD, ABCDEFG 七个 输 出信号用数组 SEG(0)~ SEG(6)表示,程序描述如下。 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY DECODER47 IS PORT( D : IN STD_LOGIC_VECTOR(3 DOWNTO 0 ); SEG : OUT STD_LOGIC_VECTOR(6 DOWNTO 0) ); END DECODER47; ARCHITECTURE STR OF DECODER47 IS BEGIN SEG<="0111111" WHEN D=0 ELSE "0000110" WHEN D=1 ELSE "1011011" WHEN D=2 ELSE "1001111" WHEN D=3 ELSE "1100110" WHEN D=4 ELSE "1101101" WHEN D=5 ELSE "1111101" WHEN D=6 ELSE "0000111" WHEN D=7 ELSE "1111111" WHEN D=8 ELSE "1101111" WHEN D=9 ELSE "0000000"; END STR; 4. 四选一数据选择器 四选一数据选择器的工作原理是从四路输入数据中选择一路数据输出。 输入信号是四 路数据 D0、 D1、 D2、 D3 和两个地址输入端 A1、 A0, 输出一 路数据 Y, 数据可以是一位 二进制数也可以是多位二进制数。四选一数据选择器的逻辑表达式是: += )AA(DY 010 +)AA(D 011 +)AA(D 012 )AA(D 013 四选一数据选择器的真值表如表 2-5-2 所列。 表 2-5-2 四选一数据选择器的真值表 地址输入端 数据输出端 A1 A0 Y 0 0 D0 0 1 D1 1 0 D2 1 1 D3 例 2-5-4 四选一数据选择器的 VHDL 程序名是 MUX44.VHD,程序描述如下。 LIBRARY IEEE ; 162 USE IEEE.STD_LOGIC_1164.ALL ; USE IEEE.STD_LOGIC_ARITH.ALL ; USE IEEE.STD_LOGIC_UNSIGNED.ALL ; ENTITY MUX44 IS PORT ( D3, D2, D1, D0 : IN STD_LOGIC_VECTOR( 3 DOWNTO 0) ; A : IN STD_LOGIC_VECTOR( 1 DOWNTO 0) ; Y : OUT STD_LOGIC_VECTOR( 3 DOWNTO 0 ) ) ; END MUX44 ; ARCHITECTURE A OF MUX44 IS BEGIN Y<= D0 WHEN A="00" ELSE D1 WHEN A="01" ELSE D2 WHEN A="10" ELSE D3 WHEN A="11" ELSE "0000" ; END A ; 用 MAX +PLUS2 仿真的结果如图 2-5-4 所示。 图 2-5-4 四选一数据选择器的仿真波形 2. 数据分配器 数据分配器的功能是将一路输入数据从多个输出通道中选择一个通道输出。 输入信号 是一路数据 D 和两个地址输入端 A1、 A0, 输 出 信号是四路数据 Y0, Y1, Y2, Y3。数 据 可以是一位二进制数也可以是多位二进制数。 数据分配器的真值表如表 2-5-3 所列。 表 2-5-3 数据分配器的真值表 地址输入端 数据输出端 A1 A0 Y0 Y1 Y2 Y3 0 0 D 0 0 0 0 1 0 D 0 0 1 0 0 0 D 0 1 1 0 0 0 D 例 2-5-5 数据分配器的 VHDL 程序名是 DEMUX.VHD,程序描述如下。 LIBRARY IEEE ; 163 USE IEEE.STD_LOGIC_1164.ALL ; USE IEEE.STD_LOGIC_ARITH.ALL ; USE IEEE.STD_LOGIC_UNSIGNED.ALL ; ENTITY DEMUX IS PORT ( D : IN STD_LOGIC_VECTOR( 3 DOWNTO 0) ; S : IN STD_LOGIC_VECTOR( 1 DOWNTO 0) ; Y0, Y1, Y2, Y3 : OUT STD_LOGIC_VECTOR( 3 DOWNTO 0 ) ) ; END DEMUX ; ARCHITECTURE STR OF DEMUX IS BEGIN PROCESS (D, S) BEGIN Y0 <= "0000" ; Y1 <= "0000" ; Y2 <= "0000" ; Y3 <= "0000" ; CASE S IS WHEN "00" => Y0 <= D ; WHEN "01" => Y1 <= D ; WHEN "10" => Y2 <= D ; WHEN OTHERS => Y3 <= D ; END CASE ; END PROCESS ; END STR ; 3. 比较器 比较器的功能是比较两个二进制数 A 和 B 的数 值大小。 A、 B 可以是一 位二进制数, 也可以是多位二进制数。 比较的结 果有三种可能, 即 A>B, A=B, A<B。 比较器的 输入信 号是 A 和 B 两路数据, A 和 B 的位 宽相同, 三个输出信号 YG, YE, YL 分别表示 A>B, A=B, A<B。 比较器的电路图如图 2-5-5 所示。 A YG YE B YL 图 2-5-5 比较器的电路图 例 2-5-6 比较器的 VHDL 程序名是 COMP.VHD,程序描述如下。 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY COMP IS PORT ( A, B : IN STD_LOGIC_VECTOR(3 DOWNTO 0); YG,YE,YL : OUT STD_LOGIC 164 ); END COMP; ARCHITECTURE STR OF COMP IS BEGIN PROCESS (A,B) BEGIN IF A > B THEN YG <= '1'; YE <= '0'; YL <= '0'; ELSIF A = B THEN YG <= '0'; YE <= '1'; YL <= '0'; ELSE YG <= '0'; YE <= '0'; YL <= '1'; END IF; END PROCESS; END STR ; 4. 三态输出门 三态输出门有三种可能的输出状态: 高电平、 低 电平和高阻态。 在 VHDL 语言预定义 STD_LOGIC 数据类型中,用大写字母 “Z”表示高阻态。三态输出门简称三态门,主要用 于总线结构中。 三态门的电路图如图 2-5-6 所示 , 有一个输入端 A, 一个输出端 Y 和一个 控制端 EN。当 EN= ‘1’时, Y=A;当 EN= ‘0’时, Y= ‘Z’。 例 2-5-7 三态门的 VHDL 程序名称是 TSG.VHD,程序描述如下。 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY TSG IS PORT ( A, EN : IN STD_LOGIC; Y : OUT STD_LOGIC ); END TSG; ARCHITECTURE STR OF TSG IS BEGIN Y <= ‘Z’ WHEN EN=‘1’ ELSE A ; END STR ; 也可以用 IF-ELSE 语句来描述三态门,结构体程序如下: ARCHITECTURE STR OF TSG IS BEGIN PROCESS (A,EN ) BEGIN IF EN= ‘1’THEN Y <= A ; ELSE Y <= ‘Z’; END IF; END PROCESS; A Y EN 图 2-5-6 三态门 165 END STR ; 2.5.2 常见的时序逻辑电路设计 在时序逻辑电路中, 任意时刻的输出信号不仅取决于当时的输入信号, 而且还取决于 电路的原来状态。组成时序电路的基本单元是各种触发器,如 D 触发 器、 T 触发器、 RS 触发器和 JK 触发器等。触发器具有存储记忆功能,能够保存电路的初始状态数据和当前 状态数据。 时序逻辑电路分为同步时序电路和异步时序电路。 在同步时序电路中, 所有触发器的 时钟信号共用一个时钟信号, 触发器的状态变化是同时发生的。 如寄存器、 移位寄存器和 同步计数器等。 而在异步时序逻辑电路中, 各个触发器的时钟信号不是共用的, 因此各个 触发器的状态变化不是同时发生的。异步计数器是一种常用的异步时序逻辑电路。 1.带异步复位端的 D 触发器 带异步复位端的 D 触发器的电路符号如图 2-5-7 所示。 时钟信号 CP 上升 沿触发, Rd 是异步复位端, 低电平有效, D 是数据输入端, Q 是数据输出端。 表 2-5-4 是 D 触 发器的 特性表。 表 2-5-4 D 触发器的特性表 输入端 输出端 Rd CP 1n Q + 0 × 0 1 上升沿 D 例 2-5-8 用 VHDL 语言描述的 D 触发器的程序如下,程序名称是 DFF1.VHD。 LIBRARY IEEE; D Q CP Rd 图 2-5-7 带异步复位端的 D 触发器 USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY dff1 IS PORT( CP, D, Rd : IN STD_LOGIC; Q : OUT STD_LOGIC ); END dff1; ARCHITECTURE a OF dff1 IS BEGIN PROCESS (CP, Rd) BEGIN IF (Rd=‘0’) THEN Q <= ‘0’; ELSIF (CP’ EVENT AND CP=‘1’) THEN Q <= D; END IF; END PROCESS; END a; 2. T 触发器 T触发器的电路符号如图 2-5-8 所示 。 时钟信号 CP 上升沿触发, T 是控制输入端, Q 166 是数据输出端。表 2-5-5 是 T 触发器的特性表。 表 2-5-5 T 触发器的特性表 输入端 输出端 CP T 1n Q + × 0 保持 上升沿 1 翻转 例 2-5-9 用 VHDL 语言描述的 T 触发器的程序如下,程序名称是 TFF1.VHD。 LIBRARY IEEE; T Q CP 图 2-5-8 T 触发器 USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY TFF1 is PORT( CP, T : IN STD_LOGIC; Q : BUFFER STD_LOGIC ); END TFF1; ARCHITECTURE a OF TFF1 IS BEGIN PROCESS(CP) BEGIN IF (CP’ EVENT AND CP=‘1’) THEN IF (T=‘1’) THEN Q <= NOT Q; END IF; END IF; END PROCESS; END a; 3. JK 触发器 带有异步复位和置位功能的 JK 触发器的电路符号如图 2-5-9 所示。 JK 触发器的输入 端有异步置位输入端 SD, 低电平有效, 异步复位输入端 Rd, 低电平有效, 输入时钟信号 CP,上升沿触发, Q和 QB 是触 发器的两个信号输出端, Q 和 QB 状态相反。表 2-5-6 是 JK 触发器的特性表。 表 2-5-6 JK 触发器的特性表 输入端 输出端 CP SD Rd J K Q QB 0 1 1 0 × 1 0 × × 0 1 0 0 保 持 0 1 0 1 1 0 1 0 上升沿 1 1 1 1 翻 转 例 2-5-10 用 VHDL 语言 描述的 JK 触 发器的程序如下,程序名称是 JKFF1.VHD。仿真结 果如图 2-5-10 所示。 LIBRARY IEEE; 167 SD Q J CP QB USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY JKFF1 IS PORT( CP, SD, Rd, J, K : IN STD_LOGIC; Q , QB : OUT STD_LOGIC ); END JKFF1; ARCHITECTURE a OF JKFF1 IS SIGNAL QN, QBN : STD_LOGIC; BEGIN PROCESS(CP, SD, Rd, J, K) BEGIN IF (SD=‘0’) AND (Rd=‘1’) THEN QN <= ‘1’; QBN <= ‘0’; ELSIF (SD=‘1’) AND (Rd=‘0’) THEN QN <= ‘0’; QBN <= ‘1’; ELSIF (CP’ EVENT AND CP=‘1’) THEN IF (J=‘0’) AND (K=‘1’) THEN QN <= ‘0’; QBN <= ‘1’; ELSIF (J=‘1’) AND (K=‘0’) THEN QN <= ‘1’; QBN <= ‘0’; ELSIF (J=‘1’) AND (K=‘1’) THEN QN <= NOT QN; QBN <=NOT QBN; END IF; END IF; END PROCESS; Q <= QN; QB<= QBN; END a; 用 MAX +PLUS2 仿真的结果如图 2-5-10 所示。 168 图 2-5-10 JK 触发器的仿真波形 4. 移位寄存器 如图 2-5-11 所示的电路是带有串行输入、 串行输出、 并行输入和并行输出的移位寄存 器。 其中 DI 是串行数据输入端, DO 是串行数据输出端, CP 是时钟输入端。 Rd 是异 步清 零输入端, 低电平有效, LD 是同步置数控制端, 低电平有效。 D0、 D1、 D2、 D3 是并 行 数据输入端, Q0、 Q1、 Q2、 Q3 是并行 数据输出端。 当 Rd=0 时 电路的输出端输出低电平 , 当 Rd=1, LD=0 时, 电路 工作在预置数状态。 当 Rd=1, LD=1 时, 电路工 作在移位状态。 移位寄存器的功能如表 2-5-7 所列。 表 2-5-7 移位寄存器的功能表 控制端 CP Rd LD 工作状态 × 0 × 异步清零 1 0 并行输入 上升沿 1 1 串行右移 例 2-5-11 用 VHDL 语言描述的移位寄存器的程序如下,程序名称是 SHIFT1.VHD。 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY SHIFT1 is PORT( CP, Rd, LD, DI : IN STD_LOGIC; D : IN STD_LOGIC_VECTOR(0 TO 3); DO : OUT STD_LOGIC; Q : OUT STD_LOGIC_VECTOR(0 TO 3 ) ); END SHIFT1; DI Q0 Q1 Q2 Q3 Rd Do LD CP D0 D1 D2 D3 图 2-5-11 移位寄存器 ARCHITECTURE a OF SHIFT1 IS SIGNAL QN : STD_LOGIC_VECTOR(0 TO3 ); BEGIN PROCESS(CP, Rd, LD) BEGIN IF Rd=‘0’ THEN QN <= “0000”; ELSIF (CP’ EVENT AND CP=‘1’) THEN IF LD=‘0’ THEN QN <= D; ELSE 169 QN(0) <= DI; FOR I IN 0 TO 2 LOOP QN(I+1) <= QN(I); END LOOP; END IF; END IF; END PROCESS; DO <= QN(3); Q <= QN; END A; 5. 同步计数器 同步计数器分为加法计数器和减法计数器, 也可以按照计数容量分类, 分为十进制计 数器、十六进制计数器和六十进制计数器等。同步十六进制加法计数器参见例 2-1-2。 ( 1)带预置数的同步加法计数器 带预置数的同步加法计数器的电路符号如图 2-5-12 所示。 CP 是时钟输入端,上升沿 有效。 Rd 是 异步清零控制端,低电平有效, LD 是同步置数控制端,低电平有效。 D0、 D1、 D2、 D3 是预置数数据输入端, Q0、 Q1、 Q2、 Q3 是计数输出端。 EN 是计数器状态 控制端, 当 EN=1 时, 计 数器工作在计数状态, 当 EN=0 时, 计数器的状态保持不变。 Co 是进位输出端, 当计数器输出 0000~ 1110 时, Co=0, 只有当 计数器输出 1111 时, Co=1。 表 2-5-8 是带预置数同步加法计数器功能表。 表 2-5-8 带预置数的同步加法计数器功能表 控制端 CP Rd LD EN 工作状态 × 0 × × 异步清零 1 0 × 同步置数 上升沿 1 1 1 计数 例 2-5-12 用 VHDL 语言 描述的同步十六进制加法计数器的程序名称是 CNT16.VHD, 程序 描述如下。 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY CNT16 IS PORT( CP, EN, Rd, LD :IN STD_LOGIC; D :IN STD_LOGIC_VECTOR(3 DOWNTO 0); Co :OUT STD_LOGIC; Q :OUT STD_LOGIC_VECTOR(3 DOWNTO 0) ); END CNT16; ARCHITECTURE STR OF CNT16 IS SIGNAL QN : STD_LOGIC_VECTOR(3 DOWNTO 0 ) ; BEGIN 170 Co<= ‘1’ WHEN (QN = “1111” AND EN=‘1’) ELSE ‘0’; PROCES (CP, RD) EN Q0 Q1 Q2 Q3 Rd Co LD CP D0 D1 D2 D3 图 2-5-12 带预置数的同步加法计数器 BEGIN IF (Rd =‘0’) THEN QN<= ”0000”; ELSIF (CP’ EVENT AND CP=‘1’) THEN IF (LD=‘0’) THEN QN <= D; ELSIF (EN=‘1’) THEN QN <= QN+1; END IF; END IF; END PROCESS; Q <= QN; END STR ; ( 2) 同步可逆计数器 同步可逆计数器可以用加法计数方式也可以用减法计数方式。 如图 2-5-13 所示的计 数 器是用一个控制信号 UD 来控制计数器的计数方式的,当 UD=1 时,计数器是加法计数, 当 UD=0 时 , 计数器是减法计数。 Rd 是异步清零 控制端, 低电平有效。 表 2-5-9 是同 步可 逆计数器功能表。 表 2-5-9 同步可逆计数器功能表 例 2-5-13 用 VHDL 语言 描述的同步可逆计数器的程序名称是 UDCNT.VHD,程序描 述如 下。 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY UDCNT IS PORT( CP, UD, Rd : IN STD_LOGIC; Q : OUT STD_LOGIC_VECTOR(3 DOWNTO 0) ); END UDCNT; ARCHITECTURE a OF UDCNT IS SIGNAL QN : STD_LOGIC_VECTOR(3 DOWNTO 0); BEGIN PROCES (CP, Rd) BEGIN 控制端 CP Rd UD 工作状态 × 0 × 异步清零 1 1 加法计数 上升沿 1 0 减法计数 171 Q0 Q1 Q2 Q3 UD Rd CP IF (Rd =‘0’) THEN QN<= ”0000”; ELSIF (CP’EVENT AND CP=‘1’) THEN IF UD =‘1’ THEN QN <= QN + 1; ELSE QN <= QN - 1; END IF; END IF; END PROCESS; Q <= QN ; END a; ( 3)同步六十进制加法计数器 在数字系统中, 常常用 BCD 码来表示十进制数, 即用四位二进制码表示一位十进制 数, 用八位二进制码来表示两位十进制数。 如在时间计数电路中, 用十二进制、 二十四进 制和六十进制计数器。同步六十进制加法计数器的电路符号如图 2-5-14 所示。 同步六十进制加法计数器的电路符号如图 2-5-12 所示。 CP 是时钟输入端,上升沿有 效。 Rd 是异 步清零控制端, 低电平有效。 LD0 和 LD1 是两 个同步置数控制端, 低电平有 效。 D0~ D7 是预置数数据输入端, Q0~ Q7 是计 数输出端。 EN 是计数器状态控制端, 当 EN=1 时, 计数器工作在计数状态, 当 EN=0 时, 计数器的状态保持不变。 Co 是进位 输出 端,当计数器输出 00~ 58 时, Co=0,只有当计数器输出 59 时, Co=1。表 2-5-10 是同 步 六十进制加法计数器功能表。 表 2-5-10 同步六十进制加法计数器功能表 例 2-5-14 用 VHDL 语言 描述的同步六十进制加法计数器的程序名称是 CNT60.VHD, 程序 控制端 CP Rd LD0 LD1 EN 工作状态 × 0 × × × 异步清零 1 0 1 × 1 1 0 × Q3~ Q0= D3~ D0 Q7~ Q4= D7~ D4 上升沿 1 1 1 1 计数 EN Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Rd LD0 Co LD1 CP D0 D1 D2 D3 D4 D5 D6 D7 图 2-5-14 同步六十进制加法计数器 172 如下。 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY CNT60 IS PORT(CP, EN, Rd :IN STD_LOGIC; D :IN STD_LOGIC_VECTOR(7 DOWNTO 0); LD :IN STD_LOGIC_VECTOR(1 DOWNTO 0); Co :OUT STD_LOGIC; Q :OUT STD_LOGIC_VECTOR(7 DOWNTO 0) ); END CNT60; ARCHITECTURE STR OF CNT60 IS SIGNAL QN : STD_LOGIC_VECTOR(7 DOWNTO 0 ) ; BEGIN Co<= '1' WHEN (QN=X"59" AND EN='1') ELSE '0'; --X"59" 是 BCD 码 PROCES (CP, Rd) BEGIN IF (Rd ='0') THEN QN<= X"00"; ELSIF (CP' EVENT AND CP='1') THEN IF (LD(0)='0'AND LD(1)='0') THEN QN <= D; ELSIF (LD(0)='0') THEN QN (3 DOWNTO 0)<= D(3 DOWNTO 0); ELSIF (LD(1)='0') THEN QN (7 DOWNTO 4)<= D(7 DOWNTO 4); ELSIF (EN='1') THEN IF QN(3 DOWNTO 0 )=9 THEN QN(3 DOWNTO 0)<="0000"; IF QN(7 DOWNTO 4)=5 THEN QN(7 DOWNTO 4)<="0000"; ELSE QN(7 DOWNTO 4)<= QN(7 DOWNTO 4)+1; END IF; ELSE QN(3 DOWNTO 0)<= QN( 3 DOWNTO 0)+1; END IF; END IF; -- END ELSIF-CP END IF; -- END IF-Rd END PROCESS; Q <= QN; END STR ; 173 用 MUX+PLUS2 仿真的结果如图 2-5-15 所示。 6. 异步计数器 在同步计数器中, 所有触发器的状态的的变化都是在同一个时钟信号 CP 的作用下同 时发生的。 而在异步计数器中, 各个触发器的时钟信号是各自独立的。 因此, 各个 触发器 的状态变化不是同时发生的。 异步计数器与同步计数器的不同之处就在于时钟信号的提供 方式不同 , 它也可以 组 成各种计 数 器。用 T′触发 器组成的 异 步二进制 加 法计数器 如 图 2-5-16 所示 , T 触发器是用 D 触发器 组成的, Q 和 QB 是 D 触发器的两个输出端, Q 和 QB 状态相反。 CP 是计数脉冲输入端,时钟信号 CP0 与 CP 连接,时钟信号 CP1 与 QB0 连接, Q0 和 Q1 是异步计数器的输出端。 ′ 例 2-5-15 用 VHDL 语言描述的异步计数器的程序名称是 ACNT4.VHD,程序描述如下。 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY ACNT4 IS PORT( CP : IN STD_LOGIC; Q0, Q1 : OUT STD_LOGIC ); END ACNT4; ARCHITECTURE a OF ACNT4 IS SIGNAL QN0, QN1 : STD_LOGIC; SIGNAL QBN0, QBN1 : STD_LOGIC; SIGNAL D0, D1 : STD_LOGIC; 图 2-5-15 同步六十进制加法计数器的仿真波形图 图 2-5-16 异步计数器 Q0 Q1 D0 Q0 QB0 CP0 D1 Q1 QB1 CP1 CP 174 BEGIN Q0 <= QN0; Q1 <= QN1; D0 <= QBN0; D1 <= QBN1; PROCESS (CP,QBN0) BEGIN IF CP'event AND CP='1' THEN QN0 <= D0; QBN0 <= NOT D0; END IF; IF QBN0'event AND QBN0='1' THEN QN1 <= D1; QBN1 <= NOT D1; END IF; END PROCESS; END a; 2.5.3 状态机设计 1. 有限状态机 (Finite State Machine ) 有限状态机 (Finite State Machine ) 是 由状态寄存器和组合逻辑电路构成的,能够根 据控制信号 按照预先设 定的状态进 行状态间转 移, 是协调 相关信号动 作、完成特 定 操 作 的控制中心,属于一种同步时序逻辑电路。状态机分为 Moore 型和 Mealy 型。在 Moore 型状态机中, 状态机的输出信号只是当前状态的函数, 与输入信号无关。 Moore 型 状态机 的内部结构图如图 2-5-17 所示。而在 Mealy 型状态机中,状态机的输出信号是当前状态 和输入信号的函数, Mealy 型状态机的内部结构图如图 2-5-18 所示。 常用的状态机由三个部分组成。 即当前状态寄存器 ( Current State,简 称 CS )、下 一 状 态组合逻辑 ( Next State,简称 NS ) 和输出组合逻辑 ( Output Logic,简称 OL )。 当前状态寄存器的任务是保存当前状态 Current_state。当前 状态的实现是在状态时钟 信号 CP 发生有效变化时,通过对状态寄存器进行赋值,其输入信号是 NS 组合逻辑 电路 的输出信号 ( Next_state) , 其输出将作为输出组合逻辑和 NS 组合逻辑电路的输入。 Rd 是 异步复位端,低电平有效。 NS 组合逻辑电路的作用是根据状态机的输入信号 DIN 和当 前状态 Current_state 确定 下一个状态 Next_state 的取值。 CS 寄存器 NS 组合逻辑 OL 组合逻辑 DIN DOUT CP Rd 图 2-5-17 Moore 状态机的内部结构图 175 CS 寄存器 NS 组合逻辑 OL 组合逻辑 DIN DOUT 输出组合逻辑电路的任务是确定状态机的对外输出信号 DOUT。在 Moore 型状态 机 中, 输出组合逻辑电路的输入信号是状态机的当前状态, 因此, 在 CP 发生有效变化时输 出信号变化。 而在 Mealy 型状态机中, 输出组合逻辑电路的输入信号是状态机的当前状态 和状态机的输入信号 DIN。 因此, 当 CP 发生有 效变化时和输入信号改变时输出信号都可 能改变。 用 VHDL 描述状态机常用的方法有: 三个 PROCESS 语句描述和两个 PROCESS 语句 描述。三个 PROCESS 语句描述在带有反馈的状态机部分讨论。用两个 PROCESS 语句描 述的方法是把 NS 组合逻 辑电路和输出组合逻辑电路在一个 PROCESS 语 句中描述,用另 一个 PROCESS语句描述当前状态寄存器。 用枚举 型数据类型定义一个状态变量数据类型, 状态变量的元素用符号表示。 例如 TYPE STATE_M IS (S0,S1,S2,S3); 定义 STATE_M 是 一 个 新的数 据类 型,名 称是 “STATE_M”。定义 数据 类型的 元素 是 S0,S1,S2,S3。 例 2-5-16 用 VHDL 描 述状态转换图如图 2-5-19 所示的状态机。该状态机属于 MOORE 型, 状态机有四种状态, 分别是 S0, S1, S2, S3。 有一个状态输入信号 DIN 和一个 状态 输出信号 DOUT。程序如下: LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY MOORE1 IS PORT( CP, Rd : IN STD_LOGIC; -- CLOCK DIN : IN STD_LOGIC; -- Input Signal DOUT : OUT STD_LOGIC -- Output Signal ); END MOORE1; ARCHITECTURE a OF MOORE1 IS TYPE STATE_M IS (S0, S1, S2, S3); --State Type Declare SIGNAL State : STATE_M; -- Current State SIGNAL NextState : STATE_M; -- Next State BEGIN REG1 : PROCESS (Rd, CP) BEGIN IF Rd = '0' THEN 0 0S 176 1 1S 1 3S 0 2S 0 1 0 0 1 0 1 1 DIN DOUT CurentState <= S0; ELSIF CP'EVENT AND CP = '1' THEN CurrentState <= NextState; END IF; END PROCESS REG1; COM1 : PROCESS (DIN, CurrentState) BEGIN CASE CurrentState IS WHEN S0 => --STATE S0 IF DIN = '0' THEN --INPUT=0 NextState <= S0; ELSE NextState <= S1; END IF; DOUT <= '0'; WHEN S1 => IF DIN = '0' THEN NextState <= S1; ELSE NextState <= S2; END IF; DOUT <= '1'; WHEN S2 => IF DIN = '0' THEN NextState <= S0; ELSE NextState <= S3; END IF; DOUT <= '0'; WHEN S3 => IF DIN = '1' THEN NextState <= S0; ELSE NextState <= S3; END IF; DOUT <= '1'; END PROCESS COM1; END a; 2. 带有反馈的状态机 图 2-5-20 所 示的电路是一个带有反馈的 Mealy 型状态机,是由一个寄存器和两个 组合逻辑电路组成的。用三个进程语句描述如下: 177 REG F1 F2 A D CP Rd 图 2-5-20 带有反馈的 Mealy 状态机 CD ENTITY Mealy IS PORT( CP : IN STD_LOGIC; A : IN STD_LOGIC; D : OUT STD_LOGIC ); END Mealy; ARCHITECTURE a OF Mealy IS SIGNAL B : STD_LOGIC; SIGNAL C : STD_LOGIC; BEGIN REG1 : PROCESS (CP) BEGIN IF CP'EVENT AND CP = '1' THEN C <= B; END IF; END PROCESS REG1; TRANS1 : PROCESS (A, C) BEGIN B <= F1 (A,C); END PROCESS; OUTPUT1 : PROCESS (A, C) BEGIN D <= F2 (A,C); END PROCESS; END a; 该电路包含组合逻辑电路和时序逻辑电路, 既有输入信号也有输出信号, 具有一般数 字电路的结构特点。 因此对于复杂电路的描述可以参照此例, 即把一个复杂电路分成几个 功能模块,分别用进程语句加以描述。 第三章 Verilog HDL 硬件描述语言 3.1 Verilog HDL 概述 3.1.1 Verilog HDL 的特点 178 Verilog HDL 和 VHDL 一样, 是目前大规模集成电路设计中最具代表性、 使用最广泛 的硬件描述语言之一。 作为硬件描述语言, Verilog HDL 具有如下特点: 1. 能够在不同的抽象层次上, 如系统级、 行为级、 RTL( Register Transfer Level)级 、 门级和开关级,对设计系统进行精确而简练的描述; 2. 能够在每个抽象层次的描述上对设计进行仿真验证, 及时发现可能存在的设计错 误,缩短设计周期,并保证整个设计过程的正确性; 3. 由于代码描述与具体工艺实现无关, 便于设计标准化, 提高设计的可重用性。 如 果有 C 语言 的编程经验, 只需很短的时间内就能学会和掌握 Verilog HDL, 因此, Verilog HDL 可以作为学习 HDL 设计方法的入门和基础。 3.1.2 Verilog HDL 的基本结构 Verilog HDL 描述是由模块( module)构成的,每个模块对应的是硬件电路中的逻辑 实体。 因此, 每个模块都有自己独立的功能或结构, 以及用于与其它模块之间相互通信的 端口。 例如, 一个模块可以代表一个简单的门, 一个计数器, 一个存储器, 甚至是计算 机 系统等。 例 3-1-1 加法器的 verilog 描述 /**************************************************************/ // MODULE: adder // FILE NAME: add.v // VERSION: v1.0 // DATE: May 5 th , 2003 // AUTHOR: Peter // CODE TYPE: RTL // DESCRIPTION: An adder with two inputs (1 bit), one output (2 bits). /**************************************************************/ module adder (in1, in2, sum); input in1,in2; output [1:0] sum; wire in1,in2; reg [1:0] sum; always @ (in1 or in2) begin sum=in1+in2; end endmodule 从这个例子中可以看出,一段完整的代码主要由以下几部分组成: 第一部分是代码的注释部分, 主要用于简要介绍设计的各种基本信息。 从上面的注释 中可以了解到一些基本信息, 如代码中加法器的主要功能、 设计工程师、 完成的日期及版 本。 例 3-1-1 的模块名是 adder, 有两个 输入端口 in1, in2 和一个 输出端口 sum。 其中, 输 入信号是一 位的,其数 据类型声明 为连线型 (wire);输出是两 位的寄存器 类型。这些 信 息 都可以在注释中注明。 这一部分内容为可选项, 建议在设计中采用, 以提高代码的可维护 性。 第二部分是模块定义行,这一行以 module 开头 ,然后是模块名和端口列表,标志着 179 后面的代码是设计的描述部分。 第三部分是端口类型和数据类型的说明部分, 用于端口、 数据类型和参数的定义等等。 第四部分是 描述的主体 部分,对设 计的模块进 行描述,实 现设计要求 。模 块 中 “ always-begin”和“ end” 构成一个执行块, 它一直监测输入信号, 其中任意一个发生变 化时,两个输入的值相加,并将结果赋值给输出信号。 这些定义和描述可以出现在模块中的任何位置, 但是变量、 寄存器、 线网和参数的使 用必须出现在相应的描述说明部分之后。 为了使模块描述清晰和具有良好的可读性, 建议 将所有的说明部分放在设计描述之前。 第五部分是结束行, 就是用关键词 endmodule 表 示模块定义的结束。 模块中除了结束 行以外,所有语句都需要以分号结束。 3.2 Verilog HDL 语言要素 3.2.1 基本语法定义 Verilog HDL 源代码是由大量的基本语法元素构成, 其中包括空白部分 ( White space) 、 注释 ( Comment) 、 运算符 ( Operator) 、 数值 ( Number) 、 字符串 ( String) 、 标识 符 ( Identifier) 和关键字( Keyword) 。 1. 注释 在代码中添加注释行可以提高代码的可读性和可维护性。 Verilog HDL 中注释行的定 义与 C 语言完全一致,分为两类: 第一类是单行注释,以“ //”开始到本行行末结束,不允许续行。 第二类是 多 行注释, 以 “ /*” 开 始,以 “ */” 结 束 。可以跨 越 多行,但 是 中间不允 许 嵌套。 2. 运算符 Verilog HDL 中运算符可以分以下几类,如表 3-2-1 所列。 表 3-2-1 Verilog HDL 的运算符 其中, 单目运算符包括按位取反运算符 (~) 、 逻辑非 (! ) 以及全部缩位运算符 ( &, ~&, |, ~|, ^, ^~/ 或 ~^) ,三 目运算符只有条件运算符一个( ?:) ,其余的均为双目运算符。 由于 Verilog HDL 是在 C 语言基础上开发的, 因此两者的运算符也十分类似, 本节将 主要介绍与 C 语言不同的运算符的功能。 (1) 相等与全等运算符 这四个 运算 符的比 较过 程完全 相同 ,不同 之处 在于不 定态 或高阻 态的 运算。 相等 运 算中, 如果任何一个操作数中存在不定态或高阻态, 将得到一个不定态的结果; 而在全等 运算符分类 所含运算符 算术运算符 +,-, *, /, % 关系运算符 <, >, <=, >= 相等与全等运算符 = =, !=, = = =, != = 逻辑运算符 ! , &&, || 位运算符 ~, &, |, ^, ^~或 ~^ 缩位运算符 &, ~&, |, ~|, ^, ^~/或 ~^ 逻辑移位运算符 <<, >> 位拼接运算符 {} 条件运算符 ?: 180 运算中, 则是将不定态和高阻态看作是逻辑状态的一种, 同样参与比较, 当两个操作数的 相应位都是 X 或 Z 时, 认为全等关系成立, 否则, 运算结果为 0。 相 等 与全等运算符如表 3-2-2 所列。 表 3-2-2 相等与全等运算符 例 3-2-1 相等与全等运算符的比较 // example for logical equality & case equality module equality; initial begin $display (“ ’bx ==’bx is %b”, ’bx==’bx ); $display (“ ’bx===’bx is %b”, ’bx===’bx ); $display (“ ’bz ! =’bx is %b”, ’bz !=’bx ); $display (“ ’bz !==’bx is %b”, ’bz !==’bx ); end endmodule 模块 equality 的执行会产生如下结果: ’bx ==’bx is x ’bx===’bx is 1 ’bz ! =’bx is x ’bz !==’bx is 1 (2) 缩位运算符 缩位运算符是对单个操作数进行与、 或、 非等操作, 与逻辑运算符的区别是最终结果 和操作数的位数无关, 一定是一位的逻辑值。 如: &a 等效于 ( ( a[0]&a[1]) &a[2]) &a[3]。 (3) 移位运算符 移位运算符是完成数据的左移、右移功能,移出的空位用 0 填补。 (4) 位拼接运算符 位拼接运算符是将两个或多个信号的某些位拼接起来。如 {a, b[3:0], w, 3’b101} = {a, b[3], b[2], b[1], b[0], w, 1’b1, 1’b0, 1’b1} (5) 条件运算符 条件运算符是唯一的一个三目运算符。 条件运算符的格式为: <条件表达式 >? <条件为真时的表达式 >: <条件为假时的表达式 > 如对于 2 选 1 的 MUX 可以采用如下描述: output = select ? input1: input2; 即 select= 1 时,输出为 input1,否则为 input2。 与 C 语言一 样, Verilog HDL 也规定了运算符的优先级,如表 3-2-3 所列 。当不同优 先级的运算符出现在同一个表达式时, 需要遵从优先级的顺序, 因此, 可以通过添加括号 的方式,清晰运算次序,提高代码的可读性。 表 3-2-3 运算符的优先级顺序 a===b a 与 b 全等,包括 x,z a!==b a 与 b 不全等,包括 x,z a==b a 与 b 相等,结果可能是不定态 a!=b a 与 b 不相等,结果可能是不定态 181 运算符 优先级 !~ * / % + - << >> < <= > >= == != === !== & !& ^ ~^ | ~| && || ?: 最高 最低 3. 数值 Verilog HDL的数值集合由以下四个基本的值组成: 0—— 代表逻辑0或假状态; 1——代表逻辑1或真状态; X(或x)——代表逻辑不定态; Z(或z)——代表高阻态。 因此, 前面介绍条件运算符时, 对于 MUX 描述语句的解释是: select= 1 时, 输出为 input1, 否则 为 input2。 而 不能够认为, select= 1 时, 输出为 input1, selece=0 时 输出为 input2。 常数按照其数值类型可以划分为整数和实数两种。 Verilog HDL的整数 可以是十进制、 十六进制、八进制或二进制。 整数定义的格式为: <位宽>’<基数><数值> 位宽: 描述常量所含二进制数的位数, 用十进制整数表示, 是可选项, 如果没有这 一 项,可以从常量的值推断出。 基数:可选 项,可以是 b(B), o(O) ,d(D), h(H)分别表示二进制、 八进制、十 进制和十 六 进制。基数缺省默认为十进制数。 数值:是由基数所决定的表示常量真实值的一串 ASCII 码。如果基数定义为 b 或 B, 数值可以是 0, 1, x(X), z(Z)。对于基数是 d 或 D 的情况,数值符可以是从 0 到 9 的任何十 进制数,但不可以是 X 或 Z。举例如下: 15 (十进制15) 'h15 (十进制21,十六进制15) 5'b10011 (十进制19,二进制10011) 12'h01F (十进制31,十六进制01F) 'b01x (无十进制值,二进制01x) 需要注意如下内容: (1)数值常量中的下划线“_”是为了增加可读性,可以忽略,如“ 8'b1100_0001”是8 位二进制数。 (2)数值常量中的“?”表示高阻状态,如“2'B1?”表示2位的二进制数其中的一位是 高阻状态。 Verilog HDL中实数用双精度浮点型数据来描述。 实数既可以用小数 (如12.79) 也 可 以用科学计数法的方式(如23E7,表示23乘以10的7次方)来表达。带小数点的实数在小 182 数点两侧都必须至少有一位数字。例如: 1.2 0.5 128.78329 1.7e8(指数符号可以是e或E) 123.3232_234_32(下划线忽略) 下面的几个例子是无效的格式: .25 3. 3.E3 .8e-1 实数可以根据四舍五入的原则转化为整数, 将实数赋值给一个整数时, 这种转化会自 行发生。例如,在转化成整数时,实数 25.5 和 25.8 都变成 26,而 25.2 则变成 25。 4. 字符串 字符串指同一行内写在双引号之间的字符序列串, 在表达式和赋值语句中字符串用作 操作数, 而且要转换成无符号整型常量, 用若干个 8 位二 进制 ASCII 码的形式表示, 其中 每 8 位二进制 ASCII 码代表一个字符。例如,字符串“ ab”等价于 16’h5758。 在 Verilog HDL 中引入字符串的主要目的是配合仿真工具, 显示一些按照指定格式输 出的相关信息。 因此, 需 要一些特殊字符配合, 输出特定格式, 特殊字符如表 3-2-4 所列。 表 3-2-4 特殊字符 特殊字符 含 义 \n 换行 \t Tab 键 \\ 反斜杠 \ \” 引号” \ddd 由三位八进制数表示的 ASIC 值 %% % 例 3-2-2 字符串 module string_test; reg [8*14:1] stringvar; initial begin stringvar = "Hello world"; $display("%s is stored as %h", stringvar,stringvar); stringvar = {stringvar,"!!!"}; $display("%s is stored as %h", stringvar,stringvar); end endmodule 183 输出结果是: Hello world is stored as 00000048656c6c6f20776f726c64 Hello world!!! is stored as 48656c6c6f20776f726c64212121 在 Verilog HDL 中,字符串赋值过程中,如果字符串的位数超出字符串变量的位数, 截掉字符串的高位部分, 低位对齐; 反之, 如果字符串的位数少于字符串变量的位数, 高 位部分用 0 补齐。例 3-2-2 中, stringvar 的位宽是 8*14 位, 即 112 位,而“ Hello world” 是 8*11 位, 所以, 输出结果的第一行出现三个空格; 同理, 输出数据的前六位用 0 代替。 5. 标识符 与 C 语言等 一样, Verilog HDL 语法中最基本的部分就是标识符。一般说来, Verilog HDL 中的标识符有普通标识符、转义标识符,下面分别给以具体说明。 普通标识符的命名规则是: ( 1)必须由字母或者下划线开头,字母区分大小写; ( 2)后续部分可以是字母、数字、下划线或 $; ( 3)总长度要小于 1024 个字符串的长度; 合法的普通标识符举例如下: sdfj_kiu // 允许在标识符内部包含下划线 _sdfji // 允许以下划线开头 转义标识符指以反斜杠“ \”开头,以空白符结尾的任意字符串序列。空白符可以是 一个空格、一个 TAB 键、一个制表符或者一个换行符等。转义字符本身没有意义,如 \sfji // \sfji 与 sfji 等价 \23kie // 可以以任意可打印的字符开头 \*239d // 理由同上 标识符的第一个字符不能够是 “ $” , 因为在 Verilog HDL 中, “ $” 专门用 来代表系统 命令 (如系统任务和系统函数 )。 6. 关键字 与其它语言一样, 每种语言有自己的保留字。 这些字是用户所不能作为名字来使用的 标识符。 Veriog HDL 中约有 98 个关键词,其中常用的有: always endmodule reg and assign begin for case or function output parameter wait if else input while end 3.2.2 数据类型 Verilog HDL 的数据类型是指在硬件数字电路中数据进行存储和传输的方式。按照物 理数据类型分类, Verilog HDL 中变量分为线型和寄存器型两种,两者在驱动方式、保持 方式和对应的硬件实现都不相同。 这两种变量在定义时要设置位宽, 缺省值为一位。 变量 的每一位可以是 0, 1, x 或 z,其中 x 代表一个未 被预置初始状态的变量,或是由于两个 或更多个驱动装置试图将之设定为不同的值而引起的冲突型变量; z 代表高阻状态或悬空 状态。 本节主要介绍几种常用的数据类型: 参数常量、 线型变量和寄存器型变量, 以及存储 器的定义方式。其他数据类型可以参考 IEEE1364-1995 中相关定义和规定。 1. 参数( Parameters) 参数是常量的一种,经常用来定义延时、线宽、寄存器位数等物理量,可以增加 代 码的可读性和可维护性。 参数定义的格式为: 184 parameter 参数名 1=表达式 1,参数名 2=表达式 2,参数名 3=表达式 3,……; 例 3-2-3 参数定义 // Parameter example module example_for_parameters (reg_a, bus_addr, …); parameter msb=7, lsb=0, delay=10, bus_width=32; reg [msb:lsb] reg_a; reg [bus_width:0] bus_addr; and #delay (out, and1, and2); … endmodule 说明: ( 1)例 3-2-3 中用 文字参数 delay 代替延时常数 10,用 bus_width 代 替总线宽度常数 32, 用 msb 和 lsb 分别代 替最高有效位 7 和最低 有效位 0, 增 加了代码的可读性, 并方便修改设计; ( 2)可以在一条语句中定义多个参数,中间用逗号隔开; ( 3) 对于含有参数的模块通常称为参数化模块。参数化模块的设计,体现出可重用 设计的思想,在仿真中也有很大的作用。 2. 线型变量( Nets) 线型变量通常表示硬件电路中元器件间的物理连接。 它的值由驱动元件的值决定, 并 具有实时更新性。 Verilog HDL 提供了多种线型变量, 与电路中各种类型的连线相对应, 具体如表 3-2-5 所列。 线型变量不具备电荷保持作用 ( trireg 型 除外) , 因此没有存储数据的能力, 其逻辑 值由驱动源 提供和保持 。各种线型 变量在没有 驱动源的情 况下呈现高 阻态( trireg 保持在 不定态) 。 表 3-2-5 常用线型变量及说明 wire 型变量是常用的线型变量。 线型变量定义的格式为: wire in1, in2; wire [7:0] local_bus; 第一种格式中将 in1, in2 定义为 1 位 的线型变量, 第二种格式中将 local_bus 定义为 8 位宽的线型变量。 线型变量主要通过 assign 语句赋 值, 3.3 节中 将进一步讲述其赋值方式。 3. 寄存器型变量( Registers) 寄存器型变量表示一个抽象的数据存储单元, 它并不特指寄存器, 而是所有具有存储 能力的硬件电路的通称, 如触发器、 锁存器等。 此外, 寄存器型变量还包括测试文件中的 激励信号。 虽然这些激励信号并不是电路元件, 仅是虚拟驱动源, 但由于保持数值的特性, 仍然属于寄存器变量。 寄存器类型只能在 always 语句和 initial 语句中被赋值, 并且它的值 从一个赋值到另一个赋值被保存下来。寄存器型变量的缺省值是不定态 X。 线型变量 功能说明 wire, tri 标准连线 wor, trior 多重驱动时,具有线或特性的连线 wand, triand 多重驱动时,具有线与特性的连线 tri1,tri0 上拉电阻、下拉电阻 supply1, supply0 电源线、地线 trireg 具有电荷保持特性的连线 185 寄存器型变量与线型变量的显著区别是寄存器型数据在接受下一次赋值之前, 始终保 持原值不变,而线型变量需要有持续的驱动。 寄存器型变量的主要分类如表 3-2-6 所列,其中, integer、 real 和 time 主要用于纯数 学的抽象描述,不对应任何具体的硬件电路。 表 3-2-6 常用寄存器型变量及说明 寄存器型变量是常用的寄存器型变量。 寄存器型变量的定义方式为: reg in3, in4; // reg [7:0] local_bus; //local_bus 为 8 位宽的寄存器型变量 第一种格式中将 in3,in4 定义为 1 位的寄存器型变量,第二种格式中将 local_bus 定义 为 8 位宽的寄存器型变量。寄存器型变量主要通过块语句赋值, 3.3 节 中将进一步讲述其 赋值方式。 4. 存储器( Memories) 在设计中,经常有存储指令或存储数据等操作,因此,需要掌握对存储器的定义 和 描述方式。 存储器定义的格式为: reg [wordsize-1:0] memory_name[memsize-1:0]; 例如: parameter wordsize=16, memsize=1024; reg [wordsize-1:0] mem_ram [memsize-1:0]; 定义了一个由 1024 个 16 位寄存器构 成的存储器,即存储器的字长为 16 位,容量为 1K。 3.2.3 系统任务与系统函数 前面介绍标识符时提到, 标识符的第一个字符不能够是 “ $” , 因为在 Verilog HDL 中, “ $” 专门用 来代表系统命令 (如系统任务和系统函数 ), 本节将具体介绍常用的系统命令和 系统函数的功能。 Verilog HDL 共提供了 10 类, 80 余种 系统任务与系统功能,下面主要介绍在设计和 仿真过程中常用的系统任务与系统函数。 1. 系统任务 $display 与 $write 系统任务 $display 与 $write 属显示类系统任务( Display system tasks) ,主要 用于仿真 过程中,将一些基本信息或仿真的结果按照需要的格式输出。 $display和 $write 调用的格式为: $display ( “格式控制字符串” ,输出变量名表项) ; $write ( “格式控制字符串” ,输出变量名表项) ; 例 3-2-4 系统任务调用 module disp; reg [31:0] rval; initial 寄存器型变量 功能说明 reg 常用的寄存器型变量 integer 32 位带符号整数型变量 real 64 位带符号整数型变量 time 无符号时间变量 186 begin rval = 101; $display("rval = %h hex %d decimal",rval,rval); $display("rval = %o octal\nrval = %b bin",rval,rval); // line 7 $display("rval has %c ascii character value",rval); $display("current scope is %m"); $display("%s is ascii value for 101\n ",101); $write("rval = %h hex %d decimal",rval,rval); $write("rval = %o octal\nrval = %b bin",rval,rval); $write("rval has %c ascii character value\n",rval); $write("current scope is %m"); $write("%s is ascii value for 101\n",101); end endmodule 显示结果为: rval = 00000065 hex 101 decimal rval = 00000000145 octal rval = 00000000000000000000000001100101 bin rval has e ascii character value current scope is disp e is ascii value for 101 rval = 00000065 hex 101 decimalrval = 00000000145 octal rval = 00000000000000000000000001100101 binrval has e ascii character value current scope is disp e is ascii value for 101 通过对例 3-2-4 中两组显示结果的分析,可以了解到这两种系统任务唯一的区别是: $display 在 输出结束后会自动换行,而 $write 则只有在加入相应的换行符“ \n”时 才会产 生换行。 调用系统任务 $display 和 $write 时,需要注意: ( 1) 格式控制字符串的内容包括两部分:一部分为与输出变量在输出时需要一 并 显 示的普通字符,如例 3-2-4 第 7 行中 的“ rval =” ;另一部分为对输出的格式进 行格式控制的格式说明符, 如第 7 行中的 “ %o”“ \n”。“ \n” 主要用于 换行, “ %o” 是格式说明符, 格式说明符以 “%” 开 头 , 后面是控制字符, 将输出的 数据转换成指定的格式输出,具体如表 3-2-7 所列。 ( 2) 输出变量名表项指要输出的变量名。如果有多个变量需要输出,各个变量 名 之 间可以用逗号隔开。 ( 3) 在输出变量表项缺省时,将直接输出引号中的字符串。这些字符串不仅可 以 是 普通的字符串,而且可以是字符串变量。 表 3-2-7 格式说明符定义 格式说明符 输出格式 %h 或% H 以十六进制数的形式输出 %d 或% D 以十进制数的形式输出 %o 或% O 以八进制数的形式输出 %b 或% B 以二进制数的形式输出 %c 或% C 以 ASCII 码字符的形式输出 %s 或% S 以字符串的形式输出 %v 或% V 输出线型数据的驱动强度 187 %m 或% M 输出模块的名称 2. 系统任务 $monitor 系统任务 $monitor 也属于 显示类系统任务, 同样用于仿真过程中, 基本信息或仿真的 结果的输出。 $monitor调用的格式为: $monitor ( “格式控制字符串” ,输出变量名表项) ; $monitor 具 有监控功能, 当系统任务被调用后, 就相当于启动了一个后台进程, 随时 对敏感变量进行监控, 如果发现其中的任意一个变量发生变化, 整个参数列表中变量或表 达式的值都将输出显示。 3. 系统任务 $readmem 系统任务 $readmem 属文本 读写类系统任务 ( File input-output system tasks) , 用于从 文本文件中读取数据到存储器中。 $readmem 可以在仿真的任何时刻被执行。 系统任务调用的格式为: $readmemb(“<数据文件名称 >”, <存储器名称 >); $readmemb(“<数据文件名称 >”, <存储器名称 >, <起始地址 >); $readmemb(“<数据文件名称 >”, <存储器名称 >, <起始地址 >, <结束地址 >); $readmemh(“<数据文件名称 >”, <存储器名称 >); $readmemh(“<数据文件名称 >”, <存储器名称 >, <起始地址 >); $readmemh(“<数据文件名称 >”, <存储器名称 >, <起始地址 >, <结束地址 >); 系统任务 $readmem 中 , 被 读取的数据文件内容只能够包含空白符、 注释行、 二进制 或十六进制的数字, 同样也可以存在不定态 x、 高阻态 z 和 下划线 _。 其中, 数字不能够包 含位宽和格式说明。 调用 $readmemb 时, 每个数 字必须是二进制, $readmemh 中必须 是十 六进制数字。 此外, 数据 文件中 地址 (在本 节专 指存储 器中 的地址 指针 )的表 示格 式为, “ @” 后面加上十六进制数字。 同一个数据文件中可以出现多个地址。 当系统任务遇到一个地址 时,立刻将该地址后面的数据存放到存储器中相应的地址单元中。 例 3-2-5 8 位 MCU 测试代码以及数据文件的部分内容 // RAM 的定义 reg [7:0] ram[0:65535]; // RAM wire [7:0] data_in; // 输入数据 reg [7:0] data_out; // 输出数据 // 输出数据的设计 always @(posedge CLK) begin if (RW) data_out = #4 ram[ADDRESS]; // 输出的数据 else data_out = #4 8'hzz; end // 输入数据的设计 always @(negedge CLK) begin if(~RW) #4 ram[ADDRESS] = data_in; 188 end // 测试向量库的调入 initial begin $readmemh("c:/SimFile/00_test_all.txt",ram); // $readmemh("c:/SimFile/01_test_lda.txt",ram); // $readmemh("c:/SimFile/02_test_ldxldy.txt",ram); … end 数据文件“ c:/SimFile/00_test_all.txt”的内容为: @0000 23 23 23 45 65 67 … @fff0 23 34 54 67 此例中调用 的系统任务 是 $readmemh,因此数据 文件中采用 十六进制数 据。此外 , RAM 的存放过程是按照定义中指定的顺序变化的,即从 0 开始到 65535 结束。 4. 系统任务 $stop 与 $finish $stop与 $finish属仿真控制类系统任务( Simulation control system tasks) ,主要用于仿 真过程中对仿真器的控制作用。 $stop具有暂 停功能, 这时, 设计人员可以输入相应的命令, 实现人机对话。 通常执 行 完 $stop后,会出现系统提示,如: “ Break at time.v line 13” 。 $finish的作用是结束仿真进程,输出信息包括系统结束时间、模块名称等,如: ** Note: $finish : time.v(13) Time: 300 ns Iteration: 0 Instance: /test 5. 系统函数 $time $time 属于仿真时间类系统函数 ( Simulation time system functions) , 通常与显示类系 统任务配合,以 64 位整数的形式显示仿真过程中某一时刻的时间。 例 3-2-6 $time 与 $monitor 应用示例 `timescale 10 ns / 1 ns module test; reg set; parameter delay = 3; initial begin $monitor($time,"set=",set); #delay set = 0; #delay set = 1; end 189 endmodule 显示结果为: 0 set=x 3 set=0 6 set=1 3.2.4 编译向导 Verilog HDL 中编译向导的功能和 C 语言中编译预处理的功能非常接近,在编译时首 先对这些编译向导进行“预处理” ,然后保持其结果,将其与源代码一起进行编译。 编译向导的标志是在某些标识符前添加反引号“ `” ,在 Verilog HDL 中,完整的编译 向导集合如下: `define `timescale `include `celldefine `default_nettype `else `endcelldefine `endif `ifdef `undef `nounconnected_drive `resetall `unconnected_drive 本书主要介绍常用的编译向导,其他编译向导的使用可以参考 IEEE1364-1995 标准 。 1. 宏定义 `define 宏定义 `define 的作用是用于文本定义,和 C 语言的 #define 类 似,即在编译时通知编 译器,用宏定义中的文本直接替换代码中出现的宏名。 宏定义的格式为: `define <宏名 > <宏定义的文本内容 > 宏定义语句可以用于模块的任意位置,通常写在模块的外面,有效范围是从宏定 义 开始到源代码描述结束。此外,建议采用大写字母表示宏名,以便于与变量名相区别。 每条宏定义语句只可以定义一个宏替换,且结束时没有分号;否则,分号也将作 为 宏定义内容。在调用宏定义时,也需要用撇号“`”作开头,后面跟随宏定义的宏名。 通过下面的示例可知,采用宏定义能够提高代码的可读性和可移植性。 `define WORDSIZE 8 reg [1:`WORDSIZE] data; //define a nand with variable delay `define VAR_NAND(dly) nand #dly ` VAR_NAND (2) g121 (q21, n10, n11); // delay is 2 ` VAR_NAND (5) g122 (q22, n10, n11); // delay is 5 组成宏定义的字符串不能够被以下标识符分隔开,如注释行、数字、字符串、确 认 符、关键词、双目和三目字符运算符,否则,该宏定义是非法的。 例如: `define first_half “start of string $display (`first_half end of string”); 上面的例子就是由于被引号隔开,使得宏定义非法。 2. 仿真时间尺度 `timescale 仿真时间尺度是指对仿真器的时间单位及对时间计算的精度进行定义。 格式为: `timescale <时间单位 > /<时间精度 > 时间单 位和 时间精 度都 由整数 和计 时单位 组成 的。合 法的 整数有 1, 10, 100;合 法 190 的计时单位为 s、 ms(10 s)、 us(10 s)、 ns(10 s)、 ps(10 s)和 fs(10 s)。 3? 6? 9? 12? 15? 在仿真时间尺度中, 时间单位用来定义模块内部仿真时间和延迟时间的基准单位; 时 间精度用来声明该模块仿真时间的精确程度。 如 : `timescale 1 ns/100 ps 指以 1ns 作为 仿真 的时间单位,以 100ps 的计算精度对仿真过程中涉及到的延时量进行计算。 时间精度和时间单位的差别最好不要太大。 因为在仿真过程中, 仿真时间是以时间精 度累计的, 两者差异越大, 仿真花费的时间就越长。 另外, 时间精度值至少要和时间单位 一样精确, 时间精度值不能大于时间单位值。 如果一个设计中存在多个 `timescale, 则采用 最小的时间单位。 3. 文件包含 `include 编译向导中,文件包含 `include 的作 用是在文件编译过程中,将语句中指定的源代码 全部包含到另外一个文件中。格式如下: `include “文件名” 如: `include “ global.v” `include “ ../../library/mux.v” 其中, 文件名中可以指定包含文件的路径, 既可以是相对路径名, 也可以是完整的路径名。 每条文件包含语句只能够用于一个文件的包含, 但是, 包含文件允许嵌套包含, 即包含的 文件中允许再去包含另外一个文件。 3.3 Verilog HDL 基本语句 3.3.1 过程语句( Structured procedures) Verilog HDL 中,所有的描述都是通过下面四种结构中的一种实现的: (1) initial 语句 (2) always 语句 (3) task 任务 (4) function 函数 在一个模块内部可以有任意多个 initial 语句和 always 语句,两者都是从仿真的起始 时刻开始执行的, 但是 initial 语句后面 的块语句只执行一次, 而 always 语 句则循环地重复 执行后面的块语句,直到仿真结束。 task 任务 和 function 函数 可以在模块内部从一处或多处被调用, 具体使用方法将在后 面的章节中介绍。 1. initial 语句 initial 语句的格式为: initial begin 语句 1; 语句 2; … 语句 n; end 在前面的源代码里面已经多次出现 initial 语句, 如例 3-2-4、例 3-2-5、例 3-2-6 等。 下面将按照 initial 块语句的形式分别介绍。 (1) 无时延控制的 initial 语句 : initial 语句 从 0 时刻开 始执行, 在 下面的例子中, 寄存器 变 量 a 在 0 时刻被赋值为 4。 191 reg a; … initial a = 4; … (2) 带时延控制的 initial 语句 : initial 语句 从 0 时刻开 始执行, 寄存器变量 b 在时 刻 5 时被 赋值为 3。 reg b; … initial #5 b = 3; … (3) 带顺序过程块 (begin-end)的 initial 语句 : initial 语句 从 0 时刻开 始执行, 寄存 器变量 start 在时刻 0 时被赋值为 0,又在时刻 10 时被赋值为 1。 reg start; … initial begin start=0; #10 start=1; end 2. always 语句 always 语句在仿真过程中是不断重复执行的,描述格式为: always <时序控制 > <进程语句 >; 在前面的源代码里面也同样多次出现过 always 语句,以下是一些基本示例: (1) 不带时序控制的 always 语句:由于没有时延控制,而 always 语句是重复执行的,因 此下面的 always 语句将在 0 时刻无限循环。 always clock = ~ clock; (2) 带时延控制的 always 语句:产生一个 50M 的时钟。 always #100 clock = ~ clock; (3) 带事件控制的 always 语句:在时钟上升沿,对数据赋值。 always @ (posedge clock ) data = data_in; 3.3.2 赋值语句 Assignments 赋值语句是 Verilog HDL 中对线型和寄存器型变量赋值的主要方式, 根据赋值对象的 不同分为连续赋值语句和过程赋值语句,两者的主要区别是: (1) 赋值对 象不 同:连 续赋 值语句 用于 对线型 变量 的赋值 ;过 程赋值 语句 完成对 寄 存 器变量的赋值,具体内容如表 3-3-1 所列。 表 3-3-1 赋值语句的赋值对象 赋值语句 赋值对象 连续赋值语句 线型变量(标量或矢量) 线型变量(矢量)中的某一位 线型变量(矢量)中的某几位 上述三种的任意组合 过程赋值语句 寄存器型变量(标量或矢量) 寄存器变量(矢量)中的某一位 192 寄存器变量(矢量)中的某几位 存储器 上述四种的任意组合 (2) 赋值过 程实 现方式 不同 :线型 变量 一旦被 连续 赋值语 句赋 值后, 赋值 语句右 端 表 达式中 的信 号有任 何变 化,都 将实 时地反 映到 左端的 线型 变量中 ;过 程赋值 语 句 只有在 语句 被执行 到时 ,赋值 过程 才能够 进行 一次, 而且 赋值过 程的 具体执 行 时 间还受到各种因素的影响。 (3) 语句出 现的 位置不 同: 连续赋 值语 句不能 够出 现在任 何一 个过程 块中 ;过程 赋 值 语句只能够出现在过程块中。 (4) 语句结构不同:连续赋值语句以关键词 assign 为先导;过程赋值语句不需要任何 先导的关键词,但是,语句的赋值分为阻塞型和非阻塞型。 下面将分别介绍两种赋值语句的具体应用。 1. 连续赋值语句 Continuous assignments assign 为连续赋值语句,用于对 wire 型变量进行赋值。其基本的描述语法为: assign # [delay] <线型变量 > = <表达式 > ; 例 3-3-1 四位加法器的 verilog 的描述 module adder (sum_out, carry_out, carry_in, ina, inb); output [3:0] sum_out; output carry_out; input [3:0] ina, inb; input carry_in; wire carry_out, carry_in; wire [3:0] sum_out, ina, inb; assign {carry_out, sum_out} = ina + inb + carry_in; endmodule 例 3-3-1 是一个四位加法器的描述,其中,通过连续赋值语句对进位位和运算结果统 一赋值,这种组合赋值过程在设计中会经常涉及到。 例 3-3-2 使用带门延的 assign 语句 `timescale 1ns/1ns module MagnitudeComparator(A,B,AgtB,AeqB,AltB); //parameters first parameter BUS = 8; parameter EQ_DELAY = 5, LT_DELAY = 8, GT_DELAY = 8; input [BUS-1:0] A,B; output AgtB,AeqB,AltB ; wire [BUS-1:0] A,B; assign #EQ_DELAY AeqB = A == B; //第 13 行 assign #GT_DELAY AgtB = A > B; assign #LT_DELAY AltB = A < B; 193 endmodule 例 3-3-3 过程赋值与连续赋值的比较 `timescale 1ns/1ns module assignment_for_bit(in1,in2,out1,out2); input [1:0] in1,in2; output [1:0] out1,out2; wire [1:0] out1; //定义输出信号类型 reg [1:0] out2; wire [1:0] in1, in2; //定义输入信号类型 // 连续赋值语句部分 assign out1 = in1 & in2; // 过程赋值语句 always @( in1 or in2 ) out2= in1 & in2; endmodule 例 3-3-2和例 3-3-3都含 有通过连续赋值语句赋值的源代码, 其中, 例 3-3-2的第 13~15 行带有延迟,大部分综合工具不支持延迟语句,因此,延迟主要用于仿真过程中。 仿真需要 EDA 工具的支撑才能够完成。目前,数字电路设计中前端采用的仿真工具 主要有 Cadence 公司 的 Verilog-XL, NC-Verilog, Synopsys 公司 的 VCS, Mentor 公司 的 ModelSim, Aldec 公司 的 Active-HDL 等。图 3-3-1 是例 3-3-2 的仿真波形图(用 Mentor 公司的 Modelsim 仿真器得到) ,从中可以清晰的看到: 245ns 时, B=8’h40,A=8’h08; 8ns 后,即 253ns 时, AltB 由 0 变为 1。 295ns 时, B=8’h00, A=8’h08; 8ns 后,即 303ns 时, AltB 由 1 变为 0。 图 3-3-1 例 3-3-2 仿真波形图 例 3-3-3 中 第一部分是连续赋值语句部分。由于 out2 是寄 存器型变量,因此第二部 采 用 过 程 赋 值 语 句 对 out2 赋值。 图 3-3-2 是例 3-3-3 综合 后生成的逻辑电路图 (用 Synplicity 公司 的 Synplify 综合器得 ) , 从中可以看到, 虽然 out2 是寄 存器型变量, 但是综合结果与线型的 out1 相同。 因此, 存 器 型 变 量 对 应 的 硬 件 电 路 并 不 一 定 是 寄 存 器 。 分 到 寄 194 ral assignments) 键 词 , 而 且只能够 在 always 语句或 in 。 其 基 本 的 描 述 语 法 为 : <寄 表达式 > ; <1> 或 <寄存器型变量 表达式 > ; <2> 型 过 程 赋 值(即描 述 方 式 <1>)和 非阻塞性 过程 赋 值 lways @( in1 or in2 ) out2= in1 & in2; 3. 阻 ( Blocking l assignment) Verilog HD 语 句 分 为 阻 塞 型 赋 值 语 句 与 非 阻 塞 型 两 种 描 述 语 句 的 特 性 , 合理选择, 以便于得 到理想的设计。 首先通过例 解 一 下 两 种 赋 值 语 句 的 定 义 及 其 区 别 。 例 3-3-4 阻塞型赋值语句与非阻塞型赋值语句的比较 ataout_c,dataout_d, ,dataout_b; taout_d; dataout_a,dataout_b; block assignment ut_b=dataout_a; ays @(posedge clock) // nonblock assignment c; 图 3-3-2 例 3-3-3 综合的逻辑电路图 2. 过程赋值语句( Procedu 过程赋值语 句用于对寄 存器类变量 赋值,没有 任何先导的 关 itial 语 句 的 过 程 块 中 赋 值 存 器 型 变 量 > = < > <= < 过程赋值 语 句有两种 赋 值形式: 阻 塞 ( 即 描 述 方 式 <2>) 。 例 3-3-1 中的第二部分就是采用阻塞型过程赋值方式的描述: // 过程赋值语句 a 塞 型 赋 值 语 句 与 非 阻 塞 型 赋 值 语 句 & nonblocking procedura L 按 照 过 程 赋 值 语 句 被 有 效 执 行 的 顺 序 , 将 过 程 赋 值 赋 值 语 句 , 设 计 者 可 以 根 据 这 3-3-4 及 其 仿 真 波 形 图 感 性 地 了 module block_nonblock ( dataout_a, dataout_b,d data_in,clock ); input data_in,clock; output dataout_a output dataout_c,da reg reg dataout_c,dataout_d; always @(posedge clock) // begin dataout_a=data_in; datao end alw begin dataout_c <= data_in; dataout_d <= dataout_ end endmodule 195 例 3-3-5 例 3-3 g data_in,clock; dataout_c,dataout_d; onblk ( dataout_a, dataout_b,dataout_c,dataout_d, data_in,clock); itial lock=0; data_in=0; data_in =1; ck = ~clock; lways #100 data_in = ~data_in ; initial begin #1000 $stop; #20 $finish; end endmodule -4 的测试程序 module test_blk_nonblk; re wire dataout_a,dataout_b; wire block_nonblock blk_n in begin c #40 end always #50 clo a 图 3-3-3 例 3-3-4 的仿真波形图 例 3-3-4 中 , 分别通过阻塞型赋值语句对 dataout_a, dataout_b 赋 值 ; 非 阻塞型赋值 语句对 dataout_c, dataout_d 赋值。 从图 3-3-3 中可以 看到, dataout_a, dataout_b 和 dataout_c 比 dataout_c 晚了一个时钟周期。 某 个 角 度 讲 , 非阻塞 并 行 块 的 执 行 十 分 相 象 , 这 些 在 后 面 还 会 讲 到 。 语句块用来将两条或多条语句组合在一起,使其在格式上更象一条语句。块语句 有 的波形图完全一致,但是 dataout_d 由此可以得到阻塞与非阻塞型赋值语句的基本区别是,阻塞型赋值语句的执行受 到 前后顺序的影响, 只有在第一条语句执行完之后才可以执行第二条语句, 而在非阻塞型赋 值语句中, 则是在某一规定时刻同时完成, 不受先后顺序的影响。 从 型 赋 值 语 句 的 执 行 顺 序 与 3.3.3 块语句( Block statements) 196 两种, 一种是 begin-end 语句 , 通常用来标识按照给定顺序执行的串行块 ( Sequential block) ; 1. 串 (1) 串行块中的每条语句都是依据块中的排列次序顺序执行。 (2) 串行块中每条语句的延时都是相对于前一条语句执行结束的相对时间。 (3) 串行块的起始执行时间是块中第一条语句开始执行的时间, 结束时间是最后一条语句 执 行 结 束 的 在例 3-3-5 在并行块中还将给出其他示例。 例 3-3-5 中节选 clock=0; data_in=0; #40 data_in 2. 并 行 无 关 。 ( 2) 的 延 时 都 是 相 对 于 整 个 并 行 块 开 始 执 行 的 绝 对 时 间 。 ( 3) 程 控 制 转 入 并 行 块 的 时 间 , 结束时间是并行块 中按执行时间排序,最后执行的那条语句结束的时间。 例 3-3-6 中 制 , 从图 3-3-4 中可以看到,两个寄存器型变量最终得到了相同的结果。 例 3-3-6 quential_parallel; ; // d declared as a parameter and ra; // seq and para are declared as 8-bit registers egin 8'h35; q = 8'hE2; l rk para = 8'h35; para = 8'hE2; 7; egin #400 $stop; 一种是 fork-join 语句,用来标识并行执行的并行块 (Parallel block)。 行 块 (begin-end) 串行块具有如下特点: 时 间 。 中, 就使用了串行块定义仿真的延迟时间。 // initial begin =1; end 块 ( fork-join) 并行块具有如下特点: ( 1) 行 块 中 的 每 条 语 句 都 是 同 时 并 行 执 行 的 , 与 排 列 次 序 行 块 中 每 条 语 句 并 行 块 的 起 始 执 行 时 间 是 流 分 别 采 用 串 行 块 和 并 行 块 描 述 了 两 个 寄 存 器 型 变 量 , 分 别 加 以 延 时 控 串行块与并行块 module se parameter d = 50 reg [7:0] seq, pa initial b #d seq= #d se #d seq = 8'h00; #d seq = 8'hF7; end initia fo #50 #100 #150 para = 8'h00; #200 para = 8'hF join initial b 197 #30 $finis end endm h; odule 图 3-3-4 例 3-3-6 的仿真波形图 3.3.4 条 件 语 句 ( 1. if-else 语句 if-else语句是 用来判断所给的条件是否满足,根据判定的结果(真或假)决定执行给 出 的 式 1) 块语句 1; 块语句 n+1; 第 一 种 情 况 ; 当 条 件 表 达 式 不 达 式 的 值 为 0, x, z) 时 , 停 止 执行块语句 1, 此时会形成锁存 器,保存块语句 第二种情况下,如果条件表达式不成立,执行块语句 2。这 样,在硬件电路上通常会 形 成 多 路 选 择 器 第 三 种 情 况 达 式 是 否 成 立 , 根据表达式的值判断执行的块语句。 由于 if-else的嵌套 , 需 设 计 要 求 。 这一部分内容在 3.7 节 设 计 实 例 中 将 例 3-3-7是 一 其中使用了大量的 if-else嵌套。 例 3-3-8是其仿 真 代 码 , 最 后 是 例 3-3-7 成绩统计器的 verilog 描述 ore_grade(reset,Sum,Grade,Total_A,Total_B,Total_C,Total_D); t [3:0] Grade; [3:0] Total_A,Total_B,Total_C,Total_D; Conditional statement) 两 种 操 作 之 一 。 Verilog HDL语言共提供了 3种形式的 if-else语句。 ( 1) if(表达式) 块语句 1; ( 2) if(表达式) 块语句 1; else 块语句 2; ( 3) if ( 表 达 else if(表达式 2) 块语句 2; else if(表达式 3) 块语句 3; … else if(表达式 n) 块语句 n; else 下 , 如 果 条 件 表 达 式 成 立 ( 即 表 达 式 的 值 为 1时) ,执行后面的块语句 1 成 立 ( 即 表 1的执行结果。 。 下 , 依 次 检 查 表 要 注 意 if与 else的配对 关系, 以 免 无 法 实 现 继 续 介 绍 。 个考试成绩统计器的设计, 显 示 的 输 出 结 果 。 module sc input reset; input [7:0]Sum; outpu output reg [3:0] Grade; 198 reg [3:0] Total_A,Total_B,Total_C,Total_D; ; ys @(Sum or reset) _C=0;Total_B=0;Total_A=0; e=4'hd; D=Total_D+1; e=4'hc; tal_C+1; nd um<8'd85) rade=4'hb; l_B+1; rade=4'ha; nd dule 例 3-3-8 测 core; ,Total_B,Total_C,Total_D; (reset,Sum,Grade,Total_A,Total_B,Total_C,Total_D); 10 reset=1; wire [7:0] Sum alwa if (~reset) begin Grade=0; Total_D=0; Total end else begin if (Sum<8'd60) begin Grad Total_ end else if (Sum<8'd75) begin Grad Total_C=To e else if(S begin G Total_B=Tota end else begin G Total_A=Total_A+1; e end endmo 试 程 序 module test_s reg reset; reg [7:0] Sum; wire [3:0] Grade; wire [3:0] Total_A score_grade score initial begin reset=0; Sum=8'd50; # #100 Sum=8'd100; #100 Sum=8'd80; #100 Sum=8'd90; 199 #100 Sum=8'd60; #100 Sum=8'd100; Sum=8'd80; d100; =8'd80; d90; 'd60; 'd100; d80; $display("Total_A=",Total_A); Total_A= 6 Total_B= 3 = 2 1 多 路 条 件 分 支 的 结 构 , 多用于多条件译码电路的描述中, 如译码 器 机 及 微 处 理 器 的 指 令 译 码 等 。 Verilog HDL语言共提供了 3种形 式的 ca 句 。 (1) 2; ; n+1; case (2) ) 块语句 2; … 值 n: 块语句 n; default: 块语句 n+1; endcase (3) casex(敏感表达式) 值 1:块语句 1; 值 2:块语句 2; … 值 n: 块语句 n; default: 块语句 n+1; endcase #100 #100 Sum=8' #100 Sum #100 Sum=8' #100 Sum=8 #100 Sum=8 #100 Sum=8' $display("Total_B=",Total_B); $display("Total_C=",Total_C); $display("Total_D=",Total_D); end endmodule 最 终 显 示 结 果 为 : Total_C Total_D= 2. case 语句 case语句构成 了 一 个 、 数据选择器、 状 态 se语 case(敏感表达式) 值 1: 块语句 1; 值 2: 块语句 … 值 n: 块语句 n default: 块语句 end casez( 敏 感 表 达 式 值 1: 块语句 1; 值 2: 200 这三种语句的描述方式唯一的区别就是对敏感表达式的判断, 其中, 第 一种要求敏感表 达式的值 与 给定的值 1、值 2……或 值 n中的一个 全 等时,执 行 后面相应 的 块语句; 如 果 均 不 等时, 执行 default语句。 第二种 ( casez) 则认为, 如果给定的值中有某一位 (或某几位) 是 高阻态 ( z) ,则认 为该 位为“ 真” , 敏感表 达式 与其比 较时 不予判 断, 只需比 较其 他位。第 三种 ( casex) 则扩充为, 如果给定的值中有某一位 (或某几位) 是高阻态 ( z) 或不定态 ( x) , 同样认为其为 “真” , 不予 判断。 例 3-3-9是采用 casez和 casex编写 的代码, 可以感性地了解 cases 和 casex的应用。 例 3-3-9 case 语句的使用示例 // example for casez casez (encoder) 4'b1???: high_lvl=3; 4'b01??: high_lvl=2; 4'b001?: high_lvl=1; 4'b0001: high_lvl=0; default: high_lvl=0; endcase // example for casex casex (encoder) 4'b1xxx: high_lvl=3; 4'b01xx: high_lvl=2; 4'b001x: high_lvl=1; 4'b0001: high_lvl=0; default: high_lvl=0; endcase case语句与 if-else语句的功 能十分接近, 可以全部转化为 if-else语句描述。 当控制条件集 中在某个敏感表达式的变化上时, 同样也可以将 if-else语句 改写成 case语 句 。 只是, if-else语 句可以实现优先权的描述,而 case无法实现。具体内容及示例将在 3.7节具体介绍。 3.3.5 循环语句 Verilog HDL 中存在 4 种 类型的循环语句,可以控制语句的执行次数。这四种语句分别 是 for 语句、 repeat 语句、 while 语句和 forever 语句。 1. for 语句 与 C 语言完全相同, for 语句的描述格式为: for ( 循环变量赋初值;循环结束条件;循环变量增值 ) 块语句; 即在第一次循环开始前, 对循环变量赋初值; 循环开始后, 判断初值是否符合循环结束 条件,如果不符合,执行块语句,然后给循环变量增值;再次判断是否符合循环结束条件, 如果符合循环结束条件,循环过程终止。 例 3-3-10 采用 for 语句描述的七人投票器 module vote(pass,vote); input [6:0]vote; output pass; reg pass; reg [2:0] sum; integer i; 1 always @(vote) begin sum=0; for (i=0; i<=7; i=i+1) if (vote[i]) sum=sum+1; if (sum[2]) pass=1; else pass=0; end endmodule 2. repeat 语句 repeat 语句可以连续执行一条语句若干次,描述格式为: repeat (循环次数表达式 ) 块语句; 在例 3-3-11 中,经过重复 8 次,实现了 16 位数据前 8 位与后 8 位的数据交换。 例 3-3-11 repeat 语句的应用 if(rotate==1) repeat(8) begin temp=data[15]; data = {data<<1,temp}; end 例 3-3-12 采用 repeat 语句实现两个 8 位二进制数乘法 module mult_repeat(outcome,a,b); parameter size=8; input [size:1] a,b; output[2*size:1] outcome; reg [2*size:1] temp_a,outcome; reg [size:1] temp_b; always @(a or b) begin outcome=0; temp_a=a; temp_b=b; repeat ( size ) begin if (temp_b[1]) outcome = outcome + temp_a ; temp_a = temp_a <<1 ; temp_b = temp_b>>1 ; end end endmodule 3. while 语句 while 语句是不停地执行某一条语句,直至循环条件不满足时退出。描述格式为: while(循环执行条件表达式) 块语句; 2 while 语句在 执行时,首先判断循环执行表达式是否为真,如果为真,执行后面的块语 句,然后再返回判断循环执行条件表达式是否为真,依据判断结果确定是否需要继续执行。 while 语句 与 for 语句 十分类似,都需要判断循环执行条件是否为真,以此确定是否需 要继续执行块语句。 例 3-3-13 通过调用 while 语句实现从 0 到 100 的计数过程 initial begin count = 0; while (count < 101) begin $display("Count=%d",count); count = count + 1; end end 4. forever 语句 forever语句可以无条件地连续执行语句, 多用在 “ initial” 块 中, 生成周期性输入波形, 通常为不可综合语句。描述格式为: forever 块语句; 例 3-3-14 通过 forever语句生成一个 50M的时钟信号 initial begin Clock=0; forever #100 Clock =~Clock; end 3.3.6 任务与函数 Verilog HDL 分模块对系统加以描述,但有时这种划分并不一定方便或显得勉为其难。 因此, Verilog HDL 还提供了任务和函数的描述方法。通常在描述设计的开始阶段,设计者 更多关注总体功能的实现, 之后再分阶段对各个模块的局部进行细化实现, 任务和函数对这 种设计思路的实现有很大的帮助。 任务和 函数 ,是在 模块 内部将 一些 重复描 述或 功能比 较单 一的部 分, 作为一 个相 对 独 立地进行描述,在设计中可以多次调用。 1. 任务与函数的区别 函数需要在一个仿真时间单位内完成; 而任务定义中可以包含任意类型的定时控制部分 及 wait 语句等。 函数不能调用任务,而任务可以调用任何任务和函数。 函数只允许有输入变量且至少有一个, 不能够有输出端口和输入输出端口; 任务可以没 有任何端口,也可以包括各种类型的端口。 函数通过函数名返回一个值;任务则不需要。 2. task 任务 任务可以在源代码中的不同位置执行共同的代码段, 这些代码段已经用任务定义编写成 任务,因此,能够从源代码的不同位置调用任务。 任务的定义与引用都在一个模块内部完成,任务内部可以包含时序控制,即时延控制, 并且任务也能调用任何任务(包括其本身)和函数。 定义格式为: 3 task <任务名 >; <端口及数据类型定义语句 > <语句 1> <语句 2> … <语句 n> endtask 调用格式为: <任务名 >(端口 1,端口 2, ……) ; 例 3-3-15 交通灯的时序控制代码 module traffic_lights; reg clock, red, amber, green; parameter on = 1, off = 0, red_tics = 350, amber_tics = 30, green_tics = 200; // initialize colors. initial begin red = off; amber = off; green = off; end always begin // sequence to control the lights. red = on; // turn red light on light(red, red_tics); // and wait. green = on; // turn green light on light(green, green_tics); // and wait. amber = on; // turn amber light on light(amber, amber_tics); // and wait. end // task to wait for “tics” positive edge clocks // before turning “color” light off. task light(color,tics); output color; input [31:0] tics; begin repeat (tics) @ (posedge clock); color = off; // turn light off. end endtask always begin // waveform for the clock. #100 clock = 0; #100 clock = 1; end 4 endmodule // traffic_lights. 3. function 函数 函数与 task 任务一样,也可以在模块中的不同位置执行同一段代码;不同之处是函数 只能返回一个值, 它不能包含任何时间控制语句。 函数可以调用其它函数, 但是不能调用任 务。此外,函数必须至少带有一个输入端口,在函数中允许没有输出或输入输出说明。 函数的定义格式为: function <位宽说明 >函数名 ; <输入端口与类型说明 > <局部变量说明 > begin <语句 1> <语句 2> …… <语句 n> end endfunction 函数的调用是通过将函数作为表达式中的操作数来实现的。其调用格式为: <函数名 >(<表达式 1>, <表达式 2>,…… ) 例 3-3-16 通过函数的调用实现阶乘的运算过程 module tryfact; function [31:0] factorial; // define the function input [3:0] operand; reg [3:0] i; begin factorial = 1; for (i = 2; i <= operand; i = i + 1) factorial = i * factorial; end endfunction integer result, n; // test the function initial begin for (n = 0; n <= 7; n = n+1) begin result = factorial(n); $display("%0d factorial=%0d", n, result); end end endmodule // tryfact 5 显示结果为: 0 factorial=1 1 factorial=1 2 factorial=2 3 factorial=6 4 factorial=24 5 factorial=120 6 factorial=720 7 factorial=5040 3.4 仿真验证( Simulation) 仿真是电 路 设计中用 来 对设计者 的 硬件描述 和 设计结果 进 行调试( Debug) 、验 证 ( Verification)的方法之一。 当设计者采用 HDL 描述设计了一个硬件电路后,需要验证其正确性。采用自顶向下的 设计方法时,从系统级、行为级、 RTL( Register Transfer Level)到门级, 每个层次的设计 结果都需要仿真,确保设计中的错误尽早发现及时解决,以缩短设计周期。 传统的仿真过程是: 建立 测试向量 ( Test vector) ——仿真 ——与标准数据文件 ( Golden file) 比较。 但是 Verilog HDL 更倾向于把上述过程集中在一个统一的测试文件 ( Test file 或 test fixture) 里 , 对待测电路 ( DUV, Design under verification) 施加仿真输入矢量, 通过观 察该设计在仿真输入矢量作用下的反应 (如波形图, 与期望的仿真输出矢量比较) 来判断是 否达到设计目标。 图 3-4-1 仿真示意图 测试文件是一个没有输入和输出的顶层模块。 一个测试文件包括被测模块的映射, 测试 向量, 仿真结果的显示或输出, 以及辅助模块的映射和各种必须环境的建立。 典型的测试文 件形式为: module module_name; //数据类型声明 //被测模块的映射 //施加测试向量 //显示仿真结果 endmodule 例 3-3-5 和例 3-3-8 就是 用于仿真的测试代码,可以看到,测试文件中有对被测模块的 映射以及通过 initial 行为描述施加的测试向量,没有输入端口和输出端口的定义。 例 3-4-1是一 个双向计数器的源代码, 要求计数器能够实现异步复位, 同步装载功能。 例 3-4-2 是计数 器的测试代码,采用行为描述对源代码仿真,覆盖率达到 100%,以证 明源代 码的正确性。 例 3-4-1 带异步复位的双向计数器 6 /***************************************************************/ // MODULE: up/down counter // // FILE NAME: cnt_rtl.v // VERSION: 1.0 // DATE: Nov.06,2002 // AUTHOR: Peter // // CODE TYPE: RTL Level // // DESCRIPTION: This module defines an up/down counter with // asynchronous set and reset inputs, and synchronous load, // up/down control, and count enable. // /***************************************************************/ //DEFINES `define BITS 8 //Number of bits in counter //TOP MODULE module Counter( clk, in, reset_n, preset_n, load, up_down, count_en, out, carry_out ); //INPUTS input clk; //Clock input [`BITS-1:0] in; //Input input reset_n; //Active low; //asynchronous reset input preset_n; //Active low, //asynchronous preset input load; //synchronous load input input up_down; //synchronous up/down control input count_en; //synchronous count //enable control //OUTPUTS output [`BITS-1:0] out; //Output output carry_out; //Carry output //INOUTS //SIGNAL DECLARATIONS wire clk; 7 wire [`BITS-1:0] in; wire reset_n; wire preset_n; wire load; wire up_down; wire count_en; reg [`BITS-1:0] out; wire carry_out; reg carry_up; reg carry_dn; //PARAMATERS //ASSIGN STATEMENTS assign carry_out = up_down? & carry_up : carry_dn ; //MAIN CODE //Look at the edge of clock for state transitions always @(posedge clk or negedge reset_n or negedge preset_n) begin carry_up <= 1'b0; carry_dn <= 1'b0; if (~reset_n) begin // This is the reset condition out <= `BITS'h0; carry_dn <= 1'b1; end else if (~preset_n) begin // This is the preset condition. Note that in this implementation, // the reset had priority over preset out <= ~`BITS'h0; carry_up <= 1'b1; end else if (load) begin // This is implementation, load has priority over count enable out <= in; if (in ==~`BITS'h0) carry_up <= 1'b1; else if ( in == `BITS'h0) carry_dn <= 1'b1; end else if (count_en) begin if (up_down == 1'b1) begin out <= out+1; 8 if (out ==~`BITS'h1) carry_up <= 1'b1; end else if (up_down == 1'b0) begin out <= out -1 ; if (out ==`BITS'h1) carry_dn <= 1'b1; end end end endmodule //Counter 例 3-4-2 计数器的测试程序 /***************************************************************/ // MODULE: counter simulation // // FILE NAME: cnt_sim.v // VERSION: 1.0 // DATE: Nov.6, 2002 // AUTHOR: Peter // // CODE TYPE: Simulation // // DESCTIPTION: This module provides stimuli for simulating an // up/down counter. // It tests the asynchronous rest and preset controls and the synchronous load // control. // It loads a value and counts up until the counter overflows. It then loads a new // value // and counts down until the counter overflows. During each cycle, the output is // compared // to the expected output. /***************************************************************/ //DEFINES `define DEL 20 //delay `define BITS 8 //Number of bits in counter `define PATTERN1 `BITS'h1 //starting data for counting up `define PATTERN2 `BITS'h3 //starting data for counting down // TOP MODULE module cnt_sim(); // INPUTS // OUTPUTS // INOUTS // SIGNAL DECLARARTIONS reg clock; reg reset_n; 9 reg preset_n; reg load; reg up_down; reg count_en; reg [`BITS-1:0] data_in; wire [`BITS-1:0] data_out; wire overflow; reg [`BITS-1:0] cycle_count; // cycle count variable integer test_part; // which part of the test are we doing? reg [`BITS-1:0] count_test; // used to compare against the counter // output reg carry_test; // used to compare against the carry // output //PARAMETERS //ASSIGN STATEMENTS //MAIN CODE //INSTANTIATE THE COUNTER Counter counter( .clk(clock), .in(data_in), .reset_n(reset_n), .preset_n(preset_n), .load(load), .up_down(up_down), .count_en(count_en), .out(data_out), .carry_out(overflow)); //initialize inputs initial begin // give all the inputs a certain number, or sth will be wrong.such as up_down clock=1; data_in=`BITS'b0; reset_n=1; preset_n=1; load=0; up_down=1'b1; count_en=0; cycle_count=`BITS'b0; test_part=0; // indicate which part we are doing. end //generate the clock always #50 clock = ~clock; 10 //simulate always @(negedge clock) begin case (test_part) 0: begin case (cycle_count) `BITS'h0: begin //assert the reset signal reset_n=0 ; //wait for the outputs to change asynchronously # `DEL # `DEL // test output if (data_out === `BITS'h0 ) $display ("Reset is working"); else begin $display ("\nERROR at time %0t:", $time); $display ("Reset is not working"); $display ("data_out = %h\n",data_out); //use $stop for debugging $stop; end //deassert the reset signal reset_n=1; //set the expected outputs count_test=`BITS'h0; carry_test=1'bx; end `BITS'h1: begin //assert the preset signal preset_n=0 ; //wait for the outputs to change asynchronously # `DEL # `DEL // test output if (data_out === ~`BITS'h0 ) $display ("Preset is working"); else begin $display ("\nERROR at time %0t:", $time); $display ("Preset is not working"); $display ("data_out = %h\n",data_out); //use $stop for debugging $stop; 11 end //deassert the preset signal preset_n=1'b1; //set the expected outputs count_test=~`BITS'h0; end `BITS'h2: begin //load data into the counter data_in=`PATTERN1; load=1'b1; end `BITS'h3: begin //Test outputs if (data_out === `PATTERN1) $display ("Load is working"); else begin $display ("\nERROR at time %0t:", $time); $display ("Load is not working"); $display ("expected data_out = %h",`PATTERN1); $display ("actual data_out = %h\n",data_out); //use $stop for debugging $stop; end //deassert the load signal load=1'b0; //set the expectd outputs count_test=`PATTERN1; end `BITS'h4:begin //Test outputs to see that data was not lost if (data_out === `PATTERN1) $display ("Counter hold is working"); else begin $display ("\nERROR at time %0t:", $time); $display ("Counter hold is not working"); $display ("expected data_out = %h",`PATTERN1); $display ("actual data_out = %h\n",data_out); //use $stop for debugging $stop; end //count up count_en =1'b1; 12 up_down=1'b1; //set the expected outputs count_test=`PATTERN1; carry_test=1'b0; end ~`BITS'h0: begin //start the second part of the test test_part=1'b1; end endcase end 1: begin case (cycle_count) `BITS'h4: begin //load data into the counter data_in =`PATTERN2; load = 1'b1; end `BITS'h5: begin //Test outputs if (data_out === `PATTERN2) $display ("Load is working"); else begin $display ("\nERROR at time %0t:", $time); $display ("Load is not working"); $display ("expected data_out = %h",`PATTERN2); $display ("actual data_out = %h\n",data_out); //use $stop for debugging $stop; end //count down count_en =1'b1; up_down=1'b0; load = 1'b0; //set the expected outputs count_test=`PATTERN2; end ~`BITS'h0: begin //start the third part of the test 13 test_part =2; end endcase end 2:begin case (cycle_count) `BITS'h5: begin $display("\nSimulation complete - no errors\n"); $finish; end endcase end endcase //Test the counter output if (data_out !== count_test) begin $display ("\nERROR at time %0t:", $time); $display ("Count is incorrect"); $display ("expected data_out = %h",count_test); $display ("actual data_out = %h\n",data_out); //use $stop for debugging $stop; end //Test the overflow if we are counting if ((count_en) && (overflow!==carry_test)) begin $display ("\nERROR at time %0t:", $time); $display ("Carry out is incorrect"); $display ("expected carry = %h", carry_test); $display ("actual carry = %h\n",overflow); //use $stop for debugging $stop; end //Determine the expected outputs for the next cycle if (up_down === 1'b1) begin if (count_en === 1'b1) count_test = count_test + `BITS'h1; if (count_test === ~`BITS'h0) carry_test = 1'b1; else carry_test = 1'b0; end else if (up_down === 1'b0) begin if (count_en === 1'b1) count_test = count_test - `BITS'h1; if (count_test === `BITS'h0) 14 carry_test = 1'b1; else carry_test = 1'b0; end // Increment the cycle counter cycle_count = cycle_count + 1'b1; end endmodule //cnt_sim 图 3-4-2 双向计数器的仿真波形图 从图 3-4-2 双 向计数器的仿真波形图中可以看到, 计数器正向计数计满后, 转向反项计 数, 这些测试矢量的施加都是通过测试代码自动运行的。 因此, 编写测试代码与设计源代码 一样,都需要一定的技巧。 3.5 可综合性描述( Coding for Synthesis) 综合是根据 厂家 提供的 单元库, 将源代码 ( Verilog HDL 或 VHDL) 转换成 网表的过程。 网表是使用硬件描述语言对门级电路的描述,即原理图的语言描述,是单纯的结构性描述, 与网表相对应的是门级电路原理图。 EDA 工具的综合过程包括映射( mapping)和优 化 ( optimization) 两部分。 在映射完成后, EDA 工 具按照设计者提供的约束条件 ( constraint) 对设计完成优化,以达到设计要求。约束条件有面积、速度、功耗和可测性等。 可综合 性是 指电路 描述 的综合 收敛 性,也 就是 说,一 个电 路的描 述在 多大程 度上 可 以 由 EDA 工具 自动生成合情合理的电路实现。如果设计采用不可综合语句描述,综合器将无 法映射,也就无法生成原理图和网表。因此,可综合性是设计中必须考虑的因素之一。 下面以 4-16 译码器为例,介绍代码描述对设计可综合性的影响。 例 3-5-1、例 3-5-2 和例 3-5-3 分别采用 assign 语句、 case 语句和 for-loop 语句 描述译码 器,采用相同的 XinlinxVirtex 工艺库,不施加任何约束综合后得到不同的结果。 例 3-5-1 由 assign 语句描述的译码器 module decoder ( binary_in , // 4 bit binary input decoder_out , // 16-bit out enable // Enable for the decoder ); input [3:0] binary_in ; input enable ; output [15:0] decoder_out ; wire [15:0] decoder_out ; assign decoder_out = (enable) ? (1 << binary_in) : 16'b0 ; 15 endmodule 图 3-5-1 例 3-5-1 的综合结果 图 3-5-2 例 3-5-1 综合后生成的 log 文件 例 3-5-2 由 case 语句描述的译码器 Target Part : 40mx02-3 Combinational Cells : 30 of 295 (10%) Sequential Cells : 0 Total Cells : 30 of 295(10%) Clock Buffers : 0 IO Cells : 21 Details: and2: 26 comb : 1 inv : 4 comb : 1 inbuf : 5 outbuf : 16 Found clock enable with period 1000ns #### START TIMING REPORT #### Performance Summary ************************ Requested Estimated Requested Estimated Clock Frequency Frequency Period Period Slack enable 1.0 MHz 43.3 MHz 1000.0 23.1 976.9 ================================================================= 16 module decoder ( binary_in , // 4 bit binary input decoder_out , // 16-bit out enable // Enable for the decoder ); input [3:0] binary_in ; input enable ; output [15:0] decoder_out ; reg [15:0] decoder_out ; always @ (enable or binary_in) if (enable) begin decoder_out = 0; case (binary_in) 4'h0 : decoder_out = 16'h0001; 4'h1 : decoder_out = 16'h0002; 4'h2 : decoder_out = 16'h0004; 4'h3 : decoder_out = 16'h0008; 4'h4 : decoder_out = 16'h0010; 4'h5 : decoder_out = 16'h0020; 4'h6 : decoder_out = 16'h0040; 4'h7 : decoder_out = 16'h0080; 4'h8 : decoder_out = 16'h0100; 4'h9 : decoder_out = 16'h0200; 4'hA : decoder_out = 16'h0400; 4'hB : decoder_out = 16'h0800; 4'hC : decoder_out = 16'h1000; 4'hD : decoder_out = 16'h2000; 4'hE : decoder_out = 16'h4000; 4'hF : decoder_out = 16'h8000; endcase end endmodule 17 18 图 3-5-3 例 3-5-2 的综合结果 Target Part : 40mx02-3 Combinational Cells : 44 of 295 (15%) Sequential Cells : 0 Total Cells : 44 of 295(15%) 图 3-5-4 例 3-5-2 综合后生成的 log 文件 Clock Buffers : 1 IO Cells : 21 Details: and2: 24 comb : 1 inv : 4 comb : 1 dll : 16 comb : 1 clkbuf : 1 clock buffer inbuf : 4 outbuf : 16 Found clock enable with period 1000ns #### START TIMING REPORT #### Performance Summary ************************ Requested Estimated Requested Estimated Clock Frequency Frequency Period Period Slack enable 1.0 MHz 52.7 MHz 1000.0 19.0 981.0 例 3-5-3 用循环语句描述的译码器 module decoder ( binary_in , // 4 bit binary input decoder_out , // 16-bit out enable // Enable for the decoder ); input [3:0] binary_in ; input enable ; output [15:0] decoder_out ; reg [15:0] decoder_out ; 19 integer i ; always @ (enable or binary_in) if (enable) begin decoder_out = 0; for (i = 0; i < 16; i = i +1) begin decoder_out = (i == binary_in) ? i + 1 : 16'h0; // line 18 end end endmodule 图 3-5-5 例 3-5-3 的综合结果 Target Part : 40mx02-3 Combinational Cells : 5 of 295 (2%) Sequential Cells : 0 Total Cells : 5 of 295(2%) Clock Buffers : 1 IO Cells : 21 Details: and2: 3 comb : 1 dll : 2 comb : 1 clkbuf : 1 clock buffer inbuf : 4 outbuf : 16 Found clock enable with period 1000ns #### START TIMING REPORT #### Performance Summary ************************ Requested Estimated Requested Estimated Clock Frequency Frequency Period Period Slack enable 1.0 MHz 103.8 MHz 1000.0 9.6 990.4 ================================================================= 20 图 3-5-6 例 3-5-3 的综合 log 文件 从图 3-5-2,图 3-5-4 和图 3-5-6 的 log 文件可以看 到, 综合生成的逻辑电路中, 例 3-5-3 的频率最高 ( 103.8 MHz) ,例 3-5-2 的面积最大 ( Total Cells is 44) , 由 此 可见, 代码的描述 方式对电路的性能(面积、速度等)有很大影响。 由于不 同的 综合工 具支 持不同 的可 综合性 描述 语句, 因此 ,在设 计中 ,常用 的可 综 合 性描述原则是: 1. 在时序电路中,采用非阻塞语句代替阻塞语句; always @ (posedge clock) q <= d; 2. 在组合逻辑中,采用阻塞语句; always @ (a or b or sl) if (sl) d = a; else d = b; 3. 确保敏感信号的完整性; always @ (a or b) // this event list is missing signal sl if (sl) d = a; else d = b; 4. 添加适当的注释, 如果将代码删掉, 依然能够识别出设计的内容, 这是一个完美的 注释。作为初学者,可以适当添加部分注释,逐步完善。 // example of bad comments // add a and b together always @ (a or b) c = a + b; // Good commenting 21 // 8 bit unsigned adder for data signals ‘a’ and ‘b’ // output is sent to UART2 always @ (a or b) c = a + b; 在设计中, 通常要求芯片面积小, 速度快, 因此, 综合器提供了优化功能, 但是, 设计 者在代码描述中也要注意描述对于面积的影响,如例 3-5-4 和例 3-5-5 中 介绍的资源共享、 优化算法等。同时,可以查阅 EDA 公司提供的有关综合方面的资料,更多地了解可综合性 描述。 例 3-5-4 加法器 module add_separate (a,b,c,d,sel,out_data); input [3:0] a,b,c,d; input sel; output [4:0] out_data; reg [4:0] out_data; always @(sel or a or b or c or d) if (sel==1) out_data<= a+b; else out_data<= c+d; endmodule 例 3-5-5 采用资源共享的加法器 module add_together(a,b,c,d,sel,out_data); input [3:0] a,b,c,d; input sel; output [4:0] out_data; reg [4:0] out_data; reg [3:0] mux1,mux2; always @(mux1 or mux2) out_data = mux1+mux2; always @(sel or a or b or c or d) if (sel==1) begin mux1<=a; mux2<=b; end else begin mux1<=c; mux2<=d; 22 end endmodule 两段代码采用了不同的描述方式, 实现相同的功能, 区别是例 3-5-5 采 用了资源共享的 方式, 首先选择数据, 然后进行加法, 因此, 对 应的电路中只有一个加法器, 减小了电路面 积。 图 3-5-7 例 3-5-4 的综合结果 图 3-5-8 例 3-5-5 的综合结果 3.6 设计实例 本节将选用一些典型电路进行 verilog 描述,以便大家更好的理解语言的使用。 3.6.1 译码电路 由于 3-8 译码器和编码器在前几章已有描述,本节只给出使用 case 语句实现的 verilog 描述。 例 3-6-1 3-8 译码器的 verilog 描述 module decoder(a,b,c,cntl,y); 23 input a,b,c; input [2:0] cntl; output [7:0] y; wire a,b,c; wire [2:0] cntl; reg [7:0] y; wire [2:0] data_in; assign data_in={c,b,a}; //输入码 always @ (data_in or cntl) if ( cntl == 3'b100 ) case (data_in) //译码 3'b000: y=8'b1111_1110; 3'b001: y=8'b1111_1101; 3'b010: y=8'b1111_1011; 3'b011: y=8'b1111_0111; 3'b100: y=8'b1110_1111; 3'b101: y=8'b1101_1111; 3'b110: y=8'b1011_1111; 3'b111: y=8'b0111_1111; endcase else y=8'b1111_1111; //失效 endmodule 3.6.2 编码电路 这里介绍一个具有优先级的 8-3 编码 器。 可以使用 if-else-if 语句描述优先级电路, 其基 本语言结构如下: if ( 条件表达式 1 ) 块语句 1 else if ( 条件表达式 2 ) 块语句 2 … else if ( 条件表达式 n) 块语句 n else 块语句 n+1 综合器将按条件表达式 1 到 n 的顺序 安排条件判断的次序, 从而实现优先级功能。 需要 注意的是,最后一个 if 后面的 else 是不可缺少的,否则综合器将会生成一个隐式锁存器。 下面是一个具有优先级的 8-3 编码器 源码和相应的综合。其中,输入端 0 具有最高优 先级, 而 7 的优先级最低。 例 3-6-2 编码器的 verilog 描述 module coder(data_in, data_out, enable); input [7:0] data_in; input enable; output [2:0] data_out; wire [7:0] data_in; reg [2:0] data_out; always @ ( data_in or enable ) 24 if ( enable ) data_out=3'bz; else if ( ~data_in[0] ) data_out=3'b000; else if ( ~data_in[1] ) data_out=3'b001; else if ( ~data_in[2] ) data_out=3'b010; else if ( ~data_in[3] ) data_out=3'b011; else if ( ~data_in[4] ) data_out=3'b100; else if ( ~data_in[5] ) data_out=3'b101; else if ( ~data_in[6] ) data_out=3'b110; else if ( ~data_in[7] ) data_out=3'b111; else data_out=3'bz; endmodule 3.6.3 数据分配器 数据传输中, 有时需要将数据分配到不同的数据通道上, 能够完成这种功能的电路称为 数据分配器,其电路结构为单输入多输出形式,功能如同多位开关,将输入 D 送 到指定的 数据通道上,如图 3-6-1 所示。 图 3-6-1 数 据 分 配 器 示 意 图 数 据 分 配 器 当 选 择 的 控 制 条 件 集 中 在 某 几 个 方 便 、 直 观 。 下 面 是 采 用 case 语句描述 数 据 分 配 器 的 例 例 3-6-3 数 3,dp4); put reset; //复位信号 cntl; //控制信号,决定输入数据的流向 //输入数据 ; //数据通道 1 2 utput [3:0] dp3; //数据通道 3 通 道 4 ; p2, dp3, dp4; ys @ (reset or cntl or d) t) dp2=4'b0; 在 语 言 描 述 上 有 多 种 方 式 , 可 以 采 用 if, case 等 条 件 语 句 。 变 量 上 时 , case 语 句 描 述 显 得 更 为 子 。 据 分 配 器 的 verilog 描述 module demux (reset, cntl, d,dp1,dp2,dp in input [1:0] input [3:0] d; output [3:0] dp1 output [3:0] dp2; //数 据 通 道 o output [3:0] dp4; //数 据 wire reset; wire [1:0] cntl wire [3:0] d; reg [3:0] dp1, d alwa if (rese begin //复位 dp1=4'b0; 25 dp3=4'b0; //通道选通 : dp4 = d; //缺省 egin ; dp2=4’bzzzz; 与数据分配器相对应的是数据选择器, 也就是通常说的 MUX, 同样可以使用 case 语句 、 -else 语句描 述数据, 源码的结构与上述的数据分配器基本相同, 只要把通道选通部分的方 dp4=4'b0; end else case (cntl) 2'b00: dp1 = d; 2'b01: dp2 = d; 2'b10: dp3 = d; 2'b11 default: b dp1=4’bzzzz dp3=4’bzzzz; dp4=4’bzzzz; end endcase endmodule 图 3-6-2 数据分配器综合后的电路逻辑图 if 26 向 翻 转 过 来 就 可 以 了 。 .6.4 同步计数器 同步计数器按时钟的节拍计数, 可以设置异步或同步使能端和清零端。 下面是一个具有 步 使 能 /清零端的 8 位二进制同步计数器的 Verilog 描述,其符号如图 3-6-3 所示。 3 同 图 3-6-3 计数器结构示意图 例 3-6-5 同步计数器的 verilog 描述 ,result); , en , clr; :0] result; dge clk ) begin 0; <= result+1; nd 例 3-6-6 计 imescale 1ns/1ns //定义时间精度 , en, clr; ter counter(clk,en,clr,result); //源码映射 100 个时间 module counter(clk,en,clr input clk output [7:0] result; reg [7 always @( pose if ( en ) if (clr || result = = 8'b1111_1111) result <= 8'b0000_000 else result e endmodule 数 器 的 测 试 文 件 `t module test; reg clk wire [7: 0] result; coun initial //产生时钟信号,周期为 //单位 begin #10 clk=1; 27 forever #5 clk=~cl0 k; //测试使能功能 10 en=0; en=0; 1; 0; 1; //测试清零功能 clr=0; nd #20000 $stop; //经过 20000 个时间单位,停止运行。 #20 $finish; //再经过 20 个时间单位,结束。 暂 停 计 数 , 但不会使计数器清零, 而清零信号则使计数器清零。 图 3-6-5 给出了 综合后的 逻辑图。 end initial begin # #190 en=1; #150 #240 en= #1970980 en= #140 en= end initial begin #10 clr=0; #130 clr=1; #150 e initial begin end endmodule 从图 3-6-4 的 仿真波形图上可以看到使能信号和清零信号的作用。 其中, 使能信号的失 效 图 3-6-4 同步计数器仿真示意图 28 图 3-6-5 同步计数器的综合结果 3.6.5 移位寄存器 这里以左移寄存器为例介绍移位寄存器的 Verilog 设计。在左 移寄存器的操作中寄存器 的每一位顺序左移一位, 最低位补 0。 若将最高位数据输出到最低位就是循环左移移位寄存 器 了 。 下 面 是 一 存 例 3-6-7 左 lk, en, clr, data_in, data_out ); [7:0] data_in; [7:0] data_out; :0] data_in; osedge clk ) if ( en ) [7:0] = 8'b0; ata_out[7:0] = data_in << 1 ; le 下面是循环左移寄存器的源代码。 例 3-6-8 循 存 n ) if ( clr ) begin data_out[7:1] = data_in [6:0]; data_out[0] = data_in [7]; end 左移功能使用移位运算符描述, 而循环功能则分开描述, 单独处理最高位和最低位的关 。 左移寄存器和循环左移寄存器的差别只在于第 0 位的输 入是来自于第 7 位还是 补入一个 。 个 左 移 寄 器 的 源 代 码 。 移 寄 存 器 module shift_left( c input clk,en,clr; input output wire [7 reg [7:0] data_out; always @ ( p if ( clr ) data_out else d endmodu 环 左 移 寄 器 always @ ( posedge clk ) if ( e data_out[7:0] = 8'b0; else 系 0 29 图 3-6-6 移位寄存器和循环寄存器的综合结果 3.6.6 述 , 这 里 以 一 个 例 子 说 明 用 Verilog HDL 进 行 有 分为五角和一元两种, 根据两种币值的投币 信 号 可 以 用 状 态 机 描 述 的 问 题 , 表 3-5-1 描 述 了 入 的 币 值 决 定 下 一 个 状 态 的 变 化 。 TATUS1: 投币时,售货机内已有 5 角; 5: 投币时,售货机内已有 2 元 5 角; STATUS6: 投币时,售货机内已有 3 元; 瓶 饮 料 后 转 至 FIVE 或 五 角 作 为 基 数 , 状态转移至 TEN 或 FIFTEEN,开 始 码 , one-hot 码以及自定义码等,每种编码方 NS)和输出逻辑( OL)三部分组成,可以依据 状态机的不同结构采用不同的 1. 将 CS、 NS 与 OL 分 别 2. 将 C O ; 3. 将 NS 与 OL 混 合 , S 单独描述; 4. 将 CS 与 S 混 合 , L 单 独 5. 将 CS 与 L 混合 , NS单独描述。 下 面 的 源 代 中 采 用 第 种 描 述 -hot 码。 表 3-5-1 自 动 售 饮 five_jiao one 当前状态 下一状态 找零 售货 有 限 状 态 机 的 设 计 在 2.5.3 中 对 有 限 状 态 机 已 经 有 了 详 尽 的 描 限 状 态 机 设 计 的 过 程 。 例 3-5-7 自 动 售 饮 料 机 要 求 每 次 投 币 一 枚 , 指 示 售 货 机 是 否 发 货 , 以及是否找零。 这 是 一 个 此 状 态 机 , 共 定 义 了 7 个 状 态 , 根 据 每 次 投 7 个状态的含义: STATUS0: 投币时,售货机内没有硬币; S STATUS2: 投币时,售货机内已有 1 元; STATUS3: 投币时,售货机内已有 1 元 5 角; STATUS4: 投币时,售货机内已有 2 元; STATUS 由于投币信号 five_jiao 和 one_yuan 不会同时为 1,所以只有三种组合会引起状态发生 转移。饮料价格为 2.5 元,当已投入 2.5 元 时 , 仍 继 续 投 币 , 则 售 一 TEN 状态; 若已投入 3 元, 则 将 找 零 的 新的转移。 状 态 机 的 编 码 方 式 很 多 , 如 顺 序 码 , 格 雷 式均有各自的特点,如 one-hot 码,尽管编码电路较大,但是需要的状态译码电路较少。 状态机由当前状态( CS) 、下一状态( Verilog 描述方法,常用的方法有: 描 述 ; S、 NS 与 L 混 合 描 述 C N O 描 述 ; O 码 3 方 法 , 编 码 方 式 为 7 位 one 料 机 状 态 表 _yuan 1 0 0 0 STAT STATUS1 1 0 US0 STATUS2 STATUS0 0 0 0 0 0 0 1 0 0 0 1 0 STATUS1 STATUS2 STATUS3 STATUS1 0 0 0 0 0 0 1 0 0 0 1 0 STATUS2 STATUS3 STATUS4 STATUS2 0 0 0 0 0 0 1 0 0 0 1 0 STATUS3 STATUS4 STATUS5 STATUS3 0 0 0 0 0 0 30 1 0 0 S4 STATUS5 STATUS6 0 0 0 0 0 0 1 STATU 0 STATUS4 0 1 0 0 0 US5 STATUS1 STATUS2 STATUS0 0 0 0 1 1 1 1 STAT 0 1 0 0 US6 STATUS2 STATUS3 STATUS0 0 0 1 1 1 1 0 1 STAT 0 描例 3-6-9 自 述 five_jiao,one_yuan, clk, reset, sell, five_jiao_out); 3'b000 S ne S S3 3'b010 S 3'b101 ta _jiao or one_yuan) x ta A next_state = `STATUS0; A 动 售 货 机 的 verilog odule auto_sell ( m input five_jiao,one_yuan; input clk, reset; output sell, five_jiao_out; reg sell, five_jiao_out; reg [2:0] current_state; eg [2:0] next_state; r 0 `define STATUS AT`define T US1 3'b001 S2 3'b011 `define STATU defi TATU` `define STATUS4 3'b110 5 3'b111 `define STATUS AT`define T US6 always @ (posedge clk) begin nt_state = next_state; curre d en c ren salways @ ( ur t_ te or reset or five begin if (!reset) begin ne t_state = `STATUS0; five_jiao_out = 0; sell=0; end se el urrent_s te)case (c `ST TUS0: begin five_jiao_out = 0; sell=0; if (five_jiao) next_state = `STATUS1; if (one_yuan) next_state = `STATUS2; else else end `ST TUS1: begin five_jiao_out = 0; sell=0; if (five_jiao) next_state = `STATUS2; 31 else if (one_yuan) next_state = `STATUS3; else next_state = `STATUS1; `STATUS2: next_state = `STATUS2; `STATUS3: next_state = `STATUS3; `STATUS4: next_state = `STATUS4; `STATUS5: five_jiao_out=0; if (five_jiao) next_state=`STATUS1; i state = `STATUS2; el next_state=`STATUS0; A ll= (fi tate=`STATUS2; en e_yuan ) next_state = `STATUS3; ; end te=`STATUS0; five_jiao_out=1; end end efault: begin end begin five_jiao_out = 0; sell=0; if (five_jiao) next_state = `STATUS3; else if (one_yuan) next_state = `STATUS4; else end begin five_jiao_out = 0; sell=0; if (five_jiao) next_state = `STATUS4; else if (one_yuan) next_state = `STATUS5; else end begin five_jiao_out = 0; sell=0; if (five_jiao) next_state = `STATUS5; else if (one_yuan) next_state = `STATUS6; else end begin sell=1; else f ( one_yuan ) next_ se end `ST TUS6: begin se 1; if ve_jiao) begin next_s five_jiao_out=0; d else if ( on begin five_jiao_out = 0 else begin next_sta d next_state=`STATUS0; sell=0; five_jiao_out=0; 32 end endcase end endmodule 3.6.7 单元。 ALU 设有 2 个输 入端 A, B, 1 个输出端 Y。不同的 ALU 有不同的功能,其具体功 能及操作方式由其指令系统决定。 通常先由操作码对送入 A、 B 端口的数据进行选择, 然后 通过控制信号执行 A、 B 的相应操作。 为了更加接近实际的工业设计,我们选用美国 ROCKWELL 公司生产的 6502 微处理器 所使用的 ALU 作为实例。 6502 ,是一款性能相当出色的微 处 理 复杂逻辑电路设计 在本节将选择 ALU 作 为设计示例来介绍一下使用 verilog 语言设计复杂逻辑电路的方 法。 ALU, 即算术逻辑运算单元 ( arithmatic logic unit) ,是 CPU、 MCU、 DSP 等器件的核心 共有 56 条 指 令 , 13 种寻址方式 器 。 它 的 ALU 可以执行加、减、与、或、异或以及移位、加 1、减 1 八种基本操作。 图 3-6-7 两 端 口 ALU 示意图 需 要 特 别 指 功 能 。 它可以把累加器 ( A_Reg) 、 堆栈( SP_Reg) _Reg、 Y_Reg) 等作为输入信号,通过不同的操作,来实 现微处理器的 1 其 源 码 如 下 例 3-6-10 算 p, ,A_Reg,SP_Reg, ut); _Reg,A_Reg,SP_Reg; ; endcase CK) b10001: a= X_Reg; 出 的 是 , 6502 的 ALU 还 可 以 具 有 地 址 计 算 、 变 址 寄 存器( X 3 种寻址方式。 : 术 逻 辑 单 元 的 verilog 描述 module alu ( CLOCK,C_Flag, Alu_O ATA_In,X_Reg, Y_RegD C_IN, Alu_O put CLOCK,C_Flag; in input [4:0] Alu_Op; _Reg,Yinput [7:0] DATA_In,X output C_IN; output [7:0] Alu_Out; g C_INre reg [7:0] Alu_Out,a,b ; always @(negedge CLOCK) casex (Alu_Op) g; 5'b01011: b=SP_Re default: b=DATA_In; lways @(negedge CLOa casex (Alu_Op) 'bx0100, 5' 5'b00010, 5 5'b00011, 5'b00101, 5'b10x01: a= Y_Reg; 5'b00001, 5'b10000, 5'b0011x, 5'b10011, 5'bx1111: a= A_Reg; default: a=DATA_In; 33 endcase always @(negedge CLOCK) casex(Alu_Op) 5'b000xx: {C_IN, Alu_Out} <= a; //直通 5'b0010x: {C_IN, Alu_Out} <= a+b; //加 法 位 加 &b ; //与 : Alu_Out <=a|b ; //或 : Alu_Out <=a^b ; //异或 在 上 面 的 源 器 , 将参与运算的操作数存放在 A、 B 寄存器 中,然后又通过一个多路选择器,在操作码的控制下实现对 算 的 操 作 数 实 际 能是 5 位 , 故 定 辅 好 。 下 面 给 出 上 码 例 3-6-11 使 数 ys edge CLOCK) _IN, Alu_Out} <= DATA_In + X_Reg; p_DataInPlusY: {C_IN, Alu_Out} <= DATA_In + Y_Reg; `AluOp_FromA: {C_IN, Alu_Out} <= A_Reg; `AluOp_FromX: {C_IN, Alu_Out} <= X_Reg; … `AluOp_ROL: Alu_Out <= {DATA_In[6:0],C_Flag}; `AluOp_ROR: Alu_Out <= {C_Flag, DATA_In[7:1]}; default: ; endcase 5'b1000x, 5'b10010: {C_IN, Alu_Out} <= a-b ; //减 法 5'b0100x, 5'b01010: Alu_Out <= a - 1'b1; //减 1 5'b1010x, 5'b10111: {C_IN, Alu_Out}<= a+ 1'b1; //加 1 5'b00111: {C_IN, Alu_Out} <= a+b + C_Flag; //带 进 5'b01011: {C_IN, Alu_Out} <= b; //直通 5'b00110: Alu_Out <= a - b - (~C_Flag); //带进位减 5'b10011: Alu_Out <=a 5'b11111 5'b01111 5'b11000: Alu_Out <= {a[6:0], 1'b0}; //左移 5'b11001: Alu_Out <= {1'b0, a[6:0]}; //右移 5'b11010: Alu_Out <= {a[6:0], C_Flag}; //带进位左移 5'b11011: Alu_Out <= {C_Flag, a[6:0]}; //带进位右移 default: ; endcase endmodule 码 中 , 首先通过 2 个多 路 选 择 ALU 进行 的操作。因为参与运 有 3 个 , A, B( 4 位)和 C_Flag( 1 位) ,输 出若考虑进位,则运算结果可 义 了 助 信 号 C_IN。此处用编码表示操作码,虽然便于优化,但可读性不 述 源 的 参 数 化 描 述 , 可 以 看 到 可 读 性 好 了 很 多 。 用 参 化 设 计 的 alu alwa @(neg case(Alu_Op) `AluOp_DataIn: {C_IN, Alu_Out} <= DATA_In; `AluOp_DataInPlusX: {C `AluO 34 第四章 数字系统设计问题 .1 多功能数字钟的设计 .1.1 设计要求 设计一个能进行时、 分、 秒计时的十二小时制或二十四小时制的数字钟, 并具有定时与 钟 功 能 , 能在设定的时间发出闹铃音, 能非常方便地对小时、 分钟和秒进行手动调节以校 时 间 , 每 逢 整 点 , 产 生 报 时 音 报 时 。 系 统 框 图 如 图 4-1-1 所示: 二 进 制 的 计 数 规 律 , 用同步计数或异步计数 都 可 是 这 个 、 B 进行模式选择, 其中, AB=00 为模式 0, 系统为计时状态; AB=01 为 模 rn 信号,当 turn=0 时,表 示在手动校对时,选择调整分钟部分;当 turn=1 时 , 式 下 , 每 按 一 次 , 计 数 器 加 1。 铃 进 态 显 示 信 号 (发光管) : LD_alert 指示是 否设置了闹铃功能; LD_h 指示当前调 整 的 是 置 后 ( LD_alert=1) , 系统应 启动 一比较 电路 ,当计 时与 预设闹 铃时 间 相 等时,启动闹铃声,直到关闭闹铃信号有效。 整 点 报 时 由 分 和 秒 计 时 同 一 个 扬 声 器 驱 动 信 号 out。 系统计时时钟为 clk=1Hz, 选择另一时钟 clk_1k=1024Hz 作 为 产生闹铃声、 报时音的时 4 4 闹 准 图 4-1-1 数字钟系统框图 4.1.2 设计提示 此设计问题可分为主控电路、 计数器模块和扫描显示三大部分, 其中计数器部分的设计 是已经非常熟悉的问题, 只要掌握六十进制、 十 以 实 现 , 扫描显示模块在第一章中也已经介绍, 所 以 主 控 电 路 中 各 种 特 殊 功 能 的 实 现 设 计 问 题 的 关 键 。 用两个电平信号 A 式 1,系统为手动校时状态; AB=10 为模式 2,系统为闹钟设置状态。 设置一个 tu 表 示 在 手 动 校 对 时 , 选 择 调 整 小 时 部 分 。 设置一个 change 信 号 , 在 手 动 校 时 或 闹 钟 设 置 模 设置一个 reset 信 号 , 当 reset=0 时, 整个系统复位; 当 reset=1 时 , 系统 进行计时或 其它特殊功能操作。 设置一个关闭闹铃信号 reset1, 当 reset1=0 时 , 关闭闹铃信号; reset1=1 时, 可对闹 行 设 置 。 设 置 状 小 时 信 号 ; LD_m 指示当前调整的是分钟信号。 当 闹 铃 功 能 设 时 为 0( 或 60) 启动, 与 闹 铃 声 共 用 35 钟 信 号 。 控 -1 件 系 图 -1- V rilog HDL 参考 表 -1 数字钟主控电路状态表 4.2 数字式竞赛抢答器 4.2.1 参 賽 的 数 字 式 抢 答 器 , 每组设一个按钮供抢答使用。 抢答器具有第 择 计 出 状 态 注 主 电 路 状 态 表 如 表 4-1 所列。硬 统 示 意 如 图 4 2 所 示 。 e 代码见附录一。 4-1 模式 选 秒、 分、 时 数器脉冲 输 备 reset set1 rn _h _m LD_alertRe A B Tu LD LD 0 x x x x x 0 0 0 系 统 复 位 1 x 0 0 x Clk 0 0 0 系统计时 1 x 0 1 0 Change=↑ 分计数器加 1 0 1 0 1 x 0 1 1 Change=↑ 时计数器加 1 手动校时 1 0 0 1 1 1 0 0 Change=↑ 分计数器加 1 0 1 1 1 1 1 0 1 Change=↑ 时计数器加 1 1 0 1 设置闹钟 1 0 x x x x 0 0 0 关闭闹钟 图 4-1-2 数字钟硬件系统示意图 设计要求 设 计 一 个 可 容 纳 四 组 36 一信号鉴别和锁存功能, 使除第一抢答者外的按钮不起作用; 设置一个主持人 “复位” 按 钮 , 主 持 人 复 位 后 , 开始抢答, 第一信号鉴别锁存电路得到信号后, 用指示灯显示抢答组别, 扬 声 器 设置犯规电路, 对提前抢答和超时答题 (例如 3 分钟) 的组 别鸣笛示警, 并由组别显示 电路显示出犯规组别。 设置一个 计分电 路, 每组开 始预 置10 分, 由主持 人记 分,答 对一 次加1 分, 答错一次 减1分。 系统框图如图 4-2-1 所示。 或 锁 存 器 实 现 。 设置抢答按钮 K1、 K2、 人 复 分 电 路 是 一 个 相 对 独 立 的 模 块 , 采用十进制加/减计数器、 数码管数码扫描显示, 设 置复位信号 reset1、加分 信号 up、减 分信号 down, reset1=0 时 ,所有得分回到起始分( 10 分) ,且加 分 、减分 信号 无效; reset1=1 时, 由第 一信号 鉴别 锁存电 路的 输出信 号选 择进行 减 分 的 组 别 , 每 按 一 次 up,第一 抢答组加一分;每按一次 down,第 一抢答组组减一分。 硬件系统示意图如图 4-2-2 所示。Verilog HDL 参考代码见附录二。 发 出 2 ~3秒的音响。 4.2.2 设计提示 图 4-2-1 抢答器系统框图 此设计问题可分为第一信号鉴别锁存模块、 答题计时模块、 计分电路模块和扫描显示模 块四部分。 第一信号鉴别锁存模块的关键是准确判断出第一抢答者并将其锁存, 在得到第一信号后 将输入端封锁, 使其它组的抢答信号无效, 可 以 用 触 发 器 K3、 K4,主持人复位信号 reset,扬声器驱动信号 out。 reset=0 时,第一信号鉴 别锁存电路 、答题计时 电路复位, 此状态下, 若有抢答按 钮 按 下,鸣 笛示 警并显 示犯 规组别 ; reset=1 时 , 开 始抢答 ,由 第一信 号鉴 别锁存 电路 形成第一 抢答信号, 进行组别显示, 控制扬声器发出音响, 并启动答题计时电路, 若计时时间到主持 位 信 号 还 没 有 按 下 , 则 由 扬 声 器 发 出 犯 规 示 警 声 。 计 加 37 图 4-2-2 数字抢答器硬件系统示意图 4.3 数字频率计 4.3.1 设计要求 设计一个能测量方波信号频率的频率计, 测量结果用十进制数显示, 测量的频率范围是 1~100KHz, 分成两个频段, 即 1~999Hz, 1KHz~100KHz, 用 三位数码管显示测量频率, 用 LED 显示表示单位,如亮绿灯表示 Hz,亮红灯表示 KHz。 具有自动校验和测量两种功能,即能用标准时钟校验测量精度。 具有超量程报警功能,在超出目前量程档的测量范围时,发出灯光和音响信号。 系统框图如图 4-3-1 所示。 38 图 4-3-1 频率计系统框图 4.3.2 设计提示 脉冲信号的频率就是在单位时间内所产生的脉冲个数,其表达式 T N f = , f 为被测信号 的频率, N 为计数器所累计的脉冲个数, T 为产生 N 个脉冲所需的时间, 所以在 1 秒 时间内 计数器所记录的结果,就是被测信号的频率。 此设计问题可分为测量 /校验选择模块、计数器模块、送存选择报警模块、锁存模 块 和 扫描显示模块几部分。 测量 /校验选择模块的输入信号为: 选择信号 selet、 被测信号 meas、 测试信 号 test,输 出 信号为 CP1,当 selet=0 时 ,为测量状态, CP 1 =meas;当 selet=1 时,为校验状态, CP1=test。 校验与测量共用一个电路,只是被测信号 CP 1 不同而已。 计数器对 CP 1 信号进行计数, 在 1 秒 定时结束后, 将计数器结果送锁存器锁存, 同时将 计数器清零,为下一次采样测量做好准备。 设置 1 秒定 时信号 (周期为 2 秒) ,在 1 秒定时时间 内 的 所 有 被 测 信 号 送 计 数 器 输 入 端 。 设置量程档控制开关 K,单位显示信号 Y,当 K=0 时,为 1~999Hz 量程 档,数码管显 示的数值为被测信号频率值, Y 显示绿色,即单位为 Hz;当 K=1 时,为 1KHz~100KHz 量 程档,被测信号频率值为数码管显示的数值乘 1000, Y 显示红色,即单位为 KHz。 设置超出量程档测量范围示警信号 alert。计数器 由四级十进制计数构成(带进位 C) 。 若被测信号频率小于 1KHz( K=0) , 则 计数器只进行三级十进制计数, 最大显示值为 999.Hz, 如果被 测信 号频率 超过 此范围 ,示 警信号 驱动 灯光、 扬声 器报警 ;若 被测信 号为 1KHz~100KHz( K=1) , 计 数器进行四位十进制计数, 取高三位显示, 最大显示值为 99.9KHz, 如果被测信号频率超过此范围,报警。 送存选择、报警电路状态表如表 4-3-1 所列。 表 4-3-1 送存选择、报警电路状态表 量程控制 计数器 锁存 小数点位置 报警信号 K Q40 C D3 D2 D1 alert 0 0 1 1 0 1 X X 0 0 0 1 Q3 Q2 Q1 Q3 Q2 Q1 Q4 Q3 Q2 Q4 Q3 Q2 右第一位 右第一位 右第二位 右第二位 0 1 0 1 硬件系统示意图案如图 4-3-2 所示。 39 图 4-3-2 数字频率计硬件系统示意图 4.4 拔河游戏机 4.4.1 设计要求 设计一个能进行拔河游戏的电路。 电路使用 15 个 (或 9 个 ) 发 光二极管表示拔河的 “电 子绳” ,开 机 后只有中 间 一个发亮 , 此即拔河 的 中心点。 游 戏甲乙双 方 各持一个 按 钮,迅速 地、 不断地按动产生脉冲, 谁按得快, 亮点向谁方向移动, 每按一次, 亮点移动一次。 亮点 移到任一方终端二极管, 这一方就获胜, 此时双方按钮均无作用, 输出保持, 只有复位后才 使亮点恢复到中心。 由裁判下达比赛开始命令后,甲乙双方才能输入信号,否则,输入信号无效。 用数码管显示获胜者的盘数,每次比赛结束自动给获胜方加分。 系统框图如图 4-4-1 所示。 图 4-4-1 拔河游戏机系统框图 4.4.2 设计提示 此设计问题可以分为加 /减计数器、译码器和甲乙双方的得分计数显示电路几部分。 设置参赛双方输入脉冲信号 in1、 in2, 用可逆计数器的加、 减计数输入端分别接受两路 按钮脉冲信号。 设置裁判员“开始”信号 begin, begin 有效后,可逆计数器才接受 in1、 in2 信号。 用一个四线 -十六线译码器, 输出接 15 个 (或 9 个) 发光二极管, 设置一个复位信号 reset, 比赛开始, reset 信号使译 码器输入为 1000,译码后 中心处二极管点亮,当计数器进行加法 计数时,亮点向右移,减法计数时,亮点向左移。 当亮点移到任一方终端时,由控制电路产生一个信号使计数器停止接受计数脉冲。 将双方 终端二极管 “点亮” 信号 分别接两个得分计数显示电路, 当一方取胜时, 相应的 得分计数器进行一次得分计数,这样得到双方取胜次数的显示。 设置一个记分计数器复位信号 reset1,使双方得分可以清零。 硬件系统示意图如图 4-4-2 所示。 40 图 4-4-2 拔河游戏机硬件系统示意图 4.5 洗衣机控制器 4.5.1 设计要求 设计一个洗衣机洗涤程序控制器,控制洗衣机的电机作如下规律运转: 图 4-5-1 洗衣机控制器控制要求 用两位 数码 管预置 洗涤 时间( 分钟 数) ,洗涤 过 程在送 入预 置时间 后开 始运转 ,洗 涤中 按倒计时方式对洗涤过程作计时显示, 用 LED表示电机的正、 反转, 如 果定时时间到, 则停 机并发出音响信号。 系统框图如图 4-5-2 所示。 41 图 4-5-2 洗衣机控制器系统框图 4.5.2 设计提示 此设计 问题可分为洗涤预置时间编码模块、 减法计数显示、 时序电路、 译码驱动模块四 大部分。 设置预置信号 LD, LD 有 效后, 可以对洗涤时间计数器进行预置数, 用数据开关 K1~K10 分别代表数字 1、2、… 、 9、0,用 编码器对数据开关 K1~K10 的电平信号进行编码,编码 器真值表如表 4-5-1 所列,编码后的数据寄存。 设置洗涤开始信号 start, start 有效 则洗涤时间计数器进行倒计数, 并用数码管显示, 同时启动时序电路工作。 时序电路中含有 20 秒定 时信号, 10秒定时信号, 设为 A、 B, A、 B 为 “0” 表示定时时 间未到,A、B 为“1”表示定时时间到。 时序电路状态表如表 4-5-2 所列。 42 表 4-5-1 编码器真值表 数据开关电平信号 编码器输出 K1 K2 K3 K4 K5 K6 K7 K8 K9 K10 Q3 Q2 Q1 Q0 ↑ 0 0 0 0 0 0 0 0 0 0 ↑ 0 0 0 0 0 0 0 0 0 0 ↑ 0 0 0 0 0 0 0 0 0 0 ↑ 0 0 0 0 0 0 0 0 0 0 ↑ 0 0 0 0 0 0 0 0 0 0 ↑ 0 0 0 0 0 0 0 0 0 0 ↑ 0 0 0 0 0 0 0 0 0 0 ↑ 0 0 0 0 0 0 0 0 0 0 ↑ 0 0 0 0 0 0 0 0 0 0 ↑ 0 0 0 1 0 0 1 0 0 0 1 1 0 1 0 0 0 1 0 1 0 1 1 0 0 1 1 1 1 0 0 0 1 0 0 1 0 0 0 0 表 4-5-2 时序电路状态表 状态 电机 时间 /S S 0 S 1 S 2 S 3 正转 停止 反转 停止 20 10 20 10 状 态 编 码 为 : S0=00 S1=01 S2=11 S3=10 若选JK触发器,其输出为Q 2 Q1 逻辑赋值后的状态表如表 4-5-3 所列。 表 4-5-3 逻辑赋值后的状态表 A B Q n 2 Q n 1 Q2 n+1 Q1 n+1 说明 0 X 1 X X 0 X 1 0 X 1 X X 0 X 1 0 0 0 0 0 1 0 1 1 1 1 1 1 0 1 0 0 0 0 1 0 1 1 1 1 1 1 0 1 0 0 0 维持S 0 S0→S1 维持S 1 S1→S2 维持S 2 S2→S3 维持S 3 S3→S0 参考例 1-4-3 可 以 完 成 时 序 电 路 的 设 计 。 设置电机正转信号run, 反转信号rev, 暂停信号pause, 由时 序电路的输出Q 2Q1经译码驱 动模块,可使显示信号正确反映电路的工作状态,译码驱动模块真值表如表 4-5-4 所 列 。 直到洗涤计时时间到,时序电路异步复位,并启动音响电路。 硬件系统示意图如图 4-5-3 所示。Verilog HDL 参考代码见附录三。 表 4-5-4 译 码 驱 动 电 路 真 值 表 43 Q2 Q1 run rev pause 0 0 0 1 1 1 1 0 1 0 0 0 0 0 1 0 0 1 0 1 图 4-5-3 洗衣机控制器硬件系统示意图 4.6 电子密码锁 4.6.1 设计要求 设计一个电子密码锁, 在 锁开的状态下输入密码, 设置的密码共 4位 , 用数 据开关 K1~K10 分别代表 数 字 1、2、… 、 9、0,输 入的密码 用 数码管显 示 ,最后输 入 的密码显 示 在最右边 的数码管上, 即每输入一位数, 密码在数码管上的显示左移一位。 可删除输入的数字, 删除 的是最后输入的数字, 每删除一位, 密码在数码管的显示右移一位, 并在左边空出的位上补 充“ 0” 。 用 一 位输出电平的状态代表锁的开闭状态。 为保证密码锁主人能打开密码锁, 设置 一个万能密码,在主人忘记密码时使用。 系统框图如图 4-6-1 所示。 44 图 4-6-1 密码锁系统框图 4. 6. 2 设计提示 此设计问题可分为密码输入删除控制模块、寄存模块、比较模块、扫描显示几部分。 在密码输入删除控制模块中, 用编码器对数据开关 K1-K10 的电平信号进行编码, 编码 器真值表如表 4-5-1 所列 。 输入密码是在锁打开的状态下进行的, 每输入一位数, 密码在数 码管上的显示左移一位。设置删除信号 back, 每按下一次 back,删除 最后输入的数字,密 码在数码管的显示右移一位,并在左边空出的位上补充“ 0” ,状态表如表 4-6-1 所列。 表 4-6-1 密码输入删除控制电路状态表 密码锁状态 数据开关 删除信号 数码管显示 lock K i back D3 D2 D1 D0 1 1 ↑ 0 0 ↑ 右移 左移 设置密码确认信号 set, 当四位密码输入完毕后,按下 set, 则密码被送寄存器锁存, 比较模块得 A 数据,同时密码显示电路清零。 设置密码锁状态显示信号 lock, lock=0( LED 灭 ) 表示锁未开; lock=1( LED 亮) 表示 锁已打开。 设置关锁信号 close, 当密码 送寄存模块锁存后, 按下 close, 则 密码锁 lock=0, 锁被锁上。 设置密码检验信号 cheak, 在 lock=0状 态下, 从数据 开关输入四位开锁数码, 按下 cheak, 则开锁数码送寄存模块锁存, 数据比较模块得到 B 数据, 若 A=B, 则 D触发器被置 “1” ,锁 被打开,否则,lock 保持为“0” 。 万能密码(例如 0007)可预先设置在比较模块中。 密码锁的硬件系统示意图如图 4-6-2 所示。Verilog HDL 参考代码见附录四。 45 图 4-6-2 密码锁硬件系统示意图 4.7 脉冲按键电话按键显示器 4.7.1 设计要求 设计一个具有 7 位显示 的电话按键显示器, 显示器应能正确反映按键数字, 显示器显示 从低位向高位前移, 逐位显示按键数字, 最低位为当前显示位, 七位数字输入完毕后, 电话 接通,扬 声 器发出“ 嘟 ——嘟” 接 通声响, 直 到有接听 信 号输入, 若 一直没有 接 听,10 秒 钟后,自动挂断,显示器清除显示,扬声器停止,直到有新号码输入。 系统框图如图 4-7-1 所示。 图 4-7-1 脉冲按键电话按键显示器系统框图 4.7.2 设计提示 此设计题 与密码锁有相似之处, 可分为号码输入显示控制模块、 主控制模块和扫描显 46 示模块几部分。 在号码输入显示控制模块中,用数据开关 K1— K10 分别代 表数字 1、2、……9、0,用 编码器 对数 据开关 K1-K10 的电平信 号进行 编码 ,得四 位二 进制数 Q, 编码器 真值 表在表 4-5-1 中已经 给出。每输 入一位号码 ,号码在数 码管上的显 示左移一位 ,状态表如 表 4-7-1 所列。 表 4-7-1 号码输入显示控制模块状态表 C 数据开关 数码管显示 D7 D6 D5 D4 D3 D2 D1 1 1 1 1 1 1 1 1 ↑ 0 0 0 D4 0 D2 0 ↑ ↑ ↑ ↑ ↑ ↑ 0 0 0 0 0 0 D6 0 0 0 0 0 D5 D5 0 0 0 0 D4 D4 0 0 0 D3 D3 D3 D3 0 0 0 D2 D2 D2 D2 0 0 D1 D1 D1 D1 D1 D1 0 Q Q Q Q Q Q Q 0 X 熄灭 熄灭 熄灭 熄灭 熄灭 熄灭 熄灭 K i 当七位号码输入完毕后,由主控制模块启动扬声器,使扬声器发出“嘟——嘟”声响, 同时启动等待接听 10 秒计时电路。 设置接 听信 号 answer,若定时 时间 到还没 有接 听信号 输入 ,则号 码输 入显示 控制 电路 的 C 信号有效, 显示器清除显示, 并且扬声器停止, 若在 10 秒计时 未到时有接听信号输入, 同样 C 信号有效、扬声器停止。 设置挂断信号 reset, 任 何时刻只要有挂断信号输入, 启动 3秒计数器 C, 3 秒后系统 C 有效,系统复位。 主控制模块状态表如表 4-7-2 所列。 表 4-7-2 主控制模块状态表 接听信号 answer 挂断信号 reset 等待接听 10 秒计时 3 秒计数器 C 扬声器 X ↑ X X X ↑ 时间到 X X X X 时间到 0 0 0 停止 停止 停止 硬件系统示意图如图 4-7-2 所示。 47 图 4-7-2 脉冲按键电话按键显示器硬件系统示意图 4.8 乘法器 4.8.1 设计要求 设计一个能进行两个十进制数相乘的乘法器, 乘数和被乘数均小于 100, 通过按键输入, 并用数码管显示, 显示器显示数字时从低位向高位前移, 最低位为当前显示位。 当按下相乘 键后,乘法器进行两个数的相乘运算,数码管将乘积显示出来。 系统框图如图 4-8-1 所示。 图 4-8-1 乘法器系统框图 4. 8. 2 设计提示 此设计问题可分为乘数被乘数输入控制模块、 寄存模块、 乘法模块和扫描显示模块几部 48 分。 乘数和被乘数的输入仍用数据开关 K1—K10 分别 代表数字 1、 2、…、9、 0,用编码器 对数据开关 K1~K10 的电 平信号进行编码, 编码器真值表如表 4-5-1 所列 。 用两个数码管显 示乘数,两个数码管显示被乘数。 设置 “相乘” 信号 mul, 当 乘数输入完毕后, mul 有 效使输入的乘数送寄存器模块寄存。 再输入被乘数,显示在另两个数码管上。 设置“等于”信号 equal,当乘数和被乘数输入后,equal 有 效,使被乘数送寄存模块 寄存,同时启动乘法摸块。 两数相乘的方法很多, 可以用移位相加的方法, 也可以将乘法器看成计数器, 乘积的初 始值为零, 每一个时钟周期将被乘数的值加到积上, 同时乘数减一, 这样反复执行, 直到乘 数为零。 硬件系统示意图如图 4-8-2 所示。Verilog HDL 参考代码见附录五。 图 4-8-2 乘法器硬件系统示意图 4. 9 乒乓球比赛游戏机 4. 9. 1 设计任务 设计一个由甲乙双方参赛,有裁判的三人乒乓球游戏机。 用 8 个 (或更多个) LED 排成一条直线,以中点为界,两边各代表参赛双方的位置, 其中一只点亮的 LED 指示球的当前位置, 点亮的 LED 依此从左到右, 或从右到左, 其移动 的速度应能调节。 当 “球” (点亮的那只 LED) 运动到某方的最后一位时, 参赛者应能果断地按下位于自 己一方的按钮开关, 既表示启动球拍击球, 若击中则球向相反方向移动, 若未击中, 球掉出 桌外,则对方得一分。 设置自动记分电路,甲乙双方各用两位数码管进行记分显示,每计满 11 分为 1 局。 甲乙双方各设一个发光二极管表示拥有发球权, 每隔 2 次自 动交换发球权, 拥有发球权 的一方发球才有效。 系统框图如图 4-9-1 所示。 4. 6. 2 设计提示 49 图 4-9-1 乒乓球比赛游戏机系统框图 此设计问题可分为控制模块、加 /减计数模块、译码显示模块和甲乙方得分显示模 块 几 部分。 设置甲乙两 方击球脉冲 信号 in1、 in2,一方的击 球信号使加 /减计数器加 法计数,则 另 一方的击球信号就使加 /减计数器减法计数, 译码模块输出端 Y1-Y8 接 LED 模拟乒乓球的轨 迹, Y0、 Y9 为球掉出桌外信号, 经控制模块实现移位方向的控制, 真值表如表 4-9-1 所列。 表 4-9-1 加减计数、译码显示真值表 时钟 加 /减控制 计数器输出 译码器输出 clk DU / Q 3 Q 2 Q 1 Q 0 Y8 Y7 Y6 Y5 Y4 Y3 Y2 Y1 ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 1 0 0 1 0 0 0 1 1 0 1 0 0 0 1 0 1 0 1 1 1 1 0 0 0 0 1 1 1 0 1 1 0 0 1 0 1 0 1 0 0 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 1 0 1 0 0 0 0 0 0 0 设置发球劝拥有显示信号 S1、 S2,控制模块使每两次交换发球权。 加 /减控制信号 DU / 由乒乓球到达 Y8、 Y1 和击球信号 in1、 in2 及发 球权拥有信号 S1、 S2 共同产生,真值表如表 4-9-2 所示。 表 4-9-2 DU / 信号产生真值表 50 Y8 Y1 In1 In2 S1 S2 DU / 1 0 0 1 0 ↑ ↑ 0 0 1 1 0 1 0 当球到达 Y8 或 Y1 时, 参赛者没有及时击中, 则球掉出桌外, 加 /减计数模块停止计数, 对方得一分。 设置捡球信号 reset1, 通过 加 /减计数模块的异步置数端实现捡球, 当甲方拥有发球权时, 捡球信号将球放到 Y1;乙方拥有发球权时,捡球信号将球放到 Y8。 在控制模块中对甲、 乙双方的得分进行检测, 只要有一方的得分达到 11, 则一局结束。 设置裁判员复位信号 reset,在每局结束后将双方得分清零。 由调节晶振产生的时钟脉冲信号的频率,可以调节球的运动速度。 乒乓球比赛游戏机的硬件系统示意图如图 4-9-2 所示。 Verilog HDL 参考代 码见附录六。 图 4-9-2 乒乓球比赛游戏机硬件系统示意图 4.10 具有四种信号灯的交通灯控制器 4.10.1 设计要求 设计一个具有四种信号灯的交通灯控制器。 设计要求是: 由一条主干道和一条支干道汇 合成十字路口, 在每个入口处设置红、 绿、 黄、 左拐允许四盏信号灯, 红灯亮禁止通行, 绿 51 灯亮允许通行, 黄灯亮则给行驶中的车辆有时间停在禁行线外, 左拐灯亮允许车辆向左拐弯。 信号灯变换次序为:主支干道交替允许通行,主干道每次放行 40S,亮 5S 红灯让 行驶中的 车辆有时间停到禁行线外,左拐放行 15 秒, 亮 5S 红灯;支干道放行 30S,亮 5S 黄灯 ,左 拐放行 15 秒,亮 5S 红灯 ……。各计时电路为倒计时显示。 系统框图如图 4-7-1 所示。 图 4-10-1 具有四种信号灯的交通灯控制器系统框图 4. 10. 2 设计提示 此设计问题可分成定时模块、主控电路、译码驱动电路和扫描显示几部分。 定时模块中设置 40 秒、 30 秒、 15 秒、 5 秒计时电 路,倒计时可以用减法计数器实现。 状态表如表 4-10-1 所示。 状态 主干道 支干道 时间 /S S 0 S 1 S 2 S 3 S 4 S 5 S 6 S 7 绿灯亮,允许通行 黄灯亮,停车 左拐灯亮,允许左行 黄灯亮,停车 红灯亮,禁止通行 红灯亮,禁止通行 红灯亮,禁止通行 红灯亮,禁止通行 红灯亮,禁止通行 红灯亮,禁止通行 红灯亮,禁止通行 红灯亮,禁止通行 绿灯亮,允许通行 黄灯亮,停车 左拐灯亮,允许左行 黄灯亮,停车 40 5 15 5 30 5 15 5 由于主干道和支干道红灯亮的时间分别为 55 秒和 65 秒, 所 以 , 还要设置 55 秒、 65 秒 倒计时显示电路。 仿照例 1-4-3,可以进行主控电路和译码显示电路的设计,注意这里的状态数为 8 个, 要用三个 JK 触发器才能完成主控时序部分的设计。 设置主干道红灯显示信号 LA1,黄灯显示信号 LA2,绿灯显示信号 LA3,左拐灯信号 52 LA4; 支干道红灯显示信号 LB1, 黄 灯 显示信号 LB2, 绿灯显示信号 LB3, 左 拐灯信号 LB4。 设置系统使能信号 EN,时钟信号 clk。 硬件系统示意图如图 4-10-2 所示。Verilog HDL 参考代码见附录七。 图 4-10-2 具有四种信号灯的交通灯控制器硬件系统示意图 4.11 出租车自动计费器 4.11.1 设计要求 设计一个出租车自动计费器,计费包括起步价、行车里程计费、等待时间计费三部分, 用三位数码管显示总金额, 最大值为 99.9 元。 起步价为 5.0 元 , 3 公里 之内按起步价计费, 超过 3 公里, 每公里增加 1 元 , 等待时间单价为每 1 分 钟 0.1 元 。 用两位数码管显示总里程, 最大值为 99 公里,用两位数码管显示等待时间,最大值为 99 分钟。 系统框图如图 4-11-1 所示。 4.11.2 设计提示 此设计问题可分为主控模块、 里程计数模块、 等待时间计数模块、 计费模块和扫描显示 模块。 在行车里程计费模块中, 将行驶的里程数转换为与之成正比的脉冲个数, 实际情况下可 以用干簧继电器做里程传感器, 安装在与汽车相连接的蜗轮变速器上的磁铁使干簧继电器在 汽车每前进十米闭合一次, 即输出一个脉冲, 则每行驶 1公里, 输出 100 个脉冲 。 所以设计 时,以 clk1 模拟传感器输出的脉冲,100 个 clk1 模拟 1 公 里 路程,3 公里 之内为起步价, 即 300 个 clk1 之内为起步价,以后每公里增加 1 元,即每十个 clk1 增加 0.1 元。 53 图 4-11-1 出租车自动计费器系统框图 在等待时间计数模块中, 等待时间计费也变换成脉冲个数计算, 以秒脉冲 clk 作 为 时 钟 输入,则每 1 分 钟 0.1 元,即每 60 个秒脉冲增加 0.1 元。 在主控模块中,设置行驶状态输入信号 drive, 行驶状态显示信号 run, 起步价预先固 定设置在电路中, 由 drive 信号异步 置数至计费模块, 同时使系统显示当前为行驶状态 run, 里程计数工作,到 3 公里后,每十个 clk1 脉冲使计费增加 0.1 元,计费显示在数码管上。 设置刹车信号 break,等 待状态显示信号 pause, 由 break 信 号使系统显示当前状态为 pause,等待时间计数模块工作,每 1 分钟计费增加 0.1 元。 系统硬件示意图如图 4-11-2 所示。Verilog HDL 参考代码见附录八。 图 4-11-2 出租车自动计费器硬件系统框图 4.12 自动售邮票机 4.12.1 设计要求 54 设计一个自动售邮票机,用开关电平信号模拟投币过程,每次投一枚硬币,但可以 连 续投入数枚硬币。 机器能自动识别硬币金额, 最大为 1 元, 最小 为 5 角。 设定 票价为 2.5 元, 每次售一张票。 购票时 先投 入硬币 ,当 投入的 硬币 总金额 达到 或超过 票的 面值时 ,机 器发出 指示 , 这 时可以按取票键取出票。 如果所投硬币超过票的面值则会提示找零钱, 取完票以后按找零键 则可以取出零钱。 系统框图如图 4-12-1 所示。 图 4-12-1 自动售邮票机系统框图 4.12.2 设计提示 此设计问题可分为币值输入相加模块、主控模块和币值显示几部分。 在币值输入相加模块中, 用两个开关电平输入按钮分别代表 2 种硬币输入 , one 表示 1 元, half 表示 5 角, 每 按一次, 表示投入一枚硬币。 设置 5 角和 1 元输 入计数电路, 并设置 控制电路,由 5 角和 1 元输入的次数控制十进制加法器的加数 A 和被加数 B,使 输入的币 值实时相加。 用两位数码管显示当前的投入币值, 显示的币值位×元×角, 输入控制模块状 态表如表 4-12-1 所列。 在主控模块中设置一个复位信号 reset, 用于中止 交易 (系统复位) 。 设 置 一个取票信号 ok, 一个邮 票给出信号 tout, tout 接 LED 显示, 灯亮则表示可以取票, 否则取票键无效, 按 ok 键取票, 灯灭。设置一个取零钱信号 charge,一个零钱输出信号 mout, mout 接 LED 显 示,灯亮则表示有零钱,按 charge 取零钱,灯灭。 主控模块中是一个状态机, 在第三章中对此种状态机已经进行了详细的描述, 在表 3-7-1 所列的状态种,当币值等于 2.5 元时 ,有邮票给出,不找零钱;当币值为 3.0 元时, 有邮票 给出,找零钱;其余情况下,既无票给出也不找零钱。 表 4-12-1 币值输入相加模块状态表 55 5 角输入 5角计数器输出 加数 1元输入 1元计数器输出 被加数 half A one B 0 ↑ ↑ ↑ ↑ ↑ 0 1 2 3 4 5 0. 0 1. 0 1. 5 2. 0 2. 5 0 ↑ ↑ ↑ 0 1 2 3 0. 0 1. 0 2. 0 3. 0 0. 5 系统硬件示意图如图 4-12-2 所示。Verilog HDL 参考代码见附录九。 图 4-12-2 自动售邮票机硬件系统框图 4.13 电梯控制器 4.13.1 设计要求 设计一个八层楼房自动电梯控制器, 用八个 LED显示电梯行进过程, 并有数码管显示电 56 梯当前所 在 楼层位置 , 在每层电 梯 入口处设 有 请求按钮 开 关,请求 按 钮按下则 相 应 楼 层 的 LED 亮。 用 CLK 脉冲 控制电梯运动, 每来一个 CLK 脉冲 电梯升 (降) 一层。 电梯到达有请求的 楼层后, 该层次的指示灯灭, 电梯门打开 (开门指示灯亮) ,开 门 5 秒 后 , 电梯门自动关 闭 , 电梯继续运行。 控制电路应能记忆所有楼层请求信号, 并按如下运行规则依次相应: 运行过程中先响应 最早的请求, 再响应后续的请求。 如果无请求则停留当前层。 如果有两个同时请求信号, 则 判断请求信号离当偍层的距离, 距离近请求的先响应, 再响应较远的请求。 每个请求信号保 留至执行后清除。 系统框图如图 4-13-1 所示。 图 4-13-1 电梯控制器系统框图 4.13.2 设计提示 此设计问题可分为请求信号输入模块、 主控模块、 移位寄存显示模块和楼层显示几部分。 在请求信号输入模块中,设置八个开关电平信号 d1,d2,d3,d4,d5,d6,d7,d8 表示 8 个楼层 的请求信号,每次最多允许两个信号同时请求。 在主控模块中设置开门指示信号 door, door=1 为开门状态; door=0 为关门状态。 在移位寄存显示模块中设置八个 LED 显示信号 o1,o2,o3,o4,o5,o6,o7,o8,表 示当前所在 楼层及发出请求信号的楼层。 用移位寄存模块的 up 表 示电梯上行 (右移) , down 表示电梯下行 (左移) , 电梯初始状 态是处在一层,当前楼层经主控模块送数码管显示。 当前楼层信号 A 和请求信号 B 在主 控模块中进行实时比较, 当 A<B 时 , 主控模块的输 出使移位寄存模块的 UP 信号有效 ,电梯上行,直到 A=B,电梯开门( door=1) 5 秒,若 A>B, 则移位寄存模块的 down 信号有 效, 电梯下行, 直到 A=B, 电梯开门 5 秒, 如此反复。 若没有请求信号输入,则电梯停在当前楼层不动。 若同时有两个请求信号输入,主控模块应能将两个请求信号分别与当前楼层信号比较, 57 使电梯先去距离较近的楼层。 系统硬件示意图如图 4-13-2 所示。Verilog HDL 参考代码见附录十。 图 4-13-2 电梯控制器硬件系统示意图 参考文献 1 黄正瑾 .在系统编程技术及其应用 .南京:东南大学出版社, 1997 2 彭介华 .电子技术课程设计指导 .北京:高等教育出版社, 1997 3 李国丽,朱维勇 .电子技术实验指导书 .合肥:中国科技大学出版社, 2000 4 潘松,黄继业 .EDA 技术实用教程 .北京:科学出版社, 2002 5 郑家龙,王小海,章安元 .集成电子技术基础教程 .北京:高等教育出版社, 2002 6 宋万杰,罗丰,吴顺君 .CPLD 技术及其应用 .西安:西安电子科技大学出版社, 1999 7 张昌凡, 龙 永红, 彭涛 .可编程逻辑器件及 VHDL 设计技术 .广州: 华南工学院出版社, 2001 8 卢杰,赖毅 .VHDL 与数字电路设计 .北京:科学出版社, 2001 9 王金明,杨吉斌 .数字系统设计与 Verilog HDL.北京:电子工业出版社, 2002 10 阎石 .数字电子技术基础 .北京:高等教育出版社, 1998 11 IEEE design automation standard committee. IEEE Standard Hardard Description Language Based on Verilog? Hardware Description Language. IEEE (The Institute of Electrical and Electronics Engineers,Inc.) 1996 12 Verilog-XL Reference. Cadence Design System. San Jose,1998 13 Bob Zeidman, Verilog Designer’s Library, Prentice Hall PTR, 1999 14 Dougias J.Smith, HDL chip Design, Doone Publication, 1998 15 J.Bhasker, Verilog HDL Synthesis A Practical Primer, Star Galaxy Publishing, July,2000 16 高明伦 .Verilog 与 PC 机接口电路设计 . 合肥:安徽科学技术出版社, 2003 17 张明 .Verilog HDL 实用教程 .成都:电子科技大学出版社, 1999 18 栾铭,高 明伦 .工业控制芯片中状态机的描述方法 .合肥:第三届全球智能与自动化大会, 2000 19 J.Bhasker 著,徐振林等译 .Verilog HDL 硬件描述语言 .北京:机械工业出版社, 2000 58 20 刘明业,将敬旗,刁岚松等译 .硬件描述语言 Verilog.北京:清华大学出版社, 2001 21.侯伯亨, 顾新 .VHDL 硬件描述语言与数字逻辑电路设计 .西安: 西安电子科技大学出版社, 1999 //多功能数字钟 附录一 多功能数字钟主控电路 verilog HDL 参考代码 /*信号定义: clk:时钟信号,频率 1Hz; clk_1k:产生闹铃声、报时音的时钟信号,频率 1024Hz; mode:功能控制信号;为 0:计时功能; 为 1:闹铃功能 ; 为 2:手动校对功能; turn:在手动 校对时,选择是调整时间,还是分钟,若长时间按住该键,可使秒信号清零, 用于精确调时; change:手动调整时,每按一次,计数器加 1,按住不放,则连续快速加 1; hour, min, sec:时,分,秒显示信号; alert:扬声器驱动信号; LD_alert:闹铃功能设置显示信号; LD_hour:小时调整显示信号; LD_min:分钟调整显示信号。 */ module clock(clk,clk_1k,mode,change,turn,alert,hour,min,sec,LD_alert,LD_hour,LD_min); input clk,clk_1k,mode,change,turn; output alert,LD_alert,LD_hour,LD_min; output[7:0] hour,min,sec; reg[7:0]hour,min,sec,hour1,min1,sec1,ahour,amin; reg[1:0]m,fm,num1,num2,num3,num4; reg[1:0]loop1,loop2,loop3,loop4,sound; reg LD_hour,LD_min; reg clk_1Hz,clk_2Hz,minclk,hclk; reg alert1,alert2,ear; reg count1,count2,counta,countb; reg cn1,cn2; wire ct1,ct2,cta,ctb,m_clk,h_clk; always @(posedge clk) begin clk_2Hz<=~clk_2Hz; if(sound==3)begin sound<=0;ear<=1;end else begin sound<=sound+1;ear<=0;end end 59 always @(posedge clk_2Hz) //由 4Hz 的输入时钟产生 1Hz 的时基信号 clk_1Hz<=~clk_1Hz; always @(posedge mode) //mode信号控制系统的功能转换 begin if(m==2)m<=0;else m<=m+1;end always @(posedge turn) fm<=~fm; always //产生 count1,count2,counta,countb 四个信号 begin case(m) 2:begin if(fm) begin count1<=change;{LD_min,LD_hour}<=2;end else begin counta<=change;{LD_min,LD_hour}<=1;end {count2,countb}<=0; end 1:begin if(fm) begin count2<=change;{LD_min,LD_hour}<=2;end else begin countb<=change;{LD_min,LD_hour}<=1;end {count1,counta}<=2'b00; end default:{count1,count2,counta,countb,LD_min,LD_hour}<=0; endcase end always @(negedge clk) //如果长时间按下“ change”键,则生成“ num1”信号用于连续快速加 1 if(count2) begin if(loop1==3)num1<=1; else begin loop1<=loop1+1;num1<=0;end end else begin loop1<=0;num1<=0;end always @(negedge clk) if(countb) begin if(loop2==3)num2<=1; else begin loop2<=loop2+1;num2<=0;end end else begin loop2<=0;num2<=0;end always @(negedge clk) if(count1) begin 60 if(loop3==3)num3<=1; else begin loop3<=loop3+1;num3<=0;end end else begin loop3<=0;num3<=0;end always @(negedge clk) if(counta) begin if(loop4==3)num4<=1; else begin loop4<=loop4+1;num4<=0;end end else begin loop4<=0;num4<=0;end assign ct1=(num3&clk)|(!num3&m_clk); assign ct2=(num1&clk)|(!num1&count2); assign cta=(num4&clk)|(!num4&h_clk); assign ctb=(num2&clk)|(!num2&countb); always @(posedge clk_1Hz) //秒计时和秒调整 if(!(sec1^8'h59)|turn&(!m)) begin sec1<=0;if(!(turn&(!m)))minclk<=1; end else begin if(sec1[3:0]==4'b1001) begin sec1[3:0]<=4'b0000;sec1[7:4]<=sec1[7:4]+1;end else sec1[3:0]<=sec1[3:0]+1;minclk<=0; end assign m_clk=minclk||count1; always @(posedge ct1) //分计时和分调整 begin if(min1==8'h59)begin min1<=0;hclk<=1;end else begin if(min1[3:0]==9) begin min1[3:0]<=0;min1[7:4]<=min1[7:4]+1;end else min1[3:0]<=min1[3:0]+1;hclk<=0; end end assign h_clk=hclk||counta; always @(posedge cta) //小时计时和小时调整 if(hour1==8'h23)hour1<=0; else if(hour1[3:0]==9) 61 begin hour1[7:4]<=hour1[7:4]+1;hour1[3:0]<=0;end else hour1[3:0]<=hour1[3:0]+1; always @(posedge ct2) //定时功能中的分钟调节 if(amin==8'h59)amin<=0; else if(amin[3:0]==9) begin amin[3:0]<=0;amin[7:4]<=amin[7:4]+1;end else amin[3:0]<=amin[3:0]+1; always @(posedge ctb) //定时功能中的小时调节 if(ahour==8'h23)ahour<=0; else if(ahour[3:0]==9) begin ahour[3:0]<=0;ahour[7:4]<=ahour[7:4]+1;end else ahour[3:0]<=ahour[3:0]+1; always //闹铃功能 if((min1==amin)&&(hour1==ahour)&&(amin|ahour)&&(!change)) if(sec1<8'h20)alert1<=1; //控制闹铃时间长短 else alert1<=0; else alert1<=0; always //时,分,秒的显示控制 case(m) 3'b00:begin hour<=hour1;min<=min1;sec<=sec1;end //计时状态 3'b01:begin hour<=ahour;min<=amin;sec<=8'hzz;end //定时状态 3'b10:begin hour<=hour1;min<=min1;sec<=8'hzz;end //校时状态 endcase assign LD_alert=(ahour|amin)?1:0; assign alert=((alert1)?clk_1k&clk:0)|alert2; always begin if((min1==8'h59)&&(sec1>8'h54)||(!(min1|sec1))) if(sec1>8'h54)alert2<=ear&clk_1k; else alert2<=!ear&clk_1k; else alert2<=0; end endmodule //动态扫描显示模块 module sel(in1,in2,in3,in4,in5,in6,in7,in8,clk,ms1,ms2,ms3,ms4,ms5,ms6,ms7,ms8,a,b,c,d,e,f,g); input clk; input [3:0] in1,in2,in3,in4,in5,in6,in7,in8; 62 output ms1,ms2,ms3,ms4,ms5,ms6,ms7,ms8,a,b,c,d,e,f,g; reg ms1,ms2,ms3,ms4,ms5,ms6,ms7,ms8,a,b,c,d,e,f,g; reg [3:0] temp,flag; always@(posedge clk) begin {ms1,ms2,ms3,ms4,ms5,ms6,ms7,ms8}=8'b00000000; flag=flag+1; case (flag) 0:begin temp=in1;ms1=1;end 1:begin temp=in2;ms2=1;end 2:begin temp=in3;ms3=1;end 3:begin temp=in4;ms4=1;end 4:begin temp=in5;ms5=1;end 5:begin temp=in6;ms6=1;end 6:begin temp=in7;ms7=1;end 7:begin temp=in8;ms8=1;end endcase case(temp) 4'd0:{a,b,c,d,e,f,g}=7'b1111110; 4'd1:{a,b,c,d,e,f,g}=7'b0110000; 4'd2:{a,b,c,d,e,f,g}=7'b1101101; 4'd3:{a,b,c,d,e,f,g}=7'b1111001; 4'd4:{a,b,c,d,e,f,g}=7'b0110011; 4'd5:{a,b,c,d,e,f,g}=7'b1011011; 4'd6:{a,b,c,d,e,f,g}=7'b1011111; 4'd7:{a,b,c,d,e,f,g}=7'b1110000; 4'd8:{a,b,c,d,e,f,g}=7'b1111111; 4'd9:{a,b,c,d,e,f,g}=7'b1111011; default:{a,b,c,d,e,f,g}=7'b1111110; endcase end endmodule 数字钟系统顶层 gdf 文件如附录一图所示。 63 附录一图 数字钟顶层 gdf 文件 附录二 数字式竞赛抢答器主控电路 verilog HDL 参考代码 //抢答信号锁存显示电路 /*信号定义: clk:时钟信号; in1、 in2、 in3、 in4:抢答按钮信号; o1、 o2、 o3、 o4:抢答 LED 显示信号; judge:裁判员抢答开始信号; ju:系统复位信号。 */ module sel(clk,in1,in2,in3,in4,judge,o1,o2,o3,o4,o5,ju); input clk,in1,in2,in3,in4,judge; output o1,o2,o3,o4,o5,ju; reg o1,o2,o3,o4,o5,block,ju; reg[7:0] count; always@(posedge clk) if(ju) begin {o1,o2,o3,o4,o5,block}<=6'b000000; count<=0; end // else begin if(in1) begin if(!block) begin o1<=1; block<=1; count<=1; 64 end end // else if(in2) begin if(!block) begin o2<=1; block<=1; count<=1; end end // else if(in3) begin if(!block) begin o3<=1; block<=1; count<=1; end end // else if(in4) begin if(!block) begin o4<=1; block<=1; count<=1; end end // if(count!=0) begin if(count==8'b00010010) begin count<=0; o5<=1; end else begin count<=count+1; end 65 end end always@(posedge judge) ju=!ju; endmodule //计分显示电路 /*信号定义: clk:时钟信号; c1、 c2、 c3、 c4:抢答组别输入信号; add:加分按钮; min:减分按钮; reset:初始分( 10 分)设置信号; count1l、 count1h:第一组得分输出; count2l、 count2h:第二组得分输出; count3l、 count3h:第三组得分输出; count4l、 count4h:第四组得分输出。 */ module count(clk,c1,c2,c3,c4,add,min,reset,count1l,count1h,count2l,count2h,count3l,count3h,count4l,cou nt4h); input clk,c1,c2,c3,c4,add,min,reset; output [3:0] count1l,count1h,count2l,count2h,count3l,count3h,count4l,count4h; reg [3:0] count1h,count1l,count2h,count2l,count3h,count3l,count4h,count4l; always@(posedge clk) if(reset) {count1h,count1l,count2h,count2l,count3h,count3l,count4h,count4l}=32'h10101010; else if(add) begin // if(c1) begin if(count1l==9) begin count1l=0; if(count1h==9) count1h=0; else count1h=count1h+1; end else count1l=count1l+1; end // // 66 if(c2) begin if(count2l==9) begin count2l=0; if(count2h==9) count2h=0; else count2h=count2h+1; end else count2l=count2l+1; end // // if(c3) begin if(count3l==9) begin count3l=0; if(count3h==9) count3h=0; else count3h=count3h+1; end else count3l=count3l+1; end // // if(c4) begin if(count4l==9) begin count4l=0; if(count4h==9) count4h=0; else count4h=count4h+1; end else count4l=count4l+1; end // 67 end else if(min) begin // if(c1) begin if(count1l!=4'b0000) count1l=count1l-1; else if(count1h!=4'b0000) begin count1h=count1h-1; count1l=9; end end // if(c2) begin if(count2l!=4'b0000) count2l=count2l-1; else if(count2h!=4'b0000) begin count2h=count2h-1; count2l=9; end end // if(c3) begin if(count3l!=4'b0000) count3l=count3l-1; else if(count3h!=4'b0000) begin count3h=count3h-1; count3l=9; end end // if(c4) begin else if(count4h!=4'b0000) if(count4l!=4'b0000) count4l=count4l-1; begin count4h=count4h-1; 68 count4l=9; // 数字式竞赛抢答器顶层 gdf 文件如附录二图所示。 end end end endmodule 附录二图 数字式竞赛抢答器顶层 gdf 文件 69 附录三 洗衣机控制器主控电路 verilog HDL 参考代码 //洗衣机控制器 /*信号定义: d0,d1,….d9:数据开关信号,分别代表 0, 1, ……9; start:开始信号; reset:复位信号; t1l,t1h,t2l,t2h:预置时间; forward:正转状态输出显示; back:反转状态输出显示; stop:停止状态输出显示; sound:停机音响输出; */ module wash(clk,d1,d2,d3,d4,d5,d6,d7,d8,d9,d0,start,reset,t1l,t1h,t2l,t2h,forward,back,stop,sound); input d1,d2,d3,d4,d5,d6,d7,d8,d9,d0,clk,reset,start; output forward,stop,back,sound; output[3:0] t1h,t1l,t2h,t2l; reg[3:0] t1l,t1h,t2l,t2h; reg forward,stop,back,sound,flag; always@(posedge clk) //预置时间 if({d1,d2,d3,d4,d5,d6,d7,d8,d9,d0}!=10'b0000000000) // begin t1h<=t1l; if(d0) t1l<=0; if(d1) t1l<=1; if(d2) 70 t1l<=2; if(d3) t1l<=3; if(d4) t1l<=4; if(d5) t1l<=5; if(d6) t1l<=6; if(d7) t1l<=7; if(d8) t1l<=8; if(d9) t1l<=9; end // else begin if(start&&{forward,stop,back}==3'b000) {forward,stop,back}<=3'b100; else if(reset) {forward,stop,back}<=3'b010; begin t2l<=0; {forward,stop,back,sound,flag,t2h,t2l,t1h,t1l}<=21'b000000000000000000000; else if({forward,stop,back}!=3'b000) begin //************ case({forward,stop,back}) 3'b100: //// begin if(t2h==2) begin {t2h,t2l}<=8'h00; end else if(t2l==9) begin t2h<=t2h+1; end else t2l<=t2l+1; 71 end end //// 3'b010: begin if(t2l==9) begin t2l<=4'b0000; if(flag) begin {forward,stop,back}<=3'b100; /// if(t1l==0) begin if(t1h!=0) begin t1h<=t1h-1; t1l<=9; end end else t1l<=t1l-1; /// end else {forward,stop,back}<=3'b001; flag<=!flag; end else t2l<=t2l+1; end // 3'b001: //************* begin if(t2h==2) begin {t2h,t2l}<=8'h00; {forward,stop,back}<=3'b010; end else // begin if(t2l==9) 72 begin t2l<=0; t2h<=t2h+1; end else t2l<=t2l+1; end end //************ endcase //************* if({t1h,t1l}==8'b00000000) {forward,stop,back,sound,flag,t2h,t2l}<=13'b0001000000000; // end end // endmodule 洗衣机控制器顶层 gdf 文件如附录三图所示。 附录三图 洗衣机控制器顶层 gdf 文件 73 附录四 密码锁主控电路 verilog HDL 参考代码 //密码锁 /*信号定义: n0,n1,….n9:数据开关信号,分别代表 0, 1, ……9; back:删除信号; cheak:密码检验信号; set:密码确认信号; close:关锁信号; lock:密码锁状态显示信号; num1, num2, num3, num4:密码输出显示信号,每一个数都是四位二进制数。 */ module code(n0,n1,n2,n3,n4,n5,n6,n7,n8,n9,back,check,set,close,lock,num1,num2,num3,num4,clk); input n0,n1,n2,n3,n4,n5,n6,n7,n8,n9; input back,check,set,close,clk; output lock; output [3:0] num1,num2,num3,num4; reg lock; reg [3:0] num1,num2,num3,num4,temp; reg[15:0] code; always@(posedge clk) begin //密码输入显示控制 if({n0,n1,n2,n3,n4,n5,n6,n7,n8,n9}!=10'b0000000000) begin case({n9,n8,n7,n6,n5,n4,n3,n2,n1,n0}) 10'b0000000001:temp=1'd0; 10'b0000000010:temp=1'd1; 10'b0000000100:temp=1'd2; 10'b0000001000:temp=1'd3; 10'b0000010000:temp=1'd4; 74 10'b0000100000:temp=1'd5; 10'b0001000000:temp=1'd6; 10'b0010000000:temp=1'd7; 10'b0100000000:temp=1'd8; 10'b1000000000:temp=1'd9; endcase num4<=num3; num3<=num2; num2<=num1; num1<=temp; end else if(back) //密码删除控制 begin num1<=num2; num2<=num3; num3<=num4; num4<=1'd0; end end always@(posedge clk) //开锁判断 begin if(lock==0&&check) begin if(code=={num4,num3,num2,num1}) lock<=1; else if({num4,num3,num2,num1}==16'b0000000000000111) lock<=1; end else if(lock==1&&close) lock<=0; end always@(posedge clk) begin if(lock==1&&set) code<={num4,num3,num2,num1}; end endmodule 75 密码锁顶层 gdf 文件如附录四图所示。 附录四图 密码锁顶层 gdf 文件 附件五 乘法器主控电路 Verilog HDL 参考代码 //乘法器 /*信号定义 clk:时钟信号; mul:相乘信号; d0、 d1、 d2……d9:数据开关信号,分别代表 0, 1, ……9; o1、 o2、 o3、 o4:乘积输出信号。 */ module mul(clk,com,d0,d1,d2,d3,d4,d5,d6,d7,d8,d9,o1,o2,o3,o4); input clk,com,d0,d1,d2,d3,d4,d5,d6,d7,d8,d9; output [3:0] o1,o2,o3,o4; reg [3:0] o1,o2,o3,o4,mul1h,mul1l,mul2h,mul2l; reg flag,hl; always@(posedge clk) begin // if({d0,d1,d2,d3,d4,d5,d6,d7,d8,d9}!=10'b0000000000) begin o4<=o3; o3<=o2; o2<=o1; case({d0,d1,d2,d3,d4,d5,d6,d7,d8,d9}) 10'b1000000000:o1<=0; 10'b0100000000:o1<=1; 10'b0010000000:o1<=2; 76 10'b0001000000:o1<=3; 10'b0000100000:o1<=4; 10'b0000010000:o1<=5; 10'b0000001000:o1<=6; 10'b0000000100:o1<=7; 10'b0000000010:o1<=8; 10'b0000000001:o1<=9; endcase end // if(com) begin mul1h<=o2; mul1l<=o1; mul2h<=o4; mul2l<=o3; flag<=1; hl<=1; {o1,o2,o3,o4}<=16'h0000; end // if(flag) begin if({mul2h,mul2l}==8'b00000000) begin flag<=0; end else if(hl) begin // if(mul1l+o1>9||mul1l>=8&&o1>=8||mul1l==9&&o1>=7||o1==9&&mul1l>=7) begin if(mul1l>=8&&o1>=8||mul1l==9&&o1>=7||o1==9&&mul1l>=7) begin if(mul1l==8) o1<=o1-2; else if(mul1l==9) o1<=o1-1; else o1<=o1-3; end else o1<=mul1l+o1-10; if(o2==9) 77 begin o2<=0; if(o3==9) begin o3<=0; o4<=o4+1; end else o3<=o3+1; end else o2<=o2+1; end else o1<=o1+mul1l; hl<=0; // end else begin // if(mul1h+o2>9||mul1h>=8&&o2>=8||mul1h==9&&o1>=7||o1==9&&mul1h>=7) begin if(mul1h>=8&&o2>=8||mul1h==9&&o1>=7||o1==9&&mul1h>=7) begin if(mul1h==8) o2<=o2-2; else if(mul1h==9) o2<=o2-1; else o2<=o2-3; end else o2<=o2+mul1h-10; if(o3==9) begin o3<=0; o4<=o4+1; end else o3<=o3+1; end else o2<=mul1h+o2; 78 /// hl<=1; if(mul2l==0) begin mul2l<=9; mul2h<=mul2h-1; end else mul2l<=mul2l-1; /// // end end // end endmodule 乘法器顶层 gdf 文件如附录五图所示。 附录五图 乘法器顶层 gdf 文件 79 附录六 乒乓球比赛游戏机主控电路 verilog HDL 参考代码 //乒乓球比赛游戏机 /*信号定义: in1、 in2:甲乙双方击球信号; o1,02,……o8:乒乓球轨迹模拟显示信号; right1,right2:甲乙双方发球权拥有显示信号; score1,score2:甲乙双方得分显示; clk:时钟信号; reset:裁判员复位信号。 */ module pp(in1,clk,reset,in2,o1,o2,o3,o4,o5,o6,o7,o8,right1,right2,score1l,score1h,score2l,score2h); input in1,in2,clk,reset; output [3:0] score1l,score1h,score2l,score2h; output o1,o2,o3,o4,o5,o6,o7,o8,right1,right2; reg[2:0] count; reg [3:0] score1l,score1h,score2l,score2h; reg o0,o1,o2,o3,o4,o5,o6,o7,o8,o9,right1,right2,ldir,rdir,temp; always@(posedge clk) begin if(o1&&in1) begin {o1,o2,o3,o4,o5,o6,o7,o8}<=8'b01000000; rdir<=1; 80 ldir<=0; end if(reset==1) begin {score1h,score1l}<=8'b00000000; {score2h,score2l}<=8'b00000000; count=0; end else if(o8&&in2) {o1,o2,o3,o4,o5,o6,o7,o8}<=8'b00000010; rdir<=0; ldir<=1; end else if(o9||o0) // begin rdir<=0; ldir<=0; if(count<4) begin count=count+1; else if(count==4) begin count=0; temp=right2; right2=right1; right1=temp; end if(right1) {o0,o1,o2,o3,o4,o5,o6,o7,o8,o9}<=10'b0100000000; else {o0,o1,o2,o3,o4,o5,o6,o7,o8,o9}<=10'b0000000010; if(o9)begin if(score1l[0]&&score1l[3]) begin score1l<=4'b0000;score1h<=4'b0001;end else if(score1h[0]&&score1l[0]) begin {score1h,score1l}<=8'b00000000; {score2h,score2l}<=8'b00000000; count=0; end else score1l<=score1l+1; end else if(o0) begin if(score2l[0]&&score2l[3]) 81 begin score2l<=4'b0000;score2h<=4'b0001;end else if(score2h[0]&&score2l[0]) begin {score1h,score1l}<=8'b00000000; {score2h,score2l}<=8'b00000000; count=0; end else score2l<=score2l+1; end end // else if(rdir&&!o1) {o0,o1,o2,o3,o4,o5,o6,o7,o8,o9}<={o0,o1,o2,o3,o4,o5,o6,o7,o8,o9}>>1; else if(ldir&&!o8) {o0,o1,o2,o3,o4,o5,o6,o7,o8,o9}<={o0,o1,o2,o3,o4,o5,o6,o7,o8,o9}<<1; if({o0,o1,o2,o3,o4,o5,o6,o7,o8,o9}==10'b0000000000) begin right1=1; {o0,o1,o2,o3,o4,o5,o6,o7,o8,o9}<=10'b0100000000; end end endmodule 乒乓球比赛游戏机顶层 gdf 文件如附录六图所示。 附录六图 乒乓球比赛游戏机顶层 gdf 文件 82 附录七 具有四种信号等的交通灯控制器主控电路 Verilog HDL 参考代码 //交通灯控制器 /*信号定义: clk:系统时钟信号; en:系统使能信号; lampa1:主干道红灯信号; lampa2:主干道黄灯信号; lampa3:主干道绿灯信号; lampa4:主干道左拐信号; lampb1:支干道红灯信号; lampb2:支干道黄灯信号; lampb3:支干道绿灯信号; lampb4:主支干道左拐信号; acounth,acountl:主干道计时输出; bcounth,bcountl:支干道计时输出。 */ module traffic(clk,en,lampa1,lampa2,lampa3,lampa4,lampb1,lampb2,lampb3,lampb4,acounth,acountl,bco unth,bcountl); output[3:0] acounth,acountl,bcounth,bcountl; output lampa1,lampa2,lampa3,lampa4,lampb1,lampb2,lampb3,lampb4; input clk,en; reg tempa,tempb; reg[2:0] counta,countb; reg[7:0] numa,numb; 83 reg[7:0] ared,ayellow,agreen,aleft,bred,byellow,bgreen,bleft; reg lampa1,lampa2,lampa3,lampa4,lampb1,lampb2,lampb3,lampb4; always @(en) if(!en) begin ared<=8'b01010101; ayellow<=8'b00000101; agreen<=8'b01000000; aleft<=8'b00010101; bred<=8'b01100101; byellow<=8'b00000101; bleft<=8'b00010101; bgreen<=8'b00110000; end assign {bcounth,bcountl}=numb; always@(posedge clk) begin if(en) begin if(!tempa) begin assign {acounth,acountl}=numa; tempa<=1; case(counta) 0:begin numa<=agreen;{lampa1,lampa2,lampa3,lampa4}<=2;counta<=1;end 1:begin numa<=ayellow;{lampa1,lampa2,lampa3,lampa4}<=4;counta<=2;end 2:begin numa<=aleft;{lampa1,lampa2,lampa3,lampa4}<=1;counta<=3;end 3:begin numa<=ayellow;{lampa1,lampa2,lampa3,lampa4}<=4;counta<=4;end 4:begin numa<=ared;{lampa1,lampa2,lampa3,lampa4}<=8;counta<=0;end default:{lampa1,lampa2,lampa3,lampa4}<=8; endcase end else begin if(numa>1) if(numa[3:0]==0) begin numa[3:0]<=4'b1001; numa[7:4]<=numa[7:4]-1; end else numa[3:0]<=numa[3:0]-1; if(numa==2)tempa<=0; end end 84 else begin {lampa1,lampa2,lampa3,lampa4}<=4'b1000; counta<=0;tempa<=0; end end always@(posedge clk) begin if(en) begin if(!tempb) begin tempb<=1; case(countb) 0:begin numb<=bred;{lampb1,lampb2,lampb3,lampb4}<=8;countb<=1;end 1:begin numb<=bgreen;{lampb1,lampb2,lampb3,lampb4}<=2;countb<=2;end 2:begin numb<=byellow;{lampb1,lampb2,lampb3,lampb4}<=4;countb<=3;end 3:begin numb<=bleft;{lampb1,lampb2,lampb3,lampb4}<=1;countb<=4;end 4:begin numb<=byellow;{lampb1,lampb2,lampb3,lampb4}<=4;countb<=0;end default: {lampb1,lampb2,lampb3,lampb4}<=8; endcase end else begin if(!numb[3:0])begin numb[3:0]<=9; numb[7:4]<=numb[7:4]-1; end else numb[3:0]<=numb[3:0]-1; if(numb==2)tempb<=0; end end else begin {lampb1,lampb2,lampb3,lampb4}<=4'b1000; tempb<=0; countb<=0; end end endmodule 具有四种信号等的交通灯控制器顶层 gdf 文件如附录七图所示。 85 附录七图 具有四种信号等的交通灯控制器顶层 gdf 文件 附录八 出租车自动计费器主控电路 Verilog HDL 参考代码 //出租车自动计费器 /*信号定义: clk: 模拟里 程数和等待时间的时钟信号: 每 100 个 clk 模拟 1 公里, 每 60 个 clk 为 1 分钟; reset:复位信号,将计费设置成初值 5 元,里程和等待时间设置成初值 0; flag:状态标志输出: flag=0 为行驶状态, flag=1 为停车等待状态; c1,c2,c3,c4:计费输出显示信号; m2,m1:等待时间输出显示信号; w2,w1:行驶里程数输出显示信号。 */ module ntaxi(clk,reset,flag,ifw,c1,c2,c3,c4,m2,m1,w2,w1); input clk,reset,flag; output [3:0] c1,c2,c3,c4,m1,m2,w1,w2; output ifw; reg ifw; reg[3:0] t1,t2,t3,t4,c1,c2,c3,c4,m1,m2,w1,w2,tempf; reg[6:0] tempm; reg[5:0] tempt; // always@(posedge clk) if(reset) begin {c4,c3,c2,c1}<=16'h0050; tempm<=7'b0000000; tempt<=6'b000000; 86 tempf<=4'b0000; {t1,t2,t3,t4}<=16'h0200; {c1,c2,c3,c4,m1,m2,w1,w2}<=32'h05000000; end else begin //*** if(ifw) begin if(tempt<60) tempt<=tempt+1; end else begin if(tempf<10) tempf<=tempf+1; if(tempm<100) tempm<=tempm+1; end //*** if(ifw&&tempt==59) begin tempt<=6'b000000; if(w1==9) begin w1<=0; if(w2==9) w2<=0; else w2<=w2+1; end else w1<=w1+1; end // if(!ifw&&tempm==99) begin tempm<=7'b0000000; if(m1==9) begin m1<=0; if(m2==9) m2<=0; else 87 m2<=m2+1; end else m1<=m1+1; end // if(!ifw&&tempf==9||ifw&&tempt==59) begin tempf<=4'b0000; if(t1==9) begin t1<=0; if(t2==9) begin t2<=0; if(t3==9) begin t3<=0; if(t4==9) t4<=0; else t4<=t4+1; end else t3<=t3+1; end else t2<=t2+1; end else t1<=t1+1; end // if(m2==4'b0000&&m1>=3) {c4,c3,c2,c1}<={t4,t3,t2,t1}; // end always@(posedge flag) ifw<=!ifw; endmodule 出租车自动计费器顶层 gdf 文件如附录八图所示。 88 附录八图 出租车自动计费器顶层 gdf 文件 附录九 自动售邮票机主控电路 Verilog HDL 参考代码 //自动售邮票机 /*信号定义: clk:时钟信号; reset:系统复位清零; half: 5 角硬币模拟信号; one: 1 元硬币模拟信号; mout:有找零钱输出显示; tout:有邮票输出信号; charge:取零钱; ok:取邮票; mh:投入金额数码显示的高 4 位; ml: 投入金额数码显示的低 4 位。 */ module ticket(one,half,mh,ml,tout,mout,reset,clk,ok,charge); parameter a=0,b=1,c=2,d=3,e=4; //定义 5 个状态 input one,half,reset,clk,ok,charge; output tout,mout,mh,ml; reg mout,tout; reg[3:0] money; reg[3:0] mh; reg[3:0] ml; always@(posedge clk) 89 begin tout=1; end if(reset) begin tout=0; mout=0; money=a; {mh,ml}=8'b00000000; end case(money) a: if(half) begin money=b;{mh,ml}=8'b00000101;end else if(one) begin money=c;{mh,ml}=8'b00010000;end b: if(half) begin money=c;{mh,ml}=8'b00010000;end else if(one) begin money=d;{mh,ml}=8'b00010101;end c: if(half) begin money=d;{mh,ml}=8'b00010101;end else if(one) begin money=e;{mh,ml}=8'b00100000;end d: if(half) begin money=e;{mh,ml}=8'b00100000;end else if(one) begin money=a; {mh,ml}=8'b00100101; mout=0; tout=1; //sell end e: if(half) begin money=a; {mh,ml}=8'b00100101; tout=1; //sell end else if(one) begin money=a; {mh,ml}=8'b00110000; endcase 90 if({mh,ml}==8'b00100101) begin if(ok) begin tout=0;mout=0;{mh,ml}=8'b00000000;end if({mh,ml}==8'b00110000) begin end //电梯控制器 output o1,o2,o3,o4,o5,o6,o7,o8,door,fl; end if(ok) begin tout=0;mout=1;{mh,ml}=8'b00000101;end end if(charge&&mout==1) begin {mh,ml}=8'b00000000;mout=0;end endmodule 自动售邮票机顶层 gdf 文件如附录九图所示。 附录九图 自动售邮票机顶层 gdf 文件 附录十 电梯控制器主控电路 Verilog HDL 参考代码 /*信号定义: clk:时钟信号; d1,d2,d3,d4,d5,d6,d7,d8:楼层请求信号; o1,o2,o3,o4,o5,o6,o7,o8:楼层及请求信号状态显示; door:开门指示信号; fl:送数码管显示的当前楼层数。 */ module lift(clk,d1,d2,d3,d4,d5,d6,d7,d8,o1,o2,o3,o4,o5,o6,o7,o8,door,fl); input clk,d1,d2,d3,d4,d5,d6,d7,d8; reg o1,o2,o3,o4,o5,o6,o7,o8,door,up,down; reg[8:1] des; reg[2:0] count; reg[3:0] low,high,fl; always@(posedge clk) begin // if(d1)begin des[1]<=1;if(low>1||low==4'b0000)low<=1;end 91 if(d2)begin des[2]<=1;if(high<2&&{d3,d4,d5,d6,d7,d8}==6'b000000)high<=2;if(low>2||low==4'b0000&&!d 1)low<=2;end if(d3)begin des[3]<=1;if(high<3&&{d4,d5,d6,d7,d8}==5'b00000)high<=3;if((low>3||low==4'b0000)&&{d1, d2}==2'b00)low<=3;end if(d4)begin des[4]<=1;if(high<4&&{d5,d6,d7,d8}==4'b0000)high<=4;if((low>4||low==4'b0000)&&{d1,d2,d 3}==3'b000)low<=4;end if(d5)begin des[5]<=1;if(high<5&&{d6,d7,d8}==3'b000)high<=5;if((low>5||low==4'b0000)&&{d1,d2,d3,d4 }==4'b0000)low<=5;end if(d6)begin des[6]<=1;if(high<6&&{d7,d8}==2'b00)high<=6;if((low>6||low==4'b0000)&&{d1,d2,d3,d4,d5} ==5'b00000)low<=6;end if(d7)begin des[7]<=1;if(high<7&&!d8)high<=7;if((low>7||low==4'b0000)&&{d1,d2,d3,d4,d5,d6}==6'b0000 00)low<=7;end if(d8)begin des[8]<=1;if(high<8)high<=8;end // if({o1,o2,o3,o4,o5,o6,o7,o8}==8'b00000000) begin {o1,o2,o3,o4,o5,o6,o7,o8}<=8'b10000000; fl<=1; end else if(count==3'b101) begin count<=0; door<=0; if(low==fl) low<=4'b0000; if(high==fl) high<=4'b0000; end else if(count!=0) begin count<=count+1; door<=1; end else if(o1&&des[1]) begin count<=1; des[1]<=0; end 92 else if(o2&&des[2]) begin count<=1; des[2]<=0; end else if(o3&&des[3]) begin count<=1; des[3]<=0; end else if(o4&&des[4]) begin count<=1; des[4]<=0; end else if(o5&&des[5]) begin count<=1; des[5]<=0; end else if(o6&&des[6]) begin count<=1; count<=1; else if(up) des[6]<=0; end else if(o7&&des[7]) begin count<=1; des[7]<=0; end else if(o8&&des[8]) begin des[8]<=0; end // begin if(fl<high) begin {o1,o2,o3,o4,o5,o6,o7,o8}<={o1,o2,o3,o4,o5,o6,o7,o8}>>1; fl<=fl+1; end else 93 up<=0; end // else if(down) begin if(fl>low&&low!=4'b0000) begin {o1,o2,o3,o4,o5,o6,o7,o8}<={o1,o2,o3,o4,o5,o6,o7,o8}<<1; fl<=fl-1; end else down<=0; end else //**************** begin if(low!=4'b0000&&low<fl) begin if(high>fl&&high-fl<fl-low) up<=1; else down<=1; end else if(high>fl) up<=1; end //*************** end endmodule 电梯控制器顶层 gdf 文件如附录十图所示。 附录十图 电梯控制器顶层 gdf 文件 94 -- Predefined enumeration types DLE, DC1, DC2, DC3, DC4, NAK, SYN, ETB, 附录十一 VHDL 标准程序包文件 1. STANDARD.VHD package STANDARD is type BOOLEAN is (FALSE, TRUE); type BIT is ('0', '1'); type CHARACTER IS ( NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL, BS, HT, LF, VT, FF, CR, SO, SI, CAN, EM, SUB, ESC, FSP, GSP, RSP, USP, ' ', '!', '"', '#', '$', '%', '&', ''', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 95 'X', 'Y', 'Z',' '[', '\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',' '{', '|', '}', '~', DEL, C128, C129, C130, C131, C132, C133, C134, C135, C136, C137, C138, C139, C140, C141, C142, C143, C144, C145, C146, C147, C148, C149, C150, C151, C152, C153, C154, C155, C156, C157, C158, C159, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?'); type SEVERITY_LEVEL is (NOTE, WARNING, ERROR, FAILURE); -- Predefined numeric types type INTEGER is range -2147483648 to 2147483647; type REAL is range -1.7e38 to 1.7e38; -- Predefined type TIME type TIME is range -9223372036854775808 to 9223372036854775807 units fs; -- femtosecond ps = 1000 fs; -- picosecond ns = 1000 ps; -- nanosecond us = 1000 ns; -- microsecond ms = 1000 us; -- millisecond sec = 1000 ms; -- second min = 60 sec; -- minute hr = 60 min; -- hour 96 end units; subtype DELAY_LENGTH is TIME range 0 fs to TIME'HIGH; -- A function that returns the current simulation time, Tc: impure function NOW return DELAY_LENGTH; -- Predefined numeric subtypes: subtype NATURAL is INTEGER range 0 to INTEGER'HIGH; subtype POSITIVE is INTEGER range 1 to INTEGER'HIGH; -- Predefined array types: type STRING is array (POSITIVE range <>) of CHARACTER; type BIT_VECTOR is array (NATURAL range <>) of BIT; -- The predefined types for opening files: type FILE_OPEN_KIND is ( READ_MODE, -- Resulting access mode is read-only. WRITE_MODE, -- Resulting access mode is write-only. APPEND_MODE); -- Resulting access mode is write-only; information -- is appended to the end of the existing file. type FILE_OPEN_STATUS is ( OPEN_OK, -- File open was successful. STATUS_ERROR, -- File object was already open. NAME_ERROR, -- External file not found or inaccessible. MODE_ERROR); -- Could not open file with requested access mode. -- The 'FOREIGN attribute: attribute FOREIGN: STRING; end STANDARD; 97 2. STD_LOGIC_1164.VHD -- -------------------------------------------------------------------- -- Title : std_logic_1164 multi-value logic system -- Library : This package shall be compiled into a library -- : symbolically named IEEE. -- : -- Developers: IEEE model standards group (par 1164) -- Purpose : This packages defines a standard for designers -- : to use in describing the interconnection data types -- : used in vhdl modeling. -- : -- Limitation: The logic system defined in this package may -- : be insufficient for modeling switched transistors, -- : since such a requirement is out of the scope of this -- : effort. Furthermore, mathematics, primitives, -- : timing standards, etc. are considered orthogonal -- : issues as it relates to this package and are therefore -- : beyond the scope of this effort. -- : -- Note : No declarations or definitions shall be included in, -- : or excluded from this package. The "package declaration" 98 -- : defines the types, subtypes and declarations of -- : std_logic_1164. The std_logic_1164 package body shall be -- : considered the formal definition of the semantics of -- : this package. Tool developers may choose to implement -- : the package body in the most efficient manner available -- : to them. -- -------------------------------------------------------------------- -- modification history : -- -------------------------------------------------------------------- -- version | mod. date:| -- v4.200 | 01/02/92 | -- -------------------------------------------------------------------- PACKAGE std_logic_1164 IS ------------------------------------------------------------------- -- logic state system (unresolved) ------------------------------------------------------------------- TYPE std_ulogic IS ( 'U', -- Uninitialized 'X', -- Forcing Unknown '0', -- Forcing 0 '1', -- Forcing 1 'Z', -- High Impedance 'W', -- Weak Unknown 'L', -- Weak 0 'H', -- Weak 1 '-' -- Don't care ); ------------------------------------------------------------------- -- unconstrained array of std_ulogic for use with the resolution function ------------------------------------------------------------------- TYPE std_ulogic_vector IS ARRAY ( NATURAL RANGE <> ) OF std_ulogic; ------------------------------------------------------------------- -- resolution function ------------------------------------------------------------------- FUNCTION resolved ( s : std_ulogic_vector ) RETURN std_ulogic; ------------------------------------------------------------------- -- *** industry standard logic type *** ------------------------------------------------------------------- SUBTYPE std_logic IS resolved std_ulogic; ------------------------------------------------------------------- -- unconstrained array of std_logic for use in declaring signal arrays ------------------------------------------------------------------- 99 TYPE std_logic_vector IS ARRAY ( NATURAL RANGE <>) OF std_logic; ------------------------------------------------------------------- -- common subtypes ------------------------------------------------------------------- SUBTYPE X01 IS resolved std_ulogic RANGE 'X' TO '1'; -- ('X','0','1') SUBTYPE X01Z IS resolved std_ulogic RANGE 'X' TO 'Z'; -- ('X','0','1','Z') SUBTYPE UX01 IS resolved std_ulogic RANGE 'U' TO '1'; -- ('U','X','0','1') SUBTYPE UX01Z IS resolved std_ulogic RANGE 'U' TO 'Z'; -- ('U','X','0','1','Z') ------------------------------------------------------------------- -- overloaded logical operators ------------------------------------------------------------------- FUNCTION "and" ( l : std_ulogic; r : std_ulogic ) RETURN UX01; FUNCTION "nand" ( l : std_ulogic; r : std_ulogic ) RETURN UX01; FUNCTION "or" ( l : std_ulogic; r : std_ulogic ) RETURN UX01; FUNCTION "nor" ( l : std_ulogic; r : std_ulogic ) RETURN UX01; FUNCTION "xor" ( l : std_ulogic; r : std_ulogic ) RETURN UX01; FUNCTION "xnor" ( l : std_ulogic; r : std_ulogic ) RETURN UX01; FUNCTION "not" ( l : std_ulogic ) RETURN UX01; ------------------------------------------------------------------- -- vectorized overloaded logical operators ------------------------------------------------------------------- FUNCTION "and" ( l, r : std_logic_vector ) RETURN std_logic_vector; FUNCTION "and" ( l, r : std_ulogic_vector ) RETURN std_ulogic_vector; FUNCTION "nand" ( l, r : std_logic_vector ) RETURN std_logic_vector; FUNCTION "nand" ( l, r : std_ulogic_vector ) RETURN std_ulogic_vector; FUNCTION "or" ( l, r : std_logic_vector ) RETURN std_logic_vector; FUNCTION "or" ( l, r : std_ulogic_vector ) RETURN std_ulogic_vector; FUNCTION "nor" ( l, r : std_logic_vector ) RETURN std_logic_vector; FUNCTION "nor" ( l, r : std_ulogic_vector ) RETURN std_ulogic_vector; FUNCTION "xor" ( l, r : std_logic_vector ) RETURN std_logic_vector; FUNCTION "xor" ( l, r : std_ulogic_vector ) RETURN std_ulogic_vector; FUNCTION "xnor" ( l, r : std_logic_vector ) RETURN std_logic_vector; FUNCTION "xnor" ( l, r : std_ulogic_vector ) RETURN std_ulogic_vector; FUNCTION "not" ( l : std_logic_vector ) RETURN std_logic_vector; 100 FUNCTION "not" ( l : std_ulogic_vector ) RETURN std_ulogic_vector; ------------------------------------------------------------------- -- conversion functions ------------------------------------------------------------------- FUNCTION To_bit ( s : std_ulogic; xmap : BIT := '0') RETURN BIT; FUNCTION To_bitvector ( s : std_logic_vector ; xmap : BIT := '0') RETURN BIT_VECTOR; FUNCTION To_bitvector ( s : std_ulogic_vector; xmap : BIT := '0') RETURN BIT_VECTOR; FUNCTION To_StdULogic ( b : BIT ) RETURN std_ulogic; FUNCTION To_StdLogicVector ( b : BIT_VECTOR ) RETURN std_logic_vector; FUNCTION To_StdLogicVector ( s : std_ulogic_vector ) RETURN std_logic_vector; FUNCTION To_StdULogicVector ( b : BIT_VECTOR ) RETURN std_ulogic_vector; FUNCTION To_StdULogicVector ( s : std_logic_vector ) RETURN std_ulogic_vector; ------------------------------------------------------------------- -- attributes for conversion functions ------------------------------------------------------------------- attribute Synth_Conversion_Function: STRING; attribute Synth_Conversion_Function of To_bit: function is "BIT"; attribute Synth_Conversion_Function of To_bitvector: function is "BIT_VECTOR"; attribute Synth_Conversion_Function of To_StdULogic: function is "std_ulogic"; attribute Synth_Conversion_Function of To_StdLogicVector: function is "std_logic_vector"; attribute Synth_Conversion_Function of To_StdULogicVector: function is "std_ulogic_vector"; ------------------------------------------------------------------- -- strength strippers and type convertors ------------------------------------------------------------------- FUNCTION To_X01 ( s : std_logic_vector ) RETURN std_logic_vector; FUNCTION To_X01 ( s : std_ulogic_vector ) RETURN std_ulogic_vector; FUNCTION To_X01 ( s : std_ulogic ) RETURN X01; FUNCTION To_X01 ( b : BIT_VECTOR ) RETURN std_logic_vector; FUNCTION To_X01 ( b : BIT_VECTOR ) RETURN std_ulogic_vector; FUNCTION To_X01 ( b : BIT ) RETURN X01; FUNCTION To_X01Z ( s : std_logic_vector ) RETURN std_logic_vector; FUNCTION To_X01Z ( s : std_ulogic_vector ) RETURN std_ulogic_vector; FUNCTION To_X01Z ( s : std_ulogic ) RETURN X01Z; 101 FUNCTION To_X01Z ( b : BIT_VECTOR ) RETURN std_logic_vector; FUNCTION To_X01Z ( b : BIT_VECTOR ) RETURN std_ulogic_vector; FUNCTION To_X01Z ( b : BIT ) RETURN X01Z; FUNCTION To_UX01 ( s : std_logic_vector ) RETURN std_logic_vector; FUNCTION To_UX01 ( s : std_ulogic_vector ) RETURN std_ulogic_vector; FUNCTION To_UX01 ( s : std_ulogic ) RETURN UX01; FUNCTION To_UX01 ( b : BIT_VECTOR ) RETURN std_logic_vector; FUNCTION To_UX01 ( b : BIT_VECTOR ) RETURN std_ulogic_vector; FUNCTION To_UX01 ( b : BIT ) RETURN UX01; ------------------------------------------------------------------- -- edge detection ------------------------------------------------------------------- FUNCTION rising_edge (SIGNAL s : std_ulogic) RETURN BOOLEAN; FUNCTION falling_edge (SIGNAL s : std_ulogic) RETURN BOOLEAN; ------------------------------------------------------------------- -- object contains an unknown ------------------------------------------------------------------- FUNCTION Is_X ( s : std_ulogic_vector ) RETURN BOOLEAN; FUNCTION Is_X ( s : std_logic_vector ) RETURN BOOLEAN; FUNCTION Is_X ( s : std_ulogic ) RETURN BOOLEAN; END std_logic_1164; 102 type UNSIGNED is array (NATURAL range <>) of STD_LOGIC; 3. STD_LOGIC_ARITH.VHD ----------------------------------------------------------------------------- -- Copyright (c) 1994, 1990 - 1993 by Synopsys, Inc. All rights reserved. -- -- -- -- This source file may be used and distributed without restriction -- -- provided that this copyright statement is not removed from the file -- -- and that any derivative work contains this copyright notice. -- -- -- -- Package name: std_logic_arith -- -- -- -- Description: This package contains a set of arithmetic operators -- -- and functions. -- ------------------------------------------------------------------------------ library IEEE; use IEEE.std_logic_1164.ALL; package std_logic_arith is type SIGNED is array (NATURAL range <>) of STD_LOGIC; subtype SMALL_INT is INTEGER range 0 to 1; 103 function "+"(L: UNSIGNED; R: UNSIGNED) return UNSIGNED; function "+"(L: SIGNED; R: SIGNED) return SIGNED; function "+"(L: UNSIGNED; R: SIGNED) return SIGNED; function "+"(L: SIGNED; R: UNSIGNED) return SIGNED; function "+"(L: UNSIGNED; R: INTEGER) return UNSIGNED; function "+"(L: INTEGER; R: UNSIGNED) return UNSIGNED; function "+"(L: UNSIGNED; R: STD_ULOGIC) return STD_LOGIC_VECTOR; function "-"(L: UNSIGNED; R: UNSIGNED) return UNSIGNED; function "-"(L: SIGNED; R: UNSIGNED) return SIGNED; function "-"(L: UNSIGNED; R: INTEGER) return UNSIGNED; function "-"(L: INTEGER; R: UNSIGNED) return UNSIGNED; function "+"(L: SIGNED; R: INTEGER) return SIGNED; function "+"(L: INTEGER; R: SIGNED) return SIGNED; function "+"(L: UNSIGNED; R: STD_ULOGIC) return UNSIGNED; function "+"(L: STD_ULOGIC; R: UNSIGNED) return UNSIGNED; function "+"(L: SIGNED; R: STD_ULOGIC) return SIGNED; function "+"(L: STD_ULOGIC; R: SIGNED) return SIGNED; function "+"(L: UNSIGNED; R: UNSIGNED) return STD_LOGIC_VECTOR; function "+"(L: SIGNED; R: SIGNED) return STD_LOGIC_VECTOR; function "+"(L: UNSIGNED; R: SIGNED) return STD_LOGIC_VECTOR; function "+"(L: SIGNED; R: UNSIGNED) return STD_LOGIC_VECTOR; function "+"(L: UNSIGNED; R: INTEGER) return STD_LOGIC_VECTOR; function "+"(L: INTEGER; R: UNSIGNED) return STD_LOGIC_VECTOR; function "+"(L: SIGNED; R: INTEGER) return STD_LOGIC_VECTOR; function "+"(L: INTEGER; R: SIGNED) return STD_LOGIC_VECTOR; function "+"(L: STD_ULOGIC; R: UNSIGNED) return STD_LOGIC_VECTOR; function "+"(L: SIGNED; R: STD_ULOGIC) return STD_LOGIC_VECTOR; function "+"(L: STD_ULOGIC; R: SIGNED) return STD_LOGIC_VECTOR; function "-"(L: SIGNED; R: SIGNED) return SIGNED; function "-"(L: UNSIGNED; R: SIGNED) return SIGNED; function "-"(L: SIGNED; R: INTEGER) return SIGNED; function "-"(L: INTEGER; R: SIGNED) return SIGNED; function "-"(L: UNSIGNED; R: STD_ULOGIC) return UNSIGNED; function "-"(L: STD_ULOGIC; R: UNSIGNED) return UNSIGNED; function "-"(L: SIGNED; R: STD_ULOGIC) return SIGNED; function "-"(L: STD_ULOGIC; R: SIGNED) return SIGNED; function "-"(L: UNSIGNED; R: UNSIGNED) return STD_LOGIC_VECTOR; function "-"(L: SIGNED; R: SIGNED) return STD_LOGIC_VECTOR; function "-"(L: UNSIGNED; R: SIGNED) return STD_LOGIC_VECTOR; function "-"(L: SIGNED; R: UNSIGNED) return STD_LOGIC_VECTOR; 104 function "-"(L: UNSIGNED; R: INTEGER) return STD_LOGIC_VECTOR; function "-"(L: INTEGER; R: UNSIGNED) return STD_LOGIC_VECTOR; function "ABS"(L: SIGNED) return STD_LOGIC_VECTOR; function "<"(L: UNSIGNED; R: SIGNED) return BOOLEAN; function "<="(L: UNSIGNED; R: UNSIGNED) return BOOLEAN; function "<="(L: UNSIGNED; R: INTEGER) return BOOLEAN; function "-"(L: SIGNED; R: INTEGER) return STD_LOGIC_VECTOR; function "-"(L: INTEGER; R: SIGNED) return STD_LOGIC_VECTOR; function "-"(L: UNSIGNED; R: STD_ULOGIC) return STD_LOGIC_VECTOR; function "-"(L: STD_ULOGIC; R: UNSIGNED) return STD_LOGIC_VECTOR; function "-"(L: SIGNED; R: STD_ULOGIC) return STD_LOGIC_VECTOR; function "-"(L: STD_ULOGIC; R: SIGNED) return STD_LOGIC_VECTOR; function "+"(L: UNSIGNED) return UNSIGNED; function "+"(L: SIGNED) return SIGNED; function "-"(L: SIGNED) return SIGNED; function "ABS"(L: SIGNED) return SIGNED; function "+"(L: UNSIGNED) return STD_LOGIC_VECTOR; function "+"(L: SIGNED) return STD_LOGIC_VECTOR; function "-"(L: SIGNED) return STD_LOGIC_VECTOR; function "*"(L: UNSIGNED; R: UNSIGNED) return UNSIGNED; function "*"(L: SIGNED; R: SIGNED) return SIGNED; function "*"(L: SIGNED; R: UNSIGNED) return SIGNED; function "*"(L: UNSIGNED; R: SIGNED) return SIGNED; function "*"(L: UNSIGNED; R: UNSIGNED) return STD_LOGIC_VECTOR; function "*"(L: SIGNED; R: SIGNED) return STD_LOGIC_VECTOR; function "*"(L: SIGNED; R: UNSIGNED) return STD_LOGIC_VECTOR; function "*"(L: UNSIGNED; R: SIGNED) return STD_LOGIC_VECTOR; function "<"(L: UNSIGNED; R: UNSIGNED) return BOOLEAN; function "<"(L: SIGNED; R: SIGNED) return BOOLEAN; function "<"(L: SIGNED; R: UNSIGNED) return BOOLEAN; function "<"(L: UNSIGNED; R: INTEGER) return BOOLEAN; function "<"(L: INTEGER; R: UNSIGNED) return BOOLEAN; function "<"(L: SIGNED; R: INTEGER) return BOOLEAN; function "<"(L: INTEGER; R: SIGNED) return BOOLEAN; function "<="(L: SIGNED; R: SIGNED) return BOOLEAN; function "<="(L: UNSIGNED; R: SIGNED) return BOOLEAN; function "<="(L: SIGNED; R: UNSIGNED) return BOOLEAN; function "<="(L: INTEGER; R: UNSIGNED) return BOOLEAN; 105 function "<="(L: SIGNED; R: INTEGER) return BOOLEAN; function "<="(L: INTEGER; R: SIGNED) return BOOLEAN; function ">"(L: UNSIGNED; R: UNSIGNED) return BOOLEAN; function ">"(L: SIGNED; R: SIGNED) return BOOLEAN; function ">"(L: INTEGER; R: UNSIGNED) return BOOLEAN; function ">="(L: INTEGER; R: UNSIGNED) return BOOLEAN; function ">"(L: UNSIGNED; R: SIGNED) return BOOLEAN; function ">"(L: SIGNED; R: UNSIGNED) return BOOLEAN; function ">"(L: UNSIGNED; R: INTEGER) return BOOLEAN; function ">"(L: SIGNED; R: INTEGER) return BOOLEAN; function ">"(L: INTEGER; R: SIGNED) return BOOLEAN; function ">="(L: UNSIGNED; R: UNSIGNED) return BOOLEAN; function ">="(L: SIGNED; R: SIGNED) return BOOLEAN; function ">="(L: UNSIGNED; R: SIGNED) return BOOLEAN; function ">="(L: SIGNED; R: UNSIGNED) return BOOLEAN; function ">="(L: UNSIGNED; R: INTEGER) return BOOLEAN; function ">="(L: SIGNED; R: INTEGER) return BOOLEAN; function ">="(L: INTEGER; R: SIGNED) return BOOLEAN; function "="(L: UNSIGNED; R: UNSIGNED) return BOOLEAN; function "="(L: SIGNED; R: SIGNED) return BOOLEAN; function "="(L: UNSIGNED; R: SIGNED) return BOOLEAN; function "="(L: SIGNED; R: UNSIGNED) return BOOLEAN; function "="(L: UNSIGNED; R: INTEGER) return BOOLEAN; function "="(L: INTEGER; R: UNSIGNED) return BOOLEAN; function "="(L: SIGNED; R: INTEGER) return BOOLEAN; function "="(L: INTEGER; R: SIGNED) return BOOLEAN; function "/="(L: UNSIGNED; R: UNSIGNED) return BOOLEAN; function "/="(L: SIGNED; R: SIGNED) return BOOLEAN; function "/="(L: UNSIGNED; R: SIGNED) return BOOLEAN; function "/="(L: SIGNED; R: UNSIGNED) return BOOLEAN; function "/="(L: UNSIGNED; R: INTEGER) return BOOLEAN; function "/="(L: INTEGER; R: UNSIGNED) return BOOLEAN; function "/="(L: SIGNED; R: INTEGER) return BOOLEAN; function "/="(L: INTEGER; R: SIGNED) return BOOLEAN; function SHL(ARG: UNSIGNED; COUNT: UNSIGNED) return UNSIGNED; function SHL(ARG: SIGNED; COUNT: UNSIGNED) return SIGNED; function SHR(ARG: UNSIGNED; COUNT: UNSIGNED) return UNSIGNED; function SHR(ARG: SIGNED; COUNT: UNSIGNED) return SIGNED; 106 function CONV_INTEGER(ARG: INTEGER) return INTEGER; function CONV_INTEGER(ARG: UNSIGNED) return INTEGER; function CONV_INTEGER(ARG: SIGNED) return INTEGER; function CONV_INTEGER(ARG: STD_ULOGIC) return INTEGER; function CONV_UNSIGNED(ARG: INTEGER; SIZE: INTEGER) return UNSIGNED; function CONV_UNSIGNED(ARG: UNSIGNED; SIZE: INTEGER) return UNSIGNED; function CONV_UNSIGNED(ARG: SIGNED; SIZE: INTEGER) return UNSIGNED; function CONV_UNSIGNED(ARG: STD_ULOGIC; SIZE: INTEGER) return UNSIGNED; function CONV_SIGNED(ARG: INTEGER; SIZE: INTEGER) return SIGNED; function CONV_SIGNED(ARG: UNSIGNED; SIZE: INTEGER) return SIGNED; function CONV_SIGNED(ARG: SIGNED; SIZE: INTEGER) return SIGNED; function CONV_SIGNED(ARG: STD_ULOGIC; SIZE: INTEGER) return SIGNED; function CONV_STD_LOGIC_VECTOR(ARG: INTEGER; SIZE: INTEGER) return STD_LOGIC_VECTOR; function CONV_STD_LOGIC_VECTOR(ARG: UNSIGNED; SIZE: INTEGER) return STD_LOGIC_VECTOR; function CONV_STD_LOGIC_VECTOR(ARG: SIGNED; SIZE: INTEGER) return STD_LOGIC_VECTOR; function CONV_STD_LOGIC_VECTOR(ARG: STD_ULOGIC; SIZE: INTEGER) return STD_LOGIC_VECTOR; ------------------------------------------------------------------- -- attributes for conversion functions ------------------------------------------------------------------- attribute Synth_Conversion_Function of CONV_INTEGER: function is "INTEGER"; attribute Synth_Conversion_Function of CONV_UNSIGNED: function is "UNSIGNED"; attribute Synth_Conversion_Function of CONV_SIGNED: function is "SIGNED"; attribute Synth_Conversion_Function of CONV_STD_LOGIC_VECTOR: function is "STD_LOGIC_VECTOR"; ------------------------------------------------------------------- -- zero extend STD_LOGIC_VECTOR (ARG) to SIZE, -- SIZE < 0 is same as SIZE = 0 -- returns STD_LOGIC_VECTOR(SIZE-1 downto 0) function EXT(ARG: STD_LOGIC_VECTOR; SIZE: INTEGER) return STD_LOGIC_VECTOR; -- sign extend STD_LOGIC_VECTOR (ARG) to SIZE, -- SIZE < 0 is same as SIZE = 0 -- return STD_LOGIC_VECTOR(SIZE-1 downto 0) function SXT(ARG: STD_LOGIC_VECTOR; SIZE: INTEGER) return STD_LOGIC_VECTOR; 107 end std_logic_arith; 4. STD_LOGIC_SIGNED.VHD ----------------------------------------------------------------------------- -- Copyright (c) 1994, 1990 - 1993 by Synopsys, Inc. All rights reserved. -- -- -- -- This source file may be used and distributed without restriction -- -- provided that this copyright statement is not removed from the file -- -- and that any derivative work contains this copyright notice. -- -- -- -- Package name: std_logic_signed -- -- -- -- Description: This package contains a set of signed arithemtic -- -- operators and functions. -- ----------------------------------------------------------------------------- library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_arith.all; package std_logic_signed is function "+"(L: STD_LOGIC_VECTOR; R: STD_LOGIC_VECTOR) return 108 STD_LOGIC_VECTOR; function "+"(L: STD_LOGIC_VECTOR; R: INTEGER) return STD_LOGIC_VECTOR; function "+"(L: INTEGER; R: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR; function "+"(L: STD_LOGIC_VECTOR; R: STD_LOGIC) return STD_LOGIC_VECTOR; function "+"(L: STD_LOGIC; R: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR; function "-"(L: STD_LOGIC_VECTOR; R: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR; function "-"(L: STD_LOGIC_VECTOR; R: INTEGER) return STD_LOGIC_VECTOR; function "-"(L: INTEGER; R: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR; function "-"(L: STD_LOGIC_VECTOR; R: STD_LOGIC) return STD_LOGIC_VECTOR; function "-"(L: STD_LOGIC; R: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR; function "+"(L: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR; function "-"(L: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR; function "ABS"(L: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR; function "*"(L: STD_LOGIC_VECTOR; R: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR; function "<"(L: STD_LOGIC_VECTOR; R: INTEGER) return BOOLEAN; function "<"(L: INTEGER; R: STD_LOGIC_VECTOR) return BOOLEAN; function "<="(L: STD_LOGIC_VECTOR; R: INTEGER) return BOOLEAN; function "<="(L: INTEGER; R: STD_LOGIC_VECTOR) return BOOLEAN; function ">"(L: STD_LOGIC_VECTOR; R: INTEGER) return BOOLEAN; function ">"(L: INTEGER; R: STD_LOGIC_VECTOR) return BOOLEAN; function ">="(L: STD_LOGIC_VECTOR; R: INTEGER) return BOOLEAN; function ">="(L: INTEGER; R: STD_LOGIC_VECTOR) return BOOLEAN; function "="(L: STD_LOGIC_VECTOR; R: INTEGER) return BOOLEAN; function "="(L: INTEGER; R: STD_LOGIC_VECTOR) return BOOLEAN; function "/="(L: STD_LOGIC_VECTOR; R: INTEGER) return BOOLEAN; function "/="(L: INTEGER; R: STD_LOGIC_VECTOR) return BOOLEAN; function CONV_INTEGER(ARG: STD_LOGIC_VECTOR) return INTEGER; end std_logic_signed; 109 5. STD_LOGIC_UNSIGNED.VHD ----------------------------------------------------------------------------- -- Copyright (c) 1994, 1990 - 1993 by Synopsys, Inc. All rights reserved. -- -- -- -- This source file may be used and distributed without restriction -- -- provided that this copyright statement is not removed from the file -- -- and that any derivative work contains this copyright notice. -- -- -- -- Package name: std_logic_unsigned -- -- -- -- Description: This package contains a set of unsigned arithemtic -- -- operators and functions. -- ----------------------------------------------------------------------------- library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_arith.all; package std_logic_unsigned is function "+"(L: STD_LOGIC_VECTOR; R: STD_LOGIC_VECTOR) return 110 STD_LOGIC_VECTOR; function "+"(L: STD_LOGIC_VECTOR; R: INTEGER) return STD_LOGIC_VECTOR; function "+"(L: INTEGER; R: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR; function "+"(L: STD_LOGIC_VECTOR; R: STD_LOGIC) return STD_LOGIC_VECTOR; function "+"(L: STD_LOGIC; R: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR; function "-"(L: STD_LOGIC_VECTOR; R: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR; function "-"(L: STD_LOGIC_VECTOR; R: INTEGER) return STD_LOGIC_VECTOR; function "-"(L: INTEGER; R: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR; function "-"(L: STD_LOGIC_VECTOR; R: STD_LOGIC) return STD_LOGIC_VECTOR; function "-"(L: STD_LOGIC; R: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR; function "+"(L: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR; function "*"(L: STD_LOGIC_VECTOR; R: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR; function "<"(L: STD_LOGIC_VECTOR; R: INTEGER) return BOOLEAN; function "<"(L: INTEGER; R: STD_LOGIC_VECTOR) return BOOLEAN; function "<="(L: STD_LOGIC_VECTOR; R: INTEGER) return BOOLEAN; function "<="(L: INTEGER; R: STD_LOGIC_VECTOR) return BOOLEAN; function ">"(L: STD_LOGIC_VECTOR; R: INTEGER) return BOOLEAN; function ">"(L: INTEGER; R: STD_LOGIC_VECTOR) return BOOLEAN; function ">="(L: STD_LOGIC_VECTOR; R: INTEGER) return BOOLEAN; function ">="(L: INTEGER; R: STD_LOGIC_VECTOR) return BOOLEAN; function "="(L: STD_LOGIC_VECTOR; R: INTEGER) return BOOLEAN; function "="(L: INTEGER; R: STD_LOGIC_VECTOR) return BOOLEAN; function "/="(L: STD_LOGIC_VECTOR; R: INTEGER) return BOOLEAN; function "/="(L: INTEGER; R: STD_LOGIC_VECTOR) return BOOLEAN; function CONV_INTEGER(ARG: STD_LOGIC_VECTOR) return INTEGER; end std_logic_unsigned; 111