computer system foundationCSF 2026
演習 D / 8-BIT ALU

8ビット ALU

A・B・C で作った回路を1つの箱にまとめ、opcode で「どの計算をするか」を選べるようにする。CPU の心臓部である。

難易度 ★★★ 所要 30–45分 前提 演習A・B
01

ALU は「計算の切り替えスイッチ」

ALU(Arithmetic Logic Unit/算術論理演算ユニット)は、CPU の中で実際に計算を行う部分である。 新しい計算方法を発明するわけではない。すでに作った回路を並べておき、opcode という選択信号でどれか1つの結果を出す——それが ALU である。

これまで(演習A・B・C)
加算器は足し算だけ
減算器は引き算だけ
比較器は比較だけ
ALU(この演習)
6つの計算を1つの箱に入れ
opcode で選ぶ
= CPU の命令の正体
02

opcode と演算の対応

opcode演算a=10, b=3 のときの result
3'b000ADDa + b13
3'b001SUBa − b7
3'b010ANDa & b2
3'b011ORa | b11
3'b100XORa ^ b9
3'b101NOT~a245
3'b110 / 111未使用0(default)
zero フラグは「結果がちょうど 0 であったか」を表す1ビットである。 CPU はこれを見て「等しかったら分岐する」を判断する。演習Cで eq を作ったのと同じ考え方である。
03

作るモジュールの仕様

module
alu8
input
a [7:0] / b [7:0] / opcode [2:0]
output
result [7:0] — 演算結果 / cout — 加減算の桁上がり(論理演算では 0) / zero — 結果が 0 なら 1
04

書いて、その場でチェック

加算・減算の 9 ビット結果(add_result / sub_result)と zero フラグはあらかじめ書いてある。 あなたが書くのは always @(*) の中の case 文——opcode に応じてどの結果を result と cout に流すか、という部分である。

module alu8 (
    input      [7:0] a,
    input      [7:0] b,
    input      [2:0] opcode,
    output reg [7:0] result,
    output reg       cout,
    output           zero
);
endmodule
Ctrl / ⌘ + Enter でも実行
答え合わせ(自分で試してから開く)
always @(*) begin
    case (opcode)
        3'b000: {cout, result} = add_result;
        3'b001: {cout, result} = sub_result;
        3'b010: begin result = a & b; cout = 0; end
        3'b011: begin result = a | b; cout = 0; end
        3'b100: begin result = a ^ b; cout = 0; end
        3'b101: begin result = ~a;    cout = 0; end
        default: begin result = 8'd0; cout = 0; end
    endcase
end

default を書く理由:すべての opcode を書かないと、指定されなかった場合に「前の値を保持する回路(ラッチ)」が勝手に作られてしまう。 組み合わせ回路のつもりが記憶回路になる、という有名な落とし穴である。

05

考えてみよう

  1. SUB(3'b001)で zero が 1 になるのはどんなときですか。それは演習Cのどのフラグと同じ意味でしょうか。
  2. この ALU に 左シフト(a << 1)を追加するとしたら、opcode をいくつにして、case にどう書き足しますか。実際に足してチェッカーで試してみましょう。
  3. default を消すと何が起きますか。チェッカーで確かめてから、なぜそうなるか説明してみましょう。
  4. ADD と SUB で cout の意味が違う(片方は桁上がり、もう片方は桁借りの裏返し)。CPU はこの1本の線をどう使い分けているでしょうか。
発展:本物のツール(EDA Playground)でも動かす 任意

実際のシミュレータで波形も見たい人は、EDA Playground に設計を貼り付けて実行できる(Gmail アカウントが必要)。 手順は 使い方ガイド へ。

TESTBENCH(左パネル)
module tb_alu8;
    reg [7:0] a, b;
    reg [2:0] opcode;
    wire [7:0] result;
    wire cout, zero;

    alu8 uut (.a(a), .b(b), .opcode(opcode),
              .result(result), .cout(cout), .zero(zero));

    initial begin
        $dumpfile("dump.vcd");
        $dumpvars(0, tb_alu8);
        $monitor("op=%b a=%0d b=%0d | result=%0d cout=%0d zero=%0d",
                  opcode, a, b, result, cout, zero);

        a = 8'd10; b = 8'd3;
        opcode = 3'b000; #10;
        opcode = 3'b001; #10;
        opcode = 3'b010; #10;
        opcode = 3'b011; #10;
        opcode = 3'b100; #10;
        opcode = 3'b101; #10;

        a = 8'd5; b = 8'd5;
        opcode = 3'b001; #10;

        $display("--- テスト完了 ---");
        $finish;
    end
endmodule