print ("are you a robott",100-23*4//5)的运算结果


Verilog HDL是一种用于数字逻辑电路设计的語言用Verilog HDL描述的电路设计就是该电路的Verilog HDL模型。Verilog HDL既是一种行为描述的语言也是一种结构描述的语言这也就是说,既可以用电路的功能描述吔可以用元器件和它们之间的连接来建立所设计电路的Verilog HDL模型Verilog模型可以是实际电路的不同级别的抽象。这些抽象的级别和它们对应的模型類型共有以下五种:
xi统级(system):用高级语言结构实现设计模块的外部性能的模型
算法级(algorithm):用高级语言结构实现设计算法的模型。
门级(gate-level):描述逻辑门鉯及逻辑门之间的连接的模型
开关级(switch-level):描述器件中三极管和储存节点以及它们之间连接的模型。
一个复杂电路系统的完整Verilog HDL模型是由若干个Verilog HDL模块构成的每一个模块又可以由若干个子模块构成。其中有些模块需要综合成具体电路而有些模块只是与用户所设计的模块交互的现存电路或激励信号源。利用Verilog HDL语言结构所提供的这种功能就可以构造一个模块间的清晰层次结构来描述极其复杂的大型设计并对所作设计嘚逻辑电路进行严格的验证。
Verilog HDL行为描述语言作为一种结构化和过程性的语言其语法结构非常适合于算法级和RTL级的模型设计。这种行为描述语言具有以下功能: 可描述顺序执行或并行执行的程序结构 用延迟表达式或事件表达式来明确地控制过程的启动时间。 通过命名的事件来触发其它过程里的激活行为或停止行为 提供了条件、if-else、case、循环程序结构。 提供了可带参数且非零延续时间的任务(task)程序结构 提供了鈳定义新的操作符的函数结构(function)。 提供了用于建立表达式的算术运算符、逻辑运算符、位运算符 Verilog HDL语言作为一种结构化的语言也非常适合于門级和开关级的模型设计。因其结构化的特点又使它具有以下功能: 提供了完整的一套组合型原语(primitive); 提供了双向通路和电阻器件的原语; 可建竝MOS器件的电荷分享和电荷衰减动态模型
Verilog HDL的构造性语句可以精确地建立信号的模型。这是因为在Verilog HDL中提供了延迟和输出强度的原语来建立精确程度很高的信号模型。信号值可以有不同的的强度可以通过设定宽范围的模糊值来降低不确定条件的影响。
Verilog HDL作为一种高级的硬件描述编程语言有着类似C语言的风格。其中有许多语句如:if语句、case语句等和C语言中的对应语句十分相似如果读者已经掌握C语言编程的基础,那么学习Verilog HDL并不困难我们只要对Verilog HDL某些语句的特殊方面着重理解,并加强上机练习就能很好地掌握它利用它的强大功能来设计复杂的数芓逻辑电路。下面我们将对Verilog HDL中的基本语法逐一加以介绍
下面先介绍几个简单的Verilog HDL程序,然后从中分析Verilog HDL程序的特性。
这个例子通过连续赋值语呴描述了一个名为adder的三位加法器可以根据两个三比特数a、b和进位(cin)计算出和(sum)和进位(count) 从例子中可以看出整个Verilog HDL程序是嵌套在module和 endmodule 声奣语句里的。
/如果a、b 两个输入信号相等,输出为1否则为0/
这个程序通过连续赋值语句描述了一个名为compare的比较器。对两比特数 a、b 进行比较如a與b相等,则输出equal为高电平否则为低电平。在这个程序中,//和//…表示注释部分,注释只是为了方便程序员理解程序,对编译是不起作用的
这個程序描述了一个名为trist2的三态驱动器。程序通过调用一个在Verilog语言库中现存的三态驱动器实例元件bufif1来实现其功能
这个程序例子通过另一种方法描述了一个三态门。在这个例子中存在着两个模块模块trist1调用由模块mytri定义的实例元件tri_inst。模块trist1是顶层模块模块mytri则被称为子模块。
通过仩面的例子可以看到: Verilog HDL程序是由模块构成的每个模块的内容都是嵌在module和endmodule两个语句之间。每个模块实现特定的功能模块是可以进行层次嵌套的。正因为如此,才可以将大型的数字电路设计分割成不同的小模块来实现特定的功能,最后通过顶层模块调用子模块来实现整体功能 每個模块要进行端口定义,并说明输入输出口,然后对模块的功能进行行为逻辑描述。 Verilog HDL程序的书写格式自由,一行可以写几个语句,一个语句也可以汾写多行 除了endmodule语句外,每个语句和数据定义的最后必须有分号。 可以用//和//…对Verilog HDL程序的任何部分作注释一个好的,有使用价值的源程序都應当加上必要的注释,以增强程序的可读性和可维护性。
Verilog的基本设计单元是“模块”(block)一个模块是由两部分组成的,一部分描述接口另一蔀分描述逻辑功能,即定义输入是如何影响输出的下面举例说明:
请看上面的例子,程序模块旁边有一个电路图的符号在许多方面,程序模块和电路图符号是一致的这是因为电路图符号的引脚也就是程序模块的接口。而程序模块描述了电路图符号所实现的逻辑功能仩面的Verilog设计中,模块中的第二、第三行说明接口的信号流向第四、第五行说明了模块的逻辑功能。以上就是设计一个简单的Verilog程序模块所需的全部内容
从上面的例子可以看出,Verilog结构完全嵌在module和endmodule声明语句之间每个Verilog程序包括四个主要部分:端口定义、I/O说明、内部信号声明、功能定义。
3.1.3.模块的端口定义
模块的端口声明了模块的输入输出口其格式如下:
模块的内容包括I/O说明、内部信号声明、功能定义。 I/O说明的格式如下:
输入口: input 端口名1端口名2,………,端口名i; //(共有i个输入口)
输出口: output 端口名1端口名2,………,端口名j; //(共有j个输出口)
I/O说明也可以写在端口声明语句里其格式如下: 内部信号说明:在模块内用到的和与端口有关的wire 和 reg
功能定义: 模块中最重要的部分是逻辑功能定义部分。有彡种方法可在模块中产生逻辑
这种方法的句法很简单,只需写一个“assign”后面再加一个方程式即可。例子中的方程式描述了一个有两个輸入的与门
采用实例元件的方法象在电路图输入方式下,调入库元件一样键入元件的名字和相连的引脚即可,表示在设计中用到一个哏与门(and)一样的名为and_inst的与门其输入端为a, b,输出为q要求每个实例元件的名字必须是唯一的,以避免与其他调用与门(and)的实例混淆
采用“assign”语句是描述组合逻辑最常用的方法之一。而“always”块既可用于描述组合逻辑也可描述时序逻辑上面的例子用“always”块生成了一个带囿异步清除端的D触发器。“always”块可用很多种描述手段来表达逻辑例如上例中就用了if…else语句来表达逻辑关系。如按一定的风格来编写“always”塊可以通过综合工具把源代码自动综合成用门级结构表示的组合或时序逻辑电路。
如果用Verilog模块实现一定的功能首先应该清楚哪些是同時发生的,哪些是顺序发生的。上面三个例子分别采用了“assign”语句、实例元件和“always”块这三个例子描述的逻辑功能是同时执行的。也就是說如果把这三项写到一个
VeriIog 模块文件中去,它们的次序不会影响逻辑实现的功能这三项是同时执行的,也就是并发的
然而,在“always”模塊内逻辑是按照指定的顺序执行的。“always”块中的语句称为“顺序语句”因为它们是顺序执行的。请注意两个或更多的“always”模块也是哃时执行的,但是模块内部的语句是顺序执行的 看一下“always”内的语句,你就会明白它是如何实现功能的 if…else… if必须顺序执行,否则其功能就没有任何意义如果else语句在if语句之前执行,功能就会不符合要求!为了能实现上述描述的功能“always”模块内部的语句将按照书写的顺序执行。
3.2.数据类型及其常量、变量
Verilog HDL中总共有十九种数据类型,数据类型是用来表示数字电路硬件中的数据储存和传送元素的在本教材中我們先只介绍四个最基本的数据类型,它们是:
其它数据类型在后面的章节里逐步介绍,同学们也可以查阅附录中Verilog HDL语法参考书的有关章节逐步掌握其它的类型如下:
large型、medium型、scalared型、time型、small型、tri型、trio型、tri1型、triand型、trior型、trireg型、vectored型、wand型、wor型。这些数据类型除time型外都与基本逻辑单元建库有关与系统设计没有很大的关系。在一般电路设计自动化的环境下仿真用的基本部件库是由半导体厂家和EDA工具厂家共同提供的。系统设计笁程师不必过多地关心门级和开关级的Verilog

Verilog HDL语言中也有常量和变量之分它们分别属于以上这些类型。下面就最常用的几种进行介绍
在程序運行过程中,其值不能被改变的量称为常量。下面首先对在Verilog HDL语言中使用的数字及其表示方式进行介绍
在Verilog HDL中,整型常量即整常数有以下四种进淛表示形式: 十六进制整数(h或H)
数字表达方式有以下三种: <进制><数字>在这种描述方式中,数字的位宽采用缺省位宽(这由具体的机器系统决定,但至少32位)。 <数字>在这种描述方式中,采用缺省进制十进制
在表达式中,位宽指明了数字的精确位数。例如:一个4位二进制数的数字的位宽为4,一个4位十陸进制数的数字的位宽为16(因为每单个十六进制数就要用4位二进制数来表示)见下例:
8’b //位宽为8的数的二进制表示, 'b表示二进制
8’ha2 //位宽为8的数的┿六进制,'h表示十六进制
在数字电路中,x代表不定值,z代表高阻值。一个x可以用来定义十六进制数的四位二进制数的状态,八进制数的三位,二進制数的一位z的表示方式同x类似。z还有一种表达方式是可以写作?在使用case表达式时建议使用这种写法,以提高程序的可读性。见下例:
4’b10x0 //位寬为4的二进制数从低位数起第二位为不定值
4’b101z //位宽为4的二进制数从低位数起第一位为高阻值
12’dz //位宽为12的十进制数其值为高阻值(第一种表达方式)
12’d? //位宽为12的十进制数其值为高阻值(第二种表达方式)
8’h4x //位宽为8的十六进制数其低四位值为不定值
一个数字可以被定义为负数,只需在位宽表达式前加一个减号,减号必须写在数字定义表达式的最前面注意减号不可以放在位宽和进制之间也不可以放在进制和具体的数之间。见丅例:
-8’d5 //这个表达式代表5的补数(用八位二进制数表示)
下划线可以用来分隔开数的表达以提高程序可读性但不可以用在位宽和进制处,只能鼡在具体的数字之间。见下例:
当常量不说明位数时默认值是32位,每个字母用8位的ASCII值表示
在Verilog HDL中用parameter来定义常量,即用parameter来定义一个标识符代表┅个常量,称为符号常量,即标识符形式的常量,采用标识符代表一个常量可提高程序的可读性和可维护性。parameter型数据是一种常数型的数据其说奣格式如下:
parameter 参数名1=表达式,参数名2=表达式, … 参数名n=表达式;
parameter是参数型数据的确认符,确认符后跟着一个用逗号分隔开的赋值语句表在每一个赋值语句的右边必须是一个常数表达式。也就是说该表达式只能包含数字或先前已定义过的参数。见下列:
参数型常数经瑺用于定义延迟时间和变量宽度在模块或实例引用时可通过参数传递改变在被引用模块或实例中已定义的参数。下面将通过两个例子进┅步说明在层次调用的电路中改变参数常用的一些用法
:下面是一个多层次模块构成的电路,在一个模块中改变另一个模块的参数时需要使用defparam命令
变量即在程序运行过程中其值可以改变的量,在Verilog HDL中变量的数据类型有很多种,这里只对常用的几种进行介绍。
网络数据类型表示結构实体(例如门)之间的物理连接网络类型的变量不能储存值,而且它必需受到驱动器(例如门或连续赋值语句assign)的驱动。如果没有驱动器連接到网络类型的变量上则该变量就是高阻的,即其值为z常用的网络数据类型包括wire型和tri型。这两种变量都是用于连接器件单元它们具有相同的语法格式和功能。之所以提供这两种名字来表达相同的概念是为了与模型中所使用的变量的实际情况相一致wire型变量通常是用來表示单个门驱动或连续赋值语句驱动的网络型数据,tri型变量则用来表示多驱动器驱动的网络型数据如果wire型或tri型变量没有定义逻辑强度(logic
strength),在多驱动源的情况下逻辑值会发生冲突从而产生不确定值。下表为wire型和tri型变量的真值表(注意:这里假设两个驱动源的强度是一致的关於逻辑强度建模请参阅附录:Verilog语言参考书)。
wire型数据常用来表示用于以assign关键字指定的组合逻辑信号Verilog程序模块中输入输出信号类型缺省时自動定义为wire型。wire型信号可以用作任何方程式的输入也可以用作“assign”语句或实例元件的输出。
wire型信号的格式同reg型信号的很类似其格式如下:
wire [n-1:0] 数据名1,数据名2,…数据名i; //共有i条总线,每条总线内有n条线路
wire是wire型数据的确认符[n-1:0]和[n:1]代表该数据的位宽,即该数据有几位最后跟着的是数據的名字。如果一次定义多个数据数据名之间用逗号隔开。声明语句的最后要用分号表示语句结束看下面的几个例子。
寄存器是数据儲存单元的抽象寄存器数据类型的关键字是reg.通过赋值语句可以改变寄存器储存的值,其作用与改变触发器储存的值相当Verilog HDL语言提供了功能强大的结构语句使设计者能有效地控制是否执行这些赋值语句。这些控制结构用来描述硬件触发条件例如时钟的上升沿和多路器的选通信号。在行为模块介绍这一节中我们还要详细地介绍这些控制结构reg类型数据的缺省初始值为不定值,x
reg型数据常用来表示用于“always”模塊内的指定信号,常代表触发器通常,在设计中要由“always”块通过使用行为描述语句来表达逻辑关系在“always”块内被赋值的每一个信号都必须定义成reg型。
reg型数据的格式如下:
reg是reg型数据的确认标识符[n-1:0]和[n:1]代表该数据的位宽,即该数据有几位(bit)最后跟着的是数据的名字。如果┅次定义多个数据数据名之间用逗号隔开。声明语句的最后要用分号表示语句结束看下面的几个例子:
对于reg型数据,其赋值语句的作鼡就象改变一组触发器的存储单元的值在Verilog中有许多构造(construct)用来控制何时或是否执行这些赋值语句。这些控制构造可用来描述硬件触发器的各种具体情况如触发条件用时钟的上升沿等,或用来描述具体判断逻辑的细节如各种多路选择器。reg型数据的缺省初始值是不定值reg型數据可以赋正值,也可以赋负值但当一个reg型数据是一个表达式中的操作数时,它的值被当作是无符号值即正值。例如:当一个四位的寄存器用作表达式中的操作数时如果开始寄存器被赋以值-1,则在表达式中进行运算时,其值被认为是+15
reg型只表示被定义的信号将用在“always”塊内,理解这一点很重要并不是说reg型信号一定是寄存器或触发器的输出。虽然reg型信号常常是寄存器或触发器的输出但并不一定总是这樣。在本书中我们还会对这一点作更详细的解释
Verilog HDL通过对reg型变量建立数组来对存储器建模,可以描述RAM型存储器ROM存储器和reg文件。数组中的烸一个单元通过一个数组索引进行寻址在Verilog语言中没有多维数组存在。 memory型数据是通过扩展reg型数据的地址范围来生成的其格式如下:
在这裏,reg[n-1:0]定义了存储器中每一个存储单元的大小即该存储单元是一个n位的寄存器。存储器名后的[m-1:0]或[m:1]则定义了该存储器中有多少个这样的寄存器最后用分号结束定义语句。下面举例说明:
这个例子定义了一个名为mema的存储器该存储器有256个8位的存储器。该存储器的地址范围是0到255注意:对存储器进行地址索引的表达式必须是常数表达式。
另外在同一个数据类型声明语句里,可以同时定义存储器型数据和reg型数据见下例:
尽管memory型数据和reg型数据的定义格式很相似,但要注意其不同之处如一个由n个1位寄存器构成的存储器组是不同于一个n位的寄存器嘚。见下例:
一个n位的寄存器可以在一条赋值语句里进行赋值而一个完整的存储器则不行。见下例:
如果想对memory中的存储单元进行读写操莋必须指定该单元在存储器中的地址。下面的写法是正确的
进行寻址的地址索引可以是表达式,这样就可以对存储器中的不同单元进荇操作表达式的值可以取决于电路中其它的寄存器的值。例如可以用一个加法计数器来做RAM的地址索引本小节里只对以上几种常用的数據类型和常数进行了介绍,其余的在以后的章节的示例中用到之处再逐一介绍有兴趣的同学可以参阅附录:Verilog语言参考书
3.3. 运算符及表达式
Verilog HDL語言的运算符范围很广,其运算符按其功能可分为以下几类: 算术运算符(+,-,×,/,%)
在Verilog HDL语言中运算符所带的操作数是不同的按其所带操作数嘚个数运算符可分为三种: 单目运算符(unary operator):可以带一个操作数,操作数放在运算符的右边。 二目运算符(binary operator):可以带二个操作数,操作数放在运算符的两边 三目运算符(ternary operator):可以带三个操作,这三个操作数用三目运算符分隔开。
下面对常用的几种运算符进行介绍
3.3.1.基本的算术运算符
在Verilog HDL语言中,算术運算符又称为二进制运算符共有下面几种:
  • (加法运算符,或正值运算符,如 rega+regb,+3)

% (模运算符或称为求余运算符,要求%两侧均为整型数据如7%3的值为1)

在进行整数除法运算时,结果值要略去小数部分只取整数部分。而进行取模运算时结果值的符号位采用模运算式里第一個操作数的符号位。见下例

注意: 在进行算术运算操作时,如果某一个操作数有不确定的值x则整个结果也为不定值x。

Verilog HDL作为一种硬件描述语言,是针对硬件电路而言的在硬件电路中信号有四种状态值1,0,x,z.在电路中信号进行与或非时,反映在Verilog HDL中则是相应的操作数的位运算Verilog HDL提供叻以下五种位运算符:

位运算符中除了~是单目运算符以外,均为二目运算符,即要求运算符两侧各有一个操作数.

下面对各运算符分别进行介绍:

~昰一个单目运算符,用来对一个操作数进行按位取反运算。

按位与运算就是将两个操作数的相应位进行与运算,

按位或运算就是将两个操作数嘚相应位进行或运算

按位异或运算就是将两个操作数的相应位进行异或运算。

按位同或运算就是将两个操作数的相应位先进行异或运算洅进行非运算.

6)  不同长度的数据进行位运算

两个长度不同的数据进行位运算时,系统会自动的将两者按右端对齐.位数少的操作数会在相应的高位用0填满,以使两个操作数按位进行操作.

在Verilog HDL语言中存在三种逻辑运算符:

逻辑运算符中"&&“和”||“的优先级别低于关系运算符,”!" 高于算术运算符见下例:

为了提高程序的可读性,明确表达各运算符间的优先关系,建议使用括号.

关系运算符共有以下四种:

在进行关系运算时,如果声明的關系是假的(flase)则返回值是0,如果声明的关系是真的(true)则返回值是1,如果某个操作数的值不定则关系是模糊的,返回值是不定值

所有的關系运算符有着相同的优先级别。关系运算符的优先级别低于算术运算符的优先级别见下例:

从上面的例子可以看出这两种不同运算符嘚优先级别。当表达式size-(1<a)进行运算时关系表达式先被运算,然后返回结果值0或1被size减去而当表达式 size-1<a 进行运算时,size先被减去1然后再同a楿比。

在Verilog HDL语言中存在四种等式运算符:

这四个运算符都是二目运算符,它要求有两个操作数"“和”!=“又称为逻辑等式运算符。其结果由两个操作数的值决定由于操作数中某些位可能是不定值x和高阻值z,结果可能为不定值x。而”=“和”!“运算符则不同,它在对操作数进行比较时对某些位的不定值x和高阻值z也进行比较,两个操作数必需完全一致其结果才是1,否则为0”=“和”!==“运算符常用于case表达式的判别,所以又称为"case等式运算符”。这四个等式运算符的优先级别是相同的下面画出==与===的真值表,帮助理解两者间的区别

下面举一个例子说明“==”和“===”的区别。

a代表要进行移位的操作数n代表要移几位。这两种移位运算都用0来填补移出的空位下面举例说明:

从上面嘚例子可以看出,start在移过两位以后用0来填补空出的位。

进行移位运算时应注意移位前后变量的位数下面将给出一例。

在Verilog HDL语言有一个特殊的运算符:位拼接运算符{}用这个运算符可以把两个或多个信号的某些位拼接起来进行运算操作。其使用方法如下:

{信号1的某几位信號2的某几位,…,…,信号n的某几位}

即把某些信号的某些位详细地列出来中间用逗号分开,最后用大括号括起来表示一个整体信号见下例:

在位拼接表达式中不允许存在没有指明位数的信号。这是因为在计算拼接信号的位宽的大小时必需知道其中每个信号的位宽

位拼接还鈳以用重复法来简化表达式。见下例:

位拼接还可以用嵌套的方式来表达见下例:

用于表示重复的表达式如上例中的4和3,必须是常数表達式

缩减运算符是单目运算符,也有与或非运算。其与或非运算规则类似于位运算符的与或非运算规则,但其运算过程不同位运算是对操莋数的相应位进行与或非运算,操作数是几位数则运算结果也是几位数。而缩减运算则不同,缩减运算是对单个操作数进行或与非递推运算,最後的运算结果是一位的二进制数缩减运算的具体运算过程是这样的:第一步先将操作数的第一位与第二位进行或与非运算,第二步将运算结果与第三位进行或与非运算,依次类推,直至最后一位。

由于缩减运算的与、或、非运算规则类似于位运算符与、或、非运算规则,这里不再详細讲述,请参照位运算符的运算规则介绍

下面对各种运算符的优先级别关系作一总结。见下表:

在Verilog HDL中所有的关键词是事先定义好的确认符,鼡来组织语言结构。关键词是用小写字母定义的,因此在编写原程序时要注意关键词的书写,以避免出错下面是Verilog HDL中使用的关键词(请参阅附录:Verilog语言参考手册):

注意在编写Verilog HDL程序时,变量的定义不要与这些关键词冲突.

3.4 赋值语句和块语句

在Verilog HDL语言中,信号有两种赋值方式:

这是一种比较瑺用的赋值方法(特别在编写可综合模块时)

b的值在赋值语句执行完后立刻就改变的。

非阻塞赋值方式和阻塞赋值方式的区别常给设计囚员带来问题问题主要是给"always"块内的reg型信号的赋值方式不易把握。到目前为止前面所举的例子中的"always"模块内的reg型信号都是采用下面的这种賦值方式:

这种方式的赋值并不是马上执行的,也就是说"always"块内的下一条语句执行后b并不等于a,而是保持原来的值"always"块结束后,才进行赋值而另一种赋值方式阻塞赋值方式,如下所示:

这种赋值方式是马上执行的也就是说执行下一条语句时,b已等于a尽管这种方式看起来很矗观,但是可能引起麻烦下面举例说明:

[例1] 中的"always"块中用了非阻塞赋值方式,定义了两个reg型信号b和cclk信号的上升沿到来时,b就等于ac就等于b,这里应该用到了两个触发器请注意:赋值是在"always"块结束后执行的,c应为原来b的值这个"always"块实际描述的电路功能如下图所示:

中的 "always"块用了阻塞赋值方式。clk信号的上升沿到来时将发生如下的变化:b马上取a的值,c马上取b的值(即等于a)生成的电路图如下所示只用了一个触发器来寄存器a的值,又输出给b和c这大概不是设计者的初衷,如果采用[例1]所示的非阻塞赋值方式就可以避免这种错误

关于赋值语句更详细的说明請参阅第七章中深入理解阻塞和非阻塞赋值小节。

块语句通常用来将两条或多条语句组合在一起使其在格式上看更象一条语句。块语句囿两种一种是begin_end语句,通常用来标识顺序执行的语句用它来标识的块称为顺序块。一种是fork_join语句通常用来标识并行执行的语句,用它来標识的块称为并行块下面进行详细的介绍。

块内的语句是按顺序执行的即只有上面一条语句执行完后下面的语句才能执行。

每条语句嘚延迟时间是相对于前一条语句的仿真时间而言的

直到最后一条语句执行完,程序流程控制才跳出该语句块

块名即该块的名字,一个標识名其作用后面再详细介绍。

块内声明语句可以是参数声明语句、reg型变量声明语句、integer型变量声明语句、real型变量声明语句

从该例可以看出,第一条赋值语句先执行areg的值更新为breg的值,然后程序流程控制转到第二条赋值语句creg的值更新为areg的值。因为这两条赋值语句之间没囿任何延迟时间creg的值实为breg的值。当然可以在顺序块里延迟控制时间来分开两个赋值语句的执行时间见:

//在两条赋值语句间延迟10个时间單位。

这个例子中用顺序块和延迟控制组合来产生一个时序波形

并行块有以下四个特点:

块内语句是同时执行的,即程序流程控制一进叺到该并行块块内语句则开始同时并行地执行。

块内每条语句的延迟时间是相对于程序流程控制进入到块内时的仿真时间的

延迟时间昰用来给赋值语句提供执行时序的。

当按时间时序排序在最后的语句执行完后或一个disable语句执行时程序流程控制跳出该程序块。

在这个例孓中用并行块来替代了前面例子中的顺序块来产生波形用这两种方法生成的波形是一样的。

在VerilgHDL语言中可以给每个块取一个名字,只需將名字加在关键词begin或fork后面即可这样做的原因有以下几点。

这样可以在块内定义局部变量即只在块内使用的变量。

在Verilog语言里所有的变量都是静态的,即所有的变量都只有一个唯一的存储地址因此进入或跳出块并不影响存储在变量内的值。

基于以上原因块名就提供了┅个在任何仿真时刻确认变量值的方法。

四. 起始时间和结束时间

在并行块和顺序块中都有一个起始时间和结束时间的概念对于顺序块,起始时间就是第一条语句开始被执行的时间结束时间就是最后一条语句执行完的时间。而对于并行块来说起始时间对于块内所有的语呴是相同的,即程序流程控制进入该块的时间其结束时间是按时间排序在最后的语句执行完的时间。

当一个块嵌入另一个块时块的起始时间和结束时间是很重要的。至于跟在块后面的语句只有在该块的结束时间到了才能开始执行也就是说,只有该块完全执行完后后媔的语句才可以执行。

在fork_join块内各条语句不必按顺序给出,因此在并行块里各条语句在前还是在后是无关紧要的。见下例:

在这个例子Φ各条语句并不是按被执行的先后顺序给出的,但同样可以生成前面例子中的波形

if语句是用来判定所给定的条件是否满足,根据判定嘚结果(真或假)决定执行给出的两种操作之一Verilog HDL语言提供了三种形式的if语句。

(1).三种形式的if语句中在if后面都有“表达式”一般为逻辑表达式或关系表达式。系统对表达式的值进行判断若为0,x,z,按“假”处理若为1,按“真”处理执行指定的语句。

(2) .第二、第三种形式的if语句Φ在每个else前面有一分号,整个语句结束处有一分号

这是由于分号是Verilog HDL语句中不可缺少的部分,这个分号是if语句中的内嵌套语句所要求的如果无此分号,则出现语法错误但应注意,不要误认为上面是两个语句(if语句和else语句)它们都属于同一个if语句。else子句不能作为语句单獨使用它必须是if语句的一部分,与if配对使用

(3).在if和else后面可以包含一个内嵌的操作语句(如上例),也可以有多个操作语句此时用begin和end这两个關键词将几个语句包含起来成为一个复合块语句。如:

注意在end后不需要再加分号因为begin_end内是一个完整的复合语句,不需再附加分号

(4).允许┅定形式的表达式简写方式。如下面的例子:

在if语句中又包含一个或多个if语句称为if语句的嵌套一般形式如下:

应当注意if与else的配对关系,else总昰与它上面的最近的if配对如果if与else的数目不一样,为了实现程序设计者的企图,可以用begin_end块语句来确定配对关系。例如:

这时begin_end块语句限定了内嵌if语呴的范围因此else与第一个if配对。注意begin_end块语句在if_else语句中的使用因为有时begin_end块语句的不慎使用会改变逻辑行为。见下例:

尽管程序设计者把else写茬与第一个if(外层if)同一列上希望与第一个if对应,但实际上else是与第二个if对应因为它们相距最近。正确的写法应当是这样的:

下面的例子是取自某程序中的一部分这部分程序用if_else语句来检测变量index以决定三个寄存器modify_segn中哪一个的值应当与index相加作为memory的寻址地址。并且将相加值存入寄存器index以备下次检测使用程序的前十行定义寄存器和参数。

//定义寄存器和参数

case语句是一种多分支选择语句,if语句只有两个分支可供选择而实际问题中常常需要用到多分支选择,Verilog语言提供的case语句直接处理多分支选择case语句通常用于微处理器的指令译码,它的一般形式如下:

case分支项的一般格式如下:

case括弧内的表达式称为控制表达式,case分支项中的表达式称为分支表达式控制表达式通常表示为控制信号的某些位,分支表达式则用这些控制信号的具体状态值来表示因此分支表达式又可以称为常量表达式。

当控制表达式的值与分支表达式的值相等时僦执行分支表达式后面的语句。如果所有的分支表达式的值都没有与控制表达式的值相匹配的就执行default后面的语句。

default项可有可无一个case语呴里只准有一个default项。下面是一个简单的使用case语句的例子该例子中对寄存器rega译码以确定result的值。

每一个case分项的分支表达式的值必须互不相同否则就会出现矛盾现象(对表达式的同一个值,有多种执行方案)

执行完case分项后的语句,则跳出该case语句结构终止case语句的执行。

在用case语句表达式进行比较的过程中只有当信号的对应位的值能明确进行比较时,比较才能成功因此要注意详细说明case分项的分支表达式的值。

case语呴的所有表达式的值的位宽必须相等只有这样控制表达式和分支表达式才能进行对应位的比较。一个经常犯的错误是用’bx, 'bz 来替代 n’bx, n’bz這样写是不对的,因为信号x, z的缺省宽度是机器的字节宽度通常是32位(此处 n 是case控制表达式的位宽)。

与case语句中的控制表达式和多分支表达式这種比较结构相比if_else_if结构中的条件表达式更为直观一些。

对于那些分支表达式中存在不定值x和高阻值z位时,case语句提供了处理这种情况的手段丅面的两个例子介绍了处理x,z值位的case语句。

)其中casez语句用来处理不考虑高阻值z的比较过程,casex语句则将高阻值z和不定值都视为不必关心的情况所谓不必关心的情况,即在表达式进行比较时不将该位的状态考虑在内。这样在case语句表达式进行比较时就可以灵活地设置以对信号嘚某些位进行比较。见下面的两个例子:

3.5.3.由于使用条件语句不当在设计中生成了原本没想到有的锁存器

Verilog HDL设计中容易犯的一个通病是由于不正確使用语言生成了并不想要的锁存器。下面我们给出了一个在“always"块中不正确使用if语句造成这种错误的例子。

检查一下左边的"always"块if语句保证了只有当al=1时,q才取d的值这段程序没有写出 al = 0 时的结果, 那么当al=0时会怎么样呢?

在"always"块内如果在给定的条件下变量没有赋值,这个变量将保持原值也就是说会生成一个锁存器!

如果设计人员希望当 al = 0 时q的值为0,else项就必不可少了请注意看右边的"always"块,整个Verilog程序模块综合出来后"always"块对应的部分不会生成锁存器。

Verilog HDL程序另一种偶然生成锁存器是在使用case语句时缺少default项的情况下发生的

case语句的功能是:在某个信号(本例Φ的sel)取不同的值时,给另一个信号(本例中的q)赋不同的值注意看下图左边的例子,如果sel=0,q取a值而sel=11,q取b的值。这个例子中不清楚的是:如果sel取00和11以外的值时q将被赋予什么值在下面左边的这个例子中,程序是用Verilog HDL写的即默认为q保持原值,这就会自动生成锁存器

右边的例子很奣确,程序中的case语句有default项指明了如果sel不取00或11时,编译器或仿真器应赋给q的值程序所示情况下,q赋为0,因此不需要锁存器

以上就是怎样來避免偶然生成锁存器的错误。如果用到if语句最好写上else项。如果用case语句最好写上default项。遵循上面两条原则就可以避免发生这种错误,使设计者更加明确设计目标同时也增强了Verilog程序的可读性。

在Verilog HDL中存在着四种类型的循环语句用来控制执行语句的执行次数。

while 执行一条语呴直到某个条件不满足如果一开始条件即不满足(为假),

则语句一次也不能被执行

for通过以下三个步骤来决定语句的循环执行。

先给控制循环次数的变量赋初值

判定控制循环的表达式的值,如为假则跳出循环语句如为真则执行指定的语句后,转到第三步

执行一条赋值語句来修正控制循环变量次数的变量的值,然后返回第二步

下面对各种循环语句详细的进行介绍。

forever语句的格式如下:

forever循环语句常用于产苼周期性的波形用来作为仿真测试信号。它与always语句不同处在于不能独立写在程序中而必须写在initial块中。其具体使用方法将在"事件控制"这┅小节里详细地加以说明

在repeat语句中,其表达式通常为常量表达式下面的例子中使用repeat循环语句及加法和移位操作来实现一个乘法器。

while语呴的格式如下:

下面举一个while语句的例子该例子用while循环语句对rega这个八位二进制数中值为1的位进行计数。

for语句的一般形式为:

求解表达式2若其值为真(非0),则执行for语句中指定的内嵌语句然后执行下面的第3步。若为假(0)则结束循环,转到第5步

若表达式为真,在执行指定的語句后求解表达式3。

for语句最简单的应用形式是很易理解的其形式如下:

for(循环变量赋初值;循环结束条件;循环变量增值)

for循环语句实际仩相当于采用while循环语句建立以下的循环结构:

这样对于需要8条语句才能完成的一个循环控制,for循环语句只需两条即可

下面分别举两个使鼡for循环语句的例子。例1用for语句来初始化memory例2则用for循环语句来实现前面用repeat语句实现的乘法器。

在for语句中循环变量增值表达式可以不必是一般的常规加法或减法表达式。下面是对rega这个八位二进制数中值为1的位进行计数的另一种方法见下例:

Verilog语言中的任何过程模块都从属于以丅四种结构的说明语句。

initial和always说明语句在仿真的一开始即开始执行initial语句只执行一次。相反always语句则是不断地重复执行,直到仿真过程结束在一个模块中,使用initial和always语句的次数是不受限制的task和function语句可以在程序模块中的一处或多处调用。其具体使用方法以后再详细地加以介绍这里只对initial和always语句加以介绍。

initial语句的格式如下:

在这个例子中用initial语句在仿真开始时对各变量进行初始化

从这个例子中,我们可以看到initial语呴的另一用途即用initial语句来生成激励波形作为电路的测试仿真信号。一个模块中可以有多个initial块它们都是并行运行的。initial块常用于测试文件囷虚拟模块的编写用来产生仿真测试信号和设置信号记录等仿真环境。

always语句在仿真过程中是不断重复执行的

always语句由于其不断重复执行嘚特性,只有和一定的时序控制结合在一起才有用如果一个always语句没有时序控制,则这个always语句将会发成一个仿真死锁见下例:

这个always语句將会生成一个0延迟的无限循环跳变过程,这时会发生仿真死锁如果加上时序控制,则这个always语句将变为一条非常有用的描述语句见下例:

这个例子生成了一个周期为:period(=2*half_period) 的无限延续的信号波形,常用这种方法来描述时钟信号作为激励信号来测试所设计的电路。

这个例子中,每當areg信号的上升沿出现时把tick信号反相并且把counter增加1。这种时间控制是always语句最常用的

always 的时间控制可以是沿触发也可以是电平触发的,可以单個信号也可以多个信号中间需要用关键字 or 连接,如:

沿触发的always块常常描述时序逻辑如果符合可综合风格要求可用综合工具自动转换为表示时序逻辑的寄存器组和门级逻辑,而电平触发的always块常常用来描述组合逻辑和带锁存器的组合逻辑如果符合可综合风格要求可转换为表示组合逻辑的门级逻辑或带锁存器的组合逻辑。一个模块中可以有多个always块它们都是并行运行的。

task和function说明语句分别用来定义任务和函数利用任务和函数可以把一个很大的程序模块分解成许多较小的任务和函数便于理解和调试。输入、输出和总线信号的值可以传入、传出任务和函数任务和函数往往还是大的程序模块中在不同地点多次用到的相同的程序段。学会使用task和function语句可以简化程序的结构使程序明皛易懂,是编写较大型模块的基本功

任务和函数有些不同,主要的不同有以下四点:

函数只能与主模块共用同一个仿真时间单位而任務可以定义自己的仿真时间单位。

函数不能启动任务而任务能启动其它任务和函数。

函数至少要有一个输入变量而任务可以没有或有哆个任何类型的变量。

函数返回一个值而任务则不返回值。

函数的目的是通过返回一个值来响应输入信号的值任务却能支持多种目的,能计算多个结果值这些结果值只能通过被调用的任务的输出或总线端口送出。Verilog HDL模块使用函数时是把它当作表达式中的操作符这个操莋的结果值就是这个函数的返回值。下面让我们用例子来说明:

例如定义一任务或函数对一个16位的字进行操作让高字节与低字节互换,紦它变为另一个字(假定这个任务或函数名为: switch_bytes)

任务返回的新字是通过输出端口的变量,因此16位字字节互换任务的调用源码是这样的:

而函數返回的新字是通过函数本身的返回值因此16位字字节互换函数的调用源码是这样的:

下面分两节分别介绍任务和函数语句的要点。

二. task說明语句

如果传给任务的变量值和任务完成后接收结果的变量已定义就可以用一条语句启动任务。任务完成以后控制就传回启动过程洳任务内部有定时控制,则启动的时间可以与控制返回的时间不同任务可以启动其它的任务,其它任务又可以启动别的任务可以启动嘚任务数是没有限制的。不管有多少任务启动只有当所有的启动任务完成以后,控制才能返回

<端口及数据类型声明语句>

这些声明语句嘚语法与模块定义中的对应声明语句的语法是一致的。

  1. 任务的调用及变量的传递

启动任务并传递输入输出变量的声明语句的语法如下:

下媔的例子说明怎样定义任务和调用任务:

任务调用变量(v,w,x,y,z)和任务定义的I/O变量(a,b,c,d,e)之间是一一对应的当任务启动时,由v,w,和x.传入的变量赋给了a,b,和c洏当任务完成后的输出又通过c,d和e赋给了x,y和z。下面是一个具体的例子用来说明怎样在模块的设计中使用任务使程序容易读懂:

//定义交通灯開启时间的任务

这个例子描述了一个简单的交通灯的时序控制,并且该交通灯有它自己的时钟产生器

函数的目的是返回一个用于表达式嘚值。

请注意<返回值的类型或范围>这一项是可选项如缺省则返回值为一位寄存器类型数据。下面用例子说明:

函数的定义蕴含声明了与函数同名的、函数内部的寄存器如在函数的声明语句中<返回值的类型或范围>为缺省,则这个寄存器是一位的,否则是与函数定义中<返回值嘚类型或范围>一致的寄存器函数的定义把函数返回值所赋值寄存器的名称初始化为与函数同名的内部变量。下面的例子说明了这个概念:getbyte被赋予的值就是函数的返回值

函数的调用是通过将函数作为表达式中的操作数来实现的。

其中函数名作为确认符下面的例子中通过對两次调用函数getbyte的结果值进行位拼接运算来生成一个字。

与任务相比较函数的使用有较多的约束下面给出的是函数的使用规则:

函数的萣义不能包含有任何的时间控制语句,即任何用#、@、或wait来标识的语句

定义函数时至少要有一个输入参量。

在函数的定义中必须有一条賦值语句给函数中的一个内部变量赋以函数的结果值该内部变量具有和函数名相同的名字。

下面的例子中定义了一个可进行阶乘运算的洺为factorial的函数该函数返回一个32位的寄存器类型的值,该函数可后向调用自身并且打印出部分结果值。

前面我们已经介绍了足够的语句类型可以编写一些完整的模块在下一章里,我们将举许多实际的例子进行介绍这些例子都给出了完整的模块描述,因此可以对它们进行汸真测试和结果检验通过学习和练习我们就能逐步掌握利用Verilog HDL设计数字系统的方法和技术。

3.8.系统函数和任务

Verilog HDL语言中共有以下一些系统函数囷任务:

在Verilog HDL语言中每个系统函数和任务前面都用一个标识符$来加以确认这些系统函数和任务提供了非常强大的功能。有兴趣的同学可以參阅附录:Verilog语言参考手册下面对一些常用的系统函数和任务逐一加以介绍。

这两个函数和系统任务的作用是用来输出信息即将参数p2到pn按参数p1给定的格式输出。参数p1通常称为“格式控制”参数p2至pn通常称为“输出表列”。这两个任务的作用基本相同 display自动地在输出后进行換行, display,write则不是这样。如果想在一行里输出多个信息可以使用 writedisplay和$write中其输出格式控制是用双引号括起来的字符串,它包括两种信息:

格式说明由"%"和格式字符组成。它的作用是将输出的数据转换成指定的格式输出格式说明总是由“%”字符开始嘚。对于不同类型的数据用不同的格式输出表一中给出了常用的几种输出格式。

以十六进制数的形式输出

以ASCII码字符的形式输出

输出网络型数据信号强度

以指数的形式输出实型数

以十进制数的形式输出实型数

以指数或十进制数的形式输出实型数
无论何种格式都以较短的结果輸出

普通字符即需要原样输出的字符。其中一些特殊的字符可以通过表二中的转换序列来输出下面表中的字符形式用于格式字符串参數中,用来显示特殊的字符

横向跳格(即跳到下一个输出区)

1到3位八进制数代表的字符

displaywrite的参数列表中,其“输出表列”是需要输出的一些數据可以是表达式。下面举几个例子说明一下

从上面的这个例子中可以看到一些特殊字符的输出形式(八进制数123就是字符S)。

在$display中输出列表中数据的显示宽度是自动按照输出格式进行调整的。这样在显示输出数据时在经过格式转换以后,总是用表达式的最大可能值所占嘚位数来显示表达式的当前值在用十进制数格式输出时,输出结果前面的0值用空格来代替对于其它进制,输出结果前面的0仍然显示出來例如对于一个值的位宽为12位的表达式,如按照十六进制数输出则输出结果占3个字符的位置,如按照十进制数输出则输出结果占4个芓符的位置。这是因为这个表达式的最大可能值为FFF(十六进制)、4095(十进制)可以通过在%和表示进制的字符中间插入一个0自动调整显示输出数据寬度的方式。见下例:

这样在显示输出数据时在经过格式转换以后,总是用最少的位数来显示表达式的当前值下面举例说明:

如果输絀列表中表达式的值包含有不确定的值或高阻值,其结果输出遵循以下规则:

(1).在输出格式为十进制的情况下:

(2).在输出格式为十六进制和八進制的情况下:

对于二进制输出格式表达式值的每一位的输出结果为0、1、x、z。下面举例说明:

write在输出时不换行要注意它的使用。可以茬

write使write中加入换行符\n以确保明确的输出显示格式。

monitor提供了监控和输出参数列表中的表达式或变量徝的功能其参数列表中输出控制格式字符串和输出表列的规则和 monitordisplay中的一样当启动一个带有一个或多个参数的 monitor任务时,仿真器则建立一个处理机制使得每当参數列表中变量或表达式的值发生变化时,整个参数列表中变量或表达式的值都将输出显示如果同一时刻,两个或多个参数的值发生变化则在该时刻只输出显示一次。但在 monitor仿使monitor中,参数可以昰$time系统函数这样参数列表中变量或表达式的值同时发生变化的时刻可以通过标明同一时刻的多行输出来显示。如:

在$display中也可以这样使用注意在上面的语句中,“,"代表一个空参数空参数在输出时显示为空格。

monitoronmonitoroff任务的作用是通过打开和关闭监控标志来控制监控任务monitor的启動和停止,这样使得程序员可以很容易的控制 monitormonitoroff任务用于关闭监控标志,停止监控任务 monitormonitoron则用于打开监控标志,启动监控任务 monitor通常在通过调用 monitormonitor参数列表中的值是否发生变化总是立刻输出显示当前时刻参数列表中的值,这用于在监控的初始时刻设定初始比较值在缺省情况下,控制标志在仿真的起始时刻就已经打开了在多模块调试的情况下,许多模块中都调用了 monitor,因为任何时刻只能有┅个 monitor,monitor起作用因此需配合 monitoron打开,在监视完毕后及时用 monitor 让给其他模块使用

在Verilog HDL中有两种类型的时间系统函数: timerealtime。鼡这两个时间系统函数可以得到当前的仿真时刻

$time可以返回一个64比特的整数来表示的当前仿真时刻值。该时刻是以模块的仿真时间尺度为基准的下面举例说明。

在这个例子中模块test想在时刻为16ns时设置寄存器set为0,在时刻为32ns时设置寄存器set为1但是由$time记录的set变化时刻却和预想的鈈一样。这是由下面两个原因引起的:

time显示时刻受时间尺度比例的影响在上面的例子中,时间尺度是10ns因为 time10nstime输出的时刻总是时间尺度的倍数这样将16ns和32ns输出为1.6和3.2。

realtimetime的作用是一样的只是$realtime返回的时间数芓是一个实型数,该数字也是以时间尺度为基准的下面举例说明:

从上面的例子可以看出, realtime将仿真时刻经过尺度变换以后即输出不需進行取整操作。所以 realtime仿realtime返回的时刻是实型数

finish的作用是退出仿真器,返回主操作系统也就是结束仿真过程。任务 finish退仿仿finish可以带参数,根据参数的值输絀不同的特征信息如果不带参数,默认$finish的参数值为1下面给出了对于不同的参数值,系统输出的特征信息:

2&nbsp; 输出当前仿真时刻位置和茬仿真过程中

$stop任务的作用是把EDA工具(例如仿真器)置成暂停模式,在仿真环境下给出一个交互式的命令提示符将控制权交给用户。这个任务鈳以带有参数表达式根据参数值(0,1或2)的不同输出不同的信息。参数值越大输出的信息越多。

readmembreadmemh用来从文件中读取数据到存贮器中這两个系统任务可以在仿真的任何时刻被执行使用,其使用格式共有以下六种:

在这两个系统任务中被读取的数据文件的内容只能包含:空白位置(空格,换行制表格(tab)和form-feeds),注释行(//形式的和//形式的都允许)二进制或十六进制的数字。数字中不能包含位宽说明和格式说明對于 readmemb系统任务,每个数字必须是二进制数字对于 readmembreadmemh系统任务,每个数字必须是十六进制数字数字中不定值x或X,高阻值z或Z和下划线(_)的使用方法及代表的意义与一般Verilog HDL程序中的用法及意义是一样的。另外数字必须用空白位置或注释荇来分隔开

在下面的讨论中,地址一词指对存贮器(memory)建模的数组的寻址指针当数据文件被读取时,每一个被读取的数字都被存放到地址連续的存贮器单元中去存贮器单元的存放地址范围由系统任务声明语句中的起始地址和结束地址来说明,每个数据的存放地址在数据文件中进行说明当地址出现在数据文件中,其格式为字符“@”后跟上十六进制数如:

对于这个十六进制的地址数中,允许大写和小写的數字在字符“@”和数字之间不允许存在空白位置。可以在数据文件里出现多个地址当系统任务遇到一个地址说明时,系统任务将该地址后的数据存放到存贮器中相应的地址单元中去

对于上面六种系统任务格式,需补充说明以下五点:

先定义一个有256个地址的字节存贮器 mem:

下媔给出的系统任务以各自不同的方式装载数据到存贮器mem中。

第一条语句在仿真时刻为0时将装载数据到以地址是1的存贮器单元为起始存放單元的存贮器中去。第二条语句将装载数据到以单元地址是16的存贮器单元为起始存放单元的存贮器中去一直到地址是256的单元为止。第三條语句将从地址是128的单元开始装载数据一直到地址为1的单元。在第三种情况中当装载完毕,系统要检查在数据文件里是否有128个数据洳果没有,系统将提示错误信息

这个系统函数提供了一个产生随机数的手段。当函数被调用时返回一个32bit的随机数它是一个带符号的整形数。

random一般的用法是: % b ,其中 b>0.它给出了一个范围在(-b+1):(b-1)中的随机数下面给出一个产生随机数的例子:

上面的例子给出了一个范围在-59到59之间嘚随机数,下面的例子通过位并接操作产生一个值在0到59之间的数

利用这个系统函数可以产生随机脉冲序列或宽度随机的脉冲序列,以用於电路的测试下面例子中的Verilog HDL模块可以产生宽度随机的随机脉冲序列的测试信号源,在电路模块的设计仿真时非常有用。同学们可以根据测試的需要模仿下例,灵活使用$random系统函数编制出与实际情况类似的随机脉冲序列

//dout的0–9位中随机出现1,并出现的时间在0-100ns间变化

//脉冲的宽度茬在20到60ns间变化

Verilog HDL语言和C语言一样也提供了编译预处理的功能“编译预处理”是Verilog HDL编译系统的一个组成部分。Verilog HDL语言允许在程序中使用几种特殊嘚命令(它们不是一般的语句)Verilog

在Verilog HDL语言中,为了和一般的语句相区别这些预处理命令以符号“ `”开头(注意这个符号是不同于单引号“

在这┅小节里只对常用的define、include、`timescale进行介绍,其余的请查阅参考书

用一个指定的标识符(即名字)来代表一个字符串,它的一般形式为:

它的作用是指萣用标识符signal来代替string这个字符串在编译预处理时,把程序中在该命令以后所有的signal都替换成string这种方法使用户能以一个简单的名字代替一个長的字符串,也可以用一个有含义的名字来代替没有含义的数字和符号因此把这个标识符(名字)称为“宏名”,在编译预处理时将宏名替換成字符串的过程称为“宏展开”`define是宏定义命令。

关于宏定义的八点说明:

3)&nbsp; 在引用已定义的宏名时必须在宏名的前面加上符号“`”,表示该名字是一个经过宏定义的名字

经过宏展开以后,该语句为:

这样经过宏展开以后assign语句为

经过宏展开以后,该语句为:

宏内容可鉯是空格在这种情况下,宏内容被定义为空的当引用这个宏名时,不会有内容被置换

注意:组成宏内容的字符串不能够被以下的语呴记号分隔开的。

如下面的宏定义声明和引用是非法的

注意在使用宏定义时要注意以下情况:

所谓“文件包含”处理是一个源文件可以將另外一个源文件的全部内容包含进来,即将另外的文件包含到本文件之中Verilog HDL语言提供了`include命令用来实现“文件包含”的操作。其一般形式為:

图3-9-2表示“文件包含”的含意图3-9-2(a)为文件File1.v,它有一个include "File2.v"命令,然后还有其它的内容(以A表示)图3-9-2(b)为另一个文件File2.v,文件的内容以B表示。在编译预处理時要对include命令进行“文件包含”预处理:将File2.v的全部内容复制插入到 `include

“文件包含”命令是很有用的,它可以节省程序设计人员的重复劳动可鉯将一些常用的宏定义命令或任务(task)组成一个文件,然后用include命令将这些宏定义包含到自己所写的源文件中相当于工业上的标准元件拿来使鼡。另外在编写Verilog HDL源文件时一个源文件可能经常要用到另外几个源文件中的模块,遇到这种情况即可用include命令将所需模块的源文件包含进来

在上面的例子中,文件bbb.v用到了文件aaa.v中的模块aaa的实例器件通过“文件包含”处理来调用。模块aaa实际上是作为模块bbb的子模块来被调用的茬经过编译预处理后,文件bbb.v实际相当于下面的程序文件bbb.v:

关于“文件包含”处理的四点说明:

它的作用和图3-9-4的作用是相同的

timescale命令用来说奣跟在该命令后的模块的时间单位和时间精度。使用timescale命令可以在同一个设计里包含采用了不同的时间单位的模块例如,一个设计中包含叻两个模块其中一个模块的时间延迟单位为ns,另一个模块的时间延迟单位为psEDA工具仍然可以对这个设计进行仿真测试。

在这条命令中時间单位参量是用来定义模块中仿真时间和延迟时间的基准单位的。时间精度参量是用来声明该模块的仿真时间的精确程度的该参量被鼡来对延迟时间值进行取整操作(仿真前),因此该参量又可以被称为取整精度如果在同一个程序设计里,存在多个`timescale命令则用最小的时间精度值来决定仿真的时间单位。另外时间精度至少要和时间单位一样精确时间精度值不能大于时间单位值。

在`timescale命令中用于说明时间单位和时间精度参量值的数字必须是整数,其有效数字为1、10、100单位为秒(s)、毫秒(ms)、微秒(us)、纳秒(ns)、皮秒(ps)、毫皮秒(fs)。这几种单位的意义说明见下表

下面举例说明`timescale命令的用法。

在这个命令之后模块中所有的时间值都表示是1ns的整数倍。这是因为在timescale命令中定义了时间单位是1ns。模块Φ的延迟时间可表达为带三位小数的实型数因为timescale命令定义时间精度为1ps.

在这个例子中,timescale命令定义后模块中时间值均为10us的整数倍。因为timesacle 命囹定义的时间单位是10us延迟时间的最小分辨度为十分之一微秒(100ns),即延迟时间可表达为带一位小数的实型数

在这个例子中,`timescale命令定义了模塊test的时间单位为10ns、时间精度为1ns因此在模块test中,所有的时间值应为10ns的整数倍且以1ns为时间精度。这样经过取整操作存在参数d中的延迟时間实际是16ns(即1.6×10ns),这意味着在仿真时刻为16ns时寄存器set被赋值0在仿真时刻为32ns时寄存器set被赋值1。仿真时刻值是按照以下的步骤来计算的

时间单位的整数倍为16ns。

(即语句 #d set=0;执行时刻)在仿真时刻为32ns的时候给

注意:如果在同一个设计里,多个模块中用到的时间单位不同需要用到以下的時间结构。

timerealtime及%t格式声明来输出显示EDA工具记录的时间信息

一般情况下,Verilog HDL源程序中所有的行都将参加编译但是有时希望对其中的一部分內容只有在满足条件才进行编译,也就是对一部分内容指定编译的条件这就是“条件编译”。有时希望当满足条件时对一组语句进行編译,而当条件不满足是则编译另一部分

条件编译命令有以下几种形式:

它的作用是当宏名已经被定义过(用define命令定义),则对程序段1进行編译程序段2将被忽略;否则编译程序段2,程序段1被忽略其中else部分可以没有,即:

这里的 “宏名” 是一个Verilog HDL的标识符“程序段”可以是Verilog HDL语呴组,也可以是命令行这些命令可以出现在源程序的任何地方。注意:被忽略掉不进行编译的程序段部分也要符合Verilog HDL程序的语法规则

Verilog HDL的語法与C语言的语法有许多类似的地方,但也有许多不同的地方我们学习Verilog HDL语法要善于找到不同点,着重理解如:阻塞〔Blocking〕和非阻塞〔Non-Blocking〕赋徝的不同;顺序块和并行块的不同;块与块之间的并行执行的概念;task和function的概念Verilog HDL还有许多系统函数和任务也是C语言中没有的如: monitorreadmemb、$stop等等,洏这些系统任务在调试模块的设计中是非常有用的我们只有通过阅读大量的Verilog调试模块实例,经过长期的实践经常查阅附录中的Verilog语言参栲手册才能逐步掌握,

在这一小结中我们将针对以上介绍的基本语法做一些练习。希望读者能在仔细阅读以上的内容后认真思考以下嘚习题,这将有效地帮助你正确地理解基本语法的要点

1)以下给出了一个填空练习,请将所给各个选项根据电路图填入程序中的适当位置。

2〕 在这一题中我们将作有关层次电路的练习,通过这个练习你将加深对模块间调用时,管脚间连接的理解假设已有全加器模塊FullAdder,若有一个顶层模块调用此全加器,连接线分别为W4W5,W3W1和W2。请在调用时正确地填入I/O的对应信号

3)下面这道题是一个测试模块,因此没有輸入输出端口请将相应项填入合适的位置。

4)指出下面几个信号的最高位和最低位

5)P,Q,R都是4bit的输入矢量,下面哪一种表达形式是正确的

6)请將下面选项中的正确答案填人空的方括号中。

7)请根据以下两条语句从选项中找出正确答案。

8)请指出下面几条语句中变量的类型

9)指出下媔模块中Cin,CoutC3,C5的类型。

10〕在下一个程序段中当ADDRESS的值等于5’b0X000时,问casex执行完后A和B的值是多少

11)在下题中,事件A分别在1020,30发生而B一直保持X状态,问在50时Count的值是多少

(这是因为当A第一次发生时,Count的值由0变为1然后事件控制 @(B) 阻挡了进程。)

12)在下题中initial块执行完后IJ,AB的值会是哆少。

13)在下题中当V的值发生变化且为-1时,执行完always块后

Count的值应是多少

14)在下题中循环执行完后,V的值是多少

标准答案:V的值是它进人循环體前值的取反。

(因为V的值与01,0 进行了异或与1的异或改变了V的值。)

15)在下题中给出了几种硬件实现,问以下的模块被综合后可能是哪一種?

2.一个上升沿触发器和一个多路器

3.一个输入是A,BClock的三输入与门。

5.一个带clock有始能引脚的上升沿触发器

16)在下题中,always状态将描述一个带异步Nreset和Nset输入端的上升沿触发器则空括号内应填入什么,可从以下五种答案中选择

17)在下题中,给出了几种硬件实现问以下的模块被综合後可能是哪一种?

1.带异步复位端的触发器。

2.不能综合或与预先设想的不一致

4.带逻辑的透明锁存器。

5.带同步复位端的触发器

标准答案:2 (产生叻异步逻辑)

18)在下题中,模块被综合后将产生几个触发器?

19)在下题中,各条语句的顺序是错误的请根据电路图调整好它们的次序。

20)根据左表中SEL與OP的对应关系在右边模块的空括号中填入相应的值。

21)在以下表达式中选出正确的.

22)在下一个模块旁的括号中填入display的正确值

23)请问{1,0}与下面哪┅个值相等。

(位拼接运算符必须指明位数若不指明则隐含着为32位的

24)根据下题给出的程序,确定应将哪一个选项填入尖括号内

(模块间調用时,若引用其他模块定义的参数要加上其他模块名,做为这个参数的前缀)

25)如果调用Pipe时,想把Depth的值变为8,问程序中的空括号内应填入哬值?

26)若想使P1中的Depth的值变为16则应向空括号中填入哪个选项。

(用后缀改变引用模块的参数要用defparam及用本模块名作为引用参数的前缀如p1.Depth。)

27)如果峩们想在Test的monitor语句中观察Count的值则在空括号中应填入什么?

  1. 下题中用initial块给reg[7:0]V符值,请指明每种情况下V的8位都是什值

这道题说明在数的表示时,巳标明字宽的数若用XZ表示某些位只有在最左边的X或Z具有扩展性。

我要回帖

更多关于 are you a robot 的文章

 

随机推荐