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

JosephHalfmoon

前回、割り込みを使うにあたって、Longan nanoはRISC-Vコアといいながら、RISC-Vのことなんぞ、ちーとも勉強しないまま、漫然とCでコーディングしている具合の悪さを痛感いたしました。やはりコアを知るべきでしょう。そのためにはアセンブラで書いてみるのが一番。つらつら本を読んでも右から左に忘れてしまいますが、コーディングしてバグったことは忘れない?

とはいえ、突然、知らないコアの(マニュアルさえ読んでいない)アセンブラが直ぐに書ける筈がありません。何か、RISC-Vのアセンブラ書きのチュートリアルないかいな、と思ってWebを探してみたら、素晴らしいものがありました。YouTubeの動画です。

RISC V ASM Tutorial

全12回のシリーズ動画であります。説明されているのは、Martin Fink氏。このチュートリアルをご説明になっていた時点では、Western Digital社のCTO様でありました。調べてみると、昨年、WD社を離れてしまわれたようなので、先のCTO様とよぶべきですかね。WD社といえばストレージの老舗、大手であります。どのような目論見あってRISC-Vに肩入れしているのか調べてもいないのですが、そのCTOともあろう人が、みずから、アセンブリ言語コードを書いて、説明をされているのであります。ターゲットボードは、RISC-Vでは先頭を走る?SiFive社のHiFive1ボード(Arduinoフォームファクタの開発ボード)であります。Longan nanoとは、使われているデバイスも違い、開発プラットフォーム(SDK)も異なるのでありますが、

同じRISC-Vコアだし、アセンブラの書き方は同じでしょ。

と勝手に「想定」したところ、やっぱりまったく問題ないようです。Fink氏が使っておられる開発環境は Visual Studio Code + PlatformIOという構成。そのままドンピシャリであります。

英語のビデオですが、YouTubeが字幕(英語)をつけてくれるし、Fink氏の語り口が分かり易いので、英語が不得手な私でもよく分かります。ただ、全12回、1回数分から、最長でも20分ほどとはいえ、いかにも長い。再生回数をみてみると、初回は1万8千回、2回目は9千回と減っていき、最終回は2千回を下回ります。私のように、最初から最後まで視聴した人は、全世界で2千人以下でありましょうか。しかし、ぶっちゃけ、

  • 普段Visual Studio Codeを使っている
  • 普段PlatformIOを使っている
  • アセンブラは書いたことがある(gasなら尚可)
  • デバッガも使ったことがある

のであれば、第7回と、第9回を見れば、多分、とりあえずアセンブリ言語コードのファイルを書き始めることが可能じゃないかと思います。

このビデオを見て、今回は、アセンブラで書いた関数にCから「行ってこい」するだけのコードを前回までのデモソースに追加し、動かしてみました。でも本格的にRISC-Vのアセンブラにハマり込むのは、

  • Longan nanoにつながるデバッグI/F買ってから
  • 本シリーズでなく「ぐだぐだ低レベルプログラミング」で

ということにいたしたいと思います。

まず、アセンブラのソースは例によって「.S」拡張子です。今回は、いかにもな

testASM.S

なるファイル名で書きました。どのアセンブリ言語のソースもそうですが、最初にどういうコードをどう書くのかアセンブラ疑似命令をいくつか並べないとなりません。最低線は3個で済むようです。

.section

で、アセンブルするセクションを決め(プログラムを格納するのであれば普通.textセクション)、メモリに対するアライメントを決め(このマイコン用の小さいRISC-Vの命令長は2バイト)、リンカに外部に公開してリンクしてもらうラベルを伝える、と。

以下盛り込んだソースがこちら。

この関数、Cのプロトタイプ風に表現すると

void testASM(void);

てな感じです。引数なし、戻り値なし。呼べば、戻ってくるだけの関数。しかし、大事なお作法、

  1. 開始時にはスタック上に領域を確保し
  2. 戻り番地をスタックに保存
  3. retで戻る前には、戻り番地を適切なレジスタに呼び出して
  4. 確保したスタック領域を解放し
  5. 戻る

は、ビデオで習った通りに実装してあります。RISC-Vの資料など見ているとレジスタは無味乾燥なx0からx31のような番号で呼ばれているようですが、アセンブラでは無味乾燥な名だけでなく、意味を背負ったエイリアス名も使用できるようです。例えば、戻り番地を持つレジスタは x1 ではなく、ra(多分、return addressをつづめたのだと思う)と呼べると。意味といって、zeroのようにハードウエアで決まっているものもありますが、多くはABI上、そのように使えというお約束、お作法の類。でもこれに従っておくと、とても書きやすく、分かり易い。とても良い習慣ではないかと思いました。

ちょっとアセンブラを書いた経験のある人ならば、

  • オペランドは、x86同様、右にソース、左にデスティネーション
  • 計算は3オペランドあり
  • RISCなのでメモリアクセスはロード、ストア命令による
  • メモリアドレスは()の中にベースレジスタを書き、カッコの外にオフセット

という程度が分かれば、すんなりかけるのではないですか(マニュアル見てニーモニック確かめないとならないですが、それほど数も無いようだし。)

実際に、ビルドした後、本当にアセンブルされているのか、オブジェクトファイルをディスアセンブルしてみたのがこちら。

PlatformIOは、しらない間にツールチェーン一式を仕込んでおいてくれます。Longan nanoでプロジェクトを作れば、コンパイラなどと一緒に所謂binutilなども全てそろっているので、パスさえ通せばOK. ちゃんとソースに書いたとおりのコードにアセンブルされてます。

あとは、Cのmain()関数の中で呼び出すだけ。

でもね、何もしないのではつまらない。次回は、引数を与えて、何か「軽く」計算し、戻り値を返すようにしたい。まあ、そのくらいならデバッグインタフェースが無いままでも出来るでしょう。

鳥なき里のマイコン屋(92) Longan nano、外部割込み に戻る

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