FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://sites.google.com/site/fpgadevelopindex/

RocketChipをカスタマイズするためのチュートリアル(2. Chiselによるパイプラインの改造)

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を改造して、新規演算を実装する

実はこの実験は既に試行されている。

cgtokyo.hatenablog.com

まだチマチマとRISC-Vについて私が調査していた時代、もうChiselを触っていろいろ試行されていた。FPGAXでも発表されてご挨拶させて頂いた。

パクリといってはアレだが、同じようにBitReverseを実装してみよう。

新しくBITREV命令を定義して、ALU動作を定義する。

BITREV rD, rA

RISC-Vの命令定義フィールドは以下のようになっているので、CUSTOMのところに新しく1つ命令を追加する。

f:id:msyksphinz:20170819134638p:plain

f:id:msyksphinz:20170819134959p:plain

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

一応、エラー無くコンパイルが完了した。次はGCCの改造かな。

msyksphinz.hatenablog.com