ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

gem5与SystemC联合仿真环境搭建:从零到一完整指南

gem5与SystemC联合仿真环境搭建:从零到一完整指南 做芯片体系结构、架构验证或者 SoC 早期性能评估的人多半绕不开这组工具gem5 做微架构级仿真SystemC 做系统级建模。以前我总觉得两者是两条平行路线直到做虚拟原型课题时被要求把 CPU cycle 级别的行为和外设总线级的行为放到同一张时间表里跑才意识到联合仿真的必要。gem5 与 SystemC 联合仿真环境搭建也因此成了很多项目的第一道坎。这篇文章不省略关键细节我会把从零到一的过程写清楚包括为什么要这么装、每条命令在干什么、报错怎么查适合第一次接触这套环境的读者按步骤操作。gem5 与 SystemC 联合仿真本质上是把两个仿真世界打通一边以 tick 为单位模拟处理器内部流水线、Cache、访存顺序另一边以 SystemC kernel 的事件驱动来调度总线、外设、甚至 RTL 接口。两者协同既能做架构探索又能做系统级验证。这篇内容虽然叫第一章但整套环境搭建完之后后续写自己的 sc_module 才会有地基。1. 联合仿真的分工逻辑gem5 管 CPUSystemC 管系统级1.1 为什么单靠 gem5 不够用gem5 在体系结构研究里几乎人手一个。它自带多种 CPU 模型例如 AtomicSimpleCPU、TimingSimpleCPU、O3CPU还有比较完整的 Cache 层级模型适合做指令流水、命中率、访存带宽、延迟这一类微观分析。但 gem5 对真实 SoC 周边世界的建模并不擅长你想往里面挂一个带中断的 DMA、一组有真实时序的 AHB/AXI 外设就得手动写很多抽象代码还不一定和 SystemC 那边的验证模型共用同一个平台。SystemC 的优势在事务级建模和虚拟原型。它用事件驱动内核调度 module、port、sc_time描述总线仲裁、FIFO 行为、寄存器读写这类系统级逻辑非常顺手。但你让 SystemC 去模拟一个乱序执行的超标量处理器效率和细节都很难兼顾。SystemC 的 sc_module 里当然也能写算法级 CPU但那种模型没有流水线级状态指令颗粒度太粗拿来跑架构研究完全不够看。两边单独用都有明显短板。gem5 的周边建模弱SystemC 的处理器微架构细粒度难以覆盖。于是联合仿真成为通用做法让 gem5 提供高性能的 CPU 模型让 SystemC 提供标准的系统级连接框架两边通过时间同步和事件转换完成协作。1.2 联合仿真的架构与你需要具备的基础联合仿真环境在逻辑上并不复杂gem5 被包成一个特殊的 SystemC 模块SystemC 的 kernel 负责整个仿真时间推进gem5 侧按照自己的 clock domain 处理 tick。两个调度器之间不是硬实时关系而是通过时间量子做同步每一次切换都可能是几万 tick 的批处理。你可以理解为两个原本各说各话的调度器中间加了一位翻译按固定的时间粒度对表。这套方式的典型收益是处理器模型继续用 gem5周边 IP 验证环境继续用 SystemC两边各自保留熟悉的开发调试方式。它特别适合三种人体系结构方向的研究生需要在 CPU 模型上挂自定义 SoC 外围做性能评估SoC 前端验证工程师需要在纯 TLM 环境里加入高保真 CPU 行为还有做虚拟原型平台的技术团队想统一不同来源的模型。如果你是第一次接触建议先确认三件事能用 Linux 命令行能看懂基础 C 类与继承关系以及曾经编译过至少一个需要 configure 和 make 几步走的开源项目。有了这三样基础下面这一堆依赖和命令就不会让你头大。如果只是听说过 gem5 的名字建议先花半小时熟悉 Linux 软件安装再回来继续。2. 搭建前的环境准备版本、依赖与源码获取2.1 推荐工具链版本与安装方式别一上来就只装 gem5先把基础工具链搞定。我在 Ubuntu 20.04、22.04 和 Rocky Linux 9 上都试过原则是系统自带的 gcc/g 优先Python 用系统 Python3 而不是 Conda 里的SCons 单独用 pip 装到用户目录。混合用 Conda 的库和系统编译工具链最容易出现 ABI 不匹配尤其 SystemC 这种和 C 版本强绑定的项目稍不注意就是一连串奇怪报错。推荐的最低版本大致如下组件建议版本/最低版本说明gcc/g10.x 以上太老的编译器直接放弃标准支持不够Python3.8 以上某些构建脚本依赖新语法SCons3.1.2 以上构建 gem5 的构建工具zlib/libpng系统源安装即可编译与运行仿真的通用依赖protobuf 相关库可选但建议装生成 trace 等实验功能需要Boost系统源安装即可许多内部模块编译期依赖在 Debian/Ubuntu 上我会先执行一条安装命令把通用工具装齐sudo apt-get install -y build-essential git scons python3-dev \ zlib1g-dev libpng-dev libprotobuf-dev protobuf-compiler \ libboost-all-dev libhdf5-dev在 Red Hat 系上名字略有区别用 yum/dnf 装 gcc-c、python3-devel、scons、zlib-devel、libpng-devel、protobuf-devel 即可。关键不是把包列表背下来而是理解 gem5 是大型 C 项目所有涉及头文件链接的相关库都要有-dev或-devel版本否则编译阶段会报找不到头文件。2.2 获取 gem5 源码与目录初识依赖装好之后找一个干净的 workspace不要放在带中文、带空格、或者网络挂载目录里。gem5 的 SCons 构建脚本对路径很敏感这些问题通常到最后才暴露排查成本极高。mkdir -p $HOME/ws cd $HOME/ws git clone https://gem5.googlesource.com/public/gem5 cd gem5如果官方地址拉不下来可以用 GitHub 上官方同步的镜像但注意版本发布节奏和 googlesource 可能差几个 commit。我通常直接 clone 官方源然后切到当前稳定发布分支而不是用最新 main。联合仿真代码变化频繁图新版本反而容易碰到刚改坏的东西。源码目录里比较重要的有三个src/是全部模型与模拟器核心代码src/systemc/这套联合仿真相关的适配层就在这build/是编译输出目录第一次构建会自动创建configs/放运行脚本和 Python 配置。第一次打开目录不用强迫自己全部看明白只看这几个就能定位问题。3. 核心编译流程一条命令一条命令地把环境跑通3.1 编译 gem5 主模拟器进入目录后第一件事不是直接跑完整目标而是确认 SCons 能正确调用 Pythonpython3 $(which scons) --version能正常看到版本号再构建主模拟器python3 $(which scons) build/ARM/gem5.opt -j4这里build/ARM/gem5.opt表示构建 ARM ISA 的优化版本模拟器。ISA 选 ARM 只是一个演示如果你的项目跑 x86 就把路径改成build/X86/gem5.opt。-j4指定并行任务数第一次编译不要开满我先用 4之后增量编译再调高。为什么是gem5.opt而不是gem5.debug或者gem5.fast三个模式的差别主要在断言和调试符号的保留上debug 最慢但报错最详细fast 去掉了大量运行时检查opt 保留 DPRINTF 调试输出同时做了一定优化用于日常开发和跑实验最平衡。编译耗时取决于机器一般 20 到 60 分钟期间日志末尾会不断滚动 C 编译和归档信息。不要中断也不要同时开一堆其他占用内存的软件gem5 编译时内存占用并不低。如果编译中途被系统杀掉优先检查 swap 和并行度不要急着改代码。3.2 编译 SystemC 相关共享库单独一个 gem5.opt 还不足以叫联合仿真环境还需要把 gem5 编译成可供外部 C 程序链接的共享库这就是我接下来要做的python3 $(which scons) build/ARM/libgem5_ARM.so -j4libgem5_ARM.so 是 gem5 对外开放的 C 接口库SystemC 模块通过它实例化 gem5 的 CPU 模型、读取仿真参数、并驱动时钟推进。早期版本可能没有这个目标但现在主流版本都支持你可以在build/ARM/下看到这个文件。编译完成后我习惯把编译输出检查一遍ls -lh build/ARM/gem5.opt build/ARM/libgem5_ARM.so看到两个文件都存在并且gem5.opt带可执行权限环境的核心部分就算通了。如果你只是为了快速验证模拟器本身gem5.opt已经能干很多活至于 SystemC 那边的具体对接代码在下一章再展开第一步只保证它已经作为库存在于工程里。这一步特别容易出问题的地方是链接错误比如找不到libpython或者引入其他动态库失败。不要急着改代码先看 gem5 源码里的SConstruct和build_ARM/下的日志通常是你缺少某些-dev开发包补装后再重新执行同样的 SCons 命令。3.3 不急着跑模型用构建产物做冒烟验证很多人拿到 gem5.opt 第一件事就是找一个配置脚本跑 demo但联合仿真环境这个题目下我建议先把冒烟验证做扎实。一个最轻量的验证方式是查看版本与帮助信息./build/ARM/gem5.opt --version能正常输出 gem5 版本字符串说明动态库链接、Python 初始化、命令行解析这一条最基本的链路没问题。之后再跑一个内置的最小配置脚本比如 gem5 仓库中自带的 hello 测试用例如果它也能跑完并在终端看到仿真统计说明 CPU 模型与内存系统也正常。在联合仿真环境下libgem5_ARM.so 的存在比运行一个完整 Linux 启动实验更重要。我的习惯是先把.so生成时间记下来之后所有 SystemC 测试程序都链接到这一份产物。如果你以后一旦改了 gem5 源码必须重建库否则 SystemC 那边链接的是旧符号表那种“明明改了代码但行为没变”的诡异问题十有八九是这样造成的。4. 工程化配置目录规划与环境变量4.1 建议的工作目录布局环境能编译通过之后接下来要把它变成一个可维护的工程。我见过很多人直接把所有项目文件堆在 gem5 目录里git 状态一片混乱最后分不清哪份代码是自己的哪份是官方代码。正确做法是把 gem5 源码当作一个只读依赖单独的代码放在外面。我会这样组织$HOME/ws/ ├── gem5/ # gem5 源码固定版本不轻易改动 ├── my_sysc/ # 自己的 SystemC sc_main 与 sc_module │ ├── include/ # 自己写的头文件 │ ├── src/ # C 源文件 │ └── scripts/ # 构建脚本 └── artifacts/ # 编译产物、日志、实验结果这样做的原因很简单gem5 迭代很快如果你把自己的模型直接塞进src/升级源码时非常痛苦。把两者分离后升级时只替换 gem5 目录自己代码不受影响。对刚入门的人来说这套目录结构也能避免很多改错地方的低级错误。4.2 环境变量与 Makefile 模板联合仿真编译不仅牵涉 gem5还牵涉你自己的 C 工程。环境变量能省掉大量重复路径输入至少设置两个export GEM5_ROOT$HOME/ws/gem5 export GEM5_LIB$GEM5_ROOT/build/ARM export MY_SYSC_ROOT$HOME/ws/my_sysc我通常把这四行写进~/.gem5_env.sh每次开终端 source 一下避免每个新终端都重复输入。不建议写进.bashrc直接全局生效因为不同项目可能用不同 gem5 版本全局变量反而容易带到错误工程里。为了不记住一堆 SCons 参数我还会在my_sysc/下放一个极简 MakefileGEM5_ROOT ? $(HOME)/ws/gem5 GEM5_BUILD ? $(GEM5_ROOT)/build .PHONY: gem5 lib clean gem5: python3 $$(which scons) -C $(GEM5_ROOT) build/ARM/gem5.opt -j4 lib: python3 $$(which scons) -C $(GEM5_ROOT) build/ARM/libgem5_ARM.so -j4 clean: rm -rf $(GEM5_BUILD)/ARMMakefile 最大的好处是把“第一次怎么编译”变成一条固定命令下次重新做环境或者换机器只要make gem5 make lib就够不会因为忘记参数而抓狂。4.3 链路级冒烟测试的思路环境搭好以后我通常不直接把所有 SystemC 模块写出来跑而是先做一次最小链路验证。所谓链路验证就是确认 gem5 库能被独立 C 程序正常拉起。这阶段我甚至不关心 SystemC 内部实现细节只需要知道动态链接器能不能找到libgem5_ARM.so。顺手检查一下动态依赖ldd $HOME/ws/gem5/build/ARM/libgem5_ARM.so | head -20如果出现一堆 not found多半是 PATH 和 LD_LIBRARY_PATH 没有包含 gem5 的构建目录。把export LD_LIBRARY_PATH$GEM5_LIB:$LD_LIBRARY_PATH加上再ldd就会干净很多。这条验证非常值得做。因为很多联合仿真代码报错不是源程序逻辑错而是运行时链接器压根找不到 gem5 的共享库。你瞪了几小时代码最后发现只是没设环境变量这种教训我至少遇到三次。5. 实操中会遇到的问题排查表与避坑经验5.1 高频问题速查表这一小节整理我在搭建过程中遇到或者帮别人排查过的高频问题你直接对照着查即可。问题现象最常见原因处理方式SCons 报错找不到 Python.h缺少 python3-dev / python3-devel安装对应系统的开发包gem5.opt 编译到一半被杀内存不足或 -j 开太大降到 -j2加大 swap链接时报 libpython 找不到系统 Python 版本与编译环境不一致尽量用系统 Python不要用 Conda 默认 Pythonlibgem5_ARM.so 编译出现 symbol undefined缺某个 -dev 包或之前编译中断补装依赖后从干净 build 目录重新构建运行时报 gem5 无法进入模拟动态库找不到设置 LD_LIBRARY_PATH 指向 build/ARMldd 显示 libstdc 版本冲突多个 gcc 版本混用统一用系统默认 gcc或用 update-alternatives 固定版本这张表能覆盖八成起步问题。剩下两成往往是路径、权限这种最不起眼的因素建议第一反应先看完整日志不要只盯着最后几行。SCons 会把每个编译子任务写成日志按关键字error:搜一遍最快。5.2 三个容易忽略的细节第一个容易被忽略的细节是版本一致性。SystemC 侧如果用了外部 SystemC 仿真库那 gem5 内嵌 SystemC kernel 的版本预期必须匹配否则会出现事件调度上的诡异行为。如果直接用 gem5 自带 SystemC 适配层这个问题会小很多但一旦自己写外部 sc_main还是要关注 C 标准版本。第二个是同一台机器上不要有多个 Python 环境交替使用。我用 Conda 时遇到过 SCons 内部的distutils与系统路径冲突报错几乎没规律。解决方法是编译 gem5 之前用which python3确认指向的是/usr/bin/python3或你自己手工安装的独立 Python。第三个是千万不要提前优化。不是所有编译任务都要一次-j32跑满第一次编译用低并行度反而更容易从日志中定位是哪一步失败。我见过太多人开-j64直接把机器跑死最后淘汰出一堆临时文件还得从头再来。更稳妥的做法是先-j4等日志稳定了再在增量编译时用高一些的并行度。最后再分享一个小技巧把每次成功的构建命令和版本组合记在一个单独的NOTES.md里最好精确到 gcc 版本、SCons 版本、gem5 的 commit hash。联合仿真环境出问题时回看这份记录能帮你快速判断到底是代码问题还是环境漂移。按这个流程走下来我从零搭建到能跑通最小 SystemC 链路基本稳定在一个晚上左右运气好还能留出时间熟悉工具链。
返回列表