目录
一.基本语法
1.模块的结构
Verilog程序的基本设计单元是“模块”,模块完全定义在module 和endmodule关键字之间。 每个模块包含四个主要部分:模块声明,端口定义,数据类型说明和 逻辑功能描述。
module <模块名> (
<端口>
<端口>
......
);
<端口定义>
<数据类型说明>
<逻辑功能描述>
endmodule
举例是一个32位加法器的模块。
module add32(in1 ,in2,out); //模块声明
input in1,in2; // 端口定义
output out;
wire[31: 0] in1,in2,out; // 数据类型说明
assign out = in1 + in2; // 逻辑功能描述
endmodule
1.模块声明
模块声明包括 模块名,以及输入、输出端口列表。 格式如下:
module 模块名 (端口1,端口2,端口3 ....);
2.端口定义
说明模块端口的方向 (输入、输出、双向等),其格式如下:
input 端口1,端口2,端口3 ....; //输入端口
output 端口1,端口2,端口3 ....; //输出端口
inout 端口1,端口2,端口3 ....; //双向端口
3.数据类型说明
对模块中所有用到的信号(包括端口信号、节点信号等)都必须进行数据类型的定义。
reg a ;
wire[31:0] out;
//对于端口,可以将数据类型说明与端口定义放在一条语句中完成。 于是上文代码
module add32(in1 ,in2,out); //模块声明
input wire[31: 0] in1,in2; // 端口定义,数据类型说明在一条语句中完成
output wire[31: 0] out;
assign out = in1 + in2; // 逻辑功能描述
endmodule
对于端口,还可以将端口定义、数据类型说明都放在模块声明语句中,而不再放在模块内部,更为简便。
//将端口定义、数据类型说明放在模块声明中
module add32( input wire[31: 0] in1 ,
input wire[31: 0] in2,
output wire[31: 0] out);
assign out = in1 + in2; // 逻辑功能描述
endmodule
4.逻辑功能描述
模块中最核心的部分是逻辑功能描述,可以有多种方法在模块中描述和定义逻辑功能。 几种基本方法如下:
用assign 连续赋值语句定义
用always过程快定义
调用元件(也称为元件例化)
2.语言要素及数据类型
2.1语言要素
1.空白符
包括 空格(\b)、制表符(\t)、换行符(\n)和换页符
如果 空白符不出现在字符串中,则空白符被忽略。使得代码结构层次清晰,方便阅读与修改。
2. 注释符
有两种形式
(1)单行注释符:以 // 开始到本行结束,不允许续行
(2)多行注释符:以 / * 到 */ 结束
注意: 多行注释不允许嵌套,但单行注释可以在多行注释中使用。
3.关键字(keyword)
也称保留字 ,是专用词,用户不能随便使用,所有关键字都是小写的。
例如:initial
4. 标识符(identifier)
程序代码中对象的名字。用户自己设计的,区分大小写的,可以是任意字母,数字,“_”(下划线)和$(美元符号)的组合。
但是第一个字符必须是字母或者下划线,不能是数字或$
(以$开始的标识符是为系统任务和系统函数保留的)
例如:count , sum ,a
5.转义标识符(escaped indentifier)
转义标识符以反斜线符号“\”开头,以空白符结尾,可以包含任意字符
\2021
\#res*~
反斜线和结束空白符并不是转义标识符的一部分,也就是说,标识符\din 和标识符din是相同的。
转义标识符与关键字并不完全相同。标识符\initial 与关键字initial是不同的。
2.2 常量
程序运行过程中,其值不能被改变的量称为常量。(constant)
Verilog HDL 中主要有3中类型:整数、实数和字符串。(其中只有整数型常量是可以综合的。)
为了对电路精确建模,增加了两种逻辑状态(不区分大小写) z和x
Verilog HDL 中的4中逻辑值状态:
0:低电平,逻辑假
1:高电平,逻辑真
z:高阻态
x:不确定或未知的逻辑状态。
1 整数的表示:
+/-<size>'<base><value>
即 +/-<位宽>'<进制><数字>
size 为二进制数的位宽,base为进制,value是基于进制的数字序列。
进制的4中表示形式:二进制(b或B),八进制(O,o),十进制(d,D,或默认),十六进制(h,H)
十六进制中的a~f和z和x一样,不区分大小写。
书写和使用时注意:
(1)在较长的数字之间可以用下划线分开: 12‘b1111_1011_1010.
(2)当数字不说明位宽和进制时,默认为32位的十进制数。
17 //表示 十进制17
-9 //十进制 -9
(3)如果没有定义位宽,位宽就是相应值中定义的位数
’h0111 //16位十六进制数16’h0000_0001_0001_0001
‘o321 //9位八进制数 9'o011_010_001
(4)x或z在二进制中代表1位x或z,八进制中代表3位x或z,以此类推 ,其代表的宽度取决于所用的进制。
12‘b0111xxxx //等价于12'h7x
7’b101zzz //等价于7'o5z
8'Bxxxx1111 //等价于8'HxF
(5)如果定义的位宽大于实际的位数,通常在左边填0补位,但如果最左边一位为z或x,就用z或x进行补位。
如果定义的位宽小于实际的位数,那左边的位被截掉。
2‘b0111 //等价于 2’b11
(6)“?”是高阻态z的另一种表示符号。在数字的表示中“?”和z是完全等价,可以相互替代。
(7)整数可以带符号,而且正负号应放在最左边。负数通常表示为二进制补码的形式。
32‘d23 //表示十进制23
-6'd12 //表示十进制-12,用二进制表示为 110100,最高位为符号位。
(8)在位宽和“ ‘ “之间以及进制和数字之间允许出现空格,但在" ’ "和进制之间以及数字之间不允许出现空格。
3(\b)‘h(\b)101 /* 合法*/
3'(\b) o101 /* 不合法*/
2.实数
实数(real)就是浮点数,定义方式有两种:
(1)十进制格式
0.02
2.3
(2)科学计数法
(e前面必须有数字,后面必须是整数)
4.2e3
2E-2
(3)转换方法
通过四舍五入,将实数转化为最接近的整数
3.字符串
字符串是由一对双引号括起来的字符序列。出现在双引号内的任意字符都将被作为字符串的一部分。
2.3 变量和数据类型
在程序运行中,其值可以变的量称为变量(variable)。变量应该由名字,占据一定的存储空间,在该存储空间内存放变量的值。
每种变量都有其在电路中的实际意义。数据类型被设计用来表述数字硬件电路中数据的存储和传输。
Verilog HDL语言中,根据赋值和对值的保持方式不同,将数据类型分为两大类:线网型(net)和变量型(wire)。
1.net型
该类型数据相当于硬件电路中的各种物理连接(节点和导线),用来连接各个模块以及输入输出。
其特点是输出值紧跟着输入值的变化而变化,没有电荷的保持作用(trireg除外)。
net型数据必须由驱动源驱动,有两种驱动方式
(1)在结构描述中将其连接到一个门元件或模块的输出端;
(2)用持续赋值语句assign对其进行赋值。
类型 | 功能 | 可综合性 |
wire,tri | 两种常见的net型变量 | 可以 |
supply1,supply0 |
分别为电源逻辑(1)和地逻辑(0) | 可以 |
表格中展时的数据类型仅仅是可综合的,没有展时不可综合的,下同
2.variable型
variable型变量必须放在过程语句中(always,initial),通过过程赋值语句赋值。
被赋值的信号也必须定义成variable型
它并不意味着一定是对应硬件上的触发器或寄存器等存储单元,在综合器进行综合时,variable型变量会根据被赋值的具体情况来确定是映射为连线还是映射为存储单元(触发器或寄存器)
integer型变量
是整数寄存器,最常用的变量类型,这种寄存器中存储整数,常用于对循环控制变量的说明,例如用来表述循环次数等。
类型 | 功能 | 可综合性 |
reg | 常用的variable型变量 | 可以 |
integer | 32位带符号整型变量 | 可以 |
2.4 参数
参数型数据是被命名的常量,在仿真开始前对其赋值,在整个仿真过程中,其值保持不变,数据的具体类型是由被赋的值来决定的。
参数通常出现在模块的内部,用来定义状态机的状态,数据位宽,以及延时大小
用参数parameter 来定义符号常量,即用parameter 来定义一个标识符代表一个常量
parameter 参数名1 = 表达式1,参数名2 = 表达式2,参数名3 = 表达式3,.。。;
参数最大的特点是它可以在编译时被方便的修改,所以常用来对一些需要调整的数据建模,在模块实例化时根据需要进行配置。
parameter是在模块内部的局部定义,而 `define 是全局性的宏定义。
2.5 向量
1.标量与向量
位宽为1的变量称为标量,默认的位宽为1位
位宽大于1的变量称为向量 (包括net型和variable型)
向量通常通过位宽定义语法[msb:lsb] 指令地址范围。
msb表示向量的最高有效位(most significant bit),lsb表示向量的最低有效位(lease significant bit)
注意 msb和lsb必须是常数值或者parameter,或者在编译时计算结果位常数的表达式,而且可以为任意的整数:整数,负数或0 (msb可以大于,等于甚至小于lsb)
2.位选择和域选择
在向量中,可以指其中的某一位或者相邻若干位进行操作,这些指定的一位或者相邻位分别称为位选择和域选择
2.6 存储器
通过对reg型变量建立数组来实现寄存器建模。数组中每一个单元通过数组索引进行寻址。memory型数据是通过扩展reg型数据的地址范围来生成的。
memory型数据的定义格式如下:
reg[n-1:0] 存储器名[m-1,0];
或 reg[n-1:0] 存储器名[m,0];
其中[n-1:0]表示存储器的字长,定义了存储器中每一个存储单元的大小
[m,0]位存储器的容量,定义了该存储器由多少个寄存器。
reg data[7:0]; //8个1位寄存器组成的存储器data
reg[0:3] a; // a[0]是最高有效位 a[3]是最低有效位
注意:
reg[7:0] rega; // 1个8位的寄存器
reg amem[7:0]; //8个1位寄存器的存储器
这是不同的
rega = 0; // 合法
amem = 0; //不合法
一个n位的寄存器可以在一条赋值语句里进行赋值,一个完整的存储器不可以
对存储器赋值时,只能对某一个单元进行赋值。 amem[0 ] = 1;
还有一种方法对存储器赋值,采用系统任务 (仅限于电路仿真)
$ readmemb(加载二进制值)
$readmemh (加载十六进制值)
2.7 运算符
1.1 算术运算符
+ | 加 | - | 减 |
* | 乘 | / | 除 |
% | 求余 | ** | 乘方 |
1.2 逻辑运算符