Verilog语法速成(二)
13.6 常量:
整数型
• 基数格式
– [size] ‘baseformat value
– size:以位计的常数的位宽;
– baseformat代表进制:八进制:o或O、二进制:b或B、十进制:d或D、十六进制:h或H
– 其中十六进制数值a到f以及x和z不区分大小写
• 注意事项
– 为了增加数值的可读性,可以在数字之间 增加下划线 ,例如“8’b1001_0011”表示位宽为8位的二进制数10010011。下划线可以随意使用在整数或实数中,但是 不能作为首字符 。
– 在二进制表示中,x,z只表示相应位的 逻辑状态 ;在八进制表示中,一位x或z代表 3个二进制位 都处于x或者z状态;在十六进制表示中,一位x或z代表 4个二进制位 都处于x或者z状态。
– 当 没有说明位宽 [size]时,整数的位宽为 机器的字长 (至少为32位);当位宽比数值的实际二进制位少时, 高位部分会被舍去 ;当位宽比数值的实际二进制位多时,且最高位为0或1时, 则高位由0填充 ;当位宽比数值的实际二进制位多时,且 最高位为x或z时 ,则高位相应 由x或z填充 。
示例:
• 3’b101:位宽为三位的二进制数:二进制101,十进制数值为:5,合法
• 5’o37:位宽为5位的八进制数,虽然一个八进制数的位宽为3,但是八进制3转换为2进制为011,故可以省去0位,即合法,十进制数值为:31.
• 12’habc:位宽为12位的十六进制数,其中abc为三个十六进制数,每个十六进制数的位宽为4,故总和为12,合法,十进制数值为:2748
• -4d’10:非法,“ ‘ “符号位置错误
• 4’b1x0x:合法,其中从低位到高位的第一位和第三位为未知,故数值未知
• 12’h13x:合法,十二位位宽,十六进制,其中从低位到高位的第一位十六进制数为未知数,故数值未知
• 32’bz:合法,这是三十二位位宽,二进制数,其为高阻态。
• 23456:合法,默认为十进制,23456.
• ‘hc3:合法,默认为至少32位(机器的字长),其十进制值为:195
• 4’d-8:不合法,数值其中有非法字符
• 3’[]b101:不合法,b前有非法字符
• (3+2)’b11010:不合法,位宽限制不可有算术符号。
实数型(少用)
– 实数常量可以用十进制或科学表示法表示。
12
unsized decimal (zero-extended to 32 bits)
'H83a
unsized hexadecimal (zero- extended to 32 bits)
8'b1100_ 0001
8-bit binary
64'hff01
64-bit hexadecimal (zero- extended to 64 bits)
9'O17
9-bit octal
32'bz01x
Z-extended to 32 bits
3’b1010_ 1101
3-bit number, truncated to 3’b101
6.3
decimal notation
32e- 4
scientific notation for 0.0032
4.1E3
scientific notation for 4100
字符串型
Verilog中,字符串大多用于显示信息的命令中($display and $monitor)。
• 8位ASCII值表示的字符可看作是无符号整数
• 字符串要在一行中用双引号括起来,也就是不能跨行。
• 字符串中可以使用一些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”
如果字符串被用作Verilog HDL中表达式或者赋值语句中的操作数,则每个字符串(包括空格)被看做是8位的ASCII值序列,即一个字符对应8位的ASCII值。比如,为了储存字符串“hello world!”,就需要定义一个8*12位的变量:
reg[1:8*12] message;
initial
begin
message =“hello world!”;
end
转义符及格式符将在验证支持部分讨论
格式符
转义符
格式符%0d表示没有前导0的十进制数
13.7 参数型
• 在Verilog HDL中,参数是一个非常重要的数据类型,属于常量,在 仿真开始之前 就被赋值,在 仿真过程中保持不变 。采用参数定义方法可以提高程序的 可读性 和维护性。参数常用来定义延迟时间和变量的位宽。
• 参数类型的定义格式:
• parameter 参数名1=表达式1,参数名2=表达式2,………,参数名n=表达式n;
• 其中,表达式既可以是常数,也可以是表达式。参数定义完以后,程序中出现的所有的参数名都将被替换为相对应的表达式。
• 如:parameter LENGTH=32,WEIGHT=16;
• 用参数声明一个可变常量,常用于定义延时及宽度变量。
• 参数定义的语法:parameter <list_of_assignment>;
• 可一次定义多个参数, 用逗号隔开 。
• 在使用文字(literal)的地方都可以使用参数。
• 参数的定义是 局部 的,只在 当前模块 中有效。
• 参数定义可使用 以前定义的整数和实数参数 。
注意: 参数file不是string,而是一个整数,其值是所有字母的 扩展ASCII值 。若file=“AB”,则file值为8‘h4142。用法:
$fopen(file);
$display(“%s”, file);
13.8 变量型
Reg & Wire
1)Wire型: 线网表示逻辑单元的物理连接,可以对应为电路中的 物理信号连线 ,这种变量类型不能保持电荷(除trireg之外)。连线型变量必须要有 驱动源 ,一种是连接到一个们或者模块的 输出端 ,另一种是用assign 连续赋值语句对它进行赋值 。若没有驱动源,将保持高阻态z (除trireg之外)。
2)定义语法:
wire [msb:lsb] 变量名1,变量名2, ...,变量名n;
其中,msb和lsb定义了范围,它们之间用冒号分隔,并且均为常数表达式。如果没有定义范围,则缺省值为1位变量。下面是一些wire型变量定义的一些例子:
wire a,b; //定义了两个wire类型的变量
wire L; //定义了一个wire类型的变量
wire[7:0] databus; //定义了一组8位总线
wire[32:1]busA, busB, busC; //定义了三组32位总线
3)不同的wire类型
线网型数据类型
功能说明
wire,tri
标准连线(缺省为该类型)
wor,trior
多重驱动时,具有线或特性的连线型
trireg
具有电荷保持特性的连线型数据
tri1
上拉电阻
tri0
下拉电阻
supply0
电源线,用于对“地”建模,为低电平0
supply1
电源线,用于对电源线建模,为高电平1
wand,trand
多重驱动时,具有线与特性的连线型
最常见的是wire(连线)和tri(三态线)两种,它们的语法和语义一致。不同之处在于:wire型变量通常用来表示单个门驱动或连续赋值语句驱动的连线型数据。tri型数据变量则用来表示 多驱动器驱动的连线型数据,主要用于定义三态的线网 。
wor和tiror代表多重驱动时,具有 线或特性 的线网类型。
wand和tirand代表多重驱动时,具有 线与特性 的线网类型。
wand/triand
• tri0的特征是,若 无驱动源驱动 ,其值为0,用于 下拉电阻 建模。
• tri1的特征是,若 无驱动源驱动 ,其值为1,用于 上拉电阻 建模。
trio/ tri1
• supply0用于对“地”建模,即低电平0;
• supply1用于对电源建模,即高电平1;
• 如supply0表示Gnd. Supply1表示Vcc。
• trireg线网能存储数值(类似于寄存器型数据类型),并且用于 电容节点 的建模。当三态寄存器(trireg)的所有驱动源都处于高阻态(z)时,三态寄存器线网将保持作用在线网上的最后一个逻辑值。三态寄存器线网的缺省初始值为x。
• 一个trireg网络型数据用于模拟电荷存储。电荷量强度可以下面的关键字来控制: small、medium、;large 。默认的电荷强度为medium。一个trireg网络型数据能够模拟一个电荷存储节点,该节点的电荷量将随时间而逐渐衰减。对于一个trireg网络型数据,仿真时其电荷衰减时间应当制定为延迟时间。
4)关键点:
• 线网被合并到连接,但这些可以被优化掉
• 除了wire 外, 综合工具不支持 其他线网类型
• 甚至在建立隐式声明时,也要在每个模块的 顶部明确声明所有线网 。这样通过明确意图,增加了Verilog 代码的可读性和可维护性
• 只用supply0 和supply1 声明地和电源线网
1)reg型: 寄存器保存在initial always task 和function 内赋的值。
• reg是最常用的寄存器类型
• Reg型变量时最常见也是最重要的寄存器型数据类型,它是数据存储单元的抽象类型,其对应的硬件电路元件具有状态保持作用,能够存储数据,如触发器、锁存器等。reg型变量常用于 行为级描述 中,由过程赋值语句对其进行赋值。
• reg 型数据域wire型数据的区别在于,reg型数据类型 保持最后一次的赋值 ,而wire型数据需要有 持续的驱动 。一般情况下,reg型数据的默认初始值为不定值x,缺省时的 位宽为1 位。
• 2)定义语法 :
– reg [ Range] RegisterOrMemory,...;
– integer RegisterOrMemory,...;
– time RegisterOrMemory,...;
– real RegisterName,...;
• 规则
– 寄存器只能用过程赋值来赋值
– 在 给定的设备 中,integer 有最大大小,但它 至少 有32 位。time 寄存器的长度也有相似的保证,但它 至少 有64 位
– 类型是integer 或time 的寄存器一般像有相同数量位数的reg 一样运转。Integer 和time 的个别位和部分选择可以和reg 一样建立。但在表达式中,integer 的 值作为有符号数处理 ,而reg 或time的值作为 无符号数处理
– 在 同一时间只能访问(读或写) 存储器数组的一个 完整元素 。要 访问存储器数组一个元素的个别位 ,该元素的内容首先要被复制到一个适当的寄存器
– 寄存器这个术语也表示一个 硬件寄存器 (即触发器),名字是假设表示一个软件寄存器(即一个变量)。Verilog 寄存器可以并用于描述和合并组合逻辑、锁存器、触发器和连接
– 用reg 描述逻辑,integer 描述循环变量和计算,real 在系统模型中使用,time 保存测试装。 real、time 和integer 不可综合
• net声明
<net_type> [range] [ delay ] <net_name>[, net_name];
net_type: net类型
range: 矢量范围,以[MSB:LSB]格式
delay: 定义与net相关的延时
net_name: net名称,一次可定义多个net, 用逗号分开。
• 寄存器声明
<reg_type> [range] <reg_name>[, reg_name];
reg_type:寄存器类型
range: 矢量范围,以[MSB:LSB]格式。只对reg类型有效
reg_name :寄存器名称,一次可定义多个寄存器,用逗号分开
• 书写规范建议
– 对数值类型,按降序方式定义位宽
• 1)存储器型(memory): 本质上还是 寄存器型变量阵列 ,只是Verilog HDL语言中没有多维数组,所有就用reg型变量建立寄存器组来实现存储器的功能,也就是扩展的reg型数据地址范围。存储器型变量可以描述 RAM型 、 ROM型存储器 以及reg文件。数组中的每一个单元通过一个数组索引进行寻址。
• 存储器型变量的一般声明格式:
• reg<rangel1><name_of_register><range2>;
• 其中,range1和range2都是可选项,缺省时都为1.
• range1:表示存储器中寄存器的位宽,格式为[msb:lsb]
• range2:表示寄存器的个数,格式为[msb:lsb],即有msb-lsb+1个
• name_of_register:变量名称列表,一次可以定义多个名称,之间用逗号分开。
存储器类型举例:
• reg[7:0] mem1[255:0]; //定义了一个有256个8位寄存器的存储器mem1 地址范围是0到255.
• reg[15:0]mem2[127:0],reg1,reg2;//定义了一个具有128个16位寄存器的存储器mem2和2个16位的寄存器reg1和reg2
•
• 2)注意: memory型和reg型数据的差别。一个由n个1位寄存器构成的寄存器和一个n位寄存器的 意义是不同的 。
• reg[n-1:0]a; //表示一个n位的寄存器a
• reg mem1[n-1:0]; //表示一个由n个1位寄存器构成的存储器mem1.
• 一个n位的寄存器可以在一条赋值语句里进行赋值,而一个完整的存储器则不行。
• 如果想对存储器中的存储单元进行读写操作,则必须指定该单元在存储器中的地址。
• 如:mem1[2]=0;//给mem1存储器中的第三个存储单元赋值为0
13.9 信号分类:
信号可以分为端口信号和内部信号。出现在端口列表中的信号是端口信号,其它的信号为内部信号。
对于端口信号,输入端口 只能是net类型 。输出端口可以是 net类型 ,也可以是 register类型 。若输出端口在过程块中赋值则为register类型;若在过程块外赋值(包括实例化语句),则为net类型。
内部信号类型与输出端口相同,可以是net或register类型。判断方法也与输出端口相同。若在过程块中赋值,则为register类型;若在过程块外赋值,则为net类型。
若信号既需要在过程块中赋值,又需要在过程块外赋值。这种情况是有可能出现的,如决断信号。这时需要一个中间信号转换。
13.10 运算符:
• 1)常用的算术运算符
– 加法+
– 减法-
– 乘法*
– 除法/
– 求模%
• 应注意的问题
– 算术操作结果的位数长度
• 算术表达式 结果的长度由最长的操作数 决定
– 有符号数和无符号数
• 在设计中,请先按无符号数进行,无符号数的值一般存储在线网、reg(寄存器)变量及普通(没有符号标记s)的基数格式表示的整数型中,有符号数值一般存储在存储在整型变量、十进制形式的整数、有符号的reg(寄存器)变量及有符号的线网中
• 2)关系运算符
>(大于)
<(小于)
>=(不小于)
<=(不大于)
• 关系操作符的结果为真(1)或假(0)。如果操作数中 有一位为x或z ,那么 结果为x
• 3)相等关系运算符
==(逻辑相等)
===(全等于)
!=(逻辑不等)
!==(不全等)
• 相等关系运算符是对两个操作数进行比较,比较的结果有三种:真1,假0,和 不定值(x).
• Verilog HD语言中四种相等关系运算符:等于(==)、不等于(!=)、全等于(===)。
• 这四种运算符都是双目运算符,要求有两个操作数。他们的优先级是相同的。
• “==”和“!=”称为逻辑等式运算符,其结果有两个操作数的值决定,由于操作数中某些位可 能是不定值x和高阻态值z,所以结果可能是不定值x。
• “===”和“!==”运算符则不同,他是对操作数按位比较,两个操作数必须完全一样,其结果才是1,否则都是0.但是,若两个操作数 对应位出现不定值x和高阻值z,则可以认为是相同的。
• “===”和“!==”运算符常用于case表达式的判别,所以又称为“case”等式运算符。
• 4)逻辑运算符
• 逻辑与运算符“&&”
• 逻辑或运算符“||”
• 逻辑非运算符(!)
其中逻辑与和逻辑或,是双目运算符。
逻辑非为单目运算符。
在逻辑运算符的操作中,如果操作数是1位的,那么1就代表逻辑真,0就代表逻辑假。
如果是操作数是由多位的,则当操作数每一位都是0时才是逻辑0值,只要有一位1,这个操作数就是逻辑1值。
例如:寄存器变量a、b的初值分别是4’b1110和4’b0000,则
!a=0, !b=1; a&&b=0; a||b=1;
需要注意的是,若操作数中 存在不定态x ,则逻辑运算的结果 也是不定态x 。
• 5)数字逻辑电路中,信号与信号之间的运算称之位运算符。
Verilog HDL提供了一下五种类型的位运算符:
• 按位取反(~) (相当于非门运算)
• 按位与(&)(相当于与门运算)
• 按位或(|)(相当于或门运算)
• 按位异或(^)(相当于异或门运算)
• 按位同或(^~)(相当于同或门运算)
• 位逻辑运算对其自变量的每一位进行操作。
6)归约操作符
• 归纳运算符按位进行逻辑运算,属于 单目运算符 。由于这一类运算符操作的结果产生1位逻辑值,因而被形象地称为 缩位运算符 。
• 在Verilog HDL中,缩位运算符包括&(与)、|(或)、^(异或)以及相应的非操作~&、 ~|、 ~^、 ^~ .归纳运算符的操作数只有一个。这里需注意和位运算进行区别。
• 举例:a=4’b1101,则 &a= 1&1&0&1 =0
7)移位操作符
>> 逻辑右移
<< 逻辑左移
移位操作符对其左边的操作数进行向左或向右的位移位操作。
第二个操作数(移位位数)是无符号数
若第二个操作数是x或z则结果为x
<< 将左边的操作数左移右边操作数指定的位数
>> 将左边的操作数右移右边操作数指定的位数
在赋值语句中,如果右边(RHS)的结果:
位宽大于左边,则把 最高位截去
位宽小于左边,则 零扩展
8)条件运算符
• 语法结构:cond_expr?expr1:expr2
• 如果cond_expr为真,选择expr1;如果cond_expr为假,选择expr2。如果cond_expr为x或z,结果将是按以下逻辑expr1和expr2按位操作的值:0与0得0,1与1得1,其余情况为x。
9)连接运算符
• 语法定义:
– {expr1,expr2,…,exprN}
• 例如:
wire [7:0] Dbus;
assign Dbus[7:4]={Dbus[0], Dbus[1], Dbus[2], Dbus[3]};
assign Dbus={Dbus[3:0],Dbus[7:4]};
10)复制运算符
{Const_int{Var}} 复制一个变量或在{ }中的值
前两个{ 符号之间的正整数Const_in指定 复制次数 。
示例:
module replicate ();
reg [3: 0] rega;
reg [1: 0] regb, regc;
reg [7: 0] bus;
initial begin
rega = 4’b1001;
regb = 2'b11;
regc = 2'b00;
end
initial fork
#10 bus <= {4{regb}}; // bus = 11111111
// regb is replicated 4 times.
#20 bus <= { {2{regb}}, {2{regc}} };
// bus = 11110000. regc and regb are each
// replicated, and the resulting vectors
// are concatenated together
#30 bus <= { {4{rega[1]}}, rega };
// bus = 00001001. rega is sign-extended
#40 $finish;
整理:SANGHUSUN
四川大学物理学院ASIC设计实验室
E-mail:SANGHUSUN @foxmail.com
2020.07.31