夜雨聆风学习资料网

ARTICLE · 1089117

Verilator 源码深度剖析:内部组件、关系与执行流程

Verilator 源码深度剖析:内部组件、关系与执行流程

摘要:本文从源代码层面深入剖析 Verilator 的内部架构,涵盖其目录结构、核心类体系(V3Global、V3Options、AstNode、V3Number、V3Graph 等)、编译管线的四大阶段(前端 → 语义分析 → 变换优化 → 后端代码生成),以及 ~50+ 个 AST 变换 Pass 的执行流程。全文以大量流程图、列表和表格为主,每个流程图后均配有列表说明,适合希望理解 Verilator 内部工作机制或准备参与贡献的开发者阅读。

目录:

  • • 📁 目录结构一览
  • • 🏛️ 核心组件体系
    • • V3Global:全局状态中心
    • • V3Options:配置状态中心
    • • AstNode:AST 节点基类
    • • V3Number:四值逻辑数值系统
    • • V3Graph:图算法基础设施
    • • 核心组件全景关系图
  • • ⚙️ 编译管线:四大阶段
    • • 入口点与初始化
    • • 阶段一:前端(文本 → AST)
    • • 阶段二:语义分析与展开
    • • 阶段三:变换与优化 Pass
    • • 阶段四:后端代码生成
  • • 🔄 变换 Pass 的详细执行序列
  • • 🧩 Pass 执行架构:Visitor 模式
  • • 🎯 总结

📁 目录结构一览

Verilator 的源代码组织清晰,各目录职责分明:

目录
职责
bin/verilator
 Perl 包装脚本,处理环境变量并调用核心二进制
include/
运行时头文件与全局例程(verilated*.h、verilated*.cpp、verilated.mk)
src/
翻译器核心源代码(编译器主体)
test_regress/
内部回归测试套件
examples/
示例(hello_world_c、hello_world_sc 等)

📋 说明:

  • • 根节点 ROOT:代表 Verilator 源码仓库的根目录,所有其他目录都从它展开。
  • • bin/ 目录:
    • • verilator:用户直接调用的 Perl 包装脚本,负责设置环境变量。
    • • verilator_bin:真正执行编译的核心二进制。
    • • verilator_bin_dbg:调试版本,保留更多断言与调试符号。
  • • src/ 目录:
    • • V3*.cpp/.h:翻译器的全部核心源代码。
    • • verilog.y:Bison 语法描述文件,定义 Verilog 语法规则。
    • • verilog.l:Flex 词法描述文件,定义 Token 识别规则。
  • • include/ 目录:
    • • verilated.h:仿真运行时核心头文件,用户 App 必须包含。
    • • verilated.mk:生成的 Makefile 会包含此文件,统一构建规则。
    • • verilated_vcd_c.h:VCD 波形输出支持。
  • • 其他目录:
    • • test_regress/:内部回归测试,保证每次提交不破坏现有功能。
    • • examples/:入门示例,包含 C 与 SystemC 两种风格。
    • • docs/:文档源文件,生成官网上的手册。
  • • bin/verilator:Perl 包装脚本,负责命令参数处理、设置 VERILATOR_ROOT 环境变量、可选地在调试器中包装执行,然后调用真正的二进制 verilator_bin(或调试模式下的 verilator_bin_dbg)。
  • • include/:包含全局例程和头文件,用户编译生成的 C++ 模型时需要将这些文件加入编译器的 -I 路径。
  • • src/:Verilator 翻译器的全部核心源代码,所有 V3* 开头的文件都在此。
  • • 输出目录:所有生成的文件放在 --Mdir 指定的目录中,默认为 obj_dir。

🏛️ 核心组件体系

Verilator 的内部实现围绕几个核心类展开,它们构成了整个编译器的骨架。

📋 说明:

  • • 🌐 全局状态层:
    • • V3Global:全局单例,持有 AST 根节点与所有全局配置。
    • • V3Options:存储命令行解析结果,约 150+ 个选项变量。
    • • 两者关系:V3Global包含V3Options。
  • • 🌳 AST 层:
    • • AstNode:所有 AST 节点的抽象基类。
    • • AstNetList:整棵 AST 的根节点。
    • • AstModule:表示一个 Verilog 模块。
    • • AstVar:表示信号 / 变量。
    • • AstAlways:表示 always 过程块。
  • • 🔧 工具层:
    • • V3Number:四值逻辑数值系统,用于编译期常量计算。
    • • V3Graph:通用有向图框架,用于数据流分析。
    • • FileLine:源码位置追踪,用于错误报告。
  • • ⚙️ 变换 Pass 层:
    • • VNVisitor:所有 Pass 的基类。
    • • V3Width:宽度推断。
    • • V3Const:常量传播。
    • • V3Order:执行顺序排序。
  • • 📤 后端层:
    • • V3EmitC:C++ 代码发射器,遍历 AST 输出最终代码。
  • • 关键依赖关系:
    • • V3Global持有 AST 根节点。
    • • AstNode使用V3Number 表示常量。
    • • VNVisitor遍历AstNode,并构建V3Graph。
    • • V3EmitC遍历 AST 树根进行代码发射。

V3Global:全局状态中心

V3Global 是整个 Verilator 程序的顶层类,作为全局单例(实例名为 v3Global)存在。

  • • 核心成员:
    • • m_rootp:指向 AST 的根节点 AstNetList。
    • • opt:包含 V3Options 对象,存储所有命令行选项和配置状态。
  • • 关键方法:
    • • boot():初始化子系统。
    • • readFiles():读取并解析输入文件。
  • • 使用模式:在 Verilator 代码中,任何地方都可以通过 v3Global 访问全局状态。

📋 说明:

  • • 单例 VG(V3Global):整个程序生命周期只有一个实例,任何地方都能访问。
  • • 成员 m_rootp:指向 AST 根节点,编译全过程围绕它进行。
  • • 成员 opt:指向 V3Options,所有配置从这里读取。
  • • 方法 boot():由 main() 调用,初始化全局子系统。
  • • 方法 readFiles():由 main() 调用,读取并解析源文件。
  • • 访问者 ANY:任意 V3 代码都可以通过 v3Global.rootp 或 v3Global.opt 访问全局状态。
  • • 外部产物:
    • • AST 树:由 m_rootp 指向。
    • • 编译选项:由 opt 指向。

V3Options:配置状态中心

V3Options 是 Verilator 的配置中心,使用 Pimpl(Pointer to Implementation)模式隐藏实现细节,包含约 150+ 个成员变量表示不同的配置设置。

  • • 核心职责:
    • • 解析命令行参数(通过 V3OptionParser)。
    • • 存储所有选项值(如 --cc、--exe、--trace 等)。
    • • 提供配置查询接口。
  • • 访问方式:通过 v3Global.opt 访问单例。

📋 说明:

  • • 输入 CMD:用户在命令行输入的所有参数。
  • • 解析器 VP(V3OptionParser):负责把命令行拆解成结构化选项。
  • • 核心 VO(V3Options):解析后的配置单例,所有模块都从这里读取配置。
  • • 配置项示例:
    • • --cc:选择 C++ 代码生成模式。
    • • --exe:生成可执行文件。
    • • --trace:启用波形追踪。
    • • --top-module:指定顶层模块。
    • • --Mdir:指定输出目录。
    • • ... 150+ 选项:还有大量其他选项。
  • • 消费者:
    • • V3Width:宽度推断时读取配置(如是否有符号扩展)。
    • • V3Order:排序时读取配置(如是否需要多线程)。
    • • V3EmitC:发射代码时读取配置(如输出目录、文件命名)。

AstNode:AST 节点基类

AstNode 是 Verilator 抽象语法树中所有节点的抽象基类,是整个编译过程中核心的中间表示。

  • • 节点链接结构:
    • • op1p() ~ op4p():四个子节点指针,用于表示操作数或子语句。
    • • nextp():指向下一个兄弟节点。
    • • backp():指向父节点或前一个兄弟节点。
  • • 类型系统:
    • • type():返回 VNType 枚举值,标识具体节点子类。
    • • dtypep():指向 AstNodeDType,描述节点值的数据类型。
  • • 源码追踪:
    • • fileline():返回 FileLine* 对象,追踪源文件和行号。
  • • 类型检查宏(避免 RTTI 开销):
    • • VN_IS(nodep, NodeType):类型判断。
    • • VN_CAST(nodep, NodeType):类型转换。
    • • VN_AS(nodep, NodeType):断言类型并转换。

📋 说明:

  • • 根基类 AstNode:所有 AST 节点的共同祖先,提供统一的类型系统与链接机制。
  • • 子类分类:
    • • AstNetList:整个 AST 的根节点。
    • • AstModule:表示一个 Verilog 模块。
    • • AstNodeExpr:所有表达式的基类,如常量、加法、位选择。
    • • AstNodeStmt:所有语句的基类,如 always、if、赋值。
    • • AstNodeDType:数据类型描述节点。
  • • 表达式子类:
    • • AstConst:编译期常量(值存在 V3Number 中)。
    • • AstAdd:加法运算。
    • • AstSel:位选择(如 a[3:0])。
  • • 语句子类:
    • • AstAlways:always 块。
    • • AstIf:条件语句。
    • • AstAssign:赋值语句。
  • • 继承层次:AstNode 是所有节点的祖先,AstNodeExpr 和 AstNodeStmt 是两大分支。

AST 节点链接结构示意图:

📋 说明:

  • • 根节点 N1(AstNetList):整棵树的入口。
  • • 模块节点 N2(AstModule):根节点的唯一子模块。
  • • 变量链:N3(clk)→ N4(rst)→ N5(count),通过 nextp 连接成兄弟链表。
  • • 过程块 N6(AstAlways):位于变量链之后。
  • • if 语句 N7:always 块的子节点。
  • • 赋值语句 N8(AstAssign):if 语句的子节点。
  • • backp 反向链接:N2 指向 N1,N3 指向 N2,N4 指向 N3,形成双向链表。
  • • nextp 虚线箭头:表示兄弟节点的横向链接。
  • • 实线箭头:表示父子节点的纵向链接。

V3Number:四值逻辑数值系统

V3Number 是 Verilator 内部的数值表示系统,在编译期常量折叠和 AST 中表示常量值。

  • • 核心能力:
    • • 任意精度整数运算(宽度可配置)。
    • • 四值逻辑(0、1、X、Z)表示。
    • • 64 位 IEEE 754 浮点数支持。
    • • 字符串支持。
  • • 存储策略(按位宽优化):
    • • 内联存储:宽度 ≤ MAX_INLINE_WIDTH(通常 128 或 256 位)。
    • • 动态存储:宽度更大的数字使用动态分配的向量。
    • • 字符串存储:字符串类型存储在 m_string 成员中。
  • • 四值逻辑编码:使用双比特编码。
    • • 00 → 逻辑 0
    • • 10 → 逻辑 1
    • • 01 → Z(高阻)
    • • 11 → X(未知)

📋 说明:

  • • 核心 NUM(V3Number):任意精度数值表示。
  • • 判断 MODE:根据位宽选择存储策略。
  • • 三种存储策略:
    • • INLINE:位宽较小,直接栈上存储,避免堆分配。
    • • DYN:位宽较大,堆分配向量。
    • • STR:字符串类型,存储在 m_string 成员。
  • • 四值编码 ENC:
    • • 00:逻辑 0。
    • • 10:逻辑 1。
    • • 01:Z(高阻)。
    • • 11:X(未知)。
  • • 运算符重载 OPS:
    • • OP1 算术运算(加减乘除)。
    • • OP2 位运算(与或非异或)。
    • • OP3 比较运算(大小相等)。
    • • OP4 浮点运算。

V3Graph:图算法基础设施

V3Graph 是 Verilator 提供的通用有向图框架,许多优化 Pass 使用图算法来分析数据流并确定安全的变换。

  • • 核心类:
    • • V3Graph:图容器,管理顶点和边。
    • • V3GraphVertex:顶点基类。
    • • V3GraphEdge:边基类。
  • • 典型用途:
    • • 数据流分析(依赖图构建)。
    • • 拓扑排序(确定 eval() 中语句的执行顺序)。
    • • 弱连通分量分析。
  • • 输出:支持 GraphViz dot 格式输出,便于可视化。

📋 说明:

  • • 核心 VG(V3Graph):图容器,管理所有顶点和边。
  • • 顶点与边:
    • • VV(V3GraphVertex):顶点基类,所有顶点继承自此。
    • • VE(V3GraphEdge):边基类,所有边继承自此。
  • • 三种典型用途:
    • • U1 数据流分析:构建依赖图,分析信号之间的依赖关系。
    • • U2 拓扑排序:确定 eval() 中语句的执行顺序。
    • • U3 弱连通分量:拆分逻辑块以便并行执行。
  • • 输出:
    • • ORDER:拓扑排序结果,决定 C++ 代码中语句顺序。
    • • DOT:GraphViz dot 格式文件,可用图形工具可视化依赖图。

核心组件全景关系图

📋 说明:

  • • 🌐 顶层控制层:
    • • main():程序入口。
    • • process():编译主流程,负责编排各阶段。
  • • ⚙️ 全局状态层:
    • • V3Global:全局单例,持有 AST 与配置。
    • • V3Options:命令行解析结果。
    • • main() 调用 process(),process() 通过 V3Global 访问所有状态。
  • • 🌳 AST 中间表示层:
    • • AstNetList:树根,V3Global 持有。
    • • AstModule:模块节点。
    • • AstVar、AstAlways:模块内的变量与过程块。
    • • AstNodeStmt、AstNodeExpr:语句和表达式基类。
  • • 🔧 工具支撑层:
    • • V3Number:数值系统,被多个 Pass 使用。
    • • V3Graph:图框架,用于数据流分析。
    • • FileLine:源码位置追踪,用于错误报告。
  • • ⚙️ Pass 变换层:
    • • VNVisitor:所有 Pass 的基类,遍历 AST。
    • • V3Param、V3Width、V3Const、V3Order:典型 Pass。
    • • VNVisitor 遍历 AST 根节点,并使用 V3Number 与 V3Graph。
  • • 📤 后端发射层:
    • • V3EmitC:遍历 AST 树根,使用 V3Number 发射最终 C++ 代码。

⚙️ 编译管线:四大阶段

Verilator 的编译管线分为四个顺序执行的阶段,从 Verilog/SystemVerilog 源代码到最终的可执行仿真模型。

📋 说明:

  • • 🚪 入口点:
    • • bin/verilator:用户命令行调用的 Perl 脚本。
    • • verilator_bin:真正执行的二进制。
    • • main():C++ 程序入口。
    • • V3Global::boot():初始化全局状态。
    • • process():进入编译主流程。
  • • 📝 阶段一:前端:
    • • V3PreProc:预处理,处理宏与包含。
    • • V3PreLex:基于 Flex 的词法分析。
    • • V3ParseBison:基于 Bison 的语法分析。
    • • 输出:AST 抽象语法树。
  • • 🔍 阶段二:语义分析:
    • • Link:链接引用。
    • • V3Param:参数展开。
    • • V3Width:宽度推断。
    • • Elaboration:设计展开。
  • • ⚡ 阶段三:变换优化:
    • • ~50+ 变换 Pass:一系列 AST 变换。
    • • V3Order:调度排序。
  • • 📤 阶段四:后端:
    • • V3EmitC:C++ 代码发射。
    • • 输出:.cpp / .h / .mk 文件。
  • • 阶段间数据流:process() ⇒ 前端 ⇒ AST ⇒ 语义分析 ⇒ 展开后 AST ⇒ 变换 ⇒ 优化后 AST ⇒ 后端 ⇒ 文件。

四大阶段数据流全景:

📋 说明:

  • • 输入 SRC:.v 或 .sv 源代码文件。
  • • 中间 AST 表示:
    • • A1:原始 AST(前端输出)。
    • • A2:展开后 AST(语义分析输出)。
    • • A3:优化后 AST(变换优化输出)。
  • • 输出 OUT:.cpp / .h / .mk 文件。
  • • 前端数据:
    • • T1:Token 流。
    • • T2:原始 AST。
  • • 语义分析数据:
    • • T3:位宽信息。
    • • T4:参数绑定。
  • • 优化数据:
    • • T5:常量折叠结果。
    • • T6:执行顺序图。
  • • 虚线箭头:表示数据的辅助流动方向。

入口点与初始化

  • • bin/verilator(Perl 包装脚本):
    • • 处理命令行参数。
    • • 设置 VERILATOR_ROOT 环境变量。
    • • 可选地在调试器(gdb、valgrind、rr)中包装执行。
    • • 调用核心二进制 verilator_bin(或 verilator_bin_dbg)。
  • • src/Verilator.cpp::main():
    • • 初始化 V3Options 解析命令行参数。
    • • 创建 V3Global 单例存储全局状态。
    • • 调用 V3Global::boot() 初始化子系统。
    • • 调用主函数 process()。

📋 说明:

  • • 输入 START:用户输入的命令行,例如 verilator --cc top.v。
  • • Perl 脚本 PERL:bin/verilator,进行环境配置。
  • • 环境变量 ENV:设置 VERILATOR_ROOT,让核心二进制找到运行时文件。
  • • 调试分支 DBG:
    • • 是:走 DBGBIN(verilator_bin_dbg),可被 gdb/valgrind 调试。
    • • 否:走 BIN(verilator_bin),普通执行。
  • • 核心入口 MAIN:Verilator.cpp::main() 函数。
  • • 参数解析 PARSE:V3Options 解析命令行,生成配置。
  • • 单例初始化 INIT:创建 V3Global,全局状态就绪。
  • • 子系统启动 BOOT:V3Global::boot() 初始化各子系统。
  • • 主流程 PROC:调用 process(),进入四大编译阶段。

阶段一:前端(文本 → AST)

前端将文本形式的 HDL 转换为抽象语法树(AST),分为三个子步骤:

子步骤
组件
实现文件
职责
预处理
V3PreProcV3PreProc.cpp
处理 include、define、ifdef 等指令,展开宏,管理条件编译
词法分析
V3PreLexverilog.l
基于 Flex 的分词器,识别关键字、标识符、数字、运算符
语法分析
V3ParseBisonverilog.y
基于 Bison 的解析器,从 token 流构造 AST 节点

前端处理流程图:

📋 说明:

  • • 输入 SRC:top.v 源代码文件。
  • • 预处理器 PP(V3PreProc):
    • • PP1:处理 include 指令,插入被包含文件。
    • • PP2:展开 define 宏。
    • • PP3:处理条件编译 ifdef / ifndef / else / endif。
    • • 输出:TOK Token 流。
  • • 词法分析 LEX(V3PreLex):
    • • 基于 Flex 实现。
    • • 输出多种 Token 类型:
      • • T1:yaINTNUM 整数常量。
      • • T2:yaID__ETC 标识符。
      • • T3:yaSTRING 字符串。
      • • T4:运算符 Token。
  • • 语法分析 PARSE(V3ParseBison):
    • • 基于 Bison 实现。
    • • 从 Token 流构造 AST 节点。
  • • AST 节点输出:
    • • N1:AstModule,模块节点。
    • • N2:AstVar,变量节点。
    • • N3:AstAlways,过程块。
    • • N4:AstAssign,赋值语句。
  • • 最终输出 AST:完整的抽象语法树。
  • • 预处理:维护 FileLine 对象进行文件和行号追踪。
  • • 词法分析:返回 yaINTNUM、yaID__ETC、yaSTRING 等 token,支持基于状态的扫描以适应不同语言上下文。
  • • 语法分析:创建专门的节点类型(AstModule、AstVar、AstAlways 等),管理解析状态。

阶段二:语义分析与展开

  • • 链接(Link):AST 中的 cell 被链接,读取并解析额外文件,函数、变量引用被链接到定义。
  • • 参数解析:参数被解析,设计被展开。
  • • 宽度推断(V3Width):为所有表达式计算位宽,是最复杂的 Pass 之一,处理 SystemVerilog 复杂的类型传播规则。

📋 说明:

  • • 输入 AST:前端生成的原始 AST。
  • • 链接 LINK:解析所有引用,读取被包含文件,将变量/函数引用链接到定义。
  • • 参数展开 PARAM(V3Param):为每个参数组合克隆模块,消除参数化。
  • • 宽度推断 WIDTH(V3Width):
    • • W1:插入 AstCCast,处理类型转换。
    • • W2:插入 AstExtend,处理符号/零扩展。
    • • W3:插入 AstSel,处理位提取。
    • • WIDTHC(V3WidthCommit):提交宽度推断结果。
  • • 展开 ELAB:生成完整的层次结构,将高层描述展开为低层逻辑。
  • • 输出 AST2:展开后 AST,准备进入变换优化阶段。

阶段三:变换与优化 Pass

Verilator 在 AST 上执行~50+ 个顺序变换 Pass,每个 Pass 实现为一个独立的 Visitor 类,遍历并修改 AST。

阶段四:后端代码生成

  • • V3EmitC:C++ 代码发射子系统的主协调器。
  • • 多个专门的发射类生成 C++ 模型的不同方面:emitc()、emitcInlines()、emitcSyms()、emitcTrace() 等。
  • • 最终输出 .cpp、.h 文件和构建系统文件(Makefile)。

📋 说明:

  • • 输入 AST:变换优化后的 AST。
  • • 协调器 EMITC(V3EmitC):代码发射主协调器,按类别调用不同发射器。
  • • 六大发射器:
    • • E1(emitc()):主 C++ 代码,输出 Vtop.cpp 与 Vtop.h。
    • • E2(emitcInlines()):内联函数,输出 Vtop__Inlines.h。
    • • E3(emitcSyms()):符号表,输出 Vtop__Syms.cpp/.h。
    • • E4(emitcTrace()):波形追踪代码,输出 Vtop__Trace.cpp/.h。
    • • E5(emitcMakefile()):构建规则,输出 Vtop.mk。
    • • E6(emitcCells()):单元层次,输出 Vtop__Cell.h。
  • • 输出文件:
    • • OUT1 ~ OUT7:最终用户可编译的文件集合。

🔄 变换 Pass 的详细执行序列

以下是 Verilator 编译管线中主要变换 Pass 的执行序列,按阶段分组:

阶段
关键 Pass
目的
展开完成V3Param
、V3Width、V3WidthCommit
解析参数、计算信号位宽
插桩V3Coverage
、V3Assert、V3Randomize
添加覆盖率/断言/随机化代码
结构变换V3Inst
、V3Scope、V3Inline、V3Task
展平层级、内联代码
逻辑优化V3Const
、V3Gate、V3Unknown、V3Tristate
简化逻辑、常量传播、X 消除
调度排序V3Active
、V3Delayed、V3Order
确定执行顺序
代码准备V3Expand
、V3Premit、V3Depth、V3Descope
为 C++ 发射做准备

完整 Pass 序列流程图:

📋 说明:

  • • 输入 START:展开后 AST,从语义分析阶段进入。
  • • 前置 LINK(V3LinkResolve):符号解析,链接所有引用。
  • • 📦 插桩阶段 G1:
    • • V3Coverage:插入覆盖率统计代码。
    • • V3Assert:插入断言检查。
    • • V3Randomize:插入随机化逻辑。
  • • 🏗️ 结构变换阶段 G2:
    • • V3Inst:实例展开(消除模块实例化)。
    • • V3Scope:作用域展平(消除层次结构)。
    • • V3Inline:模块内联。
    • • V3Task:任务展开。
  • • ⚡ 逻辑优化阶段 G3:
    • • V3Const:常量折叠。
    • • V3Gate:门级优化。
    • • V3Unknown:X 消除(四值转二值)。
    • • V3Tristate:三态消除。
    • • V3Const(再次):第二次常量折叠,处理新暴露的常量。
  • • 📐 调度排序阶段 G4:
    • • V3Active:拆分 always 块为 active 区域。
    • • V3Delayed:处理非阻塞赋值的延迟语义。
    • • V3Order:确定 eval() 内的执行顺序。
  • • 📝 代码准备阶段 G5:
    • • V3Expand:展开复杂表达式。
    • • V3Premit:预提交优化。
    • • V3Depth:计算表达式深度。
    • • V3Descope:去除作用域信息。
  • • 输出 DONE:优化完成的 AST,准备进入后端发射。
  • • V3Param:通过为每个唯一的参数组合创建专门的克隆来消除参数化模块。必须在大多数其他变换之前执行,因为参数影响信号位宽、数组大小和条件生成块。
  • • V3Width:执行类型推断和位宽计算,插入 AstCCast 进行类型转换,添加 AstExtend 进行符号/零扩展,当位宽不匹配时添加 AstSel 进行位提取。
  • • V3Const:在编译的不同阶段多次调用,执行常量折叠和传播。
  • • V3Inst:分两阶段进行实例展开——dearrayAll() 移除 cell 数组,instAll() 将模块实例转换为线网赋值。

🧩 Pass 执行架构:Visitor 模式

所有变换 Pass 都使用 Visitor 设计模式遍历 AST。

  • • 基类:每个 Pass 继承 VNVisitor 基类,并为特定的 AST 节点类型实现 visit 方法。
  • • 主执行循环:变换序列由 Verilator.cpp::process() 编排。
  • • 优势:
    • • 每个 Pass 只需关注自己关心的节点类型。
    • • 通过 Visitor 模式实现 AST 的解耦遍历。
    • • 新 Pass 的添加不影响现有 Pass。

Visitor 模式类关系图:

📋 说明:

  • • 节点基类 AstNode(抽象):
    • • 提供 op1p() ~ op4p() 四个子节点指针。
    • • nextp() / backp() 用于兄弟与父节点链接。
    • • type() 返回节点类型(VNType 枚举)。
    • • dtypep() 返回数据类型描述。
    • • fileline() 返回源码位置。
  • • 表达式基类 AstNodeExpr(抽象):所有表达式的祖先,提供 width() 方法。
  • • 语句基类 AstNodeStmt(抽象):所有语句的祖先。
  • • 访问者基类 VNVisitor(抽象):
    • • visit(AstNode*):处理节点。
    • • iterate(AstNode*):遍历节点。
    • • iterateChildren(AstNode*):遍历子节点。
  • • 典型 Pass:
    • • V3Width:实现 visit(AstAdd*)、visit(AstVar*)、visit(AstConst*)。
    • • V3Const:实现 visit(AstAdd*)、visit(AstMul*)、visit(AstSel*)。
    • • V3Order:实现 visit(AstAlways*)、visit(AstAssign*)。
    • • V3EmitC:实现 visit(AstNetList*)、visit(AstModule*)。
  • • 继承关系(<|--):所有 Pass 继承自 VNVisitor,所有节点继承自 AstNode。
  • • 依赖关系(..>):VNVisitor 依赖 AstNode 进行遍历。

Pass 编排与 Visitor 遍历流程:

📋 说明:

  • • 参与者 PROC(process()):主控流程,顺序调用各 Pass。
  • • 参与者 AST:被所有 Pass 遍历和修改。
  • • 步骤 1-2:PROC 调用 V3Param,后者克隆参数化模块并返回。
  • • 步骤 3-4:PROC 调用 V3Width,后者计算位宽并插入 Cast。
  • • 步骤 5-6:PROC 调用 V3Const,后者折叠常量表达式。
  • • 步骤 7-8:PROC 调用 V3Order,后者确定执行顺序。
  • • 步骤 9-10:PROC 调用 V3EmitC,后者遍历 AST 生成 C++ 代码。
  • • -->> 虚线:表示返回控制权给 PROC。

Visitor 遍历 AST 的详细展开图:

📋 说明:

  • • 访问者 VIS(VNVisitor):从 AST 根节点开始遍历。
  • • 遍历顺序:
    • • ROOT(AstNetList):遍历根。
    • • M1(AstModule):进入模块。
    • • V1(AstVar clk)→ V2(AstVar rst)→ V3(AstVar count):按 nextp 横向遍历变量。
    • • A1(AstAlways):进入过程块。
    • • IF(AstIf):进入条件语句。
    • • AS1(AstAssign count=0):then 分支。
    • • AS2(AstAssign count=count+1):else 分支。
  • • visit 方法处理(虚线箭头):VIS 对每个节点调用对应的 visit 方法,进行具体处理。
  • • 实线箭头:表示遍历路径(iterate 与 iterateChildren)。
  • • 虚线箭头:表示访问者对节点的具体处理。

🎯 总结

完整编译流程一图总览:

📋 说明:

  • • 输入 SRC:Verilog/SystemVerilog 源代码。
  • • 📝 前端 FE:预处理 + 词法 + 语法分析。
    • • 内部组件:V3PreProc、V3PreLex、V3ParseBison。
    • • 输出:AST1 原始 AST。
  • • 🔍 语义分析 ME:链接 + 参数展开 + 宽度推断 + 展开。
    • • 内部组件:V3Param、V3Width、V3WidthCommit。
    • • 输出:AST2 展开后 AST。
  • • ⚡ 变换优化 OPT:~50+ 个 Pass。
    • • 内部组件:V3Const、V3Gate、V3Order。
    • • 输出:AST3 优化后 AST。
  • • 📤 后端发射 BE:C++ 代码生成。
    • • 内部组件:V3EmitC、V3EmitCFunc、V3EmitCMake。
    • • 输出:FILES(.cpp / .h / .mk)。
  • • 🔨 编译 COMPILE:GCC/Clang 编译生成的 C++ 代码。
  • • 🚀 最终产物 EXE:可执行仿真器。
  • • 目录结构:bin/(包装脚本)、src/(核心源代码)、include/(运行时头文件)、test_regress/(回归测试)。
  • • 核心类体系:
    • • V3Global:全局单例,持有 AST 根节点和配置。
    • • V3Options:配置中心,150+ 个选项变量。
    • • AstNode:AST 基类,四子指针 + next/back 链接,VNType 类型系统。
    • • V3Number:四值逻辑数值系统,内联/动态/字符串三种存储策略。
    • • V3Graph:通用有向图框架,用于数据流分析和拓扑排序。
  • • 编译管线:
    1. 1. 前端:预处理 → 词法分析(Flex)→ 语法分析(Bison)→ AST。
    2. 2. 语义分析:链接 → 参数展开 → 宽度推断 → 展开。
    3. 3. 变换优化:~50+ 个 Pass,按依赖关系顺序执行。
    4. 4. 后端:V3EmitC 协调多个发射类生成 C++ 代码。
  • • Pass 架构:Visitor 模式,每个 Pass 继承 VNVisitor,由 process() 统一编排。

📚 参考资源

  • • Verilator Internals Manual:https://verilator.org/guide/latest/internals.html
  • • Verilator 官方文档:https://verilator.org/guide/latest/
  • • GitHub 仓库:https://github.com/verilator/verilator
  • • DeepWiki Verilator 文档:https://deepwiki.com/verilator/verilator

相关学习资料