Veloris.
返回索引
概念基础 2026-02-11

硬件描述语言入门:Verilog 不是 C,别用写程序的思维写电路

6 分钟
2.0k words

Verilog 不是编程语言:一个嵌入式FPGA工程师的 HDL 入门指南

💡 如果你有 C / Python 的编程基础,刚开始接触 Verilog 时一定会觉得它”长得很像编程语言”——有变量、有 if-else、有 for 循环、甚至还有函数。

但这是一个危险的错觉。

Verilog 不是在”编程”,而是在描述一个电路。你写的每一行代码,都对应着芯片里的一段真实硬件。如果你用”写软件”的思维来写 Verilog,你会掉进无数的坑里——综合出来的电路要么功能错误,要么面积爆炸,要么时序全崩。

这篇文章会帮你完成一次关键的思维转换:从”软件编程思维”切换到”硬件描述思维”。这是学习 FPGA 最难也最重要的一步。


目录


1. 什么是 HDL

HDL(Hardware Description Language,硬件描述语言) 是用文本形式描述数字电路结构和行为的语言。它不是让 CPU “执行”的指令,而是让 EDA 工具”翻译”成电路的图纸。

目前主流的 HDL 有两种:

语言特点主流地区最新标准
Verilog语法接近 C,上手快国内、北美、亚洲IEEE 1364-2005
VHDL语法严谨,类型检查强欧洲、航空航天IEEE 1076-2008

💬 你可能会问:Verilog 和 VHDL 选哪个?

如果你在国内做 FPGA 开发,选 Verilog——教程多、社区大、招聘要求也以 Verilog 为主。两种语言没有本质优劣,重要的不是语言本身,而是用 HDL 为真实电路建模的思维方式


2. HDL 和 C 语言的三大本质区别

这是本文最重要的一节。理解了这三个区别,你就抓住了 HDL 的灵魂。

区别一:并发 vs 串行

// C语言:逐行串行执行
a = b + c;    // 先执行这行
d = e + f;    // 再执行这行
// Verilog:所有语句同时执行
assign a = b + c;    // 这两行
assign d = e + f;    // 同时发生!

C 语言运行在 CPU 上,CPU 逐条取指令、逐条执行——串行。而 Verilog 描述的是硬件电路,所有模块、所有 assign 语句、所有 always 块都在同时运行。这是硬件的物理特性:电路一旦通电,所有部分同时工作。

区别二:互联 vs 调用

C 语言中的函数通过”调用-返回”通信。Verilog 中的模块通过信号线互联——它们不是”调用关系”,而是”连线关系”。

// 模块实例化 = 连线,不是函数调用
adder u_add (
    .a   (data_a),    // 信号线连接
    .b   (data_b),
    .sum (result)
);

当你”实例化”一个模块时,你并不是在”调用”它,而是在 PCB 上”焊接”了一颗芯片,然后用导线把信号接上去。

区别三:时间 vs 无时间

C 语言没有”时间”的概念——代码执行多快取决于 CPU 频率,程序员通常不关心。但在硬件世界里,时间就是一切

  • 信号从输入到输出有传播延迟
  • 寄存器只在时钟边沿采样数据
  • 建立时间(Setup)和保持时间(Hold)必须满足
// 时钟边沿驱动——这是硬件世界的心跳
always @(posedge clk) begin
    q <= d;  // 只在时钟上升沿,q才更新为d的值
end

💡 工程师手记:我从 C 语言转到 Verilog 时,最难适应的就是”并发”。写 C 的时候,我习惯了”上一行执行完才执行下一行”。写 Verilog 时,我还在用这个思维,结果综合出来的电路完全不是我想的样子。直到有一天我对自己说:“忘掉代码,想象电路”——从那以后一切才开始顺畅。


3. Verilog 核心概念速览

基本设计单元:module

Verilog 的一切都围绕 module(模块) 展开。一个 module 就像一颗芯片——有输入引脚、有输出引脚、有内部逻辑。

module led_blink (
    input  wire clk,       // 输入:时钟
    input  wire rst,       // 输入:复位
    output reg  led        // 输出:LED
);

    reg [23:0] counter;

    always @(posedge clk) begin
        if (rst)
            counter <= 24'd0;
        else
            counter <= counter + 1'b1;
    end

    always @(posedge clk) begin
        if (rst)
            led <= 1'b0;
        else if (counter == 24'd0)
            led <= ~led;
    end

endmodule

一个 module 由五个部分组成:

部分说明
端口定义模块的输入输出”引脚”
I/O 声明每个端口的方向(input / output / inout)和位宽
参数声明可选,用 parameter 定义可配置的常量
内部信号wirereg 类型的中间信号
功能定义assignalways、模块实例化等

参数化设计

通过 parameter 让模块可配置,实现代码复用:

module counter #(
    parameter WIDTH = 8     // 默认8位
)(
    input  wire             clk,
    input  wire             rst,
    output reg [WIDTH-1:0]  count
);
    always @(posedge clk) begin
        if (rst) count <= {WIDTH{1'b0}};
        else     count <= count + 1'b1;
    end
endmodule

// 实例化时指定参数
counter #(.WIDTH(16)) u_cnt16 (.clk(clk), .rst(rst), .count(cnt));

4. 三种描述方法

Verilog 提供三种描述硬件的方式,它们可以混合使用:

数据流描述(assign)

assign 连续赋值,适合描述组合逻辑

assign y = a & b;           // 与门
assign sum = a + b;          // 加法器
assign mux = sel ? d1 : d0;  // 2选1 MUX

特点:输入变化 → 输出立即跟随变化(有传播延迟),只能给 wire 类型赋值。

行为描述(always / initial)

always 块描述时序逻辑和复杂组合逻辑

// 时序逻辑:时钟边沿触发
always @(posedge clk) begin
    if (rst) q <= 1'b0;
    else     q <= d;
end

// 组合逻辑:敏感列表用 *
always @(*) begin
    case (sel)
        2'b00: out = in0;
        2'b01: out = in1;
        2'b10: out = in2;
        2'b11: out = in3;
    endcase
end

关键区别

  • <=(非阻塞赋值):用于时序逻辑always @(posedge clk)
  • =(阻塞赋值):用于组合逻辑always @(*)
  • 绝对不要混用! 这是 Verilog 新手最常犯的错误之一。

结构化描述(实例化)

通过实例化已有模块来搭建更大的系统——就像用现成的芯片搭电路:

// 实例化一个加法器模块
adder u_add (
    .a   (data_a),
    .b   (data_b),
    .sum (result)
);

核心理解:Verilog 中所有 always 块、所有 assign 语句、所有实例化模块——它们都是并行执行的。书写顺序不影响执行顺序。它们之间通过信号名互相连接。


5. 可综合 vs 不可综合

这是 Verilog 初学者必须搞清楚的一个概念:不是所有 Verilog 语法都能变成电路

类型说明示例
可综合能被综合工具转化为真实电路的语句assignalways @(posedge clk)if-elsecase+*
不可综合只能在仿真中使用,无法变成电路#10(延迟)、initial$display$readmemh

判断法则:如果你自己都想象不出这段代码对应什么样的硬件电路,那它大概率不可综合。

// ✅ 可综合:你能想象出这是一个加法器
assign c = a + b;

// ✅ 可综合:你能想象出这是一个MUX
assign y = (sel) ? d1 : d0;

// ❌ 不可综合:硬件里没有"等10纳秒"这种东西
#10 c = b;

// ❌ 不可综合:除法没有直观的硬件实现(需要算法展开)
assign result = a / b;

不可综合的语法并非没用——它们是 Testbench(仿真测试台) 的核心工具。initial 块用于生成测试激励,#10 用于控制仿真时序,$display 用于打印调试信息。


6. 总结

要点核心内容
HDL 是什么硬件描述语言,描述电路结构,不是编程语言
与 C 的三大区别并发 vs 串行、互联 vs 调用、有时间 vs 无时间
Verilog 基本单元module = 一颗芯片(端口 + 参数 + 信号 + 逻辑)
三种描述方法数据流(assign)、行为(always)、结构化(实例化)
可综合 vs 不可综合能想象出电路 = 可综合;只为仿真服务 = 不可综合

给初学者的一句话:学 Verilog 最重要的不是背语法,而是每写一行代码都问自己:这对应什么样的电路? 当你能在脑海中把代码”翻译”成门电路和寄存器时,你就真正入门了。


常见问题

Q1:Verilog 和 SystemVerilog 什么关系?

SystemVerilog 是 Verilog 的超集,在可综合部分增加了 logic 类型、interfaceenum 等便利特性,在验证部分增加了面向对象、随机约束等高级功能。新项目推荐使用 SystemVerilog,但核心的硬件描述思维是一样的。

Q2:为什么 <== 不能混用?

<=(非阻塞赋值)模拟的是寄存器行为——所有赋值在时钟边沿”同时”生效。=(阻塞赋值)模拟的是组合逻辑——按顺序立即生效。在时序逻辑中用 = 会导致仿真与实际硬件行为不一致,产生难以排查的 Bug。

Q3:Verilog 的 for 循环能综合吗?

能,但含义完全不同。C 的 for 是”重复执行 N 次”,Verilog 的 for 是”展开成 N 份并行硬件”。for (i=0; i<8; i=i+1) a[i] = b[i] & c[i]; 会被综合成 8 个独立的与门,而不是一个与门执行 8 次。

Q4:学 Verilog 之前需要什么基础?

最重要的是数字电路基础:组合逻辑(与/或/非门、MUX、译码器)、时序逻辑(触发器、计数器、状态机)。如果你有 C 语言基础会更容易上手语法,但记住:思维方式要完全重建


参考资料


系列导航:本文是「FPGA 入门系列」第 6 篇。

如果这篇文章对你有帮助,欢迎点赞、收藏,也欢迎在评论区分享你学 Verilog 时踩过的坑——相信我,你踩的坑别人也会踩。

End of file.