4. Rocket Chip生成器:Chisel语言基础,用Rocket Chip生成SoC,配置L1/L2 Cache与总线
好,咱们进入正题。这一章我打算聊聊Rocket Chip生成器。说实话,我第一次接触Rocket Chip时,心里是有点发怵的——一个能自动生成完整SoC的工具,听起来就像黑魔法。但用熟了之后你会发现,它其实就是一套用Scala写的“硬件模板库”,核心是Chisel语言。
4.1 Chisel语言:硬件构建的“脚手架”
Chisel,全称是Constructing Hardware in a Scala Embedded Language。说白了,它是在Scala里嵌入的一套硬件描述语言。你写的是Scala代码,但生成的是Verilog网表。
为什么用Chisel而不是直接写Verilog? 我个人习惯用Chisel做三件事:参数化、代码复用、自动生成。举个例子,你想生成一个32位或64位的RISC-V核,用Verilog你得写两套代码,用Chisel改个参数就行。
核心概念: Chisel把硬件模块看成“对象”,连线看成“赋值”,寄存器看成“状态”。你写的是面向对象的硬件描述。
来看一个最简单的Chisel模块——一个带使能的计数器:
import chisel3._
class CounterWithEnable 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
}
io.out := count
}
这段代码里,RegInit定义了一个初始值为0的8位寄存器。when语句相当于Verilog里的always @(posedge clk)。嗯,这里要注意:Chisel默认所有寄存器都是时钟上升沿触发,复位是同步复位。
我在项目中遇到过一个坑:Chisel生成的Verilog里,复位信号默认是高有效。如果你的FPGA板子是低有效复位,记得在生成时加参数--resetType asyncLow。
4.2 Rocket Chip生成器:一键生成SoC
Rocket Chip是UC Berkeley开发的开源SoC生成器。它基于Chisel,能生成完整的RISC-V处理器系统。你给它一个配置文件,它给你吐出一个完整的SoC——包括CPU核、缓存、总线、外设。
下图是Rocket Chip生成SoC的核心架构:
这张图展示了Rocket Chip生成SoC的典型结构。Rocket Core通过L1缓存连接到TileLink总线,总线再挂载L2缓存、外设和DDR控制器。你想想看,这其实就是个标准的现代处理器架构,只不过所有模块都是自动生成的。
4.3 配置L1/L2 Cache:参数化设计
Rocket Chip的缓存配置是通过Scala的case class实现的。你不需要改任何硬件代码,改几个参数就行。
L1 Cache配置示例:
// 在Config文件中配置L1缓存
class MyL1Config extends Config((site, here, up) => {
case CacheBlockBytes => 64 // 缓存块大小:64字节
case L1ICacheSets => 64 // 指令缓存组数
case L1ICacheWays => 4 // 指令缓存路数(4路组相联)
case L1DCacheSets => 64 // 数据缓存组数
case L1DCacheWays => 4 // 数据缓存路数
case L1DCacheMSHRs => 8 // 未命中状态寄存器数
})
L2 Cache配置示例:
// L2缓存配置
class MyL2Config extends Config((site, here, up) => {
case L2Sets => 1024 // L2缓存组数
case L2Ways => 8 // L2缓存路数(8路组相联)
case L2BankCount => 4 // L2缓存bank数
case L2CapacityKB => 512 // L2总容量:512KB
})
我的经验: L1缓存大小建议设为16KB~64KB,L2缓存建议128KB~2MB。FPGA上L2别设太大,否则布线会很难受。我曾经在一个项目中把L2设成4MB,结果综合了整整两天还没跑完...
4.4 总线配置:TileLink与AXI的桥接
Rocket Chip内部使用TileLink总线协议。但很多FPGA外设IP用的是AXI协议。怎么办?Rocket Chip提供了TileLink-to-AXI桥接器。
总线配置示例:
// 添加AXI外设桥接
class MySoCConfig extends Config(new WithNBreakpoints(1) ++
new WithL1ICacheSets(64) ++
new WithL1DCacheSets(64) ++
new WithL2Cache(512, 1024, 8) ++
new WithAXI4MemPort ++ // 添加AXI内存端口
new WithAXI4MMIOPort ++ // 添加AXI MMIO端口
new BaseConfig) // 基础配置
这段配置做了三件事:
- WithAXI4MemPort:将TileLink总线桥接到AXI4,连接DDR控制器
- WithAXI4MMIOPort:桥接外设总线,连接UART、SPI等
- WithL2Cache:配置L2缓存参数
避坑指南: 我曾经在配置总线时,忘记设置MMIO地址映射范围。结果外设地址和内存地址冲突,CPU一访问外设就死机。记得在配置中显式指定外设的基地址:WithMMIOBase(0x40000000L)。
4.5 生成SoC的完整流程
配置写好了,怎么生成SoC?流程很简单:
- 编写Config文件:定义缓存大小、总线类型、外设数量
- 运行生成命令:
make verilog CONFIG=MySoCConfig - 检查生成结果:在
build/目录下找到Top.v和Top.anno.json - 集成到FPGA工程:将生成的Verilog文件添加到Vivado或Quartus工程
生成命令示例:
# 进入Rocket Chip目录
cd rocket-chip
# 生成Verilog
make verilog CONFIG=MySoCConfig
# 生成的文件在:
# build/MySoCConfig/Top.v
# build/MySoCConfig/Top.anno.json
生成的Top.v文件里,你会看到Rocket Core、L1/L2缓存、TileLink总线、AXI桥接器全部例化好了。你只需要在FPGA工程里添加时钟、复位和IO引脚约束就行。
4.6 实战建议:从简单开始
如果你是第一次用Rocket Chip,我建议你:
- 先用默认配置:Rocket Chip自带的
DefaultConfig能生成一个完整的SoC,先跑通再说 - 逐步修改参数:先改L1缓存大小,再改L2,最后改总线
- 仿真验证:用
make emulator CONFIG=MyConfig生成C++仿真模型,跑几个测试程序
嗯,这一章的内容就这些。Rocket Chip生成器说白了就是个“硬件编译器”,你给它配置,它给你SoC。关键是要理解Chisel的参数化思想——所有硬件模块都是可配置的,你改的是Scala代码,生成的是Verilog网表。
一句话总结: Chisel是笔,Rocket Chip是模板,配置是蓝图。三者结合,你就能在FPGA上快速搭建一个RISC-V SoC。
公众号:蓝海资料掘金营,微信deep3321