微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > VHDL:中文版Verilog HDL简明教程:第3章 Verilog语言要素(续)

VHDL:中文版Verilog HDL简明教程:第3章 Verilog语言要素(续)

时间:06-06 来源:互联网 点击:
反斜线 ( ) 用于对确定的特殊字符转义。
n 换行符
t 制表符
\ 字符本身
字符
206 八进制数206对应的字符

3.7 数据类型

  Verilog HDL 有两大类数据类型。
  1) 线网类型。net type 表示Verilog结构化元件间的物理连线。它的值由驱动元件的值决定,例如连续赋值或门的输出。如果没有驱动元件连接到线网,线网的缺省值为z。
  2) 寄存器类型。register type表示一个抽象的数据存储单元,它只能在always语句和initial语句中被赋值,并且它的值从一个赋值到另一个赋值被保存下来。寄存器类型的变量具有x 的缺省值。

3.7.1 线网类型

  线网数据类型包含下述不同种类的线网子类型。

* wire
* tri
* wor
* trior
* wand
* triand
* trireg
* tri1
* tri0
* supply0
* supply1

简单的线网类型说明语法为:

net_kind [msb:lsb] net1, net2, . . . , netN;

net_kind 是上述线网类型的一种。msb和lsb 是用于定义线网范围的常量表达式;范围定义是可选的;如果没有定义范围,缺省的线网类型为1位。下面是线网类型说明实例。

wire Rdy, Start; //2个1位的连线。
wand [2:0] Addr; //Addr是3位线与。

当一个线网有多个驱动器时,即对一个线网有多个赋值时,不同的线网产生不同的行为。例如,

wor Rde;
. . .
assign Rde = Blt Wyl;
. . .
assign Rde = Kbl | Kip;

本例中,Rde有两个驱动源,分别来自于两个连续赋值语句。由于它是线或线网,Rde的有效值由使用驱动源的值(右边表达式的值)的线或(wor)表(参见后面线或网的有关章节)决定。

1. wire和tri线网
  用于连接单元的连线是最常见的线网类型。连线与三态线(tri)网语法和语义一致;三态线可以用于描述多个驱动源驱动同一根线的线网类型;并且没有其他特殊的意义。

wire Reset;
wire [3:2] Cla, Pla, Sla;
tri [ MSB-1 : LSB +1] Art;
如果多个驱动源驱动一个连线(或三态线网),线网的有效值由下表决定。
wire (或 tri) 0 1 x z
0 0 x x 0
1 x 1 x 1
x x x x x
z 0 1 x z
下面是一个具体实例:

assign Cla = Pla Sla;
. . .
assign Cla = Pla ^ Sla;

在这个实例中,Cla有两个驱动源。两个驱动源的值(右侧表达式的值)用于在上表中索引,以便决定Cla的有效值。由于Cla是一个向量,每位的计算是相关的。例如,如果第一个右侧表达式的值为01x, 并且第二个右测表达式的值为11z,那么Cla 的有效值是x1x (第一位0和1在表中索引到x, 第二位1和1在表中索引到1,第三位x 和z在表中索引到x)。

2. wor和trior线网
  线或指如果某个驱动源为1,那么线网的值也为1。线或和三态线或(trior)在语法和功能上是一致的。

wor [MSB:LSB] Art;
trior [MAX-1: MIN-1] Rdx, Sdx, Bdx;
如果多个驱动源驱动这类网,网的有效值由下表决定。
wor (或 trior) 0 1 x z
0 0 1 x 0
1 1 1 1 1
x x 1 x x
z 0 1 x z

3. wand和triand线网
  线与(wand)网指如果某个驱动源为0,那么线网的值为0。线与和三态线与(triand)网在语法和功能上是一致的。

wand [-7 : 0] Dbus;
triand Reset, Clk;
如果这类线网存在多个驱动源,线网的有效值由下表决定。
wand (或 triand) 0 1 x z
0 0 0 0 0
1 0 1 x 1
x 0 x x x
z 0 1 x z

4. trireg线网
  此线网存储数值(类似于寄存器),并且用于电容节点的建模。当三态寄存器(trireg)的所有驱动源都处于高阻态,也就是说,值为z时,三态寄存器线网保存作用在线网上的最后一个值。此外,三态寄存器线网的缺省初始值为x。

trireg [1:8] Dbus, Abus;

5. tri0和tri1线网
  这类线网可用于线逻辑的建模,即线网有多于一个驱动源。tri0(tri1)线网的特征是,若无驱动源驱动,它的值为0(tri1的值为1)。

tri0 [-3:3] GndBus;
tri1 [0:-5] OtBus, ItBus;
下表显示在多个驱动源情况下tri0或tri1网的有效值。

tri0 (tri1) 0 1 x z
0 0 x x 0
1 x 1 x 1
x x x x x
z 0 1 x 0(1)

6. supply0和supply1线网
  supply0用于对“地”建模,即低电平0;supply1网用于对电源建模,即高电平1;例如:

supply0 Gnd, ClkGnd;
supply1 [2:0] Vcc;

3.7.2 未说明的线网

  在Verilog HDL中,有可能不必声明某种线网类型。在这样的情况下,缺省线网类型为1位线网。
可以使用`default_nettype编译器指令改变这一隐式线网说明方式。使用方法如下:

`default_nettype net_kind

例如,带有下列编译器指令:

`default_nettype wand

任何未被说明的网缺省为1位线与网。

3.7.3 向量和标量线网

  在定义向量线网时可选用关键词scalared 或vectored。如果一个线网定义时使用了关键词vectored, 那么就不允许位选择和部分选择该线网。换句话说,必须对线网整体赋值(位选择和部分选择在下一章中讲解)。例如:

wire vectored [3:1] Grb;
//不允许位选择Grb[2]和部分选择Grb [3:2]
wor scalared [4:0] Best;
//与wor [4:0] Best相同,允许位选择Best [2]和部分选择Best [3:1]。
如果没有定义关键词,缺省值为标量。

3.7.4 寄存器类型

  有5种不同的寄存器类型。

* reg
* integer
* time
* real
* realtime

1. reg寄存器类型
  寄存器数据类型reg是最常见的数据类型。reg类型使用保留字reg加以说明,形式如下:

reg [ msb: lsb] reg1, reg2, . . . regN;
msb和lsb 定义了范围,并且均为常数值表达式。范围定义是可选的;如果没有定义范围,缺省值为1位寄存器。例如:
reg [3:0] Sat; //Sat为4 位寄存器。
reg Cnt; //1位寄存器。
reg [1:32] Kisp, Pisp, Lisp;
寄存器可以取任意长度。寄存器中的值通常被解释为无符号数, 例如:
reg [1:4] Comb;
. . .
Comb = -2; //Comb 的值为14(1110),1110是2的补码。
Comb = 5; //Comb的值为15(0101)。

2. 存储器
  存储器是一个寄存器数组。存储器使用如下方式说明:

reg [ msb: 1sb] memory1 [ upper1: lower1],
memory2 [upper2: lower2],. . . ;
例如:
reg [0:3 ] MyMem [0:63]
//MyMem为64个4位寄存器的数组。
reg Bog [1:5]
//Bog为5个1位寄存器的数组。
MyMem和Bog都是存储器。数组的维数不能大于2。注意存储器属于寄存器数组类型。线网数据类型没有相应的存储器类型。
  单个寄存器说明既能够用于说明寄存器类型,也可以用于说明存储器类型。

parameter ADDR_SIZE = 16 , WORD_SIZE = 8;
reg [1: WORD_SIZE] RamPar [ ADDR_SIZE-1 : 0], DataReg;

RamPar是存储器,是16个8位寄存器数组,而DataReg是8位寄存器。
  在赋值语句中需要注意如下区别:存储器赋值不能在一条赋值语句中完成,但是寄存器可以。因此在存储器被赋值时,需要定义一个索引。下例说明它们之间的不同。

reg [1:5] Dig; //Dig为5位寄存器。
. . .
Dig = 5'b11011;

  上述赋值都是正确的, 但下述赋值不正确:

reg BOg[1:5]; //Bog为5个1位寄存器的存储器。
. . .
Bog = 5'b11011;

  有一种存储器赋值的方法是分别对存储器中的每个字赋值。例如:

reg [0:3] Xrom [1:4]
. . .
Xrom[1] = 4'hA;
Xrom[2] = 4'h8;
Xrom[3] = 4'hF;
Xrom[4] = 4'h2;

  为存储器赋值的另一种方法是使用系统任务:
  1) $readmemb (加载二进制值)
  2) $readmemb (加载十六进制值)
  这些系统任务从指定的文本文件中读取数据并加载到存储器。文本文件必须包含相应的二进制或者十六进制数。例如:

reg [1:4] RomB [7:1] ;
$ readmemb (ram.patt, RomB);

Romb是存储器。文件“ram.patt”必须包含二进制值。文件也可以包含空白空间和注释。下面是文件中可能内容的实例。

1101
1110
1000
0111
0000
1001
0011

  系统任务$readmemb促使从索引7即Romb最左边的字索引,开始读取值。如果只加载存储器的一部分,值域可以在$readmemb方法中显式定义。例如:
$readmemb (ram.patt, RomB, 5, 3);

在这种情况下只有Romb[5],Romb[4]和Romb[3]这些字从文件头开始被读取。被读取的值为1101、1100和1000。
文件可以包含显式的地址形式。

@hex_address value
如下实例:
@5 11001
@2 11010

在这种情况下,值被读入存储器指定的地址。
  当只定义开始值时,连续读取直至到达存储器右端索引边界。例如:

$readmemb (rom.patt, RomB, 6);
//从地址6开始,并且持续到1。
$readmemb ( rom.patt, RomB, 6, 4);
//从地址6读到地址4。

3. Integer寄存器类型
  整数寄存器包含整数值。整数寄存器可以作为普通寄存器使用,典型应用为高层次行为建模。使用整数型说明形式如下:

integer integer1, integer2,. . . intergerN [msb:1sb] ;

msb和lsb是定义整数数组界限的常量表达式,数组界限的定义是可选的。注意容许无位界限的情况。一个整数最少容纳32位。但是具体实现可提供更多的位。下面是整数说明的实例。

integer A, B, C; //三个整数型寄存器。
integer Hist [3:6]; //一组四个寄存器。

一个整数型寄存器可存储有符号数,并且算术操作符提供2的补码运算结果。
整数不能作为位向量访问。例如,对于上面的整数B的说明,B[6]和B[20:10]是非法的。一种截取位值的方法是将整数赋值给一般的reg类型变量,然后从中选取相应的位,如下所示:

reg [31:0] Breg;
integer Bint;
. . .
//Bint[6]和Bint[20:10]是不允许的。
. . .
Breg = Bint;
/*现在,Breg[6]和Breg[20:10]是允许的,并且从整数Bint获取相应的位值。*/

上例说明了如何通过简单的赋值将整数转换为位向量。类型转换自动完成,不必使用特定的函数。从位向量到整数的转换也可以通过赋值完成。例如:

integer J;
reg [3:0] Bcq;

J = 6; //J的值为32'b0000...00110。
Bcq = J; // Bcq的值为4'b0110。

Bcq = 4'b0101.
J = Bcq; //J的值为32'b0000...00101。

J = -6; //J 的值为 32'b1111...11010。
Bcq = J; //Bcq的值为4'b1010。

注意赋值总是从最右端的位向最左边的位进行;任何多余的位被截断。如果你能够回忆起整数是作为2的补码位向量表示的,就很容易理解类型转换。

4. time类型
  time类型的寄存器用于存储和处理时间。time类型的寄存器使用下述方式加以说明。

time time_id1, time_id2, . . . ,time_idN [ msb:1sb];

msb和lsb是表明范围界限的常量表达式。如果未定义界限,每个标识符存储一个至少64位的时间值。时间类型的寄存器只存储无符号数。例如:

time Events [0:31]; //时间值数组。
time CurrTime; //CurrTime 存储一个时间值。

5. real和realtime类型
  实数寄存器(或实数时间寄存器)使用如下方式说明:

//实数说明:
real real_reg1, real_reg2, . . ., real_regN;
//实数时间说明:
realtime realtime_reg1, realtime_reg2, . . . ,realtime_regN;
realtime与real类型完全相同。例如:
real Swing, Top;
realtime CurrTime;
real说明的变量的缺省值为0。不允许对real声明值域、位界限或字节界限。

  当将值x和z赋予real类型寄存器时,这些值作0处理。

real RamCnt;
. . .
RamCnt = 'b01x1Z;
RamCnt在赋值后的值为'b01010。

3.8 参数
  参数是一个常量。参数经常用于定义时延和变量的宽度。使用参数说明的参数只被赋值一次。参数说明形式如下:

parameter param1 = const_expr1, param2 = const_expr2, . . . ,
paramN = const_exprN;

下面为具体实例:

parameter LINELENGTH = 132, ALL_X_S = 16'bx;
parameter BIT = 1, BYTE = 8, PI = 3.14;
parameter STROBE_DELAY = ( BYTE + BIT) / 2;
parameter TQ_FILE = /home/bhasker/TEST/add.tq;

参数值也可以在编译时被改变。改变参数值可以使用参数定义语句或通过在模块初始化语句中定义参数值(这两种机制将在第9章中详细讲解)。
习题

1. 下列标识符哪些合法,哪些非法?
COunT, 1_2 Many, **1, Real?, wait, Initial
2. 系统任务和系统函数的第一个字符标识符是什么?
3. 举例说明文本替换编译指令?
4. 在Verilog HDL中是否有布尔类型?
5. 下列表达式的位模式是什么?
7'o44, 'Bx0, 5'bx110, 'hA0, 10'd2, 'hzF
6. 赋值后存储在Qpr中的位模式是什么?
reg [1:8*2] Qpr;
. . .
Qpr = ME ;
7. 如果线网类型变量说明后未赋值,其缺省值为多少?
8. Verilog HDL 允许没有显式说明的线网类型。如果是这样,怎样决定线网类型?
9. 下面的说明错在哪里?
integer [0:3] Ripple;
10. 编写一个系统任务从数据文件“memA.data”中加载32×64字存储器。
11. 写出在编译时覆盖参数值的两种方法。

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top