読者です 読者をやめる 読者になる 読者になる

ハードウェアエンジニアの備忘録

電子工学(半導体物性)→応用光学・半導体プロセス→アナログ回路→C/C++→C#/.NETと低レイヤーから順調に(?)キャリアを登ってきているハードウェアエンジニアの備忘録。ブログ開始時点でiOSやサーバーサイドはほぼ素人です。IoTがマイブーム。

FPGA入門 備忘録② ~基本回路、組み合わせ回路編~

備忘録①からの続きになる。なお本では前回と今回の間にXilinxのISEのインストール方法、回路図エディタの使用方法などが記されている。

論理素子

まずは基本的なところから、VHDLverilogの記法の違い。

名称 機能 VHDL Verilog HDL
NOTゲート(インバータ) 論理反転 not ~
ANDゲート 論理積 and &
ORゲート 論理和 or |
XORゲート 排他的論理和 xor ^
NANDゲート 否定論理積 nand なし
NORゲート 否定論理和 nor なし
XNORゲート 定排他的論理和 xnor ~^

自分の目的はVerilog HDLを使いこなせるようになることなので、VHDLについては以後触れない。 さて、本の中では以下の様な回路を回路図エディタ、VHDLVerilog HDLの3種類で作成することを試みている。

f:id:tosh419:20160708234257p:plain

module INV_VERILOG(PSW0, LED0);
  input PSW0;
  output LED0;

  assign LED0 = ~PSW0;

endmodule

assign LED0 = ~PSW0;という記述の~がNOTゲートを表している。LED0が出力で、PSW0が入力で、PSW0をインバートしたものがLED0に接続されているということだ。

ANDゲート

VerilogではANDゲートを

assign Q = A & B;

と表し、VHDLでは、

Q <= A and B;

と表す。 これを踏まえて、SW1とSW2の両者が入力されると、LED0が点灯するようなverilog のコードを書いてみよう。

module AND_VERILOG(SW0, SW1, LED0);
  input SW0;
  input SW1;
  output LED0;

  assign LED0 = SW0 & SW1;
endmodule

ORゲート

VerilogではORゲートを

assign Q = A | B;

と表し、VHDLでは、

Q <= A or B;

と表す。

XORゲート

VerilogではXORゲートを

assign Q = A ^ B;

と表し、VHDLでは、

Q <= A xor B;

と表す。

NANDゲート

VerilogではNANDゲートを

assign Q = ~(A & B);

と表し、VHDLでは

Q <= A band B

と表す。verilogではAとBのANDを括弧でくくりそれをNOTすることでNANDを表現している。

NORゲート

VerilogではNORゲートを

assign Q = ~(A | B);

と表し、VHDLでは

Q <= A nor B;

と表す。verilogではAとBのORを括弧でくくりそれをNOTすることでNORを表現している。

XNORゲート

VerilogではXNORゲートを

assign Q = A ~^ B;

と表し、VHDLでは

Q <= A xnor B;

と表す。

組み合わせ回路

上でも示したように、NANDゲートはANDゲートとNOTゲートを接続して作成できる。つまり、ANDゲートとNOTゲートによる組み合わせ回路といえる。NORゲートも同様にORゲートとNOTゲートの組み合わせで作成できる。では、ANDやOR、NOTゲートはどうだろう。実はこれもNANDゲートで作ることが可能だ。以下の図はそれぞれNANDゲートのみで構成したNOTゲート、 ANDゲート、ORゲートだ。

f:id:tosh419:20160709202201p:plain

また、XORはAND、OR、NOTを組み合わせれば構成できるので、NANDで置き換えることが可能だ。

f:id:tosh419:20160709204351p:plain

HDLの3つの記述方法

HDLにはいくつかの記述方法があり、回路によって記述しやすいものを使用する。記述方法は大きく3つあり、

  • 論理演算子を組み合わせて記述する方法
  • テーブルを用いる方法
  • プリミティブを直接指定する方法

にわけられる。それぞれXOR回路を例に順に見ていく。

論理演算子を組み合わせて記述する方法

module XOR1_VERILOG(A, B, C);
  input A;
  input B;
  output C;

  assign C = ~(~A & ~(B & B)) & (~(~A & A) & B)));

endmodule

上の回路図と合わせて見ると理解できると思う。

テーブルを用いる方法

テーブルとは真理値表のことで、より複雑な論理を記述するのに向いている。テーブルの記述にはいくつかの方法があり、VHDLではwith select、case文を用いて、Verilogではassign、case文を用いて書く方法がある。 まずはassignを用いて書いた場合を見てみよう。

module XOR2_VERILOG(A, B, C);
  input A;
  input B;
  output C;

   wire [1:0] Q;

  assign Q = {A, B};
  assign C = (Q == 2'b00) ? 1'b0 :
  assign C = (Q == 2'b01) ? 1'b1 :
  assign C = (Q == 2'b10) ? 1'b1 :
  assign C = (Q == 2'b11) ? 1'b0 : 1'b0;

endmodule

まずwire宣言から。これは2bit幅のバスラインQを宣言している。 assign Q = {A, B}でQの上位ビットをAに、下位ビットをBに接続している。 次に、assign C = (Q==2b'00) ? 1'b0 :で、Qの値が00であれば、Cに0を出力する事を指定している。最後の1'b0はCのデフォルト値で、Qの値が未指定の場合の出力値を0としている。

次に、case文で記述したものを見てみる。

module XOR3_VERILOG(A, B, C);
  input A;
  input B;
  output C;
  
  reg C;
  
  wire [1:0] Q;

  assign Q = {A, B};

  always @(Q) begin
    case (Q)
      2'b00:  C = 1'b0;
      2'b01:  C = 1'b1;
      2'b10:  C = 1'b0;
      2'b11:  C = 1'b0;
      default:  C = 1'b0;
    endcase
  end

endmodule

Cはalways文の中で使用するので、reg C;と宣言する必要がある。always文については長くなるので後回し。

プリミティブを直接指定する方法

各デバイス特有の回路を、直接指定する際に使用する。したがって、指定できるプリミティブはデバイスによって異なる。FPGAごとに使えるライブラリのガイド一覧はXIlinxのHPで公開されている。 verilogでXOR2プリミティブを直接呼ぶ例を見てみよう。

module XOR4_VERILOG_TOP(A, B, C);
  input A;
  input B;
  output C;

  XOR2 instance1(.O(C), .IO(A), .I1(B));

endmodule

instance1という名前のXOR2に各信号を接続している。カッコ内の.O(C)という記述はXOR2の出力ピンOと外部出力ピンCが接続されることを意味する。ここの信号名OやIOなどはライブラリガイドに則った名称である必要がある。

マルチプレクサ

実用的な組み合わせ回路の一例としてマルチプレクサを取り上げる。マルチプレクサの真理値表は、

D0 D1 S0 O
0 0 0 0
0 1 0 0
1 0 0 1
1 1 0 1
0 0 1 0
0 1 1 1
1 0 1 0
1 1 1 1

f:id:tosh419:20160711213455p:plain

右図で示すようにマルチプレクサはS0が0の時はO=D0となり、1の時はO=D1となる。つまり、Oに出力するデータを、S0によって選択する回路となっている。

1bit2入力マルチプレクサの内部回路を見てみよう。 f:id:tosh419:20160711220022p:plain

S0=0の時は、D0側のANDゲートがアサート(EN)され、D1側はネゲート(Disable)される。逆に、S0=1の時はD0側のANDゲートがネゲートされ、D1側のANDゲートがアサートされる。

これは、S=1の時にスイッチが押されて、DOにDINの値が出力され、S=0の時はスイッチが切り離され、DOが0となると考えられる。verilogでは以下のように書ける。

module M2_1_VERILOG(D0, D1, S0, 0);
  
  input D0;
  input D1;
  input S0;
  output O;

  wire d0_sel;
  wire d1_sel;

  assign d0_sel = D0 & ~S0;
  assign d1_sel = D1 & S0;
  assign O = d0_sel | d1_sel;

endmodule

デコーダ(デマルチプレクサ)

マルチプレクサの対になるのが、デマルチプレクサである。デマルチプレクサは一般にデコーダとして使われることが多く、ここでは2進数のデータを10進数に戻すバイナリデシマルデコーダを指している。 2bitバイナリデシマルデコーダの真理値表を以下に示す。

A1 A0 E D3 D2 D1 D0
- - 0 0 0 0 0
0 0 1 0 0 0 1
0 1 1 0 0 1 0
1 0 1 0 1 0 0
1 1 1 1 0 0 0

E=1の時、Aに入力されるバイナリコードに応じて、出力Dのいずれかが1となる。出力Dは最下位ビットから順にD0=1、D1=2、D2=3、D3=4という形で、10進数を表している。 ここで、E=0の時は入力Aに関わらず、出力D0〜D3はすべて0になる。したがって、Aで選択した出力先のDの値はEの入力値に等しくなる。Eをデータ入力、Aを制御入力と考えると、Eの出力先を切り替える回路として利用できる。これはマルチプレクサと逆の動きなので、デマルチプレクサと呼ぶ。

Eをデータ入力として、Aを制御入力として使うものがデマルチプレクサで、Aをデータ入力とし、Eは制御入力(EN端子)として使うものがデコーダとなる。

2bitバイナリデシマルデコーダの内部回路は以下のようになっている。

f:id:tosh419:20160711224309p:plain

Eをデータ入力とした時、A0及びA1で4つのANDゲートのいずれかをアサートする選択回路となっていることが分かる。verilogのコードも示す。

module D2_4E_VERILOG(A0, A1, E, D0, D1, D2, D3);
  
  input A0;
  input A1;
  input E;
  output D0;
  output D1;
  output D2;
  output D3;

  assign D0 = E & ~A0 & ~A1;
  assign D1 = E & A0 & ~A1;
  assign D2 = E & ~A0 & A1;
  assign D3 = E & A0 & A1;

endmodule

とりあえずは、ここで今回は終わり。備忘録③は加算回路から。