2009-05 ← → 2009-05ARM
あらすじ
- 秋月AE-18F2550基板 が1000円で売られているので、これを飼いならそうという企画。
- ちなみに、18F2550のピン数だけ増設した40pin DIP IC(18F4550)は単体で400円 、同じくDIP品の18F2550も400円で売られている。
- これをUSBマイコンとして使うのに必要最小限の配線はUSB(Bもしくはmini-B)コネクタと抵抗2個、20MHz水晶1個程度であり非常に廉価だ。(USBのプルアップ抵抗も内蔵されている)
- 参考:UBWサイトにある回路図 --> 水晶は20MHzに。(秋月のと同じファームが使えるので)。
- どちらのICとも内蔵Flash32K,RAM2K(うち1KはUSB-FIFOと共用)で、開発にはCコンパイラを使用することも出来る。
現在のステータス
- PICライターは作った --- PIC入門
- 開発環境は整えた ----- UBWを試す
- MCHIP製(C18用) bootloaderをsdcc+gpasm環境でビルドできるようにした。--- PIC入門
- diolan製(MPASM) bootloaderをAE-18F2550に移植した ------------- 下記参照のこと。
- diolan製(MPASM) bootloaderを改造してHIDmon( + bootloadHID)にしてみよう。 ----完成。
- bootloaderとしては充分高速です。
- コマンドライン上で書き込めるので、Makefileやバッチに記述すると開発サイクルが短縮できます。
- HIDデバイスなのでデバイスドライバーのインストールが不要です。
- HIDmonの機能がわりと充実しています。
- ユーザーメモリーエリア(0〜0x3ff)を一切壊さないので、ユーザープログラムの挙動観察に適します。
- ユーザープログラムからputcなどを実行するとPCのコンソールに高速に表示されます。
- コードサイズは2kB以下です。ユーザー領域は30kB使用できます。
- とりあえず、MCHIP版ブートローダーから起動するバージョンを作成
read more:HIDboot
- HID Bootloader 兼 HID monitor . お勧めです。
read more:HIDmon-2550
私はPICのライター環境やMCHIP純正開発環境のことをほとんど何も知りません。
- が、意外な純正ツールを発見致しました。
read more: PICkit2
~
- PIC18F4550 も作ってみた。
read more: PIC18F4550
- HIDmon-2550 がほぼ完成し、目的を達した。
- AVRにも2550相当のチップが欲しいところだ。
- AT90USB162 はあるが、DIP品がない 。
~
- ファームウェアをpollコマンドに対応させた。これによりgraphの描画が速くなる。
- FSR2を使用しているところが気に入らなかったので、全部FSR0に書き換えた。
- 一部動作しなくなる場面があったが変更差分攻撃(累積変更を加えていって動かなくなる修正点を見つける)で撃破。--- sm_ctrl_rx みたいな関数を呼んで戻ってきたところでFSR2の値がBDT_STATを指していることを利用している奴がいた。
- コード量が増えたので、ダイエット。
現在、余白は408バイト。
- 全ファンクション実装済みでこれだ。
- つまり1640バイトで実装できたということ。
- 同じ処理をAVR gccで書いたらコードサイズはたぶん2/3くらい?になるのではないかと思う。
~ ~ ~
-
現状のHIDmon (bootloader)
000+---------------+ | WORK AREA | 0xa6 bytes 0a6+---------------+ | 未使用 | 100+---------------+ | | | 未使用 | | | 400+---------------+ | USB BDT | 16byte 410+---------------+ | EP0 OUT BUFFER| 64byte 450+---------------+ | EP0 IN BUFFER| 64byte | 未使用 | 500+---------------+ | EP1 OUT BUFFER| 64byte 540+---------------+ | EP1 IN BUFFER| 64byte | 未使用 | 600+---------------+ | | | 未使用 | | | 7ff+---------------+
-
EP1は実際には使われていない。
000+---------------+
| |
| |
| |
| 未使用 |
| |
| |
| |
400+---------------+
| USB BDT | 32byte
420+---------------+
| |
| WORK AREA | 0xa6 bytes
500+---------------+
| EP0 OUT BUFFER| 64byte
540+---------------+
| EP0 IN BUFFER| 64byte
600+---------------+
| |
| 未使用 |
| |
7ff+---------------+
- BDTは余裕を見て8個確保(そんなにいらないと思う)
- こうすると、どのようなメリットがあるのか?
- HIDmonとして使用した場合に、アプリ側の使用メモリー(0〜0x400)をほぼそのままダンプすることが可能になる。
- BSRの値を0x04に固定することで、WORKエリアの参照以外に、BDT書き換えも同一バンクで実行できるので、FSR0を使用する必要がなくなる。
- WORK AREA内の boot_cmd[64] boot_rep[64] というパケットバッファをそのまま送受信に使ってもよいことになる(と思う)そうすると(EP0 IN BUFFERへの)メモリー転送が不要になり、速度を稼げるかもしれない。
- ためしにリンカースクリプトをちょっと書き換えて上図のような配置にしてみたが、一応動作しているようだ。
- 0番地を書き変えている奴がいる。不可能なはずだが・・・。
~ ~ ~
- もうPICに飽きたつもりだったが、もうすこしだけ追求してみる。
- 上記メモリーマップの変更は実施した。
- BIOSエントリーを用意した。
- エントリーの先頭に movlb 0x04 を入れた。
- puts / gets 関連のバッファとエントリーを設定した。
- sdccで"Hello World"のフレームワークを作成した。
- 面白いのは、ユーザーアプリ動作中でもメモリーダンプが出来ることだ。
- putcが全然動かない。・・・何故?・・・
- その後、いろいろいじっていたら、ついに動かなくなった。
- 原因は分からない。
小休止。
- 原因が分からないので諦めた。
- ソース丸ごと公開。もう煮るなり焼くなりしてください。
↓↓↓↓
read more:HIDmon-2550
~ ~ ~
バグってはいないけど動きません。何故?
-
farなポインタは3バイトであり、ROMの場合 TBLPTRL,TBLPTRH,TBLPTRU(通常は0)が指し示す。
-
RAMの場合は bit[23:16]は 0x80 になる。(判別のためわざとそうしている)
-
EEPROMのgptrは未実装である。
-
ポインタの値の受け渡しは、下位より、FSR0L,PRODL,WREG の計24ビット。
-
ポインタで参照したメモリー値は WREGで返却する。
void _gptrget1(void) __naked { __asm /* decode generic pointer MSB (in WREG) bits 6 and 7: * 00 -> code * 01 -> EEPROM * 10 -> data * 11 -> unimplemented */ btfss _WREG, 7 bra _lab_01_ # ; data pointer ; data are already in FSR0 movff _PRODL, _FSR0H movf _POSTINC0, w return # _lab_01_: ; code or eeprom btfsc _WREG, 6 bra _lab_02_ # ; code pointer movff _FSR0L, _TBLPTRL movff _PRODL, _TBLPTRH movwf _TBLPTRU # tblrd*+ # ; result in WREG movf _TABLAT, w # return # _lab_02_: ; EEPROM pointer ; unimplemented yet _end_: return __endasm; }
-
*_gptrput1 はこの逆の動作を行う。 [#j329b176]
-
_gptrput1はもうすこしあっさりしている。
-
書き込むデータ値は FSR1(stackptr) からPOP(PREINC1)して取得している。
-
FSR1が+1される(ユーザースタックレベルが変化する)
-
RAM以外は実装されていない。
void _gptrput1(void) __naked
{
__asm
/* decode generic pointer MSB (in WREG) bits 6 and 7:
* 00 -> code (unimplemented)
* 01 -> EEPROM (unimplemented)
* 10 -> data
* 11 -> unimplemented
*/
btfss _WREG, 7
bra _lab_01_
/* data pointer */
/* data are already in FSR0 */
movff _PRODL, _FSR0H
movff _PREINC1, _POSTINC0
return
_lab_01_:
/* code or eeprom */
btfss _WREG, 6
return
/* code pointer, cannot write code pointers */
_lab_02_:
/* EEPROM pointer */
/* unimplemented yet */
return
__endasm;
}
~ ~
- いっそのこと [FSR0L,PRODL,WREG]渡しを止めて、[FSR0L,FSR0H]を引数にして
- 直接 movff _PREINC1, _POSTINC0 とか movwf _POSTINC0すればよいのに。
- と、思っていたら、 near という(16ビットの)ポインタがあって、そっちだと
_gptrput1が呼ばれずに、以下のコードが落ちる。
003A 00459 _00105_DS_:
003A C000 FFE9 00460 MOVFF r0x00, FSR0L
003E C000 FFF3 00461 MOVFF r0x01, PRODL
0042 5000 00462 MOVF r0x02, W
0044 EC00 F000 00463 CALL __gptrget1
0048 6E00 00464 MOVWF r0x05
004A 5000 00465 MOVF r0x05, W
004C E000 00466 BZ _00108_DS_
00467 ; .line 27; main.c *t++ = *s++;
---> t++ を実行している。 ポインタ参照はすでに上記で実行済み
004E 2A00 00468 INCF r0x00, F
0050 B0D8 00469 BTFSC STATUS, 0
0052 2A00 00470 INCF r0x01, F
0054 B0D8 00471 BTFSC STATUS, 0
0056 2A00 00472 INCF r0x02, F
---> *s++ = val を実行している。
0058 C000 FFE9 00473 MOVFF r0x03, FSR0L
005C C000 FFEA 00474 MOVFF r0x04, FSR0H
0060 C000 FFEF 00475 MOVFF r0x05, INDF0
0064 2A00 00476 INCF r0x03, F
0066 B0D8 00477 BTFSC STATUS, 0
0068 2A00 00478 INCF r0x04, F
006A D000 00479 BRA _00105_DS_
- 上記のコードはROM上のsポインタからRAM上のtポインタに文字列コピー(strcpy)
するコードだ。
- つまり1文字をコピーするのに18ステップ+_gptrget1の処理ステップ数掛かる。
- MOVFFが多用されているので、1ステップ2クロック掛かる部分が多い。
- 仮に50クロック/文字だとして、64バイトコピーは3200クロックも掛かるのだ。
- さすがにだめだろ。
- できたてのHIDmon(ドッグフード)を使って、sdccのポインタに関する挙動を調査していくと、ある事実にぶち当たった。
ある事実とは
- ポインタのインクリメントに失敗するのだ。
-
まさに、このコード!
---> t++ を実行している。 ポインタ参照はすでに上記で実行済み 004E 2A00 00468 INCF r0x00, F 0050 B0D8 00469 BTFSC STATUS, 0 0052 2A00 00470 INCF r0x01, F 0054 B0D8 00471 BTFSC STATUS, 0 0056 2A00 00472 INCF r0x02, F
-
さいしょのINCF r0x00, F が機能しない。そんな馬鹿な!
-
機能はしているのだけれど、0番地だけビット化け(0xc7マスクが常時発生している感じ)なのだ。
-
0番地以外(0〜3ffまで見た)は大丈夫。
-
なんで????
- 上の騒動はメモリー破壊とか、見えないDMAではなかった。
- HIDmon(picmon.exe) のUSB端子マスク機能が悪さをしていた。
- HIDmon(picmon.exe) を修正して、0番地問題は解決。
で、sdccが全く(意図に沿って)動かない原因も分かった。
003A C000 FFE9 00460 MOVFF r0x00, FSR0L
003E C000 FFF3 00461 MOVFF r0x01, PRODL
0042 5000 00462 MOVF r0x02, W <==この命令がバグっていた。
0044 EC00 F000 00463 CALL __gptrget1
- 上記3行目のMOVF がまともにr0x02の仮想レジスタをフェッチしていないのだ。
- 理由は、AccessBankだ。このローダーはXINST=1で焼いているので、sdccのバイナリーを正しく動作させることは出来ないわけだ。
- どうりで、ポインタ関連のアクセスが全滅だったわけだ
~ ~ ~ ~ ~ ~ ~ ~ ~ ~
- GET_DESCRIPTORでstring tableが取得できないようだ。
- SnoopyProで調べてみると、おかしなGET_DESCRIPTORリクエストが出ている。
- オリジナルではちゃんと動いているのかな?
- これもXINSTビットの呪いか?
- 比較実験のために、外部ライターで焼き直すのが面倒くさくなってきた。
- もう一台作るか・・・
~
- diolanのオリジナルに非常に近いROMで試したけれど、string tableが取得できていない。
- SnoopyProの結果はだいたい同じだ。
- じゃあ、sdcc版Firmware-Bと比較するしかない。
~
-
オチはこうだ。sdcc版Firmware-Bはstring tableが省略されていた。
-
そして、 最後のオチとしては、
#define USB_MFG_DESC_off 12
-
この12が曲者で、なんと16進だから 0x12 = 18になっていたのだ。(がーん)
-
このアセンブラは10進で数値を書きたいときは .12 と書くらしい。(それ 0.12じゃん)
- 落とし穴だらけ
- そして修正済みファームでもstring tableを取得できない。
- string tableの先頭2バイトだけがパケット転送されている。
原因はわりとすぐに分かった。
- requestパケット内の wLengthが 0x0402 になっていて
- 返却するstring table は 0x0eバイト長だが、 wLengthの下位1バイトと比較して短いほうを選択している。
- このコーディング、激しくダメじゃん。
- おまけに string table は MANUFACTUREだけしかサポートされていなかった。
- そこいらへんを修正して、HID_Reportの送受テストをやろうとしたがなかなかうまくいかない。
- Interrupt転送(というかdiolan版HID REPORT)のときとControl転送のときでは、HID_Reportのバッファ形式が異なるようだ。
- Interrupt転送版には、USBパケットの先頭に挿入される HID ReportIDが省略されている。
- Reportの種類は1種類だけ、サイズのバリエーションがないというわけか。
~ ~
- cpfseq はあるが、cpfsneは無い。それから、これらの比較命令はリテラル(定数)版が欠落。
- Wregの値によって複数分岐する処理が書けない。
- cpfsgt,cpfsltはあるが、cpfsgeとかcpfsleがない。
- sublwはあるがcmplw(つまりWregを破壊しない版)がない。
- 条件分岐のbnzとかbzの分岐オフセットが8ビットしかない。
- レジスタが異常に少ない(Wregだけ?)ので割り込みで退避するコンテクストが少なそうに見えるが実は大量に必要で、退避操作も大変。
- TBLPTRU,TBLPTRH,TBLPTRL,TBLLAT
- PRODH,PRODL,
- BSR
- FSR0H,FSR0L,
- FSR1H,FSR1L,
~ ~ ~ ~
Linuxモデルの返品率は、Windowsモデルの返品率よりも高い?
- むしろVistaモデルを返品したい気持ち。(アプリが動かないとかで)
- Linuxなら最初から動かないのを承知で買うので無問題。
- ARM搭載のubuntuノートなら大歓迎だが。
- 一般PCショップで売るのは難しいのかも。
- だが、現実に玄箱とかの例があるので不可能というわけではない。
~ ~ ~ ~
- ECHOBACK はOK。
- メモリーダンプ(現在は仮実装で、ROMダンプ)まで出来た。
躓き(落とし穴)
- スキップ命令の後ろに2ワード命令(goto ,callなど)を入れてはいけない。ハングする。
- boot_rep[64]の送信バッファはboot_cmd[64]受信バッファのコピー置き場にすこし使っていた。
- が、これを上書きするような処理を書いてしまってはまった。
- 変数の大小判断マクロを書いてみたが、なかなかちゃんと動かなかった。(書き方がまずかった)
- だって、引き算の方向が普通のCPUと逆だもん。
- USBの受信FIFOは、処理時間が長いときには次のパケットで書き潰されることがあるようだ。
- 次のパケットが来る前に別のバッファにコピーしないと駄目なの(????)
現在進行中。 - 実は非常に難儀している。 - とりあえずUSBデバイスとして認識し、ProductStringなどを正しく参照できるようにしたところだが - 現在、HID_Reportのやりとりでパケット同期タイミングずれがあり(ひとつ古いパケットが蓄積されていてハンドシェイクがうまくいってない)四苦八苦しているところ。 - ハンドシェイク問題はほぼ解決した。 - このPICはノイズパケットをいろいろ受信している(?)ようで、それらを無視するような条件をいれないとだめ。 - 具体的には、HID_ReportIDとCMDが正しい値域に納まっているかどうかを検査する。 - 範囲外のパケットを無視しないと上記のような問題が発生する。
HIDmon-2550 を実装中
- Flash書き込みと消去を実装
- ホスト側ツールも仮制作
- 単独のブートローダーとしての動作を確認。
- このブートローダーにアプリケーションの書き込みを行わせて、アプリケーションが起動することを確認。
~
read more: HIDmon-2550
~ ~ ~
- http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en533924
- http://www.microfan.jp/shop/45_230.html
- 20ピンなんだけど、あんまりお勧めしないなぁ・・・。
- だって33円足すと18F2550とか18F4550が買えるわけだし。
- 省ピンでないと困ることって、ある?
- むしろ、アレか。1.8Vから5.5Vまでいけるので、JTAGアダプター作りにはGoodかも。
- 電圧だけ下げた2550だってある。735円だ。
- http://www.microfan.jp/shop/45_89.html
- もちろん、PIC18F14K50-DIP20だって秋月が扱えばもっと安いとは思う。
- 思うけれど、あそこって大量仕入れだから扱わないだろうなぁ・・・・
~ ~ ~
- HIDmon-2550のgpasm対応をやっていたが、最終的に、諦めた。
- gpasmが悪いのは確かだが、どこが悪いのかがついに分からなかった。
- 例のUSBディスクリプタのアセンブラ記述だが、どのような書き方をしても正しいコードを落とすことが出来ないのには参った。
- どうやったら、このような嫌がらせアセンブラを作ることが出来るのか。
- 理解に苦しむ。
- PICのアーキテクチャーそのものも嫌がらせレベルは3くらいあるが、
- gpasmのUSBディスクリプタが記述できない嫌らしさレベルはフェーズ5くらいあると思う。
詳細は、HIDmon-2550のソースファイルにあるMakefile.gpasmを使ってみれば分かります。
~ ~ ~
- ----- 日記が長くなりすぎたので分割 ------
~