第三章 硬件描述语言入门:模块化设计、数据类型、运算符、并行与顺序语句
好,咱们进入第三章。说实话,很多初学者觉得硬件描述语言就是写代码,跟C语言差不多。嗯,这个想法很危险。我见过太多人把Verilog写成C语言,结果综合出来的电路完全不是那么回事。
硬件描述语言,说白了就是「用文本描述电路」。你写的每一行,最终都会变成实实在在的门电路、触发器和连线。这一点,请务必刻在脑子里。
3.1 模块化设计:搭积木的艺术
模块化设计,是我个人最推崇的设计方法。你想想看,一个复杂的芯片动辄几千万门,如果全写在一个文件里,调试起来简直要命。
模块(Module)是Verilog/VHDL的基本单元。一个模块对应一个硬件功能块,比如计数器、加法器、状态机。
看个最简单的例子:
module counter (
input wire clk,
input wire rst_n,
output reg [7:0] count
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
count <= 8'd0;
else
count <= count + 1'b1;
end
endmodule
这个模块有输入输出端口,内部实现了8位计数器。我在项目中遇到过,有人把复位逻辑写在顶层,结果底层模块复位时序对不上,整个系统跑飞了。所以我的习惯是:每个模块自己管好自己的复位,顶层只负责连线。
uart_tx、spi_master、fifo_ctrl。别用 module1、top 这种名字,三个月后你自己都看不懂。
3.2 数据类型:线网与寄存器
Verilog里最核心的数据类型就两个:wire 和 reg。很多人搞不清它们的区别,我当年也迷糊过。
| 类型 | 含义 | 典型用法 |
|---|---|---|
| wire | 线网类型,表示物理连线 | assign语句赋值、模块端口连接 |
| reg | 寄存器类型,表示存储单元 | always块内赋值 |
注意了,reg 不一定会综合成寄存器。如果你在组合逻辑的always块里用 reg,它综合出来只是连线。我曾经在代码评审时看到有人把组合逻辑的 reg 当成寄存器用,结果时序分析一塌糊涂。
VHDL那边,常用的是 std_logic 和 std_logic_vector。我个人觉得VHDL的类型系统更严谨,但写起来也啰嗦些。
-- VHDL 示例
signal data_bus : std_logic_vector(7 downto 0);
signal clk : std_logic;
3.3 运算符:不只是加减乘除
硬件描述语言的运算符,跟软件语言很像,但有些坑要注意。
- 算术运算符:+、-、*、/。除法尽量少用,综合出来资源很大。我一般用移位代替乘除。
- 逻辑运算符:&&、||、!。用于条件判断。
- 位运算符:&、|、^、~。这是硬件设计的核心,直接对应门电路。
- 拼接运算符:{ }。这个很实用,比如
{a, b}把两个信号拼起来。
== 比较两个32位信号,结果综合工具报了很多警告。原因是 === 和 == 在Verilog里行为不同。=== 会比较x和z状态,而 == 不会。仿真时可能没问题,但综合时要注意。
3.4 并行与顺序语句:硬件思维的分水岭
这是最让软件工程师头疼的地方。硬件里,所有模块是并行执行的。你写的两个always块,它们是同时工作的,不是顺序执行。
并行语句:
assign连续赋值语句always过程块(多个always块之间并行)module实例化(多个模块并行运行)
顺序语句:
always块内部的语句(按顺序执行)if-else、case语句for循环(综合时展开)
看个例子就明白了:
// 两个always块并行执行
always @(posedge clk) begin
a <= b + c;
end
always @(posedge clk) begin
d <= e & f;
end
// 但块内部是顺序的
always @(*) begin
if (sel)
y = a;
else
y = b;
end
嗯,这里要注意:组合逻辑用 always @(*),时序逻辑用 always @(posedge clk)。别搞混了。我见过有人把组合逻辑写成时序逻辑,结果多了一级寄存器,时序怎么调都调不对。
3.5 实战建议:从一个小模块开始
我建议初学者先别急着写大项目。从最简单的模块开始:
- 写一个8位加法器(组合逻辑)
- 写一个带使能的计数器(时序逻辑)
- 写一个简单的状态机(FSM)
- 把这三个模块连起来,做一个简单的控制逻辑
每写完一个模块,就用仿真工具跑一下波形。看看信号是不是按你预期变化的。我曾经在调试一个SPI接口时,发现数据总是错位,最后发现是时钟沿采错了。这种问题,仿真一眼就能看出来。
好了,第三章就到这里。记住:硬件描述语言不是写软件,是画电路。用这个思路去学,事半功倍。