FPGA/CPLD应用技术(Verilog语言版)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.2 数据类型及常量变量

知识分部网络

任何程序或代码运行处理的对象都是数据。用硬件描述语言设计的电路模块代码所处理的数据就是实际电路中的物理连线、存储单元中的逻辑值。这些逻辑值存储在变量中,逻辑信号在连线上的传送方式用变量的数据类型来说明。

小知识

Verilog HDL经常用以下4种逻辑值状态来表示电连接线上的逻辑信号值。

(1)0:表示低电平、逻辑0或逻辑“非”;

(2)1:表示高电平、逻辑1或逻辑“真”;

(3)x或X:不确定或未知的逻辑状态;

(4)z或Z:高阻态;

上面的x和z都不区分大小写。

2.2.1 标识符

Verilog HDL中的标识符用于表示电路系统中的模块、寄存器、输入/输出端口、连线等物理对象的名字。标识符可以是任意一组字母、数字以及符号“$”和下画线“_”的组合,但首字符必须是字母或者下画线。

以下是几个合法的标识符:

reg_out、count、P0、F、ABC$、_M1_d1。

而下面几个标识符则是不合法的:8_data、avr%、*out。

必须注意标识符是区分大小写的,例如,count与COUNT是两个不同的标识符。

标识符还可以是以符号“\”开头,以空白符结尾的任何字符组成的转义标识符,例如,“\8_data”定义了一个转义标识符,但反斜线和结束空白符并不是转义标识符的一部分,再如,标识符“\reg_out”与标识符“reg_out”是一样的。

小资料

在Verilog HDL语言内部已经使用的标识符称为关键字或保留字,这些保留字用户不能随便使用。例如,always、input、output、nand、not、or、module、begin、end等内部保留字。

需注意的是:所有关键字全部采用小写字母组成。例如,input是一个内部保留字,而INPUT则是用户定义的标识符,两者表示不同的意义。

同时,必须注意内部保留字与转义标识符并不完全相同。例如,转义标识符\initial与保留字initial并不同,具有不同的意义。

2.2.2 常量

常量在程序运行过程中不会改变其数值,常量的取值类型一般是整数型数据、实数型数据和字符串数据。

1.整数型数据

整数型数据可以用二进制、十进制、八进制和十六进制这四种形式来表示。Verilog中的整数按如下方式书写:

+/-<位宽>′ <进制符号><数字>

上述的“位宽”表示整数以二进制形式存在时的位数,表示进制的符号有4种:b或B表示二进制;o或O表示八进制;d或D表示十进制(通常十进制整数可以不标示进制符号);h或H表示十六进制。

以下是一些合法的整数表示方式:

8′b01010101     //位宽为8位的二进制数01010101
4′hE            //位宽为4位的十六进制数E,十六进制数中的a到f值不区分大小写
5′D25           //位宽为5位的十进制数25
6′o70           //位宽为6位的八进制数70
8′Hz            //位宽为8位的十六进制数z,即zzzzzzzz
8′B1x_000001    //可以使用下画线来提高程序的可读性,下画线本身没有任何意义
4 ′h 3e         //可以在位宽与字符之间、进制与数值之间出现空格,但在′
                 //和进制之间、数值间不能出现空格

下面是一些非法的书写整数的例子:

3′h-3E             //负数符号“-”必须放在最左边,正数符号“+”可以省略不写
(4+4)′O36          //位宽不能为表达式
8 ′b11110000       //在′和进制之间不能出现空格

小知识

在书写和使用整数时要注意以下一些问题:

(1)书写较长的数值使用下画线,可以提高可读性。例如,16′b1100_0110_0000_1010。

(2)x或z表示的宽度取决于所用的进制,即在二进制中代表1位x或z,在八进制中代表3位x或z,在十六进制中代表4位x或z。例如:

8′h1x        //等价于 8′b0001xxxx
4′bz         //等价于 4′bzzzz

(3)如果没有定义一个整数的位宽,则宽度为相应值中定义的位数。例如:

′hD2      //8位十六进制数
′o17      //6位八进制数

(4)如果定义的位宽比实际的位数长,通常在数的左边填“0”补位。如果数的最左边一位为x或z,就相应地用x或z在左边补位。例如:

8′b11      //定义的位宽为8,比实际位数2大,左边补0,数值为:00000011
8′bx110    //数的最左边为x,左边补x后数值为:xxxxx110

(5)如果定义的位宽比实际的位数小,那么将最左边的位舍掉。例如:

4′b1111_0101   //定义的位宽为4小于实际位数8,最左边4位被截断,数值为:
                //4′b0101

整数是可以带符号(正、负号)的,并且正(+)、负(-)号应写在最左边。负数通常用二进制补码的形式来表示。

2.实数型数据

实数型数据可以用十进制方式表示,如2.55、4.23、10.0等,但不能省略小数点后面的数字,例如,实数10.0不能写成:10.。

实数型数据还可以用科学记数法表示,例如,十进制数10000.0用科学记数法表示是1.0E4;而9.32e2就表示十进制数932。

3.字符串数据

在Verilog中字符串是一个用双引号引出的字符序列,如“Hello!Welcome.”。

小提示

字符串数据不能分成多行书写。

4.parameter型

在Verilog HDL中常用关键字parameter来定义符号常量,即用parameter来定义一个标识符表示常量,其说明格式如下:

parameter  参数名1=表达式,参数名2=表达式,……,参数名n=表达式;

例如:

parameter width=8;
parameter e=25,f=27;
parameter delay_time=100;

parameter是定义参数型常量的关键字,在其后跟着一个用逗号分隔开的赋值语句表。在每一个赋值语句的右边必须是一个常数表达式,该表达式只能包括数字或先前已定义过的参数,例如:

parameter n=100,m=80;
parameter average=(n+m)/2;

代码中使用符号常量,可以增强程序的可读性和可维护性。由parameter定义的参数型常量在模块中一般表示延迟时间、信号线宽度等。例如,下面的2输入与非门的测试模块nand2_test.tst中,定义了参数型常量delay1和delay2来分别表示延迟时间100ns和200ns。

//nand2_test.tst
timescale  1ns/1ns
module nand2_test;
    reg  a,b;
    wire f;
    parameter delay1=100,delay2=200;   //定义参数型常量delay1和delay2
    nand2_ex_1  U1  (a,b,f);
    initial begin
       a=1′b0;  b=1′b0;
       #delay1 a=1′b1;                //经过100ns的延迟后a=1,b没变。
       #delay1 a=1′b0;  b=1′b1;      //经过第二个100ns的延迟后a=0,b=1
       #delay1 a=1′b1;                //经过第三个100ns的延迟后a=1,而b仍为0
       #delay2 $finish;               //再经过200ns的延迟后结束仿真
    end
endmodule

2.2.3 变量及其数据类型

变量是指在程序执行过程中其值可以变化的量。Verilog HDL中变量的数据类型有很多种,这里只介绍最常用的线网wire型、寄存器reg型、寄存器memory型。

1.wire线网型

wire线网型是最常用的数据类型,它相当于组合逻辑电路中的各种连接线,其特点是输出的值紧随输入值的变化而变化,不能暂存。

Verilog HDL模块中的输入/输出信号类型默认定义为wire型。模块中引用实例元件的输出信号变量以及用“assign”语句赋值的变量,一般都定义为wire型。例如,在2选1数据选择器模块mux2_1_ex1.v中,输入/输出端口说明语句:“input P0,P1,S;”和“output F”将输入/输出信号P0、P1、S均默认定义为wire类型变量,而门级原语的引用实例U1、U2和U3的输出端变量not_S、andcntrl1和andcntrl2要用语句:“wire not_S,andcntrl1,andcntrl2;”来说明为wire类型;在模块mux2_1_ex2.v中用“assign”语句赋值的输出端口变量F则由端口说明语句默认定义为wire类型。

小经验

在用Verilog进行系统开发时,为了避免出现实际位宽与期望位宽不一致的情况,所有的wire信号必须显性定义。

定义一根单信号连线为wire型变量的格式如下:

wire 信号名1,信号名2,……;

上述各个电路模块例子中所定义的wire型变量都表示一根连线。但在实际电路系统中,经常会遇到总线,如地址总线、数据总线等,它们具有多位数据线。用Verilog来描述n位总线信号为wire型变量的格式如下:

wire[n-1:0] 信号名1,信号名2,……;

例如:

wire[7:0] data;        //说明一个8位数据总线data为wire型
wire[31:0] adder;      //说明一个32位地址总线adder为wire型

2.reg寄存器型

reg类型定义的是一种能暂存数据的变量,定义一个reg型信号变量的格式与定义wire型变量的格式类似,如下所示:

reg 信号名1,信号名2,……;         //说明信号1,信号2,…为1位reg型
reg[n-1:0] 信号名1,信号名2,……;//说明信号1,信号2,…为n位reg型

例如:

reg q;
reg[7:0] a,b;

reg型变量在定义时默认的初始值为不定值x,在设计时要求给寄存器变量赋予明确的值。如果寄存器变量没有得到新的赋值,它将一直保持原有的值不变。

reg型变量与wire型变量不同,除了可以与wire型变量一样表示组合逻辑电路中的连接线,reg型变量还可以在时序电路中对应具有状态保持作用的电路元件,如触发器、寄存器等。

小提示

用reg数据类型定义的信号必须放在过程块(如always、initial)中通过过程赋值语句赋值。

3.memory寄存器型

Verilog HDL可通过reg变量来建立数组,用来表示一组存储器,称为memory寄存器。定义在Verilog’ IEEE 2001标准中,memory寄存器型变量的格式如下:

reg[n-1:0] 存储器名[m-1:0];

这里n代表每个存储单元的大小,即该存储单元是一个n位的寄存器。而m为地址空间范围,定义了该存储器中有m个这样的寄存器(m个n位寄存器)。例如:

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

上例定义了一个存储器,该存储器有256个8位的寄存器,名字叫memory1。该存储器的地址范围是0~255。

如果要对memory1中的存储单元进行读写操作,必须指定该单元在存储器的地址。例如下面的语句:

memory1[10]=0;  //给memory1中第10个存储单元赋值为零
memory1[2]=16;    //给memory1中第2个存储单元赋值为16