2012年07月29日

jzlibの使い方

まだ完成はしていないのだが、説明を書き始めた。ソースコードは最後にリンクを提示

jzlib とは

    PIC32MX 用の libc ライブラリで、AVR-libc のように使えることを目指したもの。もともと 中華PMP などに使われていた JZ47xx 用のライブラリとして作っていたもので放置していたのだが、PIC32MX 用に作り直すことにした。名前は、もともと jzlib だったので継承している。

      現状はまだ未完成。特に割り込みが動いていないのが致命的。... ではあるが、まぁなんとかなるだろう。

    基本的にこのライブラリは、AVR-libc である。実際に AVR-libc のソースコードを元にしている。AVR-libc には、アセンブラで書かれたものがかなりある。その部分は、NetBSD のソースを借用して なんとか した(つもり)。基本的には、AVR-libc なのだが、デバイス系の ライブラリは ほとんどない。あるのは、デバイスのレジスタの定義 と 割り込みサポート と タイマー 程度。要するに余計なものと思われるものは、一切ない。ただし、割り込みだけは別。MIPS はすごくめんどくさいのだ。それだけは、AVR-libc のように簡単に使えるものを目指して提供している。

    タイマーというのは、OS では基本的な機能。時刻管理用の ostimer と 定期的に callback を呼び出す timer の 2 つの機能を提供することにした。 これらは、割り込みと違って使わないという選択もできる。

    あとは、core 用の シンプルなライブラリとマクロ。割り込み禁止・許可をする cli()/sei() , irq_disable_irq()/irq_restore() とか。ディレイ関係。関数版 は基本 mips16e 用。mips16e では、使える命令に制限があるし、ディレイも定義したクロック数(1ループ 2CLK) にならない。

対応チップ

    対応予定のチップ
     110 120 130 150 210 220 230 250 の B/C/D
     (320 340 360 ?) 420 440 の H/L
    確認可能なチップ (所持しているもの)
    220 B/D , 250 B , 440 H

    実際のところ、スタックのアドレス以外は同じもの。3xx/4xx は、ostimer/timer が変わるが、シンボル名を変えることで対応している。( 後は 1xx/2xx と同じ )

    5xx/6xx/7xx は、データシート未読のため、対応を考えていない。

インストール方法

    MIPS の gcc は、Pinguino の環境を借用できるようにした。Pinguino は、Windows/Linux/Mac 用の環境があるので、あとは shell とか sed とかがあれば良い。

      借用しているのは、gcc の コンパイラのみ。なので、mips-elf-gcc を自分でビルドしても良い。
      Windows では、MSYS を使っている。

     ・ msys-1.0.10
     ・ Pinguino downloads

    このあたりがインストールされていることを前提にして、インストール方法を説明する。

    Pinguino をインストールすると たとえば /c/PinguinoX.3/win32/p32/bin/ に mips-gcc がある。例えば Windows だと 次のようにする。

    $ export PATH=$PATH:/c/PinguinoX.3/win32/p32/bin
    $ export CROSS_COMPILE=mips-
    $ sh install.sh

    ( gcc の prefix は Windows では mips- 、Linux や Mac , 野良ビルドならば、mips-elf- )
    ( 注意 : Pinguino は最後に追加すること。make が含まれていて、ちょっと具合が悪い )

    install.sh では、${CROSS_COMPILE}gcc があるディレクトリを基点にして、../jzlib に 環境を インストール し、p32mx-gcc という コマンド(シェル) を インストールする。

    (付録)インストールされるファイル一覧

    jzlib/lib/2xx/
    crt2xx.o libc.a

    jzlib/lib/4xx/
    crt4xx.o libc.a

    jzlib/lib/ldscripts/
    p32mxb12k.x p32mxb3k.b p32mxb3k.bu p32mxb3k.x

    jzlib/include/
    alloca.h ctype.h inttypes.h math.h setjmp.h stdio.h string.h
    assert.h errno.h limits.h p32mx stdint.h stdlib.h

    jzlib/include/p32mx/
    interrupt.h p32mx_capt.h p32mx_i2c.h p32mx_spi.h p32mx_wdt.h
    ostimer.h p32mx_clkc.h p32mx_intc.h p32mx_sys.h timer.h
    p32mx_2xx_regs.h p32mx_core.h p32mx_nvm.h p32mx_timer.h wdt.h
    p32mx_4xx_regs.h p32mx_devcfg.h p32mx_port.h p32mx_uart.h
    p32mx_adc.h p32mx_dmac.h p32mx_rtc.h p32mx_usbotg.h

PIC32MX 用 の プログラム の ビルド方法。

    #include <p32mx/p32mx_core.h>
    #include <p32mx/p32mx_port.h>

    // Olimex PIC32-PINGUINO-MX220
    // LED1 (Green) D13 = RB15 Active H (1: Light on)
    // LED2 (Red ) D9 = RA10 Active H (1: Light on)
    // BUT D8 = RB7 Active L (0 : Pressed)

    int main() {
    TRIS_CLR(1) = 1 << 15;
    TRIS_CLR(0) = 1 << 10;
    LAT_CLR(1) = 1 << 15;
    LAT_SET(0) = 1 << 10;
    for (;;) {
    delay_us(500000);
    LAT_INV(1) = 1 << 15;
    }
    }

    これは、Lチカのプログラム例だが、これを test.c という名前で作ったとする。

    $ p32mx-gcc -mmcu=p32mx220-bu -O2 test.c -lc
    $ mips-objcopy -O ihex a.out test.hex

    これで、AN1388 ブートローダ用 HEX ファイルが出来上がる。

    mips16e 用のコードの場合は、次のようにする。

    $ p32mx-gcc -mmcu=p32mx220-bu -mips16 -O2 test.c -lc
    $ mips-objcopy -O ihex a.out test.hex

     (注意:野良ビルドの mips-elf-gcc だと objcopy がエラーになる。i386 用なら問題ないので、そちらを使う。)

    -mmcu= で指定する MCU は、p32m[1234][2345]0 。f032b と続けても良いがなにも変わらない。 例外は、320f064/320f128 。これだけは ここまで指定する必要がある。

      実際のところ、crtXX.o (スタートアップファイル) も libc.a も 1xx/2xx では、共通。違いはなにかというと
       define : -D__32MX1XX__ , -D__32MX2XX__ , など
       stack 位置 : -Wl,--defsym,__stack=0xa0002000 など。

      RAM サイズは、x10 が 4K , x20 が 8K , x30 が 16K , x40 と x50 が 32K としている。320 だけはこのルールに会わないので、f064/f128 まで指定する必要がある。

      3xx/4xx は、crtXXX.o libc.a を別にしたのだが、今は同じもの。ただし、今後は変わる可能性がある。
      タイマーなどは、シンボル名(の プレフィックス)を変えて対応したので、両方 libc.a に含まれている。あと、リンカスクリプトは別になる。違いは Config が置かれるアドレス値のみ。

    -mmcu= のサフィックスの -bu は、ブートローダ用の リンカスクリプトの指定

      program addr 1xx/2xx 3xx/4xx
      -bu 0x9d004000 p32mxb3k.bu p32mxb12k.bu
      -b 0x9d001000 p32mxb3k.b p32mxb12k.b
      (-x) 0x9d000000 p32mxb3k.x p32mxb12k.x

    一般的には、最初の 4KB には ベクタが置かれるらしく、スタートアドレスは +0x1000 。jzlib では、シングルベクタモードを使うので、ベクタ領域は 1KB 。スタートアップは、ベクタ領域の先頭にしていて オフセットは 0 。というわけで、4KB ずらして 作れば対応できる。(-b を指定する)

    220 用の AN1388 ブートローダは、ブート領域に入りきらず ユーザ領域の先頭 12KB を占有している。ベクタ領域とあわせて +0x4000 (16KB !) ずらすことで対応する。(-bu を指定する)

    コード領域を節約したければ、先頭からにする。一応 ブート領域の 先頭のリセットアドレスには、スタートアップのアドレスへの JMP 命令を置いたので、ブート領域が書き込み可能であれば、動くはず。(書き込み不可なら -b を指定する)

    -lc をつけると、リンカのための オプションが付く。

    付録) AN1388 ブートローダでの書き込み。

      $ pic32prog -S test.hex

      pic32prog r55 以降を使うと書き込みができる。ただし べりファイがうまくないので、-S が必要。
       ・ pic32prog-r56-20120725.tar.gz

      スナップショットを取ったものを 置いておく。Windows で使えるように、pic32prog.exe を ビルドしたものと入れ替えた、

    付録) オプション例(コンパイル・リンク時)

    -mips32r2 -EL -D__32M4KCORE__ -D__32MX2XX__ \
    -nostdinc -I/c/PinguinoX.3/win32/p32/bin/../jzlib/include

    オプション例(リンク時のみ)

    -Xlinker -T /c/PinguinoX.3/win32/p32/bin/../jzlib/lib/ldscripts/p32mxb3k.x \
    -Wl,--defsym,__stack=0xa0002000 \
    /c/PinguinoX.3/win32/p32/bin/../jzlib/lib/2xx/crt2xx.o \
    -nostdlib -L/c/PinguinoX.3/win32/p32/bin/../jzlib/lib/2xx

API 概要(1) デバイスのレジスタ

    libc は、置いておくとして、ヘッダーファイルの 定義は MicroChip とまったく互換性がない。そもそもレジスタの定義以外の API はほとんどない。

    まず デバイスのレジスタの定義だが、データシートに書いてある名前 に _ を付けたものを 一応は用意している。

    p32mx/p32mx_2xx_regs.h -- 1xx/2xx 用
    p32mx/p32mx_4xx_regs.h -- 3xx/4xx 用

    これには、2 つの使い方がある。

    -D_USE_SFRS_BASE_ADDR

    とすると、ベースアドレス相対でアクセスする。define しないと 絶対アドレスでのアクセスになる。

    レジスタは、2 つの 64KB 領域に分類される。64KB 以内であれば、1 命令での ロード・ストアが可能で効率が良いはず。なお、ほかの ヘッダファイルも ベースアドレス相対が可能なものがいくつかある。

    デバイス毎のヘッダーファイル

    p32mx/p32mx_adc.h
    p32mx/p32mx_capt.h
    p32mx/p32mx_clkc.h
    p32mx/p32mx_dmac.h
    p32mx/p32mx_i2c.h
    p32mx/p32mx_nvm.h
    p32mx/p32mx_port.h
    p32mx/p32mx_rtc.h
    p32mx/p32mx_spi.h
    p32mx/p32mx_sys.h
    p32mx/p32mx_timer.h
    p32mx/p32mx_uart.h
    p32mx/p32mx_usbotg.h
    p32mx/p32mx_wdt.h

    デバイス毎の定義を作っていて、こちらを使うようにしている。レジスタの名前や フィールドの名前を 一定の規則で変えている。

    規則は次のとおり

      ・ U1OTGCON など デバイス番号があるものは 、数字を _ に変更して U_OTGCON などとする。
      ・ SPI など 複数のデバイスがあるものは、SPI_CON(0) といった指定をする。
      ・ SET/CLR/INV のサフィックスは _ をつける SPI_CON_CLR(0) など。
      ・ PORT の場合 LATACLR などと定義されるが、A-G は _ に変更。
       たとえば LAT_CLR(1) は B を指す。
      ・ いくつか例外がある。DCH_CON(x) など。

    フィールドの定義は、これら デバイス単位の ヘッダファイルにしか定義されていない。デバイス名_フィールド名で、bit 位置 を 示す。bit 幅の定義はない。どうせデータシートを読まないとコードは書けないから なくても問題ないと判断した。データシートを検索するためのフィールド名が 1ヶ所あればよい。

    あと、PORTなど bit 位置が明らかなものの、フィールド名は 定義しないようにした。

    注意) これらの定義は、基本的に 1xx/2xx 向け。3xx/4xx での違いは 一応対応したが、あまり信用ならない。

    補足) PPS -- Peripheral Pin Select の使い方

      p32mx/p32mx_sys.h

      PIC32MX は デバイスの 入出力 の ピンの割り当てを 設定する機能がある。これを使わないと UART とかは使えない。便利でもあり不便でもある。PPS の定義は、p32mx_sys.h で、ほかの雑多なものと 一緒にまとめてしまっている。定義名は、ちょっと変えた。グループ 1-4 に整理してあるので、このヘッダファイルの中を見てほしい。

      なお、3xx/4xx には、PPS はない。定義する側だと、やっかいなものがなくて 少し対応が楽になった。5xx/6xx/7xx に対応するには、上で示してきたヘッダファイルを 全部チェックして必要なものは、追加しないといけない。すごく面倒なので、とにかく後回し。

API 概要(2) CPU

    p32mx/p32mx_core.h

    これに、core 関係の定義を集約している。asm を使った定義もこれだけには存在する。

      static inline unsigned long __irq_disable();
      static inline void __irq_restore(unsigned long c);
      static inline void __nop();
      static inline void __cli();
      static inline void __sei();
      static inline void __delay_loop_1(unsigned int loops);
      extern void delay_loop_1(unsigned int loops);

      extern unsigned long irq_disable();
      extern void irq_restore(unsigned long c);
      extern void nop();
      extern void cli();
      extern void sei();
      extern void delay_loop_1(unsigned int loops);

      extern void delay_us(unsigned int us);
      extern void delay_ms(unsigned int ms);

      __ext_bits(c, pos, width);
      __ins_bits(d, c, pos, width);

    沢山定義されているが、一般的に使うのは、これぐらい。

    irq_disable/irq_restore と cli/sei の使い分けだが、ネストして使いたいときに irq_disable/irq_restore を使う。

    あと、delay_us/delay_ms だが、OSCC の レジスタを見て CPU クロックを推定している。ただ、8MHz 以外の クロックではうまくないかも。USB を使う前提なら 必ず 8MHz の水晶になるはずで、大きな問題はないはず。

    __ext_bits/__ins_bits は、ビットフィールドの 取り出し・結合の命令 EXT/INS に展開する。使用上の注意だが、mips16e では使えず、pos / width は、定数でなければならない。

API 概要(3) 割り込み とタイマー (未完了)

    p32mx/p32mx_intc.h
    p32mx/interrupt.h
    p32mx/ostimer.h
    p32mx/timer.h

    これらに定義してある。

    割り込みを有効にするには、まず interrupt_enable() を call する必要がある。チップやら core やらいろいろ設定しなければならないのだ。似た名前の機能に irq_enable() というのがあったりするが、これは 関係ない。

    SIGNAL(JZ_OSTIMER_IRQ)
    {
    __intc_ack_irq(JZ_OSTIMER_IRQ);
    current_cycle_high++;
    return IRQ_HANDLED;
    }

    これは ostimer.c の一部だが 、こんな風に使う。まず、SIGNAL(IRQ番号) で関数を定義する。AVR-libc と似ているが、多重割り込みは許可しないので、オプションはない。

    __intc_ack_irq(IRQ番号) は、割り込みフラグ ( IFS(x) ) をクリアする。一部のデバイスでは、下位に多数の割り込み要因があり、それらをクリアしてからでないと、割り込みフラグをクリアできないケースがある。このため、割り込み関数側で __intc_ack_irq() を call しなければならないとしている。

    また、戻り値が 0 のとき、割り込みマスク ( IEC(x) ) を クリアしない。IRQ_HANDLED をリターンしないと、次の割り込みが来ないことになる。ただし、割り込みマスクの 制御は別途可能。

    __intc_mask_irq(IRQ番号)
    __intc_unmask_irq(IRQ番号)

    という API がある。

API 概要(4) コンフィグ(未完了)

    MicroChip の コンパイラだと #pragma が定義されていて、コンフィグを設定できる。だが、汎用の gcc でそれは無理。うまい方法はないかと 考えて 一応作ってみた。

    p32mx/p32mx_devcfg.h

    がその定義。

      #define _BUILD_CONFIG
      #include <p32mx/p32mx_devcfg.h>
      #define CONFIG_FPLLIDIV IDIV_4
      #define CONFIG_UPLLIDIV IDIV_4
      #define CONFIG_JTAGEN 0 /* Disabled */
      #include <p32mx/p32mx_devcfg_build.h>

    こんな C のコード を作ってコンパイルすると ...

    struct devcfg0 __attribute__((section(".config_BFC00BFC"))) _config0;
    struct devcfg1 __attribute__((section(".config_BFC00BFC"))) _config1;
    struct devcfg2 __attribute__((section(".config_BFC00BFC"))) _config2;
    struct devcfg3 __attribute__((section(".config_BFC00BFC"))) _config3;

    といった、section 指定のデータが作られて、リンク時に

    config3 : ORIGIN = 0xBFC00BF0, LENGTH = 0x4
    config2 : ORIGIN = 0xBFC00BF4, LENGTH = 0x4
    config1 : ORIGIN = 0xBFC00BF8, LENGTH = 0x4
    config0 : ORIGIN = 0xBFC00BFC, LENGTH = 0x4

    という風に config データが置かれるアドレスに 情報が格納される。#define は省略時デフォルト値が 入る。

    ちなみに、値を確認するには、

    $ mips-objdump -D a.out |less

    などとすると良い。

    付録)デフォルト値

    #define CONFIG_USERID 0xffff
    #define CONFIG_PMDL1WAY 0 /* Multiple Reconfiguration is Allowed */
    #define CONFIG_IOL1WAY 0 /* Multiple Reconfiguration is Allowed */
    #define CONFIG_FUSBIDIO 0 /* USBID is port */
    #define CONFIG_FVBUSONIO 0 /* Vbuson is port */

    #define CONFIG_FPLLIDIV IDIV_2 /* 8 MHz -> 4 MHz */
    #define CONFIG_FPLLMUL MUL_20 /* 4 MHz -> 80 MHz */
    #define CONFIG_FPLLODIV ODIV_2 /* 80 MHz -> 40 MHz */
    #define CONFIG_UPLLIDIV IDIV_2 /* 8 MHz -> 4 MHz */
    #define CONFIG_UPLLEN 0 /* Eable USB PLL */

    #define CONFIG_FWDTWINSZ WISZ_25 /* 25 % */
    #define CONFIG_FWDTEN 0 /* Watchdog Timer is disabled */
    #define CONFIG_WINDIS 1 /* Watchdog Timer is non-Window mode */
    #define CONFIG_WDTPS 0 /* 1/1 */
    #define CONFIG_FCKSM CSDCMD /* Clock Switching Disabled */
    #define CONFIG_FPBDIV 0 /* PBCLK = SYSCLK */
    #define CONFIG_OSCIOFNC 1 /* CLKO output Disabled */
    #define CONFIG_POSCMOD 2 /* Primary Osillator is HS mode */
    #define CONFIG_IESO 0 /* Switchover mode is Disbled */
    #define CONFIG_FSOSCEN 0 /* Secondary Osillator is Disabled */
    #define CONFIG_FNOSC POSCPLL /* Pri osc + PLL */

    #define CONFIG_DEBUG 2 /* DEBUG OFF */
    #define CONFIG_JTAGEN 1 /* Enabled */
    #define CONFIG_ICSEL ICS_PGx3 /* PGEx3 */
    #define CONFIG_PWP PWP_SIZE(0) /* All Writable */
    #define CONFIG_BWP 1 /* Writable */
    #define CONFIG_CP 1 /* No protection */
    #define CONFIG_DEBUG 0x3 /* OFF */
    #define CONFIG_JTAGEN 1 /* Enabled */
    #define CONFIG_ICSEL 3 /* PGEx1 */
    #define CONFIG_PWP 0x3f /* OFF */
    #define CONFIG_BWP 1 /* OFF */
    #define CONFIG_CP 1 /* OFF */


API 概要(5) ヒープ(未完了)

    char *__malloc_heap_start = &__heap_start;
    char *__malloc_heap_end = &__heap_end;

    これは、stdlib/malloc.c での定義。この __head_start/__heap_end がどうなっているかというと。リンク・スクリプトで次のように定義している。

    __heap_start = . ;
    .heap :
    {
    *(.heap)
    } >kseg1_data_mem
    . = ALIGN(4) ;
    __heap_end = . ;
    _end = . ;
    _bss_end = . ;

    .heap セクションに 配列を定義すると その分の領域が取られる。

    char __attribute__((section(".heap"))) head_area[1024];

    例えばこういう風にすると、head_area のサイズ分が確保される。( 変数名はなんでも良い )

    似たものとして、.bdt セクションがある。これは、USB を使う場合に必要な BDT -- Buffer Descriptor Table を置く専用のセクションで 512B アラインという制限から .data の前に置かれる。(注意: この領域はリセットでクリアされない)

    あと、.head_nv セクションも作ってみた。これは .bss の後ろに配置され、

    __heap_nv_stgart
    __heap_nv_end

    で領域を知ることができる。リセットでクリアされない領域なので、便利が使い方ができるかも知れない。

API 概要(6) 一般例外ベクタ

    プログラムが不正な命令を実行したりすると、EBase + 0x180 の 一般例外ベクタに飛んでくる。デフォルトでは、単に ERET して 知らないふりをする。が、これではまずいだろうということで、対応できるようにした。

    void
    __attribute__((section(".gen_handler0")))
    never_return()
    {
    int i;
    for (i=0;;i++) {
    __delay_loop_1(2000000);
    LAT_INV(0) = 1 << 10;
    }
    }

    このように、.gen_handler0 セクションにプログラムを置くと それが実行される。エラーである以上 リターンする必要がないだろうということで、このように C で書いてしまうこともできる。関数名はなんでも良く。セクションだけが重要。

    ただし、スタック は使わないほうが良い。こんな風に 単純な LED 点滅ルーチンにしておいた方が無難。

gcc 野良ビルドの方法 (メモ)

    binutils-2.20.1

    ./configure --prefix=/d/MIPS32_Toolchain --target=mips-elf --disable-shared \
    --disable-threads --enable-languages=c --disable-dssi \
    --disable-plugin

    gcc-4.4.3

    ./configure --target=mips-elf --prefix=/d/MIPS32_Toolchain --enable-languages=c \
    --with-dwarf2 --disable-doc --disable-shared --disable-libada \
    --disable-libssp --disable-nls --disable-fixed-point \
    --with-build-time-tools=/d/MIPS32_Toolchain/mips-elf/bin \
    --disable-tui --disable-gdbtk --disable-threads --disable-bootstrap \
    --enable-multilib --enable-sim --with-float=soft --without-headers

    メモだけ。古めだが、最新のものでも同じオプションでいけるはず。ちなみに mips-elf-objcopy は、最新版でもダメだった。

現状と 今後の予定

    今は、Olimex PIC32-PINGUINO-MX220 (Muser でも買える 1572円) でデバッグしている。やはり ブートローダがあると 便利すぎるぐらい便利。ただ、これでは、コンフィグとかのテストはできない。

    割り込みのところが、かなり難航していて、うまくいっていない。これをクリアできたら、秋月で安価に買える PIC32MX220F032B に移ろうかと思う。といっても、PIC32-PINGUINO-MX220 と同じプログラムが動くはずなので、主に pin32prog の 改造のデバッグ と コンフィグ関係。

    それとは別に実用的なプログラムを作って、ライブラリが使えるものなのかどうか確認したい。USB デバイスライブラリ と 応用プログラムを予定している。これも以前 AVR 向けに作ったものがあるので、移植ということになる。こちらのデバッグも PIC32-PINGUINO-MX220 を使うつもり。USB デバイスでは、割り込みを使わない予定なので、割り込みが動いていなくとも 作ることはできそう。

    なお、割り込みのデバッグ状況だが、ostimer を使ってデバッグしている。1 回割り込みを起こすことができて 割り込み関数に飛んでくることは確認できた。だが、割り込みからの復帰で 一般例外を起こしている。アドレスは、アセンブラの __intent らしい。一般例外ベクタで ERET だけすると、平タスクに復帰してくる。たぶんスタックがおかしいか、実行モードがおかしいか そんなところ。あと、シャドウレジスタを使った版があるのだが、こちらも全然だめ。これもなんとかしたいが、レジスタのセーブ・リストアをする版の方が先。

    retrobsd

      PIC32MX で動くらしいのだが、これの ソースコードも参考にしようかと思う。
       ・ retrobsd-src-r561-20110729.tar.gz ( 参考用のスナップショット )

      USB device とか SDカードのドライバもある。MicroChip のコードがベースだが、参考にしたい。

ソースコード

     ・ jzlib-0.1.tar.gz

    割り込みが動いていないという重大が制限があるが、とりあえず。0.1 とした。

     ・ jzlib-0.1a.tar.gz

    __ext()/__ins() など、いくつか修正を入れた。割り込みはやっぱりまだ。あと examples を入れた。簡単なプログラムだが、ビルドの仕方とか の参考用に。

     ・ jzlib-0.2.tar.gz

    printf を使えるようにする準備。一応 examples/hello.c がビルドできた。

      シリアルの送信は、割り込みなしでも 一応はできる。FIFO は 4 段 + 送信バッファで、5 バイトまで一気に送ることも可能。それは良いのだが、stdio (FILE *) を使うと一気にサイズが増える。さらに printf でまた増える。hello.hex が 9.5KB とか。

        G _sbss_begin
        G _sbss_end
        B __iob
        G __malloc_heap_end
        G __malloc_heap_start
        G __malloc_margin
        S __brkval
        S __flp
        S _sbss_begin
        S _sbss_end
        T _2xx_serial0_init
        T calloc
        T clkc_get_pbclk
        T fdevopen
        T free
        T irq_disable
        T irq_restore
        T jzcon_append
        T jzcon_setup
        T malloc
        T memset
        b __putfunc
        s __putfunc_num
        t __jzcon_putc
        t __serial_putc
        t smallclr

        FILE * を使うと malloc を使う。こういうので結構コード量が増える。

        R __clz_tab
        T __udivdi3
        T __umoddi3
        T fputc
        T memchr
        T printf
        T strlen
        T vfprintf
        r blanks.1661
        r zeroes.1662

        こちらは、printf で増えたもの。vfprintf が本体なのだが、NetBSD のコードを使っている。%f とかの変換は OFF にしているのにでかい。AVR のものが使えなかったのは、アセンブラのコードを一部使っているため。RetoroBSD のライブラリが小さければ、入れ替えるつもり。

      USB device ライブラリの移植を始めているのだが、これがまたサイズがでかい。

      AVR:

      text data bss dec hex filename
      1026 0 412 1438 59e cdc_main.o
      601 0 0 601 259 cdcmsc162.o
      165 0 0 165 a5 desc_cdcmsc.o
      1326 0 18 1344 540 usb162.o
      3368 0 446 3814 ee6 serial162.elf

      MIPS32R2
      text data bss dec hex filename
      1980 0 412 2392 958 cdc_main.o
      976 0 0 976 3d0 cdcmsc162.o
      156 24 0 180 b4 desc_cdcmsc.o
      1800 512 20 2332 91c usb162.o
      7976 544 540 9060 2364 serial162.elf

      MIPS16
      text data bss dec hex filename
      1560 0 412 1972 7b4 cdc_main.o
      692 0 0 692 2b4 cdcmsc162.o
      156 24 0 180 b4 desc_cdcmsc.o
      1304 512 20 1836 72c usb162.o
      6788 544 540 7872 1ec0 serial162.elf

      だいぶ埋めたから、これから大きくは増えないものの、こんな差になっている。MIPS16 使っても AVR の 1.5 倍は覚悟しないとダメそうだ。もともと print + USB serial でデバッグしようなんて考えていたのだが、この調子だと肝心のプログラムが入らなさそうになっている。(USB HID ブートローダ使うと 16KB 取られる)

シリアル出力の確認

    #include <stdio.h>
    #include <p32mx/p32mx_core.h>
    #include <p32mx/p32mx_port.h>
    #include <p32mx/p32mx_uart.h>
    #include <p32mx/p32mx_sys.h>
    #include <p32mx/console.h>

    // Olimex PIC32-PINGUINO-MX220
    // LED1 (Green) D13 = RB15 Active H (1: Light on)
    // LED2 (Red ) D9 = RA10 Active H (1: Light on)
    // BUT D8 = RB7 Active L (0 : Pressed)

    uint8_t heap_area[256] HEAP_SECTION;

    #define UART_CH 0
    static inline void serial_setbrg(const long baud) {
    U_BRG(UART_CH) = (clkc_get_pbclk()/16/baud - 1 ) & 0xffff;
    }

    int main() {
    int i;
    regx_clr(TRIS , 1, 15);
    regx_clr(TRIS , 0, 10);
    regx_clr(LAT , 1, 15);
    regx_clr(LAT , 0, 10);

    for (i=0;i<5;i++) {
    __delay_loop_1(10000000);
    regx_inv(LAT, 0, 10);
    }
    serial_init(0);
    serial_setbrg(50); // veri slow rate
    jzcon_setup();
    for (i=0;i<4;i++) {
    __delay_loop_1(10000000);
    regx_inv(LAT, 1, 15);
    }
    RPB15R = PPS1_OUT_U1TX;; // assign PB15
    printf("hello world\n");
    regx_clr(LAT , 1, 15);
    RPB15R = PPS1_OUT_NONE;; // assign PB15
    for (i=0;i<5;i++) {
    __delay_loop_1(10000000);
    regx_inv(LAT, 0, 10);
    }
    for (i=0;i<5;i++) {
    __delay_loop_1(10000000);
    regx_inv(LAT, 1, 15);
    }
    for (;;) {
    }
    }

    hello.c -fno-builtin-printf を付けないでコンパイルすると、この printf は、gcc の機能でputs になる。余計なことを ... と思ったが、puts でないと今のところ動かないようだ。

    さて、ボーレートを 50 まで落として確認してみたところ、LED の光り具合で なにか送信していることを目視で確認できた。

    RPB15R = PPS1_OUT_U1TX;
    RPB15R = PPS1_OUT_NONE;

    というのが、U1TX を PB15 に割り当てたり元に戻したりするためのコード。出力については同一機能を複数の ピンに割り当てられる。だから本来どこに割り当たっているか気にせず、LED に割り当てられる。( 入力は逆で、同一のピンを複数の機能に割り当てられる。)

    あと、ポートの制御だが、regx_set/_clr/_inv という風にできるようにした。それぞれ _SET/_CLR/_INV のレジスタに ビットをセットする。regx は、REGx のようにインデックスが着くレジスタの場合に使用し(0 オリジン)、インデックスがないレジスタには、reg_ を使う。

    clkc_get_pbclk() は PBCLK の周波数を推定する関数。特に USB を使うようなチップでは、PLL に 4MHz を入力することになっているため、それを基準にして PLL の倍数 や ポストスケーラーの設定から 周波数を 決めている。あと Fast RC オシレータは 8MHz と分かっているのでそこから計算。対になる機能として、clkc_get_sysclk() がある。こちらも同様だが、CPU のクロックを推定する機能。

    ところで、 -fno-builtin-printf を付けたら動かないと書いたが、初期化前の LED の点滅さえ動かなくなる。ライタに問題があったり、リンクスクリプトに問題があったりするのかも知れない。printf がバグっているかどうか以前の問題なのだ。

jzlib-0.2a

     ・ jzlib-0.2a.tar.gz

    ぜんぜん問題は解決していないが、0.2a にした。つけた機能は FLASH の書き込み のコード。あと割り込みベクタをまったく使わない リンクの サポート。-bu としていたところを -nvbu とする予定 (一部のみ対応)

    あまり関係ないが、RetroBSD も少し見ている。MicroChip のコード由来だが、USB HID ブートローダのコード(sys/pic32/usbboot)も入っていた。見たいコードが一式入っているかんじで参考になる。

jzlib-0.2b

     ・ jzlib-0.2b.tar.gz

    printf を使うとサイズがひどいことになっていた原因は、ll とか long long をサポートしているためだと分かった。サポートしないようにしたところ、4KB 以上も減った。あと、割り込みベクタを生成しないオプション を作ったところ、1KB 以上減った。結局 printf で 2KB 強増えるだけで済むようにできた。

    text data bss dec hex filename
    9468 276 112 9856 2680 hello.elf (0.2a) printf
    5624 20 368 6012 177c hello.elf (0.2b) printf
    4392 20 368 4780 12ac hello.elf (0.2b) printf , no vector
    2044 20 368 2432 980 hello.elf (0.2b) puts, no vector

    割り込みベクタを生成しないオプションは、デバッグで違いを見るためと ブートローダを作れるようにする準備。で、HEX ファイルを見ていて気がついたのだが、heap 領域の初期値が、HEX ファイルに出てくる。どうも (NOLOAD) を指定しないとだめらしい。

    HEX ファイルはチェックしないとまずい。Pinguino IDE に付属の MPHidFlash を試しに使ったら、ブートローダを飛ばしてしまった。いまのところ安全なのは、pic32prog 。これは書き込む領域をチェックしてくれているみたい。

HEX ファイルの確認

    まずは、簡単に構造を説明しておくと ..

     : サイズ(2) オフセット(4) タイプ(2) データ(サイズx2) チェックサム(2)
      (x) は桁数
    タイプ
    00 データ
    01 EOF
    04 拡張アドレス
    05 スタートアドレス (通常使われないが、objcopy で出てくる)

    こうなっている。zjlib で 典型的なのは、こんな感じ。

     :020000049D005D
     :10400000009D1A3C10405A270800400300000000A1
      :
     :04414000000000007B
     :10418000009D1A3C10405A27080040030000000020
     :10420000009D1A3C10425A2708004003000000009D
      :
     :02000004BFC07B
     :10000000009D1A3C10405A270800400300000000E1
     :040000059D0040001A
     :00000001FF

    スペースを入れるとこう。

     :02 0000 04 9D00 5D
     :10 4000 00 009D1A3C10405A270800400300000000 A1
      :
     :04 4140 00 00000000 7B
     :10 4180 00 009D1A3C10405A270800400300000000 20
     :10 4200 00 009D1A3C10425A270800400300000000 9D
      :
     :02 0000 04 BFC0 7B
     :10 0000 00 009D1A3C10405A270800400300000000 E1
     :04 0000 05 9D004000 1A
     :00 0000 01 FF

    随分見やすくなった。意味は、

      0x9d00_0000 (プログラムフラッシュの先頭) 〜
       オフセット 0x4000 (スタートアドレス) からのデータ
       オフセット 0x4180 (一般例外ベクタ) からのデータ
       オフセット 0x4200 (割り込みベクタ) からのデータ
      0xbfc0_0000 (ブートフラッシュの先頭) 〜
       オフセット 0x0000 (リセットアドレス) からのデータ
      スタートアドレス (0x9d004000)

    リセットアドレスに ジャンプ命令を入れているのだが、ブートローダを使う場合には保護されているはずだし、ツールもはじいてくれるから ... と思って入れていたのだ。

    だが、ちょっとまずい。最後の EOF を除く 3 行は削除した方が安心できる。

    コンフィグデータを生成すると (examples/config-sample.hex)

     :04 0BF0 00 FFFFFF0F F5
     :04 0BF4 00 D979F9FF B3
     :04 0BF8 00 5BCE60FF 71
     :04 0BFC 00 EEFFFF7F 8A

    このようなデータが 最後の方に追加される。これも pic32prog でははじいてくれるが、うっかり書き込むと危険。

    さて、PINGUINO IDE に付属の PIC32-Pinguino_HIDBoot_MX220.hex をちょっと見てみよう。 :02000004 のデータ が 各エリアの先頭を示すはずだから grep してみる。

     :02 0000 04 0000 fa
     :02 0000 04 1fc0 1b
     :02 0000 04 0000 fa
     :02 0000 04 1fc0 1b
    :
     :02 0000 04 0000 fa
     :02 0000 04 1d00 dd
     :02 0000 04 0000 fa
     :02 0000 04 1d00 dd
     :02 0000 04 0000 fa
     :02 0000 04 1d00 dd
     :02 0000 04 0000 fa
     :02 0000 04 1d00 dd

    随分沢山出てくるのだった。アドレス 0000 のものは、続いて 1fc0 , 1d00 の行が出てくるから、無視して良さそう。で、1fc0 , 1d00 が何かというと 物理アドレス。上で出てくる BFC0 , 9D00 は仮想アドレス。仮想アドレスの 上位 2 bit を 0 にすると物理アドレスになる。

    pic32prog では仮想アドレスでも 問題ないのだが ... 正しくは物理アドレスを渡さないといけない。 ちなみに沢山出てくるのは、連続していないと とりあえず出力しているためのようだ。

     :04 0bf4 00d979f9ff b3
     :04 0bf8 005bce60ff 71
     :04 0bfc 00eeffff7f 8a

    ちなみに、このようなコンフィグデータが含まれている。:040bf で grep すれば見つかる。

    もう少し詳しくみてみる。

     : 02 0000 04 0000 fa
     : 02 0000 04 1fc0 1b
     : 04 0bf4 00 d979f9ff b3
     : 02 0000 04 0000 fa
     : 02 0000 04 1fc0 1b
     : 04 0bf8 00 5bce60ff 71
     : 02 0000 04 0000 fa
     : 02 0000 04 1fc0 1b
     : 04 0bfc 00 eeffff7f 8a
     : 02 0000 04 0000 fa
     : 02 0000 04 1fc0 1b
     : 10 0000 00 c0bf1a3c10005a270800400300000000 3f
     : 10 0010 00 00601a40c0045a7f0500401300000000 31
      :
     : 10 01e0 00 0000a530009d083c402f082508000001 b4
     : 04 01f0 00 00000000 0b
     : 02 0000 04 0000 fa
     : 02 0000 04 1fc0 1b
     : 10 0380 00 009d1a3c5c2f5a270800400300000000 23
     : 02 0000 04 0000 fa
     : 02 0000 04 1d00 dd
     : 10 0180 00 009d1a3c2c295a270800400300000000 5b
     : 02 0000 04 0000 fa
     : 02 0000 04 1d00 dd
      :

    最初の方にコンフィグデータがあり、次にブートフラッシュの先頭にもどる。0000 はリセットアドレスで、スタートアップが入っている。0380 と 0180 は、ブート時の割り込みベクタ。この短さだと 実際には割り込みを使っていないのかも知れない。で、どうもこれだけのようだ。3KB あるが、スタートアップと コンフィグにしか使われていない感じ。プログラム本体は、0a00 から始まっていて、2f90 まで続いている。( ちなみに、アプリケーションプログラムは、0x3000 から ベクター領域になっていて 0x4000 がスタートアドレスになる )

    PIC32MX は、プログラムフラッシュも書き込み保護ができる。 保護されていたら ブートローダを飛ばすこともなかったのだが ...

jzlib-0.2d

     ・ jzlib-0.2d.tar.gz

    ブートローダの検討』で紹介した ブートローダのソースコードを同梱したのが変更の目玉。ただし、テストも初めていない。コードを書いてみて API を早めに修正したいのだ。(動く動かないより重要だと考えてえいる)。ブートローダが必要になったので、テストの優先順位は高いが 先のはなし。

    API として p32mx/bootloader.h を追加している。自製のブートローダも作るつもりで、共通化できるところを入れている。

    あと、p32mx_core.h も結構変更している。HID ブートローダ の移植で 変更が必要になった。あと p32mx_usbotg.h も。

    さらに、5xx/6xx/7xx に対応可能かすこし調べだしている。見ていると UART とか SPI とか数が増えていて、番号とアドレスの関係が ややこしいことになっている。で SPI4 とか UART6 とか PORTG を index の意味で 定義することにした。

ポート制御の簡潔な記法

    #include <p32mx/p32mx_core.h>
    #include <p32mx/p32mx_port.h>

    int main() {
    port_clr(TRIS , PB15);
    port_clr(TRIS , PA10);
    port_set(LAT , PB15);
    port_clr(LAT , PA10);
    for (;;) {
    __delay_loop_1(10000000);
    port_inv(LAT , PB15);
    }
    }

    こういう書き方が出来ることが分かった。
    実体は、

    #define port_clr(...) regx_clr(__VA_ARGS__)
    #define PB15 1,15

    NG regx_clr(TRIS , PB15);
    OK port_clr(TRIS , PB15);

    どうも一回 可変引数の マクロを通すと これができるらしい。次の版でいれよう。

     ・ jzlib-0.2e.tar.gz

    とりあえず次の版を置いておく。hidboot の コードを変更。作成中の USBASP ブートローダのコードをマージ。あと usb_device.c をちょっと見ている。使っていない関数を statc に変更して整理。

      なんというか、usb_device.c がでかい。(ブートローダでは)絶対動かないコードが多数含まれているんじゃないかと思える。でも if 文で直接 call されているようで切り離せない。とりあえず、static にしてみた。(すこし小さくなる)。あと、ブートローダ・コンフィグのコードは最適化して小さくするようにしたので、マージ。

      text data bss dec hex filename
      7968 52 535 8555 216b usbboot.elf

      結果 1KB ほど小さくなった。


    ところで、printf を使うと全く動かなる件。やっぱり分からない。

    < ok.out: file format elf32-littlemips
    ---
    > bad.out: file format elf32-littlemips

    < 9d0040d4: 2508483c addiu t0,t0,18492
    ---
    > 9d0040d4: 25085164 addiu t0,t0,20836

    < 9d0041e8: 77401147 jalx 9d00451c <_2xx_serial0_init>
    ---
    > 9d0041e8: 77401355 jalx 9d004d54 <_2xx_serial0_init>

    a.out を objdump で見ると、頭の部分の違いは、こんな感じ。40d4 の違いは、.data の初期値 の アドレスで問題ない。で、_2xx_serial0_init まで同じだとすると ... LED が点灯しないはずはないのだ。FLASH に期待どうりに書きこまれていないのではないか? というのが今の疑い。ひょっとして、HEX ファイルの作り方が正しくない?

    で、pic32prog のソースコードを見ているのだが、読み込みは基本的には問題なさそう。

      内部は、フラッシュ用データとブート用データに分けて管理している。で、読み込んできたデータを それぞれのバッファに埋め込むような処理になっている。そして、1KB 毎にどこのデータが変更されたかも管理していて、変更のあったところを書き込むようだ。

      読み込むのは、物理アドレス(1D00_0000 , 1FC0_0000) と 仮想アドレス (9D00_0000 , 9FC0_0000) に対応していてどちらでも良い。ただし kseg1 アドレスは、範囲外にされてしまう。コンフィグ情報は、kseg1 アドレス (BFC0_0000) にしているので、そのままでは書き込めない。あと、リセットアドレスに埋め込んでいるジャンプコードも。

      ついでに書いておくとスタートアドレスは、単に無視する。あってもなくても良い。

printf が動かない理由

    少し進展があった。まず、一般例外が起きているはずで、これが起きたら LED を高速点滅させることで分かるようにした。言うは易しで、割り込みをサポートしないつもりのタイプで これを出来るようにするのは結構面倒だった。また、この機能はブートローダでも使いたい。... これで結構なやんだのだが 次のようにすることにした。

    で、printf というか FILE 構造体を扱う場合は calloc が呼ばれていたのだが ... よくよくコードを見たら calloc を使わないような造りができるのだった。それを直したら、かなり現象がシンプルになった。最初から全く動かないという現象はなくなって、動いたり動かなかったりになった。動かない場合でも、途中までは動く。で、動かなくなるところは、jzcon_apped という関数。

    あまりに単純な関数なので、何故動かないかなかなか分からなかったのだが、hello.elf の逆アセンブルを眺めていてようやく気がついた。

    単純化したとは言え printf とかいろいろ関数がある中で、この関数だけが gp を使っている。

      オプションなしだと、gp は、グローバル変数のアクセスに使われる。使いかたは、

      9d004ed4: 8f828010 lw v0,-32752(gp)

      9d004f58: af828010 sw v0,-32752(gp)

      gp には、常に 0xa0007FF0 が入っていて、この場合、0xa0000000 の変数をアクセスするはずなのだ。どうも全ての変数がこのようなアクセスをするわけではないようで、配列とかは、絶対値をロードして使っている。auto 変数にも gp は当然使わない。ライブラリも グローバル変数はあまり使わない。malloc は当然使うので、ややこしい現象になっていた(らしい)。

      例外を起こすから、gp の値がおかしいとしか思えないのだが ... どうするのが正しいのか良くわかっていない。

      そう言えば、以前も Jz47xx のライブラリとして使った時も相当悩んだのだった。ここさえクリアすればいろんなものが動くようになりそう。

    BMX

      いままで、関係無いだろうと思って見ていなかったのだが ... いろいろと機能があった。

      BMXCON_BMXERRIS: Bus Error from CPU Instruction Access bit
      BMXCON_BMXERRDS: Bus Error from CPU Data Access bit
      BMXCON_BMXERRDMA: Bus Error from DMA bit
      BMXCON_BMXERRICD: Enable Bus Error from ICD Debug Unit bit
      BMXCON_BMXERRIXI: Enable Bus Error from IXI bit

      例外をそもそも起こさないというモードが設定できた。これらは、Reset で 1 なので 0 にすると例外を起こさなくなる。

      BMXCON_BMXWSDRM: CPU Instruction or Data Access from Data RAM Wait State bit

      ブートローダで唯一クリアしているビット。0 ウエイトにしている。

      BMXDRMSZ: DATA RAM SIZE REGISTER
      BMXPFMSZ: PROGRAM FLASH (PFM) SIZE REGISTER
      BMXBOOTSZ: BOOT FLASH (IFM) SIZE REGISTER

      RAM やフラッシュの サイズが見える レジスタもあった。これを元にスタックの値を設定したほうが良いかも知れない。フラッシュのサイズは、ブートローダで使える。

      BMXDKPBA: DATA RAM KERNEL PROGRAM BASE ADDRESS REGISTER
      BMXDUDBA: DATA RAM USER DATA BASE ADDRESS REGISTER
      BMXDUPBA: DATA RAM USER PROGRAM BASE ADDRESS REGISTER

      (BMXDKPBA > BMXDUDBA > BMXDUPBA)

      User Mode /Kernel Mode に RAM を割り当てる機能らしい。どうも 簡単な 論理→物理変換機能で、この値だけ物理アドレスを上位にずらすらしい。 User Mode は分かるのだが、Kernel Mode までずらせてなにが嬉しいのだろう? このキーワードでググればなにかヒントが出てくるのかも。それはともかく間違えて設定するとまずい。要注意のようだ。

      /* Step 1. */
      xfer_instruction (a, 0x3c04bf88); // lui a0, 0xbf88
      xfer_instruction (a, 0x34842000); // ori a0, 0x2000 - address of BMXCON
      xfer_instruction (a, 0x3c05001f); // lui a1, 0x1f
      xfer_instruction (a, 0x34a50040); // ori a1, 0x40 - a1 has 001f0040
      xfer_instruction (a, 0xac850000); // sw a1, 0(a0) - BMXCON initialized

      /* Step 2. */
      xfer_instruction (a, 0x34050800); // li a1, 0x800 - a1 has 00000800
      xfer_instruction (a, 0xac850010); // sw a1, 16(a0) - BMXDKPBA initialized

      /* Step 3. */
      xfer_instruction (a, 0x8c850040); // lw a1, 64(a0) - load BMXDMSZ
      xfer_instruction (a, 0xac850020); // sw a1, 32(a0) - BMXDUDBA initialized
      xfer_instruction (a, 0xac850030); // sw a1, 48(a0) - BMXDUPBA initialized

      pic32prog を見ていたらこんなコードがあった。割り込みを受けたくないなら、BMXCONを 設定するのが普通なのkも知れない。あと、BMXDKPBA, BMXDUPBA, BMXDUDBA をちゃんと設定している。この場合は BMXDKPBA=2K だからなにか理由があって使っているのだろうけれども、設定した方がよいのかも。

    試してみた。

      アドレスで例外を起こさないようにしたら、今度は、 Reserved instruct になった。関数ポインタ使っているから当然か。コンパイルオプションで -G 0 を付けると gp を使わなくなる。でも結果は同じ。

printf が動いた。

    jzcon が悪いのなら使わなければ良い。もともと 大したことはしていなくて、ただの FILE の分配機能 -- printf とすると serial にも出力するし、console にも出力する みたいなことをするものなのだ。

    で、USB シリアルに接続して、表示させたところ見事に出力された。

      hello world 5 5 000003b0
      DEVCFG3 = 0x3fffffff
      DEVCFG2 = 0xfff979d9
      DEVCFG1 = 0xff60cedb
      DEVCFG0 = 0x7fffffeb

    今もっとも見たいのは、DEVCFG で JTAG が disable されているかどうか。残念なことに disable されていた。また、書き込み保護を一切していない。これだと壊れる場合も出てくるのは当然か。

    それはともかく、これで知りたい情報が いくらでも見れる。jzlib のデバッグが進むはず。

      gp a0007ff0
      sp a0001fd8
      c0_status 10100000
      c0_cause 18800028
      c0_prid 00018769
      c0_intctl 00000020
      c0_srsctl 00000000
      c0_ebase 9d004000
      c0_compare ffffffff

    次にいろいろな レジスタ。とりあえず p32mx_core.h にある read_xx 関数を片っ端から表示させた。c0_srsctl が意外。これだと シャドウレジスタが存在しないことになる。c0_intctl は、VS=1 という設定だけが入っている。これはひとつの 割り込みベクタが 32B という意味。( マルチベクタモードでは、64 個あるから 全部で 2KB 。)

      a0000000: 00000000, 00000000, 00000000, 00000000
      a0000010: 00000000, 00000000, 00000000, 00000000
      a0000020: 00000200, 00000000, 00000190, 9d004220
      a0000030: 00000000, 00000000, 00000000, 00000200

    次にメモリダンプ。グローバル変数は、stdout などが入る __iob[] の 1 つしか使っていない。そのデータ。

      ここで、ブートローダの ダンプも取った。これで 、完全に元に戻せる。一安心。

    さて、 malloc 関係のデータのアドレスを参照してみる(参照だけで call しない)と ...

      __malloc_mergin addr =
      : , , ,
      : , , ,
      : , , ,
      : , , ,

    ... これはそのまま貼り付けてみたもの。こういう風に %08x の部分のみ 文字化けする。これはなにを意味しているのか? なぜこうなるかを調べることで原因がわかるはず。

    まず、%x の処理を見てみることにした。


      case 'X':
      xdigs = "0123456789ABCDEF";
      goto hex;
      case 'x':
      xdigs = "0123456789abcdef";
      hex: _uintmax = UARG();
      :
      :
      :

      case HEX:
      do {
      *--bp = xdigs[(size_t)
      (_uintmax & 15)];
      _uintmax >>= 4;
      } while (_uintmax);
      break;

      こんな感じ。xdigs に入れるアドレスが 変わってしまえば、文字化けするかも知れないが ... ほかの理由はあまり考え付かない。とりあえず 上半分が アセンブラでどうなっているか追跡。

      $L149:
      move $16,$4
      $L151:
      li $4,1
      b $L62
      $L34:
      lw $5,$L160
      sw $2,60($sp)
      sw $4,56($sp)
      sw $5,88($sp)
      b $L23
      $L127:
      lw $2,$L162
      sw $2,88($sp)
      $L23:

      $L160:
      .word $LC2
      $L161:
      .word $LC0
      $L162:
      .word $LC1

      $LC1:
      .ascii "0123456789ABCDEF\000"
      .align 2
      $LC2:
      .ascii "0123456789abcdef\000"
      .text

      データを置いて、そのアドレスも置く。で lw で アドレスを取ってきている。これが a.out で どうなったか見てみる。

      304: 6c01 li a0,1
      306: 1023 b 34e <vfprintf+0x34e>
      308: b5ba lw a1,5f0 <vfprintf+0x5f0>
      30a: d20f sw v0,60(sp)
      30c: d40e sw a0,56(sp)
      30e: d516 sw a1,88(sp)
      310: 1002 b 316 <vfprintf+0x316>
      312: b2ba lw v0,5f8 <vfprintf+0x5f8>
      314: d216 sw v0,88(sp)
      316: 6aff li v0,255

      9d004d40: 5014
      9d004d42: 9d00
      9d004d44: 4ff8
      9d004d46: 9d00
      9d004d48: 5000
      9d004d4a: 9d00
      9d004d4c: 5048
      9d004d4e: 9d00

      9d004d50: 5038
      9d004d52: 9d00
      9d004d54: 5038
      9d004d56: 9d00
      9d004d58: 5028
      9d004d5a: 9d00

      アドレスのテーブルは合っていて、それへのアクセスも合っているように見える。...ただ、lw が関数の先頭からの相対になっている。実をいうと MIPS の命令にそのようなものはない。ルールがあって、関数の先頭が入っているレジスタが決まっているのだ。call 先アドレスを そこに入れて call する。このルールどおりでないと、ローカルなデータアクセスも 正しくできない。... というのは間違いだった。PC 相対ができるみたいだ。

      グローバル変数を使うとこのルールが破られる? ... どうも腑に落ちないが そういうことでもない限りこういう現象にならないような気がする。

      見落としていたが、%d は正しく表示できている。... ということは、やはり %x での string のアドレスが 間違っていることになる。

      で、やっぱり PC 相対で lw している。a.out の vfprintf の 部分を比べてみたが、fputc とかの 関数 call のアドレス以外 まったく同一。そして上で示したように テーブルのアドレス値もあっているのだ。もうなにがなんだか。

        __malloc_mergin addr = a0000000
        a0000000: 00000200, a0000070, a0000270, ffffffff
        a0000010: 00000000, 00000000, 00000000, 00000000
        a0000020: 00000000, 00000000, 00000000, 00000000
        a0000030: 00000000, 00000200, 00000000, 000001e2

      原因ははっきりしたので、printf だけは直せる。static 変数を使わないようにすれば良いわけだ。問題は解決していないのだが、printf が動かないことには、情報が取れない。

      直したらこのように動くようになった。サイズが小さくなるというおまけまでついた。

      text data bss dec hex filename
      4060 12 612 4684 124c hello.elf (org)
      3856 12 612 4480 1180 hello.elf (new)

      ただ、現象がどのようになるのか見るのに printf は使えなくなった。代わりのものでデバッグしなくては。

      ちょっと進展というか ...

      __malloc_mergin addr = a0000000
      a0000000: 00000200, a0000070, a0000270, 00000000
      a0000010: 00000000, 00000000, 00000000, 00000000
      a0000020: 00000000, 00000000, 00000000, 00000000
      a0000030: 00000000, 00000000, 00000200, 00000000
      a0000040: 00000214, 9d004220, 00000000, 00000000
      a0000050: 00000000, 00000200, 00000000, 00000000
      a0000060: 9d004220, 00000000, 00000000, 00000000
      a0000070: 00000000, 00000000, 00000000, 00000000

      たとえばこういう風にメモリがちゃんと初期化されて見える場合がある。

      __malloc_mergin addr = a0000000
      a0000000: ffffffff, ffffffff, ffffffff, 00000000
      a0000010: 00000000, 00000000, 00000000, 00000000
      a0000020: 00000000, 00000000, 00000000, 00000000
      a0000030: 00000000, 00000000, 00000200, 00000000
      a0000040: 00000214, 9d004220, 00000000, 00000000
      a0000050: 00000000, 00000200, 00000000, 00000000
      a0000060: 9d004220, 00000000, 00000000, 00000000
      a0000070: 00000000, 00000000, 00000000, 00000000
      a0000080: 00000000, 00000000, 00000000, 00000000
      a0000090: 00000000, 00000000, 00000000, 00000000
      __malloc_mergin addr = a0000000 val = 00001234
      a0000000: 00001234, ffffffff, ffffffff, 00000000
      a0000010: 00000000, 00000000, 00000000, 00000000
      a0000020: 00000000, 00000000, 00000000, 00000000

      一方 メモリへの書き込みがうまくいくが、メモリの初期化がおかしい場合がある。3 ワード だけ all 1 になっているが、3 ワードしかデータがないのだ。そして初期値を入れるのは、スタートアップだけ。アドレスが間違っていればこうなるが、la で とってきているので、間違いはない。

      ... これは! なぜなのか? FLASH がちゃんと読めない場合があるということではないのだろうか?

      c0_config a4210582

      C0_CONFIG の値をとっていなかったので、メモしておく。キャッシュは OFF ( LSB から 3bit が 2 / ON なら 3)

jzlib-0.3c

     ・ jzlib-0.3c.tar.gz

    そんなわけで、メモリを読んで ダンプし、書き込んだものと比較することにした。

    書き込んだものとは ... objcopy で作った ihex フォーマット。これと比較するには、プログラム自体も 比較可能な形式での ihex を出力するのが便利だと考えた。

    で、新規に作ったのが、util/ihex_dump.h -- 全部 inline 関数。なので、HOST 側でも少しの手直しで使える。

      本来手直しなど不要のはずだったのだが、... MIPS32 と MIPS16 を混ぜた時、gcc のバグ?で不具合が生じた。関数の最後で 関数を call すると j 命令でジャンプするようになっている。だが、MIPS32 から MIPS16 に j 命令では移行できない。これがリンク時にエラーになる。しょうがないので、inline 関数の __nop() を最後に入れている。

       ・ ihex_conv.c

      ついでに例として ihex ファイルを整形する プログラムを置いておく。


    あと、リンク・スクリプトを RetroBSD の usbboot のものをベースに つくり変えた。こちらの方がシンプルで改造しやすい。

    で、結果なのだが、ちゃんと同じものが書けているようだ。ただし、ちゃんと動いたケースに限ったはなし。途中でおかしくなるものがどうなるかは確認できていない。

    ところで、PIC32MX は、CRC を計算する機能を持っている。どこにあるかというと DMAC の中らしい。CRC16 と PIC32MX で検索したら サンプルコードを みつけた。→ ここ 。DMAC の使い方の練習にもなるかも知れない。メモ。

      DCRCDATA=get_non_direct_seed(seed);// seed the CRC generator
      DCRCXOR=0x1021; // Use the standard CCITT CRC 16 polynomial: X^16+X^12+X^5+1

      DCRCCON=0x00000FC0; // CRC enabled, polynomial length 16, append mode,
      // CRC attached to the DMA channel 0.

      良くわかっているわけではないが、どうもこのあたりのレジスタが CRC 自体の設定らしい。

      DMACONSET=0x00008000; // enable the DMA controller

      // do something else while the CRC calculation takes place
      // poll to see that the transfer was done

      DCH_CON(0)=0x03; // channel off, priority 3, no chaining
      DCH_ECON(0)=0; // no start irqs, no match enabled

      // program channel transfer
      DCH_SSA(0) = vtop(buffer); // transfer source physical address
      DCH_DSA(0)= vtop(&tmp); // transfer destination physical address
      DCH_SSIZ(0)=size+2; // source size
      DCH_DSIZ(0)=4; // dst size
      DCH_CSIZ(0) =size+2; // 200 bytes transferred per event
      DCH_INTCLR(0)=0x00ff00ff; // DMA0: clear events, disable interrupts
      DCH_INTCLR(1)=0x00ff00ff; // DMA1: clear events, disable interrupts

      DCH_CONSET(0)=0x80; // channel 0 on
      DCH_ECONSET(0)=0x00000080; // initiate a transfer

      DMA のチャネル制御を jzlib 用に書き直すとこう。 なかなか長い呪文だ。それはともかく、CRC モジュールはひとつで、使う DMA チャネルを選ぶという使い方のようだ。

関連記事
posted by すz at 20:39| Comment(0) | TrackBack(0) | PIC32MX
この記事へのコメント
コメントを書く
お名前: [必須入力]

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

ホームページアドレス:

コメント: [必須入力]

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


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

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