2010年10月31日

USBBOOTプログラム用ライブラリ

Linux は Linux でやるとして、USBBOOT で動かすプログラム用の開発環境が欲しくなってきた。

いままで LCD 自体を動かすとか、そういう目的に使ってきたが、テスト目的でも割り込みを使ったり段々高度な使い方になって行きそうだ。デバッグにしてもメッセージを画面に残したいし。ライブラリ化していかないと、収集が付かなくなりそう。

  • 理想は、avr-libc のようなもの。デバイスを生で扱えるレイヤー と 少し使い易くして デバイスの違いを吸収してくれるレイヤー。それと printf みたいな共通ルーチン。

  • ある程度の規模の組み込みシステムだと newlib が良く使われているのだが、ソースだけで 16MB とか規模がすごく大きくなっている。テストやちょっとしたものを作りたいだけなので、規模が大きいのはどうかという気がする。それにデバイスを制御する部分はないので、そこは自分でつくらないといけない。

  • PIC24F や PIC32 だと MPLAB という開発環境がある。豊富な周辺装置を扱っているのでこういうのも参考にできたらと思うのだが、残念ながら PIC は良く知らないのだ。かといってこれから調べるのも大変そうだし ... ライセンス的な不安もある。

とりあえず。次の方針でライブラリーを整備していこうかと思う。

  • avr-libc を参考にして、avr-libc 風味で作る。

  • とは言え、Ingenic が用意した define や マクロがあり、これらは linux や USBBOOT でも使われている。この定義は、整理して使う。

  • 共通関数は、avr-libc でサポートしているものを avr-libc 自体や 他のライブラリから取ってくる。

さて、avr-libc 風味とはなにか?


  • どの CPU かが分かる define 。

    avr-gcc だと -mmcu=atmega88 とか CPU を指定すると __AVR_ATmega88__ が define されるようになっている。汎用の mipsel を使うのでそこまでは出来ないが、CPU の定義で 内部で使うヘッダーを切り替えるようなことはしたい。

    とりあえず __JZ4725B__ とかにしておこうと思う。メーカ名までいれると長くなりすぎ。

  • SIGNAL で定義する割り込み関数。

    avr-gcc だと SINGAL(SIG_ADC) とかで定義した関数は、レジスタをセーブリストアするプロローグ・エピローグに 変更され 割り込みベクタに直接登録される。

    mips では、割り込みベクタは共通になっているので、そういうわけにはいかないのだが、
    そこから先に 擬似ベクタテーブルを置いて、そこから割り込み要因毎に call しようかと思う。

    で、weak シンボルを使って、SIGNAL で定義した関数があれば、デフォルトと入れ替える。
    関数は、特別な関数ではなく普通の C 関数。

    実は割り込みでなにをどうセーブして... といったあたりが良く分かっていない。
    まずは、効率より動かすことが先。-- なんとかして セーブして C 関数を call するところから始めようと思う。
    で、その先で 擬似ベクタテーブルに call してもどってきたら リストアして eret 。

  • avr-libc と同じレベルの共通関数。

    なにからなにまで入れると大きくなるし作るのが面倒なので、avr-libc と同じレベルにしておく。

    いろいろ検討したのだが、newlib だと ライブラリが別のライブラリ関数を call して ... 芋づる式に 大きくなっていくようだ。どうも avr-libc の stdio を移植したほうが良さそう。

    • avr-libc では、最終的に getc/putc のレベルになるようだ。

    • 一方 string とか アセンブラになってしまっている。どうしたものかと思ったのだが、ここだけ newlib 使っても良いのかも知れない。


こんな感じでどうだろう? それに加えて CPU やマシンで使うのに便利な関数群を加える。

  • キャッシュフラッシュ関数 や c0 レジスタのアクセスマクロ

    デバイスを扱うとき メモリに書き込むのを保証しないとダメで キャッシュフラッシュ関数 が必要になる。

    大雑把な 全キャッシュフラッシュと 範囲指定のキャッシュフラッシュぐらいは用意しないと 面倒。

    c0 レジスタのアクセスマクロ としては 割り込み禁止/許可ぐらいにしようかと思うのだが、
    内部の割り込み処理を作るのに必要なものもあるので、それらもついでに 定義しておく。

  • LCD を使う putc のような共通関数。

    Ingenic の ツールとかは、シリアルつかってメッセージを出力する共通関数がある。だが PMP を使う場合 シリアルを引き出すのが難しい。

    逆に LCD は接続されているので、機種ごとに LCD の 出力モジュールを用意したらどうかと思う。

    今、出来ているのは、ILI9325 ILI9326 だけだが、いくつか GPIO の定義が必要なものの、だいたい 共通化できそうな感じ。

    一方 Neo Slim 3000 は、HSYNC/VSYNC を使ったインターフェイスになっていて、ちゃんとフレームバッファを定義した上で、LCD モジュールを 使わないと 表示できない。さらに DMA やら 定期的に転送するための 割り込み処理も必要。

    これもなんとか作って、必要なものが揃っているか確認するとともに、有用に使えるようにしておきたい -- とは思っているのだが、なかなか厳しそう。

  • USBBOOT 対応

    USBBOOT にも対応ではなく、改造版 USBBOOT に対応。

    0x80600000 から ロードして C 関数と同じく a0,a1,a2 にパラメータをセットして call する。a0,a1 は、argc , argv 。

    実行モード は、カーネルモード (Normal)/割り込み禁止でスタート。

    NAND FLASH や microSD/SD からロードすることは別途考える。(= 当面考えない)


作業は進んでいる。判ったことを少し。

  • avr-libc は、かなりアセンブラ化されている。string 系は使えない。printf の中核の vsprintf も だいぶカスタマイズされていて、float 関係の下位関数がアセンブラで使えない。

  • 使えないものは、newlib ではなくて NetBSD から取ってくることにした。vsprintf が似ているのが理由。下位関数を含めて入れ替えて、file 構造体のアクセスを avr-libc に合わせる。

  • ライセンス上あまりあちこちから取ってきたくないので、NetBSD に統一する。string も移植するし、libm も必要なら、NetBSD から持ってくる。

  • NetBSD のバージョンは、2.1 にした。古いほうが シンプルだろうというのが理由。.. ただアセンブラの記法が 合わない。量はすくないし、勉強がてら自力で変換するつもり。

ここまでが MIPS32 共通部分の話。jz47xx 固有の部分や CPU の設定は、全部作る。とはいえ jz47xx のヘッダーは Ingenic が出しているものを ベースにして整理する。

全体の規模は、共通部分 1万行 / jz のヘッダ 1 万行 ぐらい。これに 自作のコードが付くことになる。

... なかなか大変になってきたが、一度作っておくと後が楽になるはず。

ここらで一旦 スナップショット。

当面は、usbboot に同梱。ヘッダーファイルの整理中の状態も入れてあるが、入れるのはこれだけ。次は消す。

ベースにした NetBSD の一部も 置いておく。適当に移植したので分からなくなったときの参照用。

現状:

  • 共通部分はビルドできるようになった。(ただし未テスト) ディレクトリは、

     - include/, stdio/, stdlib/ -- avr-libc-1.7.0 ベース
    - string/, others/ -- NetBSD-2.1 ベース (setjmp.S だけ newlib)

  • jz47xx 固有 + MIPS32 (4Kc ?) は、ヘッダが大分できた。

    - include/jz/ -- jz_xx.h は Ingenic の カーネルヘッダベース (jz_core.h を除く)
       jz_core.h は、主に MIPS32 関係で自作
     - jz/ -- cache.c は、usbboot から取ってきて API を NetBSD 風にした。
       vector.S は、割り込みベクタテーブル avr-libc を参考に weak シンボル。
       delay_us.c delay_ms.c は、ビジーループでの遅延。CPU クロックは CPM の 設定から計算。
       出来ているのはここまで。割り込みを作りたいが全然。
     - drivers/ -- 作ったものを追加していく予定だが、全然。
       一応 ili9325/ili9326 の コードは置いてある。

  • おまけ。

      - done/ jz47xx のヘッダを モジュール毎に切り出したもので、作業用。後で消す。
      - notyet/ まだ反映済みでないもの。CIM と DBLK だけ残っている。

  • Todo
    やるべきことは山ほどあるが、やりたいことから手を付ける。

    (1) hello world 。

    まずは、リンクできてプログラムが動くことを確認する。

    最初に確認したいのは、getopt 。次に puts / fwrite / printf (浮動小数点なし)

    そのためには、puts をつくらないといけない。作るにあたって serial / usbboot / LCD を 選択できるようなことも 考えたい。

    (2) マシン情報の表示

    c0_prid や OTP の情報。CPU クロック と メモリ 量の表示。

    マシン非依存でないと困る。

    (3) 割り込み

    まずは、OS タイマー割り込みを カウントするものでも良いから。割り込み処理ができるようにする。

なんだかんだ変更したので次の版。

変更点:

  • テストプログラムをビルドできるようにした。

    tests/01_hello : ただの hello world 。

    .rel.dyn というセクションがないと怒られたので、target.ld に追加。

    あと、-msoft-float でビルドするようにした。libgcc.a をリンクすると 今度は、.eh_frame がないと怒られそれも追加。

    さらに、malloc 用の __heap_start / __heap_end も追加。一応 16MB でも動くような値にしている。
    メモリマップは、

    0x80600000 text
    data
    6MB bss
    heap
    0x80c00000 stack

    xburst_stage2 がメモリの後ろ 4MB に配置されるので、16MB のことを考えると 0x80c00000 までにしておかなければならない。
    先頭が、0x80600000 なのは、Linux の zImage と同じにしておくため。
    プログラムエリアは 6MB しかないが、前の 6MB も 基本自由に使える。

    malloc と realloc には、 __heap_end との比較のために STACK_POINTER() というマクロを使っているのだが、auto 変数 のアドレス使って適当にやっている。

  • printf を使えるように した。

    FILE *fdopen_serial(FILE *fp);
    FILE *fdopen_ubcomm(FILE *fp, void *p);
    FILE *fdopen_ili9325(FILE *fp, int reset, int cs, int backlight);
    FILE *fdopen_ili9326(FILE *fp, int reset, int cs, int backlight);

    こんな関数群を作った。fp に stdout を指定することで、printf が使えるようになるはず。
    ... だが、これだけでは stderr が出力されないし、複数のデバイスに出力することも出来ない。

    void jzcon_append(FILE *fp);
    void jzcon_setup();

    だいぶいんちきなんだが、一回 fdopen した状態で jzcon_append() するというのを 出力したいデバイス分やって jzcon_append() することで、stdout , stderr に表示できるようにした。

    この関数群を drivers に入れて jz/console.h に定義をまとめた。

    ちなみに、argc/argv は USBBOOT経由で 渡ってくるのだが、コマンドラインが そのまま argv[1] に入っている。普通に使うには、parse_args でバラさないといけない。

    いろいろ初期化が面倒だが、そうしておかないと 低レベル処理のテストがかえって面倒になるので、やむを得ないところ。

これで ようやく Todo(1) hello world の準備ができた。あとはデバッグあるのみ。

とか書きながら、テスト前にさらに変更。

変更点:

  • Todo(2) マシン情報の表示 。

    一応、マシン情報の表示と CPU クロック, メモリ量の表示を作った。

  • Todo(3)に着手。-- OS タイマー周りを Linux のコード見ながらいれてみた。

    OS タイマーは、32bit カウンタで、クロックソースを 外部クロック(12MHz), RTCクロック(32.768 kHz), ペリフェラルクロック PCLK (CPU クロックの 数分の1 (設定による)) から選べて、それを 1/N にするプリスケーラ もある。(N = 1,4,16,64,256,1024)

    自由度が高いのだが、高解像度(PCLK = 100MHz オーダー) と 低解像度 (外部クロック/4 = 3MHz) の 2 つの使い方を 標準ということにした。

    32bit しかないのでいずれオーバーフローするわけだが、100MHz で 43 秒持つ。3Mhz なら 23.8 分も持つ。

    これだけ持てば 割り込み処理がうごかなくとも ちょっとしたベンチマークをするのに使える。それは有用だが、割り込み自体のテストするのにはちょっと具合が悪い。

    割り込み自体のテスト用に 他のタイマーも調べてみたのだが、クロックソース と プリスケーラの機能はみな同じだった。WDT(ウオッチドックタイマー)も同じで 使い方が判ったので jz/wdt.h を作って avr-libc 流に使えるようにしておいた。

    さて、割り込み周り。とりあえず jz/interrupt.h を作って SIGNAL(IRQ_TCU0) とかで割り込み関数を定義するところは作った。

    あと、OS タイマーの割り込み処理も作った。実をいうと細いところはまだ理解していない。... のだが、作った割り込み処理が動くような仕様にしたいと思っている。

    ちょっと細かくなるのだが、

    SIGNAL(IRQ_TCU0)
    {
    REG_TCU_TFCR = TCU_TFCR_OSTFCL; /* ACK timer */
    current_cycle_high++;
    __intc_ack_irq(IRQ_TCU0);
    }

    これが動く仕様にする。
    __intc_mask_irq(), __intc_unmask_irq() は、関数を呼ぶ側で面倒を見るが、__intc_ack_irq() は自分でやる。

    追記: デバイス側の 割り込み要因をクリアしないと INTC の割り込み要因はクリアされないようだ。
    __intc_ack_irq() はダミーで意味をなさない。

  • Todo(3)-- タイマー周りを いれてみた。

    沢山ある 16bit タイマーのうち 1 つを (秒間 100回とか)定期的に割り込みを起こすようにする。基本は OS タイマーと同じだが、違う所もありまだ仕掛り中。

    ちょっとタイマーの機能についてメモ。

    各タイマーは、16bit で 2つの比較レジスタを持つ。ひとつは HALF MATCH。タイマーがこれ以上になると HALF MATCH 割り込みを起こす(もちろんマスクできる) 。もうひとつは FULL MATCH 。こちらは、FULL MACH 割り込みを起こすと共にタイマーを 0 にリセットする。

    で、PWM は、HALF MATCH と FULL MATCH を使って実装するわけだ。定期的に 割り込みを起こすだけなら、HALF MATCH は使わない。

    よく見たら OS Timer も 比較レジスタがあって 0xffffffff に設定されていた。こちらは時刻用だから、フリーランニングにするのが普通。

    WDT も同じく 比較レジスタは 1つ。

    ちなみに、WDT は 16bit もあるし クロックソースやプリスケーラがあって自由度が高いのだが、avr-libc スタイルの wdt.h では、RTC をソースにして、15ms 〜 128 秒の間で 使えるようにしている。

    最大128 秒と大きくしたのは、立ち上げ時とかシャットダウンのときに使えるかなと思ったため。


割り込みの勉強

まず MIPS のレジスタについて。

汎用レジスタは 32 あるが、それぞれ次のように別名が付いていて、特定の役割を持っている。

  • r0 (zero) -- 常に 0
  • r1 (at) -- アセンブラ用 (32bit値 や アドレスのロードなど)
  • r2-r3 (v0-v1) 戻り値 32bitまで : v0
  • r4-r7 (a0-a3) 関数の引数
  • r8-r15 (t0-t7) 関数内で自由に使える 
  • r15-r23 (s0-s7) 関数内で使う場合セーブしてから使う
  • r24 (t8)
  • r25 (t9) 関数の call 先 アドレスにも使用
  • r26-r27 (k0-k1) 割り込み処理でのみ使用するレジスタ。
  • r28 (gp) グローバルポインタ PIC で使用
  • r29 (sp) スタックポインタ
  • r30 (fp) フレームポインタ
  • r31 (ra) リターンアドレス
    ※ PIC はポジションインディペンドコードのこと
    ※ at をプログラムで使いたいときは、set .noat とする。

さて、TX19A のマニュアルを見ると こいつはシャドウレジスタというのを持っているが、 r0/r26/r27/r28 は 対象外。r29 も バンク 1-7 で共通だそうだ。一方 M4K(PIC32MX で採用) とか M14Kc もシャドウレジスタを持っていてこちらは 32 個全部が対象。

MIPS32 と 言っても いろいろあるようなのだが、どうも Jz47xx は、普通の MIPS32 で シャドウレジスタ は持っていないらしい。

次にどういう風に処理するか検討

  • ユーザーモードはサポートしない。OS を作りたいわけではなく、マイクロコントローラのように使いたいだけなのだから。
  • そうすると、正常系の例外・割り込みは 装置からの割り込み (と MIPS のソフトウェア割り込み) のみ。.. ということになるはず。
  • 普通にスタックにレジスタをセーブして、割り込み処理の C 関数を call すれば良さそう。なにをセーブすべきかというと zero, s0-s7, k0-k1 以外 と c0_status , c0_epc(割り込み発生アドレス)。
  • 戻ってきたら レジスタ と c0_status (の IE)をリストアして (セーブした)c0_epc に jump 。
    あとは全部 C 関数に任せる。

    (メモ)
  • ERET は、ユーザモードに戻る命令らしく使わない。
  • セーブ・リストアの処理をするのに k0,k1 が使える。

    割り込みの許可と stack ポインタの復帰を atomic にやりたいのだが、どうするのだろう?
    割り込みを許可したら その時点で 割り込みが入る可能性がある。stack を元に戻していないと stack がどんどん大きくなる可能性がある。

    stack を開放してから k0 に入れておいた c0_status の値を mtc0 。で、k1 に入れておいた 戻りアドレスに jump ?
    -- そうなると mfc0 で割り込み可にした瞬間に 割り込みが入る可能性がある。で、k1 が壊されてしまう。k1 をセーブしても同じで最後に k1 を使わざるを得ない。困った。
    -- ひょっとして アドレスの範囲を見て return しようとしていたら、epc ではなく、k1 に jump ?

割り込み処理の C 関数では、(とりあえず)簡単にするため 割り込み禁止のまま 実行することにする。

とりあえずこの方針で。

実をいうと全然動いていない。どうもコンパイル環境も関係あるようなので、自分でビルドした gcc(4.2.4) で動かす方針で、検討する。 (MinGW環境でmipsel-gccをビルド の記事参照)

    にある dingux_toolchain_20091022.tar.bz2 を linux や xburst_stage1/2 をビルドするのに使っているのだが、このライブラリは MinGW+MSYS 環境 でも使えるようにしたいと思っている。で、自分でビルドしているわけだが、gcc-4.3.4 を使いたいところ 古い MinGW の環境のため make の関係で gcc-4.2.4 までしかダメ。別に gcc が、古くても大丈夫だろうとタカを括っていたのだが、なにか変。-fno-pic を指定しても、一部 PIC 特有の gp を使ったコードになっている。
    とにかく、OpenWRT は動いているのだから そのパッチを使ったら良いだろうということで、試してみている。

全然関係なかった。


    void msg_init(void *p)
    {
    comm_area = p;
    }

    というコードに注目しているのだが、
  • 自分でビルドした gcc の場合
    -fno-pic などを付けようがなにをしようが、

    msg_init:
    j $31
    sw $4,%gp_rel(comm_area)($28)

    というコードになってしまう。gp($28) に正しい値が設定されていないと誤動作する。

  • dingux_toolchain の動作

    -fno-pic では、
    msg_init:
    lui $28,%hi(__gnu_local_gp)
    addiu $28,$28,%lo(__gnu_local_gp)
    lw $2,%got(comm_area)($28)
    j $31
    sw $4,0($2)
    -fno-pic -mno-abicalls では、
    msg_init:
    lui $2,%hi(comm_area)
    j $31
    sw $4,%lo(comm_area)($2)
    -fpic では、
    lw $2,%got(comm_area)($28)
    j $31
    sw $4,0($2)

    という風にオプションに対してコードが変わる。-fno-pic では、gp($28) に値が設定されていることを期待していない。

  • 対策
    外部から呼び出される場合(startup を含む)、gp の設定をちゃんとする。外部を呼び出す場合 gp をセーブして call し戻ってきたら gp をリストアする。

    こんな感じか。

やはりよく判らない。ちょっと実験。


    head.S:
    .text
    .extern c_main
    .globl _start
    .ent _start
    .type _start, @function
    .set noreorder
    _start:
    la $gp, __gnu_local_gp
    j c_main
    nop
    .set reorder
    .end _start
    main.c:

    extern void msg_init();
    extern void msg_proc();

    void c_main(int argc, char *argv[]) {
    int i;

    msg_init((void *)argv);

    for (;;) {
    msg_proc();
    }
    }

    msg.c:

    struct comm_struct {
    void (*proc)();
    };

    struct comm_struct *comm_area;

    static inline unsigned long read_gp() {
    unsigned int c = 0;
    __asm__ __volatile__(" \
    .set noreorder; \
    move %0,$gp; \
    .set reorder" : "=r" (c): );
    return c;
    }

    static inline void write_gp(unsigned long c) {
    __asm__ __volatile__("\
    .set noreorder; \
    move $gp,%0; \
    .set reorder" :: "r" (c));
    }

    void msg_init(void *p)
    {
    comm_area = p;
    }

    void msg_proc () {
    unsigned int saved_gp;
    saved_gp = read_gp();
    if (comm_area && comm_area->proc)
    (comm_area->proc)();
    write_gp(saved_gp);
    }

    上記の head.S main.c msg.c から成るプログラムを 自分ビルドの mipsel-gcc (ver 4.2.4) で作成。共通オプションは、

    -O2 -fno-zero-initialized-in-bss -mips32

    リンクは、binutils-2.19.1 で

       mipsel-ld -nostdlib -EL -T target.ld head.o main.o msg.o -o hello.elf


    で 共通オプションのみ, -fpic を追加, -fpie を追加で比べてみたが同じものが出来た。

    80600000 _start:
    80600000: 3c1c8060 lui gp,0x8060
    80600004: 279c00a0 addiu gp,gp,160
    80600008: 08180004 j 80600010 c_main
    8060000c: 00000000 nop

    80600010 c_main:
    80600010: 27bdffe8 addiu sp,sp,-24
    80600014: afbf0010 sw ra,16(sp)
    80600018: 0c18000e jal 80600038 msg_init
    8060001c: 00a02021 move a0,a1
    80600020: 0c180010 jal 80600040 msg_proc
    80600024: 00000000 nop
    80600028: 0c180010 jal 80600040 msg_proc
    8060002c: 00000000 nop
    80600030: 08180008 j 80600020 c_main+0x10
    80600034: 00000000 nop

    80600038 msg_init:
    80600038: 03e00008 jr ra
    8060003c: af84ffe4 sw a0,-28(gp)

    80600040 msg_proc:
    80600040: 27bdffe8 addiu sp,sp,-24
    80600044: afbf0014 sw ra,20(sp)
    80600048: afb00010 sw s0,16(sp)
    8060004c: 03808021 move s0,gp
    80600050: 8f82ffe4 lw v0,-28(gp)
    80600054: 10400006 beqz v0,80600070 msg_proc+0x30
    80600058: 00000000 nop
    8060005c: 8c420000 lw v0,0(v0)
    80600060: 10400003 beqz v0,80600070 msg_proc+0x30
    80600064: 00000000 nop
    80600068: 0040f809 jalr v0
    8060006c: 00000000 nop
    80600070: 0200e021 move gp,s0
    80600074: 8fbf0014 lw ra,20(sp)
    80600078: 8fb00010 lw s0,16(sp)
    8060007c: 03e00008 jr ra
    80600080: 27bd0018 addiu sp,sp,24

    どうしても いきなり gp を使うと説明したものの具体例。

    -G 0 を 付けてみたら、-fpic とか -fpie に関係なく次のコードになった。

    80600000 _start:
    80600000: 3c1c8060 lui gp,0x8060
    80600004: 279c00a8 addiu gp,gp,168
    80600008: 08180004 j 80600010 c_main
    8060000c: 00000000 nop

    80600010 c_main:
    80600010: 27bdffe8 addiu sp,sp,-24
    80600014: afbf0010 sw ra,16(sp)
    80600018: 0c18000e jal 80600038 msg_init
    8060001c: 00a02021 move a0,a1
    80600020: 0c180011 jal 80600044 msg_proc
    80600024: 00000000 nop
    80600028: 0c180011 jal 80600044 msg_proc
    8060002c: 00000000 nop
    80600030: 08180008 j 80600020 c_main+0x10
    80600034: 00000000 nop

    80600038 msg_init:
    80600038: 3c028060 lui v0,0x8060
    8060003c: 03e00008 jr ra
    80600040: ac44008c sw a0,140(v0)

    80600044 msg_proc:
    80600044: 27bdffe8 addiu sp,sp,-24
    80600048: afbf0014 sw ra,20(sp)
    8060004c: afb00010 sw s0,16(sp)
    80600050: 03808021 move s0,gp
    80600054: 3c028060 lui v0,0x8060
    80600058: 8c42008c lw v0,140(v0)
    8060005c: 10400006 beqz v0,80600078 msg_proc+0x34
    80600060: 00000000 nop
    80600064: 8c420000 lw v0,0(v0)
    80600068: 10400003 beqz v0,80600078 msg_proc+0x34
    8060006c: 00000000 nop
    80600070: 0040f809 jalr v0
    80600074: 00000000 nop
    80600078: 0200e021 move gp,s0
    8060007c: 8fbf0014 lw ra,20(sp)
    80600080: 8fb00010 lw s0,16(sp)
    80600084: 03e00008 jr ra
    80600088: 27bd0018 addiu sp,sp,24


    どうも -G 0 は、いわゆる static に 近いようだ。で、-G 0 を付けないと gp が正しいことを常に期待するコードになっている。

    ... となると xburst_stage1 は -G 0 付きが良さそう。xburst_stage2 は、リロケータブルだから、-G 0 なしにしなくてはならない。で、ロードして実行するプログラムは、基本どちらでも良いが、libgcc が -G 0 なしだと思うので -G 0 なしに統一しておいたほうが良さそう。

    ちなみに xburst_stage2 は、アドレステーブルを自分で変換している。仕様をあわせないとたぶん動かない。

    ところで、-fno-zero-initialized-in-bss は、まともな start-up を持っていないので初期化をさぼるために入れている。いずれ外さないといけないと思っている。

問題がひとつ判った。jalr v0 となっている所。xburst_stage2 のコードは、飛び先アドレスを t9 に入れておかないといけない。理由は、t9 相対で gp を設定しているため。 逆に gp は壊してしまうので、-G 0 を付けない場合は、call 前後でセーブ・リストアが必要。

ここを直したら、プログラムが動くようになった。ようやくデバッグが出来る。

デバッグが少し進んで printf が使えるようになった。一旦スナップショット。


変更点:

  • printf , getopt が動くのを確認。

    いろいろバグはあった。修正した中で重大かも知れない仕様変更をした。

    変更したのは、stdin/stdout/stderr 。ポインタではなく、実体のアドレスに変更。
    - 要するに代入できない。まだ malloc を動かしたくないのが理由。

    usbboot のプログラムのほうにもバグがあったので修正。

    たぶん malloc や memcpy/memset は未確認。memcpy/memset はアセンブラで結構複雑。
    - 条件によって動くコードが変わるのだ。

  • Todo(2) マシン情報の表示 。

    一応、マシン情報の表示と CPU クロック, メモリ量の表示が出来た。

    Jz4725: c0_prid 0x0ad0024f
    Jz4725B: c0_prid 0x1ed0024f
    (Jz4750 : c0_prid 0x1ed0024f )
    Jz4755: c0_prid 0x2ed0024f


    CPU クロック, メモリ量 は、Jz4725/Jz4725B で正しく取得できている。
    ただ OTP は全然。Jz4750/Jz4755 系でないとダメなのかも。

    c0_prid は、何だろう? 8bit づつに分けて 4 つのフィールドになっているらしい。
    - ベンダー 0xd0 core 0x02 rev 0x4f で、最上位の 1 バイトはベンダー定義。

    Jz4725B は Jz4755 と違って core の拡張はないようだから これで合っているようだ。ベンダー定義の部分は、 00001010 と 00011110 の違いか。

  • xburst_stage1 が 動くのを確認。

    自分ビルド gcc-4.2.4 を使ってのビルドで動作が確認できた。
    オプションは、-G 0 。

    xburst_stage2 はまだ動かせていない。

    Todo
    (3) 割り込み (つづき)

    いま作ってある ostimer と timer を動かす。

    (4) timer の 実用化。

    callback を登録できるようにする。-- なにやら OS っぽくなってきたが、次のステップで必要なのだ。

    (5) ベンチマーク

    時間が分かるようになったならベンチマークをしてみたい。MXU あり/なしの jpeg-6b とか をやってみたい。

    (6) generic-LCD での表示。

    要するに Neo Slim 3000 で LCD を使えるようにしたいのだ。ここまで出来れば終了の予定。

バージョンアップした。

変更点:

  • Jz4755 (T555) での動作確認と修正

    jzlib 自体は 変更なし。マシン情報の表示はできたが、予想に反して OTP は見えない。

posted by すz at 14:12| Comment(0) | TrackBack(0) | Jz47xx(USBBOOT)