3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

RXファミリMCUでwasm3を使用してC/C++ + WebAssemblyを動かす

Last updated at Posted at 2022-04-03

環境準備

最近購入したTarget Board for RX671でWebAssemblyインタプリタの実装であるwasm3を使用して、C/C++ + WebAssemblyを動かす手順を確認してみました。

  • WebAssemblyはC言語やRust言語等の様々なプログラミング言語をコンパイルターゲットにして、ウェブブラウザ等の様々な環境で実行されるプログラミング言語のことです。

準備した環境は以下の通りです。

  1. Target Board for RX671 (評価ボード)
  2. e2 studio 2021-07 (統合開発環境)
  3. GCC for Renesas 8.3.0.202102-GNURX Windows (RXマイコン用コンパイラ)
  4. wasm3 v0.5.0 (https://github.com/wasm3/wasm3)
  5. Emscripten (WebAssemblyへのコンパイラツールチェーン)
  6. Git (Emscriptenダウンロード用)
  7. MSYS2 64-bit (xxdコマンド用)

Emscriptenのインストール

WebAssemblyへのコンパイラツールチェーンであるEmscriptenをインストールするためにコマンドプロンプトで下記コマンドを実行します。

  • Emscriptenのインストールには別途Pythonのインストールが必要です。
  • インストールが完了したらemcmdprompt.batをダブルクリックで実行しておきます。
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
git pull
emsdk install latest
emsdk activate latest

C/C++のソースコードをWebAssemblyバイナリに変換する

Emscriptenを使用して、wasm3上で実行するWebAssemblyバイナリを作成します。
今回はC/C++のソースコードにTarget Board for RX671に実装されているLEDをチカチカさせるための処理を実装します。
以下の内容のCソースファイルを作成します。

test_wasm.c
#include <stdint.h>

void delay          (int ms);
void led_low        (void);
void led_high       (void);

#define WASM_EXPORT                   __attribute__((used)) __attribute__((visibility ("default")))
#define WASM_EXPORT_AS(NAME)          WASM_EXPORT __attribute__((export_name(NAME)))
#define WASM_IMPORT(MODULE,NAME)      __attribute__((import_module(MODULE))) __attribute__((import_name(NAME)))
#define WASM_CONSTRUCTOR              __attribute__((constructor))

WASM_IMPORT("function", "delay")           void delay          (int ms);
WASM_IMPORT("function", "led_low")         void led_low        (void);
WASM_IMPORT("function", "led_high")        void led_high       (void);

void setup() {
  led_high();
}

// the loop function runs over and over again forever
void loop() {
  led_low();
  delay(100);
  led_high();
  delay(900);
}

/*
 * Entry point
 */
WASM_EXPORT
void _start() {
  setup();
  while (1) { loop(); }
}

上記のCソースファイルを下記コマンドでWebAssemblyバイナリに変換します。

  • ワーニングが表示されますが今回は無視してください。
  • test_wasm.cと同じディレクトリにtest_wasm.wasmが作成されていれば成功です。
emcc test_wasm.c -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s WASM=1 -o test_wasm.html

WebAssemblyバイナリをC言語で読み込める配列に変換する

WebAssemblyバイナリに変換したら、下記コマンドでMSYS2にVimをインストールします(xxdコマンドを使用するためです)。

pacman -S vim

次にxxdコマンドを使用してC言語で読み込める配列に変換します。

  • 下記コマンドを実行するとtest_wasm.hが生成されます。
xxd -i test_wasm.wasm > test_wasm.h

e2 studioで新規プロジェクトを作成する

e2 studioを起動して上記URLからダウンロードしたwasm3を動かすための新規プロジェクトを作成します。

  • 新規プロジェクトで使用するコンパイラはGCC for Renesas RX 8.3.0202102を選択しておきます。

  • Target BoardはTargetBoardRX671を選択しておいてください(クロックがTargetBoard用に自動的に設定されます)
    image.png

  • 今回はRXファミリMCU向けのデバイスドライバであるFITモジュールを使用するのでUse Smart Configuratorにチェックを入れて新規プロジェクトを作成します。
    image.png

  • 新規プロジェクトを作成したら、プロジェクトのsrcフォルダ以下にwasm3フォルダを作成します。

  • 作成したwasm3フォルダにwasm3 v0.5.0のsourceフォルダ以下をドラッグアンドドロップでコピーします。
    image.png

  • 追加したwasm3のソースコードに対して下記のインクルードパスを設定します。

    • "${workspace_loc:/${ProjName}/src/wasm3}"
    • "${workspace_loc:/${ProjName}/src/wasm3/extensions}"
    • "${workspace_loc:/${ProjName}/src/wasm3/extra}"

メインルーチンを追加してプログラムを実行する

wasm3のソースコードはRXマイコン用GCCコンパイラでビルドするとワーニングが表示されるので、wasm3_defs.hを開いて下記定義を追加しておきます。

wasm3_defs.h
#  elif defined(__arc__)
#   define M3_ARCH "arc32"

#  elif defined(__AVR__)
#   define M3_ARCH "avr"

#  elif defined(__RX__)
#   define M3_ARCH "Renesas RX"
#  endif
# endif

次にmain関数が定義されているソースコード(作成したプロジェクト名と同じ名前)を開き、下記処理を追加します。

  • m3_LinkRawFunction関数でWebAssemblyバイナリに変換したC言語の関数とのリンクを実施します(つまり、wasm3がWebAssemblyバイナリを実行してdelay関数をコールするとメインルーチンにあるm3_delay_ms関数がコールされます)。
  • 今回はTarget Board for RX671に実装されているLED0とLED1を点滅するプログラムを作成しました、そのためdelay関数の他にもled_lowled_high関数をそれぞれm3_led_lowm3_led_high関数にリンクさせています。
main.c
#include "r_smc_entry.h"
#include "platform.h"
#include <wasm3.h>
#include <m3_env.h>

#define WASM_STACK_SLOTS    1024
#define NATIVE_STACK_SIZE   (32*1024)
#define WASM_MEMORY_LIMIT   4096

m3ApiRawFunction(m3_delay_ms)
{
    m3ApiGetArg     (uint32_t, ms)

    R_BSP_SoftwareDelay(ms, BSP_DELAY_MILLISECS);

    m3ApiSuccess();
}

m3ApiRawFunction(m3_led_low)
{
	PORT3.PODR.BIT.B2 = 1;
	PORT3.PODR.BIT.B3 = 1;

    m3ApiSuccess();
}

m3ApiRawFunction(m3_led_high)
{
	PORT3.PODR.BIT.B2 = 0;
	PORT3.PODR.BIT.B3 = 0;

    m3ApiSuccess();
}

M3Result  LinkFunction  (IM3Runtime runtime)
{
    IM3Module module = runtime->modules;
    const char* function = "function";

    m3_LinkRawFunction (module, function, "delay",           "v(i)",    &m3_delay_ms);
    m3_LinkRawFunction (module, function, "led_low",         "v()",   &m3_led_low);
    m3_LinkRawFunction (module, function, "led_high",        "v()",  &m3_led_high);

    return m3Err_none;
}

void wasm_task(void *param)
{
	M3Result result = m3Err_none;

	IM3Environment env = m3_NewEnvironment ();
	if (!env)
	{
		while(1);
	}

	IM3Runtime runtime = m3_NewRuntime (env, WASM_STACK_SLOTS, NULL);
	if (!runtime)
	{
		while(1);
	}

#ifdef WASM_MEMORY_LIMIT
    runtime->memoryLimit = WASM_MEMORY_LIMIT;
#endif

	IM3Module module;
	result = m3_ParseModule (env, &module,  test_wasm_wasm,  test_wasm_wasm_len);
	if (result)
	{
		while(1);
	}

	result = m3_LoadModule (runtime, module);
	if (result)
	{
		while(1);
	}

	result = LinkFunction (runtime);
	if (result)
	{
		while(1);
	}

	IM3Function f;
	result = m3_FindFunction (&f, runtime, "_start");
	if (result)
	{
		while(1);
	}

	result = m3_CallV (f);
}

void main(void);
void main(void)
{
    PORT3.PDR.BIT.B2 = 1;
    PORT3.PDR.BIT.B3 = 1;

    wasm_task(NULL);

	while(1);
}

次に先ほど作成したtest_wasm.hをテキストエディタ等で開いて、その内容をmain関数が定義されているソースコードにペーストします。

test_wasm.h
unsigned char test_wasm_wasm[] = {
  0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9f, 0x80, 0x80,
  0x80, 0x00, 0x06, 0x60, 0x00, 0x00, 0x60, 0x00, 0x01, 0x7f, 0x60, 0x01,
  /* ... */
};
unsigned int test_wasm_wasm_len = 744;

wasm3はcallocで動的にメモリを確保します。
しかし、プロジェクト新規作成時デフォルトのヒープサイズでは足りないため、下記のようにヒープサイズを変更します。
image.png

プログラムを実行するので、下記のようにTarget Board for RX671用にデバッガ設定を変更して、デバッグを開始します。
image.png

デバッグ開始後にプログラムを実行するとTarget Board for RX671のLED0とLED1が点滅していることを確認します。

さいごに

WebAssemblyインタプリタ実装であるwasm3をRXマイコンMCU上で動作させていました。
今回はC/C++のソースコードを変換して実行しましたが、wasm3は他にもRustGo言語にも対応しているので試してみたいです。
また、C言語をネイティブに実行させたときに比べて、どの程度パフォーマンスに差が出るのか確認してみたいです。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?