HIDBootX
関連リンク PIC32MX Pinguinoで遊ぼう ブートローダーを作る USB仮想シリアル USBカスタムデバイス USBホスト 2013-02 2013-03
ブートローダーって、何?
- PICマイコンに最初にプログラムを書き込むには、必ずPICライター(PicKit2/3)が必要です。
- しかし、あらかじめ小さなプログラムを書き込んでおいて、そのプログラムの助けにより、残った領域に別のプログラムを転送して実行する、ということが可能だとしたら、2回目以降の開発ではPICライターが不要になるというメリットがあります。
- そのような手助けをする(比較的)小さなプログラムのことをブートローダーと呼んでいます。
目次
ブートローダーのメリット/デメリット
- 先述のとおり、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.X | UBW32用のブートローダー。 |
HIDBoot.X改 | このサイトで使用しているHIDBoot.XをPIC32MX220 F032Bチップ用に改造してシュリンクしたもの。PIC32MXを参照。 |
ブートローダーのための開発環境を用意する。
まず、コンパイラを入手するところから始めます。
(1)Pinguino4.X
- ダウンロード: http://code.google.com/p/pinguino32/downloads/list
- 安定版のPinguino-X4-EasyPack-Win32-Rev999-v01-2014-04.7z をダウンロードして D:\x4-easy-rev999\ に展開してください。
(2)MicroChip MPLAB
- 紛らわしいのですが、新しいほうのMPLABXは駄目です
- MicroChipのサイトから、MPLAB_IDE_8_89.zipを入手してインストールします。
- インストール時に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用に改造したものを用意しています。
- ダウンロード:HIDBoot_Mips32gcc.X.zip
ブートローダーをコンパイルしてみる。
- 上記アーカイブはすでに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命令が実行されたにも拘らず、パイプライン処理の都合上実行されてしまう命令)
- BFC0_0000(kseg1) から 9FC0_0010(kseg0)へのlong jumpはどうしても 16byte掛かりますが、
- 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(日本語)