반응형
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 설계
블록 다이어그램

설계 코드
`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 분석을 해보겠습니다~
항상 읽어주셔서 감사합니다.
반응형
'Project > RISC-V CPU Architecture Design' 카테고리의 다른 글
| [10] Single Cycle Processor Datapath 분석 (0) | 2026.01.06 |
|---|---|
| [8] RISC-V Control Unit 설계 (2) - ALU Decoder (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 |