Verilog 入门指南

发布: (2026年2月4日 GMT+8 15:26)
7 min read
原文: Dev.to

Source: Dev.to

Getting Started With Verilog 封面图

Silicon Monks

本文重点关注两件事

  • 必须理解的核心 Verilog 语法
  • 实际设计中使用的三种主要建模风格

如果你来自软件背景,请把 Verilog 看作是一种随时间描述硬件行为的方式,而不是逐行运行的程序。

什么是 Verilog?

Verilog 用于建模数字系统,例如

  • 组合逻辑
  • 时序逻辑
  • 有限状态机
  • 完整的 SoC 和 ASIC 设计

Verilog 代码可以

  • 仿真以验证功能
  • 综合以生成实际硬件

基本 Verilog 程序结构

每个 Verilog 设计都是由 模块 构成的。

module example_module (
    input  wire a,
    input  wire b,
    output wire y
);
    assign y = a & b;
endmodule

关键点

  • moduleendmodule 定义了一个硬件块。
  • 输入和输出描述了接口。
  • 内部逻辑描述了输出如何依赖于输入。

常见数据类型

Wire

用于连续连接。

wire sum;
assign sum = a ^ b;

Reg

用于在过程块内部存储值。

reg q;

尽管名字叫 reg,但它并不总是表示触发器。它仅表示信号在 always 块内部被赋值。

常用运算符

- Arithmetic   : +  -  *  /
- Logical     : &&
- Bitwise     : &
- Comparison  : ==  !=  
- Assignment  : =  (blocking)   <=  (non‑blocking)

Verilog 建模风格

Verilog 支持三种主要的建模风格。选择合适的风格取决于你正在设计的内容以及希望描述的抽象程度。

1. 行为建模

关注电路 做什么,而不是 如何构建

示例:2:1 多路复用器

module mux2_behavioral (
    input  wire a,
    input  wire b,
    input  wire sel,
    output reg  y
);
    always @(*) begin
        if (sel)
            y = b;
        else
            y = a;
    end
endmodule

特性

  • 使用 always 块。
  • 易于阅读和编写。
  • 适用于控制逻辑和有限状态机(FSM)。
  • 在谨慎编写时可综合。

2. 数据流建模

使用连续赋值和表达式描述逻辑。

示例:全加器

module full_adder_dataflow (
    input  wire a,
    input  wire b,
    input  wire cin,
    output wire sum,
    output wire cout
);
    assign sum  = a ^ b ^ cin;
    assign cout = (a & b) | (b & cin) | (a & cin);
endmodule

特性

  • 使用 assign 语句。
  • 与布尔方程非常接近。
  • 最适合组合逻辑。
  • 简洁明了。

3. 结构化建模

将低层模块相互连接,类似于原理图。

示例:使用两个半加器实现全加器

module half_adder (
    input  wire a,
    input  wire b,
    output wire sum,
    output wire carry
);
    assign sum   = a ^ b;
    assign carry = a & b;
endmodule
module full_adder_structural (
    input  wire a,
    input  wire b,
    input  wire cin,
    output wire sum,
    output wire cout
);
    wire s1, c1, c2;

    half_adder ha1 (a,   b,   s1, c1);
    half_adder ha2 (s1,  cin, sum, c2);

    assign cout = c1 | c2;
endmodule

特性

  • 层次化设计。
  • 反映真实硬件连接。
  • 在大型设计中常用。
  • 提高可复用性和清晰度。

4. 门级建模

直接使用 Verilog 原语。

示例:半加器

module half_adder (
    input  wire a,
    input  wire b,
    output wire sum,
    output wire carry
);
    xor (sum,   a, b);
    and (carry, a, b);
endmodule

特性

  • 纯门级描述。
  • 对于大型设计非常繁琐。

阻塞与非阻塞赋值

这在 Verilog 中至关重要。

阻塞 (=)

用于组合逻辑。

always @(*) begin
    x = a & b;
    y = x | c;
end

非阻塞 (<=)

用于时序逻辑。

always @(posedge clk) begin
    q <= d;
end

经验法则

  • 在组合逻辑中使用 =
  • 在时钟触发的(顺序)逻辑中使用 <=

建模顺序逻辑

示例:具有低电平有效复位的 D 触发器

module dff (
    input  wire clk,
    input  wire rst,
    input  wire d,
    output reg  q
);
    always @(posedge clk or negedge rst) begin
        if (!rst)
            q <= 1'b0;
        else
            q <= d;
    end
endmodule

所有代码片段均采用 Verilog‑2001 语法编写,除另有说明外均可综合。

Verilog 触发器示例

module dff (
    input  wire clk,
    input  wire rst,
    input  wire d,
    output reg  q
);
    always @(posedge clk or negedge rst) begin
        if (!rst)
            q <= 1'b0;
        else
            q <= d;
    end
endmodule

这种写法是可综合的,并且可以直接映射到硬件触发器。

何时使用每种建模风格

  • 行为建模 – 最适合用于控制逻辑和有限状态机,关注电路做什么而不是怎么做。对于复杂的决策逻辑,代码更易阅读和维护。
  • 数据流建模 – 适用于组合逻辑。它与布尔方程紧密对应,常用于算术电路、多路复用器以及输出直接由输入推导的简单逻辑块。
  • 结构建模 – 用于大型层次化设计。它将较小的模块连接起来形成更大的系统,反映实际硬件结构,适合可复用和可扩展的设计。
  • 门级建模 – 主要用于演示和学习。由于依赖 Verilog 门原语,代码冗长且难以管理,在现代大规模设计中很少使用,因此不适合构建可复用或可扩展的设计。

在实际项目中,通常会在同一个设计中混合使用三种风格(行为、数据流、结构)。

最终思考

Verilog 强大之处在于它允许你在不同抽象层次上对硬件进行建模。理解语法很重要,但掌握各种建模风格才是让你的设计保持整洁、可扩展且可综合的关键。

Back to Blog

相关文章

阅读更多 »

Switch Case 语句

什么是 switch 语句?Java 中的 switch 关键字用于在多个备选项中执行其中一个代码块。表达式只会被求值一次,并且比较…

Litestream 可写 VFS

请提供您希望翻译的具体摘录或摘要文本,我将为您翻译成简体中文。