2009年11月11日

Mass Storage Class の検討(2)

前記事追記からの続き。ようやく Mass Storage Class (以下 MSC)のコードが 動いたので チューニングをしてみた。すこし 一般的な話題になってきたので、追記をやめて別の記事にした。

まず、どういう観点でなにをチューニングしていくかについて、簡単に説明しておこうと思う。


  • 1. RAM の使用量の削減

    AVR は RAM 容量が少ないので、RAM の使用量を減らすことは重要。特に ライブラリ的なものでは アプリケーションが作りにくくならないように出来るだけ使用量を減らすようにすべき。

  • 2. プログラムサイズの削減

    プログラムサイズもそんなに余裕があるわけではない。ブートローダが 4KB 使っているので、全部で 12KB 。少しでも減らしておかないと、アプリケーションが作りにくくなる。

  • 3. MSC の READ/WRITE の性能チューニング

    無駄なコードを 削減していけば、性能も上がる傾向にはある。ただ、性能を上げるためには、あえてコードを追加する場合もある。
    そういう例を紹介しようと思う。



RAM の使用量の削減



シリアルにデータを送るコードで

    usbcdc_puts("\r\n");

というようなコードがある。これは既にダメダメなのだ。

AVR では文字列は、FLASH 上に 元データが配置され、(main の前の)スタートアップで FLASH 上のデータを RAM にコピーする。要するに 文字列を普通に使うだけで、FLASH と RAM の 2 つを消費する。

せめて FLASH だけにデータを置きたい。そうするためには、PSTR() マクロ を使う。こうやって FLASH に置いたデータはメモリと同じようにはアクセスできないので __LPM() などのマクロを使う。

具体的には次のようなコードに変更。

    void usbcdc_put_pstr(const char *str) {
    uint8_t ch;
    while ((ch = __LPM(str)) != 0) {
    usbcdc_putc(ch);
    str++;
    }
    }
    :
    :
    usbcdc_put_pstr(PSTR("\r\n"));


あと、ログ用のコードとバッファ。今はデバッグのために生かしてあるが、まとめて削減できるように ifdef などで 切れるようにしておく。

プログラムサイズの削減



MSCでは、32bit の変数がいくつか必要になっている。AVR では 32bit の処理は効率が悪い。8bit の処理で済ませられないか工夫する。無理な場合でも 32bit の処理をする同じようなコードがあれば1つにできないか工夫する。

こうやって 32bitの処理を減らすと コード量が随分減るのだ。

第一のケースの実際例:

msc_cbw.cbw.trans_len.u32 という変数が転送バイト数を示すのだが、READ/WRITE 以外では、転送量は 256 バイト以内。

    large_trans = msc_cbw.cbw.trans_len.ub[1]
    | msc_cbw.cbw.trans_len.ub[2]
    | msc_cbw.cbw.trans_len.ub[3];

とやっておいて、例えば INQUIRY では large_trans の場合エラーにしてしまう。ちなみに、msc_cbw.cbw.trans_len は、union で、32 bit の u32 と 16bit の配列 u16[2] あと 8bit の配列 ub[4] の定義をしている。同じデータ を 違うアクセス方法にするのは、ちょっと不安だったので volatile も付けている。

    case SC_INQUIRY:
    if (large_trans) goto ill_req;
    msc_trans_proc = msc_trans_inquiry;
    break;

で、INQUIRYの処理(msc_trans_inquiry) では、1 バイトの変数 msc_cbw.cbw.trans_len.ub[0] で処理する。

第二のケースの実際例:

    改善前:
    static void msc_trans_skip() {
    if (msc_cbw.cbw.flags & 0x80) { /* Bulk IN */
    if (usbmsc_can_putc(1)) {
    while (usbmsc_can_putc(1) && (msc_cbw.cbw.trans_len.u32 > 0L)) {
    usbmsc_putc(0);
    msc_cbw.cbw.trans_len.u32--;
    }
    }
    } else { /* Bulk OUT */
    while (usbmsc_can_getc(1) && (msc_cbw.cbw.trans_len.u32 > 0L)) {
    usbmsc_getc();
    msc_cbw.cbw.trans_len.u32--;
    }
    }
    }

    改善後:
    static void msc_trans_skip() {
    while (msc_cbw.cbw.trans_len.u32 > 0L) {
    if (msc_cbw.cbw.flags & 0x80) { /* Bulk IN */
    if (!usbmsc_can_putc(1))
    break;
    usbmsc_putc(0);
    } else {
    if (!usbmsc_can_getc(1))
    break;
    usbmsc_getc();
    }
    msc_cbw.cbw.trans_len.u32--;
    }
    }


if 文を loop の外に持っていくのが、C での常識。なのではあるが、あえて loop の中に if 文を入れて 32 bit の処理を共通にして減らす。これだけの違いで なんと 132 バイトもコードのサイズが減るのだ。(gcc3 の場合 、gcc4 では 78 バイト)

MSC の READ/WRITE の性能チューニング



改善前のコードは次のようになっていた。


    static void msc_trans_read() {
    while (usbmsc_can_putc(1) && (msc_cbw.cbw.trans_len.u32 > 0L)) {
    usbmsc_putc(serflash_proc(0));
    msc_cbw.cbw.trans_len.u32--;
    }
    if (msc_cbw.cbw.trans_len.u32 == 0L) {
    serflash_close();
    return;
    }
    }


これの何をどうチューニングするのか? 疑問に思う人もいるかも知れない。結果を先に書くと ...


    static void msc_trans_read() {
    uint8_t i;
    while (usbmsc_can_putc(32) && (msc_cbw.cbw.trans_len.u16[0] > 0)) {
    for (i=0; i<32; i++) {
    __usbmsc_putc_nc(serflash_proc(0));
    }
    msc_cbw.cbw.trans_len.u16[0]--;
    }
    if (msc_cbw.cbw.trans_len.u16[0] == 0) {
    serflash_close();
    return;
    }
    }


こう。

あらかじめ、32 バイト分の処理ができるのを確認しておいて 、いっきに処理する。

もとの usbmsc_putc は内部でチェックが入って 処理できない場合は xxx_poll を call して待ち合わせる ... みたいになっているのだが、チェックは不要なので単純化でき、さらに単純化したことで inline 関数にできる。

また、32 バイト単位の処理にしたので、msc_cbw.cbw.trans_len をあらかじめ 5bit シフト(1/32) にしておいて、デクリメントで済ます。さらに.. 1/32 にすることで、msc_cbw.cbw.trans_len が 16bit で 2MB まで表現できるようになるため、32 bit 処理をせずに 16bit で済ませられるようになる。

    今回のケースではサイズ自体が 1MB なので 転送長は必ず 16bit に収まるわけだが ... たとえば SD などを使った場合でも大丈夫なのか? ... というと 実はよくわからない。
    ただ、標準的な最大の転送長は 128KB あたり。64KB には収まらないのは確実だが、2MB という上限なら 大丈夫ではないかと思う。

バッファーを ダブルバッファーにする設定とか他の改善もしているのだが... serflash_proc(0) を 定数の 0 にして READ だけの性能を測定したところ

    # dd if=/dev/sdb of=/dev/null bs=512 count=2048
    2048+0 records in
    2048+0 records out
    1048576 bytes (1.0 MB) copied, 1.27792 s, 821 kB/s

ここまで行った。パケット長が 32バイトなので、このあたりが限界かも知れない。

ところで、serflash_proc(0) を使う正規の READ の場合は、

    # dd if=/dev/sdb of=/dev/null bs=512 count=2048
    2048+0 records in
    2048+0 records out
    1048576 bytes (1.0 MB) copied, 11.4399 s, 91.7 kB/s

と一気にしょぼくなる。今は ソフト SPI なので いたし方ない。といっても 最初に動いたときは、59.6 kB/s だったので、1.5 倍ぐらいにはなっている。

本来なら USART を使った SPI にする予定だったのだが、基板の配線間違いで ソフト SPI にせざるを得ない状況。

ここまで性能が出たので、基板を改修して USART を使った SPI を試したくなっている。

ということで、そろそろ 2号機を組もう。

サイズの削減状況(gcc3)




    text data bss dec hex
    6602 76 269 6947 1b23 改善前
    6272 0 269 6541 198d 改善後
    5988 0 76 6064 17b0 改善後 , log なし

data が 76 バイトなくなって text が 330 バイト減った。MSC 以外に CDC や USBの基本処理があるので、MSC 関係は 全体の 5割前後なので まぁがんばったと言えよう。log もばっさり切ると メモリの使用は 76 バイトにまで減る。
ちなみに、gcc4 ( gcc version 4.3.2 (WinAVR 20090313)) の場合:

    text data bss dec hex
    6740 76 269 7085 1bad 改善前
    6344 0 269 6613 19d5 改善後
    5840 0 76 5916 171c 改善後 , log なし

不思議なことに、gcc3 よりサイズが大きかったのが、最終的には gcc3 より小さくなってしまった。

今回のコードは→ usb162-0.2-wk6.tar.gz

追記1:MSC の デバイス API の検討

今の MSC は、シリアルFLASH 専用で もっとも効率よく作ろうと考えて設計している。

さて、これをベースに 違うデバイスをサポートしたり デバイスの追加が出来るようにしようと思う。

もちろん、機能拡張したからといって 性能は落としたくないし、コードもあまり増やしたくない。

結構悩んだのだが... 結局次のようにすることにした。

もともとコマンド解析は switch 文で行っているのだが、解析した結果、データ転送を行う関数を 登録するようになっている。

だから、デバイス固有の処理のうち データ転送部分はまるごと デバイス側に移動できる。

移動する関数は、まずは READ_10/WRITE_10 。関数の場所を移動するだけなので、性能については心配しなくて良くなる(はず)。

あとは、共通処理が多いか/デバイス専用処理が多いかで決める。

微妙なのだが、デバイス専用処理が多いものとして、INQUIRY, READ_CAPACITY の関数を 移動することにした。

次の問題は、どうやって これらの関数を登録させるか ... 。

コードを載せてしまうとこんな感じ。

    static void serflash_devcall(uint8_t sc_lun, uint8_t sc_cmd) {
    switch (sc_cmd) {
    case SC_INQUIRY:
    msc_trans_proc = serflash_trans_inquiry;
    break;
    case SC_READ_CAPACITY:
    msc_trans_proc = serflash_trans_read_capacity;
    break;
    case SC_READ_10:
    serflash_open(SERFLASH_READ);
    serflash_seek(msc_trans_offset.u32,SEEK_SET);
    msc_trans_proc = serflash_trans_read;
    break;
    case SC_WRITE_10:
    msc_trans_proc = serflash_trans_write;
    break;
    default:
    break;
    }
    }

    void msc_serflash_init() {
    serflash_init();
    msc_devattach(serflash_devcall);
    msc_devattach(serflash_devcall);
    }


共通の処理をやった後で、serflash_devcall を CALL して 関数を登録させる。READ など初期化処理が一部あるものも、そのときに実行する。

もちろん、serflash_devcall は直接 CALL するのではなく、関数を登録させるようにする。上の例だと 2 回登録しているから、LUN 0 と 1 の 2つのディスクとして見える。Linux では 期待どおりに動いた。

ちなみに登録する関数 msc_devattach() はこんな感じ

    int8_t msc_devattach(void (*func)(uint8_t,uint8_t)) {
    int8_t ret;
    if (msc_maxlun >= MSC_MAXLUN)
    return -1;
    ret = msc_maxlun;
    mscdev[msc_maxlun++] = func;
    return ret;
    }

配列に関数を登録するだけだが、配列の INDEX が LUN を示していて、戻り値として 割り当てた LUN を返す仕様。(一応 2枚のカードを扱うようなドライバを作れるように)

最後になってしまったが、これで何をしようとしているか ... というと SD のサポート。

SD のコードは、ChaN さん の

を参考にさせてもらおうと思う。

ポートについては、外部デバイス接続用の CONN2 を使う。

    CONN2
    1 VCC(3.3V)
    2 GND
    3 PC5/OC1B (NPN OC)
    4 NC
    5 DEV_VCC (3.1V)
    6 PB3/MISO (100 ohrm)
    7 PB1/SCK (100 ohrm)
    8 PB2/MOSI (100 ohrm)
    9 PB4/T1 or RESET (100 ohrm)
    10 GND

配線を間違えてなければ、ハード SPI が使えるはず。

実際に作ると、API 的な不備がいくつか出てくるはず。つくり終えてはじめて API が FIX する。

今回のコードは→ usb162-0.2-wk7.tar.gz

追記2: SD/MMC ドライバの検討

ChaN さん の コードは、SDHC に対応しているので 参考になるのだが、他のコードとかも見てどれぐらい簡易なものにできるか検討している。

SD/MMC では、6 バイトの固定長のコマンドを使う。6 バイトの内訳は、1 バイトのコマンド、4 バイトの アーギュメント、1 バイトの CRC。いくつかのコードを見てみたのだが ... 大体 コマンドを発行する関数があり、パラメータとして コマンドと uint32_t の アーギューメントを渡す仕様が多いようだ。

アーギュメントが意味を持つのは、READ/WRITE の オフセット指定と、初期化の一部で、その他のコマンドでは 普通 0 。

どうも コード効率が悪くなるような気がするので、このあたり工夫してみようと思っている。


  • 基本方針
    コマンドを発行する関数を作るのは同じだが、パラメータは 1 バイトにする。

    1) READ/WRITE の オフセットは、変数に覚えていて、パラメータにフラグ(CMD_MASK_LBA) を設定することで指定する。

    2) 定型のパラメータは、6 バイトまるごと を FLASH の配列に置く。これを指定する場合、パラメータに フラグ(CMD_MASK_CONST)を設定して、配列のオフセットを指定する。

    3) それ以外のコマンドは、コマンドの番号 を指定する。この場合、アーギュメントは、すべて 0 。

こんな感じ。

これをベースに組み立てていけば、アクセス自体は、なんとかなるのだろう。

    これは、どうもやりすぎみたい。

    caller が 4 バイトの定数をパラメータに追加するコストは、4 命令8バイト。call する所は、20 ヶ所より少なくなる見込みで、全部で 160 バイトより少ない。

    対応コードもある程度量があるし、逆効果になるかも。


さて、コードを見ても良くわからないことがいくつかある。

ひとつは、カードの容量をどうやって知るのかということ。もうひとつは、カードの名前みたいな 文字列を INQUIRY で返したいのだがどうやってそれを知るかということ。

後者は、単に凝りたいだけなので、必要性は薄いが、前者は できるだけ ちゃんとしたい。

調べてみると後者は、16 バイトの CID に含まれていることが判った。CID の読み出しは、CMD10 を使い シングル・ブロック・リードと同じような手順でできる。

問題は CID の中身だが、こんな感じらしい。(用語は適当)

    1 バイト: Vendor の ID (バイナリ)
    2 バイト: Vendor? /OEM の ID (ASCII)
    5 バイト: 製品名 (ASCII)
    1 バイト: 製品のリビジョン (バイナリ)
    4 バイト: シリアルナンバー (バイナリ)


このあたりを使って INQUIRY のデータにしたらどうかと思う。

次は問題の前者。これは 、16 バイトの CSD に含まれている。CSD の読み出しは、CMD9 を使い シングル・ブロック・リードと同じような手順。

CSD のフォーマットは 2 種類ある。2GB までのSD は CSDv1 で、先頭のバイトが 0x00 。SDHC は CSDv2 で、先頭のバイトが 0x40 。容量の計算は、CSDv1 の方がややこしい。ちなみに、MMC は、先頭のバイトが、10xxxxxxb で、CSD フォーマットのサイズ関係は、CSDv1 と同じ。

  • CSDv2

    C_SIZE : オフセット 7(バイト)からの 3バイトを Bigendian で見た値。
     ※ 24 ビットのうち上位 2 bit は別フィールドだが 現在は 0

    容量 = (C_SIZE + 1) x 512 KB

  • CSDv1

    C_SIZE : [73:62] の 12 ビット
    C_SIZE_MULT :[49:47] の 3 ビット(< 8)
    READ_BL_LEN: [83:80] の 4 ビット (< 12)
     ※ 先頭のビットは 127

    容量 = (C_SIZE + 1) x 4 x (2 ^ C_SIZE_MULT) x (2 ^ READ_BL_LEN)

CSDv1 はバイトアラインさえされてなくて、コード書くのも説明するのも面倒。

それはともかく、SD カードスロットもまだ接続していない。基板に適当にコネクタ付けて 後で困るのも嫌だし ... どうしようか悩み中。

あと、コードは 今回はなし。

追記3:ハード SPI とか

実をいうと ハード SPI を使うのは初めてだったりするので、ちょっと調査中。

初期化は、おまじないだから、気にしないことにして ... ソフト SPI 相当の送受信コードならすごく簡単。

    SPI の場合:
    uint8_t spi_write(uint8_t c) {
    SPDR = c;
    while(bit_is_clear(SPSR,SPIF))
    ;
    return SPDR;
    }

    USART の場合:
    uint8_t spi2_write(uint8_t c) {
    UDR1 = c;
    while (bit_is_clear(UCSR1A,RXC1))
    ;
    return UDR1;
    }


ただこのコードは無駄に待ち合わせが入るので、おもしろくない。


    for (i=0; i<32; i++) {
    __usbmsc_putc_nc(spi2_write(0));
    }

今は READの転送の部分が、こんな感じになっているのだが、inline とか使った最速のコードのアセンブラは次のもの。


    ldi r25,lo8(0)
    .L4:
    sts 206,__zero_reg__  2 <<< 転送開始
    .L3:
    lds r24,200 2
    sbrs r24,7 1/2
    rjmp .L3 2
                    <<< 転送完了確認
    lds r24,206 2
    sts 233,r18 2
    sts 241,r24 2
    subi r25,lo8(-(1)) 1
    cpi r25,lo8(32) 1
    brne .L4 1/2


for ループの部分は、10 命令(最速ケースで 16 CLOCK)。だが、転送時間 16 CLOCK を待ち合わせる。

転送をオーバラップさせることで、さらに高速にしたい。


    void spi2_send(uint8_t c) {
    UDR1 = c;
    }

    uint8_t spi2_recv_send(uint8_t c) {
    uint8_t r;
    while (bit_is_clear(UCSR1A,RXC1))
    ;
    r = UDR1;
    UDR1 = c;
    return r;
    }

    uint8_t spi2_recv() {
    while (bit_is_clear(UCSR1A,RXC1))
    ;
    return UDR1;
    }

    spi2_send(0);
    for (i=0; i<31; i++) {
    __usbmsc_putc_nc(spi2_recv_send(0));
    }
    __usbmsc_putc_nc(spi2_recv());


こんな風にして次の転送の開始を出来るだけ早くする。


    ldi r25,lo8(0)
    .L13:
    lds r24,200 2
    sbrs r24,7 1/2
    rjmp .L13 2

    lds r24,206 2
    sts 206,__zero_reg__ 2 <<< 次の転送開始
    sts 233,r18 2
    sts 241,r24 2
    subi r25,lo8(-(1)) 1
    cpi r25,lo8(31) 1
    brne .L13 1/2


ループ部分の命令数は同じだか、後ろの 5 命令分が転送とオーバラップするので高速化できる。

ここまでは、シングルバッファである SPI と共通。ダブルバッファーを活かすとどうなるか ....

実は簡単で、spi2_recv_send(0) を spi2_write(0) に置き換えれば良い。そうするとループ部分は結局最初のコードと同じになる。

多分転送が完全にオーバラップする。sbrs r24,7 にも引っかからないはずで、1ループ 16 CLOCK -- 完璧に最速。

前にも書いたが、こういうやり方には名前がついていて、ソフトウェア・パイプライニングという。覚えておくといいかも。

追記4: SD 対応中

SD のコードを書いているのだが ... ちょっと困った。

基本待ち合わせをしないよう ステートマシンとして設計しているわけだが、SD カードでは 、長時間 BUSY になったりする。

また、エラーがいつでも起きる可能性がある。ちゃんと状態遷移を設計しないとまずい。

    ちなみに ... シリアルFLASH の場合は、長時間 BUSY というのはあるが、消去と WRITE の後だけで、(不完全ながら) 対処はできている。エラーについては、初期化が完了さえすれば エラーを考える必要はない。

    不完全と書いている理由は、転送終了後に後処理を入れられないため。ここは API を変更して、後処理を入れられる構造にしないとダメ 。


さて、SD カードについて ... まずは長時間 BUSY になるケースのまとめ

  • 初期化 : 初期化が終了すると、初期化コマンドのレスポンスが (0x01) → (0x00) になる。この待ち合わせで 数百MS になる場合があるとのこと。

  • Write の後の BUSY : 性能測定の経験からいうと 100 MS ぐらいは普通にある。普通のコードは、コマンド発行前に BUSY チェックをしているようなので、この待ちが Write の後だけなのかどうかは コードを参考にしても よくわからない。

  • Read の Data パケット待ち: Read のデータが送られるまでの待ちで、非常に時間がかかる場合があるらしい。


READ/WRITE の場合で、ちゃんと状態を管理するのは 覚悟ができているので良いのだが、 CSD や CID の READ で時間がかかる前提をするのは 面倒だから 対応したくない。

あと、エラー処理 ... か。

とりあえず、今のものを usb162-0.2-wk8.tar.gz で保存。

SD カードはまだ全然ダメ。とりあえず コンパイルできるレベル。

追記5:AT90USB162 ブートローダ

SD サポートは、部品待ちなので、ひとまず置いておいて ... ブートローダについて。

AT90USB162 ブートローダ



自分仕様で ブートローダを作りたい。USB が付いているわけで、まずは USB からの書き換えをサポートしたい。

DFU ブートローダは、それなりに便利なのだが、ソースコードも公開されていないし、改造できないから、別のものを考えないといけない。-- 簡単そうなものの1つは、シリアル経由のブートローダ (AVR910 ベース) なのだが、便利であるかどうか ...というと疑問。

    ちなみに、www.mikrocontroller.net の "Bootloader" for ATTiny2313に 置いてある ATTiny2313_bootloader というのがある。

    ビルトしてみたら 848 バイト。これぐらい本体は小さいのではあるが、CDC-ACM を入れたら 結構大きくなる。

    それはともかく、ブートローダサポートがない ATTiny2313 でもそれなりに 作れるわけだ。

    DFU ブートローダ を置き換えずに、ブートローダもどきを作ることは可能なのだ。

    当面は、アプリケーションとして ブートローダもどきを作ってテストする予定。


やはり、便利なのは、USBaspLoaderだろう。

ブートローダ自体は、main.c ひとつに収まっていて 327 行。usbdrv API も usb162 を作ったときに研究したので多少知っている。HID をサポートしないといけないので面倒なのだが、たぶんなんとかなるだろう。

こうやって USBasp を理解したら、USBasp のインターフェイスを使った XMEGA ライタも作れるようになるのだろう。

USBasp プロトコルメモ



USBasp は HID プロトコルを使うという認識があると思うが、正確には、デフォルトエンドポイントでのコントロール転送 を使って送受信するプロトコル。実際には HID自体 の規約とはあまり関係なく、HID で通信するのは ホスト側の都合。

で、実際の通信は、8 バイトの SETUP パケットを受け取り、その中に埋め込まれた コマンドによって、送受信する。

以下は、USBasp のコマンドと USBaspLoader でサポートしている範囲。


    OUT IN
    1 o CONNECT 0 0
    2 o DISCONNECT 0 0
    3 o TRANSMIT 0 4
    4 o READFLASH 0 0-200
    5 o ENABLEPROG 0 1
    6 o WRITEFLASH 0-200 0
    7 o READEEPROM 0 0-200
    8 o WRITEEEPROM 0-200 0
    9 o SETLONGADRESS 0 0
    10 x SETISPSCK 0 1

    IN/OUT は HOST から見た方向

    0-200 は、AVRDUDE で使用している範囲。コード上は 0-254

    SETLONGADRESS は、上位 16bit のアドレスの設定。下位 16bit
    は、各コマンドで指定。
    SETISPSCK は ISP の クロックの設定。不要なので未サポート
    CONNECT は、なにもしない。
    DISCONNECT は、アプリケーションを実行(CONFIG した場合)
    TRANSMIT は ISP コマンドの実行。 サポートしているのは以下のもの
    0x30 シグネチャ読み出し
    0xa0 READ EEPROM byte (CONFIGした場合)
    0xc0 WRITE EEPROM byte (CONFIGした場合)
    0xac CHIP ERASE     (CONFIGした場合)


うまく作れば、最小のサイズは 2KB以下 に収まりそうな感じ。

ブートローダ領域を使わない場合どうするか



  • まず プログラム(.text)の場所をできるだけ上位アドレスにする。
  • 0 番地〜 .text の先頭までをプログラムできる範囲として扱う。
    ただし、0番地からの 2ワード(4バイト) は、別の場所に移動する。
  • DISCONNECT での実行機能で、移動した場所のリセットベクタのアドレスを計算して JMP すれば、OK 。
    ただし、ハードリセットではないので、汚した IOレジスタは元に戻さないといけない。


これぐらいで大体説明できるわけなので、簡単と言えば簡単。

XMEGA に拡張できるか?



avrdude-5.8 の avrdude.conf を見てみたところ、FLASH や EEPROM 、ヒューズやロックビットまでも 広大な1つのアドレス空間に マップされているような定義になっている。

それにアクセスする メソッドを READFLASH / WRITEFLASH に 割り付けて、あと チップイレースや シグネチャーの READ を TRANSMIT(ISP コマンド) でエミュレーションすることにすれば、avrdude を少し変更するだけで、いけるような気がする。

追記:USBaspLoader 移植

なんか、ちょっと動き出した。

まず、usb162 のバグが見つかった。ストリングのデスクリプタの定義が間違っていた。次は、正しく直したときの Linux での /proc/bus/usb/devices

    T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 46 Spd=12 MxCh= 0
    D: Ver= 1.01 Cls=ef(unk. ) Sub=02 Prot=01 MxPS= 8 #Cfgs= 1
    P: Vendor=16c0 ProdID=05dc Rev= 1.02
    S: Manufacturer=www.fischl.de
    S: Product=USBasp
    C:* #Ifs= 3 Cfg#= 1 Atr=80 MxPwr=100mA
    A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=02 Prot=01
    I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=cdc_acm
    E: Ad=83(I) Atr=03(Int.) MxPS= 8 Ivl=100ms
    I:* If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=cdc_acm
    E: Ad=82(I) Atr=02(Bulk) MxPS= 32 Ivl=0ms
    E: Ad=01(O) Atr=02(Bulk) MxPS= 32 Ivl=0ms
    I:* If#= 2 Alt= 0 #EPs= 0 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none)


要するに次の部分が出てなかった。

    S: Manufacturer=www.fischl.de
    S: Product=USBasp

avrdude では、ID だけじゃなくて、ストリングもチェックしているので、間違っていると認識されない。

ちなみに、INERFACE CLASS を Cls=ff(vend.) に変更していて、DEVICE CLASS も IAD 対応の Cls=ef(unk. ) に変更しても問題ない。Vendor と Product の ID とストリングだけをチェックして、VENDOR CLASS で、コントロール転送できればなんでも良いらしい。

上は、CDC-ACM に USBasp を通す改造をした例。CDC-ACM で ログを取ってみたところ。


    (FLASH の READ)
    SETISPSCK
    CONNECT
    ENABLEPROG
    TRANSMIT (signature read ) x 3回
    TRANSMIT (fuse /lockbit read) x 9 回
    TRANSMIT (eeprom read) x 4 回

    SETLONGADRESS
    READFLASH
    SETLONGADRESS
    READFLASH
    :
    SETLONGADRESS
    READFLASH


というシーケンスだった。SETISPSCK もとりあえず 正常終了 させるようして、fuse /lockbit read もサポートした。( ついでに signature も 読み込むようにした。)

こうした上で自分自身を Verify してみたところ OK 。

いろいろ変なのだが、とりあえず、今のものを usb162-0.2-wk9.tar.gz で保存。
posted by すz at 19:14| Comment(3) | TrackBack(0) | SRT162
この記事へのコメント
つまらない事ですが、
void usbcdc_put_pstr(const char *str)

void usbcdc_put_pstr(const prog_char *str)
の方がWarningなくて良いかもです。
Warning出た場合はPSTRを付け忘れてる…と。
Posted by n at 2009年11月12日 03:46
そうですね。それが作法ですね。直しておこうと思います。

ただ、-Wall を付けてるのですが、Warning が そもそも出ないですね。(gcc3 も gcc4 も)

avr-size で data の size をチェックすれば気がつくので あまり重要ではないですが、なんでなんだろう?
Posted by すz at 2009年11月12日 09:29
試してみたら3でも4でもWarning出ないですね…すみません(´・ω・`)
prog_charは型ではなくアトリビュートだからなのかな…。
Posted by n at 2009年11月12日 21:20
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

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


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

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