4. 硬件描述语言基础:Verilog/VHDL 模块化设计
好,咱们开始聊硬件描述语言。说实话,很多刚入行的朋友觉得 Verilog 和 VHDL 就是写代码,跟 C 语言差不多。嗯,这个想法其实挺危险的。
我当年第一次用 Verilog 写了个计数器,仿真跑得飞起,结果综合出来占了大半个芯片。为什么?因为我用 C 语言的思维在写硬件。硬件描述语言,说白了就是「画电路」的另一种方式。你写的每一行代码,最终都会变成实实在在的门电路和触发器。
4.1 模块化设计:搭积木的艺术
模块化设计,我个人习惯叫它「搭积木」。一个复杂的收发器系统,你不可能在一个模块里写完所有逻辑。那会把自己逼疯的。
举个例子,一个简单的 SPI 收发器,我会拆成这样:
- 顶层模块:负责连接各个子模块
- 时钟分频模块:产生 SPI 的 SCLK
- 数据发送模块:处理 MOSI 数据
- 数据接收模块:处理 MISO 数据
- 控制状态机模块:管理收发流程
每个模块只做一件事,做好一件事。我在项目中遇到过有人把整个收发器写在一个 always 块里,3000 行代码。调试的时候,他看代码的眼神就像在看天书。
核心原则:一个模块的代码量最好控制在 200 行以内。超过这个数,你就该考虑拆分了。
4.2 数据类型:Verilog 的四值逻辑
Verilog 的数据类型跟 C 语言完全不一样。它用的是四值逻辑:0、1、X(未知)、Z(高阻)。
你想想看,在数字电路里,除了 0 和 1,还有什么时候会出现 X?
- 寄存器没初始化的时候
- 两个驱动源同时驱动一根线的时候
- 时序违规的时候
我曾经在调试一个高速收发器时,发现数据总线上偶尔出现 X。查了两天才发现,是复位信号没处理好,导致某个寄存器在启动时处于不确定状态。
| 数据类型 | 说明 | 默认值 |
|---|---|---|
wire |
线网类型,用于 assign 赋值 | Z(高阻) |
reg |
寄存器类型,用于 always 块 | X(未知) |
integer |
32 位有符号整数 | X |
parameter |
常量定义 | 用户指定 |
注意:reg 类型不一定会综合成寄存器。如果 always 块是组合逻辑,reg 综合出来就是连线。这个坑我踩过好几次。
4.3 运算符:位宽匹配很重要
Verilog 的运算符跟 C 语言很像,但有个关键区别:位宽。
看这个例子:
reg [7:0] a, b;
reg [3:0] c;
assign c = a + b; // 结果只取低 4 位
我刚开始做设计时,总觉得这样写没问题。直到有一次,一个加法器的结果总是对不上,查了半天才发现是位宽截断导致的。
常用的运算符分类:
- 算术运算符:
+-*/% - 逻辑运算符:
&&||! - 位运算符:
&|^~ - 关系运算符:
><==!= - 移位运算符:
<<>>
个人建议:做算术运算时,先把结果位宽算清楚。比如两个 8 位数相加,结果需要 9 位。别问我怎么知道的,问就是吃过亏。
4.4 always/process 块:时序与组合的区分
always 块是 Verilog 的核心。它有两种用法:
组合逻辑:
always @(*) begin
c = a & b;
end
时序逻辑:
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
q <= 1'b0;
else
q <= d;
end
这里有个关键点:组合逻辑用阻塞赋值 =,时序逻辑用非阻塞赋值 <=。为什么?
我解释一下:非阻塞赋值在 always 块结束时才更新,这模拟了触发器的行为——数据在时钟沿到来时同时更新。而阻塞赋值是立即生效的,适合组合逻辑。
我曾经见过有人把非阻塞赋值用在组合逻辑里,结果仿真和综合结果完全不一样。嗯,那场面挺尴尬的。
4.5 assign 语句:连续赋值
assign 语句用来描述组合逻辑,它跟 always 块的区别在于:
- assign 只能赋值给 wire 类型
- assign 是连续赋值,输入一变,输出立刻变
- assign 不能描述时序逻辑
举个例子:
wire [7:0] sum;
assign sum = a + b;
这相当于在硬件里画了一个加法器。输入 a 和 b 的任何变化,都会立即反映到 sum 上。
实用技巧:简单的组合逻辑用 assign,复杂的用 always @(*)。这样代码更清晰,也更容易维护。
4.6 知识体系总览
下面这张图,是我自己总结的 Verilog 基础学习路径。你跟着这个顺序走,基本不会迷路。
这张图把今天讲的内容串起来了。你从模块化设计开始,然后掌握数据类型和运算符,接着学会 always 块和 assign 语句,最后把它们综合起来,就能写出真正的硬件了。
好了,这一章的内容就到这。记住,写 Verilog 的时候,脑子里要想着电路,而不是软件。这个思维转变过来了,后面的路就好走了。