computer system foundationCSF 2026
言語入門 / 02.1

Verilog 入門

ハードウェアを「コード」で設計する言語を、プログラミング経験ゼロから理解する。

難易度:★★☆ 所要:20–30分 前提:論理ゲート・4ビット加算器
01

Verilog とは何か

ソフトウェア(Python など)

コンピュータに「何をするか」を伝える命令書。CPU が命令を上から順番に実行する。

# 2つの数を足す
a = 5
b = 3
print(a + b)  # 8
Verilog(ハードウェア記述言語)

回路の「配線図」をコードで表す言語。すべての回路が同時に動くのが特徴である。

// 2つの数を足す回路
assign result = a + b;
// a か b が変わると
// result も即座に変わる
Verilog は「命令の実行順序」ではなく「回路の構造」を書く言語である。 電線をどうつなぐか、どのゲートを使うかを記述する。ここが、プログラミング言語と決定的に違う点である。
02

モジュール ― 回路の「箱」

Verilog の基本単位は module である。IC チップ(黒い四角いパーツ)をイメージすると分かりやすいだろう。 入力ピン出力ピンを持つ「箱」である。

and_gate モジュール(IC チップ1個ぶん) 入力 a 入力 b 出力 y
// ↓ モジュール名(IC チップの型番に相当)
module and_gate (
    input  a,     // ← 入力ピン a
    input  b,     // ← 入力ピン b
    output y      // ← 出力ピン y
);

    assign y = a & b;  // ← 回路の中身:AND 演算

endmodule  // ← 箱の終わり
キーワード意味
module … endmodule箱の外枠。IC チップ全体を定義する
input / output入力・出力ピン。外部との接続口
assign電線の結び方を書く。常時有効
03

wire と reg ― 信号を表す2つの型

wire(電線)

名前のとおり「ただの電線」である。つながれた先の値をそのまま伝えるだけで、値を記憶しない。 assign 文と組み合わせて使う。

wire sum;
assign sum = a + b;
// a や b が変わると
// sum もすぐ変わる

→ 組み合わせ回路(加算器など)で使う

reg(プロセス代入用の変数)

always ブロックの中で代入するための変数である。reg と書くだけでは値を記憶しない。 クロックの立ち上がりで更新する回路を書くと、フリップフロップ(記憶素子)として合成される。

reg [7:0] count;
always @(posedge clk)
    count <= count + 1;
// clk の立ち上がりごとに +1

→ 順序回路でも組み合わせ回路でも、always 内の代入先に使う

ビット幅の指定

信号が複数ビットの場合は [上位:下位] で幅を指定する。

宣言ビット幅表せる値
wire y1 ビット0 / 1
wire [3:0] y4 ビット0〜15
reg [7:0] y8 ビット0〜255
04

回路を書く3つの方法

① assign ― 組み合わせ回路(ゲートの配線)

「この出力はこの入力たちの演算結果」という常時有効な結線を記述する。 プログラムの行と違い、順番に実行されるわけではない

assign y   = a & b;               // AND
assign sum = a + b;               // 加算
assign sel = (a > b) ? a : b;    // 三項演算子(MUX)
& AND| OR^ XOR~ NOT + 加算- 減算== 一致?: MUX
② always @(posedge clk) ― 順序回路(クロックで動く)

クロックの立ち上がり(posedge)ごとに実行されるブロックである。 フリップフロップ(記憶素子)の動作を表す。

always @(posedge clk) begin
    if (rst)
        count <= 0;           // リセット
    else
        count <= count + 1; // インクリメント
end
代入の使い分け: always @(posedge clk) の状態更新には <=(ノンブロッキング代入)を使う。 always @(*) の組み合わせ回路には = を使う。
③ always @(*) ― if / case を使いたい組み合わせ回路

assign では書きにくい if-elsecase は always @(*) で書く。 * は「すべての入力変化に反応する」という意味である。演習D の ALU でこの形を使う。

always @(*) begin
    case (opcode)
        2'b00: result = a + b;  // ADD
        2'b01: result = a - b;  // SUB
        default: result = 0;
    endcase
end
05

完全な例:1ビット全加算器

論理ゲートのページで学んだ全加算器(Full Adder)を Verilog で書いてみましょう。 入力 a, b, cin から出力 sum, cout を計算する。

真理値表
abcinsumcout
00000
00110
01010
01101
10010
10101
11001
11111
論理式
sum = a XOR b XOR cin
cout = (a AND b) OR
(cin AND (a XOR b))

この式をそのまま assign で書ける。真理値表と式と回路が1対1で対応しているのが、Verilog の分かりやすいところである。

module full_adder (
    input  a, b, cin,   // 3本の入力ピン
    output sum, cout   // 2本の出力ピン
);

    // sum = a XOR b XOR cin
    assign sum  = a ^ b ^ cin;

    // cout = (a AND b) OR (cin AND (a XOR b))
    assign cout = (a & b) | (cin & (a ^ b));

endmodule  // わずか8行で1ビット全加算器が完成
この全加算器を4個並べると、第1章で見た 4ビット加算器(リップルキャリー) になる。 モジュールを部品として使い回せるのが Verilog の強みである。
06

テストベンチ ― 設計を検証する

テストベンチ(Testbench)は「作った回路を試す実験台」である。実際のチップを作る前に、 コンピュータ上でシミュレーションしてバグがないか確認する。

テストベンチ 全8通りの入力を与える 期待値と自動で比較する 左パネル full_adder 検証したい設計 右パネル a, b, cin sum, cout
module tb_full_adder;
    reg  a, b, cin;        // 操作する入力(reg で宣言)
    wire sum, cout;        // 観察する出力(wire で宣言)
    reg [1:0] expected;      // 期待する {cout, sum}
    integer pattern, errors;

    // 設計した full_adder を接続する
    full_adder uut (.a(a), .b(b), .cin(cin),
                    .sum(sum), .cout(cout));

    initial begin
        $dumpfile("dump.vcd");   // 波形ファイル出力
        $dumpvars(0, tb_full_adder);

        errors = 0;
        for (pattern = 0; pattern < 8; pattern = pattern + 1) begin
            {a, b, cin} = pattern;
            expected = a + b + cin;
            #10;
            if ({cout, sum} !== expected) begin
                errors = errors + 1;
                $display("FAIL: %b%b%b: expected=%b, actual=%b", a, b, cin, expected, {cout, sum});
            end
        end
        if (errors == 0)
            $display("PASS: all 8 input patterns verified");

        $finish;
    end
endmodule
# シミュレーション出力(ログ)
PASS: all 8 input patterns verified
大事なのは、テストベンチが自分で正誤を判定していることである。 出力の数字を目で見比べるのではなく、期待値と自動で照合して PASS / FAIL を出す。 演習2のブラウザ内チェッカーも、同じことをしている。
07

Verilog は実際に何に使われるのか

「Verilog で回路を書いたあと、どうなるのか」。現実では大きく2つの方向に使われる。

Path A — FPGA で実機動作

Verilog の回路を論理合成して FPGA(書き換え可能な半導体)に書き込むと、実際に動くハードウェアになる。 チップを製造しなくても、設計をすぐ実機で試せる。

Verilog コード
↓ 論理合成(Vivado / Quartus)
ビットストリーム
↓ FPGA に書き込み
実機で動作

活用例:カメラの画像処理、通信機器、AI アクセラレータ、宇宙探査機の制御回路

Path B — シミュレーション・性能評価

実チップを作る前にテストベンチで動作検証したり、新しいアーキテクチャのアイデアを シミュレーションで性能評価する。研究では「この CPU 設計は速いか」を Verilog で試す。

Verilog + テストベンチ
↓ シミュレータ(Icarus Verilog)
波形・ログで動作確認
↓ 性能測定・バグ修正
設計の正しさを確認

活用例:CPU アーキテクチャ研究、新機能の性能予測、大学や企業の研究開発

この授業では:演習2で Path B(シミュレーション) を体験する。 テストが全部パスしたとき、それが「正しい回路を設計できた」証明になる。
08

次のステップ:演習2

ここまでの知識で、演習2に挑戦できる。ブラウザ内でその場で正誤が出るので、 インストールもアカウント登録も要らない。加算器 → 減算器 → 比較器 → ALU → 小型 CPU の順に、 前に作ったものを次で使い回しながら進む。

発展:本物のツール(EDA Playground)でも動かす 任意

実際の開発で使われるシミュレータ(Icarus Verilog)と波形ビューアを体験したい人は、 EDA Playground に上のコードを貼り付けて実行できる(Gmail アカウントが必要)。 手順は 使い方ガイド へ。

推奨設定
  • Language:Verilog
  • Simulator:Icarus Verilog 12.0
  • 「Open EPWave after run」にチェック(波形表示)
  • テストベンチ → 左パネル/設計コード → 右パネル