アドレスには 3 つのタイプ,つまりメインメモリアドレス,I/O アドレ ス,設定アドレスがあります.PCI バスでは,設定アドレスは I/O アドレス とよく似た別のアドレス空間を構成します.分かりにくい ISA の設定アドレ スの場合を除き,バス上のアドレスがメモリアドレス,I/O アドレス,設定ア ドレスであるかどうかは,バスの他の線(トレース)の電圧によってのみ決まり ます.
ISA バスの場合,技術的には設定アドレス空間は無いのですが,CPU が PnP カードの PnP 設定レジスタにアクセスするための特別な方法があります. この目的のために,3 つの @ I/O アドレスが割り当てられています.ただし, それぞれのカードに 3 つずつアドレスが割り当てられるのではなく,全ての カードで 3 つのアドレスを共有します.
3 つのアドレスの名前は,それぞれ読み出しポート,書き込みポート,アドレ スポートです.各ポートの大きさはちょうど 1 バイトです.PnP カードはそ れぞれレジスタをたくさん持っていますから,たった 3 つのアドレスではカー ド 1 枚分のレジスタに対してさえ十分ではありません.特定のカードと通信 するためには,特別に割り当てられたカードの番号(ハンドル)を書き込みポー トアドレスを使って全てのカードに送ります.すると,このハンドルを持つ カードだけが監視状態になります.次に(このカードの)設定レジスタのアドレ スをアドレスポートに送ります(アドレスポートは全てのカードが共有してい ますが,現在ポートの監視を行っているのは 1 つだけです).次の通信はその カードの設定レジスタの 1 つで起こります.これは読み出しポートからの読 み出しか,書き込みポートへの書き込みによって行われます.
書き込みポートのアドレスは必ず A79 で,アドレスポートのアドレスは必ず 279 です(16 進値).しかし,読み出しポートのアドレスは固定ではなく,他 の ISA カードと重ならないと思われるアドレスに設定プログラムが設定しま す.アドレスが重なった場合は変更されます.読み出しポートのアドレスを使っ て,全ての PnP カードを「プログラムする」ことができます.したがって, isapnp 等を使ってデータの設定やチェックを行う場合には,この読み出しポート アドレスを決めなければなりません.
本文書では「アドレス」という言葉でアドレスの隣接している範囲を指し ていることがあります.アドレスはバイト単位で与えられるので,1 つのアド レスは 1 バイトの容量しかありません.しかし,I/O(とメインメモリ)アド レスにはこれより大きいサイズが必要です.従って,I/O アドレスには例えば 8 バイトの範囲が使われることが多いですし,デバイスに割り当てられるメイ ンメモリアドレスの範囲はこれよりもずっと広くなります.シリアルポート (I/O デバイス)の場合には,デバイスの最初の I/O アドレス(3F8 等)を与え れば十分です.なぜなら,このデバイスに対するアドレスの範囲は 8 バイト しかないことは常識になっているからです.この範囲のうち最初のアドレスは 「ベースアドレス(base address)」と呼ばれます.
ISA バスの場合,I/O アドレスと(メイン)メモリアドレスの「空間」にア クセスする際には,同じアドレスバスが使われます(アドレスに使われる配線 が共有されます).それでは,デバイスはアドレスバスに現れるアドレスがメ モリアドレスであるか I/O アドレスであるのかをどうやって知るのでしょう か? 実はバス上には 4 つの専用の線があり,このような情報を伝えるのです. 4 つの線のうちの特定のものに信号が流れた場合,これは CPU が I/O アドレ スからデータを読もうとしていることと,メインメモリはバス上のアドレスを 無視することを意味します.残りの 3 つの線も似たような目的で使われます. 簡単に説明すると,読み込みと書き込みの線がメインメモリと I/O アドレス の両方のために存在します(線は全部で 4 つです).
PCI バスの場合も基本的な考え方は同じで 4 つの線を使いますが,その使い 方は少し違います.つまり,4 つのうちの 1 つに信号を流すのではなく,4 つの線を全部使って 2 進数を流します(16 通りの組合せがあります).こう することにより,より多くの情報を送ることができます.16 個のうち 4 つは 前節で述べた I/O 空間とメモリ空間のために使います.設定アドレス空間が さらに 2 個を使います.残りの 10 個は他の目的に使えるように残されてい ます.
ISA バスの場合,同じアドレスを使うカードが他に無いことをチェックす るための方法が各カードに組み込まれています.複数のカードが同じ I/O ア ドレスを使っていると,どちらのカードも正しく動作しないでしょう.まとも な PnP 設定プログラムはこのような衝突が起こらないようにバスリソースを割り 当てるはずですが,その場合でも隠れているレガシーカードが重なるアドレス を持っているかもしれません.
このテストは,カードが自分の I/O レジスタにテスト番号を設定することに よって行われます.次に PnP 設定プログラムはこれを読み出し,同じテスト 番号が読み出せることを検査します.これが異なる場合には何か問題がありま す(例えば,別のカードが同じアドレスを使っている).同様に別のテストが別 のテスト番号を使って繰り返されます.このテストは実際にはカードに割り当 てられた I/O アドレスの範囲でチェックを行うので「範囲チェック」と呼ば れます.これはアドレス衝突テストと呼ぶ方が良いかもしれません.アドレス の衝突があればユーザに対してエラーメッセージが出されるので,ユーザは自 分でこれを解決しなければなりません.
伝統的には,ほとんどの I/O デバイスは CPU と通信する際に I/O メモ リしか使いません.例えばシリアルポートがこれに該当します.CPU 上で動作 しているデバイスドライバは,I/O アドレス空間とメインメモリに対して読み 込みと書き込みを行います.より高速な方法として,デバイスが直接メインメ モリにデータを書き込む方法があります.これを行う方法の 1 つが, DMA チャンネルあるいはバスマスタリングの利用です.また, メインメモリ空間の一部をデバイスに割り当てる方法もあります.このように して,デバイスはわざわざ DMA やバスマスタリングを使わないでも直接メイ ンメモリにデータの読み書きができます.このようなデバイスは通常,I/O ア ドレスも使うかもしれません.
割り込みは多くの情報を伝えることができますが,間接的にしか伝えるこ とができません.割り込みシグナル(配線上の電圧)は,あるデバイスが処理を 必要としていることを割り込みコントローラと呼ばれるチップに伝えます.す ると割り込みコントローラは CPU に信号を送ります. CPU はこのデバイスのドライバを見つけ,「割り込みサービスルーチン」(ま たは「割り込みハンドラ」)と呼ばれるドライバの一部分を実行します.この 「ルーチン」は何が起きたのかを調べ,デバイスとのデータ転送等の問題を処 理しようとします.このプログラム(ルーチン)で,何が起きたのかについ て簡単に調べることができます.なぜなら,ドライバは自分が知っているアド レス内に,調べるためのレジスタを持っているからです(デバイスの IRQ 番号 とI/O アドレスが正しく設定されている場合には).これらのレジスタにはデバ イスに関するステータス情報が格納されています.ドライバはこのレジスタの 内容を読み込み,これを調べることによって,何が起きたかを調べて正しい動 作を行うことができます.
したがって,各デバイスドライバは監視する割り込み番号(IRQ)を知っている 必要があります.PCI バス(またカーネル 2.2 以降では ISA バス上のシリア ルポート)の場合には,複数のデバイスが同じ IRQ 番号を共有することが可能 です.このような割り込みが発行されると,CPU はその割り込みを使っている 全てのデバイスの割り込みサービスルーチンを全部実行します.最初のサービ スルーチンがまず行うことは,割り込みが本当にそのデバイスに対して行われ たのかどうかを確認することです.割り込みが無ければ(alarm は偽となりま す)このルーチンは終了し,次のサービスルーチンが開始します.以降も同様 です.
PCI の割り込みは ISA の割り込みとは異なるものですが,通常は IRQ に マッピングされるため,動作はだいたい同じです.主な違いは PCI では割り 込みを共有できる点です.この共有は自動的に行われます.したがって, 特別なハードウェアやソフトウェアは必要ありません.以前は割り込みの共有 がうまく動作しないという報告もありましたが,これはだいたいデバイスドラ イバのソフトウェアの問題のようです.PCI 用のデバイスドライバは全て割り 込み共有の機能を持っていることになっています.ただし,同じ割り込みを PCI バスと ISA バスで共有することはできない点に注意してください.しか し,割り込みが重なっているデバイスが同時に使われることがなければ,不正 に割り込みを共有していてもたまたま動作することがあります.ここで「使う」 というのは,動作しているプログラムが(C 言語のプログラムの意味で)デバイ スをオープンすることです.
BIOS の CMOS を設定するためや,古い PCI カードのジャンパ設定を行うため には,おそらく PCI の割り込みシステムについての詳しい知識が必要でしょ う.各 PCI カードは INTA# から INTD# (A, B, C, D)までの 4 つ の割り込 みを使うことができます.したがって,スロットが 7 つあるシステムでは 7 x 4 = 28 個の割り込み線を別個に持つことになります.ですが,仕様では 割り込み線の数はこれより少なくてもよいことになっています.もっとも,割 り込みは共有できるので,これはたいした制限ではありません.ここで,これ らの線(配線やトレース)を W, X, Y, Z と呼ぶことにします.また,スロット 3 からの割り込み B を割り込み 3B とします.すると,線 W を使って割り込 み 1A, 2B, 3C, 4D, 5A, 6B, 7C を共有することができます.共有は,線 W を物理的に 1A, 2B 等に接続することで実現します.同様に,線 X を使って 割り込み 1B, 2C, 3D, 4A, 5B, 6C, 7D を共有することができます.そして, 起動時に BIOS が W, X, Y, Z を IRQ にマップします.その後 BIOS は,そ れぞれのデバイスがマップされた IRQ をそれぞれのデバイスのハードウェア レジスタに書き込みます.こうして,デバイスの問い合わせを行うもの全ては デバイスが使う IRQ を知ることができます.
PCI の仕様では,先に述べた配線 W, X, Y, Z に INTA#, INTB#, INTC#, INTD# というラベルがついています.ですが,この正式な記法は紛らわしいも のです.なぜなら,スロットと PCI バスのどちらに注目しているかによって INTA# の意味が2通りに変わるからです.例えば 3C が X にマップされている 場合,スロット3の INTC# が PCI バスの INTA# (X) に配線されているという ことになります.紛らわしい記法ですよね.
必要事項は他にもあります.PCI スロットでは,若い文字の割り込みから使わ なければなりません.したがって,スロットが 1 つしか割り込みを使わない ならば,割り込みは INTA# でなければなりません.割り込みを 2 つ使うなら ば,INTA# と INTB# でなければなりません.以降も同様です.スロット内の カードはデバイスを 8 つまで持つことができますが,PCI 割り込みの割り当 ては 4 つしかありません.割り込みは共有できるのでこれで問題なく,8 つ のデバイスはそれぞれ割り込みを持つことができます.デバイスの PCI 割り 込み文字は,固定値としてデバイスにハードウェア的に結線されていることも よくあります.
BIOS は ISA バスに設定してある IRQ(割り込み)と衝突しないように,PCI の IRQ を割り当てます.CMOS の BIOS メニューでユーザが IRQ を PCI バスに 割り当てることも時々あります(しかし,これが簡単でないのはこれは既に述 べた通りです).IRQ のマッピングを設定した後では,Windows が PCI カード の IRQ を全て 0 にしてしまうという場合もあります.したがって,Windows を使っている人が Windows から Linux を起動した場合,Linux では IRQ が 0 という間違った結果しか得られないということがあります.
読者の皆さんは PCI が IRQ(ISA バス)を使っているために遅い等の理由を考 えるかもしれません.ですが,それは正しくありません.ISA の割り込み コントローラチップは CPU に直結している配線を持っているので,すぐに CPU に信号を送ることができます.ISA アドレスとデータバス上の信号は PCI バス経由で CPU に届くのですが,IRQ の割り込み信号はほとんど直接 CPU ま で届きます.
アイソレーションは ISA バスでしか使えません.これは ISA バス上の各 PnP デバイスに一時的なハンドル(ID 番号またはカード選択番号(Card Select Number, CSN))を割り当てるための複雑な方法です.これよりも効率的な方法 (ただし,さらに複雑です)があるので,これは単純な方法だと言われることも あります.アイソレーションでは,監視を行っている全ての PnP デバイスへ の PnP 書き込みに対して,書き込みアドレス 1 つだけが使われます.この書 き込みアドレスは,それぞれの PnP デバイスに固有のハンドルを送る(割り当 てる)ために使われます.このハンドルの割り当てには,ハンドルが共通のア ドレスに送られた(書き込まれた)際にデバイス 1 つだけが待機していること が必要です.全ての PnP デバイスはアイソレーションの処理で使う固有のシ リアル番号を持っています.アイソレーションの動作はゲームに似ています. これは,全ての PnP デバイスが繋がっている 1 つだけの共通のバスの配線と アイソレーションプログラムの持つ値を同じにすることで行います.
「ゲーム」の最初のラウンドでは,全ての PnP デバイスはこの線を監視し, ここへビット列を同時に送ります.許されるビット値は 1 (正の電圧)または 電圧無しの「開放の 0」(開いた回路か 3 相)のどちらかです.続いて,それ ぞれの PnP デバイスはこの線でシリアル番号をビット毎に高次ビットから送 り始めます.どれかのデバイスが1を送ると,接続されている他のデバイスは 全て 1 を受け取ります.全てのデバイスが「開放の 0」を送ると,接続され ているデバイスはなにも受け取りません.この目的は,(最初のラウンドが終 わるまでに)最も大きいシリアル番号を持つもの以外を取り除くことです. 「取り除く」とは,このデバイスは書き込みアドレスの監視をやめるけれど, ゲームに勝ち残っている全てのデバイスはこのアドレスの監視を続けるという ことです.これは「ドロップアウトする」とも言います.(シリアル番号の長 さは全て同じである点に注意してください.)
まずは,まだハンドルをもらっていないデバイス全てが最初に配線に流したシ リアル番号の最も高次のビットについてだけ考えてみましょう.ある PnP デ バイスが 0 (開放の 0)を送ったけれど 1 を受け取った場合,これは他の PnP デバイスがより大きいシリアル番号を持っているということです.つまり,こ のデバイスはこのラウンドから一時的に脱落し,このラウンドが終わるまでは ビット列を読み込まなくなります.(ラウンドが終わった時点で勝者,つまり 最も大きいシリアル番号を持っているデバイスにハンドルが割り当てられます.) この時,ゲームに残っているデバイスは全て同じ先頭ビット(1)を持っていま す.そこで,このラウンドの続きではこの桁を取り除いて求めた「切り詰めた シリアル番号」だけを考えれば良いことになります.その後はこの段落の先頭 に戻り,シリアル番号全体を調べるまで繰り返しを行います(全てが 0 の場合 については以下を参照してください).
最も大きいシリアル番号がゲームから取り除かれないことは明らかです.しか し,(切り詰めたものも含めて)シリアル番号の先頭の桁が全て 0 だった場合 はどうなるのでしょうか? この場合には「開放の 0」が配線に送られ,全ての デバイスはゲームに参加したままとなります.全てのデバイスの先頭の桁が 0 ならば引き分けになり,前の段落で1を取り除いたのと同じように0を取り除き ます.それからゲームは継続し,(シリアル番号の)次の桁が送られます.
ラウンドの終わり(参加デバイスが残っているうちはシリアル番号の低位ビッ トを送り続けた後)には,最も大きいシリアル番号を持つ PnP デバイスが1つ だけ残ります.このデバイスにはハンドルが割り当てられ,それ以降はゲーム に参加できなくなります.そして,前のラウンド途中で脱落した(まだハンド ルを割り当てられていない)全てのデバイスはゲームに再び参加し,1 つ少な いデバイスの参加で新しいラウンドを始めます.このようにして全ての PnP デバイスにハンドルを割り当てます.このアルゴリズムが正しく動作すること は簡単に証明できます.
一度割り当てられたハンドルは各 PnP デバイスを指すために使われ,PnP と のデバイス設定情報のやりとりに使われます.このハンドルは PnP の設定の ためだけに使うものであり,PnP デバイスとの通常の通信には使われない点に 注意してください.コンピュータの起動時にはハンドルは全て無くなるので, PC を起動する度に PnP BIOS はアイソレーション処理を行います.
以上