Decode
本文分析Rocket Core中的譯碼邏輯已脓。
以RV32I為例厕宗,指令集手冊中RISC-V指令編碼主要有下圖六種類型
譯碼模塊需要根據(jù)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:
width即為二進制長度,RV32I中指令的BitPat寬度均為32
BitPat中利用parse函數(shù)來從指令Pattern中解析 value 和 mask。
// BitPat中parse函數(shù)的關鍵代碼
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() // 是否協(xié)處理器相關指令
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])]) = {
// 調(diào)用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即整數(shù)Int衣形,與之相對應的就是浮點數(shù)的FPCtrlSigs
FPCtrlSigs的譯碼邏輯與IntCtrlSigs相類似,參考FPU即可
指令集譯碼表
- DecodeConstants定義為abstract trait,類似于C++中的虛基類,其中只定義了一個譯碼表(table)腻菇,
- 剩下的所有xxxDecode均繼承自DecodeConstants筹吐,內(nèi)部定義了各個指令擴展的譯碼表(table)丘薛。
RISC-V模塊化的設計使得Rocket Core可以通過config配置需要的指令擴展周拐,并將選擇的指令譯碼表整合妥粟。
舉個例子勾给,例如我們需要實現(xiàn)RV32IM,配置config后,Rocket Generator會將IDecode.table與MDecode.table合并成一張譯碼表(decode_table)售睹,合并相關的邏輯如下所示(只截取部分代碼說明,完整代碼見RocketCore.scala)
val decode_table = {
// usingMulDiv來自rocket-core的配置
(if (usingMulDiv) new MDecode(pipelinedMul) ++:
// RV32I是文檔中要求必須實現(xiàn)的部分
Seq(new IDecode)
} flatMap(_.table) // 合并成一張譯碼表
合并邏輯在Chisel代碼編譯過程中自動生成所需要的譯碼邏輯,無關的指令集擴展會被直接無視飞崖,并不會產(chǎn)生冗余的譯碼邏輯電路固歪。
Decode.scala
上面IntCtrlSigs的decode函數(shù)中調(diào)用了DecodeLogic函數(shù)來譯碼,該函數(shù)就定義在Decode.scala中贰健,這里實現(xiàn)了Quine-McCluskey算法伶椿,以簡化指令Pattern的譯碼邏輯。
網(wǎng)絡上資料講解得比較清楚(但我沒弄清楚偎痛,有點復雜orz)踩麦,這里只羅列一些重要的參考資料谓谦。
重要名詞解釋
- Quine-McCluskey算法
- Term_algebra(項代數(shù))
- Boolean algebra(布爾代數(shù))
- implicant(蘊含項)
- prime implicant(素項,質項才顿,主蘊含項)
文章(來源于公眾號:故事v歷史)