7/27(土) 技書博で組込み/ベアメタルRustクックブックを販売します!

はじめに

宣伝です!

来週開催される技術書同人誌博覧会にて、組込み/ベアメタルRustクックブックを販売します。 A-9 AQUAXISさんのブースにご一緒させて頂きます。 ブース主の石原ひでみさんはFPGAの薄い本とMarkdown組版の本を、みつきんさんはNuttxの本を頒布されます。

f:id:tomo-wait-for-it-yuki:20190721132625p:plain
表紙

販売情報

価格は、500円です。 PDF版とHTML版がデフォルトで含まれます。 当日の会場にてお買い上げいただくと、先着で100名様に、紙媒体をお渡しします。 1冊のご購入につき、紙媒体を1冊まで、お渡しします。

当日お越し頂けなかった方にも、後日何らかの形でPDF版とHTML版が入手できる手段を用意します。

目次は、次の通りで、表紙込で78ページです。

  • はじめに
  • 環境構築
  • ベアメタルテクニック
    • no_std
    • panic
    • print!マクロ
    • リンカ
    • アセンブリ
    • メモリアロケータ
    • entryポイント
  • ツール
  • ライブラリ / フレームワーク
    • heapless
    • no_stdクレート
    • svd2rust
    • RTFM
    • Tock
    • testing
  • FFI
  • 組込みLinux
    • ビルド/テスト
    • Yocto
  • あとがき

正直なところ、HTML版が一番見やすいので、HTML版に500円払う気持ちで購入して頂けると嬉しいです。

ところで

100イイねついたから、100部刷ったので、買って下さいね!(切実)

LLVM IRファイルを読み込む

はじめに

ファイルに出力されたLLVM IRをC++で読み込んで遊んでみます。

環境

$ clang++ --version
clang version 8.0.1-svn360950-1~exp1~20190517004233.70 (branches/release_80)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
$ llvm-config --cxxflags --ldflags --system-libs --libs core
-I/usr/lib/llvm-8/include -std=c++11  -fno-exceptions -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
-L/usr/lib/llvm-8/lib 
-lLLVM-8

LLVM IRファイルの読み込み

アプリケーションの引数にLLVM IRファイルパスを指定する想定です。

#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IRReader/IRReader.h>
#include <llvm/Support/SourceMgr.h>

int main(int argc, char** argv) {
    if (argc < 2) {
        llvm::errs() << "Expected an argument - IR file name\n";
        exit(1);
    }

    llvm::LLVMContext Context;
    llvm::SMDiagnostic Err;
    auto module = llvm::parseIRFile(argv[1], Err, Context);

    if (!module) {
        Err.print(argv[0], llvm::errs());
        return 1;
    }

    return 0;
}

llvm::parseIRFileに、ファイルパスを与えるだけで、llvm::Module (のunique_ptr) を得ることができます。

下のコマンドでLLVM IRファイルが読み込めます。

clang++ main.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core` -o ir_reader
./ir_reader main.ll

少し内部を覗く

main.llにはmain関数があり、いくつか命令があります。 そのオペコードを表示してみます。

次のようなコードで実現できました。

    auto func = module->getFunction("main");
    std::cout << "function name: " << func->getName().data() << std::endl;
    for (auto &bb: *func) {
        auto i = 0;
        for (auto &instr: bb) {
            std::cout << "%" << i << ": " << instr.getOpcodeName() << std::endl;
            i++;
        }
    }
$ ./ir_reader main.ll 
function name: main
%0: alloca
%1: bitcast
%2: call
%3: getelementptr
%4: load
%5: call
%6: ret

お手軽ですね!

Rust × 組込みで前代未聞のInterfaceオフ会レポート

はじめに

昨日、2019年6月17日、巣鴨CQ出版社セミナールームにおいて記念すべき組込みRustのオフ会が開催されました! 今回のオフ会は、雑誌掲載前にオフ会を開催する、という前代未聞のオフ会、とのことでした。

inteface-meet-up.connpass.com

非常に盛り上がったオフ会になったので、そのレポートです。

プレゼン

さらに、LTとして2つの発表がありました。

組込みRustのすゝめ

まず、私から導入のお話をさせていただきました。

speakerdeck.com

最初に会場内でアンケートを取ったところ、

  1. まだRustをやったことがない、が半分ほど
  2. 入門はした、はまさの0人
  3. 組込み以外でRustをやっている、が2割弱
  4. 組込みRustをやっている、が3割弱

といった感じでした。 知らない人か、プロしかいない!という両極端な結果が印象的でした。

導入と言いつつ、所有権システムの説明を少し入れており、ここの部分はRust知らない方々にどの程度理解して頂けたか、が気になっています。

Rustでチョット気軽にセンサドライバ開発

@ryochack さんからホストPC上で組込みセンサドライバ開発を行う発表です。

speakerdeck.com

「いつまで僕らはC/C++を使い続けなければならないのか…」

全くその通りです!

発表内容は、下のデバイスを使って、PC上で直接Rustのセンサドライバを開発し、マイコンに持っていけるようにしよう!というものでした。

akizukidenshi.com

MPSSEのOSS実装、libmpsseからbindgenを使って、Rustバインディングを生成しています。 その時のラッパ作成の苦労話が、涙なしでは語れません。

デモされていたもののレポジトリは、こちらです。

github.com

Rustで始める自作組込みOS

@garasubo さんから、組込みOSを自作したお話しです。

speakerdeck.com

RustでOSを作る際の良かった話や、苦労話が生々しい発表でした! ライフタイムやテストフレームワークなど、良かった点もありますが、データ構造の実装が難しい、クレートが不足している、という課題もあります。

自作OSのレポジトリは、こちらです。

github.com

Nuttxでlibstdを動かす

杉野さんから、POSIX likeな組込みOSであるNuttxでlibstdを動かした発表です。 今のところ、資料公開はされていません。

茨の道をひたすら突き進むような発表を前に、参加者一同、「これはツラすぎないか…」みたいな空気になっていましたが、ある程度動くようになっていて、圧巻の内容でした!

Rustの安全性や利便性を最大限享受するためにlibstdを使いたい、というのは組込みRustやっている人が共通で持っている望みだと思います。 リンカのバグを踏んだり、OS側にAPIが不足していたり、OS側とRust側とで構造体のメンバが違ったり、と数々の難関をくぐり抜け、動いた先は--。

M5 StackをRustで動かすまで

@ciniml さんから、ESP32上でRustを動かすようにするまでに試したことの発表です。

www.slideshare.net

Rust (というかLLVM) は公式にXtensaに対応していないため、LLVMのforkを使ってrustcのXtensa対応を行われていました。 ESP-IDF側のmalloc/freeをGlobalAllocでラップして、libcoreの機能を使えるようにしていました。

@ryochackさんと同様に、bindgenを使用されており、その苦労話もありました。

embedded_graphicsクレートを使って、画像を描画することまでできていて、次はWi-Fiを動かしたい、とのことです!

docs.rs

Rustで書いたファームウェアが乗った自作キーボードのデモ

@KOBA789 さんからキーボード自作のインターン向けに作成したRustファームウェア搭載の自作キーボードについてデモがありました。

会社での無茶ぶり?から産まれたようです。 ステートマシン実装時、ステートが移るときにデータを引き継ぐところに苦労された、ということでした。

懇親会

組込みRustをやっている者同士のぶっちゃけトークが楽しかったです。 身代わりパターンめっちゃ使うよね〜、とかunsafe使いたくないんだけど、やりたいことができなくてツラい、などなど。 組込みRustでまだまだ不足している部分や、課題もありますが、やらないといつまで経っても使えるようにならないから、やる!という認識が共有されたのも、個人的には非常に嬉しかったです。

まだRustを触っていない方々から、実際のところどーなの?的な質問にも正直な回答をさせていただきました。

告知

WebAssembly Micro RuntimeでRustアプリをマイコンで動かす!

はじめに

前回、もう一歩のところだったのですが、RAMが2MB搭載されていないと動かない状態でした。

tomo-wait-for-it-yuki.hatenablog.com

私はそんなマイコン持っていないため、今回は、256KB RAMが搭載されているマイコン (これも高性能品ですが) 上でRustアプリを動作させます。

問題点

データセグメントが0x100000番地に配置されていることが問題でした。

$ wasm-objdump -x target/wasm32-unknown-unknown/release/hello_wasm.wasm 
...
Data[1]:
 - segment[0] size=16 - init i32=1048576
  - 0100000: 4865 6c6c 6f20 6672 6f6d 2052 7573 7400  Hello from Rust.

リンカオプションによる解決

調べていると、次のissueに行き当たりました。

github.com

The large memory size here is because we default to asking LLD to allocate a 1MB stack for all Rust binaries. If you'd like to reduce that though you can pass -C link-arg=-zstack-size=16 to rustc, for an appropriate stack size for your application. That should allow you to shrink quite a bit!

LLDにスタックを1MB割り当てるようなデフォルトになっており、このサイズを減らせば良い、ということでした。

ということで、リンカにオプションを与えます。

|> .cargo/config

[target.wasm32-unknown-unknown]
rustflags = [
    "-C", "link-arg=-zstack-size=16"
]

ビルドします。

$ cargo build --target=wasm32-unknown-unknown --release
$ wasm-strip target/wasm32-unknown-unknown/release/hello_wasm.wasm

中身を確認してみましょう。

$ wasm-objdump -x target/wasm32-unknown-unknown/release/hello_wasm.wasm

hello_wasm.wasm:        file format wasm 0x1

Section Details:

Type[3]:
 - type[0] (i32, i32) -> i32
 - type[1] () -> nil
 - type[2] () -> i32
...
Code[2]:
 - func[1] size=2
 - func[2] size=16 <main>
Data[1]:
 - segment[0] size=16 - init i32=16
  - 0000010: 4865 6c6c 6f20 6672 6f6d 2052 7573 7400  Hello from Rust.

これでデータセグメントが0x10 (16) から置かれるようになりました。

QEMUでの動作確認

実機で試す前に、QEMUを使って、RAM 64KB搭載のCortex-M3をターゲットにして、動作確認します。 (無理矢理動かすために、WebAssembly Micro Runtimeのページサイズを4KBにしています)

$ ninja run
[0/1] To exit from QEMU enter: 'CTRL+a, x'[QEMU] CPU: cortex-m3
qemu-system-arm: warning: nic stellaris_enet.0 has no peer
***** Booting Zephyr OS zephyr-v1.14.0 *****
Hello from Rust

よし!

実機動作

手元に偶然あるnRF52840-DKで動かしてみます。 RAMも256KBあるので、なんとかなるはずです。

$ mkdir nrf52840 && cd $_
$ cmake -GNinja -DBOARD=nrf52840_pca10056 ..
$ ninja
[10/14] Linking C executable zephyr/zephyr_prebuilt.elf
Memory region         Used Size  Region Size  %age Used
           FLASH:       76436 B         1 MB      7.29%
            SRAM:      144328 B       256 KB     55.06%
        IDT_LIST:          56 B         2 KB      2.73%
[14/14] Linking C executable zephyr/zephyr.elf

ビルドはOKです。

minicomでUARTを受信できるようにして、ファームウェアを書き込みます。

ninja flash
Welcome to minicom 2.7.1

OPTIONS: I18n 
Compiled on Aug 13 2017, 15:25:34.
Port /dev/ttyUSB0, 20:35:14

Press CTRL-A Z for help on special keys

***** Booting Zephyr OS zephyr-v1.14.0 *****
Hello from Rust

OK! 無事動きました!

WebAssembly Micro Runtimeの方は、次の通りです。

|> src/main.c

// デフォルトの512 KBでは実機のRAM 256KBに入らないため
static char global_heap_buf[128 * 1024] = { 0 };

|> iwasm/runtime/vmcore-wasm/wasm.h

// WASMは1ページ64KBとのことなので4KBから元に戻しています
#define NumBytesPerPage 65536
#define NumBytesPerPageLog2 16

WebAssembly Micro RuntimeでRustのアプリを動かす!

はじめに

WebAssembly Micro Runtimeでは、64KB程度のRAMが搭載されたマイコンであれば、wasmアプリケーションを動かすことができます。

github.com

Rustはwasmをターゲットにビルドできます。

後は、わかるね?

アプリケーションの作り方

WebAssembly Micro Runtimeのサンプルコードを修正します。 下のようにおもむろにwasmのバイナリが埋め込まれているので、これを自作のバイナリに差し替えます。

|> src/test_wasm.h

unsigned char wasm_test_file[] = { 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00,
        0x00, 0x00, 0x0D, 0x06, 0x64, 0x79, 0x6C, 0x69, 0x6E, 0x6B, 0xC0, 0x80,
        0x04, 0x04, 0x00, 0x00, 0x01, 0x13, 0x04, 0x60, 0x01, 0x7F, 0x00, 0x60,
// ...
        0x70, 0x69, 0x6E, 0x67, 0x55, 0x52, 0x4C, 0x0E, 0x61, 0x2E, 0x6F, 0x75,
        0x74, 0x2E, 0x77, 0x61, 0x73, 0x6D, 0x2E, 0x6D, 0x61, 0x70 };

将来的にはよりスマートな方法が必要ですが、当面は良いでしょう。

Rustツールチェイン

wasmのターゲットをインストールするだけです。

rustup target add wasm32-unknown-unknown

wasmバイナリ作成

cargo new hello_wasm --lib

|> Cargo.toml

[package]
name = "hello_wasm"
version = "0.1.0"
authors = ["tomoyuki-nakabayashi <tomo.wait.for.it.yuki@gmail.com>"]
edition = "2018"

[dependencies]
cty = "0.2.0"

[lib]
crate-type = ["cdylib"]

crate-typeはcdylibになるようです。 wasmバイナリのサイズを小さくするために、#![no_std]アトリビュートを使用するため、stdC言語の型定義が使えません。 そこで、ctyクレートを使って、C言語の型を表現します。

WebAssembly Micro Runtimeの方でprintfのbindingが用意されている(要確認)ので、一旦FFIを手動で用意して、呼び出します。

|> src/lib.rs

#![no_std]
use cty;

extern "C" {
    pub fn printf(fmt: *const cty::c_char, ...) -> cty::c_int;
}

#[no_mangle]
pub extern "C" fn main() -> i32 {
    unsafe {
        printf(b"Hello from Rust\0".as_ptr() as *const cty::c_char)
    }
}

use core::panic::PanicInfo;
#[panic_handler]
#[no_mangle]
pub extern "C" fn panic(_: &PanicInfo) -> ! {
    loop {}
}
cargo build --target wasm32-unknown-unknown --release

この時点で約300バイトと十分に小さいですが、念の為、wasm-stripを使ってさらにバイナリを小さくします。

$ ls -lha target/wasm32-unknown-unknown/release/
-rwxr-xr-x 2 tomoyuki tomoyuki  306 Jun 15 14:12 hello_wasm.wasm
wasm-strip target/wasm32-unknown-unknown/release/hello_wasm.wasm

strip後のサイズです。180バイトまで小さくなります。

$ ls -lha target/wasm32-unknown-unknown/release/
-rwxr-xr-x 2 tomoyuki tomoyuki  180 Jun 15 14:13 hello_wasm.wasm

wasm-objdumpを使って逆アセンブルすると、次のような感じです。

$ wasm-objdump -x target/wasm32-unknown-unknown/release/hello_wasm.wasm 

hello_wasm.wasm:        file format wasm 0x1

Section Details:

Type[3]:
 - type[0] (i32, i32) -> i32
 - type[1] () -> nil
 - type[2] () -> i32
Import[1]:
 - func[0] sig=0 <env.printf> <- env.printf
Function[2]:
 - func[1] sig=1
 - func[2] sig=2 <main>
Table[1]:
 - table[0] type=funcref initial=1 max=1
Memory[1]:
 - memory[0] pages: initial=17
Global[3]:
 - global[0] i32 mutable=1 - init i32=1048576
 - global[1] i32 mutable=0 <__heap_base> - init i32=1048592
 - global[2] i32 mutable=0 <__data_end> - init i32=1048592
Export[4]:
 - memory[0] -> "memory"
 - global[1] -> "__heap_base"
 - global[2] -> "__data_end"
 - func[2] <main> -> "main"
Code[2]:
 - func[1] size=2
 - func[2] size=16 <main>
Data[1]:
 - segment[0] size=16 - init i32=1048576
  - 0100000: 4865 6c6c 6f20 6672 6f6d 2052 7573 7400  Hello from Rust.

hexdumpを使って、適当にC言語に埋め込める形式を出力します。

$ hexdump wasm.wasm -e '16/1 "0x%02x, "' -e '"\n"'
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x03, 0x60, 0x02, 0x7f, 0x7f, 0x01,
0x7f, 0x60, 0x00, 0x00, 0x60, 0x00, 0x01, 0x7f, 0x02, 0x0e, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x06,
...

これを、WebAssembly Micro Runtimeのサンプルコードに埋め込みます。

|> src/test_wasm.h

unsigned char wasm_test_file[] = {
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x03, 0x60, 0x02, 0x7f, 0x7f, 0x01,
0x7f, 0x60, 0x00, 0x00, 0x60, 0x00, 0x01, 0x7f, 0x02, 0x0e, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x06,
// ...
};

実行

qemu_x86をターゲットに実行します。 OSはZephyrです。

$ ninja run
SeaBIOS (version rel-1.12.0-0-ga698c8995f-prebuilt.qemu.org)
Booting from ROM..***** Booting Zephyr OS zephyr-v1.14.0 *****
Hello from Rust

無事動きました! と言いたいところですが、少しごまかしが入っています。

data segmentの置き場所問題

どうもRustでwasmをターゲットにビルドすると、データセグメントが0x10_0000 (1MB)から始まる領域に置かれるようです。

$ wasm-objdump -x target/wasm32-unknown-unknown/release/hello_wasm.wasm 
...
Data[1]:
 - segment[0] size=16 - init i32=1048576
  - 0100000: 4865 6c6c 6f20 6672 6f6d 2052 7573 7400  Hello from Rust.

一方(?)、サンプルアプリケーションの方では、wasm用のヒープ領域を512KB確保しています。

|> src/main.c

static char global_heap_buf[512 * 1024] = { 0 };

この状態でアプリケーションを実行するとメモリ確保が失敗します。

Booting from ROM..***** Booting Zephyr OS zephyr-v1.14.0 *****
Instantiate memory failed: allocate memory failed.

どうもランタイムでは、データセグメントも含めて1つのメモリ領域に割り当てているように見えます。

データセグメントが1MBから始まるので、ヒープ領域を2MB確保すると、上述の通り、アプリケーションが動作します。 intelさんとこは8MBもRAMが載っていてようござんすねぇ

|> src/main.c

static char global_heap_buf[2048 * 1024] = { 0 };

ということで、後は、Rustでサンプルアプリケーションをビルドする際にデータセグメントのアドレスを指定できれば、 64KB程度のRAMが載ったマイコンボードで動きそうです!

WebAssembly Micro Runtimeお試し②

はじめに

少し前に、組込みで使えるWebAssembly Micro Runtimeが公開されました。 また、いつの間にかSTMでのデモアプリが公開されています。

github.com

リファレンスが普段触っているZephyrなので、少し動かしてみます。

前回のおさらい

試しに、qemu_cortex_m3をターゲットにビルドすると、RAM不足でビルドが失敗していました。

$ cmake -GNinja -DBOARD=qemu_cortex_m3 ..
$ ninja run
Memory region         Used Size  Region Size  %age Used
           FLASH:       69112 B       256 KB     26.36%
            SRAM:      535984 B        64 KB    817.85%
        IDT_LIST:         120 B         2 KB      5.86/opt/zephyr-sdk/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/8.3.0/../../../../arm-zephyr-eabi/bin/ld: zephyr/zephyr_prebuilt.elf section `bss' will not fit in region `SRAM'
/opt/zephyr-sdk/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/8.3.0/../../../../arm-zephyr-eabi/bin/ld: section .intList VMA [0000000020010000,0000000020010077] overlaps section bss VMA [0000000020000000,0000000020080354]
/opt/zephyr-sdk/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/8.3.0/../../../../arm-zephyr-eabi/bin/ld: region `SRAM' overflowed by 470448 bytes
collect2: error: ld returned 1 exit status
%
ninja: build stopped: subcommand failed.

64KBくらいRAMがあれば動いて欲しいところなので、なんとか動かせないか試してみます。

利用RAMサイズを減らす

twitterで@nodamushiさんがヒントをくれました。

|> core/iwasm/runtime/vmcore-wasm/wasm.h

#define MaxMemoryPages 65536
#define MaxTableElems UINT32_MAX
#define NumBytesPerPage 65536
#define NumBytesPerPageLog2 16
#define MaxReturnValues 16

wasmに詳しくないので、ページの概念が何を表現しているのかわかりませんが、とりあえず1ページ4KBくらいでよくない?ということで、定義を修正します。

#define NumBytesPerPage 4096
#define NumBytesPerPageLog2 12

main.cで確保されるヒープサイズを32KBにします。

|> core/iwasm/products/zephyr/simple/src/main.c

static char global_heap_buf[32 * 1024] = { 0 };
$ ninja run
[9/14] Linking C executable zephyr/zephyr_prebuilt.elf
Memory region         Used Size  Region Size  %age Used
           FLASH:       69112 B       256 KB     26.36%
            SRAM:       44464 B        64 KB     67.85%
        IDT_LIST:         120 B         2 KB      5.86%
[13/14] To exit from QEMU enter: 'CTRL+a, x'[QEMU] CPU: cortex-m3
qemu-system-arm: warning: nic stellaris_enet.0 has no peer
***** Booting Zephyr OS zephyr-v1.14.0 *****
Hello world!
buf ptr: 0x40000178
buf: 1234

お、動きますね! OS、アプリケーションとラインタイム合わせて、RAMの使用量は44KBくらいです。

ちなみに、global_heap_bufは26KBまで小さくしても動作します。 25KBからはRuntimeのインスタンス化に失敗します。 Runtimeに最低でも26KB程度は必要、ということでしょう。

memory_usage.txtに、RAM利用量について、次の記述がありました。

Current memory usage, take samples/littlevgl in Zephyr for example:
(1) WASM app binary:                        142K for littlevgl ui_app.wasm
(2) WASM app memory space:                  64K for littlevgl ui_app.wasm
(3) WASM app heap space:                    8K by default
(4) WASM app thread native stack:           4K by default
(5) WASM interpreter stack:                 8K by default
(6) WASM block address hash cache:          3K
(7) timer thread stack:                     4K
(8) sensor thread stack:                    4K
(9) touch screen thread stack:              4K
(10) others: vm, app mgr, queue, native lib: ~22K

Total memory usage: ~263K

(10) others: vm, app mgr, queue, native lib: ~22Kとなっているので、プラスαで何かがあって、26KBが最低ライン、というのは有り得そうです。 次回以降で、もう少し詳しく内部を見ていきましょう。

WebAssembly Micro Runtimeお試し①

はじめに

少し前に、組込みで使えるWebAssembly Micro Runtimeが公開されました。 また、いつの間にかSTMでのデモアプリが公開されています。

github.com

リファレンスが普段触っているZephyrなので、少し動かしてみます。

準備

ライブラリをインストールします。

sudo apt install lib32gcc-5-dev g++-multilib

ソースコードをビルドします。

git clone https://github.com/intel/wasm-micro-runtime.git
cd wasm-micro-runtime/core/iwasm/products/linux/
cmake
make

iwasmというバイナリができました。

$ ls
CMakeCache.txt  cmake_install.cmake  libiwasm.so  Makefile
CMakeFiles      iwasm                libvmlib.a

Zephyrで起動してみましょう。

cd core/iwasm/products/zephyr/simple
source <zephyr>/zephyr-env.sh
ln -s ../../../ iwasm
ln -s ../../../../shared-lib shared-lib
mkdir build && cd $_
cmake -GNinja -DBOARD=qemu_x86 ..

<iwasm_dir>core/iwasmディレクトリです。 <shared_lib_dir>wasm-micro-runtime/core/shared-libディレクトリです。

実行します。

$ ninja run
SeaBIOS (version rel-1.12.0-0-ga698c8995f-prebuilt.qemu.org)
Booting from ROM..***** Booting Zephyr OS zephyr-v1.14.0 *****
Hello world!
buf ptr: 0x40000180
buf: 1234

動くやん!お手軽ですねー。

次に、試しにqemu_cortex_m3にターゲットを移してみましょう。

$ cmake -GNinja -DBOARD=qemu_cortex_m3 ..
$ ninja run
Memory region         Used Size  Region Size  %age Used
           FLASH:       69112 B       256 KB     26.36%
            SRAM:      535984 B        64 KB    817.85%
        IDT_LIST:         120 B         2 KB      5.86/opt/zephyr-sdk/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/8.3.0/../../../../arm-zephyr-eabi/bin/ld: zephyr/zephyr_prebuilt.elf section `bss' will not fit in region `SRAM'
/opt/zephyr-sdk/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/8.3.0/../../../../arm-zephyr-eabi/bin/ld: section .intList VMA [0000000020010000,0000000020010077] overlaps section bss VMA [0000000020000000,0000000020080354]
/opt/zephyr-sdk/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/8.3.0/../../../../arm-zephyr-eabi/bin/ld: region `SRAM' overflowed by 470448 bytes
collect2: error: ld returned 1 exit status
%
ninja: build stopped: subcommand failed.

SRAMのサイズが足りずにリンクで死んだ!!! なんでしょうこのSRAM使用量…。500KB超えてますが…。

STMのデモ

じゃあSTMで動いているデモは一体何なのでしょうか? ということで、デモアプリのREADMEを読んでみます。

|> wasm-micro-runtime/samples/littlevgl/README.md

Since ui_app incorporated LittlevGL source code, so it needs more RAM on the device to install the application. It is recommended that RAM SIZE greater than 512KB.

512KB以上のRAMが推奨環境! かなり高性能なマイコンを要求していますね…。

デモで使用しているマイコンNUCLEO-F767ZIという512KB RAMを搭載しているものですね…。

os.mbed.com

simpleデモアプリのソースコードを見る

希望を捨てずに、デモアプリで何をしているか、少し覗いてみましょう。 アプリで使うRAMサイズが減れば、動くかもしれないですし!

|> wasm-micro-runtime/core/iwasm/products/zephyr/simple/src/main.c

static char global_heap_buf[512 * 1024] = { 0 };

ん?きみぃ?何だいこの有無を言わさず512KB確保するstatic変数の定義は!

static char global_heap_buf[16 * 1024] = { 0 };

とりあえず、16KBくらいにしたろ。

$ ninja run
[0/1] To exit from QEMU enter: 'CTRL+a, x'[QEMU] CPU: cortex-m3
qemu-system-arm: warning: nic stellaris_enet.0 has no peer
***** Booting Zephyr OS zephyr-v1.14.0 *****
Instantiate memory failed: allocate memory failed.

うん。QEMU起動まではいきましたが、ランタイムで落ちてます。

|> wasm-micro-runtime/core/iwasm/products/zephyr/simple/iwasm/runtime/vmcore-wasm/wasm_runtime.c

static WASMMemoryInstance*
memory_instantiate(uint32 init_page_count, uint32 max_page_count,
                   uint32 addr_data_size, uint32 global_data_size,
                   uint32 heap_size,
                   char *error_buf, uint32 error_buf_size)
{
    WASMMemoryInstance *memory;
    uint32 total_size = offsetof(WASMMemoryInstance, base_addr) +
                        NumBytesPerPage * init_page_count +
                        addr_data_size + global_data_size;

    /* Allocate memory space, addr data and global data */
    if (!(memory = wasm_malloc(total_size))) {
        set_error_buf(error_buf, error_buf_size,
                      "Instantiate memory failed: allocate memory failed.");
        return NULL;
    }

この辺りかな? 少しデバッガで見ていますが、サクッと解決できなさそうなので、また次回。