論理回路の基礎

何事も基本が大切です。HDLを始める前に論理回路の基礎を確認しておきましょう。

もふねこ

「えっ、いきなりVerilogを書くんじゃないの?」って思った?🐾
実は、HDLを書く前に『AND・OR・NOT』といった論理回路の基本を知っておくのが、一番の近道なんだよ!一緒にサクッとおさらいしよう!

1. 論理演算子(AND・OR・NOT)

論理回路の動作は論理式で表すことができます。論理式の演算子には以下の3種類があります。

名前別名演算子動作
論理積 AND A・B(ドット) 両方の入力が1のとき1、それ以外は0。ドットは省略可。
論理和 OR A+B どちらかの入力が1であれば1、両方0のとき0。
論理否定 NOT Ā(Aバー) 0→1、1→0 に反転する。
💡
ポイント 組み合わせ回路の入力と出力の関係は、すべてこの3種類の演算子による論理式で表すことができます。

2. ブール代数の法則

論理式はブール代数の諸法則に従います。主な法則を以下にまとめます。

AND(論理積)に関する法則

法則名式
0との積A・0 = 0
1との積A・1 = A
同一則A・A = A
補元則A・Ā = 0

OR(論理和)に関する法則

法則名式
0との和A+0 = A
1との和A+1 = 1
同一則A+A = A
補元則A+Ā = 1

共通の法則

法則名AND形式OR形式
交換則A・B = B・AA+B = B+A
結合則(A・B)・C = A・(B・C)(A+B)+C = A+(B+C)
分配則A・(B+C) = A・B+A・CA+B・C = (A+B)・(A+C)
吸収則A・(A+B) = AA+A・B = A

ド・モルガンの法則

反転の範囲を変えることで、論理和と論理積を相互に変換できる重要な法則です。

法則式
第1法則A・B の否定 = Ā + B̄
第2法則A+B の否定 = Ā ・ B̄
⚠️
覚えるポイント ド・モルガンの法則は「否定をとると AND⇔OR が入れ替わる」と覚えると分かりやすいです。詳細は後のページで扱います。
もふねこ

「法則がいっぱいあって覚えられない…」って心配しなくて大丈夫!🐾
実践のコード書きで一番よく使うのは『ド・モルガンの法則』だから、今は『こういうのがあるんだな』くらいでサラッと流してOKだよ。

実習:18の法則を144回の比較で確かめる

法則を暗記する前に、式の左辺と右辺へ同じ入力を入れてみよう。a・b・cの8通りを使い、上の18の法則を全部比べるよ。ド・モルガンの法則ではaとbが違う入力も重要なんだ。

回路・テストベンチ・実行手順のZIPを展開し、boolean-lawsフォルダで実行してね。ツールの準備は最初の実験で説明しているよ。

iverilog -g2012 -Wall -s tb_boolean_laws -o sim.vvp boolean_laws.v tb_boolean_laws.v
vvp sim.vvp
18の法則を並べた回路を見る
`timescale 1ns/1ps
`default_nettype none
// Each bit pairs the two sides of one law from the L1 tables.
// lhs[n] and rhs[n] must be equal for every input combination.
module boolean_laws (
    input  wire a,
    input  wire b,
    input  wire c,
    output wire [17:0] lhs,
    output wire [17:0] rhs
);
    // AND laws
    assign lhs[0]  = a & 1'b0;        assign rhs[0]  = 1'b0;              // A*0 = 0
    assign lhs[1]  = a & 1'b1;        assign rhs[1]  = a;                 // A*1 = A
    assign lhs[2]  = a & a;           assign rhs[2]  = a;                 // A*A = A
    assign lhs[3]  = a & ~a;          assign rhs[3]  = 1'b0;              // A*/A = 0
    // OR laws
    assign lhs[4]  = a | 1'b0;        assign rhs[4]  = a;                 // A+0 = A
    assign lhs[5]  = a | 1'b1;        assign rhs[5]  = 1'b1;              // A+1 = 1
    assign lhs[6]  = a | a;           assign rhs[6]  = a;                 // A+A = A
    assign lhs[7]  = a | ~a;          assign rhs[7]  = 1'b1;              // A+/A = 1
    // commutative
    assign lhs[8]  = a & b;           assign rhs[8]  = b & a;             // AB = BA
    assign lhs[9]  = a | b;           assign rhs[9]  = b | a;             // A+B = B+A
    // associative
    assign lhs[10] = (a & b) & c;     assign rhs[10] = a & (b & c);
    assign lhs[11] = (a | b) | c;     assign rhs[11] = a | (b | c);
    // distributive
    assign lhs[12] = a & (b | c);     assign rhs[12] = (a & b) | (a & c);
    assign lhs[13] = a | (b & c);     assign rhs[13] = (a | b) & (a | c);
    // absorption
    assign lhs[14] = a & (a | b);     assign rhs[14] = a;
    assign lhs[15] = a | (a & b);     assign rhs[15] = a;
    // De Morgan
    assign lhs[16] = ~(a & b);        assign rhs[16] = ~a | ~b;
    assign lhs[17] = ~(a | b);        assign rhs[17] = ~a & ~b;
endmodule
`default_nettype wire
全入力を比較するテストベンチを見る
`timescale 1ns/1ps
`default_nettype none
// Drives every combination of a, b and c and compares both sides of all
// 18 laws. 8 combinations x 18 laws = 144 checks.
module tb_boolean_laws;
    reg a, b, c;
    wire [17:0] lhs, rhs;
    integer checks = 0;
    integer i, law;

    boolean_laws dut(.a(a), .b(b), .c(c), .lhs(lhs), .rhs(rhs));

    task check_all;
        begin
            for (law = 0; law < 18; law = law + 1) begin
                checks = checks + 1;
                if (lhs[law] !== rhs[law])
                    $fatal(1, "FAIL: law=%0d a=%b b=%b c=%b lhs=%b rhs=%b",
                           law, a, b, c, lhs[law], rhs[law]);
            end
        end
    endtask

    initial begin
        $dumpfile("boolean_laws.vcd");
        $dumpvars(0, tb_boolean_laws);
        $display("  a b | ~(a.b)  /a+/b | ~(a+b)  /a./b");
        for (i = 0; i < 8; i = i + 1) begin
            {a, b, c} = i[2:0];
            #1;
            check_all;
            if (c == 1'b0)
                $display("  %b %b |   %b       %b   |   %b       %b",
                         a, b, lhs[16], rhs[16], lhs[17], rhs[17]);
            #1;
        end
        $display("PASS: boolean laws (%0d checks)", checks);
        $finish;
    end

    initial begin
        #1000;
        $fatal(1, "FAIL: testbench timeout");
    end
endmodule
`default_nettype wire

Windows / Icarus Verilog 13.0で2026年10月26日に実行した結果の抜粋だよ。合成面積やFPGA実機の測定結果ではなく、0/1入力の論理機能を確かめたものだよ。

  a b | ~(a.b)  /a+/b | ~(a+b)  /a./b
  0 0 |   1       1   |   1       1
  0 1 |   1       1   |   0       0
  1 0 |   1       1   |   0       0
  1 1 |   0       0   |   0       0
PASS: boolean laws (144 checks)

わざと間違えて、テストの働きも確かめよう

コピーした回路の rhs[16] = ~a | ~b を rhs[16] = ~a & ~b に変えると、a=0、b=1で不一致になるよ。吸収則の右辺を a & b にするとa=1、b=0、分配則の外側のANDをORにするとa=0、b=0、c=1で検出できた。

ところが、aとbが同じ00と11だけを試すと、間違えたド・モルガンの式でも合格してしまう(この簡易テストベンチはサイト側の検証スクリプトで実行したもので、配布ZIPには8通りすべてを回すほうが入っているよ)。PASSは、試した入力と期待値の範囲で一致したという意味なんだ。全4通りが小さく列挙できる回路では、01と10も省かず確認しよう。

3. 真理値表

真理値表とは、論理回路の入力と出力の関係を網羅的に示した表です。入力の全組み合わせに対して出力値を記述します。

AND・OR の真理値表

ABA・B(AND)A+B(OR)
0000
0101
1001
1111

ド・モルガンの法則を真理値表で証明する

「A・B の否定 = Ā + B̄」を真理値表で確認します。

ABA・BA・Bの否定(左辺)ĀB̄Ā+B̄(右辺)
0001111
0101101
1001011
1110000

左辺と右辺が一致しており、ド・モルガンの第1法則が成立することを確認できます。

4. 回路記号

論理演算子は回路記号として図示できます。回路記号を線でつないだものが回路図です。

AND、OR、NOT、NAND、NOR、XORの回路記号。小さな丸は反転を示す。下段は出力を反転したANDと入力を反転したORの等価関係。
形で区別するゲート記号の例。線は信号、小さな丸は反転を表す。

図のNANDとNORは、それぞれANDとORの出力を反転したもの。XORはORに似ているけれど、入力側にもう1本の曲線があるよ。

ゲート名MIL記法の形状JIS記法動作
ANDゲートD字型□に「&」全入力が1のとき出力1
ORゲート矢型□に「≥1」1つ以上の入力が1で出力1
NOTゲート(インバータ)三角+○□に「1」+○入力を反転して出力
NANDゲートAND+出力に○□に「&」+○AND出力の否定
NORゲートOR+出力に○□に「≥1」+○OR出力の否定
✅
ルール 記号の出力側に「○」が付くと否定(NOT)を意味します。入力側の○は、入力を反転してからゲートに入力することを意味します。3入力・4入力のゲートも存在します。

5. タイミングチャート

タイミングチャートとは、時間の流れに沿って入出力信号がどう変化するかを示した図です。横軸が時間、縦軸が信号値(0または1)を表します。

タイミングチャートの読み方

時刻入力A入力B出力 A・B(AND)出力 A+B(OR)
T1以前0000
T1(Aが1へ)1001
T2(Bが1へ)1111
T3(Aが0へ)0101
T4(Bが0へ)0000

タイミングチャートは、論理式・真理値表・回路記号とともに論理回路設計に欠かせないツールです。

6. 真理値表から論理式を導く

真理値表が与えられたとき、出力Qが1になる行に着目して論理式を導きます。

手順(出力が1になる条件が1つの場合)

  1. 出力Qが1になる行を探す
  2. その行の入力条件をAND式で表す(例:A=1, B=1 → A・B)
  3. それが論理式になる(例:Q = A・B)

手順(出力が1になる条件が複数の場合)

Qが1になる条件が複数あるとき、それぞれのAND式をORでつなぎます。

ABQ条件式
000—
011Ā・B
101A・B̄
111A・B

3つの条件をORでつなぐと:

Q = Ā・B + A・B̄ + A・B → 簡略化すると Q = A + B

✅
別の方法:出力0の条件から導く 出力が0になる条件の否定をOR式で表し、ド・モルガンの法則で変形しても同じ結果が得られます。
もふねこ

ここ、ちょっと数学っぽくて難しく感じるかもしれないね🐾
でも、実際の回路設計ツール(論理合成ツール)がこの「簡略化」を自動でやってくれるから、今は『こうやって式ができるんだ』というイメージだけ掴めれば完璧だよ!

7. 組み合わせ回路と順序回路

🔵 組み合わせ回路

  • 出力 = 入力の関数
  • 内部に記憶素子なし
  • 真理値表・論理式で表現できる
  • 入力変化 → 出力変化(遅延あり)
  • 遅延時間は入力値・経路によって異なる

🟣 順序回路

  • 出力 = 現在の状態(記憶)+入力
  • 内部にフリップフロップなどの記憶素子をもつ
  • 出力変化はクロックに同期する
  • 遅延時間は記憶素子の遅延のみでほぼ一定

組み合わせ回路の遅延(具体例)

インバータ(遅延1ns)とANDゲート(遅延2ns)からなる回路で考えます。※1ns・2nsは説明のために置いた仮の値で、測定値ではありません。実際の遅延は対象デバイス・合成結果・配置配線で決まります。

変化のパターン経路遅延時間
B=0のときAが0→1A → ANDゲート → D2ns
Bが0→1B → インバータ(C) → ANDゲート(D)1ns + 2ns = 3ns

同じ回路でも、入力変化のパターンによって出力までの遅延時間が異なります。

順序回路の遅延(フリップフロップの場合)

フリップフロップ(遅延1ns)を追加した順序回路では:

  • CLKの立ち上がりエッジを検出し、そのときのD入力値をXに転送する
  • CLKが立ち上がってから1ns後にXの値が確定する(常にほぼ一定)
  • 組み合わせ回路の遅延値によらず、出力変化タイミングはCLK基準で揃う
もふねこ

お疲れさま!一番最初の「論理回路の基礎」を乗り越えたね🐾
組み合わせ回路と順序回路の違いは、Verilogを書く時に一番重要になるポイントだから、下のまとめでしっかり復習しておこう!

🐾 もふねこのつまずきメモ(関連ページ)

このページで学んだ論理回路の基礎は、Verilogを書き始めてから「読めない・追えない」でつまずく場所でもあるんだ。式・真理値表・回路記号・波形を行き来できるように、関連ページをまとめておくね🐾

実習で確かめる、つまずきの補足

式・真理値表・記号・波形を行き来する

同じ回路を四つの表現で確かめてみよう。2入力ANDなら、式は Y = A & B、真理値表は 00→0、01→0、10→0、11→1 だよ。11 だけの試験ではORとの違いを見つけられない。01 と 10 も試すと、条件の取り違えが見えるんだ。

ド・モルガンの法則は ~(A & B) = ~A | ~B、~(A | B) = ~A & ~B。合成後にゲートの形が変わっていても、全入力の出力が一致すれば同じ論理機能だよ。最適化は面積だけでなく遅延や対象デバイスの資源にも左右されるから、ゲート数だけで優劣は決められない。

回路記号の小さな丸(バブル)は反転を表すよ。RTLビューアでは、まず入出力の名前、反転、MUXの選択信号、FFのクロックとリセットを追おう。記号の形は表示形式によって異なるため、部品名も一緒に確認してね。

ド・モルガンの法則は、記号で読むと形が見えてくるよ。出力にバブルを付けたAND(NAND)は、入力側にバブルを付けたORと同じ論理機能なんだ。~(A & B) = ~A | ~B を記号に置き換えただけだね。ゲート図で形が違って見えても、バブルの位置をたどると同じ式を指していることがあるよ。上の18の法則の実習では、この対応を全入力で確かめているんだ。

タイミングチャートでは「どのエッジで入力を取り込み、何サイクル後の出力を比べるか」を決めるよ。機能シミュレーションの値の変化と、実機の伝搬遅延は別の確認事項なんだ。物理的なセットアップ/ホールドは S2の制約 へ、4パターンを実行する練習は AND実験 へ進もう。

📌 まとめ

  • 論理回路設計は「論理式・真理値表・回路記号・タイミングチャート」の4つで行う
  • すべての論理式は AND(論理積)・OR(論理和)・NOT(論理否定) の3種類で表せる
  • AND演算子はドット(・)、OR演算子は +、NOT演算子はAの上に横棒(Ā)で表す
  • 回路記号にはMIL記法とJIS記法がある
  • 真理値表から論理式を導くには「出力が1になる行のAND式をORでつなぐ」
  • 組み合わせ回路の出力は入力の関数であり、真理値表や論理式で表現できる
  • 順序回路はフリップフロップなどの記憶素子をもち、出力はクロックに同期して変化する
  • タイミングチャートは横軸が時間、縦軸が入出力信号値を表す

🐾 もふねこの現場FAQ

Q. 先輩に回路図を見せてもらったんですが、ANDやORがVerilogのコードとどう繋がるのかわかりません。

A. 「Verilogはプログラミング言語ではなく、ゲートの接続指示書」だという意識が足りないかもしれないね🐾

計算を行う組み合わせ回路は、最終的にAND・OR・NOTの組み合わせで表されるんだ。このゲートの概念を理解しないまま、C言語などの感覚で変数に代入するつもりで書くと、出力が入力に戻ってしまう危険な「combinational loop(組み合わせループ)」というバグを生んでしまうんだ。

これをやると、論理合成ツールで大量のWarningが出て、最悪実機が壊れてしまうんだ。詳しくは、実験ログ3: Warning放置で溶かした話 ベスト5 で解説しているから、ぜひ読んでみてね!