一、前言
完成开发环境搭建后,掌握Verilog核心语法是编写硬件逻辑的基础。与软件编程语言不同,Verilog语法设计围绕"硬件电路映射"展开,数据类型与赋值语句的选择直接决定电路逻辑的正确性。
本文摒弃复杂理论,只讲新手开发必备的核心知识点,通过代码案例直观呈现语法规则,为后续流程控制、模块设计打下坚实基础。
二、核心数据类型:wire与reg(新手必掌握)
Verilog中数据类型繁多,新手只需精通wire 和reg两种,即可覆盖90%的基础开发场景,二者分别对应不同的硬件电路特性。
1. wire型:线网型数据
- 核心特性 :表示硬件电路中的"物理连线",无存储功能,值随输入信号实时变化,仅能通过连续赋值(assign)赋值。
- 适用场景:组合逻辑输出、模块间信号连接、输入端口定义。
- 定义语法 :
wire [位宽-1:0] 变量名;(位宽默认1位,可省略) - 示例:
verilog
// 定义1位wire变量a,连接输入端口
wire a;
// 定义8位wire变量data,存储8位数据
wire [7:0] data;
2. reg型:寄存器型数据
- 核心特性 :表示硬件电路中的"存储单元"(如触发器),有记忆功能,值在时钟边沿或条件触发时更新,仅能在过程块(always、initial)中赋值。
- 适用场景:时序逻辑变量、计数器、状态机状态变量。
- 定义语法 :
reg [位宽-1:0] 变量名; - 示例:
verilog
// 定义1位reg变量flag,用于标记状态
reg flag;
// 定义4位reg变量cnt,用于十进制计数
reg [3:0] cnt;
3. 核心区别总结(避坑重点)
| 数据类型 | 赋值方式 | 硬件映射 | 适用逻辑 |
|---|---|---|---|
| wire | assign连续赋值 | 物理连线 | 组合逻辑 |
| reg | 过程块赋值(always/initial) | 存储单元 | 时序逻辑 |
三、赋值语句:3种方式的严格区分
赋值语句是Verilog编写的核心,赋值方式与数据类型强绑定,混用会直接导致编译报错或逻辑错误,新手需严格区分。
1. 连续赋值(assign):专属wire型
- 语法规则 :
assign 目标变量 = 表达式; - 核心特点:赋值语句执行不受时间控制,表达式中任意信号变化,目标变量会立即更新,完全贴合组合逻辑"无记忆、实时响应"的特性。
- 案例:二输入或门(组合逻辑)
verilog
module assign_demo(
input wire a,
input wire b,
output wire y
);
// 连续赋值:y随a、b实时变化,a=1或b=1时,y立即为1
assign y = a | b;
endmodule
2. 阻塞赋值(=):专属reg型(组合逻辑过程块)
- 语法规则 :仅在
always过程块中使用,格式为变量 = 表达式; - 核心特点:赋值操作"先执行完再继续",同一过程块中,前面的赋值会立即影响后面的语句,类似软件编程语言的赋值逻辑。
- 适用场景 :
always块实现组合逻辑时(如用always写多路选择器)。 - 案例:二选一选择器(组合逻辑)
verilog
module block_assign(
input wire sel,
input wire data0,
input wire data1,
output reg out // 过程块赋值,需定义为reg型
);
// always块实现组合逻辑,敏感列表包含所有输入信号
always @(sel or data0 or data1) begin
if(sel == 1'b1)
out = data1; // 阻塞赋值,先执行
else
out = data0; // 受前面赋值影响(本案例无体现,复杂逻辑中需注意)
end
endmodule
3. 非阻塞赋值(<=):专属reg型(时序逻辑过程块)
- 语法规则 :仅在
always过程块中使用,格式为变量 <= 表达式; - 核心特点:赋值操作"先暂存,过程块结束后统一执行",同一过程块中,所有赋值语句互不影响,完美贴合时序逻辑"时钟边沿同步更新"的特性。
- 适用场景 :
always块实现时序逻辑时(如计数器、触发器)。 - 案例:简单D触发器(时序逻辑)
verilog
module non_block_assign(
input wire clk, // 时钟信号
input wire rst_n, // 低电平复位信号
input wire d, // 输入数据
output reg q // 输出数据,reg型
);
// always块实现时序逻辑,敏感列表为时钟上升沿+复位下降沿
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0)
q <= 1'b0; // 复位,输出0
else
q <= d; // 时钟上升沿,将d的值赋给q,非阻塞赋值
end
endmodule
4. 赋值语句避坑指南
- 禁止用
assign给reg型变量赋值,禁止在过程块中给wire型变量赋值; - 组合逻辑过程块(always)必须用阻塞赋值(=),时序逻辑过程块(always)必须用非阻塞赋值(<=),禁止混用;
- 过程块中,reg型变量必须初始化,避免出现不定态(x)。
四、实操验证:语法混合案例
结合上述知识点,编写一个"带复位的简单计数器",整合wire、reg、阻塞/非阻塞赋值的使用,帮助新手融会贯通。
verilog
// 顶层模块:4位二进制计数器(0-15)
module counter_demo(
input wire clk, // 时钟输入
input wire rst_n, // 低电平复位
output wire [3:0] out// 计数输出,wire型
);
// 定义reg型变量,存储计数值(时序逻辑,需reg型)
reg [3:0] cnt;
// 时序逻辑:计数器核心,非阻塞赋值
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0)
cnt <= 4'b0000; // 复位,计数值清零
else
cnt <= cnt + 1'b1; // 时钟上升沿,计数值+1
end
// 组合逻辑:将reg型的cnt赋值给wire型的out,连续赋值
assign out = cnt;
endmodule