1. Title
RISC-V CPU (RV32I)
2. Category
"SystemVerilog", "CPU", "RISC-V"
3. Key Concepts
RISC-V는 오픈소스 명령어 집합 구조(ISA)이기 때문에, 누구나 자유롭게 사용하고 확장할 수 있다. 기존의 상용 ISA(예: x86, ARM)는 라이선스 비용과 접근성의 한계가 있지만, RISC-V는 완전히 공개되어 있어 학습과 연구에 최적화되어 있다.* CPU의 목표 사양
- 명령어 집합: RV32I
- 32비트 기본 명령어 셋 (R - Type, I - Type, S - Type, B - Type, J - Type , U - Type)
- 레지스터 파일: 32개 x 32비트
- CPU 설계 방식
- 싱글사이클(single-cycle) 구조
- 모든 명령어가 단 한 사이클 내에 실행됨
- 파이프라인 미적용
- 메모리 구조
- 데이터 메모리와 명령어 메모리를 분리 (하버드 아키텍처)
- 캐시 미적용 (싱글사이클에서는 큰 장점이 없기 때문)
RISC-V ISA는 기본 명령어 셋을 중심으로 여러 확장 기능을 선택적으로 추가할 수 있도록 설계되어 있다. 그중에서 RV32I는 가장 기본적인 32비트 명령어 셋이며, 여기에 원자적 연산(RV32A), 곱셈 및 나눗셈(RV32M), 압축 명령어(RV32C) 등이 확장될 수 있다.* RV32I (Base Integer Instruction Set, 기본 정수 연산 명령어 셋)
- 32개의 32비트 레지스터 (x0~x31)
- x0 레지스터는 항상 0으로 고정되어 있음.
- x1~x31은 범용 레지스터로 사용됨.
- Load/Store 아키텍처
- 메모리에 접근하는 명령어는 Load/Store만 존재함.
- 연산은 모두 레지스터에서 수행되며, 메모리에서 직접 연산하는 방식(예: CISC의 x86 방식)은 없음.
- 고정된 32비트 명령어 포맷
- 명령어는 R-type, I-type, S-type, B-type, U-type, J-type으로 구분됨.
- 예제:
- ADD x1, x2, x3 → x1 = x2 + x3 (R-type)
- LW x1, 0(x2) → x1 = *(x2 + 0) (I-type)
- BEQ x1, x2, label → x1 == x2이면 label로 분기 (B-type)
- 지원하는 주요 명령어
- 산술 연산: ADD, SUB, AND, OR, XOR, SLT (Set Less Than) 등
- 메모리 접근: LW (Load Word), SW (Store Word)
- 분기 및 점프: BEQ, BNE, JAL, JALR
- 즉시값 연산: ADDI, ANDI, ORI, XORI
4. BLOCK DIAGRAM / FSM
** Length_Sel Module 및 JALR 컨트롤 신호 Revision 되었으니 참고
5. Code Review
`timescale 1ns / 1ps
module RV32I_Core (
input logic clk,
input logic reset,
// Instruction Memory Interface (ROM)
input logic [31:0] instr_data,
output logic [31:0] instr_addr,
// Data Memory Interface (RAM)
output logic data_mem_we,
output logic [31:0] data_mem_addr,
output logic [31:0] data_mem_wdata,
input logic [31:0] data_mem_rdata
);
// Control Signals
logic reg_file_we;
logic [3:0] alu_ctrl;
logic alu_src_sel;
logic wb_mux_sel;
logic branch_en;
logic jump_sel;
logic jal_sel;
ControlUnit U_ControlUnit (.*);
DataPath U_DataPath (.*);
endmodule
`timescale 1ns/1ps
`include "../../include/defines.sv"
module ControlUnit (
input logic [31:0] instr_data,
output logic reg_file_we,
output logic [ 3:0] alu_ctrl,
output logic alu_src_sel,
output logic wb_mux_sel,
output logic data_mem_we,
output logic branch_en,
output logic jump_sel,
output logic jal_sel
);
wire [6:0] opcode = instr_data[6:0];
wire [2:0] func3 = instr_data[14:12];
wire [6:0] func7 = instr_data[31:25];
logic [6:0] controls;
assign {reg_file_we, alu_src_sel, data_mem_we, wb_mux_sel, branch_en, jump_sel, jal_sel} = controls;
// Control signals based on opcode
always_comb begin
controls = 7'b0000000;
case (opcode)
`OP_TYPE_R : controls = 7'b1_0_0_1_0_0_0; // R-Type
`OP_TYPE_L : controls = 7'b1_1_0_0_0_0_0; // L-Type
`OP_TYPE_I : controls = 7'b1_1_0_1_0_0_0; // I-Type
`OP_TYPE_S : controls = 7'b0_1_1_1_0_0_0; // S-Type
`OP_TYPE_B : controls = 7'b0_0_0_1_1_0_0; // B-Type
`OP_TYPE_LU : controls = 7'b1_1_0_1_0_0_0; // LU-Type
`OP_TYPE_AU : controls = 7'b1_1_0_1_0_0_0; // AU_Type
`OP_TYPE_J : controls = 7'b1_0_0_0_1_1_0; // J-Type
`OP_TYPE_JL : controls = 7'b1_0_0_0_1_1_1; // JL-Type
default: controls = 7'bx; // Undefined opcode
endcase
end
// ALU control logic based on opcode and func3/func7
always_comb begin
alu_ctrl = 3'b000;
case (opcode)
`OP_TYPE_R : alu_ctrl = {func7[5], func3}; // R-Type
`OP_TYPE_L : alu_ctrl = `ADD; // L-Type
`OP_TYPE_I : begin
if ({func7[5], func3} == 4'b1101)
alu_ctrl = `SRA;
else
alu_ctrl = {1'b0, func3}; // I-Type
end
`OP_TYPE_S : alu_ctrl = `ADD; // S-Type
`OP_TYPE_B : alu_ctrl = {func7[5], func3}; // B-Type
`OP_TYPE_LU : alu_ctrl = `LUI; // LU-Type
`OP_TYPE_AU : alu_ctrl = `AUIPC; // AU_Type
`OP_TYPE_J : alu_ctrl = {func7[5], func3}; // J-Type
`OP_TYPE_JL : alu_ctrl = {func7[5], func3}; // JL-Type
default: alu_ctrl = 4'bx; // Undefined opcode
endcase
end
endmodule
`timescale 1ns/1ps
module DataPath (
input logic clk,
input logic reset,
input logic [31:0] instr_data,
output logic [31:0] instr_addr,
input logic reg_file_we,
input logic [ 3:0] alu_ctrl,
input logic alu_src_sel,
input logic wb_mux_sel,
input logic branch_en,
input logic jump_sel,
input logic jal_sel,
output logic [31:0] data_mem_addr,
output logic [31:0] data_mem_wdata,
input logic [31:0] data_mem_rdata
);
logic [31:0] PC_Data, RegFileData1, RegFileData2, aluResult, immOut, aluSrcMuxOut;
logic [31:0] wrMuxOut, branch_enMuxOut, jalMuxOut;
logic [31:0] data_mem_rdata_processed, J_PC_Data;
logic branch_enMuxSel, branch_flag;
wire [2:0] func3 = instr_data[14:12];
assign data_mem_addr = aluResult;
assign branch_enMuxSel = (branch_flag & branch_en || (instr_data[6] && instr_data[2])); // B-Type, J-Type
// Program Counter
register U_PC (
.clk(clk),
.reset(reset),
.d(PC_Data),
.q(instr_addr)
);
// Immediate Extension
extend U_Extend (
.instr_data(instr_data),
.immExt(immOut)
);
// Register File
RegisterFile U_RegFile (
.clk(clk),
.we(reg_file_we),
.RAddr1(instr_data[19:15]),
.RAddr2(instr_data[24:20]),
.WAddr(instr_data[11:7]),
.WData(wrMuxOut),
.RData1(RegFileData1),
.RData2(RegFileData2)
);
// ALU Source Multiplexer
mux_2x1 U_AluSrcMux (
.sel(alu_src_sel),
.x0 (RegFileData2),
.x1 (immOut),
.y (aluSrcMuxOut)
);
// ALU
alu U_ALU (
.alu_ctrl(alu_ctrl),
.a(RegFileData1),
.b(aluSrcMuxOut),
.pc_input(instr_addr),
.branch_flag(branch_flag),
.result(aluResult)
);
// Data Memory Read Processing
LoadDataProcessor U_LoadDataProcessor (
.func3(func3),
.data_mem_rdata(data_mem_rdata),
.processed_data(data_mem_rdata_processed)
);
// Branch & Jump Logic
mux_2x1 U_branch_en_Mux (
.sel(branch_enMuxSel),
.x0 (32'd4),
.x1 (immOut),
.y (branch_enMuxOut)
);
mux_2x1 U_JAL_Mux (
.sel(jal_sel),
.x0 (instr_addr),
.x1 (RegFileData1),
.y (jalMuxOut)
);
adder U_Adder_PC (
.a(jalMuxOut),
.b(branch_enMuxOut),
.y(PC_Data)
);
adder U_J_Adder_PC(
.a(instr_addr),
.b(32'd4),
.y(J_PC_Data)
);
// Writeback MUX
mux_3x1 U_wrMux (
.sel({jump_sel, wb_mux_sel}),
.x0 (data_mem_rdata_processed),
.x1 (aluResult),
.x2 (J_PC_Data),
.y (wrMuxOut)
);
// Data Memory Write Processing
length_sel U_LENGTH_SEL(
.data(RegFileData2),
.ram_data(data_mem_rdata_processed),
.func3(func3),
.size_data(data_mem_wdata)
);
endmodule
6. Testing and Debugging
* Testing Tools: VIVADO, Modelsim, COMPORTMASTER
module tb_MCU(
);
logic clk, reset;
always #5 clk = ~clk;
MCU DUT(
.clk(clk), .reset(reset)
);
initial begin
clk = 0;
reset = 1;
#5 reset = 0;
end
endmodule
Portfolio_Verilog_RISC-V_CPU(RV32I).pdf
2.49MB
sources_1.zip
0.01MB
'PROJECTS > CPU 설계' 카테고리의 다른 글
CPU Course Project - AXI4-Lite & SoC Project (0) | 2025.03.17 |
---|---|
CPU Course Project - RISC-V / AMBA APB (0) | 2025.03.04 |
CPU Course Project - RISC-V (Multi Cycle) (0) | 2025.02.21 |
CPU Course Project - Stop Watch Controlled by UART REVISION (0) | 2025.02.09 |