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() は自作した方がいいかもしれません.