《数字电路设计及Verilog HDL实现》课件第3章_第1页
《数字电路设计及Verilog HDL实现》课件第3章_第2页
《数字电路设计及Verilog HDL实现》课件第3章_第3页
《数字电路设计及Verilog HDL实现》课件第3章_第4页
《数字电路设计及Verilog HDL实现》课件第3章_第5页
已阅读5页,还剩121页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

3.1VerilogHDL程序的基本结构

3.2VerilogHDL的数据类型

3.3VerilogHDL的运算符

3.4VerilogHDL的基本语句

3.5模块化程序设计

第3章VerilogHDL的基本语法

VerilogHDL程序由模块(module)组成,模块的基本结构如图3.3.1所示。一个完整的模块由模块端口定义和模块内容两部分组成,模块内容包括I/O声明、信号类型声明和功能描述。3.1VerilogHDL程序的基本结构图3.1.1VerilogHDL程序模块结构例如,定义一个1位全加器full_addr模块,其输入/输出端口有5个:a、b是全加器的两个加数输入;cin是低位的进位输入;s是全加器的和输出;cout是全加器向高位的进位输

出。其格式如下:

modulefull_addr(s,cout,a,b,cin);//模块端口定义

inputa,b,cin;//I/O声明

outputs,cout;

assign{cout,s}=a+b+cin;//功能描述

endmodule模块的设计遵循以下规则:

(1)模块内容位于module和endmodule之间;每个模块都有一个名字,即模块名,如full_addr,模块名中可以包含英文字母、数字和下划线,并以英文字母开头。

(2)除endmodule外,所有的语句后面必须有分号“;”。

(3)语句可以是单条语句,也可以是用begin和end两个保留字包围起来的由多条语句组成的复合语句。

(4)可以用“/*…*/”或“//…”对程序的任何部分作注释,增加程序的可读性和可维护性。3.1.1模块端口定义

模块端口定义用来声明设计模块的输入/输出端口,其格式如下:

module模块名(端口1,端口2,端口3,…);

模块的端口是设计电路模块与外部联系的全部输入/输出端口信号,是设计实体的对外引脚,是使用时外界可以看到的部分(不包括电源线和地线),多个端口之间用逗号“,”隔开。3.1.2模块内容

模块内容用于对信号的I/O状态及信号类型进行声明,并描述模块的功能。

1.I/O声明

模块的I/O声明用来声明各端口信号流动方向,包括输入(input)、输出(output)和双向(inout)。I/O声明格式如下:

1)输入声明

如果信号位宽为1位,那么声明格式为

input端口1,端口2,端口3,…;

如果信号位宽大于1位,那么声明格式为

input[msb:lsb]端口1,端口2,端口3,…;

其中,msb和lsb分别表示信号最高位和最低位的编号。

2)输出声明

如果信号位宽为1位,那么声明格式为

output端口1,端口2,端口3,…;

如果信号位宽大于1位,那么声明格式为

output[msb:lsb]端口1,端口2,端口3,…;

3)输入、输出声明

如果信号位宽为1位,那么声明格式为

inout端口1,端口2,端口3,…;

如果信号位宽大于1位,那么声明格式为

inout[msb:lsb]端口1,端口2,端口3,…;

2.信号类型声明

信号类型声明用来说明电路的功能描述中所用信号的数据类型,常用的信号类型有连线型(wire)、寄存器型(reg)、整型(integer)、实型(real)、时间型(time)等。

3.功能描述

功能描述是VerilogHDL程序的主要部分,用来描述设计模块内部结构和模块端口间的逻辑关系,在电路上相当于器件的内部结构。功能描述可以用assign语句、实例化元件、always块语句、initial块等语句来实现。

1)用assign语句实现

这种方式很简单,只要在assign后面加一个赋值语句即可。assign语句一般适合对组合逻辑进行描述,称为连续赋值方式。

例如,描述一个两输入的与门可写为

assigna=b&c;

2)用实例元件实现

用实例化元件实现就是利用VerilogHDL提供的元件库来实现一个逻辑关系。

例如,用实例化元件表示一个两输入的与门可以写为

andu1(q,a,b);

其中,and是VerilogHDL元件库中与门的元件名;u1是实例化后的与门名称;q是与门的输出;a、b是与门的输入端。要求模块中每个实例化后的元件名称必须是唯一的。

3)用always块实现

always块语句可以实现各种逻辑,常用于组合和时序逻辑的功能描述。一个程序设计模块中可以包含一个或多个always块语句。程序运行中,在某些条件满足时,就重复执行always块中的语句。

例如,表示一个带有异步清除端的D触发器可写为

always@(posedgeclkorposedgeclr)

begin

if(clr)q<=0;

elseq<=d;

end

4)用initial块实现

initial块语句与always块语句类似,不过在程序中initial块语句只被执行一次,常用于电路的初始化。3.2.1常量

在程序运行过程中,其值不能改变的量称为常量。在VerilogHDL中有三类常量:整型、实型和字符串型。

1.整型常量

在VerilogHDL中,整型常量的表示格式为

<位宽>′<进制><数值>3.2VerilogHDL的数据类型位宽:位宽是对应的二进制宽度。当定义的位宽比常数实际的位宽大时,在常数的左边自动填补0,但如果常数的最左边一位是x或z时,那么就在左边自动填补x或z;当定义的位宽比常数实际的长度小时,在最左边的相应位就被截断。

进制:整型数有四种进制形式:

(1)二进制(b或B)。

(2)十进制(d或D)。

(3)八进制(o或Q)。

(4)十六进制(h或H)。数值:二进制数值可以用下列四种基本的值来表示:

(1)0:逻辑0或“假”。

(2)1:逻辑1或“真”。

(3)x:未知。

(4)z:高阻。

这四种值的解释都内置于语言中,如一个为0的值是指逻辑0,一个为1的值是指逻辑1,一个为z的值是指高阻抗,一个为x的值是指逻辑不定值。x值和z值都是不区分大小写的。例如:

6′B10X1Z0//6位二进制数,从低位数起第2位为高阻,第4位为不定值

5′O37

//5位八进制数

4′D98

//4位十进制数

7′H1A

//7位十六进制数

8′h4Z

//8位十六进制值,即0100zzzz

-8′D76

//8位十进制值,即-76,符号必须写在最前边另外,整型常量还有两种表示方式,其格式为

′<进制><数值>

<数值>

在不指定位宽时,缺省位宽由机器系统决定,但至少为32位;如果数值中既无位宽,也无进制,则缺省为十进制数。例如:

′O35//位宽为32位的八进制数

′H67//位宽为32位的十六进制数

92

//十进制数92

-100//十进制数-100

2.实型常量

实型数可以用十进制计数法和科学计数法两种格式表示。在表示小数时,小数点两边必须都有数字,否则为非法的表示形式。

例如:

7.56//十进制数计数法

4.

//非法表示,小数点后应有数字

34.56e2//科学计数法,其值为3456(e与E相同)

6E-2//科学计数法,其值为0.06

3.字符串型常量

字符串是用双引号括起来的字符序列,它必须写在同一行,不能分行书写。字符串中的每个字符都是以其ASCII码进行存放的,一个字符串可以看做是8位的ASCII码值序列。

例如:

″hello!″//按字母顺序存放,每个字母为8位ASCII码

另外,还存在一些特殊字符,这些特殊字符又称转义字符,用“\”来说明。常用的特殊字符的表示及含义如表3.2.1所示。表3.2.1特殊字符的表示及含义

4.参数常量

在VerilogHDL中用parameter来定义常量,即用parameter定义一个标识符代表一个常量,称为参数常量或符号常量,这样可以增加程序的可读性和可维护性。

参数常量定义格式如下:

parameter标识符1=表达式1,标识符2=表达式2,…,标识符n=表达式n;

例如:

parameterPI=3.14,A=8′B10110101,WORD_LENGTH=16;3.2.2变量

在程序运行过程中,其值可以改变的量称为变量。在VerilogHDL中,变量的数据类型很多,这里只对常用的几种变量类型进行介绍。

1.wire型

wire是网络数据类型之一,表示结构实体之间的物理连接。网络类型的变量不仅不能储存值,而且必须受到驱动器的驱动。如果没有驱动器连接到网络型的变量上,那么其值为高阻值。网络型数据有很多种,但最常用的是wire型。

wire型变量常用来表示以assign语句生成的组合逻辑信号,输入/输出信号在默认情况下自动定义为wire型。wire型信号可作为任何语句中的输入,也可作为assign语句和实例化元件的输出。

wire型变量的定义格式如下:

wire[msb:lsb]变量1,变量2,…,变量n;

其中,wire是定义符;[msb:lsb]中的msb和lsb分别表示wire型变量的最高位和最低位的编号,位宽由msb和lsb确定,如果不指定位宽,那么位宽自动默认为1;定义多个变量时,变量之间用逗号隔开。例如:

wirea,b;//定义了两个1位wire型变量a、b

wire[7:0]m,n;//定义了两个8位wire型变量m、n,最低位为第0位,最高位为第7位

wire[8:1]x,y;//定义了两个8位wire型变量x、y,最低位为第1位,最高位为第8位

2.reg型

reg是寄存器类型,是数据存储单元的抽象,其对应的是具有状态保持功能的电路元件,如触发器、锁存器等。

reg型变量只能在always和initial块中被赋值,通过赋值语句改变reg型变量的值,若reg型变量未被初始化,则其值为未知值x。reg型变量与wire型变量的区别是:wire型变量需要持续地驱动,而reg型变量保持最后一次的赋值。

reg型变量的定义格式如下:

reg[msb:lsb]变量1,变量2,…,变量n;

其中,reg是定义符;[msb:lsb]中的msb和lsb分别表示reg型变量的最高位和最低位的编号,位宽由msb和lsb确定,如果不指定位宽,则自动默认为1。例如:

regx0,y0;//定义了两个1位reg型变量x0、y0

reg[7:0]d,q;//定义了两个8位reg型变量d、q,最低位为第0位,最高位为第7位

reg[8:1]sum;//定义了一个8位reg型变量sum,最低位为第1位,最高位为第8位

3.memory型

memory型是存储器型,是通过建立reg型数组来描述的,可以描述RAM存储器、ROM存储器和reg文件。

memory型变量的定义格式如下:

reg[msb:lsb]存储单元1[n1:m1],存储单元2[n2:m2],…,存储单元i[ni:mi];

其中,[n1:m1],[n2:m2],…,[ni:mi]分别表示存储单元的编号范围。例如:

regmemory1[1023:0];//存储器为1024个单元,每个单元为1位

reg[7:0]memory2[15:0];//存储器为16个单元,每个单元为8位

reg[32:1]memory2[1:512];//存储器为512个单元,每个单元为32位

值得注意的是,对存储单元的访问可以通过数组的索引进行。例如,以下语句分别用于定义4个存储单元,每个单元为8位,单元编号为0~3,然后给每个存储单元赋值。

reg[8:1]RAM[3:0];

RAM[0]=8′H1A;

RAM[1]=8′H00;

RAM[2]=8′H55;

RAM[3]=8′H31;

4.integer型

integer型是32位带符号整型变量,用于对循环控制变量的说明,典型应用是高层次的行为建模,它与后面的time和real类型一样是不可综合的。也就是说,这些类型是纯数学的抽象描述,不与任何物理电路相对应。

integer型变量的定义格式如下:

integer变量1,变量2,…,变量n;例如:

integeri,j;//定义了两个整型变量i,j

integerd[1:8];//定义了一个含有8个数据的整型数组

5.time型

time类型用于存储和处理时间,是64位无符号数。

time型变量的定义格式如下:

time变量1,变量2,…,变量n;

6.real型

real型是64位带符号实型变量,用于存储和处理实型数据。

real型变量的定义格式如下:

real变量1,变量2,…,变量n;1.算术运算符

算术运算符包括:

+(加法运算符或正值运算符,如x+y,+8)

-(减法运算符或负值运算符,如x-y,-90)

*(乘法运算符,如x*y)

/(除法运算符,如x/y)

%(取模运算符,如x%y)3.3VerilogHDL的运算符

2.逻辑运算符

逻辑运算符包括:

&&(逻辑与)

||(逻辑或)

!(逻辑非)

逻辑运算符操作的结果为0(假)或1(真)。例如,假设:

a=′b0;//0为假

b=′b1;//1为真

那么:

A&&b结果为0(假)

a||b结果为1(真)

!a结果为1(真)

!b结果为0(假)在判断一个数是否为真时,以0代表“假”,以非0代表“真”。

例如,假设:

ABus=′b0111;

BBus=′b0101;

那么:

ABus||BBus结果为1(真)

ABus&&BBus结果为1(真)

!ABus结果为0(假)

3.关系运算符

关系运算符包括:

<

(小于)

<=

(小于等于)

>

(大于)

>=

(大于等于)

关系运算符是用来确定指定的两个操作数之间的关系是否成立的,如果成立,结果为1(真);如果不成立,结果为0(假)。例如,假设:

m=19;

n=5;

那么:

m>n结果为1(真)

a>=b结果为1(真)

m<n结果为0(假)

a<=b结果为0(假)

4.等值运算符

等值运算符包括:

==(逻辑相等)

!=(逻辑不等)

===(全等)

!==(非全等)“==”运算符称为逻辑相等运算符,而“===”称为全等运算符,两个运算符都是比较两个数是否相等的。如果两个操作数相等,那么运算结果为逻辑值1;如果两个操作数不相等,那么运算结果为逻辑0。不同的是由于操作数中的某些位可能存在不定值x或高阻值z,这时逻辑相等在进行比较时,结果为不定值x,而全等运算符是按位进行比较的,对这些不定位或高阻位也进行比较,只要两个操作数完全一致,则结果为1(真),否则结果为0。

与“==”和“===”相同,“!=”的运算结果可能为1、0或x,而“!==”的运算结果只有两种状态,即1或0。例如,假设:

d1=4′b010x;

d2=4′b010x;

那么:

d1==d2结果为x

d1===d2结果为1

5.位运算符

位运算符包括:

~

(非)

&

(与)

~&

(与非)

|

(或)

~|

(或非)

^

(异或)

^~或~^(同或)位运算符是对两个操作数按位进行逻辑运算的。当两个操作数的位数不同时,自动在位数较少的操作数的高位补0。

例如,假设:

x=8′b01011111;

y=4′b1100;

那么:

x&y=8′b00001100

x|y=8′b01011111

~x=8′b10100000

~y=4′b0011

x~&y=8′b11110011

x~|y=8′b10100000

x^y=8′b01010011

x^~y=8′b10101100

6.缩减运算符

缩减运算符包括:

&

(与)

~&

(与非)

|

(或)

~|

(或非)

^

(异或)

^~

(同或)

缩减运算符与逻辑运算符的法则一样,但缩减运算符是对单个操作数按位进行逻辑递推运算的,运算结果为1位二进制数。例如:

reg[7:0]a;

regb

b=&a;

程序中,“b=&a;”语句与“b=a[0]&a[1]&a[2]&a[3]&a[4]&a[5]&a[6]&a7];”语句等价。

7.移位运算符

移位运算符包括:

<<(左移)

>>(右移)

左移和右移运算符是对操作数进行逻辑移位操作的,空位用0进行填补。

移位运算的格式为

a<<n或a>>n

其中,a为操作数;n为移位的次数。例如,假设:

i=8;

m=3

那么:

i<<m结果为64

i>>m结果为1

8.条件运算符

条件运算符是:

?:

条件运算符是唯一的一个三目运算符,即条件运算符需要三个操作数。

条件运算符格式如下:

条件?表达式1:表达式2

条件表达式的含义是:如果条件为真,则结果为表达式1的值;如果条件为假,则结果为表达式2的值。例如:

a=10,b=20;

y=a>b?a:b;

由于a>b条件为假,因此,y的值为b的值。

9.拼接运算符

拼接运算符是:

{}

拼接运算符用来将两个或多个数据的某些位拼接起来。

拼接运算符格式如下:

{数据1的某些位,数据2的某些位,…,数据n的某些位}

例如:

X={a[7:4],b[3],c[2:0]}

表示X是由a的第7~4位、b的第3位和c的第2~0位拼接而成的。

10.运算符的优先级

在一个表达式中出现多种运算符时,其运算的优先级顺序如表3.3.1所示。表3.3.1运算符的运算优先级3.4.1赋值语句

在VerilogHDL中,赋值语句有两种:连续赋值语句和过程赋值语句。

1.连续赋值语句

连续赋值语句用来驱动wire型变量,这一变量必须事先定义过。使用连续赋值语句时,只要输入端操作数的值发生变化,该语句就重新计算并刷新赋值结果。连续赋值语句用来描述组合逻辑。3.4VerilogHDL的基本语句连续赋值语句格式如下:

assign#(延时量)wire型变量名=赋值表达式;

语句的含义是:只要右边赋值表达式中有变量发生变化,就重新计算表达式的值,新结果在指定的延时时间单位以后赋值给wire型变量。如果不指定延时量,则延时量默认为0。

例如,下面语句表示只要a或b发生变化,就重新计算a和b相与的值,计算结果赋值给c。

wirea,b,c;

assignc=a&b;

2.过程赋值语句

过程赋值语句是在initial或always语句块内赋值的,它用于对reg型、memory型、integer型、time型和real型变量赋值,这些变量在下一次过程赋值之前保持原来的值。过程赋值语句分为两类,分别为阻塞赋值和非阻塞赋值。

1)阻塞赋值

阻塞赋值的赋值符为“=”,它在该语句结束时就完成赋值操作。

阻塞赋值格式如下:

变量=赋值表达式;

2)非阻塞赋值

非阻塞赋值的赋值符为“<=”,它在块结束时才完成赋值操作。

非阻塞赋值格式如下:

变量<=赋值表达式;

过程中使用阻塞赋值与非阻塞赋值的主要区别是:一条阻塞赋值语句执行时,下一条语句被阻塞,即只有当一条语句执行结束,下条语句才能执行;非阻塞赋值语句中,各条语句是同时执行的。也可以理解为,阻塞赋值是串行执行的,非阻塞赋值是并行执行的。为了理解这两种赋值,下面分析两段程序的执行过程。程序段1:

begin

r1=2;

r2=r1;

r3=r2;

end

程序段2:

begin

r1<=2;

r2<=r1;

r3<=r2;

end3.4.2条件语句

1.if…else语句

if语句是用来判断给定的条件是否满足,根据判定的结果为真或假决定执行的操作。

VerilogHDL语言提供了三种形式的if语句。

1)if(表达式)语句

例如:

if(x>y)q=x;

2)if(表达式)语句1else语句2

例如:

if(Reset)

Q=0;

else

Q=D;

3)if(表达式1)语句1

elseif(表达式2)语句2

elseif(表达式3)语句3

elseif(表达式m)语句m

else语句n

例如:

if(x>y)Q=in1;

if(x==y)Q=in2;

else

Q=in3;

2.case语句

case语句是一种多分支语句,if语句每次只能有两个分支可供选择,而实际应用中常常需要多分支选择,VerilogHDL语言中的case语句可以直接处理多分支选择。

case语句格式如下:

case(控制表达式)

分支项表达式1:语句1

分支项表达式2:语句2

分支项表达式m:语句m

default:语句n

endcase

case语句首先对控制表达式求值,然后依次对各分支项表达式求值并进行比较,遇到第一个与控制表达式值相匹配分支中的语句被执行。一个case结构中可以有多个分支,但这些值必须互斥。缺省分支覆盖所有没有被分支表达式覆盖的其他分支。

分支表达式和各分支项表达式不必都是常量表达式。在case语句中,x值和z值作为文字值进行比较。例如,下面case结构表示的是一个3:8译码器的程序。

moduleseg(SW,Q);

input[2:0]SW;//SW为3位输入端口

output[7:0]Q;//Q为3:8译码器的8个输出端口

reg[7:0]Q;

always@(SW)//SW中有状态改变时,执行case结构的语句

begin

case(SW)

3′b000:Q=8′b11111110;

3′b001:Q=8′b11111101;

3′b010:Q=8′b11111011;

3′b011:Q=8′b11110111;

3′b100:Q=8′b11101111;

3′b101:Q=8′b11011111;

3′b110:Q=8′b10111111;

3′b111:Q=8′b01111111;

endcase

end

endmodule3.4.3循环语句

VerilogHDL中有四类循环语句,它们是:

(1)forever循环。

(2)repeat循环。

(3)while循环。

(4)for循环。

1.forever循环语句

forever循环语句用于连续执行过程,其格式如下:

forever语句

forever循环语句常用于产生周期性的波形。它与always语句不同之处在于它不能独立写在程序中,而必须写在initial块中。例如:

initial

begin

clock=0;

#5forever

#10clock=~clock;

end

2.repeat循环语句

repeat循环语句是用于执行指定循环次数的过程语句,其格式如下:

repeat(表达式)语句

repeat循环语句中的表达式通常为常量表达式,表示循环的次数。如果循环计数表达式的值不确定,即为x或z时,那么循环次数按0处理。例如,下面是用repeat语句完成算式S=1+2+3+4+…+100的程序。

initial

begin

s=0;

i=1;

repeat(100)

begin

s=s+i;

i=i+1

end

end

3.while循环语句

while循环执行过程赋值语句直到指定的条件为假,其格式如下:

while(条件)语句

执行while时,先对条件进行判断,如果条件为真,那么执行该语句;如果条件为假,那么退出循环;如果条件在开始时就为假,那么就不执行该语句;如果条件为x或z,那么按0(假)处理。

例如,下面是用while语句完成对定义的256个存储单元初始化的程序段。

reg[7:0]memory[0:255];

initial

begin

reg[7:0]i;

i=0;

while(i<=255)

memory[i]=0;

i=i+1

end

4.for循环语句

for循环语句按照指定的次数重复执行过程赋值语句若干次,其格式如下:

for(初值表达式;条件;循环变量增值)语句

for循环的执行过程为

(1)计算初值表达式。

(2)进行条件判断,若条件为真,继续第(3)步;若条件为假,则转到第(5)步。

(3)执行过程语句,对循环变量进行增值。

(4)转回第(2)步继续执行。

(5)执行for循环下面的语句。例如,下面是用for语句、加法语句和移位语句实现x×y的程序。

reg[15:0]x,;

reg[31:0]s;

initial

begin

reg[3:0]i;

s=0;

for(i=0;i<=15;i=i+1)

if(y[i])s=s+(x<<i);

end3.4.4结构声明语句

VerilogHDL中任何过程模块都从属于四种结构说明语句:

initial说明语句、always说明语句、task说明语句和function说明语句。

1.initial说明语句

initial语句常用于对各变量的初始化。一个程序模块中可以有多个initial语句,所有initial语句在程序一开始时同时执行,并且只执行一次。

initial语句格式如下:

initial语句例如:

initial

begin

reset=1;

#3reset=0;

#5reset=1;

end

2.always说明语句

与initial语句一样,一个程序中可以有多个always语句,always语句也是在程序一开始时立即被执行的,不同的是always语句不断地重复运行。但always语句后跟的语句是否执行要看其敏感事件列表是否满足,若有条件满足,则运行一次语句。

always语句格式如下:

always@(敏感事件列表)语句

always语句后面是一个敏感事件列表,该敏感事件列表的作用是激活always语句执行的条件,敏感事件可以是电平触发,也可以是边沿触发。电平触发的always块常用于描述组合逻辑的行为,而边沿触发的always块常用于描述时序行为。

always语句后面的敏感事件可以是单个事件,也可以是多个事件,多个事件之间用or连接。在敏感事件列表中,如果敏感事件是电平信号,那么直接列出信号名;如果敏感事件是边沿信号,那么可分为上升沿和下降沿,上升沿触发的信号前加关键字posedge,下降沿触发的信号前加关键字negedge。例如,用clk的上升沿使count加1的程序为

reg[7:0]count

always@(posedgeclk)

begin

count=count+1b′1;

end

如果要用clk的下降沿使count加1,只需将程序中的敏感事件改为negedgeclk即可。例如,一个电平敏感型锁存器的程序如下:

modulelatch(enable,date,q)

inputenable;

input[7:0]data;

output[7:0]q;

reg[7:0]q;

always@(enableordata)

begin

if(enable)

q<=data;

end

3.task说明语句

1)任务定义语句

任务定义语句的格式如下:

task任务名;

端口声明语句;

类型声明语句;

语句

endtask

2)任务的调用

任务调用的格式如下:

任务名(端口名列表);

例如,在运算器中,经常用到加法运算,加法运算有8位加法、16位加法、32位加法等,16位加法和32位加法又可以用8位加法器实现。这样就可以定义一个任务实现8位加法运算,而16位加法和32位加法通过多次调用8位加法运算的任务来实现。

下面代码中,定义了一个任务adder8完成8位二进制数加法运算,通过在always块中两次调用任务adder8实现了16位数a和b的加法运算,相加结果存放在sum中,进位存放在cout中。

moduleadd16(a,b,cin,sum,cout);

input[15:0]a,b;

inputcin;

output[15:0]sum;

outputcout;

reg[15:0]sum;

regcout;

regc8;

always@(aorborcin)

begin

adder8(a[7:0],b[7:0],cin,sum[7:0],c8);

adder8(a[15:8],b[15:8],c8,sum[15:8],cout);

end

taskadder8;

input[7:0]ta,tb;

inputtcin;

output[7:0]tsum;

outputtcout;

begin

{tcout,tsum}=ta+tb+tcin;

end

endtask

endmodule使用任务时,需要注意以下几点:

(1)任务的定义和调用必须在同一个模块内。任务定义不能出现在任何一个过程块内部,任务的调用应在always块、initial块或另一个任务中。

(2)任务定义时,task语句后没有端口名列表,输入输出端口名是通过端口声明语句进行顺序声明的;一个任务也可以没有输入输出端口。

(3)当任务被调用时,任务被激活。如果一个任务有输入输出端口,调用时需列出端口名列表,其顺序和类型应与任务定义中完全一致。

(4)进行任务调用时,参数的传递是按值传递的,不能按址传递。

(5)一个任务可以调用别的任务或函数,可调用的任务和函数的个数不受限制。

4.function说明语句

function说明语句用来定义函数。函数类似高级语言中的函数,用来单独完成某项具体的操作。函数可以作为表达式中的一个操作数,也可被模块、任务或其他函数调用,函数调用时有一个返回值。

1)函数语句定义

function定义格式如下:

function〈返回值的类型或范围〉函数名

端口声明语句;

类型声明语句;

语句

endfunction

2)函数的调用

function函数的调用格式如下:

函数名(端口名列表);

函数与任务一样,也是用来完成一个独立的任务的,但函数与任务有以下不同点:

(1)函数只能有一个返回值,而任务却可以有多个或没有返回值。函数的返回值只是通过函数名返回的,而任务的返回值则是通过输出端口传递的。

(2)函数至少有一个输入变量,而任务可以没有或有多个任何类型的变量。

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

(4)函数不能调用任务,而任务能调用其他任务和函数。例如,下面程序是一个用function函数实现将两位十六进制数转换成对应的两个共阳极七段LED显示代码的例子。其中,SW用于输入两位十六进制数,十六进制数要在七段LED上进行显示,必须将其转换成其对应的七段显示代码,hex0和hex1就是分别用来输出的对应的七段共阴极LED显示代码。moduledisplay(SW,hex0,hex1);

input[7:0]SW;

output[6:0]hex0;

reg[6:0]hex0;

output[6:0]hex1;

reg[6:0]hex1;

always@(SW)

begin

hex0=seg_7(SW[3:0]);

hex1=seg_7(SW[7:4]);

end

function[6:0]seg_7;

input[3:0]num;

case(num)

4′h1:seg_7=7′b1111001;

4′h2:seg_7=7′b0100100;

4′h3:seg_7=7′b0110000;

4′h4:seg_7=7′b0011001;

4′h5:seg_7=7′b0010010;

4′h6:seg_7=7′b0000010;

4′h7:seg_7=7′b1111000;

4′h8:seg_7=7′b0000000; 4′h9:seg_7=7′b0011000;

4′ha:seg_7=7′b0001000;

4′hb:seg_7=7′b0000011;

4′hc:seg_7=7′b1000110;

4′hd:seg_7=7′b0100001;

4′he:seg_7=7′b0000110;

4′hf:seg_7=7′b0001110;

4′h0:seg_7=7′b1000000;

endcase

endfunction

endmodule3.4.5编译预处理语句

1.宏定义(′define和′undef)

′define指令是用一个标识符代替一个字符串,其定义格式如下:

′define宏名字符串

例如:

′defineWORDSIZE16

reg[′WORDSIZE-1:0]data1;//相当于reg[15:0]data1;对于宏的定义应注意以下几点:

(1)宏名可以用小写字母,也可以用大写字母,为了与变量名区别,建议使用大写字母。

(2)宏定义语句后不跟分号,如果加了分号,则连同分号一起进行置换。

(3)宏定义语句可以在模块内,也可以在模块外。

(4)在引用宏时,必须在宏名前加符号“′”。宏定义将在整个文件内起作用,若要取消宏前面定义的宏,则用′undef指令。

例如:

′defineBYTE8

wire[′BYTE-1:0]bus;//相当于wire[7:0]bus;

′undefBYTE//在′undef后,BYTE的宏定义不再有效

2.文件包含(′include)

′include语句用来实现文件的包含操作,它可以将一个源文件包含到本文件中。其语句格式为

′include″文件名″

例如:

′include″d:\eda\s1.v″

编译时,这一行由d:\eda\s1.v文件中的内容替代。

3.时间尺度(′timescale)

在VerilogHDL模型中,所有时延都用单位时间表述。使用′timescale预编译指令将时间单位与实际时间相关联。该指令用于定义时间单位和时间精度。

′timescale预编译指令格式为

′timescale时间单位/时间精度其中,时间单位用来定义模块中仿真时间的基准单位,时间精度用来声明模块仿真时间的精确程度,该参数用于对时间值进行取整操作。时间单位和时间精度由值1、10和100以及单位s、ms、μs、ns、ps和fs组成。例如:

′timescale1ns/100ps

表示时延单位为1ns,时延精度为100ps。′timescale编译指令在模块说明外部出现,并且影响后面所有的时延值。例如:

′timescale10ns/100ps

always#1.55clock=~clock;

′timescale语句表示模块中的时间值均为10ns的整数倍,延时时间的最小分辨率为十分之一纳秒(100ps),即延时时间可表示为带一位小数的实型数。这样,根据时间精度,1.55取整为1.6,那么,clock变反的时间间隔为16ns。在编译过程中,′timescale指令影响这一编译指令后面所有模块中的时延值,直至遇到另一个′timescale指令。当一个设计中的多个模块带有自身的′timescale编译指令时,则用最小的时间精度来决定仿真时间单位。例如:

′timescale1ns/100ps

moduleAndFunc(Z,A,B);

outputZ;

inputA,B;

and#5.22Al(Z,A,B);

endmodule

′timescale10ns/1ns

moduleTB;

regPutA;

initial

begin

PutA=0;

#5.21PutA=1;

#10.4PutA=0;

end

endmodule在这个例子中,每个模块都有自身的′timescale编译指令。在第一个模块中,5.22对应5.2ns;在第二个模块中,5.21对应52ns,10.4对应104ns。如果仿真模块TB,那么设计中的所有模块最小时间精度为100ps,因此,所有延迟将换算成精度为100ps,延迟52ns现在对应520*100ps,104对应1040*100ps。更重要的是,仿真使用100ps为时间精度。若仿真模块AndFunc,则由于模块TB不是模块AddFunc的子模块,因此模块TB中的′timescale程序指令将不再有效。

4.条件编译(′ifdef、′else、′endif)

一般情况下,源程序中的所有语句行都参加编译。但是有时希望其中一部分语句只在条件满足时才进行编译,条件不满足时不编译这些语句,或者编译另外一组语句,这就是条件编译。条件编译语句格式如下:

′ifdefCOMPUTER-PC

parameterWORD_SIZE=16

′else

parameterWORD_SIZE=32

′endif在实际的应用系统设计时,如果将所有功能用一个模块完成,会造成模块设计复杂,可读性差。为了便于设计,可以将一个大的系统分层次、分模块进行设计,

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论