mruby/c をリンクする

mruby/cの呼び出し

サンプルのコードをテキトーに張りました.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "mrubyc.h"

int hal_write(int fd, const void *buf, int nbytes)
{
	return 1;
}
int hal_flush(int fd)
{
	return 1;
}


static const uint8_t
#if defined __GNUC__
__attribute__((aligned(4)))
#elif defined _MSC_VER
__declspec(align(4))
#endif
bin[] = {
0x45,0x54,0x49,0x52,0x30,0x30,0x30,0x36,0x7a,0xa8,0x00,0x00,0x00,0x77,0x4d,0x41,
0x54,0x5a,0x30,0x30,0x30,0x30,0x49,0x52,0x45,0x50,0x00,0x00,0x00,0x59,0x30,0x30,
0x30,0x32,0x00,0x00,0x00,0xae,0x00,0x01,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x1f,
0x21,0x00,0x14,0x10,0x01,0x4f,0x02,0x00,0x2e,0x01,0x00,0x01,0x10,0x01,0x07,0x02,
0x2e,0x01,0x01,0x01,0x11,0x01,0x22,0x01,0x00,0x03,0x0f,0x01,0x37,0x01,0x67,0x00,
0x00,0x00,0x01,0x00,0x00,0x06,0x73,0x61,0x6d,0x70,0x6c,0x65,0x00,0x00,0x00,0x02,
0x00,0x04,0x70,0x75,0x74,0x73,0x00,0x00,0x05,0x73,0x6c,0x65,0x65,0x70,0x00,0x45,
0x4e,0x44,0x00,0x00,0x00,0x00,0x08,
};

#define MEMORY_SIZE (0x400)
static uint8_t memory_pool[MEMORY_SIZE];

void mbrc_test(void)
{
	mrbc_init(memory_pool, MEMORY_SIZE);
	if(mrbc_create_task(bin, 0) != NULL){
		mrbc_run();
	}
}

mrubyc.h の作成

src/mrubyc.h は存在しますが、ライブラリ内部処理の全部のヘッダを読むみたいでした. それはユーザー側が手を出す必要のない定義が大量に含まれていたり、前回の src/hal の問題がありまして厄介でした.

API の記述はアーカイブになかったので不明で、上記の呼び出し側で使う関数が記述されていた src/rrt0.h を編集し、 include/mrubyc.h を別途作成しました.

#ifndef MRBC_INCLUDE_MRUBYC_H_
#define MRBC_INCLUDE_MRUBYC_H_

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>

struct RTcb;
struct RMutex;
#define MRBC_MUTEX_INITIALIZER { 0 }

void mrbc_tick(void);
void mrbc_init(uint8_t *ptr, unsigned int size);
void mrbc_init_tcb(struct RTcb *tcb);
struct RTcb *mrbc_create_task(const uint8_t *vm_code, struct RTcb *tcb);
int mrbc_start_task(struct RTcb *tcb);
int mrbc_run(void);
void mrbc_sleep_ms(struct RTcb *tcb, uint32_t ms);
void mrbc_relinquish(struct RTcb *tcb);
void mrbc_change_priority(struct RTcb *tcb, int priority);
void mrbc_suspend_task(struct RTcb *tcb);
void mrbc_resume_task(struct RTcb *tcb);
struct RMutex *mrbc_mutex_init(struct RMutex *mutex);
int mrbc_mutex_lock(struct RMutex *mutex, struct RTcb *tcb);
int mrbc_mutex_unlock(struct RMutex *mutex, struct RTcb *tcb);
int mrbc_mutex_trylock(struct RMutex *mutex, struct RTcb *tcb);

#ifdef __cplusplus
}
#endif
#endif // ifndef MRBC_INCLUDE_MRUBYC_H_

struct RTcb; struct RMutex; の中身はここでは未定義とし、ポインタだけ使うようにしました. 必要になるのでしたら別途編集したほうがいいです.

このような隠蔽化されたヘッダがなくやユーザーが必要な API が記述されてないのは、実用するかの評価ではかなり悪いです.

リンク

STM32F070RB での RAM 使用量は下記でした. libmrubyc.a は -Os のビルドでやってます. ライブラリだけで .text (instruction ROM) を 88% も食ってたらユーザー側のソフトをいれる余裕がなさそうです.

  • .text: 0x1c374 / 0x20000 bytes, 88%
  • .data: 0x1de8 / 0x4000 bytes, 47%

リンカが出したメモリ配置データを成形し、使用容量順に並べたのが下記です.

0x3468 .text                    libmrubyc.a(vm.o)
0x277c .text._svfprintf_r       libc.a(lib_a-svfprintf.o)
0x1dd4 .text                    libmrubyc.a(c_string.o)
0x1a30 .text                    libmrubyc.a(class.o)
0x16b4 .text._dtoa_r            libc.a(lib_a-dtoa.o)
0x1684 .text._strtod_l          libc.a(lib_a-strtod.o)
0x1528 .text                    libmrubyc.a(c_array.o)
0x1458 .text._vfiprintf_r       libc.a(lib_a-vfiprintf.o)
0x0c08 .text                    libmrubyc.a(c_hash.o)
0x0bc0 .text                    libmrubyc.a(rrt0.o)
0x09a4 .text.qsort              libc.a(lib_a-qsort.o)
0x08dc .text                    libmrubyc.a(console.o)
0x0858 .text                    libmrubyc.a(alloc.o)
0x0838 .text.__gethex           libc.a(lib_a-gdtoa-gethex.o)
0x07f0 .text                    libmrubyc.a(c_numeric.o)
0x07d4 .text._malloc_r          libc.a(lib_a-mallocr.o)
0x04dc .text._realloc_r         libc.a(lib_a-reallocr.o)
0x04cc .text.__sfvwrite_r       libc.a(lib_a-fvwrite.o)
0x04b4 .text                    libmrubyc.a(c_range.o)
0x04ac .text                    libmrubyc.a(keyvalue.o)
0x049c .text                    libgcc.a(_arm_muldivdf3.o)
0x0458 .text                    libmrubyc.a(symbol.o)
0x0454 .text                    libmrubyc.a(load.o)
0x0424 .text                    libmrubyc.a(value.o)
0x0424 .text                    libgcc.a(_arm_addsubdf3.o)
0x02e0 .text._free_r            libc.a(lib_a-freer.o)
0x0260 .text.__hexnan           libc.a(lib_a-gdtoa-hexnan.o)
0x0250 .text.__sflush_r         libc.a(lib_a-fflush.o)
0x0224 .text                    libc.a(lib_a-strcmp.o)
(以下略)

libmrubyc.a は後にして libc.a からprintf 系と dtoa, strtod あたりを削っていけば text の削減ができそうです. 軽く見た感じ printf は mrubyc でも軽量化した物を用意してるはずですから.

軽量化

ソースを見たところ float 関連で snprintf(), assert で printf() を呼んでいるのが問題の原因でした. float は src/vm_config.h で切り離せますし、assert は自作して軽量化した printf() を呼べばいいです.
今回は assert のための文字列領域も削ったのでコンパイルオプションで -DNDEBUG をつけました. .text 使用順は下記となりました.

0x2e98 .text                    libmrubyc.a(vm.o)
0x1ce4 .text                    libmrubyc.a(c_string.o)
0x18fc .text                    libmrubyc.a(class.o)
0x1504 .text                    libmrubyc.a(c_array.o)
0x0bd0 .text                    libmrubyc.a(c_hash.o)
0x0a28 .text                    libmrubyc.a(rrt0.o)
0x09a4 .text.qsort              libc.a(lib_a-qsort.o)
0x07a4 .text                    libmrubyc.a(console.o)

軽量化の後の使用領域は下記となり、メモリ資源の観点からは実用できそうです.

  • .text: 0x08980/0x20000 bytes, 27%
  • .data: 0x1808/0x4000 bytes, 37%

その他 RAM 使用量をみていたら .impure_data が 0x400 bytes 程度消費していました. 逆アセンブルしてみたところ atol() から呼ばれる strtol() がそれをなぜか使用しているみたいでした. 私が使用している arm gcc toolchain 限定でしょうけど、試しに atol() の呼び出しを消してみたら .impure_data も消えました. atol() は自作した方がいいかもしれません.