Verilog 基础语法
Published on
Verilog HDL(Hardware Description Language)是用于描述数字电路的硬件描述语言。
模块结构
基本语法
module 模块名 ( input wire 输入端口1, input wire 输入端口2, output wire 输出端口1, output wire 输出端口2);// 模块内部逻辑endmoduleendmodule端口类型
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位二进制数 10108'hFF // 8位十六进制数 FF16'd255 // 16位十进制数 2554'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; endendmodule设计实例分析
半加器 (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