Verilog HDL基础
目录
数据类型
变量类型
整型变量
存储器
标量与矢量
运算符与操作符
逻辑运算符
位操作符
关系运算符
等式运算符
条件操作符
移位操作符
缩位运算符
拼接操作符
数据类型
数据类型(Data Type)用于定义电路中的物理连线和具有数据存储功能的赋值对象,分为线网和变量两大类。
线网(nets)类型用于描述硬件电路中的物理连线。
线网定义的语法格式为: 线网子类型名 [msb:lsb] 线网名1,线网名2, . . . , 线网名n;
wire和tri是两种最常用的线网子类型。
(1) wire用于定义信号连线,描述单个驱动源驱动的线网。
(2) tri用于定义三态总线,描述多个驱动源驱动的线网。
模块的双向口inout通常需要用三态逻辑来驱动。描述三态驱动电路时,inout端口通常由连续赋值语句驱动,并且在一定条件可以被赋值为高阻状态。
wire [3:1] a, b;
tri [3:1] y;
assign y = a & b;
注意:线网表示硬件电路中的信号线或者总线,不具有数据存储功能,所以没有驱动源驱动的线网默认取值为x。
变量类型
变量(variables)用于定义具有数据存储作用的赋值对象,有寄存器变量、整型变量、实数变量、时间变量和实时间变量五种子类型。
reg用于定义称为寄存器(register)变量。寄存器变量在某种触发机制的作用下分配了一个值后,在分配下一个值之前将一直保留原有的值。
寄存器变量定义的语法格式为: reg [ msb: lsb] 变量名1, 变量名2, . . . 变量名n;
reg [7:0] q; // 定义q为8 位寄存器变量
reg tmp; // 定义tmp为 1位寄存器变量
reg [15:0] reg_A, reg_B,reg_C; // 定义16位寄存器变量
reg [3:0] tmp;
......
tmp = -2; // 位宽为4时tmp的值为1110(-2的补码),按无符号数14处理。
tmp = -1; // 位宽为4时tmp的值为1111(-1的补码),按无符号数15处理。
tmp = 5; // tmp的值为0101。
所有在过程语句(always或initial)中被赋值的对象都必须定义为寄存器类型。这是因为:当过程语句的条件满足时,过程语句中的被赋值对象才能更新,条件不满足将保持原有的值,因此过程语句中被赋值的对象应具有数据存储功能。
reg signed [3:0] tmp;
......
tmp = -2; // 位宽为4时tmp的值为1110(-2的补码)。
tmp = -1; // 位宽为4时tmp的值为1111(-1的补码)。
tmp = 5; // tmp的值为0101。
整型变量
integer用于定义整型变量,其语法格式为:
integer 变量1[msb:1sb], 变量2[msb:1sb],. . . 变量n [msb:1sb];
其中msb和lsb为定义整型变量位宽的常量或者常量表达式,缺省时默认为32位。
integer intA, intB, intC; // 定义intA,intB,intC为32位整数变量。
integer Stat [3:0]; // 定义Stat为4位整型变量。
整型变量用于存储有符号数,具体数值以二进制补码的形式表示。
例如: interger i; // 定义i为整型变量 ...
i=-6; // i值为32'b1111...11010 整型变量未被赋值时,默认初值为0。
实数变量 real用于定义实数变量,变量值以十进制数或者科学计数法表示。
实数变量定义的语法格式为: real 变量名1, 变量名2,. . . 变量名n;
real j; // 定义实数变量j
...
j = 1.8e-1; // j值为0.18
integer i;
real PI;
PI = 3.1415926;
i = PI;
存储器
存储器(memory)是由寄存器变量构成的数组。
存储器定义的语法格式为: reg [n-1:0] 存储器名 [m-1:0]; 其中[n-1:0]表示每个存储单元的位宽为n,而[m-1:0]共有m个存储单元。
注意:存储器和寄存器变量的定义和用法不同。 n个1位变量构成的存储器不同于一个n位寄存器变量。
reg [n-1:0] cnt; // 定义寄存器变量cnt,位宽为n
reg fpga_lut [n-1:0]; // 定义存储器fpga_lut,// 共有n个存储单元,每个单元存储1位数
标量与矢量
对矢量进行定义时,位宽范围由括在中括号内的一对整数、整数表达式或参数指定,中间用冒号隔开。
parameter WIDTH=16;
reg [7:0] Qtmp ; // 8位寄存器变量
wire [WIDTH-1:0] bus16b ; // 16位线网
可按位或者部分位赋值的矢量称为标量类矢量,用关键词scalared表示,相当于多个一位标量的集合。不能按位或部分位赋值的矢量称为矢量类矢量,用关键词vectored表示。
reg scalared [7:0] Qtmp; // Qtmp被定义成标量类变量
wire vectored [15:0] bus16b; // bus16被定义成矢量类线网
运算符与操作符
算术运算符(Arithmetic Operators)用于数值运算,共有+、-、*、/、和%五种运算符。
需要注意的是: (1) 整除的结果为整数; (2) 在进行取余运算时,运算结果的符号和第一个操作数的符号一致; (3) 在算术运算中,只要有一个操作数为x或z,则运算结果为x。
对于复杂的表达式,中间运算过程的位宽如何确定呢?Verilog-1995标准规定:表达式中的所有中间结果应取最长操作数的位宽。此规定也包括左侧的赋值目标。
wire [3:0] opA, opB; // 4位
wire [4:0] opC; // 5位
wire [5:0] opD; // 6位
wire [7:0] opX; // 8位
...
assign opX = (opA + opC) + (opB + opD) ;
当数值运算的操作数中既含有无符号操作数,又含有有符号操作数时,则需要特别注意运算结果。因为Verilog-1995标准规定:只要表达式中有一个操作数为无符号数,则其它操作数会自动被当做无符号数处理,并且运算结果也为无符号数。
如果需要进行有符号数值运算,则每个操作数必须定义为有符号数,并且表达式中的无符号数必须通过Verilog-2001标准中的类型转换符$signed转换为有符号数。
module add_carry_signed_2001 (input signed [2:0] a, // 3位补码输入input signed [2:0] b, // 3位补码输入input carry_in, // 进位输入,1位无符号数output wire signed [3:0] sum // 4位补码输出);assign sum = a + b + $signed({1'b0,carry_in});
endmodule
逻辑运算符
逻辑运算符(Logic Operators)用于对操作数进行逻辑运算,有&&、||、 !三种运算符。
需要说明的是:逻辑运算中的操作数和运算结果均为一位。若操作数为矢量,非0矢量会被当作逻辑1处理。例如:当a=4'b0110, b=4'b1000时,则a&&b的结果为1,a||b的结果也为1。
1'b0 && 1'bz // 结果为01'b1 || 1'bz // 结果为1!x // 结果为x
位操作符
位操作符(Bitwise Operators)用于对操作数的对应位进行操作,包括&、|、 ~、^、~^或^~共五种操作符。
当a=4'b0110、b=4'b1000时,a & b = 4'b0000a | b = 4'b1110a ^ b = 4'b1110a ~^b = 4'b0001~a = 4'b1001~b = 4'b0111
位操作符与逻辑运算符的主要区别在于:逻辑运算中的操作数和结果均为一位,而位操作中的操作数和结果既可以是一位,也可以为多位。
关系运算符
关系运算符(Relational Operators)用于判断两个操作数的大小,关系为真时返回 1,为 假时返回 0,包括>、<、>=和<=共四种运算符。
关系操作符用于如果操作数的位宽不同,Verilog将位宽较短的操作数左边添0补齐,再进行比较。
对于'b1000 > = 'b01110,等价于'b01000 > = 'b01110,因此结果为假(0)。
所有关系运算符具有相同的优先级,但低于算术运算符的优先级。在关系运算符中,若操作数中包含有x或z,则结果为x。
等式运算符
等式运算符(Equality Operators)用于判断两个操作数是否相等,比较结果为真时返回 1,为假时返回 0。
运算符“==”和“!=”称为逻辑等式运算符。应用逻辑等式运算符时,x或z被认为是无关位,操作数中含有x或z的比较结果为x。 运算符“===”和“!==”称为case等式运算符。应用case等式运算符时,严格按操作数的字符值进行比较,结果非0即1。例如,当a=4'b10x0,b=4'b10x0时,(a==b)的比较结果为x,而(a===b)的比较结果则为1。
条件操作符
条件操作的语法格式为: (条件表达式)? 条件为真时的返回值 : 条件为假时的返回值;
input d0,d1; // 2路数据
input sel; // 一位地址
output y; // 输出
assign y = sel ? d1 : d0; // 2选一
input d3,d2,d1,d0; // 4路数据
input [1:0] a; // 两位地址
output y; // 输出
assign y = a[1] ? (a[0] ? d3 : d2 ): (a[0] ? d1: d0); // 4选一
移位操作符
移位操作的语法格式为: <操作数> <移位操作符> <数值>; 移位操作符包括逻辑左移(<<)和逻辑右移(>>)、算术左移(<<<)和算术右移(>>>)共四种操作符。移位的次数由操作符后面的数值决定。“data <<n”的含义是将操作数data向左移n位,“data >> n”的含义是将操作数data向右移n位。
a=4'b0111; ....... a>>2; // 右移,结果为4'b0001 a<<1; // 左移,结果为4'b1110
计算y=d×10时,可以应用语句“assign y = ( d<<3 ) + ( d<<1 ) ;”实现。
缩位运算符
缩位运算符(Reduction Operators)用于对单个操作数进行缩位运算,结果为1位,包括 &、 |、~&、~|、^、~^或^~共六种运算符。
wire [3:0] d;
wire y1,y2;
assign y1 = &d; // 缩位与,实现 y1 = d[3] & d[2] & d[1] & d[0]
assign y2 = ~|d; // 缩位或非,实现 y2 = ~(d[3] | d[2] | d[1] | d[0])
input [7:0] din;
output even_parity,odd_parity;
// 偶校验, 奇校验
assign even_parity = ^din;
assign odd_parity = ~(^din);
input [7:0] din;
output all_zeros,all_ones;
// all_zeros为真时,din为全0
assign all_zeros = ~|din;
// all_ones为真时,din为全1
assign all_ones = &din;
拼接操作符
拼接操作符(Concatenation Operators)用于将两个或以上的操作数连接起来,形成一个新的操作数。 拼接操作的语法格式为: {操作数1[msb:lsb],操作数2[msb:lsb],...,操作数n[msb:lsb]}
wire d0,d1,d2,d3; {d0,d1,d2,d3} 与定义 wire [0:3] d 等价
input dir,dil; // 右移数据输入,左移数据输入
reg [0:15] q; // 定义16位寄存器
q[0:15] <= {dir,q[0:14]}; // 16位逻辑右移
q[0:15] <= {q[1:15],dil}; // 16位逻辑左移