Verilog 基本语法
1 基本模块说明
Verilog 使用模块(module)来描述一个电路,module 是层次化设计的基本构件,逻辑描述放在 module 内部,如下所示
module ALU
// 这里描述模块的具体内容
endmodule
module 可以表示物理块(如 IC 或 ASIC 单元)、逻辑块(如一个 CPU 设计的 ALU 部分)和整个系统。
Verilog 模块的结构由在 module 和 endmodule 关键词之间的四个主要部分组成:
- 端口信息:
module <module_name> (<port_list>); - 输入/输出说明:
input [<range>] <port_name>; - 内部信号:
wire [<range>] <wire_name>; - 功能定义: 实现具体的逻辑
如下所示
module DFF (d, clk, clr, q, qb);
input d, clk, clr;
output q, qb;
// 功能定义 + 内部信号
endmodule
- 端口在模块名字后的括号中列出
- 端口等价于硬件的引脚(pin)
- 端口可以说明为 input, output 及 inout
- 输入/输出端口信号类型声明,缺省为 wire 型
2 词汇约定
2.1 术语及定义
- 空白符:空格、tabs 及换行
- 标志符(Identifier):Verilog 中对象(如模块或信号)的名字
- LSB:最低有效位(Lease significant bit)
- MSB:最高有效位(Most significant bit)
2.2 空白符和注释
Verilog 里面的空白符不影响程序的运行,但可以提高程序的可读性。
单行注释使用//,多行注释使用/* */。
2.3 四值逻辑系统
Verilog 使用四值逻辑系统,即逻辑运算的结果可以是 0、1、Z(未定义)或 X(未连接)。
- 逻辑
0:表示低电平,对应电路的GND - 逻辑
1:表示高电平,对应电路的VCC; - 逻辑
X:表示不确定,通常用于表示无法确定逻辑值的情况; - 逻辑
Z:表示高阻态,对应电路的悬空状态。
2.4 整数常量和实数常量
Verilog中,常量(literals)可是整数也可以是实数。
整数使用 <size>'<base><value> 表示,其中:
<size>:表示整数的位数,由十进制数表示的位数表示,缺省为32位<base>:表示整数的进制,简称数基,可以是二进制(b)、八进制(o)、十进制(d)和十六进制(h),缺省为十进制<value>:表示整数的值,需要和所选进制对应,包括X和Z
实数使用十进制或者科学计数法表示。
下面给出一些整数与实数常量例子:
| 常量 | 描述 |
|---|---|
12 | 32位十进制整数 12 |
'h83a | 32位十六进制整数 83a |
8'b1100_0001 | 8位二进制整数 1100 0001 |
16'hff01 | 16位十六进制整数 ff01 |
32'bz01x | 32位二进制整数 z01x,其中z表示对应的位为高阻态,向左边扩展 |
3'b1010 1111 | 3位二进制整数 111,由于只能存储3位,截去高位,转化为 3'b111 |
1.5 | 32位实数 1.5 |
1.5E2 | 32位实数 150,表示 1.5 * 10^2 |
1.5e-2 | 32位实数 0.015,表示 1.5 * 10^-2 |
- 没有定义大小(size)的整数默认为32位
- 缺省数基默认为十进制
- 数基和数(16进制)中的字母无大小写之分
- 当数值大于指定的大小时,截去高位。如
2'b1101表示的是2'b01 - 数字中的下划线
_可以忽略
2.5 字符串
字符串通常用于测试中,显示测试结果等信息。
字符串要在一行中用双引号括起来,也就是不能跨行;字符串中可以使用一些转义符,如 \t, \n等。
display 语句中可以使用一些格式符(如 %b)在仿真时产生格式化输出,例如:
display("A = %b", A); // 输出 A 的二进制表示:A = 0001
2.6 标识符
标识符是用户在描述时给 Verilog 对象(电路模块、信号等)起的名字,必须满足以下规则:
- 必须以字母(a-z, A-Z)或( _ )开头,后面可以是字母、数字、( $ )或( _ )
- 最长为1023个字符
- 区分大小写
有效标识符:shift_reg_a, busa_index, _bus3;
无效标识符:34net, a*b_net, n@238
2.7 系统任务及函数
使用 $<identifier> 指示这是系统任务和函数,比如:
$display:打印信息$stop:停止仿真$time:返回仿真时间
2.8 编译指导
使用符号 ` 表示一个编译指导,这些编译指导使仿真编译器进行一些特殊的操作,一直保持有效直到被覆盖或解除。
比如 `timescale 1ns/1ps 表示单位时间为1ns,时钟精度为1ps。
2.9 文本包含 `include
编译指导 `include 在当前内容中插入一个文件,格式为 `include "filename",文件路径可以是绝对路径,也可以是相对路径。
- 全局的或经常用到的一些定义,如文本宏,可以放在一个文件中,然后通过
`include "filename"引入 - 在模块内部 include 一些任务,提高代码的可维护性
2.10 文本替换 `define
编译指导 `define 提供了一种简单的文本替换的功能,用法为 `define <identifier> <text>,在编译的时候yi时,所有 `<identifier> 都会被替换为 <text>。
3 数据类型
Verilog 有两大类数据类型:线网类型和寄存器类型。
- 线网类型(net type): 表示 Verilog 结构化元件间的物理连线。它的值由驱动元件的值决定,例如连续赋值或门的输出。如果没有驱动元件连接到线网,线网的缺省值为
z。 - 寄存器类型(register type):表示一个抽象的数据存储单元,它只能在
always语句和initial等过程语句中被赋值,并且它的值从一个赋值到另一个赋值被保存下来。寄存器类型的变量具有x的缺省值。
3.1 线网类型
线网类型在电路中表示元件之间的物理连线,值由输入端决定。
net 需要被持续的驱动,驱动它的可以是门和模块。当 net 驱动器的值发生变化时,新值被传送到 net 上。
在下例中,线网 out 由 or 门驱动。当 or 门的输入信号置位时将传输到线网 net 上。

线网类型如下表所示:
| 类型 | 描述 |
|---|---|
wire, tri | 标准内部连接线(缺省) |
supply1, supply0 | 电源和地 |
wor, trior | 多驱动源线或 |
wand, triand | 多驱动源线与 |
trireg | 能保存电荷的tri |
tri0, tri1 | 带上拉/下拉电阻的tri |
- 没有声明的线网类型,默认为
wire - 可以综合的线网数据类型包括
wire,tri,supply1,supply0,最常用的线网数据类型是wire wire和tri在 Verilog 仿真和综合中的实际行为是完全一样的,tri主要是一个语义提示,表明该信号可能是一个潜在的多驱动网络
线网数据类型使用语法为 net_type [msb:lsb] net_name,其中:
net_type:线网数据类型[msb:lsb]:用于定义线网范围的常量表达式,如果没有定义,缺省为1位net_name:线网名称
线网数据类型声明的例子:
module mux2to1(out, a, b, sel);
input a, b, sel; // 没有声明信号类型,默认为 wire
output out;
wire out;
assign out = (sel) ? b : a;
endmodule
3.2 寄存器类型
寄存器类型在电路中表示一个抽象的数据存储单位,有四种数据类型,分别为 reg, integer, real, time。
| 类型 | 描述 |
|---|---|
reg | 最常用的寄存器类型,无符号型 |
integer | 32位有符号整数变量,算术操作产生二进制补码形式的结果。通常用作不会由硬件实现的的数据处理 |
real | 双精度的带符号浮点变量,用法与integer相同 |
time | 64位无符号整数变量,用于仿真时间的保存与处理 |
reg 是寄存器数据类型最常见的数据类型,使用语法为 reg [msb:lsb] reg_name,其中:
[msb:lsb]:用于定义寄存器的范围,如果没有定义,缺省为1位reg_name:寄存器名称
寄存器数据类型声明的例子:
reg [3:0] counter;:4位无符号寄存器 counterreg counter;: 1位无符号寄存器 counterreg [31:1] counter;:31位无符号寄存器 counter
module mux2to1(out, a, b, sel);
input a, b, sel;
output out;
reg out;
always @(a or b or sel)
if (sel) out = b;
else out = a;
endmodule
输入端口只能是net,输出端口只能驱动net
- 输入端口只能是 net;但输入端口可以由 net/register 驱动
- 输出端口可以是 net/register 类型,但输出端口只能驱动 net
例如下面的例子,c、d、o2 是输入信号,却被定义为 register 类型,会导致错误。所以应该将 o1 定义为register 类型,其余的为 net 类型。
module example(o1, o2, a, b, c, d);
input a, b, c, d;
output o1, o2;
reg c, d; // ERROR
reg o2; // ERROR
and u1(o2, c, d); // 一般来说,输出信号放在前面,输入信号放在后面
always @(a or b)
if (a) o1 = b;
else o1 = 0;
endmodule
3.3 参数
参数是一个常量,它可以被模块实例化时指定一个值。参数声明的语法为 parameter param_name = param_value。
- 推荐:一般定义参数等常量名称用大写,变量名称用小写!
- 可一次定义多个参数,用逗号隔开,比如
parameter PARAM1 = 1, PARAM2 = 2 - 参数的定义是局部的,只在当前模块中有效。
parameter作用于声明的那个模块;`define从编译器读到这条指令开始到编译结束都有效,或者遇到`undef命令使之失效。- 也可以使用
`include实现parameter的跨文件 - 使用参数实现模块实例化时的参数传递(例化传递)
module Decoder(...); // 子模块
parameter WIDTH = 1, POLARITY = 1;
endmodule
module Top(...); // 顶层模块
Decode #(4, 0) U_D1(); // 使得 WIDTH = 4, POLARITY = 0
Decode #(8) U_D2(); // 使得 WIDTH = 8, POLARITY 不变
Decode #(.POLARITY(3)) U_D3(); // 使得 WIDTH 不变,POLARITY = 3
endmodule
3.4 位选择
位选择从向量中抽取特定的位,使用语法为 [msb:lsb],例如:
reg [31:0] Breg;
wire [31:0] Bwire;
assign Bwire[31:0] = Breg[31:0];
整数不能作为位向量访问,比如定义 interger B,使用B[31:0]会报错。解决方法是将整数赋值给一般的reg类型变量,然后从中选取相应的位。
4 运算符
Verilog 中运算符与 C/C++ 类似,包括:
- 算术运算符:
+,-,*,/,% - 逻辑运算符:
!,&&,|| - 位运算符:
&,|,^,~,>>,<< - 关系运算符:
>,<,>=,<=,!=,==,===,?: - 赋值运算符:
=,<= - 拼接运算符:
{},{n{b}}
4.1 算数运算符
- 将负数赋值给 reg 或其它无符号变量,使用 2 的补码算术。(正数的补码为原码,负数的补码为源码取反后加 1)
- 如果操作数的某一位是 x 或 z,则结果为 x
- 在整数除法中,余数舍弃
- 模运算中使用第一个操作数的符号
4.2 关系运算符
===表示全等,==表示相等
x === x //为true
4.3 赋值运算符
阻塞赋值=常用于组合逻辑,例如assign语句和always@(*)语句块;非阻塞赋值<=常用于时序逻辑,例如always@(posedge clk)语句块。
wire [11:0] data_in;
reg [11:0] data_out;
assign data_in = 6'b10111; //assign语句常给wire类型复制
always @( posedge rst) begin //always语句块常给reg类型赋值
data_out <= data_in;
end
非阻塞赋值=与C/C++相同,计算后立刻赋值;阻塞赋值<=则先进行计算,在统一赋值。
4.4 拼接运算符
连接运算符{}用于将不同的信号合成为一个信号。由于非定长常数的长度未知, 不允许连接非定长常数,以及位数不匹配的情况。
wire [5:0] a,b,c;
wire [11:0] d;
assign a = 6'b101101;
assign b = 6'b111000;
assign c = 2'b11;
assign d = {a[5],b[2:0],c};//d = 100011
assign d = {a, 5} // ERROR,a长度未知
assign d = {a, b, c} // ERROR,d的长度和a、b、c的长度不匹配
{n{}} 表示复制操作,将信号复制 n 次。
assign d = {2{4'b1011}}; // d = 8'b1011_1011
assign d = {{4{1'b1}}, 4'b1011}; // d = 8'b1111_1101
assign d = {4{1'b1}, 4'b1011}; // ERROR,4{1'b1}不是完整的信号