Litex-VexRiscv 编译SoC
Litex-VexRiscv 编译SoC
Litex 框架通过以下流程调用 VexRiscv 源码并编译成 SoC:
1 | |
流程分析
入口
make.py 是整个构建过程的入口点,负责:
1 | |
Litex 封装
core.py VexRiscvSMP 类是 Litex 对 VexRiscv 的封装,核心功能:
1 | |
VexRiscv 核心实现
VexRiscvSmpLitexCluster.scala 是用 SpinalHDL 编写的 VexRiscv SMP 集群实现:
1 | |
添加自定义指令
SimdAddPlugin示例
以下是一个简单插件的示例,它添加了一个简单的SIMD_ADD指令。
SimdAddPlugin 实现了一个 SIMD 加法指令,将 32 位寄存器分为 4 个 8 位通道,分别进行加法运算:
1 | |
如果你想把这个插件添加到某个CPU上,只需把它添加到其参数化插件列表。
示例代码分析
1.导入和类定义1
2
3
4
5import spinal.core._
import vexriscv.plugin.Plugin
import vexriscv.{Stageable, DecoderService, VexRiscv}
class SimdAddPlugin extends Plugin[VexRiscv]{
这部分代码导入了必要的包,并定义了 SimdAddPlugin 类,继承自 Plugin [VexRiscv],表明这是一个 VexRiscv 插件。
2.阶段信号定义1
object IS_SIMD_ADD extends Stageable(Bool)
这行代码定义了一个 Stageable 对象 IS_SIMD_ADD,类型为 Bool,用于在流水线中传递控制信号,表示当前指令是否是 SIMD_ADD 指令。
3.setup 方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25override def setup(pipeline: VexRiscv): Unit = {
import pipeline.config._
//Retrieve the DecoderService instance
val decoderService = pipeline.service(classOf[DecoderService])
//Specify the IS_SIMD_ADD default value when instructions are decoded
decoderService.addDefault(IS_SIMD_ADD, False)
//Specify the instruction decoding which should be applied when the instruction matches the 'key' parttern
decoderService.add(
//Bit pattern of the new SIMD_ADD instruction
key = M"0000011----------000-----0110011",
//Decoding specification when the 'key' pattern is recognized in the instruction
List(
IS_SIMD_ADD -> True,
REGFILE_WRITE_VALID -> True, //Enable the register file write
BYPASSABLE_EXECUTE_STAGE -> True, //Notify the hazard management unit that the instruction result is already accessible in the EXECUTE stage (Bypass ready)
BYPASSABLE_MEMORY_STAGE -> True, //Same as above but for the memory stage
RS1_USE -> True, //Notify the hazard management unit that this instruction uses the RS1 value
RS2_USE -> True //Same as above but for RS2.
)
)
}
setup 方法用于配置插件和获取服务:
- 获取 DecoderService 实例,用于配置指令解码规则
- 设置 IS_SIMD_ADD 的默认值为 False
- 定义指令编码模式和解码规则:
- key = M”0000011—————000——-0110011”:定义了 SIMD_ADD 指令的编码模式
- 当指令匹配该模式时,设置以下控制信号:
- IS_SIMD_ADD -> True:表示这是 SIMD_ADD 指令
- REGFILE_WRITE_VALID -> True:启用寄存器文件写入
- BYPASSABLE_EXECUTE_STAGE -> True:通知冒险管理单元指令结果在执行阶段可用于旁路
- BYPASSABLE_MEMORY_STAGE -> True:通知冒险管理单元指令结果在内存阶段可用于旁路
- RS1_USE -> True:通知冒险管理单元该指令使用 RS1 寄存器
- RS2_USE -> True:通知冒险管理单元该指令使用 RS2 寄存器
4.build 方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24override def build(pipeline: VexRiscv): Unit = {
import pipeline._
import pipeline.config._
//Add a new scope on the execute stage (used to give a name to signals)
execute plug new Area {
//Define some signals used internally by the plugin
val rs1 = execute.input(RS1).asUInt
//32 bits UInt value of the regfile[RS1]
val rs2 = execute.input(RS2).asUInt
val rd = UInt(32 bits)
//Do some computations
rd(7 downto 0) := rs1(7 downto 0) + rs2(7 downto 0)
rd(16 downto 8) := rs1(16 downto 8) + rs2(16 downto 8)
rd(23 downto 16) := rs1(23 downto 16) + rs2(23 downto 16)
rd(31 downto 24) := rs1(31 downto 24) + rs2(31 downto 24)
//When the instruction is a SIMD_ADD, write the result into the register file data path.
when(execute.input(IS_SIMD_ADD)) {
execute.output(REGFILE_WRITE_DATA) := rd.asBits
}
}
}
build 方法用于实现指令的功能逻辑:
- 在执行阶段(execute)添加一个新的 Area,用于组织相关信号
- 定义内部信号:
- rs1:从流水线输入获取 RS1 寄存器的值,转换为 UInt 类型
- rs2:从流水线输入获取 RS2 寄存器的值,转换为 UInt 类型
- rd:定义一个 32 位 UInt 类型的信号,用于存储结果
- 实现 SIMD 加法逻辑:
- rd (7 downto 0) := rs1 (7 downto 0) + rs2 (7 downto 0):第 0 个 8 位通道的加法
- rd (16 downto 8) := rs1 (16 downto 8) + rs2 (16 downto 8):第 1 个 8 位通道的加法
- rd (23 downto 16) := rs1 (23 downto 16) + rs2 (23 downto 16):第 2 个 8 位通道的加法
- rd (31 downto 24) := rs1 (31 downto 24) + rs2 (31 downto 24):第 3 个 8 位通道的加法
- 结果写回逻辑:
- 当 IS_SIMD_ADD 信号为 True 时,将 rd 的值转换为 Bits 类型,并写入 REGFILE_WRITE_DATA 信号,以便在写回阶段写入寄存器文件
添加自定义CSR
CSR 地址空间分配
RISC-V ISA 预留了 12 位编码空间(csr [11:0],即 0x000-0xFFF),最多可支持 4096 个 CSR。CSR 地址的高 4 位(csr [11:8])用于根据特权级别编码 CSR 的读写访问权限:
- 最高两位(csr [11:10])表示寄存器是读 / 写(00,01, 或 10)还是只读(11)
- 接下来两位(csr [9:8])编码可以访问 CSR 的最低特权级别
CSR 插件实现
VexRiscv 通过两个主要插件实现 CSR 功能:
- CsrAccessPlugin:
- 实现了 CSR 的读写指令
- 为其他插件提供了 API 来指定 CSR 寄存器和 CSR 指令之间的映射
- 处理 CSR 访问的特权级别检查
- CsrRamPlugin:
- 提供了一个 API,允许静态分配空间
- 创建读写端口,以 FPGA 高效的方式存储 CSR 内容
- 被各种插件用于存储 CSR 内容
VexRiscv 插件系统提供了一个 CsrInterface 服务,允许其他插件轻松地添加自定义 CSR。这个服务提供了以下主要方法:
- rw(address: Int, data: Data):将一个可读写的 CSR 映射到指定地址
- r(address: Int, data: Data):将一个只读的 CSR 映射到指定地址
- onWrite(address: Int)(callback: => Unit):当向指定地址写入时执行回调
- onRead(address: Int)(callback: => Unit):当从指定地址读取时执行回调
通过这些方法,插件可以轻松地将自定义寄存器映射到 CSR 地址空间,并定义读写行为。
CustomCsrDemoPlugin示例
CustomCsrDemoPlugin.scala 包含两个插件类:
CustomCsrDemoPlugin:添加了指令计数器和时钟周期计数器
CustomCsrDemoGpioPlugin:创建了一个直接映射到 CSR 的 GPIO 外设
这两个类都继承自Plugin[VexRiscv],表明它们是 VexRiscv 插件。
CustomCsrDemoPlugin类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29class CustomCsrDemoPlugin extends Plugin[VexRiscv]{
override def build(pipeline: VexRiscv): Unit = {
import pipeline._
import pipeline.config._
pipeline plug new Area{
val instructionCounter = Reg(UInt(32 bits))
val cycleCounter = Reg(UInt(32 bits))
cycleCounter := cycleCounter + 1
when(writeBack.arbitration.isFiring) {
instructionCounter := instructionCounter + 1
}
val csrService = pipeline.service(classOf[CsrInterface])
csrService.rw(0xB04, instructionCounter)
csrService.r(0xB05, cycleCounter)
csrService.onWrite(0xB06){
instructionCounter := 0
}
csrService.onRead(0xB07){
instructionCounter := 0x40000000
}
}
}
}
这个插件实现了以下功能:
- 创建计数器寄存器:
- instructionCounter:记录已执行的指令数
- cycleCounter:记录时钟周期数
- 计数器更新逻辑:
- cycleCounter每个时钟周期加 1
- instructionCounter在写回阶段(writeBack)触发时加 1
- CSR 映射:
- csrService.rw(0xB04, instructionCounter):将 instructionCounter 映射到 0xB04 地址,可读写
- csrService.r(0xB05, cycleCounter):将 cycleCounter 映射到 0xB05 地址,只读
- 特殊 CSR 行为:
- csrService.onWrite(0xB06):当向 0xB06 地址写入时,重置 instructionCounter
- csrService.onRead(0xB07):当从 0xB07 地址读取时,返回 0x40000000
CustomCsrDemoGpioPlugin类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class CustomCsrDemoGpioPlugin extends Plugin[VexRiscv]{
var gpio : TriStateArray = null
override def setup(pipeline: VexRiscv): Unit = {
gpio = master(TriStateArray(32 bits)).setName("gpio")
}
override def build(pipeline: VexRiscv): Unit = {
import pipeline._
import pipeline.config._
pipeline plug new Area{
val writeReg, writeEnableReg = Reg(Bits(32 bits))
val csrService = pipeline.service(classOf[CsrInterface])
csrService.rw(0xB08, writeReg)
csrService.rw(0xB09, writeEnableReg)
csrService.r(0xB0A, gpio.read)
gpio.writeEnable := writeEnableReg
gpio.write := writeReg
}
}
}
这个插件实现了以下功能:
- GPIO 接口定义:
- 在setup方法中定义了一个 32 位的三态数组 GPIO 接口
- 这个接口被设置为 master 类型,表示它是一个输出接口
- GPIO 控制寄存器:
- writeReg:存储要写入 GPIO 的值
- writeEnableReg:存储 GPIO 的写使能信号
- CSR 映射:
- csrService.rw(0xB08, writeReg):将 writeReg 映射到 0xB08 地址,可读写
- csrService.rw(0xB09, writeEnableReg):将 writeEnableReg 映射到 0xB09 地址,可读写
- csrService.r(0xB0A, gpio.read):将 gpio.read 映射到 0xB0A 地址,只读
- GPIO 控制逻辑:
- gpio.writeEnable := writeEnableReg:将 writeEnableReg 的值赋给 gpio.writeEnable
- gpio.write := writeReg:将 writeReg 的值赋给 gpio.write
自定义CSR步骤
- 创建插件类:
- 继承自Plugin[VexRiscv]
- 实现setup和 / 或build方法
- 定义自定义寄存器:
- 在插件中定义需要映射到 CSR 的寄存器
- 定义寄存器的更新逻辑
- 获取 CsrInterface 服务:
- 使用pipeline.service(classOf[CsrInterface])获取 CsrInterface 服务
- 映射 CSR 地址:
- 使用csrService.rw、csrService.r、csrService.onWrite或csrService.onRead方法映射 CSR 地址
- 配置 CPU 时添加插件:
- 在 CPU 配置中添加自定义 CSR 插件
自定义示例代码
1.CSR 插件示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31class MyCustomCsrPlugin extends Plugin[VexRiscv]{
override def build(pipeline: VexRiscv): Unit = {
import pipeline._
import pipeline.config._
pipeline plug new Area{
// 定义自定义寄存器
val myCounter = Reg(UInt(32 bits)) init(0)
val myControl = Reg(Bits(4 bits)) init(0)
// 定义寄存器更新逻辑
myCounter := myCounter + 1
// 获取CsrInterface服务
val csrService = pipeline.service(classOf[CsrInterface])
// 映射CSR地址(使用自定义CSR范围)
csrService.rw(0x7C0, myCounter) // 读写访问
csrService.rw(0x7C1, myControl) // 读写访问
// 定义特殊行为
csrService.onWrite(0x7C2){ // 当写入0x7C2时
myCounter := 0 // 重置myCounter
}
csrService.onRead(0x7C3){ // 当读取0x7C3时
// 执行一些操作
}
}
}
}
2.在 CPU 配置中添加插件:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 创建CPU配置
val cpu = new VexRiscv(
// 添加自定义CSR插件
plugins = List(
new IBusSimplePlugin(resetVector = 0x80000000l),
new DecoderSimplePlugin,
new RegFilePlugin(regFileReadyKind = plugin.SYNC, zeroBoot = false),
new IntAluPlugin,
new SrcPlugin(separatedAddSub = false, executeInsertion = true),
new FullBarrelShifterPlugin,
new HazardSimplePlugin(bypassExecute = true, bypassMemory = true, bypassWriteBack = true, bypassWriteBackBuffer = true),
new BranchPlugin(earlyBranch = false, catchAddressMisaligned = true),
new CsrPlugin(CsrPluginConfig.small), // 需要添加CsrPlugin
new MyCustomCsrPlugin // 添加自定义CSR插件
)
)