FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

RocketChipをカスタマイズするためのチュートリアル(3. RISC-V binutilsに新規命令を追加する)

RocketChipはChiselで記述されており、改造するためにはScalaの知識が必要だ。Scalaは良く知らないので試行錯誤にはなるが、ALUに何らかの命令を追加するくらいなら何とかなりそうだ。Chiselを読み解いて、ALUに新しい演算なりなんなり、入れてみたい。

前回で、ハードウェアの部分はできたと思うので、次はソフトウェア。GCCを改造する手法について調査し、テストパタンを動作させてみる。

RISC-V binutilsの改造

Rocket Chipのリポジトリには、一緒にriscv-toolsがサブリポジトリとしてついており、これを改造しよう。

binutils(特にgas)に新しい命令を追加する方法は以下が詳しい。

Adding HW/SW support for the load and store tag instructions · lowRISC

前回も紹介したが、以下の命令を追加したい。全てのビットを逆転させる命令だ。よく考えると、これは32bit限定命令だな。

f:id:msyksphinz:20170819134638p:plain

f:id:msyksphinz:20170819134959p:plain

命令の定義

命令の定義は include/opcode/riscv-opc.hで行う。ここでは、 MATCH_BITREVMASK_BITREVを定義している。

  • MASK_xxx 命令ビットフィールドのうち、どのビット位置をデコードに使用するかをマスク形式で指定する(1がデコードに使用するビット、0がそうでないビット)
  • MATCH_xxx デコードに使用するビットの、フィールド情報を記述する。
@@ -724,6 +724,10 @@
 #define CAUSE_SUPERVISOR_ECALL 0x9
 #define CAUSE_HYPERVISOR_ECALL 0xa
 #define CAUSE_MACHINE_ECALL 0xb
+
+#define MATCH_BITREV 0x0000000B
+#define MASK_BITREV  0xfff0707f
+

f:id:msyksphinz:20170819170924p:plain

次に、命令の定義を追加する。これはDECLARE_INSNにて行えるようになってる。実態は、どうやら関数を定義しているようだ。マッチする関数を作っている。

一応、念のためCUSTOM0の命令定義を削除しておいた。命令ビットフィールド的に重なってしまうからね。

  • gdb/riscv-tdep.c
#define DECLARE_INSN(INSN_NAME, INSN_MATCH, INSN_MASK) \
static inline bool is_ ## INSN_NAME ## _insn (long insn) \
{ \
  return (insn & INSN_MASK) == INSN_MATCH; \
}
@@ -951,7 +955,7 @@ DECLARE_INSN(c_sd, MATCH_C_SD, MASK_C_SD)
 DECLARE_INSN(c_addiw, MATCH_C_ADDIW, MASK_C_ADDIW)
 DECLARE_INSN(c_ldsp, MATCH_C_LDSP, MASK_C_LDSP)
 DECLARE_INSN(c_sdsp, MATCH_C_SDSP, MASK_C_SDSP)
-DECLARE_INSN(custom0, MATCH_CUSTOM0, MASK_CUSTOM0)
+// DECLARE_INSN(custom0, MATCH_CUSTOM0, MASK_CUSTOM0)
 DECLARE_INSN(custom0_rs1, MATCH_CUSTOM0_RS1, MASK_CUSTOM0_RS1)
 DECLARE_INSN(custom0_rs1_rs2, MATCH_CUSTOM0_RS1_RS2, MASK_CUSTOM0_RS1_RS2)
 DECLARE_INSN(custom0_rd, MATCH_CUSTOM0_RD, MASK_CUSTOM0_RD)
@@ -975,6 +979,7 @@ DECLARE_INSN(custom3_rs1_rs2, MATCH_CUSTOM3_RS1_RS2, MASK_CUSTOM3_RS1_RS2)
 DECLARE_INSN(custom3_rd, MATCH_CUSTOM3_RD, MASK_CUSTOM3_RD)
 DECLARE_INSN(custom3_rd_rs1, MATCH_CUSTOM3_RD_RS1, MASK_CUSTOM3_RD_RS1)
 DECLARE_INSN(custom3_rd_rs1_rs2, MATCH_CUSTOM3_RD_RS1_RS2, MASK_CUSTOM3_RD_RS1_RS2)
+DECLARE_INSN(bitrev, MATCH_BITREV, MASK_BITREV)
 #endif
 #ifdef DECLARE_CSR
 DECLARE_CSR(fflags, CSR_FFLAGS)

命令フォーマット定義

命令フォーマットは以下のようにして定義する。opcodes/riscv-opc.c に定義を追加した。

diff --git a/opcodes/riscv-opc.c b/opcodes/riscv-opc.c
index 8343198..a06209f 100644
--- a/opcodes/riscv-opc.c
+++ b/opcodes/riscv-opc.c
@@ -623,6 +623,8 @@ const struct riscv_opcode riscv_opcodes[] =
 {"sfence.vma","I",   "s,t",  MATCH_SFENCE_VMA, MASK_SFENCE_VMA, match_opcode, 0 },
 {"wfi",       "I",   "",     MATCH_WFI, MASK_WFI, match_opcode, 0 },

+{"bitrev",    "I",   "d,s",  MATCH_BITREV, MASK_BITREV, match_opcode, 0 },
+
 /* Terminate the list.  */
 {0, 0, 0, 0, 0, 0, 0}
 };

これで、

bitrev xD, xA

のフォーマットで命令を使用することができるようになった。riscv-toolsをビルドして、この新規命令に対応したbinutilsを作成し、テストしてみよう。

新規命令のコンパイルテスト

新規命令に対応した riscv64-unknown-elf-as を作成したら、以下のテストアセンブリを作成してアセンブルしてみよう。

        .section        text
        bitrev  x1, x2

アセンブルしてみる。上手く行ったので逆アセンブルしてバイナリを見てみた。

$ riscv64-unknown-elf-as bitrev.S
$ riscv64-unknown-elf-objdump -D a.out

a.out:     ファイル形式 elf64-littleriscv


セクション text の逆アセンブル:

0000000000000000 <text>:
   0:   0001008b                bitrev  ra,sp

ちゃんと追加できていた!次は、実RTLシミュレーションで動作確認かな。

msyksphinz.hatenablog.com

msyksphinz.hatenablog.com