Project/RISC-V CPU Architecture Design

[9] PC Unit, Memory 및 Single Cycle Processor 설계

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

PC Counter EZ니까 빠르게 진행할게요.

 

PC를 저장할 수 있는 pc_reg와 32bit +4 가산기 하나 만들겠습니다.

PC Unit 설계

PC Register 코드

`timescale 1ns / 1ps

module pc_reg (
    input  wire        clk_i           , // Clock input
    input  wire        rst_ni          , // Asynchronous reset (Active low)
    input  wire [31:0] pc_next_i       , // Next PC address input
    output reg  [31:0] pc_o              // Current PC address output
);

    always @(posedge clk_i or negedge rst_ni) begin
        if (!rst_ni) begin
            pc_o <= 32'b0              ; // Reset PC to 0
        end
        else begin
            pc_o <= pc_next_i          ; // Update PC at clock edge
        end
    end

endmodule

PC +4

`timescale 1ns / 1ps

module pc_plus4 (
    input  wire [31:0] pc_i            , // Current Program Counter
    output wire [31:0] pc_plus4_o        // Next sequential PC (PC + 4)
);

    // RISC-V instructions are 4 bytes (32-bit) aligned.
    // Sequential next PC is always the current PC plus 4.
    assign pc_plus4_o = pc_i + 32'd4   ;

endmodule

 

Memory 설계

Memory는 2개 설계할 겁니다.

instr_mem과 data_mem입니다. 둘 다 용량은 16kb로 잡았었고,

 

Single_Cycle의 특성상 instr_mem은 Asynchronous Reading을 합니다. 

걱정인 건 역시 리소스 사용량이네요. 이렇게 설계하면 BRAM 리소스를 쓰지 않아서 한번 봐야겠습니다.

Instruction Memory

`timescale 1ns / 1ps

module instr_mem (
    input  wire [31:0] addr_i          , // PC address input
    output wire [31:0] instr_o           // Instruction output
);

    // 16KB Instruction Memory (4096 words x 32-bit)
    reg [31:0] mem_r [0:4095]          ;

    // Load program hex for simulation and implementation
    initial begin
        $readmemh("program.hex", mem_r);
    end

    // Word addressing: 
    // Since each instruction is 4 bytes, we use bits [13:2] to access 4096 words.
    assign instr_o = mem_r[addr_i[13:2]] ;

endmodule

program.hex의 내용에 따라 돌아가는 알고리즘이 달라지겠죠??

성능 분석 할 때 결정하겠습니다.

Data Memory

Data Mem은 BRAM을 이용하겠습니다.

reg_file처럼 initial block을 이용해 초기화해 주었습니다.

`timescale 1ns / 1ps

module data_mem (
    input  wire        clk_i           , // Clock input
    input  wire        mem_write_i     , // Write enable from controller
    input  wire [31:0] addr_i          , // ALU result as memory address
    input  wire [31:0] write_data_i    , // Data to be stored (rs2)
    output wire [31:0] read_data_o       // Data loaded from memory
);

    // 16KB Data Memory (4096 words x 32-bit)
    reg [31:0] mem_r [0:4095]          ;

    integer i;

    // Initialize memory to zero
    initial begin
        for (i = 0; i < 4096; i = i + 1) begin
            mem_r[i] = 32'b0           ;
        end
    end

    // Synchronous Write: Occurs at the rising edge of the clock
    always @(posedge clk_i) begin
        if (mem_write_i) begin
            mem_r[addr_i[13:2]] <= write_data_i ;
        end
    end

    // Asynchronous Read: Provides data immediately for single-cycle paths
    assign read_data_o = mem_r[addr_i[13:2]] ;

endmodule

Single Cycle Processoer 설계

블록 다이어그램

Single Cycle Processor Datapath Block Diagram

설계 코드

`timescale 1ns / 1ps

module single_cycle_processor (
    input  wire        clk_i           ,
    input  wire        rst_ni            // Active-low reset
);

    // ---------------------------------------------------------
    // Internal Wires (Interconnects)
    // ---------------------------------------------------------
    // IF Stage
    wire [31:0] pc_w                   ;
    wire [31:0] pc_next_w              ;
    wire [31:0] pc_plus4_w             ;
    wire [31:0] pc_target_w            ;
    wire [31:0] instr_w                ;

    // ID Stage
    wire [31:0] rd_data1_w             ;
    wire [31:0] rd_data2_w             ;
    wire [31:0] imm_ext_w              ;

    // EX Stage
    wire [31:0] src_a_w                ;
    wire [31:0] src_b_w                ;
    wire [31:0] alu_result_w           ;
    wire        zero_flag_w            ;

    // MEM Stage
    wire [31:0] read_data_w            ;

    // WB Stage
    wire [31:0] result_w               ;

    // Control Signals
    wire [2:0]  imm_src_w              ;
    wire [2:0]  alu_control_w          ;
    wire        alu_src_w              ;
    wire        alu_asrc_w             ;
    wire        reg_write_w            ;
    wire        mem_write_w            ;
    wire [1:0]  result_src_w           ;
    wire        pc_src_w               ;

    // ---------------------------------------------------------
    // 1. IF Stage: Instruction Fetch
    // ---------------------------------------------------------
    pc_reg u_pc_reg (
        .clk_i       (clk_i          ),
        .rst_ni      (rst_ni         ),
        .pc_next_i   (pc_next_w      ),
        .pc_o        (pc_w           )
    );

    instr_mem u_instr_mem (
        .addr_i      (pc_w           ),
        .instr_o     (instr_w        )
    );

    pc_plus4 u_pc_plus4 (
        .pc_i        (pc_w           ),
        .pc_plus4_o  (pc_plus4_w     )
    );

    // PC Source Mux
    assign pc_next_w = (pc_src_w) ? pc_target_w : pc_plus4_w ;

    // ---------------------------------------------------------
    // 2. ID Stage: Instruction Decode & RegFile Read
    // ---------------------------------------------------------
    controller u_controller (
        .op_i         (instr_w[6:0]   ),
        .funct3_i     (instr_w[14:12] ),
        .funct7_5_i   (instr_w[30]    ),
        .zero_flag_i  (zero_flag_w    ),
        .imm_src_o    (imm_src_w      ),
        .alu_control_o(alu_control_w  ),
        .alu_src_o    (alu_src_w      ),
        .alu_asrc_o   (alu_asrc_w     ),
        .reg_write_o  (reg_write_w    ),
        .mem_write_o  (mem_write_w    ),
        .result_src_o (result_src_w   ),
        .pc_src_o     (pc_src_w       )
    );

    register_file u_register_file (
        .clk_i        (clk_i          ),
        .rs1_addr_i   (instr_w[19:15] ),
        .rs2_addr_i   (instr_w[24:20] ),
        .rd_addr_i    (instr_w[11:7]  ),
        .rd_data_i    (result_w       ),
        .reg_wen_i    (reg_write_w    ),
        .rs1_data_o   (rd_data1_w     ),
        .rs2_data_o   (rd_data2_w     )
    );

    imm_gen u_imm_gen (
        .inst_i       (instr_w        ),
        .imm_src_i    (imm_src_w      ),
        .imm_ext_o    (imm_ext_w      )
    );

    // ---------------------------------------------------------
    // 3. EX Stage: Execution & Address Calculation
    // ---------------------------------------------------------
    // ALU Source A Mux (For AUIPC)
    assign src_a_w = (alu_asrc_w) ? pc_w : rd_data1_w ;

    // ALU Source B Mux
    assign src_b_w = (alu_src_w) ? imm_ext_w : rd_data2_w ;

    alu u_alu (
        .src_a_i      (src_a_w        ),
        .src_b_i      (src_b_w        ),
        .alu_op_i     ({1'b0, alu_control_w}), // 4-bit input
        .alu_result_o (alu_result_w   ),
        .zero_flag_o  (zero_flag_w    )
    );

    // Branch/Jump Target Adder
    assign pc_target_w = pc_w + imm_ext_w ;

    // ---------------------------------------------------------
    // 4. MEM Stage: Data Memory Access
    // ---------------------------------------------------------
    data_mem u_data_mem (
        .clk_i        (clk_i          ),
        .mem_write_i  (mem_write_w    ),
        .addr_i       (alu_result_w   ),
        .write_data_i (rd_data2_w     ),
        .read_data_o  (read_data_w    )
    );

    // ---------------------------------------------------------
    // 5. WB Stage: Write Back to Register File
    // ---------------------------------------------------------
    // Result Source Mux
    assign result_w = (result_src_w == 2'b00) ? alu_result_w :
                      (result_src_w == 2'b01) ? read_data_w  :
                      (result_src_w == 2'b10) ? pc_plus4_w   : alu_result_w ;

endmodule`timescale 1ns / 1ps

module SingleCycle_Top (
    input  wire        clk_i           ,
    input  wire        rst_ni            // Active-low reset
);

    // ---------------------------------------------------------
    // Internal Wires (Interconnects)
    // ---------------------------------------------------------
    // IF Stage
    wire [31:0] pc_w                   ;
    wire [31:0] pc_next_w              ;
    wire [31:0] pc_plus4_w             ;
    wire [31:0] pc_target_w            ;
    wire [31:0] instr_w                ;

    // ID Stage
    wire [31:0] rd_data1_w             ;
    wire [31:0] rd_data2_w             ;
    wire [31:0] imm_ext_w              ;

    // EX Stage
    wire [31:0] src_a_w                ;
    wire [31:0] src_b_w                ;
    wire [31:0] alu_result_w           ;
    wire        zero_flag_w            ;

    // MEM Stage
    wire [31:0] read_data_w            ;

    // WB Stage
    wire [31:0] result_w               ;

    // Control Signals
    wire [2:0]  imm_src_w              ;
    wire [2:0]  alu_control_w          ;
    wire        alu_src_w              ;
    wire        alu_asrc_w             ;
    wire        reg_write_w            ;
    wire        mem_write_w            ;
    wire [1:0]  result_src_w           ;
    wire        pc_src_w               ;

    // ---------------------------------------------------------
    // 1. IF Stage: Instruction Fetch
    // ---------------------------------------------------------
    pc_reg u_pc_reg (
        .clk_i       (clk_i          ),
        .rst_ni      (rst_ni         ),
        .pc_next_i   (pc_next_w      ),
        .pc_o        (pc_w           )
    );

    instr_mem u_instr_mem (
        .addr_i      (pc_w           ),
        .instr_o     (instr_w        )
    );

    pc_plus4 u_pc_plus4 (
        .pc_i        (pc_w           ),
        .pc_plus4_o  (pc_plus4_w     )
    );

    // PC Source Mux
    assign pc_next_w = (pc_src_w) ? pc_target_w : pc_plus4_w ;

    // ---------------------------------------------------------
    // 2. ID Stage: Instruction Decode & RegFile Read
    // ---------------------------------------------------------
    controller u_controller (
        .op_i         (instr_w[6:0]   ),
        .funct3_i     (instr_w[14:12] ),
        .funct7_5_i   (instr_w[30]    ),
        .zero_flag_i  (zero_flag_w    ),
        .imm_src_o    (imm_src_w      ),
        .alu_control_o(alu_control_w  ),
        .alu_src_o    (alu_src_w      ),
        .alu_asrc_o   (alu_asrc_w     ),
        .reg_write_o  (reg_write_w    ),
        .mem_write_o  (mem_write_w    ),
        .result_src_o (result_src_w   ),
        .pc_src_o     (pc_src_w       )
    );

    register_file u_register_file (
        .clk_i        (clk_i          ),
        .rs1_addr_i   (instr_w[19:15] ),
        .rs2_addr_i   (instr_w[24:20] ),
        .rd_addr_i    (instr_w[11:7]  ),
        .rd_data_i    (result_w       ),
        .reg_wen_i    (reg_write_w    ),
        .rs1_data_o   (rd_data1_w     ),
        .rs2_data_o   (rd_data2_w     )
    );

    imm_gen u_imm_gen (
        .inst_i       (instr_w        ),
        .imm_src_i    (imm_src_w      ),
        .imm_ext_o    (imm_ext_w      )
    );

    // ---------------------------------------------------------
    // 3. EX Stage: Execution & Address Calculation
    // ---------------------------------------------------------
    // ALU Source A Mux (For AUIPC)
    assign src_a_w = (alu_asrc_w) ? pc_w : rd_data1_w ;

    // ALU Source B Mux
    assign src_b_w = (alu_src_w) ? imm_ext_w : rd_data2_w ;

    alu u_alu (
        .src_a_i      (src_a_w        ),
        .src_b_i      (src_b_w        ),
        .alu_op_i     ({1'b0, alu_control_w}), // 4-bit input
        .alu_result_o (alu_result_w   ),
        .zero_flag_o  (zero_flag_w    )
    );

    // Branch/Jump Target Adder
    assign pc_target_w = pc_w + imm_ext_w ;

    // ---------------------------------------------------------
    // 4. MEM Stage: Data Memory Access
    // ---------------------------------------------------------
    data_mem u_data_mem (
        .clk_i        (clk_i          ),
        .mem_write_i  (mem_write_w    ),
        .addr_i       (alu_result_w   ),
        .write_data_i (rd_data2_w     ),
        .read_data_o  (read_data_w    )
    );

    // ---------------------------------------------------------
    // 5. WB Stage: Write Back to Register File
    // ---------------------------------------------------------
    // Result Source Mux
    assign result_w = (result_src_w == 2'b00) ? alu_result_w :
                      (result_src_w == 2'b01) ? read_data_w  :
                      (result_src_w == 2'b10) ? pc_plus4_w   : alu_result_w ;

endmodule

gemini에게 Top 모듈 묶는 걸 요청하니 위와 같이 Stage 별로 깔끔하게 묶어주네요.

 

다음 포스팅에서는 Datapath 분석을 해보겠습니다~

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

반응형