Rocket Chip 代码注释已上传至github,持续更新中。
Decode
本文分析Rocket Core中的译码逻辑。
以RV32I为例,指令集手册中RISC-V指令编码主要有下图六种类型
常见编码格式
译码模块需要根据opcode与func3/func7字段对指令译码,基于指令类型生成相应的控制信号送往对应模块。
相关代码
- Instructions.cala
- IDecode.scala
- Decode.scala
Instructions.scala
该文件基于RISC-V指令集架构手册中定义的RISC-V指令集编码,使用脚本自动生成。
Instuctions.scala 文件主要包含三个部分:
1. 指令集编码
rocket-core中指令编码如下图所示
object Instructions {
def BEQ = BitPat("b?????????????????000?????1100011")
def BNE = BitPat("b?????????????????001?????1100011")
def BLT = BitPat("b?????????????????100?????1100011")
def BGE = BitPat("b?????????????????101?????1100011")
def BLTU = BitPat("b?????????????????110?????1100011")
def BGEU = BitPat("b?????????????????111?????1100011")
...
}
为了提取opcode以及func字段,Chisel中定义了BitPat类来描述不同指令编码的pattern:
指令编码Patternwidth即为二进制长度,RV32I中指令的BitPat宽度均为32
BitPat中利用parse函数来从指令Pattern中解析 value 和 mask。
// BitPat中parse函数的关键代码
for (d <- x.tail) {
if (d != '_') {
require("01?".contains(d), "Literal: " + x + " contains illegal character: " + d)
mask = (mask << 1) + (if (d == '?') 0 else 1)
bits = (bits << 1) + (if (d == '1') 1 else 0)
}
}
- mask将"?"的位置标记无效,“0”和“1”有效
- value只记录“1”的位置
以指令BEQ为例
def BEQ = BitPat("b?????????????????000?????1100011")
- value : "b00000000000000000000000001100011"
- mask : "b00000000000000000111000001111111"
2. Causes
定义了异常/中断原因的编码,具体参考RISC-V指令集手册: 特权级架构
object Causes {
val misaligned_fetch = 0x0
val fetch_access = 0x1
val illegal_instruction = 0x2
val breakpoint = 0x3
val misaligned_load = 0x4
val load_access = 0x5
val misaligned_store = 0x6
val store_access = 0x7
val user_ecall = 0x8
val supervisor_ecall = 0x9
val hypervisor_ecall = 0xa
val machine_ecall = 0xb
val fetch_page_fault = 0xc
val load_page_fault = 0xd
val store_page_fault = 0xf
3. CSRs
定义了CSR寄存器地址编码,具体参考RISC-V指令集手册: 特权级架构
object CSRs {
val ustatus = 0x0
val uie = 0x4
val utvec = 0x5
val vstart = 0x8
val vxsat = 0x9
val vxrm = 0xa
val uscratch = 0x40
val uepc = 0x41
...
}
IDecode.scala
IDecode : Instruction Decode, 主要包含两部分:
- 译码生成的控制信号
- 指令集译码表
1. IntCtrlSigs: 译码得到的控制信号
class IntCtrlSigs extends Bundle {
// 1. 译码生成的控制信号
val legal = Bool() // 是否非法指令
val fp = Bool() // 是否浮点指令
val rocc = Bool() // 是否协处理器相关指令
val branch = Bool()
val jal = Bool()
val jalr = Bool()
// 读使能信号(xs猜测是excute stage)
val rxs2 = Bool()
val rxs1 = Bool()
val scie = Bool()
// ALU 控制信号
val sel_alu2 = Bits(width = A2_X.getWidth)
val sel_alu1 = Bits(width = A1_X.getWidth)
val sel_imm = Bits(width = IMM_X.getWidth)
val alu_dw = Bool() // double word 是否是64位
val alu_fn = Bits(width = FN_X.getWidth) // ALU function
val mem = Bool()
val mem_cmd = Bits(width = M_SZ)
// 下面三个信号目前没找到连线,还不清楚
val rfs1 = Bool()
val rfs2 = Bool()
val rfs3 = Bool()
val wfd = Bool()
val mul = Bool()
val div = Bool()
val wxd = Bool()
val csr = Bits(width = CSR.SZ)
val fence_i = Bool()
val fence = Bool()
val amo = Bool() // 原子指令
val dp = Bool() // 双精度浮点指令
// 2. default:译码表缺失,说明是非法指令
// 译码结果第一项legal信号为N,表明非法
def default: List[BitPat] = List(N, ... X, X, X)
// 3. 译码逻辑主体部分,基于指令和译码表,译码得到结果,返回该Bundle
def decode(inst: UInt, table: Iterable[(BitPat, List[BitPat])]) = {
// 调用DecodeLogic方法,译码结果存在decoder中
val decoder = DecodeLogic(inst, default, table)
// 利用scala高阶特性: zip + map将decoder译码结果赋值给本Bundle中
// 展开来相当于
// legal := decoder.legal
// fp := decoder.fp
// rocc := decoder.rocc
// ... 省略十几行orz
val sigs = Seq(legal, fp, rocc, branch, jal, jalr, rxs2, rxs1, scie, sel_alu2,
sel_alu1, sel_imm, alu_dw, alu_fn, mem, mem_cmd,
rfs1, rfs2, rfs3, wfd, mul, div, wxd, csr, fence_i, fence, amo, dp)
sigs zip decoder map {case(s,d) => s := d}
// 最后返回this, 即返回赋值完成后的IntCtrlSigs
this
}
}
可以看到,译码逻辑相关的方法定义在控制信号中。
备注:
IntCtrlSigs命名中的Int即整数Int,与之相对应的就是浮点数的FPCtrlSigs
FPCtrlSigs的译码逻辑与IntCtrlSigs相类似,参考FPU即可
指令集译码表
译码表类视图- DecodeConstants定义为abstract trait,类似于C++中的虚基类,其中只定义了一个译码表(table),
- 剩下的所有xxxDecode均继承自DecodeConstants,内部定义了各个指令扩展的译码表(table)。
RISC-V模块化的设计使得Rocket Core可以通过config配置需要的指令扩展,并将选择的指令译码表整合。
举个例子,例如我们需要实现RV32IM,配置config后,Rocket Generator会将IDecode.table与MDecode.table合并成一张译码表(decode_table),合并相关的逻辑如下所示(只截取部分代码说明,完整代码见RocketCore.scala)
val decode_table = {
// usingMulDiv来自rocket-core的配置
(if (usingMulDiv) new MDecode(pipelinedMul) ++:
// RV32I是文档中要求必须实现的部分
Seq(new IDecode)
} flatMap(_.table) // 合并成一张译码表
合并逻辑在Chisel代码编译过程中自动生成所需要的译码逻辑,无关的指令集扩展会被直接无视,并不会产生冗余的译码逻辑电路。
Decode.scala
上面IntCtrlSigs的decode函数中调用了DecodeLogic函数来译码,该函数就定义在Decode.scala中,这里实现了Quine-McCluskey算法,以简化指令Pattern的译码逻辑。
网络上资料讲解得比较清楚(但我没弄清楚,有点复杂orz),这里只罗列一些重要的参考资料。
重要名词解释
- Quine-McCluskey算法
- Term_algebra(项代数)
- Boolean algebra(布尔代数)
- implicant(蕴含项)
- prime implicant(素项,质项,主蕴含项)
文章(来源于公众号:故事v历史)
网友评论