アドレスデコーダと周辺I/Oデバイス

アドレスバスとアドレス空間

プロセッサがある特定のスイッチ、メモリ等にアクセスしたいときには、 アドレスバスによって、アクセス相手を指定するのでした。 (3日目の実験内容を参照) その際、どのデバイスにアクセスしようとしているのかを、 アドレスバスから読み取り、アクセス対象のデバイスに指示をする 回路が必要になります。 このような回路をアドレス デコーダ (address decoder)と呼びます。 アドレスデコーダは、アドレスバスの値から、 アクセス対象の選択信号を作り出すことになります。

CPUボード上のI/Oデバイスのアドレスデコーダ

CPUボード上には、スイッチ(8個)、LED(8個)、7セグメントLED(4桁)、 SRAMというI/Oデバイスが載っていて、それぞれ、次のような アドレス空間にマッピングされているのでした。
アドレスの値アクセスされるデバイス
0x2000 - 0x3fffSRAM
0x4000 - 0x40037セグメントLED
0x4004スイッチ(8個)
0x4005LED(8個)
これらは、実際にはCPUボード上で、 74AHC138(PDF)という論理ICを2個用いて、 次のようなアドレスデコーダを構成することで実現されています。

74AHC138は、G1=1, /G2A=0, /G2B=0, のときのみ、 A, B, Cを3桁の2進数とみなし、その値に対応するY0〜Y7の いずれかのみが0になる、という機能を持っています。 (G1=1, /G2A=0, /G2B=0のとき以外は、Y0〜Y7はすべて1となる)

この2つの74ACH138を用いて、あわせてアドレスデコードを 実現していることに注意しましょう。 例えばSRAMがアクセス対象となることを示す信号である CSRAMnが0(負論理)となるのは、 A15=0, A14=0, A13=1のとき、ですから、 アドレスバスがそのようになる値の範囲は、 「0x2000〜0x3fff」ということになります。 A12〜A0は、そのままSRAMにつながっています。 すなわち、アドレスバスの上位3本(A15〜A13)によってSRAMが選択され、 そのSRAMの中でアクセスされる場所は、A12〜A0によって指定をされることに なります。

演習4-1

CPUボード上で、スイッチ、LED、7セグメントLEDが、それぞれ 上記のようなアドレス空間にマッピングされる理由を考えてみましょう。 また、実は例えばスイッチは0x4004だけでなく、0x5004などにアクセスしても 同じスイッチへのアクセスが起こることになります。 すなわちCPUにとっては、スイッチが別のアドレスに 2組(以上)あるように見えますが、実際には同じものが アクセスされることになります。 (このような現象をシャドウ(shadow)と呼びます) このほかに、どのようなアドレスでアクセスできるか考え、 実際にアクセスをするプログラムで動作を確認してみましょう。

※ヒント: 2進数で考えるとわかりやすいでしょう。 つまり、アドレスデコーダにつながっていないアドレスバスの値は、 CPUが区別しても、アドレスデコーダには無関係です。 すなわち、その値が0でも1でも、アドレスデコーダの出力である CSSWnなどの値には無関係ということになります。

外部バス用I/Oデコーダ

外部I/O装置(0x6000-0x6fff)へのアクセスも、上記の アドレスデコーダによってつくられるCSEXTnという信号線で 示されます。 実際には、CPUからのアドレスバス・データバスは、 このCSEXTn=0のときのみ、コネクタCN-Aに電気的に接続されています。 なおこの電気的な接続は、回路図中のバッファIC (74HCT541(PDF), 74HCT245(PDF))によって実現されています。 興味がある人はデータシートを読んでみましょう。

出力デバイスの設計

CPUボードのコネクタCN-Aには、先ほどの回路図のように アドレスバス、データバスのほかに、CPUが書き込み・読み出しを 行うときにそれぞれ0となるWRn, RDnもつながっています。 このコネクタCN-Aを、FPGAボードのコネクタに下の図のように フラットケーブルで接続し、FPGAをCPUからアクセス可能なI/Oデバイスとして 扱うことができます。 なおフラットケーブルは、ボード側の△マーク(1番ピン)どうしがつながるような 向きにすること。

この実験の前半で設計したFPGAの回路では、 実はA[0]〜A[7], A[12]〜A[15]という信号名でアドレスバスが、 D[0]〜D[7]という信号名でデータバスが、 WRn, RDnという信号名でそれぞれWRn, RDnが、 それぞれCPUボード上の信号線と接続することができます。 (CN-Aのピン数の制限から、A[8]〜A[11]は外部に接続されていません)

これらの信号線を使って、CPUが外部I/Oアドレス空間にアクセスする ときに、動作するI/Oデバイスを作ることができます。 例として、CPUボードから、アドレス空間内の0x60f0にアクセスすると、 FPGAボード上のLED8個(または7セグメントLED)の点滅を制御できるI/Oデバイスを 設計してみることにしましょう。 なおFPGAの回路設計は、回路図、VerilogHDLのどちらでもかまいませんが、 ここではVerilogHDL記述を用いることにします。

この回路をVerilogHDLで記述すると、次のようになるでしょう。

module sample(A, D, RDn,WRn, LED);
  input  [15:0] A;
  input  [7:0]  D;
  input         RDn, WRn;
  output [7:0]  LED;

  reg    [7:0]  LED;

  always@(posedge WRn)begin
    if ({A[15:12],A[7:0]} == 12'h6f0) begin
      LED <= D;
    end
  end
endmodule
アドレスバスのうちA[11]〜A[8]はFPGAボードに接続されていませんから、 この値を飛ばして、残り12本のアドレスバスであるA[15]〜A[12]と A[7]〜A[0]のみを用いてアドレスデコードをするために、 {}を用いてこれらのアドレスバスのみを扱うようにしています。 この例では、{A[15:12],A[7:0]}が0x6f0、 すなわち、A[15:12]=0x6、A[7:0]=0xf0となることを 条件として書いています。 ちなみにA[11]〜A[8]は、値が0となるか1となるかは不定(場合によって 異なる)ので、ここの値を0または1と仮定して アドレスデコードするのは避けるべきです。

なおこのmoduleの入出力ポートの記述には、この回路では用いない CPUが読み出しを行うときに用いるRDnも含まれていますが、 この記述は削除してはいけません (記述しないと、コンパイル時に、このピンがFPGA側で GNDに固定されてしまうため)。

演習4-2

このVerilogHDL記述によって、前述のような出力デバイスが 実現きる理由を理解し、その動作を確認しましょう。 なおこの動作確認の際には、CPU側のプログラムにも 修正を加える必要があります。 (FPGAボード側に作成した、アドレス0x60f0に割り当てられている 出力デバイスに値を書き込むことで、FPGAボード側のLEDが 点灯することになります。)

またこの出力デバイスを、外部I/O空間内の別のアドレスに マッピングしてみましょう。 すなわち、別のアドレス、たとえばCPU側から0x60e0に書き込みをすると LEDの点灯パターンを制御できるように、 CPUのプログラム、FPGAの回路の双方を変更してみましょう。

入力デバイスの設計

CPUボードへの入力デバイスも、先ほどの出力デバイスと同様に 設計できます。 ただし入力デバイスは、アクセスされているとき以外は、 データバスを「駆動」、すなわち0または1の値を出力してはいけません。 アクセスされていないデバイスもデータバスを「駆動」してしまうと、 データバス上で信号の「衝突」が起こり、値が不定となるだけでなく、 場合によって0と1の引っ張り合いが起こり、CMOS回路の場合は 回路の破壊が起こってしまいます。

そこで入力デバイスは、アクセスされていない状態では、 データバスから切り離すようにします。 この「切り離し」は、実際には高インピーダンス状態として 実現します。 これは、入力デバイスのデータバス側の出力と、データバスが、 非常に高いインピーダンス(抵抗)で接続された状態のことで、 事実上、電気的に切り離された状態になります。 この高インピーダンス状態は、VerilogHDLでは"Z"という値として 例えば次のように記述されます。(high impedance = HiZから)

  assign D = (CS==1 && RDn == 0)?(Dint):(8'bZZZZZZZZ);
これにより、データバスD[7:0]は、アクセス対象として選択されたことを アドレスバスから判断して1となるCS (Chip Select)が1の場合で、 かつそのアドレスに対する読み出しが起こる(RDn=0)ときのみ、 内部データバスDint[7:0]に接続され、それ以外の場合は8ビットのZ(各ビットが 高インピーダンス状態)になります。 このCS信号は、具体的には、先ほどの出力デバイスの場合と 同様に、アドレスバスの値をデコードして、モジュール内部の 信号(wire)として作成します。

なおこの?と:を用いる表記法はC言語でも使われるもので、 使えるようにしておくとかなり便利です。

(ある条件)?(条件が成り立つときの値):(それ以外のときの値)

?を疑問文のクエスチョンマークと読むと、理解しやすいでしょう。

また高インピーダンスを回路図による回路設計で用いる場合は、 primitivesの中の"tri"を用いるとよいでしょう。 (triは、tri-state bufferの略で、出力が0, 1, Zの3つの値を とることができることから、この名前があります)

なお実際に回路をつくるときには、CPUからの読み出し時に、 データバスに接続するべき値(上記の例ではDint)と、 その条件(上記の例ではCS)のところには、具体的なピンや論理式を 記述する必要があります。

演習4-3

CPUボードから0x60f1にアクセスするとFPGAボード上の スイッチ4個の値を取得できる入力デバイスを設計し、FPGAに転送後、 CPUからアクセスして動作を確認してみましょう。 FPGAの回路設計は、回路図、VerilogHDLのどちらでもかまいません。 またFPGAボード上のスイッチは4個ですので、 例えばD[0]〜D[3]を使うとよいでしょう。 (当然ですがCPU側のプログラムも、この入力デバイスの値を読み取るように 修正を加える必要があります) またそれを別のアドレスにマッピングしてみましょう。

なおこのとき、さきほどの出力デバイス(WRnに応じて動作する回路)と、 この入力デバイスを同一回路中に記述すると、データバスが、 書き込み時と読み出し時で入出力を切り替える必要があります。 そのため、出力デバイスを記述せずにデータバスを(FPGA側にとって)出力専用に するか、あるいは後述のように、データバスを、入力と出力を切り替えられる ようにinout型で定義をするようにします。

入出力デバイスの設計

入力と出力を切り替えられるデバイスも設計することができます。 例えばRAMは、同一のアドレスに対して、RDn=0となるときは読み出し、 WRn=0となるときは書き込みを行うことができます。 このような場合は、I/Oデバイスのデータバスとの接続端子を VerilogHDLの場合は、入力にも出力にもなる端子である"inout"として 宣言しておき、その値を、アクセス状況に応じて切り替えるようにします。 例えば0番地〜3番地のメモリをもつ4バイトのRAM (CPUから見たアドレスは0x6000〜0x6003)は、 次のように記述することができるでしょう。 (module文やCSの生成部分は省略しています)
  inout [7:0]  D;
  input [15:0] A;
  reg   [7:0]  ram[3:0];
  wire  [1:0]  addr;

  assign addr = A[1:0]; // address for specifiing the location in RAM

  assign CS = ...;

  assign D = (CS==1 && RDn==0)?ram[addr]:8'bZZZZZZZZ;

  always @(posedge WRn) begin
    if (CS == 1) begin
      ram[addr] <= D;
    end
  end
なおこの場合のCSは、このRAMへのアクセスが発生していることを示す信号、 すなわち、アドレスバスの値が0x6000〜0x6003のいずれかになっていることを 示す信号、ということになります。 そして、アドレスバスの値がこの範囲のときに、 アドレスバスの下位2ビットA[1:0]を用いて、RAM内の4バイト中の場所を 指定することになります。

このことに注意し、CSの生成にあたって、アドレスバスのどの部分を 判定条件に加えるかをよく考えてみましょう。

演習4-4

適当なサイズをもつRAMを設計して実装し、CPUボードからアクセスして 読み書きの動作を確認してみましょう。

例えば、上記のような4バイトRAMの0番地〜3番地(CPUから見たアドレスは 0x6000〜0x6003)のいずれかに、CPUボードのSWの値を書き込み、 またそこを読み出しでLEDで表示する、あるいは、 RAMに適当な値を書き込むプログラムと、 それらの値を読み込んでCPUボード上のLEDや7セグメントLEDに 表示するプログラムを順に実行し、正しくRAMへの読み書きが 実行できていることを確認すればよいでしょう。

※このとき、書き込みしてすぐに読み出しをすると、FPGA側に作ったRAMの動作が 追いつかないことがあるようです。 書き込み後、EZUSB_Delay(1);等で少しまったあと、 読み出しをするほうがよいようです。


戻る