ぐだぐだ低レベルプログラミング(41) 64bitのRISC-Vでインライン・アセンブラ

Joseph Halfmoon

RISC-VインタナショナルがVectorなどの拡張命令セット多数を制定したとのニュースが流れるなか、こちらはようやく64ビット命令に入ります。前回まで32ビットのRISC-V、GD32VF103でしたが、今回からは64ビット、Kendryte社のK210です。前回同様VSCode+PlatformIOでのビルドは同じ、しかしプラットフォームが異なるのでまずはアセンブラ命令を走らせる環境の確認から。

※「ぐだぐだ低レベルプログラミング」投稿順indexはこちら

RISC-Vといっても使える範囲が異なる

前回までのGD32VF103は、RISC-V的な呼び方をすると

RV32IMAC

という命令セットをサポートするコアを搭載していました。その意味は、

    • RV32I=32ビットのRISC-V整数型基本命令セット
    • M=整数型の乗算、除算命令拡張
    • A=アトミック命令拡張
    • C=圧縮命令拡張

です。今回から使用するK210は、

RV64IMAFDC

ということになります。基本部分がRV64I、64ビットの整数型命令セットになっている(RV32Iと同等の命令が全て含まれる)上に「F」と「D」が増えてます。FとDはご想像のとおり、

    • F=単精度浮動小数点命令
    • D=倍精度浮動小数点命令

です。K210を使うことで64ビット命令の実験ができるだけでなく、GD32VF103が持たなかった浮動小数点命令ができるというわけです。また、

デュアルコア

でRTOSのサポートもあるのでアトミック命令のエクササイズにも便利かな、と。

K210は当面 Arduino プラットフォーム

GD32VF103のプログラムの作成では、GD32VF103のSDKを使用していました。素の環境なので、とっつきにくい反面、なんでも出来る環境でした。

K210でもSDKの「素の環境」はあり(それも2系統)、後でそちらへ移行しようと考えています。しかし今回からしばらく Arduino互換のMaixduino環境で実験してみたいと思います。何といっても Arduino、とっつきやすいです。

Maixduino環境でのプログラムのビルドについては以下の別投稿でやってますのでご参照ください。

鳥なき里のマイコン屋(150) K210、MAiX-BiT、C/C++で恒例Lチカ

まずは、今回実験に使用の Platformio.ini を以下に示します。

[env:sipeed-maix-bit-mic]
platform = kendryte210
board = sipeed-maix-bit-mic
framework = arduino
upload_protocol = kflash
upload_port = com11
monitor_port = com11
monitor_speed = 9600

使用しているMAiX-BiTボードは、PCにUSB接続すると仮想COMポートが2つ現れます。手元のボードでは、uploadにもシリアルモニタにも最初の方のポートを指定すればOKでした。com11は手元の環境下でのアサインなので各自の環境毎に要変更です。また、シリアルモニタは抑えめの9600に設定してます。

Maixduino プラットフォーム上でのアセンブラ書きは inline で

GD32VF103上では、アセンブリ言語ソースファイルをgasでアセンブルして本体Cプログラムにリンクしてました。Maixduinoでは、Cのソース内にinlineで書いてしまうことにいたします。

今回はビルドの確認ということで、前回GD32VF103上で行った 符号付きdiv命令のうち1命令だけをK210の環境で動かしてみることにいたします。ソースはこちら。

#include <Arduino.h>

int counter;

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  counter = 0;
  Serial.begin(9600);
  while (!Serial);
  Serial.printf("MAiX BiT ASM TEST.\r\n");
}

void loop()
{
  if ((counter++ % 2) == 0)
  {
    digitalWrite(LED_BUILTIN, HIGH);
  }
  else
  {
    digitalWrite(LED_BUILTIN, LOW);
  }
  Serial.printf("Trial: %d\r\n", counter);
  //--- DUT --------------------------------
  int32_t a0i, a1i, a2i;

  a0i = 0;
  a1i = -3334;
  a2i = 1111;
  asm volatile("div %[Rd], %[Rs1], %[Rs2]\n\t"
              : [Rd] "=r" (a0i)
              : [Rs1] "r" (a1i), [Rs2] "r" (a2i)
              : );
  Serial.printf(" %d / %d = %d\n", a1i, a2i, a0i);
  //--- END OF DUT--------------------------
  delay(5000);
}

忘却力とて、gccのインラインアセンブラの書き方(メンドイ)を忘れていたので、1命令に手間取りました。

ビルド結果のアセンブリ・コードを確認

ビルドが通っても、本当に所望の命令が生成されているかは、ダンプして確かめてみないと分かりません。インライン展開でvolatile忘れたりすると「消えていた」(最適化された)なんてことがあるので。

いつもお世話になる Gnu Binutils は、PlatformIOのばあい

各自のパス\.platformio\packages\toolchain-kendryte210\bin

に入っていってました。例によって objdump を使って div 命令の前後を逆アセンブルして確認したのがこちら。

00000000800005ea <loop>:
    800005ea:	0001c797          	auipc	a5,0x1c
~途中略~
    80000634:	777d                	lui	a4,0xfffff
    80000636:	2fa7071b          	addiw	a4,a4,762
    8000063a:	45700793          	li	a5,1111
    8000063e:	02f74733          	div	a4,a4,a5
    80000642:	767d                	lui	a2,0xfffff
    80000644:	2701                	sext.w	a4,a4
~以下略~

loop()関数のラベルのところの64ビットのアドレス表記がいい感じです。また、肝心の div 命令もちゃんとありました。

a4レジスタに引数をロードするところの以下のシーケンスがちょっと分かりずらいかと思いました(私は電卓使って計算してみました。)

lui a4,0xfffff
addiw a4,a4,762

lui命令で32ビットレジスタの上位20ビットに0xfffffをロード(自動的に下位12ビットは0クリア)すると、a4レジスタには、10進符号付き -4096 が入っていることになります。そこに addiw命令(まさにRV64Iで追加されたワード幅の即値add命令です)で10進762を加えると、-3334になる、と。ソースコードでまさに設定している値であります。

実機上で動作確認

K210搭載、MAiX-BiTボード上で実際に動作させた結果が以下です(3回目のループのところを取り出しました。)

Trial: 3
-3334 / 1111 = -3

期待通りに div 命令が実行されて、符号付きの割り算がされています。

良かった。64ビットのRISC-V上でアセンブラレベルの命令実験、出来そうな感じ。

ぐだぐだ低レベルプログラミング(40) RISC-V、div、RV32M拡張その3 へ戻る

ぐだぐだ低レベルプログラミング(42) 64bitRISC-V、単精度浮動小数点add へ進む