HIDasp高速化

From:2008-09

From:HIDasp

このページは、瓶詰堂さんのHIDaspで実装されているHID Reportの転送速度向上について考察したものです

  • 時間軸が逆になっていて、上の情報のほうが新しいです。
  • 最新版アーカイブと、制作に関する説明は、HIDaspのページをご覧ください。

HIDasp速度比較


AVRUSB FLOW_CONTROLの追加

AMDマザーで計測.(SiSではこれより遅いです.)

8kB-d0read1.453 sec (5.637kB/s)
8kB-d0write & verify3.157 sec (2.594kB/s)
bash-3.2$ time ./hidspx.exe -ph -d0 8kB.hex
Detected device is ATmega88.
Erase Flash memory.
Write   Flash: 8192/8192 B
Verify  Flash: 8192/8192 B
Passed.

real    0m3.157s
user    0m0.015s
sys     0m0.015s
bash-3.2$ time ./hidspx.exe -ph -d0 -rp >rom.hex
Detected device is ATmega88.
Read    Flash: 8192/8192 B
Passed.

real    0m1.453s
user    0m0.047s
sys     0m0.000s

HID Report送信の遅延評価

  • HID Reportのパケットを受け取った後、isp書き込み時間がどの程度許されるのかを評価してみた。
  • 結果
  • bench2コマンドは第一引数が試行回数、第二引数が遅延係数(単位は10μ秒)
  • HIDパケットサイズは39バイト固定。送信(ホストからデバイス宛)のみ。
  • Low Speed USBパケットを8バイト受け取る毎に待ちループを呼び出している。
X:UHCI(intel i815e)>hidmon -i script
AVR> bench2 100 1
hid delay write start
hid delay write end, 3900 bytes/801 ms,  4868 bytes/s
AVR> bench2 100 2
hid delay write start
hid delay write end, 3900 bytes/791 ms,  4930 bytes/s
AVR> bench2 100 3
hid delay write start
hid delay write end, 3900 bytes/801 ms,  4868 bytes/s
AVR> bench2 100 4
hid delay write start
hid delay write end, 3900 bytes/811 ms,  4808 bytes/s
AVR> bench2 100 5
hid delay write start
hid delay write end, 3900 bytes/801 ms,  4868 bytes/s
AVR> bench2 100 6
hid delay write start
hid delay write end, 3900 bytes/801 ms,  4868 bytes/s
AVR> bench2 100 7
hid delay write start
hid delay write end, 3900 bytes/801 ms,  4868 bytes/s
AVR> bench2 100 8
hid delay write start
hid delay write end, 3900 bytes/801 ms,  4868 bytes/s
AVR> bench2 100 9
hid delay write start
hid delay write end, 3900 bytes/791 ms,  4930 bytes/s
AVR> bench2 100 10
hid delay write start
hid delay write end, 3900 bytes/802 ms,  4862 bytes/s
AVR> bench2 100 20
hid delay write start
hid delay write end, 3900 bytes/801 ms,  4868 bytes/s
AVR> bench2 100 30
hid delay write start
hid delay write end, 3900 bytes/801 ms,  4868 bytes/s
AVR> bench2 100 40
hid delay write start
hid delay write end, 3900 bytes/801 ms,  4868 bytes/s
AVR> bench2 100 50
hid delay write start
hid delay write end, 3900 bytes/801 ms,  4868 bytes/s
AVR> bench2 100 60
hid delay write start
hid delay write end, 3900 bytes/801 ms,  4868 bytes/s
AVR> bench2 100 70
hid delay write start
hid delay write end, 3900 bytes/802 ms,  4862 bytes/s
AVR> bench2 100 80
hid delay write start
hid delay write end, 3900 bytes/801 ms,  4868 bytes/s
AVR> bench2 100 90
hid delay write start
hid delay write end, 3900 bytes/1302 ms,  2995 bytes/s
AVR> bench2 100 110
hid delay write start
hid delay write end, 3900 bytes/1302 ms,  2995 bytes/s
AVR> bench2 100 120
hid delay write start
hid delay write end, 3900 bytes/1301 ms,  2997 bytes/s
AVR> bench2 100 130
hid delay write start
hid delay write end, 3900 bytes/1301 ms,  2997 bytes/s
AVR> bench2 100 140
hid delay write start
hid delay write end, 3900 bytes/1302 ms,  2995 bytes/s
AVR> bench2 100 150
hid delay write start
hid delay write end, 3900 bytes/1292 ms,  3018 bytes/s
AVR> bench2 100 160
hid delay write start
hid delay write end, 3900 bytes/1402 ms,  2781 bytes/s
AVR> bench2 100 170
hid delay write start
hid delay write end, 3900 bytes/1803 ms,  2163 bytes/s
AVR> bench2 100 180
hid delay write start
hid delay write end, 3900 bytes/1792 ms,  2176 bytes/s
AVR> bench2 100 190
hid delay write start
hid delay write end, 3900 bytes/1803 ms,  2163 bytes/s
AVR> q
Bye.
X:OHCI(SiS M671T)>hidmon -i script
AVR> bench2 100 1
hid delay write start
hid delay write end, 3900 bytes/203 ms,  19211 bytes/s
AVR> bench2 100 2
hid delay write start
hid delay write end, 3900 bytes/297 ms,  13131 bytes/s
AVR> bench2 100 3
hid delay write start
hid delay write end, 3900 bytes/296 ms,  13175 bytes/s
AVR> bench2 100 4
hid delay write start
hid delay write end, 3900 bytes/329 ms,  11854 bytes/s
AVR> bench2 100 5
hid delay write start
hid delay write end, 3900 bytes/391 ms,  9974 bytes/s
AVR> bench2 100 6
hid delay write start
hid delay write end, 3900 bytes/406 ms,  9605 bytes/s
AVR> bench2 100 7
hid delay write start
hid delay write end, 3900 bytes/453 ms,  8609 bytes/s
AVR> bench2 100 8
hid delay write start
hid delay write end, 3900 bytes/485 ms,  8041 bytes/s
AVR> bench2 100 9
hid delay write start
hid delay write end, 3900 bytes/500 ms,  7800 bytes/s
AVR> bench2 100 10
hid delay write start
hid delay write end, 3900 bytes/500 ms,  7800 bytes/s
AVR> bench2 100 20
hid delay write start
hid delay write end, 3900 bytes/797 ms,  4893 bytes/s
AVR> bench2 100 30
hid delay write start
hid delay write end, 3900 bytes/1188 ms,  3282 bytes/s
AVR> bench2 100 40
hid delay write start
hid delay write end, 3900 bytes/1563 ms,  2495 bytes/s
AVR> bench2 100 50
hid delay write start
hid delay write end, 3900 bytes/1828 ms,  2133 bytes/s
AVR> bench2 100 60
hid delay write start
hid delay write end, 3900 bytes/2203 ms,  1770 bytes/s
AVR> bench2 100 70
hid delay write start
hid delay write end, 3900 bytes/2515 ms,  1550 bytes/s
AVR> bench2 100 80
hid delay write start
hid delay write end, 3900 bytes/2844 ms,  1371 bytes/s
AVR> bench2 100 90
hid delay write start
hid delay write end, 3900 bytes/3203 ms,  1217 bytes/s
AVR> bench2 100 110
hid delay write start
hid delay write end, 3900 bytes/3875 ms,  1006 bytes/s
AVR> bench2 100 120
hid delay write start
hid delay write end, 3900 bytes/4172 ms,  934 bytes/s
AVR> bench2 100 130
hid delay write start
hid delay write end, 3900 bytes/4500 ms,  866 bytes/s
AVR> bench2 100 140
hid delay write start
hid delay write end, 3900 bytes/4796 ms,  813 bytes/s
AVR> bench2 100 150
hid delay write start
hid delay write end, 3900 bytes/5188 ms,  751 bytes/s
AVR> bench2 100 160
hid delay write start
hid delay write end, 3900 bytes/5562 ms,  701 bytes/s
AVR> bench2 100 170
hid delay write start
hid delay write end, 3900 bytes/5828 ms,  669 bytes/s
AVR> bench2 100 180
hid delay write start
hid delay write end, 3900 bytes/6188 ms,  630 bytes/s
AVR> bench2 100 190
hid delay write start
hid delay write end, 3900 bytes/6531 ms,  597 bytes/s
AVR> q
Bye.
    
  • 結果、なんとUHCIでは1パケット毎に直後800μ秒のISPコマンド処理時間があったとしても、コンスタントに4.8kB/秒の転送速度を保っている!
  • つまり、HIDでもインターリーブの効果はあるということだ。
  • 逆に、OHCIでは800μ秒のISPコマンド処理時間があった場合、1.3kB/秒にまで転送速度が落ちている。
  • これは謎だ。

ちなみに '-d3'オプションを指定した場合のSCLKは375kHzなので、

  • 8ビットのSPIを送るのに必要な時間は21.33μ秒。
  • 8バイトのファームウェアをページバッファにセットアップする時間はその32倍の683μ秒あれば良い。
    • (SPIコマンドは常に4バイト単位になっていて、1コマンドで正味データ1バイト分しか転送できない)

ファームウェアの容量削減

  • senshuさんにより取り纏め頂いたhidspx-0928b.zipに対して、firmware容量の削減を 行いました。
  • 機能やコンフィギュレーションは同一です。
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
avr-size --mcu=attiny2313 main.elf
  text    data     bss     dec     hex filename
  2014       4      88    2106     83a main.elf 
  • 1988バイトは気の迷いでした。(とあるコーディングミスで最適化され機能削除されていました。)
  • WinAVRのバージョンは2006-0421でビルドしていますので、最新のWinAVRではサイズが変わる(増える)かもしれません。

おまけソフト


SCK、MOSIのHi−Z化改良

  • senshuさんにより取り纏め頂いたhidspx-0928.zipに対して、SCK、MOSIのHi−Z化を 行いました。
  • ライターが書き込み動作を行っていないときはSCK、MOSIをHi−Zにします。

高速化改良その6

  • address_set , page_write , isp_command を全部含んだ融合コマンドを実装しました。
  • tiny2313では、かなり有効かと思われます。
  • それに伴って命令セット体系に少し変更が生じました。
  • ソースは現在整理中です。乞うご期待。 任務完了!

おまけソフト

注意

  • HIDmonを使用される場合は命令Fusionが使えませんので、 --使えるようにしました。
    #define INCLUDE_FUSION       0
    #define INCLUDE_MONITOR_CMD  1
    でファームを作成しなおしてください。
  • ポートの方向初期値を適切に設定してあげてください。

たぶん、ここらで打ち止めです。

  • 残りはHIDmonの若干の機能強化と、汎用USB I/O DLLもどきの作成。

高速化改良その5

変更内容(わりと堅実路線)

  • '-d<delay>' オプションのクロックをUSBaspに近づけるように努力しました。
    • 詳細は firmware/main.c にあります。
  • hidasp.c を少しリライトしました。
    • デバッグ文が多くて見づらかったためです。
  • メモリーを減らしました。
    • -d<delay> で思いのほかメモリー使用量が増えたので、ダイエットです。
  • タイマー0によるUSIクロック生成は諦めました。
    • うまくいく方法が見つかったらこっそり教えてください。

高速化改良その4

  • 紆余曲折の末、8バイト単位でのインターリーブ転送を実施することに決定しました。
  • つまり、図で書くとこんな感じ
    時間軸 0mS                         1mS                           2mS  
           +--------------------------+-----------------------------+------------------ ...
           |USB転送|                  |USB転送|                     |USB転送|
           +--------------------------+-----------------------------+------------------ ...
                    |<=ISP書き込み===>|        |<=ISP書き込み======>|        |<=ISP書き込み
  • 目標では5kB/S(但しSCLK1MHz弱程度を想定)
  • SCLKはタイマー0で与える。
  • タイマー0のプリスケーラと分周比のペアを専用のコマンドで与える。
  • ディレイオプション -d<delay> からペア値への変換表はホストPC側が担当する。

さて、うまくいきますやら・・・

結論

  • 駄目でした。(テストマシンはintel UHCI)
    8kB-d0read1.862 sec (4.4kB/s)
    8kB-d0write & verify5.368 sec (1.5kB/s)

ちなみにfirmのサイズは2048きっかりでした。

  • LED,MONITORの両方削除
  • アンロール削除
  • vendor,productIDを1字に。
  • HID Reportディスクリプタは最小限の3個(6,38,70)

そもそも5kB/Sは読みか書きのどちらかの速度です。

  • 転送ベンチが5kだったので、書き&ベリファイで5kも出るわけないです。
  • あと、現在の実装は書き込みパケットにかなり無駄があります。(page_writeの前後の2パケットは内包可能)

コードエリアが足りません。(これは深刻・・・)

avr-size --mcu=attiny2313 main.elf
  text    data     bss     dec     hex filename
  2034       4      59    2097     831 main.elf
  • 100mSのディレイが残っていたので、差し引くと1.762 sec
  • 書き込み時には無駄パケットが少し多いので、パケットをうまく統合するとreadの倍の3.6 sec 程度まで行くかもしれません。
  • でも、どう頑張っても5Kの半分(2.5k/s)よりは出ないでしょう。
  • だったら、今の最速(W&Vで2K弱)をキープして、コード保守するほうが堅実なのかもしれません。

実計測結果
テストアーカイブ:hidspx-test-0925.zip

  • マシン=Athlon64 USBホスト=VIAチップPCIカード(UHCI)
  • delayループ取り払い状態(1.5MHz SCLK)
  • ATmega88へ8192バイトのHEXを書き込み&ベリファイ
    bash-3.2$ time ./hidspx.exe -ph -d0 xx.hex
    Detected device is ATmega88.
    Erase Flash memory.
    Write   Flash: 8192/8192 B
    Verify  Flash: 8192/8192 B
    Passed.
    
    real    0m3.859s
    user    0m0.031s
    sys     0m0.015s
  • ATmega88から8192バイトのHEXを読み込み
    bash-3.2$ time ./hidspx.exe -ph -d0 -rp >a.hex
    Detected device is ATmega88.
    Read    Flash: 8192/8192 B
    Passed.
    
    real    0m1.438s
    user    0m0.015s
    sys     0m0.000s
    bash-3.2$
  • まとめ(テストマシンはAMD + UHCI)
  • delay()ループ完全取り払い(SCLK=1.5MHz)
    8kB-d0read1.438 sec (5.696kB/s)
    8kB-d0write & verify3.859 sec (2.122kB/s)
  • 書き込みパケット数を減量(統合)すれば、write & verifyはもうすこし速くなりますので、2.5kB/sも夢ではないかもしれませんが、そのまえにコードサイズのスリム化が必要です。(どなたか挑戦しませんか、アーカイブは上記のhidspx-test-0925.zipです)

とりあえず、いい夢を見させてくれました。ありがとう>USBasp


AVRbench

usbFunctionWrite()の性能テスト.

  • usbFunctionWrite()内に遅延関数の呼び出しを入れる。
  • どのくらい遅延すると、ホスト側の速度に影響するかのテスト.
  • 0060 番地が遅延係数で 1 増えるごとに100uS
  • 使い方は win32/ で
     term -iscript

テストアーカイブ:AVRbench.zip


高速化改良その3

  • たいした変更ではありませんので、ここに差分を書きます。
static uint8_t usi_trans(uint8_t data){
	USIDR=data;
	USISR=(1<<USIOIF);
+	if(wait==0) {
+	uchar CR0=(1<<USIWM0)|(1<<USICS1)|(1<<USITC);
+	uchar CR1=(1<<USIWM0)|(1<<USICS1)|(1<<USITC)|(1<<USICLK);
+	{
+		USICR=CR0;	asm("nop");	USICR=CR1;	asm("nop");
+		USICR=CR0;	asm("nop");	USICR=CR1;	asm("nop");
+		USICR=CR0;	asm("nop");	USICR=CR1;	asm("nop");
+		USICR=CR0;	asm("nop");	USICR=CR1;	asm("nop");
+		USICR=CR0;	asm("nop");	USICR=CR1;	asm("nop");
+		USICR=CR0;	asm("nop");	USICR=CR1;	asm("nop");
+		USICR=CR0;	asm("nop");	USICR=CR1;	asm("nop");
+		USICR=CR0;	asm("nop");	USICR=CR1;	asm("nop");
+	}
+	return USIDR;
+	}else{
		do{
			delay(wait);
			USICR=(1<<USIWM0)|(1<<USICS1)|(1<<USICLK)|(1<<USITC);
		} while(!(USISR&(1<<USIOIF)));
+	}
	return USIDR;
}
bash-2.02$ time ./hidspx.exe -rp >1
Detected device is ATmega88.
Read    Flash: 8192/8192 B
Passed.

real    0m1.915s
user    0m0.015s
sys     0m0.000s
bash-2.02$ time ./hidspx.exe 8K.hex
Detected device is ATmega88.
Erase Flash memory.
Write   Flash: 8192/8192 B
Verify  Flash: 8192/8192 B
Passed.

real    0m4.247s
user    0m0.015s
sys     0m0.000s

高速化改良(案のみ)

  • 書き込みのみですが、32バイトのHID Reportを送りつけられている間もSPI転送するような ソリューションが考えられます。
  • 具体的には、usbFunctionWrite()で全部貯めてからSPI書き込みではなくて、8バイト貯まったHEXをそのつどSPIで送るといった方法です。
  • 読み出しは今のところAVRUSB側にポインタと長さを渡してそれっきり、という方法でやってますのでこれも、usbFunctionReadに引っ掛けて、ちまちま読み出す、というところでしょうか。
  • ただし、delayオプションが長い場合はUSB転送の足を思いっきり引っ張るような気もしています。
  • readに対してだけ実際に試してみましたが、8Kのreadに1.4秒が限界でした.

高速化改良その2

  • たいした変更ではありませんので、ここに差分を書きます。
    static uint8_t usi_trans(uint8_t data){
    	USIDR=data;
     	USISR=(1<<USIOIF);
    +	if(wait==0) {
    +		do{
    +			USICR=(1<<USIWM0)|(1<<USICS1)|(1<<USICLK)|(1<<USITC);
    +		} while(!(USISR&(1<<USIOIF)));
    +		return USIDR;
    +	}else{
    		do{
    			delay(wait);
    			USICR=(1<<USIWM0)|(1<<USICS1)|(1<<USICLK)|(1<<USITC);
    		} while(!(USISR&(1<<USIOIF)));
    +	}
    	return USIDR;
    }
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
bash checksize main.elf 2048 128
ROM: 2010 bytes (data=4)
RAM: 93 bytes

残りあと38バイトとなりました。

6KB書き込み&ベリファイ時間計測

HIDspx -ph -d<Delay> test.hex 
DelayTime
-d04秒
-d15秒
-d48秒
  • 計測に使用したマザーボードはSiS製です。
  • マザーとHIDaspの間にはELECOM製のUSB1.1ハブが挟まっています。

ToDo:

  • 時間計測コマンド(unixのtime相当)をちゃんと作る。
    • 今使っているのは自作ツールで 16bit MSDOSのコード。
      1993/07/09  02:33            11,682 WATCH.EXE
  • そろそろATmega88では限界なのでATmega644のボードを(書き込み時間計測のためだけに)制作する。
  • Snoopy−Proを使ってUSB転送の様子を観察し、パケット送信間隔等を評価する。

なんかこう、AVRに高速に書き込むことが目的化してしまいました。笑っちゃいます。


高速化改良その1

内容

  • パケットサイズのバリエーションをさらに増やしてみました。
  • usbHidReportDescriptorを6種類用意して、サイズをそれぞれ、6,14,22,30,38,46 にしました。
  • 但し、読み込みパケットサイズは38に留めています。(現在のところ固定)
  • waitのループを瓶詰堂さんのオリジナルに戻しています(nopを消してしまいました:実は書き込み速度向上にはこれ+オプション「-d0」が一番効きます。)
  • さらに速くするには、-d0のときはdelay()を外したusi_trans()を用意するか、あるいはHIDaspを20MHz動作させる等が考えられますが、ターゲットデバイスが遅い場合は意味がありません。

ダウンロードはこちらです:hidspx-src-0920.zip

  • 書き込み速度の実測値ですが、ATmega88に6kBの書き込み&ベリファイで約5秒に短縮しました。
  • CMD_PEEKとCMD_POKEを追加してあります。(未デバッグ)
  • また、サイズは以下の通りです。
    avr-objcopy -j .text -j .data -O ihex main.elf main.hex
    bash checksize main.elf 2048 128
    ROM: 1992 bytes (data=4)
    RAM: 93 bytes

追加:

  • HIDsphの '-d4'オプションのほうが、こちらの '-d0'書き込みより速いのではないか疑惑
    • 上記調査中です。

HIDasp高速化完了

千秋様のHPへのリンク

瓶詰堂さんのHPへのリンク

senshu様から頂いたソースと、瓶詰堂さんのHIDaspソースをもとに 作成しました。

ディレクトリ構成

| hidspx-src/ HIDspx.exe とそのソースです。
| firmware/   AVR側のファームウェアHEXとそのソースです。

変更内容:

  • HID Reportの転送をHidD_GetFeature()/HidD_SetFeature()で行うようにしました。
  • usbHidReportDescriptorを4種類用意して、サイズをそれぞれ、6,14,22,30にしました。
  • 変更点はたったそれだけです。
  • 上位プロトコルは瓶詰堂さんのHIDaspと同じままです。
  • 配線はUSBのD+,D-だけを PORTD,3,2 に変えて、他は瓶詰堂さんのHIDaspと同じです。
  • usbHidReportDescriptorが異なりますので、上記exeとファームはペアでないと動作しません。

ダウンロードはこちらです:hidspx-src-0919.zip

  • 期待したほど速くはなりませんでした。
    • ATmega88に6KBのHEXを書き込み&ベリファイにて、所要時間6秒(AMDマザーボード)
    • (後でPentium4マシンで追試しましたところ9秒でしたので、実は遅くなっただけかも・・・)
  • USBの挿抜に対しては安定です。
  • コードサイズは以下のとおりです。特に縮める作業はやっていません。
    avr-objcopy -j .text -j .data -O ihex main.elf main.hex
    bash checksize main.elf 8192 1024
    ROM: 1962 bytes (data=4)
    RAM: 86 bytes

考察

  • HID Report30バイトの書き込みと読み出しを交互に1000回実行してみます。
  • 実行方法は、hidasp.cのソースにて、最初に4回testパケットを送る部分を改造して、30バイトのHID Reportを送るようにします。
  • ループ回数を1000に増やします。
  • hidspx.exe -ph -r を実行します。(書き込みデバイスは無くてよい)
  • OHCIのUSB付きマザーで試したところ、1000回の試行に6秒掛かりましたが、UHCIではその倍の12秒でした。
  • すなわち、30バイトのHID Reportを送るのに3フレーム(OHCI)もしくは6フレーム(UHCI)掛かる計算になります。
  • より正確な時間の推移を知りたい場合はSnoopy-proというWindows上のUSBスニッファーを入れて、パケットをキャプチャーすると良いです。
  • 30バイト単位でただひたすら読むだけなら、10kB/s もしくは 5kB/s くらいの速度が出るはずなのに、実際にSPI転送が入るとその1/10あるいは1/5になるのは、なにか要因があるのだと思いますが、まだ追求できていません。





落穂拾い

  • 配線は森芳電子さんの方法が洗練されているので、それに合わせたい。
    • クロック出力をターゲットに与えることが可能になっている。
    • LEDをPB2,PB3に移してあるので、TxD,RxDを使用可能になっている。(実際tiny2313ではコードを入れる空きがないけれど、同じ基板を別の用途に転用しやすい)
    • 但し、D+,D-の配置がさらに変わるので、usbdrv/以下のいくつかのソースにまで手を入れなければならない。
    • さらに、INT0がINT1に変わるので、最小限必要な割り込みベクタが2バイト増加する。
  • AVRmonitの一部の機能(RAMやI/Oに対するPEEK/POKE命令)を組み込んで、代わりにLED制御はそっちでやる。
    • すると、余ったポートにPWMを設定して昇圧回路をドライブするとか、変な付加機能をPC側から好きなように実装できる。
    • AVRを焼かないときでも汎用IO代わりに流用できるようになる。
    • AVRを焼いた直後にターゲットをリセット起動させたり、ターゲットとSPIクロス接続になっているので通信(printfデバッグ)したり出来るようになる(かも)
  • 高速化したいけれど・・・。(無理かなぁ)
    • すzさんの考察されているように、HIDデバイスに見せかけておいて、libusb経由でバルク転送とか。
    • ファーム容量的に無理っぽいのと、けっきょくそうういったことは別のAVRライターでやり尽くされているような気もする
    • 自分はATtiny2313しか知らないので、そんな世界も知らない。でも見てみたいかも。

こうやって、AVRライターの種類だけが増えていくのは、なんだかむなしいなぁ

HIDasp高速化検討中

光が見えてきたので、作業記録を書いてみる。

現状

  • HIDaspはWindowsAPIのReadFile()/WriteFile()によってHID Reportを転送している。
  • HID Reportというのは、たとえばマウスならボタン情報、座標などを含む、固定サイズのデータだ。
  • そのReportの1パケットは8バイトになっている。ReadFile()/WriteFile()に与える転送長は+1した9を与える。
  • Report_IDは0になっている。ReadFile()/WriteFile()のバッファ先頭にReport_IDを置く。
  • 他の例(AVR-DoperやHIDsph)を見るとReportIDは1〜のようだ。

問題点

  • Windows2000では、起動時のハンドシェークでやりとりがうまく行かず、ハングする。
  • WindowsXPではUSB挿入直後だけハンドシェークをミスるが、一応無視して継続する。
  • 転送速度は約1kB/秒なので、ATmega644では1分弱掛かり実用的でない。
    • ATtiny2313では4秒程度なので問題なし。

改善策(案)

  • まず、HID Reportの転送単位を変更する(32バイト程度にすると効果あり)
  • HIDaspのusbHidReportDescriptorをAVR-Doperベースのものに差し替える。
  • これは、HID Report ID 1〜5までが定義されていて、転送サイズ(REPORT_COUNT)はそれぞれ
  • (16-2),(32-2),(64-2),(128-2),(64-2) のようになっている。
  • なぜ2のべき-2なのかというと、パケットの先頭にReport ID(1〜5のどれか)を入れる必要があるからだ。
  • (でもそれは-1である理由にしかなってないんだけど)
  • 実はキリの良い値でなければならないという理由はないがLowSpeed USBのパケット長上限は8バイトなので、総転送サイズが8の倍数になっているほうが効率が良い。
  • 最後のやつだけ数列から外れているが、これはデバッグパケットらしい。


そして、HID Reportの転送にはHidD_GetFeature()/HidD_SetFeature() を使う。

  • 転送サイズはREPORT_COUNT+1を与え、バッファの先頭にはReport_IDを書き込んで使う。

今日やったこと

  • AVR-DoperのHidReportDescriptorを移植したAVR-USBのフレームワークを用意して、 32バイトパケットを単純にエコーバックするだけのファームを書く。
  • そして、ホストPC側から、接続を試みる。
  • (senshuさんのHIDspx-srcをありがたく使わせて頂いております)
  • 31バイトのHID Reportを適当にでっちあげて Report_ID=2で送信し、そのエコーが戻ってくるのを確認。<===今日はここまで

とりあえず現状報告

avr-objcopy -j .text -j .data -O ihex main.elf main.hex
bash checksize main.elf 8192 1024
ROM: 1968 bytes (data=4)
RAM: 87 bytes
  • 一応isp_commandまで入れた。
  • AVRmonitは入れていない。

明日以降の予定

  • AVRmonitをHIDベースにして、デバイスドライバ不要のやつを作ってみる。
  • ベンチマーク機能があるので、ベンチマークしてみる。
  • 予想では4kB/s程度。根拠は32バイトまとめて送るので。
  • コードサイズが余れば瓶詰さんのプロトコルを乗せてみる。すでに入っているつもり









下記の記述はHIDクラスの制約を無視した考察ですので、多分に間違いを含んでおります。 (libusbを使用する場合はOKかもしれません。)

現在再検討中です。


千秋さんのサイトでの話題

可能性を探ってみた。

  • まず、LowSpeedの転送速度は、理論値1.5Mbpsあるように見えるけれどこれは気の迷い
  • 基本、コントロール転送を使うと仮定。
  • USBでは1フレーム(1mS)に8バイトのパケットが1回送れる(あるいは受け取れる)だけなのだ。
  • じゃあ8kB/秒かというと微妙なところで、コントロール転送を用いて8バイトのベンダーコマンドを送り、デバイスから8バイトのリターンを返してもらうという1回のトランザクションの時間を計測してみると、
  • OHCIでは1mSで完結するがUHCIでは4mS掛かる。
  • つまり、OHCIでは1秒に8kB送って8kBのリターンが得られるが、
  • UHCIではその1/4の速度(2kB/秒)まで落ちる。
  • もうひとつやっかいなのがコントロール転送では8バイトの内容のうち、先頭1バイトがbmRequestTypeであり、お尻の2バイトがwLength(後続データのサイズ指定子)となっていて自由に使えない。
  • つまり送出できる正味データ量は8kB/Sではなく5kB/Sになってしまうわけだ。(UHCIではさらにその1/4)
    • HIDaspに限った話をすると、5バイトのうち最初の1バイトがコマンドバイトとして使われていて、正味のSPIデータ送出は4バイト/フレームである。
    • 但し、AVR書き込みターゲットに対するページリードがサポートされていて、4バイトのファームウェアデータを1トランザクションで転送しているようなので、書き込み速度としては4バイト/4mS=毎秒1kBとなる。
  • これを打破する手としては、1トランザクションに後続データを16〜32バイト付加してやって、1回のコントロール転送で16〜32バイト分のファームウェアデータを送りつけてやる方法だ。
  • 仮に32バイト送るとするとUHCIでは4+4=8フレームでおそらく完結するので、
  • 32バイト/8mS=4kB/秒になる計算だ。
  • 問題はそれらの処理を含めて2kBに入るかどうかなんだな。
  • かなり難しいと思う。GCC4ではバイナリコードが肥大化する傾向があるのでGCC3ベースのWinAVRを使うしかないかも。
  • むしろ、千秋さんのサイトで指摘されているとおり、tiny2313に固執するのをやめて、ATmega88あたりでゆったりとコードを組んだほうが正解なのかもしれない。
  • でも、当サイトでは無理やりコードを縮めて2313に詰め込む方向にむしろ快感を感じるので、mega88は使わないだろうと思う。(嘘。手持ちのmega88が少なくて、2313ばかりが余っているので、2313を使い切る方向で考えている。)