Project/RISC-V CPU Architecture Design

[5] ALU 설계

by 한PU 2026. 1. 2.
반응형

저번 포스팅에서 Reg File을 설계했습니다. 이번에는 ALU를 설계할게요.

ALU 블록 다이어그램

다들 한번씩은 설계해보셨죠??

Combinational Logic을 공부할때 한번씩 공부할것 같습니다.

간단하지만 굉장히 중요한 회로입니다. 

 

ALU 이론

ALU는 입력된 두 개의 데이터를 받아서, 제어 신호가 지시하는 대로 논리 연산을 수행하고 결과를 내보냅니다.

이때 clock이 낀다면 연산 속도가 굉장히 느릴겁니다.

이미 연산이 끝났는데 clock 신호를 기다린다면, 연산 이후의 동작도 1cycle씩 계속 밀릴겁니다.

그렇기에 ALU는 Combinational Logic 덩어리로 설계합니다.

 

우리가 만들 RV32I 프로세서의 ALU는 다음 4가지 종류의 연산을 처리할 수 있어야합니다.

1. Arithmetic Operation: ADD, SUB

2. Logical Operation: AND, OR, XOR

3. Shift Operation: SLL(Shift Left Logical), SRL, SRA(Shift Right Arithmetic)

    SRL과 SRA의 차이

        SRL은 >> 연산자를 이용하여 MSB의 빈칸을 0으로 채웁니다.

        SRA는 >>> 연산자를 이용하여 MSB의 빈칸을 부호 비트로 채웁니다.

4. Comparison Operation: SLT (Set Less Than), SLTU

    SLT (signed): src_a < src_b 이면 1, 아니면 0 (부호 감안)

    SLTU (unsigned): src_a < src_b 이면 1, 아니면 0 (부호 없이)

 

Operation은 총 10개네요. 자연스럽게 alu_op 신호는 4bit를 차용합니다.

ALU 설계

alu.v 코드를 작성해봅시다. always @(*), case 문법을 이용합니다.

`timescale 1ns / 1ps

module alu(
    input   wire    [31:0]  src_a       ,
    input   wire    [31:0]  src_b       ,
    input   wire    [3:0]   alu_op      ,
    output  reg     [31:0]  alu_result  ,
    output  wire            zero_flag
    );

    // 1. ALU Operation Logic
    always @(*) begin
        case (alu_op)
            // Arithmetic
            4'b0000: alu_result = src_a + src_b;                    // ADD
            4'b0001: alu_result = src_a - src_b;                    // SUB

            // Logical
            4'b0010: alu_result = src_a & src_b;                    // AND
            4'b0011: alu_result = src_a | src_b;                    // OR
            4'b0100: alu_result = src_a ^ src_b;                    // XOR

            // Shift
            4'b0101: alu_result = src_a << src_b[4:0];              // SLL
            4'b0110: alu_result = src_a >> src_b[4:0];              // SRL
            4'b0111: alu_result = $signed(src_a) >>> src_b[4:0];    // SRA 

            // Comparison
            4'b1000: begin  // SLT
                if ($signed(src_a) < $signed(src_b)) begin
                    alu_result = 32'd1;
                end
                else begin
                    alu_result = 32'd0;
                end
            end
            4'b0000: begin  // SLTU
                if (src_a < src_b) begin
                    alu_result = 32'd1;
                end
                else begin
                    alu_result = 32'd0;
                end
            end
            default: alu_result = 32'b0;
        endcase
    end

    // 2. Zero Flag
    assign zero_flag = (alu_result == 32'b0) ? 1'b1 : 1'b0;
endmodule

 

Shift에 대해 좀 더 설명하겠습니다.

왜 src_b[4:0]을 하는지에 대해...

기본적으로 프로세서의 Shift 연산자는 Shifting할 값과 그 양을 인풋으로 받습니다.

짱구를 굴려봅시다.

src_b는 shift 양을 값으로 받는데, 데이터 크기가 32bit 이므로 그 양을 결정하는 bit는 5bit가 됩니다.

11111이면 src_a의 모든 데이터 bit를 shift하겠네요. (LSB가 MSB가 됨)

ALU는 TB도 해봅시다.

 

tb 코드는 제미나이를 이용할게요.

 

ALU TestBench

`timescale 1ns / 1ps

module tb_alu;

    // ========================================================================
    // 1. Signal Declaration
    // ========================================================================
    reg  [31:0] src_a_r      ; // Stimulus reg A
    reg  [31:0] src_b_r      ; // Stimulus reg B
    reg  [3:0]  alu_op_r     ; // Stimulus reg Op
    wire [31:0] alu_result_w ; // Output wire Result
    wire        zero_flag_w  ; // Output wire Zero

    integer     err_cnt = 0  ; // Error Counter

    // ========================================================================
    // 2. Instantiate the DUT
    // ========================================================================
    alu u_alu (
        .src_a_i      (src_a_r     ),
        .src_b_i      (src_b_r     ),
        .alu_op_i     (alu_op_r    ),
        .alu_result_o (alu_result_w),
        .zero_flag_o  (zero_flag_w )
    );

    // ========================================================================
    // 3. ALU Operation Constants
    // ========================================================================
    localparam OP_ADD  = 4'b0000 ;
    localparam OP_SUB  = 4'b0001 ;
    localparam OP_AND  = 4'b0010 ;
    localparam OP_OR   = 4'b0011 ;
    localparam OP_XOR  = 4'b0100 ;
    localparam OP_SLL  = 4'b0101 ;
    localparam OP_SRL  = 4'b0110 ;
    localparam OP_SRA  = 4'b0111 ;
    localparam OP_SLT  = 4'b1000 ;
    localparam OP_SLTU = 4'b1001 ;

    // ========================================================================
    // 4. Test Procedure
    // ========================================================================
    initial begin
        $display("==================================================");
        $display("   Starting ALU Verification (Self-Checking)");
        $display("==================================================");

        // TC 1: Arithmetic
        run_test(32'd10,  32'd20,  OP_ADD, 32'd30,      1'b0, "ADD: 10 + 20");
        run_test(32'd100, 32'd100, OP_SUB, 32'd0,       1'b1, "SUB: 100 - 100 (Zero)");
        run_test(32'd10,  32'd20,  OP_SUB, -32'd10,     1'b0, "SUB: 10 - 20 (Neg)");

        // TC 2: Logic
        run_test(32'hAA, 32'h55, OP_AND, 32'h00, 1'b1, "AND: AA & 55");
        run_test(32'hAA, 32'h55, OP_OR,  32'hFF, 1'b0, "OR : AA | 55");
        run_test(32'hAA, 32'h55, OP_XOR, 32'hFF, 1'b0, "XOR: AA ^ 55");

        // TC 3: Shift
        run_test(32'd1,        32'd31, OP_SLL, 32'h80000000, 1'b0, "SLL: 1 << 31");
        run_test(32'hFFFFFFFF, 32'd31, OP_SRL, 32'd1,        1'b0, "SRL: -1 >> 31");
        run_test(-32'd4,       32'd1,  OP_SRA, -32'd2,       1'b0, "SRA: -4 >>> 1");
        run_test(32'hFFFFFFFF, 32'd31, OP_SRA, 32'hFFFFFFFF, 1'b0, "SRA: -1 >>> 31");

        // TC 4: Comparison
        run_test(32'hFFFFFFFF, 32'd1, OP_SLT,  32'd1, 1'b0, "SLT : -1 < 1 (Signed)");
        run_test(32'hFFFFFFFF, 32'd1, OP_SLTU, 32'd0, 1'b1, "SLTU: -1 < 1 (Unsigned)");

        // Final Report
        $display("==================================================");
        if (err_cnt == 0) $display("   SUCCESS: All Tests Passed! :)");
        else              $display("   FAILURE: Found %d Errors :(", err_cnt);
        $display("==================================================");
        
        $finish;
    end

    // ========================================================================
    // Task: Automated Checking
    // ========================================================================
    task run_test;
        input [31:0]  i_a       ;
        input [31:0]  i_b       ;
        input [3:0]   i_op      ;
        input [31:0]  exp_res   ;
        input         exp_zero  ;
        input [255:0] test_name ;
        begin
            src_a_r  = i_a  ;
            src_b_r  = i_b  ;
            alu_op_r = i_op ;
            
            #10; // Wait for logic

            if ((alu_result_w !== exp_res) || (zero_flag_w !== exp_zero)) begin
                $display("[FAIL] %0s", test_name);
                $display("       Got: Res=0x%h, Z=%b", alu_result_w, zero_flag_w);
                err_cnt = err_cnt + 1;
            end else begin
                $display("[PASS] %0s", test_name);
            end
        end
    endtask

endmodule

 

결과

waveform 안봐도 되는게 진짜 좋네요.

항상 AI의 발전에 감사합니다.

반응형