HIDBootX

関連リンク PIC32MX Pinguinoで遊ぼう ブートローダーを作る USB仮想シリアル USBカスタムデバイス USBホスト 2013-02 2013-03


ブートローダーって、何?

  • PICマイコンに最初にプログラムを書き込むには、必ずPICライター(PicKit2/3)が必要です。
  • しかし、あらかじめ小さなプログラムを書き込んでおいて、そのプログラムの助けにより、残った領域に別のプログラムを転送して実行する、ということが可能だとしたら、2回目以降の開発ではPICライターが不要になるというメリットがあります。
  • そのような手助けをする(比較的)小さなプログラムのことをブートローダーと呼んでいます。

https://raw.github.com/iruka-/ATMEL_AVR/master/web/jpg/pic32mx220.jpg


目次


ブートローダーのメリット/デメリット

  • 先述のとおり、PICマイコンにブートローダーをあらかじめ書き込んでおけば、PicKit2/3が無くてもプログラムを開発したり、実行したり出来るのがメリットです。
  • しかし、ブートローダーが常駐している分だけ、使用可能なFlashメモリーが少なくなる、というデメリットが出てきます。
  • なので、ブートローダーは極力小さいほうが望ましいです。
  • PICマイコンにおいては、Config設定とよばれる、チップの初期設定ビットが32bit x 4だけ存在していて、その設定が 正しくないとうまくマイコンが動作しないという問題がありますが、ブートローダーを経由した起動では、Config設定を全く気にすることなく起動できますので、Config設定トラブルに会わずに済む、というメリットもあります。


ArduinoやPinguinoのようなお手軽開発環境は、ブートローダー使用が前提になっていることが多いです。

  • そのブートローダーの出来不出来が、作業効率や、ひいては開発の楽しさに大きく影響します。




PIC32MX220用のブートローダー

名前説明
AN1388アプリケーション・ノート1388番のサンプルプログラムとしてMicroChip社が提供している。USB - HIDデバイス以外にSerial、TCP/IP、sdcardなど、さまざまなデバイスからブートするようにカスタマイズできる。ソースファイルなどはMicroChipのサイトからダウンロード可能。
旧HID BOOTLOADER過去にMicroChip社が提供していたBOOTLOADER。PIC18F/PIC24F用。PIC32MX用が今も入手できるかどうかは不明。
HIDBoot.XUBW32用のブートローダー。
HIDBoot.X改このサイトで使用しているHIDBoot.XをPIC32MX220 F032Bチップ用に改造してシュリンクしたもの。PIC32MXを参照。




ブートローダーのための開発環境を用意する。

まず、コンパイラを入手するところから始めます。

(1)Pinguino4.X

(2)MicroChip MPLAB

  • インストール時にPIC32用のコンパイラも同時に選択してインストールするようにします。

(3)MicroChip USBフレームワーク(アプリケーションライブラリ)

  • Microchip Libraries for Applications v2013-06-15 Windows を入手してインストールします。

このあたりからmicrochip-application-libraries-v2013-06-15-windows-installer.exeを入手してインストールします。


ブートローダーのソースを入手する。

  • 元ネタはUBW32のHIDBoot.Xですが、MX220用に改造したものを用意しています。


ブートローダーをコンパイルしてみる。

  • 上記アーカイブはすでにPinguino用gccでビルド出来るようにしています。
  • Pinguino用gccに実行パスを通します。(setenv.bat)
    PATH C:\PinguinoX.3\win32\p32\bin;%PATH% 
  • Makefileに記述されているMPLABのディレクトリを各自環境に合わせます。
    # FIXME!
    #   MPLAB C32 Suite Dir ( IMPORTANT! -->  NOT MPLAB-X )
    MPLAB	= C:\Program files\MPLAB\MPLAB C32 Suite
  • MicroChipのUSBフレームワークのソースが、HIDBoot_Mips32gcc.Xと並存する位置にくるようにします。
D:\MyWorkingDir\HIDBoot_Mips32gcc.X\ ------ HIDブートローダーのソース
D:\MyWorkingDir\MicroChip\ ------Microchip Libraries for Applications v2012-10-15 Windowsの中のMicroChipディレクトリをここに配置。


ブートローダーをチップに書き込む。

  • \HIDBoot_Mips32gcc.X\ ディレクトリでmakeした後、w.batを実行。



ブートローダーを使ってPIC32MXの中を覗いてみる。

  • HIDBoot_Mips32gcc.X のファームウェアにはPIC32MXのメモリーを読み書きするコマンドが含まれています。
  • これを利用すると、PIC32MXの中を簡単に覗くことができます。
  • やってみましょう。
  • HIDBoot_Mips32gcc.X/hidmon32/hidmon32.exe を起動します。
E:\HIDBoot_Mips32gcc.X\hidmon32>hidmon32.exe
USB HID device found: 24576 bytes free
MIPS> d a0000000
a0000000 00 00 00 00 01 00 00 00  04 03 09 04 e4 16 00 9d
a0000010 20 04 00 a0 30 04 00 a0  00 00 00 a0 00 00 db 13
a0000020 01 01 00 00 ff ff ff ff  00 80 00 1d 20 00 00 00
a0000030 38 00 00 a0 00 00 00 00  01 01 07 05 01 03 40 00
a0000040 00 00 00 00 20 00 00 00  00 01 00 00 10 04 00 a0
a0000050 38 04 00 a0 01 00 00 00  01 00 00 00 01 00 00 00
a0000060 00 00 00 00 20 04 00 a0  00 00 00 00 08 04 00 a0
a0000070 00 00 00 00 00 00 00 00  80 06 00 02 00 00 29 00
a0000080 00 00 02 00 00 04 00 a0  01 00 00 00 00 00 00 00
a0000090 14 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
a00000a0 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
a00000b0 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
a00000c0 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
a00000d0 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
a00000e0 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
a00000f0 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
MIPS> l bfc00000
bfc00000 3c1a9fc0       lui     k0,0x9fc0
bfc00004 275a0010       addiu   k0,k0,16
bfc00008 03400008       jr      k0
bfc0000c 00000000       nop
bfc00010 401a6000       mfc0    k0,$12
bfc00014 7f5a04c0       ext     k0,k0,0x13,0x1
bfc00018 13400002       beqz    k0,$bfc00024
bfc0001c 00000000       nop
bfc00020 00000000       nop
bfc00024 3c1da000       lui     sp,0xa000
bfc00028 27bd2000       addiu   sp,sp,8192
bfc0002c 3c1ca001       lui     gp,0xa001
bfc00030 279c8000       addiu   gp,gp,-32768
bfc00034 40096002       mfc0    t1,$12,2
bfc00038 01205820       add     t3,t1,zero
bfc0003c 7d2a1e80       ext     t2,t1,0x1a,0x4
bfc00040 7d494984       ins     t1,t2,0x6,0x4
bfc00044 40896002       mtc0    t1,$12,2
bfc00048 41dce000       wrpgpr  gp,gp
bfc0004c 408b6002       mtc0    t3,$12,2
MIPS> p
          TRISA(0xbf886010) 0x0000001f 00000000_00000000_00000000_00011111
          PORTA(0xbf886020) 0x00000010 00000000_00000000_00000000_00010000
           LATA(0xbf886030) 0x00000008 00000000_00000000_00000000_00001000
          TRISB(0xbf886110) 0x00002fbf 00000000_00000000_00101111_10111111
          PORTB(0xbf886120) 0x000001b0 00000000_00000000_00000001_10110000
           LATB(0xbf886130) 0x00008b16 00000000_00000000_10001011_00010110
MIPS>q
  • 逆アセンブル、メモリーダンプ、ポートの状態監視が出来ます。
  • mips16の命令列を逆アセンブルしたい場合は、l(エル)コマンドの引数に与える開始番地のLSBを立ててください。(奇数番地にします)-- ARM<->Thumbと同様です。
  • PIC18spxのhidmonと同機能になる(予定)です。
  • 今のところ、メモリー/ポート書き換えが未実装です。
  • ポートアドレスをポート名から知りたいときは、その名称の途中までをタイプすると教えてくれます。
    MIPS> p port
              PORTA(0xbf886020)
           PORTACLR(0xbf886024)
           PORTASET(0xbf886028)
           PORTAINV(0xbf88602c)
              PORTB(0xbf886120)
           PORTBCLR(0xbf886124)
           PORTBSET(0xbf886128)
           PORTBINV(0xbf88612c)
  • ポートアドレスの全一覧は、
    MIPS> p ?
  • ポート内容一覧は、
    MIPS> p *
  • という具合です。



ブートローダーを使って、ユーザープログラムをデバッグする。

  • 今のところ、デバッグ用の関数やコマンドは用意していません。
  • しかし、ユーザープログラムが走った後のメモリー状態をブートローダーでダンプすることは可能です。
  • ブートローダーとワークエリアが重なっていると上書きされますので、ユーザープログラムのSRAM使用番地を少し後ろにずらすことで破壊されないようにしてメモリー内容を観察します。

MIPS32とmips16の混在について

基本的にgccなのでMicroChipのpic32-gccと、Pinguinoのmips-gccはほぼ同じです。

  • 但し、#pragma で始まる指令は、pic32-gcc限定です。mips-gccではほぼ丸無視されます。
  • MPLAB-Xと組み合わせて使うxc32のほうは、うわさによると'-mips16'オプションが使えません。(無償版で)


では、混在のさせ方ですが、

  • Cソース単位で混在させる。
    • mips16でコンパイルしたいソースは '-mips16' オプション付きで、そうでないCソースは-mips16オプション無しでコンパイル。
  • 関数単位で指定する。
    __attribute__((mips16))
    __attribute__((nomips16)) 
  • を関数アトリビュートで付けます。

しかし、以下のようにしたほうが便利です。

  • 基本的に、全体のコンパイルモードはMakefile中のビルドオプションに'-mips16'を付けるか付けないかで決めます。
  • Flashの容量が許すなら'-mips16'コンパイルオプションをつける必要はありません。
  • 容量を節約したい場合のみ、全体に'-mips16'コンパイルオプションをつけてコンパイルします。


さて、そこで'-mips16'をつけるとアセンブラ段階でエラーする関数が存在することに気づきます。

  • それらの関数は、中にasm命令か、それと同義のマクロ(例えばコプロセッサレジスタを操作する命令)が含まれて居ます。
  • 同じ働きをするmips16のasm命令に置き換えるのは面倒だったり、そもそも不可能だったり、あるいはコンパイルオプションにあわせてasm命令を差し替えるというのは現実的ではありません。
  • なので、その関数を丸ごとMIPS32指定にします。
    #define _MIPS32  __attribute__((nomips16)) 
  • のようなdefineを書いて、
    int _MIPS32 SystemInit()
    {
       ・・・
    }
  • のようにします。


  • gccの困るところとして、最適化のため勝手にインライン関数化されることがあり、それによって、たとえばSystemInit()が別の関数に埋め込まれてしまうことがあります。
  • そうすると、別の関数まで _MIPS32をあたえないといけなくなります。
  • これを避けるには、__attribute__((nomips16,noinline)) のように記述します。


あと、原則のお約束ですが、

  • リセットベクターと割り込みベクターはMIPS32でなければなりません。
  • リセットハンドラー、割り込みハンドラーから呼び出される関数はmips16でも構いません。
  • もちろん、それ以外の関数はMIPS32とmips16が混在して、相互に呼び出しても構いません。




コードサイズ削減の技

ここでは、コードサイズを4kB(FLASH領域)+3kB(BOOT領域)に収める手法を紹介いたします。

■ ファームサイズ縮小のための禁断テクニック


  • crt0.Sの改造.
    • BFC0_0000(kseg1) から 9FC0_0010(kseg0)へのlong jumpはどうしても 16byte掛かりますが、
      _reset:
             la      k0, _startup  # 2命令必要.
             jr      k0            # Jump to startup code
             nop	            # 遅延スロット(Jump命令が実行されたにも拘らず、パイプライン処理の都合上実行されてしまう命令)
  • nopを省略することで、12byteにしています。
  • nopの部分には
    _startup:
           la      sp,_stack
  • の前半の命令(lui) が入ります。
  • すなわち、lui sp,high(_stack) が、遅延スロットと飛び先で、都合2回実行されます。
  • (実害はありません)


  • while() {} ループを do {} while() ループに格下げ
    • .bssのクリアと .data のコピーのループを do while ループに格下げしています。
    • .bssと.data が零バイトの場合問題がありますが、零バイトでないことがあらかじめ
    • 確定しているので実害はありません。


  • ramfuncコピーの削除
    • ramfunc(RAM上で実行される関数)は一切定義していませんので、省略します。


  • main()の呼び出しの簡略化
           la      k0, main
           jr      k0                      # Jump to startup code
    		nop
    • を jalx main に置き換えています。
    • main関数は常に mips16であることを仮定しています。
    • mainの引数(argc,argv)の設定も省略しています。
    • また、main()から制御が戻ることはないので無限ループも省略しています。


  • 各種例外ベクターの省略
    • HID bootloaderは一切の割り込みを使用していませんのでベクターエリアを全部省略しています。
    • バスエラー等も端折っています。
    • またINTxxxx() 系のシステム関数は di,ei以外ダミーにして、付随する大きなテーブルのリンク を防いでいます。


  • mips16化と、1回しか呼ばれない小さな関数の適切なinline化、
    • それから、コンパイルオプションで、-ffunction-sections -fdata-sectionsを指定して、
    • リンカオプションで、不要関数のリンク抑止 -Wl,--gc-sections を忘れずに入れておきます。


  • コンパイラに与える -Os -G4 オプション (gpアクセス)。
    • -G4 を与えると、絶対番地アクセスがgp相対になり、1命令づつ縮みます。
    • -G4 より大きな (-G8 -G16等)を与えると逆にコードサイズが増えます。


  • 9fc0_0000〜9fc0_0bf0 までの領域に詰め込み
    • _BOOTROM_ アトリビュートを与えます。
    • 但し、inline化されることがわかっている関数については、配置がそうなることが確定
    • していても _BOOTROM_ は与えずに、static inline とだけ記述します。
  • 確実にinline化したい場合は、MACRO記述に置き換えるか、そのまま手書きでinlineします。


  • USB descripterの省略
    • 長い文字列は全部1文字にしました。
    • そうしないと合計 7kBに入らないからです。

以下、予定稿

ブートローダーの内部関数説明


ブートローダーでLチカ。

  • ポートの書き換えをhidmon32.exeから対話的に行なうことが出来ます。
  • hidmon32.exeは、この対話実行をバッチで行なうことも可能になっています。
  • なので、Lチカを行なうスクリプトをhidmon32.exeに与えることで、Lチカを実行できます。

(coming soon....)





参考リンク

以下のリンクは両方とも熟読すること。でないとMIPSとかさわっちゃだめ。

とても参考になる。

『はじめて読む MIPS(リローデッド)』 by 中森章 - CQ出版社

  • www.cqpub.co.jp/interface/TechI/Vol39/app/mips_asm.pdf

PIC32ファミリ リファレンスマニュアル CPU(日本語)