2. Verilog基础语法(上):模块结构、数据类型、运算符、assign语句
各位同学,咱们今天开始啃Verilog的硬骨头。说实话,很多初学者一上来就被语法吓住了。其实没那么玄乎,你把它想象成画电路图的另一种方式就行。我当年刚学的时候,也是对着书本发懵,后来发现——说白了,Verilog就是在描述硬件长什么样、怎么连。
2.1 模块结构——Verilog的骨架
每个Verilog设计,都是从module开始,到endmodule结束。这就像盖房子先搭框架。模块就是你的电路单元,可以是一个与门,也可以是一个完整的电机驱动核。
module pwm_generator(
input wire clk,
input wire rst_n,
input wire [7:0] duty_cycle,
output reg pwm_out
);
// 内部逻辑写在这里
endmodule
注意看,端口声明有三种:input、output、inout。我在项目中遇到过新手把inout当普通输出用,结果仿真死活不对。双向口要慎用,除非你真的很清楚自己在干什么。
核心要点:模块就是硬件单元。一个模块对应一块电路。别想着用模块去实现复杂的软件算法,那是CPU干的事。
2.2 数据类型——线网与寄存器
Verilog里最常用的两种数据类型:wire和reg。嗯,这里要注意,很多人一开始搞混它们。
- wire:物理连线。它不存储值,只是传递驱动源的值。比如把A门的输出连到B门的输入。
- reg:寄存器。它能保持上一次赋的值,直到被再次赋值。但注意,
reg不一定会综合成触发器,也可能只是连线。
我建议你这样记:在always块里赋值的变量,用reg;在assign语句里赋值的,用wire。这个规则在99%的情况下都适用。
个人经验:我曾经在写SPI接口时,把wire类型误写成reg,结果综合工具报了一大堆警告。排查了半天才发现是类型搞错了。从那以后,我习惯在声明时就明确标注用途。
2.3 运算符——硬件的数学
Verilog的运算符跟C语言很像,但有些细节完全不同。你想想看,C语言里a = b + c是瞬间完成的,但在硬件里,加法器是有延迟的。
| 运算符类别 | 符号 | 说明 |
|---|---|---|
| 算术 | + - * / % | 乘除法综合出来面积大,慎用 |
| 位运算 | & | ~ ^ | 直接对应逻辑门 |
| 归约运算 | & | ^ ~^ | 单目运算符,对向量所有位操作 |
| 逻辑运算 | && || ! | 结果只有0或1 |
| 移位 | << >> | 左移补0,右移补0 |
| 拼接 | { } | 把多个信号拼成一个向量 |
举个例子,归约运算符很多人不常用,但其实很实用。比如你想检查一个8位总线是否全为0:
wire all_zero;
assign all_zero = ~|data_bus; // 归约或,再取反
这比写data_bus == 8'b0更节省资源。我在做电机电流采样时,经常用归约运算快速判断过流状态。
避坑指南:我曾经在计算PWM占空比时,直接用*做乘法,结果综合出来的乘法器占了大半个FPGA。后来改用移位加加法,面积直接降了70%。记住:硬件里乘除法很贵,能不用就不用。
2.4 assign语句——连续赋值
assign是Verilog里最直观的语句。它描述的是组合逻辑,只要右边表达式有变化,左边立刻更新。说白了,就是一根导线,只不过这根导线可以带点逻辑。
// 一个简单的比较器
assign led_on = (counter > threshold) ? 1'b1 : 1'b0;
// 电机方向控制
assign motor_dir = (speed_ref > speed_actual) ? 1'b1 : 1'b0;
注意,assign的左边必须是wire类型。如果你试图给reg赋值,综合工具会报错。这个错误我见过太多次了,尤其是从软件转过来的工程师。
另外,多个assign语句是并行执行的,不是顺序执行。这是硬件和软件最本质的区别。你写十个assign,它们同时工作,就像十个独立的逻辑门同时翻转。
实战技巧:在写电机驱动时,我习惯把所有的组合逻辑都用assign实现,只在always块里放时序逻辑。这样代码结构清晰,仿真也容易调试。你试试看,维护起来会轻松很多。
2.5 本章知识体系
下面这张图,是我自己总结的Verilog基础语法脉络。你照着这个思路学,不会乱。
这张图把本章的四个核心知识点串起来了。模块是外壳,数据类型是材料,运算符是工具,assign是施工方法。四者缺一不可。
我的建议:学语法别死记硬背。找个小项目练手,比如写个PWM发生器。边写边查,边查边改。我当年就是这么过来的,三个月后基本语法就烂熟于心了。
好了,这一章的内容就到这里。记住,Verilog不是编程语言,是硬件描述语言。你写的每一行代码,最终都会变成实实在在的电路。带着这个意识去学,事半功倍。