Home > Final > 2025-08-14

2025-08-14
Study System Verilog

RISC-V Design


RISC-V (리스크 파이브)

2010년부터 미국 UC 버클리에서 개발 중인 무료 오픈 소스 RISC 명령어셋 아키텍처

  • Base: RV32I
  • Version: 2.0

1. Single-Cycle Architecture

  • 모든 명령어가 1 clock에 동작
  • 가장 시간이 오래 걸리는 명령어를 기준으로 클럭 주기를 설계해야 함
  • 장점: 구조가 매우 단순함
  • 단점: 전체 실행 속도가 느림

2. Multi-Cycle Architecture

  • 명령어 유형(type)에 따라 필요한 clock 수가 다름
  • 명령어에 따라 실행 시간을 단축할 수 있음
  • 장점: Single-Cycle보다 빠름
  • 단점: 구조가 다소 복잡함

구현 목표

① Single-Cycle로 먼저 구현 및 이해
② Multi-Cycle로 전환 + ARMA BUS + 주변장치(Peripherals) 연동

3. Pipeline Architecture (현재 구현 X)

  • 장점: Single-Cycle보다 훨씬 빠름
  • 단점: 구조가 매우 복잡함

alt text

  • x0 → zero : 항상 0 값을 가지는 레지스터
  • x1 → ra (Return Address) : 함수 호출 후 복귀 주소 저장
  • x2 → sp (Stack Pointer) : 스택의 최상단 주소 저장
  • x3 → gp (Global Pointer) : 전역 변수 접근용 포인터
  • … (이후 x31까지 각자 용도 지정)
    → 어셈블리어에서는 x0, x1 대신 zero, ra, sp, gp 등의 별칭(alias)으로 표기됨

CPU 기본 모듈 (하버드 구조)

  • Register File
    → CPU 내부 레지스터 집합, 연산 및 데이터 임시 저장
  • ALU (Arithmetic Logic Unit)
    → 산술 및 논리 연산 수행
  • ROM / Flash (Instruction Memory)
    → 프로그램 명령어 저장
    ※ ROM은 한 번 쓰면 지울 수 없으므로, 대부분 Flash 메모리(비휘발성) 사용
  • RAM (Data Memory)
    → 프로그램 실행 중 데이터 임시 저장 (휘발성)
  • PC (Program Counter)
    → 현재 실행 중인 명령어의 주소를 가리키는 레지스터

alt text

Instruction, CPU, RAM 까지 Single-Cycle 구현
Peri는 Multi-Cycle 이후 구현

Block Diagram

alt text

Signal 이해

alt text

  • Register Source 1 (5) : 5bit RS1
  • Register Source 2 (5) : 5bit RS2
  • Register Destination (5) : 5bit RD
  • Opcode (7) : 7bit type 구분
  • Funct7 (7) : 7bit 연산 구분
  • Funct3 (3) : 3bit 연산 구분

alt text

ADD : rd(WAddr) = rs1(RA1) + rs2(RA2) / add : x7(rd), x5(rs1), x6(rs2) = x7 = x5(Data) + x6(Data)

alt text

< Data Path >

alt text

< Code : Data Path >

`timescale 1ns / 1ps

module DataPath (
    input  logic        clk,
    input  logic        reset,
    input  logic [31:0] instrCode,
    input  logic        regFileWe,
    input  logic [ 1:0] aluControl,
    output logic [31:0] instrMemAddr
);

    logic [31:0] aluResult, RFData1, RFData2;
    logic [31:0] PCSrcData, PCOutData;

    assign instrMemAddr = PCOutData;

    RegisterFile U_RegFile (
        .clk(clk),
        .we (regFileWe),
        .RA1(instrCode[19:15]),
        .RA2(instrCode[24:20]),
        .WA (instrCode[11:7]),
        .WD (aluResult),
        .RD1(RFData1),
        .RD2(RFData2)
    );

    alu U_ALU (
        .aluControl(aluControl),
        .a         (RFData1),
        .b         (RFData2),
        .result    (aluResult)
    );

    register U_PC (
        .clk  (clk),
        .reset(reset),
        .en   (1'b1),
        .d    (PCSrcData),
        .q    (PCOutData)
    );

    adder U_PC_Adder (
        .a(32'd4),
        .b(PCOutData),
        .y(PCSrcData)
    );

endmodule


module alu (
    input  logic [ 1:0] aluControl,
    input  logic [31:0] a,
    input  logic [31:0] b,
    output logic [31:0] result
);

    always_comb begin
        result = a + b;
        case (aluControl)
            2'b00: result = a + b;
            2'b01: result = a - b;
            2'b10: result = a & b;
            2'b11: result = a | b;
        endcase
    end

endmodule


module RegisterFile (
    input  logic        clk,
    input  logic        we,
    input  logic [ 4:0] RA1,
    input  logic [ 4:0] RA2,
    input  logic [ 4:0] WA,
    input  logic [31:0] WD,
    output logic [31:0] RD1,
    output logic [31:0] RD2
);

    logic [31:0] mem[0:2**5-1];

    always_ff @(posedge clk) begin
        if (we) mem[WA] <= WD;
    end

    assign RD1 = (RA1 != 0) ? mem[RA1] : 32'b0;
    assign RD2 = (RA2 != 0) ? mem[RA2] : 32'b0;

endmodule


module register (
    input  logic        clk,
    input  logic        reset,
    input  logic        en,
    input  logic [31:0] d,
    output logic [31:0] q
);

    always_ff @(posedge clk, posedge reset) begin
        if (reset) begin
            q <= 0;
        end else begin
            if (en) q <= d;
        end
    end

endmodule


module adder (
    input  logic [31:0] a,
    input  logic [31:0] b,
    output logic [31:0] y
);

    assign y = a + b;

endmodule

< Schematic : DataPath >

alt text

< Code : Control Unit >

`timescale 1ns / 1ps

module ControlUnit (
    input  logic [31:0] instrCode,
    output logic        regFileWe,
    output logic [ 1:0] aluControl
);

    // logic 은 선언하면서 연결이 안되기 때문에 wire 사용 (logic 사용시 assign 필요)
    wire [6:0] opcode = instrCode[6:0];
    wire [3:0] operator = {instrCode[30], instrCode[14:12]};

    always_comb begin
        regFileWe = 1'b0;
        case (opcode)
            7'b0110011: regFileWe = 1'b1; 
        endcase
    end

    always_comb begin
        aluControl = 2'bx;
        case (opcode)
            7'b0110011: begin // R-Type
                aluControl = 2'bx;
                case (operator)
                    4'b0000: aluControl = 2'b00; // add
                    4'b1000: aluControl = 2'b01; // sub
                    4'b0111: aluControl = 2'b10; // and 
                    4'b0110: aluControl = 2'b11; // or
                endcase
            end 
        endcase
    end

endmodule

< Code : CPU_RV32I >

`timescale 1ns / 1ps

module CPU_RV32I (
    input  logic        clk,
    input  logic        reset,
    input  logic [31:0] instrCode,
    output logic [31:0] instrMemAddr
);

    logic       regFileWe;
    logic [1:0] aluControl;

    ControlUnit U_ControlUnit (.*);
    DataPath U_DataPath (.*);

endmodule

< Schematic : CPU_RV32I >

alt text

< Code : ROM >

alt text

`timescale 1ns / 1ps

module ROM (
    input  logic [31:0] addr,
    output logic [31:0] data
);

    logic [31:0] rom[0:61];

    initial begin
        //rom[x]=32'b func7 _ rs2 _ rs1 _f3 _ rd  _  op
        rom[0] = 32'b0000000_00001_00010_000_00100_0110011;  // add x4, x2, x1
        rom[1] = 32'b0100000_00001_00010_000_00101_0110011;  // sub x5, x2, x1
        rom[2] = 32'b0000000_00000_00011_111_00110_0110011;  // and x6, x3, x0
        rom[3] = 32'b0000000_00000_00011_110_00111_0110011;  // or  x7, x3, x0
    end

    // 하위 2bit 를 없애면 4(2^2)의 배수로 표현이 됨, 4byte 단위로 이동 가능
    assign data = rom[addr[31:2]];

endmodule

< Code : MCU >

`timescale 1ns / 1ps

module MCU (
    input logic clk,
    input logic reset
);

    logic [31:0] instrCode;
    logic [31:0] instrMemAddr;

    ROM U_ROM (
        .addr(instrMemAddr),
        .data(instrCode)
    );

    CPU_RV32I U_CPU_RV32I (.*);

endmodule

< Schematic : MCU >

alt text

< Simulation >

alt text
alt text

< 파일 >

sources (Class)

sim (Class)

HW


< Design Specification >

alt text

  • RISC-V RV32I R-Type 명령어 설계

< Code : DataPath (alu) >

//...
module alu (
    input  logic [ 3:0] aluControl,
    input  logic [31:0] a,
    input  logic [31:0] b,
    output logic [31:0] result
);

    always_comb begin
        result = 32'b0;
        case (aluControl)
            4'b0000: result = a + b;
            4'b0001: result = a - b;
            4'b0010: result = a << b;
            4'b0011: result = a >> b;
            4'b0100: result = a >>> b;
            4'b0101: result = ($signed(a) < $signed(b)) ? 1 : 0;
            4'b0110: result = (a < b) ? 1 : 0;
            4'b0111: result = a ^ b;
            4'b1000: result = a | b;
            4'b1001: result = a & b;
        endcase
    end

endmodule
//...

< Code : ControlUnit >

`timescale 1ns / 1ps

module ControlUnit (
    input  logic [31:0] instrCode,
    output logic        regFileWe,
    output logic [ 3:0] aluControl
);

    // logic 은 선언하면서 연결이 안되기 때문에 wire 사용 (logic 사용시 assign 필요)
    wire [6:0] opcode = instrCode[6:0];
    wire [3:0] operator = {instrCode[30], instrCode[14:12]};

    always_comb begin
        regFileWe = 1'b0;
        case (opcode)
            7'b0110011: regFileWe = 1'b1; 
        endcase
    end

    always_comb begin
        aluControl = 4'bx;
        case (opcode)
            7'b0110011: begin // R-Type
                aluControl = 4'bx;
                case (operator)
                    4'b0000: aluControl = 4'b0000; // ADD
                    4'b1000: aluControl = 4'b0001; // SUB
                    4'b0001: aluControl = 4'b0010; // SLL
                    4'b0101: aluControl = 4'b0011; // SRL
                    4'b1101: aluControl = 4'b0100; // SRA
                    4'b0010: aluControl = 4'b0101; // SLT
                    4'b0011: aluControl = 4'b0110; // SLTU
                    4'b0100: aluControl = 4'b0111; // XOR
                    4'b0110: aluControl = 4'b1000; // OR
                    4'b0111: aluControl = 4'b1001; // AND
                endcase
            end 
        endcase
    end

endmodule

< Code : ROM >

`timescale 1ns / 1ps

module ROM (
    input  logic [31:0] addr,
    output logic [31:0] data
);

    logic [31:0] rom[0:61];

    initial begin
        //rom[x]=32'b func7 _ rs2 _ rs1 _f3 _ rd  _  op
        rom[0] = 32'b0000000_00010_00001_000_00011_0110011;  // add  x3, x1, x2
        rom[1] = 32'b0100000_00010_00001_000_00011_0110011;  // sub  x3, x1, x2
        rom[2] = 32'b0000000_00010_00001_001_00011_0110011;  // sll  x3, x1, x2
        rom[3] = 32'b0000000_00010_00001_101_00011_0110011;  // srl  x3, x1, x2
        rom[4] = 32'b0100000_00010_00001_101_00011_0110011;  // sra  x3, x1, x2
        rom[5] = 32'b0000000_00010_00001_010_00011_0110011;  // slt  x3, x1, x2
        rom[6] = 32'b0000000_00010_00001_011_00011_0110011;  // sltu x3, x1, x2
        rom[7] = 32'b0000000_00010_00001_100_00011_0110011;  // xor  x3, x1, x2
        rom[8] = 32'b0000000_00010_00001_110_00011_0110011;  // or   x3, x1, x2
        rom[9] = 32'b0000000_00010_00001_111_00011_0110011;  // and  x3, x1, x2
    end


    // 하위 2bit 를 없애면 4(2^2)의 배수로 표현이 됨, 4byte 단위로 이동 가능
    assign data = rom[addr[31:2]];

endmodule

< Simulation >

alt text

< 파일 >

sources (HW)

sim (HW)