演習C ★★★ 上級 所要:30–45分

8ビット ALU

算術演算と論理演算を1つのモジュールに統合した ALU(算術論理演算ユニット) を Verilog で設計しよう。

📚 概念説明:ALU とは

ALU(Arithmetic Logic Unit)は CPU の中心部品で、 算術演算(加算・減算)と論理演算(AND・OR・XOR・NOT)を1つのモジュールで担います。 opcode(操作コード)の値によって、どの演算を行うかを切り替えます。

opcode(3ビット) 演算 例(A=10, B=3)
3'b000ADDA + B13
3'b001SUBA − B7
3'b010ANDA & B2 (0000_1010 & 0000_0011)
3'b011ORA | B11 (0000_1010 | 0000_0011)
3'b100XORA ^ B9 (0000_1010 ^ 0000_0011)
3'b101NOT A~A245 (1111_0101)
3'b110〜111未使用result = 0(default)
ゼロフラグ(Zero Flag): 演算結果がちょうど 0 のとき、zero = 1 になります。条件分岐命令などで使われる重要なフラグです。

📝 課題

モジュール名:alu8

入力:a [7:0]b [7:0]opcode [2:0]

出力:result [7:0](演算結果)、zero(結果が0のとき1)、cout(加減算時のキャリー/ボロー)

💻 Verilog テンプレート

Design(右パネル)
// ================================
// 8ビット ALU モジュール
// opcode: 000=ADD 001=SUB 010=AND
//         011=OR  100=XOR 101=NOT
// ================================
module alu8 (
    input  [7:0] a,
    input  [7:0] b,
    input  [2:0] opcode,
    output reg [7:0] result,
    output reg       cout,
    output           zero
);

    wire [8:0] add_result = {1'b0,a} + {1'b0,b};
    wire [8:0] sub_result = {1'b0,a} + {1'b0,~b} + 9'd1;

    assign zero = (result == 8'b0);

    always @(*) begin
        case (opcode)
            3'b000: begin  // ADD
                {cout, result} = add_result;
            end
            3'b001: begin  // SUB  (borrow = ~cout)
                {cout, result} = sub_result;
            end
            3'b010: begin result = a & b; cout = 0; end  // AND
            3'b011: begin result = a | b; cout = 0; end  // OR
            3'b100: begin result = a ^ b; cout = 0; end  // XOR
            3'b101: begin result = ~a;   cout = 0; end  // NOT
            default: begin result = 8'b0; cout = 0; end
        endcase
    end

endmodule
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; // ADD: 13
        opcode=3'b001; #10; // SUB: 7
        opcode=3'b010; #10; // AND: 2
        opcode=3'b011; #10; // OR:  11
        opcode=3'b100; #10; // XOR: 9
        opcode=3'b101; #10; // NOT: 245

        // ゼロフラグテスト
        a=8'd5; b=8'd5;
        opcode=3'b001; #10; // SUB: 0 → zero=1

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

▶ EDA Playground で開く

各 opcode の演算を波形で確認しましょう。

🚀 EDA Playground を開く →

※ ログインに Gmail が必要です。 使い方ガイドを見る

🤔 考えてみよう

  1. opcode=3'b001(SUB)でゼロフラグが 1 になるのはどんなとき?CPU の条件分岐命令(例:beq)に使う場合を考えよう。
  2. 今回の ALU に SHL(左シフト)SHR(右シフト)演算を追加するとしたら、opcode と Verilog コードをどう変更しますか?
  3. always @(*)(*) は何を意味しますか?always @(a, b, opcode) と同じですか?
  4. 現実の CPU(例:RISC-V)の ALU はどんな演算をサポートしていますか?調べてみよう。
← 演習B:減算器 次:演習D Accumulator CPU →