組合せ回路は、今の入力だけで出力が決まります。 AND、OR、NOT、NAND、NOR、MUX、加算器などです。
しかしコンピュータには「過去」が必要です。 レジスタ、カウンタ、メモリ、状態機械、CPU のプログラムカウンタ、全部が過去の値を持っています。
その最小単位がフリップフロップです。 1 bit を覚える回路です。 たった 1 bit ですが、こいつを大量に並べるとレジスタになり、メモリになり、コンピュータになります。
ラッチ
最初に見るのは SR ラッチです。 NAND ゲート 2 個をたすき掛けにすると作れます。
+-------+
S ----| NAND |---- Q
+-------+ |
^ |
| v
+-------+ /Q
R ----| NAND |----+
+-------+
出力を入力に戻しています。 このフィードバックによって、回路が状態を持ちます。
SR ラッチには Set と Reset の 2 入力があります。
Set すると Q = 1、Reset すると Q = 0 になります。
どちらも入っていない間は、前の状態を保持します。
| S | R | Q(next) | 意味 |
|---|---|---|---|
| 1 | 0 | 1 | Set |
| 0 | 1 | 0 | Reset |
| 0 | 0 | Q | Hold |
| 1 | 1 | 禁止 | 不定 |
ここでは正論理っぽく書いています。 NAND で作るか NOR で作るかで入力の極性が変わるので、実際の回路図ではデータシートを見ます。 ラッチ、地味にややこしい。
D ラッチ
SR ラッチは S = 1, R = 1 の禁止入力があります。
そこで、入力を 1 本にしたものが D ラッチです。
D ----+---- S
|
NOT
|
+---- R
実際には Enable 入力を付けます。 Enable が 1 の間は入力 D が出力 Q に通り、Enable が 0 になると最後の値を保持します。
| Enable | D | Q(next) |
|---|---|---|
| 0 | x | Q |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
D ラッチは「透明」な記憶素子です。 Enable が開いている間、入力の変化が出力にそのまま出ます。 これは便利ですが、同期回路では扱いに注意が必要です。
D フリップフロップ
D フリップフロップ (D-FF) は、クロックのエッジでだけ D を取り込みます。
always @(posedge clk) begin
q <= d;
end
Verilog で書くとこれです。 これが FPGA や CPU のレジスタの基本です。
D ラッチは Enable の期間中ずっと入力が通ります。 D-FF はクロックの瞬間だけ入力を取り込みます。
| 種類 | 取り込み条件 | 感覚 |
|---|---|---|
| D ラッチ | Enable が有効な間 | 窓が開いている間ずっと通る |
| D-FF | クロックエッジ | シャッターを切った瞬間だけ |
同期回路では、基本的に D-FF を使います。 クロックの瞬間だけ状態が変わるので、設計を「各クロックで状態がどう更新されるか」として考えられます。
マスター・スレーブ構成
D-FF は、2 つの D ラッチを逆相の Enable でつなぐと作れます。
D -> [Master Latch] -> [Slave Latch] -> Q
clk=0 open clk=1 open
クロックが Low の間、マスターラッチが入力を追従します。 クロックが High になるとマスターが閉じ、スレーブが開いて、その値が出力に出ます。
こうすると、入力が出力へ一気にすり抜けることを防げます。 クロックのエッジでだけ値が更新されたように見えるわけです。
実際の IC 内部はもっと工夫されていますが、考え方としてはこれで十分です。
T フリップフロップ
T フリップフロップは、入力 T が 1 のときに出力を反転します。
| T | Q(next) |
|---|---|
| 0 | Q |
| 1 | ~Q |
D-FF を使うなら、次のように作れます。
D = Q xor T
T を常に 1 にすると、クロックごとに出力が反転します。 つまり 2 分周器になります。
clk: _-_-_-_-_-_
q: __--__--__-
カウンタの基本です。 リレーやロジック IC でカウンタを組むと、ここでちょっと感動できます。
JK フリップフロップ
JK フリップフロップは、SR ラッチの禁止入力を「反転」に割り当てたものです。
| J | K | Q(next) |
|---|---|---|
| 0 | 0 | Q |
| 1 | 0 | 1 |
| 0 | 1 | 0 |
| 1 | 1 | ~Q |
昔のロジック回路ではよく出てきます。 ただ、FPGA や現代の同期設計では D-FF を基本に考えることが多いです。
JK-FF は便利ですが、状態更新の式が少し読みにくくなります。 いまから設計するなら、D-FF に寄せたほうが見通しがいいです。
セットアップ時間とホールド時間
D-FF はクロックエッジの瞬間に D を取り込む、と言いました。 しかし実際には、クロックの少し前から少し後まで、D が安定している必要があります。
| 用語 | 意味 |
|---|---|
| セットアップ時間 | クロック前に D が安定しているべき時間 |
| ホールド時間 | クロック後に D が安定しているべき時間 |
この条件を破ると、フリップフロップの出力が 0 か 1 にすぐ決まらないことがあります。 これをメタステーブルと呼びます。
デジタル回路なのに 0 でも 1 でもない時間がある。 つらい現実です。
同期回路のタイミング制約は、だいたいこのセットアップ時間とホールド時間を守るためにあります。 FPGA の Place and Route が頑張っているのも、かなりの部分はこれです。
非同期入力とメタステーブル
スイッチ、UART の RX、外部割り込み、別クロックの信号は、こちらのクロックと同期していません。 つまり、D-FF のクロックエッジ付近で入力が変わる可能性があります。
このような非同期入力を 1 段の D-FF に直接入れると、メタステーブルが後段に伝わることがあります。
よく使う対策は、D-FF を 2 段にすることです。
async_in -> [D-FF] -> [D-FF] -> sync_in
clk clk
1 段目がメタステーブルになっても、2 段目に入るまでの 1 クロックで落ち着くことを期待します。 完全にゼロにはできませんが、現実的な確率まで下げられます。
クロックドメインクロッシング (CDC: Clock Domain Crossing) は深い沼ですが、まずは「非同期入力は 2 段同期」と覚えておくとだいぶ助かります。
チャタリング除去
機械式スイッチは、押した瞬間に 0 から 1 へ一発で切り替わるわけではありません。 接点がバウンドして、短時間に ON/OFF を繰り返します。 これをチャタリングと呼びます。
理想: ____------____
現実: ____-_-__--____
これをそのままカウンタのクロックに入れると、1 回押しただけで何回もカウントされます。 悲しい。
簡単な対策は、周期的にサンプリングして、一定時間同じ値が続いたら確定することです。
always @(posedge clk) begin
if (button == sampled) begin
count <= count + 1;
end else begin
count <= 0;
sampled <= button;
end
if (count == 16'd50000) begin
stable <= sampled;
end
end
これは雰囲気コードです。 実際にはカウンタ幅、クロック周波数、押し心地に応じて調整します。
RC フィルタとシュミットトリガでアナログ的に除去する方法もあります。 マイコンならソフトウェアで処理することも多いです。
リセット
フリップフロップにはリセットを付けることがあります。 電源投入時に状態を決めるためです。
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
q <= 1'b0;
end else begin
q <= d;
end
end
これは非同期リセットです。 クロックを待たずにリセットできます。
一方、同期リセットではクロックエッジでリセットします。
always @(posedge clk) begin
if (reset) begin
q <= 1'b0;
end else begin
q <= d;
end
end
どちらが常に正しいという話ではありません。 FPGA、ASIC、外部 IC、電源シーケンスで好みが変わります。 ただし、リセット解除のタイミングが中途半端だとメタステーブルを起こすことがあるので、リセット解除は同期化するのが定番です。
まとめ
フリップフロップは 1 bit の記憶です。 コンピュータの状態は、最終的にはこの 1 bit の集まりです。
- ラッチは Enable 中に入力を通す
- D-FF はクロックエッジで入力を取り込む
- T-FF は分周器やカウンタに使える
- セットアップ時間とホールド時間を守る必要がある
- 非同期入力はメタステーブル対策が必要
- スイッチ入力にはチャタリング除去が必要
組合せ回路にフリップフロップを足すと、状態機械になります。 状態機械を大きくしていくと CPU になります。 1 bit、あまりにも偉い。