DMA 転送途中への USB での送信
8月中に実装した機能で録音と disksystem は相手のデバイスを動かしながらデータを取得し、バッファが半分溜まったら USB で送信するというリングバッファ状態を実装していた. ここまで下地を作ってしまったので ROM dump でも実装をしてみた. 結果はシミュレータの実装にかなり手間取ったがちゃんと動いた.
転送速度
1 byte あたりの転送速度は下記となる.
- voice: 48 kHz (1 sample 12 bit で、サンプル頻度 24 kHz)
- disk: 12 kHz (かなりブレるので早めの参考値)
- ROM: 747 kHz (これもブレるので早めの参考値, 740 kHz の場合もある)
voice は DMA を利用しているが, disk では evsys の制約から DMA を利用できないので (MCUの中の) CPU でバッファへ転送している. このレベルであれば ROM は相手が動くのを待つことを無視できるので MCU への実装次第でもっと早くなるが、安いこの MCU の機能を利用して安定させたらこの速度が限界と思われる.
ROM 向けの DMA の実装
DMA channel を 3 つ用意し AH (出力, A15:8), AL (出力, A7:0), DR (入力, D7:0) に割り振る. AL は 0x100 byte 周期で descriptor 1個無限ループ. ここまではいままでの実装.
DR を descriptor を 2つ用意し、buffer を半分に割り振り、descriptor 2つで無限ループ. descriptor 1 つが終わったときに evsys 経由で割り込みをかける. 録音で実装済みで使い回す.
AH は buffer は buffer size を 0x100 bytes 単位で割り振り, 最大 0x100 bytes を 1 descriptor とする. 例えば buffer が 0x550 bytes, 開始アドレスが 0x0080 なら下記となる.
# data count link 0 0x00 0x80 1 1 0x01 0x100 2 2 0x02 0x100 3 3 0x03 0x100 4 4 0x04 0x100 5 5 0x05 0x0d0 end
これがいままでの実装でこれを下記のように AH の DMA を #1 から #5 で無限ループにする.
# data count link 0 0x00 0x80 1 1 0x01 0x100 2 2 0x02 0x100 3 3 0x03 0x100 4 4 0x04 0x100 5 5 0x05 0x100 1
DR descriptor の1つが終わると 0x2a8 bytes 読んだので address 0x0080 + 0x2a8 = 0x0328 まで読み終わったので descriptor の参照先の data の 0, 1, 2 を更新する. (ただし data 0 は無限ループ外なので書き換えても意味がない)
# data count link 0 0x00 0x80 1 1 0x06 0x100 2 2 0x07 0x100 3 3 0x03 0x100 4 4 0x04 0x100 5 5 0x05 0x100 1
DR descriptor が終わるたびに更新を続ける. address 0x1f7f まで読み込むとすると AH の参照先 data 0x1f で count と link を書き換えてしまう.
# data count link 0 0x00 0x80 1 1 0x1f 0x80 end 2 0x1b 0x100 3 3 0x1c 0x100 4 4 0x1d 0x100 5 5 0x1e 0x100 1
感想
参照先を書き換えるのは問題ないのは確認しているが descrptor を書き換えてしまうとちゃんと動くのか少し不安だったが動いてくれた.
今回の実装での手間はこの descriptor と参照先の書き換えをシミュレータで実現すること. C だけで作っていればポインタで更新されるが、 mruby の変数にしていたのでこれを更新する仕組みを作るのがかなりの手間.
一旦実装は出来たものの末尾の更新がうまくいかずにかなりの時間を要してしまった. 上記のリストであれば #0 から更新していたので #1 の link が end になっていると #2 以降が古いままになっている. これに気づくのに2日間、一番古いデータから(上記では#2 から)更新する仕組みを思いつくまで2日間かかってしまった.
今後は flash programming 以外の基本機能はようやくこれで実装したので後回しにしていたUI周りをちゃんとつくる.