RISC-Vのハードウェア仕様の中で、RV64GCとか、RV32IMACとか出てくるが、その中で"C"というのは"Compressed"命令ということで、命令長が16bitの短縮命令のことを指す。
サポートしているアーキテクチャの中で"C"が入っていれば、このCompressed 命令を動かすことが出来るということで、命令コードサイズの短縮につながる。 ARMのThumb命令、MIPSのMIPS16eに近いものだ。
似て非なる"C"命令と"E"命令
時々混乱するのだが、Compressed命令群と雰囲気が似ている命令で"E"命令群が存在する。これはRV32Eと呼ばれ、RV32I(32bit整数命令)のハードウェア的な縮退版を指す。
主な変更点としては、RV32Iでは32本ある整数レジスタを16本に減らしている。使用できる命令自体はRV32Iと同一で、ただしx16-x31までの搭載していない汎用レジスタにアクセスすると例外を発生させる。 また、カウンタレジスタ(rdcycle[h],rdtime[h], rdinstret[h])は無理に実装しなくても良いという制限がある。
というわけでRV32Eは、命令長自体を短縮させるわけではなく、ハードウェア量を減らすための実装なので、そこは注意。
Compressed命令の概要
まず命令フォーマットとして、16bit長命令であることを示すのに、下位の2bitを使う。下位の2bitが2'b11であれば、32ビット長命令以上になるが、それ以外では16bit長命令となる。 非常にリッチである。
注意すべきは、RISC-Vのサポートしているモードによって実行される命令の意味が異なるということだ。例えば、RV32/RV64ではC.FLD
であり、RV128ではC.LQ
として解釈されて実行される。
命令フォーマットは以下に示すようになっている。レジスタ本数は基本モードと同様に32本利用できるが、32本をフルにアクセスできるのはCR(レジスタ間演算)、CI(レジスタ、即値)、CSS(スタック操作命令)のみとなる。
それ以外の命令は、以下の表に従ってアクセスできるレジスタが制限される。 ロードストアに関しても同様だが、スタック相対のアクセスの場合のみ、CSS(Stack-relative Store)により専用命令が用意されている。この場合は任意のレジスタのデータをスタックに積むことが出来る。
大きく分けてCompressed命令の仕様は以下のように分類される。
- CR : レジスタ間演算命令。2オペランドを取り、1オペランドを上書きする形の命令が用意されている。
- CI : レジスタ、即値演算命令。即値とレジスタの演算、または即値を代入する演算。
- CSS : スタック相対メモリアクセス命令。スタックポインタをベースとしてメモリアクセスを行う。ロードとストアがあり、整数レジスタ用と浮動小数点レジスタ用が用意されている。
- CIW : 即値が広くとることが出来るレジスタアクセス命令。スタックポインタの更新などに使用される。
- CL : ロード命令。
- CS : ストア命令。
- CB : 分岐命令。レジスタ比較を行うが、比較対象のレジスタはゼロレジスタに限定される。PC相対分岐を行う。
- CJ : ジャンプ命令。PC相対ジャンプを行う。JumpとJump And Linkが用意されている。
RISC-V の命令セットシミュレータへのCompressed 命令実装の検討
自作命令セットシミュレータにRISC-Vを実装する検討をしているのだが、いくつか問題があった。まず、命令のモード毎に有効な命令が異なるということだ。
たとえば、下記の命令リストにおいて、C.FLW
とC.LD
は命令コード的に全く同じなので、RISC-Vのサポートしている命令形態によって意味が異なる。
したがって、とりあえずは命令デコードテーブルは重複している命令の片方を無効化して生成を行った。
- 自作RISC-Vシミュレータの命令デコードテーブルの一部
$arch_table.push(Array['c.addi4spn', 16, 'XXXXX', 'XX', 'XXXXX', 'XXXX0', '00X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""]) $arch_table.push(Array['c.fld ', 16, 'XXXXX', 'XX', 'XXXXX', 'XXXX0', '01X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""]) # $arch_table.push(Array['c.lq ', 16, 'XXXXX', 'XX', 'XXXXX', 'XXXX0', '01X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""]) # RV128 $arch_table.push(Array['c.lw ', 16, 'XXXXX', 'XX', 'XXXXX', 'XXXX0', '10X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""]) $arch_table.push(Array['c.flw ', 16, 'XXXXX', 'XX', 'XXXXX', 'XXXX0', '11X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""]) # $arch_table.push(Array['c.ld ', 16, 'XXXXX', 'XX', 'XXXXX', 'XXXX0', '11X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""]) # RV64/RV128 $arch_table.push(Array['c.fsd ', 16, 'XXXXX', 'XX', 'XXXXX', 'XXXX1', '01X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""]) # $arch_table.push(Array['c.sq ', 16, 'XXXXX', 'XX', 'XXXXX', 'XXXX1', '01X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""]) $arch_table.push(Array['c.sw ', 16, 'XXXXX', 'XX', 'XXXXX', 'XXXX1', '10X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""]) $arch_table.push(Array['c.fsw ', 16, 'XXXXX', 'XX', 'XXXXX', 'XXXX1', '11X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""]) # $arch_table.push(Array['c.sd ', 16, 'XXXXX', 'XX', 'XXXXX', 'XXXX1', '11X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""]) # RV64/RV128
- Compressed命令のテストパタンの解析
Compressed命令のテストパタンを流してみると、どうも4-byteアラインではなく2-byteアラインに命令が配置されていてもジャンプできなければならないらしい!?
RISC-Vの例外仕様では、Instruction Address Misaligned例外は4-byteにアラインされていないことが条件かと思っていたけど、RVCに関してはそうではないらしい。
An instruction address misaligned exception is generated on a taken branch or unconditional jump if the target address is not four-byte aligned. No instruction fetch misaligned exception is generated for a conditional branch that is not taken.
with a commentary note:
Instruction fetch misaligned exceptions are not possible on machines that support extensions with 16-bit-aligned instructions, such as the compressed instruction set extension, C.
なるほどー、つまりCompressed命令をサポートしている実装では、Instruction Fetch Misaligned 例外は発生しないということか。これはびっくり。
というわけで例外的に命令フェッチアライン例外を無効にして自作ISSでRVC命令を流すようにすると、とりあえずデコードは出来たようだ。
あとは中身の実装をしていかなければ。