鳥なき里のマイコン屋(94) Longan nano、アセンブラその2

JosephHalfmoon

前回、何もしないで「行って来い」だけのアセンブラ関数を書いて、生成されたオブジェクトコードを眺めてみたときに、ちと気になったことがありました。RISC-V用のツールチェーンのobjdumpがディスアセンブルしたコード、16ビット幅でした。その辺に転がっている資料をみるとRISC-Vの命令は32ビット幅の筈。なぜ?

ちゃんと資料を読んでいれば、そのような疑問は直ぐに氷解するのですが、Longan nanoの場合、どの資料を読むべきなのか「多少込み入って」います。まあ、マイコンの場合、「よくあること」ではあるのです。自分の備忘を兼ねてLongan nanoのケースを書き留めておきましょう。

マイコンボードとしてのLongan nanoは、Sipeed社

上のSipeedのリンクから、Longanへとたどっていけばボードの導入的な説明が得られます。開発環境とか設定はまずこちらからでしょう。上記のページから、GitHubへと辿った方が、Longan独特のサンプルコードなどソフトウエアを実際にコーディングするときに直ぐに役に立ちそうな情報が得られます。しかし、マイコンそのものの情報はまた別です。

搭載されているマイコン GD32VF103は、GigaDevice Semiconductor

GigaDevice自体は、Flashメモリとマイコンの半導体ベンダで、多分Flashが主で、マイコンは従?な感じなのかと想像します。そのマイコンの中ではやはりArmが中心で、RISC-Vは脇な感じがします。また、GigaDevice自体は、一応米シリコンバレーの会社といいつつ、主は中国北京のようです。ま、ともかく、Longan nanoに搭載されているGD32VF103というMCUの仕様を知るには、GigaDeviceのデータシートを参照しなければなりません。しかし、それを見るに、MCU内のRISC-Vのコアは

NUCLEI社が設計したBumblebeeという愛称?のついたRISC-Vのコア

であるようです。NUCLEI社はRISC-Vの実装が売り?の中国のIPベンダという理解です。そしてBumblebeeはGigaDevice向けに開発された(カスタム?)もののようです。RISC-Vといってもその実装の「レベル」はいろいろありえる筈なので、GD32VF103搭載のコアがどのようなものなのかはNUCLEI社のBumblebeeコアの資料を見ないとなりません。なお、込み入ったことにこのBumblebeeコアは、NUCLEI社が完全独力で実装したわけではなく、台湾の

Andes Technology社

の協力のもとに設計したようです。Andes社もRISC-Vのコアのベンダの1社です。NUCLEI社に行くと、RISC-Vの各種実装レベルのIPコアがいろいろ上げてあるのですが(この手の表は本当に全て実装されて実績もある、とは想像できないです。注文があったら作ります、くらいな感覚かね。)、Bumblebeeはその中では、N200シリーズという下から2番目、上から4番目という位置づけのようです。このマニュアルを見ると、ようやくRISC-Vのどのような命令セットが実装されているのか、分かります。

  • RISC-Vのもっとも基本的な32ビット整数型のRV32を実装している
  • それに加えていくつかオプショナルな命令セットをサポート

そのオプションの中に以下が含まれていました。

c: compression instructions with a code length of 16bits to increase code density.

ようやくこれで、ご本家

RISC-V International

発行の仕様書のどこを見たら良いかが分かります。

The RISC-V Instruction Set Manual Volume I: User-Level ISA

“C” Standard Extension for Compressed Instructions, Version 2.0 です。なお、Bumblebeeが参照しているのは、Instruction Set ManualのV.2.2の方でそちらだとChapter 12、昨年2019年12月13日版であると、Chapter 16です。

ぶっちゃけ、32ビット、64ビット幅のアーキテクチャだけでなく128ビット化などまで考慮にいれているRISC-Vなので、命令セットにも32ビットのエンコードだけでなく、48ビット、64ビットなど2バイト単位で拡張可能。その中に16ビット幅の圧縮版の命令セットも定義されていて、Bumblebeeは、それを実装している、ということです。もともと32ビット命令幅で32本のレジスタで3オペランド式の命令をコードしていたので、16ビット命令幅にすると、収まりきりません。そこで、以下の条件に当てはまる場合のみ、16ビット命令幅のコードが生成されるようです。

  1. 即値やオフセットの大きさが小さい。
  2. zero、ra、spつかった「ありがちな」操作
  3. 第1ソースとデスティネーションレジスタが同じ(実質2オペランド)
  4. レジスタ指定はABI上、よく使いそうな8本

RISC-Vの行き方は、生成される命令コードの下のビットから眺めていけば、命令の幅が決まっていく、という方式なので、16ビット幅の命令と32ビット幅の命令は、いつでもどこでも混在可能。いいね。

前回の行って来いだけのアセンブラ・ソースに1行加え、引数を2個とって、引数の加算をして、戻す、という操作を書いてみました。上の条件に当てはまるので、16ビット幅の命令だけで済む筈。なお、すばらしいことに、

  • 関数引数はABI上の「愛称」a0からa7の8本でレジスタ渡しが基本
  • 戻り値は、a0とa1で返す。

という分かり易いルールです。a0、実際には汎用レジスタx10です。a=アーギュメントと覚えていれば、忘れる心配もなし。

前回は、引数も、戻り値もなかったのでズボラに関数プロトタイプ宣言なしでしたが今回はあり。

あとは、main関数のLoopの中で、以下のように呼び出してやり、結果をシリアル・モニタへとprintfします。目論見としては、LOOPで表示した値にたいして

+1して

100足した

値がASMの後に出力される筈。

結果は以下のとおり、たった1行ですが、アセンブラ関数で引数とって、演算して結果を返せるようになりました。

鳥なき里のマイコン屋(93) Longan nano、アセンブラ関数を呼び出す へ戻る

鳥なき里のマイコン屋(95) Longan nano、RISC-Vデバッガ へ進む