2010年08月31日

USBBOOT で hello world

オリジナルの usbboot は、いろんな機能が足りない。このカテゴリでは、usbboot の改造とかについて書いていこうと思う。

目的は、Linux カーネルの直接ブート と (カーネルにドライバを入れることでの)カーネルのメッセージの取得。

そのための機能は、一応仕込んだ。これからテストしてみようと思う。

プログラム:


SDRAM へのプログラム(ファイル) のロード:

    オリジナルは、1 回の転送サイズ (MAX_LOAD_SIZE) を 12KB に制限していて とても遅いので、とりあえず 256KB に変更。

    サンプルとして DINGUX の dual_boot インストーラの zImage を ロードしてみた。

    ちゃんと ロードできたかどうかは、SDRAM のメモリをダンプする mread というコマンドを作成し xburst_stage2 にもプリミティブを作ってテストした。ちなみに ファイルに書き込む mdump というコマンドも作る予定だが、まだ作っていない。


    usbboot :> load
    Usage: load (1) (2) (3)
    1:SDRAM start address
    2:image file name
    3:device index number
    usbboot :> load 0x80600000 zImage.bin 0
    start:::::: 0x80600000
    Total size to send in byte is :3256320
    Loading data to SDRAM :
    CPU data: Boot4750
    Load last address at 0x80600000
    0x80640000
    CPU data: Boot4750
    Load last address at 0x80640000
    CPU data: Boot4750
    Load last address at 0x80680000
    CPU data: Boot4750
    Load last address at 0x806c0000
    :
    CPU data: Boot4750
    Load last address at 0x80900000
    usbboot :> mread
    Usage: mread (1) (2)
    1:start address
    2:length in byte
    usbboot :> mread 0x80600000 256
    CPU data: Boot4750
    Reading from 0x80600000 length 0x100....

    0x80600000 : 21 80 80 00 21 88 a0 00 21 90 c0 00 21 98 e0 00
    0x80600010 : 92 80 04 3c 00 b0 84 24 92 80 05 3c 40 54 a5 24
    0x80600020 : 00 00 80 ac fe ff a4 14 04 00 84 24 92 80 1d 3c
    0x80600030 : 40 54 bd 27 60 80 04 3c 20 40 84 24 31 00 05 3c
    0x80600040 : 20 6f a5 34 01 80 06 3c 60 80 1f 3c 60 00 ff 27
    0x80600050 : 60 80 1a 3c 98 19 5a 27 08 00 40 03 00 00 00 00
    0x80600060 : 21 20 00 02 21 28 20 02 21 30 40 02 21 38 60 02
    0x80600070 : 18 80 1a 3c 10 0b 5a 37 08 00 40 03 00 00 00 00
    0x80600080 : 08 00 00 10 60 80 08 3c 9c 00 08 25 00 a0 09 3c
    0x80600090 : 25 40 09 01 08 00 00 01 00 00 00 00 00 80 1a 3c
    0x806000a0 : 00 80 1b 3c 00 40 7b 37 80 ff 7b 27 00 00 41 bf
    0x806000b0 : 20 00 41 bf 40 00 41 bf 60 00 41 bf 00 00 40 bf
    0x806000c0 : 20 00 40 bf 40 00 40 bf 60 00 40 bf f7 ff 5b 17
    0x806000d0 : 80 00 5a 27 60 80 08 3c e4 00 08 25 08 00 00 01
    0x806000e0 : 00 00 00 00 08 00 e0 03 00 00 00 00 00 00 00 00
    0x806000f0 : 08 00 e0 03 00 00 00 00 08 00 e0 03 00 00 00 00
    usbboot :>


ロードしたプログラムの実行:

    オリジナルでも go コマンドがある。これは引数なしで、アドレスを call する。
    Linux の boot を考えると 引数 と コマンドラインが必要。不定値では都合が悪い。go コマンドを改造し コマンドラインの設定と 引数として渡すようにした。

      詳しく書いておくと コマンドラインを含む通信エリアというのを定義していて、通信エリアの先頭が argv になるようにしている。
      プログラムが 対応していれば、USBBOOT のプロトコルを使って メッセージを返せるようにしている。

    これをテストするプログラムを xburst_stage1/2 を参考に作ることにした。

    target.ld:
    これはプログラムの アドレスを決めるもの ... と思って良い。
    stage1/2 の違いをみると MEMORY のところだけなので、開始アドレスを 0x80600000 にした。サイズは適当で良さそうだが、最低 16MB の RAM があり後ろ 4MB に stage2 があるとして 6MB にした。

    UTPUT_ARCH(mips)
    ENTRY(_start)
    MEMORY
    {
    ram : ORIGIN = 0x80600000 , LENGTH = 6M
    }
    (後略)


    head.S:
    これはスタートアップを定義するもの。c_main に jump するのだが、c_main が return すれば、USBBOOT に制御が戻るはず。とりあえずは、stack を設定しないバージョン。

    .globl _start
    .set noreorder
    _start:
    #if 0
    /*
    * setup stack, jump to C code
    */
    la $29, 0x80bffff0 /* sp */
    #endif
    j c_main
    nop


    ビルド:

    xburst_stage1/2 の Makefile を変更しただけ。こんな風にビルドしている。

    ipsel-linux-gcc -O2 -fno-unit-at-a-time -fno-zero-initialized-in-bss -mips32 -fno-pic -mno-abicalls -c main.c -o main.o
    mipsel-linux-ld -nostdlib -EL -T target.ld head.o main.o msg.o -o xburst_hello.elf
    mipsel-linux-objcopy -O binary xburst_hello.elf xburst_hello.bin
    mipsel-linux-objdump -D xburst_hello.elf < xburst_hello.dump
    mipsel-linux-objdump -h xburst_hello.elf < xburst_hello.map
    mipsel-linux-nm -n xburst_hello.elf < System.map


    確認:

    objdump を使って xburst_hello.elf を見てみた。

    mipsel-linux-objdump -d xburst_hello.elf

    Disassembly of section .text:

    80600000 <_start>:
    80600000: 04 00 18 08 00 00 00 00 00 00 00 00 00 00 00 00 ............
    ....

    80600010 <c_main>:
    80600010: 0818003f j 806000fc
    80600014: 00a02021 move a0,a1
    ...

    806000fc <msg_init>:
    806000fc: 3c028060 lui v0,0x8060
    80600100: 03e00008 jr ra
    80600104: ac4401a0 sw a0,416(v0)


    ちゃんとロードアドレスが、0x80600000 になっていて、その先頭が entry になっている。

    _start が ディスアセンブルされていないが、

    .type _start, @function
    :
    .end _start

    を入れることで ディスアセンブルされるようになる。

    それはともかく、これが動くかというと ... 制御が渡った後に戻ってこない。


    usbboot :> go
    Usage: go (1) (2) (3)
    1:start SDRAM address
    2:device index number
    3:command_line
    usbboot :> go 0x80600000 0
    CPU data: Boot4750
    Load last address at 0x81d08130
    Executing No.0 device at address 0x80600000
    usbboot :>
    usbboot :> boot
    Error - can't retrieve XBurst CPU information: -19


    ほとんどなにもしていないのにもかかわらず戻ってこない。.. メモリに書き込んでいるぐらい。

    次のステップとして

    • メッセージを追加しての USBBOOT への return 。
    • ポーリングすることで、メッセージを出力。

    を考えているのだが ...

    原因を調べて直さないと次のステップに行けない。

    それはともかく ... こういう風に USBBOOT を使って任意のプログラムを動かせるようになれば、ちょっと大規模な マイクロコントローラのような使い方もできるようになる。ライブラリがないのがつらいところだが、Cortex-M3 とか SH-2A とかのボードを使う感覚でプログラムを作ってみるのも、面白いかもしれない。
    なにしろ、LCD や サウンド、microSD(or SD) 、バッテリーが最初から付いているのだ。メモリも最低クラスが 16MB -- 十分ではないか。

    もうひとつ嬉しいのは、ボタンとか LCD などデバイスの使い方をさがすのに USBBOOT の拡張に頼らなくて済むという点。プログラムを書いて動かしてみれば良い。ちゃんと return してくるなら reboot もしなくて済む。

    このあたりの目的には ロード + 実行のコマンドを作って使いやすくしておきたいところ。検討しておこう。

プログラム:


    動かない原因を調べるために。stage2 で 実際に実行しているところを コメント化して 実行直前の データを取ることにした 。

    ところがこれも動かない。いくつかの処理を call しているので どれが関係するのか調べていったところ ...


    jz_writeb(USB_REG_POWER,0x0); // High speed

    このコードを入れると動かなくなることが分かった。

    UDC (USB Device Controller) の Power Manegement レジスタだそうだが、何をしようとしているか不明。

    したいことは、UDC の状態を変更せず 戻れることだから、不要そうではある。


    Execute program at 0x80600000
    Command line: aaa bbb ccc ddd eee fff


    さて、stage2 での "go" 直前の状態。-- アドレスと Command line の設定はうまく行っている。

    これで 実際に実行するように変更する。そして stage2 内に仕込んだ hello() を実行してみる。

    32MB だと stage2 の先頭アドレスは 0x81c00000 になる。hello のアドレスを mipsel-linux-nm で調べてみると 0x80001610 。


    go 0x81c01610 0

    とすると

    Execute program at 0x81C01610
    Command line:
    hello world

    なんて表示された。今度は 実際のプログラム... の前に バイナリの作成について ..

    stage1 は、作成時に決めた固定のアドレスにロードするが、stage2 はリロケータブル。リンクのオプションは同じだが、target.ld が違う。

    stage1:
    _gp = ABSOLUTE(.);
    stage2:
    _gp = ALIGN(16);
    _got_end = ABSOLUTE(.);

    の違いがある。で、stage2 では、c_main でなにかおまじないをしている。
    リロケータブルなバイナリは作ってみたいとは思うが最初はパスして、stage1 流で行く。


    usbboot :> msg
    usbboot :> load 0x80600000 hello.bin
    usbboot :> go 0x80600000 0

    Execute program at 0x80600000
    Command line:
    comm_area addr:0x81D070E0
    proc_func addr:0x30783009
    XXXXXXX hello world XXXXXXX
    argc = 0x00000002
    argv[1] :



    さて、hello.bin を動かしていて、気がついたことがある。
    ロードしなおしても前のプログラムが動いてしまうのだ。

    原因は、icache の flush 忘れ。もともと 2 回以上実行することを想定していないのかも知れない。

    とりあえず。usb_main の先頭と同様に __icache_invalidate_all() を入れることで 解決した。

    次の課題は、hello.bin からのポーリング。果たして動くのか ...
    というと動かなかった。

    リロケータブルなバイナリの call はみな t9 にアドレスを入れてそこに call している。


    806000a0 <msg_proc>:
    806000a0: 3c028060 lui v0,0x8060
    806000a4: 8c420290 lw v0,656(v0)
    806000a8: 10400006 beqz v0,806000c4 <msg_proc+0x24>
    806000ac: 00000000 nop
    806000b0: 8c590010 lw t9,16(v0)
    806000b4: 13200003 beqz t9,806000c4 <msg_proc+0x24>
    806000b8: 00000000 nop
    806000bc: 03200008 jr t9
    806000c0: 00000000 nop
    806000c4: 03e00008 jr ra
    806000c8: 00000000 nop


    この msg_proc は、関数ポインタとして 登録されている udc4740Proc() を call するもの。

    関数ポインタを使った call は t9 を使って call することはクリアしている。

    うーん何が悪いのだろうか?


    Execute program at 0x80600000
    Command line:
    comm_area addr:0x81D070F0
    proc_func addr:0x47200A0D
    XXXXXXX hello world XXXXXXX
    argc = 0x00000002
    argv :0x81D070F0
    proc_func addr :0x47200A0D

    関数ポインタの値を表示したところ、答えが出ていた。不定値が入っている。よくよく見れば。関数ポインタに代入していなかった。

    これで、hello.bin の中から 10 回 udcProc を call して return しても正しく動くようになった。

これで hello world は、一旦の完成だが、 Jz47xx で動かした プログラムについてのメモを載せる予定。

一応 Linux を移植しようと思っているので、この状態で実用的なものを 自作しようとはお持っていない。基本的に テストプログラムを走らせるつもり。

ただテストといっても いろいろ考えられる。装置の解析が第一としても、XMU を使ったプログラムを動かして動作を確認するとか、オーバクロックしてどこまで動くか調べるとか 。

プログラム:

  • WK7: usbboot-wk7.tar.gz

    テストプログラムの追加。tests に 01_hello / 02_backlight / 03_portout を作成した。

    01_hello は今まで作ってきた hello world 。
    02_backlight は、PWM のポート に対して 出力に設定するもの H と L が出力できる。( パラメータで指定 )
    03_portout は、02_backlight を汎用化したもの。PA0 〜 PD31 までの範囲の ポートに対して 出力 (H/L) と 入力(Z) を設定できる。

  • WK8: usbboot-wk8.tar.gz

    ECC の仕組みが多少分かったので、cfg を修正。あと、32M/64M の改造をしたのでそれ用の cfg も追加。

    周波数を確認するために、tests/04_hz を追加。portout ベース。
    228MHz で動作している場合、port を 1Hz の周期でブリンクする(はず)。

  • WK10: usbboot-wk10.tar.gz

    tests/05_memtest/ -- メモリテストのプログラムを入れた。

      メモリがちゃんと読み書きできるかのテスト。0x8001000 から stage2 まで (END - 4MB) の領域のうち、プログラム用のエリア (0x80600000 - 6MB) を除いた部分をテストする 。

    • SDRAM のデータを ファイルに書き込む mdump を作った。


  • WK11: usbboot-wk11.tar.gz

    A-41 の LCD にアクセスするプログラムをいれた.
posted by すz at 21:51| Comment(0) | TrackBack(0) | Jz47xx(USBBOOT)
この記事へのコメント
コメントを書く
お名前: [必須入力]

メールアドレス: [必須入力]

ホームページアドレス: [必須入力]

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/40465166
※ブログオーナーが承認したトラックバックのみ表示されます。

この記事へのトラックバック