ラッチを生む3つの原因と対策
(1)ここで学ぶ内容
always文による組み合わせ回路記述で問題となるラッチ生成について、そのメカニズムと回避策を学ぶ
目標 — 以下の項目を理解し説明できる
- ラッチを生成する記述
- ラッチを生成しない記述
- ラッチ動作をする記述
- ラッチ動作をしない記述
修了判定
4つの記述からラッチを生成するものを見分ける
組み合わせ回路を記述しているつもりでも、論理合成をするとラッチが生成される場合があります。それはalways文の記述の仕方が悪いせいです。
(2)組み合わせ回路記述の鉄則
-
条件分岐がある場合には、すべての条件を記述する
違反すると → 論理合成でラッチが生成される -
複数出力がある場合、すべての条件ですべての出力を記述する
違反すると → 論理合成でラッチが生成される -
イベント式にはすべての入力信号を記述する
違反すると → シミュレーションではラッチ動作をする / 論理合成ではラッチは生成されない
入力の変化が出力に伝わらず、前の値を保持することです。これらの鉄則に違反しても文法エラーにはなりませんが、シミュレーションと論理合成の動作不一致などを引き起こします。デバッグ中に記述を書き換えるときなどに間違えやすいので、記述のチェックと論理合成後の回路チェックを入念に行いましょう。
もふねこ:
ラッチ(Latch)が意図せずできちゃうのは、ハードウェア設計の「あるあるバグ」のトップクラスだよ🐾!
「もし〜なら」の条件を書いたら、必ず「それ以外(else / default)」も書くクセをつけようね。全部の条件を埋めるのが「組み合わせ回路」の鉄則だよ!
(3)すべての条件を記述しなかった場合
アンドゲートの記述を例に考えてみましょう。真理値表に基づきHDL記述を作成します。
| A | B | Z |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
module AND_GATE(A,B,Z);
input A,B;
output Z;
reg Z;
always @(A or B) begin
if (A==1'b0 && B==1'b0)
Z <= 1'b0;
else if (A==1'b0 && B==1'b1)
Z <= 1'b0;
else if (A==1'b1 && B==1'b1)
Z <= 1'b1;
// A==1'b1 && B==1'b0 の記述がない!
end
endmodule
❌ シミュレーション結果(ラッチ動作)
同じ入力なのに出力が違う!
A ─┐ ┌──┐ ┌──
B ──┐ ┌─┘ └──┘
Z ──┘ ↑ラッチ動作(前の値を保持)
点線部分が誤動作
A=1, B=0 のときに記述がないため、シミュレーションでZが前の値を保持するラッチ動作をしてしまう
❌ 論理合成結果(ラッチが生成される)
B ─[INV]─o S ┐
├─ Z
A ──────o R ┘
↑ラッチ(SR型)が生成される
論理合成ツールは条件の不足を補うためにラッチ(SR型フリップフロップ相当)を自動生成してしまう
✅ すべての条件を記述した正しい記述
module AND_GATE(A,B,Z);
input A,B;
output Z;
reg Z;
always @(A or B) begin
if (A==1'b0 && B==1'b0)
Z <= 1'b0;
else if (A==1'b0 && B==1'b1)
Z <= 1'b0;
else if (A==1'b1 && B==1'b1)
Z <= 1'b1;
else
Z <= 1'b0; // ← A=1, B=0 の条件を追加
end
endmodule
✅ 論理合成結果(ラッチは生成されない)
A ──┐
├─[AND]─ Z
B ──┘
↑ シンプルなANDゲートのみ生成
すべての条件が記述されているため、論理合成ツールは純粋なANDゲートを生成する。波形も真理値表通りの動作になる。
条件に抜けのある記述はラッチ生成の原因になります。複雑な条件分岐があると間違いやすいので、十分に注意して記述してください。
(4)すべての出力を記述しなかった場合
この例では一つのalways文の中に DATA1、DATA2、DATA3 という三つの出力が記述されています。
❌ 出力の記述に抜けがある(ラッチが生成される)
always @(A) begin
case (A)
3'b000 : begin DATA1 <= 1'b1; DATA2 <= 1'b0; end
// ↑ DATA3 の記述がない!(A=000のとき)
3'b001 : begin DATA1 <= 1'b0; DATA3 <= 1'b1; end
// ↑ DATA2 の記述がない!(A=001のとき)
3'b010 : begin DATA1 <= 1'b0; DATA2 <= 1'b1; end
3'b101 : begin DATA1 <= 1'b1; end
// ↑ DATA2, DATA3 の記述がない!
default: begin DATA1 <= 1'b0; DATA2 <= 1'b0; DATA3 <= 1'b0; end
endcase
end
- A=000 → DATA3 の記述なし → DATA3 にラッチ生成
- A=001 → DATA2 の記述なし → DATA2 にラッチ生成
- A=101 → DATA2, DATA3 の記述なし → 両方にラッチ生成
- DATA1 はすべての条件で記述されているので、ラッチは生成されない
- defaultの記述があっても、個々の条件に抜けがあればラッチが生成される
A[0]─┬─────[AND]──[D Q]── DATA2(ラッチ付き)
A[1]─┤ [INV] G
A[2]─┼──────────────────── DATA1(ラッチなし)
└──[AND]──────[D Q]── DATA3(ラッチ付き)
G
✅ すべての出力を記述した正しい記述(不定値で埋める)
always @(A) begin
case (A)
3'b000 : begin DATA1 <= 1'b1; DATA2 <= 1'b0; DATA3 <= 1'bX; end
3'b001 : begin DATA1 <= 1'b0; DATA2 <= 1'bX; DATA3 <= 1'b1; end
3'b010 : begin DATA1 <= 1'b0; DATA2 <= 1'b1; DATA3 <= 1'bX; end
3'b101 : begin DATA1 <= 1'b1; DATA2 <= 1'bX; DATA3 <= 1'bX; end
default: begin DATA1 <= 1'b0; DATA2 <= 1'b0; DATA3 <= 1'b0; end
endcase
end
- 全条件で全出力(DATA1〜DATA3)が必ず代入される
- どちらの値でもよい場合は
1'bX(不定値)を代入する - 不定値を使うと論理合成ツールが最適化でき、回路規模が小さくなる
A[0]─┬─────[AND]──── DATA2(ラッチなし)
A[1]─┤
A[2]─┼─────────────── DATA1(ラッチなし)
└──[AND][AND]── DATA3(ラッチなし)
(5)入力信号が足りない場合
always文を使って組み合わせ回路を記述するとき、すべての入力信号をイベント式に記述しないと、シミュレーションの時にラッチ動作をします。
アンドゲートの記述を例に考えてみましょう。この例ではalways文の中に論理式で記述しています。
❌ イベント式に B が抜けている記述
module AND_GATE(A,B,Z);
input A,B;
output Z;
reg Z;
always @(A) begin // ← B が抜けている!
Z <= A & B;
end
endmodule
【RTLシミュレーション】 A ─┐ ┌── B ──┐ ┌──┘ ← B の変化が Z に伝わらない Z ───┘↑ラッチ動作(B の変化を無視)
Bの変化がZに伝わらなくなり、ラッチ動作をしてしまう
⚠️ 論理合成ではラッチは生成されない(しかし危険)
// 論理合成ツールはイベント式を参照しない
// → ANDゲートとして合成される
A ──┐
├─[AND]─ Z
B ──┘
- 論理合成ツールはイベント式を見ていないため、ラッチは生成されない
- しかし合成後のゲートレベルシミュレーションはラッチ動作をしない
- → RTLシミュレーションとゲートレベルシミュレーションの結果が一致しない!
イベント式の記述ミスはシミュレーション段階では検出しにくく、製造後に不具合として現れる危険があります。イベント式は注意して記述し、こまめにチェックしましょう。
always @(A or B) begin // 全入力信号を記述
Z <= A & B;
end
または Verilog-2001以降では always @(*) を使うと、always文内で参照している全信号が自動的にイベント式に含まれるため安全です。
(6)修了判定
8to1セレクタをcase文で記述した。①〜④のcase文で論理合成後ラッチが生成されるのはどれか?
完全一致のみ正解。
module SEL8(A, SEL, Y);
input [7:0] A;
input [2:0] SEL;
output Y;
reg Y;
always @(A or SEL) begin
case (SEL)
// ←どのcase文か選ぶ
endcase
end
endmodule
① □(選択肢)
case (SEL)
3'b000 : Y <= A[0];
3'b001 : Y <= A[1];
3'b010 : Y <= A[2];
3'b011 : Y <= A[3];
3'b100 : Y <= A[4];
3'b101 : Y <= A[5];
3'b110 : Y <= A[6];
default: Y <= 1'bX;
endcase
3'b111 が未記述だが、default があるのでラッチは生成されない。ただし 3'b111 のときは 1'bX が出力される(8to1セレクタとしての動作はしない)。
② □(選択肢)
case (SEL)
3'b000 : Y <= A[0];
3'b001 : Y <= A[1];
3'b010 : Y <= A[2];
3'b011 : Y <= A[3];
3'b100 : Y <= A[4];
3'b101 : Y <= A[5];
3'b110 : Y <= A[6];
3'b111 : Y <= A[7];
default: Y <= 1'bX;
endcase
全項目 + default が記述されており、ラッチは生成されない。②と③は同じ回路が合成される。
③ □(選択肢)
case (SEL)
3'b000 : Y <= A[0];
3'b001 : Y <= A[1];
3'b010 : Y <= A[2];
3'b011 : Y <= A[3];
3'b100 : Y <= A[4];
3'b101 : Y <= A[5];
3'b110 : Y <= A[6];
3'b111 : Y <= A[7];
endcase
全項目が重複なく記述されており、defaultがなくてもラッチは生成されない。②と同じ回路が合成される。
④ ✅ 正解(ラッチが生成される)
case (SEL)
3'b000 : Y <= A[0];
3'b001 : Y <= A[1];
3'b010 : Y <= A[2];
3'b011 : Y <= A[3];
3'b100 : Y <= A[4];
3'b101 : Y <= A[5];
3'b110 : Y <= A[6];
endcase
3'b111 が未記述 かつ default もないため、SEL=111 のときYの値が不定になり、論理合成後にラッチが生成される。
- ④のみがラッチを生成する → 選択式の取り得るすべての選択項目が記述されていないため
- ①の記述も選択項目が欠けているが、default があるのでラッチは生成されない。ただし合成された回路は8to1セレクタとしての動作はしない
- ②と③の記述は全く同じ回路が生成される。選択式の取り得るすべての選択項目が重複なく記述されていれば、defaultがなくても合成結果は同じ。ただし、検証のことも考えdefaultは必ず記述する