ARTICLE DETAIL

资讯详情

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

Gemmini深度学习加速器实战:从Chipyard环境搭建到自定义扩展

Gemmini深度学习加速器实战:从Chipyard环境搭建到自定义扩展 1. 从零认识Gemmini它到底解决什么问题第一次接触Chipyard和Gemmini的人大概率是被“敏捷硬件开发”这个词吸引过来的。Chipyard是伯克利推出的开源芯片设计框架而Gemmini是它生态里专门做深度学习加速器的一个模块。说白了Gemmini就是帮你快速生成一个能跑神经网络推理的硬件加速器从RTL到软件栈一条龙。我最初上手的时候最直观的感受是这东西把原本需要几个月才能搭起来的加速器原型压缩到了几天甚至几个小时。它解决的核心问题是——硬件加速器的设计门槛太高。传统流程里你得先写Verilog实现矩阵乘法阵列再写驱动、写编译器适配每一步都是坑。Gemmini直接给你一套参数化模板你改几个配置就能生成不同规模的加速器。适合谁来学如果你是做体系结构研究的学生、想验证AI芯片方案的工程师或者单纯对RISC-V生态感兴趣Gemmini都值得花时间。它不要求你精通Verilog但需要你对计算机体系结构有基本概念比如什么是脉动阵列、什么是DMA。小白也能上手但建议先补一下RISC-V和CNN的基础知识。2. 环境搭建与Chipyard基础准备2.1 为什么选Chipyard而不是自己搭自己从零搭一个RISC-V SoC加加速器光是处理器核、总线、内存控制器这些就够折腾几周。Chipyard的价值在于它已经帮你集成好了Rocket Core或BOOM Core、TileLink总线、DRAM模型你只需要在配置里挂上Gemmini就行。这就像装修房子Chipyard是精装房Gemmini是你要加的那个智能家居系统不用自己砌墙。另一个原因是Chipyard的构建系统基于Scala和Chisel参数化程度极高。你可以通过改一个Scala文件里的case class就改变加速器的阵列大小、数据位宽、是否支持浮点。这种灵活性在传统RTL流程里几乎不可能。2.2 依赖安装的实操细节官方文档给的依赖列表看起来简单但实际装的时候有几个坑。首先JDK版本必须是8或11我试过用JDK 17sbt直接报错。其次conda环境建议单独建一个因为Chipyard的Python依赖和系统Python容易冲突。conda create -n chipyard python3.9 conda activate chipyard git clone https://github.com/ucb-bar/chipyard.git cd chipyard ./scripts/init-submodules-no-riscv-tools.sh注意init-submodules这一步会拉取大量子模块网络不稳的话容易断。我的经验是分步执行先拉chipyard主仓库再单独初始化gemmini子模块。另外RISC-V工具链的编译非常耗时建议用./scripts/build-toolchains.sh之前先确认磁盘空间至少留30GB。提示如果你只是想在仿真里跑Gemmini可以跳过工具链编译直接用预编译的riscv-tools能省一个多小时。2.3 验证环境是否就绪装完之后跑一个最简单的make命令测试cd sims/verilator make CONFIGGemminiRocketConfig如果能看到Verilator开始编译说明环境基本OK。这一步会生成C仿真器第一次编译大概需要10-15分钟。编译成功后你会得到一个simulator-chipyard-GemminiRocketConfig的可执行文件。3. Gemmini核心架构与参数解析3.1 脉动阵列的工作机制Gemmini的核心是一个二维脉动阵列默认配置是16x16的矩阵乘法单元。它的工作方式像流水线数据从左侧和上方流入每个PE处理单元做乘加运算结果从下方和右侧流出。这种结构的优势是数据复用率高适合卷积和全连接层。但脉动阵列有个天然限制它擅长处理规则的大矩阵对稀疏矩阵或小矩阵效率低。所以Gemmini在阵列外面加了一层scratchpad内存用来缓存输入数据避免频繁访问DRAM。这个scratchpad的大小是可以配置的默认是256KB。3.2 关键参数怎么选Gemmini的配置文件在generators/gemmini/src/main/scala/gemmini/Configs.scala。几个最常改的参数参数名含义默认值调整建议tileRows阵列行数16增大提升吞吐但面积和功耗上升tileColumns阵列列数16同上建议行列相等meshRows脉动阵列行16通常等于tileRowssp_banksscratchpad bank数4增加可提升带宽sp_capacityscratchpad容量256KB根据模型大小调整acc_capacity累加器容量64KB影响输出缓存我个人的经验是先跑默认配置确认功能正确后再调参数。一开始就把阵列改成32x32编译时间翻倍不说仿真速度也会慢到无法忍受。如果你只是做算法验证16x16完全够用。3.3 数据流与指令集扩展Gemmini通过RoCC接口挂载到Rocket Core上相当于给处理器加了一条自定义指令通道。软件通过gemmini开头的C函数调用底层会翻译成RoCC指令。比如gemmini_mvin负责把数据从DRAM搬到scratchpadgemmini_mmul触发矩阵乘法。这种设计的好处是软硬件解耦你不需要改处理器核只需要在软件里调用API。但缺点是指令粒度较粗一次mvin可能搬一整块数据灵活性不如细粒度DMA。4. 从配置到仿真完整实操流程4.1 生成Verilog与编译仿真器假设你已经选好了配置比如GemminiRocketConfig接下来cd sims/verilator make CONFIGGemminiRocketConfig -j8-j8是并行编译根据你的CPU核数调整。编译完成后运行一个简单的矩阵乘法测试./simulator-chipyard-GemminiRocketConfig verbose \ ../../tests/gemmini/matmul_test如果看到PASS字样说明硬件功能正常。这一步常见的问题是仿真卡死多半是因为scratchpad容量不够数据溢出。解决办法是减小测试矩阵尺寸或者增大sp_capacity。4.2 软件栈的编译与链接Gemmini的软件栈包括驱动、运行时库、示例程序。编译命令cd software/gemmini make这会生成libgemmini.a和几个测试可执行文件。链接的时候要注意必须链接RISC-V版本的libc和libm否则会出现未定义符号。我踩过的坑是用了系统gcc编译结果指令集不匹配仿真直接崩。4.3 跑一个真实的CNN推理Gemmini自带一个MobileNet的示例在software/gemmini/example/mobilenet。编译后把可执行文件放到仿真器的load参数里./simulator-chipyard-GemminiRocketConfig \ load../../software/gemmini/example/mobilenet/mobilenet.riscv仿真过程中可以用verbose看每一层的耗时。实测下来16x16阵列跑MobileNet第一层大概需要几百万周期比纯CPU快5-10倍。但注意仿真速度极慢跑完整网络可能要几个小时建议先用小网络验证。5. 常见问题与排查技巧实录5.1 编译报错速查表报错信息原因解决方法sbt: command not foundsbt未安装或PATH不对重新运行./scripts/init-submodulesverilator: error: ...Verilator版本过低升级到4.2以上undefined reference to gemmini_...软件栈未编译先makesoftware/gemmini仿真卡死无输出scratchpad溢出减小矩阵尺寸或增大sp_capacityRoCC instruction fault指令编码错误检查gemmini的RoCC opcode配置5.2 性能调优的独家心得第一bank冲突是性能杀手。scratchpad默认4个bank如果数据访问模式不好会频繁冲突。我的做法是把输入矩阵按bank数对齐比如16x16的矩阵每行存一个bank这样并行读取效率最高。第二DMA搬运比计算更耗时。很多人只关注阵列大小忽略了数据搬运。实测中mvin指令占用了60%以上的周期。优化方法是增大DMA位宽或者用双缓冲double buffering重叠计算和搬运。第三累加器容量决定能否一次算完。如果输出矩阵太大累加器放不下就得拆成多次计算中间结果反复读写scratchpad。建议根据模型最后一层的输出尺寸来定acc_capacity。5.3 仿真加速的实用技巧Verilator仿真虽然比RTL仿真快但还是慢。几个加速手段用verbose0关闭详细日志减少max-cycles的值先跑短序列用-j参数并行编译但仿真本身是单线程的如果只是验证功能可以用C testbench直接调用Gemmini的C模型跳过RTL仿真注意C模型和RTL的行为可能有细微差异最终验证还是得跑RTL。6. 从Gemmini到自定义加速器的扩展思路Gemmini最大的价值不是它本身而是它作为模板的可扩展性。你可以改它的数据流、加新的指令、甚至替换脉动阵列为其他结构。我试过在Gemmini基础上加一个ReLU激活单元直接在阵列输出端做非线性变换省去了额外的内存往返。具体做法是在gemmini/src/main/scala/gemmini/下新建一个Scala文件继承Gemmini类重写outputStage方法。然后在配置里用你的自定义类替换默认的。编译后软件里调用gemmini_relu就能触发。另一个扩展方向是支持稀疏矩阵。Gemmini默认不支持稀疏但你可以加一个压缩格式解码器在mvin的时候解压。这需要改DMA逻辑工作量不小但收益明显——稀疏模型能省一半以上的计算周期。最后分享一个小技巧多看Chipyard的generated-src目录。每次编译后Verilog代码会生成在那里对照Scala源码看能快速理解参数是怎么映射到硬件的。我最初就是靠这个方法搞懂了tileRows和meshRows的区别。踩过几次坑之后我的建议是不要一上来就改架构先把默认配置跑通再逐步替换模块。Gemmini的文档不算完善很多细节得看源码和issue但社区活跃遇到问题搜一下基本能找到答案。
返回列表