SSブログ
English Version

レトロマイコンZ80ボードの構想(その7)SDカード2 [Z80]

 前回の記事「レトロマイコンZ80ボードの構想(その6)SDカード」でSDカードとのSPI通信のロジアナ波形を掲載しましたが、ブロック読込み時に読込みデータ間にクロックが停止する隙間がでるのが嫌なのでSPI通信の処理を見直しました。

  • リード時の隙間
     SPI通信でデータリード時にデータ間にクロックの停止する隙間が発生するのはやはりもったいない。SPIモジュール(ハード)自体の制約によるものではないので隙間なく受信するように変更しました。

  • CSをhighに変更後のクロック提供
     web上の情報ではCSをhighにしてから1バイト(0xff)を送信する必要があるという情報を見かけましたが、波形を見てみるとSDカードからの応答の最後のバイトのLSBがlowの場合、その後のクロック提供がないと、DOをlowに引っ張り続けることがあるけどクロック入力によりパラ-シリ変換がシフトされる(ゴミが残った場合吐き出される)とDOがhighに復帰するので供給するクロックは1クロック以上であればいいように思えます。
     今回はわざわざ1バイトの0xffを送らないでSPI受信処理時のクロック提供のための0xff送信でバッファを使うようにしました。
     受信完了後にバッファ内の0xffデータが送信されるのでSDカード側のごみ吐出しはできるものと思います。

 改良版でのSPI波形を参考に貼っておきます。今回はブロックライトとブロックリードの波形も付けました。

★2018/02/10 追記 {
 SDカード初期化時のクロックは333kHzです。初期化後クロック切替えの実験をしたところ、SPI1CON1のプリスケーラの再設定だけではクロックが変わらず、一旦、SPIをdisableにしてからプリスケーラを設定後、SPIをイネーブルにする必要がありました。
 picleソースで書くと

    SPISTAT[0] = $0000;
#   SPISTAT[1] = $7a;    # SPI1CON1 prescale2:1,prescale4:1  = 8:1  2M NG
    SPISTAT[1] = $7d;    # SPI1CON1 prescale1:1,prescale16:1 = 16:1 1M OK
    SPISTAT[0] = $8000;

 こんな感じです。
 コメントに書いたようにブロック書込み/読込み動作が2MHzではNG(DIのデータを取りこぼす)で1MHzではokでした。(PIC24FJのクロックは内蔵の32MHz)
}


CMD0波形

CMD8波形

ACMD41波形(アイドル状態なのでリトライ)

ACMD41波形(0x00のレスポンス(OK))

CMD58波形

CMD24(ブロックライト)波形

CMD24(ブロックライト:データ送信後wait)波形

CMD24(ブロックライト:wait解除され終了)波形

CMD17(ブロックリード)波形

CMD17(ブロックリード:SDカードがパケット送信開始)波形

CMD17(ブロックリード:パケット受信完了)波形


 今回の実験で作成した picleソースは次のとおりです。SRAM上にソースが入りきらなくなってきたのでmain()部分と関数部分を分離し、main()側でフラッシュメモリにセーブした関数ソースをインクルードしています。


実験で使用したソース:関数部分(picle言語)
#LibSpi SPI test to connect SD card for PIC24FJ64GA004 with picle # Ver 0.02 by skyriver 2018/02/08 var SPISTAT,SPIBUF; var _RPINR20,RPOR0; var _REG,AD1PCFG; var TRISA,LATA; var ByteTimer,SdhcFlg; var SpiDat,_Buf; proc init() { SPISTAT = $0240; # SPI1 status SPIBUF = $0248; RPOR0 = $06c0; AD1PCFG = $032c; AD1PCFG[0] = $ffff; # I/O TRISA = $02c0; LATA = TRISA + 4; SPISTAT[1] = $0075; # SPI1CON1 pre 2:3,Post 1:16 = 48:1 333k SPISTAT[2] = 0; # SPI1CON2 SPISTAT[0] = $8000; _RPINR20 = $06a8; _RPINR20[0] = 2; # SDI -> RP2 RPOR0[0] = $0708; # CLK -> RP0, SDO ->RP1 TRISA[0] = TRISA[0] & $fffb; LATA[0] = $0004; _Buf = Array_(0); ByteTimer = 30; } proc SpiCheck() { if ( SPISTAT[0]&1 ) { SpiDat = SPIBUF[0]; } } # wait about 1.8us * n proc TmWait( n ) { for (; n ; n=n-1) { SpiCheck(); } } proc SpiSnd( dat ) { do { SpiCheck(); } while ( SPISTAT[0]&2 ); SPIBUF[0] = dat; } func SpiRcv() { do { if ( (SPISTAT[0]&2) = 0 ) { SPIBUF[0] = $ff; } } while ( (SPISTAT[0]&1) = 0 ); return = SPIBUF[0]; } func SpiRcvAns() { do { return = SpiRcv(); } while ( return = $ff ); } proc SpiRcvN( n ) { var i; for ( i=0; i < n; i=i+1 ) { _Buf[i] = SpiRcv(); } } proc SetCsDisable() { LATA[0] = $0004; TmWait( ByteTimer ); } proc PrnVal( str, val ) { PrnStr_( str ); PrnHex_( val ); } # return 0:NG, 1:OK func BlkRead( sec ) { var ans; LATA[0] = 0; SpiSnd( $51 ); # CMD17 SpiSnd( 0 ); SpiSnd( 0 ); SpiSnd( sec / 256 ); SpiSnd( sec & $ff ); SpiSnd( $ff ); # dummy CRC ans = SpiRcvAns(); if ( ans = 0 ) { ans = SpiRcvAns(); # Data Token if ( ans = $fe ) { var i; for ( i=0; i<128; i=i+1 ) { SpiRcvN( 512 / 128 ); } } SpiRcvN( 2 ); } SetCsDisable(); return = ans = $e5; } # return 0:NG, 1:OK func BlkWrite( sec ) { var i; LATA[0] = 0; SpiSnd( $58 ); # CMD24 SpiSnd( 0 ); SpiSnd( 0 ); SpiSnd( sec / 256 ); SpiSnd( sec & $ff ); SpiSnd( $ff ); # dummy CRC if ( SpiRcvAns() = 0 ) { SpiSnd( $fe ); # send token for ( i = 0; i<512; i=i+1 ) { SpiSnd( i & $ff ); } SpiSnd( $ff ); # dummy CRC SpiSnd( $ff ); return = SpiRcvAns() = $e5; while ( SpiRcv() = 0 ) {} } SetCsDisable(); }


実験で使用したソース:main()部分(picle言語)
# SPI test to connect SD card for PIC24FJ64GA004 with picle # Ver 0.02 by skyriver 2018/02/08 use LibSpi; proc main() { var ans,i; init(); LATA[0] = $0004; for ( i=0; i<10; i=i+1 ) { SpiSnd( $ff ); } TmWait( ByteTimer * 2 ); LATA[0] = 0; SpiSnd( $40 ); # CMD0 SpiSnd( 0 ); SpiSnd( 0 ); SpiSnd( 0 ); SpiSnd( 0 ); SpiSnd( $95 ); ans = SpiRcvAns(); SetCsDisable(); if ( ans <> 1 ) { PrnVal( "Cmd0Res:", ans ); } else { LATA[0] = 0; SpiSnd( $48 ); # CMD8 SpiSnd( 0 ); SpiSnd( 0 ); SpiSnd( 1 ); SpiSnd( $aa ); SpiSnd( $87 ); ans = SpiRcvAns(); SpiRcvN( 4 ); # 16M:00 00 01 aa SetCsDisable(); if ( ans <> 1 ) { PrnVal( "CMD8 err:", ans ); } else { if ( (_Buf[2] <> $01) | (_Buf[3] <> $aa ) ) { PrnStr_("SDC not v2" ); } else { SdhcFlg = 1; do { LATA[0] = 0; SpiSnd( $77 ); # CMD55 SpiSnd( 0 ); SpiSnd( 0 ); SpiSnd( 0 ); SpiSnd( 0 ); SpiSnd( $65 ); ans = SpiRcvAns(); if ( ans = 1 ) { SpiSnd( $69 ); # ACMD41 SpiSnd( $40 ); SpiSnd( $ff ); SpiSnd( $80 ); SpiSnd( $00 ); SpiSnd( $17 ); ans = SpiRcvAns(); } SetCsDisable(); } while ( ans <> 0 ); LATA[0] = 0; SpiSnd( $7a ); # CMD58 SpiSnd( 0 ); SpiSnd( 0 ); SpiSnd( 0 ); SpiSnd( 0 ); SpiSnd( $fd ); ans = SpiRcvAns(); SpiRcvN( 4 ); # 16MB:c0 ff 80 00 SetCsDisable(); if ( ans <> 0 ) { PrnVal( "CMD58 err:", ans ); } if ( BlkWrite( 4 ) ) { if ( BlkRead( 4 ) ) { PrnStr_( "Ok" ); } } } } } LATA[0] = $0004; }


★2018/05/09 追記
 SDHCタイプのSDカードのブロックサイズは 512バイトでCP/Mのディスクリード/ライト時の最小サイズ(128バイト)より大きいため、ブロッキング/デブロッキング処理する必要があります。また、ブロッキング/デブロッキング処理のサンプルソースがCP/Mのディスク内に添付されているケースもあるようです。

 この処理を実施した場合、読込みは速くなる(1セクタの読込みで4個分読めるので)けど、書込みは一度該当セクタを読込み、書込み対象部分をアップデート後、セクタ書込みする必要があるので2倍程度遅くなります。
 ※BIOSへの書き込み要求時にC-regに下記情報が渡されるので単純に2倍遅くなるわけではない。
  C=0 - Write can be deferred
  C=1 - Write must be immediate
  C=2 - Write can be deferred, no pre-read is necessary.

 実際待ち時間が生じるのはHI-TECH Cでコンパイルする場合などのケースであり、この場合、読込みと同様に書込みも頻繁に行われているのでブロッキング/デブロッキングの処理で遅くしたくはありません。

 今回はSDHCカードのセクタの先頭の128バイトのみを使うことでブロッキング/デブロッキング処理を省略し、書込み時のパフォーマンス低下を防ぐことにしました。
(SDカードを無駄遣いしているようにも思えますが、CP/Mで使用できるのはSDカードの先頭部分のほんの少しだけなので無駄ということにはならない)

 SDHCのコマンドを眺めていたらブロックサイズを設定するコマンドがあったので試してみました。ブロックサイズを128にできれば未使用部分のセクタ先頭以外の3/4の読み/書きが不要になり高速化できるはずです。

 このブロックサイズ指定のコマンドへの対応はSDカードに依存するようなことがネット情報にあり、16GBのSDカードを用いた実験結果としては
  • ブロック指定コマンドに対してはOK応答を返してくるけど、ブロック読込みで128バイト読込み、再度ブロック読込みすると129バイト目からのデータを吐き出してくるように見える(下記キャプチャ)

  • ブロックサイズ設定がうまく行ったら二つ目のセクタの読み出し位置が先頭から129バイト目になるのではという期待もありましたが、読み出し位置はブロックサイズ指定後も変化しませんでした(指定対象サイズがセクタではなくブロックなので当然かも)

 下のキャプチャがブロックサイズ指定のコマンドを発行している時の波形でSDカードからは OK 応答が帰ってきています。(その後クロックを1MHzに上げている)

ブロックサイズ指定コマンド波形


 下のキャプチャは128バイト分のブロック読込み後、別のセクタのブロック読込みを開始している時の波形です。
 始めのブロックの最後の2バイトがCRCコードになっておらず、次のブロック読込みのコマンド発行中に129バイト目以降のデータが送られてきています(いずれもデータは0e5h)

ブロックサイズ指定後のブロック読み出し



[TOP] [ 前へ ] 連載記事 [ 次へ ]

nice!(0)  コメント(1) 

nice! 0

コメント 1

skyriver

 CP/Mにおけるブロッキング/デブロッキング処理の対策とSDカードのブロックサイズ指定コマンドに関する実験結果を追記しました。

by skyriver (2018-05-09 23:34) 

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。