DMAリクエスト

こんにちはuzakadeuです。

このブログは、UNIXやPC上で純粋な(ハードと関わりのない)アプリケーションを作っていた私が、「ファーム開発に投げ込まれて、戸惑ったことを忘れる前に書き留めておきたい」という動機で始めました。フィルタ等の作り方、Z変換固定小数点数演算については良書がたくさん出ているので、今のところ書く予定がありません。


今日はDMAリクエストについてお話しします。

DMA(Direct Memory Access)は一般に次の二種類の転送を行います。


メモリ⇔ペリフェラル間の転送は、次のようなハンドシェークを行います。

(ペリフェラル)「DMAさんデータを下さい」
(DMA)    「はい、データを送るよ」
(ペリフェラル)「今の時点で欲しい個数分のデータを受け取りました。いったん送信を止めて下さい」
(ペリフェラル)「DMAさんまたデータを下さい」
(DMA)    「はい、データを送るよ」
...(以下繰り返し)...

このうちペリフェラルからの発言がDMAリクエストです。タイミング図などではDMA_reqと略したりします。

なぜDMAリクエストが必要なのか?

DMAを使ってデータを転送する際、三つのレベルで転送を制御します。

  1. 転送元、転送先、データ幅、転送量など*2を指定する。これはプログラムを書く事で指定します。
  2. ペリフェラルが、データを転送(送信or受信)できる状態である、データを転送できない状態である、というレベルでハンドシェークする。
  3. DMAとペリフェラルが持つバス制御回路が、バス使用権を得た、データを送った、データを確かに受け取った、というレベルで転送を制御する。

もちろん二番目がDMAリクエストです。

DMAリクエストの必要性は、DMAリクエストがない場合の問題点を考えるとわかりやすいと思います。

  • まず、DMAには最初の転送を開始するタイミングがわかりません。ペリフェラルは電子回路なので、enable設定にしてすぐに転送できるようになるとは限りません。準備期間が必要な場合もあります。
  • 転送間隔がわかりません。例えば同期式シリアルポートはフレーム同期信号を受け取る度にデータを転送します*3が、この間隔はDMAにはわかりません。
  • ペリフェラル自ら転送を中断する方法がありません。例えばエラーが発生した場合、ペリフェラル自ら転送を一時中断したい場合があります。割り込み等でDMAを中断してもらう方法も一応ありますが、遅く、不確実*4で、プログラマの負担を増やします。

こういった問題点をDMAリクエストというたった一本の信号線が解決してくれます。つまり、

ペリフェラルは、データを転送して欲しい時はDMAリクエスト信号をactiveに保ち、データを転送して欲しくない時はDMAリクエストをinactiveに保ちます。

...たったこれだけです。

DMAリクエスト一回あたりの転送量

DMAには、DMAリクエストを認識する毎に転送するデータ量を設定することができます。


簡単で扱いやすいのはDMAリクエストあたりに1単位(データ幅で指定したデータ一個)だけ転送する設定です。
例えば、ペリフェラルが20個のデータを転送したい場合、DMAは1個転送してはDMAリクエストを確認し、というのを20回繰り返します。この方法は転送効率が悪く、バスを占有する時間が長い、無駄に電力を消費するという弱点はありますが、ハンドシェークが破綻することはありません。


DMAリクエストあたりに複数のデータを転送することも可能です。
例えば、ペリフェラルが20個のデータを転送したい場合、DMAは20個のデータを転送します。ペリフェラルは20個のデータを転送したのでDMAリクエストをinactiveに落とします。この方法は転送効率がよく、バスを占有する時間が短く、消費する電力は最小限であるという長所があります。
ただしペリフェラルが20個のデータを要求している時に、DMAが30個のデータを転送する設定になっていると、余分な10個のデータは転送するけれどペリフェラルが受け取れないオーバフロー状態が発生し、10個のデータは消失してしまいます。*5

ペリフェラルがDMAリクエストをアサートする条件と、DMAがDMAリクエスト1回あたりに転送するデータ量は整合させなければなりません。


DMAリクエスト周りのバグは分かりにくく、ハードやファームに不慣れな人は諦めてしがいがちです。
たしかにハードのバグや不理尽な仕様が原因であることはありますが、致命的なバグ以外はあまり相手にしてもらえませんし、修正したチップが手に入るまでは長い期間がかかります。
仕様書をよく読み、情報を仕入れ、ハードウェア設計者の気持ちになって仮説を立て、実験で実証して、解決策を考えて行くことが肝要です。こういった作業の繰り返しが、無駄がなく、バグの少ないファームウェアにつながって行きます。


これはファーム開発全般に言える事だと思います。

*1:ペリフェラルとは、シリアルポート、表示系、CDカードなどのコントローラのことだと思ってください。

*2:最近のDMAは高機能なのでもっとたくさんのオプションを指定できます

*3:FIFOバッファなどがある場合は少し事情が違います

*4:プログラムは割り込みを無視するかも知れないし、割り込み処理が間違っているかも知れません

*5:ペリフェラルの仕様に依存して他にもいろんな問題が発生することがあります