第三章 硬件描述语言入门:模块化设计、数据类型、运算符、并行与顺序语句

好,咱们进入第三章。说实话,很多初学者觉得硬件描述语言就是写代码,跟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_txspi_masterfifo_ctrl。别用 module1top 这种名字,三个月后你自己都看不懂。

3.2 数据类型:线网与寄存器

Verilog里最核心的数据类型就两个:wirereg。很多人搞不清它们的区别,我当年也迷糊过。

类型 含义 典型用法
wire 线网类型,表示物理连线 assign语句赋值、模块端口连接
reg 寄存器类型,表示存储单元 always块内赋值

注意了,reg 不一定会综合成寄存器。如果你在组合逻辑的always块里用 reg,它综合出来只是连线。我曾经在代码评审时看到有人把组合逻辑的 reg 当成寄存器用,结果时序分析一塌糊涂。

VHDL那边,常用的是 std_logicstd_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-elsecase 语句
  • 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)。别搞混了。我见过有人把组合逻辑写成时序逻辑,结果多了一级寄存器,时序怎么调都调不对。

核心要点:写Verilog时,脑子里要时刻想着「这个语句对应什么电路」。是连线?是寄存器?还是组合逻辑?想清楚了再下笔。

3.5 实战建议:从一个小模块开始

我建议初学者先别急着写大项目。从最简单的模块开始:

  1. 写一个8位加法器(组合逻辑)
  2. 写一个带使能的计数器(时序逻辑)
  3. 写一个简单的状态机(FSM)
  4. 把这三个模块连起来,做一个简单的控制逻辑

每写完一个模块,就用仿真工具跑一下波形。看看信号是不是按你预期变化的。我曾经在调试一个SPI接口时,发现数据总是错位,最后发现是时钟沿采错了。这种问题,仿真一眼就能看出来。

好了,第三章就到这里。记住:硬件描述语言不是写软件,是画电路。用这个思路去学,事半功倍。