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限定命令だな。
命令の定義
命令の定義は include/opcode/riscv-opc.h
で行う。ここでは、 MATCH_BITREV
とMASK_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 +
次に、命令の定義を追加する。これは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シミュレーションで動作確認かな。