RocketChipはChiselで記述されており、改造するためにはScalaの知識が必要だ。Scalaは良く知らないので試行錯誤にはなるが、ALUに何らかの命令を追加するくらいなら何とかなりそうだ。Chiselを読み解いて、ALUに新しい演算なりなんなり、入れてみたい。
Rocket CoreのALU構成
Rocket CoreのALUは、Chiselでは以下のような記述になっており、まあ一応読みやすい。
class ALU(implicit p: Parameters) extends CoreModule()(p) { val io = new Bundle { val dw = Bits(INPUT, SZ_DW) val fn = Bits(INPUT, SZ_ALU_FN) val in2 = UInt(INPUT, xLen) val in1 = UInt(INPUT, xLen) val out = UInt(OUTPUT, xLen) val adder_out = UInt(OUTPUT, xLen) val cmp_out = Bool(OUTPUT) } // ADD, SUB val in2_inv = Mux(isSub(io.fn), ~io.in2, io.in2) val in1_xor_in2 = io.in1 ^ in2_inv io.adder_out := io.in1 + in2_inv + isSub(io.fn) ... // AND, OR, XOR val logic = Mux(io.fn === FN_XOR || io.fn === FN_OR, in1_xor_in2, UInt(0)) | Mux(io.fn === FN_OR || io.fn === FN_AND, io.in1 & io.in2, UInt(0)) val shift_logic = (isCmp(io.fn) && io.cmp_out) | logic | shout val out = Mux(io.fn === FN_ADD || io.fn === FN_SUB, io.adder_out, shift_logic) io.out := out if (xLen > 32) { require(xLen == 64) when (io.dw === DW_32) { io.out := Cat(Fill(32, out(31)), out(31,0)) } } }
まず、ALUで何を演算させるかのデコードだが、これはALUの入力であるio.fn
から制御されているらしい。
このio.fn
はどのような定義になっているのかというと、元を辿っていくとデコーダから出力される制御信号生成器から出力されているらしい。
LUI-> List(Y,N,N,N,N,N,N,N,A2_IMM, A1_ZERO,IMM_U, DW_XPR,FN_ADD, N,M_X, MT_X, N,N,N,N,N,Y,CSR.N,N,N,N,N), ADDI-> List(Y,N,N,N,N,N,N,Y,A2_IMM, A1_RS1, IMM_I, DW_XPR,FN_ADD, N,M_X, MT_X, N,N,N,N,N,Y,CSR.N,N,N,N,N), SLTI -> List(Y,N,N,N,N,N,N,Y,A2_IMM, A1_RS1, IMM_I, DW_XPR,FN_SLT, N,M_X, MT_X, N,N,N,N,N,Y,CSR.N,N,N,N,N), SLTIU-> List(Y,N,N,N,N,N,N,Y,A2_IMM, A1_RS1, IMM_I, DW_XPR,FN_SLTU, N,M_X, MT_X, N,N,N,N,N,Y,CSR.N,N,N,N,N), ANDI-> List(Y,N,N,N,N,N,N,Y,A2_IMM, A1_RS1, IMM_I, DW_XPR,FN_AND, N,M_X, MT_X, N,N,N,N,N,Y,CSR.N,N,N,N,N), ORI-> List(Y,N,N,N,N,N,N,Y,A2_IMM, A1_RS1, IMM_I, DW_XPR,FN_OR, N,M_X, MT_X, N,N,N,N,N,Y,CSR.N,N,N,N,N), XORI-> List(Y,N,N,N,N,N,N,Y,A2_IMM, A1_RS1, IMM_I, DW_XPR,FN_XOR, N,M_X, MT_X, N,N,N,N,N,Y,CSR.N,N,N,N,N), SLLI-> List(Y,N,N,N,N,N,N,Y,A2_IMM, A1_RS1, IMM_I, DW_XPR,FN_SL, N,M_X, MT_X, N,N,N,N,N,Y,CSR.N,N,N,N,N), SRLI-> List(Y,N,N,N,N,N,N,Y,A2_IMM, A1_RS1, IMM_I, DW_XPR,FN_SR, N,M_X, MT_X, N,N,N,N,N,Y,CSR.N,N,N,N,N),
この中で、FN_xxx
で記述されているのがALUの信号で、A1_xxx
, A2_xxx
と定義されているのが、ALUの入力値として定義されているオペランドだ。
このFN_xxx
はALUで定義されているものが多い。
object ALU { val SZ_ALU_FN = 4 def FN_X = BitPat("b????") def FN_ADD = UInt(0) def FN_SL = UInt(1) def FN_SEQ = UInt(2) def FN_SNE = UInt(3) def FN_XOR = UInt(4) def FN_SR = UInt(5) def FN_OR = UInt(6) ...
なんとなく分かってきたので、Chiselをちょっと変更して、新規ALU命令を実行してみよう。
ALUを改造して、新規演算を実装する
実はこの実験は既に試行されている。
まだチマチマとRISC-Vについて私が調査していた時代、もうChiselを触っていろいろ試行されていた。FPGAXでも発表されてご挨拶させて頂いた。
パクリといってはアレだが、同じようにBitReverseを実装してみよう。
新しくBITREV
命令を定義して、ALU動作を定義する。
BITREV rD, rA
RISC-Vの命令定義フィールドは以下のようになっているので、CUSTOMのところに新しく1つ命令を追加する。
ALUの演算追加
ALUに新演算を追加する。まず、ALU_FN
の4ビットのフィールドが全部使われているので、5ビットに拡張しておこう。
object ALU { - val SZ_ALU_FN = 4 - def FN_X = BitPat("b????") + val SZ_ALU_FN = 5 + def FN_X = BitPat("b?????") def FN_ADD = UInt(0) def FN_SL = UInt(1) def FN_SEQ = UInt(2) @@ -26,6 +26,7 @@ object ALU def FN_SGE = UInt(13) def FN_SLTU = UInt(14) def FN_SGEU = UInt(15) + def FN_REV = UInt(16) def FN_DIV = FN_XOR def FN_DIVU = FN_SR @@ -43,6 +44,7 @@ object ALU def cmpUnsigned(cmd: UInt) = cmd(1) def cmpInverted(cmd: UInt) = cmd(0) def cmpEq(cmd: UInt) = !cmd(3) + def isRev(cmd: UInt) = cmd === FN_REV } import ALU._ @@ -87,8 +89,15 @@ class ALU(implicit p: Parameters) extends CoreModule()(p) { // AND, OR, XOR val logic = Mux(io.fn === FN_XOR || io.fn === FN_OR, in1_xor_in2, UInt(0)) | Mux(io.fn === FN_OR || io.fn === FN_AND, io.in1 & io.in2, UInt(0)) + def revBit (v: Bits): UInt = Cat( + v( 0), v( 1), v( 2), v( 3), v( 4), v( 5), v( 6), v( 7), + v( 8), v( 9), v(10), v(11), v(12), v(13), v(14), v(15), + v(16), v(17), v(18), v(19), v(20), v(21), v(22), v(23), + v(24), v(25), v(26), v(27), v(28), v(29), v(30), v(31)) + val reverse = revBit (io.in1) val shift_logic = (isCmp(io.fn) && io.cmp_out) | logic | shout - val out = Mux(io.fn === FN_ADD || io.fn === FN_SUB, io.adder_out, shift_logic) + val logic_rev = Mux (isRev (io.fn), reverse, shift_logic) + val out = Mux(io.fn === FN_ADD || io.fn === FN_SUB, io.adder_out, logic_rev) io.out := out if (xLen > 32) {
命令ビットフィールドの追加
次に、BITREV
命令を追加する。CUSTOM0
のところに、BITREV
命令を追加した。
※ 2017/08/21 デコーダが一部間違っていたので修正しました。
diff --git a/src/main/scala/rocket/IDecode.scala b/src/main/scala/rocket/IDecode.scala index ca8ae4a..c8d93fa 100644 --- a/src/main/scala/rocket/IDecode.scala +++ b/src/main/scala/rocket/IDecode.scala @@ -123,7 +123,9 @@ class IDecode(implicit val p: Parameters) extends DecodeConstants CSRRC-> List(Y,N,N,N,N,N,N,Y,A2_ZERO,A1_RS1, IMM_X, DW_XPR,FN_ADD, N,M_X, MT_X, N,N,N,N,N,Y,CSR.C,N,N,N,N), CSRRWI-> List(Y,N,N,N,N,N,N,N,A2_IMM, A1_ZERO,IMM_Z, DW_XPR,FN_ADD, N,M_X, MT_X, N,N,N,N,N,Y,CSR.W,N,N,N,N), CSRRSI-> List(Y,N,N,N,N,N,N,N,A2_IMM, A1_ZERO,IMM_Z, DW_XPR,FN_ADD, N,M_X, MT_X, N,N,N,N,N,Y,CSR.S,N,N,N,N), - CSRRCI-> List(Y,N,N,N,N,N,N,N,A2_IMM, A1_ZERO,IMM_Z, DW_XPR,FN_ADD, N,M_X, MT_X, N,N,N,N,N,Y,CSR.C,N,N,N,N)) + CSRRCI-> List(Y,N,N,N,N,N,N,N,A2_IMM, A1_ZERO,IMM_Z, DW_XPR,FN_ADD, N,M_X, MT_X, N,N,N,N,N,Y,CSR.C,N,N,N,N), + BITREV-> List(Y,N,N,N,N,N,N,N,A2_X, A1_RS1, IMM_X, DW_XPR,FN_REV, N,M_X, MT_X, N,N,N,N,N,N,CSR.N,N,N,N,N)) + } class SDecode(implicit val p: Parameters) extends DecodeConstants @@ -301,7 +303,8 @@ class D64Decode(implicit val p: Parameters) extends DecodeConstants class RoCCDecode(implicit val p: Parameters) extends DecodeConstants { val table: Array[(BitPat, List[BitPat])] = Array( - CUSTOM0-> List(Y,N,Y,N,N,N,N,N,A2_ZERO,A1_RS1, IMM_X, DW_XPR,FN_ADD, N,M_X, MT_X, N,N,N,N,N,N,CSR.N,N,N,N,N), + // CUSTOM0-> List(Y,N,Y,N,N,N,N,N,A2_ZERO,A1_RS1, IMM_X, DW_XPR,FN_ADD, N,M_X, MT_X, N,N,N,N,N,N,CSR.N,N,N,N,N), + // BITREV-> List(Y,N,Y,N,N,N,N,N,A2_X, A1_RS1, IMM_X, DW_XPR,FN_REV, N,M_X, MT_X, N,N,N,N,N,N,CSR.N,N,N,N,N), CUSTOM0_RS1-> List(Y,N,Y,N,N,N,N,Y,A2_ZERO,A1_RS1, IMM_X, DW_XPR,FN_ADD, N,M_X, MT_X, N,N,N,N,N,N,CSR.N,N,N,N,N), CUSTOM0_RS1_RS2-> List(Y,N,Y,N,N,N,Y,Y,A2_ZERO,A1_RS1, IMM_X, DW_XPR,FN_ADD, N,M_X, MT_X, N,N,N,N,N,N,CSR.N,N,N,N,N), CUSTOM0_RD-> List(Y,N,Y,N,N,N,N,N,A2_ZERO,A1_RS1, IMM_X, DW_XPR,FN_ADD, N,M_X, MT_X, N,N,N,N,N,Y,CSR.N,N,N,N,N), diff --git a/src/main/scala/rocket/Instructions.scala b/src/main/scala/rocket/Instructions.scala index 81579f5..8020bd1 100644 --- a/src/main/scala/rocket/Instructions.scala +++ b/src/main/scala/rocket/Instructions.scala @@ -170,7 +170,8 @@ object Instructions { def FMSUB_D = BitPat("b?????01??????????????????1000111") def FNMSUB_D = BitPat("b?????01??????????????????1001011") def FNMADD_D = BitPat("b?????01??????????????????1001111") - def CUSTOM0 = BitPat("b?????????????????000?????0001011") + def BITREV = BitPat("b0000000??????????000?????0001011") +// def CUSTOM0 = BitPat("b?????????????????000?????0001011") def CUSTOM0_RS1 = BitPat("b?????????????????010?????0001011") def CUSTOM0_RS1_RS2 = BitPat("b?????????????????011?????0001011") def CUSTOM0_RD = BitPat("b?????????????????100?????0001011")
Scalaのコンパイル
一応これで実装は完了した。デバッグのために、まずはコンパイルしてコンパイルエラーが無いか確認しよう。
cd emulator
make