Project/RISC-V CPU Architecture Design

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

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

Control Unit을 설계해 봅시다.

 

Control Unit은 CPU Architecture의 핵심이라고 생각합니다.

우선 Control Unit의 하위 블록들을 좀 설정해 봅시다.

RISC V의 경우 Main Decoder와 ALU Decoder가 필요합니다.

Control Unit 블록 다이어그램

Control Unit 블록 다이어그램

Main Decoder

Main Decoder는 OP Code를 Decoding 합니다.

자연스럽게 input은 OP Code 7 bits를 받아옵니다.

output은 입력된 OP Code를 Decoding 하여 여러 신호를 출력합니다.

 

우리가 만들 프로세서의 OP Code를 살펴봅시다.

RV32I ISA

ISA를 보면 뭐가 많습니다... 하위 7bit를 눈 빠지게 쳐다보면 결국 아래와 같습니다.

OP Code 종류

  • R-Type
    • OP Code: 0110011
    • ISA: ADD, SUB, SLL, SLT, XOR, SRL, SRA, OR, AND
  • I-Type (ALU)
    • OP Code: 0010011
    • ISA: ADDI, SLTI, XORI, ORI, ANDI 등
  • I-Type (Load)
    • OP Code: 0000011
    • ISA: LB, LH, LW, LBU, LHU
  • S-Type
    • OP Code: 0100011
    • ISA: SB, SH, SW
  • B-Type
    • OP Code: 1100011
    • ISA: BEQ, BNE, BLT, BGE, BLTU, BGEU
  • U-Type (LUI)
    • OP Code: 0110111
    • ISA: LUI
  • U-Type (AUIPC)
    • OP Code: 0010111
    • ISA: AUIPC
  • J-Type (JAL)
    • OP Code: 1101111
    • ISA: JAL
  • J-Type (JALR)
    • OP Code: 1100111
    • ISA: JALR

위 9개의 OP Code를 Decoding 하여 아래의 신호를 조절합니다.

  • ALU의 두 번째 입력 결정
    • Register Data(0) vs ImmGen의 상수(1)
  • 저장할 값이 어디서 왔는지 결정
    • ALU의 결과(0) vs 메모리 리딩값(1)
  • 쓰기 동작 여부 결정
    • 안 쓴다(0) vs 쓴다(1)
  • ALU Decoder에게 알려주는 control signal (2bit)

총 7 bits이며, case 문으로 간단히 구성하면 알아서 Decoder를 만들어주겠지만...

공부하시는 분들은 Main Decoder의 내부 회로 logic gate를 살펴보는 것도 좋습니다.

직접 그리기는 좀 어려우니까 Vivado Implementation 이후 schematic을 까보면 좋겠네요.

 

이러한 logic gate를 이해하기 위해선 앞선 OP Code가 어떻게 구성되는지와 "중복되는 값들이 있는지" 여부로 결정됩니다.

Main Decoder 설계

OP Code 진리표

앞서 말한 대로 부분 부분 쪼개줍니다.

 

gate-level modeling 혹은 Dataflow 모델링을 이용하면 굉장히 오래 걸리겠으나...

우리는 Behavioral Modeling을 통해 쉽게 설계할 수 있습니다. Verilog의 큰 장점이 되겠네요. (Tool 의존도가 크지만 ㅎㅎ)

 

case문을 이용한 코드는 아래와 같습니다.

Main Decoder 코드

`timescale 1ns / 1ps

module main_decoder (
    input  wire [6:0] op_i             , // Opcode input
    output reg        reg_write_o      , // Register write enable
    output reg  [2:0] imm_src_o        , // Immediate source select
    output reg        alu_src_o        , // ALU source B select
    output reg        mem_write_o      , // Memory write enable
    output reg  [1:0] result_src_o     , // Register writeback select
    output reg        branch_o         , // Branch instruction flag
    output reg        jump_o           , // Jump instruction flag
    output reg  [1:0] alu_op_o         , // ALU operation hint
    output reg        alu_asrc_o         // ALU source A select (AUIPC)
);

    always @(*) begin
        // Default values
        reg_write_o  = 1'b0  ; imm_src_o    = 3'b000 ; alu_src_o    = 1'b0  ; mem_write_o = 1'b0  ;
        result_src_o = 2'b00 ; branch_o     = 1'b0  ; jump_o       = 1'b0  ; alu_op_o    = 2'b00 ; 
        alu_asrc_o   = 1'b0  ;

        case (op_i)
            7'b0110011: begin // R-type
                reg_write_o  = 1'b1  ; alu_op_o     = 2'b10 ; 
            end
            7'b0010011: begin // I-type ALU
                reg_write_o  = 1'b1  ; imm_src_o    = 3'b000 ; alu_src_o    = 1'b1  ; alu_op_o    = 2'b10 ;
            end
            7'b0000011: begin // Load
                reg_write_o  = 1'b1  ; imm_src_o    = 3'b000 ; alu_src_o    = 1'b1  ; result_src_o = 2'b01 ;
            end
            7'b0100011: begin // Store
                imm_src_o    = 3'b001 ; alu_src_o    = 1'b1  ; mem_write_o  = 1'b1  ;
            end
            7'b1100011: begin // Branch
                imm_src_o    = 3'b010 ; branch_o     = 1'b1  ; alu_op_o     = 2'b01 ;
            end
            7'b1101111: begin // JAL
                reg_write_o  = 1'b1  ; imm_src_o    = 3'b100 ; jump_o       = 1'b1  ; result_src_o = 2'b10 ;
            end
            7'b1100111: begin // JALR
                reg_write_o  = 1'b1  ; imm_src_o    = 3'b000 ; alu_src_o    = 1'b1  ; jump_o       = 1'b1  ; 
                result_src_o = 2'b10 ;
            end
            7'b0110111: begin // LUI
                reg_write_o  = 1'b1  ; imm_src_o    = 3'b011 ; alu_src_o    = 1'b1  ; alu_op_o     = 2'b11 ;
            end
            7'b0010111: begin // AUIPC
                reg_write_o  = 1'b1  ; imm_src_o    = 3'b011 ; alu_src_o    = 1'b1  ; alu_asrc_o   = 1'b1  ;
            end
            default: ; 
        endcase
    end

endmodule

Test Bench

TB 돌려야겠죠? 제미나이한테 또또 부탁합니다.

`timescale 1ns / 1ps

module tb_main_decoder();
    // ---------------------------------------------------------
    // 1. Signal Declaration
    // ---------------------------------------------------------
    reg  [6:0] op_r             ; // Stimulus reg for Opcode
    wire       reg_write_w      ; // Output wire for RegWrite
    wire [2:0] imm_src_w        ; // Output wire for ImmSrc
    wire       alu_src_w        ; // Output wire for ALUSrc
    wire       mem_write_w      ; // Output wire for MemWrite
    wire [1:0] result_src_w     ; // Output wire for ResultSrc
    wire       branch_w         ; // Output wire for Branch
    wire       jump_w           ; // Output wire for Jump
    wire [1:0] alu_op_w         ; // Output wire for ALUOp
    wire       alu_asrc_w       ; // Output wire for ALU_ASrc

    // ---------------------------------------------------------
    // 2. DUT Instantiation
    // ---------------------------------------------------------
    main_decoder u_main_decoder (
        .op_i         (op_r         ),
        .reg_write_o  (reg_write_w  ),
        .imm_src_o    (imm_src_w    ),
        .alu_src_o    (alu_src_w    ),
        .mem_write_o  (mem_write_w  ),
        .result_src_o (result_src_w ),
        .branch_o     (branch_w     ),
        .jump_o       (jump_w       ),
        .alu_op_o     (alu_op_w     ),
        .alu_asrc_o   (alu_asrc_w   )
    );

    // ---------------------------------------------------------
    // 3. Test Procedure
    // ---------------------------------------------------------
    initial begin
        $display("------------------------------------------------------------");
        $display("Opcode | RW | ImmS | ASrc | MW | ResS | Br | Jp | ALUOp | AASrc");
        $display("------------------------------------------------------------");

        // R-type
        op_r = 7'b0110011; #10;
        $display("R-type | %b  | %b  | %b    | %b  | %b   | %b  | %b  | %b    | %b", 
                  reg_write_w, imm_src_w, alu_src_w, mem_write_w, result_src_w, branch_w, jump_w, alu_op_w, alu_asrc_w);

        // I-ALU
        op_r = 7'b0010011; #10;
        $display("I-ALU  | %b  | %b  | %b    | %b  | %b   | %b  | %b  | %b    | %b", 
                  reg_write_w, imm_src_w, alu_src_w, mem_write_w, result_src_w, branch_w, jump_w, alu_op_w, alu_asrc_w);

        // Load
        op_r = 7'b0000011; #10;
        $display("Load   | %b  | %b  | %b    | %b  | %b   | %b  | %b  | %b    | %b", 
                  reg_write_w, imm_src_w, alu_src_w, mem_write_w, result_src_w, branch_w, jump_w, alu_op_w, alu_asrc_w);

        // Store
        op_r = 7'b0100011; #10;
        $display("Store  | %b  | %b  | %b    | %b  | %b   | %b  | %b  | %b    | %b", 
                  reg_write_w, imm_src_w, alu_src_w, mem_write_w, result_src_w, branch_w, jump_w, alu_op_w, alu_asrc_w);

        // Branch
        op_r = 7'b1100011; #10;
        $display("Branch | %b  | %b  | %b    | %b  | %b   | %b  | %b  | %b    | %b", 
                  reg_write_w, imm_src_w, alu_src_w, mem_write_w, result_src_w, branch_w, jump_w, alu_op_w, alu_asrc_w);

        // JAL
        op_r = 7'b1101111; #10;
        $display("JAL    | %b  | %b  | %b    | %b  | %b   | %b  | %b  | %b    | %b", 
                  reg_write_w, imm_src_w, alu_src_w, mem_write_w, result_src_w, branch_w, jump_w, alu_op_w, alu_asrc_w);

        // JALR
        op_r = 7'b1100111; #10;
        $display("JALR   | %b  | %b  | %b    | %b  | %b   | %b  | %b  | %b    | %b", 
                  reg_write_w, imm_src_w, alu_src_w, mem_write_w, result_src_w, branch_w, jump_w, alu_op_w, alu_asrc_w);

        // LUI
        op_r = 7'b0110111; #10;
        $display("LUI    | %b  | %b  | %b    | %b  | %b   | %b  | %b  | %b    | %b", 
                  reg_write_w, imm_src_w, alu_src_w, mem_write_w, result_src_w, branch_w, jump_w, alu_op_w, alu_asrc_w);

        // AUIPC
        op_r = 7'b0010111; #10;
        $display("AUIPC  | %b  | %b  | %b    | %b  | %b   | %b  | %b  | %b    | %b", 
                  reg_write_w, imm_src_w, alu_src_w, mem_write_w, result_src_w, branch_w, jump_w, alu_op_w, alu_asrc_w);

        $display("------------------------------------------------------------");
        $finish;
    end

endmodule

결과 확인

테스트벤치 결과

짠~ 다음 포스트에서는 ALU Decoder를 설계하고 상위 블록에서 묶어서 Control Unit을 만들도록 하겠습니다.

별로 안 남았네요. 이제 각 부분에서 MUX 좀 끼워주고, PC Counter 설계하고, Memory 연결하면 끝날 것 같습니다.

반응형