4. 仿真环境目录结构:项目根目录设计、RTL源码目录、验证组件目录、脚本目录、仿真结果目录

聊到仿真环境,我第一个想说的就是目录结构。

你可能觉得这玩意儿有啥好讲的?不就是建几个文件夹嘛。

嗯,我以前也这么想。直到有一次,一个项目做到一半,验证环境乱成一锅粥——脚本找不到RTL文件,仿真结果散落在各个角落,新来的同事看了三天都没搞明白东西在哪儿。那次之后,我花了整整一个周末重构了整个目录结构。说白了,一个好的目录结构,能让你的验证效率翻倍。

核心原则:目录结构要清晰、可扩展、易维护。让任何人看一眼就知道什么东西该放哪儿。

4.1 项目根目录设计

我个人习惯,项目根目录下只放最核心的东西。别把乱七八糟的文件都堆在根目录,那会让人崩溃。

一个典型的根目录长这样:

project_name/
├── rtl/                # RTL源码
├── verification/       # 验证组件
├── scripts/            # 脚本目录
├── sim_results/        # 仿真结果
├── docs/               # 文档
├── tools/              # 工具
├── Makefile            # 顶层Makefile
└── README.md           # 项目说明

你看,一共就7个东西。每个目录做什么,名字已经说清楚了。

这里有个小技巧:根目录下的Makefile一定要写。我见过太多项目,脚本散落在各个目录里,每次跑仿真都要手动敲一长串命令。有了顶层Makefile,你只需要 make sim 或者 make clean,省心多了。

我的经验:Makefile里至少提供这几个目标:sim(跑仿真)、clean(清理结果)、regression(回归测试)、wave(打开波形)。这样团队里所有人都能用同一套命令。

4.2 RTL源码目录

RTL目录的设计,说白了就是怎么组织你的设计代码。

我建议按模块来分。别把所有.v文件都扔到一个目录里,那跟把衣服全塞一个衣柜没啥区别——找的时候得翻半天。

rtl/
├── top/                # 顶层模块
│   └── chip_top.v
├── core/               # 核心模块
│   ├── core_alu.v
│   ├── core_ctrl.v
│   └── core_regfile.v
├── peripheral/         # 外设模块
│   ├── uart.v
│   ├── spi.v
│   └── gpio.v
├── memory/             # 存储模块
│   ├── sram_wrapper.v
│   └── rom_wrapper.v
└── include/            # 头文件
    ├── defines.vh
    └── parameters.vh

为什么会这样分?你想想看,当你要修改UART模块时,你只需要去 rtl/peripheral/ 目录下找。如果所有文件都混在一起,你得先搞清楚哪个文件是UART,然后再去翻。

注意:头文件目录 include/ 一定要单独放。我曾经吃过一次亏,把 `defines.vh` 放在某个模块目录下,结果其他模块引用时路径写错了,编译报错查了半天。后来我统一把所有头文件放到 rtl/include/,再也没出过这种问题。

4.3 验证组件目录

验证组件这块,我习惯按验证方法学的思路来组织。说白了,就是UVM那套东西——但目录结构要清晰,别搞得太复杂。

verification/
├── tb/                 # 测试平台
│   ├── tb_top.sv
│   └── tb_config.sv
├── agents/             # 代理
│   ├── uart_agent/
│   ├── spi_agent/
│   └── gpio_agent/
├── sequences/          # 序列
│   ├── base_seq.sv
│   └── test_seq.sv
├── tests/              # 测试用例
│   ├── test_base.sv
│   ├── test_smoke.sv
│   └── test_regression.sv
├── models/             # 参考模型
│   └── ref_model.sv
├── coverage/           # 覆盖率
│   └── cov_collector.sv
└── env/                # 验证环境
    └── env_top.sv

每个agent目录下,我习惯再分一层:

uart_agent/
├── uart_driver.sv
├── uart_monitor.sv
├── uart_sequencer.sv
└── uart_agent_pkg.sv

这样设计的好处是:每个agent都是独立的,可以复用。我在一个项目里写好的UART agent,拿到另一个项目里,改一下配置就能直接用。

避坑指南:我曾经把所有的sequence都写在一个文件里,结果文件越来越大,最后到了3000多行。找某个sequence得翻半天。后来我改成每个test一个文件,清爽多了。

4.4 脚本目录

脚本目录,嗯,这里我要多说两句。很多人不重视脚本的组织,觉得能跑就行。但你想过没有——当项目有几十个脚本时,你还能找到哪个是干嘛的吗?

scripts/
├── sim/                # 仿真脚本
│   ├── run_sim.sh
│   ├── compile.sh
│   └── wave.sh
├── regression/         # 回归脚本
│   ├── run_regression.sh
│   └── report_gen.sh
├── analysis/           # 分析脚本
│   ├── coverage_merge.sh
│   └── log_parser.py
└── utils/              # 工具脚本
    ├── clean.sh
    └── backup.sh

我个人习惯,脚本用 .sh 或者 .py。别用 .bat,除非你确定团队里所有人都用Windows。说实话,在IC验证这个领域,Linux才是主流。

每个脚本的开头,我建议加一段注释:

#!/bin/bash
#=============================================
# 脚本功能:运行单个测试用例
# 用法:./run_sim.sh <test_name> [seed]
# 作者:xxx
# 最后修改:2024-01-15
#=============================================

别小看这几行注释。我遇到过好几次,脚本是半年前写的,自己都忘了怎么用。有了注释,看一眼就知道。

注意:脚本的路径引用一定要用相对路径,或者通过环境变量。我见过有人写死路径,结果项目换个目录就全废了。用 $(dirname "$0") 获取脚本所在目录,然后基于它去引用其他文件,这样最靠谱。

4.5 仿真结果目录

仿真结果目录,说白了就是你的仿真输出放哪儿。这个目录设计得好不好,直接影响到你调试的效率。

sim_results/
├── test_smoke/         # 单个测试用例结果
│   ├── seed_1/
│   ├── seed_2/
│   └── seed_3/
├── test_regression/
│   ├── seed_1/
│   └── seed_2/
├── coverage/           # 覆盖率结果
│   ├── merged/
│   └── reports/
├── logs/               # 日志
│   └── sim.log
└── waves/              # 波形文件
    └── test_smoke.fsdb

每个测试用例的结果目录里,我习惯放这些文件:

  • sim.log:仿真日志,记录所有打印信息
  • wave.fsdb/vcd:波形文件,用于调试
  • coverage_report:覆盖率报告
  • test_status:测试结果(PASS/FAIL)

为什么要按seed分目录?你想想看,同一个测试用例跑不同的随机种子,结果可能不一样。如果不分开,后一次的结果会把前一次的覆盖掉,到时候想对比都对比不了。

我的习惯:每次跑完仿真,自动生成一个 summary.html 文件,里面列出所有测试用例的结果、运行时间、覆盖率等信息。这样一眼就能看出哪些用例过了,哪些挂了。省得一个个去翻日志。

4.6 整体结构图

说了这么多,我画个图帮你理一理思路。这个图展示了整个仿真环境的目录结构,以及各个目录之间的关系。

仿真环境目录结构总览 project_name/ rtl/ verification/ scripts/ sim_results/ docs/ top/ core/ peripheral/ memory/ include/ tb/ agents/ sequences/ tests/ models/ coverage/ env/ sim/ regression/ analysis/ utils/ test_smoke/ test_reg/ coverage/ logs/ waves/ 目录结构设计要点 • 根目录只放核心文件,保持简洁 • RTL按模块分目录,头文件单独放 • 验证组件按UVM结构组织,每个agent独立 • 脚本按功能分类,统一使用相对路径 • 仿真结果按测试用例和seed分目录,方便调试

4.7 总结一下

目录结构这事儿,说白了就是「规矩」。规矩立好了,团队里所有人都按同一套来,效率自然就上去了。

我最后再啰嗦一句:目录结构不是一成不变的。项目初期可以简单点,随着项目变大再慢慢细化。别一开始就搞得太复杂,那反而会让人望而却步。

嗯,关于目录结构就聊到这儿。记住我说的那几个原则,你的仿真环境搭建就能少走很多弯路。