저번 포스트에서 Main Decoder를 만들었습니다.
즉슨, OP Code가 입력되면 Main Decoder가 여러 결정 신호를 보내는데, 그중 하나로 ALUOP 신호가 있습니다.
ALU Decoder는 이 ALUOP 신호를 가이드라인 삼아서, Instruction에 포함된 세부 정보를 입력으로 받아
ALU를 제어하는 신호를 출력합니다.
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 설계
입니다.
고민좀 해볼게요
'Project > RISC-V CPU Architecture Design' 카테고리의 다른 글
| [10] Single Cycle Processor Datapath 분석 (0) | 2026.01.06 |
|---|---|
| [9] PC Unit, Memory 및 Single Cycle Processor 설계 (0) | 2026.01.06 |
| [7] RISC-V Control Unit 설계 (1) - Main Decoder (0) | 2026.01.05 |
| [6] Immediate Generator 설계 (0) | 2026.01.03 |
| [5] ALU 설계 (0) | 2026.01.02 |