前言:解决90%新手的核心困惑
很多刚学Verilog的小伙伴都会遇到同一个难题:代码单词看得懂,每一行是干嘛的完全不知道,运行结果摸不着头脑,知识点杂乱看不懂。
根本原因只有一个:你在用C语言软件思维学Verilog硬件语言。
我们熟悉的STM32、51单片机C语言,是串行顺序执行 ,CPU按代码一行行跑;而Verilog是并行硬件描述语言,代码写完不是给CPU跑的指令,是直接搭建一套真实的硬件电路,所有模块同时工作。
这篇博客专门为纯小白打造,不讲晦涩理论,全程通俗比喻、逐行拆解代码、讲清每一步运行过程和结果,帮你从零入门Verilog。
一、核心认知:FPGA与单片机的本质区别(入门必看)
想要学好Verilog,第一步必须扭转思维,搞懂软硬件的核心差异。
1.1 单片机(MCU/STM32)------ 现成房子+工人干活
单片机的硬件是固定死的,出厂是什么结构就是什么结构。
-
C语言代码 :相当于指挥CPU工人,按顺序一步步做事,跑完上一行才会执行下一行,属于串行执行。
-
局限性:只能修改执行步骤,无法更改硬件结构。
1.2 FPGA ------ 一堆空白乐高积木
FPGA内部是大量可自由组合的逻辑单元,没有固定电路结构。
-
Verilog代码 :相当于拼乐高、搭电路 ,代码综合后会直接生成硬件电路,所有电路模块、逻辑门同时并行工作。
-
优势:硬件结构由你定义,想搭与门、计数器、加法器都可以灵活实现。
一句话总结:C语言是「软件指令,顺序执行」;Verilog是「硬件连线,并行运行」。
二、Verilog最小单元:Module模块(所有代码的根基)
任何Verilog代码,都必须包裹在 module 和 endmodule 之间,它是Verilog的最小独立单元。
通俗理解:module = 一块独立的芯片,有输入输出引脚,内部是你搭建的硬件电路。
2.1 三大核心引脚定义
-
input wire:芯片输入引脚,外部信号通过导线流入模块,wire是纯物理导线,只传信号、不存数据。
-
output wire:芯片输出引脚,模块内部电路处理后的信号向外输出。
2.2 零基础完整模板(逐行注释)
Plain
// 定义一个名为and_gate的与门硬件模块
module and_gate (
input wire a, // 输入引脚a:外部输入0/1信号
input wire b, // 输入引脚b:第二路外部输入信号
output wire y // 输出引脚y:电路最终结果输出
);
// 硬件电路逻辑:搭建与门电路,永久绑定信号关系
assign y = a & b;
endmodule
2.3 C语言 vs Verilog 核心对比
| 对比维度 | C语言 | Verilog Module |
|---|---|---|
| 程序入口 | main() 函数 | module 硬件模块 |
| 数据载体 | 内存变量(可存储数据) | wire导线(仅传信号)、reg寄存器(存数据) |
| 编译结果 | CPU执行指令 | 硬件逻辑电路 |
| 运行方式 | 串行顺序执行 | 全局并行运行 |
三、六大核心关键词(小白必背,零基础吃透)
这6个关键词是Verilog所有代码的基础,搞懂它们就能看懂80%的基础代码,全程人话解读,无晦涩术语。
| 关键词 | 通俗解释 | 硬件本质 | 实操示例 |
|---|---|---|---|
| module | 定义一块独立硬件模块 | 封装好的电路单元 | module add(...) 搭建加法器电路 |
| input | 模块外部信号输入引脚 | 电路板输入焊盘 | input wire clk 时钟信号输入 |
| output | 模块信号对外输出引脚 | 电路板输出焊盘 | output wire led 控制LED输出 |
| wire | 纯导线,只传信号、无记忆 | 电路板铜线 | wire mid; 电路中间连接线 |
| reg | 寄存器,可存储0/1数据,有记忆 | D触发器存储单元 | reg q; 保存时钟触发的数据 |
| assign | 永久连线赋值,实时联动 | 硬件固定飞线 | assign y=a&b; 实时运算与门 |
重点:Wire 和 Reg 终极区分(新手最大误区)
-
Wire:纯导线,无记忆、不存数据,输入变输出立刻变,只能用assign驱动。
-
Reg:带记忆的触发器,平时数据保持不变,只有时钟触发时才更新数据。
四、核心重难点:组合逻辑 vs 时序逻辑
所有Verilog代码,只分为这两种逻辑,彻底搞懂就能打通所有代码逻辑。
4.1 组合逻辑:无时钟、无记忆、实时响应
核心特点:输出只由当前输入决定,和历史状态无关,不需要时钟,输入变、输出立刻变。
固定写法 :assign + wire + = 阻塞赋值
代码示例+逐行拆解
Plain
wire y; // 定义一根输出导线
assign y = a & b;// 永久连线:y永远等于a、b的与运算结果
运行过程与结果
-
硬件生成:一个独立的与门电路
-
实时运算规则:
a=0,b=0 → y=0
a=0,b=1 → y=0
a=1,b=1 → y=1
- 核心属性:无延迟、无记忆,全程实时联动
4.2 时序逻辑:有时钟、有记忆、边沿更新
核心特点 :依赖时钟信号,自带记忆功能,不会随输入实时变化,只有时钟上升沿瞬间才更新数据。
固定写法 :always @(posedge clk) + reg + <= 非阻塞赋值
代码示例+逐行拆解
Plain
reg q; // 定义寄存器,用于存储数据
always @(posedge clk)// 敏感列表:仅时钟上升沿(0跳1瞬间)触发
q <= d; // 时钟到来时,将d的值存入q保存
运行过程与结果
-
时钟未触发时:无论输入d怎么变化,q的值保持不变,锁住历史数据
-
时钟上升沿瞬间:瞬间抓取d的当前值,更新保存到q中
-
硬件生成:D触发器存储电路,具备记忆功能
极简记忆口诀
assign 对应组合逻辑,实时连线无记忆;always时钟块对应时序逻辑,边沿触发存数据。
五、新手必踩坑:赋值符号 = 和 <= 区别
90%新手报错、仿真结果异常,都是因为赋值符号用错,直接记死规则即可,无需深究原理。
5.1 固定使用规则(万能准则)
-
组合逻辑 assign 中:只用 = 阻塞赋值
-
时序逻辑 always 时钟块中:只用 <= 非阻塞赋值
5.2 正确与错误示范
Plain
// 正确:组合逻辑使用 =
assign y = a & b;
// 正确:时序逻辑使用 <=
always @(posedge clk) begin
q <= d;
end
// 错误!时序块禁止用=,会导致时序错乱、仿真出错
always @(posedge clk) begin
q = d;
end
5.3 人话通俗解释
-
= 阻塞赋值:立即同步更新,适合纯导线连线的实时逻辑
-
<= 非阻塞赋值:所有信号统一在时钟边沿同步更新,避免硬件电路互相干扰
六、高频运算符讲解(含C语言差异对比)
Verilog运算符看似和C语言一样,但硬件行为完全不同,新手极易混淆。
6.1 按位运算符(硬件高频使用)
& 按位与、| 按位或、^ 按位异或、~ 按位取反
规则:对二进制每一位单独运算,示例:3'b101 & 3'b011 = 3'b001
6.2 逻辑运算符(和C语言一致)
&& 逻辑与、|| 逻辑或、! 逻辑非
规则:只判断整体真假,多用于条件判断
6.3 独有运算符:拼接 {}
C语言无此运算符,作用是拼接多组二进制数据
示例:{3'b101,2'b11}
拆解:3位二进制101 + 2位二进制11,拼接结果为5位二进制 10111
6.4 核心本质差异
-
C语言
a+b:CPU复用同一个加法器,分时软件计算 -
Verilog
a+b:直接生成一个全新的硬件加法器,全程并行工作
七、完整可运行实战案例(串联所有知识点)
需求
搭建带时钟寄存的与门电路:实时计算a、b与运算结果,仅在时钟上升沿保存并输出结果。
Plain
module test_demo(
input wire clk, // 时钟输入
input wire a, // 输入信号a
input wire b, // 输入信号b
output reg q_out // 寄存器输出,存储结果
);
// 1. 组合逻辑:实时计算a、b与运算
wire mid_and;
assign mid_and = a & b;
// 2. 时序逻辑:时钟边沿保存结果
always @(posedge clk) begin
q_out <= mid_and;
end
endmodule
全程运行流程详解
-
上电后,组合逻辑持续工作,mid_and 随a、b实时变化,无延迟;
-
时钟无上升沿时,q_out 锁住原有数据,不会跟随输入变化;
-
时钟0→1上升沿瞬间,抓取当前mid_and的值,更新保存到q_out;
-
最终硬件:与门电路 + D触发器电路,双模块全程并行工作。
八、小白入门终极避坑总结
看完这篇,彻底告别Verilog入门迷茫,记住这4条核心准则即可:
-
思维转变:放弃C语言顺序执行思维,牢记Verilog是硬件并行电路。
-
信号区分:单纯连线用wire,需要存储数据、时钟更新用reg。
-
逻辑区分:实时无记忆用assign组合逻辑;时钟记忆更新用always时序逻辑。
-
赋值规则:assign只用=,时钟always块只用<=,绝不混用。