LPCXpresso LPC1769でFatFsを動かす - DMA編
前回の延長で、FatFsをDMAモードで動かしました。オリジナルコードをベースにDMA完了割り込み処理を修正する程度で動いたのですが、そこからDMAの最適化を試みるも敢え無く敗退でした。
動作概要
DMA処理の概要は以下の通りです(オリジナルコードの処理です):
<SD Cardからの読み出し>
- SDカードとのインターフェースにはSPIを使用しますが、SPIは以下の図に示すとおり、Master (LPC1769)から①ダミーデータ(今回は0xFFを使用)を送信することによって、②Slave (SD Card)のデータ送信を促します
- そのため、ダミーデータの送信用に0xFFを512個(一回のDMA転送で読み出すデータ量)設定した配列を宣言。データは固定値のためROMに配置
- DMA_CH0を上記①のダミーデータ送信用として使用
- DMA_CH1を②の読み出しデータ受信用に使用
- DMAを起動し、完了割り込みを待つ
<SD Cardへの書き込み>
- 読み出しの逆で、①送信バッファには書き込みデータを設定。データを書き込むと②ダミーデータを受信します
- そのため、②ダミーデータ受信用のバッファを512バイト宣言
- DMA_CH0を書き込みデータの送信用として使用
- DMA_CH1をダミーデータの受信用に使用
- DMAを起動し、完了割り込みを待つ
動作概要を見ていただくと分かるのですが、DMAの半分はダミーデータの送受信に使っておりなんとなく無駄が多いです。そこで、以下の最適化にチャレンジしました。
DMA処理の最適化
最適化1:片方向のDMA
SD Card読み出しではCH1しか有効データの処理に使っていないのだから、CH0のDMA送信を止めてしまえという無謀なアイデア。しかし、SPIの仕様としてマスターからダミーデータを送らない限りSD Cardからのデータが出てこないので、敢え無くボツ。
最適化2:ダミーデータ用バッファをなくす
送信用のダミーデータは広大なROMに配置すればよいので容量的には気にならないのですが、ダミーデータ受信用のRAMに512バイト無駄使いするのはちともったいないです(まあ、64KBあるのでけちなことを言うなですが・・)。あと、余計なデータ書き込みがあるとバス帯域を消費して転送性能にも影響が出そうです。オリジナルコードの作者さんは、この点を改善しようとして挫折した痕跡がコードに残っていました。
LPC17xx GPDMAの機能として、転送のDestinationバッファのポインタを自動インクリメントする・しないを指定するフラグDI(Destination increment)がDMACCxControlレジスタ内に存在します。CMSISライブラリのGPDMA_Setup() 関数を使ってPeripheral to MemoryのDMAをセットアップするとライブラリ内でDIを有効に設定するのですが、受信データがダミーになる場合はGPDMA_Setup()を呼んだ後でDIを無効にすることによって、ダミーデータを配列に格納せず読み捨てることができないかというものです。受信したダミーデータを汎用レジスタに書き込むだけでRAMを使わないようにすれば、メモリー帯域の節約にもなりそうです。
しなしながら、オリジナルコードのコメントにもありますが、DIを後付で無効にすると、DMA完了割り込みが発生してくれません。原因は分からないのですが、残念ながらボツです。
最適化3:バーストサイズの拡張
GPDMA_Setup() 使ってDMAの初期設定を行うと、Memory - SPI間のDMAではバーストサイズが4byteに設定されます。すなわち、4byte転送する毎にDMACがバスマスタを一旦放棄してMCUが動ける隙間を作ります。リアルタイムOSを使用する場合は、DMAがMCUを占有しないという点でこれくらいのバーストサイズが適切なのかと思いますが、頻繁にバスマスターの遷移を行うせいか、前回行ったソフトベースの転送に比べて若干性能が劣ります(転送性能の詳細は次項に記載)。そのため、バーストサイズの拡大を試みました。
バーストサイズの設定は、GPDMA_Setup() 内でDMACCxControlレジスタのSBSize, DBSizeを設定することで行っているため、ライブラリのコードを変更して強制的に8byteのバーストサイズを設定してみました。結果はP2M(SD Cardからの読み出し)時に、DMA_CH0(ダミーデータの送信)はDMA完了割り込みが発生するのですが、DMA_CH1(SD Cardからの受信データー)の完了割り込みが発生してくれず、NGでした。
SPIクロックを下げる、バッファのアライメントをバーストサイズ境界にする(8byteバーストの場合、DWORD境界に配置)など試したのですが解決せず、残念ながらボツです。
最適化4:ダミーデータ用RAMの配置移動
オリジナルコードではDMA処理関数内のローカル変数として512byteのダミーデータ用配列を宣言しています。すなわち、スタックにダミーデータを取るため、0x1000 0000~0x1000 7FFFのメインSRAMを消費することになります。この領域を使うのはもったいないので、0x2007 C000~0x2008 3FFFのAHB SRAMにバッファを配置してみました。せめてもの最適化ということで・・ ダミーデータバッファの宣言に __attribute__ ((section (".bss.$RAM2*"))) を付けることで配置を制御しています。
結局最適化は動かず、最適化4以外はオリジナルコードのまま(CMSISライブラリv3対応の書き換えのみ実施)となりました。ソースは以下のリンクから:
動作確認結果
以下の通り性能測定を行いました。条件は、前回のソフト転送モードと同等です。
Hello from the ChaN FatFs Demo on LPC1700 Version 0.0.2, Martin Thomas 7/2010 xprintf is working CPU Clock: 120MHz Peripheral Clock: 60MHz SPI Clock: 30MHz FatFs module test monitor for LPC17xx/SSP LFN Enabled, Code page: 1252 >di 0 rc=0 >fi 0 rc=0 FR_OK > >fo 10 test. 1.txt rc=0 FR_OK > >fw 10000000 30 10000000 bytes written with 833 kB/sec. >fw 10000000 31 10000000 bytes written with 465 kB/sec. >fw 10000000 32 10000000 bytes written with 526 kB/sec. >fw 10000000 33 10000000 bytes written with 468 kB/sec. >fw 10000000 34 10000000 bytes written with 587 kB/sec. > >fc rc=0 FR_OK >fl ----A 2010/07/15 11:57 50000000 test1.txt 1 File(s), 50000000 bytes total 0 Dir(s), 1912852480 bytes free > >fo 1 test1.txt rc=0 FR_OK > >fr 10000000 10000000 bytes read with 2149 kB/sec. >fr 10000000 10000000 bytes read with 1483 kB/sec. >fr 10000000 10000000 bytes read with 1541 kB/sec. >fr 10000000 10000000 bytes read with 1543 kB/sec. >fc rc=0 FR_OK >fo 1 test1.txt rc=0 FR_OK > >fr 10000000 10000000 bytes read with 2149 kB/sec. >
結果は、書き込みではソフト転送とほぼ同じ性能が出ていますが、読み出しでは7~10%程度劣る結果となりました。たぶんバーストサイズが小さいことが響いているのではと思うのですが。
おわりに
人様のコードベースではありますが、DMAまでなんとか動作。最適化については敢え無く敗退でした。最適化2のDI無効化は強引感がありますが、最適化3のバーストサイズ拡大は動いてもよさそうなんですが。DMACの設定だけでなく、SSP(SPI)側の設定とか何か抜けているところがあるのか。CMSISライブラリのお仕着せパターンに従った設定ベースでの使用ならなんとかなるのですが、それを超える個別パターンになるといきなりハードルが高くなる感じです。
« LPCXpresso LPC1769でFatFsを動かす | トップページ | LPCXpresso LPC1769でFatFsを動かす–USB Host編 »
「NXP-ARM」カテゴリの記事
- LPCXpresso IDEでのC++コードサイズ肥大化の対策(2011.10.15)
- XBee APIモードライブラリのLPCXpressoへの移植(2011.10.09)
- LPCXpresso LPC1769でFree RTOSを使う(2011.07.27)
- LPCXpresso LPC1769でFatFsを動かす–USB Host編(2011.07.19)
- LPCXpresso LPC1769でFatFsを動かす - DMA編(2011.05.14)
コメント
この記事へのコメントは終了しました。
« LPCXpresso LPC1769でFatFsを動かす | トップページ | LPCXpresso LPC1769でFatFsを動かす–USB Host編 »
受信時の、送信ダミーデータの件ですが、普通、DMACはソースアドレスを固定(インクリメントしない)ように設定できると思いますが、LPCのは出来ないんですか?
送信時は、普通、受信データなんかケアせず捨てればいいと思いますが、LPCのSPIはケアしないと止まってしまったりするんでしょうか?
投稿: 通りすがりの者です | 2012年3月17日 (土) 12時30分
コメントありがとうございます。
ご指摘の通り、LPCもソースアドレス・インクリメントの指定bit SIが DMA channel control registerにあり、ソースアドレスを固定できる筈なのですが、SPIではSI = 0(固定)にするとDMAがうまく動いてくれずこの問題を解決できませんでした。GPIOのポート(ソース固定)から読み込んだデータをデスティネーションアドレス(メモリアドレス)をインクリメントしながら読み込むようなケースはうまくいくようなのですが、SPIはメモリ・メモリ間のDMAのような動きになり、片方のアドレスを固定する動作がうまく動きませんでした。方法はあると思うのですが分かっていません。
投稿: todotani | 2012年3月17日 (土) 14時19分