ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Chisel从零起步:学习路线、工具链与RTL生成器核心概念

Chisel从零起步:学习路线、工具链与RTL生成器核心概念 我经常在社区里收到一类问题“我想学Chisel有没有一套从零开始的教程”这个问题我过去很难用一段话回答干净。不是Chisel本身有多难而是市面上的资料默认你已经掌握了某些前提——Scala语法、RTL设计、构建工具、仿真流程——每份资料单独看都讲得不错串起来却到处是断点。很多人学了两天就卡在环境上卡在“这段代码到底是怎么变成硬件的”上然后默默放弃。这套教程的“第零章”就是为了处理这些断点而存在的。这篇序文不教任何语法只负责把整条学习路径摊开给你看Chisel是什么、学完能做什么、按什么顺序学、环境怎么搭、资料怎么选。无论你是FPGA工程师、数字IC方向的学生还是对RISC-V生态感兴趣的软件开发者读完这一章你至少能省掉一周盲目试错的时间。1. 为什么要有“第零章”碎片化资料比语法更难1.1 我早年踩过的信息碎片化坑我第一次接触Chisel是在一个开源处理器项目里仓库的代码看起来非常优雅一个参数化的寄存器堆几十行就搞定。我当时想这不比Verilog的generate写起来舒服多了然后我去查教程发现第一个绊脚石根本不是语法而是“哪个版本的教程是当前有效的”。Chisel这十年变化很大。早期用的还是UInt(width 8)这种旧API后来改成UInt(8.W)Chisel 3.6之后默认的编译后端从Scala写的FIRRTL编译器换成了CIRCT里的firtool版本坐标也从edu.berkeley.cs挪到了org.chipsalliance。论坛上一个2020年的帖子讲的是Chisel 3.2的写法放到现在可能连import都过不了编译。更麻烦的是很多人把Chisel和HLS混为一谈以为写一段C-like代码就能自动生成电路。这个误解会让你在错误的路上走很久。资料越查越多观点越来越乱最后还得靠自己在GitHub issues里翻答案。所以我想用一套成体系的教程把“资料之间断掉的地方”补上把“版本演变造成的坑”提前标出来。1.2 这套教程到底在讲什么简单说这套教程的目标是让你从“熟悉基本数字电路但不太会Scala”的状态走到“能用Chisel独立设计、仿真、测试一个参数化模块”的状态。整条路线分成四个阶段阶段一Scala基础——只讲够用的那部分不讲类型类和Monad阶段二Chisel核心语法——Module、IO、Bundle、Reg、Wire、when、状态机、Mem阶段三仿真与测试——chiseltest的使用波形查看测试怎么设计阶段四实战项目——从简单计数器到可配置FIFO再到尝试接入一个小型RISC-V部件。后面的章节会严格按这个顺序推进。第零章先把路线图画出来你心里有底了学起来才不会慌。1.3 这套教程适合谁我在社区遇到过三类典型学员他们的切入点和诉求完全不一样第一类是FPGA工程师习惯Verilog/VHDL想用Chisel提高参数化复用能力但担心学了之后和主流工程脱节。我的建议很直接你不需要把Chisel用到量产项目里但值得在上板验证之前用它快速做原型和算法验证。第二类是在校学生课程或毕设需要做RISC-V相关课题而Rocket Chip、BOOM这些核心项目都是Chisel写的。对他们来说Chisel不是可选项是必选项。第三类是软件工程师对硬件好奇被“用Scala写硬件”这个概念吸引。这类读者最容易踩的坑是用软件思维去推电路所以我后面专门用篇幅讲电路思维的转换。这三类人的背景不同但在前几章的公共路径上是一样的把Scala调到够用水平把Chisel的类型系统啃明白把测试跑通。2. 先把概念划清楚Chisel到底是什么不是什么2.1 一句话理解ChiselChisel全称是Constructing Hardware in a Scala Embedded Language本质上是嵌在Scala里的一门硬件构造语言。“构造”这个词很关键。你在Chisel里写的东西描述的是“如何生成硬件”而不是像Verilog那样直接罗列寄存器传输级的结构。Scala代码在运行期被执行不断调用Chisel库里的类和方法在内存里构建出一张电路图。这张图再交给后面的编译工具翻译成Verilog。我习惯用模具来类比Chisel代码是一套可编程的模具Scala是操作模具的手firtool是把模具压出来的成品运到仓库的传送带。同样的模具参数不同压出来的零件形状就不同。这在生成器的语境里非常自然。2.2 从Scala到Verilog中间发生了什么Chisel项目的编译流程不是一步到位的它分成两层Scala代码 - FIRRTL中间表示 - SystemVerilog/VerilogFIRRTL是Flexible Intermediate Representation for RTL专门为硬件中间表示设计的一种IR。为什么要多这一层因为Scala代码经过执行产生的是Chisel对象的抽象语法树直接翻译成Verilog会有很多冗余和低效而在FIRRTL这一层可以统一做大量优化——常量折叠、死逻辑消除、寄存器推断、连接合法性检查等——结束后再发射成Verilog。更妙的是这一层的存在让硬件变换变成了一种可编程能力。有些人做低功耗插入、有些人做调试探针注入都是在FIRRTL层面写自定义pass。这也是Chisel生态能持续演进的底层原因。早期FIRRTL编译器用Scala实现现在已经全面切换到CIRCT项目的firtool用MLIR框架重写性能和可扩展性都升了一级。2.3 和Verilog、HLS的边界在哪里很多人第一次接触Chisel都会问它和HLS是不是一回事不是。HLSHigh-Level Synthesis让你写C/C工具负责推测出架构和状态机你基本不直接描述寄存器。Chisel恰恰相反你仍然明确描述每一个模块、寄存器、线网和连接关系只是这种描述用的是Scala语法。我整理过一个粗线条的对比可以帮你快速定位维度Verilog/SystemVerilogChiselHLS抽象级别RTL贴近门级RTL到生成器级算法级电路结构控制完全手动手动但可参数化工具推断参数化能力generate 宏较笨拙Scala循环/函数/类灵活C模板编译报错友好度低常到波形阶段才暴露类型检查提前拦截很多错误依赖工具链调试生态成熟正在追赶依赖仿真典型用途工业量产IP开源CPU、教学、参数化生成器算法硬件快速实现所以Chisel的位置非常清晰它依然是“你在画电路”不是“你描述问题让机器画电路”。它换掉的只是画电路的那支笔不是画电路这个核心动作。这个认知越早建立越不容易在后面写出“看起来像软件其实应该长成电路”的代码。3. 这套教程的路线图四条阶段主线与每章的落点3.1 阶段一Scala只学够用的20%不少同学在Scala这道坎上被劝退这是一个巨大的冤枉。Chisel真正用到的Scala特性并不多远没有到需要精通函数式编程的程度。你只需要重点掌握这几样val不可变变量、def方法定义、if/else和for循环、Map和List的基本操作、Option的简单使用、case class和模式匹配、字符串插值。这些东西加起来认真学一个周末就能上手。真正不需要碰的是隐式转换的底层原理、类型类、Monad、Actor模型、Future并发这些。它们确实让Scala很强但在Chisel的日常开发里不是必备品。等你在Chisel里写了三个月之后如果真想回头深挖Scala那是加分项一开始扑进去只会消耗你对Chisel的热情。阶段一结束的检验标准是你能独立写一个带参数的函数能把一个整数序列用map变换成另一个序列能定义case class并做模式匹配。做到这一步Scala部分就算通关了。3.2 阶段二Chisel语法与电路思维并进进入Chisel语法后我的建议是“语法和电路思维两条腿一起走”千万不要把Chisel当成一本字典从头背到尾。必学清单如下Module和IO每个硬件模块的入口UInt、SInt、Bool、Bundle、Vec数据类型体系Wire、Reg、RegInit组合逻辑元件和时序元件when / elsewhen / otherwise电路级条件选择Mux和MuxLookup多路选择器状态机的标准写法状态寄存器和次态组合逻辑的分离Mem和SyncReadMem片上存储器的抽象BlackBox对接已有的Verilog IP和厂商原语。这一阶段最容易犯的错误是把Scala的if当成硬件分支来用。记住Scala的if是在生成期执行的when才是生成出来的硬件Mux。很多刚转过来的人在这个点上迷惑后面章节我会专门用一章对比两者。阶段二结束时你应该能独立写出一个带使能和同步复位的双向计数器并且能画出它对应的寄存器级结构。3.3 阶段三用测试逼自己写对电路在Chisel生态里测试不像Verilog那样必须搭一台外部仿真器chiseltest可以在Scala环境内直接对模块做poke/expect/step。这让测试的启动成本大幅降低也让我养成了“写完模块立刻补测试”的习惯。这一阶段你会接触三样东西chiseltest最常用的Scala测试框架写起来像单元测试treadleChisel自带的轻量仿真引擎适合快速跑功能验证Verilator高性能Verilog仿真器适合生成Verilog后做更接近真实的验证。我强烈建议在第一篇独立教程示例里就引入测试。它能让你立刻看到“我在模块里写的逻辑到底对不对”而不是等上板才发现问题。芯片设计的真理永远是仿真阶段多花一个小时板级调试少花三天。3.4 阶段四真实项目练手语法和测试都跑通之后一定要尽快进入真实项目。我推荐三个递增难度的练手方向第一用Chisel写一个带AXI接口的寄存器堆或FIFO。这里面涉及队列、流控、指针和跨时钟域处理的经典问题能把前面学的语法点全部串起来。第二实现一个简单的五级流水线CPU。Berkeley的RISC-V Sodor和很多高校教学项目都有现成版本可参考但你一定要自己从零写一遍哪怕功能有限也比读过十遍别人的代码强。第三尝试用Chipyard搭建一个小型SoC。Chipyard把Rocket Chip、BOOM和各种外设整合成了一套可配置的SoC生成框架。当你的一个自写模块真正和Rocket处理器一起跑起来时你会对整条工具链的理解上一个台阶。4. 第一道关环境工具链怎么搭才不劝退4.1 工具清单和版本对应关系环境搭建是很多人倒在第一关的地方但不是因为难而是因为版本错配。我整理了一张最小工具清单工具作用推荐版本JDKChisel依赖JVM环境JDK 17LTSsbtScala构建工具1.9通常随项目配置自动下载Verilator生成Verilog后的仿真5.x按需安装用于仿真iverilog轻量Verilog仿真器11.x备选firtoolFIRRTL到Verilog的编译器构建时由Chisel自动拉取sbt会在首次构建时下载Scala编译器和依赖这是整个环境搭建中最耗时的一步。不要慌多花几分钟等缓存建立起来后面就顺畅了。如果你的主力系统是Windows我建议直接装WSL2在Ubuntu环境里完成所有操作。原生Windows下sbt的路径处理偶尔会出其不意我遇到过几次诡异的编译错误换到WSL后就消失了。4.2 一个最小工程的手把手搭建我直接给一份可运行的工程配置。创建一个目录比如chisel-tutorial然后在根目录放build.sbtThisBuild / scalaVersion : 2.13.14 ThisBuild / version : 0.1.0 val chiselVersion 6.0.0 // 换成你安装时官网的最新稳定版即可 lazy val root (project in file(.)) .settings( name : chisel-tutorial, libraryDependencies Seq( org.chipsalliance %% chisel % chiselVersion, org.chipsalliance %% chiseltest % chiselVersion % Test ), scalacOptions Seq( -language:reflectiveCalls, -language:implicitConversions, -deprecation, -feature, -unchecked ) )然后创建src/main/scala/Counter.scalaimport chisel3._ class Counter(max: Int 5) extends Module { val io IO(new Bundle { val en Input(Bool()) val out Output(UInt(8.W)) }) val count RegInit(0.U(8.W)) when(io.en) { count : count 1.U when(count max.U) { count : 0.U } } io.out : count }测试文件放在src/test/scala/CounterTest.scalaimport chisel3._ import chiseltest._ import org.scalatest.flatspec.AnyFlatSpec class CounterTest extends AnyFlatSpec with ChiselScalatestTester { Counter should wrap around at max in { test(new Counter(5)) { c val expected Seq(0, 1, 2, 3, 4, 5, 0, 1, 2) c.io.en.poke(true.B) for (exp - expected) { c.io.out.expect(exp.U) c.clock.step(1) } } } }再写一个生成Verilog的入口src/main/scala/Main.scalaimport circt.stage.ChiselStage object Main extends App { ChiselStage.emitSystemVerilog(new Counter(5), Array(--target-dir, generated)) }然后在命令行运行sbt run sbt testOnly CounterTest如果一切顺利你会看到测试通过generated目录下出现一个可读性不错的SystemVerilog文件。这套最小工程模板后面所有章节都会复用你可以直接把它当作起步脚手架。4.3 我踩过的环境坑第一个坑是JDK版本。Chisel 6要求JDK 17如果你系统默认是JDK 8或11sbt启动时会有各种奇怪的警告。务必在安装前用java -version确认最好设置JAVA_HOME指向JDK 17。第二个坑是sbt首次下载依赖非常慢。这不是你的问题也不是sbt的问题是网络带宽和Maven Central之间的距离问题。等就完了首次构建下载几百MB依赖并不罕见。如果你在公司或学校网络里有镜像源按需配置一下能快很多。第三个坑是Verilator版本太低。部分老版本Verilator对SystemVerilog的新特性支持不完整chiseltest在调用时可能报错或行为异常。如果apt源里的版本太老可以从源码编译或者用包管理器装最新版。宁可在这一步多花半小时也不要在后面问题满天飞时再回头排查。第四个坑是在IDE里直接跑sbt命令。IDEA或VSCode内置终端通常没问题但有时Media类型或内存设置不对会导致sbt卡在编译阶段。我的习惯是开发在IDE里写代码编译测试在命令行终端里跑两边分开互不干扰。5. 跟官方教程死磕不如这样用资源搭配5.1 三份值得反复看的核心资料Chisel生态的资料并不少但质量参差版本混乱很多人一头扎进去就迷路了。按我的经验优先看这三份第一是官网chisel-lang.org。它提供API文档和Coding Standard适合当字典查。API文档会标出哪些接口是stable、哪些正在演进这对避坑很有用。第二是Chisel Learning Journey。这是Chisel团队自己维护的分步式教程从安装环境到基础语法再到小项目循序渐进的节奏设计得很好并且紧跟最新版本。学第一遍语法时跟着它走一遍比我推荐的任何第三方博客都靠谱。第三是Chisel Book作者是Martin Schoeberl可以在线免费阅读。它的特点是每个例子都短小精悍直接可跑而且覆盖了大量用于教学的经典电路模板——计数器、FIFO、状态机、流水线。我建议把它当案头书写模块前先翻一翻有没有可参考的模板。早期很流行的Chisel Bootcamp是Jupyter Notebook风格的交互式教程历史价值很大但现在内容已经部分迁移到新项目中了我不再推荐作为第一入口。学习时精力有限先跟着主线走再回头补充历史背景。5.2 开源项目里藏着最好的实战代码文档永远只能教你规范想写出好代码一定得读真实项目。Chisel圈最值得读的开源项目我排一个优先级第一个是riscv-sodor一个教学用的小型RISC-V处理器。代码量不大结构清晰非常适合第一次阅读“完整CPU”的Chisel实现。读它的过程就像看一本教科书级的参考实现。第二个是Rocket Chip这是一套完整的Rocket处理器SoC生成框架。它的代码复杂度高不少但如果你关心参数化SoC生成、缓存层次设计、Rocket核心的流水线细节这里是最好的教材。第三个是BOOM基于Rocket Chip的顺序/乱序执行的超标量处理器。如果你对乱序执行感兴趣想理解ROB、物理寄存器堆、调度队列这些部件怎么用Chisel高效表达BOOM源码值得反复看。进阶之后还可以看Chipyard它把Rocket Chip、BOOM、各种外设和仿真基础设施整合成了一套完整的SoC设计平台。做综合性的SoC项目时Chipyard几乎是绕不开的。读这些项目时不要从头到尾盲看而是先带着问题去翻某一个子模块。比如我想知道“带参数的可配置优先级编码器怎么写”就去Rocket Chip的源码里搜PriorityEncoder看它的接口设计再把实现简化提取出来用到自己的工程里。5.3 遇到问题去哪里问、怎么问不被嫌弃学习和使用的路上一定会遇到问题。按优先级排序我的提问路径是先搜Stack Overflow的chisel标签。很多经典问题有详细答案尤其是类型错误、连接错误这类高频问题。再翻Chisel项目的GitHub Discussions。这里官方维护者和核心贡献者活跃能就某个具体问题给出权威答案。不要害怕用英文提问Chisel社区对新手相当宽容。最后是Matrix上的#chisel频道适合讨论快速问答和闲聊式的问题。国内用户注意时区差异通常欧美工作时间的深夜或清晨回复率最高。提问时一定要附上三样东西完整的最小复现代码、你用的Chisel版本和Scala版本、完整的报错日志。只贴半段代码和一句“为什么报错”的人基本得不到有效的帮助。这不是社区冷漠而是信息不足时没人能替你猜。6. 调整三个心态避免在第五天放弃6.1 Chisel不是HLS“偷懒”的前提是你懂电路我见过很多软件背景的读者被“用Scala写硬件”吸引进来然后试图写一大段循环和递归指望工具自动生成一个高效的硬件架构。这是Chisel新手最大的误区。Chisel的高效来自“生成器”和“参数化”而不是“自动推断结构”。举一个例子你想实现一个多路选择器在Chisel里依然要明确画出mux结构你想写一个状态机依然要自己定义状态编码自己写次态转移逻辑。Scala只是让这些结构可以循环生成、参数配置而不是代替你做架构决策。所以Chisel入门的前提是对数字电路有基本概念组合逻辑、时序逻辑、寄存器、状态机、总线时序。如果你对RTL本身完全陌生我建议先花一两周读一点Verilog不必精通但要能看懂一个简单的模块。电路思维是Chisel不能替你补的课。6.2 别钻Scala的牛角尖“会写”比“精通”重要很多学Chisel的人从第一天开始就想搞明白Scala里的implicit转换到底怎么工作。这是过度准备会拖垮学习节奏。Chisel官方库大量使用隐式转换让你能写3.U、1.B、max.U但底层的机制在90%的场景下你不需要理解。它就像汽车仪表盘上的指示灯你不需要会修发动机才能开车。等你真的遇到类型不匹配、隐式转换报错时再回来研究机制也不迟。我给自己的规矩是每个新概念先问“我现在用得上吗”用不上就记个笔记先跳过。等到用得上再回来深挖理解收益最大。6.3 类型安全只是第一道防线验证不能省Chisel最吸引我的一点是很多错误在编译期就能暴露总线宽度不匹配、类型不一致、驱动冲突等Scala的类型系统和Chisel的连接规则会提前报错。这让写Chisel时的心流体验比Verilog好不少。但类型安全绝对不代表逻辑正确。你完全可以在类型安全的代码里写出一个时序完全错误的计数器或者一个死锁的FIFO。Chisel的类型检查只保证“信号的形状对不对”完全不保证“信号的行为对不对”。所以从第一课开始就要养成“写完模块立刻写测试”的习惯。Chisel项目里测试代码量常常是设计的2到3倍这很正常。花在测试上的时间会在你后续做系统集成时成倍地赚回来。7. 从第零章到第一章最小自检清单与学习顺位7.1 学完第零章你应该有这些收获这一章虽然没讲具体语法但最重要的三件事应该已经装进你脑子里第一你能用自己的话说清楚Chisel、Verilog和HLS三者之间的区别。尤其要能说清“Chisel的代码是在生成期执行的它描述的是电路结构而非算法”。第二你明白Chisel到Verilog要经过FIRRTL和firtool这一层。知道中间层的作用后面看到各种编译警告和优化选项时才不会一头雾水。第三你手头有一套能跑通的最小工程。环境问题解决得越早后续的学习阻力就越小。如果现在还没搭好环境我建议先停下来把第4章的步骤做完再往下一章走。7.2 从第零章到第一章前先做一次自检在开始学下一章之前我建议你对照这份清单检查一下电脑上能成功编译并运行Counter例子能生成Verilog文件能在CounterTest里跑通测试并看到测试结果能大致描述“Scala代码 - FIRRTL - SystemVerilog”这条链路手边有Chisel官方文档和Chisel Book这两个可以随时查阅的资源入口明确了自己属于哪类学习者知道自己学Chisel要解决的具体问题。如果上述五项全部通过你就可以放心进入第一章了。7.3 第一章会讲什么第一章我们会进入真正的Chisel代码数据类型体系和Module/IO/Bundle的完整写法并在一个带使能、带同步复位的计数器基础上向你展示Chisel和Verilog最核心的思维差异。第一章会有完整的工程代码、电路结构对照图和配套测试顺着第零章搭好的环境直接跑就行。最后说一句我的个人体会学Chisel的正确节奏不是坐在那里把所有概念都搞懂再动手而是动手跑通、动手改写、动手破坏再从报错中学。拿本章的计数器例子练手你可以试着把max改成128把计数位宽改成16位给自己加一个清零输入。折腾出来的问题往往是最有价值的学习素材。
返回列表