学校では教えてくれないこと

DMA対応と言われたら(1)

2010.6

やあ、みんな。ビリーだよ。

暗号シリーズが終わり、今回から新シリーズだよ。

ビリーがお客様のハードウェア上のソフト開発を受託する時、「転送パフォーマンスを向上させたいのでDMA対応をしてほしい」と言われることがよくある。「DMA対応」とは、具体的にどういうことをすればいいんだろうか。

今回のシリーズでは、DMA転送の概要、デバイスドライバの実装方法、注意すべき点などについて説明していくよ。

DMAとは

DMAは、Direct Memory Accessの略なんだ。訳すと「直接メモリアクセス」だね。いったい、何が「直接」なんだろう。DMAを行わない場合は、メモリ(※補足1)にアクセスするのは「CPU」になるよね。これを「PIO」方式と言うんだ。PIOは、Programmed I/Oの略だよ。一般的には、PIO方式の対義語がDMA方式ということになるんだ。

※補足1

DMA転送の対象は、メモリ⇔メモリだけではなく、メモリ⇔周辺I/Oデバイスの場合もあるんだ。デバイスドライバのDMA対応という場合は、主に後者ということになるね。ちなみに、メモリと同じようにアクセスが可能なI/OをMemory-Mapped I/O方式という。

DMA方式とは、CPUを介さずに「直接」メモリにアクセスすることをいうんだ。

ここでCPUの代わりをしてくれるのが「DMAコントローラ」というデバイスで、略してDMAC(でぃーまっく)とも呼ばれたりするよ。最近では、組込み向けCPUでも汎用的に使えるDMACを内蔵しているのが当たり前になってきたよね。

DMA転送のメリット

PIO転送とDMA転送のアクセスの違いを図にしてみたよ。この図は、周辺デバイスからデータを読み出して、メモリに書き込む1サイクルの流れを書いてみた。複数のデータを転送する場合は、(1)(2)の処理を繰り返すことになるよね。

DMA転送の説明図

転送を繰り返す場合、PIOでは毎回CPUが自分で転送する必要がある。そのため、大量のデータを読み書きしようとすると、比例してCPU処理の負荷が大きくなるよね。

DMA転送の場合は、最初の設定さえしてやれば実際のデータ転送はDMACがやってくれる。CPUは転送が完了するまで何もする必要がないんだ。しかも、DMACはデータ転送に最適化されているから、CPUよりも高速に転送することができるんだ。

つまり、DMA転送を使うことで、CPUの負荷を上げずに、ハードウェアの能力を利用した高速なデータ転送が実現できるということになるよね。これがDMA転送のメリットなんだ。

※補足2

ちなみに、バス(bus)というのは、接続されているデバイスが共通で使うデータ通信路のことだ。バスという用語は、誰でも自由に乗降できる乗り合い馬車が語源であるといわれているんだよ。バスには必ず管理者がいて、各デバイスのデータがぶつからずに上手くバス上を流れるようにアクセス制御を行う。これをバス調停(bus arbitration)といって、この管理者のことをバスアービタ(bus arbitor)というんだ。

DMA転送のデメリット

良いことばかりに見えるDMA転送だけど、もちろんデメリットもある。そのひとつは、DMACが転送している間は、他のデバイスはバスにアクセスできないということなんだ。

DMA転送のデメリットの説明図

上記のようにCPU, DMAC, メインメモリが同じバスに接続されている場合は、転送が始まると、ほとんどCPUがプログラムを実行できなくなってしまうよね(※補足3)。これではちょっと問題だ。実は、DMACにはいくつかの転送モードがあって、DMA転送中でもCPUが動作できるような仕組みが用意されているんだ。

※補足3

CPUは、通常メモリにおいてある命令(プログラム)を順次読み出して実行するからね。

バスマスタ型デバイス

細かくいうと、DMA機能を持ったデバイスには、マスタとスレーブの2種類があるんだ。マスタはDMA転送を起動する人で、スレーブはDMAマスタからの指示に従ってデータを転送する人のことだよ。

ここまで話してきた汎用DMACは、DMAマスタデバイスの代表的なもの。でも、DMAマスタとして動作するのはDMACだけではなくて、下の表にしたような、高速通信を行う必要がある周辺I/Oデバイスコントローラは、DMAマスタ機能を内蔵しているものがあるよ。

周辺I/Oデバイス主な接続バス備考
ハードディスクSCSI、IDE/ATA、Serial ATA
グラフィックスAGP、PCI Express
USBホストPCI(USB2.0)、PCI Express(USB3.0)UHCI、OHCI、EHCI、xHCI
ネットワークPCIEthernet

これらのコントローラは、自分専用のDMACを内蔵していて、DMAマスタになって能動的にDMA転送を起動することができるんだ。USBホストやEthernetコントローラは、一般にPCI系のバスに接続されていることが多い。PCIバスの規格では、接続したデバイスがDMAマスタになる機能がサポートされているため、DMA転送とは非常に相性がいいんだ。

このようなDMAマスタデバイスを「バスマスタ型デバイス」と言うこともある。最近のPCでは、USBやネットワークは必須機能になっているから、最初からマザーボードやチップセットに内蔵されている。

例えば、Intelチップセットでは、ICH(I/O Controller Hub)というコンパニオンチップにまとめられているんだ。こういう場合も、CPUとはPCIと互換性のあるバスで接続されているので、ソフトウェア(デバイスドライバ)の視点からはPCIの場合と同じように扱うことができるんだよ。

デバイスドライバを開発する時は、最初の調査段階で、対象のデバイスコントローラが「バスマスタ型かどうか」を確認することがとっても大事だ。バスマスタ型の場合とそうでない場合とでは、デバイスドライバの構造が大きく変わってしまうんだよ。

また最近は、組込み向けCPUで、バスマスタ型の周辺I/Oデバイスを内蔵しているものが増えてきたんだ。この場合は、PCIのような汎用バスではなく、CPUアーキテクチャに依存したバスに接続されることになるよね。

「どのバスに接続されているのか」「同じバス上にどんなデバイスがいるのか」という点もデバイスドライバを作るうえで重要なポイントになるよ。

ここまで、DMA転送のメリット/デメリットに加えて、バスマスタ型のDMAデバイスについて触れたので、マスタとくればスレーブだね。

DMAスレーブデバイス

DMAマスタではないデバイスでDMA転送を行いたい場合は、外部の汎用DMACを使うことになるね。このようなデバイスを、マスタに対して、DMAスレーブデバイスというんだ。

DMAマスタデバイスの場合は、専用のDMACがデバイス内部に組み込まれているので、デバイスドライバからその存在を意識する必要はあまりない。でも、DMAスレーブデバイスの場合は、汎用DMACに対して細かいパラメータ設定を行う必要があって、ハードウェアに関する知識が必要になるんだ。

DMA伝送モード

一般的に、汎用DMACの転送モードには以下の3種類がある。使用目的やDMAスレーブデバイスの機能に応じて、これらのモードを使い分けなくてはならないんだ。ただし、DMACによっては、全ての転送モードがサポートされているとは限らないから、データシートで仕様をよく確認する必要があるね。

(1)サイクルスチール転送モード

DMA転送を1サイクル実行するたびにバス制御権を解放する方式だ。シングル転送モードと呼ばれることもある。DMA転送中もCPUや他のDMAマスタデバイスが並行して動作できるんだけど、転送速度は遅くなってしまう。

(2)バースト転送モード

DMA転送を開始すると、指定転送回数が完了するまで連続でDMA転送を実行し、バス制御権を解放しない方式だ。ブロック転送モードと呼ばれることもある。バスを占有するので高速に転送できるんだけど、DMA転送中はCPUや他のDMAマスタデバイスは動作できない。(1)と(2)は逆の関係にあるね。

(3)デマンド転送モード

DMAスレーブデバイスから発行されるDMA転送要求信号によってDMA転送を開始し、要求信号がアクティブになっている間だけDMA転送を行う方式だ。DMAスレーブデバイスが要求信号を取り下げると、汎用DMACはDMA転送を中断してバスの制御権を解放するんだ。DMAスレーブデバイスの要求(状態)に応じて転送が実行されるので、バスを効率よく利用できる方式だ。ただし、専用の信号が使えるようにハードウェアが設計されている必要があるんだ。

汎用DMACの設定

汎用DMACの設定パラメータはDMACの仕様に依存するので、ここでは例を挙げて説明してみよう。ルネサスエレクトロニクスのSH系CPUに内蔵されている汎用DMACの例をとってみるよ。

設定パラメータ概要
転送元アドレス転送元のアドレスを設定する。ソースアドレスとも呼ばれる。
転送先アドレス転送先のアドレスを設定する。デスティネーションアドレスとも呼ばれる。
転送データ長1サイクルで転送するバイト数を選択する。1/2/4/8/32バイトなどがある。
転送回数DMA転送を実行する回数を設定する。転送中にデクリメントされていき、0になると転送完了。
アドレスモード転送元/転送先のアクセス方式を設定する。
  • シングルアドレスモード
  • デュアルアドレスモード
バスモードDMA転送時のバスの制御方式を設定する。
  • サイクルスチール転送モード
  • バースト転送モード
転送要求モードDMA転送要求の種別を決定する。
  • 内蔵周辺モジュールリクエスト
  • 外部リクエストモード(DREQ信号)
  • オートリクエストモード
アドレス増減モード転送元/転送先アドレスの増減方式を設定する。増加/固定/減少を選択する。
その他
  • バスのアクセスウェイト数
  • DMAチャネル優先度
  • DMA転送要求の信号特性
  • 割り込み通知の許可/禁止
など

かなり細かいパラメータ設定が必要であることが分かるかな?

このなかでビリーが面白いと思った「アドレス増減モード」というパラメータについて、詳しく説明してみたいんだ。アドレス増減モードをどのように設定するかは、転送元/転送先として、何を想定しているかによって決まる。いくつか例を考えてみよう。

【例1】メモリ⇔メモリ間DMA転送

転送元/転送先ともメモリ上のアドレスの場合は、両方とも単に「増加」とする。両方とも同じようにアドレスが増加していくからね。

説明図

【例2】メモリ⇔レジスタ間DMA転送

I/OデバイスがFIFOバッファを内蔵している場合、その読み書きのための窓口は、普通レジスタで提供されているんだ。このレジスタが、メモリマップドI/Oでメモリ空間上のあるアドレスに見えるシステムの場合、直接DMA転送を行うことができる。

転送元がメモリで、転送先がレジスタである場合(つまりFIFOバッファへの書き込み)を考えてみよう。この場合は、転送元アドレスはメモリ上にあるので「増加」でいいんだけど、窓口となるアクセスするレジスタは変わらないので、転送先アドレスは「固定」とする必要があるのがわかるかな。

逆に、FIFOバッファからデータを読み出す場合は、転送元アドレスが「固定」で転送先アドレスが「増加」になるよね。DMAスレーブ機能を持つI/Oデバイスに対してDMA対応する場合は、このパターンが多くなるんだ。

説明図

【例3】グラフィックスの矩形領域コピー

ビリーが「アドレスの増減」の必要性を最初に理解したのが、これだったんだ。10年くらい前は、グラフィックスではBitBLT(びっとぶりっと)と呼ばれるDMA転送機能があった。最近のグラフィックスコントローラは専用DMACを内蔵しているのが当たり前になっているから、もう死語かもしれないなあ。

グラフィックスコントローラは、一般にVRAM(Video RAM)あるいはフレームバッファという専用のメモリ空間が割り当てられているんだ。ここに適切なフォーマットのデータを設定すると、画面上の表示画素(ピクセル)の色を制御することができる。BitBLTは、VRAM上でピクセルデータを高速に移動させるためのDMA転送といえるんだ。

ここで、VRAM上を2次元のX-Y平面とみなして、ある領域(x1,y1)-(x2,y2)を別の領域(x3,y3)-(x4,y4)にコピーすることを考えてみよう。領域がまったく重ならない場合は、転送元/転送先それぞれの左上座標を始点として、アドレスを両方とも「増加」させることで問題ない。メモリ間DMA転送と同じだね。

説明図

でも、転送元と転送先の領域が重なっている場合は注意が必要だ。同様にコピーすると、以下のようにおかしな結果になってしまう。なぜだかわかるよね?

説明図

原因は「コピー中に転送元の領域を上書きしてしまう」から。途中で元のデータが変更されてしまっては正しくコピーできないよね。この問題は、始点となる座標を変えて、アドレスを「減少」モードに設定することで解消できるんだ。上記の例であれば、右下座標を始点にして戻りながらコピーするとうまくいくよね。

説明図

これは右下方向で重なるパターンだけど、他にも右上、左上、左下の3パターンあり、それぞれ開始座標とアドレス増減モードの設定を変えてやる必要があることになるよね。アドレス増減モードの「減少」の考え方がわかったかな?グラフィックスを例にとって説明したけど、他にもこういうケースはあるよね。

次回は、デバイスドライバでDMA対応する際に注意しなければならない点について説明してみよう。

DMA対応と言われたら(2)へ続く

ページのトップへ