2017年04月25日

Daruma BASIC 1.0pre6 公開

 Daruma BASIC 1.0pre6 を公開しました。Raspberry Pi 1/2/3/Zero で、ベアメタル (OS なし) で BASIC が走ります。タイニー版じゃないよ。ちゃんと実数計算もできますからね。

 ベアメタル版はファイル入出力が遅いので、今まで起動時にフォントデータの読み込みのため10秒近くかかっていた。今回から、フォントデータをバイナリに埋め込むことにした。動画で見てわかる通り、立ち上がりは5秒程度に短縮された。これなら、ほぼ「電源 ON で即 BASIC」と言っても差し支えないでしょう。

 やっと「とりあえず動く」レベルには達したと思うので、サンプルやドキュメントをもう少し整備していきたい。周辺装置 (GPIO/SPI/I2C) のサポートも必要だろうけど、もう少しあとかな。

posted by toshinagata at 23:11| 日記

2017年04月23日

ラズパイのベアメタル開発:circle と newlib を併用する

 ラズパイのベアメタル開発。先日も書いたけど、ラズパイ3で動かすのに手こずっていた。何とかできたみたいなので、手順を書いておきます。

 まず、ラズパイ1の開発に使っていた Maccasoft さんのカーネルは、ラズパイ2以上を使うことは想定されていない。ペリフェラルのアドレスを書き換えて、スタートアップも書き換えれば何とかなるんだと思うけど,ちょっと自分の力では無理だったので、別のカーネルを探すことにした。

 採用したのは circle。ラズパイ1・2・3・Zero で動作確認されている。ただし、標準ライブラリを一切使わない想定で書かれているので、少し込み入ったプログラムを書こうとすると不自由を感じる。そこで、newlib と併用することを検討した。

 今回は、ツールチェインとして ARMDeveloper サイトで提供されているものを使った。"6-2017-q1-update" というもので、GCC のバージョンは 6.3.1。インストールして、実行パスを通しておく。

 Circle の Makefile を参考にして、リンクフェーズのコマンドを下のようにしてみた。リンカを ld から gcc にして、-nostartfiles を指定し、circle の startup.o をリンクする。

$ arm-eabi-none-gcc -o kernel7.elf -nostartfiles -Wl,-Map,kernel7.map \
-T $(CIRCLEHOME)/circle.ld $(CIRCLEHOME)/lib/startup.o $(OBJS) $(LIBS) -lm

 いろいろつまづきました。最初に引っかかったのは、「__aeabi_idiv が二重定義」というエラー。これは libgcc.a にあるのだが、circle/lib/libstub.S でも定義されている。無視してくれないんだ。さらに調べてみると、libgcc.a では同じオブジェクトファイルに __aeabi_idiv__aeabi_idivmod が定義されていて、後者が circle では定義されていないため、このオブジェクトファイルをリンクしようとして、定義済みの __aeabi_idiv とぶつかっていることが判明した。Circle の __aeabi_idiv を削るのはいろいろ問題を起こしそうだったので、libstub.S に以下の記述を書き加えて、Daruma BASIC のプロジェクトに追加することにした。

  .globl  __aeabi_idivmod
__aeabi_idivmod:
  push {r0, r1, lr}
  bl __aeabi_idiv
  pop {r1, r2, lr}
  mul r3, r2, r0
  sub r1, r1, r3
  bx lr

 ARM のアセンブラなんて使うの初めてだよ。なんかどきどきする。剰余の計算としてはあまり効率良くない感じだけど、とりあえずは動かすことが優先だ。

 次に遭遇したのが、uint8_t などが二重定義になっている、というエラー。これは、circle の stdint.h と newlib の stdint.h がぶつかっていることが原因。よく見ると、circle の stdint.h は全く使われていないので、_stdint.h などとリネームすれば良かった。

 次は、このエラー。

error: kernel7.elf uses VFP register arguments, /usr/local/(...中略...)
/lib/libm.a(lib_a-s_ceil.o) does not

 どうも、circle のビルドの時に指定している -mfloat-abi=hard があかんらしい。いろいろ検討したが、結局 -mfloat-abi=softfp に切り替えることにした。速度が少し落ちるのかも知れないけど、ここも動かすことを優先。

 次に、malloc() 系の処理。Newlib では _sbrk() をユーザーに提供させて、それを使って malloc() 系の関数を実装しているのだが、circle は自前でメモリ管理を行っているので、そちらを使うようにする。_sbrk() は 0 を返すようにして、circle の malloc()/free() をリンクすればよい。calloc(), realloc() は circle では実装されていないので、malloc() を使って書いておく。また、newlib は内部で _malloc_r(), _free_r(), _calloc_r(), _realloc_r() というリエントラント対応の関数を持っていて、これらも上書きしておかないと、circle の実装と newlib の実装を混用することになり、問題が起きる。(参考:https://sourceware.org/ml/newlib/2000/msg00143.html "user-defined malloc"

 そういうわけで、こんな感じで実装。

void *calloc(size_t count, size_t size)
{
  void *p = malloc(count * size);
  if (p != NULL)
    memset(p, 0, count * size);
  return p;
}

void *realloc(void *ptr, size_t size)
{
  void *p = malloc(size);
  if (p != NULL) {
    memmove(p, ptr, size);
    free(ptr);
  }
  return p;
}

struct _reent;
void *_malloc_r(struct _reent *r, size_t size)
{  return malloc(size); }

void _free_r(struct _reent *r, void *ptr)
{  free(ptr); }

void *_calloc_r(struct _reent *r, size_t count, size_t size)
{  return calloc(count, size); }

void *_realloc_r(struct _reent *r, void *ptr, size_t size)
{  return realloc(ptr, size); }

 また、strtoul() も二重定義になることがわかった。これも、_strtoul_r() というリエントラント可能なバージョンがあって、こちらをリンクしようとしてこけるらしい。そこで、_strtoul_r() もダミー実装を追加した。

unsigned long _strtoul_r(struct _reent *r, const char *s, char **ptr, int base)
{  return strtoul(s, ptr, base); }

 これで、単純なプログラムは動作することを確認したのだが、実際に Daruma BASIC をビルドしてみると、動作しない。しかも、途中で止まるとかそういうのではなくて、最初から立ち上がりもしない。いろいろコードを変更して調べていたとき、標準ライブラリの strcmp() をリンクすると立ち上がらなくなることがわかった。kernel7.map を調べて、原因が判明。strcmp() 由来の .eh_frame セクションがあるのだが、__init_start ラベルがその前にある。__init_start は、静的オブジェクトのコンストラクタを呼ぶのに使われているので、.eh_frame セクションの中身をコンストラクタのアドレスだと思って呼び出していることになる。そりゃ暴走するわな。というわけで、リンカスクリプトを修正して、.init_array の先頭が __init_start になるようにした。

--- circle/circle.ld	2017-04-23 19:12:35.000000000 +0900
+++ DarumaBasic/build_raspi_bm/circle.ld	2017-04-23 22:00:45.000000000 +0900
@@ -18,9 +18,9 @@
 
-	__init_start = .;
 
 	.init_array : {
+	__init_start = .;
 		*(.init_array*)
+	__init_end = .;
 	}
 
-	__init_end = .;
 

 一応これで、circle と newlib は共存できるようになったのかな。細かい不具合のあぶり出しはこれからだけど。

タグ:Raspberry Pi
posted by toshinagata at 22:28| 日記

2017年04月19日

踏切事故:人助けはいいけど、まず自分の安全を確保しよう

 京急で、踏切に進入した人を救助しようとして、2人とも亡くなられた件。最初の男性がそのような行動を選んだ事情や、助けようとして力及ばなかった方の無念を思うと、切ないというほかない。心からお悔やみを申し上げます。

 その上で、以前も書いたことだけど、人命救助を目指して自分も事故に巻き込まれることは、絶対に避けないといけない。犠牲者が増えてしまう確率が格段に上がるわけじゃないですか。ここは、まず停止ボタンを押して、あとは遮断機の外から大声で呼びかけるのが、唯一の解だったと思う。仮に、踏切に進入したのが自分の家族だったら、衝動的に助けに走ってしまうだろうけど、そうでないのなら、一旦冷静になって、自分の安全を確保することが大事。

 こんなツイートを見かけました。現役の車掌さんだそうです。

タグ:社会
posted by toshinagata at 22:47| 日記