アドレスの値 | アクセスされるデバイス |
0x2000 - 0x3fff | SRAM |
0x4000 - 0x4003 | 7セグメントLED |
0x4004 | スイッチ(8個) |
0x4005 | LED(8個) |
この2つの74ACH138を用いて、あわせてアドレスデコードを 実現していることに注意しましょう。 例えばSRAMがアクセス対象となることを示す信号である CSRAMnが0(負論理)となるのは、 A15=0, A14=0, A13=1のとき、ですから、 アドレスバスがそのようになる値の範囲は、 「0x2000〜0x3fff」ということになります。 A12〜A0は、そのままSRAMにつながっています。 すなわち、アドレスバスの上位3本(A15〜A13)によってSRAMが選択され、 そのSRAMの中でアクセスされる場所は、A12〜A0によって指定をされることに なります。
※ヒント: 2進数で考えるとわかりやすいでしょう。 つまり、アドレスデコーダにつながっていないアドレスバスの値は、 CPUが区別しても、アドレスデコーダには無関係です。 すなわち、その値が0でも1でも、アドレスデコーダの出力である CSSWnなどの値には無関係ということになります。
これらの信号線を使って、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に固定されてしまうため)。
またこの出力デバイスを、外部I/O空間内の別のアドレスに マッピングしてみましょう。 すなわち、別のアドレス、たとえばCPU側から0x60e0に書き込みをすると LEDの点灯パターンを制御できるように、 CPUのプログラム、FPGAの回路の双方を変更してみましょう。
そこで入力デバイスは、アクセスされていない状態では、 データバスから切り離すようにします。 この「切り離し」は、実際には高インピーダンス状態として 実現します。 これは、入力デバイスのデータバス側の出力と、データバスが、 非常に高いインピーダンス(抵抗)で接続された状態のことで、 事実上、電気的に切り離された状態になります。 この高インピーダンス状態は、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)のところには、具体的なピンや論理式を 記述する必要があります。
なおこのとき、さきほどの出力デバイス(WRnに応じて動作する回路)と、 この入力デバイスを同一回路中に記述すると、データバスが、 書き込み時と読み出し時で入出力を切り替える必要があります。 そのため、出力デバイスを記述せずにデータバスを(FPGA側にとって)出力専用に するか、あるいは後述のように、データバスを、入力と出力を切り替えられる ようにinout型で定義をするようにします。
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バイトRAMの0番地〜3番地(CPUから見たアドレスは 0x6000〜0x6003)のいずれかに、CPUボードのSWの値を書き込み、 またそこを読み出しでLEDで表示する、あるいは、 RAMに適当な値を書き込むプログラムと、 それらの値を読み込んでCPUボード上のLEDや7セグメントLEDに 表示するプログラムを順に実行し、正しくRAMへの読み書きが 実行できていることを確認すればよいでしょう。
※このとき、書き込みしてすぐに読み出しをすると、FPGA側に作ったRAMの動作が 追いつかないことがあるようです。 書き込み後、EZUSB_Delay(1);等で少しまったあと、 読み出しをするほうがよいようです。