DMAとはどういったものでどういう働きをするのか

原作: &a.uhclem; 訳: &a.yasu; 10 December 1996. Direct Memory Access (DMA)は, 中央演算処理装置 (CPU)からの干渉なく データを計算機中である場所から別の場所に動かすための手法です. DMA機能の実装の方法はそれぞれの計算機アーキテクチャ間で異なるもので あるため, ここでの議論はIBMパーソナルコンピュータ(PC), PC/ATとその互換機におけるDMAサブシステムの実装と働きに限定します. PCの DMAサブシステムは, Intelの 8237 DMAコントローラをベースにして います. 8237はそれぞれ独立にプログラムできる4つのDMAチャネルを持ち, それぞれどのチャネルもいつでもアクティブにできます. これらのチャネルは順に 0, 1, 2, 3となっています. PC/ATからは, セカンド 8237 チップが追加され,それらは 4, 5, 6, 7と なっています. オリジナルの DMAコントローラ(0, 1, 2, 3)は, 1回の転送で1バイト 転送します. セカンドDMAコントローラ(4, 5, 6, 7)は1回で 隣接する2つのメモリ番地から 16ビット転送します. ここで, 最初のバイトは通常偶数のアドレスになります. 2つのコントローラは全く同じものであり, 転送量が異なるのは セカンドコントローラがシステムに直結しているためです. 8237 は個々のチャネルについて, DRQと-DACKという2つの電気信号を 持っています. その他に, HRQ (Hold Request), HLDA (Hold Acknowledge), -EOP (End of Process)があり, バス制御信号として -MEMR (Memory Read), -MEMW (Memory Write), -IOR (I/O Read), and -IOW (I/O Write)があります. 8237 DMACは, いわゆる``fly-by'' DMAコントローラです. これは, データの移動を行う際に, データは DMACチップを通過せず, DMACチップに格納されないことを意味します. また, DMACはI/Oポートとメモリアドレス間でのみデータを 転送することができますが, 2つのI/Oポートもしくは2つのメモリアドレス 間ではできません. 注: 8237は, 非``fly-by''モードでは, 互いに接続された 2つのチャネルでのメモリ-メモリ間でのDMA操作を許可します. しかし, PCメーカは, ただでさえ乏しいこのリソースをこんなふうに 使ったりしません。 なぜなら, CPUを使用してメモリ間のデータを動かす方が早いからです. PCアーキテクチャでは, それぞれのDMAチャネルは, 通常そのDMAを 使用するハードウェアがそのチャネルについてDRQを使って 転送を要求した時のみ動作します. DMA転送の例

これはDMA転送の手順の例です. この例では, フロッピーディスクコントローラ (FDC)が ディスケットから1バイト読み込んで, DMAを使って,メモリの0x00123456番地に 格納したいとします. 処理は, FDCによって, DRQ2信号を有効にして DMAコントローラに要求を伝えることで開始します. DMAコントローラはDRQ2シグナルが有効になったことを記録します. するとDMAコントローラはDMAチャネル2がプログラムされ, 有効に なっていることを確認します. DMAコントローラはまた, 他のDMAチャネルがアクティブでないか, または より高い優先度を持っていないかを確認します. 一旦これらのチェックが完了すると, DMACはDMACがバスを使うために バスを開放するようにCPUに要求します. DMACはCPUにHRQ信号を送ってバスを要求します. CPUはHRQ信号を検出し, 現在の指示の実行を完了します. 一旦プロセッサがバスを開放することができる状態になると, 解放を 行います. 通常は CPU により駆動される信号 (-MEMR, -MEMW, -IOR, -IOW, その他)を すべてハイインピーダンス (ハイともローとも指定しない)状態にした後, CPUは HLDA信号を有効にして DMAコントローラにバスを明け渡したことを 伝えます. プロセッサによっては, CPUはバスを使用しないいくつかの 命令を追加して実行することもできますが, しかし,プロセッサの内部キャッシュやパイプライン以外のメモリから 何か読み出すといった指示に到達したら結局CPUは待たなくてはなりません. ここで,DMACが バスを「託される」と, DMACはその -MEMR, -MEMW, -IOR, -IOW 出力信号をアクティブにし, DMACから出力されるアドレスは 0x3456にセットされます.これは 転送しようとする特定のメモリ番地をバイトで指示するのに使われます. するとDMACはDMA転送をリクエストしたデバイスに転送が始まることを 知らせます.これは -DACK信号をアクティブにすることで行われます. フロッピーディスクコントローラの場合は, -DACK2を アクティブにすることで行われます. バスのデータ線に転送されるバイトにを出力することについては フロッピーディスクコントローラが責任をもつことになります. もし,フロッピーディスクコントローラがバス上にバイトデータを 出力するのに余計な時間を必要としなければ (もし周辺装置がもっと時間を必要とする場合には, READY信号を 経由してDMACに通知します), DMAは 1 DMAクロック待ち, メモリにバス上のバイトデータを格納するために -MEMW および -IOR 信号を解除します. そして FDCはバイトデータが転送されたことを認識します. DMAサイクルは1度に1バイトしか転送しないので, FDCはDRQ2信号を止めて, DMACに転送の終了を知らせます. DMACは-DACK2信号を解除して, FDCはバス上へのデータ出力を 停止しなくてはならないことを知らせます. 次にDMACは他のDMAチャネルのいずれかに要求がきていないか チェックを行います. もしどのチャネルのDRQも有効になっていなければ, DMAコントローラは処理を完了して, -MEMR, -MEMW, -IOR, -IOW および アドレス信号をハイインピーダンス状態にします. 最後に, DMAはHRQ信号を解除します. CPUはこれを見ると,HOLDA信号を 解除します. そしてCPUは自らの -MEMR, -MEMW, -IOR, -IOW 信号および アドレス線を有効にし, 命令の実行やメインメモリや周辺機器へのアクセスを 再開します. 典型的なフロッピーディスクの1セクタについては, 上記のプロセスが それぞれのバイトについて1回行われ, 全部で512回繰り返されます. 1バイト転送される毎に,DMAC内のアドレスレジスタはインクリメントされ, 何バイト転送すればよいかを示すカウンタがデクリメントされます. カウンタが0になると, DMAはカウンタが0になったことを示すEOP信号を 送り, DMAコントローラがCPUによって再びプログラムされるまで これ以上データは転送されなくなります. このイベントはターミナルカウント(TC)とも呼ばれます. EOP信号は1本しかありません. なぜならどんな時もただ1つのDMAチャネル のみをアクティブにすることができるためです. もし, バッファの転送が完了した時に周辺機器から割り込みを発生させたい とき, 周辺機器は -DACK信号およびEOP信号の両方が同時に発信されたか どうかをテストします. それが生じると, DMACはCPUの介在がなければ これ以上はその周辺機器についての情報を転送しないことを意味します. すると周辺機器はプロセッサの注意を得るために割り込み信号のうちの1つを 発信します. DMAチップ自身は割り込みを生じさせる能力は持っていません. 周辺機器とそれに関連するハードウェアが割り込みを生成する責任を 持ちます. DMACが要求を出したときにはCPUは常にバスをDMACに開放しますが, この動作は, DMACがアクティブになった時にプロセッサが命令を実行するのに かかる時間がわずかに変化することを除いては, アプリケーション, オペレーティングシステムの両方からはわからないということを 理解することが重要です. そのため, プロセッサが確かにDMA転送が完了したことを知るためには, 周辺装置やDMAチップ中のレジスタを調べたり,周辺装置からの割り込みを 受け取る必要があります. DMA ページレジスタ および 16メガ アドレス空間制限

これまで述べたのとは異なり, DMACはアドレス線を 0x0123456 にセットする 代わりに 0x3456 だけをセットすることにあなたは気づいたかも しれません. この理由について少し説明します. オリジナルのIBM PCがデザインされた時, IBMは, DMACと割込み制御チップの 両方を, 8085(8ビットプロセッサで, 16ビットのアドレス空間(64k)を持つ)と 組み合わせて使うように設計されたチップを使うことを選びました. IBM PCが64k以上のメモリをサポートしていたため, DMACが64kを越えるメモリ番地に読み込み又は書き込みを行うために 変更を行う必要が生じました. この問題を解決するためにIBMが行ったのは, それぞれのDMAチャネルに ついてラッチを追加することでした. つまり, 読み込む又は書き込む先の アドレスの上位ビットに保持するためのものです. DMAチャネルがアクティブな時はいつでも, このラッチの内容はアドレスバスに書かれて, そのチャネルのDMA操作が 終了するまでそこに保持されます. これらのラッチは「ページレジスタ」と呼ばれます. そのため上記に示した例では, DMACはアドレスの0x3456の部分をバス上に 置き, DMAチャネル2に対するページレジスタは, 0x0012xxxxをバス上に 置きます. これらの2つの値が組み合わされてアクセスされるメモリ中の完全な アドレスを形成します. ページレジスタのラッチはDMAチップとは独立であるので, 読み込まれる又は書き込まれるメモリ領域は, 64kの物理的境界を またいではなりません. DMACがメモリの0xffff番地をアクセスすると, データの転送後, DMACはアドレスレジスタをインクリメントし, 0x0000番地にある次のバイトを アクセスします. 0x10000番地ではありません. これはおそらく意図されたものとは異なっているでしょう. 注: 「物理的な」 64Kの境界を 8086モードの 64k「セグメント」と混同してはいけません. セグメントは, セグメント レジスタにオフセットレジスタを加算して作られるものです. ページレジスタにはアドレスのオーバーラップはありません. さらに複雑なことには, PC/ATでは外部のDMAアドレスのラッチは 8ビットしか保持しません. よって8+16で24ビットになり, これは DMAが0から16メガの間のメモリ番地しか指し示せないことを 意味します. 16メガ以上のメモリを持ったより新しいマシンにおいても, PCコンパチブルなDMAでは16メガ以上のメモリ番地にはアクセスできません. この制限を避けるために, オペレーティングシステムは 16メガ以下にある物理的な64kの境界をまたがない領域にバッファを 予約します. そして, DMACはデータを周辺機器からそのバッファに 転送するようにプログラムされます. 一旦DMACがこのバッファに データを動かすと, オペレーティングシステムは本当にデータを 格納したいアドレスにバッファからデータをコピーします. 16メガを越えるアドレスからDMAベースの周辺機器にデータを 書き込む際には, データは16メガ以下に位置したバッファから最初に コピーされなくてはならず, その後, DMACはバッファからハードウェアに データをコピーすることができます. FreeBSDでは, これらの予約バッファは 「バウンスバッファ」と呼ばれます. MS-DOSの世界では, これらは「スマートバッファ」などと呼ばれます. DMA操作モードとその設定

8237 DMA はいくつかのモードで動作します. 主なモードは, 以下のとおりです. シングル転送モード シングルバイト(もしくはワード)が転送されます. DMAは1バイト毎にバスを開放し, 再び要求しなくてはなくてはなりません. これは一般に, すぐにはデータのブロック全てを転送できないデバイスに よって使用されます. 周辺装置は次の転送の準備ができる毎にDMAを要求します. フロッピーディスクコントローラは1バイトのバッファしか持たないので, このモードを使用します. ブロック/デマンド転送モード 一旦DMACがシステムバスを取得すると, 最大64kまでのデータブロック 全体が転送されます. もし周辺装置が余分に時間を必要とするときは, 転送を一時中断するためにREADY信号を有効にします. READY信号は過度に使われるべきではなく, 遅い周辺装置の転送の場合は シングル転送モードを代わりに使うべきです. ブロック転送モードとデマンド転送モードの違いは, 一旦ブロック転送が 始まると,転送カウンタか0になるまでそれが行われるところです. 1バイト転送するにはDRQが -DACK が有効になるまでの間だけ 有効であれば充分です. デマンドモードはDRQが有効な間は転送が続けられます. DMACが転送を一時中止した場合はバスを解放してCPUに返します. その後、DRQが有効になると, 転送は中断したところから再開されます. データの転送, 特に転送に使われるメモリ番地が16Mを越える場合に, CPUを使った方が効率がよくなるまでCPUの速度が向上する以前の 古いハードディスクコントローラはデマンドモードを使っていました. カスケード転送モード このメカニズムはDMAチャネルがバスを要求することを許可する ものですが, 接続されたデバイスはバス上のアドレス情報の配置に ついてDMACに代わって責任を持ちます. これはいわゆる「バスマスタ」というものです. カスケードモードのDMAチャネルがバスのコントロールを受け取ると, DMAは通常行われるようなバス上のアドレスとI/Oコントロール信号の 出力を行いません. 代わりに, DMAはこのチャネルの -DACK信号を 有効にします. この時点で, アドレスとバスコントロール信号の供給は DMAチャネルに接続されたデバイスが担当します. 周辺機器はシステムバスの完全なコントロールを行い, 16メガ以下の任意のアドレスの読み込みおよび書き込みを行うことが できます. 周辺機器はバスの使用を終えると, DRQ線を無効にして, DMAコントローラはCPUもしくは他のDMAチャネルに制御を返します. カスケードモードは複数のDMAコントローラを相互接続するのに 使われます. PC内ではDMAチャネル4がまさにこの用途に使われています. 周辺機器がDMAチャネル0, 1, 2, 3でバスを要求すると, スレーブDMAコントローラは HLDREQ を有効にしますが, この線は実際にはプライマリDMAコントローラのDRQ4に接続されています. プライマリのDMAコントローラはその後 HLDREQ を使ってCPUにバスを 要求します. バスが与えられると, -DACK4が有効になり, この線は実際にはスレーブDMAコントローラの HLDA信号に 接続されています. スレーブDMAコントローラはその後要求したDMAチャネルに対して データを転送するか, SCSIコントローラのような バスマスタリングを要求する周辺機器にバスを許可します. このような配線がおこなわれているため, PC/ATシステムでは DMAチャネルは 0, 1, 2, 3, 5, 6, 7のみが使用できます. 注: 初期のIBM PCコンピュータでは, DMAチャネル0は操作の リフレッシュのために予約されていますが, 最近のシステムでは通常, 周辺機器によって使用することができます. 周辺機器がバスマスタリングを行っている時は, システムバスを保持している間絶えずメモリにもしくはメモリから データを転送することが重要です.もし, 周辺機器がこのように できないときは, システムがメインメモリのリフレッシュを 行なえるようにしばしばバスを開放しなくてはなりません. 全てのPCでメインメモリとして使われるダイナミックRAMは, 中身が「満たされている」ビットを保持するため 頻繁にアクセスされなくてはなりません. ダイナミックRAMは, それぞれが1ビットのデータを記憶するコンデンサが たくさん集まって構成されています. これらのコンデンサは充電された 状態で"1", 充電されていない状態で"0"を表します. 全てのコンデンサは放電するため, "1"の値を保持するために, 一定の間隔で電力を加える必要があります. 実際にRAMチップはRAMの適切な場所に電力を送る作業を行ないますが, メモリのリフレッシュ作業がRAMを普通にアクセスする時と 衝突しないように, それをいつ行なうかを コンピュータが休止状態の時に知らせなくてはなりません. もしコンピュータがメモリのリフレッシュを行なえない場合は, メモリの中身はわずか数ミリ秒で壊れてしまいます。 メモリの読み込みと書き込みのサイクルはリフレッシュサイクルとして カウントされる(ダイナミックRAMのリフレッシュサイクルは 実際には不完全なメモリ読み込みサイクルになります)ので, 周辺機器のコントローラが連続するメモリ番地からデータの読み込み または書き込みを行う間は, メモリの全てがリフレッシュされます. バスマスタリングはいくつかのSCSIホストインターフェースやその他の ハイパフォーマンスな周辺機器コントローラに見られます. 自動初期化転送モード このモードにおいてDMAはバイト, ブロック, デマンド転送を行いますが, DMA転送カウンタが0になると, カウンタとアドレスはDMAチャネルが もともとプログラムされた時のものに戻されます. これは, 周辺機器が転送を要求している間は転送が続けられることを 意味します. 転送領域としてDMACにプログラムされた固定バッファの中で, 出力操作でDMACがデータを読み出す前もって新しいデータを 書き込んだり入力操作でDMACが書き込んだあとに, そこから新しいデータを読み出す作業はCPUが受け持ちます. このテクニックは, サンプリング用のバッファが小さいもしくは それを持たないオーディオデバイスによく使われます. この「環状」バッファの管理は更なるCPUオーバーヘッドになりますが, DMAカウンタが0になり, 再プログラムされるまでDMAが停止してしまう ことによって起きる遅延は, この方法でしかなくす事ができない 場合もあります. DMAのプログラミング

プログラムされるDMAチャネルは, 通常, 設定を行う前に 「マスクする」べきです. これはハードウェアが予期せずDRQを有効にすると, たとえ全てのパラメータが 満たされてない場合や更新されていない場合でも, DMACは それに応答してしまうからです. マスクを行ってから,ホストは転送の方向(メモリからI/O, もしくはI/Oからメモリ)と, 転送に使用するDMA操作のモード (シングル, ブロック, デマンド, カスケードなど)を設定し, 最後に アドレスや転送の長さを設定します. 設定される長さはDMACに転送させたい量よりも1少なくなります. アドレスや転送長のLSBとMSBは同じ8ビットI/Oポートに書き込まれます. そのためDMACが最初のバイトをLSBとして, 2番目のバイトをMSBとして 受け取ることを保証するために, 最初に別のポートに書き込みを行なって LSBとMSBの判別を行なうフリップフロップをクリアしておく必要があります. そして,DMAのページレジスタを更新します. これはDMACの外部にあり I/Oポートの別のセットを通してアクセスされます. すべての設定ができると, DMAチャネルはマスクを解除することができます. そのDMAチャネルは「準備ができた」とみなされ, DRQが有効になると 応答します. 8237のプログラミングの正確な詳細については, ハードウェアデータブックを参照してください. PCシステムにおけるI/Oマップについても参照する必要があるでしょう. このマップにはDMAおよびページレジスタのポートがどこに位置するのかを 書いてあります. 以下に完全な表を示します. DMAポートのマップ

IBM-PCとPC/ATに基づくすべてのシステムでは, 同じI/Oポートに配置された DMAハードウェアを持っています. その完全なリストを以下に示します. DMAコントローラ2に割り当てられたポートは, AT以外のデザインでは 未定義になっています. 0x00 - 0x1f DMA コントローラ #1 (Channels 0, 1, 2 and 3)

DMA アドレス および カウントレジスタ 0x00 write Channel 0 starting address 0x00 read Channel 0 current address 0x02 write Channel 0 starting word count 0x02 read Channel 0 remaining word count 0x04 write Channel 1 starting address 0x04 read Channel 1 current address 0x06 write Channel 1 starting word count 0x06 read Channel 1 remaining word count 0x08 write Channel 2 starting address 0x08 read Channel 2 current address 0x0a write Channel 2 starting word count 0x0a read Channel 2 remaining word count 0x0c write Channel 3 starting address 0x0c read Channel 3 current address 0x0e write Channel 3 starting word count 0x0e read Channel 3 remaining word count DMA コマンドレジスタ 0x10 write Command Register 0x10 read Status Register 0x12 write Request Register 0x12 read - 0x14 write Single Mask Register Bit 0x14 read - 0x16 write Mode Register 0x16 read - 0x18 write Clear LSB/MSB Flip-Flop 0x18 read - 0x1a write Master Clear/Reset 0x1a read Temporary Register 0x1c write Clear Mask Register 0x1c read - 0x1e write Write All Mask Register Bits 0x1e read - 0xc0 - 0xdf DMA コントローラ #2 (Channels 4, 5, 6 and 7)

DMA アドレス および カウントレジスタ 0xc0 write Channel 4 starting address 0xc0 read Channel 4 current address 0xc2 write Channel 4 starting word count 0xc2 read Channel 4 remaining word count 0xc4 write Channel 5 starting address 0xc4 read Channel 5 current address 0xc6 write Channel 5 starting word count 0xc6 read Channel 5 remaining word count 0xc8 write Channel 6 starting address 0xc8 read Channel 6 current address 0xca write Channel 6 starting word count 0xca read Channel 6 remaining word count 0xcc write Channel 7 starting address 0xcc read Channel 7 current address 0xce write Channel 7 starting word count 0xce read Channel 7 remaining word count DMA コマンドレジスタ 0xd0 write Command Register 0xd0 read Status Register 0xd2 write Request Register 0xd2 read - 0xd4 write Single Mask Register Bit 0xd4 read - 0xd6 write Mode Register 0xd6 read - 0xd8 write Clear LSB/MSB Flip-Flop 0xd8 read - 0xda write Master Clear/Reset 0xda read Temporary Register 0xdc write Clear Mask Register 0xdc read - 0xde write Write All Mask Register Bits 0xde read - 0x80 - 0x9f DMA ページレジスタ

0x87 r/w DMA Channel 0 0x83 r/w DMA Channel 1 0x81 r/w DMA Channel 2 0x82 r/w DMA Channel 3 0x8b r/w DMA Channel 5 0x89 r/w DMA Channel 6 0x8a r/w DMA Channel 7 0x8f Refresh