W29C040 への書き込みは 0x100 bytes 単位であり、この転送がとても早いことにいまさら気づいた. 1 byte 単位への書き込みでも polling なしに 0x100 bytes 単位で書いたほうが早いのではないかといまさら気づいた. byte programming data の command 発行から不揮発データ書き込み完了は typ(ical) 7 us から 9 us, SST39SF040 は typ の記載がなく max 20 us となる.
一方 MCU から SPI 経由では 1 memory cycle が 4 SPI cycle となり、これが実測 2.0 us 程度で、コマンド発行に 4 memory cycle がいるので 8.0 us となる. よって片方の flash で待ってる間にもう片方へコマンドをいれると書き込みの待ち時間は減らせる.
ここで2通りの転送方法を検討する.
従来の 1 DMA cycle を拡張
従来の 1 DMA cycle は CPU memory へ command 発行 → PPU memory へ command 発行 → CPU memory へ polling → PPU memory へ polling としているから、(CPU memory へ command 発行 → PPU memory へ command 発行) x n → DMA 終了として polling を省略してしまう. n は 4 から 8 程度をいまのところ想定している. これで単純計算で n 倍早くなる.
利点はファームウェアの変更が少ないことと、memory で 0xff で埋まってる場所を飛ばすとか小さすぎる sector erase の対応(未実装)などの細かい処理を入れやすい.
欠点は次の方法より遅いことと descriptor を多用するので MCU の RAM を消費しがちである. 現状としては RAM (.data) の容量不足は感じておらず、 flash (.text と .rodata) の容量不足は確定的で、flash の容量が多いモデルにすることが決まっており必然的に RAM の容量が増えるから大したことではない.
1 DMA cycle で動的に拡張
1 DMA cycle は (CPU memory へ command 発行 → PPU memory へ command) x n として DMA 動作中に別の DMA をかけて SPI 出力前にデータを更新するなり、n 回ループを達成できたら DMA を止めてしまうなどを MCU のデバイスだけで動かして、 MCU の中の CPU は関知しないという方法. n は 0x100 単位を想定している.
この方法を検討したところ、address の部分は TC をカウンタとして利用すること address と data の更新用の DMA を設けるなどすれば動かせることを確認. ただし、 TC は 2 つ, TC または TCC は 1つ、 DMA ch は 4 つ必要となる. address の increment counter は TCC でもできるのだが、 TCC はカウンタ読み出しコマンドの発行が必要でこれが手間である(TC はいらない). ただ、TC は 16 bit で比較することに制限があるのでこれだけのための DMA を止める条件に TCC のカウンタが必要となる.
この動的更新の確認はできており、そのついでに EVSYS の組み方を修正して、タイマで 1 memory cycle にわざと待ちをいれることも可能にした. typ 8 us では動かないこともあるとか、 typ 25 us のデバイスもあるので polling をしない代わりに周期時間の調整は重要となる.
この方式は data 0xff を飛ばすとか、sector size を考慮するとか細かい部分で並列的に動かすというのは実質不可能なので、chip erase や erase に対する polling は同時、バンク切り替えも同時、PCからのデータ更新も同時として1まとまりに動作させることが必要になると予想される.
erase の polling
これは必要であるが、現状の方式では programming cycle のあとに必ず polling をいれていたので、erase と programming の polling は兼用となっていた. 片方がなくなるので polling の組み直しが必要となってしまった.