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

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

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

FPGA入門 備忘録④ 〜順序回路編〜

備忘録③の続きになる。

RSフリップフロップ

f:id:tosh419:20160724205656p:plain

上図はORゲートを用いた状態記憶回路である。ORゲートの出力Qが一方の入力に帰還しているため入力Sが一度1になると、出力Qはその後、入力Sの状態にかかわらず1を保持し続ける。

f:id:tosh419:20160724210239p:plain

上図は、最初の回路にリセット入力Rを追加したもので、これにより出力Qを0に戻すことが可能になる。 以下にRSフリップフロップの真理値表を示す。入力SとRが共に0の時は共に0となる直前の出力Qが保持される。ただし、直前の入力が共に0の時は、いずれの値が保持されるかは分からない。 Rが0でSが1の時、Q=1、Rが1でSが0の時はQ=0となる。R=1、S=1の時、Q=1となるが、この状態からR=0、S=0となった場合に、Q=1の保持が確実でないのは各ゲートの遅延などでR=1、S=0となってからR=0、S=0となる可能性があるためだ。そのため、R=1、S=1は一般に使わない。

入力 出力
R S Q
0 0 前の状態
0 1 1
1 0 0
1 1 1(使用禁止)

f:id:tosh419:20160724212938p:plain

上に簡略化の流れを示す。まずひとつ目の図はORゲートをNORゲートに変更して、ふたつ目の図のように書ける。次に、ふたつ目の図のANDゲートはNOTゲートと組み合わせて、3つめの図のようにNORゲートに置き換え可能だ。ここで、各NORゲートの出力をそれぞれQ、Q_とすると、4つ目の図のようになる。

4つ目の図の真理値表を書くと、

入力 出力
R S Q Q'|Q_
0 0 前の状態 前の状態|前の状態
0 1 1 1|0
1 0 0 0|1
1 1 1 0|0

ここで、前述のようにRとSが共に1の時に使わないのであれば、Q=Q'として更に、5つ目の図のように簡略化できる。

入力 出力
R S Q Q_
0 1 1 0
1 0 0 1
1 1 0(使用禁止) 0(使用禁止)

最後に、5つ目の図は6つ目のよく見る図のように簡略化できる。 簡略化しNORゲートのみを使って表すことで、回路規模が小さくできる。XilinxFPGAではプリミティブとして、後述のDフリップフロップと同期セット・リセット入力付きDフリップフロップなどが用意されている。 ただ、これらのプリミティブを用いずに基本ゲート(Look Up Table)のみを用いて、フリップフロップを構成することができる。

RSフリップフロップverilogによる記述を示す。

module RSFF_VERILOG(SW0, SW1, LED0, LED1);

  input SW0;
  input SW1;
  output LED0;
  output LED1;

  assign LED0 = ~(SW0 | LED1);
  assign LED1 = ~(SW1 | LED0);

endmodule

Dフリップフロップ

RSフリップフロップはSにセットした値、もしくはRでリセットした値を時間経過に関係なく保持することができる1bitのメモリだ。しかし、その値を保持するためにはS=0、R=0という状態を保持する必要がある。これではまだ使い勝手が悪い。つまり実際の回路ではある瞬間のみに値の書き込みを行い、それ以降はSおよびRの状態と無関係に値を保持する動作を求められる。そのような動作を実現するために書き込む値を入力する端子と書き込み制御を行う端子を分離したフリップフロップの一つがDフリップフロップだ。このDフリップフロップと後で説明するクロックイネーブル入力付きDフリップフロップ、及び基本ゲートがあればほとんどの完全同期式回路を記述できる。

以下にDフリップフロップの回路シンボルと真理値表を示す。 f:id:tosh419:20160724215602p:plain

入力 出力
D CLK Q
0 0
1 1
0 - 直前の値
1 - 直前の値

DフリップフロップはDの入力状態をCLKから入力される信号の立ち上がりエッジで取り込み、Qに出力する。Qの出力は次のCLKの立ち上がりエッジが来るまで、変化しない。特に断りがなければ、立ち上がりエッジで取り込むポジティブエッジタイプを使う。立ち下がりで取り込むネガティブエッジタイプのシンボルを参考までに以下に示す。

f:id:tosh419:20160724220601p:plain

RSフリップフロップはRとSを用いて、書き込むデータを表すとともに書き込むか、保持するかの動作の制御も行っていた。これに対し、Dフリップフロップではデータを入力するDと書き込み制御を行うCLKという風にデータ入力と制御入力を分離している。このデータと制御の分離は非常に重要で、大きな回路規模の設計を行うときには、データパスの設計なのか、制御パスの設計なのかを常に意識する必要がある。

Dフリップフロップverilog記述を以下に示す。

module DFF_VERILOG(SW0, SW1, LED0);

  input SW0;
  input SW1;
  output LED0;

  reg LED0;

  always @(posedge SW1) begin
    LED0 <= SW0;
  end

endmodule

always文のところで、DFFの制御を書いている。SW1の立ち上がりエッジで、SW0の値が取り込まれ、LED0に出力される。

セットアップ時間とホールド時間

DフリップフロップはCLKに入力される信号の立ち上がりエッジでデータを取り込む。したがって、データ入力はCLKの立ち上がりエッジ以前に確定している必要がある。このCLKの立ち上がりエッジからデータが確定していなければならない時間がセットアップ時間tsである。 また、内部のRS-FFが安定し、入力データがDフリップフロップに取り込まれるまでには時間を要する。したがって、入力データはCLKの立ち上がりエッジから特定の時間以上保持する必要があり、この時間がホールド時間thである。よって、ts+thの期間はデータ入力は0または1のいずれかに保持しなければならない。

f:id:tosh419:20160724225436p:plain

f:id:tosh419:20160724231904p:plain

トグルフリップフロップ

トグルフリップフロップの回路図を示す。Dフリップフロップの出力Qが自己のデータ自己DにNOTゲート経由で帰還されている。

f:id:tosh419:20160725203652p:plain

f:id:tosh419:20160725204627p:plain

Dフリップフロップの初期状態Q=0とすれば、Qは1となる。最初のクロックの立ち上がりで、Q=1が取り込まれ、直後にQ=1、Q=0となる。次のクロックの立ち上がりで、Q=0が取り込まれるので、直後にQ=0、Q=1となる。出力Qから出力される信号は、入力されるクロック周波数の半分の周波数となるため、この回路を2分周回路という。

上の回路は常にクロック信号の1/2の周波数の信号を生成するが、これを以下のように変更すると、制御信号T=1の間だけ1/2の周波数の信号を出力し、T=0のときは前の値を保持することができる。 f:id:tosh419:20160725205625p:plain

この回路をシンボルで書くと以下のようになる。 f:id:tosh419:20160725205806p:plain

verilogでトグルフリップフロップの記述をしてみよう。

module TFF_VERILOG(PSW0, LED0);

  input PSW0;
  output LED0;

  reg LED0;

  always @ (posed PSW0) begin 
    LED0 <= ~LED0;
  end

endmodule

クロックイネーブル

前述のDフリップフロップでは次のクロックの立ち上がりまでの期間、データの記憶が可能だが、もっと長い期間データを記憶しておくことが必要だ。そこで、次のクロックが来ても、データを更新しないような制御入力端子を追加したものが、クロックイネーブルだ。

f:id:tosh419:20160725210724p:plain

クロックイネーブル付きDフリップフロップではCE入力が0のときにはいくらエッジが来ても出力Qは変化しない。CEが1の時のクロックの立ち上がりエッジでD入力からのデータを取り込み、出力Qへ反映する。

内部回路を以下に示す。クロック入力を抑制するにはCLKにANDゲートを挿入する方法もあるが、完全同期式回路ではフリップフロップのCLKにゲートを挿入することは禁止される。

代わりに、以下の図のようにフリップフロップの入力Dの前に幾つかのゲートを挿入する。

f:id:tosh419:20160725214224p:plain

外部入力CEにより、現在のQの出力値を保持するか、外部入力Dからの値を取り込むか選択するようになっている。マルチプレクサはS0=0のとき、Q=D0、S0=1の時はQ=D1となる。 以下にverilogのコードを示す。

module DFFE_VERILOG(SW0, SW1, SW2, LED0);
 
  input SW0;
  input SW1;
  input SW2;
  output LED0;
  
  reg LED0;

  always @(posedge SW0) begin
    if (SW1 == 1'b1) begin
      LED0 <= SW2;
    end
  end

endmodule

セットリセット

フリップフロップに保持されている値を強制的に1あるいは0にするための制御入力がそれぞれセット、リセットである。Dフリップフロップを例にセット入力端子Sとリセット入力端子Rを追加した回路シンボルと内部回路を以下に示す。

f:id:tosh419:20160725220003p:plain

verilogのコードを以下に示す。

module DFFERS_VERILOG(SW0, SW1, SW2, SW3, SW4, LED0);

  input SW0;
  input SW1;
  input SW2;
  input SW3;
  input SW4;
  output LED0;

  reg LED0;

  always @(posedge SW0) begin
    if (SW == 1'b1) begin
      LED0 <= 1'b0;
    end
    else if (SW2 == 1'b1) begin
      LED0 <= 1'b1;
    end
    else if (SW3 == 1'b1) begin
      LED0 <= SW4;
    end
  end

endmodule

クロックイネーブル/セットリセット付きトグルフリップフロップ

クロックイネーブルやセットリセットの機能についてはDフリップフロップに限られず、他のフリップフロップにも追加できる。また、クロックイネーブル機能とセットリセット機能は同時に追加できる。以下はクロックイネーブル/セットリセット付きトグルフリップフロップの回路シンボルと内部回路である。

f:id:tosh419:20160730203430p:plain

f:id:tosh419:20160730204459p:plain

verilogのコードを示す。

module TFFERS_VERILOG(SW0, SW1, SW2, SW3, LED0);

  input SW0;
  input SW1;
  input SW2;
  input SW3;
  output LED0;

  reg LED0;

  always @(posed SW0) begin
    if (SW1 == 1'b1) begin
      LED0 <= 1'b0;
    end
    else if (SW2 == 1'b1) begin
      LED0 <= 1'b1;
    else if (SW3 == 1'b1) begin
      LED0 <= ~LED0;
    end
  end

endmodule

制御端子の優先順位はリセットが最優先で、次にセット、クロックイネーブルと続き、トグル入力は最も優先順位が低くなっている。

完全同期式回路

複数のフリップフロップを用いる場合、気をつけなければならないのが、クロックスキューだ。以下の回路のようにすべての順序回路の状態変化が同時に起こる回路を完全同期式回路という。こうすることで、すべての回路の0,1の変化がクロックに同期して起こるため、1クロックを単位とした時間の概念を回路設計に取り入れることができる。

f:id:tosh419:20160730210235p:plain

2つのDフリップフロップの例

以下に2つのDフリップフロップを直列接続した回路を示す。

D1に入力される信号は1クロック遅れてD2に入力され、さらに1クロック遅れた信号がQ2から出力される。 D1に入力されるパルスは2番めのパルスの立ち上がりで最初のDフリップフロップに取り込まれる。FD1の出力であるD2は直後に1を立ち上がる。この時後段のフリップフロップは2番めのクロックの立ち上がりで、D2の状態を取り込むが、このとき D2はまだ0であるため、FD2には0が取り込まれ、出力Q2は0のままだ。 FD2に1が取り込まれるのは3番めのクロックの立ち上がりで、3番めのクロックが立ち上がるとき、D1は0となっているので、FD1には0が取り込まれ、直後にD2は0に立ち下がる。しかし、同時にFD2がD2の状態1を取り込むので、直後に出力Q2が1となる。

ここで、注意すべきはD2の信号で、特性が同一の2つのDフリップフロップに全く同じタイミングで立ち上がるクロック信号が入力されているため、状態が変わる直前の状態が取り込まれている。

しかし、この動作が保証されるためには2つの条件があり、1つはセットアップ時間を満たしているかということ。前段のDフリップフロップの出力は必ずクロックの立ち上がりで、遷移するので、後段のDフリップフロップに入力される信号は1つ前のクロックの立ち上がりから、遷移するまでの遅れ時間TCKO経過してから確定している。

もう一つはホールド時間を満たしているかということで、前段のDフリップフロップの出力は、クロックの立ち上がりから、状態遷移するまでに遅れ時間がある。この遅れ時間が、後段のDフリップフロップの入力のホールド時間より長くなければならない。

FPGAにはクロック供給用にスキューが小さくなるように設計されたグローバルラインと呼ばれる専用配線がある。XilinxFPGAではBUFGというプリミティブで使用できる。また、外部のオシレータ等をこのグローバルラインに接続するには、専用のGCLKというピンと、IBUFGというプリミティブを使用する必要がある。

カウンタ

2bitバイナリカウンタの内部回路を以下に示す。最初のトグルフリップフロップは2分周回路となり、後段のトグルフリップフロップはQ0=1の時にクロックの立ち上がりで値を反転させるので、出力Q1の信号周波数はQ0の更に1/2、つまり入力クロックの1/4の周波数となる。結果、入力をCLK、出力をQ1とした時、本回路は4分周回路となる。

f:id:tosh419:20160730213825p:plain

更にトグルフリップフロップを追加して、以下の様な回路を作ると、出力Q2はクロック信号の1/8、出力Q3は1/16の周波数となる。 ここで、出力Q0をLSB、Q3をMSBとして、Q0〜Q3を4bitのデータとして捉えると、0000b〜1111bまでクロックの立ち上がりが来るごとにインクリメントしていることがわかり、このように出力が2進数となっているカウンタをバイナリカウンタという。

f:id:tosh419:20160730220846p:plain

最後に、2bitバイナリカウンタ、4bitバイナリカウンタのverilogコードを示す。

module CB2_VERILOG(CLK, Q);
  
  input CLK;
  output [1:0] Q;

  reg [1:0] Q;

  always @(posedge CLK) begin
    Q <= Q + 1'b1;
  end

endmodule
module CB4_VERILOG(CLK, R, CE, Q, TC, CEO);

  input CLK;
  input R;
  input CE;
  output [3:0] Q;
  output TC;
  output CEO;

  reg [3:0] Q;

  always @(posedge CLK) begin
    if (R == 1'b1) begin
      Q <= 4'd0;
    end
    else if (CE == 1'b1) begin
      Q <= Q + 1'b1;
    end
  end

  assign TC = &Q;
  assign CEO = &Q & CE;

endmodule