2011年10月29日

USBbootToolのコンフィグ

Ingenic から出ている USBBootTool は、Android 機に(も)使われている。これを使うと ファームウェアの更新はもとより、全く違うファームウェアですら書き込める。さらに、4740 や 4725B/4755 といった PMP 機にも対応している(ようだ: 未確認)。NAND Flash の PMP 機 への書き込みも興味があるのだが、まずは Android 機の設定について調べてみよう。あと、Jz4770 に対応した版もある。確認していないが、同じような設定方法のはず。

今確認できているのは、Ronzi A3 と IQQ M1 。これらの設定の違いを見てみることにする。

    注) Ronzi A3 (と Ramos V65Pro)の公式ファームウェアは、Ingenic のものを変更している。 ファームウェアのファイルが単一のファイルになっているのだが、Ingenic のツールで扱えるように 分解できる。

    Jz4770 に対応した版は、いまのところ Jz4770 開発環境の中だけにある。公式版は Jz4760 まで。

    追記: V65Pro の情報を右端に追加。これについては、最後にまとめてコメント


    機種    Ronzi A3 IQQ M1 V65Pro
    ファイル名 4760(B)_default.ini 4760_xam5001.ini 4760(B)_default.ini
    [UserSetting]
    ForceErase 1 0 0
    Format 1 0 0

    この設定を IQQ M1 と同じにすると VFAT 領域を初期化しなくても済みそう。

    [Fileset]
    MaxFileCount 7 7 8
    EndPage 7812000 7812000 7812000

    Flash のサイズが違うのに同じ設定 -- これより小さな容量のマシンは気にしなくとも良いということか。

    [File5]
    FileName=userdata.img
    StartAddr 0x19000000 0x1F400000

    [File6]
    StartAddr 0x3A400000 0x40800000

    [File7]
    StartAddr 0x40000000 0x46400000

    [File8]
    StartAddr 0

    IQQ M1 は、サイズが大きくなっている。.. のだが userdata.img の開始位置からずれてくる。このあたりは、パーティションになっているので 整合性を取れば自由にサイズを変更できそう。

    [OpenCardSetting]
    PowerPin 123 108 123
    PowerOffLevel 0 1 0
    CidPsnModel SD_MODEL FIXED SD_MODEL

    これは 非常に重要な項目。これを間違えると まずいと思われる。... のだが、関係なかったりするかも知れない。

    [PLL]
    EXTCLK 12 12 12
    CPUSPEED 114 240 114
    PHMDIV 3 3 3
    BOUDRATE 57600 115200 57600

    CPUSPEED が違うが 書き込み中だけの話なので、実は気にしなくて良い。BOUDRATE は、シリアルの設定だが、製品では関係ない。

    今、Ramos V65Pro を購入中なのだが、Ronzi A3 の設定と違うのは、 PowerPin, PowerOffLevel ぐらいのはず。これも調べておこう。

    追記: V65Pro

    ヒストリには、たとえば、mbr-xboot_r300_60bA3w_110509.bin といったファイル名が残っている。確かに V65Pro は、Jz4760B 。なのだが、設定は同じ。違うのは、File1-7 は使わずに File8 を使う点。ひょっとしたら、File8 はもともとこういうものかも知れない。

    あと、ForceErase=0, Format=0 となっていた。全部をひとつのファイルにしているから関係なさそうなものだが... 。

    さて気になる PowerPin だが、Ronzi A3 とまったく同じだった。チップを変更したが基本は同じなのか ... そもそもあまり関係ないのかはわからない。それはともかく、Ronzi A3 のツールで設定も変更せずに、分割したファームウェアが書けるようだ。これは、ちょっとうれしい。


USBBoot の仕組みとファイル名の関係

    Ingenic の CPU は、USBBoot の機能を持っているわけだが、どんなタイプのメモリが付いているかも分からない状態ではメモリを設定できずメモリにアクセスできない。

    だから、最初はキャッシュにプログラムをロードする。そのプログラムが、メモリの設定を行うことで、メモリにロードできるようになる。

    最初のものが stage1 といわれるもので、CPU名/fw.bin など の小さな bin ファイル。JZ4760/B の場合は、DDR2 なので 4760/fw_ddr2.bin を使う。

    stage1 は、メモリの設定いがいにも CPU クロックの設定や GPIO の設定を受け持っている。これらの設定は、上記の ini ファイルの値を元に fw.bin のイメージをメモリ上で書き換えることで、stage1 プログラムに渡す。

      fw.bin は SDRAM , fw_ddr2.bin は、DDR2 だが、他に DDR3 の fw_ddr3.bbin と Mobile SDRAM の fw_mddr.bin がある。

    メモリにロードするプログラムは、stage2 だが、ファイル名は、CPU名/usb_boot.bin になっている。
    これは、USB ドライバを持っていて、ROM のドライバの機能を上書きして、(USB はリセットしないで) 新たな機能を加える。ちなみに、ハードウェアに関する 2.3 のパラメータは、stage1 と共用している。(多分 PowerPin とか シリアルの設定とか)

    これと通信することで、内蔵フラッシュに書きこむのだが、SD カード と NAND Flash では書きこむ方法が違う。特に NAND Flash は OOB という 領域の管理が面倒。LinuxNandcfg.ini と MiniOSNandcfg.ini で Linux と (PMP 機で使われる) MiniOS の 2通りに対応している。... ただ、Android 機は、いまのところ全部 SD カードなので関係なく気にする必要はない。

    この stage2 は、書きこむ機能はあるが、吸いだす機能は持っていない。ドライバにもそういう機能はない。悪用を防ぐ目的だとは思うが、USBBoot で書き込めるファームウェアを最低1つ持っていないと気軽にはいじれない。ファームウェアが公開されているかどうかは、重要なポイント。

      補足: 吸いだすことは、android が立ち上がった状態で adb に接続すれば、簡単にできる。Jz47xx の android 機は(いまのところ) 最初から root で shell が動く上に busybox も入っている。

      busybox を使って、内蔵フラッシュから dd で吸い上げれば良いわけだ。system.img は read-only だから たぶん USBBoot で書き込み可能なファームウェアを再構築できる。その上 /data/app も残っているみたいだから、プレインストールアプリも吸い出せる。

      novo 7 basic もこうやって再構築できるかも知れないのだが ... 失敗すると危険なので、公式ファームウェアが出るのを待ったほうが良さそう。

    Ingenic 版について

      ftp://ftp.ingenic.cn/3sw/00tools/usb_boot/

      ここに、Ingenic オリジナルの USBBootTool がある。バージョンは、r166 (2011/06/09)。
      ソースコードも置いてあり、ちょっとだけ古い 2011/03/04 の日付になっている。

      この Windows 版 USBBootTool は、driver と UI のプログラム, そして driver インターフェイスの dll から成っている。

      ソースコードが、全てを含んでいるかどうかについては調べていないが、少なくとも UI のプログラムは入っていた。これを元にすれば、英語版も作れそうなものだが、誰も作っていないようだ。

      あと、Jz4770 への対応は、stage1,stage2 のプログラムで済む。ドライバも含めて、ツール自体は関係ない(はず)

      ftp://ftp.ingenic.cn/3sw/Jz4770/01LinuxBsp/20110729/
    • usb_boot_burn-jz4770-20111025.rar

      ... と思って一応確認したら 10/28 に更新されている。 USBBootTool の最新版が単独でダウンロードできるようになった。

      ちょっと見たのだが、上で説明したのと大分内容が違う。要注意。

        まず config の TOPの ファイル名が違う。今まで config.ini だったのが Setting.ini になっている。

        # CpuType: 0 - 25\40,1 - 4750 ,2 - 4755 ,3 - 4725B , 4 - 4760, 5 - 4760B,6 - 4770
        # SystemUsed: 0 - miniCos ,1 - Linux , 2 - Other #
        # ForceErase/Format: 1 - Used ,0 - Unused #
        # DDRType: 0-fw.bin, 1-fw_mddr.bin,2-fw_ddr1.bin,3-fw_ddr2.bin
        # (OLD: 0--SDRAM(fw.bin), 1--DDR(fw_ddr1.bin), 2--DDR2(fw_ddr2.bin), 3--MDDR(fw_mddr.bin) )
        # StorageMedia: 1-SD/MMC,0-NAND

        [UserSetting]
        CpuType=6
        SystemUsed=1
        ForceErase=0
        Format=0
        ProgrammeCount=0
        DDRType=0
        StorageMedia=1

        ここらあたりは、項目は同じ。DDRType は、stage1 のファイル名を指定するのだが、番号と ファイル名の対応が変わっている。Jz4770 は DDR2 を使うのが普通のはずだが fw_ddr2.bin はないし使っていない。

        あと CPUType について、IQQ M1 は、Jz4760B にかかわらず 4 を使っていた。分離するほどの違いはなかったはずなのだが ... 何故 5 を新設したのだろう?

        # AdvanceModel: 1 - Used ,0 - Unused #
        # FileXUsed: 0 - Filehistory-0 ,1 - Filehistory-1 ,2 - Filehistory-2#

        [FileSetting]
        AdvanceModel=1
        File1Used=0
        File2Used=0

        よく分からないのが、 AdvanceModel。[FileSetting]に関係するはずだが ...


    Novo 7 basic 版について (osakanataro 氏の記事 参照)

      USBbootTool も出ている。これの config をチェックしてみたところ、上記 Ingenic 4770 版ではなく、Ronzi A3/V65Pro 版を元にしていることが分かった。

      内容が 4770 追加になっているし、config のコメントに lynx というのが残っている。やはり 7 つのファイルを書き込むのだが、cache.bin の位置がどうも違うようだ。

      あと、mbr-xboot.bin には、microSD カードから ファームウェア をアップデートする機能があるらしい。... といっても Novo 7 だけかも知れないのだが ...
posted by すz at 12:03| Comment(2) | TrackBack(0) | Jz47xx(USBBOOT)

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)

2010年10月01日

USBBOOT で LCD制御

A-41 の LCD は、コントローラで制御する Smart LCD 接続になっていることが判った。おそらく 兄弟機である A-33 も同じだろう。

  • Smart LCD 接続では、フレームバッファを用意して、定期的に 出力するような制御は不要になり、PORT を叩くだけで制御できる。

  • そのかわり、どのようなコントローラが載っていてどのようにコマンドを発行するのかという情報が必要になる。

  • Smart LCD なら、Linux でデバッグする前に、単純なプログラムで試してみることが出来るし、そうするほうが開発効率が良い。

... というわけで USBBOOT で動かせるプログラムとして LCDを制御するようなものを作ってみようと思う。

ピンのアサインについて

    LCD が どのように GPIO に割り付けられるのか? .. これを知らないといけないわけだが、SLCD コントローラを使う前提になるから、だいたいは決まってしまうのだ。

    LCD_DAT0-LCD_DAT17 (LCD の DB0-DB15(+ DB16,DB17) に接続)
    LCD_VSYNC( = SLCD_CS) LCD の WR
    LCD_HSYNC( = SLCD_RS) LCD の RS

    LCD の CS/RD を使う場合は別途 GPIOを(PORTD から)割り当てる。
     - A-41 では RD は 固定 (read しない/できない)

    Linux のコードだと PIN_CS_N / PIN_RESET_N が GPIO として定義されることになっている。RESET も GPIO かも知れない。

    追記:
    A-41 は、
     LCD RESET = 102 PD23 LCD_SPL(LCD)
     LCD CS = 103 PD22 LCD_CLS(LCD)
    だということが分かった。


コントローラについて

    そもそもこれがなにか分からない。だから試行錯誤しなければならないのだ。

  • A-41 では、ILI9325 が有力だが、違うかも知れない。

    FPC-TLN240T36A1 というのが載っているのだが、
    "FPC-TLN240T36A5 9325"でググるとなにやら沢山ヒットする。

  • A-33 は 400x240 なので、320x240 用の LCD コントローラは使っていない。ILI9326 か?

    ちなみに、ILI9325/ILI3926 では アプリケーションノートの PDF もあり、初期化例が載っている。

解析方法について

  • CSとRESET のピンアサインだけは、探す必要がある。CS(RESET) を プルダウンしてみて、GPIO を読めばたぶん分かる。
  • まずは、解析などせず ILI9325 だと思ってプログラムを作成し動かしてみる。全然だめなら解析する。

  • 解析は、ロジアナを使うつもりだが、16ch なので、16bit + RS/WR を一度には取れない。まずは、RS/WR + 下位8bit 分。
    これで、インデックス レジスタの設定を見て どういうレジスタを使っているかを知る。

  • それであたりが付けられなかったら RS/WR + 上位 8bit も取る。同じシーケンスのはず(RD がないから状態によってシーケンスが変わらない)だから、全部が分かるはず。

  • でも、まずは 解析の練習を フォトフレーム KDPDK24 でやるつもり。
     - オリジナルのファームウェアを消す段階になったら、8bit タイプの LCD に交換するのも良いかも知れない。なぜなら 8bit 分GPIO が空く。多分 GPIO の空きは 0 だから貴重。

  • 問題は、テストピン。外れたりすると面倒だから 配線をハンダ付けしてしまうかも。配線材は、ピンヘッダ用接続ケーブル[CB-PH10P-250] を切って使うつもり。

     - PDPDK24 の 20pin の空きパターン(1.0mm ピッチ)でハンダ付けしているのだが、線が太すぎてなかなか難しい。
     - ちょっと反則だが、
      
     これを切ってテストピン代わりに付けるか。
    - 強度が足りなかった。プローブ付けたらもげてしまった。
    - やはり ハンダ付けか。

追記:

    PDPDK24の分析練習は終了。ST7785 か そのあたりのコントローラのようだ。2Ah(CASET), 2Bh(RASET), 2Ch(RAMWR) の繰り返しで画面を更新している。

    picfun: 2.4/2.8インチ QVGA液晶表示器の制御 に、ILI9325 のライブラリがある。LCD が使えるかどうかのテストならこれを参考にしたほうが分かりやすい。

バスについて



  • ILI9325 で 16bit アクセスをする場合、DB17-10 が上位 8bit / DB8-1 が下位 8 bit 。ちなみに、8bit アクセスする場合 DB17-10 に接続し、上位→下位の順で 2 回 Write する。(8bit 用のコードを参考にするのに必要な情報)

    16bit アクセスすると 決め打ちしているが、IPU が使える条件は、32bit (R8G8B8) か 16bit(R5G6B5) しかない。IPU を使わないと 動画性能が落ちるのでかならず使っているはず。


追記:実際のコードを組んでみる。

RESET と CS は判ったので、コードを組みはじめることはできそうだ。(確認したのは、A-33 だが、A-41 も同じだと思う。)

で、A-41 は ILI9325 だと思うので、これ用のコードを検討する。

まずは、ILI9325AN -- アプリケーションノートにあるコードを見てみると ... いくつかのパネルに対してのコードが別々になっていた。

コードを比較してみると ..

  • 初期化の電圧設定 と ガンマ曲線の設定のみが違う。
     - 違うパネルのデータを使っても 表示だけはできそう。
  • ついでに ILI9326AN を見てみると、ILI9325 と手順はほぼ同じものの、レジスタのアドレスと値が違う。各パネルの違いは、ILI9325 と同じような感じ。

とりあえず、アプリケーションノートにあるコードを元にして picfun のコードを参考に プログラムを作成しようと思う。


ちょっといくつか ILI93xx の コマンドリファレンスを 切りだして 画像にしてみた。描画エリアと 描画方向を指定して データを連続で送る ... ということは出来ると思うのだが .. コマンド体系は同じように見えるが、それぞれコマンド自体は違うようだ。


    picfun のコード(ILI9325)だと、
    ST7785 での、2Ah(CASET), 2Bh(RASET), 2Ch(RAMWR)がそれぞれ
  • 0x20 : Horizontal GRAM Address Set
  • 0x21 : Vertical GRAM Address Set
  • 0x22 : Write DATA to GRAM
    に相当している。
    ILI9326/ILI9328 も同じだが、ILI9327 は、2A/2B/2C で ST7785 と同じ。

これだけで描画するというのもひとつの手だ。ただのテストなのだから 凝ったことをする必要はない。

GPIO のアクセス方法

    bit 単位の set/clear 関数はマクロにあるのだが、まとめてアクセスする関数は定義されていない。どのようにアクセスするものなのかまずはチェック

    • GPIO 関係の レジスタは、3 つ 1組みになっている。Read-only の xxx というレジスタに対して、Write-only の xxxS/xxxC というレジスタがある。機能は、該当ビットを 1 にする (xxxS) / 0 にする (xxxC)。-- 以下 xxxS/xxxC については省略して説明。
       - 複数bit の 値を 1 オペレーションで設定することはできない。
    • xxx は PAyy,PByy,PCyy .. とネーミングされているが、マクロは PXyy となっていて統一的に扱う。以下の説明でも PXyy を使う。
    • PXIN -- 入力に設定されたとき ピンの値が読める。
    • PXDAT -- 出力に設定されたときの GPIO の値。
    • PXFUN / PXSEL -- この 2 つで GPIO として使う設定になる。0/0 が GPIO 。
    • PXDIR -- GPIO の方向。1 で 出力。
    • PXPE -- pull-up または pull-down (PIN によって決まっている) の設定。0 で pull-up/pull-down。

      まとめ
    • 出力設定 PXFUN = 0 , PXSEL = 0 , PXDIR = 1
    • 入力設定 PXFUN = 0 , PXSEL = 0 , PXDIR = 0
    • 出力 H: PXDAT = 1 / L PXDAT = 0


delay について

    コードには、delayms など ms 単位での待ちをいれている。これをちゃんと実装しないと、まずそうだ。
    また、GPIO のアクセスで WR のパルス幅をコントロールしないといけない。適当に作ると 早すぎて コントローラの条件を満たさないかも知れない。

      USBBOOT では 228 MHz が デフォルトになっている。1 clock 4ns ぐらい。

    Linux のコードを参考にして、__delay(loops) と mdelay(ms) を作ろうと思う。具体的には、

      #define CPUCLOCK 228000000

      static inline void __delay(unsigned int loops)
      {
      __asm__ __volatile__ (
      " .set noreorder \n"
      " .align 3 \n"
      "1: bnez %0, 1b \n"
      " subu %0, 1 \n"
      " .set reorder \n"
      : "=r" (loops)
      : "0" (loops));
      }

      static inline void mdelay(unsigned int ms)
      {
      int i;
      for (i=0; i<ms; i++) {
      __delay(CPUCLOCK / 2000);
      }
      }

    これ。__delay の 1 loop は 2clock のようだ。




A-41 の LCD に表示できた。

  • WK11: usbboot-wk11.tar.gz

  • 16bit の ILI9325 用のコードで画面クリアが出来た。
     - 青、緑、赤で 全面の塗りつぶしも OK で、予想通り R5G6B5。

  • ただし、最初は安定しなかった。時々画面が白のままになる。
     - delay を増やすことで、正しくクリアできるようにはなった。

      タイミングのチェック:
    • CS/RS/データの確定後 10ns 待って WR を ↓。
    • 50ns 以上(500ns 以下)待って WR を ↑
    • 40ns 以上待って 次のステップ。

     - 追記: ILI_93XX_Reset() が適当だったので安定しないことがわかった。
      普通に clear/set するようにしたら 安定した。

  • フォントの描画コードも入れている。
     - 表示はできているが、現状は色がおかしい。
      → バグがわかった。
     - デバッグに利用できるのももうすぐ。
      → スクロール機能が必須。
  • ILI9326 用のコードも作ったので、A-33 でもやってみたいが、予備が届くまでおあづけかな。
     - バッテリー充電機能が不明なので、いちいちバッテリーを外している。これが面倒。
     - だが使えることを確認して安心したい。

A-33 も動いた。

  • WK12: usbboot-wk12.tar.gz

    結局、制御できるかどうか安心したくて、やってみることにした。

  • ILI9326 16bit インターフェイス用のコードが動くことが確認できた。初期化のコードは、アプリケーションノートから取ってきたものだが、他は、ILI9325 用のコードをベースに レジスタ番号を ILI9326 に合わせただけ。あっさり動作した。

  • 画面サイズは、横 240 x 縦 432 だった。(A-41 は 横 240 x 縦 320)

    これで一安心。

    Linux 移植でのデバッグでも 情報を画面に 残せるようなる。これをベースにして jz_ubcomm ドライバ に組み込む予定。(正規の SLCD ドライバは別途考える)

      A-41 から I2Cを引き出して LED を点灯できるようになったのだが、ほとんど使わないうちに いらなくなりそうだ。

      Neo Slim 3000 の方は、Smart LCD 接続でなく RGB インターフェイス。こうなると、A-41/A-33 で進めたほうが 良さそうだ。

      ところで、予備を everbuying で 2 つ 買っているのだが、売り切れで 1 つしか入手できないという連絡があった。結局差額を支払って A-32 にチェンジ。A-33 とだいたい同じ兄弟機のはずだし、中を見たいし、A-33 ほど安っぽくはないし .. まぁいいか。

    これで LCD制御はひとまず完了。

    • Linux で主に使うのは、フレームバッファの転送だけなので 凝った機能を使わない。SleepMode に入る/出る という機能もあるが、アプリケーションノートそのままだし。

    • いずれ P5-5 にも 手を付けると思うが、先のはなし。

    おまけ: ILI9325 メモ

    • R03h : Entry Mode
       (9326 では、R03h = R003h)

      D15/D14 : TRI/DFM = 00 → 65K mode
      D12 : BGR = 1 → swap R and R (??)
      D5/D4 : I/D = 11 → incliment/decliment:
                      V inc , H inc
      D3 : AM = 0 → update direction = Horizontal

      例:
      ILI93XX_CtrlWrite(0x03, 0x1030);


      TRI/DFM の設定で、16bit モードでも 256K Color の書き込みができる。ただし、2 回の書き込みが必要。(8bit モードでは、3回)

      BGR=1 と下記の REV=1 で、RGB データを逆順にできる。何故か逆順が 普通に R5G6B5 としてアクセスできる設定。

      重要なのが、I/D と AM 。データを書き込む方向を指定するもので、これを使えば画面を回転させられる。

    • R61h : Gate Scan Control
       (9326 では、R61h = R401h,R6Ah = R404h)

      D2 : NDL = 0 → Non-Display Area = black ?
      D1 : VLE = 0 → virtial scrolling = disabled
      D0 : REV = 1 → change endian

      例:
      ILI93XX_CtrlWrite(0x61, 0x0003);
      ILI93XX_CtrlWrite(0x6A, vscroll_base);


      NDL は、表示外エリアが白か黒かを指定するもの。
      REV は、それぞれ R,G,B の MSB の指定。

      VLE は、縦スクロールの許可。これを使えば長辺方向にスクロールできる。Linux の本来のドライバは、フレームバッファから転送するので必要ないが、デバッグでコンソールもどきにするとき欲しい機能。

      とりあえず Pixel 描画以外に、これだけ知っているだけで十分使える。

      あとガンマ曲線の設定を理解できれば、好みの色調に出来て嬉しいかも。

      それ以外で気になるのは...

    • R2BHh : Frame Rate
       (9326 では、R20Bh だが値の意味が違う)

      デフォルト 96 Hz だが、40 Hz まで落とせる。(9326 では、デフォルト 80 Hz / 最小 30 Hz)

      ひょっとして、フレームレートを下げると液晶本体の消費電力もかなり減ったりしないのだろうか? 一度調査してみたい。

    ちょっと修正。

    • WK13: usbboot-wk13.tar.gz

      上記の RESET の操作を修正。
      あと、exec コマンドを追加。0x80600000 にロードして go する簡易コマンド。

      まだ動いていないが、MinGW で動くように気がついたらコードを修正している。
posted by すz at 23:53| Comment(0) | TrackBack(0) | Jz47xx(USBBOOT)

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)