IC FPGA


👉网页版:集成电路设计知识点与笔记

👉作业代码及复习总结:Github地址

Key Word

集成电路设计名词:

  • Synthesis:综合,包括translation + optimization + mapping
  • 工艺无关性:设计可以应用到任何工艺
  • HDL Compiler:HDL Compiler将HDL描述转换为Synopsys 设计块,并传送给Design Compiler
  • Design Compiler:Design Compiler将 Synopsys设计块映射到用户指定库的门级设计
  • Design(设计):完成一个或多个逻辑功能的电路
  • Cell (单元) :一个设计在另一个设计中的实例
  • Reference (引用) :单元(Cell)指向的原始设计,如verilog module
  • Port (端口) :设计的输入或输出接口
  • Pin (引脚) :单元(Cell)的输入或输出接口
  • Net (网线) :Port-Pin或Pin-Pin之间的连接线
  • Clock (时钟) :指定为时钟源的Port或Pin
  • 时序弧:一段连接线(net)延时或一个单元(cell)延时
  • 环境约束:对电路工作的外在环境,包括电路的工作电压、工作温度以及工艺变化,以及电路与周边其它电路的关系,包括输入引脚的延时及驱动能力,输出引脚的负载及输出延时。
  • Load Budget:负载预算,即输出端口驱动能力最小单元驱动(反相器),限制每个输入端口的输入电容,估算输出端口驱动模块的数目。
  • Wire Load Model:wire load model根据芯片面积和标准单元的扇出估算连接线上的电容,分为top,enclosed,segmented三种类型。
  • Constraint:约束,Design Compiler优化一个设计到目标工艺库的目标
  • 设计规则约束:与工艺相关的限制,如最大的传输时间、最大的扇出、最大电容等。
  • 优化约束:设计目标及要求。如最大延时、最小延时、最大面积、最大功耗等。
  • PAD:IO pad是一个芯 管脚处理模块,即可以将芯片管脚的信号经过处理送给芯片内部,又可以讲芯片内部输出的信号经过处理送到芯片管脚
  • 上升(下降)延时:信号从 10%电平上升(下降)到 90%电平所需要的时间
  • False Path:在电路中一种有物理连接的路径,是不传播信号的通路,或是忽略这个通路上的时序约束
  • 多周期通路:在有些情况下,两个寄存器之间的组合逻辑延迟可能要求多于一个时钟周期,这些通路应设置为Multi-Cycle通路,解决办法有dont_touch、ungroup、uniquify
    • 最简单的方法是uniquify, 但需要较多的存储器和编译时间
    • 如果希望保留设计层次并资源共享,使用dont_touch
    • 如果希望得到一个最好的结果,推荐使用 ungroup,但需要的存储器和编译时间最多。
  • 逻辑级优化:technology independent optimization,对电路的布尔表达式进行优化,对整个设计面积/速度特性有全局的影响,包括flatten ( 缺省为关 ) : 清除所有中间变量,使用布尔分配定律去除所有括号;structure : 提取公用逻辑
  • 门级优化:从工艺库中选择元件以实现这些逻辑结构,以满足电路指定的时间、设计规则和面积目标,包括map,使设计与工艺相关
  • Slack:要求时间与实际到达时间的差值结果。
  • 资源共享:指多节代码共享一组逻辑。
  • CTLF:(Compiled Timing Library Format)编译的时序库格式。特定工艺元件数据的标准格式。
  • GCF:(General constraint Format)通用约束格式。约束数据的标准格式。
  • MIPD:(Module Input Port Delay)模块输入端口延时。模块输入或输入输出端口的固有互连延时
  • MITD:(Multi-source Interconnect Transport Delay)多重互连传输延时。与SITD相似,但支持多个来源的不同延时。
  • PLI:(Programming Language Interface)编程语言界面。基于C的对Verilog数据结构的程序访问。
  • SDF:Standard Delay Format.(标准延迟格式)。时序数据OVI标准格式。
  • SITD:Single-Source Interconnect Transprot Delay,单一源互连传输延迟。和MIPD相似,但支持带脉冲控制的传输延迟。
  • SPF:Standard Parasitic Format.(标准寄生参数格式)。提取的寄生参数数据的标准格式。
  • IOPATH延时:器件从输入端口到输出端口的一个合法路径上的延时
  • 惯性延迟:若路径延时小于门的固有延时,信号会被淹没。
  • 传输延迟:输入上每个变化都会反映到输出上。
  • 模块路径(module path):穿过模块,连接模块输入(input端口或inout端口)到模块输出(output端口或inout端口)的路径。
  • 路径延时(path delay):与特定路径相关的延时
  • 时序检查(timing check):监视两个输入信号的时间关系并进行检查的系统任务,以保证电路能正确工作。
  • 时序驱动设计(timing driven design):从前端到后端的整个设计流程中,用时序信息连接不同的设计阶段。
  • 块延时:将全部延时集中到最后一个门上。这种模型简单但不够精确,只适用于简单电路。因为当到输出端有多个路径时不能描述不同路径的不同延时。
  • 分布延时:将延时分散到每一个门。在相同的输出端上,不同的路径有不同的延时。
  • 路径延时:在专用的specify块描述模块从输入端到输出端的路径延时。
  • SDPD,状态依赖路径延时:在说明的条件成立时赋予路径一个延时。
  • NonRecurring-Engineering ( NRE ):集成电路产品的研制开发费,设计人工费,计算机软硬件 设备折旧费以及试制过程中所需的制版、工艺加工、测试分析等研发过程中的一次性开支。
  • Wafer Size: 晶圆大小,4英寸, 6英寸,8英寸,12英寸
  • Feature size:特征尺寸
  • Die size:芯片棵柆尺寸
  • Moore’s Law:加工能力每18个月翻一番
  • 等效门:一个等效门是一个二输入NAND门
  • RTL:寄存器传输级
  • HDL:硬件描述语言
  • Defect density:缺陷密度,影响成品率
  • yield:成品率(良率)
  • FPGA:现场可编程阵列
  • 系统级设计:确定算法,完成行为级模型并对系统功能进行验证,完成系统软硬件划分,确定系统功能框图。由系统工程师完成。要求系统工程师熟悉硬件设计、软件设计、PCB设计等。主要工具:SPW、matlab等。
  • 前端设计:根据系统框图和算法,完成RTL设计及其验证。将RTL设计进行综合,并完成综合后验证,得到门级网表。由前端工程师完成。主要工具:schematic editor、Verilog/VHDL simulator、Synopsys Design Compiler、Power Compiler、Prime Time、DFT、Formality等。
  • 后端设计:根据网表及综合约束,完成版图设计及验证。由后端工程师完成,主要工具:ultra/SoC Encounter、 Prime Time /layout editor、DRC、ERC、LVS的Calibra。
  • MPW(Multi-Project Wafer):将多种具有相同工艺的集成电路设计放在同一个硅圆片。
  • 仿真器:读入HDL并进行解释及执行的一种软件
  • 抽象级:描述方式的详细程度,如行为级和门级
  • ASIC:专用集成电路(Application Specific Integrated Circuit)
  • ASIC Vender:芯片制造商,开发并提供单元库
  • Bottom-up design flow :一种先构建底层单元,然后由底层单元构造更大的系统的设计方法
  • Top-down design flow :一种设计方法,先用高抽象级构造系统,然后再设计下层单元
  • Tcl:Tool command Language, 向交互程序输入命令的描述语言
  • 行为级:用功能块之间的数据流对系统进行描述;在需要时在函数块之间进行调度赋值
  • RTL级/功能级:用功能块内部或功能块之间的数据流和控制信号描述系统;基于一个已定义的时钟的周期来定义系统模型
  • 结构级/门级:用基本单元(primitive)或低层元件(component)的连接来描述系统以得到更高的精确性,特别是时序方面;逻辑综合时用特定工艺和低层元件将RTL描述映射到门级网表
  • primitives(基本单元) : Verilog语言已定义的具有简单逻辑功能的功能模型(models)
  • 扇出(fan-out):是一个定义单个逻辑门能够驱动的数字信号输入最大量的专业术语

集成电路设计流程

参考书目

  • 《Verilog HDL 设计与验证》人民邮电出版社,EDA先锋工作室 吴继华

  • 《Verilog HDL数字设计与综合》 电子工业出版社,Samir Palnitkar,夏宇闻等译。

  • 《硬件描述语言Verilog》 清华大学出版社,Thomas &Moorby,刘明业等译

VMware虚拟机

  • VMWare虚拟机软件可以在一台机器上同时运行两个以上Windows、LINUX操作系统。多启动系统在一个时刻只能运行一个系统,在系统切换时需要重新启动机器。

  • VMWare是真正“同时”运行多个操作系统在主系统的平台上,就象标准Windows应用程序那样切换。而且每个操作系统你都可以进行虚拟的分区、配置而不影响真实硬盘的数据,可以通过网卡将几台虚拟机用网卡连接为一个局域网。

Icarus 仿真工具

  • Icarus仿真工具是一个轻量、免费、开源的Verilog编译器,基于C++实现。安装文件中已经包含 GTKWave,支持Verilog/VHDL文件的编译和仿真,以命令行方式操作,通过testbench文件可以生成对应的仿真波形数据文件,通过自带的GTKWave可以查看仿真波形图,支持将Verilog转换为VHDL文件。

  • 可以检查Verilog文件的语法错误,并进行一些基本的时序仿真。Icarus Verilog 显得极其小巧,最新版安装包大小仅有17MB,支持全平台:Windows+ Linux+MacOS,并且源代码开源。

Yosys综合工具

  • http://eduhub.cn

  • http://www.clifford.at/yosys/about.html

  • 支持 Verilog-2005 的可综合的设计

  • 将Verilog转换为其他的设计格式(BLIF/EDIF/BTOR/ SMT-LIB/ simple RTL Verilog)

  • 内建的Formal功能

  • 将RTL综合为针对AISC标准单元库的门级网表(在有ASIC标准单元的Liberty库的前提下面)

  • 将RTL设计综合映射为Xilinx 7系和Lattice iCE40 FPGA

IC设计的挑战-技术问题

  • 设计复杂度的提高
    • 硬件复杂度
    • 软件复杂度

规模,特征尺寸的缩小,时钟频率提高,低功耗问题,可测试性

IC设计方法的变化

集成电路设计的一些基本概念

  • nonrecurring-engineering ( NRE ):集成电路产品的研制开发费,设计人工费,计算机软硬件设备折旧费以及试制过程中所需的制版、工艺加工、测试分析等研发过程中的一次性开支。

  • wafer size: 4英寸, 6英寸,8英寸,12英寸

  • feature size:特征尺寸

  • die size:芯片棵柆尺寸

  • Moore’s Law:加工能力每18个月翻一番

  • 等效门:一个等效门是一个二输入NAND门

  • RTL:寄存器传输级

  • HDL:硬件描述语言

  • defect density:缺陷密度,影响成品率

  • yield:成品率(良率)

集成电路设计方法分类

  • 全定制设计

  • 基于门阵列的设计

  • 基于现场可编程阵列FPGA的设计 (Field Programmable Gate Array )

  • 基于标准单元的设计

全定制设计(Full-custom)

  • 全定制设计:从晶体管开始手工完成集成电路的电路设计、仿真、版图设计的一种方法。

  • 设计的精度很高,可以最大程度优化芯片的性能,不会浪费太多芯片资源。

  • 花费更多的人力和时间成本。

基于门阵列的设计方法

  • 门阵列是指由半导体厂商准备出已经在硅片上形成了被称为基本单元的逻辑门的母板,通过按照用户希望的电路进行布线,在母板上形成电路的半客户定制品芯片。

  • 门阵列可分为有信道和无信道两种。

特点

  • 可大幅度的缩短生产工期;

  • 低成本:即使是小批量的数字电路,仍能实现低成本,原因在于实现相同的功能,FPGA使用LUT等浪费的资源比较多,而门阵列则不存在这个问题,所以实现相同的功能,门阵列所使用的资源数量要远远小于FPGA,从而减少电路面积,降低总成本。

  • 安全性:FPGA的电路编程数据存储在ROM。可通过监控启动时ROM和FPGA之间的位流,截取和复制电路数据。门阵列的专用电路设计在半定制IC上硬连线实现,从而使其不可能被复制。

产品

  • 瑞萨的门阵列产品

  • NEC电子近日推出了一种可将微控制器和门阵列封入到一个封装中的新型ASIC “PFESiP”

现场可编程阵列 FPGA

  • 主要特点:
  1. 小批量系统提高系统集成度、可靠性的最佳选择之一。

  2. 不需要投片生产,就能得到合用的芯片。

  3. 可做其它全定制或半定制ASIC电路的中试样片。

  4. 设计周期短、开发费用最低、风险最小

  • 主要提供商:
  1. Altera PLD的发明者

  2. Xilinx FPGA的发明者

  3. Actel 采用非易失工艺(反熔丝或Flash工艺)

  • 结构

    • FPGA芯片集成了丰富的门及各种IO

    • 存储器SRAM

    • 高速存储器接口DDR4

    • 时钟 PLL 及DCM

    • DSP IP

    • XADC

    • 高速通信接口PCIe

    • CPU

    • IP种类丰富

  • Xilinx公司成立于1984年, 1985年推出第一片FPGA,已经有近30年的历史

标准单元库

  • 单元库是进行集成电路设计的一个重要部分,可由制造厂家,或第三方提供,也可自己开发。

  • 为了支持不同层次的设计,单元库的内容包括:

    • 行为模型(behavioral model )
    • Verilog/VHDL模型(verilog/VHDL model )
    • 电路原理图(circuit schematic )
    • 单元符号 (cell icon,symbol )
    • 详细的时序模型 (detailed timing model )
    • 测试策略 (test strategy)
    • 线负载模型(wire-load model )
    • 布线模型(routing model )
    • 物理版图 (physical layout )

EDA工具提供商

  • Cadence

  • Synopsys

    • 电路设计及仿真版图输出及验证、逻辑综合、版图自动设计
  • Mentor

    • FPGA设计、Modelsim、Calibre、DFT PCB设计
  • 华大九天

    • 电路设计及仿真版图输出及验证

集成电路设计流程

  • IC设计涉及到大量的EDA工具,要求每个工程师对这些工具都有些了解

完成一个IC设计,从系统的角度来看,主要分为三个阶段:

  • 系统级设计:确定算法,完成行为级模型并对系统功能进行验证,完成系统软硬件划分,确定系统功能框图。由系统工程师完成。要求系统工程师熟悉硬件设计、软件设计、PCB设计等。主要工具:SPW、matlab等。

  • 前端设计:根据系统框图和算法,完成RTL设计及其验证。将RTL设计进行综合,并完成综合后验证,得到门级网表。由前端工程师完成。主要工具:schematic editor、Verilog/VHDL simulator、Synopsys Design Compiler、Power Compiler、Prime Time、DFT、Formality等。

  • 后端设计:根据网表及综合约束,完成版图设计及验证。由后端工程师完成,主要工具:ultra/SoC Encounter、 Prime Time /layout editor、DRC、ERC、LVS的Calibra。

软/硬件协同设计技术

软硬件详细设计完成划分后的软件和硬件的设计实现。

硬件综合是在厂家综合库的支持下,完成行为级、RTL以及逻辑级的综合。

代码优化完成对设计实现后的系统进行优化,主要是与处理器相关的优化和与处理器无关的优化。与处理器相关的优化受不同的处理器类型影响很大,一般根据处理器进行代码选择、主要是指令的选择;指令的调度(并行、流水线等)、寄存器的分配策略等;与处理器无关的优化主要有常量优化、变量优化和代换、表达式优化、消除无用变量、控制流优化和循环内优化等。

软硬件协同仿真和验证完成设计好的系统的仿真和验证,保证目标系统的功能实现、满足性能要求和限制条件,从整体上验证整个系统。

软硬件协同设计在实际应用中表现为软硬件协同设计平台的开发。从系统组成的角度,可以用图1来表述软硬件协同设计平台的系统组成。其中设计空间搜索部分由体系结构库、设计库、成本库、系统功能描述和系统设计约束条件组成。设计空间搜索的任务是对不同的目标要求找到恰当的解决办法。体系结构库是存放协同设计支持的各种体系结构数据库,一般是通过不同的模型表现出来。到目前为止,使用较多的模型有状态转换模型(有限状态机)、事件驱动模型、物理结构组成模型、数据流程模型和混合模型等。

体系结构的丰富程度决定了对目标系统的软硬件协同设计的支持力度。设计库中包含可以使用的程序或网表的设计执行数据库,为新的设计提供参考依据。成本库中提供设计成本的计算方法以及由目标系统的资源消耗、电源消耗、芯片面积、实时要求等组成的数据库,是工作在给定平台上的明确界定。

调度模型主要确定软件各个任务子程序之间执行次序。

通信模型主要确定硬件之间的通信和软件与硬件之间的通信模式。

基于标准单元的设计流程

到目前为止,集成电路设计流程有发展有四个阶段在:

(1)基于逻辑图的设计流程

(2)基于RTL的设计流程,80年代之后

(3)深亚微米集成电路设计流程

(4)基于IP的设计流程

后三个流程大体相似,后两者需要更多的验证。

RTL设计及验证

  • 采用HDL(Hardware Description Language)完成电路设计

  • HDL主要有两种:Verilog和VHDL

  • Verilog 1983年由Phil Moorby所创

    • 1995年制定IEEE-1364标准。
    • 2001年推出Verilog-2001标准
    • 2005年推出Verilog-2005标准,即SystemVerilog
  • VHDL:IEEE 1706-1985标准。

什么是逻辑综合?

逻辑综合-Synthesis

  • RTL代码 + 设计约束 + 设计库

  • 提交综合工具

  • 综合工具主要:synopsys:design compiler/physical compiler

​ cadence: build gates/RTL compiler

​ magma: blaster create

物理综合

对于深亚微米(0.18um以下)设计,设计过程不同。在之前,延时主要在门上。连接线上的延时主要是按照wireload模型进行估算。

到0.18um,特别是0.13um以下,连接线延时占70%以上,而工作时钟频率往往也比较高,在400M以上。因此必须在综合时能够比较精确估算延时。

采用物理综合流程进行设计。

首先是进行预布局,得到门的位置后,再估算连接延时。这些延时反标注后,再进行电路综合。

预布局的过程是:面积估算、IP自动布局、手工调整。门逻辑预布局。这个时候,可以对电路性能进行初步估算。如果达不到性能要求-》修改设计,因为此时往往面积估得会小一些。

版图设计(layout)

后端设计工具很多,synopsys:applo/ultra、cadence:SE/SocENcounter, magma/blaster fusion,都是很好的工具。没有优劣。差别再于细节处理不同。

Magma简单易用,内部有一个设计流程,并把设计流程中许多细节包括在一个大的命令(实际是TCL)中。对于专家,可以修改这些命令。

工艺支持最好的是synopsys的ultra。有人抱怨SE不好用,没有undo。

后端Layout设计

后端设计基本上包括以下几个步骤:

  • 设计准备

  • 布局

  • 时钟产生

  • 布线

  • 分析

  • 验证

设计流程

版图设计是一个基于时序的 floorplan, place、route工具

设计准备

  • 输入数据并对设计进行初始化

导入网表及时序文件

  • 建立物理数据库:导入库,网表,以及时序信息

Floorplan

  • 不放置任何单元,只控制芯片的芯片的大小、长宽比率,建立横向或纵向的core和I/O row

Building the Floorplan

Placing Block

I/O Placed

Power Planning

Plan Power – Strips

Place Cells

时钟树设计流程

时钟树设计

Using the CT- Gen Integration in SE

Setting the Clock Tree Constraints

Routing flow

Routed Design

Verify

完成布线后,如果有violation,final router将在数据库中保留信息标记。这时,可以用Verify Connectivity和Verify Geometry命令来做进一步的检查。

  • Verify Connectivity检查是否完成了所有的连接,并在连接线上加入信息(info)。

  • 使用Report Info 来产生信息报告。

  • Verify Geometry检查短路和设计规则violation并产生图形化的信息标记。用户应在执行了 Verify Connectivity和Verify Geometry后产生一个报告。

Report Delay(SDF)

MPW(Multi-Project Wafer)

  • MPW将多种具有相同工艺的集成电路设计放在同一个硅圆片。

  • 实验费用就由所有参加多项目晶圆的项目按照各自所占的芯片面积分摊,极大地降低了实验成本(90%)。

  • 可以得到数十片芯片样品,用于设计开发阶段的实验、测试。

  • 多项目晶圆提高了设计效率,降低了开发成本。

Verilog语言简介

术语定义(terms and definitions)

  • 硬件描述语言HDL:描述电路硬件及时序的一种编程语言

  • 仿真器:读入HDL并进行解释及执行的一种软件

  • 抽象级:描述方式的详细程度,如行为级和门级

  • ASIC:专用集成电路(Application Specific Integrated Circuit)

  • ASIC Vender:芯片制造商,开发并提供单元库

  • Bottom-up design flow :一种先构建底层单元,然后由底层单元构造更大的系统的设计方法 。

  • Top-down design flow :一种设计方法,先用高抽象级构造系统,然后再设计下层单元。

  • RTL level:寄存器传输级(Register Transfer Level),可综合的一种设计抽象级

  • Tcl:Tool command Language, 向交互程序输入命令的描述语言

什么是硬件描述语言HDL

  • 具有特殊结构能够对硬件逻辑电路的功能进行描述的一种高级编程语言

  • 这种特殊结构能够:

    • 描述电路的连接
    • 描述电路的功能
    • 以不同的抽象级描述电路
    • 描述电路的时序
    • 描述具有并行性
  • HDL主要有两种:Verilog和VHDL

    • Verilog起源于C语言,因此非常类似于C语言,容易掌握
    • VHDL起源于ADA语言,格式严谨,不易学习
    • VHDL出现较晚,但标准化早。IEEE 1706-1985标准

Verilog的历史

  • Verilog HDL是在1983年由GDA(GateWay Design Automation)公司的Phil Moorby所创。Phil Moorby后来成为Verilog-XL的主要设计者和Cadence公司的第一个合伙人。

  • 在1984~1985年间,Moorby设计出了第一个Verilog-XL的仿真器。

  • 1986年,Moorby提出了用于快速门级仿真的XL算法。

  • 1990年,Cadence公司收购了GDA公司

  • 1991年,Cadence公司公开发表Verilog语言,成立了OVI(Open Verilog International)组织来负责Verilog HDL语言的发展。

  • 1995年制定了Verilog HDL的IEEE标准,即IEEE1364。

  • 2001年推出Verilog-2001标准

  • 2005年推出Verilog-2005标准,即SystemVerilog

  • IEEE Std 1800-xxxx:SystemVerilog现行标准

  • Verilog-AMS:模拟及混合信号描述 的Verilog扩展

Verilog的用途

  • Verilog的主要应用包括:
    • ASIC和FPGA工程师编写可综合的RTL代码
    • 使用高抽象级描述仿真系统,进行系统建模开发
    • 测试工程师用于编写各种层次的测试程序
    • 用于ASIC和FPGA单元或更高层次的模块的仿真模型开发

抽象级(Levels of Abstraction)

  • Verilog既是一种行为描述的语言也是一种结构描述语言。

  • Verilog模型可以是实际电路的不同级别的抽象。

  • 这些抽象的级别包括:

    • 行为级(系统说明):设计文档/算法描述
    • RTL/功能级:Verilog
    • 门级/结构级:Verilog
    • 版图/物理级:几何图形

Verilog可以在三种抽象级上进行描述:

  • 行为级

    • 用功能块之间的数据流对系统进行描述
    • 在需要时在函数块之间进行调度赋值。
  • RTL级/功能级

    • 用功能块内部或功能块之间的数据流和控制信号描述系统
    • 基于一个已定义的时钟的周期来定义系统模型
  • 结构级/门级

    • 用基本单元(primitive)或低层元件(component)的连接来描述系统以得到更高的精确性,特别是时序方面。
    • 逻辑综合时用特定工艺和低层元件将RTL描述映射到门级网表

Verilog语言结构

Verilog-2001语言结构

module DFF (
  // 端口说明
    output reg    q   ,
    output wire  qb ,
    input   wire  d   ,     // input data
                          clk,     /*input clock */ 
                          clr
    );
/*
  clk is posedge and clr is active low 
*/
    assign qb = !q;
    always @(posedge clk , negedge clr)
        if(!clr)
    q <= 0;
        else
    q <= d;
endmodule

SystemVerilog

module DFF (
  // 端口说明
    output reg     q    ,
                wire   qb  ,
    input   wire   d    ,    // input data
                           clk ,  /*input clock */ 
                           clr
    );
/*
  clk is posedge and clr is active low 
*/
    assign qb = !q;
    always @(posedge clk or negedge clr)
        if(!clr)
    q <= 0;
        else
    q <= d;
endmodule

空白符和注释

某标准制定的注释规则

  1. 注释每一个功能逻辑,说明该功能逻辑的功能以及意图;

  2. 注释的文本内容与注释符之间至少留有一个空格;

  3. 验证修改时产生的无用代码应删除,而不能仅与以变更为注释;

  4. 对所有的端口、信号、变量、函数、过程、常量、任务声明进行注释,应说明其意义、方向、有效值、作用等,宜注释与声明同行,如果注释过长,则将注释置于声明的上一行;

  5. 注释应放在靠近被注释的代码附近,注释应简洁、精练;

  6. 过程、任务、函数的注释应放在其代码之前,不应插入其中,避免中断代码的连贯性;

  7. 在结束标识符、关键字(end、endcase等)后注明当前结束哪一个程序段,注释与结束标识符之间留有适当的间隔;

  8. 局部区域内的注释应列对齐,以增强代码的可读性,如下图:

属性(Attributes)

(* begins an attribute, terminated by a *).

  • 属性指定Verilog的对象或语句的特殊属性,供特定软件工具(如逻辑综合)使用。属性是在Verilog-2001标准增加的。

  • 属性可以作为声明、module、语句或端口的前缀出现。

  • 属性可以作为运算符的或对函数调用的后缀出现。

  • 属性可以被赋值。如果未指定值,则默认值为1。

  • 可以指定多个属性,用逗号隔开。

  • Verilog-2001标准没有定义标准属性;软件工具或其他标准可以根据需要定义属性。

标识符(identifiers)

  • 标识符是用户在描述时给Verilog对象 定义的名称

  • 标识符必须以字母(a-z, A-Z)或( _ )开头,后面可以是字母、数字、$ 或 _ 。

  • 最长可以是1023个字符;标识符区分大小写,sel和SEL是不同的标识符;模块、端口和实例的名称都是标识符

有效标识符举例:
       shift_reg_a
       busa_index
       _bus3
无效标识符举例:
      34net        // 开头不是字母或“_”
      a*b_net   // 包含了非字母或数字, “$” “_”
      n@238     //包含了非字母或数字, “$” “_”
Verilog区分大小写,所有Verilog关键词使用小写字母。
转义符,主要是EDA工具使用
      \34net     // 合法
      \a*b_net   // 合法
      \n@238     // 合法

某标准中的信号命名规则

  1. 信号、变量名称不应超过32个(英文/数字)字符;

  2. 信号、变量名称一律采用小写英文字母或数字;clk_32M, mux4x1

  3. 同一个时钟源驱动的时钟在不同模块和设计层级采用相同的时钟信号名称;

  4. 使用同一复位信号的所有模块采用一致的复位信号名称;

  5. 定义总线时,采用一致的位排列顺序。对于Verilog,使用[x:0];

  6. 低电平有效信号名称后缀为“_n”。例如:enable_n,reset_n;

  7. 时钟信号名称前缀为“clk”。例如:clk_cpu;

  8. 复位信号名称前缀为“rst”。例如:rst_n;

  9. 三态信号名称后缀为“_z”。例如:data_z;大写?

  10. 异步信号名称后缀为“_a”。例如:strobe_a;asyn

  11. 寄存器输出信号名称后缀为“_r”。例如:count_r;reg

  12. 状态变量命名使用不同的后缀。例如_cs当前状态,_ns下一状态;

  13. 寄存器数据输入信号如果与寄存器同名,则添加后缀“_i”或者“_in”;

  14. 测试相关信号名称后缀为“_t”;

  15. 模块输入信号添加前缀“_i”,输出信号添加前缀“_o”。

整数常数和实数常数

Verilog中,常量(literals)可是整数也可以是实数

  • 整数的大小可以定义也可以不定义。整数表示为:
 <size>'<base><value>

​ 其中 size :十进制数表示的二进制位数(bit),缺省为32位

​ base:数基,可为b、o、d、h进制,缺省为10进制

​ value:是所选数基内任意有效数字,包括X、Z。

  • 实数常量可以用十进制或科学表示法表示。

  • 整数的大小可以定义也可以不定义。整数表示为:

    • 数字中(_)忽略,便于阅读,但不能出现在数字的首位。
    • 没有定义大小(size)整数缺省为32位
    • 缺省数基为十进制
    • 数基(base)和数字(16进制)中的字母无大小写之分
    • 当数值value大于指定的位数时,截去高位。如 2’b1101表示的是2’b01;小于指定的位数?
  • 实数常量

    • 实数可用科学表示法或十进制表示
    • 科学表示法表示方式:

    ​ <尾数><e或E><指数>, 表示: 尾数×10^指数

字符串(string)

Verilog中,字符串大多用于显示信息的命令中。Verilog没有字符串数据类型.

  • 字符串要在一行中用双引号括起来,不能跨行

  • 字符串中可以使用一些C语言转义(escape)符,如\t \n

  • 可以使用一些C语言格式符(如%b)在仿真时产生格式化输出:

​ ” This is a normal string”

​ ”This string has a \t tab and ends with a new line\n”

​ ”This string formats a value: val = %b”

转义符及格式符将在验证支持部分讨论

格式符

%h %o %d %b %c %s %v %m %t
hex oct dec bin ACSII string strength module time

转义符

\t \n \ \” <1-3 digit octal number>
tab 换行 反斜杠 双引号 ASCII representation of above

格式符%0d表示没有前导0的十进制数

输入输出信号及数据类型

三种端口:

  • input
  • output
  • inout

三类(class)数据类型:

  • net(连线) : 表示器件之间的物理连接

  • register(寄存器) :表示抽象存储元件

  • parameter(参数) : 运行时的常数(run-time constants)

module DFF (
  // 端口说明
    output reg    q   ,
    output wire  qb ,
    input   wire  d   ,     // input data
                  clk,     /*input clock */ 
                  clr
    );
/*
  clk is posedge and clr is active low 
*/
    assign qb = !q;
    always @(posedge clk , negedge clr)
        if(!clr)
    q <= 0;
        else
    q <= d;
endmodule

net的分类

  • 有多种net类型用于设计(design-specific)建模和工艺(technology-specific)建模

  • 没有声明的net的缺省类型为 1 位无符号wire类型。但这个缺省类型可由下面的编译指令改变:
   `default_nettype <nettype>

register类的类型

  • 寄存器类有五种数据类型

  • 不要混淆寄存器数据类型与结构级存储元件,如udp_dff

Verilog中net和register声明语法

举例:

    reg                a;              //一个标量寄存器
    wand            w;              // 一个标量wand类型net
    wire signed [3 : 0] d;     // 一个标量wand类型net
    reg   [ 3 :  0]  v;              // 从MSB到LSB的4位寄存器向量
    reg   signed [ 7 : 0]  m, n;         // 两个有符号的8位寄存器
    tri     [15:  0]  busa;        // 16位三态总线
    wire      [ 0: 31]  w1, w2;    // 两个32位wire,MSB为bit0
    integer  i;                               // 32位整数i

选择正确的数据类型

  • 输入只能是net
  • 输出没有限制
  • 过程赋值必须reg

参数(parameters)

  • 用参数声明一个可变常量,常用于定义延时及宽度变量。

  • 参数定义的语法:parameter <赋值语句列表>;

  • 可一次定义多个参数,用逗号隔开。

  • 在使用常数(literal)的地方都可以使用参数。

  • 参数的定义是局部的,只在当前模块中有效。

  • 参数定义可使用以前定义的整数和实数参数。

module DFF (q, qb, d, clk, clr);
    parameter n = 4,
           m = n;
    output [n - 1 : 0] q, qb;
    input   [m - 1 : 0] d;
    input  clk,  clr;
    reg [n – 1 : 0] q, qb;
    wire [m - 1 : 0] d;
    wire  clk, clr;

    assign qb = ~q;

    always @(posedge clk or negedge clr)
        if(!clr)
    q <= 0;
        else
    q <= d;

endmodule

参数重载(overriding)

Defparam语句(现在综合工具还不支持)

  • 可用defparam语句在编译时重载参数值。

  • defparam语句引用参数的层次化名称。

  • 使用defparam语句可单独重载任何参数值。

功能描述

持续赋值(continuous assignment)

  • 描述的是组合逻辑

  • 在过程块外部使用。

  • 对net类型的信号赋值。

  • 在等式左边可以有一个简单延时说明。

  • 只限于在表达式左边用#delay形式

  • 可以是显式或隐含表示。

语法:

<assign> [#delay] [strength] <net_name> = <expressions>;
wire     out;
assign #2 out  = a & b; // 显式
wire     inv  = ~in; // 隐含

过程块语句

块语句用来将多个语句组织在一起,使得他们在语法上如同一个语句。

块语句分为两类:

  • 顺序块:语句置于关键字begin和end之间,块中的语句以顺序方式执行。

  • 并行块:关键字fork和join之间的是并行块语句,块中的语句并行执行。

  • Fork和join语句常用于test bench描述。这是因为可以一起给出矢量及其绝对时间,而不必描述所有先前事件的时间。

过程块(procedural block)

过程语句有两种:

  • initial :只执行一次

  • always :循环执行

所有过程在时间0执行一次

  • 过程块之间

  • assign语句之间并行执行

  • 过程块与assign语句

过程赋值(procedural assignment)

  • 在过程块中的赋值称为过程赋值。

  • 表达式左边的信号必须是寄存器类型(如reg类型)

  • 等式右边可以是任何有效的表达式,数据类型也没有限制。

  • 如果信号没有声明则缺省为wire类型。使用过程赋值语句给wire赋值会产生错误。

持续赋值描述

module adder (
      input  wire           a    , 
                                   b    ,
                                   cin  ,
      output wire [1:0]   out
      );
//     reg               half_sum;

    assign  half_sum   = a ^ b ^ cin ; // OK
    assign  half_carry  = a & b | a & !b & cin | !a & b & cin ; 
    assign  out             = {half_carry, half_sum} ;
/*
   always @( a or b or cin)    begin
      half_sum  = a ^ b ^ cin ; // OK
      half_carry = a & b | a & !b & cin | !a & b & cin ; 
      out            = {half_carry, half_sum} ;
   end
*/
endmodule

过程时序控制的种类

在过程块中可以说明过程时序。过程时序控制有三类:

  • 简单延时(#delay):延迟指定时间步后执行

  • 边沿敏感的时序控制:@(<signal>)

    • 在信号发生翻转后执行。
    • 可以说明信号有效沿是上升沿(posedge)还是下降沿(negedge)。
    • 可以用关键字 or 或 , 指定多个参数。
  • 电平敏感的时序控制:wait(<expr>)

    • 直至expr值为真时(非零)才执行。
    • 若expr已经为真则立即执行。
module wait_test;
    reg clk,  waito,  edgeo;
    initial begin clk=0; edgeo=0; waito=0; end
    always #10 clk = ~clk;
    always @(clk)  #2 edgeo = ~edgeo;
    always wait(clk)  #2  waito = ~waito;
endmodule

简单延时

在test bench中使用简单延时(#延时)施加激励,或在行为模型中模拟实际延时。

module muxtwo (
      input     a, b, sl;
      output  reg out
     );
always @( sl or a or b)
      if (! sl)
            #10 out = a; 
// 从a到out延时10个时间单位
      else
            #12 out = b;
//从b到out延时12个时间单位
endmodule

编译指令(Compiler Directives)

  • ( `)符号说明一个编译指令

  • 这些编译指令使仿真编译器进行一些特殊的操作

  • 编译指令一直保持有效直到被覆盖或解除

  • `resetall 复位所有的编译指令为缺省值,应该在其它编译指令之前使用

`timescale

  • `timescale 说明时间单位及精度

    格式:`timescale /

    如:`timescale 1 ns / 100 ps

​ time_unit: 延时或时间的测量单位

​ time_precision: 延时值超出精度要先舍入后使用

  • `timescale必须在模块之前出现

  • time_precision不能大于time_unit

  • time_precision和time_unit的表示方法:integer unit_string

    • integer : 可以是1, 10, 100
    • unit_string: 可以是s(second), ms(millisecond), us(microsecond), ns(nanosecond), ps(picosecond), fs(femtosecond)
    • 以上integer和unit_string可任意组合
  • precision的时间单位应尽量与设计的实际精度相同。

    • precision是仿真器的仿真时间步。
    • 若time_unit与precision_unit差别很大将严重影响仿真速度。
    • 如说明一个timescale 1s / 1ps,则仿真器在1秒内要扫描其事件序列1012次;而timescale 1s/1ms则只需扫描103次。
  • 如果没有timescale说明将使用缺省值,一般是s。

边沿敏感时序

时序控制@可以用在RTL级或行为级组合逻辑或时序逻辑描述中。可以用关键字posedgenegedge限定信号敏感边沿。敏感表中可以有多个信号,用关键字or或 ,连接。

module reg_ adder (out, a, b, clk);
      input clk;
      input [2: 0] a, b;
      output [3: 0] out;
      reg [3: 0] out;
      reg [3: 0] sum;
   always @( a or b) // 若a或b发生任何变化,执行
      #5 sum = a + b;
   always @( posedge clk) // 在clk上升沿执行
           out = sum;
endmodule

注:事件控制符or和位或操作符|及逻辑或操作符||没有任何关系。

注2:如果信号列表中一个信号指定了边沿,则其它信号也必须指定边沿。

module reg_adder (
      input wire clk, rst;
      input wire [2 : 0] a, b;
      output [3: 0] out
      );
      reg [3: 0]  sum;
/*
     always @( posedge clk, rst) // 错误
           if(!rst)  out <= 0;
           else       out <= a + b;
*/
     always @( posedge clk, negedge rst) // 正确
           if(!rst)  out <= 0;
           else       out <= a + b;
endmodule

wait语句

wait用于行为级代码中电平敏感的时序控制。

下面 的输出锁存的加法器的行为描述中,使用了用关键字or的边沿敏感时序以及用wait语句描述的电平敏感时序。

module latch_adder (
      input  wire            enable,
      input  wire  [2: 0]  a, 
                           b,
      output  reg [3: 0]   out
      );
   always @( a or b)   begin
            wait (!enable)   // 当enable为低电平时执行加法
            out = a + b;
    end
endmodule

注:综合工具还不支持wait语句。

条件语句(if分支语句)

ifif-else 语句:

always #20
      if (index > 0) // 开始外层 if
               if (rega > regb) // 开始内层第一层 if
                     result = rega;
                  else
                     result = 0; // 结束内层第一层 if
          else if (index == 0)  
                   begin
                      $display(" Note : Index is zero");
                      result = regb;
                   end
                else
                    $display(" Note : Index is negative");
  • 可以多层嵌套。在嵌套if序列中,else和前面最近的if相关。

  • 为提高可读性及确保正确关联,使用begin…end块语句指定其作用域。

条件语句(case分支语句)

在Verilog中重复说明case项是合法的,因为Verilog的case语句只执行第一个符合项。

module compute (
    input wire  [7: 0] rega     , 
                       regb     ,
    input wire  [2: 0] opcode ,
    output  reg [7: 0] result
    );

always @( rega , regb , opcode)
      case (opcode)
            3'b000 :   result = rega + regb;
            3'b001 :   result = rega - regb;
            3'b010 ,                 // 多个case有同一个结果
            3'b100 :   result = rega / regb;
            default :  begin
                 result = 'bx;
                 $display (" no match");
             end
       endcase
endmodule

case语句是测试表达式与另外一系列表达式分支是否匹配的多路条件语句。

  • case语句进行逐位比较以求完全匹配(包括x和z)。

  • default语句可选,在没有任何条件成立时执行。此时如果未说明default,Verilog不执行任何动作。

  • 多个default语句是非法的。

重要内容:

使用default语句是一个很好的编程习惯,特别是用于检测x和z。

Casez和casex为case语句的变体,允许比较无关(don‘t-care)值。

  • case表达式的任何位为无关值时,在比较过程中该位不予考虑。

  • 在casez语句中,? 和 z 被当作无关值。

  • 在casex语句中,?,z 和 x 被当作无关值。

case (表达式)
        <表达式>, <表达式>:赋值语句或空语句;
        <表达式>, <表达式>:赋值语句或空语句;
                                default:赋值语句或空语句;
endcase

casez语句

module coding3_8(
    input wire [7:0] a,
    output reg [2:0] o
    );

    always @(a)
        casez(a)
            8'b1???_????:    o = 3'b111;
            8'b01??_????:    o = 3'b110;
            8'b001?_????:    o = 3'b101;
            8'b0001_????:    o = 3'b100;
            8'b0000_1???:    o = 3'b011;
            8'b0000_01??:    o = 3'b010;
            8'b0000_001?:    o = 3'b001;
            8'b0000_0001:    o = 3'b000;
            default:         o = 3'bx;
        endcase
endmodule

casex语句

module coding3_8(
    input wire [7:0] a,
    output reg [2:0] o
    );

    always @(a)
        casex(a)
            8'b1xxx_xxxx:    o = 3'b111;
            8'b01xx_xxxx:    o = 3'b110;
            8'b001x_xxxx:    o = 3'b101;
            8'b0001_xxxx:    o = 3'b100;
            8'b0000_1xxx:    o = 3'b011;
            8'b0000_01xx:    o = 3'b010;
            8'b0000_001x:    o = 3'b001;
            8'b0000_0001:    o = 3'b000;
            default:                o = 3'bx;
        endcase
endmodule

循环(looping)语句

循环(looping)语句-repeat

repeat:将一块语句循环执行确定次数。

repeat (次数表达式) 语句

module multiplier #(
      parameter size = 8
      ) (
      input   wire [size : 1]        op_a, op_b,
      output  reg [2 * size : 1]   result
      );
      reg [2 * size : 1] shift_opa;
      reg [size : 1] shift_opb;
      always @( op_a or op_b) begin
            result = 0;
            shift_opa = op_a; // 零扩展至16位
            shift_opb = op_b;
            repeat (size) begin
                  #10 if (shift_opb[1]) result = result + shift_opa;
                  shift_opa = shift_opa << 1; // Shift left
                  shift_opb = shift_opb >> 1; // Shift right
            end
      end
endmodule

上述程序是模拟手工乘法

    5  0 1 0 1   opa
    3  0 0 1 1   opb -> shift_opb
----------------------
       0 1 0 1   shift_opa
     0 1 0 1        
   0 0 0 0
 0 0 0 0
----------------------
0 0 0 0 1 1 1 1    15 <- result

循环语句–while

while:只要表达式为真(不为0),则重复执行一条语句(或语句块)

module tmp(
     input wire [7:0] a,
     output reg [3:0] count
     );
    reg [7: 0] tempreg;

     always @(*) begin
          count = 0;
          tempreg = a;
          while (tempreg) // 统计tempreg中 1 的个数
          begin
                if (tempreg[ 0]) count = count + 1;
                tempreg = tempreg >> 1; // 右移
          end
    end
endmodule

注:While(A): A如果是个二进制串,中每一位都不为1的时候跳出

循环语句–forever

forever:一直执行到仿真结束

​ forever应该是过程块中最后一条语句。其后的语句将永远不会执行。

​ forever语句不可综合,通常用于test bench描述。

...
reg clk;
initial
      begin
      clk = 0;
      forever
            begin
                 #10 clk = 1;
                 #10 clk = 0;
      end
end
...
  • 这种行为描述方式可以非常灵活的描述时钟,可以控制时钟的开始时间及周期占空比。仿真效率也高。

  • Forever 可以实现各种占空比的时钟信号

循环语句–for

for:只要条件为真就一直执行

条件表达式若是简单的与0比较通常处理得更快一些。但综合工具可能不支持与0的比较。(循环变量可以声明为integer或reg矢量)

integer index, i, j;
// X检测
Initial begin
    for (index = 0; index < size; index = index + 1)
          if (val[ index] === 1'bx)
                $display (" found an X");

    // 存储器初始化; “!= 0”仿真效率高
    for (i = size; i != 0; i = i - 1)
          memory[ i- 1] = 0;

    // 阶乘序列
    factorial = 1;
    for (j = num; j != 0; j = j - 1)
          factorial = factorial * j;
end

注意:verilog 不支持 i++ 的写法, i=i+1 代替

模块实例化(module instances

通过模块实例化构造设计的层次体系

Verilog操作符

操作符类型

下表以优先级顺序列出了Verilog操作符。注意“与”操作符的优先级总是比相同类型的“或”操作符高。本章将对每个操作符用一个例子作出解释。

变量的位数与符号

  • Verilog根据表达式中变量的长度对表达式的值自动地进行调整。

  • Verilog自动截断或扩展赋值语句中右边的值以适应左边变量的长度。

  • 当一个负数赋值给无符号变量如reg时,Verilog自动完成二进制补码计算

module sign_size;
     reg [3:0]  a, 
                b;
     reg [15:0] c;
   initial begin
         a = -1;   // a是无符号数,因此其值为4’b1111
         b = 8; c = 8;  // b = c = 4’b1000
     #10 b = b + a; // 结果4’b10111截断, b=4’b0111
     #10 c = c + a; // c = 16’b0000_0000_0001_0111
     #10 c = b + a;
   end
endmodule
module sign_size;
     reg signed [ 3 : 0] a;
     reg signed [15 : 0] c;
initial begin
       a = -1; 
       c  = 8; 
       c  = c + a;
   end
endmodule

算术操作符

+    加
-    减
*    乘
/    除
%    模
  • 将负数赋值给reg或其它无符号变量使用2的补码算术。

  • 如果操作数的某一位是x或z,则结果为x

  • 在整数除法中,余数舍弃

  • 模运算中使用第一个操作数的符号

module arithops;
      parameter five = 5;
      integer ans, int;
      reg [3: 0] rega, regb;
      reg [3: 0] num;
   initial begin
      rega = 3;
      regb = 4'b1010;
      int    = -3;     //int = 1111……1111_1101
   end
   initial begin
      #10 ans   = five * int;         // ans = -15
      #10 ans   = (int + 5) / 2;     // ans = 1
      #10 ans   = five / int;          // ans = -1
      #10 num = rega + regb;    // num = 1101
      #10 num = rega + 1;          // num = 0100
      #10 num = int;                   // num = 1101
      #10 num = regb % rega;   // num = 1
      #80 $finish;
   end
endmodule

注意:integer和reg类型在算术运算时的差别。integer是有符号数,而reg是无符号数。

除法及模余

`timescale 1 ns / 1 ns

module divider ();
    integer a, b;
    initial begin
        a = 7; b = 4;
        $display("%0d/%0d quot and rem is %0d %0d", a, b, a/b, a%b);
        a = 7; b = -4;
        $display("%0d/%0d quot and rem is %0d %0d", a, b, a/b, a%b);
        a = -7; b = 4;
        $display("%0d/%0d quot and rem is %0d %0d", a, b, a/b, a%b);
        a = -7; b = -4;
        $display("%0d/%0d quot and rem is %0d %0d", a, b, a/b, a%b);
        $finish;
    end
endmodule

7/ 4 quotient and remainder is 1 3

7/-4 quotient and remainder is -1 3

-7/ 4 quotient and remainder is -1 -3

-7/-4 quotient and remainder is 1 -3

  • 在整数除法中,余数舍弃

  • 模运算中使用第一个操作数的符号

有符号数与无符号数混合运算

`timescale 1ns/1ns
module operate ();
    reg signed[3: 0] rega;
    reg            [3: 0] regb;
    reg signed[7: 0] regc;
    initial begin
         rega = 4'b1001;      // -8'd7, 0扩展至8'b0000_1001
         regb = 4'b1110;      // 8'd14, 0扩展至8'b0000_1110
         regc = 8'b0;
         #100    regc = rega + regb;  //regc = 8'b0001_0111
         $display($time," regc = %d %b", regc, regc); 
        #100 $finish;
    end
endmodule
  • 进行有有符号数计算,强制regb 是有符号数

    如果在设计中regb虽然声明为无符号数,但它实际上是有符号数。在这种情况下,我们需要使用系统函数$signed将其转换为有符号数再参与计算。

`timescale 1ns/1ns
module operate ();
    reg signed[3: 0] rega;
    reg            [3: 0] regb;
    reg signed[7: 0] regc;
    initial begin
         rega = 4'b1001;      //-8'd7, 符号扩展至8'b1111_1001
         regb = 4'b1110;      //-8'd2, 符号扩展至8'b1111_1110
         regc = 8'b0;
         #100    regc = rega + $signed (regb) ;  //regc = 8'b1111_0111
         $display($time," regc = %d %b", regc, regc); 
        #100 $finish;
    end
endmodule
  • 进行有符号数计算,但regb 是无符号数,即正数

    如果无符号数要与有符号数进行计算,由于无符号数是正数,因此我们需要将无符号数扩展1位,即高位补0,这个补入的0作为符号参与计算。如前例中,regb是一个无符号数,即正数,要参与计算需先0扩展,再转换为有符号数,如下例所示,regc的计算结果为7。

`timescale 1ns/1ns
module operate ();
    reg signed[3: 0] rega;
    reg            [3: 0] regb;
    reg signed[7: 0] regc;
    initial begin
         rega = 4'b1001;      //-8'd7, 符号扩展至8'b1111_1001
         regb = 4'b1110;      // 8'd14, 符号扩展至8'b0000_1110
         regc = 8'b0;
         #100    regc = rega + $signed ({1’b0, regb}) ;  //regc = 8'b0000_0111
         $display($time," regc = %d %b", regc, regc); 
        #100 $finish;
    end
endmodule 

SYNOPSYS DesignWare Library

  • DesignWare Library是一组可重用的、可综合的IP块,集成在Synopsys综合环境中。

  • 缺省的DesignWare库是standard.sldb,包括:

    • adder: + , +1
    • substractor: -, -1
    • comparator: ==, !=, <, <=, >, >=
    • mulpilier, divider
    • sin cos tan
    • Sqrt FIR IIR

Usage Through Operator Inferencing

module DW01_add_oper (in1,  in2, sum); 
    parameter wordlength = 8; 
    input [wordlength-1:0] in1, in2; 
    output [wordlength-1:0] sum; 

     assign sum = in1 +  in2;
endmodule
module DW01_add_inst( inst_A, inst_B, inst_CI, SUM_inst, CO_inst ); 
    parameter width = 8; 
    input [width-1 : 0] inst_A; 
    input [width-1 : 0] inst_B; 
    input inst_CI; 
    output [width-1 : 0] SUM_inst; 
    output CO_inst; 

// Instance of DW01_add 
DW01_add #(width) 
           U1 (.A(inst_A), .B(inst_B), .CI(inst_CI), .SUM(SUM_inst), .CO(CO_inst) );

endmodule

加法器设计

行波进位加法器(RPL/RCA)

超前进位加法器

CLA(Carry Look-ahead Adder)

4位超前进位加法器(CLA)

16位CLA加法器

块间超前进位加法器

快速16位CLA加法器

16位选择进位加法器(CSA)

CSA(Carry Select Adder)

定点乘法器设计

  • 乘法器是高性能微处理器中的关键部件,是进行高速计算特别是信号处理等方面应用时所必须的。

乘法计算方法

  • 乘法计算过程
    • 部分积产生
    • 部分积相加

乘法运算的关键

  • 要提高乘法计算速度,需要:
    • 加快部分积的形成
    • 减少部分积数目
      • 采用多位扫描、跳过连续的0/1串和对乘数重编码(如Booth算法)等处理方法
    • 加快部分积加法运算的速度
      • 一般是利用进位保留加法器(CSA, Carry Save Adder )先使参与操作的部分积形成两个数(这两个数分别是伪和与局部进位)

二阶Booth算法

67×67位乘法器的改进四阶Booth算法实现西安交通大学电子与信息工程学院

加法器树-保留进位加法器

  • 基本的加法器单元多采用(2,2)计数器(半加器)、(3,2)计数器、(5,3)计数器和(7,3)计数器等

加法器树—4-2压缩加法器

  • 4-2压缩器的加法器单元在乘法器的设计中得到了广泛采用。它可以较快地完成中间伪和的产生,而逻辑又比较简单

加法器树—线性阵列

  • 结构最简单规整,速度最慢,需要8级4-2加法器串联

    18个部分积:18-4-2-2-2-2-2-2-2=0,共8级

!

加法器树—Wallace Tree

  • 速度最快,但结构不规则

    18=4×4+2 -> 8+2 =4×2+2 -> 4+2 -> 2+2 -> 2, 共4级

加法器树—折衷结构

  • 32位乘法器只有18个部分积,可以采用折衷结构:在速度和规则性进行折衷

    共5级

乘法器结构示意图

二级流水乘法器

module DW02_mult_2_stage_inst  #(
        parameter  A_width = 8, 
                            B_width = 8  
    )( 
        input [A_width-1 : 0] inst_A, 
        input [B_width-1 : 0] inst_B, 
        input                            inst_TC, 
        input                            inst_CLK, 
        output [A_width+B_width-1 : 0] PRODUCT_inst
    ); 
      // Instance of DW02_mult_2_stage 
    DW02_mult_2_stage #(A_width, B_width) U1 ( 
    .A     (inst_A        ), 
    .B       (inst_B        ), 
    .TC    (inst_TC    ), 
    .CLK    (inst_CLK    ),
    .PRODUCT(PRODUCT_inst) 
    );
endmodule

除法器设计

串行除法- 恢复余数除法

恢复余数除法(Restoring Division Algorithm):

  1. 将被除数最高位移入,作为被减数R

  2. R = R – D。如果R为负,恢复被减数R。若R为正,则保留余数。

恢复余数除法计算流程

不恢复余数除法(Non-RDA)

脉动阵列除法-加法单元

并行除法器

算术运算的性能

  • SMIC 130nm CMOS工艺32位加法器

  • 门数是以最小2输入与非门的面积统计的(面积:5.0922)

最慢 6ns 3ns 最快
面积(um2) 1071 1468 1660 3792
门数 210 288 326 745
速度(ns) 13.56 6 3 1.23
  • SMIC 130nm CMOS工艺32位 乘法器
最慢 8ns 4ns 最快
面积(um2) 14003 15140 28215 36268
门数 2750 2793 5541 7122
速度(ns) 15 7.75 3.83 3.31

按位逻辑操作符

~    not
&    and
|    or
^    xor
~ ^    xnor
^ ~    xnor

按位操作符对矢量中相对应位运算。

regb = 4'b1 0 1 0
regc = 4'b1 x 1 0
num = regb & regc = 1 0 1 0 ;

位值为x时不一定产生x结果。如#50时的or计算。

当两个操作数位数不同时,位数少的操作数零扩展到相同位数。

a = 4'b1011;
b = 8'b01010011;
c = a | b; // a零扩展为 8'b00001011
module bitwise ();
      reg [3: 0] rega, regb, regc;
      reg [3: 0] num;
   initial begin
      rega = 4'b1001;
      regb = 4'b1010;
      regc = 4'b11x0;
   end
   initial begin
      #10 num = rega & 0;         // num = 0000
      #10 num = rega & regb;   // num = 1000
      #10 num = rega  |  regb;   // num = 1011
      #10 num = regb & regc;   // num = 10x0
      #10 num = regb   | regc;    // num = 1110
    #10 num = rega  |  regc;    // num = ?
      #10 $finish;
   end
endmodule
module bitwise (
      input wire [3: 0] rega , 
                                  regb , 
                                  regc ,
      output reg [3: 0] num
      );
    always @(rega, regb, regc) begin
         num = ~rega; 
         num =    rega & regb; 
         num =    rega ^ regb;          num =    rega |   regb    | regc; 
         num =    rega |   regb  & regc; 
         num = (  rega |  regb) & regc; 
    end
endmodule

逻辑操作符

!    not
&&    and
||    or
  • 逻辑操作符的结果为一位1,0或x。

  • 逻辑操作符只对逻辑值运算。

  • 如操作数为全0,则其逻辑值为false

  • 如操作数有一位为1,则其逻辑值为true

  • 若操作数只包含0、x、z,则逻辑值为x

逻辑反操作符将操作数的逻辑值取反。例如,若操作数为全0,则其逻辑值为0,逻辑反操作值为1。

module logical (
   input wire [3 : 0] rega , 
                                 regb , 
   output reg [3 : 0]  ans
   );

   always @(rega, regb) begin
      ans = regb && rega;
      ans = regb || rega;
      ans = ! regb
    end
endmodule

逻辑反与位反的对比

! logical  not    逻辑反
~   bit-wise not      位反

逻辑反的结果为一位1,0或x。位反的结果与操作数的位数相同

逻辑反操作符将操作数的逻辑值取反。例如,若操作数为全0,则其逻辑值为0,逻辑反操作值为1。

一元归约操作符

&    and
|    or
^    xor
~ ^    xnor
^ ~    xnor
  • 归约操作符的操作数只有一个。

  • 对操作数的所有位进行位操作。

  • 结果只有一位,可以是0, 1, X。

module reduction();
      reg val;
      reg [3: 0] rega, regb, regc;
      initial begin
            rega = 4'b0100;
            regb = 4'b1111;
      end
      initial begin
           #10 val = & rega ;     // val = 0
           #10 val =  |  rega ;      // val = 1
           #10 val = & regb ;     // val = 1
           #10 val =  |  regb ;     // val = 1
           #10 val =  ^ rega ;     // val = 1
           #10 val =  ^ regb ;     // val = 0
           #10 val = ~| rega;      // (nor) val = 0
           #10 val = ~& rega;    // (nand) val = 1
           #10 val = ^rega && &regb; // val = 1
                  $finish;
      end
endmodule
module reduction(
    input wire [3 : 0] rega , 
                                  regb , 
    output  reg           val
    );

    always @(rega, regb)  begin
           val = & rega ;
           val =  |   rega ;
           val = ^   rega ;
           val = ^   rega && & regb;
     end
endmodule

移位操作种类

>>      逻辑右移
<<      逻辑左移
>>>    算术右移
<<<    算术左移

第二个操作数(移位位数)是无符号数

若第二个操作数是x或z则结果为x

举例:

​ 1000_1110

​ 算术右移两位: 1110_0011

​ 逻辑右移两位: 0010_0011

​ 逻辑左移两位: 0011_1000

​ 循环右移两位: 1010_0011

若=左右符号位数不一致,则先全部扩展

建议:表达式左右位数一致

module shift ;
      reg [9: 0] num, num1;
      reg [7: 0] rega, regb;
   initial      rega = 8'b00001100; 
   initial begin
      #10 num = rega << 5 ;            // num = 01_1000_0000
      #10 regb = rega << 5 ;            // regb =      1000_0000
      #10 num = rega >> 3;             // num = 00_0000_0001
      #10 regb  = rega >> 3 ;            // regb =      0000_0001
      #10 num = 10'b11_1111_0000;
      #10 rega = num << 2;             //rega =       1100_0000
      #10  num1 = num << 2;          //num1=11_1100_0000
      #10 rega = num >> 2;             //rega =       1111_1100
      #10  num1 = num >> 2;//num1= ?
endmodule

  • 算术右移必须等式左右两边的变量都是有符号数,如果都是无符号数相当于逻辑右移,尽量不要在无符号数使用算术右移
  • 移位的电路实现

module shift (
      input  wire   [7 : 0] rega    , 
      output  reg   [7 : 0]  num1 ,
      input  wire   [ 2: 0]  index
       );         
    always @(rega, regb,  index)  begin
         num1 = rega <<    2;  
         num1 = rega >>    2 ;  
         num1 = rega >>>  2 ;  
         num1 = rega <<<  2 ;  
         num2 = rega >>  index ;  
     end
endmodule

循环右移

BS的实现(全译码)

  • 1位:8选1

  • 8位:8个8选1

  • 用管逻辑电路实现

循环右移

  • CMOS电路本质是对电容充放电的过程

逻辑右移

算术右移

右移控制

逻辑左移

全译码方式

  • 对表示移位次数的二进制位进行完全译码,分别给出各种移位的单独控制线。

  • 对于32位字长来说,移位部分有32根控制线SC31~ SC0分别控制移31~0位时的操作

  • 桶形移位寄存器,开关阵列规则,电路最规则,最慢

BS的实现(全编码)

  • 5个叠加就是要移位位数,电路最不规则,最快

BS的实现(部分译码)

  • 折衷:4×32和8×32的开关阵列组成,快位移每次固定移动四位

数据通路

桶式移位器BS(Barrel Shifter)

  • 是高速微处理器中的常用部件

  • 能在单周期内完成多种方式、各种位数的移位操作。

  • 用于实现移位指令、浮点计算中的小数点对齐等。

关系操作符

>    大于
<    小于
>=    大于等于
<=    小于等于

module relationals (
      input wire [3: 0] rega , 
                                   regb ,
      output reg           val
      );

   always @(rega, regb) begin
        val = rega >    regb ; //符号位0,且不是全零
        val = rega <    regb ; //符号位1
        val = rega = = regb ; //全0
        val = rega > = regb ; //符号位0
        val = rega <= regb ; //符号位1,或全零
   end
endmodule
  • 判断大小的电路是减法器来实现

  • 上述代码的实现电路需要5位来实现,增加一位符号位

相等操作符

  • == 的电路规模小

相同操作符

  • === 字母比较
  • === 唯一一个不能综合的操作符

条件操作符

  • 三态门实现

  • 数据总线上后来用二选一电路替代三态门

  • 三态门给双向数据端口建模

条件操作符的语法为:

   <LHS> = <condition> ? <true_expression> : <false_expression>

其意思是:if condition为真, 则 LHS=true_expression, 否则 LHS = false_expression

每个条件操作符必须有三个参数,缺少任何一个都会产生错误。

registger = condition ? true_value : false_value;

上式中,若condition为真则register等于true_value;若condition为假则register等于false_value。一个很有意思的地方是,如果条件值不确定,且true_value和false_value不相等,则

例如:assign out = (sel == 0) ? a : b;

若sel为0则out =a;若sel为1则out = b。如果sel为x或z,若a = b =0,则out = 0;若a≠b,则out值不确定。

级联操作符

  • 必须指定位数,不能缺省!如‘b0

复制操作符

拼接与复制

module replicate (
      input wire [1 : 0] rega ,
                                   regb ,
      output reg [3 : 0] out   , 
                                    bus
      );

   always @(rega, regb) begin
        bus = {2{ regb}}; 

        out =  {rega,  regb};
   end
endmodule

作业1

请使用verilog描述一个16位CSA加法器adder_16,如下图所示。其中:

a_in和b_in是输入数据,sum_o是和输出信号,

c_in是进位输入,c_o是进位输出信号。

要求:4位一组,组内使用CLA进位链。

仿真工具:

  • Modesim
  • ActiveHDL
  • iverilog

Verilog语言的结构级描述

术语及定义 (terms and definations)

  • 结构描述 : 用门及门的连接描述器件的功能

  • primitives(基本单元) : Verilog语言已定义的具有简单逻辑功能的功能模型(models)

结构描述

  • Verilog结构描述表示一个逻辑图
  • 结构描述用已有的元件构造。

  • 结构描述等价于逻辑图。它们都是连接简单元件来构成更为复杂的元件。Verilog使用其连接特性完成简单元件的连接。
  • 在描述中使用元件时,通过建立这些元件的实例来完成。
  • 上面的例子中MUX是没有反馈的组合电路,使用中间或内部信号将门连接起来。描述中忽略了门的实例名,并且同一种门的所有实例可以在一个语句中实例化。
  • 上面的锁存器(latch)是一个时序元件,其输出反馈到输入上。它没有使用任何内部信号。它使用了实例名并且对两个nor门使用了分开的实例化语句。

Verilog基本单元(primitives)

  • Verilog基本单元提供基本的逻辑功能,这些基本单元是预定义的,不需要用户定义。

  • 大多数ASIC和FPGA元件库是用这些基本单元开发的。基本单元库是自下而上的设计方法的一部分。

用于底层建模的基本单元

  • 上拉、下拉电阻:

    –pullup pulldown

  • MOS管单元:

    –cmos rcmos //r是带电阻的

    –pmos rpmos

    –nmos rnmos

  • 传输线单元:

    –tran rtran

    –tranif0 rtranif0 //带三态门

    –tranif1 rtranif1

基本单元的引脚 (pin)的可扩展性

  • 基本单元引脚数目由连接到门上的net的数量决定。因此当基本单元输入或输出的数量变化时用户不需要重定义一个新的逻辑功能。
  • 所有门(除了not和buf)可以有多个输入,但只能有一个输出。
  • not和buf门可以有多个输出,但只能有一个输入。

带条件的基本单元

  • Verilog有四种不同类型的条件基本单元

  • 这四种基本单元只能有三个引脚:output, input, enable

  • 这些单元由enable引脚使能。

    当条件基本单元使能信号无效时,输出高阻态Z。

  • 条件基本单元有三个端口:输出、数据输入、使能输入

基本单元实例化

  • 在端口列表中,先说明输出端口,然后是输入端口

  • 实例化时实例的名字是可选项

    ​ and (out, in1, in2, in3, in4); // unnamed instance

    ​ buf b1 (out1, out2, in); // named instance

  • 延时说明是可选项。所说明的延时是固有延时。输出信号经过所说明的延时才变化。没有说明时延时为0。

    ​ notif0 #3.1 n1 (out, in, cntrl); // delay specified

  • 信号强度说明是可选项

    ​ not (strong1, weak0) n1 (inv, bit); // strength specified

Intrinsic 反相器 Y=~A

惯性,小于#10不取反,起到过滤作用(惯性延迟)

Module实例化(module instantiation)

  • 模块实例化时实例必须有一个名字。
  • 使用位置映射时,端口次序与模块的说明相同。
  • 使用名称映射时,端口次序与位置无关
  • 没有连接的输入端口初始化值为x

实例数组(Array of Instances)

  • 实例名字后有范围说明时会创建一个实例数组。在说明实例数组时,实例必须有一个名字 (包括基本单元实例)。其说明语法为:

    ​ <模块名字> <实例名字> <范围> (<端口>);

  • 如果范围中MSB与LSB相同,则只产生一个实例。
  • 一个实例名字只能有一个范围。
  • 下面以模块comp为例说明这些情况

generate

逻辑强度(strength)模型

  • Verilog提供多级逻辑强度。

  • 逻辑强度模型决定信号组合值是可知还是未知的,以更精确的描述硬件的行为。

  • 下面这些情况是常见的需要信号强度才能精确建模的例子。

    –开极输出(Open collector output)(需要上拉)

    –多个三态驱动器驱动一个信号

    –MOS充电存储

    –ECL门(emitter dotting)

  • 逻辑强度是Verilog模型的一个重要部分。通常用于元件建模,如ASIC和FPGA库开发工程师才使用这么详细的强度级。但电路设计工程师使用这些精细的模型仿真也应该对此了解。

信号强度值系统

  • 用户可以给基本单元实例或net定义强度。

  • 基本单元强度说明语法:

    ​ <基本单元名> <强度> <延时> <实例名> (<端口>);

    例:nand (strong1, pull0) #( 2: 3: 4) n1 (o, a, b); // strength and delay

    ​ or (supply0, highz1) (out, in1, in2, in3); // no instance name

  • 用户可以用%v格式符显示net的强度值

    $monitor ($time,, " output = %v", f);

  • 电容强度(large, medium, small)只能用于net类型trireg和基本单元tran

    ​ 例如:trireg (small) tl;

Verilog多种强度决断

  • 在Verilog中,级别高的强度覆盖级别低的强度

仿真工具及testbench编写

  • 主要有三种仿真算法
    • 基于时间的(SPICE仿真器)
    • 基于事件的(Verilog-XL和NC Verilog仿真器)
    • 基于周期的(cycle)

仿真算法

  • 基于时间的算法用于处理连续的时间及变量

    • 在每一个时间点对所有电路元件进行计算
    • 效率低。在一个时间点只有约2~10%的电路活动
  • 基于事件的算法处理离散的时间、状态和变量

    • 只有电路状态发生变化时才进行处理,只模拟哪些可能引起电路状态改变的元件。仿真器响应输入引脚上的事件,并将值在电路中向前传播。
    • 是应用最为广泛的仿真算法
    • 效率高。“evaluate when necessary”
  • 基于周期的仿真以时钟周期为处理单位(与时间无关)

    • 只在时钟边沿进行计算,不管时钟周期内的时序
    • 使用两值逻辑 (1, 0)
    • 只关心电路功能而不关心时序,对于大型设计,效率高
    • 仅适用于同步电路。

基于事件仿真的时轮(time wheel)

  • 仿真器在编译数据结构时建立一个事件队列。
  • 只有当前时间片中所有事件都处理完成后,时间才能向前。
  • 仿真从时间0开始,而且时轮只能向前推进。只有时间0的事件处理完后才能进入下一时片。
  • 在同一个时间片内发生的事件在硬件上是并行的。
  • 理论上时间片可以无限。但实际上受硬件及软件的限制。

一个完整的简单例子 test fixture

  • 被测试器件DUT是一个二选一多路器。测试程序(test fixture)提供测试激励及验证机制。

  • Test fixture使用行为级描述,DUT采用门级描述。下面将给出Test fixture的描述、DUT的描述及如何进行混合仿真。

DUT 被测器件 (device under test)

  • a, b, sel是输入端口,out是输出端口。所有信号通过这些端口从模块输入/输出。

  • 另一个模块可以通过模块名及端口说明使用多路器。实例化多路器时不需要知道其实现细节。这正是自上而下设计方法的一个重要特点。模块的实现可以是行为级也可以是门级,但并不影响高层次模块对它的使用。

module MUX2_1 (
// Port declarations
    output wire out,
    input    wire    a, 
                             b, 
                             sel
);
       wire sel_, a1, b1;

        not  (sel_, sel);
        and (a1, a, sel_);
        and (b1, b, sel);
        or    (out, a1, b1);
endmodule

Test Fixture — 如何说明实例

Test fixture 激励描述

module testfixture;
 // Data type declaration
    reg   a, d, s;
    wire o;
 // MUX instance
    MUX2_1 mux (o, a, d, s);
 // Apply stimulus
    initial
    begin
               a = 0; d = 1; s = 0;
          #5 d = 0;
          #5 d = 1; s = 1;
          #5 a = 1;
          #5 $finish;
      end
 // Display results
endmodule

  • 例子中,a, d, s说明为reg类数据。reg类数据是寄存器类数据信号,在重新赋值前一直保持当前数据。
  • #5 用于指示等待5个时间单位。
  • $finish是结束仿真的系统任务。

Test Fixture 响应产生

Verilog提供了一些系统任务和系统函数,包括:

语言专用标记( tokens)

系统任务及函数

$<identifier>

  • $符号指示这是系统任务和函数

  • 系统函数有很多,如:

    • 返回当前仿真时间$time

    • 显示/监视信号值($display, $monitor)

    • 停止仿真$stop

    • 结束仿真$finish

      $monitor($time, “a = %b, b = %h”, a, b);

module DFF (q, qb, d, clk, clr);
  // 端口说明
    output q, qb;
    input d,  // input data
              clk, /*input clock */ clr;
    reg q;
    wire qb, d, clk, clr;
/*
  clk is posedge and 
  clr is active low 
*/
    assign qb = !q;
    always @(posedge clk or negedge clr)
        if(!clr)
    q <= 0;
        else
    q <= d;
endmodule

完整的Test Fixture

module testfixture;
  // 数据类型说明
    reg a, b, s;
    wire o;
  // MUX实例化
    MUX2_1 mux (o, a, b, s);
  // 施加激励
   initial begin
            a = 0; b = 1; s = 0;
        #5 b = 0;  
        #5 b = 1; s = 1;
        #5 a = 1;
        #5 $finish;
   end
// 显示结果
  initial
    $monitor($time,," o=%b a=%b b=%b s=%b", o, a, b, s);
endmodule

启动Verilog-XL

其它常用选项

  • verilog

    ​ 显示其帮助(所有可用选项)

  • verilog +gui

    ​ 启动图形界面

  • verilog –v –y:

    ​ 定义仿真库

  • verilog +define+<macro>

    ​ 重新定义宏变量

  • verilog +sdf_file

    ​ 重新指定sdf文件

波形显示工具—simvision

SHM:波形数据库

波形显示工具从数据库,如SHM数据库中读取数据。使用下面的系统任务可以对SHM数据库进行操作:

系统任务 描述
$shm_open(“waves.shm”); 打开一个仿真数据库。同时只能打开一个库写入。
$shm_probe(); 选择信号,当它们的值变化时写入仿真库
$shm_close; $shm_save; 关闭仿真库 将仿真数据库写到磁盘
initial
begin
    $shm_open(./data/lab.shm”);
    $shm_probe( );
end

用$shm_probe设置信号探针

  • $shm_probe的语法:

    $shm_probe(scope0, node0, scope1, node1, …);

    • 每个node都是基于前面scope的说明(层次化的)

    • scope参数缺省值为当前范围(scope)。node参数缺省值为指定范围的所有输入、输出及输入输出。

模块实例化

在$shm_probe中使用scope/node对作为参数。参数可以使用缺省值或两个参数都设置。例如:

  • $shm_probe( ); 观测当前范围(scope)所有端口

  • $shm_probe(“A”); 观测当前范围所有节点

  • $shm_probe(alu, adder); 观测实例alu和adder的所有端口

  • $shm_probe(“S”, top.alu, “AC”); 观测:

    (1): 当前范围及其以下所有端口,除库单元

    (2):top.alu模块及其以下所有节点,包括库单元

VCD数据库

Verilog提供一系列系统任务用于记录信号值变化保存到标准的VCD(Value Change Dump)格式数据库中。大多数波形显示工具支持VCD格式。

VCD数据库是仿真过程中数据信号变化的记录。它只记录用户指定的信号。

  • 用户可以用$dump系统任务打开一个数据库,保存信号并控制信号的保存。除$dumpvars外,其它任务的作用都比较直观。 *$dumpvars将在后面详细描述*。
  • 必须首先使用$dumpfile系统任务,并且在一次仿真中只能打开一个VCD数据库。
  • 在仿真前(时间0前)必须先指定要观测的信号,这样才能看到信号完整的变化过程。
  • 仿真时定期的将数据保存到磁盘是一个好的习惯,万一系统出现问题数据也不会全部丢失。
  • VCD数据库不记录仿真结束时的数据。因此如果希望看到最后一次数据变化后的波形,必须在结束仿真前使用$dumpall

要给$dumpvars提供层次(levels)及范围(scope)参数,例如

$dumpvars;   // Dump所有层次的信号
$dumpvars (1, top); // Dump top模块中的所有信号
$dumpvars (2, top.u1); // Dump实例top. u1及其下一层的信号
$dumpvars (0, top.u2, top.u1.u13.q); // Dump top.u2及其以下所有信号,以及信号top.u1.u13.q。
$dumpvars (3, top.u2, top.u1); // Dump top.u1和top.u2及其下两层中的所有信号。

用下面的代码可以代替前面test fixture的$monitor命令:

initial
        begin
            $dumpfile (“verilog.dump”);
            $dumpvars (0, testfixture);
    end

$dumpvars语法

练习

module adder_rpl(
    input wire [3 : 0]   a, 
              b,
    input wire              ci,
    output wire            co,
    output reg [3 : 0]   s
    );
    reg [3 : 0]  g, 
                      p;
    reg [4 : 0]  c;

    assign co = c[4];
    always @(a, b, ci) begin
        c[0] = ci;
        g     = a & b;
        p     = a ^ b;
        c[4:1] = g | (p & c[3:0]);
        s        = p ^ c[3:0];
    end
endmodule
module adder_rpl(
    input wire [3 : 0]   a, 
              b,
    input wire              ci,
    output wire            co,
    output reg [3 : 0]   s
    );
    reg [3 : 0]  g, 
                      p;
    reg [4 : 0]  c;
    integer  i;
    assign co = c[4];
    always @(a, b, ci) begin
        c[0] = ci;
        for(i = 0; i < n; i = i+1) begin
            g[i]     = a[i]&b[i];
            p[i]     = a[i]^b[i];
            c[i+1]   = g[i] | (p[i]&c[i]);
            s[i]     = p[i]^c[i];
         end
endmodule

请分别给出下面电路的结构级和行为级模型

ALU的功能

  • 算术运算

    • 加、减法运算
  • 逻辑运算

    • 按位逻辑计算
    • and nand or nor xor xnor buf not
    • 输出全0,全1

逻辑运算

如何实现逻辑运算?

算术运算

注:异或取反是同或,同或取反是异或

注:M是控制是逻辑运算还是算术运算,对于第一种方式:0:逻辑运算,1:算术运算

作业3

ALU计算益出的判断方法

Verilog的可综合描述风格

描述风格简介

  • 如果逻辑输出在任何时候都直接由当前输入组合决定,则为组合逻辑。

  • 如果输出在任何给定时刻不能由输入的状态决定,则为时序逻辑。

通常综合输出不会只是一个纯组合或纯时序逻辑。

   一定要清楚

​ 所写的源代码会综合出什么类型逻辑,至少要知道其拓朴结构

​ 要得到指定的逻辑结构,需要如何描述

这是非常重要的。

不支持的Verilog结构

持续赋值

持续赋值驱动值到net上。因为驱动是持续的,所以输出将随任意输入的改变而随时更新,因此将产生组合逻辑。

module orand (out, a, b, c, d, e);
     input a, b, c, d, e;
     output out;
     assign out = e & (a | b) & (c | d);
endmodule
module orand ( //另类的assign
    input wire     a, 
                en, 
    output wire out);
    assign out = en ? a : out;
endmodule

延迟赋值语句

语法: LHS = RHS;

  • 时序控制延迟的是赋值而不是右边表达式的计算。

  • 在延迟赋值语句中RHS表达式的值都有一个隐含的临时存储。

  • 可以用来简单精确地模拟寄存器交换和移位。

非阻塞过程赋值语句

阻塞过程赋值执行完成后再执行在顺序块内下一条语句。

非阻塞赋值不阻塞过程流,仿真器读入一条赋值语句并对它进行调度之后,就可以处理下一条赋值语句。

若过程块中的所有赋值都是非阻塞的,赋值按两步进行:

  1. 仿真器计算所有RHS表达式的值,保存结果,并进行调度在时序控制指定时间的赋值。
  2. 在经过相应的延迟后,仿真器通过将保存的值赋给LHS表达式完成赋值。

阻塞与非阻塞赋值语句行为差别举例

过程块语句

  • 任意边沿

    • 在所有输入信号的任意边沿进入的过程块称为组合块

    always @( a or b) // 与门

    y = a & b;

  • 单个边沿

    • 在一个控制信号的单个边沿上进入的过程块产生同步逻辑。这种过程块称为同步块。

    always @( posedge clk) // D flip-flop

    q <= d;

    • 同步块也可以对异步复位信号的变化产生敏感
always @( posedge clk or negedge rst_)
     if (! rst_)   
         q <= 0;
     else
         q <= d;

组合块描述风格

module DFF (q, qb, d, clk, clr);
    output q, qb;
    input d,  // input data
              clk, /*input clock */ clr;
    reg q;
    wire qb, d, clk, clr;

    assign qb = !q;

    always @(a, b, c…)    begin
        过程赋值语句;
        高级描述语句
            if语句;
            case语句;
            循环语句;
    end
endmodule
  • 任意边沿

    • 在所有输入信号的任意边沿进入的过程块称为组合块。

    ​ always @( a or b)

    ​ y = a & b;

    • 敏感列表要完全
    • 采用阻塞赋值语句
    • 可能产生组合逻辑或锁存器
    • 过程赋值语句产生组合逻辑
    • IF或CASE语句可能产生锁存器
    • 循环语句?

组合块:敏感列表要完全

在下面的例子,a, b, sl是块的输入

  • sl用作条件, a、b用在过程赋值语句的右边

将块的所有输入都列入敏感表是很好的描述习惯。不同的综合工具对不完全敏感表的处理有所不同。有的将不完全敏感表当作非法。其他的则产生一个警告并假设敏感表是完全的。在这种情况下,综合输出和RTL描述的仿真结果可能不一致。

2001标准对敏感列表的简化

条件语句

ifif-else 语句:

  • 可以多层嵌套。在嵌套if序列中,else和前面最近的if相关。

  • 为提高可读性及确保正确关联,使用begin…end块语句指定其作用域。

module compute (result, rega, regb, opcode);
input [7: 0] rega, regb;
input [2: 0] opcode;
output [7: 0] result;
reg [7: 0] result;
always @( rega or regb or opcode)
      case (opcode)
            3'b000 :   result = rega + regb;
            3'b001 :   result = rega - regb;
            3'b001 ,   // 多个case有同一个结果
            3'b010 :   result = rega / regb;
            default :  begin
                 result = 'bx;
                 $display (" no match");
             end
       endcase
endmodule

在Verilog中重复说明case项是合法的,因为Verilog的case语句只执行第一个符合项。

case、if语句可互相代替

会产生锁存器(latch)

在always块中,条件语句如果没有说明所有条件,即条件不完全,将产生latch。

在下面的例子中,由于没有定义enable为低电平时data的状态,因此enable为低电平时data的值必须保持,综合时将产生一个锁存器

module latch (
     input  wire     data, 
        enable,
     output reg      q
);

     always @( enable, data)
          if (enable)
               q = data;
endmodule

注:assign q = en?in,q //FPGA:锁存器; ASIC:反馈震荡电路

自然完全的条件语句

module comcase (
    input wire a, b, c, d,
    output reg e
    );

    always @( a or b or c or d)
        case ({ a, b})
            2'b11: e = d;
            2'b10: e = ~c;
            2'b01: e = 1'b0;
            2'b00: e = 1'b1;
        endcase
endmodule

module compif (
    input  wire a, b, c, d,
    output reg e
    );

    always @( a or b or c or d)
        if (a & b)
            e = d;
        else if (a & ~b)
            e = ~c;
        else if (~ a & b)
            e = 1'b0;
        else if (~ a & ~b)
            e = 1'b1;
endmodule

例中定义了所有可能的选项,综合结果是纯组合逻辑,没有不期望的锁存器产生。

不完全条件语句

在上面的例子中,当a变为零时,不对e赋新值。因此e保存其值直到a变为1。这是锁存器的特性。

default完全条件语句

​ 综合工具将 ‘bx作为无关值,因此if语句类似于“ full case”,可以进行更好的优化。

​ 例中没有定义所有选项,但对没有定义的项给出了缺省行为。同样,其综合结果为纯组合逻辑——没有不期望的锁存器产生。

指示完全条件语句

module dircase (a, b, c, d);
    input b, c;
    input [1: 0] a;
    output reg d;

    always @( a or b or c)
        case (a) // synopsys synthesis case = full    synopsys:公司的名字,综合指令指示完全,最好不要用,产生了工具依赖

            2'b00: d = b;
            2'b01: d = c;
        endcase
endmodule

和前例一样,没有定义所有case项,但综合指令通知优化器缺少的case项不会发生。结果也为纯组合逻辑,没有不期望锁存器产生。注意如果缺少的case项发生,而其结果未定义,综合结果和RTL的描述的行为可能不同。

综合指令

  • 大多数综合工具都能处理综合指令。

  • 综合指令可以嵌在Verilog注释中,因此他们在Verilog仿真时忽略,只在综合工具解析时有意义。

  • 不同工具使用的综合指令在语法上不同。但其目的相同,都是在RTL代码内部进行最优化。

  • 通常综合指令中包含工具或公司的名称。例如,下面介绍的Envisia synopsys synthesis工具的编译指示都以synopsys synthesis开头。

综合指令举例

  • 这里列出部分Cadence综合工具中综合指令。这些与其他工具,如Synopsys Design Compiler,中的指令很相似。

    // synopsys synthesis on

    // synopsys synthesis off

    // synopsys synthesis case = full, parallel, mux

  • 结构指令

    // synopsys synthesis architecture = cla or rpl

  • FSM指令

    // synopsys synthesis enum xyz

    // synopsys synthesis state_vector sig state_vector_ flag

综合指令 — case指示

  • case语句通常综合为一个优先级编码器,列表中每个case项都比后面的case项的优先级高。

  • case指令按下面所示指示优化器:

    • //synopsys synthesis case = parallel

      建立并行的编码逻辑,彼此无优先级。

    • //synopsys synthesis case = mux

      若库中有多路器,使用多路器建立编码逻辑。

    • //synopsys synthesis case = full

      假定所有缺少的case项都是“无关”项,使逻辑更为优化并避免产生锁存器。

条件完全的例外

有时使用了case full指示,case语句也可能综合出latch。

下面的描述综合时产生了一个latch。

要求:条件语句中各分支的赋值对象一致。

注:条件完全语句指的是同一组信号

带复位、置位的锁存器latch的描述示例

下面的例子给出了一个复杂一些的复位分支。由于是一个latch,因此敏感表是完全的。

module latch (
    input wire enable, d, set, clr,
    output reg q
    );

   always @( *)     begin
        if (set)
            q = 1;
        else if (clr)
            q = 0;
        else if (enable)
            q = d;
    end
endmodule

异端

module latch (
    input wire     enable , 
                d , 
    output reg q
);
    always @( *)
    q = enable ? d : q; 
endmodule


module latch (
    input wire     enable , 
                d , 
    output wire q
);
    assign q = enable ? d : q; 
endmodule

module latch (
    input wire     enable , 
                d , 
    output reg q
);
always @( *)
    if( enable )
        q = d ;
    else
        q = q; 
endmodule

条件互斥与条件非互斥

注:条件互斥产生并行电路,条件不互斥产生带优先级的串行电路

条件非互斥的if语句

电路结构

互斥的非互斥表达:

module single_if(a, b, c, d, sl, z);
    input a, b, c, d;
    input [1:0] sl;
    output reg z;
    wire [3:0] sel = sl == 2’b11 ? 4’b1000:
                              sl == 2’b10 ? 4’b0100:
                              sl == 2’b01 ? 4’b0010:
                                                       4’b0001;
    always @()  begin
            if (sel[3])            z = d;
            else if (sel[2])     z = c;
            else if (sel[1])     z = b;
            else if (sel[0])     z = a;
            else                     z = 0;
     end
endmodule

总结:If语句条件完全:逻辑组合电路,不完全:锁存器;如果完全,互斥,并行电路;完全但是不互斥,串行带优先级电路

for 循环语句

module adder_rpl(a, b, ci, co, s);
    parameter n = 4;
    input wire [n-1 : 0] a, b;
    input wire ci;
    output wire co;
    output reg [n-1 : 0] s;
    reg [n-1 : 0] g, p;
    reg [n : 0]  c;
    integer  I;
    assign co = c[n];
    always @(*) begin
        c[0] = ci;
        for(i = 0; i < n; i = i+1) begin
            g[i]     = a[i] & b[i];
            p[i]     = a[i] ^ b[i];
            c[i+1] = g[i] | (p[i] & c[i]);
            s[i]     = p[i] ^ c[i];
         end
      end
endmodule

非结构化的for循环

综合工具处理循环的方法是将循环内的结构重复。在循环中包含不变化的表达式会使综合工具花很多时间优化这些冗余逻辑。

同步块描述风格

module DFF (q, qb, d, clk, clr);
    output q, qb;
    input d,  // input data
              clk, /*input clock */ clr;
    reg q;
    wire qb, d, clk, clr;

    assign qb = !q;

    always @(posedge clk);
    begin
        过程赋值语句;
        高级描述语句
            if语句;
            case语句;
            循环语句;
    end
endmodule
  • 在一个控制信号的单个边沿上进入的过程块产生同步逻辑。这种过程块称为同步块

​ always @( posedge clk)

​ q <= d;

  • 同步块会产生D触发器:并不是所有在同步块中赋值的信号都会产生D触发器,这依赖于描述风格
  • 同步块RTL代码中使用非阻塞赋值
  • 组合块的RTL代码中使用阻塞赋值

阻塞、非阻塞语句区别

非阻塞赋值语句并行执行,因此临时变量不可避免地在一个周期中被赋值,在下一个周期中被采样。

非阻塞过程赋值区别

阻塞与非阻塞语句:不要混合使用

同步块中的条件语句-带使能的寄存器

  • 条件不完全的条件语句产生带使能端的D触发器条件完全的条件语句在D触发器前产生选择逻辑

  • 在寄存器的描述中,敏感列表是不完全的。综合时会产生warning

module dffn (
    input   wire  d   ,  
               clk , 
               en  ,
    output reg    q);

    always @( posedge clk)
        if (en)
            q <= d;
endmodule

复位

复位是可综合编码风格的重要环节。状态机中一般都有复位。

  • 同步复位描述:在同步块内,当复位信号有效时,进行复位操作;当复位信号无效时,执行该块的同步行为。如果将复位信号作为条件语句的条件,且在第一个分支中进行复位,综合工具可以更容易的识别复位信号。

  • 异步复位:在同步块的敏感表中包含复位信号的激活边沿。在块内,复位描述方式与同步方式相同。

`timescale 1ns / 1ns
module reset_tb;
    reg clk, d, rst ;
    wire q, qa;
    sync u0 ( clk, d, rst , q );
    async u1 ( clk, d, rst , qa );
    always #5 clk = !clk;
    initial begin
        clk = 0; d = 1;
        rst = 1;
    @(posedge clk);
    @(negedge clk);
        rst = 0;
    @(posedge clk);
    @(negedge clk);
        rst = 1;
        d = 1;
    @(negedge clk);
        rst = 0;
        d = 1; 
    @(negedge clk);
        #10 $finish;
end
endmodule

不好的复位描述方式

下面的异步复位描述(异步复位和同步块分开)是一种很不好的描述风格,并且有的综合工具不支持。在仿真中,如果r和ck在同一时刻改变,则结果不可确定。

综合工具不能胜任的工作

  • 时钟树
  • 复杂的时钟方案
  • 组合逻辑反馈循环和脉冲发生器
  • 存储器,IO
  • 专用宏单元
  • 做的和人工做的一样好

综合工具善于优化组合逻辑。但设计中有很大一部分不是组合逻辑。

  • 例如,时钟树。时钟树是全局的、芯片范围的问题。在没有版图布局信息的情况下,要给出较优的结果,综合工具对块的大小有一定的限制。
  • 综合工具不能很好地处理复杂时钟。通常,只允许要综合的块含有一个时钟。但设计中经常使用两相时钟或在双沿时钟。
  • 综合工具不易实现脉冲产生逻辑,如单个脉冲,或结果依赖于反馈路径延迟的组合反馈逻辑。对这种情况,插入延迟元件使一个信号推迟到达的效果并不好。
  • 不能用综合产生大块存储器,因为综合工具会用flip-flop实现。
  • 不是所有的综合工具都能很好地从工艺库里挑选择大的单元或宏单元,这需要用户人工实例化。一些宏单元,例如大的结构规则的数据通路元件,最好使用生产商提供的硅编译器产生。
  • 综合工具不保证产生最小结果。通常综合结果不如人工结果,只要你有足够的时间。

代码练习

练习1

CLA Adder 4bit RTL代码

`timescale 1ns/1ns
module CLA_Adder(
input wire [3:0] a ,
b ,
input wire ci,
output wire [3:0] s , 
output wire co 
);
wire [3:0] p , g;
wire [3:0] c;
assign g = a&b ;
assign p = a^b ;
assign c[0] = g[0]|(p[0]&ci) ;
assign c[1] = g[1]|(p[1]&g[0])|(p[1]&p[0]&ci) ;
assign c[2] = g[2]|(p[2]&g[1])|(p[2]&p[1]&g[0])|(p[2]&p[1]&p[0]&ci) ;
assign c[3] = g[3]|(p[3]&g[2])|(p[3]&p[2]&g[1])|(p[3]&p[2]&p[1]&g[0])|(p[3]&p[2]&p[1]&p[0]&ci);
assign co = c[3] ;
assign s[3] = p[3]^c[2];
assign s[2] = p[2]^c[1];
assign s[1] = p[1]^c[0];
assign s[0] = p[0]^ci ;
endmodule

ALU 4bit CLA RTL代码

module ALU_CLA4_G(
input wire [3:0] a ,
b ,
input wire ci ,
input wire M ,
input wire [3:0] S , 
output wire [3:0] Do , 
output wire co ,
output wire V ,
Z
);
reg [3 : 0] g , p ;
wire [3 : 0] G , P ;
wire [4 : 0] c ;
integer i;
always @(*)
    for( i = 0; i < 4; i = i + 1) begin
        g[i] =     ( S[3] & a[i] & b[i] ) ||
                ( S[2] & a[i] & !b[i] ) ||
                !M;
        p[i] = !( ( S[3] & a[i] & b[i] ) ||
                ( S[2] & a[i] & !b[i] ) ||
                ( S[1] & !a[i] & b[i] ) ||
                ( S[0] & !a[i] & !b[i] ) );
end

assign G[0] = g[0] ;
assign G[1] = g[1] | ( p[1] & G[0] ) ;
assign G[2] = g[2] | ( p[2] & G[1] ) ;
assign G[3] = g[3] | ( p[3] & G[2] ) ; 
assign P[0] = p[0] ;
assign P[1] = p[1] & P[0] ;
assign P[2] = p[2] & P[1] ;
assign P[3] = p[3] & P[2] ; 
assign c[0] = ci ;
assign c[4 : 1] = G | ( P & {4{c[0]}} );
assign Do = p ^ c[3 : 0];
assign co = c[4];
assign V = c[4] ^ c[3];
assign Z = ! ( | Do);
endmodule

ALU CLA 4bit 测试程序

`timescale 1ns/1ns
module ALU_CLA4_G_tb();
reg [3:0] a ,
b ;
reg ci;
reg M ;
reg [3:0] S ;
wire [3:0] Do ; 
wire co;
wire V ,
Z ;
ALU_CLA4_G uG(
.a ( a ),
.b ( b ),
.ci ( ci ),
.M ( M ),
.S ( S ), 
.Do ( Do ), 
.co ( co ),
.V ( V ),
.Z ( Z )
);

integer i;
reg error ;
integer func ;
reg [3 : 0] DO_ex;
reg [3 : 0] opA,
            opB;
initial begin
    //logic test
    func = 0;
    error = 0;
    a = 4'b1100;
    b = 4'b1010;
    ci= 1'b1;
    M = 1'b0;
    for(i = 0; i < 16; i = i+1) begin
        S = i;
        #10 if( Do != DO_ex) 
                error = 1;
            else
                error = 0;
end 

    //add test
func = func + 1;
S = 4'b1001; ci = 1'b0; M = 1'b1;
    for(i = 0; i < 256; i = i+1) begin
        {a, b} = i;
        #10 if(Do != DO_ex) 
                error = 1;
            else
                error = 0;
    end
//sub test
func = func + 1;
S = 4'b0110; ci = 1'b1; M = 1'b1;
a = 4'b1100;
b = 4'b1010;
#10 if(Do != DO_ex) 
        error = 1;
    else
        error = 0;


//carry test
func = func + 1;
S = 4'b1001; ci = 1'b0; M = 1'b1;
a = 4'b1100;
b = 4'b0110;
#10 if(co == 1'b0) 
        error = 1;
    else
        error = 0;
a = 4'b0100;
b = 4'b0110;
#10 if(co == 1'b1) 
        error = 1;
    else
        error = 0;

//V test
//a + b
// a = 4'b0111; b = 4'b0010 V = 1
// a = 4'b0111; b = 4'b1010 V = 0
// a = 4'b1100; b = 4'b1010 V = 1
// a = 4'b1100; b = 4'b0110 V = 0
func = func + 1;
S = 4'b1001; ci = 1'b0; M = 1'b1;
a = 4'b0111;
b = 4'b0010;
#10 if(V == 1'b0) 
        error = 1;
    else
        error = 0;
a = 4'b0111;
b = 4'b1010;
#10 if(V == 1'b1) 
        error = 1;
    else
        error = 0;
a = 4'b1100;
b = 4'b1010;
#10 if(V == 1'b0) 
        error = 1;
    else
        error = 0;

a = 4'b1100;
b = 4'b0110;
#10 if(V == 1'b1) 
        error = 1;
    else
        error = 0;
//a - b
// a = 4'b0111; b = 4'b0010 V = 0
// a = 4'b0111; b = 4'b1010 V = 1
// a = 4'b1100; b = 4'b1010 V = 0
// a = 4'b1100; b = 4'b0110 V = 1
func = func + 1;
S = 4'b0110; ci = 1'b1; M = 1'b1;
a = 4'b0111;
b = 4'b0010;
#10 if(V == 1'b1) 
        error = 1;
    else
        error = 0;
a = 4'b0111;
b = 4'b1010;
#10 if(V == 1'b0) 
        error = 1;
    else
        error = 0;

a = 4'b1100;
b = 4'b1010;
#10 if(V == 1'b1) 
        error = 1;
    else
        error = 0;
a = 4'b1100;
b = 4'b0110;
#10 if(V == 1'b0) 
        error = 1;
    else
        error = 0;
#10 $finish;
end   

//DO expected
always @(*) begin
    opA = a;
    opB = b;
    case({S, ci, M})
//0000 10 0 置全0
//0001 10 !A & !B nor
//0010 10 !A & B notand
//0011 10 !A not A
        6'b0000_10: DO_ex = 32'b0;
        6'b0001_10: DO_ex = ~opA & ~opB;
        6'b0010_10: DO_ex = ~opA & opB;
        6'b0011_10: DO_ex = ~opA;
//0100 10 A & !B andnot
//0101 10 !B not B
//0110 10 A&!B | !A&B xor
//0111 10 !A | !B nand
        6'b0100_10: DO_ex = opA & ~opB;
        6'b0101_10: DO_ex = ~opB;
        6'b0110_10: DO_ex = opA ^ opB;
        6'b0111_10: DO_ex = ~opA | ~opB;
//1000 10 A & B and
//1001 10 A&B | !A & !B xnor
//1010 10 B 传送B
//1011 10 A&B | !A&B |!A&!B notor 

        6'b1000_10: DO_ex = opA & opB;
        6'b1001_10: DO_ex = opA ~^ opB;
        6'b1010_10: DO_ex = opB;
        6'b1011_10: DO_ex = ~(opA & ~opB);
//1100 10 A 传送A
//1101 10 A&B | A&!B | !A&!B or not
//1110 10 A&B | A&!B | !A&B or 
//1111 10 1 置全1
        6'b1100_10: DO_ex = opA ;
        6'b1101_10: DO_ex = ~(~opA & opB);
        6'b1110_10: DO_ex = opA | opB;
        6'b1111_10: DO_ex = {32{1'b1}};
//1001 01 A ^ B ^ C 加法
//0110 11 ( A ~^ B) ^ C 减法
        6'b1001_01: DO_ex = opA + opB;
        6'b0110_11: DO_ex = opA - opB;
        default: DO_ex = 32'bx;
    endcase
end
endmodule

ALU CLA 32bit RTL代码

`timescale 1ns/1ns
module ALU_CLA32 (
input wire [31 : 0] opA , //操作数A
opB , //操作数B
input wire [3 : 0] S , //工作模式
input wire M , //逻辑操作
Cin , //进位输入
output wire [31 : 0] DO , //数据输出
output wire N ,
Z ,
V ,
C
);
wire [8 : 0] Co;
wire [7 : 0] Zo, Vo;
assign Co[0] = Cin;

generate
genvar i;
for( i = 0; i < 8; i = i + 1 ) begin : u
    ALU_CLA4_G uALU_CLA4_G(
    .a ( opA[i*4 + 3 : i*4] ),
    .b ( opB[i*4 + 3 : i*4] ),
    .ci ( Co[i] ),
    .M ( M ),
    .S ( S ),
    .Do ( DO[i*4 + 3 : i*4] ),
    .co ( Co[ i+1 ] ),
    .V ( Vo[ i ] ),
    .Z ( Zo[ i ] )
    );
    end
endgenerate
assign V = Vo[ 7];
assign N = DO[31];
assign C = Co[ 8];
assign Z = & Zo;
endmodule

ALU CLA 32bit 测试程序

`timescale 1ns/1ns
module ALU_CLA32_tb();
reg [31 : 0] opA ,
opB ;
reg [3 : 0] S ;
reg M ,
Cin ;
wire [31 : 0] DO ;
wire N , Z ,
V , C ;
ALU_CLA32 uALU32(
.opA ( opA ), //操作数A
.opB ( opB ), //操作数B
.S ( S ), //工作模式选择信号
.M ( M ), //逻辑操作控制信号
.Cin ( Cin ), //进位输入信号
.DO ( DO ), //数据输出
.N ( N ),
.Z ( Z ),
.V ( V ),
.C ( C )
);

initial begin
opA = 32'hFFFF_FFFF;
opB = 32'h0000_0001;
S = 4'b1001;
M = 1'b1;
Cin = 1'b0;
#10 opA = 32'hFFFF_FFFF;
opB = 32'h0000_0000;
#10 opA = 32'hFFFF_FFFF;
opB = 32'hFFFF_FFFF;
#10 opA = 32'h7FFF_FFFF;
opB = 32'h0FFF_FFFF;
#10 opA = 32'h3FFF_FFFF;
opB = 32'h0FFF_FFFF;
#10 $finish;
end
endmodule

练习2

移位器RTL代码

`timescale 1ns/1ns
module shifter (
input wire clk ,
input wire D_sr ,
sr ,
input wire D_sl ,
sl ,
input wire [n-1 : 0] D ,
input wire ld ,
output reg [n-1 : 0 ] Q
);
always @(posedge clk)
    if ( ld )
        Q <= D;
    else if( sr )
        Q <= {D_sr, Q[3 : 1]};
    else if( sl )
        Q <= {Q[2 : 0], D_sl };
    else
        Q <= 0;
endmodule

`timescale 1ns/1ns
module shifter (
input wire clk ,
input wire D_sr ,
sr ,
input wire D_sl ,
sl ,
input wire [n-1 : 0] D ,
input wire ld ,
output reg [n-1 : 0 ] Q
);
wire [n-1 : 0] D_in;
assign D_in =     ( {4{ld}} & D ) |
                ( {4{sr}} & {D_sr, Q[n-1 : 1]} ) |
                ( {4{sl}} & {Q[n-2 : 0], D_sl } ); 
always @(posedge clk)
Q <= D_in;
endmodule

练习3:counter3

RTL代码

`timescale 1ns/1ns
module fsm (
input wire clk ,
rst ,
output wire Y2 ,
Y1 ,
Y0
);
always @(posedge clk, negedge rst)
    if( ! rst ) begin
        Q1 <= 0;
        Q0 <= 0;
        end
    else begin
        Q1 <= Q1 ^ Q0;
        Q0 <= ! Q0;
    end
assign Y2 = Q1;
assign Y1 = Q0;
assign Y0 = Q1 | Q0;
endmodule

练习 4

FSM 方法1

`timescale 1ns/1ns
module fsm1 (
input wire clk ,
reset_n ,
X ,
output wire Z1 ,
Z2
);
reg [1: 0] cnt;
always @(posedge clk, negedge reset_n)
    if( ! reset_n )
        cnt <= 2'b00;
    else if( X ) begin
        if(cnt != 2'b11)
            cnt <= cnt + 1'b1;
        end
assign Z1 = ^ cnt ;
assign Z2 = & cnt ;
endmodule

FSM 测试程序

`timescale 1ns/1ns
module fsm1_tb();
reg clk ,
reset ,
x ;
wire Z1 ,
Z2 ;
fsm3 ufsm3(
.clk ( clk ),
.reset_n ( reset ),
.x ( x ),
.Z1 ( Z1 ),
.Z2 ( Z2 )
);
always #5 clk = !clk;

initial begin
    clk = 0;
    reset = 0;
    x = 0;
    @(posedge clk);
        reset = 1'b1;
    @(negedge clk);
        x = 1;
    repeat(5) @(posedge clk);
    $finish;
    end
endmodule

FSM 方法 2

module fsm2 (
input wire clk
,
reset ,
x ,
output wire Z1 ,
Z2
);
reg [1: 0] state;
parameter ST0 = 2'b00, ST1 = 2'b01,
ST2 = 2'b11, ST3 = 2'b10;
always @(posedge clk, negedge reset)
    if(!reset)
        state <= ST0;
    else if( x ) 
        case(state)
            ST0 : state <= ST1;
            ST1 : state <= ST2;
            ST2 : state <= ST3;
            default: state <= ST3;
        endcase
assign Z1 = state == ST1 || state == ST2 ;
assign Z2 = state == ST3;
endmodule

FSM 方法3

module fsm3 (
input wire clk ,
reset ,
x ,
output wire Z1 ,
Z2
);
reg [2: 0] x_dly;
always @(posedge clk, negedge reset)
    if(!reset)
        x_dly <= 0;
    else begin 
        x_dly[0] <= x;
        x_dly[1] <= x_dly[0];
        x_dly[2] <= x_dly[1] | x_dly[2];
    end
assign Z1 = x_dly[0] & !x_dly[2];
assign Z2 = x_dly[2] ;
endmodule

练习 5

“111”检测

module detect111(
input wire clk ,
Rst ,
x ,
output wire Out 
);
reg [2: 0] x_dly;
always @(posedge clk)
    if(Rst)
        x_dly <= 0;
    else begin 
        x_dly[0] <= x;
    x_dly[1] <= x_dly[0];
    x_dly[2] <= (&x_dly[1:0] & x) | x_dly[2];
    end
assign Out = x_dly[2] ;
endmodule

`timescale 1ns/1ns
module decect111_tb();
reg clk ,
reset ,
x ;
wire Out ;
detect111 udect(
.clk ( clk ),
.Rst ( reset ),
.x ( x ),
.Out ( Out )
);
always #5 clk = !clk;
    reg [9:0] xin; 
    initial begin
        clk = 0;
        reset = 0;
        xin = 10'b01_0110_1110;
        x = 0;
        @(posedge clk);
            reset = 1'b1;
        repeat(12) begin
            @(negedge clk);
                x = xin[9];
                xin = xin << 1;
            end
        $finish;
    end
endmodule

module detect_fsm(
input wire clk ,
reset ,
x ,
output wire Out 
);
reg [1: 0] state;
parameter zero = 2'b00, one = 2'b01,
two = 2'b11, thre = 2'b10;
always @(posedge clk, negedge reset)
    if(!reset)
        state <= 0;
    else 
        case(state)
            zero: if(x) state <= one;
            one: if(x) 
                    state <= two;
                else
                    state <= zero;
            two: if(x) 
                    state <= thre;
                else
                    state <= zero;
            default: state <= thre;
        endcase
assign Out = state == thre ;
endmodule

module detect_fsm(
input wire clk ,
reset ,
x ,
output wire Out 
);
reg [1: 0] state ;
parameter zero = 2'b00, one = 2'b01,
two = 2'b11, thre = 2'b10;
always @(posedge clk, negedge reset)
// if(!reset) state <= 0;
// else 
    case(state)
        zero: if(x) state <= one;
        one: if(x) 
                state <= two;
            else
                state <= zero;
        two: if(x) 
                state <= thre;
            else
                state <= zero;
                thre: state <= state;
        default: state <= 'bx;
    endcase
assign Out = state == thre ;
endmodule

作业

Verilog中的高级结构

task定义

任务的主要特点

  • 任务可以有input,output 和 inout参数。
  • 传送到任务的参量和与任务I/O说明顺序相同。尽管传送到任务的参量名称与任务内部I/O说明的名字可以相同,但在实际中这通常不好。参量名的唯一性可以使任务具有好的模块性。
  • 可以在任务内使用时序控制。
  • 在Verilog中任务定义一个新范围(scope)
  • 要禁止任务,使用关键字disable 。

从代码中多处调用任务时要小心。因为任务的局部变量只有一个拷贝,并行调用任务可能导致错误的结果。在任务中使用时序控制时这种情况时常发生。

在任务中引用module的变量时要小心。如果想使任务能多个过程块中调用,则所有在任务或函数内部用到的变量都必须列在端口列表中。

任务的简单例子

  • 调用那一时刻传递值,传递新值需要等待下一次调用
  • 等task完成一个任务才相应上升沿
  • 注意参数覆盖

任务的测试

`timescale 1ns/1ns
module mult_tb();
reg clk ,
en_mult ;
reg [3: 0] a ,
b ;
wire [7: 0] out;
mult umult(
.clk ( clk ),
.en_mult ( en_mult ),
.a ( a ),
.b ( b ),
.out ( out )
);
always #5 clk = !clk;

initial begin
    clk = 0;
    a = 2;
    b = 10;
    en_mult = 0;
    @(negedge clk)
    a = 4;
    b = 8;
    @(negedge clk)
    a = 5;
    b = 6;
    en_mult = 1;
    @(negedge clk)
    a = 7;
    b = 8;
    #10 $finish;
    end
endmodule

参数传递

module mult (
input wire clk ,
en_mult ,
input wire [3: 0] a ,
b ,
output reg [7: 0] out
);
    always @( posedge clk)
    multme (a, b, out);
    task multme; // 任务定义
    input [3: 0] xme,
            tome;
    output [7: 0] result;
    wait (en_mult)
    result = xme * tome;
    endtask
endmodule

静态任务:参数覆盖

`timescale 1ns/1ns
module mult (
input wire clk ,
en_mult ,
input wire [3: 0] a , b
output reg [7: 0] out1 ,
out2
);
always @( posedge clk)
    multme (a, b, out1);
    // always @( negedge clk)
    // multme (a, b, out2);
task multme;
    input [3: 0] xme,
                tome;
    output [7: 0] result;
    begin
    wait(en_mult);
    #7 result = xme * tome;
    end
endtask
endmodule

`timescale 1ns/1ns
module mult (
input wire clk ,
en_mult ,
input wire [3: 0] a , b
output reg [7: 0] out1 ,
out2
);
always @( posedge clk)
multme (a, b, out1);
always @( negedge clk)
multme (a, b, out2);
task multme;
    input [3: 0] xme,
                tome;
    output [7: 0] result;
    begin
    wait(en_mult);
    #7 result = xme * tome;
    end
endtask
endmodule

动态任务:参数传递

`timescale 1ns/1ns
module mult (
input wire clk ,
en_mult ,
input wire [3: 0] a , b
output reg [7: 0] out1 ,
out2
);
always @( posedge clk)
    multme (a, b, out1);
always @( negedge clk)
    multme (a, b, out2);
task automatic multme;
    input [3: 0] xme,
                tome;
    output [7: 0] result;
    begin
    wait(en_mult);
    #7 result = xme * tome;
    end
endtask
endmodule
automatic

函数定义

函数的特点

  • 函数定义中不能包含任何时序控制语句
  • 函数至少有一个输入,不能包含任何输出或双向端口
  • 函数只返回一个数据,其缺省为register类型。
  • 传送到函数的参数顺序和函数输入参数的说明顺序相同。
  • 函数在模块(module)内部定义且只能在模块内部调用。
  • 函数不能调用任务,但任务可以调用函数。
  • 函数在Verilog中定义了一个新的范围(scope)。
  • 虽然函数只返回单个值,但返回的值可以使用{信号}赋值。这在需要有多个输出时非常有效。
    • ​ {o1, o2, o3, o4} = f_ or_ and (a, b, c, d, e);

函数举例

module orand (
        input [7: 0] a, b, c, d, e,
        output [7 : 0] out
        );
        reg [7: 0] out;
        always @( a or b or c or d or e)
              out = f_or_and (a, b, c, d, e); // 函数调用
        function [7 : 0] f_or_and;
                input [7 : 0] a, b, c, d, e;
                if  (e = = 1)
                        f_or_and = (a | b) & (c | d);
                    else
                        f_or_and = 0;
        endfunction
endmodule
  • 函数中不能有时序控制,但调用它的过程语句可以有时序控制。
  • 函数名f_or_and在函数中作为register使用

返回向量的函数

  • 要返回一个向量值(多于一位),在函数定义时在函数名前说明范围。函数中有多条语句时用begin和end。
  • 不管在函数内对函数名进行多少次赋值,值只返回一次。下例中,函数还在内部声明了一个整数。
module foo(
        input [7: 0] loo,
        output [3: 0] goo);
// 可以持续赋值中调用函数
        wire [3: 0] goo = zero_count ( loo );
        function [3: 0] zero_count;
             input [7: 0] in_ bus;
             integer  I;
             begin
                 zero_count = 0;
                 for (I = 0; I < 8; I = I + 1)
                     if (! in_bus[ I ])
                         zero_count = zero_count + 1;
            end
        endfunction
endmodule

函数返回类型

  • 函数返回值可以声明为其它register类型:integer, real, 或time。
  • 在任何表达式中都可调用函数
module checksub (
        output  reg  neg,
        input   wire [7 : 0]  a,  b
        );
        function  integer  subtr;
                input [7: 0] in_a, in_b;
                subtr = in_a - in_b; // 结果可能为负
        endfunction
        always @ (a,  b)
                if (subtr( a, b) < 0)
                         neg = 1;
                     else
                         neg = 0;
endmodule
  • 递归函数必须声明为动态的,因为第二次调用会覆盖第一次的值
  • 逻辑综合工具不支持递归调用

递归函数-阶乘函数

`timescale 1ns/1ns 递归函数-阶乘函数
module fact();
reg [7 : 0] in_a;
integer out1,
out2;
initial begin
    in_a = 7;
    out1 = factorial_R( in_a );
    out2 = factorial ( in_a );
    #10 in_a = 14;
    out1 = factorial_R( in_a );
    out2 = factorial ( in_a );
    #10 $finish;
end
initial $monitor("%d %d %d", in_a, 
out1, out2);

function automatic integer factorial_R;
input [7 : 0] oper;
if( oper >= 2)
    factorial_R = oper * factorial_R( oper - 1 );
else
    factorial_R = 1;
endfunction

function integer factorial;
input [7 : 0] oper;
integer i;
begin
factorial = 1;
for(i = 2; i <= oper; i = i + 1)
    factorial = factorial * i;
end
endfunction
endmodule

递归函数(recursive Function)

参数化函数

  • 函数中可以对返回值的个别位进行赋值。
  • 函数值的位数、函数端口甚至函数功能都可以参数化。
. . .
parameter MAX_BITS = 8;
reg [MAX_BITS: 1]  D;
function  [MAX_BITS: 1]  reverse_bits;
        input [MAX_BITS-1: 0] data;
        integer K;
        for (K = 0; K < MAX_BITS; K = K + 1)
            reverse_bits [MAX_BITS - K] = data [K];
endfunction
always @ (posedge clk)
        D = reverse_bits (D) ;
. . .

多module调用函数

  • 一个模块定义的function不能直接调用
  • 一般把函数单独一个文件存储,调用使用’include

Verilog的任务及函数

  • 任务和函数必须在module内部定义和调用
  • 在任务和函数中不能声明wire
  • 所有输入/输出都是局部寄存器
  • 任务/函数执行完成后才返回结果。
  • 例如,若任务/函数中有forever语句,则永远不会返回结果

函数

  • 函数没有时序控制,因此综合结果为组合逻辑。函数可以在过程块内或持续赋值语句中调用。
  • 下例中的or/and块由持续赋值语句调用函数实现
module orand (
     input a, b, c, d, e,
     output wire out
     ); 
     assign out = forand (a, b, c, d, e);
     function forand;
          input a, b, c, d, e;
          if (e == 1)
               forand = (a| b) & (c| d);
          else
               forand = 0;
     endfunction
endmodule

函数—条件不完全?

module orand (
     input a, b, c, d, e,
     output wire out
     ); 
     assign out = forand (a, b, c, d, e);
     function forand;
          input a, b, c, d, e;
          if (e == 1)
               forand = (a| b) & (c| d);
//          else
//               forand = 0;
     endfunction
endmodule

任务

module orandtask (out, a, b, c, d, e);
     input a, b, c, d, e;
     output out; reg out;
     always @( a or b or c or d or e)
          orand (out, a, b, c, d, e);
     task orand;
          input a, b, c, d, e;
          output out;
          if (e == 1)
               out = (a| b) & (c| d);
          else
               out = 0;
     endtask
endmodule

任务一般只在测试程序中使用,因为:

  • 没有时序控制的任务如同函数
  • 带有时序控制的任务不可综合

下面是用任务描述的or/and块:

条件不完全会产生什么?

module orandtask (
input a, b, c, d, e,
output reg out 
);
always @( * )
orand (a, b, c, d, e, out);
task orand;
    input a, b, c, d, e;
    output out;
    if (e == 1)
        out = (a| b) & (c| d);
// else
// out = 0;
endtask
endmodule

任务:电路1

任务:电路2

命名块(named block)

  • 在关键词begin或fork后加上 :<块名称> 对块进行命名

  • 在命名块中可以声明局部变量

  • 可以使用关键词disable禁止一个命名块

  • 命名块定义了一个新的范围

  • 命名块会降低仿真速度

禁止命名块和任务

module do_arith (out, a, b, c, d, e, clk, en_mult);
        input clk, en_mult;
        input [7: 0] a, b, c, d, e;
        output [15: 0] out;
        reg [15: 0] out;
        always @( posedge clk)
                begin : arith_block // *** 命名块 ***
                        reg [3: 0] tmp1, tmp2; // *** 局部变量 ***
                        {tmp1, tmp2} = f_or_and (a, b, c, d, e); // 函数调用
                        if (en_mult)  multme (tmp1, tmp2, out); // 任务调用
                end
        always @( negedge en_mult) begin // 中止运算
                disable  multme ; // *** 禁止任务 ***
                disable  arith_block; // *** 禁止命名块 ***
        end
// 下面[定义任务和函数
        ……
endmodule
  • disable语句终结一个命名块或任务的所有活动。也就是说,在一个命名块或任务中的所有语句执行完之前就返回。

​ 语法:

​ disable <块名称>

​ 或

​ disable <任务名称>

  • 当命名块或任务被禁止时,所有因他们调度的事件将从事件队列中清除
  • disable是典型的不可综合语句。
  • 在前面的例子中,只禁止命名块也可以达到同样的目的:所有由命名块、任务及其中的函数调度的事件都被取消。

对验证的支持

验证系统中的任务(task)及函数(function)

  • Verilog读取当前仿真时间的系统函数

​ $time :返回一个64位整数时间值。

​ $stime :返回一个32位整数时间值。

​ $realtime :返回一个实数时间值。

​ 返回值使用调用模块中`timescale定义的时间单位

  • Verilog支持文本输出的系统任务:

​ $display

​ $strobe

​ $write

​ $monitor

输出格式化时间信息

  • 若使用多个`timescale,以最小的时间精度显示时间值。
  • 可用系统任务$timeformat结合格式符%t全局控制时间显示方式。
  • $timeformat系统任务的语法为:
    • $timeformat(<unit>,<precision>,<suffix>,<min_width>);

对#延迟,Verilog将延迟值舍入最近(四舍五入)时间精度值。

例如,上面的例子修改为:

显示信号值 — $display

  • $display输出参数列表中信号的当前值。

​ 语法:$display([“ format_specifiers”,] )

  • $display输出时自动换行。

​ $display ($time, “%b \t %h \t %d \t %o”, sig1, sig2, sig3, sig4);

​ $display ($time, “%b \t”, sig1, “%h \t”, sig2, “% d \t”, sig3, “%o”, sig4);

  • $display支持二进制、八进制、十进制和十六进制。缺省基数为十进制。

​ $display (sig1, sig2, sig3, sig4);

​ $displayb (sig1, sig2, sig3, sig4);

​ $displayo (sig1, sig2, sig3, sig4);

​ $displayh (sig1, sig2, sig3, sig4);

显示信号值—$write和$strobe

  • $write与$display相同,不同的是不会自动换行

​ $write($time, “%b \t %h \t %d \t %o \t”, sig1, sig2, sig3, sig4);

  • $strobe与$display相同,不同的是在仿真时间前进之前的信号值。而$display和$write立即显示信号值。也就是说$strobe显示稳定状态信号值,而$display和$write可以显示信号的中间状态值。

​ $strobe($time, “%b \t %h \t %d \t %o \t”, sig1, sig2, sig3, sig4);

  • $write和$strobe都支持多种数基,缺省为十进制。

    • $writeb $strobeb

    • $writeo $strobeo

    • $writeh $strobeh

监视信号值—$monitor

  • $monitor持续监视参数列表中的变量。

  • 在一个时间片中,参数表中任何信号发生变化,$monitor将在仿真时间前进前显示参数表的信号值。

  • 后面的$monitor将覆盖前面的$monitor。

  • 可以用系统任务$monitoron和$monitoroff控制持续监视。

  • $monitor支持多种基数。缺省为十进制。

     \$monitor (\$time, “%b \t %h \t %d \t %o”, sig1, sig2, sig3, sig4);
  • $monitor是唯一的不断输出信号值的系统任务。其它系统任务在返回值之后就结束。

  • $monitor和$strobe一样,显示参数列表中信号的稳定状态值,也就是在仿真时间前进之前显示信号。在一个时间步中,参数列表中信号值的任何变化将触发$monitor 。但$time,$stime,$realtime不能触发。

  • 任何后续的$monitor覆盖前面调用的$monitor。只有新的$monitor的参数列表中的信号被监视,而前面的$monitor的参数则不被监视。

  • 可以用$monitoron和$monitoroff系统任务控制持续监视,使用户可以在仿真时只监视特定时间段的信号。

  • $monitor参数列表的形式与$display相同。

  • $monitor支持多种基数。缺省为十进制。

    • $monitorb

    • $monitoro

    • $monitorh

文件输出

. . .
integer MCD1;
    MCD1 = $fopen("<name_of_file>");
    $fdisplay( MCD1, P1, P2, .., Pn);
    $fwrite( MCD1, P1, P2, .., Pn);
    $fstrobe( MCD1, P1, P2, .., Pn);
    $fmonitor( MCD1, P1, P2, .., Pn);
    $fclose( MCD1);
. . .
  • $fopen打开一个文件并返回一个多通道描述符(MCD)。

    • MCD是与文件唯一对应的32位无符号整数。

    • 如果文件不能打开并进行写操作,MCD等于0。

    • 如果文件成功打开,MCD中的一位被置位。

  • 以$f开始的显示系统任务将输出写入与MCD相对应的文件中。

  • $fopen打开参数中指定的文件并返回一个32位无符号 整数MCD,MCD是与文件一一对应的多通道描述符。如果文件不能打开并进行写操作,它返回0。

  • $fclose关闭MCD指定的通道。

  • 输出信息到log文件和标准输出的四个格式化显示任务($display, $write, $monitor, $strobe)都有相对应的任务用于向指定文件输出。

  • 这些对应的任务($fdisplay,$fwrite,$fmonitor,$fstrobe)的参数形式与对应的任务相同,只有一个例外:第一个参数必须是一个指定向何哪个文件输出的MCD。MCD可以是一个表达式,但其值必须是一个32位的无符号整数。这个值决定了该任务向哪个打开的文件写入。

  • MCD可以看作由32个标志构成的组,每个标志代表一个单一的输出通道。

文件输入

  • Verilog中有两个系统任务可以将数据文件读入寄存器组。一个读取二进制数据,另一个读取十六进制数据:

  • $readmemb

    $readmemb (“file_name”, );

    $readmemb (“file_name”, , );

    $readmemb (“file_name”, , , );

  • $readmemh

    $readmemh (“ file_name”, );

    $readmemh (“ file_name”, , );

    $readmemh (“ file_name”, , , );

系统任务$readmemb和$readmemh从一个文本文件读取数据并写入存储器。

  • 如果数据为二进制,使用$readmemb;如果数据为十六进制,使用$readmemh。
  • filename指定要读入的文件。
  • mem_name指定存储器信号名称。
  • start和finish给出存储器加载的地址。Start为开始地址,finish为结束地址。如果不指定开始和结束地址,$readmem按从低端开始读入数据,与说明顺序无关。
$readmemb和$readmemh的文件格式 :
            $readmemb("mem_file. txt", mema);
  • 可以指定二进制(b)或十六进制(h)数
  • 用下划线“_”提高可读性。
  • 可以包含单行或多行注释。
  • 可以用空格和换行区分各个数据。
  • 可以给后面的值设定一个特定的地址,格式为:
    • @(hex_address)
      • 十六进制地址的大小写不敏感。
      • 在@和数字之间不允许有空格。

作业:数据转换器

Verilog Test Bench使用简介

设计组织

虚线表示编译时检测输入文件是否存在及可读并允许生成输出文件。

test bench组织

  • 简单的test bench向要验证的设计提供向量,人工验证输出。
  • 复杂的test bench是自检测的,其结果自动验证。

施加激励

产生激励并加到设计有很多 种方法。一些常用的方法有:

  • 从一个initial块中施加线激励
  • 从一个循环或always块施加激励
  • 从一个向量或整数数组施加激励
  • 记录一个仿真过程,然后在另一个仿真中回放施加激励

线性激励

线性激励有以下特性:

  • 只有变量的值改变时才列出
  • 易于定义复杂的时序关系
  • 对一个复杂的测试,测试程序(test bench)可能非常大
module inline_ tb;
    reg [7: 0] data_ bus, addr;
    wire [7: 0] results;
    DUT u1 (results, data_ bus, addr);
    initial
        fork
              data_bus = 8'h00;
              addr = 8'h3f;
          #10 data_ bus = 8'h45;
          #15 addr = 8'hf0;
          #40 data_ bus = 8'h0f;
          #60 $finish;
    join
endmodule

循环激励

从循环产生激励有以下特性:

  • 在每一次循环,修改同一组激励变量
  • 有固定的时序关系
  • 代码紧凑
`timescale 1ns/1ns
module loop_tb;
    reg clk;
    reg [7:0] stimulus;
    wire [7:0] results;
    integer i;
    DUT u1 (results, stimulus);
    always begin // clock generation
        clk = 1;  #5  clk = 0;  #5
    end
    initial begin
        for (i = 0; i < 256; i = i + 1)
       @( negedge clk)  stimulus = i;
       #20 $finish;
   end
endmodule

数组激励

从数组产生激励有以下特性:

  • 在每次反复中,修改同一组激励变量
  • 激励数组可以直接从文件中读取
module array_ tb;
    reg [7: 0] stim_ array[ 15: 0]; // 数组
    reg [7 : 0] stimulus;
    wire result;
    integer i;
    DUT u1 (results, stimulus);
    initial begin
        // 从数组读入数据
        #20 stimulus = stim_array[0];
        #30 stimulus = stim_array[15]; // 线激励
        #20 stimulus = stim_array[1];
        for (i = 14; i > 1; i = i - 1) // 循环
            #50 stimulus = stim_array[i] ;
        #30 $finish;
    end
endmodule

矢量采样

在仿真过程中可以对激励和响应矢量进行采样,作为其它仿真的激励和期望结果。

module capture_tb;
    parameter period = 20
    reg [7:0] in_vec, out_vec;
    integer RESULTS, STIMULUS;
    DUT u1 (out_vec, in_vec);
    initial  begin
         STIMULUS = $fopen("vec.txt") ;
    if (STIMULUS != 0 ) forever #( period/2)
            $fstrobeb (STIMULUS, "%b", in_vec);
    end
    initial  begin
    RESULTS = $fopen("results.txt") ;
    if (RESULTS != 0 ) #( period/2) forever                
        #( period/2)
                  $fstrobeb (RESULTS, "%b", out_vec);

    end
endmodule

矢量回放

  • 保存在文件中的矢量反过来可以作为激励
module read_file_tb;
    parameter num_vecs = 256;
    reg [7:0] data_bus;
    reg [7:0] stim [num_vecs-1:0];
    integer i;
    DUT u1 (results, data_bus)
    initial
        begin // Vectors are loaded
            $readmemb ("vec.txt", stim);
            for (i =0; i < num_vecs ; i = i + 1)
            #50 data_bus = stim[i];
    end
endmodule

  • 使用矢量文件输入/输出的优点:

    • 激励修改简单

    • 设计反复验证时直接使用工具比较矢量文件。

错误及警告报告

  • 使用文本或文件输出类的系统任务报告错误及警告
always @( posedge par_err)
    $display (" error-bus parity errors detected");
always @( posedge cor_err)
    $display("warning-correctable error detected");

一个更为复杂的test bench可以:

  • 不但能报告错误,而能进行一些动作,如取消一个激励块并跳转到下一个激励。
  • 在内部保持错误跟踪,并在每次测试结束时产生一个错误报告。

建立时钟

建立时钟(period = 20)

使用task

在test bench中使用task可以压缩重复操作,提高代码效率。

问题:

  • 什么操作可以容易的在fork…join块做到,而不容易在begin…end块做到?
  • 通常怎样产生规则激励和不规则激励?
  • 从一个文件中读取激励时使用什么数据类型?
  • 在行为级时钟模型中能做哪些在门级时钟模型中很难或不能作到的事?

解答:

  • fork…join块中不但能够赋值,还可以并行执行循环、条件语句、任务或函数调用。
  • 循环或always块能有效地产生规则激励,不规则激励适合用在initial块产生。
  • 用寄存器组(存储器)并用$readmem系统任务从一个文件以读取向量。
  • 行为级代码可以很容易地产生一个启动时间不规则的时钟波形,并且可以在时刻零初始化时钟。

存储器建模

描述存储器必须做两件事:

  • 说明一个适当容量的存储器。
  • 提供内容访问的方式,例如:
    • 只读
    • 读和写
    • 写同时读
    • 多个读操作,同时进行单个写操作
    • 同时有多个读和多个写操作,有保证一致性的方法

简单ROM描述

下面的ROM描述中使用二维寄存器组定义了一个存储器mem。ROM的数据单独保存在文件my_rom_data中,如右边所示。通常用这种方法使ROM数据独立于ROM描述。

reg [7 : 0] mem [15 : 0]// mem为16x8的寄存器数组
reg MemA [3 : 0]; //MemA是4x1位寄存器数组
time Events [3 : 0]; //time数组
integer int [3 : 0]; //整数数组int[3 : 0] 

Verilog-1995标准中,不能直接访问一维数组中的某个寄存器数据中的一位,比如要取出下面array[7][5],需要将array[7]赋给一个reg变量,再从这个变量中取出第5位:

reg [7 : 0] array [15 : 0];
reg [7 : 0] arrayreg;
reg reg5;
initial begin
 arrayreg = array[7];
 reg5 = arrayreg[5];
end

寄存器和数组

`timescale 1ns/1ns
module reg_tb;
 reg [3 : 0] RegA; //一个4位的reg类型数据
 reg MemA [3 : 0]; //一个reg类型的 4x1 的一维数组
 initial begin
    RegA = 4'b1101; //合法
    MemA = 4'b0010; //非法
    MemA[3] = 1'b0;
    MemA[2] = 1'b0;
    MemA[1] = 1'b1;
    MemA[0] = 1'b0;
    $display("RegA = %b ", RegA);
    $display("MemA = %b ", {MemA[3],MemA[2],MemA[1],MemA[0]});
    #100 $finish;
    end
endmodule

Verilog-2001标准的数组声明

`timescale 1ns /1ns
module array;
 reg [7 : 0] mem [15 : 0][7 : 0];
 reg [7 : 0] out; 
 integer i, j, k;
 initial begin
 for ( i = 0; i < 16; i = i + 1)
 for(j = 0; j < 8; j = j + 1)
mem[i][j] = i + j;
 for ( i = 0; i < 16; i = i + 1) begin
 for(j = 0; j < 8; j = j + 1) begin
out = mem[i][j];
$write("\t %0d", out);
 end
 $write("\n");
 end
 $display("out = %b", out);
 $write("out = ");
 for( k = 0; k < 8; k = k + 1) 
 $write("%b", mem[i-1][j-1][7 - k]);
 $write("\n");
 $finish;
 end
endmodule

MSB为0的数组

存储器数据装入

可以使用循环或系统任务给存储器装入初始化数据

  • 用循环给存储器的每个字赋值
      for (i= 0; i < memsize; i = i+ 1) // initialize memory
          mema[ i] = {wordsize{ 1'b1}}; 
  • 调用系统任务$readmem
$readmemb("mem_file. txt", mema); 

可以用系统任务$readmem给一个ROM或RAM加载数据。对于ROM,开始时写入的数据就是其实际内容。对于RAM,可以通过初始化,而不是用不同的写周期给每个字装入数据以减少仿真时间。

异步存储器接口时序

同步SRAM存储器接口时序 - SMIC65nm_1Kx32

简单的异步RAM描述

RAM描述比ROM略微复杂,因为必须既有读功能又有写功能,而读写通常使用同一数据总线。这要求使用新的处理双向数据线的建模技术。在下面的例子中,若读端口未使能,则模型不驱动数据总线;此时若数据总线没有写数据驱动,则总线为高阻态Z。这避免了RAM写入时的冲突。

`timescale 1ns /1ns
module mymem (
    inout  wire [3:0] data,
    input  wire [3:0] addr,
    input  wire     rd, 
        wr
    );
    reg [3:0] memory [15:0]; // 16*4
// 读
    assign data = rd ? memory[addr] : 4'bz;
// 写
    always @( posedge wr)
         memory[addr] <= data;
endmodule 

这个描述可综合,但许多工具仅仅产生一个寄存器堆,因此与一个真正的存储器相比耗费更多的面积。

异步RAM测试

参数化存储器描述

在下面的例子中,给出如何定义一个字长和地址均参数化的只读存储器件。

module scalable_ROM  #(
    parameter addr_bits = 8,                     // 地址总线宽度
            wordsize = 8,                     // 字宽
            words = (1 << addr_bits) // mem容量
    ) (
    output [wordsize - 1 : 0] mem_word,    // 存储器字
    input   [addr_bits -1 : 0] address           // 地址总线
    );

    reg [wordsize -1 :  0] mem [words-1 : 0]; // mem声明

// 输出存储器的一个字
    assign  mem_word = mem[address];

endmodule 

例中存储器字范围从0而不是1开始,因为存储器直接用地址线确定地址。 也可以用下面的方式声明存储器并寻址。

reg [wordsize:1] mem [1:words]; // 从地址1开始的存储器 
// 存储器寻址时地址必须加1 
wire [wordsize:1] mem_word = mem[ address + 1]; 

存储器数据赋值

可以使用循环或系统任务给存储器装入初始化数据

可以用 系统任务$readmem给一个ROM或RAM加载数据。对于ROM, 开始时写入的数据就是其实际内容。对于RAM,可以通过初始化,而不 是用不同的写周期给每个字装入数据以减少仿真时间

使用双向端口

  • 用关键词inout声明一个双向端口
    • inout [7:0] databus;
  • 双向端口声明遵循下列规则:
    • inout端口不能声明为寄存器类型,只能是net类型。
    • 这样仿真器若有多个驱动时可以确定结果值。
      • 对inout端口可以从任意一个方向驱动数据。端口数据类型缺省为net类 型。不能对net进行过程赋值,只能在过程块外部持续赋值,或将它连 接到基本单元。
  • 在同一时间应只从一个方向驱动inout端口。
    • 例如:在RAM模型中,如果使用双向数据总线读取RAM数据,同时在 数据总线上驱动写数据,则会产生逻辑冲突,使数据总线变为未知。
    • 必须设计与inout端口相关的逻辑以确保正确操作。当把该端口作为输 入使用时,必须禁止输出逻辑

双向驱动器建模

存储器端口建模

问题:

  • 在Verilog中用什么结构定义一个存储器组?
  • 如何向存储器加载数据?
  • 如何通过一个双向(inout)端口传送数据?

解答:

  • 在Verilog中将存储器声明为一个一个2维寄存器阵列。
  • 可以用系统任务$readmem或$readmemb或用过程赋值向存储器加载数据
  • 因为inout两端信号必须都是net数据类型,因此只能使用基本单元,子模块,或持续赋值驱动数据。同时还必须注意确保在任何一端不要发生驱动冲突。

延时模型

术语及定义

  • 模块路径(module path): 穿过模块,连接模块输入(input端口或inout端口)到模块输出(output端口或inout端口)的路径。
  • 路径延时(path delay):与特定路径相关的延时
  • 时序检查(timing check):监视两个输入信号的时间关系并进行检查的系统任务,以保证电路能正确工作。
  • 时序驱动设计(timing driven design):从前端到后端的整个设计流程中,用时序信息连接不同的设计阶段

延时模型类型(Delay Modeling Types)

块延时(Lumped Delay)

  • 块延时方法是将全部延时集中到最后一个门上。这种模型简单但不够精确,只适用于简单电路。因为当到输出端有多个路径时不能描述不同路径的不同延时。
  • 可以用这种方法描述器件的传输延时,并且使用最坏情况下的延时(最大延时)。

分布延时(Distributed Delays)

分布延时方法是将延时分散到每一个门。在相同的输出端上,不同的路径有不同的延时。分布延时有两个缺点:

  • 在结构描述中随规模的增大而变得异常复杂。
  • 仍然不能描述基本单元(primitive)中不同引脚上的不同延时。

路径延时(Module Path Delays)

在专用的specify块描述模块从输入端到输出端的路径延时。

  • 精确性:所有路径延时都能精确说明。

  • 模块性:时序说明与功能描述分开说明

    • 功能验证独立于时序验证。

    • 在不同的抽象级中保持不变。

Specify块

  • specify块定义了模块的时序部分
    • 时序信息和功能在不同的块中描述,这样功能验证独立于时序验证。specify块在不同的抽象级中保持不变。
    • 功能描述中的延时,如#delay在综合时不起作用
  • 由specify开始, 到endspecify结束,并且在模块内部
  • specify块可以:
    • 描述穿过模块的路径及其延时
    • 使用关键字specparam在specify中进行参数声明
    • 定义特定模块或特定模块路径的脉冲过滤限制
    • 描述时序检查以保证器件的时序约束能够得到满足

精确延时控制

  • 说明门和模块路径的上升(rise)、下降(fall)和关断(turn-off)延时
and #(2, 3)  (out, in1, in2, in3); // rise, fall
bufif0 #( 3, 3, 7) (out, in, ctrl); // rise, fall, turn- off
(in => out) = (1, 2); // rise, fall
(a => b) =  (5, 4, 7); // rise, fall, turn-off
  • 在路径延时中可以说明六个延时值(0 ->1, 1 ->0, 0 ->Z, Z ->1, 1 ->Z, Z ->0)
(C => Q) = (5, 12, 17, 10, 6, 22);
  • 在路径延时中说明所有12个延时值(0 ->1, 1 ->0, 0 ->Z, Z ->1, 1 ->Z, Z ->0, 0 ->X, X ->1, 1 ->X, X ->0, X ->Z, Z ->X)
(C => Q) = (5, 12, 17, 10, 6, 22, 11, 8, 9, 17, 12, 16);
  • 上面所说明的每一个延时还可细分为最好、典型、最坏延时
or #( 3.2:4.0:6.3) o1( out, in1, in2); // min: typ: max
not #( 1:2:3, 2:3:5) (o, in);  // min: typ: max for rise, fall
(b => y) = (2:3:4, 3:4:6, 4:5:8); // min: typ: max for rise, fall, and turnoff

最坏延时检测set up,最好检测hold time

延时说明定义的是门或模块的固有延时。输入上的任何变化要经过说明的延时才能在输出端反映出来。如果没有延时说明,则基本单元的延时为0。分布关断延时只对三态基本单元有效。

  • 上升延时是输出转换为 1 时的延时
  • 下降延时是输出转换为 0 时的延时
  • 关断延时输出转换为 Z 时的延时
  • 到X的延时是最小延时,而从X到其它值的转换使用最大延时
    • 如果说明了上升、下降和关断延时,则1->X的延时使用上升和关断延时的最小值。X->0的延时为下降延时;X->Z的延时为关断延时。
    • 如果只说明了上升和下降延时,则1->X和X->0使用下降延时,X->Z使用上升和下降延时的最小延时
    • 如果只说明了一个延时,则所有跳变使用这个延时。
    • 如果说明了六个延时,则1->X使用1->X和1->Z中最小延时;X->0使用1->0和X->0的最大延时;X->Z使用1->Z和0->Z中的最大延时。

模块路径的并行连接和全连接

  • 路径说明必须括在圆括号内
  • *>表示全连接,也就是所有输入连接到所有输出
  • =>表示并行连接,也就是信号对之间的连接

路径延时说明的例子:

// 从 a 到 out 和从 b 到 out的路径延时说明
      (a, b => out) = 2.2;
     (a => out1, out2) = 2.2;   // 使用错误,可使用 (a *> out1, out2) = 2.2;

// 从 r 到 o1 和 o2 的上升、下降延时说明
      (r *> o1, o2) = (1, 2);

// 从 a[1] 到 b[1] 和 从 a[0] 到 b[0] 的路径延时说明
      (a[ 1: 0] => b[ 1: 0]) = 3; // 并行连接

// 从 a 到 o 的全路径延时说明
      (a[7: 0] *> o[7: 0]) = 6.3; // full connection

路径的极性声明

简单路径可以声明极性。

  • “+”表示正极性(positive polarity)
  • “-”表示负极性(negative polarity)
  • 极性声明是可选项
  • 正极性表示从输入到输出是同相,即不会发生信号反转
  • 负极性表示异相,信号会发生反转
  • 极性声明不会影响仿真,但可能会被其它EDA工具,如时序验证工具 使用。

例如: (V + => W) = (3, 4, 5);

边沿敏感路径声明

边沿敏感路径,是指输入端口是时钟信号,并且需指定触发边沿(posedge /negedge)。

例1: (posedge clk => (out +: in)) = (1, 2);

表示从clk的上升沿到out的模块路径,其上升延时是1,下降延时是2。 “+: in”表示 out = in,且是同相传输。

例2:(negedge clk => (out -: in)) = (1, 2);

表示从clk的下降沿到out的模块路径,其上升延时是1,下降延时是2。 (out -: in)表示out= -in,是反相传输。

例3:(clk => (out : in)) = (1,2);

表示从clk到out的模块路径,其上升延时是1,下降延时是2。 从in到out的数据路径的传输极性是不确定的,同相或者反相。

状态依赖路径延时SDPD

状态依赖路径延时在说明的条件成立时赋予路径一个延时。

有时路径延时可能依赖于其它输入的逻辑值。SDPD就是用于说明这种情况。在例子中,b到x的延时依赖于a的状态。

SDPD说明语法:

if <condition> 路径延时说明;

SDPD说明不使用else子句。条件值为X或Z则认为条件成立。当一个路径中有多个条件成立时使用最小值。

所有输入状态都应说明。若没有说明则使用分布延时(若说明了分布延时),否则使用零延时。

条件有一些限制,但许多仿真器并不遵循IEEE标准的限制。

module XOR2 (x, a, b);
     input a, b;
     output x;
          xor (x, a, b);
     specify
          if (a) (b=> x) = (5: 6: 7);
          if (!a) (b=> x) = (5: 7: 8);
          if (b) (a=> x) = (4: 5: 7);
          if (!b) (a=> x) = (5: 7: 9);
     endspecify
endmodule

specify块参数

specify块中的参数由关键字specparam说明。specparam参数和模块中parameter定义的参数作用范围不同,并且specparam定义的参数不能重载。下面总结了两种参数的差别:

  • specify参数
    • 关键字为specparam声明
    • 必须在specify块内声明
    • 只能在specify块内使用
    • 不能使用defparam重载
  • 模块参数
    • 使用关键字parameter声明
    • 必须在specify块外声明
    • 只能在specify块外使用
    • 可以用defparam重载
    • 占用存储器,因为在每个模块实例中复制
module noror (O, A, B, C);
     output O;
     input A, B, C;
          nor n1 (net1, A, B);
          or o1 (O, C, net1);
     specify
          specparam ao = 2, bo = 3, co = 1;
          (A => O) = ao;
          (B => O) = bo;
          (C => O) = co;
     endspecify
endmodule

路径脉冲控制

使用specparam参数PATHPULSE$控制模块路径对脉冲的处理。

语法:

PATHPULSE$ = (< reject_value>, <error_value>?)
PATHPULSE$< path_source>$< path_destination> =
 (< reject_value>, <error_value>?)

  • 可以用PATHPULSE$声明的 specparam参数说明全局脉冲控制
  • PATHPULSE$声明的 specparam参数缩小了指定模块或模块内特定路径的模块路径延时的范围。
  • 只声明一个值时,error_value和reject_value相同,如
 PATHPULSE$ = 3;  等价于 PATHPULSE$ = (3, 3);
  • 脉冲宽度小于reject_value的信号将被滤掉,而小于error_value的值会使输出产生不定状态。
  • 由上面带斜率的波形可以看出,模块中en信号在时间t发生变化并开始影响q;若en脉冲在时间t+2结束,则q没有被完全驱动,q将恢复原值,如点波形所示。若en脉冲在时间t+9结束,q则可能完成驱动,也可能没有,处于未知状态。如果en到t+9一直有效,q将输出新值。

Verilog时序检查

  • 使用时序检查以验证设计的时序
  • 时序检查完成下列工作:
    • 确定两个指定事件之间的时差
    • 比较时差与指定的时限
    • 如果时差超过指定时限则产生时序不能满足的报告。这个报告只是一个警告信息,不影响模块的输出
  • Verilog支持的时序检查有:
    • setup(建立时间)
    • hold(保持时间)
    • pulse width(脉冲宽度)
    • clock period(时钟周期)
    • skew(倾斜)
    • recovery(覆盖)

•系统任务$setup在数据变化到时钟沿的时差小于时限则报告一个timing violation,如

$setup( data, posedge clk, 4);

•系统任务$hold在时钟沿到数据变化的时差小于时限则报告一个timing violation,如

$hold( posedge clk, data, 3);

$setuphold$setup$hold的联合。

$setuphold( posedge clk, data, 4, 3);

时序检查 — 条件时序检查

在条件时序检查中,是否进行时序检查取决于条件表达式的计算值

条件表达式中条件只能是一个标量信号,这个信号可以:

  • 用位反操作符(~)取反。
  • 用等于操作符(= =或!=)与一个标量常量进行比较
  • 用相同操作符(===或!==)与一个标量常量进行比较
  • 若条件表达式计算值为1、x或z则认为条件成立。

由于条件时序检查的条件表达式中只能有一个信号,因此需要多个信号产生条件时必须使用哑逻辑使将它们表达为一个内部信号表示才能用于条件时序检查。

  • 可以说明并使用一个notifier来显示时序不满足(violation)

$setuphold( ref_event, data_event, s_limit, h_limit, NOTIFY);

  • notifier是可选的
  • notifier是一个1位的寄存器
  • 时序检查产生violation时,Verilog报告信息并使notifier翻转
  • 当时序violation产生时,可以用notifier使输出变为未定义值。
  • 有两种方法使notifier影响输出值
  • 将notifier作为UDP的一个输入端口
  • 在高级行为模块中,不需要将notifier声明为端口也可以对其进行操作。

可能导致所有D触发器全部置x

notifier举例

`timescale 1ns/ 1ns
module dff_notifier (q, ck, d, rst, FLAG);
     input ck, d, rst;
     output q, FLAG;
     reg FLAG; // 1-bit notifier
// dff 网表
     ……
     specify
          (ck => q) = (2: 3: 4);
          $setup( d, posedge ck , 2, FLAG);
     endspecify
endmodule

module test;
     reg ck, d, rst;
     dff_notifier (q, ck, d, rst, notifier);
// 产生激励并检查响应
     always @( notifier) begin
          rst = 1; #10 rst = 0;
      end
endmodule

D触发器时序检查举例

`timescale 10 ps / 1 ps
`celldefine
module LVT_DGRNHDV0 ( Q, QN, CK, D, RN); 
input CK, D, RN;
output Q, QN;
reg NOTIFIER, NOTIFIERw, NOTIFIERs, NOTIFIERh, NOTIFIERc, NOTIFIERm; 
supply1 xSN, EN; 
buf X0 (xRN, RN); 
buf IC (clk, CK); 
udp_edfft I0 (n0, D, clk, xRN, xSN, EN, NOTIFIER); 
buf I1 (Q, n0); 
not I2 (QN, n0); 
and I4 (Deff, D, xRN); 
specify 
(posedge CK => (Q : D)) = (1.0, 1.0); // arc CK --> Q
(posedge CK => (QN : D)) = (1.0, 1.0); // arc CK --> QN 
$width(posedge CK, 1.0, 0, NOTIFIERw); 
$setup( D, posedge CK, 1.0, NOTIFIERs); 
$hold (posedge CK, D, 1.0, NOTIFIERh); 
$recovery(RN, posedge CK, 1.0, NOTIFIERc); 
$removal (RN, negedge CK, 1.0, NOTIFIERm); 
endspecify
endmodule
`endcelldefine

UDP udp_edfft定义

DFF测试程序

`timescale 10ps / 1ps
module dffa_tb ();
reg d, clk, rst
;
wire q, qn
;
DFFSRX1 udff ( 
.D ( d ), .CK ( clk ), .RN ( rst ), 
.Q ( q ), 
.QN( qn
)
);
// always #5 clk = !clk
;
initial begin
    clk = 0;
    d = 0;
    rst = 0;
    #2 clk = 1;
    #2 clk = 0;
    #1 rst = 1;
    #1 d = 1;
    #2 clk = 1;
    #2 clk = 0;
    #0.8 clk = 1;
    #0.8 clk = 0;
    #0.8 clk = 1;
    #0.8 clk = 0;
    #1 d = 0;
    #0.8 clk = 1;
    #2 clk = 0;
    #1 d = 1;
    #0.8 clk = 1;
    #2 clk = 0;
    #2 clk = 1;
    #0.8 d = 0;
    #2 clk = 0;
    #1 rst = 0;
    #0.8 clk = 1;
    #2 clk = 0;
    #0.8 rst = 1;
    #10 $finish;
    end
endmodule

选择仿真延迟模型

SDF时序标注

术语及定义

  • CTLF:(Compiled Timing Library Format)编译的时序库格式。特定工艺元件数据的标准格式。
  • GCF:(General constraint Format)通用约束格式。约束数据的标准格式。
  • MIPD:(Module Input Port Delay)模块输入端口延时。模块输入或输入输出端口的固有互连延时
  • MITD:(Multi-source Interconnect Transport Delay)多重互连传输延时。与SITD相似,但支持多个来源的不同延时。
  • PLI:(Programming Language Interface)编程语言界面。基于C的对Verilog数据结构的程序访问。
  • SDF:Standard Delay Format.(标准延迟格式)。时序数据OVI标准格式。
  • SITD:Single-Source Interconnect Transprot Delay,单一源互连传输延迟。和MIPD相似,但支持带脉冲控制的传输延迟。
  • SPF:Standard Parasitic Format.(标准寄生参数格式)。提取的寄生参数数据的标准格式。

精确时序仿真–时序标注

通常的Verilog元件库仅包含固定时序数据。

若要进行精确的时序仿真,还需要的数据有:

  • 输入传输时间

  • 固有延迟

  • 驱动强度

  • 总负载

  • 互连寄生

  • 环境因子

    • 过程
    • 温度
    • 电压

    同时还需要仿真最坏情况下的数据和最佳情况下时钟,反过来也要做一次。在没有时序标注时Verilog仿真器做不到这一点。

时序数据流

时序数据流程

延时计算器需要:

  • 综合出来的网表
  • 布局布线工具产生的简化的寄生参数

延迟计算器可以产生:

  • 粗略延迟,仅基于设计连线和层次
  • 详细延迟,由后端工具提取的寄生参数信息

有时序驱动的自顶而下的设计方法中,时序约束贯穿整个设计流程。与时序数据仅向后反馈的情况,如从布线布线工具反馈到综合工具,相比,这种方法时序收敛速度快。

前端和后端工具使用统一的延迟计算器 会提高时序收敛速度。

大多数EDA工具接受标准延迟格式(SDF)。

SDF(标准延迟格式)

标准延迟格式(SDF)是统一的时序信息表示方法,与工具无关。它可以表示:

  • 模块路径延迟——条件的和无条件的
  • 器件延迟
  • 互连延迟
  • 端口延迟
  • 时序检查
  • 路径和net时序约束

注意:在specify块中不能说明互连延迟或输入端口延迟。要用互连延迟仿真,必须进行时序标注。

模块输入端口延迟(MIPD)描述的是到模块输入端口或双向端口的延迟。延迟为惯性的且影响三种跳变:到1,到0,和到z。

单一源输入传输延迟(SITD)和MIPD相似,但使用传输延迟并且有全局和局部脉冲控制。SITD影响6种跳变:0到1,1到0,0到z,z到0,1到z,z到1。

多重输入传输延迟(MITDs)和SITD相似,但允许为每个源-负载通路说明独立延迟。

SDF(Stand Delay Format)文件

标准延时格式(SDF)是一种标准的,与工具无关的表示时序数据的文本格式。SDF文件通常用于Verilog仿真。教程不对SDF做详细介绍。

应注意的是,Verilog仿真器必须能够将SDF文件中的数据标注用于仿真。这些数据包括:

  • 增量或绝对延时,如模块路径,器件、内部连接和端口(包括输入端口延时)
  • 时序检查,如setup, hold, recovery, skew, width period
  • 时序约束,如path
  • 条件或无条件模块路径延时
  • 设计、实例、类型或库的专用数据
  • 比例、环境、工艺及用户定义基本单元

SDF允许不同工具共享延时数据。可以将关键路径信息由综合器传递给布局布线工具,也可将内部连接线延时信息由布局布线工具反传给仿真器。

SDF举例

内部连接延时

内部连接延时是对器件之间连接线延时的估算。例如:

上面的例子中的内部连接延时说明了一个input到output连接的线延时。

延时分上升、下降和关断延时,每种延时又有最好、典型和最坏值。

  • 限定于敏感边沿的iopath,时钟到输出;用上升、下降的最好、典型、最坏值说明。

  • 条件iopath,input到output;用上升、下降和关断的最好、典型、最坏值说明

IOPATH延时

IOPATH延时是器件从输入端口到输出端口的一个合法路径上的延时。

例如:

在上面IOPATH延时的例子中包括:

  • 端口IOPATH,从输入到输出;用最好、典型和最坏值说明上升和下降延时。
  • 限定敏感边沿的IOPATH,从时钟到输出,用最好、典型和最坏值描述其上升和下降延时。
  • 条件IOPATH,从输入到输出;用最好、典型和最坏值描述其上升、下降和关断延时。

在上面IOPATH延时的例子中,实例test.u1.u2 需要一个如下面所示的specify块用于反标注。

specify
     ( in => o1) = (1:2:3, 1:3:4);
     ( ck => o1) = (2:3:4, 4:5:6);
     if ( en ) ( in => o2) = (2:4:5, 4:5:6, 4:5:7);
endspecify

注:SDF文件中的时序信息覆盖specify块中的时序信息

SDF实例-dffpg模块及测试程序

SDF实例

SDF标注工具

用系统任务$sdf_annotate标注SDF时序信息。

可以交互式界面调用这个任务,或在源代码中调任务。

$sdf_annotate ("sdf_file", [module_instance,
          "config_file"," log_file", "mtm_spec",
          "scale_factors"," scale_type"]);
  1. sdf_file:SDF文件名称和绝对或相对路径

  2. module_instance:标注范围。缺省为调用$sdf_annotate所在的范围

  3. config_file:配置文件的绝对或相对路径。缺省使用预设的设置。

  4. Log_file:日志文件名,缺省为sdf.log。可以用+sdf_verbose选项生成一个日志文件。

  5. Mtm_spec:选择标注的时序值,可以是{MINIMUM,TYPICAL,MAXIMUM,TOOL_CONTROL}之一。缺省为TOOL_CONTROL(命令行选项)。这个参数覆盖配置文件中MTM关键字。

  6. Scale_factors:min:typ:max格式的比例因子,缺省为1.0:1.0:1.0。这个参数覆盖配置文件SCALE_FACTORS关键字。

  7. Scale_type:选择比例因子;可以是{FROM_MINIMUM, FROM_TYPICAL, FROM_MAXIMUM, FROM_MTM}之一。缺省为FROM_MTM。这个参数覆盖配置文件中SCALE_TYPE关键字。

注意:除sdf_file的所有参数可以忽略。sdf_file可以是任意名字,然后在运行时使用命令行选项+sdf_file选项指定一个sdf_file。

执行SDF标注

在下面的例子中,在设计的最顶层进行带比例的SDF标注

module top;
    . . . . . . . .
    initial      $sdf_annotate ("my.sdf", , , , , 1.6:1.4:1.2);
    . . . . . . . .
endmodule

在下面的例子中,对不同的实例分开标注

module top;
    . . . . . . . .
    cpu u1 ( . . .
    fpu u2 ( . . .
    dma u3 ( . . .
    . . . . . . . .
    initial begin
        $sdf_annotate ("sdffiles/cpu.sdf", u1, ,"logfiles/cpu_sdf.log");
        $sdf_annotate ("sdffiles/fpu.sdf", u2, ,"logfiles/fpu_sdf.log");
        $sdf_annotate ("sdffiles/dma.sdf", u3, ,"logfiles/dma_sdf.log");
    end
    . . . . . . . .
endmodule

和SDF标注相关的命令行选项:

惯性(inertial)和传输(transport)延时模型

对于惯性延迟,若路径延时小于门的固有延时,信号会被淹没。

对于传输延迟,输入上每个变化都会反映到输出上。

仿真器使用缺省的延迟模型,有的可以用命令行选项,有的用仿真器专用的编译指令指定延迟模型。

固有延时和传输延时模型

仿真时可以用固时延时模型或传输延时模型

  • 固有延时模型(缺省模型)不传送脉冲宽度小于电路延时的信号。这是开关电路的行为特性
  • 在传输延时模型中,输入上的所有变化在路径延时之后反映到输出上。这是传输线的行为特性。
  • 采用命令行选项+transport_path_delays设置传输延时模型

注意:记住使用+pathpulse用于路径延时控制

编译控制的使用

Verilog模型库

生产商提供了大量的Verilog库。这些库并不是Verilog仿真器专用的,但其库管理格式都基于Verilog-XL风格。

库中每个元件都包括功能及工具专用的时序及工艺信息。

  • ASIC和FPGA生产商开发并提供工艺专用库
  • 设计人员用库中的元件建立网表
  • 仿真器在编译时扫描模型库寻找实例化模块

合成库可以支持多种工具,例如它可以包含下列工具所需要的信息

  • 仿真器(如Verilog-XL和NC Verilog)
  • 综合器(如Synopsys)
  • 时序分析器(如Pearl)
  • 故障仿真(Verifault-XL)

单元库建模

建立Verilog单元模型库,需要:

  • 每个元件(或单元)用一个module描述
  • 将相关的module放在同一个文件或同一个目录中

可以用两种抽象级描述库单元

  • 结构级
    • 用 Verilog基本单元或UDP
    • 用于描述组合逻辑或简单的时序逻辑
  • 行为级
    • 用过程块或赋值语句
    • 用于描述大的或复杂的元件,如RAM或ROM

库单元的特点:

  • 每个库单元的描述在编译指令`celldefine和`endcelldefine之间
  • 每个库单元的描述有两部分:
    • 功能描述
    • 时序描述

Verilog库的使用

在Cadence Verilog仿真器中使用Verilog库:

  • 使用库文件
    • 在命令行中使用选项:-v file_name
  • 使用库目录
    • 在命令行中使用选项 –y directory_name
    • 在命令行中使用选项 +libext+file_extension

在使用库目录时,如果每个文件都有一个扩展名,则在Cadence Verilog仿真器必须用+libext选项指定其扩展名。仿真器中没有缺省地使用.v作扩展名

使用-v或-y选项指定库时,只编译那些设计中用到的模块。如果在命令行中直接输入库文件名而没有使用-v选项 (或在文件中使用编译指令`include),则库中所有模块都被编译。使用选项大大压缩编译时间及内存空间。在NC Verilog中也压缩了使用的磁盘空间。

如果没有使用-v选项,而是:

  • 在命令行中直接输入库文件名,
  • 或在文件中使用编译指令`include,

则库中所有module都被编译。使用选项大大压缩编译时间及内存空间。在 NC Verilog中也压缩了使用的磁盘空间。

库文件扫描

每一个-v选项指定一个库文件

verilog test.v design.v -v library_file.v

`timescale 1ns/1ps
`celldefine
module BUFX2 (Y, A);
output Y;
input A;
buf I0(Y, A);
specify
// delay parameters
    specparam
        tplh$A$Y = 1.0,
        tphl$A$Y = 1.0;
// path delays
    (A *> Y) = (tplh$A$Y, tphl$A$Y);
endspecify
endmodule // BUFX2
`endcelldefine

库目录扫描

每一个-y选项指定一个库目录。

+libext+选项指定有效的文件扩展名。

verilog test.v design.v -y library_directory +libext+.v

编译指令`uselib

  • 定义设计中使用的库元件(包括UDP)的位置
  • 一直有效,直到遇到另一个`uselib或`resetall
  • 覆盖任何命令行选项中库的设置。也就是说如果不能在`uselib指定的位置找到元件,仿真器不会再按命令行中-v或-y选项去寻找。

编译指令`uselib使用举例

在`uselib中库的指定可以使用由`define定义的宏进行文本替换。

`define  TTL_LIB    dir=/libs/TTL  libext=. v
`define  TTL_UDP   file=/ libs/ TTL_U/udp.lib
`uselib  `TTL_LIB    `TTL_UDP

在命令行中用+define+选项给宏一个值。设计易于管理,可移植性高。

某Windows仿真器—建库

编写与大小无关的源代码

Verilog是对大小写敏感的语言,如sel和SEL是不同的标识符

  • Verilog 关键字均使用小写,如input, output
  • 标识符中大小写都可以使用,但Sel和sel是不同的标识符
  • 仿真时使用-u选项进入大小写不敏感模式。仿真器将所有标识符转换为大写形式,而关键字仍保持为小写。
module MUX2_1 (out, a, b, sel);
     output out;
     input a, b, sel;
     not not1( SEL, sel);
     and and1( a1, a, SEL);
     and and2( b1, b, sel);
     or or1( out, a1, b1);
endmodule

在正常情况下,左边例子中sel和SEL是不同的信号。若使用-u选项,sel和SEL变为相同的信号。

将产生错误的仿真结果。

如果在大小写不敏感的工具中使用这个模型,则用-u选项可以找出错误。

可以用-d选项输出-u选项产生的大小写不敏感的描述

编译指令

尽管编译指令是Verilog语言的一部分,但其作用取决于编译器,因此不同的仿真器中其作用可能不同。

  • `resetall将编译指令变为缺省值。

  • Cadence Verilog仿真器在遇到`resetall时,文本宏定义不变。要清除文本宏定义,使用

​ `undef macro_name

  • 在使用`include编译指令时,使用+incdir命令行选项指定所包含文件的查找路径。

​ +incdir+directory1+directory2+…directoryN

Include不要用路径

  • 仿真器首先查找当前目录,若没有找到再沿指定路径顺序查找。

编译指令从出现时开始有效,直到被覆盖或使其失效。因此编译指令是全局的。

下列编译指令是Verilog IEEE标准中的:

`include举例

定义文本宏文件举例

FIFO_control_define.v

`ifndef FIFO_CONTROL

`define FIFO_CONTROL

//Tx register address
`define TxSOF0         8'b0000_0000
`define TxFramehead00    8'b0000_0001
`define TxFramehead01    8'b0000_0010
`define TxFramehead02    8'b0000_0011
`define TxFramehead03    8'b0000_0100
`define TxFramehead04    8'b0000_0101
`define TxFramehead05    8'b0000_0110
`define TxEOF0        8'b0000_0111


`endif  

verilog FIFO.v FIFO_ctrl.v +incdir+./include

`timescale 1ns /1ns
`include “FIFO_control_define.v”
module FIFO (
……
);
……
endmodule
`timescale 1ns /1ns
`include “FIFO_control_define.v”
module FIFO_ctrl (
……
);
……
endmodule

实例化不同module

`ifdef FPGA //实例化 Alrera FPGA的64x8 RAM2P
SRAM64x8 uSRAM64x8 (
.byteena_a ( 1'b0 ),
.clock ( clk ),
.data ( DB ),
.rdaddress ( AA ),
.rden ( CENA ),
.wraddress ( AB ),
.wren ( CENB ),
.q ( QA )
);
`else //实例化SMIC SRAM2P
S65NLLHS2PH64x8 uS65NLLHS2PH64x8 (
.QA ( QA ), 
.CLKA ( clk ), 
.CLKB ( clk ), 
.CENA ( CENA ), 
.CENB ( CENB ), 
.BWENB ( 8'b0 ),
.AA ( AA ), 
.AB ( AB ), 
.DB ( DB )
);
`endif

定义文本宏

在命令行定义文本宏: +define+命令行参数

语法:+define+MACRO_NAME=“MACRO_TEXT

注意:文本宏的覆盖可能影响设计的结构,可能强制NC Verilog重新编译全部或部分设计

  • 文本宏中字符串长度没有限制。

  • 清除文本宏定义,使用:

​ `undef macro_name

  • 清除所有文本宏定义,使用

​ `undefall

verilog test. v +define+gate="or"
`define gate and
module test;
     reg a, b;
     `gate (c, a, b);
     initial
          begin
               a= 0; b= 1;
               $monitor ($time,, c, a, b);
               #1 $finish;
          end
endmodule

复习

问题:

  • 当仿真器遇到编译指令`resetall时将所有编译指令置为缺省值吗?
  • 使用什么选项指定库的名字?
  • 如果仿真器没有在编译指令`uselib指定的库中找到实例的定义,它会去哪里寻找?

解答:

  • 不是。当使用编译指令`resetall时,IEEE规范没有说明如何处理文本宏。Cadence Verilog仿真器对文本宏不作处理。要重文本宏,使用编译指令`undef。
  • 使用-v选项和/或-y及+libext+选项。
  • 不会再去别的位置查找。

高性能编码风格

if 语句

case语句

晚到达信号处理

​ 设计时通常知道哪一个 信号到达的时间要晚一些。这些信息可用于构造HDL,使到达晚的信号离输出近一些。

​ 下面的例子中,针对晚到达信号重新构造if和case语句,以提高逻辑性能。

晚到达的是数据信号-无优先级

顺序if语句可以根据关键信号构造HDL。在例1.1a 中,输入信号d处于选择链的最后一级,也就是说d最靠近输出。

假如信号b_is_late是晚到达信号,我们就要重新构造例1.1a使其最优化。

晚到达的是数据信号-保持优先级

​ 顺序if语句可以根据关键信号构造HDL。在例1.1a 中,输入信号d处于选择链的最后一级,也就是说d最靠近输出。

​ 假如信号b_is_late是晚到达信号,我们就要重新构造例1.1a使其最优化。

晚到达的是控制信号

如果晚到达信号作为if语句条件分支的条件,也应使这个信号离输出最近。在下面的例子中,CTRL_is_late是晚到达的控制信号

if-case嵌套语句

归约XOR

树形结构实现

高性能编码技术

module BEFORE (ADDRESS, PTR1, PTR2, B, CONTROL, COUNT);
    input [7:0] PTR1, PTR2;
    input [15:0] ADDRESS, B;
    input CONTROL; // CONTROL is late arriving
    output [15:0] COUNT;
    parameter [7:0] BASE = 8’b10000000;
    wire [7:0] PTR, OFFSET;
    wire [15:0] ADDR;

    assign PTR = (CONTROL ) ? PTR1 : PTR2;
    assign OFFSET = BASE - PTR; 
    assign ADDR = ADDRESS - {8’h00, OFFSET};
    assign COUNT = ADDR + B;

endmodule

在某些情况下,可以通过重复逻辑来提高速度。

在下面的例子中,CONTROL是一个晚到达的输入信号。要提高性能,就要减少CONTROL到输出之间的逻辑。

module PRECOMPUTED (ADDRESS, PTR1, PTR2, B, CONTROL, COUNT);
    input [7:0] PTR1, PTR2;
    input [15:0] ADDRESS, B;
    input CONTROL;
    output [15:0] COUNT;
    parameter [7:0] BASE = 8’b10000000;
    wire [7:0] OFFSET1,OFFSET2;
    wire [15:0] ADDR1,ADDR2,COUNT1,COUNT2;

    assign OFFSET1 = BASE - PTR1; // Could be f(BASE,PTR)
    assign OFFSET2 = BASE - PTR2; // Could be f(BASE,PTR)
    assign ADDR1 = ADDRESS - {8’h00 , OFFSET1};
    assign ADDR2 = ADDRESS - {8’h00 , OFFSET2};
    assign COUNT1 = ADDR1 + B;
    assign COUNT2 = ADDR2 + B;
    assign COUNT = (CONTROL == 1’b1) ? COUNT1 : COUNT2;

endmodule

​ 在下面的例子中,if语句的条件表达中包含有操作符。

其它要注意的问题

  • 不要引入不必要的latch
  • 敏感表要完整
  • 非结构化的for循环
  • 资源共享

不要产生不需要的latch

条件分支不完全的条件语句(if和case语句)将会产生锁存器

敏感表要完整

不完整的的敏感表将引起综合后网表的仿真结果与以前的不一致。

资源共享

资源共享是指多节代码共享一组逻辑。例如:

资源共享可以由RTL代码控制。

例如,可以改变编码风格强制资源共享。

只有在同一个条件语句(if和case)不同的分支中的算术操作才会共享。

条件操作符 ?: 中的算术操作不共享。

括号的作用

利用括号分割逻辑

逻辑构造块的编码格式

下面介绍某些常用逻辑块,如译码器的不同的编码格式。每种块给出了一个通常格式和建议格式。

所有的例子的位宽都是参数化的。

3-8译码器

译码器

优先级编码器—高位优先

优先级编码器

显式有限状态机

逻辑综合

逻辑综合介绍

基于标准单元的设计流程

什么是综合?

工艺无关性

设计可以转换到任何工艺上。

Logic Synthesis Overview

HDL Compiler

HDL Compiler将HDL描述转换为Synopsys设计块,并传送给Design Compiler

在逻辑图中, 我们可以可看到,verilog文件已经转换为 GTECH 库(the synopsys default)

Design Compiler

Design Compiler将 Synopsys设计块映射到用户指定库的门级设计

Synopsys相关文件

注意:

1.这三个文件总是按所列顺序读取

2.对于相同的设置项,后面的设置覆盖前面的设置

.bashrc文件

####################################################################
# This for synopsys software 2020.5.26
######################################################
export LM_LICENSE_FILE=27020@server1:27020@server-lic
export SNPSLMD_LICENSE_FILE=27020@server1:27020@server-lic
export PATH=$PATH:/sbin:/usr/sbin
####################################################################
# For DC2020
# install_software = syn_VR-2020.09-SP5
####################################################################
export SYNOPSYS=/opt/eda/synopsys
export SYN_DC_DIR=$SYNOPSYS/syn/R-2020.09-SP5
export PATH=$PATH:$SYN_DC_DIR/bin
export PATH=$PATH:$SYN_DC_DIR/linux64/bin
export PATH=$PATH:$SYN_DC_DIR/linux64/syn/bin
####################################################################
# For PrimeTime-2008
export SYN_PT_DIR=$SYNOPSYS/pt2008
export PATH=$PATH:$SYN_PT_DIR/bin
####################################################################

.synopsys_dc.setup中定义的内容

  • link_library :这个库用来解释所输入的设计描述
    • 在用户HDL代码中的门级网表或实例化的单元
  • target_library:所要映射到的ASIC工艺库
    • 在综合过程中使用的wire load或Operating condition模型
  • symbol_library:用来生成逻辑图
  • search_path:定义所引用的库或设计的查找路径
  • synthetic_library:要用到的designware库
  • 其它变量
search_path =  {. /my_library} + search_path;
link_library = {“*”, “my_link.db”, “dw_foundation.sldb”} ;
target_library = {my_target.db};
symbol_library = {generic.sdb} ;
synthetic_library = {“dw_foundation.sldb”};

hdlin_translate_off_skip_text = "TRUE"
edifout_netlist_only = "TRUE"
verilogout_no_tri = true ;
plot_command = "lpr -Plp" ;\
view_script_submenu_items =\
{"Avoid assign statement", "set_fix_multiple_port_nets -all -buffer_constant", \
"Change Naming Rule", "change_names -rule verilog -hierarchy", \
"Write SDF", "write_sdf -version 1.0 -context verilog chip.sdf"}
# from the System Variable Group
set smic13_path /data/techlib/smic/smic13/smicSC/aci/sc-x
set search_path [list ${smic13_path}/symbols/synopsys 
 ${smic13_path}/synopsys $search_path]
set link_library [list * typical_1v2c25.db dw_foundation.sldb]
set target_library [list typical_1v2c25.db]
set symbol_library [list smic13g.sdb]
set synthetic_library [list dw_foundation.sldb standard.sldb]
set command_log_file "./command.log"
set designer "Yuds"
set company "PKU"
set find_converts_name_lists "false"

ASIC综合的设计流程

综合对象

设计对象(逻辑图示)

  • Design(设计):完成一个或多个逻辑功能 的电路
  • Cell (单元) :一个设计在另一个设计中的实例
  • Reference (引用) :单元(Cell)指向的原始设计
  • Port (端口) :设计的输入或输出
  • Pin (引脚) :单元(Cell)的输入或输出
  • Net (网线) :Port-Pin或Pin-Pin之间的连接线
  • Clock (时钟) :指定为时钟源的Port或Pin上所加的波形

设计对象(Verilog图示)

练习

静态时序分析

静态时序分析(Design Time)

  • 分析电路是否符合时序约束( timing constraint),不需要仿真
    • 将设计划分为一系列时序路径( timing path )
    • 计算每一个路径的延时
    • 检查所有的路径延时是否符合时序约束

Design Compiler中的时序路径

  • Design Time将设计划分为一系列的信号路径,每一路径都有起点和终点
    • 起点:输入端口(Port)和时序器件的clock引脚(pin)
    • 终点:输出端口(port)和时序器件的数据输入引脚(pin)

时序路径组(group)

如何将时序路径构成一组?

时序路径按控制终点的时钟划分成组

与每一个时钟相关联的一组路径构成一个组

缺省情况下,其它与时钟无关的所有路径构成一个组

练习

逻辑图转换为时序图

  • 为了计算总的延时,Design Time将每一条路径划分为时序弧 (timing arc)。
  • 时序弧:一段连接线(net)延时或一个单元(cell)延时

  • 计算路径延时举例
    • 将所有延着路径的网线和单元的时序弧加起来。

IP Library

DesignWare Library

  • DesignWare Library是一组可重用的、可综合的IP块,集成在Synopsys综合环境中。

  • 缺省的DesignWare库是standard.sldb,包括:

    • adder: + , +1
    • substractor: -, -1
    • comparator: ==, !=, <, <=, >, >=
    • mulpilier, divider
    • sin cos tan
    • Sqrt FIR IIR
  • 如果用户在设计中使用了DesignWare的元件,用户可以使用约束来改变其实现方式

  • 实现方式查看DW手册

  • 如果用户要使用DesignWare库,必须在.synopsys_dc.setup中设置 “synthetic_library” 和“search_path”

  • Example:synthetic_library = {“dw_foundation.sldb”}

  • 如果模块在不同的库中都有并且有相同的名字,则使用所列的第一个库中的模块。

DesignWare Part

  • 有两种方法使用DesignWare中的元件:

    – 推 断: 由design compiler根据约束选择DesignWare元件

    – 实例化: 显式的实例化synthetic模块

  • Example

  • Example : assign c = a + b;

DW-Select implementation

  • 在使用DesignWare库中的元件时, 我们可以选择实现方式。例如,当使用元件 “dw01_add”时, 可以明确指定这个加法器是一个cl*a-adder 或是一个 *rpl –adder。

  • 如何指定?

​ – 在RTL 代码中指定(嵌入式)

​ – 使用dc_shell命令“set_implementation”

  • 与DesignWare相关的所有信息包含在Synopsys联机文档(SOLD)中的 “DesignWare”部分。

Synthesis Implementation

Implementation – 嵌入式

  • Example – 加法器,采用元件 “dw01_add”,实现方式 “ cla”

DesignWare – set_implementation

  • 指定DesignWare元件的实现方式

    – 选择要设定实现方式的的元件,查看其实例名是什么。

    – 根据Synopsys联机文档的DesignWare部分选择实现方式

    – 使用dc_shell命令

set_implementation implementation_name instance_name

– compile

– report -resource

DesignWare 仿真

  • 推断方式 : 和以前一样,不需要任何专门处理。

  • 实例化方式: 在$SYNOPSYS目录, 可以找到designware的仿真模型, 在做仿真时加上这个仿真模型

​ – 其实际目录是:

​ » $SYNOPSYS/dw/dw0x/src – for VHDL

​ » $SYNOPSYS/dw/dw0x/src_ver – for verilog

//synopsys translate_off
`include "/synopsys/synthesis/cur/dw/dw01/src_ver/DW01_sub.v"
`include "/synopsys/synthesis/cur/dw/dw02/src_ver/DW02_mult.v"
`include "/synopsys/synthesis/cur/dw/dw02/src_ver/DW_div.v"
`include "/synopsys/synthesis/cur/dw/dw02/src_ver/DW02_mac.v"
`include "/synopsys/synthesis/cur/dw/dw02/src_ver/DW_square.v"
//synopsys translate_on

设置设计约束

设置环境约束

为什么要描述一个真实的外部环境

  • 必须要清楚,缺省的条件是不实际的

    – 输入驱动不是无限大。

​ – 负载电容通常不是0

​ – 要考虑工艺, 温度, 及电压的变化

  • 工作环境影响目标库元件的选择以及设计的时序
  • 用户定义的真实的环境 描述电路的工作条件。

设计环境– Operating Environment

工作条件—在综合库中定义

  • 工作条件模型 scale 元件延时,
  • 指导优化器模拟工艺、温度和电压变化。

输入驱动强度—驱动电阻

PAD的输入驱动强度

  • 如果设计如下:

​ 假定使用的输入PAD为PC3D01, 如下图所示。我们可以设置输入驱动强度为0.2468 (ns/pf)

PAD的输出负载

  • 如果设计如下

    ​ 假设使用的输出PAD为PC3O01, 如下列所示。我们可以设置输出负载为0.096 (pf)

负载预算–Load Budget

  • 可以采用下列规则:
    • 假设输出端口是由驱动能力弱的单元驱动(最小的反相器)
    • 限制每个输入端口的输入电容(如负载不大于10个AND2的电容)
    • 估算输出端口驱动模块的数目

Wire Load Model

  • wire load model根据芯片面积和标准单元的扇出估算连接线上的电容

  • 在编译时设置这项信息,可以更精确的建立设计模型。

三种模型

#或设“auto_wire_load_selection”为true,由工具自动选择

set_app_var auto_wire_load_selection false
set_wire_load_model –name smic13_wl20
set_wire_load_mode enclosed

Wire Load Model举例(Design Time)

  • 要计算连接线的R、C及连线面积
  1. 确定连接线的fanout数

  2. 在wire_load_model的fanout-length对中查找连接线长度

  3. 长度乘上电容(或R或面积)系数

​ (fanout = 3 ® length = 2.8)

Cwire = length(2.8) ´ capacitance coefficent (1.3) =3.64 load units

Rwire = length (2.8) ´ resistance coefficient (3.0) = 8.4 resistance units

Net area = length (2.8) ´ area coefficient (0.04) = 0.112 net area units

(fanout = 7  length = 3.3 + (7-4)*0.15 ) = 3.75

Cwire = (3.3 + (7-4)*0.15) ´ capacitance coefficent (1.3)

设置设计约束

约束(constraint)

  • 约束是Design Compiler优化一个设计到目标工艺库的目标
  • 设计规则约束:与工艺相关的限制,如最大的传输时间、最大的扇出、最大电容等。
  • 优化约束:设计目标及要求。如最大延时、最小延时、最大面积、最大功耗等。
  • 在编译时,Design Compiler试图满足所有约束。

优化约束

  • 按关注的次序,优化约束有:

1.最大延时

2.最小延时

3.最大功耗

4.最大面积

  • 对于组合电路,我们在时序上只需设置最大延时和最小延时

需要什么设计约束?

需要约束的时序路径有四类:

1.输入端口到输出端口的组合逻辑

2.输入端口到时序元件的数据输入端

3.时序元件的时钟端到时序元件的数据输入端

4.时序元件的时钟端到输出端口

Maximum Delay Constraint

  • 对于组合电路,主要是

    – 选择时序通路起点和终点。

    Attributes/Optimization Constraints/Timing Constraints

时序元件相关的路径

时序元件相关的时序路径有三类:

1.输入端口到时序元件的数据输入端

2.时序元件的时钟端到时序元件的数据输入端

3.时序元件的时钟端到输出端口

时序电路->指定时钟

  • 选择时钟端口

  • Attributes/Clocks/Specify

时钟分布的特点

  • 许多ASIC设计有一个或多个时钟。时钟树的实现决定了设计的最高工作频率。 如果实现的不好可能导致整个设计不能正常工作。
  • 上面的图显示了一个时钟网络。时钟sysclk从一个I/O PAD输入后由单元CDR驱 动。这个时钟网格有n个负载。
  • 时序图中显示了时钟树的clock skew和时钟插入延迟。 有效的时钟树就是减小时钟倾斜和时钟插入延迟。
  • 插入延迟是时钟有效沿从芯片的输入引脚到达负载的时间。
  • 时钟倾斜是同一个时钟有效沿到达各个负载的时间差。

时钟树设计

  • 上面的图是一个时钟树的逻辑图表示。插入到根(root)单元和叶(leaf)单元 之间的缓冲器称为分布单元(Distribution cell)。最后的分布单元驱动一组 叶单元。
  • 时钟树可以分为任意级数。这个例子为两级。靠近root单元的级数低(lower level),靠近叶单元的级数高(upper level)
  • 时钟树的插入延迟是就从根单元到叶单元之间所有缓冲器的固有延时及其连 接线延时的总和。
  • clock skew是时钟树不同分支之间的延迟差。
  • 一个有效的时钟分布就是要减小插入延迟和时钟倾斜。要达到这些设计目标 ,要求时钟分布满足下列标准:
    • 分布单元在驱动和负载之间合理匹配,减少传输时间(transition time) 。
    • 时钟树中输出单元到后面的输入间的互连线产生的延时差要小。
    • 时钟树的每一级的连接线及负载要均衡。

  • 上面的图给出了一个可能的时钟树分配系统的布局和内部连接布线。
  • 这是一个理想的时钟树物理实现。这个布局中,树的每一级的输入、输出对 之间的连线长度完全相等。如,树最底层的根单元到各个分配单元之间的距 离几乎完全相等。最顶层的各组叶单元的数目也相同。但是这种实现对于布 线不一定是最优的。为了达到最优布线,叶单元的布局不会非常规则。
  • 另外,在这个实现中假定各个叶单元的输入引脚的负载是相同的。但负载很 有可能并不相同。
  • 在布局前设计时钟树是非常困难的。必须考虑布局以及不同叶单元的输入引 脚的负载的影响。

指定时钟

时钟树建模

  • 时钟建模需要两个参数

1.指定时钟网络的延迟

​ » set_clock_latency –rise tr –fall tf find (clock, CLK)

2.指定时钟网络的倾斜(uncertainty)

​ » set_clock_uncertainty –rise tp –fall tm find (clock, CLK)

时钟树建模举例

时钟树建模对建立时间的影响

假设库中上升沿D触发器(Flip Flop) setup time = 1ns

create_clock –period 10 –waveform {0 5} find (port CLK)

set_clock_latency –rise 1 –fall 2 find (port CLK)

set_clock_uncertainty –rise 0.5 –fall 0.7 find (port CLK)

  • 假设库中上升沿D触发器的 (Flip Flop)保持时间( hold time) = 1ns
    • create_clock –period 10 –waveform {0 5} find (port CLK)
    • set_clock_latency –rise 1 –fall 2 find (port CLK)
    • set_clock_uncertainty –rise 0.5 –fall 0.8 find (port CLK)

时钟源延迟模型

  • 源延迟是从实际时钟源到设计的时钟定义点的传播延迟

create_clock -period 10 find(port, CLK)

set_clock_latency -source 3 find(port, CLK)

set_clock_latency 1 find(port, CLK)

衍生时钟(Derived Clock)

外部时钟延迟(举例)

current_design my_design
create_clock –p 10 find(port, CLK)
create_clock –p 10 –name VCLK      //虚拟时钟
set_clock_latency –source 2 find(clock, CLK)
set_clock_latency –source 1 find(clock,VCLK)
set_clock_latency 1 find(clock, CLK)
/* set_propagated_clock all_clocks()*/ /*For post-layout Synthesis*/
set_input_delay 0.4 –clock VCLK find(port, A)

时序电路

输入延时模型

设置输入延时

  • 如果输入是PAD( top level),则将输入延时设置一个适当值。(参考PAD的数据手册或使用characterize命令)

设置输出延时

  • 如果设计的输出连接到PAD(top level),将输出延时设置为一个适当的值。(参考PAD数据手册或使用characterize命令)

What have we modeled?

  • 假设 clock cycle = p
  • 输入延时 = a ; a + b < p
  • 输出 延时= e ; d + e < p

  • 假设时钟CLK的时钟周期(clock cycle) = p
  • 输入延时 = a ; a + b < p
  • 输出 延时= e ; d + e < p

设置面积约束

设计规则约束

  • 设计规则 (design rule)是绝对不能违反的,即使时间及面积约束不能满足也在所不惜。

  • 设计规则约束有三类:

  1. set_max_capacitance

  2. set_max_transition

  3. set_max_fanout

设置最大转换时间

计算最大转换时间

计算最大的fanout_load

计算最大电容

设置扇出负载

False Path

  • false path是不传播信号的通路,或是忽略这个通路上的时序约束。
  • set_false_path可以禁止一个通路的基于时序的综合
  • 它主要用于:

约束异步通路

从逻辑上约束 false path

  • set_false_path -from {A} -through {C} -to {OUT}

  • set_false_path -from {B} -through {D} -to {OUT}

多周期通路

  • 在有些情况下,两个寄存器之间的组合逻辑延迟可能要求多于一个时钟周期,这些通路应设置为multicycle通路。

设计检查

  • 在设置设计属性和设计约束后,建议下一步对设计进行检查。
  • Analysis/Check Design
  • 你可能遇到下列警告:

  • 这个警告信息称为 “multiple design instance”, 它产生的原因是用相同的HDL描述多次实例化(instance)。

  • 如何处理 ?

​ – dont_touch

​ – ungroup

​ – uniquify

dont_touch

  • 停止对低层设计的重新编译
  • 层次将被保留
  • 单个设计描述可以共享
  • 用于那些不需要用户做优化的块
  • 在设计优化过程中, dont_touch 块不会再优化
  • 如果dont_touch设置在一个unmapped 设计, 设计将保持unmapped

Attributes/Optimization Directives/Design

  • 使用过程

    对块进行约束

    – 块编译

    – 选择设计中要多实例化的块

    – Attributes/Optimization Directives/Design,设置dont_touch按钮

    – 用层次编译方式编译整个设计

Ungroup

  • 过程

    – 选中多实例化设计块

    Attributes/Optimization

Directives/Design 并设置

​ Ungroup按钮

– 用层次化方式编译整个设计

  • 展开设计层次
  • 不保留设计层次
  • 消耗更多的存储器
  • 更多的编译时间
  • 产生最好的设计结果

Uniquify

  • 为每一个实例建立一个单独的文件
  • 可以选择一个单元或整个设计层次进行uniquify
  • 允许设计定制自己的界面
  • 如果环境的变化很大,使用uniquify*而不使用*Compile+dont_touch
  • 与compile+dont_tough相比,Uniquify 使用更多的存储器和更长的编译时间
  • 选择设计层次的最顶层(top)
  • Edit/Uniquify/Hierarchy

  • 使用变量uniquify_naming_style**创建新的设计名称, 缺省为 %s_%d

多设计实例(总结)

  • 采用“dont_touch, ungroup, uniquify”解决
  • 最简单的方法是uniquify, 但需要较多的存储器和编译时间
  • 如果希望保留设计层次并资源共享,使用dont_touch
  • 如果希望得到一个最好的结果,推荐使用 ungroup,但需要的存储器和编译时间最多。

总结

  • 设计实际的设计环境

    – 输入延时, 输出延时

    – 输入驱动强度, 输出负载

    – 工作条件

    – Wireload model

  • 设置Design Rule约束(DRC)

    – Maximum fanout

    – Maximum transition time

    – Maximum capacitance

  • 设置设计约束

    – 最大延时, 最小延时

    – 说明时钟

    – 最大面积

    – False path, multi-cycle path和multi-frequency时钟

    – 动态功耗, 泄漏功耗

  • 处理多实例化问题

    dont_touch

    – ungroup

    – uniquify

  • 在编译设计前进行检查

  • 保存设置文件并执行脚本文件

设计优化

设计编译

Compile: the “art”of Synthesis

Architectural-Level Optimization

逻辑级优化(Logic-Level)

  • 对电路的布尔表达式进行优化

  • 对整个设计面积/速度特性有全局的影响

  • 策略

    – structure

    – Flatten

    – 如果两者都选用, 则设计先flatten后structure

structuring和flattening

Flattening

Structuring

门级优化–映射

  • 使用逻辑优化产生的结构
  • 从工艺库中选择元件以实现这些逻辑结构,以满足电路指定的时间、设计规则和面积目标
  • 局部影响设计的area/speed特性

Mapping flow

–寻找基本配置的门来实现逻辑结构

–从逻辑上重新安排元件,以满足设计规则、面积、速度和功耗目标。

组合电路映射

时序电路映射

映射强度

  • 有三种强度, low, medium, high, 决定编译的映射过程所花费的CPU时间的相对数

–low – 较快的综合, 不做所有算法

–medium – 缺省, 对大多数设计是足够的

–high – 将关键路径重新综合,但会花费较多的CPU时间;在有些情况下,编译会陷入死循环。

  • Note: 在Synopsys的编译过程

​ 需要很长时间,因此在进行编译

​ 之前,请确认设置的属性和约束。

Compile Summary

逻辑级优化

– 优化电路的布尔表达式优化

– Flattening (off by default)

​ » 消除所有的结构

​ » 当设计输入少于20个时,关闭structure,打开flattening

– Structuring

​ » 寻找共因子以减小面积

​ » 时序驱动的 (default)

​ » 布尔优化 (area optimization only)

门级优化

– Mapping ( medium effort is default )

​ » 从工艺库中选择元件

​ » 使用逻辑级优化产生的结构

层次化编译技术

– top-down, bottom up and characterize

– Timing budgeting

​ » Design Budgeting

​ » Automatic Chip Synthesis (ACS)

Synthesis Report and Analysis

  • Analysis/Report
    • 通过报告及分析,可以查看设置的属性及优化后的结果

Report We will Generate

  • 属性报告

–所有的属性、clock、端口、设计及net

  • 分析报告

–面积、层次、约束、时间、节点时间

网线(net) Report

  • 网线报告显示每一条net的静态结果

****************************************
Report : net
Design : ALU_CLA32
Version: R-2020.09-SP5
Date : Tue Dec 14 14:59:23 2021
****************************************
Operating Conditions: typical_1v2c25 Library: typical_1v2c25
Wire Load Model Mode: enclosed
Design Wire Load Model Library
------------------------------------------------
ALU_CLA32 smic13_wl20 typical_1v2c25
Net Fanout Fanin Load Resistance Pins Attributes
--------------------------------------------------------------------------------
Cin 1 1 0.02 0.00 2 
Co 1 1 0.05 0.00 2 
DO[0] 1 1 0.05 0.00 2 
DO[31] 1 1 0.05 0.00 2 
M 1 1 0.02 0.00 2 
S[0] 1 1 0.02 0.00 2 
c[1] 2 1 0.05 0.00 3

端口(port) Report

  • dc_shell命令

​ report_port –verbose { port_list }

​ 或在选项菜单中选择verbose(详细)

****************************************
Report : port
 -verbose
Design : ALU_CLA32
Version: R-2020.09-SP5
Date : Tue Dec 14 15:05:39 2021
****************************************
Pin Wire Max Max Connection
Port Dir Load Load Trans Cap Class Attrs
--------------------------------------------------------------------------------
Cin in 0.0000 0.0000 1.02 0.04 -- 
M in 0.0000 0.0000 1.02 0.04 -- 
S[0] in 0.0000 0.0000 1.02 0.04 -- 
S[1] in 0.0000 0.0000 1.02 0.04 -- 
opA[0] in 0.0000 0.0000 1.02 0.04 -- 
Co out 0.0300 0.0000 -- -- -- 
DO[0] out 0.0300 0.0000 -- -- -- 
DO[1] out 0.0300 0.0000 -- -- -- 
DO[2] out 0.0300 0.0000 -- -- -- 
DO[3] out 0.0300 0.0000 -- -- --

面积(area) Report

  • 面积报告显示设计的门数

****************************************
Report : area
Design : ALU_CLA32
Version: R-2020.09-SP5
Date : Tue Dec 14 15:10:42 2021
****************************************
Library(s) Used:
 typical_1v2c25 (File: /data/techlib/smic/smic13/smicSC/aci/sc-x/synopsys/typical_1v2c25.db)
Number of ports: 255
Number of nets: 689
Number of cells: 475
Number of combinational cells: 467
Number of sequential cells: 0
Number of macros/black boxes: 0
Number of buf/inv: 229
Number of references: 15
Combinational area: 4567.703377
Buf/Inv area: 1276.444782 
Noncombinational area: 0.000000
Macro/Black Box area: 0.000000
Net Interconnect area: 92680.464661
Total cell area: 4567.703377
Total area: 97248.168037

层次(hierarchy) Report

  • 层次报告显示每块的使用的元件及其层次

Hierarchical area distribution
------------------------------
 Global cell area Local cell area
 ------------------ ----------------------------
Hierarchical cell Absolute Percent Combi- Noncombi- Black-
 Total Total national national boxes Design
-------------------------------- --------- ------- --------- --------- ------ ------------
ALU_CLA32 4567.7034 100.0 499.0356 0.0000 0.0000 ALU_CLA32
u[0].uALU_CLA4_G 799.4754 17.5 799.4754 0.0000 0.0000 ALU_CLA4_G_0
u[1].uALU_CLA4_G 549.9576 12.0 549.9576 0.0000 0.0000 ALU_CLA4_G_7
u[2].uALU_CLA4_G 490.5486 10.7 490.5486 0.0000 0.0000 ALU_CLA4_G_6
u[3].uALU_CLA4_G 449.8110 9.8 449.8110 0.0000 0.0000 ALU_CLA4_G_5
u[4].uALU_CLA4_G 470.1798 10.3 470.1798 0.0000 0.0000 ALU_CLA4_G_4
u[5].uALU_CLA4_G 451.5084 9.9 451.5084 0.0000 0.0000 ALU_CLA4_G_3
u[6].uALU_CLA4_G 436.2318 9.6 436.2318 0.0000 0.0000 ALU_CLA4_G_2
u[7].uALU_CLA4_G 420.9552 9.2 420.9552 0.0000 0.0000 ALU_CLA4_G_1
-------------------------------- --------- ------- --------- --------- ------ ------------
Total 4567.7034 0.0000 0.0000

report_hierarchy -full

报告每个模块的使用的元件及其层次

****************************************
Report : hierarchy
 -full
Design : ALU_CLA32
Version: R-2020.09-SP5
Date : Tue Dec 14 15:19:40 2021
****************************************
ALU_CLA32
 ALU_CLA4_G_0
 AND2X8 typical_1v2c25
 AOI2BB1X4 typical_1v2c25
 AOI2BB2X4 typical_1v2c25
 AOI22X1 typical_1v2c25
 AOI22X2 typical_1v2c25
 AOI221X2 typical_1v2c25
 AOI221XL typical_1v2c25
 BUFX8 typical_1v2c25
 CLKAND2X12 typical_1v2c25
 CLKBUFX2 typical_1v2c25
 CLKBUFX3 typical_1v2c25
 ……

引用(reference) Report

  • 引用报告显示设计中引用的静态结果

****************************************
Report : reference
Design : ALU_CLA32
Version: R-2020.09-SP5
Date : Tue Dec 14 15:26:07 2021
****************************************
Attributes:
 b - black box (unknown)
 bo - allows boundary optimization
 d - dont_touch
 mo - map_only
 h - hierarchical
 n - noncombinational
 r - removable
 s - synthetic operator
 u - contains unmapped logic
Reference Library Unit Area Count Total Area Attributes
-----------------------------------------------------------------------------
ALU_CLA4_G_0 799.475409 1 799.475409 h
ALU_CLA4_G_1 420.955193 1 420.955193 h
ALU_CLA4_G_2 436.231796 1 436.231796 h
……
BUFX2 typical_1v2c25 6.789600 1 6.789600 
BUFX4 typical_1v2c25 8.487000 1 8.487000

约束(constraint) Report

  • 约束报告显示编译过的设计是否满足约束
  • dc_shell> report_constraint –all_violators

Startpoint: opB[0] (input port)
 Endpoint: DO[31] (output port)
 Path Group: default
 Path Type: max
 Des/Clust/Port Wire Load Model Library
 ------------------------------------------------
 ALU_CLA32 smic13_wl20 typical_1v2c25
 Point Incr Path
 -----------------------------------------------------------------------------------
 input external delay 0.00 0.00 r
 opB[0] (in) 0.24 0.24 r
 U30/Y (BUFX6) 0.14 0.38 r
 u[0].uALU_CLA4_G/U19/Y (INVX16) 0.05 0.43 f
 u[0].uALU_CLA4_G/U2/Y (CLKAND2X12) 0.09 0.52 f
 ……
 u[7].uALU_CLA4_G/U2/Y (XOR2X4) 0.12 5.28 r
 DO[31] (out) 0.00 5.28 r
 data arrival time 5.28
 max_delay 5.30 5.30
 output external delay 0.00 5.30
 data required time 5.30
 -----------------------------------------------------------------------------------
 data required time 5.30
 data arrival time -5.28
 -----------------------------------------------------------------------------------
 slack (MET) 0.02

时序(timing) Report

  • 用户应知道report_timing产生的路径信息

Operating Conditions: typical_1v2c25 Library: typical_1v2c25
Wire Load Model Mode: enclosed
 Startpoint: Cin (input port)
 Endpoint: DO[31] (output port)
 Path Group: default
 Path Type: max
 Des/Clust/Port Wire Load Model Library
 ------------------------------------------------
 ALU_CLA32 smic13_wl20 typical_1v2c25
 Point Incr Path
 --------------------------------------------------------------------------
 input external delay 0.00 0.00 r
 Cin (in) 0.22 0.22 r
 U38/Y (BUFX4) 0.18 0.39 r
 u[0].uALU_CLA4_G/ci (ALU_CLA4_G_0) 0.00 0.39 r
 u[0].uALU_CLA4_G/U41/Y (NAND2X8) 0.06 0.46 f
……
 u[7].uALU_CLA4_G/Do[3] (ALU_CLA4_G_1) 0.00 4.85 r
 DO[31] (out) 0.00 4.85 r
 data arrival time 4.85
 max_delay 5.30 5.30
 output external delay 0.00 5.30
 data required time 5.30
 --------------------------------------------------------------------------
 data required time 5.30
 data arrival time -4.85
 --------------------------------------------------------------------------
 slack (MET) 0.45
  • 路径延时、路径要求时间和总结部分

时序(timing) 报告

  • 时序报告显示设计的最大或最小路径延时,缺省时显示一个最大延时路径。

What is slack?

  • slack是要求时间与实际到达时间的差值结果。
  • slack为正或0表示约束满足
  • slack为负表示约束没有得到满足。

Report Power

  • dc_shell> report_power
Global Operating Voltage = 2.5
Power-specific unit information :
    Voltage Units = 1V
    Capacitance Units = 1.000000pf
    Time Units = 1ns

    Dynamic Power Units = 1mW (derived from V,C,T units)
    Leakage Power Units = 1nW
    Cell Internal Power = 13.4819 mW (58%)
    Net Switching Power = 9.7291 mW (42%)
    Total Dynamic Power = 23.2109 mW (100%)
    Cell Leakage Power = 123.8077 nW 
Operating Conditions: typical_1v2c25 Library: typical_1v2c25
Wire Load Model Mode: enclosed
Design Wire Load Model Library
ALU_CLA32 smic13_wl20 typical_1v2c25
------------------------------------------------
Global Operating Voltage = 1.2 
Power-specific unit information :
 Voltage Units = 1V
 Capacitance Units = 1.000000pf
 Time Units = 1ns
 Dynamic Power Units = 1mW (derived from V,C,T units)
 Leakage Power Units = 1pW
 Cell Internal Power = 734.8602 uW (23%)
 Net Switching Power = 2.4284 mW (77%)
 ---------
Total Dynamic Power = 3.1633 mW (100%)
Cell Leakage Power = 222.9129 nW

交互式显示

  • 从报告中选择一条信息,相应的项会在逻辑图中高亮度显示(按show按钮)

  • 高亮度显示可以穿过层次

Highlight

  • 显示最长和最短路径的另一种方法是:Analysis/Highlight

Point Timing Report

  • Point timing report 显示逻辑图中两个所选的点之间的时序信息。

  • Analysis/report ®point timing

用逻辑图分析电路

  • 确定逻辑图中所选的引脚上的信号的到达时间

  • 确定在逻辑图中选择的网线的负载

Analysis/Show Net Load

保存设计

  • 在退出Design Analyzer之前,将设计保存到文件
  • File / Save 以DB格式保存设计
  • File / Save as可以其它的输出格式保存设计

–Synopsys formats

​ » equation: .eq

​ » state table: .st

–Verilog: .v

–VHDL: .vhd

–PLA( Berkeley Espresso): .pla

–EDIF

  • File / Save as

  • 将文件保存为Verilog格式,进行门级仿真,并用Verilog in接口将其转换为OPU数据库用来布局布线

  • 如果不能 Verilog in,请检查assign问题

  • 如果存在任何assign问题,选择这个块并用下面的dc_shell命令修复:

–set_fix_mutiple_port_nets –all –buffer_constants

–compile –map_effort medium

Fix multiple Port Net

  • 清除verilog assignment问题

–set_fix_mutiple_port_nets –all –buffer_constants

–compile –map_effort medium

门级仿真 (verilog)

  • 输出门级网表(有两种方法)

1.File / Save As ® Verilog ( for File format)

2.dc_shell> write –format verilog –hierarchy –output chip.vg

  • 产生SDF(有两种方法)

1.File /Save info ® Design timing

2.dc_shell> write_sdf –version 1.0 –context verilogchip.sdf

  • 修改testfixture文件

​ $sdf_annotate(“the_SDF_file_name”,

​ the_top_level_module_instance_name);

​ For example: $sdf_annotate(“chip.sdf”, top);

  • 用Verilog-XL进行仿真

©北京大学 JackHCC


文章作者: 杰克成
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 杰克成 !
评论
  目录