Verilog HDL(Hardware Description Language)是用于描述数字电路的硬件描述语言。
模块结构
#基本语法
#module 模块名 (
input wire 输入端口1,
input wire 输入端口2,
output wire 输出端口1,
output wire 输出端口2
);
// 模块内部逻辑endmodule
endmodule
端口类型
#input
:输入端口output
:输出端口inout
:双向端口
模块实例化
#模块可以作为独立的硬件单元被其他模块实例化使用:
// 命名端口连接方式
half_adder ha_uut (.a(ha_a), .b(ha_b), .sum(ha_sum), .carry(ha_carry));
// 位置端口连接方式
half_adder ha_uut (ha_a, ha_b, ha_sum, ha_carry);
数据类型
#线网类型 (Net Types)
#类型 | 用途 | 特点 |
---|
wire | 表示连接线 | 不能存储数据,用于模块间连接 |
tri | 三态线网 | 支持多驱动源 |
寄存器类型 (Register Types)
#类型 | 用途 | 特点 |
---|
reg | 可存储数据的变量 | 可在 always 块中赋值 |
integer | 32 位有符号整数 | 用于计数、循环等 |
real | 双精度浮点数 | 仿真时使用,不可综合 |
数字表示方法
#<位宽>'<进制><数值>
// 示例:
4'b1010 // 4位二进制数 1010
8'hFF // 8位十六进制数 FF
16'd255 // 16位十进制数 255
4'o17 // 4位八进制数 17
'b1 // 位宽不指定的二进制数
特殊状态
#X
:不定态,表示未知或不确定的值Z
:高阻态,表示三态缓冲器的高阻抗输出
运算符分类
#算术运算符
#运算符 | 功能 | 示例 |
---|
+ | 加法 | a + b |
- | 减法 | a - b |
* | 乘法 | a * b |
/ | 除法 | a / b |
% | 取模 | a % b |
** | 幂运算 | 2 ** 3 = 8 |
对于 2 的幂次取模可优化为位掩码操作:
$$
n \% 2^k = n \&(2^k-1)
$$

n % 8 = n & 7 // 8 = 2³, 7 = 111₂
n % 16 = n & 15 // 16 = 2⁴, 15 = 1111₂
位运算符
#运算符 | 功能 | 示例 |
---|
& | 按位与 | a & b |
` | ` | 按位或 |
^ | 按位异或 | a ^ b |
~ | 按位取反 | ~a |
逻辑运算符
#运算符 | 功能 | 示例 |
---|
&& | 逻辑与 | a && b |
` | | ` |
! | 逻辑非 | !a |
关系运算符
#运算符 | 功能 | 示例 |
---|
< | 小于 | a < b |
<= | 小于等于 | a <= b |
> | 大于 | a > b |
>= | 大于等于 | a >= b |
== | 逻辑相等 | a == b |
!= | 逻辑不等 | a != b |
等式运算符
#运算符 | 功能 | 说明 |
---|
=== | Case 相等 | 精确比较,包括 X/Z 状态 |
!== | Case 不等 | 精确比较不等 |
==? | 通配符相等 | X/Z 作为 don’ t care |
!=? | 通配符不等 | X/Z 作为 don’ t care |
移位运算符
#运算符 | 功能 | 说明 |
---|
<< | 逻辑左移 | 右边补 0 |
>> | 逻辑右移 | 左边补 0 |
<<< | 算术左移 | 与逻辑左移相同 |
>>> | 算术右移 | 保持符号位 |
缩减运算符
#缩减运算符对单个操作数的所有位执行运算:
运算符 | 功能 | 示例 |
---|
& | 缩减与 | &8'b11111111 = 1 |
` | ` | 缩减或 |
^ | 缩减异或 | ^8'b11001100 = 0 (偶校验) |
~& | 缩减与非 | ~&bits |
`~ | ` | 缩减或非 |
~^ , ^~ | 缩减异或非 | ~^bits |
赋值语句
#连续赋值 (assign)
#用于组合逻辑,信号变化时立即更新:
assign sum = a ^ b;// 异或运算
assign carry = a & b;// 与运算
阻塞赋值 (=)
#- 使用
=
操作符 - 按顺序执行,每步等待前一步完成
- 主要用于组合逻辑
- 在
always @(*)
块中使用
always @(*) begin
a = 8'h10;
b = a + 1;// b 立即得到 a 的新值
c = b + 1;// c 立即得到 b 的新值
end
非阻塞赋值 (<=)
#- 使用
<=
操作符 - 所有赋值同时调度,使用信号的旧值
- 主要用于时序逻辑
- 在
always @(posedge clk)
块中使用
always @(posedge clk) begin
x <= 8'h20;
y <= x + 1;// y 使用 x 的旧值
z <= y + 1;// z 使用 y 的旧值
end
黄金规则
#- 组合逻辑
always @(*)
- 使用阻塞赋值 =
- 时序逻辑
always @(posedge clk)
- 使用非阻塞赋值 <=
- 初始化块
initial
- 通常使用阻塞赋值 =
编译器指令
#时间单位设置
#`timescale 1ns/1ps// 时间单位1ns,精度1ps
宏定义
#`define WORD_WIDTH 16
`define BYTE_WIDTH 8
`define DEBUG_MODE
// 使用宏
reg [`WORD_WIDTH-1:0] word_data;
条件编译
#`ifdef DEBUG_MODE
`define DEBUG_PRINT(msg) $display("[DEBUG] %s", msg)
`else
`define DEBUG_PRINT(msg) // 空宏
`endif
系统任务
#显示任务
#任务 | 功能 |
---|
$display | 显示信息并换行 |
$write | 显示信息不换行 |
$monitor | 监控信号变化 |
$time | 获取当前仿真时间 |
文件操作
#integer file_handle;
file_handle = $fopen("output.txt", "w");
$fwrite(file_handle, "数据: %h\n", data);
$fclose(file_handle);
仿真控制
#任务 | 功能 |
---|
$finish | 结束仿真 |
$stop | 暂停仿真 |
$dumpfile | 指定 VCD 波形文件名 |
$dumpvars | 转储变量到波形文件 |
随机数生成
#data = $random;// 生成随机数
data = $random(seed);// 使用种子生成随机数
测试平台 (Testbench)
#测试平台是验证模块功能的仿真环境:
module simple_module_tb;
// 测试信号声明
reg a, b; // 输入用reg
wire and_out, or_out, xor_out; // 输出用wire
// 实例化被测模块
simple_module uut (
.a(a), .b(b),
.and_out(and_out),
.or_out(or_out),
.xor_out(xor_out)
);
// 测试激励
initial begin
$dumpfile("test.vcd");
$dumpvars(0, simple_module_tb);
// 测试向量
a = 0; b = 0; #10;
a = 0; b = 1; #10;
a = 1; b = 0; #10;
a = 1; b = 1; #10;
$finish;
end
endmodule
设计实例分析
#半加器 (Half Adder)
#实现两个 1 位数的加法:
module half_adder (
input wire a, b,
output wire sum, carry
);
assign sum = a ^ b;// 异或得到和
assign carry = a & b;// 与运算得到进位
endmodule
全加器 (Full Adder)
#使用两个半加器构建:
module full_adder (
input wire a, b, cin,
output wire sum, cout
);
wire sum1, carry1, carry2;
half_adder ha1 (.a(a), .b(b), .sum(sum1), .carry(carry1));
half_adder ha2 (.a(sum1), .b(cin), .sum(sum), .carry(carry2));
assign cout = carry1 | carry2;
endmodule