Background

Verilog 基础语法

Verilog HDL(Hardware Description Language)是用于描述数字电路的硬件描述语言。

模块结构

基本语法

module 模块名 (
input wire 输入端口1,
input wire 输入端口2,
output wire 输出端口1,
output wire 输出端口2
);
// 模块内部逻辑endmodule
endmodule

端口类型

模块实例化

模块可以作为独立的硬件单元被其他模块实例化使用:

// 命名端口连接方式
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 块中赋值
integer32 位有符号整数用于计数、循环等
real双精度浮点数仿真时使用,不可综合

数字表示方法

<位宽>'<进制><数值>
// 示例:
4'b1010 // 4位二进制数 1010
8'hFF // 8位十六进制数 FF
16'd255 // 16位十进制数 255
4'o17 // 4位八进制数 17
'b1 // 位宽不指定的二进制数

特殊状态

运算符分类

算术运算符

运算符功能示例
+加法a + b
-减法a - b
*乘法a * b
/除法a / b
%取模a % b
**幂运算2 ** 3 = 8

对于 2 的幂次取模可优化为位掩码操作:

n%2k=n&(2k1)n \% 2^k = n \&(2^k-1)

image

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 @(*) begin
a = 8'h10;
b = a + 1;// b 立即得到 a 的新值
c = b + 1;// c 立即得到 b 的新值
end

非阻塞赋值 (<=)

always @(posedge clk) begin
x <= 8'h20;
y <= x + 1;// y 使用 x 的旧值
z <= y + 1;// z 使用 y 的旧值
end

黄金规则

  1. 组合逻辑 always @(*) - 使用阻塞赋值 =
  2. 时序逻辑 always @(posedge clk) - 使用非阻塞赋值 <=
  3. 初始化块 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
JrHimself

© 2025 Aria

萌 ICP 备 20252003 号 GitHub Email