Project/RISC-V CPU Architecture Design

[8] RISC-V Control Unit 설계 (2) - ALU Decoder

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

저번 포스트에서 Main Decoder를 만들었습니다.

즉슨, OP Code가 입력되면 Main Decoder가 여러 결정 신호를 보내는데, 그중 하나로 ALUOP 신호가 있습니다.

 

ALU Decoder는 이 ALUOP 신호를 가이드라인 삼아서, Instruction에 포함된 세부 정보를 입력으로 받아

ALU를 제어하는 신호를 출력합니다.

 

ALU Decoder 진리표

ALU Decoder 진리표

case 문 쓰면 쉽게 설계할 수 있겠네요.

 

중요한 내용들은 아래 Main Decoder 포스트에서 다 써서 이번에는 바로 코드로 넘어가겠습니다.

https://han-pu.tistory.com/153

 

[7] RISC-V Control Unit 설계 (1) - Main Decoder

Control Unit을 설계해 봅시다. Control Unit은 CPU Architecture의 핵심이라고 생각합니다.우선 Control Unit의 하위 블록들을 좀 설정해 봅시다.RISC V의 경우 Main Decoder와 ALU Decoder가 필요합니다.Control Unit 블록

han-pu.tistory.com

 

ALU Decoder 코드

설계 코드

`timescale 1ns / 1ps

module alu_decoder(
    input   wire    [1:0]   alu_op_i        ,   // Opcode from Main Decoder
    input   wire    [2:0]   funct3_i        ,   // Instruction funct3
    input   wire            funct7_5_i      ,   // Instruction funct7[5]
    input   wire            op_5_i          ,   // Opcode[5] for I/R checking
    output  reg     [2:0]   alu_control_o       // Control Signal to ALU
    );

    always @(*) begin
        case (alu_op_i)
            // 00: ADD for memory and addr-based instructions
            2'b00: begin
                alu_control_o = 3'b000  ;
            end 
            // 01: SUB for branch comparison
            2'b01: begin
                alu_control_o = 3'b001  ;
            end
            // 10: R and I type ALU Operation
            2'b10: begin
                case (funct3_i)
                    3'b000: begin
                        if (funct7_5_i && op_5_i) begin
                            alu_control_o = 3'b001;
                        end
                        else begin
                            alu_control_o = 3'b000;
                        end
                    end
                    3'b010: begin   // SLT
                        alu_control_o = 3'b101;
                    end
                    3'b110: begin   // OR
                        alu_control_o = 3'b011;
                    end
                    3'b111: begin
                        alu_control_o = 3'b010;
                    end
                    default: begin
                        alu_control_o = 3'b000;
                    end
                endcase
            end
            2'b11: begin    // ADD for LUI
                alu_control_o = 3'b000;
            end
            default: alu_control_o = 3'b000;
        endcase
    end
endmodule

TB 코드

`timescale 1ns / 1ps

module tb_alu_decoder ();

    // ---------------------------------------------------------
    // 1. Signal Declaration
    // ---------------------------------------------------------
    reg  [1:0] alu_op_r        ; // Stimulus: alu_op
    reg  [2:0] funct3_r        ; // Stimulus: funct3
    reg        funct7_5_r      ; // Stimulus: funct7[5]
    reg        op_5_r          ; // Stimulus: opcode[5]
    wire [2:0] alu_control_w   ; // Output: alu_control

    integer    err_cnt = 0     ; // Error Counter

    // ---------------------------------------------------------
    // 2. DUT Instantiation
    // ---------------------------------------------------------
    alu_decoder u_alu_decoder (
        .alu_op_i      (alu_op_r     ),
        .funct3_i      (funct3_r     ),
        .funct7_5_i    (funct7_5_r   ),
        .op_5_i        (op_5_r       ),
        .alu_control_o (alu_control_w)
    );

    // ---------------------------------------------------------
    // 3. Test Procedure
    // ---------------------------------------------------------
    initial begin
        $display("=========================================================");
        $display("   Starting Comprehensive ALU Decoder Verification");
        $display("=========================================================");

        // --- Case 1: Memory Access / AUIPC (Expect ADD: 000) ---
        check_case(2'b00, 3'b000, 1'b0, 1'b0, 3'b000, "Load/Store/AUIPC");
        check_case(2'b00, 3'b111, 1'b1, 1'b1, 3'b000, "Load/Store Don't Care Check");

        // --- Case 2: Branch (Expect SUB: 001) ---
        check_case(2'b01, 3'b000, 1'b0, 1'b0, 3'b001, "Branch (BEQ/BNE)");
        check_case(2'b01, 3'b010, 1'b1, 1'b1, 3'b001, "Branch Don't Care Check");

        // --- Case 3: ALU Operations (alu_op = 10) ---
        // 3-1. ADD (R-type)
        check_case(2'b10, 3'b000, 1'b0, 1'b1, 3'b000, "R-type ADD");
        // 3-2. SUB (R-type) - Only when OP5=1 and F7_5=1
        check_case(2'b10, 3'b000, 1'b1, 1'b1, 3'b001, "R-type SUB");
        // 3-3. ADDI (I-type) - Even if F7_5 is 1, should be ADD because OP5=0
        check_case(2'b10, 3'b000, 1'b1, 1'b0, 3'b000, "I-type ADDI (F7_5 check)");
        // 3-4. Logical & Comparison
        check_case(2'b10, 3'b010, 1'b0, 1'b1, 3'b101, "SLT (Set Less Than)");
        check_case(2'b10, 3'b110, 1'b0, 1'b1, 3'b011, "OR operation");
        check_case(2'b10, 3'b111, 1'b0, 1'b1, 3'b010, "AND operation");
        // 3-5. Default (Unknown funct3)
        check_case(2'b10, 3'b100, 1'b0, 1'b1, 3'b000, "Default funct3 (XOR/Other)");

        // --- Case 4: LUI (Expect ADD: 000) ---
        check_case(2'b11, 3'b000, 1'b0, 1'b1, 3'b000, "LUI operation");

        // --- Final Report ---
        $display("=========================================================");
        if (err_cnt == 0)
            $display("   SUCCESS: All Decoder Cases Passed!");
        else
            $display("   FAILURE: Found %d mismatches.", err_cnt);
        $display("=========================================================");
        $finish;
    end

    // ---------------------------------------------------------
    // Task: check_case
    // ---------------------------------------------------------
    task check_case;
        input [1:0]  i_alu_op   ;
        input [2:0]  i_f3       ;
        input        i_f7_5     ;
        input        i_op5      ;
        input [2:0]  exp_ctrl   ;
        input [255:0] t_name    ;
        begin
            alu_op_r   = i_alu_op ;
            funct3_r   = i_f3     ;
            funct7_5_r = i_f7_5   ;
            op_5_r     = i_op5    ;
            #10; // Wait for logic

            if (alu_control_w !== exp_ctrl) begin
                $display("[FAIL] %0s | Got:%b Exp:%b", t_name, alu_control_w, exp_ctrl);
                err_cnt = err_cnt + 1;
            end else begin
                $display("[PASS] %0s", t_name);
            end
        end
    endtask

endmodule

결과 확인

결과창

잘 나왔다네요.

이제 Main Decoder와 ALU Decoder를 하위 블록으로 갖는 상위 블록 Control Unit을 만들어봅시다.

사실 그냥 Instantiation하고 wire 연결 정도입니다. ㅎ

 

Control Unit 설계

다른건 그냥 연결인데, 주의할 점은 pc select signal 정도입니다.

jump 신호가 들어오거나, branch 신호 및 Z flag가 들어올 때 다음 PC를 타깃 addr로 바꾸도록 만들어봅시다.

Control Unit 코드

`timescale 1ns / 1ps

module controller (
    input  wire [6:0] op_i             , // Opcode field
    input  wire [2:0] funct3_i         , // funct3 field
    input  wire       funct7_5_i       , // funct7 bit 5 (inst[30])
    input  wire       zero_flag_i      , // Zero flag from ALU
    output wire [2:0] imm_src_o        , // Immediate generator control
    output wire [2:0] alu_control_o    , // Final ALU control signal
    output wire       alu_src_o        , // ALU source B mux select
    output wire       alu_asrc_o       , // ALU source A mux select (AUIPC)
    output wire       reg_write_o      , // Register file write enable
    output wire       mem_write_o      , // Data memory write enable
    output wire [1:0] result_src_o     , // Writeback source select
    output wire       pc_src_o           // Next PC select logic (Branch/Jump)
);

    // ---------------------------------------------------------
    // Internal Wires
    // ---------------------------------------------------------
    wire [1:0] alu_op_w                ;
    wire       branch_w                ;
    wire       jump_w                  ;

    // ---------------------------------------------------------
    // Main Decoder: Generates basic control signals
    // ---------------------------------------------------------
    main_decoder u_main_decoder (
        .op_i         (op_i           ),
        .reg_write_o  (reg_write_o    ),
        .imm_src_o    (imm_src_o      ),
        .alu_src_o    (alu_src_o      ),
        .mem_write_o  (mem_write_o    ),
        .result_src_o (result_src_o   ),
        .branch_o     (branch_w       ),
        .jump_o       (jump_w         ),
        .alu_op_o     (alu_op_w       ),
        .alu_asrc_o   (alu_asrc_o     )
    );

    // ---------------------------------------------------------
    // ALU Decoder: Generates specific ALU control signals
    // ---------------------------------------------------------
    alu_decoder u_alu_decoder (
        .alu_op_i      (alu_op_w      ),
        .funct3_i      (funct3_i      ),
        .funct7_5_i    (funct7_5_i    ),
        .op_5_i        (op_i[5]       ),
        .alu_control_o (alu_control_o )
    );

    // ---------------------------------------------------------
    // PCSrc Logic: Decide whether to branch or jump
    // ---------------------------------------------------------
    assign pc_src_o = jump_w | (branch_w & zero_flag_i) ;

endmodule

 

항상 블로그 읽어주셔서 감사합니다.

다음 포스트에선 PC Counter 및 전체 Processor를 만들어보겠습니다.

 

Single Processor를 완성하면 현재 두가지 옵션이 있는데,

1. Multi, Pipelined 만들고 성능 분석

2. 성능 분석 환경 먼저 구축하고 Multi, Pipelined 설계

입니다.

 

고민좀 해볼게요

반응형