From 1410d3309ae9557ab102942bbc4d8449f2206fe5 Mon Sep 17 00:00:00 2001 From: Richard Qin Date: Sat, 7 Nov 2026 03:10:08 +0800 Subject: refactor: reorganize RTL directory structure --- rv64cpu/fpga/gowin/.gitkeep | 0 rv64cpu/fpga/xilinx/.gitkeep | 0 rv64cpu/rtl/bus/.gitkeep | 0 rv64cpu/rtl/cache/.gitkeep | 0 rv64cpu/rtl/core/.gitkeep | 0 rv64cpu/rtl/core/alu.sv | 123 --- rv64cpu/rtl/core/branch_unit.sv | 52 - rv64cpu/rtl/core/compressed_decoder.sv | 234 ----- rv64cpu/rtl/core/csr_unit.sv | 1221 ------------------------ rv64cpu/rtl/core/debug/.gitkeep | 0 rv64cpu/rtl/core/decoder.sv | 672 ------------- rv64cpu/rtl/core/ex_mem_reg.sv | 281 ------ rv64cpu/rtl/core/ex_stage.sv | 120 --- rv64cpu/rtl/core/execute/alu.sv | 123 +++ rv64cpu/rtl/core/execute/branch_unit.sv | 52 + rv64cpu/rtl/core/execute/compressed_decoder.sv | 234 +++++ rv64cpu/rtl/core/execute/decoder.sv | 672 +++++++++++++ rv64cpu/rtl/core/execute/hazard_unit.sv | 200 ++++ rv64cpu/rtl/core/fp_regfile.sv | 41 - rv64cpu/rtl/core/fpu/.gitkeep | 0 rv64cpu/rtl/core/fpu/include/.gitkeep | 0 rv64cpu/rtl/core/hazard_unit.sv | 200 ---- rv64cpu/rtl/core/id_ex_reg.sv | 287 ------ rv64cpu/rtl/core/id_stage.sv | 197 ---- rv64cpu/rtl/core/if_id_reg.sv | 72 -- rv64cpu/rtl/core/if_stage.sv | 64 -- rv64cpu/rtl/core/include/.gitkeep | 0 rv64cpu/rtl/core/include/csr_pkg.sv | 232 ----- rv64cpu/rtl/core/include/rv64_pkg.sv | 162 ---- rv64cpu/rtl/core/mem_stage.sv | 430 --------- rv64cpu/rtl/core/mem_wb_reg.sv | 188 ---- rv64cpu/rtl/core/mmu/.gitkeep | 0 rv64cpu/rtl/core/pipeline/ex_mem_reg.sv | 281 ++++++ rv64cpu/rtl/core/pipeline/ex_stage.sv | 120 +++ rv64cpu/rtl/core/pipeline/id_ex_reg.sv | 287 ++++++ rv64cpu/rtl/core/pipeline/id_stage.sv | 197 ++++ rv64cpu/rtl/core/pipeline/if_id_reg.sv | 72 ++ rv64cpu/rtl/core/pipeline/if_stage.sv | 64 ++ rv64cpu/rtl/core/pipeline/mem_stage.sv | 430 +++++++++ rv64cpu/rtl/core/pipeline/mem_wb_reg.sv | 188 ++++ rv64cpu/rtl/core/pipeline/wb_stage.sv | 168 ++++ rv64cpu/rtl/core/pmp_checker.sv | 181 ---- rv64cpu/rtl/core/privileged/csr_unit.sv | 1221 ++++++++++++++++++++++++ rv64cpu/rtl/core/privileged/pmp_checker.sv | 181 ++++ rv64cpu/rtl/core/privileged/trap_ctrl.sv | 279 ++++++ rv64cpu/rtl/core/regfile.sv | 67 -- rv64cpu/rtl/core/registers/fp_regfile.sv | 41 + rv64cpu/rtl/core/registers/regfile.sv | 67 ++ rv64cpu/rtl/core/trap_ctrl.sv | 279 ------ rv64cpu/rtl/core/wb_stage.sv | 168 ---- rv64cpu/rtl/packages/csr_pkg.sv | 232 +++++ rv64cpu/rtl/packages/rv64_pkg.sv | 162 ++++ rv64cpu/rtl/perips/.gitkeep | 0 rv64cpu/rtl/soc/.gitkeep | 0 rv64cpu/scripts/.gitkeep | 0 rv64cpu/sw/baremetal/cache_test/.gitkeep | 0 rv64cpu/sw/baremetal/hello_uart/.gitkeep | 0 rv64cpu/sw/baremetal/timer_irq/.gitkeep | 0 rv64cpu/sw/common/.gitkeep | 0 rv64cpu/tb/common/.gitkeep | 0 rv64cpu/tb/compliance/.gitkeep | 0 rv64cpu/tb/core/.gitkeep | 0 rv64cpu/tb/soc/.gitkeep | 0 rv64cpu/tb/unit/.gitkeep | 0 rv64cpu/waves/.gitkeep | 0 65 files changed, 5271 insertions(+), 5271 deletions(-) delete mode 100644 rv64cpu/fpga/gowin/.gitkeep delete mode 100644 rv64cpu/fpga/xilinx/.gitkeep delete mode 100644 rv64cpu/rtl/bus/.gitkeep delete mode 100644 rv64cpu/rtl/cache/.gitkeep delete mode 100644 rv64cpu/rtl/core/.gitkeep delete mode 100644 rv64cpu/rtl/core/alu.sv delete mode 100644 rv64cpu/rtl/core/branch_unit.sv delete mode 100644 rv64cpu/rtl/core/compressed_decoder.sv delete mode 100644 rv64cpu/rtl/core/csr_unit.sv delete mode 100644 rv64cpu/rtl/core/debug/.gitkeep delete mode 100644 rv64cpu/rtl/core/decoder.sv delete mode 100644 rv64cpu/rtl/core/ex_mem_reg.sv delete mode 100644 rv64cpu/rtl/core/ex_stage.sv create mode 100644 rv64cpu/rtl/core/execute/alu.sv create mode 100644 rv64cpu/rtl/core/execute/branch_unit.sv create mode 100644 rv64cpu/rtl/core/execute/compressed_decoder.sv create mode 100644 rv64cpu/rtl/core/execute/decoder.sv create mode 100644 rv64cpu/rtl/core/execute/hazard_unit.sv delete mode 100644 rv64cpu/rtl/core/fp_regfile.sv delete mode 100644 rv64cpu/rtl/core/fpu/.gitkeep delete mode 100644 rv64cpu/rtl/core/fpu/include/.gitkeep delete mode 100644 rv64cpu/rtl/core/hazard_unit.sv delete mode 100644 rv64cpu/rtl/core/id_ex_reg.sv delete mode 100644 rv64cpu/rtl/core/id_stage.sv delete mode 100644 rv64cpu/rtl/core/if_id_reg.sv delete mode 100644 rv64cpu/rtl/core/if_stage.sv delete mode 100644 rv64cpu/rtl/core/include/.gitkeep delete mode 100644 rv64cpu/rtl/core/include/csr_pkg.sv delete mode 100644 rv64cpu/rtl/core/include/rv64_pkg.sv delete mode 100644 rv64cpu/rtl/core/mem_stage.sv delete mode 100644 rv64cpu/rtl/core/mem_wb_reg.sv delete mode 100644 rv64cpu/rtl/core/mmu/.gitkeep create mode 100644 rv64cpu/rtl/core/pipeline/ex_mem_reg.sv create mode 100644 rv64cpu/rtl/core/pipeline/ex_stage.sv create mode 100644 rv64cpu/rtl/core/pipeline/id_ex_reg.sv create mode 100644 rv64cpu/rtl/core/pipeline/id_stage.sv create mode 100644 rv64cpu/rtl/core/pipeline/if_id_reg.sv create mode 100644 rv64cpu/rtl/core/pipeline/if_stage.sv create mode 100644 rv64cpu/rtl/core/pipeline/mem_stage.sv create mode 100644 rv64cpu/rtl/core/pipeline/mem_wb_reg.sv create mode 100644 rv64cpu/rtl/core/pipeline/wb_stage.sv delete mode 100644 rv64cpu/rtl/core/pmp_checker.sv create mode 100644 rv64cpu/rtl/core/privileged/csr_unit.sv create mode 100644 rv64cpu/rtl/core/privileged/pmp_checker.sv create mode 100644 rv64cpu/rtl/core/privileged/trap_ctrl.sv delete mode 100644 rv64cpu/rtl/core/regfile.sv create mode 100644 rv64cpu/rtl/core/registers/fp_regfile.sv create mode 100644 rv64cpu/rtl/core/registers/regfile.sv delete mode 100644 rv64cpu/rtl/core/trap_ctrl.sv delete mode 100644 rv64cpu/rtl/core/wb_stage.sv create mode 100644 rv64cpu/rtl/packages/csr_pkg.sv create mode 100644 rv64cpu/rtl/packages/rv64_pkg.sv delete mode 100644 rv64cpu/rtl/perips/.gitkeep delete mode 100644 rv64cpu/rtl/soc/.gitkeep delete mode 100644 rv64cpu/scripts/.gitkeep delete mode 100644 rv64cpu/sw/baremetal/cache_test/.gitkeep delete mode 100644 rv64cpu/sw/baremetal/hello_uart/.gitkeep delete mode 100644 rv64cpu/sw/baremetal/timer_irq/.gitkeep delete mode 100644 rv64cpu/sw/common/.gitkeep delete mode 100644 rv64cpu/tb/common/.gitkeep delete mode 100644 rv64cpu/tb/compliance/.gitkeep delete mode 100644 rv64cpu/tb/core/.gitkeep delete mode 100644 rv64cpu/tb/soc/.gitkeep delete mode 100644 rv64cpu/tb/unit/.gitkeep delete mode 100644 rv64cpu/waves/.gitkeep (limited to 'rv64cpu') diff --git a/rv64cpu/fpga/gowin/.gitkeep b/rv64cpu/fpga/gowin/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/fpga/xilinx/.gitkeep b/rv64cpu/fpga/xilinx/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/rtl/bus/.gitkeep b/rv64cpu/rtl/bus/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/rtl/cache/.gitkeep b/rv64cpu/rtl/cache/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/rtl/core/.gitkeep b/rv64cpu/rtl/core/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/rtl/core/alu.sv b/rv64cpu/rtl/core/alu.sv deleted file mode 100644 index 08635ce..0000000 --- a/rv64cpu/rtl/core/alu.sv +++ /dev/null @@ -1,123 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/alu.sv -// Description: 64-bit Arithmetic Logic Unit (ALU) - -module alu - import rv64_pkg::*; -( - input alu_op_t alu_op, // ALU operation select (from decoder) - input logic [XLEN-1:0] operand_a, // Operand 1 (register rs1 value or PC) - input logic [XLEN-1:0] operand_b, // Operand 2 (register rs2 value or immediate) - - output logic [XLEN-1:0] alu_result // ALU result -); - - // Shift amount: RV64I uses lower 6 bits of rs2 (shamt[5:0]) - logic [5:0] shamt; - assign shamt = operand_b[5:0]; - - logic signed [XLEN-1:0] operand_a_signed; - logic signed [XLEN-1:0] operand_b_signed; - logic signed [127:0] mul_signed_signed; - logic signed [127:0] mul_signed_unsigned; - logic [127:0] mul_unsigned_unsigned; - - assign operand_a_signed = operand_a; - assign operand_b_signed = operand_b; - assign mul_signed_signed = operand_a_signed * operand_b_signed; - assign mul_signed_unsigned = operand_a_signed * $signed({1'b0, operand_b}); - assign mul_unsigned_unsigned = operand_a * operand_b; - - always_comb begin - alu_result = '0; - - case (alu_op) - ALU_ADD: alu_result = operand_a + operand_b; - ALU_SUB: alu_result = operand_a - operand_b; - - // Logical left shift: the shift amount is the lower 6 bits of rs2 - ALU_SLL: alu_result = operand_a << shamt; - - // Set less than (signed) - ALU_SLT: alu_result = {63'b0, $signed(operand_a) < $signed(operand_b)}; - - // Set less than (unsigned) - ALU_SLTU: alu_result = {63'b0, operand_a < operand_b}; - - ALU_XOR: alu_result = operand_a ^ operand_b; - - // Logical right shift: the shift amount is the lower 6 bits of rs2 - ALU_SRL: alu_result = operand_a >> shamt; - - // Arithmetic right shift: the shift amount is the lower 6 bits of rs2 - ALU_SRA: alu_result = $unsigned($signed(operand_a) >>> shamt); - - ALU_OR: alu_result = operand_a | operand_b; - ALU_AND: alu_result = operand_a & operand_b; - - ALU_MUL: begin - alu_result = mul_signed_signed[XLEN-1:0]; - end - - ALU_MULH: begin - alu_result = mul_signed_signed[127:64]; - end - - ALU_MULHSU: begin - alu_result = mul_signed_unsigned[127:64]; - end - - ALU_MULHU: begin - alu_result = mul_unsigned_unsigned[127:64]; - end - - ALU_DIV: begin - if (operand_b == '0) begin - alu_result = {XLEN{1'b1}}; - end else if ((operand_a == 64'h8000_0000_0000_0000) && - (operand_b == 64'hFFFF_FFFF_FFFF_FFFF)) begin - alu_result = operand_a; - end else begin - alu_result = $unsigned(operand_a_signed / operand_b_signed); - end - end - - ALU_DIVU: begin - alu_result = (operand_b == '0) ? {XLEN{1'b1}} : (operand_a / operand_b); - end - - ALU_REM: begin - if (operand_b == '0) begin - alu_result = operand_a; - end else if ((operand_a == 64'h8000_0000_0000_0000) && - (operand_b == 64'hFFFF_FFFF_FFFF_FFFF)) begin - alu_result = '0; - end else begin - alu_result = $unsigned(operand_a_signed % operand_b_signed); - end - end - - ALU_REMU: begin - alu_result = (operand_b == '0) ? operand_a : (operand_a % operand_b); - end - - default: alu_result = '0; - endcase - end - -endmodule diff --git a/rv64cpu/rtl/core/branch_unit.sv b/rv64cpu/rtl/core/branch_unit.sv deleted file mode 100644 index c9662ce..0000000 --- a/rv64cpu/rtl/core/branch_unit.sv +++ /dev/null @@ -1,52 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/branch_unit.sv -// Description: Branch Unit, responsible for determining whether conditional jump instructions are effective - -module branch_unit - import rv64_pkg::*; -( - input logic is_branch, // Indicates whether the current instruction is a branch instruction - input logic [2:0] branch_op, // Specific branch types func3 - input logic [XLEN-1:0] operand_a, // operand 1 - input logic [XLEN-1:0] operand_b, // operand 2 - output logic branch_taken // Branch taken signal -); - - always_comb begin - branch_taken = 1'b0; - - if (is_branch) begin - case (branch_op) - // 3'b000: BEQ (Branch if Equal) - BRANCH_EQ: branch_taken = (operand_a == operand_b); - // 3'b001: BNE (Branch if Not Equal) - BRANCH_NE: branch_taken = (operand_a != operand_b); - // 3'b100: BLT (Branch if Less Than, Signed) - BRANCH_LT: branch_taken = ($signed(operand_a) < $signed(operand_b)); - // 3'b101: BGE (Branch if Greater Than or Equal, Signed) - BRANCH_GE: branch_taken = ($signed(operand_a) >= $signed(operand_b)); - // 3'b110: BLTU (Branch if Less Than, Unsigned) - BRANCH_LTU: branch_taken = (operand_a < operand_b); - // 3'b111: BGEU (Branch if Greater Than or Equal, Unsigned) - BRANCH_GEU: branch_taken = (operand_a >= operand_b); - default: branch_taken = 1'b0; - endcase - end - end - -endmodule diff --git a/rv64cpu/rtl/core/compressed_decoder.sv b/rv64cpu/rtl/core/compressed_decoder.sv deleted file mode 100644 index ccbb043..0000000 --- a/rv64cpu/rtl/core/compressed_decoder.sv +++ /dev/null @@ -1,234 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// File path: rtl/core/compressed_decoder.sv -// Description: RV64C/Zca decompressor. Produces an equivalent 32-bit instruction -// for the existing RV64 pipeline and flags reserved encodings. - -module compressed_decoder - import rv64_pkg::*; -( - input logic [15:0] compressed_instr, - output logic [31:0] expanded_instr, - output logic illegal_instr -); - - logic [1:0] quadrant; - logic [2:0] funct3; - logic [4:0] rd_rs1; - logic [4:0] rs2; - logic [4:0] rd_prime; - logic [4:0] rs1_prime; - logic [4:0] rs2_prime; - - assign quadrant = compressed_instr[1:0]; - assign funct3 = compressed_instr[15:13]; - assign rd_rs1 = compressed_instr[11:7]; - assign rs2 = compressed_instr[6:2]; - assign rd_prime = {2'b01, compressed_instr[4:2]}; - assign rs1_prime = {2'b01, compressed_instr[9:7]}; - assign rs2_prime = {2'b01, compressed_instr[4:2]}; - - function automatic logic [31:0] r_instr( - input logic [6:0] funct7, - input logic [4:0] rs2_i, - input logic [4:0] rs1_i, - input logic [2:0] funct3_i, - input logic [4:0] rd_i, - input logic [6:0] opcode_i - ); - return {funct7, rs2_i, rs1_i, funct3_i, rd_i, opcode_i}; - endfunction - - function automatic logic [31:0] i_instr( - input logic [11:0] imm_i, - input logic [4:0] rs1_i, - input logic [2:0] funct3_i, - input logic [4:0] rd_i, - input logic [6:0] opcode_i - ); - return {imm_i, rs1_i, funct3_i, rd_i, opcode_i}; - endfunction - - function automatic logic [31:0] s_instr( - input logic [11:0] imm_i, - input logic [4:0] rs2_i, - input logic [4:0] rs1_i, - input logic [2:0] funct3_i, - input logic [6:0] opcode_i - ); - return {imm_i[11:5], rs2_i, rs1_i, funct3_i, imm_i[4:0], opcode_i}; - endfunction - - function automatic logic [31:0] b_instr( - input logic [12:0] imm_i, - input logic [4:0] rs2_i, - input logic [4:0] rs1_i, - input logic [2:0] funct3_i - ); - return {imm_i[12], imm_i[10:5], rs2_i, rs1_i, funct3_i, - imm_i[4:1], imm_i[11], OPCODE_BRANCH}; - endfunction - - function automatic logic [31:0] u_instr( - input logic [19:0] imm_i, - input logic [4:0] rd_i, - input logic [6:0] opcode_i - ); - return {imm_i, rd_i, opcode_i}; - endfunction - - function automatic logic [31:0] j_instr( - input logic [20:0] imm_i, - input logic [4:0] rd_i - ); - return {imm_i[20], imm_i[10:1], imm_i[11], imm_i[19:12], rd_i, OPCODE_JAL}; - endfunction - - always_comb begin - logic [11:0] ci_imm; - logic [11:0] addi4spn_imm; - logic [11:0] lw_imm; - logic [11:0] ld_imm; - logic [11:0] lwsp_imm; - logic [11:0] ldsp_imm; - logic [11:0] swsp_imm; - logic [11:0] sdsp_imm; - logic [12:0] branch_imm; - logic [20:0] jump_imm; - logic [5:0] shamt; - logic [19:0] lui_imm; - - ci_imm = {{6{compressed_instr[12]}}, compressed_instr[12], compressed_instr[6:2]}; - addi4spn_imm = {2'b00, compressed_instr[10:7], compressed_instr[12:11], - compressed_instr[5], compressed_instr[6], 2'b00}; - lw_imm = {5'b00000, compressed_instr[5], compressed_instr[12:10], - compressed_instr[6], 2'b00}; - ld_imm = {4'b0000, compressed_instr[6:5], compressed_instr[12:10], 3'b000}; - lwsp_imm = {4'b0000, compressed_instr[3:2], compressed_instr[12], - compressed_instr[6:4], 2'b00}; - ldsp_imm = {3'b000, compressed_instr[4:2], compressed_instr[12], - compressed_instr[6:5], 3'b000}; - swsp_imm = {4'b0000, compressed_instr[8:7], compressed_instr[12:9], 2'b00}; - sdsp_imm = {3'b000, compressed_instr[9:7], compressed_instr[12:10], 3'b000}; - branch_imm = {{4{compressed_instr[12]}}, compressed_instr[12], - compressed_instr[6:5], compressed_instr[2], - compressed_instr[11:10], compressed_instr[4:3], 1'b0}; - jump_imm = {{9{compressed_instr[12]}}, compressed_instr[12], - compressed_instr[8], compressed_instr[10:9], compressed_instr[6], - compressed_instr[7], compressed_instr[2], compressed_instr[11], - compressed_instr[5:3], 1'b0}; - shamt = {compressed_instr[12], compressed_instr[6:2]}; - lui_imm = {{14{compressed_instr[12]}}, compressed_instr[12], compressed_instr[6:2]}; - - expanded_instr = 32'h0000_0013; - illegal_instr = 1'b0; - - unique case (quadrant) - 2'b00: begin - unique case (funct3) - 3'b000: begin // C.ADDI4SPN - expanded_instr = i_instr(addi4spn_imm, 5'd2, 3'b000, rd_prime, OPCODE_OP_IMM); - illegal_instr = (addi4spn_imm == 12'b0); - end - 3'b001: expanded_instr = i_instr(ld_imm, rs1_prime, 3'b011, rd_prime, OPCODE_LOAD_FP); - 3'b010: expanded_instr = i_instr(lw_imm, rs1_prime, 3'b010, rd_prime, OPCODE_LOAD); - 3'b011: expanded_instr = i_instr(ld_imm, rs1_prime, 3'b011, rd_prime, OPCODE_LOAD); - 3'b101: expanded_instr = s_instr(ld_imm, rs2_prime, rs1_prime, 3'b011, OPCODE_STORE_FP); - 3'b110: expanded_instr = s_instr(lw_imm, rs2_prime, rs1_prime, 3'b010, OPCODE_STORE); - 3'b111: expanded_instr = s_instr(ld_imm, rs2_prime, rs1_prime, 3'b011, OPCODE_STORE); - default: illegal_instr = 1'b1; - endcase - end - - 2'b01: begin - unique case (funct3) - 3'b000: expanded_instr = i_instr(ci_imm, rd_rs1, 3'b000, rd_rs1, OPCODE_OP_IMM); - 3'b001: expanded_instr = i_instr(ci_imm, rd_rs1, 3'b000, rd_rs1, OPCODE_OP_IMM_32); - 3'b010: expanded_instr = i_instr(ci_imm, 5'd0, 3'b000, rd_rs1, OPCODE_OP_IMM); - 3'b011: begin - if (rd_rs1 == 5'd2) begin - expanded_instr = i_instr({{2{compressed_instr[12]}}, compressed_instr[12], - compressed_instr[4:3], compressed_instr[5], - compressed_instr[2], compressed_instr[6], 4'b0000}, - 5'd2, 3'b000, 5'd2, OPCODE_OP_IMM); - illegal_instr = (compressed_instr[12:2] == 11'b0); - end else begin - expanded_instr = u_instr(lui_imm, rd_rs1, OPCODE_LUI); - illegal_instr = (rd_rs1 == 5'd0) || (lui_imm == 20'b0); - end - end - 3'b100: begin - unique case (compressed_instr[11:10]) - 2'b00: begin - expanded_instr = i_instr({6'b000000, shamt}, rs1_prime, 3'b101, rs1_prime, OPCODE_OP_IMM); - illegal_instr = (shamt == 6'b0); - end - 2'b01: begin - expanded_instr = i_instr({6'b010000, shamt}, rs1_prime, 3'b101, rs1_prime, OPCODE_OP_IMM); - illegal_instr = (shamt == 6'b0); - end - 2'b10: expanded_instr = i_instr(ci_imm, rs1_prime, 3'b111, rs1_prime, OPCODE_OP_IMM); - 2'b11: begin - unique case ({compressed_instr[12], compressed_instr[6:5]}) - 3'b000: expanded_instr = r_instr(7'b0100000, rs2_prime, rs1_prime, 3'b000, rs1_prime, OPCODE_OP); - 3'b001: expanded_instr = r_instr(7'b0000000, rs2_prime, rs1_prime, 3'b100, rs1_prime, OPCODE_OP); - 3'b010: expanded_instr = r_instr(7'b0000000, rs2_prime, rs1_prime, 3'b110, rs1_prime, OPCODE_OP); - 3'b011: expanded_instr = r_instr(7'b0000000, rs2_prime, rs1_prime, 3'b111, rs1_prime, OPCODE_OP); - 3'b100: expanded_instr = r_instr(7'b0100000, rs2_prime, rs1_prime, 3'b000, rs1_prime, OPCODE_OP_32); - 3'b101: expanded_instr = r_instr(7'b0000000, rs2_prime, rs1_prime, 3'b000, rs1_prime, OPCODE_OP_32); - default: illegal_instr = 1'b1; - endcase - end - endcase - end - 3'b101: expanded_instr = j_instr(jump_imm, 5'd0); - 3'b110: expanded_instr = b_instr(branch_imm, 5'd0, rs1_prime, 3'b000); - 3'b111: expanded_instr = b_instr(branch_imm, 5'd0, rs1_prime, 3'b001); - default: illegal_instr = 1'b1; - endcase - end - - 2'b10: begin - unique case (funct3) - 3'b000: begin - expanded_instr = i_instr({6'b000000, shamt}, rd_rs1, 3'b001, rd_rs1, OPCODE_OP_IMM); - illegal_instr = (shamt == 6'b0); - end - 3'b001: begin - expanded_instr = i_instr(ldsp_imm, 5'd2, 3'b011, rd_rs1, OPCODE_LOAD_FP); - end - 3'b010: begin - expanded_instr = i_instr(lwsp_imm, 5'd2, 3'b010, rd_rs1, OPCODE_LOAD); - illegal_instr = (rd_rs1 == 5'd0); - end - 3'b011: begin - expanded_instr = i_instr(ldsp_imm, 5'd2, 3'b011, rd_rs1, OPCODE_LOAD); - illegal_instr = (rd_rs1 == 5'd0); - end - 3'b100: begin - if (!compressed_instr[12] && (rs2 == 5'd0)) begin - expanded_instr = i_instr(12'b0, rd_rs1, 3'b000, 5'd0, OPCODE_JALR); - illegal_instr = (rd_rs1 == 5'd0); - end else if (!compressed_instr[12]) begin - expanded_instr = r_instr(7'b0000000, rs2, 5'd0, 3'b000, rd_rs1, OPCODE_OP); - end else if (compressed_instr[12] && (rd_rs1 == 5'd0) && (rs2 == 5'd0)) begin - expanded_instr = 32'h0010_0073; // EBREAK - end else if (compressed_instr[12] && (rs2 == 5'd0)) begin - expanded_instr = i_instr(12'b0, rd_rs1, 3'b000, 5'd1, OPCODE_JALR); - end else begin - expanded_instr = r_instr(7'b0000000, rs2, rd_rs1, 3'b000, rd_rs1, OPCODE_OP); - end - end - 3'b101: expanded_instr = s_instr(sdsp_imm, rs2, 5'd2, 3'b011, OPCODE_STORE_FP); - 3'b110: expanded_instr = s_instr(swsp_imm, rs2, 5'd2, 3'b010, OPCODE_STORE); - 3'b111: expanded_instr = s_instr(sdsp_imm, rs2, 5'd2, 3'b011, OPCODE_STORE); - default: illegal_instr = 1'b1; - endcase - end - - default: illegal_instr = 1'b1; - endcase - end - -endmodule diff --git a/rv64cpu/rtl/core/csr_unit.sv b/rv64cpu/rtl/core/csr_unit.sv deleted file mode 100644 index a25e17a..0000000 --- a/rv64cpu/rtl/core/csr_unit.sv +++ /dev/null @@ -1,1221 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/csr_unit.sv - -module csr_unit - import rv64_pkg::*; - import csr_pkg::*; -#( - parameter logic [XLEN-1:0] HART_ID = '0, - parameter logic [XLEN-1:0] VENDOR_ID = '0, - parameter logic [XLEN-1:0] ARCH_ID = '0, - parameter logic [XLEN-1:0] IMPL_ID = '0, - parameter logic [XLEN-1:0] MISA_VALUE = 64'h8000_0000_0014_11ad, - parameter int PHYS_ADDR_WIDTH = 56, - parameter int PMP_GRANULARITY = 0 -)( - input logic clk, - input logic rst, - input logic csr_req, - input logic [2:0] csr_op, - input logic [11:0] csr_addr, - input logic [XLEN-1:0] csr_rs1_data, - input logic trap_ctrl_trigger, - input logic trap_ctrl_to_s_mode, - input logic [XLEN-1:0] trap_ctrl_mepc, - input logic [XLEN-1:0] trap_ctrl_mcause, - input logic [XLEN-1:0] trap_ctrl_mtval, - input logic trap_ctrl_mret_en, - input logic trap_ctrl_sret_en, - input logic m_timer_interrupt, - input logic m_software_interrupt, - input logic m_external_interrupt, - input logic s_timer_interrupt, - input logic s_software_interrupt, - input logic s_external_interrupt, - input logic fp_state_dirty, - input logic fp_fflags_valid, - input logic [4:0] fp_fflags, - output logic [XLEN-1:0] csr_rdata, - output logic [XLEN-1:0] csr_mstatus, - output logic [XLEN-1:0] csr_sstatus, - output logic [XLEN-1:0] csr_medeleg, - output logic [XLEN-1:0] csr_mideleg, - output logic [XLEN-1:0] csr_mie, - output logic [XLEN-1:0] csr_sie, - output logic [XLEN-1:0] csr_mip, - output logic [XLEN-1:0] csr_sip, - output logic [XLEN-1:0] csr_mtvec, - output logic [XLEN-1:0] csr_stvec, - output logic [XLEN-1:0] csr_mepc, - output logic [XLEN-1:0] csr_sepc, - output logic [XLEN-1:0] csr_satp, - output logic [2:0] csr_frm, - output logic [1:0] csr_priv_mode, - output logic [XLEN-1:0] csr_trap_vector, - output logic csr_trap_to_s_mode, - output logic [XLEN-1:0] csr_trap_vector_next, - output logic csr_trap_to_s_mode_next, - output pmpcfg_entry_t csr_pmpcfg_entries [PMP_ENTRY_COUNT], - output pmpaddr_entry_t csr_pmpaddr_entries [PMP_ENTRY_COUNT], - output logic csr_illegal_access -); - - typedef enum logic [1:0] { - PRIV_MODE_U = 2'b00, - PRIV_MODE_S = 2'b01, - PRIV_MODE_M = 2'b11 - } priv_mode_t; - - localparam int MSTATUS_MIE_BIT = 3; - localparam int MSTATUS_MPIE_BIT = 7; - localparam int MSTATUS_VS_LO = 9; - localparam int MSTATUS_VS_HI = 10; - localparam int MSTATUS_MPP_LO = 11; - localparam int MSTATUS_MPP_HI = 12; - localparam int MSTATUS_FS_LO = 13; - localparam int MSTATUS_FS_HI = 14; - localparam int MSTATUS_XS_LO = 15; - localparam int MSTATUS_XS_HI = 16; - localparam int MSTATUS_MPRV_BIT = 17; - localparam int MSTATUS_SUM_BIT = 18; - localparam int MSTATUS_MXR_BIT = 19; - localparam int MSTATUS_TVM_BIT = 20; - localparam int MSTATUS_TW_BIT = 21; - localparam int MSTATUS_TSR_BIT = 22; - localparam int MSTATUS_UXL_LO = 32; - localparam int MSTATUS_UXL_HI = 33; - localparam int MSTATUS_SXL_LO = 34; - localparam int MSTATUS_SXL_HI = 35; - localparam int MSTATUS_SD_BIT = 63; - localparam logic [1:0] STATUS_XLEN_64 = 2'b10; - localparam int SSTATUS_SIE_BIT = 1; - localparam int SSTATUS_SPIE_BIT = 5; - localparam int SSTATUS_SPP_BIT = 8; - localparam int SSTATUS_VS_LO = MSTATUS_VS_LO; - localparam int SSTATUS_VS_HI = MSTATUS_VS_HI; - localparam int SSTATUS_FS_LO = MSTATUS_FS_LO; - localparam int SSTATUS_FS_HI = MSTATUS_FS_HI; - localparam int SSTATUS_XS_LO = MSTATUS_XS_LO; - localparam int SSTATUS_XS_HI = MSTATUS_XS_HI; - localparam int SSTATUS_SUM_BIT = MSTATUS_SUM_BIT; - localparam int SSTATUS_MXR_BIT = MSTATUS_MXR_BIT; - localparam int SSTATUS_UXL_LO = MSTATUS_UXL_LO; - localparam int SSTATUS_UXL_HI = MSTATUS_UXL_HI; - localparam int SSTATUS_SD_BIT = MSTATUS_SD_BIT; - localparam int SATP_MODE_LO = 60; - localparam int SATP_MODE_HI = 63; - localparam int SATP_ASID_LO = 44; - localparam int SATP_ASID_HI = 59; - localparam int SATP_PPN_LO = 0; - localparam int SATP_PPN_HI = 43; - localparam logic [3:0] SATP_MODE_BARE = 4'd0; - localparam logic [3:0] SATP_MODE_SV39 = 4'd8; - localparam logic [3:0] SATP_MODE_SV48 = 4'd9; // Stub: unsupported until Sv48 is implemented. - localparam logic [3:0] SATP_MODE_SV57 = 4'd10; // Stub: unsupported until Sv57 is implemented. - localparam logic [3:0] SATP_MODE_SV64 = 4'd11; // Reserved/future stub. - localparam int MCOUNTINHIBIT_CY_BIT = 0; - localparam int MCOUNTINHIBIT_IR_BIT = 2; - localparam int COUNTEREN_CY_BIT = 0; - localparam int COUNTEREN_TM_BIT = 1; - localparam int COUNTEREN_IR_BIT = 2; - localparam int HPM_COUNTER_COUNT = 29; - localparam int INTERRUPT_S_SOFTWARE_BIT = int'(INTERRUPT_S_SOFTWARE); - localparam int INTERRUPT_S_TIMER_BIT = int'(INTERRUPT_S_TIMER); - localparam int INTERRUPT_S_EXTERNAL_BIT = int'(INTERRUPT_S_EXTERNAL); - localparam int INTERRUPT_M_SOFTWARE_BIT = int'(INTERRUPT_M_SOFTWARE); - localparam int INTERRUPT_M_TIMER_BIT = int'(INTERRUPT_M_TIMER); - localparam int INTERRUPT_M_EXTERNAL_BIT = int'(INTERRUPT_M_EXTERNAL); - localparam logic [XLEN-1:0] S_INTERRUPT_MASK = - (64'h1 << INTERRUPT_S_SOFTWARE) | - (64'h1 << INTERRUPT_S_TIMER) | - (64'h1 << INTERRUPT_S_EXTERNAL); - localparam logic [XLEN-1:0] M_INTERRUPT_MASK = - (64'h1 << INTERRUPT_M_SOFTWARE) | - (64'h1 << INTERRUPT_M_TIMER) | - (64'h1 << INTERRUPT_M_EXTERNAL); - localparam logic [XLEN-1:0] INTERRUPT_MASK = S_INTERRUPT_MASK | M_INTERRUPT_MASK; - - // Machine-mode CSRs - logic [XLEN-1:0] mstatus_reg; - logic [XLEN-1:0] medeleg_reg; - logic [XLEN-1:0] mideleg_reg; - logic [XLEN-1:0] mie_reg; - logic [XLEN-1:0] mtvec_reg; - logic [XLEN-1:0] mcounteren_reg; - logic [XLEN-1:0] mscratch_reg; - logic [XLEN-1:0] mepc_reg; - logic [XLEN-1:0] mcause_reg; - logic [XLEN-1:0] mtval_reg; - logic [XLEN-1:0] mip_reg; - logic [XLEN-1:0] mtinst_reg; - logic [XLEN-1:0] mtval2_reg; - logic [XLEN-1:0] menvcfg_reg; - logic [XLEN-1:0] mstateen_reg [4]; - pmpcfg_entry_t pmpcfg_entries [PMP_ENTRY_COUNT]; - pmpaddr_entry_t pmpaddr_entries [PMP_ENTRY_COUNT]; - - // Supervisor-mode CSRs - logic [XLEN-1:0] sstatus_reg; - logic [XLEN-1:0] stvec_reg; - logic [XLEN-1:0] scounteren_reg; - logic [XLEN-1:0] senvcfg_reg; - logic [XLEN-1:0] sstateen_reg [4]; - logic [XLEN-1:0] sscratch_reg; - logic [XLEN-1:0] sepc_reg; - logic [XLEN-1:0] scause_reg; - logic [XLEN-1:0] stval_reg; - logic [XLEN-1:0] satp_reg; - logic [XLEN-1:0] hstatus_reg; - logic [XLEN-1:0] hedeleg_reg; - logic [XLEN-1:0] hideleg_reg; - logic [XLEN-1:0] hie_reg; - logic [XLEN-1:0] htimedelta_reg; - logic [XLEN-1:0] hcounteren_reg; - logic [XLEN-1:0] hgeie_reg; - logic [XLEN-1:0] henvcfg_reg; - logic [XLEN-1:0] hstateen_reg [4]; - logic [XLEN-1:0] htval_reg; - logic [XLEN-1:0] hip_reg; - logic [XLEN-1:0] hvip_reg; - logic [XLEN-1:0] htinst_reg; - logic [XLEN-1:0] hgatp_reg; - logic [XLEN-1:0] vsstatus_reg; - logic [XLEN-1:0] vsie_reg; - logic [XLEN-1:0] vstvec_reg; - logic [XLEN-1:0] vsscratch_reg; - logic [XLEN-1:0] vsepc_reg; - logic [XLEN-1:0] vscause_reg; - logic [XLEN-1:0] vstval_reg; - logic [XLEN-1:0] vsip_reg; - logic [XLEN-1:0] vsatp_reg; - - // Counters - logic [XLEN-1:0] mcycle_reg; - logic [XLEN-1:0] minstret_reg; - logic [XLEN-1:0] time_reg; - logic [XLEN-1:0] mcountinhibit_reg; - logic [XLEN-1:0] mhpmcounter_reg [HPM_COUNTER_COUNT]; - - // Floating-point CSRs - logic [4:0] fflags_reg; - logic [2:0] frm_reg; - - priv_mode_t priv_mode_q; - - logic [11:0] csr_addr_canonical; - logic is_write_cmd; - logic is_set_cmd; - logic is_clear_cmd; - logic has_non_zero_data; - logic csr_write_en; - logic [XLEN-1:0] trap_vector_target; - logic csr_addr_supported; - logic csr_privilege_illegal; - logic csr_write_illegal; - logic csr_stateen_illegal; - logic [XLEN-1:0] csr_read_data; - logic [XLEN-1:0] mip_value; - logic [XLEN-1:0] sip_value; - logic [XLEN-1:0] sie_value; - logic [XLEN-1:0] trap_vector_q; - logic trap_to_s_q; - logic trap_taken; - logic trap_taken_to_s_mode; - logic [XLEN-1:0] trap_taken_mepc; - logic [XLEN-1:0] trap_taken_mcause; - logic [XLEN-1:0] trap_taken_mtval; - - // Canonicalize addresses, decode operations, and prepare CSR/trap metadata. - assign csr_addr_canonical = canonical_addr(csr_addr); - assign is_write_cmd = (csr_op == 3'b001) || (csr_op == 3'b101); - assign is_set_cmd = (csr_op == 3'b010) || (csr_op == 3'b110); - assign is_clear_cmd = (csr_op == 3'b011) || (csr_op == 3'b111); - assign has_non_zero_data = |csr_rs1_data; - assign csr_addr_supported = csr_is_supported(csr_addr_canonical); - assign csr_write_en = csr_req & csr_addr_supported & !csr_privilege_illegal & - !csr_write_illegal & !csr_stateen_illegal & - (is_write_cmd | ((is_set_cmd | is_clear_cmd) & has_non_zero_data)); - assign trap_taken = trap_ctrl_trigger; - assign trap_taken_to_s_mode = trap_ctrl_to_s_mode; - assign trap_taken_mepc = trap_ctrl_mepc; - assign trap_taken_mcause = trap_ctrl_mcause; - assign trap_taken_mtval = trap_ctrl_mtval; - - assign trap_vector_target = trap_taken_to_s_mode ? stvec_reg : mtvec_reg; - assign csr_read_data = read_csr(csr_addr_canonical); - assign csr_rdata = csr_read_data; - assign mip_value = compose_mip(); - assign sip_value = mip_value & S_INTERRUPT_MASK; - assign sie_value = mie_reg & S_INTERRUPT_MASK; - assign csr_mstatus = mstatus_reg; - assign csr_sstatus = sstatus_reg; - assign csr_medeleg = medeleg_reg; - assign csr_mideleg = mideleg_reg; - assign csr_mie = mie_reg; - assign csr_sie = sie_value; - assign csr_mip = mip_value; - assign csr_sip = sip_value; - assign csr_mtvec = mtvec_reg; - assign csr_stvec = stvec_reg; - assign csr_mepc = mepc_reg; - assign csr_sepc = sepc_reg; - assign csr_satp = satp_reg; - assign csr_frm = frm_reg; - assign csr_priv_mode = priv_mode_q; - assign csr_trap_vector = trap_vector_q; - assign csr_trap_to_s_mode = trap_to_s_q; - assign csr_trap_vector_next = trap_vector_target; - assign csr_trap_to_s_mode_next = trap_taken_to_s_mode; - assign csr_write_illegal = csr_req && csr_is_read_only_by_encoding(csr_addr) && - (is_write_cmd || ((is_set_cmd || is_clear_cmd) && has_non_zero_data)); - assign csr_privilege_illegal = csr_req && csr_addr_supported && - !csr_privilege_allows_access(csr_addr, csr_addr_canonical); - assign csr_stateen_illegal = csr_req && csr_access_blocked_by_stateen(csr_addr, csr_addr_canonical); - assign csr_illegal_access = csr_req && (!csr_addr_supported || csr_privilege_illegal || - csr_write_illegal || csr_stateen_illegal); - - for (genvar pmp_entry_idx = 0; pmp_entry_idx < PMP_ENTRY_COUNT; pmp_entry_idx++) begin : gen_pmp_outputs - assign csr_pmpcfg_entries[pmp_entry_idx] = pmpcfg_entries[pmp_entry_idx]; - assign csr_pmpaddr_entries[pmp_entry_idx] = pmpaddr_entries[pmp_entry_idx]; - end - - // Main CSR state machine: counts cycles, processes CSR writes, and reacts to traps/returns. - always_ff @(posedge clk) begin - if (rst) begin - mstatus_reg <= reset_mstatus_value(); - medeleg_reg <= legalize_medeleg('0); - mideleg_reg <= '0; - mie_reg <= '0; - mtvec_reg <= '0; - mcounteren_reg <= '0; - mscratch_reg <= '0; - mepc_reg <= '0; - mcause_reg <= '0; - mtval_reg <= '0; - mip_reg <= '0; - mtinst_reg <= '0; - mtval2_reg <= '0; - menvcfg_reg <= '0; - for (int unsigned stateen_idx = 0; stateen_idx < 4; stateen_idx++) begin - mstateen_reg[stateen_idx] <= '0; - sstateen_reg[stateen_idx] <= '0; - hstateen_reg[stateen_idx] <= '0; - end - for (int unsigned pmp_entry_idx = 0; pmp_entry_idx < PMP_ENTRY_COUNT; pmp_entry_idx++) begin - pmpcfg_entries[pmp_entry_idx] <= '0; - pmpaddr_entries[pmp_entry_idx] <= '0; - end - sstatus_reg <= reset_sstatus_value(); - stvec_reg <= '0; - scounteren_reg <= '0; - senvcfg_reg <= '0; - sscratch_reg <= '0; - sepc_reg <= '0; - scause_reg <= '0; - stval_reg <= '0; - satp_reg <= '0; - hstatus_reg <= '0; - hedeleg_reg <= '0; - hideleg_reg <= '0; - hie_reg <= '0; - htimedelta_reg <= '0; - hcounteren_reg <= '0; - hgeie_reg <= '0; - henvcfg_reg <= '0; - htval_reg <= '0; - hip_reg <= '0; - hvip_reg <= '0; - htinst_reg <= '0; - hgatp_reg <= '0; - vsstatus_reg <= reset_sstatus_value(); - vsie_reg <= '0; - vstvec_reg <= '0; - vsscratch_reg <= '0; - vsepc_reg <= '0; - vscause_reg <= '0; - vstval_reg <= '0; - vsip_reg <= '0; - vsatp_reg <= '0; - mcycle_reg <= '0; - minstret_reg <= '0; - time_reg <= '0; - mcountinhibit_reg <= '0; - for (int unsigned hpm_idx = 0; hpm_idx < HPM_COUNTER_COUNT; hpm_idx++) begin - mhpmcounter_reg[hpm_idx] <= '0; - end - fflags_reg <= '0; - frm_reg <= '0; - priv_mode_q <= PRIV_MODE_M; - trap_vector_q <= '0; - trap_to_s_q <= 1'b0; - end else begin - if (!mcountinhibit_reg[MCOUNTINHIBIT_CY_BIT]) begin - mcycle_reg <= mcycle_reg + 64'd1; - end - time_reg <= time_reg + 64'd1; - if (!trap_taken && !mcountinhibit_reg[MCOUNTINHIBIT_IR_BIT]) begin - minstret_reg <= minstret_reg + 64'd1; - end - - if (csr_write_en && csr_is_writable(csr_addr_canonical)) begin - if (csr_is_pmpcfg(csr_addr_canonical)) begin - write_pmpcfg_csr(csr_addr_canonical, csr_write_next(read_csr(csr_addr_canonical), csr_rs1_data, csr_op)); - end else if (csr_is_pmpaddr(csr_addr_canonical)) begin - write_pmpaddr_csr(csr_addr_canonical, csr_write_next(read_csr(csr_addr_canonical), csr_rs1_data, csr_op)); - end else if (csr_is_mhpmcounter(csr_addr_canonical)) begin - mhpmcounter_reg[hpmcounter_index(csr_addr_canonical)] <= - csr_write_next(read_csr(csr_addr_canonical), csr_rs1_data, csr_op); - end else begin - unique case (csr_addr_canonical) - CSR_MSTATUS: begin - logic [XLEN-1:0] next_data; - logic [XLEN-1:0] legal_data; - next_data = csr_write_next(mstatus_reg, csr_rs1_data, csr_op); - legal_data = legalize_mstatus(next_data); - mstatus_reg <= legal_data; - sstatus_reg <= mirror_mstatus_to_sstatus(sstatus_reg, legal_data); - end - CSR_MEDELEG: medeleg_reg <= legalize_medeleg(csr_write_next(medeleg_reg, csr_rs1_data, csr_op)); - CSR_MIDELEG: mideleg_reg <= legalize_mideleg(csr_write_next(mideleg_reg, csr_rs1_data, csr_op)); - CSR_MIE: mie_reg <= legalize_interrupt_csr(csr_write_next(mie_reg, csr_rs1_data, csr_op)); - CSR_MTVEC: mtvec_reg <= csr_write_next(mtvec_reg, csr_rs1_data, csr_op); - CSR_MSCRATCH: mscratch_reg <= csr_write_next(mscratch_reg, csr_rs1_data, csr_op); - CSR_MEPC: mepc_reg <= csr_write_next(mepc_reg, csr_rs1_data, csr_op); - CSR_MCAUSE: mcause_reg <= csr_write_next(mcause_reg, csr_rs1_data, csr_op); - CSR_MTVAL: mtval_reg <= csr_write_next(mtval_reg, csr_rs1_data, csr_op); - CSR_MIP: mip_reg <= legalize_interrupt_csr(csr_write_next(mip_reg, csr_rs1_data, csr_op)); - CSR_MTINST: mtinst_reg <= '0; - CSR_MTVAL2: mtval2_reg <= csr_write_next(mtval2_reg, csr_rs1_data, csr_op); - CSR_MCOUNTEREN: mcounteren_reg <= legalize_counteren(csr_write_next(mcounteren_reg, csr_rs1_data, csr_op)); - CSR_MCOUNTINHIBIT: mcountinhibit_reg <= legalize_mcountinhibit(csr_write_next(mcountinhibit_reg, csr_rs1_data, csr_op)); - CSR_MENVCFG: menvcfg_reg <= legalize_menvcfg(csr_write_next(menvcfg_reg, csr_rs1_data, csr_op)); - CSR_MSTATEEN0: mstateen_reg[0] <= legalize_mstateen(0, csr_write_next(mstateen_reg[0], csr_rs1_data, csr_op)); - CSR_MSTATEEN1: mstateen_reg[1] <= legalize_mstateen(1, csr_write_next(mstateen_reg[1], csr_rs1_data, csr_op)); - CSR_MSTATEEN2: mstateen_reg[2] <= legalize_mstateen(2, csr_write_next(mstateen_reg[2], csr_rs1_data, csr_op)); - CSR_MSTATEEN3: mstateen_reg[3] <= legalize_mstateen(3, csr_write_next(mstateen_reg[3], csr_rs1_data, csr_op)); - CSR_SSTATUS: begin - logic [XLEN-1:0] next_sstatus; - next_sstatus = csr_write_next(sstatus_reg, csr_rs1_data, csr_op); - sstatus_reg <= legalize_sstatus(next_sstatus); - mstatus_reg <= apply_sstatus_to_mstatus(mstatus_reg, next_sstatus); - end - CSR_SIE: mie_reg <= (mie_reg & ~S_INTERRUPT_MASK) | - (legalize_interrupt_csr(csr_write_next(sie_value, csr_rs1_data, csr_op)) & - S_INTERRUPT_MASK); - CSR_STVEC: stvec_reg <= csr_write_next(stvec_reg, csr_rs1_data, csr_op); - CSR_SCOUNTEREN: scounteren_reg <= legalize_counteren(csr_write_next(scounteren_reg, csr_rs1_data, csr_op)); - CSR_SENVCFG: senvcfg_reg <= legalize_senvcfg(csr_write_next(senvcfg_reg, csr_rs1_data, csr_op)); - CSR_SSTATEEN0: sstateen_reg[0] <= legalize_sstateen(0, csr_write_next(sstateen_reg[0], csr_rs1_data, csr_op)); - CSR_SSTATEEN1: sstateen_reg[1] <= legalize_sstateen(1, csr_write_next(sstateen_reg[1], csr_rs1_data, csr_op)); - CSR_SSTATEEN2: sstateen_reg[2] <= legalize_sstateen(2, csr_write_next(sstateen_reg[2], csr_rs1_data, csr_op)); - CSR_SSTATEEN3: sstateen_reg[3] <= legalize_sstateen(3, csr_write_next(sstateen_reg[3], csr_rs1_data, csr_op)); - CSR_SSCRATCH: sscratch_reg <= csr_write_next(sscratch_reg, csr_rs1_data, csr_op); - CSR_SEPC: sepc_reg <= csr_write_next(sepc_reg, csr_rs1_data, csr_op); - CSR_SCAUSE: scause_reg <= csr_write_next(scause_reg, csr_rs1_data, csr_op); - CSR_STVAL: stval_reg <= csr_write_next(stval_reg, csr_rs1_data, csr_op); - CSR_SIP: mip_reg <= (mip_reg & ~S_INTERRUPT_MASK) | - (legalize_interrupt_csr(csr_write_next(sip_value, csr_rs1_data, csr_op)) & - S_INTERRUPT_MASK); - CSR_SATP: begin - logic [XLEN-1:0] next_satp; - next_satp = csr_write_next(satp_reg, csr_rs1_data, csr_op); - satp_reg <= legalize_satp(next_satp, satp_reg); - end - CSR_HSTATUS: hstatus_reg <= legalize_hstatus(csr_write_next(hstatus_reg, csr_rs1_data, csr_op)); - CSR_HEDELEG: hedeleg_reg <= legalize_medeleg(csr_write_next(hedeleg_reg, csr_rs1_data, csr_op)); - CSR_HIDELEG: hideleg_reg <= legalize_mideleg(csr_write_next(hideleg_reg, csr_rs1_data, csr_op)); - CSR_HIE: hie_reg <= '0; - CSR_HTIMEDELTA: htimedelta_reg <= csr_write_next(htimedelta_reg, csr_rs1_data, csr_op); - CSR_HCOUNTEREN: hcounteren_reg <= legalize_counteren(csr_write_next(hcounteren_reg, csr_rs1_data, csr_op)); - CSR_HGEIE: hgeie_reg <= '0; - CSR_HENVCFG: henvcfg_reg <= legalize_menvcfg(csr_write_next(henvcfg_reg, csr_rs1_data, csr_op)); - CSR_HSTATEEN0: hstateen_reg[0] <= legalize_mstateen(0, csr_write_next(hstateen_reg[0], csr_rs1_data, csr_op)); - CSR_HSTATEEN1: hstateen_reg[1] <= legalize_mstateen(1, csr_write_next(hstateen_reg[1], csr_rs1_data, csr_op)); - CSR_HSTATEEN2: hstateen_reg[2] <= legalize_mstateen(2, csr_write_next(hstateen_reg[2], csr_rs1_data, csr_op)); - CSR_HSTATEEN3: hstateen_reg[3] <= legalize_mstateen(3, csr_write_next(hstateen_reg[3], csr_rs1_data, csr_op)); - CSR_HTVAL: htval_reg <= csr_write_next(htval_reg, csr_rs1_data, csr_op); - CSR_HIP: hip_reg <= '0; - CSR_HVIP: hvip_reg <= '0; - CSR_HTINST: htinst_reg <= '0; - CSR_HGATP: hgatp_reg <= legalize_hgatp(csr_write_next(hgatp_reg, csr_rs1_data, csr_op), hgatp_reg); - CSR_VSSTATUS: vsstatus_reg <= legalize_sstatus(csr_write_next(vsstatus_reg, csr_rs1_data, csr_op)); - CSR_VSIE: vsie_reg <= '0; - CSR_VSTVEC: vstvec_reg <= csr_write_next(vstvec_reg, csr_rs1_data, csr_op); - CSR_VSSCRATCH: vsscratch_reg <= csr_write_next(vsscratch_reg, csr_rs1_data, csr_op); - CSR_VSEPC: vsepc_reg <= csr_write_next(vsepc_reg, csr_rs1_data, csr_op); - CSR_VSCAUSE: vscause_reg <= csr_write_next(vscause_reg, csr_rs1_data, csr_op); - CSR_VSTVAL: vstval_reg <= csr_write_next(vstval_reg, csr_rs1_data, csr_op); - CSR_VSIP: vsip_reg <= '0; - CSR_VSATP: begin - logic [XLEN-1:0] next_vsatp; - next_vsatp = csr_write_next(vsatp_reg, csr_rs1_data, csr_op); - vsatp_reg <= legalize_satp(next_vsatp, vsatp_reg); - end - CSR_MCYCLE: mcycle_reg <= csr_write_next(mcycle_reg, csr_rs1_data, csr_op); - CSR_MINSTRET: minstret_reg <= csr_write_next(minstret_reg, csr_rs1_data, csr_op); - CSR_FFLAGS: begin - logic [XLEN-1:0] next_flags; - next_flags = csr_write_next({{(XLEN-5){1'b0}}, fflags_reg}, csr_rs1_data, csr_op); - fflags_reg <= next_flags[4:0]; - mstatus_reg <= set_mstatus_fs_dirty(mstatus_reg); - sstatus_reg <= set_sstatus_fs_dirty(sstatus_reg); - end - CSR_FRM: begin - logic [XLEN-1:0] next_frm; - next_frm = csr_write_next({{(XLEN-3){1'b0}}, frm_reg}, csr_rs1_data, csr_op); - frm_reg <= next_frm[2:0]; - mstatus_reg <= set_mstatus_fs_dirty(mstatus_reg); - sstatus_reg <= set_sstatus_fs_dirty(sstatus_reg); - end - CSR_FCSR: begin - logic [XLEN-1:0] next_fcsr; - next_fcsr = csr_write_next(compose_fcsr(), csr_rs1_data, csr_op); - fflags_reg <= next_fcsr[4:0]; - frm_reg <= next_fcsr[7:5]; - mstatus_reg <= set_mstatus_fs_dirty(mstatus_reg); - sstatus_reg <= set_sstatus_fs_dirty(sstatus_reg); - end - default: ; - endcase - end - end - - if (fp_state_dirty) begin - mstatus_reg <= set_mstatus_fs_dirty(mstatus_reg); - sstatus_reg <= set_sstatus_fs_dirty(sstatus_reg); - end - - if (fp_fflags_valid) begin - fflags_reg <= fflags_reg | fp_fflags; - end - - if (trap_taken) begin - trap_vector_q <= trap_vector_target; - trap_to_s_q <= trap_taken_to_s_mode; - // Update privilege-specific CSRs based on whether the trap was delegated. - if (trap_taken_to_s_mode) begin - logic [XLEN-1:0] next_sstatus; - next_sstatus = sstatus_reg; - next_sstatus[SSTATUS_SPIE_BIT] = sstatus_reg[SSTATUS_SIE_BIT]; - next_sstatus[SSTATUS_SIE_BIT] = 1'b0; - next_sstatus[SSTATUS_SPP_BIT] = (priv_mode_q == PRIV_MODE_S); - sstatus_reg <= next_sstatus; - mstatus_reg <= apply_sstatus_to_mstatus(mstatus_reg, next_sstatus); - sepc_reg <= trap_taken_mepc; - scause_reg <= trap_taken_mcause; - stval_reg <= trap_taken_mtval; - priv_mode_q <= PRIV_MODE_S; - end else begin - logic [XLEN-1:0] next_mstatus; - next_mstatus = mstatus_reg; - next_mstatus[MSTATUS_MPIE_BIT] = mstatus_reg[MSTATUS_MIE_BIT]; - next_mstatus[MSTATUS_MIE_BIT] = 1'b0; - next_mstatus[MSTATUS_MPP_HI:MSTATUS_MPP_LO] = encode_priv_mode(priv_mode_q); - mstatus_reg <= legalize_mstatus(next_mstatus); - sstatus_reg <= mirror_mstatus_to_sstatus(sstatus_reg, legalize_mstatus(next_mstatus)); - mepc_reg <= trap_taken_mepc; - mcause_reg <= trap_taken_mcause; - mtval_reg <= trap_taken_mtval; - priv_mode_q <= PRIV_MODE_M; - end - end else begin - if (trap_ctrl_mret_en) begin - priv_mode_t return_mode; - logic [XLEN-1:0] next_mstatus; - return_mode = decode_priv_mode(mstatus_reg[MSTATUS_MPP_HI:MSTATUS_MPP_LO]); - next_mstatus = mstatus_reg; - next_mstatus[MSTATUS_MIE_BIT] = mstatus_reg[MSTATUS_MPIE_BIT]; - next_mstatus[MSTATUS_MPIE_BIT] = 1'b1; - next_mstatus[MSTATUS_MPP_HI:MSTATUS_MPP_LO] = encode_priv_mode(PRIV_MODE_U); - if (return_mode != PRIV_MODE_M) begin - next_mstatus[MSTATUS_MPRV_BIT] = 1'b0; - end - mstatus_reg <= legalize_mstatus(next_mstatus); - sstatus_reg <= mirror_mstatus_to_sstatus(sstatus_reg, legalize_mstatus(next_mstatus)); - priv_mode_q <= return_mode; - end - - if (trap_ctrl_sret_en) begin - logic [XLEN-1:0] next_sstatus; - logic [XLEN-1:0] next_mstatus; - next_sstatus = sstatus_reg; - next_sstatus[SSTATUS_SIE_BIT] = sstatus_reg[SSTATUS_SPIE_BIT]; - next_sstatus[SSTATUS_SPIE_BIT] = 1'b1; - next_sstatus[SSTATUS_SPP_BIT] = 1'b0; - next_mstatus = apply_sstatus_to_mstatus(mstatus_reg, next_sstatus); - next_mstatus[MSTATUS_MPRV_BIT] = 1'b0; - sstatus_reg <= legalize_sstatus(next_sstatus); - mstatus_reg <= legalize_mstatus(next_mstatus); - priv_mode_q <= (sstatus_reg[SSTATUS_SPP_BIT]) ? PRIV_MODE_S : PRIV_MODE_U; - end - end - end - end - - // Map CSR aliases (cycle/instret) to their canonical machine addresses. - function automatic logic [11:0] canonical_addr(input logic [11:0] addr); - case (addr) - CSR_CYCLE: canonical_addr = CSR_MCYCLE; - CSR_INSTRET: canonical_addr = CSR_MINSTRET; - default: canonical_addr = addr; - endcase - endfunction - - function automatic bit csr_is_pmpcfg(input logic [11:0] addr); - csr_is_pmpcfg = (addr >= CSR_PMPCFG0) && (addr <= CSR_PMPCFG15) && (addr[0] == 1'b0); - endfunction - - function automatic bit csr_is_pmpaddr(input logic [11:0] addr); - csr_is_pmpaddr = (addr >= CSR_PMPADDR0) && (addr <= CSR_PMPADDR63); - endfunction - - function automatic bit csr_is_mhpmcounter(input logic [11:0] addr); - csr_is_mhpmcounter = (addr >= CSR_MHPMCOUNTER3) && (addr <= CSR_MHPMCOUNTER31); - endfunction - - function automatic bit csr_is_hpmcounter(input logic [11:0] addr); - csr_is_hpmcounter = (addr >= CSR_HPMCOUNTER3) && (addr <= CSR_HPMCOUNTER31); - endfunction - - function automatic bit csr_is_mhpmevent(input logic [11:0] addr); - csr_is_mhpmevent = (addr >= CSR_MHPMEVENT3) && (addr <= CSR_MHPMEVENT31); - endfunction - - function automatic bit csr_is_counter(input logic [11:0] addr); - return (addr == CSR_CYCLE) || (addr == CSR_TIME) || (addr == CSR_INSTRET) || csr_is_hpmcounter(addr); - endfunction - - function automatic int unsigned counter_index(input logic [11:0] addr); - if (csr_is_hpmcounter(addr)) begin - return int'(addr) - int'(CSR_CYCLE); - end - case (addr) - CSR_CYCLE: return COUNTEREN_CY_BIT; - CSR_TIME: return COUNTEREN_TM_BIT; - CSR_INSTRET: return COUNTEREN_IR_BIT; - default: return COUNTEREN_CY_BIT; - endcase - endfunction - - function automatic int unsigned hpmcounter_index(input logic [11:0] addr); - if (csr_is_hpmcounter(addr)) begin - return int'(addr) - int'(CSR_HPMCOUNTER3); - end - return int'(addr) - int'(CSR_MHPMCOUNTER3); - endfunction - - function automatic int unsigned pmpcfg_csr_base_entry(input logic [11:0] addr); - return (int'(addr) - int'(CSR_PMPCFG0)) * 4; - endfunction - - function automatic int unsigned pmpaddr_csr_entry(input logic [11:0] addr); - return int'(addr) - int'(CSR_PMPADDR0); - endfunction - - function automatic logic [XLEN-1:0] pmpaddr_writable_mask(); - logic [XLEN-1:0] mask; - mask = '0; - for (int bit_idx = 0; bit_idx < (PHYS_ADDR_WIDTH - 2); bit_idx++) begin - mask[bit_idx] = 1'b1; - end - return mask; - endfunction - - function automatic bit pmpcfg_byte_is_legal(input pmpcfg_entry_t cfg); - logic [1:0] address_matching; - address_matching = cfg[PMPCFG_A_HI:PMPCFG_A_LO]; - if (!cfg[PMPCFG_R_BIT] && cfg[PMPCFG_W_BIT]) begin - return 1'b0; - end - if ((PMP_GRANULARITY >= 2) && (address_matching == PMP_MODE_NA4)) begin - return 1'b0; - end - return 1'b1; - endfunction - - function automatic pmpcfg_entry_t legalize_pmpcfg_byte(input pmpcfg_entry_t cfg); - pmpcfg_entry_t legal_cfg; - legal_cfg = cfg; - legal_cfg[6:5] = 2'b00; - return legal_cfg; - endfunction - - function automatic bit pmpaddr_is_locked(input int unsigned entry_idx); - bit locked; - locked = pmpcfg_entries[entry_idx][PMPCFG_L_BIT]; - if (entry_idx < (PMP_ENTRY_COUNT - 1)) begin - locked |= pmpcfg_entries[entry_idx + 1][PMPCFG_L_BIT] && - (pmpcfg_entries[entry_idx + 1][PMPCFG_A_HI:PMPCFG_A_LO] == PMP_MODE_TOR); - end - return locked; - endfunction - - function automatic logic [XLEN-1:0] read_pmpcfg_csr(input logic [11:0] addr); - logic [XLEN-1:0] data; - int unsigned base_entry; - data = '0; - base_entry = pmpcfg_csr_base_entry(addr); - for (int byte_idx = 0; byte_idx < PMP_CFG_ENTRIES_PER_RV64_CSR; byte_idx++) begin - if ((base_entry + byte_idx) < PMP_ENTRY_COUNT) begin - data[(byte_idx * 8) +: 8] = pmpcfg_entries[base_entry + byte_idx]; - end - end - return data; - endfunction - - function automatic logic [XLEN-1:0] read_pmpaddr_csr(input logic [11:0] addr); - return pmpaddr_entries[pmpaddr_csr_entry(addr)] & pmpaddr_writable_mask(); - endfunction - - task automatic write_pmpcfg_csr(input logic [11:0] addr, input logic [XLEN-1:0] write_data); - int unsigned base_entry; - pmpcfg_entry_t next_cfg; - base_entry = pmpcfg_csr_base_entry(addr); - for (int byte_idx = 0; byte_idx < PMP_CFG_ENTRIES_PER_RV64_CSR; byte_idx++) begin - if ((base_entry + byte_idx) < PMP_ENTRY_COUNT) begin - next_cfg = legalize_pmpcfg_byte(write_data[(byte_idx * 8) +: 8]); - if (!pmpcfg_entries[base_entry + byte_idx][PMPCFG_L_BIT] && pmpcfg_byte_is_legal(next_cfg)) begin - pmpcfg_entries[base_entry + byte_idx] <= next_cfg; - end - end - end - endtask - - task automatic write_pmpaddr_csr(input logic [11:0] addr, input logic [XLEN-1:0] write_data); - int unsigned entry_idx; - entry_idx = pmpaddr_csr_entry(addr); - if (!pmpaddr_is_locked(entry_idx)) begin - pmpaddr_entries[entry_idx] <= write_data & pmpaddr_writable_mask(); - end - endtask - - // Apply the CSR op semantics (write/set/clear) for a single register. - function automatic logic [XLEN-1:0] csr_write_next( - input logic [XLEN-1:0] current_data, - input logic [XLEN-1:0] write_data, - input logic [2:0] op_code - ); - logic [XLEN-1:0] next_data; - unique case (op_code) - 3'b001, 3'b101: next_data = write_data; // CSRRW / CSRRWI - 3'b010, 3'b110: next_data = current_data | write_data; // CSRRS / CSRRSI - 3'b011, 3'b111: next_data = current_data & ~write_data; // CSRRC / CSRRCI - default: next_data = current_data; - endcase - return next_data; - endfunction - - function automatic bit csr_is_writable(input logic [11:0] addr); - case (addr) - CSR_MVENDORID, - CSR_MARCHID, - CSR_MIMPID, - CSR_MHARTID, - CSR_MCONFIGPTR, - CSR_TIME: csr_is_writable = 1'b0; - default: csr_is_writable = 1'b1; - endcase - endfunction - - function automatic bit csr_is_read_only_by_encoding(input logic [11:0] addr); - return addr[11:10] == 2'b11; - endfunction - - function automatic bit csr_is_supported(input logic [11:0] addr); - bit supported; - if (csr_is_pmpcfg(addr) || csr_is_pmpaddr(addr) || - csr_is_mhpmcounter(addr) || csr_is_hpmcounter(addr) || csr_is_mhpmevent(addr)) begin - supported = 1'b1; - end else begin - unique case (addr) - CSR_MSTATUS, - CSR_MISA, - CSR_MEDELEG, - CSR_MIDELEG, - CSR_MIE, - CSR_MTVEC, - CSR_MSCRATCH, - CSR_MEPC, - CSR_MCAUSE, - CSR_MTVAL, - CSR_MIP, - CSR_MTINST, - CSR_MTVAL2, - CSR_MCOUNTEREN, - CSR_MCOUNTINHIBIT, - CSR_MENVCFG, - CSR_MSTATEEN0, - CSR_MSTATEEN1, - CSR_MSTATEEN2, - CSR_MSTATEEN3, - CSR_MVENDORID, - CSR_MARCHID, - CSR_MIMPID, - CSR_MHARTID, - CSR_MCONFIGPTR, - CSR_SSTATUS, - CSR_SIE, - CSR_STVEC, - CSR_SCOUNTEREN, - CSR_SENVCFG, - CSR_SSTATEEN0, - CSR_SSTATEEN1, - CSR_SSTATEEN2, - CSR_SSTATEEN3, - CSR_SSCRATCH, - CSR_SEPC, - CSR_SCAUSE, - CSR_STVAL, - CSR_SIP, - CSR_SATP, - CSR_HSTATUS, - CSR_HEDELEG, - CSR_HIDELEG, - CSR_HIE, - CSR_HTIMEDELTA, - CSR_HCOUNTEREN, - CSR_HGEIE, - CSR_HENVCFG, - CSR_HSTATEEN0, - CSR_HSTATEEN1, - CSR_HSTATEEN2, - CSR_HSTATEEN3, - CSR_HTVAL, - CSR_HIP, - CSR_HVIP, - CSR_HTINST, - CSR_HGATP, - CSR_HGEIP, - CSR_VSSTATUS, - CSR_VSIE, - CSR_VSTVEC, - CSR_VSSCRATCH, - CSR_VSEPC, - CSR_VSCAUSE, - CSR_VSTVAL, - CSR_VSIP, - CSR_VSATP, - CSR_MCYCLE, - CSR_MINSTRET, - CSR_CYCLE, - CSR_INSTRET, - CSR_TIME, - CSR_FFLAGS, - CSR_FRM, - CSR_FCSR: - supported = 1'b1; - default: - supported = 1'b0; - endcase - end - return supported; - endfunction - - function automatic bit csr_privilege_allows_access( - input logic [11:0] requested_addr, - input logic [11:0] canonicalized_addr - ); - logic [1:0] minimum_privilege; - minimum_privilege = requested_addr[9:8]; - if (!csr_is_supported(canonicalized_addr)) begin - return 1'b0; - end - if (minimum_privilege == 2'b10) begin - return 1'b0; - end - return encode_priv_mode(priv_mode_q) >= minimum_privilege; - endfunction - - function automatic bit csr_access_blocked_by_stateen( - input logic [11:0] requested_addr, - input logic [11:0] canonicalized_addr - ); - int unsigned current_counter_index; - if (csr_is_counter(requested_addr) && (priv_mode_q != PRIV_MODE_M)) begin - current_counter_index = counter_index(requested_addr); - if (!mcounteren_reg[current_counter_index]) begin - return 1'b1; - end - if ((priv_mode_q == PRIV_MODE_U) && !scounteren_reg[current_counter_index]) begin - return 1'b1; - end - end - if ((canonicalized_addr == CSR_SENVCFG) && (priv_mode_q == PRIV_MODE_S)) begin - return !mstateen_reg[0][STATEEN0_ENVCFG_BIT]; - end - return 1'b0; - endfunction - - function automatic logic [XLEN-1:0] legalize_mstatus(input logic [XLEN-1:0] data); - logic [XLEN-1:0] legal_data; - legal_data = data; - if (legal_data[MSTATUS_MPP_HI:MSTATUS_MPP_LO] == 2'b10) begin - legal_data[MSTATUS_MPP_HI:MSTATUS_MPP_LO] = encode_priv_mode(PRIV_MODE_U); - end - legal_data[MSTATUS_XS_HI:MSTATUS_XS_LO] = 2'b00; - legal_data[MSTATUS_UXL_HI:MSTATUS_UXL_LO] = STATUS_XLEN_64; - legal_data[MSTATUS_SXL_HI:MSTATUS_SXL_LO] = STATUS_XLEN_64; - legal_data[MSTATUS_SD_BIT] = (legal_data[MSTATUS_FS_HI:MSTATUS_FS_LO] == 2'b11) || - (legal_data[MSTATUS_XS_HI:MSTATUS_XS_LO] == 2'b11) || - (legal_data[MSTATUS_VS_HI:MSTATUS_VS_LO] == 2'b11); - return legal_data; - endfunction - - function automatic logic [XLEN-1:0] legalize_sstatus(input logic [XLEN-1:0] data); - logic [XLEN-1:0] legal_data; - legal_data = '0; - legal_data[SSTATUS_SIE_BIT] = data[SSTATUS_SIE_BIT]; - legal_data[SSTATUS_SPIE_BIT] = data[SSTATUS_SPIE_BIT]; - legal_data[SSTATUS_SPP_BIT] = data[SSTATUS_SPP_BIT]; - legal_data[SSTATUS_VS_HI:SSTATUS_VS_LO] = data[SSTATUS_VS_HI:SSTATUS_VS_LO]; - legal_data[SSTATUS_FS_HI:SSTATUS_FS_LO] = data[SSTATUS_FS_HI:SSTATUS_FS_LO]; - legal_data[SSTATUS_SUM_BIT] = data[SSTATUS_SUM_BIT]; - legal_data[SSTATUS_MXR_BIT] = data[SSTATUS_MXR_BIT]; - legal_data[SSTATUS_UXL_HI:SSTATUS_UXL_LO] = STATUS_XLEN_64; - legal_data[SSTATUS_SD_BIT] = (legal_data[SSTATUS_FS_HI:SSTATUS_FS_LO] == 2'b11) || - (legal_data[SSTATUS_XS_HI:SSTATUS_XS_LO] == 2'b11) || - (legal_data[SSTATUS_VS_HI:SSTATUS_VS_LO] == 2'b11); - return legal_data; - endfunction - - function automatic logic [XLEN-1:0] reset_mstatus_value(); - return legalize_mstatus('0); - endfunction - - function automatic logic [XLEN-1:0] reset_sstatus_value(); - return legalize_sstatus('0); - endfunction - - function automatic logic [XLEN-1:0] set_mstatus_fs_dirty(input logic [XLEN-1:0] current); - logic [XLEN-1:0] next_data; - next_data = current; - next_data[MSTATUS_FS_HI:MSTATUS_FS_LO] = 2'b11; - return legalize_mstatus(next_data); - endfunction - - function automatic logic [XLEN-1:0] set_sstatus_fs_dirty(input logic [XLEN-1:0] current); - logic [XLEN-1:0] next_data; - next_data = current; - next_data[SSTATUS_FS_HI:SSTATUS_FS_LO] = 2'b11; - return legalize_sstatus(next_data); - endfunction - - function automatic logic [XLEN-1:0] legalize_medeleg(input logic [XLEN-1:0] data); - return (data & MEDELEG_WRITABLE_MASK) | MEDELEG_READ_ONLY_ONE_MASK; - endfunction - - function automatic logic [XLEN-1:0] legalize_mideleg(input logic [XLEN-1:0] data); - return data & MIDELEG_WRITABLE_MASK; - endfunction - - function automatic logic [XLEN-1:0] legalize_interrupt_csr(input logic [XLEN-1:0] data); - return data & INTERRUPT_MASK; - endfunction - - function automatic logic [XLEN-1:0] compose_mip(); - logic [XLEN-1:0] pending; - pending = legalize_interrupt_csr(mip_reg); - pending[INTERRUPT_M_SOFTWARE_BIT] |= m_software_interrupt; - pending[INTERRUPT_M_TIMER_BIT] |= m_timer_interrupt; - pending[INTERRUPT_M_EXTERNAL_BIT] |= m_external_interrupt; - pending[INTERRUPT_S_SOFTWARE_BIT] |= s_software_interrupt; - pending[INTERRUPT_S_TIMER_BIT] |= s_timer_interrupt; - pending[INTERRUPT_S_EXTERNAL_BIT] |= s_external_interrupt; - return pending; - endfunction - - function automatic logic [XLEN-1:0] legalize_mcountinhibit(input logic [XLEN-1:0] data); - logic [XLEN-1:0] legal_data; - legal_data = '0; - legal_data[MCOUNTINHIBIT_CY_BIT] = data[MCOUNTINHIBIT_CY_BIT]; - legal_data[MCOUNTINHIBIT_IR_BIT] = data[MCOUNTINHIBIT_IR_BIT]; - legal_data[31:3] = data[31:3]; - return legal_data; - endfunction - - function automatic logic [XLEN-1:0] legalize_counteren(input logic [XLEN-1:0] data); - logic [XLEN-1:0] legal_data; - legal_data = '0; - legal_data[COUNTEREN_CY_BIT] = data[COUNTEREN_CY_BIT]; - legal_data[COUNTEREN_TM_BIT] = data[COUNTEREN_TM_BIT]; - legal_data[COUNTEREN_IR_BIT] = data[COUNTEREN_IR_BIT]; - legal_data[31:3] = data[31:3]; - return legal_data; - endfunction - - function automatic logic [XLEN-1:0] legalize_senvcfg(input logic [XLEN-1:0] data); - logic [XLEN-1:0] legal_data; - legal_data = '0; - legal_data[ENVCFG_FIOM_BIT] = data[ENVCFG_FIOM_BIT]; - legal_data[ENVCFG_CBIE_HI:ENVCFG_CBIE_LO] = - menvcfg_reg[ENVCFG_CBIE_HI:ENVCFG_CBIE_LO] & data[ENVCFG_CBIE_HI:ENVCFG_CBIE_LO]; - legal_data[ENVCFG_CBCFE_BIT] = menvcfg_reg[ENVCFG_CBCFE_BIT] & data[ENVCFG_CBCFE_BIT]; - legal_data[ENVCFG_CBZE_BIT] = menvcfg_reg[ENVCFG_CBZE_BIT] & data[ENVCFG_CBZE_BIT]; - return legal_data; - endfunction - - function automatic logic [XLEN-1:0] legalize_menvcfg(input logic [XLEN-1:0] data); - logic [XLEN-1:0] legal_data; - legal_data = '0; - legal_data[ENVCFG_FIOM_BIT] = data[ENVCFG_FIOM_BIT]; - legal_data[ENVCFG_CBIE_HI:ENVCFG_CBIE_LO] = data[ENVCFG_CBIE_HI:ENVCFG_CBIE_LO]; - legal_data[ENVCFG_CBCFE_BIT] = data[ENVCFG_CBCFE_BIT]; - legal_data[ENVCFG_CBZE_BIT] = data[ENVCFG_CBZE_BIT]; - return legal_data; - endfunction - - function automatic logic [XLEN-1:0] legalize_hstatus(input logic [XLEN-1:0] data); - logic [XLEN-1:0] legal_data; - legal_data = '0; - legal_data[6] = data[6]; // VTSR - legal_data[7] = data[7]; // VTW - legal_data[8] = data[8]; // VTVM - legal_data[12] = data[12]; // SPVP - legal_data[14] = data[14]; // SPV - legal_data[16:15] = data[16:15]; // GVA and VSBE are WARL here. - legal_data[33:32] = STATUS_XLEN_64; // VSXL - return legal_data; - endfunction - - function automatic logic [XLEN-1:0] legalize_mstateen(input int unsigned index, input logic [XLEN-1:0] data); - logic [XLEN-1:0] legal_data; - legal_data = '0; - if (index == 0) begin - legal_data[STATEEN0_ENVCFG_BIT] = data[STATEEN0_ENVCFG_BIT]; - end - legal_data[STATEEN_SE0_BIT] = data[STATEEN_SE0_BIT]; - return legal_data; - endfunction - - function automatic logic [XLEN-1:0] legalize_sstateen(input int unsigned index, input logic [XLEN-1:0] data); - logic [XLEN-1:0] legal_data; - legal_data = '0; - if (index == 0) begin - legal_data[STATEEN0_ENVCFG_BIT] = data[STATEEN0_ENVCFG_BIT] & mstateen_reg[0][STATEEN0_ENVCFG_BIT]; - end - legal_data[STATEEN_SE0_BIT] = data[STATEEN_SE0_BIT] & mstateen_reg[index][STATEEN_SE0_BIT]; - return legal_data; - endfunction - - function automatic logic [XLEN-1:0] legalize_satp( - input logic [XLEN-1:0] data, - input logic [XLEN-1:0] current - ); - logic [XLEN-1:0] legal_data; - logic [3:0] mode; - mode = data[SATP_MODE_HI:SATP_MODE_LO]; - legal_data = current; - - unique case (mode) - SATP_MODE_BARE: begin - legal_data = '0; - end - SATP_MODE_SV39: begin - legal_data = '0; - legal_data[SATP_MODE_HI:SATP_MODE_LO] = SATP_MODE_SV39; - legal_data[SATP_ASID_HI:SATP_ASID_LO] = data[SATP_ASID_HI:SATP_ASID_LO]; - legal_data[SATP_PPN_HI:SATP_PPN_LO] = data[SATP_PPN_HI:SATP_PPN_LO]; - end - SATP_MODE_SV48, - SATP_MODE_SV57, - SATP_MODE_SV64: begin - legal_data = current; - end - default: begin - legal_data = current; - end - endcase - - return legal_data; - endfunction - - function automatic logic [XLEN-1:0] legalize_hgatp( - input logic [XLEN-1:0] data, - input logic [XLEN-1:0] current - ); - logic [XLEN-1:0] legal_data; - logic [3:0] mode; - mode = data[SATP_MODE_HI:SATP_MODE_LO]; - legal_data = current; - - unique case (mode) - SATP_MODE_BARE: legal_data = '0; - SATP_MODE_SV39: legal_data = data; - SATP_MODE_SV48, - SATP_MODE_SV57, - SATP_MODE_SV64: legal_data = current; - default: legal_data = current; - endcase - - return legal_data; - endfunction - - function automatic logic [XLEN-1:0] apply_sstatus_to_mstatus( - input logic [XLEN-1:0] mstatus_cur, - input logic [XLEN-1:0] sstatus_data - ); - logic [XLEN-1:0] next_data; - next_data = mstatus_cur; - next_data[SSTATUS_SIE_BIT] = sstatus_data[SSTATUS_SIE_BIT]; - next_data[SSTATUS_SPIE_BIT] = sstatus_data[SSTATUS_SPIE_BIT]; - next_data[SSTATUS_SPP_BIT] = sstatus_data[SSTATUS_SPP_BIT]; - next_data[SSTATUS_VS_HI:SSTATUS_VS_LO] = sstatus_data[SSTATUS_VS_HI:SSTATUS_VS_LO]; - next_data[SSTATUS_FS_HI:SSTATUS_FS_LO] = sstatus_data[SSTATUS_FS_HI:SSTATUS_FS_LO]; - next_data[SSTATUS_SUM_BIT] = sstatus_data[SSTATUS_SUM_BIT]; - next_data[SSTATUS_MXR_BIT] = sstatus_data[SSTATUS_MXR_BIT]; - next_data[MSTATUS_UXL_HI:MSTATUS_UXL_LO] = STATUS_XLEN_64; - return legalize_mstatus(next_data); - endfunction - - function automatic logic [XLEN-1:0] mirror_mstatus_to_sstatus( - input logic [XLEN-1:0] sstatus_cur, - input logic [XLEN-1:0] mstatus_data - ); - logic [XLEN-1:0] next_data; - next_data = legalize_sstatus(sstatus_cur); - next_data[SSTATUS_SIE_BIT] = mstatus_data[SSTATUS_SIE_BIT]; - next_data[SSTATUS_SPIE_BIT] = mstatus_data[SSTATUS_SPIE_BIT]; - next_data[SSTATUS_SPP_BIT] = mstatus_data[SSTATUS_SPP_BIT]; - next_data[SSTATUS_VS_HI:SSTATUS_VS_LO] = mstatus_data[MSTATUS_VS_HI:MSTATUS_VS_LO]; - next_data[SSTATUS_FS_HI:SSTATUS_FS_LO] = mstatus_data[SSTATUS_FS_HI:SSTATUS_FS_LO]; - next_data[SSTATUS_SUM_BIT] = mstatus_data[SSTATUS_SUM_BIT]; - next_data[SSTATUS_MXR_BIT] = mstatus_data[MSTATUS_MXR_BIT]; - next_data[SSTATUS_UXL_HI:SSTATUS_UXL_LO] = STATUS_XLEN_64; - next_data[SSTATUS_SD_BIT] = mstatus_data[MSTATUS_SD_BIT]; - return legalize_sstatus(next_data); - endfunction - - // Pack the discrete FP status bits into the architectural FCSR layout. - function automatic logic [XLEN-1:0] compose_fcsr(); - logic [XLEN-1:0] value; - value = '0; - value[4:0] = fflags_reg; - value[7:5] = frm_reg; - return value; - endfunction - - function automatic priv_mode_t decode_priv_mode(input logic [1:0] encoded); - case (encoded) - 2'b00: decode_priv_mode = PRIV_MODE_U; - 2'b01: decode_priv_mode = PRIV_MODE_S; - default: decode_priv_mode = PRIV_MODE_M; - endcase - endfunction - - function automatic logic [1:0] encode_priv_mode(input priv_mode_t mode); - case (mode) - PRIV_MODE_U: encode_priv_mode = 2'b00; - PRIV_MODE_S: encode_priv_mode = 2'b01; - default: encode_priv_mode = 2'b11; - endcase - endfunction - - function automatic logic [XLEN-1:0] read_csr(input logic [11:0] addr); - logic [XLEN-1:0] data_out; - if (csr_is_pmpcfg(addr)) begin - data_out = read_pmpcfg_csr(addr); - end else if (csr_is_pmpaddr(addr)) begin - data_out = read_pmpaddr_csr(addr); - end else if (csr_is_mhpmcounter(addr) || csr_is_hpmcounter(addr)) begin - data_out = mhpmcounter_reg[hpmcounter_index(addr)]; - end else if (csr_is_mhpmevent(addr)) begin - data_out = '0; - end else begin - unique case (addr) - CSR_FFLAGS: data_out = {{(XLEN-5){1'b0}}, fflags_reg}; - CSR_FRM: data_out = {{(XLEN-3){1'b0}}, frm_reg}; - CSR_FCSR: data_out = compose_fcsr(); - CSR_MSTATUS: data_out = mstatus_reg; - CSR_MISA: data_out = MISA_VALUE; - CSR_MEDELEG: data_out = medeleg_reg; - CSR_MIDELEG: data_out = mideleg_reg; - CSR_MIE: data_out = mie_reg; - CSR_MTVEC: data_out = mtvec_reg; - CSR_MSCRATCH: data_out = mscratch_reg; - CSR_MEPC: data_out = mepc_reg; - CSR_MCAUSE: data_out = mcause_reg; - CSR_MTVAL: data_out = mtval_reg; - CSR_MIP: data_out = mip_value; - CSR_MTINST: data_out = mtinst_reg; - CSR_MTVAL2: data_out = mtval2_reg; - CSR_MCOUNTEREN: data_out = mcounteren_reg; - CSR_MCOUNTINHIBIT: data_out = mcountinhibit_reg; - CSR_MENVCFG: data_out = menvcfg_reg; - CSR_MSTATEEN0: data_out = mstateen_reg[0]; - CSR_MSTATEEN1: data_out = mstateen_reg[1]; - CSR_MSTATEEN2: data_out = mstateen_reg[2]; - CSR_MSTATEEN3: data_out = mstateen_reg[3]; - CSR_MVENDORID: data_out = VENDOR_ID; - CSR_MARCHID: data_out = ARCH_ID; - CSR_MIMPID: data_out = IMPL_ID; - CSR_MHARTID: data_out = HART_ID; - CSR_MCONFIGPTR: data_out = '0; - CSR_SSTATUS: data_out = sstatus_reg; - CSR_SIE: data_out = sie_value; - CSR_STVEC: data_out = stvec_reg; - CSR_SCOUNTEREN: data_out = scounteren_reg; - CSR_SENVCFG: data_out = legalize_senvcfg(senvcfg_reg); - CSR_SSTATEEN0: data_out = sstateen_reg[0]; - CSR_SSTATEEN1: data_out = sstateen_reg[1]; - CSR_SSTATEEN2: data_out = sstateen_reg[2]; - CSR_SSTATEEN3: data_out = sstateen_reg[3]; - CSR_SSCRATCH: data_out = sscratch_reg; - CSR_SEPC: data_out = sepc_reg; - CSR_SCAUSE: data_out = scause_reg; - CSR_STVAL: data_out = stval_reg; - CSR_SIP: data_out = sip_value; - CSR_SATP: data_out = satp_reg; - CSR_HSTATUS: data_out = hstatus_reg; - CSR_HEDELEG: data_out = hedeleg_reg; - CSR_HIDELEG: data_out = hideleg_reg; - CSR_HIE: data_out = hie_reg; - CSR_HTIMEDELTA: data_out = htimedelta_reg; - CSR_HCOUNTEREN: data_out = hcounteren_reg; - CSR_HGEIE: data_out = hgeie_reg; - CSR_HENVCFG: data_out = henvcfg_reg; - CSR_HSTATEEN0: data_out = hstateen_reg[0]; - CSR_HSTATEEN1: data_out = hstateen_reg[1]; - CSR_HSTATEEN2: data_out = hstateen_reg[2]; - CSR_HSTATEEN3: data_out = hstateen_reg[3]; - CSR_HTVAL: data_out = htval_reg; - CSR_HIP: data_out = hip_reg; - CSR_HVIP: data_out = hvip_reg; - CSR_HTINST: data_out = htinst_reg; - CSR_HGATP: data_out = hgatp_reg; - CSR_HGEIP: data_out = '0; - CSR_VSSTATUS: data_out = vsstatus_reg; - CSR_VSIE: data_out = vsie_reg; - CSR_VSTVEC: data_out = vstvec_reg; - CSR_VSSCRATCH: data_out = vsscratch_reg; - CSR_VSEPC: data_out = vsepc_reg; - CSR_VSCAUSE: data_out = vscause_reg; - CSR_VSTVAL: data_out = vstval_reg; - CSR_VSIP: data_out = vsip_reg; - CSR_VSATP: data_out = vsatp_reg; - CSR_MCYCLE: data_out = mcycle_reg; - CSR_MINSTRET: data_out = minstret_reg; - CSR_CYCLE: data_out = mcycle_reg; - CSR_INSTRET: data_out = minstret_reg; - CSR_TIME: data_out = time_reg; - default: data_out = '0; - endcase - end - return data_out; - endfunction - -endmodule diff --git a/rv64cpu/rtl/core/debug/.gitkeep b/rv64cpu/rtl/core/debug/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/rtl/core/decoder.sv b/rv64cpu/rtl/core/decoder.sv deleted file mode 100644 index c39a637..0000000 --- a/rv64cpu/rtl/core/decoder.sv +++ /dev/null @@ -1,672 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/decoder.sv -// Description: Instruction decoder for RV64IMAFD base ISA + Zicsr + Zifencei -// Translates 32-bit instructions into control signals for the pipeline. -// C is handled by the IF-stage decompressor before this decoder. - -module decoder - import rv64_pkg::*; -( - input logic [31:0] instr, // 32-bit instruction - input logic [1:0] priv_mode, // Current privilege mode from CSR - input logic mstatus_tsr, // Trap SRET in S-mode - input logic mstatus_tw, // Trap WFI in S/U-mode - input logic mstatus_tvm, // Trap supervisor memory-management ops in S-mode - - // Register File - output logic [4:0] rs1_addr, // Source register 1 address - output logic [4:0] rs2_addr, // Source register 2 address - output logic [4:0] rd_addr, // Destination register address - output logic reg_write_en, // Register file write enable - - // ALU Control - output alu_op_t alu_op, // ALU operation select - output logic alu_src1_sel, // 0 = rs1, 1 = PC - output logic alu_src2_sel, // 0 = rs2, 1 = immediate - - // Immediate - output logic [XLEN-1:0] imm, // Sign-extended immediate - - // Memory Access - output logic is_load, // MEM stage: read from memory - output logic is_store, // MEM stage: write to memory - output logic [2:0] mem_size, // funct3 → byte/half/word/double + sign - output logic is_amo, // A extension atomic memory operation - output amo_op_t amo_op, // LR/SC/AMO operation selector - output logic is_fp, - output logic fp_reg_write_en, - output logic fp_store_data_sel, - output fp_op_t fp_op, - output fp_fmt_t fp_fmt, - output logic [2:0] fp_rm, - - // Branch / Jump - output logic is_branch, // EX: conditional branch - output logic is_jump, // EX: unconditional jump (JAL/JALR) - output logic [2:0] branch_op, // funct3 → BEQ/BNE/BLT/BGE/BLTU/BGEU - - // Writeback Source - output logic [1:0] wb_sel, // 0 = ALU result, 1 = MEM data, 2 = PC+4 - - // RV64 Word Operations - output logic is_word_op, // EX: operate on lower 32 bits, sext result - - // System / Privilege - output logic is_csr, // CSR instruction - output logic [2:0] csr_op, // funct3 → CSRRW/CSRRS/CSRRC/CSRRWI/CSRRSI/CSRRCI - output logic is_ecall, // ECALL - output logic is_ebreak, // EBREAK - output logic is_mret, // MRET - output logic is_sret, // SRET - output logic is_wfi, // WFI - output logic is_sfence_vma, // SFENCE.VMA - output logic is_fence, // FENCE - output logic is_fence_i, // FENCE.I (Zifencei) - - // Error - output logic illegal_instr // Unrecognised or malformed instruction -); - - // ============================ - // Instruction Field Extraction - // ============================ - typedef enum logic [1:0] { - PRIV_MODE_U = 2'b00, - PRIV_MODE_S = 2'b01, - PRIV_MODE_M = 2'b11 - } priv_mode_t; - - logic [6:0] opcode; - logic [2:0] funct3; - logic [6:0] funct7; - logic [4:0] amo_funct5; - - assign opcode = instr[6:0]; - assign rd_addr = instr[11:7]; - assign funct3 = instr[14:12]; - assign rs1_addr = (opcode == OPCODE_LUI) ? 5'b00000 : instr[19:15]; - assign rs2_addr = instr[24:20]; - assign funct7 = instr[31:25]; - assign amo_funct5 = instr[31:27]; - - // ============================ - // Immediate Generation - // ============================ - logic [XLEN-1:0] imm_i, imm_s, imm_b, imm_u, imm_j; - logic [11:0] csr_addr_field; - - // I-type: instr[31:20] - assign imm_i = {{52{instr[31]}}, instr[31:20]}; - - // S-type: {instr[31:25], instr[11:7]} - assign imm_s = {{52{instr[31]}}, instr[31:25], instr[11:7]}; - - // B-type: {instr[31], instr[7], instr[30:25], instr[11:8], 1'b0} - assign imm_b = {{51{instr[31]}}, instr[31], instr[7], instr[30:25], instr[11:8], 1'b0}; - - // U-type: {instr[31:12], 12'b0} - assign imm_u = {{32{instr[31]}}, instr[31:12], 12'b0}; - - // J-type: {instr[31], instr[19:12], instr[20], instr[30:21], 1'b0} - assign imm_j = {{43{instr[31]}}, instr[31], instr[19:12], instr[20], instr[30:21], 1'b0}; - assign csr_addr_field = instr[31:20]; - - function automatic int priv_rank(logic [1:0] mode); - case (mode) - PRIV_MODE_U: return 0; - PRIV_MODE_S: return 1; - 2'b10: return 2; // Reserved/H-mode, treat as higher than S - default: return 3; // Machine mode (or anything else) highest - endcase - endfunction - - function automatic logic priv_ge(logic [1:0] cur, logic [1:0] req); - return priv_rank(cur) >= priv_rank(req); - endfunction - - function automatic logic csr_access_allowed(logic [1:0] cur, logic [11:0] addr); - logic [1:0] req_priv; - req_priv = addr[9:8]; - return priv_ge(cur, req_priv); - endfunction - - // ============================ - // Main Decode Logic - // ============================ - always_comb begin - // Default values (all signals inactive) - alu_op = ALU_ADD; - alu_src1_sel = 1'b0; // rs1 - alu_src2_sel = 1'b0; // rs2 - reg_write_en = 1'b0; - imm = '0; - is_load = 1'b0; - is_store = 1'b0; - mem_size = 3'b0; - is_amo = 1'b0; - amo_op = AMO_NONE; - is_fp = 1'b0; - fp_reg_write_en = 1'b0; - fp_store_data_sel = 1'b0; - fp_op = FP_OP_NONE; - fp_fmt = FP_FMT_S; - fp_rm = funct3; - is_branch = 1'b0; - is_jump = 1'b0; - branch_op = 3'b0; - wb_sel = 2'b00; // ALU result - is_word_op = 1'b0; - is_csr = 1'b0; - csr_op = 3'b0; - is_ecall = 1'b0; - is_ebreak = 1'b0; - is_mret = 1'b0; - is_sret = 1'b0; - is_wfi = 1'b0; - is_sfence_vma = 1'b0; - is_fence = 1'b0; - is_fence_i = 1'b0; - illegal_instr = 1'b0; - - case (opcode) - - // LUI — rd = imm_u - - OPCODE_LUI: begin - reg_write_en = 1'b1; - alu_src1_sel = 1'b0; // doesn't matter, ALU_ADD(0, imm_u) - alu_src2_sel = 1'b1; // immediate - imm = imm_u; - alu_op = ALU_ADD; // result = 0 + imm_u (rs1 forced to x0 externally or use alu_src1_sel trick) - // NOTE: EX stage should feed 0 to ALU operand 1 when alu_src1_sel==0 and opcode==LUI. - // Alternative: add a dedicated alu_src1_sel=2 for "zero". For now, the pipeline - // can detect LUI by opcode and zero out the operand. - end - - // AUIPC — rd = PC + imm_u - - - OPCODE_AUIPC: begin - reg_write_en = 1'b1; - alu_src1_sel = 1'b1; // PC - alu_src2_sel = 1'b1; // immediate - imm = imm_u; - alu_op = ALU_ADD; // result = PC + imm_u - end - - // ----------------- - // JAL — rd = PC+4; PC = PC + imm_j - OPCODE_JAL: begin - reg_write_en = 1'b1; - is_jump = 1'b1; - alu_src1_sel = 1'b1; // PC - alu_src2_sel = 1'b1; // immediate - imm = imm_j; - alu_op = ALU_ADD; // target = PC + imm_j - wb_sel = 2'b10; // writeback PC+4 - end - - // ------------------ - // JALR — rd = PC+4; PC = (rs1 + imm_i) & ~1 - - OPCODE_JALR: begin - reg_write_en = 1'b1; - is_jump = 1'b1; - alu_src1_sel = 1'b0; // rs1 - alu_src2_sel = 1'b1; // immediate - imm = imm_i; - alu_op = ALU_ADD; // target = rs1 + imm_i (LSB cleared by EX stage) - wb_sel = 2'b10; // writeback PC+4 - end - - // ----------------- - // Branch — compare rs1, rs2; if taken, PC = PC + imm_b - OPCODE_BRANCH: begin - reg_write_en = 1'b0; // branches never write rd - is_branch = 1'b1; - alu_src1_sel = 1'b1; // PC (ALU computes branch target) - alu_src2_sel = 1'b1; // immediate - imm = imm_b; - alu_op = ALU_ADD; // branch target = PC + imm_b - branch_op = funct3; // BEQ/BNE/BLT/BGE/BLTU/BGEU - end - - // ----------------- - // Load — rd = Mem[rs1 + imm_i] - OPCODE_LOAD: begin - reg_write_en = 1'b1; - is_load = 1'b1; - alu_src1_sel = 1'b0; // rs1 - alu_src2_sel = 1'b1; // immediate - imm = imm_i; - alu_op = ALU_ADD; // effective address = rs1 + imm_i - mem_size = funct3; // LB/LH/LW/LD/LBU/LHU/LWU - wb_sel = 2'b01; // writeback from memory - end - - // ----------------- - // Store — Mem[rs1 + imm_s] = rs2 - OPCODE_STORE: begin - reg_write_en = 1'b0; - is_store = 1'b1; - alu_src1_sel = 1'b0; // rs1 - alu_src2_sel = 1'b1; // immediate (NOT rs2 — rs2 is store data) - imm = imm_s; - alu_op = ALU_ADD; // effective address = rs1 + imm_s - mem_size = funct3; // SB/SH/SW/SD - end - - // ----------------- - // OP-IMM (I-type ALU) — rd = rs1 OP imm - // ADDI, SLTI, SLTIU, XORI, ORI, ANDI, SLLI, SRLI, SRAI - OPCODE_OP_IMM: begin - reg_write_en = 1'b1; - alu_src1_sel = 1'b0; // rs1 - alu_src2_sel = 1'b1; // immediate - imm = imm_i; - - case (funct3) - 3'b000: alu_op = ALU_ADD; // ADDI - 3'b010: alu_op = ALU_SLT; // SLTI - 3'b011: alu_op = ALU_SLTU; // SLTIU - 3'b100: alu_op = ALU_XOR; // XORI - 3'b110: alu_op = ALU_OR; // ORI - 3'b111: alu_op = ALU_AND; // ANDI - 3'b001: alu_op = ALU_SLL; // SLLI - 3'b101: alu_op = (funct7[5]) ? ALU_SRA : ALU_SRL; // SRAI / SRLI - default: alu_op = ALU_ADD; - endcase - end - - // ----------------- - // OP-IMM-32 (I-type ALU, 32-bit word variants for RV64) - // ADDIW, SLLIW, SRLIW, SRAIW - OPCODE_OP_IMM_32: begin - reg_write_en = 1'b1; - is_word_op = 1'b1; // EX stage: operate on lower 32 bits, sext result - alu_src1_sel = 1'b0; // rs1 - alu_src2_sel = 1'b1; // immediate - imm = imm_i; - - case (funct3) - 3'b000: alu_op = ALU_ADD; // ADDIW - 3'b001: alu_op = ALU_SLL; // SLLIW - 3'b101: alu_op = (funct7[5]) ? ALU_SRA : ALU_SRL; // SRAIW / SRLIW - default: begin - alu_op = ALU_ADD; - illegal_instr = 1'b1; - end - endcase - end - - // ----------------- - // OP (R-type ALU) — rd = rs1 OP rs2 - // ADD, SUB, SLL, SLT, SLTU, XOR, SRL, SRA, OR, AND - OPCODE_OP: begin - reg_write_en = 1'b1; - alu_src1_sel = 1'b0; // rs1 - alu_src2_sel = 1'b0; // rs2 - - if (funct7 == 7'b0000001) begin - case (funct3) - 3'b000: alu_op = ALU_MUL; - 3'b001: alu_op = ALU_MULH; - 3'b010: alu_op = ALU_MULHSU; - 3'b011: alu_op = ALU_MULHU; - 3'b100: alu_op = ALU_DIV; - 3'b101: alu_op = ALU_DIVU; - 3'b110: alu_op = ALU_REM; - 3'b111: alu_op = ALU_REMU; - default: illegal_instr = 1'b1; - endcase - end else begin - case (funct3) - 3'b000: alu_op = (funct7[5]) ? ALU_SUB : ALU_ADD; // SUB / ADD - 3'b001: alu_op = ALU_SLL; // SLL - 3'b010: alu_op = ALU_SLT; // SLT - 3'b011: alu_op = ALU_SLTU; // SLTU - 3'b100: alu_op = ALU_XOR; // XOR - 3'b101: alu_op = (funct7[5]) ? ALU_SRA : ALU_SRL; // SRA / SRL - 3'b110: alu_op = ALU_OR; // OR - 3'b111: alu_op = ALU_AND; // AND - default: alu_op = ALU_ADD; - endcase - end - end - - // ----------------- - // OP-32 (R-type ALU, 32-bit word variants for RV64) - // ADDW, SUBW, SLLW, SRLW, SRAW - OPCODE_OP_32: begin - reg_write_en = 1'b1; - is_word_op = 1'b1; - alu_src1_sel = 1'b0; // rs1 - alu_src2_sel = 1'b0; // rs2 - - if (funct7 == 7'b0000001) begin - case (funct3) - 3'b000: alu_op = ALU_MUL; - 3'b100: alu_op = ALU_DIV; - 3'b101: alu_op = ALU_DIVU; - 3'b110: alu_op = ALU_REM; - 3'b111: alu_op = ALU_REMU; - default: illegal_instr = 1'b1; - endcase - end else begin - case (funct3) - 3'b000: alu_op = (funct7[5]) ? ALU_SUB : ALU_ADD; // SUBW / ADDW - 3'b001: alu_op = ALU_SLL; // SLLW - 3'b101: alu_op = (funct7[5]) ? ALU_SRA : ALU_SRL; // SRAW / SRLW - default: begin - alu_op = ALU_ADD; - illegal_instr = 1'b1; - end - endcase - end - end - - // ----------------- - // MISC-MEM — FENCE / FENCE.I - OPCODE_MISC_MEM: begin - case (funct3) - 3'b000: is_fence = 1'b1; // FENCE (NOP in single-core in-order) - 3'b001: is_fence_i = 1'b1; // FENCE.I — flush I-Cache - default: illegal_instr = 1'b1; - endcase - end - - // ----------------- - // SYSTEM — ECALL / EBREAK / xRET / WFI / SFENCE.VMA / CSR* - OPCODE_SYSTEM: begin - if (funct3 == 3'b000) begin - // Privileged instructions (non-CSR) - reg_write_en = 1'b0; - case ({funct7, instr[24:20]}) // {funct7, rs2} - 12'b0000000_00000: is_ecall = 1'b1; // ECALL - 12'b0000000_00001: is_ebreak = 1'b1; // EBREAK - 12'b0011000_00010: begin // MRET - if (priv_mode == PRIV_MODE_M) - is_mret = 1'b1; - else - illegal_instr = 1'b1; - end - 12'b0001000_00010: begin // SRET - if (!priv_ge(priv_mode, PRIV_MODE_S)) - illegal_instr = 1'b1; - else if ((priv_mode == PRIV_MODE_S) && mstatus_tsr) - illegal_instr = 1'b1; - else - is_sret = 1'b1; - end - 12'b0001000_00101: begin // WFI - if ((priv_mode != PRIV_MODE_M) && mstatus_tw) - illegal_instr = 1'b1; - else - is_wfi = 1'b1; - end - default: begin - if (funct7 == 7'b0001001) begin - if (!priv_ge(priv_mode, PRIV_MODE_S)) - illegal_instr = 1'b1; - else if ((priv_mode == PRIV_MODE_S) && mstatus_tvm) - illegal_instr = 1'b1; - else - is_sfence_vma = 1'b1; // SFENCE.VMA - end else begin - illegal_instr = 1'b1; - end - end - endcase - end else begin - // CSR instructions - // funct3: 001=CSRRW, 010=CSRRS, 011=CSRRC, - // 101=CSRRWI, 110=CSRRSI, 111=CSRRCI - reg_write_en = 1'b1; - is_csr = 1'b1; - csr_op = funct3; - imm = imm_i; // CSR address = instr[31:20] (reuse imm_i path) - if (!csr_access_allowed(priv_mode, csr_addr_field)) begin - reg_write_en = 1'b0; - is_csr = 1'b0; - illegal_instr = 1'b1; - end - end - end - - // ----------------- - // AMO — A extension (LR/SC/AMO*) - OPCODE_AMO: begin - reg_write_en = 1'b1; - is_amo = 1'b1; - alu_src1_sel = 1'b0; - alu_src2_sel = 1'b1; - imm = '0; - alu_op = ALU_ADD; - wb_sel = 2'b01; - - unique case (funct3) - 3'b010: mem_size = 3'b010; // AMO*.W - 3'b011: mem_size = 3'b011; // AMO*.D - default: illegal_instr = 1'b1; - endcase - - unique case (amo_funct5) - 5'b00010: begin - amo_op = AMO_LR; - if (rs2_addr != 5'd0) begin - illegal_instr = 1'b1; - end - end - 5'b00011: amo_op = AMO_SC; - 5'b00001: amo_op = AMO_SWAP; - 5'b00000: amo_op = AMO_ADD; - 5'b00100: amo_op = AMO_XOR; - 5'b01100: amo_op = AMO_AND; - 5'b01000: amo_op = AMO_OR; - 5'b10000: amo_op = AMO_MIN; - 5'b10100: amo_op = AMO_MAX; - 5'b11000: amo_op = AMO_MINU; - 5'b11100: amo_op = AMO_MAXU; - default: begin - amo_op = AMO_NONE; - illegal_instr = 1'b1; - end - endcase - - if (illegal_instr) begin - reg_write_en = 1'b0; - is_amo = 1'b0; - amo_op = AMO_NONE; - end - end - - // ----------------- - // Floating-Point — F/D extensions - OPCODE_LOAD_FP: begin - is_fp = 1'b1; - is_load = 1'b1; - fp_reg_write_en = 1'b1; - alu_src1_sel = 1'b0; - alu_src2_sel = 1'b1; - imm = imm_i; - alu_op = ALU_ADD; - wb_sel = 2'b01; - unique case (funct3) - 3'b010: begin - mem_size = 3'b010; // FLW - fp_fmt = FP_FMT_S; - end - 3'b011: begin - mem_size = 3'b011; // FLD - fp_fmt = FP_FMT_D; - end - default: begin - is_fp = 1'b0; - fp_reg_write_en = 1'b0; - illegal_instr = 1'b1; - end - endcase - end - - OPCODE_STORE_FP: begin - is_fp = 1'b1; - is_store = 1'b1; - fp_store_data_sel = 1'b1; - alu_src1_sel = 1'b0; - alu_src2_sel = 1'b1; - imm = imm_s; - alu_op = ALU_ADD; - unique case (funct3) - 3'b010: begin - mem_size = 3'b010; // FSW - fp_fmt = FP_FMT_S; - end - 3'b011: begin - mem_size = 3'b011; // FSD - fp_fmt = FP_FMT_D; - end - default: begin - is_fp = 1'b0; - fp_store_data_sel = 1'b0; - illegal_instr = 1'b1; - end - endcase - end - - OPCODE_MADD, - OPCODE_MSUB, - OPCODE_NMSUB, - OPCODE_NMADD: begin - is_fp = 1'b1; - fp_reg_write_en = 1'b1; - fp_rm = funct3; - unique case (instr[26:25]) - 2'b00: fp_fmt = FP_FMT_S; - 2'b01: fp_fmt = FP_FMT_D; - default: illegal_instr = 1'b1; - endcase - unique case (opcode) - OPCODE_MADD: fp_op = FP_OP_MADD; - OPCODE_MSUB: fp_op = FP_OP_MSUB; - OPCODE_NMSUB: fp_op = FP_OP_NMSUB; - default: fp_op = FP_OP_NMADD; - endcase - if (illegal_instr) begin - is_fp = 1'b0; - fp_reg_write_en = 1'b0; - fp_op = FP_OP_NONE; - end - end - - OPCODE_OP_FP: begin - is_fp = 1'b1; - fp_rm = funct3; - unique case (instr[26:25]) - 2'b00: fp_fmt = FP_FMT_S; - 2'b01: fp_fmt = FP_FMT_D; - default: illegal_instr = 1'b1; - endcase - - unique case (funct7) - 7'b0000000, 7'b0000001: begin fp_op = FP_OP_ADD; fp_reg_write_en = 1'b1; end - 7'b0000100, 7'b0000101: begin fp_op = FP_OP_SUB; fp_reg_write_en = 1'b1; end - 7'b0001000, 7'b0001001: begin fp_op = FP_OP_MUL; fp_reg_write_en = 1'b1; end - 7'b0001100, 7'b0001101: begin fp_op = FP_OP_DIV; fp_reg_write_en = 1'b1; end - 7'b0101100, 7'b0101101: begin - fp_op = FP_OP_SQRT; - fp_reg_write_en = 1'b1; - if (rs2_addr != 5'd0) illegal_instr = 1'b1; - end - 7'b0010000, 7'b0010001: begin - fp_reg_write_en = 1'b1; - unique case (funct3) - 3'b000: fp_op = FP_OP_SGNJ; - 3'b001: fp_op = FP_OP_SGNJN; - 3'b010: fp_op = FP_OP_SGNJX; - default: illegal_instr = 1'b1; - endcase - end - 7'b0010100, 7'b0010101: begin - fp_reg_write_en = 1'b1; - unique case (funct3) - 3'b000: fp_op = FP_OP_MIN; - 3'b001: fp_op = FP_OP_MAX; - default: illegal_instr = 1'b1; - endcase - end - 7'b0100000, 7'b0100001: begin - fp_op = FP_OP_CVT_F2F; - fp_reg_write_en = 1'b1; - end - 7'b1010000, 7'b1010001: begin - reg_write_en = 1'b1; - unique case (funct3) - 3'b010: fp_op = FP_OP_EQ; - 3'b001: fp_op = FP_OP_LT; - 3'b000: fp_op = FP_OP_LE; - default: illegal_instr = 1'b1; - endcase - end - 7'b1100000, 7'b1100001: begin - fp_op = FP_OP_CVT_F2I; - reg_write_en = 1'b1; - end - 7'b1101000, 7'b1101001: begin - fp_op = FP_OP_CVT_I2F; - fp_reg_write_en = 1'b1; - end - 7'b1110000, 7'b1110001: begin - if (rs2_addr == 5'd0 && funct3 == 3'b000) begin - fp_op = FP_OP_MV_F2X; - reg_write_en = 1'b1; - end else if (rs2_addr == 5'd0 && funct3 == 3'b001) begin - fp_op = FP_OP_CLASS; - reg_write_en = 1'b1; - end else begin - illegal_instr = 1'b1; - end - end - 7'b1111000, 7'b1111001: begin - if (rs2_addr == 5'd0 && funct3 == 3'b000) begin - fp_op = FP_OP_MV_X2F; - fp_reg_write_en = 1'b1; - end else begin - illegal_instr = 1'b1; - end - end - default: illegal_instr = 1'b1; - endcase - - if (illegal_instr) begin - is_fp = 1'b0; - fp_reg_write_en = 1'b0; - reg_write_en = 1'b0; - fp_op = FP_OP_NONE; - end - end - - // ----------------- - // Unrecognised opcode - default: begin - illegal_instr = 1'b1; - end - - endcase - end - -endmodule diff --git a/rv64cpu/rtl/core/ex_mem_reg.sv b/rv64cpu/rtl/core/ex_mem_reg.sv deleted file mode 100644 index b8e5b9e..0000000 --- a/rv64cpu/rtl/core/ex_mem_reg.sv +++ /dev/null @@ -1,281 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/ex_mem_reg.sv -// Description: Execute to Memory Pipeline Register -// Latches results from the EX stage and bypassed control signals from ID/EX. -// Supports stall and flush operations. - -module ex_mem_reg - import rv64_pkg::*; -( - input logic clk, - input logic rst, - - // Pipeline Control - input logic stall_en, - input logic flush_en, - - // ---------------------- - // Inputs from EX Stage (computed results) - input logic [XLEN-1:0] ex_alu_result, - input logic ex_branch_taken, - input logic [XLEN-1:0] ex_branch_target, - - // ---------------------- - // Bypassed Inputs from ID/EX Register - - // Data/Operands - input logic [XLEN-1:0] ex_rs1_rdata, // Needed for CSRRW/RS/RC - input logic [XLEN-1:0] ex_rs2_rdata, // Needed for store data - input logic [XLEN-1:0] ex_fs1_rdata, - input logic [XLEN-1:0] ex_fs2_rdata, - input logic [XLEN-1:0] ex_fs3_rdata, - input logic [4:0] ex_rs1_addr, // Serves as zimm for CSRRWI/SI/CI - input logic [4:0] ex_fs3_addr, - input logic [XLEN-1:0] ex_pc, // PC passed along for exceptions/CSRs - input logic [XLEN-1:0] ex_imm, // Imm passed for CSR writes - input logic [INST_WIDTH-1:0] ex_instr, - input logic [2:0] ex_instr_len, - - // Memory Control - input logic ex_is_load, - input logic ex_is_store, - input logic [2:0] ex_mem_size, - input logic ex_is_amo, - input amo_op_t ex_amo_op, - input logic ex_is_fp, - input logic ex_fp_reg_write_en, - input logic ex_fp_store_data_sel, - input fp_op_t ex_fp_op, - input fp_fmt_t ex_fp_fmt, - input logic [2:0] ex_fp_rm, - input logic [XLEN-1:0] ex_fp_result, - input logic [4:0] ex_fp_fflags, - - // Writeback Control - input logic ex_reg_write_en, - input logic [4:0] ex_rd_addr, - input logic [1:0] ex_wb_sel, - - // System / Exception Control - input logic ex_is_csr, - input logic [2:0] ex_csr_op, - input logic ex_illegal_instr, - input logic ex_instr_access_fault, - input logic ex_instr_page_fault, - input logic ex_is_ecall, - input logic ex_is_ebreak, - input logic ex_is_mret, - input logic ex_is_sret, - input logic ex_is_wfi, - input logic ex_is_sfence_vma, - input logic ex_is_fence, - input logic ex_is_fence_i, - - - // ---------------------- - // Outputs to MEM Stage - - // Data/Operands - output logic [XLEN-1:0] mem_alu_result, - output logic mem_branch_taken, - output logic [XLEN-1:0] mem_branch_target, - output logic [XLEN-1:0] mem_rs1_rdata, - output logic [XLEN-1:0] mem_rs2_rdata, - output logic [XLEN-1:0] mem_fs1_rdata, - output logic [XLEN-1:0] mem_fs2_rdata, - output logic [XLEN-1:0] mem_fs3_rdata, - output logic [4:0] mem_rs1_addr, - output logic [4:0] mem_fs3_addr, - output logic [XLEN-1:0] mem_pc, - output logic [XLEN-1:0] mem_imm, - output logic [INST_WIDTH-1:0] mem_instr, - output logic [2:0] mem_instr_len, - - // Memory Control - output logic mem_is_load, - output logic mem_is_store, - output logic [2:0] mem_mem_size, - output logic mem_is_amo, - output amo_op_t mem_amo_op, - output logic mem_is_fp, - output logic mem_fp_reg_write_en, - output logic mem_fp_store_data_sel, - output fp_op_t mem_fp_op, - output fp_fmt_t mem_fp_fmt, - output logic [2:0] mem_fp_rm, - output logic [XLEN-1:0] mem_fp_result, - output logic [4:0] mem_fp_fflags, - - // Writeback Control - output logic mem_reg_write_en, - output logic [4:0] mem_rd_addr, - output logic [1:0] mem_wb_sel, - - // System / Exception Control - output logic mem_is_csr, - output logic [2:0] mem_csr_op, - output logic mem_illegal_instr, - output logic mem_instr_access_fault, - output logic mem_instr_page_fault, - output logic mem_is_ecall, - output logic mem_is_ebreak, - output logic mem_is_mret, - output logic mem_is_sret, - output logic mem_is_wfi, - output logic mem_is_sfence_vma, - output logic mem_is_fence, - output logic mem_is_fence_i -); - - always_ff @(posedge clk) begin - if (rst) begin - mem_instr <= '0; - mem_instr_len <= 3'd4; - mem_alu_result <= '0; - mem_branch_taken <= '0; - mem_branch_target <= '0; - mem_rs1_rdata <= '0; - mem_rs2_rdata <= '0; - mem_fs1_rdata <= '0; - mem_fs2_rdata <= '0; - mem_fs3_rdata <= '0; - mem_rs1_addr <= '0; - mem_fs3_addr <= '0; - mem_pc <= '0; - mem_imm <= '0; - - // Critical control signals must be zeroed out - mem_is_load <= '0; - mem_is_store <= '0; - mem_mem_size <= '0; - mem_is_amo <= '0; - mem_amo_op <= AMO_NONE; - mem_is_fp <= '0; - mem_fp_reg_write_en <= '0; - mem_fp_store_data_sel <= '0; - mem_fp_op <= FP_OP_NONE; - mem_fp_fmt <= FP_FMT_S; - mem_fp_rm <= '0; - mem_fp_result <= '0; - mem_fp_fflags <= '0; - mem_reg_write_en <= '0; - mem_rd_addr <= '0; - mem_wb_sel <= '0; - - // Exceptions and system control - mem_is_csr <= '0; - mem_csr_op <= '0; - mem_illegal_instr <= '0; - mem_instr_access_fault <= '0; - mem_instr_page_fault <= '0; - mem_is_ecall <= '0; - mem_is_ebreak <= '0; - mem_is_mret <= '0; - mem_is_sret <= '0; - mem_is_wfi <= '0; - mem_is_sfence_vma <= '0; - mem_is_fence <= '0; - mem_is_fence_i <= '0; - - end else if (flush_en) begin - mem_instr <= '0; - mem_instr_len <= 3'd4; - // Critical control signals must be zeroed out - mem_branch_taken <= '0; - mem_is_load <= '0; - mem_is_store <= '0; - mem_mem_size <= '0; - mem_is_amo <= '0; - mem_amo_op <= AMO_NONE; - mem_is_fp <= '0; - mem_fp_reg_write_en <= '0; - mem_fp_store_data_sel <= '0; - mem_fp_op <= FP_OP_NONE; - mem_fp_fmt <= FP_FMT_S; - mem_fp_rm <= '0; - mem_fp_result <= '0; - mem_fp_fflags <= '0; - mem_reg_write_en <= '0; - mem_rd_addr <= '0; - mem_wb_sel <= '0; - - // Exceptions and system control - mem_is_csr <= '0; - mem_csr_op <= '0; - mem_illegal_instr <= '0; - mem_instr_access_fault <= '0; - mem_instr_page_fault <= '0; - mem_is_ecall <= '0; - mem_is_ebreak <= '0; - mem_is_mret <= '0; - mem_is_sret <= '0; - mem_is_wfi <= '0; - mem_is_sfence_vma <= '0; - mem_is_fence <= '0; - mem_is_fence_i <= '0; - end else if (~stall_en) begin - mem_alu_result <= ex_alu_result; - mem_branch_taken <= ex_branch_taken; - mem_branch_target <= ex_branch_target; - mem_rs1_rdata <= ex_rs1_rdata; - mem_rs2_rdata <= ex_rs2_rdata; - mem_fs1_rdata <= ex_fs1_rdata; - mem_fs2_rdata <= ex_fs2_rdata; - mem_fs3_rdata <= ex_fs3_rdata; - mem_rs1_addr <= ex_rs1_addr; - mem_fs3_addr <= ex_fs3_addr; - mem_pc <= ex_pc; - mem_imm <= ex_imm; - mem_instr <= ex_instr; - mem_instr_len <= ex_instr_len; - - mem_is_load <= ex_is_load; - mem_is_store <= ex_is_store; - mem_mem_size <= ex_mem_size; - mem_is_amo <= ex_is_amo; - mem_amo_op <= ex_amo_op; - mem_is_fp <= ex_is_fp; - mem_fp_reg_write_en <= ex_fp_reg_write_en; - mem_fp_store_data_sel <= ex_fp_store_data_sel; - mem_fp_op <= ex_fp_op; - mem_fp_fmt <= ex_fp_fmt; - mem_fp_rm <= ex_fp_rm; - mem_fp_result <= ex_fp_result; - mem_fp_fflags <= ex_fp_fflags; - mem_reg_write_en <= ex_reg_write_en; - mem_rd_addr <= ex_rd_addr; - mem_wb_sel <= ex_wb_sel; - - mem_is_csr <= ex_is_csr; - mem_csr_op <= ex_csr_op; - mem_illegal_instr <= ex_illegal_instr; - mem_instr_access_fault <= ex_instr_access_fault; - mem_instr_page_fault <= ex_instr_page_fault; - mem_is_ecall <= ex_is_ecall; - mem_is_ebreak <= ex_is_ebreak; - mem_is_mret <= ex_is_mret; - mem_is_sret <= ex_is_sret; - mem_is_wfi <= ex_is_wfi; - mem_is_sfence_vma <= ex_is_sfence_vma; - mem_is_fence <= ex_is_fence; - mem_is_fence_i <= ex_is_fence_i; - end - // If stall_en && !flush_en, maintain current state - end - -endmodule diff --git a/rv64cpu/rtl/core/ex_stage.sv b/rv64cpu/rtl/core/ex_stage.sv deleted file mode 100644 index 287bfd7..0000000 --- a/rv64cpu/rtl/core/ex_stage.sv +++ /dev/null @@ -1,120 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/ex_stage.sv -// Description: Execute Stage -// Performs ALU operations, branch/jump calculations, and generates memory addresses. - -module ex_stage - import rv64_pkg::*; -( - // Input from ID/EX level pipeline register - input logic [XLEN-1:0] ex_pc, - input logic [XLEN-1:0] ex_rs1_rdata, - input logic [XLEN-1:0] ex_rs2_rdata, - input logic [XLEN-1:0] ex_imm, - - // Execution Control Signals - input alu_op_t ex_alu_op, - input logic ex_alu_src1_sel, // 0: rs1_rdata, 1: PC - input logic ex_alu_src2_sel, // 0: rs2_rdata, 1: imm - input logic ex_is_branch, - input branch_op_t ex_branch_op, - input logic ex_is_jump, - input logic ex_is_word_op, - - // Output to EX/MEM pipeline register & hazard unit - output logic [XLEN-1:0] ex_alu_result, - output logic ex_branch_taken, - output logic [XLEN-1:0] ex_branch_target -); - - logic [XLEN-1:0] alu_mux_a; - logic [XLEN-1:0] alu_mux_b; - logic [XLEN-1:0] alu_operand_a; - logic [XLEN-1:0] alu_operand_b; - logic [XLEN-1:0] alu_raw_result; - logic branch_unit_taken; - - // ------------- - // ALU Input Multiplexers (MUX) - assign alu_mux_a = (ex_alu_src1_sel == 1'b1) ? ex_pc : ex_rs1_rdata; - assign alu_mux_b = (ex_alu_src2_sel == 1'b1) ? ex_imm : ex_rs2_rdata; - - // ------------- - // RV64 Word Operation (32-bit) Pre-conditioning - // RV64 requires word operations (ADDW/SLLW/SRLW/SRAW) to operate on the - // lower 32 bits and sign-extend the result. - always_comb begin - if (ex_is_word_op) begin - // Shift operations require specific 32-bit extension forms - if (ex_alu_op == ALU_SRA) - alu_operand_a = { {32{alu_mux_a[31]}}, alu_mux_a[31:0] }; // Sign extend for arithmetic shift - else if ((ex_alu_op == ALU_SRL) || (ex_alu_op == ALU_DIVU) || (ex_alu_op == ALU_REMU)) - alu_operand_a = { 32'b0, alu_mux_a[31:0] }; // Zero extend for logical shift - else if ((ex_alu_op == ALU_DIV) || (ex_alu_op == ALU_REM)) - alu_operand_a = { {32{alu_mux_a[31]}}, alu_mux_a[31:0] }; - else - alu_operand_a = alu_mux_a; // ADDW/SUBW/SLLW don't care about upper bits - - // RV32 shift amount is strictly 5 bits (0-31), ignoring bit 5 which might be set in 64-bit registers - if (ex_alu_op == ALU_SLL || ex_alu_op == ALU_SRL || ex_alu_op == ALU_SRA) - alu_operand_b = { 59'b0, alu_mux_b[4:0] }; - else if ((ex_alu_op == ALU_DIVU) || (ex_alu_op == ALU_REMU)) - alu_operand_b = { 32'b0, alu_mux_b[31:0] }; - else if ((ex_alu_op == ALU_DIV) || (ex_alu_op == ALU_REM)) - alu_operand_b = { {32{alu_mux_b[31]}}, alu_mux_b[31:0] }; - else - alu_operand_b = alu_mux_b; - end else begin - // 64-bit operations pass through unchanged - alu_operand_a = alu_mux_a; - alu_operand_b = alu_mux_b; - end - end - - // -------------- - // Instantiate the core computing engine ALU - alu alu_inst ( - .alu_op (ex_alu_op), - .operand_a (alu_operand_a), - .operand_b (alu_operand_b), - .alu_result (alu_raw_result) - ); - - // ------------- - // Branch Unit - branch_unit bu_inst ( - .is_branch (ex_is_branch), - .branch_op (ex_branch_op), - .operand_a (ex_rs1_rdata), - .operand_b (ex_rs2_rdata), - .branch_taken (branch_unit_taken) - ); - - // ------------- - // 32-bit instruction result truncation and sign extension - assign ex_alu_result = ex_is_word_op ? - { {32{alu_raw_result[31]}}, alu_raw_result[31:0] } : - alu_raw_result; - - // ------------- - // Branch Target Calculation - assign ex_branch_target = alu_raw_result & ~64'b1; - - assign ex_branch_taken = ex_is_jump | (ex_is_branch & branch_unit_taken); - -endmodule diff --git a/rv64cpu/rtl/core/execute/alu.sv b/rv64cpu/rtl/core/execute/alu.sv new file mode 100644 index 0000000..08635ce --- /dev/null +++ b/rv64cpu/rtl/core/execute/alu.sv @@ -0,0 +1,123 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/alu.sv +// Description: 64-bit Arithmetic Logic Unit (ALU) + +module alu + import rv64_pkg::*; +( + input alu_op_t alu_op, // ALU operation select (from decoder) + input logic [XLEN-1:0] operand_a, // Operand 1 (register rs1 value or PC) + input logic [XLEN-1:0] operand_b, // Operand 2 (register rs2 value or immediate) + + output logic [XLEN-1:0] alu_result // ALU result +); + + // Shift amount: RV64I uses lower 6 bits of rs2 (shamt[5:0]) + logic [5:0] shamt; + assign shamt = operand_b[5:0]; + + logic signed [XLEN-1:0] operand_a_signed; + logic signed [XLEN-1:0] operand_b_signed; + logic signed [127:0] mul_signed_signed; + logic signed [127:0] mul_signed_unsigned; + logic [127:0] mul_unsigned_unsigned; + + assign operand_a_signed = operand_a; + assign operand_b_signed = operand_b; + assign mul_signed_signed = operand_a_signed * operand_b_signed; + assign mul_signed_unsigned = operand_a_signed * $signed({1'b0, operand_b}); + assign mul_unsigned_unsigned = operand_a * operand_b; + + always_comb begin + alu_result = '0; + + case (alu_op) + ALU_ADD: alu_result = operand_a + operand_b; + ALU_SUB: alu_result = operand_a - operand_b; + + // Logical left shift: the shift amount is the lower 6 bits of rs2 + ALU_SLL: alu_result = operand_a << shamt; + + // Set less than (signed) + ALU_SLT: alu_result = {63'b0, $signed(operand_a) < $signed(operand_b)}; + + // Set less than (unsigned) + ALU_SLTU: alu_result = {63'b0, operand_a < operand_b}; + + ALU_XOR: alu_result = operand_a ^ operand_b; + + // Logical right shift: the shift amount is the lower 6 bits of rs2 + ALU_SRL: alu_result = operand_a >> shamt; + + // Arithmetic right shift: the shift amount is the lower 6 bits of rs2 + ALU_SRA: alu_result = $unsigned($signed(operand_a) >>> shamt); + + ALU_OR: alu_result = operand_a | operand_b; + ALU_AND: alu_result = operand_a & operand_b; + + ALU_MUL: begin + alu_result = mul_signed_signed[XLEN-1:0]; + end + + ALU_MULH: begin + alu_result = mul_signed_signed[127:64]; + end + + ALU_MULHSU: begin + alu_result = mul_signed_unsigned[127:64]; + end + + ALU_MULHU: begin + alu_result = mul_unsigned_unsigned[127:64]; + end + + ALU_DIV: begin + if (operand_b == '0) begin + alu_result = {XLEN{1'b1}}; + end else if ((operand_a == 64'h8000_0000_0000_0000) && + (operand_b == 64'hFFFF_FFFF_FFFF_FFFF)) begin + alu_result = operand_a; + end else begin + alu_result = $unsigned(operand_a_signed / operand_b_signed); + end + end + + ALU_DIVU: begin + alu_result = (operand_b == '0) ? {XLEN{1'b1}} : (operand_a / operand_b); + end + + ALU_REM: begin + if (operand_b == '0) begin + alu_result = operand_a; + end else if ((operand_a == 64'h8000_0000_0000_0000) && + (operand_b == 64'hFFFF_FFFF_FFFF_FFFF)) begin + alu_result = '0; + end else begin + alu_result = $unsigned(operand_a_signed % operand_b_signed); + end + end + + ALU_REMU: begin + alu_result = (operand_b == '0) ? operand_a : (operand_a % operand_b); + end + + default: alu_result = '0; + endcase + end + +endmodule diff --git a/rv64cpu/rtl/core/execute/branch_unit.sv b/rv64cpu/rtl/core/execute/branch_unit.sv new file mode 100644 index 0000000..c9662ce --- /dev/null +++ b/rv64cpu/rtl/core/execute/branch_unit.sv @@ -0,0 +1,52 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/branch_unit.sv +// Description: Branch Unit, responsible for determining whether conditional jump instructions are effective + +module branch_unit + import rv64_pkg::*; +( + input logic is_branch, // Indicates whether the current instruction is a branch instruction + input logic [2:0] branch_op, // Specific branch types func3 + input logic [XLEN-1:0] operand_a, // operand 1 + input logic [XLEN-1:0] operand_b, // operand 2 + output logic branch_taken // Branch taken signal +); + + always_comb begin + branch_taken = 1'b0; + + if (is_branch) begin + case (branch_op) + // 3'b000: BEQ (Branch if Equal) + BRANCH_EQ: branch_taken = (operand_a == operand_b); + // 3'b001: BNE (Branch if Not Equal) + BRANCH_NE: branch_taken = (operand_a != operand_b); + // 3'b100: BLT (Branch if Less Than, Signed) + BRANCH_LT: branch_taken = ($signed(operand_a) < $signed(operand_b)); + // 3'b101: BGE (Branch if Greater Than or Equal, Signed) + BRANCH_GE: branch_taken = ($signed(operand_a) >= $signed(operand_b)); + // 3'b110: BLTU (Branch if Less Than, Unsigned) + BRANCH_LTU: branch_taken = (operand_a < operand_b); + // 3'b111: BGEU (Branch if Greater Than or Equal, Unsigned) + BRANCH_GEU: branch_taken = (operand_a >= operand_b); + default: branch_taken = 1'b0; + endcase + end + end + +endmodule diff --git a/rv64cpu/rtl/core/execute/compressed_decoder.sv b/rv64cpu/rtl/core/execute/compressed_decoder.sv new file mode 100644 index 0000000..ccbb043 --- /dev/null +++ b/rv64cpu/rtl/core/execute/compressed_decoder.sv @@ -0,0 +1,234 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// File path: rtl/core/compressed_decoder.sv +// Description: RV64C/Zca decompressor. Produces an equivalent 32-bit instruction +// for the existing RV64 pipeline and flags reserved encodings. + +module compressed_decoder + import rv64_pkg::*; +( + input logic [15:0] compressed_instr, + output logic [31:0] expanded_instr, + output logic illegal_instr +); + + logic [1:0] quadrant; + logic [2:0] funct3; + logic [4:0] rd_rs1; + logic [4:0] rs2; + logic [4:0] rd_prime; + logic [4:0] rs1_prime; + logic [4:0] rs2_prime; + + assign quadrant = compressed_instr[1:0]; + assign funct3 = compressed_instr[15:13]; + assign rd_rs1 = compressed_instr[11:7]; + assign rs2 = compressed_instr[6:2]; + assign rd_prime = {2'b01, compressed_instr[4:2]}; + assign rs1_prime = {2'b01, compressed_instr[9:7]}; + assign rs2_prime = {2'b01, compressed_instr[4:2]}; + + function automatic logic [31:0] r_instr( + input logic [6:0] funct7, + input logic [4:0] rs2_i, + input logic [4:0] rs1_i, + input logic [2:0] funct3_i, + input logic [4:0] rd_i, + input logic [6:0] opcode_i + ); + return {funct7, rs2_i, rs1_i, funct3_i, rd_i, opcode_i}; + endfunction + + function automatic logic [31:0] i_instr( + input logic [11:0] imm_i, + input logic [4:0] rs1_i, + input logic [2:0] funct3_i, + input logic [4:0] rd_i, + input logic [6:0] opcode_i + ); + return {imm_i, rs1_i, funct3_i, rd_i, opcode_i}; + endfunction + + function automatic logic [31:0] s_instr( + input logic [11:0] imm_i, + input logic [4:0] rs2_i, + input logic [4:0] rs1_i, + input logic [2:0] funct3_i, + input logic [6:0] opcode_i + ); + return {imm_i[11:5], rs2_i, rs1_i, funct3_i, imm_i[4:0], opcode_i}; + endfunction + + function automatic logic [31:0] b_instr( + input logic [12:0] imm_i, + input logic [4:0] rs2_i, + input logic [4:0] rs1_i, + input logic [2:0] funct3_i + ); + return {imm_i[12], imm_i[10:5], rs2_i, rs1_i, funct3_i, + imm_i[4:1], imm_i[11], OPCODE_BRANCH}; + endfunction + + function automatic logic [31:0] u_instr( + input logic [19:0] imm_i, + input logic [4:0] rd_i, + input logic [6:0] opcode_i + ); + return {imm_i, rd_i, opcode_i}; + endfunction + + function automatic logic [31:0] j_instr( + input logic [20:0] imm_i, + input logic [4:0] rd_i + ); + return {imm_i[20], imm_i[10:1], imm_i[11], imm_i[19:12], rd_i, OPCODE_JAL}; + endfunction + + always_comb begin + logic [11:0] ci_imm; + logic [11:0] addi4spn_imm; + logic [11:0] lw_imm; + logic [11:0] ld_imm; + logic [11:0] lwsp_imm; + logic [11:0] ldsp_imm; + logic [11:0] swsp_imm; + logic [11:0] sdsp_imm; + logic [12:0] branch_imm; + logic [20:0] jump_imm; + logic [5:0] shamt; + logic [19:0] lui_imm; + + ci_imm = {{6{compressed_instr[12]}}, compressed_instr[12], compressed_instr[6:2]}; + addi4spn_imm = {2'b00, compressed_instr[10:7], compressed_instr[12:11], + compressed_instr[5], compressed_instr[6], 2'b00}; + lw_imm = {5'b00000, compressed_instr[5], compressed_instr[12:10], + compressed_instr[6], 2'b00}; + ld_imm = {4'b0000, compressed_instr[6:5], compressed_instr[12:10], 3'b000}; + lwsp_imm = {4'b0000, compressed_instr[3:2], compressed_instr[12], + compressed_instr[6:4], 2'b00}; + ldsp_imm = {3'b000, compressed_instr[4:2], compressed_instr[12], + compressed_instr[6:5], 3'b000}; + swsp_imm = {4'b0000, compressed_instr[8:7], compressed_instr[12:9], 2'b00}; + sdsp_imm = {3'b000, compressed_instr[9:7], compressed_instr[12:10], 3'b000}; + branch_imm = {{4{compressed_instr[12]}}, compressed_instr[12], + compressed_instr[6:5], compressed_instr[2], + compressed_instr[11:10], compressed_instr[4:3], 1'b0}; + jump_imm = {{9{compressed_instr[12]}}, compressed_instr[12], + compressed_instr[8], compressed_instr[10:9], compressed_instr[6], + compressed_instr[7], compressed_instr[2], compressed_instr[11], + compressed_instr[5:3], 1'b0}; + shamt = {compressed_instr[12], compressed_instr[6:2]}; + lui_imm = {{14{compressed_instr[12]}}, compressed_instr[12], compressed_instr[6:2]}; + + expanded_instr = 32'h0000_0013; + illegal_instr = 1'b0; + + unique case (quadrant) + 2'b00: begin + unique case (funct3) + 3'b000: begin // C.ADDI4SPN + expanded_instr = i_instr(addi4spn_imm, 5'd2, 3'b000, rd_prime, OPCODE_OP_IMM); + illegal_instr = (addi4spn_imm == 12'b0); + end + 3'b001: expanded_instr = i_instr(ld_imm, rs1_prime, 3'b011, rd_prime, OPCODE_LOAD_FP); + 3'b010: expanded_instr = i_instr(lw_imm, rs1_prime, 3'b010, rd_prime, OPCODE_LOAD); + 3'b011: expanded_instr = i_instr(ld_imm, rs1_prime, 3'b011, rd_prime, OPCODE_LOAD); + 3'b101: expanded_instr = s_instr(ld_imm, rs2_prime, rs1_prime, 3'b011, OPCODE_STORE_FP); + 3'b110: expanded_instr = s_instr(lw_imm, rs2_prime, rs1_prime, 3'b010, OPCODE_STORE); + 3'b111: expanded_instr = s_instr(ld_imm, rs2_prime, rs1_prime, 3'b011, OPCODE_STORE); + default: illegal_instr = 1'b1; + endcase + end + + 2'b01: begin + unique case (funct3) + 3'b000: expanded_instr = i_instr(ci_imm, rd_rs1, 3'b000, rd_rs1, OPCODE_OP_IMM); + 3'b001: expanded_instr = i_instr(ci_imm, rd_rs1, 3'b000, rd_rs1, OPCODE_OP_IMM_32); + 3'b010: expanded_instr = i_instr(ci_imm, 5'd0, 3'b000, rd_rs1, OPCODE_OP_IMM); + 3'b011: begin + if (rd_rs1 == 5'd2) begin + expanded_instr = i_instr({{2{compressed_instr[12]}}, compressed_instr[12], + compressed_instr[4:3], compressed_instr[5], + compressed_instr[2], compressed_instr[6], 4'b0000}, + 5'd2, 3'b000, 5'd2, OPCODE_OP_IMM); + illegal_instr = (compressed_instr[12:2] == 11'b0); + end else begin + expanded_instr = u_instr(lui_imm, rd_rs1, OPCODE_LUI); + illegal_instr = (rd_rs1 == 5'd0) || (lui_imm == 20'b0); + end + end + 3'b100: begin + unique case (compressed_instr[11:10]) + 2'b00: begin + expanded_instr = i_instr({6'b000000, shamt}, rs1_prime, 3'b101, rs1_prime, OPCODE_OP_IMM); + illegal_instr = (shamt == 6'b0); + end + 2'b01: begin + expanded_instr = i_instr({6'b010000, shamt}, rs1_prime, 3'b101, rs1_prime, OPCODE_OP_IMM); + illegal_instr = (shamt == 6'b0); + end + 2'b10: expanded_instr = i_instr(ci_imm, rs1_prime, 3'b111, rs1_prime, OPCODE_OP_IMM); + 2'b11: begin + unique case ({compressed_instr[12], compressed_instr[6:5]}) + 3'b000: expanded_instr = r_instr(7'b0100000, rs2_prime, rs1_prime, 3'b000, rs1_prime, OPCODE_OP); + 3'b001: expanded_instr = r_instr(7'b0000000, rs2_prime, rs1_prime, 3'b100, rs1_prime, OPCODE_OP); + 3'b010: expanded_instr = r_instr(7'b0000000, rs2_prime, rs1_prime, 3'b110, rs1_prime, OPCODE_OP); + 3'b011: expanded_instr = r_instr(7'b0000000, rs2_prime, rs1_prime, 3'b111, rs1_prime, OPCODE_OP); + 3'b100: expanded_instr = r_instr(7'b0100000, rs2_prime, rs1_prime, 3'b000, rs1_prime, OPCODE_OP_32); + 3'b101: expanded_instr = r_instr(7'b0000000, rs2_prime, rs1_prime, 3'b000, rs1_prime, OPCODE_OP_32); + default: illegal_instr = 1'b1; + endcase + end + endcase + end + 3'b101: expanded_instr = j_instr(jump_imm, 5'd0); + 3'b110: expanded_instr = b_instr(branch_imm, 5'd0, rs1_prime, 3'b000); + 3'b111: expanded_instr = b_instr(branch_imm, 5'd0, rs1_prime, 3'b001); + default: illegal_instr = 1'b1; + endcase + end + + 2'b10: begin + unique case (funct3) + 3'b000: begin + expanded_instr = i_instr({6'b000000, shamt}, rd_rs1, 3'b001, rd_rs1, OPCODE_OP_IMM); + illegal_instr = (shamt == 6'b0); + end + 3'b001: begin + expanded_instr = i_instr(ldsp_imm, 5'd2, 3'b011, rd_rs1, OPCODE_LOAD_FP); + end + 3'b010: begin + expanded_instr = i_instr(lwsp_imm, 5'd2, 3'b010, rd_rs1, OPCODE_LOAD); + illegal_instr = (rd_rs1 == 5'd0); + end + 3'b011: begin + expanded_instr = i_instr(ldsp_imm, 5'd2, 3'b011, rd_rs1, OPCODE_LOAD); + illegal_instr = (rd_rs1 == 5'd0); + end + 3'b100: begin + if (!compressed_instr[12] && (rs2 == 5'd0)) begin + expanded_instr = i_instr(12'b0, rd_rs1, 3'b000, 5'd0, OPCODE_JALR); + illegal_instr = (rd_rs1 == 5'd0); + end else if (!compressed_instr[12]) begin + expanded_instr = r_instr(7'b0000000, rs2, 5'd0, 3'b000, rd_rs1, OPCODE_OP); + end else if (compressed_instr[12] && (rd_rs1 == 5'd0) && (rs2 == 5'd0)) begin + expanded_instr = 32'h0010_0073; // EBREAK + end else if (compressed_instr[12] && (rs2 == 5'd0)) begin + expanded_instr = i_instr(12'b0, rd_rs1, 3'b000, 5'd1, OPCODE_JALR); + end else begin + expanded_instr = r_instr(7'b0000000, rs2, rd_rs1, 3'b000, rd_rs1, OPCODE_OP); + end + end + 3'b101: expanded_instr = s_instr(sdsp_imm, rs2, 5'd2, 3'b011, OPCODE_STORE_FP); + 3'b110: expanded_instr = s_instr(swsp_imm, rs2, 5'd2, 3'b010, OPCODE_STORE); + 3'b111: expanded_instr = s_instr(sdsp_imm, rs2, 5'd2, 3'b011, OPCODE_STORE); + default: illegal_instr = 1'b1; + endcase + end + + default: illegal_instr = 1'b1; + endcase + end + +endmodule diff --git a/rv64cpu/rtl/core/execute/decoder.sv b/rv64cpu/rtl/core/execute/decoder.sv new file mode 100644 index 0000000..c39a637 --- /dev/null +++ b/rv64cpu/rtl/core/execute/decoder.sv @@ -0,0 +1,672 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/decoder.sv +// Description: Instruction decoder for RV64IMAFD base ISA + Zicsr + Zifencei +// Translates 32-bit instructions into control signals for the pipeline. +// C is handled by the IF-stage decompressor before this decoder. + +module decoder + import rv64_pkg::*; +( + input logic [31:0] instr, // 32-bit instruction + input logic [1:0] priv_mode, // Current privilege mode from CSR + input logic mstatus_tsr, // Trap SRET in S-mode + input logic mstatus_tw, // Trap WFI in S/U-mode + input logic mstatus_tvm, // Trap supervisor memory-management ops in S-mode + + // Register File + output logic [4:0] rs1_addr, // Source register 1 address + output logic [4:0] rs2_addr, // Source register 2 address + output logic [4:0] rd_addr, // Destination register address + output logic reg_write_en, // Register file write enable + + // ALU Control + output alu_op_t alu_op, // ALU operation select + output logic alu_src1_sel, // 0 = rs1, 1 = PC + output logic alu_src2_sel, // 0 = rs2, 1 = immediate + + // Immediate + output logic [XLEN-1:0] imm, // Sign-extended immediate + + // Memory Access + output logic is_load, // MEM stage: read from memory + output logic is_store, // MEM stage: write to memory + output logic [2:0] mem_size, // funct3 → byte/half/word/double + sign + output logic is_amo, // A extension atomic memory operation + output amo_op_t amo_op, // LR/SC/AMO operation selector + output logic is_fp, + output logic fp_reg_write_en, + output logic fp_store_data_sel, + output fp_op_t fp_op, + output fp_fmt_t fp_fmt, + output logic [2:0] fp_rm, + + // Branch / Jump + output logic is_branch, // EX: conditional branch + output logic is_jump, // EX: unconditional jump (JAL/JALR) + output logic [2:0] branch_op, // funct3 → BEQ/BNE/BLT/BGE/BLTU/BGEU + + // Writeback Source + output logic [1:0] wb_sel, // 0 = ALU result, 1 = MEM data, 2 = PC+4 + + // RV64 Word Operations + output logic is_word_op, // EX: operate on lower 32 bits, sext result + + // System / Privilege + output logic is_csr, // CSR instruction + output logic [2:0] csr_op, // funct3 → CSRRW/CSRRS/CSRRC/CSRRWI/CSRRSI/CSRRCI + output logic is_ecall, // ECALL + output logic is_ebreak, // EBREAK + output logic is_mret, // MRET + output logic is_sret, // SRET + output logic is_wfi, // WFI + output logic is_sfence_vma, // SFENCE.VMA + output logic is_fence, // FENCE + output logic is_fence_i, // FENCE.I (Zifencei) + + // Error + output logic illegal_instr // Unrecognised or malformed instruction +); + + // ============================ + // Instruction Field Extraction + // ============================ + typedef enum logic [1:0] { + PRIV_MODE_U = 2'b00, + PRIV_MODE_S = 2'b01, + PRIV_MODE_M = 2'b11 + } priv_mode_t; + + logic [6:0] opcode; + logic [2:0] funct3; + logic [6:0] funct7; + logic [4:0] amo_funct5; + + assign opcode = instr[6:0]; + assign rd_addr = instr[11:7]; + assign funct3 = instr[14:12]; + assign rs1_addr = (opcode == OPCODE_LUI) ? 5'b00000 : instr[19:15]; + assign rs2_addr = instr[24:20]; + assign funct7 = instr[31:25]; + assign amo_funct5 = instr[31:27]; + + // ============================ + // Immediate Generation + // ============================ + logic [XLEN-1:0] imm_i, imm_s, imm_b, imm_u, imm_j; + logic [11:0] csr_addr_field; + + // I-type: instr[31:20] + assign imm_i = {{52{instr[31]}}, instr[31:20]}; + + // S-type: {instr[31:25], instr[11:7]} + assign imm_s = {{52{instr[31]}}, instr[31:25], instr[11:7]}; + + // B-type: {instr[31], instr[7], instr[30:25], instr[11:8], 1'b0} + assign imm_b = {{51{instr[31]}}, instr[31], instr[7], instr[30:25], instr[11:8], 1'b0}; + + // U-type: {instr[31:12], 12'b0} + assign imm_u = {{32{instr[31]}}, instr[31:12], 12'b0}; + + // J-type: {instr[31], instr[19:12], instr[20], instr[30:21], 1'b0} + assign imm_j = {{43{instr[31]}}, instr[31], instr[19:12], instr[20], instr[30:21], 1'b0}; + assign csr_addr_field = instr[31:20]; + + function automatic int priv_rank(logic [1:0] mode); + case (mode) + PRIV_MODE_U: return 0; + PRIV_MODE_S: return 1; + 2'b10: return 2; // Reserved/H-mode, treat as higher than S + default: return 3; // Machine mode (or anything else) highest + endcase + endfunction + + function automatic logic priv_ge(logic [1:0] cur, logic [1:0] req); + return priv_rank(cur) >= priv_rank(req); + endfunction + + function automatic logic csr_access_allowed(logic [1:0] cur, logic [11:0] addr); + logic [1:0] req_priv; + req_priv = addr[9:8]; + return priv_ge(cur, req_priv); + endfunction + + // ============================ + // Main Decode Logic + // ============================ + always_comb begin + // Default values (all signals inactive) + alu_op = ALU_ADD; + alu_src1_sel = 1'b0; // rs1 + alu_src2_sel = 1'b0; // rs2 + reg_write_en = 1'b0; + imm = '0; + is_load = 1'b0; + is_store = 1'b0; + mem_size = 3'b0; + is_amo = 1'b0; + amo_op = AMO_NONE; + is_fp = 1'b0; + fp_reg_write_en = 1'b0; + fp_store_data_sel = 1'b0; + fp_op = FP_OP_NONE; + fp_fmt = FP_FMT_S; + fp_rm = funct3; + is_branch = 1'b0; + is_jump = 1'b0; + branch_op = 3'b0; + wb_sel = 2'b00; // ALU result + is_word_op = 1'b0; + is_csr = 1'b0; + csr_op = 3'b0; + is_ecall = 1'b0; + is_ebreak = 1'b0; + is_mret = 1'b0; + is_sret = 1'b0; + is_wfi = 1'b0; + is_sfence_vma = 1'b0; + is_fence = 1'b0; + is_fence_i = 1'b0; + illegal_instr = 1'b0; + + case (opcode) + + // LUI — rd = imm_u + + OPCODE_LUI: begin + reg_write_en = 1'b1; + alu_src1_sel = 1'b0; // doesn't matter, ALU_ADD(0, imm_u) + alu_src2_sel = 1'b1; // immediate + imm = imm_u; + alu_op = ALU_ADD; // result = 0 + imm_u (rs1 forced to x0 externally or use alu_src1_sel trick) + // NOTE: EX stage should feed 0 to ALU operand 1 when alu_src1_sel==0 and opcode==LUI. + // Alternative: add a dedicated alu_src1_sel=2 for "zero". For now, the pipeline + // can detect LUI by opcode and zero out the operand. + end + + // AUIPC — rd = PC + imm_u + + + OPCODE_AUIPC: begin + reg_write_en = 1'b1; + alu_src1_sel = 1'b1; // PC + alu_src2_sel = 1'b1; // immediate + imm = imm_u; + alu_op = ALU_ADD; // result = PC + imm_u + end + + // ----------------- + // JAL — rd = PC+4; PC = PC + imm_j + OPCODE_JAL: begin + reg_write_en = 1'b1; + is_jump = 1'b1; + alu_src1_sel = 1'b1; // PC + alu_src2_sel = 1'b1; // immediate + imm = imm_j; + alu_op = ALU_ADD; // target = PC + imm_j + wb_sel = 2'b10; // writeback PC+4 + end + + // ------------------ + // JALR — rd = PC+4; PC = (rs1 + imm_i) & ~1 + + OPCODE_JALR: begin + reg_write_en = 1'b1; + is_jump = 1'b1; + alu_src1_sel = 1'b0; // rs1 + alu_src2_sel = 1'b1; // immediate + imm = imm_i; + alu_op = ALU_ADD; // target = rs1 + imm_i (LSB cleared by EX stage) + wb_sel = 2'b10; // writeback PC+4 + end + + // ----------------- + // Branch — compare rs1, rs2; if taken, PC = PC + imm_b + OPCODE_BRANCH: begin + reg_write_en = 1'b0; // branches never write rd + is_branch = 1'b1; + alu_src1_sel = 1'b1; // PC (ALU computes branch target) + alu_src2_sel = 1'b1; // immediate + imm = imm_b; + alu_op = ALU_ADD; // branch target = PC + imm_b + branch_op = funct3; // BEQ/BNE/BLT/BGE/BLTU/BGEU + end + + // ----------------- + // Load — rd = Mem[rs1 + imm_i] + OPCODE_LOAD: begin + reg_write_en = 1'b1; + is_load = 1'b1; + alu_src1_sel = 1'b0; // rs1 + alu_src2_sel = 1'b1; // immediate + imm = imm_i; + alu_op = ALU_ADD; // effective address = rs1 + imm_i + mem_size = funct3; // LB/LH/LW/LD/LBU/LHU/LWU + wb_sel = 2'b01; // writeback from memory + end + + // ----------------- + // Store — Mem[rs1 + imm_s] = rs2 + OPCODE_STORE: begin + reg_write_en = 1'b0; + is_store = 1'b1; + alu_src1_sel = 1'b0; // rs1 + alu_src2_sel = 1'b1; // immediate (NOT rs2 — rs2 is store data) + imm = imm_s; + alu_op = ALU_ADD; // effective address = rs1 + imm_s + mem_size = funct3; // SB/SH/SW/SD + end + + // ----------------- + // OP-IMM (I-type ALU) — rd = rs1 OP imm + // ADDI, SLTI, SLTIU, XORI, ORI, ANDI, SLLI, SRLI, SRAI + OPCODE_OP_IMM: begin + reg_write_en = 1'b1; + alu_src1_sel = 1'b0; // rs1 + alu_src2_sel = 1'b1; // immediate + imm = imm_i; + + case (funct3) + 3'b000: alu_op = ALU_ADD; // ADDI + 3'b010: alu_op = ALU_SLT; // SLTI + 3'b011: alu_op = ALU_SLTU; // SLTIU + 3'b100: alu_op = ALU_XOR; // XORI + 3'b110: alu_op = ALU_OR; // ORI + 3'b111: alu_op = ALU_AND; // ANDI + 3'b001: alu_op = ALU_SLL; // SLLI + 3'b101: alu_op = (funct7[5]) ? ALU_SRA : ALU_SRL; // SRAI / SRLI + default: alu_op = ALU_ADD; + endcase + end + + // ----------------- + // OP-IMM-32 (I-type ALU, 32-bit word variants for RV64) + // ADDIW, SLLIW, SRLIW, SRAIW + OPCODE_OP_IMM_32: begin + reg_write_en = 1'b1; + is_word_op = 1'b1; // EX stage: operate on lower 32 bits, sext result + alu_src1_sel = 1'b0; // rs1 + alu_src2_sel = 1'b1; // immediate + imm = imm_i; + + case (funct3) + 3'b000: alu_op = ALU_ADD; // ADDIW + 3'b001: alu_op = ALU_SLL; // SLLIW + 3'b101: alu_op = (funct7[5]) ? ALU_SRA : ALU_SRL; // SRAIW / SRLIW + default: begin + alu_op = ALU_ADD; + illegal_instr = 1'b1; + end + endcase + end + + // ----------------- + // OP (R-type ALU) — rd = rs1 OP rs2 + // ADD, SUB, SLL, SLT, SLTU, XOR, SRL, SRA, OR, AND + OPCODE_OP: begin + reg_write_en = 1'b1; + alu_src1_sel = 1'b0; // rs1 + alu_src2_sel = 1'b0; // rs2 + + if (funct7 == 7'b0000001) begin + case (funct3) + 3'b000: alu_op = ALU_MUL; + 3'b001: alu_op = ALU_MULH; + 3'b010: alu_op = ALU_MULHSU; + 3'b011: alu_op = ALU_MULHU; + 3'b100: alu_op = ALU_DIV; + 3'b101: alu_op = ALU_DIVU; + 3'b110: alu_op = ALU_REM; + 3'b111: alu_op = ALU_REMU; + default: illegal_instr = 1'b1; + endcase + end else begin + case (funct3) + 3'b000: alu_op = (funct7[5]) ? ALU_SUB : ALU_ADD; // SUB / ADD + 3'b001: alu_op = ALU_SLL; // SLL + 3'b010: alu_op = ALU_SLT; // SLT + 3'b011: alu_op = ALU_SLTU; // SLTU + 3'b100: alu_op = ALU_XOR; // XOR + 3'b101: alu_op = (funct7[5]) ? ALU_SRA : ALU_SRL; // SRA / SRL + 3'b110: alu_op = ALU_OR; // OR + 3'b111: alu_op = ALU_AND; // AND + default: alu_op = ALU_ADD; + endcase + end + end + + // ----------------- + // OP-32 (R-type ALU, 32-bit word variants for RV64) + // ADDW, SUBW, SLLW, SRLW, SRAW + OPCODE_OP_32: begin + reg_write_en = 1'b1; + is_word_op = 1'b1; + alu_src1_sel = 1'b0; // rs1 + alu_src2_sel = 1'b0; // rs2 + + if (funct7 == 7'b0000001) begin + case (funct3) + 3'b000: alu_op = ALU_MUL; + 3'b100: alu_op = ALU_DIV; + 3'b101: alu_op = ALU_DIVU; + 3'b110: alu_op = ALU_REM; + 3'b111: alu_op = ALU_REMU; + default: illegal_instr = 1'b1; + endcase + end else begin + case (funct3) + 3'b000: alu_op = (funct7[5]) ? ALU_SUB : ALU_ADD; // SUBW / ADDW + 3'b001: alu_op = ALU_SLL; // SLLW + 3'b101: alu_op = (funct7[5]) ? ALU_SRA : ALU_SRL; // SRAW / SRLW + default: begin + alu_op = ALU_ADD; + illegal_instr = 1'b1; + end + endcase + end + end + + // ----------------- + // MISC-MEM — FENCE / FENCE.I + OPCODE_MISC_MEM: begin + case (funct3) + 3'b000: is_fence = 1'b1; // FENCE (NOP in single-core in-order) + 3'b001: is_fence_i = 1'b1; // FENCE.I — flush I-Cache + default: illegal_instr = 1'b1; + endcase + end + + // ----------------- + // SYSTEM — ECALL / EBREAK / xRET / WFI / SFENCE.VMA / CSR* + OPCODE_SYSTEM: begin + if (funct3 == 3'b000) begin + // Privileged instructions (non-CSR) + reg_write_en = 1'b0; + case ({funct7, instr[24:20]}) // {funct7, rs2} + 12'b0000000_00000: is_ecall = 1'b1; // ECALL + 12'b0000000_00001: is_ebreak = 1'b1; // EBREAK + 12'b0011000_00010: begin // MRET + if (priv_mode == PRIV_MODE_M) + is_mret = 1'b1; + else + illegal_instr = 1'b1; + end + 12'b0001000_00010: begin // SRET + if (!priv_ge(priv_mode, PRIV_MODE_S)) + illegal_instr = 1'b1; + else if ((priv_mode == PRIV_MODE_S) && mstatus_tsr) + illegal_instr = 1'b1; + else + is_sret = 1'b1; + end + 12'b0001000_00101: begin // WFI + if ((priv_mode != PRIV_MODE_M) && mstatus_tw) + illegal_instr = 1'b1; + else + is_wfi = 1'b1; + end + default: begin + if (funct7 == 7'b0001001) begin + if (!priv_ge(priv_mode, PRIV_MODE_S)) + illegal_instr = 1'b1; + else if ((priv_mode == PRIV_MODE_S) && mstatus_tvm) + illegal_instr = 1'b1; + else + is_sfence_vma = 1'b1; // SFENCE.VMA + end else begin + illegal_instr = 1'b1; + end + end + endcase + end else begin + // CSR instructions + // funct3: 001=CSRRW, 010=CSRRS, 011=CSRRC, + // 101=CSRRWI, 110=CSRRSI, 111=CSRRCI + reg_write_en = 1'b1; + is_csr = 1'b1; + csr_op = funct3; + imm = imm_i; // CSR address = instr[31:20] (reuse imm_i path) + if (!csr_access_allowed(priv_mode, csr_addr_field)) begin + reg_write_en = 1'b0; + is_csr = 1'b0; + illegal_instr = 1'b1; + end + end + end + + // ----------------- + // AMO — A extension (LR/SC/AMO*) + OPCODE_AMO: begin + reg_write_en = 1'b1; + is_amo = 1'b1; + alu_src1_sel = 1'b0; + alu_src2_sel = 1'b1; + imm = '0; + alu_op = ALU_ADD; + wb_sel = 2'b01; + + unique case (funct3) + 3'b010: mem_size = 3'b010; // AMO*.W + 3'b011: mem_size = 3'b011; // AMO*.D + default: illegal_instr = 1'b1; + endcase + + unique case (amo_funct5) + 5'b00010: begin + amo_op = AMO_LR; + if (rs2_addr != 5'd0) begin + illegal_instr = 1'b1; + end + end + 5'b00011: amo_op = AMO_SC; + 5'b00001: amo_op = AMO_SWAP; + 5'b00000: amo_op = AMO_ADD; + 5'b00100: amo_op = AMO_XOR; + 5'b01100: amo_op = AMO_AND; + 5'b01000: amo_op = AMO_OR; + 5'b10000: amo_op = AMO_MIN; + 5'b10100: amo_op = AMO_MAX; + 5'b11000: amo_op = AMO_MINU; + 5'b11100: amo_op = AMO_MAXU; + default: begin + amo_op = AMO_NONE; + illegal_instr = 1'b1; + end + endcase + + if (illegal_instr) begin + reg_write_en = 1'b0; + is_amo = 1'b0; + amo_op = AMO_NONE; + end + end + + // ----------------- + // Floating-Point — F/D extensions + OPCODE_LOAD_FP: begin + is_fp = 1'b1; + is_load = 1'b1; + fp_reg_write_en = 1'b1; + alu_src1_sel = 1'b0; + alu_src2_sel = 1'b1; + imm = imm_i; + alu_op = ALU_ADD; + wb_sel = 2'b01; + unique case (funct3) + 3'b010: begin + mem_size = 3'b010; // FLW + fp_fmt = FP_FMT_S; + end + 3'b011: begin + mem_size = 3'b011; // FLD + fp_fmt = FP_FMT_D; + end + default: begin + is_fp = 1'b0; + fp_reg_write_en = 1'b0; + illegal_instr = 1'b1; + end + endcase + end + + OPCODE_STORE_FP: begin + is_fp = 1'b1; + is_store = 1'b1; + fp_store_data_sel = 1'b1; + alu_src1_sel = 1'b0; + alu_src2_sel = 1'b1; + imm = imm_s; + alu_op = ALU_ADD; + unique case (funct3) + 3'b010: begin + mem_size = 3'b010; // FSW + fp_fmt = FP_FMT_S; + end + 3'b011: begin + mem_size = 3'b011; // FSD + fp_fmt = FP_FMT_D; + end + default: begin + is_fp = 1'b0; + fp_store_data_sel = 1'b0; + illegal_instr = 1'b1; + end + endcase + end + + OPCODE_MADD, + OPCODE_MSUB, + OPCODE_NMSUB, + OPCODE_NMADD: begin + is_fp = 1'b1; + fp_reg_write_en = 1'b1; + fp_rm = funct3; + unique case (instr[26:25]) + 2'b00: fp_fmt = FP_FMT_S; + 2'b01: fp_fmt = FP_FMT_D; + default: illegal_instr = 1'b1; + endcase + unique case (opcode) + OPCODE_MADD: fp_op = FP_OP_MADD; + OPCODE_MSUB: fp_op = FP_OP_MSUB; + OPCODE_NMSUB: fp_op = FP_OP_NMSUB; + default: fp_op = FP_OP_NMADD; + endcase + if (illegal_instr) begin + is_fp = 1'b0; + fp_reg_write_en = 1'b0; + fp_op = FP_OP_NONE; + end + end + + OPCODE_OP_FP: begin + is_fp = 1'b1; + fp_rm = funct3; + unique case (instr[26:25]) + 2'b00: fp_fmt = FP_FMT_S; + 2'b01: fp_fmt = FP_FMT_D; + default: illegal_instr = 1'b1; + endcase + + unique case (funct7) + 7'b0000000, 7'b0000001: begin fp_op = FP_OP_ADD; fp_reg_write_en = 1'b1; end + 7'b0000100, 7'b0000101: begin fp_op = FP_OP_SUB; fp_reg_write_en = 1'b1; end + 7'b0001000, 7'b0001001: begin fp_op = FP_OP_MUL; fp_reg_write_en = 1'b1; end + 7'b0001100, 7'b0001101: begin fp_op = FP_OP_DIV; fp_reg_write_en = 1'b1; end + 7'b0101100, 7'b0101101: begin + fp_op = FP_OP_SQRT; + fp_reg_write_en = 1'b1; + if (rs2_addr != 5'd0) illegal_instr = 1'b1; + end + 7'b0010000, 7'b0010001: begin + fp_reg_write_en = 1'b1; + unique case (funct3) + 3'b000: fp_op = FP_OP_SGNJ; + 3'b001: fp_op = FP_OP_SGNJN; + 3'b010: fp_op = FP_OP_SGNJX; + default: illegal_instr = 1'b1; + endcase + end + 7'b0010100, 7'b0010101: begin + fp_reg_write_en = 1'b1; + unique case (funct3) + 3'b000: fp_op = FP_OP_MIN; + 3'b001: fp_op = FP_OP_MAX; + default: illegal_instr = 1'b1; + endcase + end + 7'b0100000, 7'b0100001: begin + fp_op = FP_OP_CVT_F2F; + fp_reg_write_en = 1'b1; + end + 7'b1010000, 7'b1010001: begin + reg_write_en = 1'b1; + unique case (funct3) + 3'b010: fp_op = FP_OP_EQ; + 3'b001: fp_op = FP_OP_LT; + 3'b000: fp_op = FP_OP_LE; + default: illegal_instr = 1'b1; + endcase + end + 7'b1100000, 7'b1100001: begin + fp_op = FP_OP_CVT_F2I; + reg_write_en = 1'b1; + end + 7'b1101000, 7'b1101001: begin + fp_op = FP_OP_CVT_I2F; + fp_reg_write_en = 1'b1; + end + 7'b1110000, 7'b1110001: begin + if (rs2_addr == 5'd0 && funct3 == 3'b000) begin + fp_op = FP_OP_MV_F2X; + reg_write_en = 1'b1; + end else if (rs2_addr == 5'd0 && funct3 == 3'b001) begin + fp_op = FP_OP_CLASS; + reg_write_en = 1'b1; + end else begin + illegal_instr = 1'b1; + end + end + 7'b1111000, 7'b1111001: begin + if (rs2_addr == 5'd0 && funct3 == 3'b000) begin + fp_op = FP_OP_MV_X2F; + fp_reg_write_en = 1'b1; + end else begin + illegal_instr = 1'b1; + end + end + default: illegal_instr = 1'b1; + endcase + + if (illegal_instr) begin + is_fp = 1'b0; + fp_reg_write_en = 1'b0; + reg_write_en = 1'b0; + fp_op = FP_OP_NONE; + end + end + + // ----------------- + // Unrecognised opcode + default: begin + illegal_instr = 1'b1; + end + + endcase + end + +endmodule diff --git a/rv64cpu/rtl/core/execute/hazard_unit.sv b/rv64cpu/rtl/core/execute/hazard_unit.sv new file mode 100644 index 0000000..ee90356 --- /dev/null +++ b/rv64cpu/rtl/core/execute/hazard_unit.sv @@ -0,0 +1,200 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/hazard_unit.sv +// Description: Hazard Control Unit +// Handles data hazards (forwarding, Load-Use stalls), control hazards +// (branch misprediction flushes), and memory stalls (D-Cache misses). + +module hazard_unit + import rv64_pkg::*; +( + // ----------------------- + // Inputs from ID Stage (for Load-Use detection) + // ----------------------- + input logic [4:0] id_rs1_addr, + input logic [4:0] id_rs2_addr, + + // ----------------------- + // Inputs from EX Stage (ID/EX register outputs) + // ----------------------- + input logic [4:0] ex_rs1_addr, + input logic [4:0] ex_rs2_addr, + input logic [4:0] ex_rd_addr, + input logic ex_reg_write_en, + input logic ex_is_load, + input logic ex_is_csr, + input logic ex_branch_taken, + input logic [XLEN-1:0] ex_branch_target, + + // ----------------------- + // Inputs from MEM Stage (EX/MEM register outputs) + // ----------------------- + input logic [4:0] mem_rd_addr, + input logic mem_reg_write_en, + input logic mem_result_late, + input logic mem_is_csr, + input logic mem_stall_req, // D-Cache miss or memory busy + + // ----------------------- + // Trap Controller Inputs + // ----------------------- + input logic trap_flush_req, + input logic trap_pc_sel, + input logic [XLEN-1:0] trap_target_pc, + + // ----------------------- + // Inputs from WB Stage (MEM/WB register outputs) + // ----------------------- + input logic [4:0] wb_rd_addr, + input logic wb_reg_write_en, + + // ----------------------- + // Outputs: EX-stage operand forwarding mux selects + // ----------------------- + // 00: no forward (use regfile), 01: forward from MEM, 10: forward from WB + output logic [1:0] forward_a_sel, + output logic [1:0] forward_b_sel, + + // ----------------------- + // Outputs: Pipeline stall and flush controls + // ----------------------- + output logic if_stall_en, + output logic if_id_stall_en, + output logic id_ex_stall_en, + output logic ex_mem_stall_en, + output logic if_id_flush_en, + output logic id_ex_flush_en, + + // ----------------------- + // Outputs: IF-stage PC redirect controls + // ----------------------- + output logic pc_redirect_sel, + output logic [XLEN-1:0] pc_redirect_target, + output logic pc_redirect_is_trap +); + + // ----------------------- + // Data Forwarding (EX-stage operand bypass) + // ----------------------- + // Forwarding resolves RAW hazards by routing results from + // MEM or WB back to the EX ALU inputs. We compare EX-stage + // source register addresses (the instruction currently in EX) + // against the destination registers of instructions in MEM/WB. + // + // Priority: MEM > WB (MEM result is more recent). + + // Operand A (ex_rs1_addr) + always_comb begin + if (mem_reg_write_en && !mem_result_late && (mem_rd_addr != 5'b0) && (mem_rd_addr == ex_rs1_addr)) begin + forward_a_sel = 2'b01; // Forward from MEM stage + end else if (wb_reg_write_en && (wb_rd_addr != 5'b0) && (wb_rd_addr == ex_rs1_addr)) begin + forward_a_sel = 2'b10; // Forward from WB stage + end else begin + forward_a_sel = 2'b00; // No forwarding + end + end + + // Operand B (ex_rs2_addr) + always_comb begin + if (mem_reg_write_en && !mem_result_late && (mem_rd_addr != 5'b0) && (mem_rd_addr == ex_rs2_addr)) begin + forward_b_sel = 2'b01; // Forward from MEM stage + end else if (wb_reg_write_en && (wb_rd_addr != 5'b0) && (wb_rd_addr == ex_rs2_addr)) begin + forward_b_sel = 2'b10; // Forward from WB stage + end else begin + forward_b_sel = 2'b00; // No forwarding + end + end + + // ----------------------- + // Load-Use Hazard Detection + // ----------------------- + // If the instruction in EX is a load, and the instruction in + // ID reads from the load's destination, we must stall for 1 + // cycle because the load data is not available until MEM. + logic load_use_stall; + assign load_use_stall = ex_is_load && (ex_rd_addr != 5'b0) && + ((ex_rd_addr == id_rs1_addr) || (ex_rd_addr == id_rs2_addr)); + + logic csr_use_stall; + assign csr_use_stall = ((ex_is_csr && ex_reg_write_en && (ex_rd_addr != 5'b0) && + ((ex_rd_addr == id_rs1_addr) || (ex_rd_addr == id_rs2_addr))) || + (mem_is_csr && mem_reg_write_en && (mem_rd_addr != 5'b0) && + ((mem_rd_addr == id_rs1_addr) || (mem_rd_addr == id_rs2_addr)))); + + // ----------------------- + // PC Redirect Selection (Trap has top priority) + // ----------------------- + always_comb begin + pc_redirect_sel = 1'b0; + pc_redirect_target = '0; + pc_redirect_is_trap = 1'b0; + + if (trap_pc_sel) begin + pc_redirect_sel = 1'b1; + pc_redirect_target = trap_target_pc; + pc_redirect_is_trap = 1'b1; + end else if (ex_branch_taken) begin + pc_redirect_sel = 1'b1; + pc_redirect_target = ex_branch_target; + end + end + + // ----------------------- + // Stall and Flush Signal Generation + // ----------------------- + always_comb begin + // Defaults: no stalls, no flushes + if_stall_en = 1'b0; + if_id_stall_en = 1'b0; + id_ex_stall_en = 1'b0; + ex_mem_stall_en = 1'b0; + if_id_flush_en = 1'b0; + id_ex_flush_en = 1'b0; + + if (trap_flush_req) begin + if_id_flush_en = 1'b1; + id_ex_flush_en = 1'b1; + end else begin + // (1) Memory stall (D-Cache miss): freeze the entire pipeline + if (mem_stall_req) begin + if_stall_en = 1'b1; + if_id_stall_en = 1'b1; + id_ex_stall_en = 1'b1; + ex_mem_stall_en = 1'b1; + end + + // (2) Value-use stall: freeze IF and IF/ID, insert bubble into ID/EX. + // Loads and CSR reads both produce their register result after EX. + if ((load_use_stall || csr_use_stall) && !mem_stall_req) begin + if_stall_en = 1'b1; + if_id_stall_en = 1'b1; + id_ex_flush_en = 1'b1; + end + + // (3) Branch/jump misprediction: flush IF/ID and ID/EX + // Branch flush overrides load-use stall (the stalled instruction is invalid anyway) + if (ex_branch_taken) begin + if_id_flush_en = 1'b1; + id_ex_flush_en = 1'b1; + // Cancel load-use stall if branch is also taken + if_stall_en = 1'b0; + if_id_stall_en = 1'b0; + end + end + end + +endmodule diff --git a/rv64cpu/rtl/core/fp_regfile.sv b/rv64cpu/rtl/core/fp_regfile.sv deleted file mode 100644 index 67e83f9..0000000 --- a/rv64cpu/rtl/core/fp_regfile.sv +++ /dev/null @@ -1,41 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// File path: rtl/core/fp_regfile.sv -// Description: 32-entry 64-bit floating-point register file for RV64F/RV64D. - -module fp_regfile - import rv64_pkg::*; -( - input logic clk, - input logic rst, - - input logic [4:0] fs1_addr, - input logic [4:0] fs2_addr, - input logic [4:0] fs3_addr, - output logic [XLEN-1:0] fs1_rdata, - output logic [XLEN-1:0] fs2_rdata, - output logic [XLEN-1:0] fs3_rdata, - - input logic fp_write_en, - input logic [4:0] fd_addr, - input logic [XLEN-1:0] fd_wdata -); - - logic [XLEN-1:0] regs [0:31]; - - assign fs1_rdata = (fp_write_en && (fs1_addr == fd_addr)) ? fd_wdata : regs[fs1_addr]; - assign fs2_rdata = (fp_write_en && (fs2_addr == fd_addr)) ? fd_wdata : regs[fs2_addr]; - assign fs3_rdata = (fp_write_en && (fs3_addr == fd_addr)) ? fd_wdata : regs[fs3_addr]; - - always_ff @(posedge clk) begin - if (rst) begin - for (int unsigned reg_idx = 0; reg_idx < 32; reg_idx++) begin - regs[reg_idx] <= '0; - end - end else if (fp_write_en) begin - regs[fd_addr] <= fd_wdata; - end - end - -endmodule diff --git a/rv64cpu/rtl/core/fpu/.gitkeep b/rv64cpu/rtl/core/fpu/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/rtl/core/fpu/include/.gitkeep b/rv64cpu/rtl/core/fpu/include/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/rtl/core/hazard_unit.sv b/rv64cpu/rtl/core/hazard_unit.sv deleted file mode 100644 index ee90356..0000000 --- a/rv64cpu/rtl/core/hazard_unit.sv +++ /dev/null @@ -1,200 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/hazard_unit.sv -// Description: Hazard Control Unit -// Handles data hazards (forwarding, Load-Use stalls), control hazards -// (branch misprediction flushes), and memory stalls (D-Cache misses). - -module hazard_unit - import rv64_pkg::*; -( - // ----------------------- - // Inputs from ID Stage (for Load-Use detection) - // ----------------------- - input logic [4:0] id_rs1_addr, - input logic [4:0] id_rs2_addr, - - // ----------------------- - // Inputs from EX Stage (ID/EX register outputs) - // ----------------------- - input logic [4:0] ex_rs1_addr, - input logic [4:0] ex_rs2_addr, - input logic [4:0] ex_rd_addr, - input logic ex_reg_write_en, - input logic ex_is_load, - input logic ex_is_csr, - input logic ex_branch_taken, - input logic [XLEN-1:0] ex_branch_target, - - // ----------------------- - // Inputs from MEM Stage (EX/MEM register outputs) - // ----------------------- - input logic [4:0] mem_rd_addr, - input logic mem_reg_write_en, - input logic mem_result_late, - input logic mem_is_csr, - input logic mem_stall_req, // D-Cache miss or memory busy - - // ----------------------- - // Trap Controller Inputs - // ----------------------- - input logic trap_flush_req, - input logic trap_pc_sel, - input logic [XLEN-1:0] trap_target_pc, - - // ----------------------- - // Inputs from WB Stage (MEM/WB register outputs) - // ----------------------- - input logic [4:0] wb_rd_addr, - input logic wb_reg_write_en, - - // ----------------------- - // Outputs: EX-stage operand forwarding mux selects - // ----------------------- - // 00: no forward (use regfile), 01: forward from MEM, 10: forward from WB - output logic [1:0] forward_a_sel, - output logic [1:0] forward_b_sel, - - // ----------------------- - // Outputs: Pipeline stall and flush controls - // ----------------------- - output logic if_stall_en, - output logic if_id_stall_en, - output logic id_ex_stall_en, - output logic ex_mem_stall_en, - output logic if_id_flush_en, - output logic id_ex_flush_en, - - // ----------------------- - // Outputs: IF-stage PC redirect controls - // ----------------------- - output logic pc_redirect_sel, - output logic [XLEN-1:0] pc_redirect_target, - output logic pc_redirect_is_trap -); - - // ----------------------- - // Data Forwarding (EX-stage operand bypass) - // ----------------------- - // Forwarding resolves RAW hazards by routing results from - // MEM or WB back to the EX ALU inputs. We compare EX-stage - // source register addresses (the instruction currently in EX) - // against the destination registers of instructions in MEM/WB. - // - // Priority: MEM > WB (MEM result is more recent). - - // Operand A (ex_rs1_addr) - always_comb begin - if (mem_reg_write_en && !mem_result_late && (mem_rd_addr != 5'b0) && (mem_rd_addr == ex_rs1_addr)) begin - forward_a_sel = 2'b01; // Forward from MEM stage - end else if (wb_reg_write_en && (wb_rd_addr != 5'b0) && (wb_rd_addr == ex_rs1_addr)) begin - forward_a_sel = 2'b10; // Forward from WB stage - end else begin - forward_a_sel = 2'b00; // No forwarding - end - end - - // Operand B (ex_rs2_addr) - always_comb begin - if (mem_reg_write_en && !mem_result_late && (mem_rd_addr != 5'b0) && (mem_rd_addr == ex_rs2_addr)) begin - forward_b_sel = 2'b01; // Forward from MEM stage - end else if (wb_reg_write_en && (wb_rd_addr != 5'b0) && (wb_rd_addr == ex_rs2_addr)) begin - forward_b_sel = 2'b10; // Forward from WB stage - end else begin - forward_b_sel = 2'b00; // No forwarding - end - end - - // ----------------------- - // Load-Use Hazard Detection - // ----------------------- - // If the instruction in EX is a load, and the instruction in - // ID reads from the load's destination, we must stall for 1 - // cycle because the load data is not available until MEM. - logic load_use_stall; - assign load_use_stall = ex_is_load && (ex_rd_addr != 5'b0) && - ((ex_rd_addr == id_rs1_addr) || (ex_rd_addr == id_rs2_addr)); - - logic csr_use_stall; - assign csr_use_stall = ((ex_is_csr && ex_reg_write_en && (ex_rd_addr != 5'b0) && - ((ex_rd_addr == id_rs1_addr) || (ex_rd_addr == id_rs2_addr))) || - (mem_is_csr && mem_reg_write_en && (mem_rd_addr != 5'b0) && - ((mem_rd_addr == id_rs1_addr) || (mem_rd_addr == id_rs2_addr)))); - - // ----------------------- - // PC Redirect Selection (Trap has top priority) - // ----------------------- - always_comb begin - pc_redirect_sel = 1'b0; - pc_redirect_target = '0; - pc_redirect_is_trap = 1'b0; - - if (trap_pc_sel) begin - pc_redirect_sel = 1'b1; - pc_redirect_target = trap_target_pc; - pc_redirect_is_trap = 1'b1; - end else if (ex_branch_taken) begin - pc_redirect_sel = 1'b1; - pc_redirect_target = ex_branch_target; - end - end - - // ----------------------- - // Stall and Flush Signal Generation - // ----------------------- - always_comb begin - // Defaults: no stalls, no flushes - if_stall_en = 1'b0; - if_id_stall_en = 1'b0; - id_ex_stall_en = 1'b0; - ex_mem_stall_en = 1'b0; - if_id_flush_en = 1'b0; - id_ex_flush_en = 1'b0; - - if (trap_flush_req) begin - if_id_flush_en = 1'b1; - id_ex_flush_en = 1'b1; - end else begin - // (1) Memory stall (D-Cache miss): freeze the entire pipeline - if (mem_stall_req) begin - if_stall_en = 1'b1; - if_id_stall_en = 1'b1; - id_ex_stall_en = 1'b1; - ex_mem_stall_en = 1'b1; - end - - // (2) Value-use stall: freeze IF and IF/ID, insert bubble into ID/EX. - // Loads and CSR reads both produce their register result after EX. - if ((load_use_stall || csr_use_stall) && !mem_stall_req) begin - if_stall_en = 1'b1; - if_id_stall_en = 1'b1; - id_ex_flush_en = 1'b1; - end - - // (3) Branch/jump misprediction: flush IF/ID and ID/EX - // Branch flush overrides load-use stall (the stalled instruction is invalid anyway) - if (ex_branch_taken) begin - if_id_flush_en = 1'b1; - id_ex_flush_en = 1'b1; - // Cancel load-use stall if branch is also taken - if_stall_en = 1'b0; - if_id_stall_en = 1'b0; - end - end - end - -endmodule diff --git a/rv64cpu/rtl/core/id_ex_reg.sv b/rv64cpu/rtl/core/id_ex_reg.sv deleted file mode 100644 index 777cc46..0000000 --- a/rv64cpu/rtl/core/id_ex_reg.sv +++ /dev/null @@ -1,287 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/id_ex_reg.sv -// Description: ID/EX Pipeline Register -// Latches control signals and data from ID stage and passes them to EX stage. -// Supports stall (freeze) and flush. - -module id_ex_reg - import rv64_pkg::*; -( - input logic clk, - input logic rst, - - // Pipeline Control - input logic stall_en, - input logic flush_en, - - - // Inputs from ID Stage - - // Instruction payload - input logic [INST_WIDTH-1:0] id_instr, - input logic [2:0] id_instr_len, - - // Data/Operands - input logic [XLEN-1:0] id_pc, - input logic [XLEN-1:0] id_rs1_rdata, - input logic [XLEN-1:0] id_rs2_rdata, - input logic [XLEN-1:0] id_fs1_rdata, - input logic [XLEN-1:0] id_fs2_rdata, - input logic [XLEN-1:0] id_fs3_rdata, - input logic [4:0] id_rs1_addr, - input logic [4:0] id_rs2_addr, - input logic [4:0] id_fs3_addr, - input logic [XLEN-1:0] id_imm, - - // Execution Control - input alu_op_t id_alu_op, - input logic id_alu_src1_sel, - input logic id_alu_src2_sel, - input logic id_is_word_op, - input logic id_is_branch, - input branch_op_t id_branch_op, - input logic id_is_jump, - - // Memory & Writeback Control - input logic id_is_load, - input logic id_is_store, - input logic [2:0] id_mem_size, - input logic id_is_amo, - input amo_op_t id_amo_op, - input logic id_is_fp, - input logic id_fp_reg_write_en, - input logic id_fp_store_data_sel, - input fp_op_t id_fp_op, - input fp_fmt_t id_fp_fmt, - input logic [2:0] id_fp_rm, - input logic id_reg_write_en, - input logic [4:0] id_rd_addr, - input logic [1:0] id_wb_sel, - - // System / Exception - input logic id_is_csr, - input logic [2:0] id_csr_op, - input logic id_illegal_instr, - input logic id_instr_access_fault, - input logic id_instr_page_fault, - input logic id_is_ecall, - input logic id_is_ebreak, - input logic id_is_mret, - input logic id_is_sret, - input logic id_is_wfi, - input logic id_is_sfence_vma, - input logic id_is_fence, - input logic id_is_fence_i, - - - // Outputs to EX Stage - - // Instruction payload - output logic [INST_WIDTH-1:0] ex_instr, - output logic [2:0] ex_instr_len, - - // Data/Operands - output logic [XLEN-1:0] ex_pc, - output logic [XLEN-1:0] ex_rs1_rdata, - output logic [XLEN-1:0] ex_rs2_rdata, - output logic [XLEN-1:0] ex_fs1_rdata, - output logic [XLEN-1:0] ex_fs2_rdata, - output logic [XLEN-1:0] ex_fs3_rdata, - output logic [4:0] ex_rs1_addr, - output logic [4:0] ex_rs2_addr, - output logic [4:0] ex_fs3_addr, - output logic [XLEN-1:0] ex_imm, - - // Execution Control - output alu_op_t ex_alu_op, - output logic ex_alu_src1_sel, - output logic ex_alu_src2_sel, - output logic ex_is_word_op, - output logic ex_is_branch, - output branch_op_t ex_branch_op, - output logic ex_is_jump, - - // Memory & Writeback Control - output logic ex_is_load, - output logic ex_is_store, - output logic [2:0] ex_mem_size, - output logic ex_is_amo, - output amo_op_t ex_amo_op, - output logic ex_is_fp, - output logic ex_fp_reg_write_en, - output logic ex_fp_store_data_sel, - output fp_op_t ex_fp_op, - output fp_fmt_t ex_fp_fmt, - output logic [2:0] ex_fp_rm, - output logic ex_reg_write_en, - output logic [4:0] ex_rd_addr, - output logic [1:0] ex_wb_sel, - - // System / Exception - output logic ex_is_csr, - output logic [2:0] ex_csr_op, - output logic ex_illegal_instr, - output logic ex_instr_access_fault, - output logic ex_instr_page_fault, - output logic ex_is_ecall, - output logic ex_is_ebreak, - output logic ex_is_mret, - output logic ex_is_sret, - output logic ex_is_wfi, - output logic ex_is_sfence_vma, - output logic ex_is_fence, - output logic ex_is_fence_i -); - - always_ff @(posedge clk) begin - if (rst) begin - ex_instr <= '0; - ex_instr_len <= 3'd4; - ex_pc <= '0; - ex_rs1_rdata <= '0; - ex_rs2_rdata <= '0; - ex_fs1_rdata <= '0; - ex_fs2_rdata <= '0; - ex_fs3_rdata <= '0; - ex_rs1_addr <= '0; - ex_rs2_addr <= '0; - ex_fs3_addr <= '0; - ex_imm <= '0; - ex_alu_op <= ALU_ADD; // NOP is effectively ADD x0, x0, 0 - ex_alu_src1_sel <= '0; - ex_alu_src2_sel <= '0; - ex_is_word_op <= '0; - ex_is_branch <= '0; - ex_branch_op <= BRANCH_EQ; - ex_is_jump <= '0; - ex_is_load <= '0; - ex_is_store <= '0; - ex_mem_size <= '0; - ex_is_amo <= '0; - ex_amo_op <= AMO_NONE; - ex_is_fp <= '0; - ex_fp_reg_write_en <= '0; - ex_fp_store_data_sel <= '0; - ex_fp_op <= FP_OP_NONE; - ex_fp_fmt <= FP_FMT_S; - ex_fp_rm <= '0; - ex_reg_write_en <= '0; // VERY IMPORTANT: Do not write to regfile on NOP - ex_rd_addr <= '0; - ex_wb_sel <= '0; - ex_is_csr <= '0; - ex_csr_op <= '0; - ex_illegal_instr <= '0; - ex_instr_access_fault <= '0; - ex_instr_page_fault <= '0; - ex_is_ecall <= '0; - ex_is_ebreak <= '0; - ex_is_mret <= '0; - ex_is_sret <= '0; - ex_is_wfi <= '0; - ex_is_sfence_vma <= '0; - ex_is_fence <= '0; - ex_is_fence_i <= '0; - end else if (flush_en) begin - ex_instr <= '0; - ex_instr_len <= 3'd4; - ex_alu_op <= ALU_ADD; // NOP is effectively ADD x0, x0, 0 - ex_alu_src1_sel <= '0; - ex_alu_src2_sel <= '0; - ex_is_word_op <= '0; - ex_is_branch <= '0; - ex_branch_op <= BRANCH_EQ; - ex_is_jump <= '0; - ex_is_load <= '0; - ex_is_store <= '0; - ex_mem_size <= '0; - ex_is_amo <= '0; - ex_amo_op <= AMO_NONE; - ex_is_fp <= '0; - ex_fp_reg_write_en <= '0; - ex_fp_store_data_sel <= '0; - ex_fp_op <= FP_OP_NONE; - ex_fp_fmt <= FP_FMT_S; - ex_fp_rm <= '0; - ex_reg_write_en <= '0; // VERY IMPORTANT: Do not write to regfile on NOP - ex_rd_addr <= '0; - ex_wb_sel <= '0; - ex_is_csr <= '0; - ex_csr_op <= '0; - ex_illegal_instr <= '0; - ex_instr_access_fault <= '0; - ex_instr_page_fault <= '0; - ex_is_ecall <= '0; - ex_is_ebreak <= '0; - ex_is_mret <= '0; - ex_is_sret <= '0; - ex_is_wfi <= '0; - ex_is_sfence_vma <= '0; - ex_is_fence <= '0; - ex_is_fence_i <= '0; - end else if (~stall_en) begin - ex_instr <= id_instr; - ex_instr_len <= id_instr_len; - ex_pc <= id_pc; - ex_rs1_rdata <= id_rs1_rdata; - ex_rs2_rdata <= id_rs2_rdata; - ex_fs1_rdata <= id_fs1_rdata; - ex_fs2_rdata <= id_fs2_rdata; - ex_fs3_rdata <= id_fs3_rdata; - ex_rs1_addr <= id_rs1_addr; - ex_rs2_addr <= id_rs2_addr; - ex_fs3_addr <= id_fs3_addr; - ex_imm <= id_imm; - ex_alu_op <= id_alu_op; - ex_alu_src1_sel <= id_alu_src1_sel; - ex_alu_src2_sel <= id_alu_src2_sel; - ex_is_word_op <= id_is_word_op; - ex_is_branch <= id_is_branch; - ex_branch_op <= id_branch_op; - ex_is_jump <= id_is_jump; - ex_is_load <= id_is_load; - ex_is_store <= id_is_store; - ex_mem_size <= id_mem_size; - ex_is_amo <= id_is_amo; - ex_amo_op <= id_amo_op; - ex_is_fp <= id_is_fp; - ex_fp_reg_write_en <= id_fp_reg_write_en; - ex_fp_store_data_sel <= id_fp_store_data_sel; - ex_fp_op <= id_fp_op; - ex_fp_fmt <= id_fp_fmt; - ex_fp_rm <= id_fp_rm; - ex_reg_write_en <= id_reg_write_en; - ex_rd_addr <= id_rd_addr; - ex_wb_sel <= id_wb_sel; - ex_is_csr <= id_is_csr; - ex_csr_op <= id_csr_op; - ex_illegal_instr <= id_illegal_instr; - ex_instr_access_fault <= id_instr_access_fault; - ex_instr_page_fault <= id_instr_page_fault; - ex_is_ecall <= id_is_ecall; - ex_is_ebreak <= id_is_ebreak; - ex_is_mret <= id_is_mret; - ex_is_sret <= id_is_sret; - ex_is_wfi <= id_is_wfi; - ex_is_sfence_vma <= id_is_sfence_vma; - ex_is_fence <= id_is_fence; - ex_is_fence_i <= id_is_fence_i; - end - // If stall_en && !flush_en, maintain current state - end - -endmodule diff --git a/rv64cpu/rtl/core/id_stage.sv b/rv64cpu/rtl/core/id_stage.sv deleted file mode 100644 index 9003b42..0000000 --- a/rv64cpu/rtl/core/id_stage.sv +++ /dev/null @@ -1,197 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/id_stage.sv -// Description: Instruction Decode Stage, instantiate the Decoder and RegFile, extract control signals and operands - -module id_stage - import rv64_pkg::*; -( - input logic clk, - input logic rst, - - // Inputs from IF Stage (via IF/ID Pipeline Register) - input logic [XLEN-1:0] if_pc, // PC of the current instruction - input logic [INST_WIDTH-1:0] if_instr, // The 32-bit instruction fetched - input logic [2:0] if_instr_len, - input logic [1:0] csr_priv_mode, // Current privilege mode from CSR - input logic csr_mstatus_tsr, - input logic csr_mstatus_tw, - input logic csr_mstatus_tvm, - - // Write-back Inputs from WB Stage (to RegFile) - input logic wb_reg_write_en, // Register write enable from WB stage - input logic [4:0] wb_rd_addr, // Destination register address from WB - input logic [XLEN-1:0] wb_rd_wdata, // Data to write back to register - input logic wb_fp_write_en, - input logic [4:0] wb_fd_addr, - input logic [XLEN-1:0] wb_fd_wdata, - - // Outputs to EX Stage - // (Notice the 'id_' prefix to signify signals leaving the ID stage) - // Data/Operands - output logic [XLEN-1:0] id_pc, // Pass-through PC for branch/jump calculation - output logic [2:0] id_instr_len, - output logic [XLEN-1:0] id_rs1_rdata, // Source register 1 data - output logic [XLEN-1:0] id_rs2_rdata, // Source register 2 data - output logic [XLEN-1:0] id_fs1_rdata, - output logic [XLEN-1:0] id_fs2_rdata, - output logic [XLEN-1:0] id_fs3_rdata, - output logic [4:0] id_rs1_addr, // Rs1 index (for Forwarding & CSR zimm) - output logic [4:0] id_rs2_addr, // Rs2 index (for Forwarding) - output logic [4:0] id_fs3_addr, - output logic [XLEN-1:0] id_imm, // Sign-extended generic immediate - - // Execution Control (ALU & Branch) - output alu_op_t id_alu_op, // ALU operation code - output logic id_alu_src1_sel,// 0: rs1_rdata, 1: PC - output logic id_alu_src2_sel,// 0: rs2_rdata, 1: imm - output logic id_is_word_op, // 1: RV64 32-bit operation (e.g. ADDW) - output logic id_is_branch, // 1: B-type branch instruction - output branch_op_t id_branch_op, // Specific condition for branch (BEQ, BNE...) - output logic id_is_jump, // 1: JAL or JALR - - // Memory & Writeback Control (Passed down to MEM/WB stages eventually) - output logic id_is_load, // 1: Load instruction - output logic id_is_store, // 1: Store instruction - output logic [2:0] id_mem_size, // Memory access size (Byte/Half/Word/Double) - output logic id_is_amo, - output amo_op_t id_amo_op, - output logic id_is_fp, - output logic id_fp_reg_write_en, - output logic id_fp_store_data_sel, - output fp_op_t id_fp_op, - output fp_fmt_t id_fp_fmt, - output logic [2:0] id_fp_rm, - output logic id_reg_write_en,// 1: Instruction will write to a register - output logic [4:0] id_rd_addr, // Destination register index - output logic [1:0] id_wb_sel, // Writeback source (0: ALU, 1: MEM, 2: PC+4) - - // System / Exception - output logic id_is_csr, - output logic [2:0] id_csr_op, - output logic id_illegal_instr, - output logic id_is_ecall, - output logic id_is_ebreak, - output logic id_is_mret, - output logic id_is_sret, - output logic id_is_wfi, - output logic id_is_sfence_vma, - output logic id_is_fence, - output logic id_is_fence_i -); - // Internal Wires for connecting sub-modules - logic [4:0] rs1_addr; - logic [4:0] rs2_addr; - logic [4:0] fs3_addr; - - // Pass-through PC and reg indices directly to next stage - assign id_pc = if_pc; - assign id_instr_len = if_instr_len; - assign id_rs1_addr = rs1_addr; - assign id_rs2_addr = rs2_addr; - assign fs3_addr = if_instr[31:27]; - assign id_fs3_addr = fs3_addr; - - // Instruction Decoder Instantiation - - decoder u_decoder ( - .instr (if_instr), - .priv_mode (csr_priv_mode), - .mstatus_tsr (csr_mstatus_tsr), - .mstatus_tw (csr_mstatus_tw), - .mstatus_tvm (csr_mstatus_tvm), - - // Outputs to RegFile read ports - .rs1_addr (rs1_addr), - .rs2_addr (rs2_addr), - .rd_addr (id_rd_addr), - - // Outputs to EX stage (ALU & execution control) - .alu_op (id_alu_op), - .alu_src1_sel (id_alu_src1_sel), - .alu_src2_sel (id_alu_src2_sel), - .is_word_op (id_is_word_op), - .imm (id_imm), - - // Branch & Jump control - .is_branch (id_is_branch), - .branch_op (id_branch_op), - .is_jump (id_is_jump), - - // Memory control - .is_load (id_is_load), - .is_store (id_is_store), - .mem_size (id_mem_size), - .is_amo (id_is_amo), - .amo_op (id_amo_op), - .is_fp (id_is_fp), - .fp_reg_write_en(id_fp_reg_write_en), - .fp_store_data_sel(id_fp_store_data_sel), - .fp_op (id_fp_op), - .fp_fmt (id_fp_fmt), - .fp_rm (id_fp_rm), - - // Writeback control - .reg_write_en (id_reg_write_en), - .wb_sel (id_wb_sel), - - // System Control - .is_csr (id_is_csr), - .csr_op (id_csr_op), - .is_ecall (id_is_ecall), - .is_ebreak (id_is_ebreak), - .is_mret (id_is_mret), - .is_sret (id_is_sret), - .is_wfi (id_is_wfi), - .is_sfence_vma (id_is_sfence_vma), - .is_fence (id_is_fence), - .is_fence_i (id_is_fence_i), - .illegal_instr (id_illegal_instr) - ); - - // Register File Instantiation - regfile u_regfile ( - .clk (clk), - .rst (rst), - - // Read ports (from Decoder) - .rs1_addr (rs1_addr), - .rs2_addr (rs2_addr), - .rs1_rdata (id_rs1_rdata), - .rs2_rdata (id_rs2_rdata), - - // Write port (from WB stage) - .reg_write_en (wb_reg_write_en), - .rd_addr (wb_rd_addr), - .rd_wdata (wb_rd_wdata) - ); - - fp_regfile u_fp_regfile ( - .clk (clk), - .rst (rst), - .fs1_addr (rs1_addr), - .fs2_addr (rs2_addr), - .fs3_addr (fs3_addr), - .fs1_rdata (id_fs1_rdata), - .fs2_rdata (id_fs2_rdata), - .fs3_rdata (id_fs3_rdata), - .fp_write_en (wb_fp_write_en), - .fd_addr (wb_fd_addr), - .fd_wdata (wb_fd_wdata) - ); - -endmodule diff --git a/rv64cpu/rtl/core/if_id_reg.sv b/rv64cpu/rtl/core/if_id_reg.sv deleted file mode 100644 index 0ea4fae..0000000 --- a/rv64cpu/rtl/core/if_id_reg.sv +++ /dev/null @@ -1,72 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/if_id_reg.sv -// Description: IF/ID Pipeline Register -// Latches PC and instruction from IF stage and passes them to ID stage. -// Supports stall (freeze) and flush (insert NOP). - -module if_id_reg - import rv64_pkg::*; -( - input logic clk, - input logic rst, - - // Pipeline Control from Hazard Unit - input logic stall_en, // 1: Freeze pipeline register state - input logic flush_en, // 1: Clear register (insert NOP) - - // Inputs from IF Stage - input logic [XLEN-1:0] if_pc, // PC from IF stage - input logic [INST_WIDTH-1:0] if_instr, // Fetched instruction from IF stage - input logic [2:0] if_instr_len, - input logic if_instr_access_fault, - input logic if_instr_page_fault, - - // Outputs to ID Stage - output logic [XLEN-1:0] id_pc, // Passed to ID stage - output logic [INST_WIDTH-1:0] id_instr, // Passed to ID stage - output logic [2:0] id_instr_len, - output logic id_instr_access_fault, - output logic id_instr_page_fault -); - - localparam logic [31:0] NOP_INSTR = 32'h0000_0013; - - always_ff @(posedge clk) begin - if (rst) begin - id_pc <= '0; - id_instr <= NOP_INSTR; - id_instr_len <= 3'd4; - id_instr_access_fault <= 1'b0; - id_instr_page_fault <= 1'b0; - end else if (flush_en) begin - id_pc <= '0; - id_instr <= NOP_INSTR; - id_instr_len <= 3'd4; - id_instr_access_fault <= 1'b0; - id_instr_page_fault <= 1'b0; - end else if (~stall_en) begin - id_pc <= if_pc; - id_instr <= if_instr; - id_instr_len <= if_instr_len; - id_instr_access_fault <= if_instr_access_fault; - id_instr_page_fault <= if_instr_page_fault; - end - // If stall_en && !flush_en, maintain current state - end - -endmodule diff --git a/rv64cpu/rtl/core/if_stage.sv b/rv64cpu/rtl/core/if_stage.sv deleted file mode 100644 index c3d417b..0000000 --- a/rv64cpu/rtl/core/if_stage.sv +++ /dev/null @@ -1,64 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/if_stage.sv -// Description: Instruction Fetching Stage, includes PC registers and next-state PC calculation logic - -module if_stage - import rv64_pkg::*; -( - input logic clk, - input logic rst, - - // Pipeline Control Signals (from Hazard Unit & Trap Controller) - input logic stall_en, // Freeze PC (stall), 0: Normal operation - input logic pc_redirect_sel, // Branch/Trap redirect request - input logic [XLEN-1:0] pc_redirect_target,// Redirect target address - input logic pc_redirect_is_trap, // 1: Redirect request comes from trap controller - input logic if_instr_compressed, - - output logic [XLEN-1:0] if_pc -); - - // PC Register - logic [XLEN-1:0] pc_reg; - - // Combinational logic signal: the PC value to be written in the next clock cycle - logic [XLEN-1:0] next_pc; - - always_comb begin - next_pc = pc_reg + (if_instr_compressed ? 64'd2 : 64'd4); - end - - // Sequential Logic: Update PC Register - always_ff @(posedge clk) begin - if (rst) begin - pc_reg <= 64'h0000_0000_8000_0000; - end else if (pc_redirect_sel && pc_redirect_is_trap) begin - pc_reg <= pc_redirect_target; - end else if (~stall_en) begin - if (pc_redirect_sel) begin - pc_reg <= pc_redirect_target; - end else begin - pc_reg <= next_pc; - end - end - end - - // Output wiring - assign if_pc = pc_reg; - -endmodule diff --git a/rv64cpu/rtl/core/include/.gitkeep b/rv64cpu/rtl/core/include/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/rtl/core/include/csr_pkg.sv b/rv64cpu/rtl/core/include/csr_pkg.sv deleted file mode 100644 index d1f9416..0000000 --- a/rv64cpu/rtl/core/include/csr_pkg.sv +++ /dev/null @@ -1,232 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/include/csr_pkg.sv -// Description: Global enumerations, address definitions, and exception reason constants related to CSR and privilege-level architecture - -package csr_pkg; - import rv64_pkg::*; - - typedef enum logic [2:0] { - CSR_OP_NONE = 3'b000, - CSR_OP_WRITE = 3'b001, - CSR_OP_SET = 3'b010, - CSR_OP_CLEAR = 3'b011 - } csr_op_t; - - // --------------------------------- - // Floating-Point CSRs - // --------------------------------- - localparam logic [11:0] CSR_FFLAGS = 12'h001; // Floating-Point accrued exception flags - localparam logic [11:0] CSR_FRM = 12'h002; // Floating-Point dynamic rounding mode - localparam logic [11:0] CSR_FCSR = 12'h003; // Combined fflags/frm register - - // --------------------------------- - // Machine Mode CSRs (M-Mode) - // --------------------------------- - localparam logic [11:0] CSR_MVENDORID = 12'hF11; // Vendor ID - localparam logic [11:0] CSR_MARCHID = 12'hF12; // Architecture ID - localparam logic [11:0] CSR_MIMPID = 12'hF13; // Implementation ID - localparam logic [11:0] CSR_MHARTID = 12'hF14; // Hart ID - localparam logic [11:0] CSR_MCONFIGPTR= 12'hF15; // Machine Configuration Pointer - - localparam logic [11:0] CSR_MSTATUS = 12'h300; // Machine Status Register - localparam logic [11:0] CSR_MISA = 12'h301; // Machine ISA Register - localparam logic [11:0] CSR_MEDELEG = 12'h302; // Exception Delegation - localparam logic [11:0] CSR_MIDELEG = 12'h303; // Interrupt Delegation - localparam logic [11:0] CSR_MIE = 12'h304; // Machine Interrupt Enable Register - localparam logic [11:0] CSR_MTVEC = 12'h305; // Machine Trap Vector Register - localparam logic [11:0] CSR_MCOUNTEREN= 12'h306; // Machine Counter Enable Register - - localparam logic [11:0] CSR_MENVCFG = 12'h30A; // Machine Environment Configuration - localparam logic [11:0] CSR_MSTATEEN0 = 12'h30C; // Machine State Enable 0 - localparam logic [11:0] CSR_MSTATEEN1 = 12'h30D; // Machine State Enable 1 - localparam logic [11:0] CSR_MSTATEEN2 = 12'h30E; // Machine State Enable 2 - localparam logic [11:0] CSR_MSTATEEN3 = 12'h30F; // Machine State Enable 3 - - localparam logic [11:0] CSR_PMPCFG0 = 12'h3A0; // PMP Configuration Register 0 - localparam logic [11:0] CSR_PMPCFG15 = 12'h3AF; // PMP Configuration Register 15 - localparam logic [11:0] CSR_PMPADDR0 = 12'h3B0; // PMP Address Register 0 - localparam logic [11:0] CSR_PMPADDR63 = 12'h3EF; // PMP Address Register 63 - - localparam logic [11:0] CSR_MSCRATCH = 12'h340; // Machine Scratch Register - localparam logic [11:0] CSR_MEPC = 12'h341; // Machine Exception Program Counter - localparam logic [11:0] CSR_MCAUSE = 12'h342; // Machine Cause Register - localparam logic [11:0] CSR_MTVAL = 12'h343; // Machine Trap Value Register - localparam logic [11:0] CSR_MIP = 12'h344; // Machine Interrupt Pending Register - localparam logic [11:0] CSR_MTINST = 12'h34A; // Machine Trap Instruction - localparam logic [11:0] CSR_MTVAL2 = 12'h34B; // Machine Second Trap Value - - localparam logic [11:0] CSR_MCOUNTINHIBIT = 12'h320; // Machine Counter-Inhibit Register - localparam logic [11:0] CSR_MCYCLE = 12'hB00; // Machine Cycle Counter - localparam logic [11:0] CSR_MINSTRET = 12'hB02; // Machine Instruction Retired Counter - localparam logic [11:0] CSR_MHPMCOUNTER3 = 12'hB03; // Machine HPM Counter 3 - localparam logic [11:0] CSR_MHPMCOUNTER31 = 12'hB1F; // Machine HPM Counter 31 - localparam logic [11:0] CSR_MHPMEVENT3 = 12'h323; // Machine HPM Event Selector 3 - localparam logic [11:0] CSR_MHPMEVENT31 = 12'h33F; // Machine HPM Event Selector 31 - - // --------------------------------- - // Supervisor Mode CSRs (S-Mode) - // --------------------------------- - localparam logic [11:0] CSR_SSTATUS = 12'h100; // Supervisor Status Register - localparam logic [11:0] CSR_SIE = 12'h104; // Supervisor Interrupt Enable Register - localparam logic [11:0] CSR_STVEC = 12'h105; // Supervisor Trap Vector Register - localparam logic [11:0] CSR_SCOUNTEREN= 12'h106; // Supervisor Counter Enable Register - localparam logic [11:0] CSR_SENVCFG = 12'h10A; // Supervisor Environment Configuration - localparam logic [11:0] CSR_SSTATEEN0 = 12'h10C; // Supervisor State Enable 0 - localparam logic [11:0] CSR_SSTATEEN1 = 12'h10D; // Supervisor State Enable 1 - localparam logic [11:0] CSR_SSTATEEN2 = 12'h10E; // Supervisor State Enable 2 - localparam logic [11:0] CSR_SSTATEEN3 = 12'h10F; // Supervisor State Enable 3 - localparam logic [11:0] CSR_SSCRATCH = 12'h140; // Supervisor Scratch Register - localparam logic [11:0] CSR_SEPC = 12'h141; // Supervisor Exception Program Counter - localparam logic [11:0] CSR_SCAUSE = 12'h142; // Supervisor Cause Register - localparam logic [11:0] CSR_STVAL = 12'h143; // Supervisor Trap Value Register - localparam logic [11:0] CSR_SIP = 12'h144; // Supervisor Interrupt Pending Register - localparam logic [11:0] CSR_SATP = 12'h180; // Supervisor Address Translation and Protection Register - - // --------------------------------- - // Hypervisor CSRs (H-Extension) - // --------------------------------- - localparam logic [11:0] CSR_HSTATUS = 12'h600; // Hypervisor Status - localparam logic [11:0] CSR_HEDELEG = 12'h602; // Hypervisor Exception Delegation - localparam logic [11:0] CSR_HIDELEG = 12'h603; // Hypervisor Interrupt Delegation - localparam logic [11:0] CSR_HIE = 12'h604; // Hypervisor Interrupt Enable - localparam logic [11:0] CSR_HTIMEDELTA = 12'h605; // Hypervisor Timer Delta - localparam logic [11:0] CSR_HCOUNTEREN = 12'h606; // Hypervisor Counter Enable - localparam logic [11:0] CSR_HGEIE = 12'h607; // Hypervisor Guest External Interrupt Enable - localparam logic [11:0] CSR_HENVCFG = 12'h60A; // Hypervisor Environment Configuration - localparam logic [11:0] CSR_HSTATEEN0 = 12'h60C; // Hypervisor State Enable 0 - localparam logic [11:0] CSR_HSTATEEN1 = 12'h60D; // Hypervisor State Enable 1 - localparam logic [11:0] CSR_HSTATEEN2 = 12'h60E; // Hypervisor State Enable 2 - localparam logic [11:0] CSR_HSTATEEN3 = 12'h60F; // Hypervisor State Enable 3 - localparam logic [11:0] CSR_HTVAL = 12'h643; // Hypervisor Trap Value - localparam logic [11:0] CSR_HIP = 12'h644; // Hypervisor Interrupt Pending - localparam logic [11:0] CSR_HVIP = 12'h645; // Hypervisor Virtual Interrupt Pending - localparam logic [11:0] CSR_HTINST = 12'h64A; // Hypervisor Trap Instruction - localparam logic [11:0] CSR_HGATP = 12'h680; // Hypervisor Guest Address Translation and Protection - localparam logic [11:0] CSR_HGEIP = 12'hE12; // Hypervisor Guest External Interrupt Pending - - localparam logic [11:0] CSR_VSSTATUS = 12'h200; // Virtual Supervisor Status - localparam logic [11:0] CSR_VSIE = 12'h204; // Virtual Supervisor Interrupt Enable - localparam logic [11:0] CSR_VSTVEC = 12'h205; // Virtual Supervisor Trap Vector - localparam logic [11:0] CSR_VSSCRATCH = 12'h240; // Virtual Supervisor Scratch - localparam logic [11:0] CSR_VSEPC = 12'h241; // Virtual Supervisor Exception PC - localparam logic [11:0] CSR_VSCAUSE = 12'h242; // Virtual Supervisor Cause - localparam logic [11:0] CSR_VSTVAL = 12'h243; // Virtual Supervisor Trap Value - localparam logic [11:0] CSR_VSIP = 12'h244; // Virtual Supervisor Interrupt Pending - localparam logic [11:0] CSR_VSATP = 12'h280; // Virtual Supervisor Address Translation and Protection - - // --------------------------------- - // User Mode CSRs (U-Mode / URW) - // --------------------------------- - localparam logic [11:0] CSR_CYCLE = 12'hC00; // Cycle Counter - localparam logic [11:0] CSR_TIME = 12'hC01; // Timer Counter - localparam logic [11:0] CSR_INSTRET = 12'hC02; // Instruction Retired Counter - localparam logic [11:0] CSR_HPMCOUNTER3 = 12'hC03; // HPM Counter 3 - localparam logic [11:0] CSR_HPMCOUNTER31 = 12'hC1F; // HPM Counter 31 - - // --------------------------------- - // Exception Cause Codes (mcause.Interrupt == 0) - // --------------------------------- - localparam logic [XLEN-1:0] EXC_INST_ADDR_MISALIGNED = 64'd0; // Instruction Address Misaligned - localparam logic [XLEN-1:0] EXC_INST_FAULT = 64'd1; // Instruction Access Fault - localparam logic [XLEN-1:0] EXC_INST_ILLEGAL = 64'd2; // Illegal Instruction - localparam logic [XLEN-1:0] EXC_BREAKPOINT = 64'd3; // Breakpoint - localparam logic [XLEN-1:0] EXC_LOAD_ADDR_MISALIGNED = 64'd4; // Load Address Misaligned - localparam logic [XLEN-1:0] EXC_LOAD_FAULT = 64'd5; // Load Access Fault - localparam logic [XLEN-1:0] EXC_STORE_ADDR_MISALIGNED = 64'd6; // Store Address Misaligned - localparam logic [XLEN-1:0] EXC_STORE_FAULT = 64'd7; // Store Access Fault - localparam logic [XLEN-1:0] EXC_ECALL_FROM_U = 64'd8; // Environment Call from U-Mode - localparam logic [XLEN-1:0] EXC_ECALL_FROM_S = 64'd9; // Environment Call from S-Mode - localparam logic [XLEN-1:0] EXC_ECALL_FROM_M = 64'd11; // Environment Call from M-Mode - localparam logic [XLEN-1:0] EXC_INSTR_PAGE_FAULT = 64'd12; // Instruction Page Fault - localparam logic [XLEN-1:0] EXC_LOAD_PAGE_FAULT = 64'd13; // Load Page Fault - localparam logic [XLEN-1:0] EXC_STORE_PAGE_FAULT = 64'd15; // Store Page Fault - - localparam logic [XLEN-1:0] MEDELEG_WRITABLE_MASK = - (64'h1 << EXC_INST_ADDR_MISALIGNED) | - (64'h1 << EXC_INST_FAULT) | - (64'h1 << EXC_INST_ILLEGAL) | - (64'h1 << EXC_BREAKPOINT) | - (64'h1 << EXC_LOAD_ADDR_MISALIGNED) | - (64'h1 << EXC_LOAD_FAULT) | - (64'h1 << EXC_STORE_ADDR_MISALIGNED) | - (64'h1 << EXC_STORE_FAULT) | - (64'h1 << EXC_ECALL_FROM_U) | - (64'h1 << EXC_ECALL_FROM_S) | - (64'h1 << EXC_INSTR_PAGE_FAULT) | - (64'h1 << EXC_LOAD_PAGE_FAULT) | - (64'h1 << EXC_STORE_PAGE_FAULT); - - localparam logic [XLEN-1:0] MEDELEG_READ_ONLY_ONE_MASK = '0; - - // --------------------------------- - // Interrupt Cause Codes (mcause.Interrupt == 1) - // --------------------------------- - // User Mode Interrupts - localparam logic [XLEN-1:0] INTERRUPT_U_SOFTWARE = 64'd0; // User Mode Software Interrupt - localparam logic [XLEN-1:0] INTERRUPT_U_TIMER = 64'd4; // User Mode Timer Interrupt - localparam logic [XLEN-1:0] INTERRUPT_U_EXTERNAL = 64'd8; // User Mode External Interrupt - // Supervisor Mode Interrupts - localparam logic [XLEN-1:0] INTERRUPT_S_SOFTWARE = 64'd1; // Supervisor Mode Software Interrupt - localparam logic [XLEN-1:0] INTERRUPT_S_TIMER = 64'd5; // Supervisor Mode Timer Interrupt - localparam logic [XLEN-1:0] INTERRUPT_S_EXTERNAL = 64'd9; // Supervisor Mode External Interrupt - // Machine Mode Interrupts - localparam logic [XLEN-1:0] INTERRUPT_M_SOFTWARE = 64'd3; // Machine Mode Software Interrupt - localparam logic [XLEN-1:0] INTERRUPT_M_TIMER = 64'd7; // Machine Mode Timer Interrupt - localparam logic [XLEN-1:0] INTERRUPT_M_EXTERNAL = 64'd11; // Machine Mode External Interrupt - - localparam logic [XLEN-1:0] MIDELEG_WRITABLE_MASK = - (64'h1 << INTERRUPT_S_SOFTWARE) | - (64'h1 << INTERRUPT_S_TIMER) | - (64'h1 << INTERRUPT_S_EXTERNAL); - - // --------------------------------- - // Physical Memory Protection (PMP) - // --------------------------------- - localparam int PMP_ENTRY_COUNT = 64; - localparam int PMP_CFG_CSR_COUNT = 16; - localparam int PMP_CFG_ENTRIES_PER_RV64_CSR = 8; - - typedef logic [7:0] pmpcfg_entry_t; - typedef logic [XLEN-1:0] pmpaddr_entry_t; - - localparam int PMPCFG_R_BIT = 0; - localparam int PMPCFG_W_BIT = 1; - localparam int PMPCFG_X_BIT = 2; - localparam int PMPCFG_A_LO = 3; - localparam int PMPCFG_A_HI = 4; - localparam int PMPCFG_L_BIT = 7; - - localparam logic [1:0] PMP_MODE_OFF = 2'b00; - localparam logic [1:0] PMP_MODE_TOR = 2'b01; - localparam logic [1:0] PMP_MODE_NA4 = 2'b10; - localparam logic [1:0] PMP_MODE_NAPOT = 2'b11; - - // --------------------------------- - // Environment Configuration / State Enable - // --------------------------------- - localparam int ENVCFG_FIOM_BIT = 0; - localparam int ENVCFG_CBIE_LO = 4; - localparam int ENVCFG_CBIE_HI = 5; - localparam int ENVCFG_CBCFE_BIT = 6; - localparam int ENVCFG_CBZE_BIT = 7; - localparam int ENVCFG_PMM_LO = 32; - localparam int ENVCFG_PMM_HI = 33; - localparam int STATEEN0_ENVCFG_BIT = 62; - localparam int STATEEN_SE0_BIT = 63; - -endpackage diff --git a/rv64cpu/rtl/core/include/rv64_pkg.sv b/rv64cpu/rtl/core/include/rv64_pkg.sv deleted file mode 100644 index afa7979..0000000 --- a/rv64cpu/rtl/core/include/rv64_pkg.sv +++ /dev/null @@ -1,162 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/include/rv64_pkg.sv -// Description: RISC-V 64-bit base instruction set global definition package - -package rv64_pkg; - - localparam XLEN = 64; - localparam INST_WIDTH = 32; - - // RISC-V Base Opcode Map (inst[6:0]) - // Reference: RISC-V Unprivileged ISA V20240411, Table 34 - typedef enum logic [6:0] { - // Loads - OPCODE_LOAD = 7'b000_0011, // LB/LH/LW/LD/LBU/LHU/LWU - OPCODE_LOAD_FP = 7'b000_0111, // FLW/FLD - - // Misc-Mem - OPCODE_MISC_MEM = 7'b000_1111, // FENCE / FENCE.I (Zifencei) - - // Immediate ALU - OPCODE_OP_IMM = 7'b001_0011, // ADDI/SLTI/ANDI/ORI/XORI/SLLI/SRLI/SRAI... - OPCODE_OP_IMM_32 = 7'b001_1011, // ADDIW/SLLIW/SRLIW/SRAIW (RV64 only) - - // Upper Immediate - OPCODE_AUIPC = 7'b001_0111, // AUIPC - OPCODE_LUI = 7'b011_0111, // LUI - - // Stores - OPCODE_STORE = 7'b010_0011, // SB/SH/SW/SD - OPCODE_STORE_FP = 7'b010_0111, // FSW/FSD - - // Atomic (A extension) - OPCODE_AMO = 7'b010_1111, // LR/SC/AMOSWAP/AMOADD/... - - // Register ALU - OPCODE_OP = 7'b011_0011, // ADD/SUB/AND/OR/XOR/SLL/SRL/SRA/SLT/SLTU/MUL/DIV... - OPCODE_OP_32 = 7'b011_1011, // ADDW/SUBW/SLLW/SRLW/SRAW/MULW/DIVW... (RV64 only) - - // Floating-point fused multiply-add - OPCODE_MADD = 7'b100_0011, // FMADD.S/D - OPCODE_MSUB = 7'b100_0111, // FMSUB.S/D - OPCODE_NMSUB = 7'b100_1011, // FNMSUB.S/D - OPCODE_NMADD = 7'b100_1111, // FNMADD.S/D - - // Floating-point arithmetic - OPCODE_OP_FP = 7'b101_0011, // FADD/FSUB/FMUL/FDIV/FSQRT/FCMP/FCVT/FMV/FCLASS... - - // Branches - OPCODE_BRANCH = 7'b110_0011, // BEQ/BNE/BLT/BGE/BLTU/BGEU - - // Jumps - OPCODE_JALR = 7'b110_0111, // JALR - OPCODE_JAL = 7'b110_1111, // JAL - - // System - OPCODE_SYSTEM = 7'b111_0011, // ECALL/EBREAK/CSR*/MRET/SRET/WFI/SFENCE.VMA - - // Custom (reserved for extensions) - OPCODE_CUSTOM_0 = 7'b000_1011, - OPCODE_CUSTOM_1 = 7'b010_1011, - OPCODE_CUSTOM_2 = 7'b101_1011, - OPCODE_CUSTOM_3 = 7'b111_1011 - } opcode_t; - - // ALU Operation Encoding - // Derived from funct3 + funct7[5] of R-type / I-type instructions - typedef enum logic [4:0] { - ALU_ADD = 5'd0, // Add - ALU_SUB = 5'd8, // Subtract - ALU_SLL = 5'd1, // Shift Left Logical - ALU_SLT = 5'd2, // Set Less Than (signed) - ALU_SLTU = 5'd3, // Set Less Than (unsigned) - ALU_XOR = 5'd4, // Exclusive Or - ALU_SRL = 5'd5, // Shift Right Logical - ALU_SRA = 5'd13, // Shift Right Arithmetic - ALU_OR = 5'd6, // Or - ALU_AND = 5'd7, // And - ALU_MUL = 5'd16, // Low XLEN bits of signed multiplication - ALU_MULH = 5'd17, // High XLEN bits of signed x signed multiplication - ALU_MULHSU = 5'd18, // High XLEN bits of signed x unsigned multiplication - ALU_MULHU = 5'd19, // High XLEN bits of unsigned x unsigned multiplication - ALU_DIV = 5'd20, // Signed division - ALU_DIVU = 5'd21, // Unsigned division - ALU_REM = 5'd22, // Signed remainder - ALU_REMU = 5'd23 // Unsigned remainder - } alu_op_t; - - - // Branch Operation Encoding - // Derived from funct3 of B-type instructions - typedef enum logic [2:0] { - BRANCH_EQ = 3'b000, // BEQ: Branch if Equal - BRANCH_NE = 3'b001, // BNE: Branch if Not Equal - BRANCH_LT = 3'b100, // BLT: Branch if Less Than (signed) - BRANCH_GE = 3'b101, // BGE: Branch if Greater Than or Equal (signed) - BRANCH_LTU = 3'b110, // BLTU: Branch if Less Than (unsigned) - BRANCH_GEU = 3'b111 // BGEU: Branch if Greater Than or Equal (unsigned) - } branch_op_t; - - typedef enum logic [3:0] { - AMO_NONE = 4'd0, - AMO_LR = 4'd1, - AMO_SC = 4'd2, - AMO_SWAP = 4'd3, - AMO_ADD = 4'd4, - AMO_XOR = 4'd5, - AMO_AND = 4'd6, - AMO_OR = 4'd7, - AMO_MIN = 4'd8, - AMO_MAX = 4'd9, - AMO_MINU = 4'd10, - AMO_MAXU = 4'd11 - } amo_op_t; - - typedef enum logic [4:0] { - FP_OP_NONE = 5'd0, - FP_OP_ADD = 5'd1, - FP_OP_SUB = 5'd2, - FP_OP_MUL = 5'd3, - FP_OP_DIV = 5'd4, - FP_OP_SQRT = 5'd5, - FP_OP_SGNJ = 5'd6, - FP_OP_SGNJN = 5'd7, - FP_OP_SGNJX = 5'd8, - FP_OP_MIN = 5'd9, - FP_OP_MAX = 5'd10, - FP_OP_CVT_F2I = 5'd11, - FP_OP_CVT_I2F = 5'd12, - FP_OP_CVT_F2F = 5'd13, - FP_OP_MV_F2X = 5'd14, - FP_OP_MV_X2F = 5'd15, - FP_OP_EQ = 5'd16, - FP_OP_LT = 5'd17, - FP_OP_LE = 5'd18, - FP_OP_CLASS = 5'd19, - FP_OP_MADD = 5'd20, - FP_OP_MSUB = 5'd21, - FP_OP_NMSUB = 5'd22, - FP_OP_NMADD = 5'd23 - } fp_op_t; - - typedef enum logic [1:0] { - FP_FMT_S = 2'd0, - FP_FMT_D = 2'd1 - } fp_fmt_t; - -endpackage diff --git a/rv64cpu/rtl/core/mem_stage.sv b/rv64cpu/rtl/core/mem_stage.sv deleted file mode 100644 index d5a97dc..0000000 --- a/rv64cpu/rtl/core/mem_stage.sv +++ /dev/null @@ -1,430 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/mem_stage.sv -// Description: Memory Stage -// Latches results from the EX stage and bypassed control signals from ID/EX. -// Supports stall and flush operations. - -module mem_stage - import rv64_pkg::*; -( - input logic clk, - input logic rst, - - // Inputs from EX/MEM pipeline register - input logic [XLEN-1:0] mem_alu_result, // Used as memory address or ALU result - input logic mem_branch_taken, - input logic [XLEN-1:0] mem_branch_target, - input logic [XLEN-1:0] mem_rs1_rdata, - input logic [XLEN-1:0] mem_rs2_rdata, // Store data - input logic [XLEN-1:0] mem_fs2_rdata, - input logic [4:0] mem_rs1_addr, - input logic [XLEN-1:0] mem_pc, - input logic [XLEN-1:0] mem_imm, - input logic [INST_WIDTH-1:0] mem_instr, - input logic [2:0] mem_instr_len, - - // Memory Control - input logic mem_is_load, - input logic mem_is_store, - input logic [2:0] mem_mem_size, // LB/LH/LW/LD/LBU/LHU/LWU - input logic mem_is_amo, - input amo_op_t mem_amo_op, - input logic mem_is_fp, - input logic mem_fp_reg_write_en, - input logic mem_fp_store_data_sel, - input logic [XLEN-1:0] mem_fp_result, - input logic [4:0] mem_fp_fflags, - - // Writeback Control - input logic mem_reg_write_en, - input logic [4:0] mem_rd_addr, - input logic [1:0] mem_wb_sel, - - // Exceptions and System (bypassed through or generated) - input logic mem_is_csr, - input logic [2:0] mem_csr_op, - input logic mem_illegal_instr, - input logic mem_instr_access_fault, - input logic mem_instr_page_fault, - input logic mem_is_ecall, - input logic mem_is_ebreak, - input logic mem_is_mret, - input logic mem_is_sret, - input logic mem_is_wfi, - input logic mem_is_sfence_vma, - input logic mem_is_fence, - input logic mem_is_fence_i, - - // Data Memory Interface (to Data Cache / Memory Controller) - output logic dmem_read_req, - output logic dmem_write_req, - output logic [XLEN-1:0] dmem_addr, - output logic [XLEN-1:0] dmem_wdata, - output logic [7:0] dmem_wstrb, // Write strobe depending on size - input logic dmem_req_ready, // Wait signal from D-Cache - input logic dmem_rsp_valid, // Data valid from D-Cache - input logic [XLEN-1:0] dmem_rdata, - input logic dmem_load_access_fault, - input logic dmem_store_access_fault, - input logic dmem_load_page_fault, - input logic dmem_store_page_fault, - - // Outputs to MEM/WB Pipeline Register - output logic [XLEN-1:0] mem_wb_alu_result, - output logic [XLEN-1:0] mem_wb_mem_rdata, - output logic [XLEN-1:0] mem_wb_fp_result, - output logic [4:0] mem_wb_fp_fflags, - output logic [XLEN-1:0] mem_wb_pc, - output logic [INST_WIDTH-1:0] mem_wb_instr, - output logic [2:0] mem_wb_instr_len, - - output logic mem_wb_reg_write_en, - output logic mem_wb_fp_reg_write_en, - output logic [4:0] mem_wb_rd_addr, - output logic [1:0] mem_wb_wb_sel, - - output logic mem_wb_is_csr, - output logic [2:0] mem_wb_csr_op, - output logic [XLEN-1:0] mem_wb_rs1_rdata, - output logic [4:0] mem_wb_rs1_addr, - output logic [XLEN-1:0] mem_wb_imm, - - output logic mem_wb_illegal_instr, - output logic mem_wb_instr_access_fault, - output logic mem_wb_instr_page_fault, - output logic mem_wb_is_ecall, - output logic mem_wb_is_ebreak, - output logic mem_wb_is_mret, - output logic mem_wb_is_sret, - output logic mem_wb_load_fault, - output logic mem_wb_store_fault, - output logic mem_wb_load_page_fault, - output logic mem_wb_store_page_fault, - output logic [XLEN-1:0] mem_wb_fault_addr, - - // Control to Hazard Unit / Stall Logic - output logic mem_stall_req // Stall pipeline if D-Cache doesn't respond -); - - logic [XLEN-1:0] load_data_aligned; - logic [XLEN-1:0] store_data_aligned; - logic [XLEN-1:0] amo_write_data; - logic [XLEN-1:0] amo_wb_data; - logic [XLEN-1:0] selected_store_data; - logic [XLEN-1:0] fp_load_data; - logic [7:0] write_strobe; - logic load_addr_misaligned; - logic store_addr_misaligned; - logic amo_is_lr; - logic amo_is_sc; - logic amo_is_word; - logic amo_is_double; - logic amo_reads_memory; - logic amo_writes_memory; - logic amo_store_access; - logic amo_load_access; - logic amo_addr_misaligned; - logic amo_sc_success; - logic amo_rmw_op; - logic amo_rmw_read_phase; - logic amo_rmw_write_phase; - logic [XLEN-1:0] amo_memory_rdata; - logic amo_rmw_busy_q; - logic [XLEN-1:0] amo_rmw_old_data_q; - logic reservation_valid_q; - logic [XLEN-1:0] reservation_addr_q; - - function automatic logic addr_misaligned( - input logic [2:0] size, - input logic [XLEN-1:0] addr - ); - logic result; - case (size) - 3'b000, 3'b100: result = 1'b0; // Byte L/S - 3'b001, 3'b101: result = addr[0]; // Halfword requires bit0 = 0 - 3'b010, 3'b110: result = |addr[1:0]; // Word requires bits[1:0] = 0 - 3'b011: result = |addr[2:0]; // Doubleword requires bits[2:0] = 0 - default: result = 1'b0; - endcase - return result; - endfunction - - assign amo_is_lr = mem_is_amo && (mem_amo_op == AMO_LR); - assign amo_is_sc = mem_is_amo && (mem_amo_op == AMO_SC); - assign amo_is_word = mem_is_amo && (mem_mem_size == 3'b010); - assign amo_is_double = mem_is_amo && (mem_mem_size == 3'b011); - assign amo_rmw_op = mem_is_amo && !amo_is_lr && !amo_is_sc; - assign amo_rmw_read_phase = amo_rmw_op && !amo_rmw_busy_q; - assign amo_rmw_write_phase = amo_rmw_op && amo_rmw_busy_q; - assign amo_reads_memory = amo_is_lr || amo_rmw_read_phase; - assign amo_store_access = mem_is_amo && !amo_is_lr; - assign amo_load_access = mem_is_amo && amo_is_lr; - assign amo_addr_misaligned = mem_is_amo && addr_misaligned(mem_mem_size, mem_alu_result); - assign amo_sc_success = amo_is_sc && reservation_valid_q && - (reservation_addr_q == mem_alu_result) && - !amo_addr_misaligned && !dmem_store_access_fault && - !dmem_store_page_fault && !mem_instr_access_fault && - !mem_instr_page_fault; - assign amo_writes_memory = (amo_is_sc && amo_sc_success) || amo_rmw_write_phase; - assign amo_memory_rdata = amo_rmw_busy_q ? amo_rmw_old_data_q : dmem_rdata; - - assign load_addr_misaligned = mem_is_load && addr_misaligned(mem_mem_size, mem_alu_result); - assign store_addr_misaligned = (mem_is_store && addr_misaligned(mem_mem_size, mem_alu_result)) | - (amo_store_access && amo_addr_misaligned); - - always_ff @(posedge clk) begin - if (rst) begin - amo_rmw_busy_q <= 1'b0; - amo_rmw_old_data_q <= '0; - reservation_valid_q <= 1'b0; - reservation_addr_q <= '0; - end else begin - if (amo_rmw_read_phase && dmem_read_req && dmem_rsp_valid && !dmem_load_access_fault && - !dmem_load_page_fault && !mem_instr_access_fault && !mem_instr_page_fault) begin - amo_rmw_busy_q <= 1'b1; - amo_rmw_old_data_q <= dmem_rdata; - end else if (amo_rmw_write_phase && (!dmem_write_req || dmem_req_ready)) begin - amo_rmw_busy_q <= 1'b0; - amo_rmw_old_data_q <= '0; - end else if (!mem_is_amo) begin - amo_rmw_busy_q <= 1'b0; - amo_rmw_old_data_q <= '0; - end - - if (amo_is_lr && dmem_read_req && dmem_rsp_valid && !dmem_load_access_fault && - !dmem_load_page_fault) begin - reservation_valid_q <= 1'b1; - reservation_addr_q <= mem_alu_result; - end else if (amo_is_sc && dmem_req_ready) begin - reservation_valid_q <= 1'b0; - reservation_addr_q <= '0; - end - end - end - - // -------------------------- - // Memory Request Logic - assign dmem_read_req = (mem_is_load | amo_reads_memory) & - ~(load_addr_misaligned | (amo_reads_memory & amo_addr_misaligned)) & - ~dmem_load_access_fault & ~dmem_load_page_fault & - ~mem_instr_access_fault & ~mem_instr_page_fault; - assign dmem_write_req = (mem_is_store | amo_writes_memory) & ~store_addr_misaligned & ~dmem_store_access_fault & - ~dmem_store_page_fault & ~mem_instr_access_fault & ~mem_instr_page_fault; - assign dmem_addr = mem_alu_result; // ALU result is the computed address - - // -------------------------- - // Stall generation: Stall if memory request is not yet ready or valid - // For a simple synchronous SRAM, this might be 0, but for cache it will depend on hits/misses - // -------------------------- - always_comb begin - if (amo_rmw_read_phase && dmem_read_req) begin - mem_stall_req = 1'b1; - end else if ((dmem_read_req || dmem_write_req) && (!dmem_req_ready || (dmem_read_req && !dmem_rsp_valid))) begin - mem_stall_req = 1'b1; - end else begin - mem_stall_req = 1'b0; - end - end - - // -------------------------- - // Store Data Alignment and Strobe Generation - // -------------------------- - always_comb begin - logic [2:0] align_offset; - store_data_aligned = '0; - write_strobe = 8'b0000_0000; - selected_store_data = mem_fp_store_data_sel ? mem_fs2_rdata : mem_rs2_rdata; - - // Mem address lower 3 bits determine the alignment within the 64-bit word - align_offset = mem_alu_result[2:0]; - - if (mem_is_store || amo_writes_memory) begin - case (mem_mem_size) - // SB: Store Byte - 3'b000: begin - store_data_aligned = {56'b0, selected_store_data[7:0]} << (align_offset * 8); - write_strobe = 8'b0000_0001 << align_offset; - end - // SH: Store Halfword - 3'b001: begin - store_data_aligned = {48'b0, selected_store_data[15:0]} << (align_offset * 8); - write_strobe = 8'b0000_0011 << align_offset; - end - // SW: Store Word - 3'b010: begin - store_data_aligned = {32'b0, amo_writes_memory ? amo_write_data[31:0] : - selected_store_data[31:0]} << (align_offset * 8); - write_strobe = 8'b0000_1111 << align_offset; - end - // SD: Store Doubleword - 3'b011: begin - store_data_aligned = amo_writes_memory ? amo_write_data : selected_store_data; - write_strobe = 8'b1111_1111; - end - default: begin - store_data_aligned = '0; - write_strobe = 8'b0000_0000; - end - endcase - end - end - - assign dmem_wdata = store_data_aligned; - assign dmem_wstrb = write_strobe; - - // -------------------------- - // Load Data Alignment and Sign/Zero Extension - // -------------------------- - always_comb begin - logic [2:0] align_offset; - logic [XLEN-1:0] shifted_rdata; - load_data_aligned = '0; - - align_offset = mem_alu_result[2:0]; - - // Shift data down so the target bytes are at the bottom - shifted_rdata = dmem_rdata >> (align_offset * 8); - - case (mem_mem_size) - // LB: Load Byte (Sign extend) - 3'b000: load_data_aligned = { {56{shifted_rdata[7]}}, shifted_rdata[7:0] }; - // LH: Load Halfword (Sign extend) - 3'b001: load_data_aligned = { {48{shifted_rdata[15]}}, shifted_rdata[15:0] }; - // LW: Load Word (Sign extend) - 3'b010: load_data_aligned = { {32{shifted_rdata[31]}}, shifted_rdata[31:0] }; - // LD: Load Doubleword - 3'b011: load_data_aligned = shifted_rdata; - // LBU: Load Byte Unsigned (Zero extend) - 3'b100: load_data_aligned = { 56'b0, shifted_rdata[7:0] }; - // LHU: Load Half Unsigned (Zero extend) - 3'b101: load_data_aligned = { 48'b0, shifted_rdata[15:0] }; - // LWU: Load Word Unsigned (Zero extend) - 3'b110: load_data_aligned = { 32'b0, shifted_rdata[31:0] }; - default: load_data_aligned = '0; - endcase - end - - always_comb begin - fp_load_data = load_data_aligned; - if (mem_is_fp && (mem_mem_size == 3'b010)) begin - fp_load_data = {32'hFFFF_FFFF, load_data_aligned[31:0]}; - end - end - - always_comb begin - logic [2:0] align_offset; - logic [XLEN-1:0] shifted_rdata; - logic [31:0] amo_old_word; - logic [31:0] amo_rs2_word; - logic [31:0] amo_result_word; - logic signed [31:0] amo_old_word_signed; - logic signed [31:0] amo_rs2_word_signed; - logic signed [XLEN-1:0] amo_old_double_signed; - logic signed [XLEN-1:0] amo_rs2_double_signed; - - align_offset = mem_alu_result[2:0]; - shifted_rdata = amo_memory_rdata >> (align_offset * 8); - amo_old_word = shifted_rdata[31:0]; - amo_rs2_word = mem_rs2_rdata[31:0]; - amo_old_word_signed = amo_old_word; - amo_rs2_word_signed = amo_rs2_word; - amo_old_double_signed = amo_memory_rdata; - amo_rs2_double_signed = mem_rs2_rdata; - amo_result_word = amo_old_word; - amo_write_data = mem_rs2_rdata; - amo_wb_data = '0; - - if (amo_is_word) begin - unique case (mem_amo_op) - AMO_SWAP, AMO_SC: amo_result_word = amo_rs2_word; - AMO_ADD: amo_result_word = amo_old_word + amo_rs2_word; - AMO_XOR: amo_result_word = amo_old_word ^ amo_rs2_word; - AMO_AND: amo_result_word = amo_old_word & amo_rs2_word; - AMO_OR: amo_result_word = amo_old_word | amo_rs2_word; - AMO_MIN: amo_result_word = (amo_old_word_signed < amo_rs2_word_signed) ? - amo_old_word : amo_rs2_word; - AMO_MAX: amo_result_word = (amo_old_word_signed > amo_rs2_word_signed) ? - amo_old_word : amo_rs2_word; - AMO_MINU: amo_result_word = (amo_old_word < amo_rs2_word) ? amo_old_word : amo_rs2_word; - AMO_MAXU: amo_result_word = (amo_old_word > amo_rs2_word) ? amo_old_word : amo_rs2_word; - default: amo_result_word = amo_old_word; - endcase - amo_write_data = {{32{amo_result_word[31]}}, amo_result_word}; - amo_wb_data = amo_is_sc ? {63'b0, ~amo_sc_success} : - {{32{amo_old_word[31]}}, amo_old_word}; - end else if (amo_is_double) begin - unique case (mem_amo_op) - AMO_SWAP, AMO_SC: amo_write_data = mem_rs2_rdata; - AMO_ADD: amo_write_data = amo_memory_rdata + mem_rs2_rdata; - AMO_XOR: amo_write_data = amo_memory_rdata ^ mem_rs2_rdata; - AMO_AND: amo_write_data = amo_memory_rdata & mem_rs2_rdata; - AMO_OR: amo_write_data = amo_memory_rdata | mem_rs2_rdata; - AMO_MIN: amo_write_data = (amo_old_double_signed < amo_rs2_double_signed) ? - amo_memory_rdata : mem_rs2_rdata; - AMO_MAX: amo_write_data = (amo_old_double_signed > amo_rs2_double_signed) ? - amo_memory_rdata : mem_rs2_rdata; - AMO_MINU: amo_write_data = (amo_memory_rdata < mem_rs2_rdata) ? amo_memory_rdata : - mem_rs2_rdata; - AMO_MAXU: amo_write_data = (amo_memory_rdata > mem_rs2_rdata) ? amo_memory_rdata : - mem_rs2_rdata; - default: amo_write_data = amo_memory_rdata; - endcase - amo_wb_data = amo_is_sc ? {63'b0, ~amo_sc_success} : amo_memory_rdata; - end - end - - // -------------------------- - // Pass Output Signals to WB Stage - // -------------------------- - assign mem_wb_alu_result = mem_alu_result; - assign mem_wb_mem_rdata = mem_is_amo ? amo_wb_data : load_data_aligned; - assign mem_wb_fp_result = mem_is_load ? fp_load_data : mem_fp_result; - assign mem_wb_fp_fflags = mem_fp_fflags; - assign mem_wb_pc = mem_pc; - assign mem_wb_instr = mem_instr; - assign mem_wb_instr_len = mem_instr_len; - - assign mem_wb_reg_write_en = mem_reg_write_en; - assign mem_wb_fp_reg_write_en = mem_fp_reg_write_en; - assign mem_wb_rd_addr = mem_rd_addr; - assign mem_wb_wb_sel = mem_wb_sel; - - assign mem_wb_is_csr = mem_is_csr; - assign mem_wb_csr_op = mem_csr_op; - assign mem_wb_rs1_rdata = mem_rs1_rdata; - assign mem_wb_rs1_addr = mem_rs1_addr; - assign mem_wb_imm = mem_imm; - - assign mem_wb_illegal_instr = mem_illegal_instr; - assign mem_wb_instr_access_fault = mem_instr_access_fault; - assign mem_wb_instr_page_fault = mem_instr_page_fault; - assign mem_wb_is_ecall = mem_is_ecall; - assign mem_wb_is_ebreak = mem_is_ebreak; - assign mem_wb_is_mret = mem_is_mret; - assign mem_wb_is_sret = mem_is_sret; - assign mem_wb_load_fault = load_addr_misaligned | (amo_load_access & amo_addr_misaligned) | - dmem_load_access_fault; - assign mem_wb_store_fault = store_addr_misaligned | dmem_store_access_fault; - assign mem_wb_load_page_fault = dmem_load_page_fault; - assign mem_wb_store_page_fault = dmem_store_page_fault; - assign mem_wb_fault_addr = (mem_instr_access_fault | mem_instr_page_fault) ? mem_pc : - ((dmem_load_page_fault | dmem_store_page_fault) ? mem_alu_result : - ((load_addr_misaligned | store_addr_misaligned | - dmem_load_access_fault | dmem_store_access_fault) ? mem_alu_result : '0)); - -endmodule diff --git a/rv64cpu/rtl/core/mem_wb_reg.sv b/rv64cpu/rtl/core/mem_wb_reg.sv deleted file mode 100644 index be0aec1..0000000 --- a/rv64cpu/rtl/core/mem_wb_reg.sv +++ /dev/null @@ -1,188 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/mem_wb_reg.sv -// Description: MEM/WB pipeline stage register (Memory Access to Write Back) -// Passes the final computation results, memory read data, and write-back control signals to the write-back stage - -module mem_wb_reg - import rv64_pkg::*; -( - input logic clk, - input logic rst, - - // ----------------------- - // Control Signals from Hazard Unit - input logic stall_en, - input logic flush_en, - - // ----------------------- - // Inputs from MEM stage - // ----------------------- - // Data and addresses - input logic [XLEN-1:0] mem_pc, - input logic [XLEN-1:0] mem_alu_result, - input logic [XLEN-1:0] mem_mem_rdata, - input logic [XLEN-1:0] mem_fp_result, - input logic [4:0] mem_fp_fflags, - input logic [INST_WIDTH-1:0] mem_instr, - input logic [2:0] mem_instr_len, - - // Write back control - input logic mem_reg_write_en, - input logic mem_fp_reg_write_en, - input logic [4:0] mem_rd_addr, - input logic [1:0] mem_wb_sel, - - // CSR & System - input logic mem_is_csr, - input logic [2:0] mem_csr_op, - input logic [XLEN-1:0] mem_rs1_rdata, - input logic [4:0] mem_rs1_addr, - input logic [XLEN-1:0] mem_imm, - input logic mem_illegal_instr, - input logic mem_instr_access_fault, - input logic mem_instr_page_fault, - input logic mem_is_ecall, - input logic mem_is_ebreak, - input logic mem_is_mret, - input logic mem_is_sret, - input logic mem_load_fault, - input logic mem_store_fault, - input logic mem_load_page_fault, - input logic mem_store_page_fault, - input logic [XLEN-1:0] mem_fault_addr, - - // ----------------------- - // Outputs to WB Stage - // ----------------------- - // Data and address - output logic [XLEN-1:0] wb_pc, - output logic [XLEN-1:0] wb_alu_result, - output logic [XLEN-1:0] wb_mem_rdata, - output logic [XLEN-1:0] wb_fp_result, - output logic [4:0] wb_fp_fflags, - output logic [INST_WIDTH-1:0] wb_instr, - output logic [2:0] wb_instr_len, - - // Write back control - output logic wb_reg_write_en, - output logic wb_fp_reg_write_en, - output logic [4:0] wb_rd_addr, - output logic [1:0] wb_wb_sel, - - // CSR & System - output logic wb_is_csr, - output logic [2:0] wb_csr_op, - output logic [XLEN-1:0] wb_rs1_rdata, - output logic [4:0] wb_rs1_addr, - output logic [XLEN-1:0] wb_imm, - output logic wb_illegal_instr, - output logic wb_instr_access_fault, - output logic wb_instr_page_fault, - output logic wb_is_ecall, - output logic wb_is_ebreak, - output logic wb_is_mret, - output logic wb_is_sret, - output logic wb_load_fault, - output logic wb_store_fault, - output logic wb_load_page_fault, - output logic wb_store_page_fault, - output logic [XLEN-1:0] wb_fault_addr - -); - // Sequential Logic: State Update - always_ff @(posedge clk) begin - if (rst) begin - wb_pc <= '0; - wb_alu_result <= '0; - wb_mem_rdata <= '0; - wb_fp_result <= '0; - wb_fp_fflags <= '0; - wb_instr <= '0; - wb_instr_len <= 3'd4; - wb_reg_write_en <= '0; - wb_fp_reg_write_en <= '0; - wb_rd_addr <= '0; - wb_wb_sel <= '0; - wb_is_csr <= '0; - wb_csr_op <= '0; - wb_rs1_rdata <= '0; - wb_rs1_addr <= '0; - wb_imm <= '0; - wb_illegal_instr <= '0; - wb_instr_access_fault <= '0; - wb_instr_page_fault <= '0; - wb_is_ecall <= '0; - wb_is_ebreak <= '0; - wb_is_mret <= '0; - wb_is_sret <= '0; - wb_load_fault <= '0; - wb_store_fault <= '0; - wb_load_page_fault <= '0; - wb_store_page_fault <= '0; - wb_fault_addr <= '0; - end else if (flush_en) begin - // Target specific control signals to clear. Leave datapath elements intact - // to avoid extreme power consumption from massive flip-flop state changes array. - wb_reg_write_en <= '0; - wb_fp_reg_write_en <= '0; - wb_is_csr <= '0; - wb_illegal_instr <= '0; - wb_instr_access_fault <= '0; - wb_instr_page_fault <= '0; - wb_is_ecall <= '0; - wb_is_ebreak <= '0; - wb_is_mret <= '0; - wb_is_sret <= '0; - wb_load_fault <= '0; - wb_store_fault <= '0; - wb_load_page_fault <= '0; - wb_store_page_fault <= '0; - wb_fault_addr <= '0; - end else if (!stall_en) begin - wb_pc <= mem_pc; - wb_alu_result <= mem_alu_result; - wb_mem_rdata <= mem_mem_rdata; - wb_fp_result <= mem_fp_result; - wb_fp_fflags <= mem_fp_fflags; - wb_instr <= mem_instr; - wb_instr_len <= mem_instr_len; - wb_reg_write_en <= mem_reg_write_en; - wb_fp_reg_write_en <= mem_fp_reg_write_en; - wb_rd_addr <= mem_rd_addr; - wb_wb_sel <= mem_wb_sel; - wb_is_csr <= mem_is_csr; - wb_csr_op <= mem_csr_op; - wb_rs1_rdata <= mem_rs1_rdata; - wb_rs1_addr <= mem_rs1_addr; - wb_imm <= mem_imm; - wb_illegal_instr <= mem_illegal_instr; - wb_instr_access_fault <= mem_instr_access_fault; - wb_instr_page_fault <= mem_instr_page_fault; - wb_is_ecall <= mem_is_ecall; - wb_is_ebreak <= mem_is_ebreak; - wb_is_mret <= mem_is_mret; - wb_is_sret <= mem_is_sret; - wb_load_fault <= mem_load_fault; - wb_store_fault <= mem_store_fault; - wb_load_page_fault <= mem_load_page_fault; - wb_store_page_fault <= mem_store_page_fault; - wb_fault_addr <= mem_fault_addr; - end - end - -endmodule diff --git a/rv64cpu/rtl/core/mmu/.gitkeep b/rv64cpu/rtl/core/mmu/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/rtl/core/pipeline/ex_mem_reg.sv b/rv64cpu/rtl/core/pipeline/ex_mem_reg.sv new file mode 100644 index 0000000..b8e5b9e --- /dev/null +++ b/rv64cpu/rtl/core/pipeline/ex_mem_reg.sv @@ -0,0 +1,281 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/ex_mem_reg.sv +// Description: Execute to Memory Pipeline Register +// Latches results from the EX stage and bypassed control signals from ID/EX. +// Supports stall and flush operations. + +module ex_mem_reg + import rv64_pkg::*; +( + input logic clk, + input logic rst, + + // Pipeline Control + input logic stall_en, + input logic flush_en, + + // ---------------------- + // Inputs from EX Stage (computed results) + input logic [XLEN-1:0] ex_alu_result, + input logic ex_branch_taken, + input logic [XLEN-1:0] ex_branch_target, + + // ---------------------- + // Bypassed Inputs from ID/EX Register + + // Data/Operands + input logic [XLEN-1:0] ex_rs1_rdata, // Needed for CSRRW/RS/RC + input logic [XLEN-1:0] ex_rs2_rdata, // Needed for store data + input logic [XLEN-1:0] ex_fs1_rdata, + input logic [XLEN-1:0] ex_fs2_rdata, + input logic [XLEN-1:0] ex_fs3_rdata, + input logic [4:0] ex_rs1_addr, // Serves as zimm for CSRRWI/SI/CI + input logic [4:0] ex_fs3_addr, + input logic [XLEN-1:0] ex_pc, // PC passed along for exceptions/CSRs + input logic [XLEN-1:0] ex_imm, // Imm passed for CSR writes + input logic [INST_WIDTH-1:0] ex_instr, + input logic [2:0] ex_instr_len, + + // Memory Control + input logic ex_is_load, + input logic ex_is_store, + input logic [2:0] ex_mem_size, + input logic ex_is_amo, + input amo_op_t ex_amo_op, + input logic ex_is_fp, + input logic ex_fp_reg_write_en, + input logic ex_fp_store_data_sel, + input fp_op_t ex_fp_op, + input fp_fmt_t ex_fp_fmt, + input logic [2:0] ex_fp_rm, + input logic [XLEN-1:0] ex_fp_result, + input logic [4:0] ex_fp_fflags, + + // Writeback Control + input logic ex_reg_write_en, + input logic [4:0] ex_rd_addr, + input logic [1:0] ex_wb_sel, + + // System / Exception Control + input logic ex_is_csr, + input logic [2:0] ex_csr_op, + input logic ex_illegal_instr, + input logic ex_instr_access_fault, + input logic ex_instr_page_fault, + input logic ex_is_ecall, + input logic ex_is_ebreak, + input logic ex_is_mret, + input logic ex_is_sret, + input logic ex_is_wfi, + input logic ex_is_sfence_vma, + input logic ex_is_fence, + input logic ex_is_fence_i, + + + // ---------------------- + // Outputs to MEM Stage + + // Data/Operands + output logic [XLEN-1:0] mem_alu_result, + output logic mem_branch_taken, + output logic [XLEN-1:0] mem_branch_target, + output logic [XLEN-1:0] mem_rs1_rdata, + output logic [XLEN-1:0] mem_rs2_rdata, + output logic [XLEN-1:0] mem_fs1_rdata, + output logic [XLEN-1:0] mem_fs2_rdata, + output logic [XLEN-1:0] mem_fs3_rdata, + output logic [4:0] mem_rs1_addr, + output logic [4:0] mem_fs3_addr, + output logic [XLEN-1:0] mem_pc, + output logic [XLEN-1:0] mem_imm, + output logic [INST_WIDTH-1:0] mem_instr, + output logic [2:0] mem_instr_len, + + // Memory Control + output logic mem_is_load, + output logic mem_is_store, + output logic [2:0] mem_mem_size, + output logic mem_is_amo, + output amo_op_t mem_amo_op, + output logic mem_is_fp, + output logic mem_fp_reg_write_en, + output logic mem_fp_store_data_sel, + output fp_op_t mem_fp_op, + output fp_fmt_t mem_fp_fmt, + output logic [2:0] mem_fp_rm, + output logic [XLEN-1:0] mem_fp_result, + output logic [4:0] mem_fp_fflags, + + // Writeback Control + output logic mem_reg_write_en, + output logic [4:0] mem_rd_addr, + output logic [1:0] mem_wb_sel, + + // System / Exception Control + output logic mem_is_csr, + output logic [2:0] mem_csr_op, + output logic mem_illegal_instr, + output logic mem_instr_access_fault, + output logic mem_instr_page_fault, + output logic mem_is_ecall, + output logic mem_is_ebreak, + output logic mem_is_mret, + output logic mem_is_sret, + output logic mem_is_wfi, + output logic mem_is_sfence_vma, + output logic mem_is_fence, + output logic mem_is_fence_i +); + + always_ff @(posedge clk) begin + if (rst) begin + mem_instr <= '0; + mem_instr_len <= 3'd4; + mem_alu_result <= '0; + mem_branch_taken <= '0; + mem_branch_target <= '0; + mem_rs1_rdata <= '0; + mem_rs2_rdata <= '0; + mem_fs1_rdata <= '0; + mem_fs2_rdata <= '0; + mem_fs3_rdata <= '0; + mem_rs1_addr <= '0; + mem_fs3_addr <= '0; + mem_pc <= '0; + mem_imm <= '0; + + // Critical control signals must be zeroed out + mem_is_load <= '0; + mem_is_store <= '0; + mem_mem_size <= '0; + mem_is_amo <= '0; + mem_amo_op <= AMO_NONE; + mem_is_fp <= '0; + mem_fp_reg_write_en <= '0; + mem_fp_store_data_sel <= '0; + mem_fp_op <= FP_OP_NONE; + mem_fp_fmt <= FP_FMT_S; + mem_fp_rm <= '0; + mem_fp_result <= '0; + mem_fp_fflags <= '0; + mem_reg_write_en <= '0; + mem_rd_addr <= '0; + mem_wb_sel <= '0; + + // Exceptions and system control + mem_is_csr <= '0; + mem_csr_op <= '0; + mem_illegal_instr <= '0; + mem_instr_access_fault <= '0; + mem_instr_page_fault <= '0; + mem_is_ecall <= '0; + mem_is_ebreak <= '0; + mem_is_mret <= '0; + mem_is_sret <= '0; + mem_is_wfi <= '0; + mem_is_sfence_vma <= '0; + mem_is_fence <= '0; + mem_is_fence_i <= '0; + + end else if (flush_en) begin + mem_instr <= '0; + mem_instr_len <= 3'd4; + // Critical control signals must be zeroed out + mem_branch_taken <= '0; + mem_is_load <= '0; + mem_is_store <= '0; + mem_mem_size <= '0; + mem_is_amo <= '0; + mem_amo_op <= AMO_NONE; + mem_is_fp <= '0; + mem_fp_reg_write_en <= '0; + mem_fp_store_data_sel <= '0; + mem_fp_op <= FP_OP_NONE; + mem_fp_fmt <= FP_FMT_S; + mem_fp_rm <= '0; + mem_fp_result <= '0; + mem_fp_fflags <= '0; + mem_reg_write_en <= '0; + mem_rd_addr <= '0; + mem_wb_sel <= '0; + + // Exceptions and system control + mem_is_csr <= '0; + mem_csr_op <= '0; + mem_illegal_instr <= '0; + mem_instr_access_fault <= '0; + mem_instr_page_fault <= '0; + mem_is_ecall <= '0; + mem_is_ebreak <= '0; + mem_is_mret <= '0; + mem_is_sret <= '0; + mem_is_wfi <= '0; + mem_is_sfence_vma <= '0; + mem_is_fence <= '0; + mem_is_fence_i <= '0; + end else if (~stall_en) begin + mem_alu_result <= ex_alu_result; + mem_branch_taken <= ex_branch_taken; + mem_branch_target <= ex_branch_target; + mem_rs1_rdata <= ex_rs1_rdata; + mem_rs2_rdata <= ex_rs2_rdata; + mem_fs1_rdata <= ex_fs1_rdata; + mem_fs2_rdata <= ex_fs2_rdata; + mem_fs3_rdata <= ex_fs3_rdata; + mem_rs1_addr <= ex_rs1_addr; + mem_fs3_addr <= ex_fs3_addr; + mem_pc <= ex_pc; + mem_imm <= ex_imm; + mem_instr <= ex_instr; + mem_instr_len <= ex_instr_len; + + mem_is_load <= ex_is_load; + mem_is_store <= ex_is_store; + mem_mem_size <= ex_mem_size; + mem_is_amo <= ex_is_amo; + mem_amo_op <= ex_amo_op; + mem_is_fp <= ex_is_fp; + mem_fp_reg_write_en <= ex_fp_reg_write_en; + mem_fp_store_data_sel <= ex_fp_store_data_sel; + mem_fp_op <= ex_fp_op; + mem_fp_fmt <= ex_fp_fmt; + mem_fp_rm <= ex_fp_rm; + mem_fp_result <= ex_fp_result; + mem_fp_fflags <= ex_fp_fflags; + mem_reg_write_en <= ex_reg_write_en; + mem_rd_addr <= ex_rd_addr; + mem_wb_sel <= ex_wb_sel; + + mem_is_csr <= ex_is_csr; + mem_csr_op <= ex_csr_op; + mem_illegal_instr <= ex_illegal_instr; + mem_instr_access_fault <= ex_instr_access_fault; + mem_instr_page_fault <= ex_instr_page_fault; + mem_is_ecall <= ex_is_ecall; + mem_is_ebreak <= ex_is_ebreak; + mem_is_mret <= ex_is_mret; + mem_is_sret <= ex_is_sret; + mem_is_wfi <= ex_is_wfi; + mem_is_sfence_vma <= ex_is_sfence_vma; + mem_is_fence <= ex_is_fence; + mem_is_fence_i <= ex_is_fence_i; + end + // If stall_en && !flush_en, maintain current state + end + +endmodule diff --git a/rv64cpu/rtl/core/pipeline/ex_stage.sv b/rv64cpu/rtl/core/pipeline/ex_stage.sv new file mode 100644 index 0000000..287bfd7 --- /dev/null +++ b/rv64cpu/rtl/core/pipeline/ex_stage.sv @@ -0,0 +1,120 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/ex_stage.sv +// Description: Execute Stage +// Performs ALU operations, branch/jump calculations, and generates memory addresses. + +module ex_stage + import rv64_pkg::*; +( + // Input from ID/EX level pipeline register + input logic [XLEN-1:0] ex_pc, + input logic [XLEN-1:0] ex_rs1_rdata, + input logic [XLEN-1:0] ex_rs2_rdata, + input logic [XLEN-1:0] ex_imm, + + // Execution Control Signals + input alu_op_t ex_alu_op, + input logic ex_alu_src1_sel, // 0: rs1_rdata, 1: PC + input logic ex_alu_src2_sel, // 0: rs2_rdata, 1: imm + input logic ex_is_branch, + input branch_op_t ex_branch_op, + input logic ex_is_jump, + input logic ex_is_word_op, + + // Output to EX/MEM pipeline register & hazard unit + output logic [XLEN-1:0] ex_alu_result, + output logic ex_branch_taken, + output logic [XLEN-1:0] ex_branch_target +); + + logic [XLEN-1:0] alu_mux_a; + logic [XLEN-1:0] alu_mux_b; + logic [XLEN-1:0] alu_operand_a; + logic [XLEN-1:0] alu_operand_b; + logic [XLEN-1:0] alu_raw_result; + logic branch_unit_taken; + + // ------------- + // ALU Input Multiplexers (MUX) + assign alu_mux_a = (ex_alu_src1_sel == 1'b1) ? ex_pc : ex_rs1_rdata; + assign alu_mux_b = (ex_alu_src2_sel == 1'b1) ? ex_imm : ex_rs2_rdata; + + // ------------- + // RV64 Word Operation (32-bit) Pre-conditioning + // RV64 requires word operations (ADDW/SLLW/SRLW/SRAW) to operate on the + // lower 32 bits and sign-extend the result. + always_comb begin + if (ex_is_word_op) begin + // Shift operations require specific 32-bit extension forms + if (ex_alu_op == ALU_SRA) + alu_operand_a = { {32{alu_mux_a[31]}}, alu_mux_a[31:0] }; // Sign extend for arithmetic shift + else if ((ex_alu_op == ALU_SRL) || (ex_alu_op == ALU_DIVU) || (ex_alu_op == ALU_REMU)) + alu_operand_a = { 32'b0, alu_mux_a[31:0] }; // Zero extend for logical shift + else if ((ex_alu_op == ALU_DIV) || (ex_alu_op == ALU_REM)) + alu_operand_a = { {32{alu_mux_a[31]}}, alu_mux_a[31:0] }; + else + alu_operand_a = alu_mux_a; // ADDW/SUBW/SLLW don't care about upper bits + + // RV32 shift amount is strictly 5 bits (0-31), ignoring bit 5 which might be set in 64-bit registers + if (ex_alu_op == ALU_SLL || ex_alu_op == ALU_SRL || ex_alu_op == ALU_SRA) + alu_operand_b = { 59'b0, alu_mux_b[4:0] }; + else if ((ex_alu_op == ALU_DIVU) || (ex_alu_op == ALU_REMU)) + alu_operand_b = { 32'b0, alu_mux_b[31:0] }; + else if ((ex_alu_op == ALU_DIV) || (ex_alu_op == ALU_REM)) + alu_operand_b = { {32{alu_mux_b[31]}}, alu_mux_b[31:0] }; + else + alu_operand_b = alu_mux_b; + end else begin + // 64-bit operations pass through unchanged + alu_operand_a = alu_mux_a; + alu_operand_b = alu_mux_b; + end + end + + // -------------- + // Instantiate the core computing engine ALU + alu alu_inst ( + .alu_op (ex_alu_op), + .operand_a (alu_operand_a), + .operand_b (alu_operand_b), + .alu_result (alu_raw_result) + ); + + // ------------- + // Branch Unit + branch_unit bu_inst ( + .is_branch (ex_is_branch), + .branch_op (ex_branch_op), + .operand_a (ex_rs1_rdata), + .operand_b (ex_rs2_rdata), + .branch_taken (branch_unit_taken) + ); + + // ------------- + // 32-bit instruction result truncation and sign extension + assign ex_alu_result = ex_is_word_op ? + { {32{alu_raw_result[31]}}, alu_raw_result[31:0] } : + alu_raw_result; + + // ------------- + // Branch Target Calculation + assign ex_branch_target = alu_raw_result & ~64'b1; + + assign ex_branch_taken = ex_is_jump | (ex_is_branch & branch_unit_taken); + +endmodule diff --git a/rv64cpu/rtl/core/pipeline/id_ex_reg.sv b/rv64cpu/rtl/core/pipeline/id_ex_reg.sv new file mode 100644 index 0000000..777cc46 --- /dev/null +++ b/rv64cpu/rtl/core/pipeline/id_ex_reg.sv @@ -0,0 +1,287 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/id_ex_reg.sv +// Description: ID/EX Pipeline Register +// Latches control signals and data from ID stage and passes them to EX stage. +// Supports stall (freeze) and flush. + +module id_ex_reg + import rv64_pkg::*; +( + input logic clk, + input logic rst, + + // Pipeline Control + input logic stall_en, + input logic flush_en, + + + // Inputs from ID Stage + + // Instruction payload + input logic [INST_WIDTH-1:0] id_instr, + input logic [2:0] id_instr_len, + + // Data/Operands + input logic [XLEN-1:0] id_pc, + input logic [XLEN-1:0] id_rs1_rdata, + input logic [XLEN-1:0] id_rs2_rdata, + input logic [XLEN-1:0] id_fs1_rdata, + input logic [XLEN-1:0] id_fs2_rdata, + input logic [XLEN-1:0] id_fs3_rdata, + input logic [4:0] id_rs1_addr, + input logic [4:0] id_rs2_addr, + input logic [4:0] id_fs3_addr, + input logic [XLEN-1:0] id_imm, + + // Execution Control + input alu_op_t id_alu_op, + input logic id_alu_src1_sel, + input logic id_alu_src2_sel, + input logic id_is_word_op, + input logic id_is_branch, + input branch_op_t id_branch_op, + input logic id_is_jump, + + // Memory & Writeback Control + input logic id_is_load, + input logic id_is_store, + input logic [2:0] id_mem_size, + input logic id_is_amo, + input amo_op_t id_amo_op, + input logic id_is_fp, + input logic id_fp_reg_write_en, + input logic id_fp_store_data_sel, + input fp_op_t id_fp_op, + input fp_fmt_t id_fp_fmt, + input logic [2:0] id_fp_rm, + input logic id_reg_write_en, + input logic [4:0] id_rd_addr, + input logic [1:0] id_wb_sel, + + // System / Exception + input logic id_is_csr, + input logic [2:0] id_csr_op, + input logic id_illegal_instr, + input logic id_instr_access_fault, + input logic id_instr_page_fault, + input logic id_is_ecall, + input logic id_is_ebreak, + input logic id_is_mret, + input logic id_is_sret, + input logic id_is_wfi, + input logic id_is_sfence_vma, + input logic id_is_fence, + input logic id_is_fence_i, + + + // Outputs to EX Stage + + // Instruction payload + output logic [INST_WIDTH-1:0] ex_instr, + output logic [2:0] ex_instr_len, + + // Data/Operands + output logic [XLEN-1:0] ex_pc, + output logic [XLEN-1:0] ex_rs1_rdata, + output logic [XLEN-1:0] ex_rs2_rdata, + output logic [XLEN-1:0] ex_fs1_rdata, + output logic [XLEN-1:0] ex_fs2_rdata, + output logic [XLEN-1:0] ex_fs3_rdata, + output logic [4:0] ex_rs1_addr, + output logic [4:0] ex_rs2_addr, + output logic [4:0] ex_fs3_addr, + output logic [XLEN-1:0] ex_imm, + + // Execution Control + output alu_op_t ex_alu_op, + output logic ex_alu_src1_sel, + output logic ex_alu_src2_sel, + output logic ex_is_word_op, + output logic ex_is_branch, + output branch_op_t ex_branch_op, + output logic ex_is_jump, + + // Memory & Writeback Control + output logic ex_is_load, + output logic ex_is_store, + output logic [2:0] ex_mem_size, + output logic ex_is_amo, + output amo_op_t ex_amo_op, + output logic ex_is_fp, + output logic ex_fp_reg_write_en, + output logic ex_fp_store_data_sel, + output fp_op_t ex_fp_op, + output fp_fmt_t ex_fp_fmt, + output logic [2:0] ex_fp_rm, + output logic ex_reg_write_en, + output logic [4:0] ex_rd_addr, + output logic [1:0] ex_wb_sel, + + // System / Exception + output logic ex_is_csr, + output logic [2:0] ex_csr_op, + output logic ex_illegal_instr, + output logic ex_instr_access_fault, + output logic ex_instr_page_fault, + output logic ex_is_ecall, + output logic ex_is_ebreak, + output logic ex_is_mret, + output logic ex_is_sret, + output logic ex_is_wfi, + output logic ex_is_sfence_vma, + output logic ex_is_fence, + output logic ex_is_fence_i +); + + always_ff @(posedge clk) begin + if (rst) begin + ex_instr <= '0; + ex_instr_len <= 3'd4; + ex_pc <= '0; + ex_rs1_rdata <= '0; + ex_rs2_rdata <= '0; + ex_fs1_rdata <= '0; + ex_fs2_rdata <= '0; + ex_fs3_rdata <= '0; + ex_rs1_addr <= '0; + ex_rs2_addr <= '0; + ex_fs3_addr <= '0; + ex_imm <= '0; + ex_alu_op <= ALU_ADD; // NOP is effectively ADD x0, x0, 0 + ex_alu_src1_sel <= '0; + ex_alu_src2_sel <= '0; + ex_is_word_op <= '0; + ex_is_branch <= '0; + ex_branch_op <= BRANCH_EQ; + ex_is_jump <= '0; + ex_is_load <= '0; + ex_is_store <= '0; + ex_mem_size <= '0; + ex_is_amo <= '0; + ex_amo_op <= AMO_NONE; + ex_is_fp <= '0; + ex_fp_reg_write_en <= '0; + ex_fp_store_data_sel <= '0; + ex_fp_op <= FP_OP_NONE; + ex_fp_fmt <= FP_FMT_S; + ex_fp_rm <= '0; + ex_reg_write_en <= '0; // VERY IMPORTANT: Do not write to regfile on NOP + ex_rd_addr <= '0; + ex_wb_sel <= '0; + ex_is_csr <= '0; + ex_csr_op <= '0; + ex_illegal_instr <= '0; + ex_instr_access_fault <= '0; + ex_instr_page_fault <= '0; + ex_is_ecall <= '0; + ex_is_ebreak <= '0; + ex_is_mret <= '0; + ex_is_sret <= '0; + ex_is_wfi <= '0; + ex_is_sfence_vma <= '0; + ex_is_fence <= '0; + ex_is_fence_i <= '0; + end else if (flush_en) begin + ex_instr <= '0; + ex_instr_len <= 3'd4; + ex_alu_op <= ALU_ADD; // NOP is effectively ADD x0, x0, 0 + ex_alu_src1_sel <= '0; + ex_alu_src2_sel <= '0; + ex_is_word_op <= '0; + ex_is_branch <= '0; + ex_branch_op <= BRANCH_EQ; + ex_is_jump <= '0; + ex_is_load <= '0; + ex_is_store <= '0; + ex_mem_size <= '0; + ex_is_amo <= '0; + ex_amo_op <= AMO_NONE; + ex_is_fp <= '0; + ex_fp_reg_write_en <= '0; + ex_fp_store_data_sel <= '0; + ex_fp_op <= FP_OP_NONE; + ex_fp_fmt <= FP_FMT_S; + ex_fp_rm <= '0; + ex_reg_write_en <= '0; // VERY IMPORTANT: Do not write to regfile on NOP + ex_rd_addr <= '0; + ex_wb_sel <= '0; + ex_is_csr <= '0; + ex_csr_op <= '0; + ex_illegal_instr <= '0; + ex_instr_access_fault <= '0; + ex_instr_page_fault <= '0; + ex_is_ecall <= '0; + ex_is_ebreak <= '0; + ex_is_mret <= '0; + ex_is_sret <= '0; + ex_is_wfi <= '0; + ex_is_sfence_vma <= '0; + ex_is_fence <= '0; + ex_is_fence_i <= '0; + end else if (~stall_en) begin + ex_instr <= id_instr; + ex_instr_len <= id_instr_len; + ex_pc <= id_pc; + ex_rs1_rdata <= id_rs1_rdata; + ex_rs2_rdata <= id_rs2_rdata; + ex_fs1_rdata <= id_fs1_rdata; + ex_fs2_rdata <= id_fs2_rdata; + ex_fs3_rdata <= id_fs3_rdata; + ex_rs1_addr <= id_rs1_addr; + ex_rs2_addr <= id_rs2_addr; + ex_fs3_addr <= id_fs3_addr; + ex_imm <= id_imm; + ex_alu_op <= id_alu_op; + ex_alu_src1_sel <= id_alu_src1_sel; + ex_alu_src2_sel <= id_alu_src2_sel; + ex_is_word_op <= id_is_word_op; + ex_is_branch <= id_is_branch; + ex_branch_op <= id_branch_op; + ex_is_jump <= id_is_jump; + ex_is_load <= id_is_load; + ex_is_store <= id_is_store; + ex_mem_size <= id_mem_size; + ex_is_amo <= id_is_amo; + ex_amo_op <= id_amo_op; + ex_is_fp <= id_is_fp; + ex_fp_reg_write_en <= id_fp_reg_write_en; + ex_fp_store_data_sel <= id_fp_store_data_sel; + ex_fp_op <= id_fp_op; + ex_fp_fmt <= id_fp_fmt; + ex_fp_rm <= id_fp_rm; + ex_reg_write_en <= id_reg_write_en; + ex_rd_addr <= id_rd_addr; + ex_wb_sel <= id_wb_sel; + ex_is_csr <= id_is_csr; + ex_csr_op <= id_csr_op; + ex_illegal_instr <= id_illegal_instr; + ex_instr_access_fault <= id_instr_access_fault; + ex_instr_page_fault <= id_instr_page_fault; + ex_is_ecall <= id_is_ecall; + ex_is_ebreak <= id_is_ebreak; + ex_is_mret <= id_is_mret; + ex_is_sret <= id_is_sret; + ex_is_wfi <= id_is_wfi; + ex_is_sfence_vma <= id_is_sfence_vma; + ex_is_fence <= id_is_fence; + ex_is_fence_i <= id_is_fence_i; + end + // If stall_en && !flush_en, maintain current state + end + +endmodule diff --git a/rv64cpu/rtl/core/pipeline/id_stage.sv b/rv64cpu/rtl/core/pipeline/id_stage.sv new file mode 100644 index 0000000..9003b42 --- /dev/null +++ b/rv64cpu/rtl/core/pipeline/id_stage.sv @@ -0,0 +1,197 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/id_stage.sv +// Description: Instruction Decode Stage, instantiate the Decoder and RegFile, extract control signals and operands + +module id_stage + import rv64_pkg::*; +( + input logic clk, + input logic rst, + + // Inputs from IF Stage (via IF/ID Pipeline Register) + input logic [XLEN-1:0] if_pc, // PC of the current instruction + input logic [INST_WIDTH-1:0] if_instr, // The 32-bit instruction fetched + input logic [2:0] if_instr_len, + input logic [1:0] csr_priv_mode, // Current privilege mode from CSR + input logic csr_mstatus_tsr, + input logic csr_mstatus_tw, + input logic csr_mstatus_tvm, + + // Write-back Inputs from WB Stage (to RegFile) + input logic wb_reg_write_en, // Register write enable from WB stage + input logic [4:0] wb_rd_addr, // Destination register address from WB + input logic [XLEN-1:0] wb_rd_wdata, // Data to write back to register + input logic wb_fp_write_en, + input logic [4:0] wb_fd_addr, + input logic [XLEN-1:0] wb_fd_wdata, + + // Outputs to EX Stage + // (Notice the 'id_' prefix to signify signals leaving the ID stage) + // Data/Operands + output logic [XLEN-1:0] id_pc, // Pass-through PC for branch/jump calculation + output logic [2:0] id_instr_len, + output logic [XLEN-1:0] id_rs1_rdata, // Source register 1 data + output logic [XLEN-1:0] id_rs2_rdata, // Source register 2 data + output logic [XLEN-1:0] id_fs1_rdata, + output logic [XLEN-1:0] id_fs2_rdata, + output logic [XLEN-1:0] id_fs3_rdata, + output logic [4:0] id_rs1_addr, // Rs1 index (for Forwarding & CSR zimm) + output logic [4:0] id_rs2_addr, // Rs2 index (for Forwarding) + output logic [4:0] id_fs3_addr, + output logic [XLEN-1:0] id_imm, // Sign-extended generic immediate + + // Execution Control (ALU & Branch) + output alu_op_t id_alu_op, // ALU operation code + output logic id_alu_src1_sel,// 0: rs1_rdata, 1: PC + output logic id_alu_src2_sel,// 0: rs2_rdata, 1: imm + output logic id_is_word_op, // 1: RV64 32-bit operation (e.g. ADDW) + output logic id_is_branch, // 1: B-type branch instruction + output branch_op_t id_branch_op, // Specific condition for branch (BEQ, BNE...) + output logic id_is_jump, // 1: JAL or JALR + + // Memory & Writeback Control (Passed down to MEM/WB stages eventually) + output logic id_is_load, // 1: Load instruction + output logic id_is_store, // 1: Store instruction + output logic [2:0] id_mem_size, // Memory access size (Byte/Half/Word/Double) + output logic id_is_amo, + output amo_op_t id_amo_op, + output logic id_is_fp, + output logic id_fp_reg_write_en, + output logic id_fp_store_data_sel, + output fp_op_t id_fp_op, + output fp_fmt_t id_fp_fmt, + output logic [2:0] id_fp_rm, + output logic id_reg_write_en,// 1: Instruction will write to a register + output logic [4:0] id_rd_addr, // Destination register index + output logic [1:0] id_wb_sel, // Writeback source (0: ALU, 1: MEM, 2: PC+4) + + // System / Exception + output logic id_is_csr, + output logic [2:0] id_csr_op, + output logic id_illegal_instr, + output logic id_is_ecall, + output logic id_is_ebreak, + output logic id_is_mret, + output logic id_is_sret, + output logic id_is_wfi, + output logic id_is_sfence_vma, + output logic id_is_fence, + output logic id_is_fence_i +); + // Internal Wires for connecting sub-modules + logic [4:0] rs1_addr; + logic [4:0] rs2_addr; + logic [4:0] fs3_addr; + + // Pass-through PC and reg indices directly to next stage + assign id_pc = if_pc; + assign id_instr_len = if_instr_len; + assign id_rs1_addr = rs1_addr; + assign id_rs2_addr = rs2_addr; + assign fs3_addr = if_instr[31:27]; + assign id_fs3_addr = fs3_addr; + + // Instruction Decoder Instantiation + + decoder u_decoder ( + .instr (if_instr), + .priv_mode (csr_priv_mode), + .mstatus_tsr (csr_mstatus_tsr), + .mstatus_tw (csr_mstatus_tw), + .mstatus_tvm (csr_mstatus_tvm), + + // Outputs to RegFile read ports + .rs1_addr (rs1_addr), + .rs2_addr (rs2_addr), + .rd_addr (id_rd_addr), + + // Outputs to EX stage (ALU & execution control) + .alu_op (id_alu_op), + .alu_src1_sel (id_alu_src1_sel), + .alu_src2_sel (id_alu_src2_sel), + .is_word_op (id_is_word_op), + .imm (id_imm), + + // Branch & Jump control + .is_branch (id_is_branch), + .branch_op (id_branch_op), + .is_jump (id_is_jump), + + // Memory control + .is_load (id_is_load), + .is_store (id_is_store), + .mem_size (id_mem_size), + .is_amo (id_is_amo), + .amo_op (id_amo_op), + .is_fp (id_is_fp), + .fp_reg_write_en(id_fp_reg_write_en), + .fp_store_data_sel(id_fp_store_data_sel), + .fp_op (id_fp_op), + .fp_fmt (id_fp_fmt), + .fp_rm (id_fp_rm), + + // Writeback control + .reg_write_en (id_reg_write_en), + .wb_sel (id_wb_sel), + + // System Control + .is_csr (id_is_csr), + .csr_op (id_csr_op), + .is_ecall (id_is_ecall), + .is_ebreak (id_is_ebreak), + .is_mret (id_is_mret), + .is_sret (id_is_sret), + .is_wfi (id_is_wfi), + .is_sfence_vma (id_is_sfence_vma), + .is_fence (id_is_fence), + .is_fence_i (id_is_fence_i), + .illegal_instr (id_illegal_instr) + ); + + // Register File Instantiation + regfile u_regfile ( + .clk (clk), + .rst (rst), + + // Read ports (from Decoder) + .rs1_addr (rs1_addr), + .rs2_addr (rs2_addr), + .rs1_rdata (id_rs1_rdata), + .rs2_rdata (id_rs2_rdata), + + // Write port (from WB stage) + .reg_write_en (wb_reg_write_en), + .rd_addr (wb_rd_addr), + .rd_wdata (wb_rd_wdata) + ); + + fp_regfile u_fp_regfile ( + .clk (clk), + .rst (rst), + .fs1_addr (rs1_addr), + .fs2_addr (rs2_addr), + .fs3_addr (fs3_addr), + .fs1_rdata (id_fs1_rdata), + .fs2_rdata (id_fs2_rdata), + .fs3_rdata (id_fs3_rdata), + .fp_write_en (wb_fp_write_en), + .fd_addr (wb_fd_addr), + .fd_wdata (wb_fd_wdata) + ); + +endmodule diff --git a/rv64cpu/rtl/core/pipeline/if_id_reg.sv b/rv64cpu/rtl/core/pipeline/if_id_reg.sv new file mode 100644 index 0000000..0ea4fae --- /dev/null +++ b/rv64cpu/rtl/core/pipeline/if_id_reg.sv @@ -0,0 +1,72 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/if_id_reg.sv +// Description: IF/ID Pipeline Register +// Latches PC and instruction from IF stage and passes them to ID stage. +// Supports stall (freeze) and flush (insert NOP). + +module if_id_reg + import rv64_pkg::*; +( + input logic clk, + input logic rst, + + // Pipeline Control from Hazard Unit + input logic stall_en, // 1: Freeze pipeline register state + input logic flush_en, // 1: Clear register (insert NOP) + + // Inputs from IF Stage + input logic [XLEN-1:0] if_pc, // PC from IF stage + input logic [INST_WIDTH-1:0] if_instr, // Fetched instruction from IF stage + input logic [2:0] if_instr_len, + input logic if_instr_access_fault, + input logic if_instr_page_fault, + + // Outputs to ID Stage + output logic [XLEN-1:0] id_pc, // Passed to ID stage + output logic [INST_WIDTH-1:0] id_instr, // Passed to ID stage + output logic [2:0] id_instr_len, + output logic id_instr_access_fault, + output logic id_instr_page_fault +); + + localparam logic [31:0] NOP_INSTR = 32'h0000_0013; + + always_ff @(posedge clk) begin + if (rst) begin + id_pc <= '0; + id_instr <= NOP_INSTR; + id_instr_len <= 3'd4; + id_instr_access_fault <= 1'b0; + id_instr_page_fault <= 1'b0; + end else if (flush_en) begin + id_pc <= '0; + id_instr <= NOP_INSTR; + id_instr_len <= 3'd4; + id_instr_access_fault <= 1'b0; + id_instr_page_fault <= 1'b0; + end else if (~stall_en) begin + id_pc <= if_pc; + id_instr <= if_instr; + id_instr_len <= if_instr_len; + id_instr_access_fault <= if_instr_access_fault; + id_instr_page_fault <= if_instr_page_fault; + end + // If stall_en && !flush_en, maintain current state + end + +endmodule diff --git a/rv64cpu/rtl/core/pipeline/if_stage.sv b/rv64cpu/rtl/core/pipeline/if_stage.sv new file mode 100644 index 0000000..c3d417b --- /dev/null +++ b/rv64cpu/rtl/core/pipeline/if_stage.sv @@ -0,0 +1,64 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/if_stage.sv +// Description: Instruction Fetching Stage, includes PC registers and next-state PC calculation logic + +module if_stage + import rv64_pkg::*; +( + input logic clk, + input logic rst, + + // Pipeline Control Signals (from Hazard Unit & Trap Controller) + input logic stall_en, // Freeze PC (stall), 0: Normal operation + input logic pc_redirect_sel, // Branch/Trap redirect request + input logic [XLEN-1:0] pc_redirect_target,// Redirect target address + input logic pc_redirect_is_trap, // 1: Redirect request comes from trap controller + input logic if_instr_compressed, + + output logic [XLEN-1:0] if_pc +); + + // PC Register + logic [XLEN-1:0] pc_reg; + + // Combinational logic signal: the PC value to be written in the next clock cycle + logic [XLEN-1:0] next_pc; + + always_comb begin + next_pc = pc_reg + (if_instr_compressed ? 64'd2 : 64'd4); + end + + // Sequential Logic: Update PC Register + always_ff @(posedge clk) begin + if (rst) begin + pc_reg <= 64'h0000_0000_8000_0000; + end else if (pc_redirect_sel && pc_redirect_is_trap) begin + pc_reg <= pc_redirect_target; + end else if (~stall_en) begin + if (pc_redirect_sel) begin + pc_reg <= pc_redirect_target; + end else begin + pc_reg <= next_pc; + end + end + end + + // Output wiring + assign if_pc = pc_reg; + +endmodule diff --git a/rv64cpu/rtl/core/pipeline/mem_stage.sv b/rv64cpu/rtl/core/pipeline/mem_stage.sv new file mode 100644 index 0000000..d5a97dc --- /dev/null +++ b/rv64cpu/rtl/core/pipeline/mem_stage.sv @@ -0,0 +1,430 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/mem_stage.sv +// Description: Memory Stage +// Latches results from the EX stage and bypassed control signals from ID/EX. +// Supports stall and flush operations. + +module mem_stage + import rv64_pkg::*; +( + input logic clk, + input logic rst, + + // Inputs from EX/MEM pipeline register + input logic [XLEN-1:0] mem_alu_result, // Used as memory address or ALU result + input logic mem_branch_taken, + input logic [XLEN-1:0] mem_branch_target, + input logic [XLEN-1:0] mem_rs1_rdata, + input logic [XLEN-1:0] mem_rs2_rdata, // Store data + input logic [XLEN-1:0] mem_fs2_rdata, + input logic [4:0] mem_rs1_addr, + input logic [XLEN-1:0] mem_pc, + input logic [XLEN-1:0] mem_imm, + input logic [INST_WIDTH-1:0] mem_instr, + input logic [2:0] mem_instr_len, + + // Memory Control + input logic mem_is_load, + input logic mem_is_store, + input logic [2:0] mem_mem_size, // LB/LH/LW/LD/LBU/LHU/LWU + input logic mem_is_amo, + input amo_op_t mem_amo_op, + input logic mem_is_fp, + input logic mem_fp_reg_write_en, + input logic mem_fp_store_data_sel, + input logic [XLEN-1:0] mem_fp_result, + input logic [4:0] mem_fp_fflags, + + // Writeback Control + input logic mem_reg_write_en, + input logic [4:0] mem_rd_addr, + input logic [1:0] mem_wb_sel, + + // Exceptions and System (bypassed through or generated) + input logic mem_is_csr, + input logic [2:0] mem_csr_op, + input logic mem_illegal_instr, + input logic mem_instr_access_fault, + input logic mem_instr_page_fault, + input logic mem_is_ecall, + input logic mem_is_ebreak, + input logic mem_is_mret, + input logic mem_is_sret, + input logic mem_is_wfi, + input logic mem_is_sfence_vma, + input logic mem_is_fence, + input logic mem_is_fence_i, + + // Data Memory Interface (to Data Cache / Memory Controller) + output logic dmem_read_req, + output logic dmem_write_req, + output logic [XLEN-1:0] dmem_addr, + output logic [XLEN-1:0] dmem_wdata, + output logic [7:0] dmem_wstrb, // Write strobe depending on size + input logic dmem_req_ready, // Wait signal from D-Cache + input logic dmem_rsp_valid, // Data valid from D-Cache + input logic [XLEN-1:0] dmem_rdata, + input logic dmem_load_access_fault, + input logic dmem_store_access_fault, + input logic dmem_load_page_fault, + input logic dmem_store_page_fault, + + // Outputs to MEM/WB Pipeline Register + output logic [XLEN-1:0] mem_wb_alu_result, + output logic [XLEN-1:0] mem_wb_mem_rdata, + output logic [XLEN-1:0] mem_wb_fp_result, + output logic [4:0] mem_wb_fp_fflags, + output logic [XLEN-1:0] mem_wb_pc, + output logic [INST_WIDTH-1:0] mem_wb_instr, + output logic [2:0] mem_wb_instr_len, + + output logic mem_wb_reg_write_en, + output logic mem_wb_fp_reg_write_en, + output logic [4:0] mem_wb_rd_addr, + output logic [1:0] mem_wb_wb_sel, + + output logic mem_wb_is_csr, + output logic [2:0] mem_wb_csr_op, + output logic [XLEN-1:0] mem_wb_rs1_rdata, + output logic [4:0] mem_wb_rs1_addr, + output logic [XLEN-1:0] mem_wb_imm, + + output logic mem_wb_illegal_instr, + output logic mem_wb_instr_access_fault, + output logic mem_wb_instr_page_fault, + output logic mem_wb_is_ecall, + output logic mem_wb_is_ebreak, + output logic mem_wb_is_mret, + output logic mem_wb_is_sret, + output logic mem_wb_load_fault, + output logic mem_wb_store_fault, + output logic mem_wb_load_page_fault, + output logic mem_wb_store_page_fault, + output logic [XLEN-1:0] mem_wb_fault_addr, + + // Control to Hazard Unit / Stall Logic + output logic mem_stall_req // Stall pipeline if D-Cache doesn't respond +); + + logic [XLEN-1:0] load_data_aligned; + logic [XLEN-1:0] store_data_aligned; + logic [XLEN-1:0] amo_write_data; + logic [XLEN-1:0] amo_wb_data; + logic [XLEN-1:0] selected_store_data; + logic [XLEN-1:0] fp_load_data; + logic [7:0] write_strobe; + logic load_addr_misaligned; + logic store_addr_misaligned; + logic amo_is_lr; + logic amo_is_sc; + logic amo_is_word; + logic amo_is_double; + logic amo_reads_memory; + logic amo_writes_memory; + logic amo_store_access; + logic amo_load_access; + logic amo_addr_misaligned; + logic amo_sc_success; + logic amo_rmw_op; + logic amo_rmw_read_phase; + logic amo_rmw_write_phase; + logic [XLEN-1:0] amo_memory_rdata; + logic amo_rmw_busy_q; + logic [XLEN-1:0] amo_rmw_old_data_q; + logic reservation_valid_q; + logic [XLEN-1:0] reservation_addr_q; + + function automatic logic addr_misaligned( + input logic [2:0] size, + input logic [XLEN-1:0] addr + ); + logic result; + case (size) + 3'b000, 3'b100: result = 1'b0; // Byte L/S + 3'b001, 3'b101: result = addr[0]; // Halfword requires bit0 = 0 + 3'b010, 3'b110: result = |addr[1:0]; // Word requires bits[1:0] = 0 + 3'b011: result = |addr[2:0]; // Doubleword requires bits[2:0] = 0 + default: result = 1'b0; + endcase + return result; + endfunction + + assign amo_is_lr = mem_is_amo && (mem_amo_op == AMO_LR); + assign amo_is_sc = mem_is_amo && (mem_amo_op == AMO_SC); + assign amo_is_word = mem_is_amo && (mem_mem_size == 3'b010); + assign amo_is_double = mem_is_amo && (mem_mem_size == 3'b011); + assign amo_rmw_op = mem_is_amo && !amo_is_lr && !amo_is_sc; + assign amo_rmw_read_phase = amo_rmw_op && !amo_rmw_busy_q; + assign amo_rmw_write_phase = amo_rmw_op && amo_rmw_busy_q; + assign amo_reads_memory = amo_is_lr || amo_rmw_read_phase; + assign amo_store_access = mem_is_amo && !amo_is_lr; + assign amo_load_access = mem_is_amo && amo_is_lr; + assign amo_addr_misaligned = mem_is_amo && addr_misaligned(mem_mem_size, mem_alu_result); + assign amo_sc_success = amo_is_sc && reservation_valid_q && + (reservation_addr_q == mem_alu_result) && + !amo_addr_misaligned && !dmem_store_access_fault && + !dmem_store_page_fault && !mem_instr_access_fault && + !mem_instr_page_fault; + assign amo_writes_memory = (amo_is_sc && amo_sc_success) || amo_rmw_write_phase; + assign amo_memory_rdata = amo_rmw_busy_q ? amo_rmw_old_data_q : dmem_rdata; + + assign load_addr_misaligned = mem_is_load && addr_misaligned(mem_mem_size, mem_alu_result); + assign store_addr_misaligned = (mem_is_store && addr_misaligned(mem_mem_size, mem_alu_result)) | + (amo_store_access && amo_addr_misaligned); + + always_ff @(posedge clk) begin + if (rst) begin + amo_rmw_busy_q <= 1'b0; + amo_rmw_old_data_q <= '0; + reservation_valid_q <= 1'b0; + reservation_addr_q <= '0; + end else begin + if (amo_rmw_read_phase && dmem_read_req && dmem_rsp_valid && !dmem_load_access_fault && + !dmem_load_page_fault && !mem_instr_access_fault && !mem_instr_page_fault) begin + amo_rmw_busy_q <= 1'b1; + amo_rmw_old_data_q <= dmem_rdata; + end else if (amo_rmw_write_phase && (!dmem_write_req || dmem_req_ready)) begin + amo_rmw_busy_q <= 1'b0; + amo_rmw_old_data_q <= '0; + end else if (!mem_is_amo) begin + amo_rmw_busy_q <= 1'b0; + amo_rmw_old_data_q <= '0; + end + + if (amo_is_lr && dmem_read_req && dmem_rsp_valid && !dmem_load_access_fault && + !dmem_load_page_fault) begin + reservation_valid_q <= 1'b1; + reservation_addr_q <= mem_alu_result; + end else if (amo_is_sc && dmem_req_ready) begin + reservation_valid_q <= 1'b0; + reservation_addr_q <= '0; + end + end + end + + // -------------------------- + // Memory Request Logic + assign dmem_read_req = (mem_is_load | amo_reads_memory) & + ~(load_addr_misaligned | (amo_reads_memory & amo_addr_misaligned)) & + ~dmem_load_access_fault & ~dmem_load_page_fault & + ~mem_instr_access_fault & ~mem_instr_page_fault; + assign dmem_write_req = (mem_is_store | amo_writes_memory) & ~store_addr_misaligned & ~dmem_store_access_fault & + ~dmem_store_page_fault & ~mem_instr_access_fault & ~mem_instr_page_fault; + assign dmem_addr = mem_alu_result; // ALU result is the computed address + + // -------------------------- + // Stall generation: Stall if memory request is not yet ready or valid + // For a simple synchronous SRAM, this might be 0, but for cache it will depend on hits/misses + // -------------------------- + always_comb begin + if (amo_rmw_read_phase && dmem_read_req) begin + mem_stall_req = 1'b1; + end else if ((dmem_read_req || dmem_write_req) && (!dmem_req_ready || (dmem_read_req && !dmem_rsp_valid))) begin + mem_stall_req = 1'b1; + end else begin + mem_stall_req = 1'b0; + end + end + + // -------------------------- + // Store Data Alignment and Strobe Generation + // -------------------------- + always_comb begin + logic [2:0] align_offset; + store_data_aligned = '0; + write_strobe = 8'b0000_0000; + selected_store_data = mem_fp_store_data_sel ? mem_fs2_rdata : mem_rs2_rdata; + + // Mem address lower 3 bits determine the alignment within the 64-bit word + align_offset = mem_alu_result[2:0]; + + if (mem_is_store || amo_writes_memory) begin + case (mem_mem_size) + // SB: Store Byte + 3'b000: begin + store_data_aligned = {56'b0, selected_store_data[7:0]} << (align_offset * 8); + write_strobe = 8'b0000_0001 << align_offset; + end + // SH: Store Halfword + 3'b001: begin + store_data_aligned = {48'b0, selected_store_data[15:0]} << (align_offset * 8); + write_strobe = 8'b0000_0011 << align_offset; + end + // SW: Store Word + 3'b010: begin + store_data_aligned = {32'b0, amo_writes_memory ? amo_write_data[31:0] : + selected_store_data[31:0]} << (align_offset * 8); + write_strobe = 8'b0000_1111 << align_offset; + end + // SD: Store Doubleword + 3'b011: begin + store_data_aligned = amo_writes_memory ? amo_write_data : selected_store_data; + write_strobe = 8'b1111_1111; + end + default: begin + store_data_aligned = '0; + write_strobe = 8'b0000_0000; + end + endcase + end + end + + assign dmem_wdata = store_data_aligned; + assign dmem_wstrb = write_strobe; + + // -------------------------- + // Load Data Alignment and Sign/Zero Extension + // -------------------------- + always_comb begin + logic [2:0] align_offset; + logic [XLEN-1:0] shifted_rdata; + load_data_aligned = '0; + + align_offset = mem_alu_result[2:0]; + + // Shift data down so the target bytes are at the bottom + shifted_rdata = dmem_rdata >> (align_offset * 8); + + case (mem_mem_size) + // LB: Load Byte (Sign extend) + 3'b000: load_data_aligned = { {56{shifted_rdata[7]}}, shifted_rdata[7:0] }; + // LH: Load Halfword (Sign extend) + 3'b001: load_data_aligned = { {48{shifted_rdata[15]}}, shifted_rdata[15:0] }; + // LW: Load Word (Sign extend) + 3'b010: load_data_aligned = { {32{shifted_rdata[31]}}, shifted_rdata[31:0] }; + // LD: Load Doubleword + 3'b011: load_data_aligned = shifted_rdata; + // LBU: Load Byte Unsigned (Zero extend) + 3'b100: load_data_aligned = { 56'b0, shifted_rdata[7:0] }; + // LHU: Load Half Unsigned (Zero extend) + 3'b101: load_data_aligned = { 48'b0, shifted_rdata[15:0] }; + // LWU: Load Word Unsigned (Zero extend) + 3'b110: load_data_aligned = { 32'b0, shifted_rdata[31:0] }; + default: load_data_aligned = '0; + endcase + end + + always_comb begin + fp_load_data = load_data_aligned; + if (mem_is_fp && (mem_mem_size == 3'b010)) begin + fp_load_data = {32'hFFFF_FFFF, load_data_aligned[31:0]}; + end + end + + always_comb begin + logic [2:0] align_offset; + logic [XLEN-1:0] shifted_rdata; + logic [31:0] amo_old_word; + logic [31:0] amo_rs2_word; + logic [31:0] amo_result_word; + logic signed [31:0] amo_old_word_signed; + logic signed [31:0] amo_rs2_word_signed; + logic signed [XLEN-1:0] amo_old_double_signed; + logic signed [XLEN-1:0] amo_rs2_double_signed; + + align_offset = mem_alu_result[2:0]; + shifted_rdata = amo_memory_rdata >> (align_offset * 8); + amo_old_word = shifted_rdata[31:0]; + amo_rs2_word = mem_rs2_rdata[31:0]; + amo_old_word_signed = amo_old_word; + amo_rs2_word_signed = amo_rs2_word; + amo_old_double_signed = amo_memory_rdata; + amo_rs2_double_signed = mem_rs2_rdata; + amo_result_word = amo_old_word; + amo_write_data = mem_rs2_rdata; + amo_wb_data = '0; + + if (amo_is_word) begin + unique case (mem_amo_op) + AMO_SWAP, AMO_SC: amo_result_word = amo_rs2_word; + AMO_ADD: amo_result_word = amo_old_word + amo_rs2_word; + AMO_XOR: amo_result_word = amo_old_word ^ amo_rs2_word; + AMO_AND: amo_result_word = amo_old_word & amo_rs2_word; + AMO_OR: amo_result_word = amo_old_word | amo_rs2_word; + AMO_MIN: amo_result_word = (amo_old_word_signed < amo_rs2_word_signed) ? + amo_old_word : amo_rs2_word; + AMO_MAX: amo_result_word = (amo_old_word_signed > amo_rs2_word_signed) ? + amo_old_word : amo_rs2_word; + AMO_MINU: amo_result_word = (amo_old_word < amo_rs2_word) ? amo_old_word : amo_rs2_word; + AMO_MAXU: amo_result_word = (amo_old_word > amo_rs2_word) ? amo_old_word : amo_rs2_word; + default: amo_result_word = amo_old_word; + endcase + amo_write_data = {{32{amo_result_word[31]}}, amo_result_word}; + amo_wb_data = amo_is_sc ? {63'b0, ~amo_sc_success} : + {{32{amo_old_word[31]}}, amo_old_word}; + end else if (amo_is_double) begin + unique case (mem_amo_op) + AMO_SWAP, AMO_SC: amo_write_data = mem_rs2_rdata; + AMO_ADD: amo_write_data = amo_memory_rdata + mem_rs2_rdata; + AMO_XOR: amo_write_data = amo_memory_rdata ^ mem_rs2_rdata; + AMO_AND: amo_write_data = amo_memory_rdata & mem_rs2_rdata; + AMO_OR: amo_write_data = amo_memory_rdata | mem_rs2_rdata; + AMO_MIN: amo_write_data = (amo_old_double_signed < amo_rs2_double_signed) ? + amo_memory_rdata : mem_rs2_rdata; + AMO_MAX: amo_write_data = (amo_old_double_signed > amo_rs2_double_signed) ? + amo_memory_rdata : mem_rs2_rdata; + AMO_MINU: amo_write_data = (amo_memory_rdata < mem_rs2_rdata) ? amo_memory_rdata : + mem_rs2_rdata; + AMO_MAXU: amo_write_data = (amo_memory_rdata > mem_rs2_rdata) ? amo_memory_rdata : + mem_rs2_rdata; + default: amo_write_data = amo_memory_rdata; + endcase + amo_wb_data = amo_is_sc ? {63'b0, ~amo_sc_success} : amo_memory_rdata; + end + end + + // -------------------------- + // Pass Output Signals to WB Stage + // -------------------------- + assign mem_wb_alu_result = mem_alu_result; + assign mem_wb_mem_rdata = mem_is_amo ? amo_wb_data : load_data_aligned; + assign mem_wb_fp_result = mem_is_load ? fp_load_data : mem_fp_result; + assign mem_wb_fp_fflags = mem_fp_fflags; + assign mem_wb_pc = mem_pc; + assign mem_wb_instr = mem_instr; + assign mem_wb_instr_len = mem_instr_len; + + assign mem_wb_reg_write_en = mem_reg_write_en; + assign mem_wb_fp_reg_write_en = mem_fp_reg_write_en; + assign mem_wb_rd_addr = mem_rd_addr; + assign mem_wb_wb_sel = mem_wb_sel; + + assign mem_wb_is_csr = mem_is_csr; + assign mem_wb_csr_op = mem_csr_op; + assign mem_wb_rs1_rdata = mem_rs1_rdata; + assign mem_wb_rs1_addr = mem_rs1_addr; + assign mem_wb_imm = mem_imm; + + assign mem_wb_illegal_instr = mem_illegal_instr; + assign mem_wb_instr_access_fault = mem_instr_access_fault; + assign mem_wb_instr_page_fault = mem_instr_page_fault; + assign mem_wb_is_ecall = mem_is_ecall; + assign mem_wb_is_ebreak = mem_is_ebreak; + assign mem_wb_is_mret = mem_is_mret; + assign mem_wb_is_sret = mem_is_sret; + assign mem_wb_load_fault = load_addr_misaligned | (amo_load_access & amo_addr_misaligned) | + dmem_load_access_fault; + assign mem_wb_store_fault = store_addr_misaligned | dmem_store_access_fault; + assign mem_wb_load_page_fault = dmem_load_page_fault; + assign mem_wb_store_page_fault = dmem_store_page_fault; + assign mem_wb_fault_addr = (mem_instr_access_fault | mem_instr_page_fault) ? mem_pc : + ((dmem_load_page_fault | dmem_store_page_fault) ? mem_alu_result : + ((load_addr_misaligned | store_addr_misaligned | + dmem_load_access_fault | dmem_store_access_fault) ? mem_alu_result : '0)); + +endmodule diff --git a/rv64cpu/rtl/core/pipeline/mem_wb_reg.sv b/rv64cpu/rtl/core/pipeline/mem_wb_reg.sv new file mode 100644 index 0000000..be0aec1 --- /dev/null +++ b/rv64cpu/rtl/core/pipeline/mem_wb_reg.sv @@ -0,0 +1,188 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/mem_wb_reg.sv +// Description: MEM/WB pipeline stage register (Memory Access to Write Back) +// Passes the final computation results, memory read data, and write-back control signals to the write-back stage + +module mem_wb_reg + import rv64_pkg::*; +( + input logic clk, + input logic rst, + + // ----------------------- + // Control Signals from Hazard Unit + input logic stall_en, + input logic flush_en, + + // ----------------------- + // Inputs from MEM stage + // ----------------------- + // Data and addresses + input logic [XLEN-1:0] mem_pc, + input logic [XLEN-1:0] mem_alu_result, + input logic [XLEN-1:0] mem_mem_rdata, + input logic [XLEN-1:0] mem_fp_result, + input logic [4:0] mem_fp_fflags, + input logic [INST_WIDTH-1:0] mem_instr, + input logic [2:0] mem_instr_len, + + // Write back control + input logic mem_reg_write_en, + input logic mem_fp_reg_write_en, + input logic [4:0] mem_rd_addr, + input logic [1:0] mem_wb_sel, + + // CSR & System + input logic mem_is_csr, + input logic [2:0] mem_csr_op, + input logic [XLEN-1:0] mem_rs1_rdata, + input logic [4:0] mem_rs1_addr, + input logic [XLEN-1:0] mem_imm, + input logic mem_illegal_instr, + input logic mem_instr_access_fault, + input logic mem_instr_page_fault, + input logic mem_is_ecall, + input logic mem_is_ebreak, + input logic mem_is_mret, + input logic mem_is_sret, + input logic mem_load_fault, + input logic mem_store_fault, + input logic mem_load_page_fault, + input logic mem_store_page_fault, + input logic [XLEN-1:0] mem_fault_addr, + + // ----------------------- + // Outputs to WB Stage + // ----------------------- + // Data and address + output logic [XLEN-1:0] wb_pc, + output logic [XLEN-1:0] wb_alu_result, + output logic [XLEN-1:0] wb_mem_rdata, + output logic [XLEN-1:0] wb_fp_result, + output logic [4:0] wb_fp_fflags, + output logic [INST_WIDTH-1:0] wb_instr, + output logic [2:0] wb_instr_len, + + // Write back control + output logic wb_reg_write_en, + output logic wb_fp_reg_write_en, + output logic [4:0] wb_rd_addr, + output logic [1:0] wb_wb_sel, + + // CSR & System + output logic wb_is_csr, + output logic [2:0] wb_csr_op, + output logic [XLEN-1:0] wb_rs1_rdata, + output logic [4:0] wb_rs1_addr, + output logic [XLEN-1:0] wb_imm, + output logic wb_illegal_instr, + output logic wb_instr_access_fault, + output logic wb_instr_page_fault, + output logic wb_is_ecall, + output logic wb_is_ebreak, + output logic wb_is_mret, + output logic wb_is_sret, + output logic wb_load_fault, + output logic wb_store_fault, + output logic wb_load_page_fault, + output logic wb_store_page_fault, + output logic [XLEN-1:0] wb_fault_addr + +); + // Sequential Logic: State Update + always_ff @(posedge clk) begin + if (rst) begin + wb_pc <= '0; + wb_alu_result <= '0; + wb_mem_rdata <= '0; + wb_fp_result <= '0; + wb_fp_fflags <= '0; + wb_instr <= '0; + wb_instr_len <= 3'd4; + wb_reg_write_en <= '0; + wb_fp_reg_write_en <= '0; + wb_rd_addr <= '0; + wb_wb_sel <= '0; + wb_is_csr <= '0; + wb_csr_op <= '0; + wb_rs1_rdata <= '0; + wb_rs1_addr <= '0; + wb_imm <= '0; + wb_illegal_instr <= '0; + wb_instr_access_fault <= '0; + wb_instr_page_fault <= '0; + wb_is_ecall <= '0; + wb_is_ebreak <= '0; + wb_is_mret <= '0; + wb_is_sret <= '0; + wb_load_fault <= '0; + wb_store_fault <= '0; + wb_load_page_fault <= '0; + wb_store_page_fault <= '0; + wb_fault_addr <= '0; + end else if (flush_en) begin + // Target specific control signals to clear. Leave datapath elements intact + // to avoid extreme power consumption from massive flip-flop state changes array. + wb_reg_write_en <= '0; + wb_fp_reg_write_en <= '0; + wb_is_csr <= '0; + wb_illegal_instr <= '0; + wb_instr_access_fault <= '0; + wb_instr_page_fault <= '0; + wb_is_ecall <= '0; + wb_is_ebreak <= '0; + wb_is_mret <= '0; + wb_is_sret <= '0; + wb_load_fault <= '0; + wb_store_fault <= '0; + wb_load_page_fault <= '0; + wb_store_page_fault <= '0; + wb_fault_addr <= '0; + end else if (!stall_en) begin + wb_pc <= mem_pc; + wb_alu_result <= mem_alu_result; + wb_mem_rdata <= mem_mem_rdata; + wb_fp_result <= mem_fp_result; + wb_fp_fflags <= mem_fp_fflags; + wb_instr <= mem_instr; + wb_instr_len <= mem_instr_len; + wb_reg_write_en <= mem_reg_write_en; + wb_fp_reg_write_en <= mem_fp_reg_write_en; + wb_rd_addr <= mem_rd_addr; + wb_wb_sel <= mem_wb_sel; + wb_is_csr <= mem_is_csr; + wb_csr_op <= mem_csr_op; + wb_rs1_rdata <= mem_rs1_rdata; + wb_rs1_addr <= mem_rs1_addr; + wb_imm <= mem_imm; + wb_illegal_instr <= mem_illegal_instr; + wb_instr_access_fault <= mem_instr_access_fault; + wb_instr_page_fault <= mem_instr_page_fault; + wb_is_ecall <= mem_is_ecall; + wb_is_ebreak <= mem_is_ebreak; + wb_is_mret <= mem_is_mret; + wb_is_sret <= mem_is_sret; + wb_load_fault <= mem_load_fault; + wb_store_fault <= mem_store_fault; + wb_load_page_fault <= mem_load_page_fault; + wb_store_page_fault <= mem_store_page_fault; + wb_fault_addr <= mem_fault_addr; + end + end + +endmodule diff --git a/rv64cpu/rtl/core/pipeline/wb_stage.sv b/rv64cpu/rtl/core/pipeline/wb_stage.sv new file mode 100644 index 0000000..b19cb67 --- /dev/null +++ b/rv64cpu/rtl/core/pipeline/wb_stage.sv @@ -0,0 +1,168 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/wb_stage.sv +// Description: Write Back Stage +// Responsible for writing back to the general-purpose register file after multiplexing the final data (ALU result, memory read data, or PC+4) + +module wb_stage + import rv64_pkg::*; +( + // Inputs from MEM/WB pipeline register + input logic [XLEN-1:0] mem_wb_alu_result, + input logic [XLEN-1:0] mem_wb_mem_rdata, + input logic [XLEN-1:0] mem_wb_fp_result, + input logic [4:0] mem_wb_fp_fflags, + input logic [XLEN-1:0] mem_wb_pc, + input logic [XLEN-1:0] mem_wb_pc_plus_4, + input logic [INST_WIDTH-1:0] mem_wb_instr, + + input logic mem_wb_reg_write_en, + input logic mem_wb_fp_reg_write_en, + input logic [4:0] mem_wb_rd_addr, + input logic [1:0] mem_wb_wb_sel, + + input logic mem_wb_is_csr, + input logic [2:0] mem_wb_csr_op, + input logic [XLEN-1:0] mem_wb_rs1_rdata, + input logic [4:0] mem_wb_rs1_addr, + input logic [XLEN-1:0] mem_wb_imm, + + input logic mem_wb_illegal_instr, + input logic mem_wb_is_ecall, + input logic mem_wb_is_ebreak, + input logic mem_wb_is_mret, + input logic mem_wb_is_sret, + input logic mem_wb_instr_access_fault, + input logic mem_wb_instr_page_fault, + input logic mem_wb_load_fault, + input logic mem_wb_store_fault, + input logic mem_wb_load_page_fault, + input logic mem_wb_store_page_fault, + input logic [XLEN-1:0] mem_wb_fault_addr, + + // Inputs from CSR Unit + input logic [XLEN-1:0] csr_rdata, // Data read from CSR unit + input logic csr_illegal_access,// CSR rejected the request + + // Outputs to Register File + output logic [XLEN-1:0] wb_rd_wdata, + output logic [4:0] wb_rd_addr, + output logic wb_reg_write_en, + output logic [XLEN-1:0] wb_fd_wdata, + output logic [4:0] wb_fd_addr, + output logic wb_fp_write_en, + output logic [4:0] wb_fp_fflags, + output logic wb_fp_state_dirty, + output logic wb_fp_fflags_valid, + + // Outputs to CSR Unit + output logic csr_req, // CSR request valid + output logic [2:0] csr_op, // CSR operation type + output logic [11:0] csr_addr, // CSR address + output logic [XLEN-1:0] csr_rs1_data, // Source data (from rs1_rdata or zimm) + + // Outputs to Exception Handler (Trap Controller) + output logic trap_illegal_instr, + output logic trap_is_ecall, + output logic trap_is_ebreak, + output logic trap_is_mret, + output logic trap_is_sret, + output logic [XLEN-1:0] trap_epc, + output logic trap_instr_access_fault, + output logic trap_instr_page_fault, + output logic trap_load_fault, + output logic trap_store_fault, + output logic trap_load_page_fault, + output logic trap_store_page_fault, + output logic [XLEN-1:0] trap_bad_addr, + output logic [INST_WIDTH-1:0] trap_bad_instr +); + + // -------------------------- + // Exception/Trap Detection + // -------------------------- + logic has_trap; + logic trap_blocker; + assign has_trap = mem_wb_illegal_instr | mem_wb_is_ecall | mem_wb_is_ebreak | + mem_wb_instr_access_fault | mem_wb_load_fault | mem_wb_store_fault | + mem_wb_instr_page_fault | mem_wb_load_page_fault | mem_wb_store_page_fault; + assign trap_blocker = has_trap | csr_illegal_access; + + // -------------------------- + // CSR Signal Routing + // -------------------------- + assign csr_req = mem_wb_is_csr & ~has_trap; + assign csr_op = mem_wb_csr_op; + assign csr_addr = mem_wb_imm[11:0]; + + // For immediate CSR instructions (CSRRWI, CSRRSI, CSRRCI), + // the source data is the zero-extended rs1_addr field. + // mem_wb_csr_op[2] is 1 for immediate variants. + assign csr_rs1_data = (mem_wb_csr_op[2]) ? {59'b0, mem_wb_rs1_addr} : mem_wb_rs1_rdata; + + // -------------------------- + // Data Multiplexer for Register File + // -------------------------- + always_comb begin + if (mem_wb_is_csr) begin + // CSR reads write the previous CSR value to the destination register + wb_rd_wdata = csr_rdata; + end else begin + case (mem_wb_wb_sel) + 2'b00: wb_rd_wdata = mem_wb_alu_result; + 2'b01: wb_rd_wdata = mem_wb_mem_rdata; + 2'b10: wb_rd_wdata = mem_wb_pc_plus_4; + default: wb_rd_wdata = '0; + endcase + end + end + + // -------------------------- + // Register File Write Control + // -------------------------- + // We only write back if the instruction didn't trap. + assign wb_rd_addr = mem_wb_rd_addr; + assign wb_reg_write_en = mem_wb_reg_write_en & ~trap_blocker; + assign wb_fd_addr = mem_wb_rd_addr; + assign wb_fd_wdata = mem_wb_fp_result; + assign wb_fp_write_en = mem_wb_fp_reg_write_en & ~trap_blocker; + assign wb_fp_fflags = mem_wb_fp_fflags; + assign wb_fp_state_dirty = wb_fp_write_en | ((|mem_wb_fp_fflags) & ~trap_blocker); + assign wb_fp_fflags_valid = (|mem_wb_fp_fflags) & ~trap_blocker; + + // -------------------------- + // Trap / Exception Signals + // -------------------------- + assign trap_illegal_instr = (mem_wb_illegal_instr & + ~mem_wb_instr_access_fault & + ~mem_wb_instr_page_fault) | + csr_illegal_access; + assign trap_is_ecall = mem_wb_is_ecall; + assign trap_is_ebreak = mem_wb_is_ebreak; + assign trap_is_mret = mem_wb_is_mret; + assign trap_is_sret = mem_wb_is_sret; + assign trap_epc = mem_wb_pc; + assign trap_instr_access_fault = mem_wb_instr_access_fault; + assign trap_instr_page_fault = mem_wb_instr_page_fault; + assign trap_load_fault = mem_wb_load_fault; + assign trap_store_fault = mem_wb_store_fault; + assign trap_load_page_fault = mem_wb_load_page_fault; + assign trap_store_page_fault = mem_wb_store_page_fault; + assign trap_bad_addr = mem_wb_fault_addr; + assign trap_bad_instr = mem_wb_instr; + +endmodule diff --git a/rv64cpu/rtl/core/pmp_checker.sv b/rv64cpu/rtl/core/pmp_checker.sv deleted file mode 100644 index 361e507..0000000 --- a/rv64cpu/rtl/core/pmp_checker.sv +++ /dev/null @@ -1,181 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/pmp_checker.sv -// Description: RISC-V Physical Memory Protection access checker. - -module pmp_checker - import rv64_pkg::*; - import csr_pkg::*; -#( - parameter int PHYS_ADDR_WIDTH = 56 -)( - input logic req_valid, - input logic [XLEN-1:0] req_paddr, - input logic [3:0] req_size, - input logic req_is_fetch, - input logic req_is_store, - input logic [1:0] priv_mode, - input pmpcfg_entry_t pmpcfg_entries [PMP_ENTRY_COUNT], - input pmpaddr_entry_t pmpaddr_entries [PMP_ENTRY_COUNT], - output logic access_fault -); - - localparam logic [1:0] PRIV_MODE_M = 2'b11; - - typedef struct packed { - logic valid; - logic [XLEN-1:0] lower_bound; - logic [XLEN-1:0] upper_bound; - } pmp_region_t; - - function automatic logic [XLEN-1:0] pmpaddr_to_paddr(input pmpaddr_entry_t pmpaddr); - logic [XLEN-1:0] paddr; - paddr = pmpaddr << 2; - if (PHYS_ADDR_WIDTH < XLEN) begin - paddr[XLEN-1:PHYS_ADDR_WIDTH] = '0; - end - return paddr; - endfunction - - function automatic int unsigned napot_trailing_ones(input pmpaddr_entry_t pmpaddr); - int unsigned ones; - ones = 0; - for (int bit_idx = 0; bit_idx < (PHYS_ADDR_WIDTH - 2); bit_idx++) begin - if (pmpaddr[bit_idx]) begin - ones++; - end else begin - break; - end - end - return ones; - endfunction - - function automatic pmp_region_t pmp_region(input int unsigned entry_idx); - pmp_region_t region; - logic [1:0] address_matching; - int unsigned ones; - logic [XLEN-1:0] napot_mask; - logic [XLEN-1:0] napot_base; - - region.valid = 1'b0; - region.lower_bound = '0; - region.upper_bound = '0; - address_matching = pmpcfg_entries[entry_idx][PMPCFG_A_HI:PMPCFG_A_LO]; - - unique case (address_matching) - PMP_MODE_OFF: begin - region.valid = 1'b0; - end - PMP_MODE_TOR: begin - region.valid = 1'b1; - region.lower_bound = (entry_idx == 0) ? '0 : pmpaddr_to_paddr(pmpaddr_entries[entry_idx - 1]); - region.upper_bound = pmpaddr_to_paddr(pmpaddr_entries[entry_idx]); - if (region.upper_bound <= region.lower_bound) begin - region.valid = 1'b0; - end - end - PMP_MODE_NA4: begin - region.valid = 1'b1; - region.lower_bound = pmpaddr_to_paddr(pmpaddr_entries[entry_idx]); - region.upper_bound = region.lower_bound + 64'd4; - end - PMP_MODE_NAPOT: begin - region.valid = 1'b1; - ones = napot_trailing_ones(pmpaddr_entries[entry_idx]); - napot_mask = (64'd1 << (ones + 1)) - 64'd1; - napot_base = (pmpaddr_entries[entry_idx] & ~napot_mask) << 2; - if (PHYS_ADDR_WIDTH < XLEN) begin - napot_base[XLEN-1:PHYS_ADDR_WIDTH] = '0; - end - region.lower_bound = napot_base; - region.upper_bound = napot_base + (64'd1 << (ones + 3)); - end - default: begin - region.valid = 1'b0; - end - endcase - - return region; - endfunction - - function automatic logic ranges_overlap( - input logic [XLEN-1:0] access_lower, - input logic [XLEN-1:0] access_upper, - input logic [XLEN-1:0] region_lower, - input logic [XLEN-1:0] region_upper - ); - return (access_lower < region_upper) && (access_upper > region_lower); - endfunction - - function automatic logic range_contains( - input logic [XLEN-1:0] access_lower, - input logic [XLEN-1:0] access_upper, - input logic [XLEN-1:0] region_lower, - input logic [XLEN-1:0] region_upper - ); - return (access_lower >= region_lower) && (access_upper <= region_upper); - endfunction - - function automatic logic permissions_allow( - input pmpcfg_entry_t cfg, - input logic is_fetch, - input logic is_store - ); - if (is_fetch) begin - return cfg[PMPCFG_X_BIT]; - end - if (is_store) begin - return cfg[PMPCFG_W_BIT]; - end - return cfg[PMPCFG_R_BIT]; - endfunction - - always_comb begin - logic [XLEN-1:0] access_lower; - logic [XLEN-1:0] access_upper; - logic [3:0] normalized_size; - logic matched; - pmp_region_t region; - - normalized_size = (req_size == 4'd0) ? 4'd1 : req_size; - access_lower = req_paddr; - access_upper = req_paddr + {{(XLEN-4){1'b0}}, normalized_size}; - matched = 1'b0; - region = '0; - access_fault = 1'b0; - - if (req_valid) begin - for (int unsigned entry_idx = 0; entry_idx < PMP_ENTRY_COUNT; entry_idx++) begin - region = pmp_region(entry_idx); - if (!matched && region.valid && - ranges_overlap(access_lower, access_upper, region.lower_bound, region.upper_bound)) begin - matched = 1'b1; - if (!range_contains(access_lower, access_upper, region.lower_bound, region.upper_bound)) begin - access_fault = 1'b1; - end else if ((priv_mode != PRIV_MODE_M) || pmpcfg_entries[entry_idx][PMPCFG_L_BIT]) begin - access_fault = !permissions_allow(pmpcfg_entries[entry_idx], req_is_fetch, req_is_store); - end - end - end - - if (!matched && (priv_mode != PRIV_MODE_M)) begin - access_fault = 1'b1; - end - end - end - -endmodule diff --git a/rv64cpu/rtl/core/privileged/csr_unit.sv b/rv64cpu/rtl/core/privileged/csr_unit.sv new file mode 100644 index 0000000..a25e17a --- /dev/null +++ b/rv64cpu/rtl/core/privileged/csr_unit.sv @@ -0,0 +1,1221 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/csr_unit.sv + +module csr_unit + import rv64_pkg::*; + import csr_pkg::*; +#( + parameter logic [XLEN-1:0] HART_ID = '0, + parameter logic [XLEN-1:0] VENDOR_ID = '0, + parameter logic [XLEN-1:0] ARCH_ID = '0, + parameter logic [XLEN-1:0] IMPL_ID = '0, + parameter logic [XLEN-1:0] MISA_VALUE = 64'h8000_0000_0014_11ad, + parameter int PHYS_ADDR_WIDTH = 56, + parameter int PMP_GRANULARITY = 0 +)( + input logic clk, + input logic rst, + input logic csr_req, + input logic [2:0] csr_op, + input logic [11:0] csr_addr, + input logic [XLEN-1:0] csr_rs1_data, + input logic trap_ctrl_trigger, + input logic trap_ctrl_to_s_mode, + input logic [XLEN-1:0] trap_ctrl_mepc, + input logic [XLEN-1:0] trap_ctrl_mcause, + input logic [XLEN-1:0] trap_ctrl_mtval, + input logic trap_ctrl_mret_en, + input logic trap_ctrl_sret_en, + input logic m_timer_interrupt, + input logic m_software_interrupt, + input logic m_external_interrupt, + input logic s_timer_interrupt, + input logic s_software_interrupt, + input logic s_external_interrupt, + input logic fp_state_dirty, + input logic fp_fflags_valid, + input logic [4:0] fp_fflags, + output logic [XLEN-1:0] csr_rdata, + output logic [XLEN-1:0] csr_mstatus, + output logic [XLEN-1:0] csr_sstatus, + output logic [XLEN-1:0] csr_medeleg, + output logic [XLEN-1:0] csr_mideleg, + output logic [XLEN-1:0] csr_mie, + output logic [XLEN-1:0] csr_sie, + output logic [XLEN-1:0] csr_mip, + output logic [XLEN-1:0] csr_sip, + output logic [XLEN-1:0] csr_mtvec, + output logic [XLEN-1:0] csr_stvec, + output logic [XLEN-1:0] csr_mepc, + output logic [XLEN-1:0] csr_sepc, + output logic [XLEN-1:0] csr_satp, + output logic [2:0] csr_frm, + output logic [1:0] csr_priv_mode, + output logic [XLEN-1:0] csr_trap_vector, + output logic csr_trap_to_s_mode, + output logic [XLEN-1:0] csr_trap_vector_next, + output logic csr_trap_to_s_mode_next, + output pmpcfg_entry_t csr_pmpcfg_entries [PMP_ENTRY_COUNT], + output pmpaddr_entry_t csr_pmpaddr_entries [PMP_ENTRY_COUNT], + output logic csr_illegal_access +); + + typedef enum logic [1:0] { + PRIV_MODE_U = 2'b00, + PRIV_MODE_S = 2'b01, + PRIV_MODE_M = 2'b11 + } priv_mode_t; + + localparam int MSTATUS_MIE_BIT = 3; + localparam int MSTATUS_MPIE_BIT = 7; + localparam int MSTATUS_VS_LO = 9; + localparam int MSTATUS_VS_HI = 10; + localparam int MSTATUS_MPP_LO = 11; + localparam int MSTATUS_MPP_HI = 12; + localparam int MSTATUS_FS_LO = 13; + localparam int MSTATUS_FS_HI = 14; + localparam int MSTATUS_XS_LO = 15; + localparam int MSTATUS_XS_HI = 16; + localparam int MSTATUS_MPRV_BIT = 17; + localparam int MSTATUS_SUM_BIT = 18; + localparam int MSTATUS_MXR_BIT = 19; + localparam int MSTATUS_TVM_BIT = 20; + localparam int MSTATUS_TW_BIT = 21; + localparam int MSTATUS_TSR_BIT = 22; + localparam int MSTATUS_UXL_LO = 32; + localparam int MSTATUS_UXL_HI = 33; + localparam int MSTATUS_SXL_LO = 34; + localparam int MSTATUS_SXL_HI = 35; + localparam int MSTATUS_SD_BIT = 63; + localparam logic [1:0] STATUS_XLEN_64 = 2'b10; + localparam int SSTATUS_SIE_BIT = 1; + localparam int SSTATUS_SPIE_BIT = 5; + localparam int SSTATUS_SPP_BIT = 8; + localparam int SSTATUS_VS_LO = MSTATUS_VS_LO; + localparam int SSTATUS_VS_HI = MSTATUS_VS_HI; + localparam int SSTATUS_FS_LO = MSTATUS_FS_LO; + localparam int SSTATUS_FS_HI = MSTATUS_FS_HI; + localparam int SSTATUS_XS_LO = MSTATUS_XS_LO; + localparam int SSTATUS_XS_HI = MSTATUS_XS_HI; + localparam int SSTATUS_SUM_BIT = MSTATUS_SUM_BIT; + localparam int SSTATUS_MXR_BIT = MSTATUS_MXR_BIT; + localparam int SSTATUS_UXL_LO = MSTATUS_UXL_LO; + localparam int SSTATUS_UXL_HI = MSTATUS_UXL_HI; + localparam int SSTATUS_SD_BIT = MSTATUS_SD_BIT; + localparam int SATP_MODE_LO = 60; + localparam int SATP_MODE_HI = 63; + localparam int SATP_ASID_LO = 44; + localparam int SATP_ASID_HI = 59; + localparam int SATP_PPN_LO = 0; + localparam int SATP_PPN_HI = 43; + localparam logic [3:0] SATP_MODE_BARE = 4'd0; + localparam logic [3:0] SATP_MODE_SV39 = 4'd8; + localparam logic [3:0] SATP_MODE_SV48 = 4'd9; // Stub: unsupported until Sv48 is implemented. + localparam logic [3:0] SATP_MODE_SV57 = 4'd10; // Stub: unsupported until Sv57 is implemented. + localparam logic [3:0] SATP_MODE_SV64 = 4'd11; // Reserved/future stub. + localparam int MCOUNTINHIBIT_CY_BIT = 0; + localparam int MCOUNTINHIBIT_IR_BIT = 2; + localparam int COUNTEREN_CY_BIT = 0; + localparam int COUNTEREN_TM_BIT = 1; + localparam int COUNTEREN_IR_BIT = 2; + localparam int HPM_COUNTER_COUNT = 29; + localparam int INTERRUPT_S_SOFTWARE_BIT = int'(INTERRUPT_S_SOFTWARE); + localparam int INTERRUPT_S_TIMER_BIT = int'(INTERRUPT_S_TIMER); + localparam int INTERRUPT_S_EXTERNAL_BIT = int'(INTERRUPT_S_EXTERNAL); + localparam int INTERRUPT_M_SOFTWARE_BIT = int'(INTERRUPT_M_SOFTWARE); + localparam int INTERRUPT_M_TIMER_BIT = int'(INTERRUPT_M_TIMER); + localparam int INTERRUPT_M_EXTERNAL_BIT = int'(INTERRUPT_M_EXTERNAL); + localparam logic [XLEN-1:0] S_INTERRUPT_MASK = + (64'h1 << INTERRUPT_S_SOFTWARE) | + (64'h1 << INTERRUPT_S_TIMER) | + (64'h1 << INTERRUPT_S_EXTERNAL); + localparam logic [XLEN-1:0] M_INTERRUPT_MASK = + (64'h1 << INTERRUPT_M_SOFTWARE) | + (64'h1 << INTERRUPT_M_TIMER) | + (64'h1 << INTERRUPT_M_EXTERNAL); + localparam logic [XLEN-1:0] INTERRUPT_MASK = S_INTERRUPT_MASK | M_INTERRUPT_MASK; + + // Machine-mode CSRs + logic [XLEN-1:0] mstatus_reg; + logic [XLEN-1:0] medeleg_reg; + logic [XLEN-1:0] mideleg_reg; + logic [XLEN-1:0] mie_reg; + logic [XLEN-1:0] mtvec_reg; + logic [XLEN-1:0] mcounteren_reg; + logic [XLEN-1:0] mscratch_reg; + logic [XLEN-1:0] mepc_reg; + logic [XLEN-1:0] mcause_reg; + logic [XLEN-1:0] mtval_reg; + logic [XLEN-1:0] mip_reg; + logic [XLEN-1:0] mtinst_reg; + logic [XLEN-1:0] mtval2_reg; + logic [XLEN-1:0] menvcfg_reg; + logic [XLEN-1:0] mstateen_reg [4]; + pmpcfg_entry_t pmpcfg_entries [PMP_ENTRY_COUNT]; + pmpaddr_entry_t pmpaddr_entries [PMP_ENTRY_COUNT]; + + // Supervisor-mode CSRs + logic [XLEN-1:0] sstatus_reg; + logic [XLEN-1:0] stvec_reg; + logic [XLEN-1:0] scounteren_reg; + logic [XLEN-1:0] senvcfg_reg; + logic [XLEN-1:0] sstateen_reg [4]; + logic [XLEN-1:0] sscratch_reg; + logic [XLEN-1:0] sepc_reg; + logic [XLEN-1:0] scause_reg; + logic [XLEN-1:0] stval_reg; + logic [XLEN-1:0] satp_reg; + logic [XLEN-1:0] hstatus_reg; + logic [XLEN-1:0] hedeleg_reg; + logic [XLEN-1:0] hideleg_reg; + logic [XLEN-1:0] hie_reg; + logic [XLEN-1:0] htimedelta_reg; + logic [XLEN-1:0] hcounteren_reg; + logic [XLEN-1:0] hgeie_reg; + logic [XLEN-1:0] henvcfg_reg; + logic [XLEN-1:0] hstateen_reg [4]; + logic [XLEN-1:0] htval_reg; + logic [XLEN-1:0] hip_reg; + logic [XLEN-1:0] hvip_reg; + logic [XLEN-1:0] htinst_reg; + logic [XLEN-1:0] hgatp_reg; + logic [XLEN-1:0] vsstatus_reg; + logic [XLEN-1:0] vsie_reg; + logic [XLEN-1:0] vstvec_reg; + logic [XLEN-1:0] vsscratch_reg; + logic [XLEN-1:0] vsepc_reg; + logic [XLEN-1:0] vscause_reg; + logic [XLEN-1:0] vstval_reg; + logic [XLEN-1:0] vsip_reg; + logic [XLEN-1:0] vsatp_reg; + + // Counters + logic [XLEN-1:0] mcycle_reg; + logic [XLEN-1:0] minstret_reg; + logic [XLEN-1:0] time_reg; + logic [XLEN-1:0] mcountinhibit_reg; + logic [XLEN-1:0] mhpmcounter_reg [HPM_COUNTER_COUNT]; + + // Floating-point CSRs + logic [4:0] fflags_reg; + logic [2:0] frm_reg; + + priv_mode_t priv_mode_q; + + logic [11:0] csr_addr_canonical; + logic is_write_cmd; + logic is_set_cmd; + logic is_clear_cmd; + logic has_non_zero_data; + logic csr_write_en; + logic [XLEN-1:0] trap_vector_target; + logic csr_addr_supported; + logic csr_privilege_illegal; + logic csr_write_illegal; + logic csr_stateen_illegal; + logic [XLEN-1:0] csr_read_data; + logic [XLEN-1:0] mip_value; + logic [XLEN-1:0] sip_value; + logic [XLEN-1:0] sie_value; + logic [XLEN-1:0] trap_vector_q; + logic trap_to_s_q; + logic trap_taken; + logic trap_taken_to_s_mode; + logic [XLEN-1:0] trap_taken_mepc; + logic [XLEN-1:0] trap_taken_mcause; + logic [XLEN-1:0] trap_taken_mtval; + + // Canonicalize addresses, decode operations, and prepare CSR/trap metadata. + assign csr_addr_canonical = canonical_addr(csr_addr); + assign is_write_cmd = (csr_op == 3'b001) || (csr_op == 3'b101); + assign is_set_cmd = (csr_op == 3'b010) || (csr_op == 3'b110); + assign is_clear_cmd = (csr_op == 3'b011) || (csr_op == 3'b111); + assign has_non_zero_data = |csr_rs1_data; + assign csr_addr_supported = csr_is_supported(csr_addr_canonical); + assign csr_write_en = csr_req & csr_addr_supported & !csr_privilege_illegal & + !csr_write_illegal & !csr_stateen_illegal & + (is_write_cmd | ((is_set_cmd | is_clear_cmd) & has_non_zero_data)); + assign trap_taken = trap_ctrl_trigger; + assign trap_taken_to_s_mode = trap_ctrl_to_s_mode; + assign trap_taken_mepc = trap_ctrl_mepc; + assign trap_taken_mcause = trap_ctrl_mcause; + assign trap_taken_mtval = trap_ctrl_mtval; + + assign trap_vector_target = trap_taken_to_s_mode ? stvec_reg : mtvec_reg; + assign csr_read_data = read_csr(csr_addr_canonical); + assign csr_rdata = csr_read_data; + assign mip_value = compose_mip(); + assign sip_value = mip_value & S_INTERRUPT_MASK; + assign sie_value = mie_reg & S_INTERRUPT_MASK; + assign csr_mstatus = mstatus_reg; + assign csr_sstatus = sstatus_reg; + assign csr_medeleg = medeleg_reg; + assign csr_mideleg = mideleg_reg; + assign csr_mie = mie_reg; + assign csr_sie = sie_value; + assign csr_mip = mip_value; + assign csr_sip = sip_value; + assign csr_mtvec = mtvec_reg; + assign csr_stvec = stvec_reg; + assign csr_mepc = mepc_reg; + assign csr_sepc = sepc_reg; + assign csr_satp = satp_reg; + assign csr_frm = frm_reg; + assign csr_priv_mode = priv_mode_q; + assign csr_trap_vector = trap_vector_q; + assign csr_trap_to_s_mode = trap_to_s_q; + assign csr_trap_vector_next = trap_vector_target; + assign csr_trap_to_s_mode_next = trap_taken_to_s_mode; + assign csr_write_illegal = csr_req && csr_is_read_only_by_encoding(csr_addr) && + (is_write_cmd || ((is_set_cmd || is_clear_cmd) && has_non_zero_data)); + assign csr_privilege_illegal = csr_req && csr_addr_supported && + !csr_privilege_allows_access(csr_addr, csr_addr_canonical); + assign csr_stateen_illegal = csr_req && csr_access_blocked_by_stateen(csr_addr, csr_addr_canonical); + assign csr_illegal_access = csr_req && (!csr_addr_supported || csr_privilege_illegal || + csr_write_illegal || csr_stateen_illegal); + + for (genvar pmp_entry_idx = 0; pmp_entry_idx < PMP_ENTRY_COUNT; pmp_entry_idx++) begin : gen_pmp_outputs + assign csr_pmpcfg_entries[pmp_entry_idx] = pmpcfg_entries[pmp_entry_idx]; + assign csr_pmpaddr_entries[pmp_entry_idx] = pmpaddr_entries[pmp_entry_idx]; + end + + // Main CSR state machine: counts cycles, processes CSR writes, and reacts to traps/returns. + always_ff @(posedge clk) begin + if (rst) begin + mstatus_reg <= reset_mstatus_value(); + medeleg_reg <= legalize_medeleg('0); + mideleg_reg <= '0; + mie_reg <= '0; + mtvec_reg <= '0; + mcounteren_reg <= '0; + mscratch_reg <= '0; + mepc_reg <= '0; + mcause_reg <= '0; + mtval_reg <= '0; + mip_reg <= '0; + mtinst_reg <= '0; + mtval2_reg <= '0; + menvcfg_reg <= '0; + for (int unsigned stateen_idx = 0; stateen_idx < 4; stateen_idx++) begin + mstateen_reg[stateen_idx] <= '0; + sstateen_reg[stateen_idx] <= '0; + hstateen_reg[stateen_idx] <= '0; + end + for (int unsigned pmp_entry_idx = 0; pmp_entry_idx < PMP_ENTRY_COUNT; pmp_entry_idx++) begin + pmpcfg_entries[pmp_entry_idx] <= '0; + pmpaddr_entries[pmp_entry_idx] <= '0; + end + sstatus_reg <= reset_sstatus_value(); + stvec_reg <= '0; + scounteren_reg <= '0; + senvcfg_reg <= '0; + sscratch_reg <= '0; + sepc_reg <= '0; + scause_reg <= '0; + stval_reg <= '0; + satp_reg <= '0; + hstatus_reg <= '0; + hedeleg_reg <= '0; + hideleg_reg <= '0; + hie_reg <= '0; + htimedelta_reg <= '0; + hcounteren_reg <= '0; + hgeie_reg <= '0; + henvcfg_reg <= '0; + htval_reg <= '0; + hip_reg <= '0; + hvip_reg <= '0; + htinst_reg <= '0; + hgatp_reg <= '0; + vsstatus_reg <= reset_sstatus_value(); + vsie_reg <= '0; + vstvec_reg <= '0; + vsscratch_reg <= '0; + vsepc_reg <= '0; + vscause_reg <= '0; + vstval_reg <= '0; + vsip_reg <= '0; + vsatp_reg <= '0; + mcycle_reg <= '0; + minstret_reg <= '0; + time_reg <= '0; + mcountinhibit_reg <= '0; + for (int unsigned hpm_idx = 0; hpm_idx < HPM_COUNTER_COUNT; hpm_idx++) begin + mhpmcounter_reg[hpm_idx] <= '0; + end + fflags_reg <= '0; + frm_reg <= '0; + priv_mode_q <= PRIV_MODE_M; + trap_vector_q <= '0; + trap_to_s_q <= 1'b0; + end else begin + if (!mcountinhibit_reg[MCOUNTINHIBIT_CY_BIT]) begin + mcycle_reg <= mcycle_reg + 64'd1; + end + time_reg <= time_reg + 64'd1; + if (!trap_taken && !mcountinhibit_reg[MCOUNTINHIBIT_IR_BIT]) begin + minstret_reg <= minstret_reg + 64'd1; + end + + if (csr_write_en && csr_is_writable(csr_addr_canonical)) begin + if (csr_is_pmpcfg(csr_addr_canonical)) begin + write_pmpcfg_csr(csr_addr_canonical, csr_write_next(read_csr(csr_addr_canonical), csr_rs1_data, csr_op)); + end else if (csr_is_pmpaddr(csr_addr_canonical)) begin + write_pmpaddr_csr(csr_addr_canonical, csr_write_next(read_csr(csr_addr_canonical), csr_rs1_data, csr_op)); + end else if (csr_is_mhpmcounter(csr_addr_canonical)) begin + mhpmcounter_reg[hpmcounter_index(csr_addr_canonical)] <= + csr_write_next(read_csr(csr_addr_canonical), csr_rs1_data, csr_op); + end else begin + unique case (csr_addr_canonical) + CSR_MSTATUS: begin + logic [XLEN-1:0] next_data; + logic [XLEN-1:0] legal_data; + next_data = csr_write_next(mstatus_reg, csr_rs1_data, csr_op); + legal_data = legalize_mstatus(next_data); + mstatus_reg <= legal_data; + sstatus_reg <= mirror_mstatus_to_sstatus(sstatus_reg, legal_data); + end + CSR_MEDELEG: medeleg_reg <= legalize_medeleg(csr_write_next(medeleg_reg, csr_rs1_data, csr_op)); + CSR_MIDELEG: mideleg_reg <= legalize_mideleg(csr_write_next(mideleg_reg, csr_rs1_data, csr_op)); + CSR_MIE: mie_reg <= legalize_interrupt_csr(csr_write_next(mie_reg, csr_rs1_data, csr_op)); + CSR_MTVEC: mtvec_reg <= csr_write_next(mtvec_reg, csr_rs1_data, csr_op); + CSR_MSCRATCH: mscratch_reg <= csr_write_next(mscratch_reg, csr_rs1_data, csr_op); + CSR_MEPC: mepc_reg <= csr_write_next(mepc_reg, csr_rs1_data, csr_op); + CSR_MCAUSE: mcause_reg <= csr_write_next(mcause_reg, csr_rs1_data, csr_op); + CSR_MTVAL: mtval_reg <= csr_write_next(mtval_reg, csr_rs1_data, csr_op); + CSR_MIP: mip_reg <= legalize_interrupt_csr(csr_write_next(mip_reg, csr_rs1_data, csr_op)); + CSR_MTINST: mtinst_reg <= '0; + CSR_MTVAL2: mtval2_reg <= csr_write_next(mtval2_reg, csr_rs1_data, csr_op); + CSR_MCOUNTEREN: mcounteren_reg <= legalize_counteren(csr_write_next(mcounteren_reg, csr_rs1_data, csr_op)); + CSR_MCOUNTINHIBIT: mcountinhibit_reg <= legalize_mcountinhibit(csr_write_next(mcountinhibit_reg, csr_rs1_data, csr_op)); + CSR_MENVCFG: menvcfg_reg <= legalize_menvcfg(csr_write_next(menvcfg_reg, csr_rs1_data, csr_op)); + CSR_MSTATEEN0: mstateen_reg[0] <= legalize_mstateen(0, csr_write_next(mstateen_reg[0], csr_rs1_data, csr_op)); + CSR_MSTATEEN1: mstateen_reg[1] <= legalize_mstateen(1, csr_write_next(mstateen_reg[1], csr_rs1_data, csr_op)); + CSR_MSTATEEN2: mstateen_reg[2] <= legalize_mstateen(2, csr_write_next(mstateen_reg[2], csr_rs1_data, csr_op)); + CSR_MSTATEEN3: mstateen_reg[3] <= legalize_mstateen(3, csr_write_next(mstateen_reg[3], csr_rs1_data, csr_op)); + CSR_SSTATUS: begin + logic [XLEN-1:0] next_sstatus; + next_sstatus = csr_write_next(sstatus_reg, csr_rs1_data, csr_op); + sstatus_reg <= legalize_sstatus(next_sstatus); + mstatus_reg <= apply_sstatus_to_mstatus(mstatus_reg, next_sstatus); + end + CSR_SIE: mie_reg <= (mie_reg & ~S_INTERRUPT_MASK) | + (legalize_interrupt_csr(csr_write_next(sie_value, csr_rs1_data, csr_op)) & + S_INTERRUPT_MASK); + CSR_STVEC: stvec_reg <= csr_write_next(stvec_reg, csr_rs1_data, csr_op); + CSR_SCOUNTEREN: scounteren_reg <= legalize_counteren(csr_write_next(scounteren_reg, csr_rs1_data, csr_op)); + CSR_SENVCFG: senvcfg_reg <= legalize_senvcfg(csr_write_next(senvcfg_reg, csr_rs1_data, csr_op)); + CSR_SSTATEEN0: sstateen_reg[0] <= legalize_sstateen(0, csr_write_next(sstateen_reg[0], csr_rs1_data, csr_op)); + CSR_SSTATEEN1: sstateen_reg[1] <= legalize_sstateen(1, csr_write_next(sstateen_reg[1], csr_rs1_data, csr_op)); + CSR_SSTATEEN2: sstateen_reg[2] <= legalize_sstateen(2, csr_write_next(sstateen_reg[2], csr_rs1_data, csr_op)); + CSR_SSTATEEN3: sstateen_reg[3] <= legalize_sstateen(3, csr_write_next(sstateen_reg[3], csr_rs1_data, csr_op)); + CSR_SSCRATCH: sscratch_reg <= csr_write_next(sscratch_reg, csr_rs1_data, csr_op); + CSR_SEPC: sepc_reg <= csr_write_next(sepc_reg, csr_rs1_data, csr_op); + CSR_SCAUSE: scause_reg <= csr_write_next(scause_reg, csr_rs1_data, csr_op); + CSR_STVAL: stval_reg <= csr_write_next(stval_reg, csr_rs1_data, csr_op); + CSR_SIP: mip_reg <= (mip_reg & ~S_INTERRUPT_MASK) | + (legalize_interrupt_csr(csr_write_next(sip_value, csr_rs1_data, csr_op)) & + S_INTERRUPT_MASK); + CSR_SATP: begin + logic [XLEN-1:0] next_satp; + next_satp = csr_write_next(satp_reg, csr_rs1_data, csr_op); + satp_reg <= legalize_satp(next_satp, satp_reg); + end + CSR_HSTATUS: hstatus_reg <= legalize_hstatus(csr_write_next(hstatus_reg, csr_rs1_data, csr_op)); + CSR_HEDELEG: hedeleg_reg <= legalize_medeleg(csr_write_next(hedeleg_reg, csr_rs1_data, csr_op)); + CSR_HIDELEG: hideleg_reg <= legalize_mideleg(csr_write_next(hideleg_reg, csr_rs1_data, csr_op)); + CSR_HIE: hie_reg <= '0; + CSR_HTIMEDELTA: htimedelta_reg <= csr_write_next(htimedelta_reg, csr_rs1_data, csr_op); + CSR_HCOUNTEREN: hcounteren_reg <= legalize_counteren(csr_write_next(hcounteren_reg, csr_rs1_data, csr_op)); + CSR_HGEIE: hgeie_reg <= '0; + CSR_HENVCFG: henvcfg_reg <= legalize_menvcfg(csr_write_next(henvcfg_reg, csr_rs1_data, csr_op)); + CSR_HSTATEEN0: hstateen_reg[0] <= legalize_mstateen(0, csr_write_next(hstateen_reg[0], csr_rs1_data, csr_op)); + CSR_HSTATEEN1: hstateen_reg[1] <= legalize_mstateen(1, csr_write_next(hstateen_reg[1], csr_rs1_data, csr_op)); + CSR_HSTATEEN2: hstateen_reg[2] <= legalize_mstateen(2, csr_write_next(hstateen_reg[2], csr_rs1_data, csr_op)); + CSR_HSTATEEN3: hstateen_reg[3] <= legalize_mstateen(3, csr_write_next(hstateen_reg[3], csr_rs1_data, csr_op)); + CSR_HTVAL: htval_reg <= csr_write_next(htval_reg, csr_rs1_data, csr_op); + CSR_HIP: hip_reg <= '0; + CSR_HVIP: hvip_reg <= '0; + CSR_HTINST: htinst_reg <= '0; + CSR_HGATP: hgatp_reg <= legalize_hgatp(csr_write_next(hgatp_reg, csr_rs1_data, csr_op), hgatp_reg); + CSR_VSSTATUS: vsstatus_reg <= legalize_sstatus(csr_write_next(vsstatus_reg, csr_rs1_data, csr_op)); + CSR_VSIE: vsie_reg <= '0; + CSR_VSTVEC: vstvec_reg <= csr_write_next(vstvec_reg, csr_rs1_data, csr_op); + CSR_VSSCRATCH: vsscratch_reg <= csr_write_next(vsscratch_reg, csr_rs1_data, csr_op); + CSR_VSEPC: vsepc_reg <= csr_write_next(vsepc_reg, csr_rs1_data, csr_op); + CSR_VSCAUSE: vscause_reg <= csr_write_next(vscause_reg, csr_rs1_data, csr_op); + CSR_VSTVAL: vstval_reg <= csr_write_next(vstval_reg, csr_rs1_data, csr_op); + CSR_VSIP: vsip_reg <= '0; + CSR_VSATP: begin + logic [XLEN-1:0] next_vsatp; + next_vsatp = csr_write_next(vsatp_reg, csr_rs1_data, csr_op); + vsatp_reg <= legalize_satp(next_vsatp, vsatp_reg); + end + CSR_MCYCLE: mcycle_reg <= csr_write_next(mcycle_reg, csr_rs1_data, csr_op); + CSR_MINSTRET: minstret_reg <= csr_write_next(minstret_reg, csr_rs1_data, csr_op); + CSR_FFLAGS: begin + logic [XLEN-1:0] next_flags; + next_flags = csr_write_next({{(XLEN-5){1'b0}}, fflags_reg}, csr_rs1_data, csr_op); + fflags_reg <= next_flags[4:0]; + mstatus_reg <= set_mstatus_fs_dirty(mstatus_reg); + sstatus_reg <= set_sstatus_fs_dirty(sstatus_reg); + end + CSR_FRM: begin + logic [XLEN-1:0] next_frm; + next_frm = csr_write_next({{(XLEN-3){1'b0}}, frm_reg}, csr_rs1_data, csr_op); + frm_reg <= next_frm[2:0]; + mstatus_reg <= set_mstatus_fs_dirty(mstatus_reg); + sstatus_reg <= set_sstatus_fs_dirty(sstatus_reg); + end + CSR_FCSR: begin + logic [XLEN-1:0] next_fcsr; + next_fcsr = csr_write_next(compose_fcsr(), csr_rs1_data, csr_op); + fflags_reg <= next_fcsr[4:0]; + frm_reg <= next_fcsr[7:5]; + mstatus_reg <= set_mstatus_fs_dirty(mstatus_reg); + sstatus_reg <= set_sstatus_fs_dirty(sstatus_reg); + end + default: ; + endcase + end + end + + if (fp_state_dirty) begin + mstatus_reg <= set_mstatus_fs_dirty(mstatus_reg); + sstatus_reg <= set_sstatus_fs_dirty(sstatus_reg); + end + + if (fp_fflags_valid) begin + fflags_reg <= fflags_reg | fp_fflags; + end + + if (trap_taken) begin + trap_vector_q <= trap_vector_target; + trap_to_s_q <= trap_taken_to_s_mode; + // Update privilege-specific CSRs based on whether the trap was delegated. + if (trap_taken_to_s_mode) begin + logic [XLEN-1:0] next_sstatus; + next_sstatus = sstatus_reg; + next_sstatus[SSTATUS_SPIE_BIT] = sstatus_reg[SSTATUS_SIE_BIT]; + next_sstatus[SSTATUS_SIE_BIT] = 1'b0; + next_sstatus[SSTATUS_SPP_BIT] = (priv_mode_q == PRIV_MODE_S); + sstatus_reg <= next_sstatus; + mstatus_reg <= apply_sstatus_to_mstatus(mstatus_reg, next_sstatus); + sepc_reg <= trap_taken_mepc; + scause_reg <= trap_taken_mcause; + stval_reg <= trap_taken_mtval; + priv_mode_q <= PRIV_MODE_S; + end else begin + logic [XLEN-1:0] next_mstatus; + next_mstatus = mstatus_reg; + next_mstatus[MSTATUS_MPIE_BIT] = mstatus_reg[MSTATUS_MIE_BIT]; + next_mstatus[MSTATUS_MIE_BIT] = 1'b0; + next_mstatus[MSTATUS_MPP_HI:MSTATUS_MPP_LO] = encode_priv_mode(priv_mode_q); + mstatus_reg <= legalize_mstatus(next_mstatus); + sstatus_reg <= mirror_mstatus_to_sstatus(sstatus_reg, legalize_mstatus(next_mstatus)); + mepc_reg <= trap_taken_mepc; + mcause_reg <= trap_taken_mcause; + mtval_reg <= trap_taken_mtval; + priv_mode_q <= PRIV_MODE_M; + end + end else begin + if (trap_ctrl_mret_en) begin + priv_mode_t return_mode; + logic [XLEN-1:0] next_mstatus; + return_mode = decode_priv_mode(mstatus_reg[MSTATUS_MPP_HI:MSTATUS_MPP_LO]); + next_mstatus = mstatus_reg; + next_mstatus[MSTATUS_MIE_BIT] = mstatus_reg[MSTATUS_MPIE_BIT]; + next_mstatus[MSTATUS_MPIE_BIT] = 1'b1; + next_mstatus[MSTATUS_MPP_HI:MSTATUS_MPP_LO] = encode_priv_mode(PRIV_MODE_U); + if (return_mode != PRIV_MODE_M) begin + next_mstatus[MSTATUS_MPRV_BIT] = 1'b0; + end + mstatus_reg <= legalize_mstatus(next_mstatus); + sstatus_reg <= mirror_mstatus_to_sstatus(sstatus_reg, legalize_mstatus(next_mstatus)); + priv_mode_q <= return_mode; + end + + if (trap_ctrl_sret_en) begin + logic [XLEN-1:0] next_sstatus; + logic [XLEN-1:0] next_mstatus; + next_sstatus = sstatus_reg; + next_sstatus[SSTATUS_SIE_BIT] = sstatus_reg[SSTATUS_SPIE_BIT]; + next_sstatus[SSTATUS_SPIE_BIT] = 1'b1; + next_sstatus[SSTATUS_SPP_BIT] = 1'b0; + next_mstatus = apply_sstatus_to_mstatus(mstatus_reg, next_sstatus); + next_mstatus[MSTATUS_MPRV_BIT] = 1'b0; + sstatus_reg <= legalize_sstatus(next_sstatus); + mstatus_reg <= legalize_mstatus(next_mstatus); + priv_mode_q <= (sstatus_reg[SSTATUS_SPP_BIT]) ? PRIV_MODE_S : PRIV_MODE_U; + end + end + end + end + + // Map CSR aliases (cycle/instret) to their canonical machine addresses. + function automatic logic [11:0] canonical_addr(input logic [11:0] addr); + case (addr) + CSR_CYCLE: canonical_addr = CSR_MCYCLE; + CSR_INSTRET: canonical_addr = CSR_MINSTRET; + default: canonical_addr = addr; + endcase + endfunction + + function automatic bit csr_is_pmpcfg(input logic [11:0] addr); + csr_is_pmpcfg = (addr >= CSR_PMPCFG0) && (addr <= CSR_PMPCFG15) && (addr[0] == 1'b0); + endfunction + + function automatic bit csr_is_pmpaddr(input logic [11:0] addr); + csr_is_pmpaddr = (addr >= CSR_PMPADDR0) && (addr <= CSR_PMPADDR63); + endfunction + + function automatic bit csr_is_mhpmcounter(input logic [11:0] addr); + csr_is_mhpmcounter = (addr >= CSR_MHPMCOUNTER3) && (addr <= CSR_MHPMCOUNTER31); + endfunction + + function automatic bit csr_is_hpmcounter(input logic [11:0] addr); + csr_is_hpmcounter = (addr >= CSR_HPMCOUNTER3) && (addr <= CSR_HPMCOUNTER31); + endfunction + + function automatic bit csr_is_mhpmevent(input logic [11:0] addr); + csr_is_mhpmevent = (addr >= CSR_MHPMEVENT3) && (addr <= CSR_MHPMEVENT31); + endfunction + + function automatic bit csr_is_counter(input logic [11:0] addr); + return (addr == CSR_CYCLE) || (addr == CSR_TIME) || (addr == CSR_INSTRET) || csr_is_hpmcounter(addr); + endfunction + + function automatic int unsigned counter_index(input logic [11:0] addr); + if (csr_is_hpmcounter(addr)) begin + return int'(addr) - int'(CSR_CYCLE); + end + case (addr) + CSR_CYCLE: return COUNTEREN_CY_BIT; + CSR_TIME: return COUNTEREN_TM_BIT; + CSR_INSTRET: return COUNTEREN_IR_BIT; + default: return COUNTEREN_CY_BIT; + endcase + endfunction + + function automatic int unsigned hpmcounter_index(input logic [11:0] addr); + if (csr_is_hpmcounter(addr)) begin + return int'(addr) - int'(CSR_HPMCOUNTER3); + end + return int'(addr) - int'(CSR_MHPMCOUNTER3); + endfunction + + function automatic int unsigned pmpcfg_csr_base_entry(input logic [11:0] addr); + return (int'(addr) - int'(CSR_PMPCFG0)) * 4; + endfunction + + function automatic int unsigned pmpaddr_csr_entry(input logic [11:0] addr); + return int'(addr) - int'(CSR_PMPADDR0); + endfunction + + function automatic logic [XLEN-1:0] pmpaddr_writable_mask(); + logic [XLEN-1:0] mask; + mask = '0; + for (int bit_idx = 0; bit_idx < (PHYS_ADDR_WIDTH - 2); bit_idx++) begin + mask[bit_idx] = 1'b1; + end + return mask; + endfunction + + function automatic bit pmpcfg_byte_is_legal(input pmpcfg_entry_t cfg); + logic [1:0] address_matching; + address_matching = cfg[PMPCFG_A_HI:PMPCFG_A_LO]; + if (!cfg[PMPCFG_R_BIT] && cfg[PMPCFG_W_BIT]) begin + return 1'b0; + end + if ((PMP_GRANULARITY >= 2) && (address_matching == PMP_MODE_NA4)) begin + return 1'b0; + end + return 1'b1; + endfunction + + function automatic pmpcfg_entry_t legalize_pmpcfg_byte(input pmpcfg_entry_t cfg); + pmpcfg_entry_t legal_cfg; + legal_cfg = cfg; + legal_cfg[6:5] = 2'b00; + return legal_cfg; + endfunction + + function automatic bit pmpaddr_is_locked(input int unsigned entry_idx); + bit locked; + locked = pmpcfg_entries[entry_idx][PMPCFG_L_BIT]; + if (entry_idx < (PMP_ENTRY_COUNT - 1)) begin + locked |= pmpcfg_entries[entry_idx + 1][PMPCFG_L_BIT] && + (pmpcfg_entries[entry_idx + 1][PMPCFG_A_HI:PMPCFG_A_LO] == PMP_MODE_TOR); + end + return locked; + endfunction + + function automatic logic [XLEN-1:0] read_pmpcfg_csr(input logic [11:0] addr); + logic [XLEN-1:0] data; + int unsigned base_entry; + data = '0; + base_entry = pmpcfg_csr_base_entry(addr); + for (int byte_idx = 0; byte_idx < PMP_CFG_ENTRIES_PER_RV64_CSR; byte_idx++) begin + if ((base_entry + byte_idx) < PMP_ENTRY_COUNT) begin + data[(byte_idx * 8) +: 8] = pmpcfg_entries[base_entry + byte_idx]; + end + end + return data; + endfunction + + function automatic logic [XLEN-1:0] read_pmpaddr_csr(input logic [11:0] addr); + return pmpaddr_entries[pmpaddr_csr_entry(addr)] & pmpaddr_writable_mask(); + endfunction + + task automatic write_pmpcfg_csr(input logic [11:0] addr, input logic [XLEN-1:0] write_data); + int unsigned base_entry; + pmpcfg_entry_t next_cfg; + base_entry = pmpcfg_csr_base_entry(addr); + for (int byte_idx = 0; byte_idx < PMP_CFG_ENTRIES_PER_RV64_CSR; byte_idx++) begin + if ((base_entry + byte_idx) < PMP_ENTRY_COUNT) begin + next_cfg = legalize_pmpcfg_byte(write_data[(byte_idx * 8) +: 8]); + if (!pmpcfg_entries[base_entry + byte_idx][PMPCFG_L_BIT] && pmpcfg_byte_is_legal(next_cfg)) begin + pmpcfg_entries[base_entry + byte_idx] <= next_cfg; + end + end + end + endtask + + task automatic write_pmpaddr_csr(input logic [11:0] addr, input logic [XLEN-1:0] write_data); + int unsigned entry_idx; + entry_idx = pmpaddr_csr_entry(addr); + if (!pmpaddr_is_locked(entry_idx)) begin + pmpaddr_entries[entry_idx] <= write_data & pmpaddr_writable_mask(); + end + endtask + + // Apply the CSR op semantics (write/set/clear) for a single register. + function automatic logic [XLEN-1:0] csr_write_next( + input logic [XLEN-1:0] current_data, + input logic [XLEN-1:0] write_data, + input logic [2:0] op_code + ); + logic [XLEN-1:0] next_data; + unique case (op_code) + 3'b001, 3'b101: next_data = write_data; // CSRRW / CSRRWI + 3'b010, 3'b110: next_data = current_data | write_data; // CSRRS / CSRRSI + 3'b011, 3'b111: next_data = current_data & ~write_data; // CSRRC / CSRRCI + default: next_data = current_data; + endcase + return next_data; + endfunction + + function automatic bit csr_is_writable(input logic [11:0] addr); + case (addr) + CSR_MVENDORID, + CSR_MARCHID, + CSR_MIMPID, + CSR_MHARTID, + CSR_MCONFIGPTR, + CSR_TIME: csr_is_writable = 1'b0; + default: csr_is_writable = 1'b1; + endcase + endfunction + + function automatic bit csr_is_read_only_by_encoding(input logic [11:0] addr); + return addr[11:10] == 2'b11; + endfunction + + function automatic bit csr_is_supported(input logic [11:0] addr); + bit supported; + if (csr_is_pmpcfg(addr) || csr_is_pmpaddr(addr) || + csr_is_mhpmcounter(addr) || csr_is_hpmcounter(addr) || csr_is_mhpmevent(addr)) begin + supported = 1'b1; + end else begin + unique case (addr) + CSR_MSTATUS, + CSR_MISA, + CSR_MEDELEG, + CSR_MIDELEG, + CSR_MIE, + CSR_MTVEC, + CSR_MSCRATCH, + CSR_MEPC, + CSR_MCAUSE, + CSR_MTVAL, + CSR_MIP, + CSR_MTINST, + CSR_MTVAL2, + CSR_MCOUNTEREN, + CSR_MCOUNTINHIBIT, + CSR_MENVCFG, + CSR_MSTATEEN0, + CSR_MSTATEEN1, + CSR_MSTATEEN2, + CSR_MSTATEEN3, + CSR_MVENDORID, + CSR_MARCHID, + CSR_MIMPID, + CSR_MHARTID, + CSR_MCONFIGPTR, + CSR_SSTATUS, + CSR_SIE, + CSR_STVEC, + CSR_SCOUNTEREN, + CSR_SENVCFG, + CSR_SSTATEEN0, + CSR_SSTATEEN1, + CSR_SSTATEEN2, + CSR_SSTATEEN3, + CSR_SSCRATCH, + CSR_SEPC, + CSR_SCAUSE, + CSR_STVAL, + CSR_SIP, + CSR_SATP, + CSR_HSTATUS, + CSR_HEDELEG, + CSR_HIDELEG, + CSR_HIE, + CSR_HTIMEDELTA, + CSR_HCOUNTEREN, + CSR_HGEIE, + CSR_HENVCFG, + CSR_HSTATEEN0, + CSR_HSTATEEN1, + CSR_HSTATEEN2, + CSR_HSTATEEN3, + CSR_HTVAL, + CSR_HIP, + CSR_HVIP, + CSR_HTINST, + CSR_HGATP, + CSR_HGEIP, + CSR_VSSTATUS, + CSR_VSIE, + CSR_VSTVEC, + CSR_VSSCRATCH, + CSR_VSEPC, + CSR_VSCAUSE, + CSR_VSTVAL, + CSR_VSIP, + CSR_VSATP, + CSR_MCYCLE, + CSR_MINSTRET, + CSR_CYCLE, + CSR_INSTRET, + CSR_TIME, + CSR_FFLAGS, + CSR_FRM, + CSR_FCSR: + supported = 1'b1; + default: + supported = 1'b0; + endcase + end + return supported; + endfunction + + function automatic bit csr_privilege_allows_access( + input logic [11:0] requested_addr, + input logic [11:0] canonicalized_addr + ); + logic [1:0] minimum_privilege; + minimum_privilege = requested_addr[9:8]; + if (!csr_is_supported(canonicalized_addr)) begin + return 1'b0; + end + if (minimum_privilege == 2'b10) begin + return 1'b0; + end + return encode_priv_mode(priv_mode_q) >= minimum_privilege; + endfunction + + function automatic bit csr_access_blocked_by_stateen( + input logic [11:0] requested_addr, + input logic [11:0] canonicalized_addr + ); + int unsigned current_counter_index; + if (csr_is_counter(requested_addr) && (priv_mode_q != PRIV_MODE_M)) begin + current_counter_index = counter_index(requested_addr); + if (!mcounteren_reg[current_counter_index]) begin + return 1'b1; + end + if ((priv_mode_q == PRIV_MODE_U) && !scounteren_reg[current_counter_index]) begin + return 1'b1; + end + end + if ((canonicalized_addr == CSR_SENVCFG) && (priv_mode_q == PRIV_MODE_S)) begin + return !mstateen_reg[0][STATEEN0_ENVCFG_BIT]; + end + return 1'b0; + endfunction + + function automatic logic [XLEN-1:0] legalize_mstatus(input logic [XLEN-1:0] data); + logic [XLEN-1:0] legal_data; + legal_data = data; + if (legal_data[MSTATUS_MPP_HI:MSTATUS_MPP_LO] == 2'b10) begin + legal_data[MSTATUS_MPP_HI:MSTATUS_MPP_LO] = encode_priv_mode(PRIV_MODE_U); + end + legal_data[MSTATUS_XS_HI:MSTATUS_XS_LO] = 2'b00; + legal_data[MSTATUS_UXL_HI:MSTATUS_UXL_LO] = STATUS_XLEN_64; + legal_data[MSTATUS_SXL_HI:MSTATUS_SXL_LO] = STATUS_XLEN_64; + legal_data[MSTATUS_SD_BIT] = (legal_data[MSTATUS_FS_HI:MSTATUS_FS_LO] == 2'b11) || + (legal_data[MSTATUS_XS_HI:MSTATUS_XS_LO] == 2'b11) || + (legal_data[MSTATUS_VS_HI:MSTATUS_VS_LO] == 2'b11); + return legal_data; + endfunction + + function automatic logic [XLEN-1:0] legalize_sstatus(input logic [XLEN-1:0] data); + logic [XLEN-1:0] legal_data; + legal_data = '0; + legal_data[SSTATUS_SIE_BIT] = data[SSTATUS_SIE_BIT]; + legal_data[SSTATUS_SPIE_BIT] = data[SSTATUS_SPIE_BIT]; + legal_data[SSTATUS_SPP_BIT] = data[SSTATUS_SPP_BIT]; + legal_data[SSTATUS_VS_HI:SSTATUS_VS_LO] = data[SSTATUS_VS_HI:SSTATUS_VS_LO]; + legal_data[SSTATUS_FS_HI:SSTATUS_FS_LO] = data[SSTATUS_FS_HI:SSTATUS_FS_LO]; + legal_data[SSTATUS_SUM_BIT] = data[SSTATUS_SUM_BIT]; + legal_data[SSTATUS_MXR_BIT] = data[SSTATUS_MXR_BIT]; + legal_data[SSTATUS_UXL_HI:SSTATUS_UXL_LO] = STATUS_XLEN_64; + legal_data[SSTATUS_SD_BIT] = (legal_data[SSTATUS_FS_HI:SSTATUS_FS_LO] == 2'b11) || + (legal_data[SSTATUS_XS_HI:SSTATUS_XS_LO] == 2'b11) || + (legal_data[SSTATUS_VS_HI:SSTATUS_VS_LO] == 2'b11); + return legal_data; + endfunction + + function automatic logic [XLEN-1:0] reset_mstatus_value(); + return legalize_mstatus('0); + endfunction + + function automatic logic [XLEN-1:0] reset_sstatus_value(); + return legalize_sstatus('0); + endfunction + + function automatic logic [XLEN-1:0] set_mstatus_fs_dirty(input logic [XLEN-1:0] current); + logic [XLEN-1:0] next_data; + next_data = current; + next_data[MSTATUS_FS_HI:MSTATUS_FS_LO] = 2'b11; + return legalize_mstatus(next_data); + endfunction + + function automatic logic [XLEN-1:0] set_sstatus_fs_dirty(input logic [XLEN-1:0] current); + logic [XLEN-1:0] next_data; + next_data = current; + next_data[SSTATUS_FS_HI:SSTATUS_FS_LO] = 2'b11; + return legalize_sstatus(next_data); + endfunction + + function automatic logic [XLEN-1:0] legalize_medeleg(input logic [XLEN-1:0] data); + return (data & MEDELEG_WRITABLE_MASK) | MEDELEG_READ_ONLY_ONE_MASK; + endfunction + + function automatic logic [XLEN-1:0] legalize_mideleg(input logic [XLEN-1:0] data); + return data & MIDELEG_WRITABLE_MASK; + endfunction + + function automatic logic [XLEN-1:0] legalize_interrupt_csr(input logic [XLEN-1:0] data); + return data & INTERRUPT_MASK; + endfunction + + function automatic logic [XLEN-1:0] compose_mip(); + logic [XLEN-1:0] pending; + pending = legalize_interrupt_csr(mip_reg); + pending[INTERRUPT_M_SOFTWARE_BIT] |= m_software_interrupt; + pending[INTERRUPT_M_TIMER_BIT] |= m_timer_interrupt; + pending[INTERRUPT_M_EXTERNAL_BIT] |= m_external_interrupt; + pending[INTERRUPT_S_SOFTWARE_BIT] |= s_software_interrupt; + pending[INTERRUPT_S_TIMER_BIT] |= s_timer_interrupt; + pending[INTERRUPT_S_EXTERNAL_BIT] |= s_external_interrupt; + return pending; + endfunction + + function automatic logic [XLEN-1:0] legalize_mcountinhibit(input logic [XLEN-1:0] data); + logic [XLEN-1:0] legal_data; + legal_data = '0; + legal_data[MCOUNTINHIBIT_CY_BIT] = data[MCOUNTINHIBIT_CY_BIT]; + legal_data[MCOUNTINHIBIT_IR_BIT] = data[MCOUNTINHIBIT_IR_BIT]; + legal_data[31:3] = data[31:3]; + return legal_data; + endfunction + + function automatic logic [XLEN-1:0] legalize_counteren(input logic [XLEN-1:0] data); + logic [XLEN-1:0] legal_data; + legal_data = '0; + legal_data[COUNTEREN_CY_BIT] = data[COUNTEREN_CY_BIT]; + legal_data[COUNTEREN_TM_BIT] = data[COUNTEREN_TM_BIT]; + legal_data[COUNTEREN_IR_BIT] = data[COUNTEREN_IR_BIT]; + legal_data[31:3] = data[31:3]; + return legal_data; + endfunction + + function automatic logic [XLEN-1:0] legalize_senvcfg(input logic [XLEN-1:0] data); + logic [XLEN-1:0] legal_data; + legal_data = '0; + legal_data[ENVCFG_FIOM_BIT] = data[ENVCFG_FIOM_BIT]; + legal_data[ENVCFG_CBIE_HI:ENVCFG_CBIE_LO] = + menvcfg_reg[ENVCFG_CBIE_HI:ENVCFG_CBIE_LO] & data[ENVCFG_CBIE_HI:ENVCFG_CBIE_LO]; + legal_data[ENVCFG_CBCFE_BIT] = menvcfg_reg[ENVCFG_CBCFE_BIT] & data[ENVCFG_CBCFE_BIT]; + legal_data[ENVCFG_CBZE_BIT] = menvcfg_reg[ENVCFG_CBZE_BIT] & data[ENVCFG_CBZE_BIT]; + return legal_data; + endfunction + + function automatic logic [XLEN-1:0] legalize_menvcfg(input logic [XLEN-1:0] data); + logic [XLEN-1:0] legal_data; + legal_data = '0; + legal_data[ENVCFG_FIOM_BIT] = data[ENVCFG_FIOM_BIT]; + legal_data[ENVCFG_CBIE_HI:ENVCFG_CBIE_LO] = data[ENVCFG_CBIE_HI:ENVCFG_CBIE_LO]; + legal_data[ENVCFG_CBCFE_BIT] = data[ENVCFG_CBCFE_BIT]; + legal_data[ENVCFG_CBZE_BIT] = data[ENVCFG_CBZE_BIT]; + return legal_data; + endfunction + + function automatic logic [XLEN-1:0] legalize_hstatus(input logic [XLEN-1:0] data); + logic [XLEN-1:0] legal_data; + legal_data = '0; + legal_data[6] = data[6]; // VTSR + legal_data[7] = data[7]; // VTW + legal_data[8] = data[8]; // VTVM + legal_data[12] = data[12]; // SPVP + legal_data[14] = data[14]; // SPV + legal_data[16:15] = data[16:15]; // GVA and VSBE are WARL here. + legal_data[33:32] = STATUS_XLEN_64; // VSXL + return legal_data; + endfunction + + function automatic logic [XLEN-1:0] legalize_mstateen(input int unsigned index, input logic [XLEN-1:0] data); + logic [XLEN-1:0] legal_data; + legal_data = '0; + if (index == 0) begin + legal_data[STATEEN0_ENVCFG_BIT] = data[STATEEN0_ENVCFG_BIT]; + end + legal_data[STATEEN_SE0_BIT] = data[STATEEN_SE0_BIT]; + return legal_data; + endfunction + + function automatic logic [XLEN-1:0] legalize_sstateen(input int unsigned index, input logic [XLEN-1:0] data); + logic [XLEN-1:0] legal_data; + legal_data = '0; + if (index == 0) begin + legal_data[STATEEN0_ENVCFG_BIT] = data[STATEEN0_ENVCFG_BIT] & mstateen_reg[0][STATEEN0_ENVCFG_BIT]; + end + legal_data[STATEEN_SE0_BIT] = data[STATEEN_SE0_BIT] & mstateen_reg[index][STATEEN_SE0_BIT]; + return legal_data; + endfunction + + function automatic logic [XLEN-1:0] legalize_satp( + input logic [XLEN-1:0] data, + input logic [XLEN-1:0] current + ); + logic [XLEN-1:0] legal_data; + logic [3:0] mode; + mode = data[SATP_MODE_HI:SATP_MODE_LO]; + legal_data = current; + + unique case (mode) + SATP_MODE_BARE: begin + legal_data = '0; + end + SATP_MODE_SV39: begin + legal_data = '0; + legal_data[SATP_MODE_HI:SATP_MODE_LO] = SATP_MODE_SV39; + legal_data[SATP_ASID_HI:SATP_ASID_LO] = data[SATP_ASID_HI:SATP_ASID_LO]; + legal_data[SATP_PPN_HI:SATP_PPN_LO] = data[SATP_PPN_HI:SATP_PPN_LO]; + end + SATP_MODE_SV48, + SATP_MODE_SV57, + SATP_MODE_SV64: begin + legal_data = current; + end + default: begin + legal_data = current; + end + endcase + + return legal_data; + endfunction + + function automatic logic [XLEN-1:0] legalize_hgatp( + input logic [XLEN-1:0] data, + input logic [XLEN-1:0] current + ); + logic [XLEN-1:0] legal_data; + logic [3:0] mode; + mode = data[SATP_MODE_HI:SATP_MODE_LO]; + legal_data = current; + + unique case (mode) + SATP_MODE_BARE: legal_data = '0; + SATP_MODE_SV39: legal_data = data; + SATP_MODE_SV48, + SATP_MODE_SV57, + SATP_MODE_SV64: legal_data = current; + default: legal_data = current; + endcase + + return legal_data; + endfunction + + function automatic logic [XLEN-1:0] apply_sstatus_to_mstatus( + input logic [XLEN-1:0] mstatus_cur, + input logic [XLEN-1:0] sstatus_data + ); + logic [XLEN-1:0] next_data; + next_data = mstatus_cur; + next_data[SSTATUS_SIE_BIT] = sstatus_data[SSTATUS_SIE_BIT]; + next_data[SSTATUS_SPIE_BIT] = sstatus_data[SSTATUS_SPIE_BIT]; + next_data[SSTATUS_SPP_BIT] = sstatus_data[SSTATUS_SPP_BIT]; + next_data[SSTATUS_VS_HI:SSTATUS_VS_LO] = sstatus_data[SSTATUS_VS_HI:SSTATUS_VS_LO]; + next_data[SSTATUS_FS_HI:SSTATUS_FS_LO] = sstatus_data[SSTATUS_FS_HI:SSTATUS_FS_LO]; + next_data[SSTATUS_SUM_BIT] = sstatus_data[SSTATUS_SUM_BIT]; + next_data[SSTATUS_MXR_BIT] = sstatus_data[SSTATUS_MXR_BIT]; + next_data[MSTATUS_UXL_HI:MSTATUS_UXL_LO] = STATUS_XLEN_64; + return legalize_mstatus(next_data); + endfunction + + function automatic logic [XLEN-1:0] mirror_mstatus_to_sstatus( + input logic [XLEN-1:0] sstatus_cur, + input logic [XLEN-1:0] mstatus_data + ); + logic [XLEN-1:0] next_data; + next_data = legalize_sstatus(sstatus_cur); + next_data[SSTATUS_SIE_BIT] = mstatus_data[SSTATUS_SIE_BIT]; + next_data[SSTATUS_SPIE_BIT] = mstatus_data[SSTATUS_SPIE_BIT]; + next_data[SSTATUS_SPP_BIT] = mstatus_data[SSTATUS_SPP_BIT]; + next_data[SSTATUS_VS_HI:SSTATUS_VS_LO] = mstatus_data[MSTATUS_VS_HI:MSTATUS_VS_LO]; + next_data[SSTATUS_FS_HI:SSTATUS_FS_LO] = mstatus_data[SSTATUS_FS_HI:SSTATUS_FS_LO]; + next_data[SSTATUS_SUM_BIT] = mstatus_data[SSTATUS_SUM_BIT]; + next_data[SSTATUS_MXR_BIT] = mstatus_data[MSTATUS_MXR_BIT]; + next_data[SSTATUS_UXL_HI:SSTATUS_UXL_LO] = STATUS_XLEN_64; + next_data[SSTATUS_SD_BIT] = mstatus_data[MSTATUS_SD_BIT]; + return legalize_sstatus(next_data); + endfunction + + // Pack the discrete FP status bits into the architectural FCSR layout. + function automatic logic [XLEN-1:0] compose_fcsr(); + logic [XLEN-1:0] value; + value = '0; + value[4:0] = fflags_reg; + value[7:5] = frm_reg; + return value; + endfunction + + function automatic priv_mode_t decode_priv_mode(input logic [1:0] encoded); + case (encoded) + 2'b00: decode_priv_mode = PRIV_MODE_U; + 2'b01: decode_priv_mode = PRIV_MODE_S; + default: decode_priv_mode = PRIV_MODE_M; + endcase + endfunction + + function automatic logic [1:0] encode_priv_mode(input priv_mode_t mode); + case (mode) + PRIV_MODE_U: encode_priv_mode = 2'b00; + PRIV_MODE_S: encode_priv_mode = 2'b01; + default: encode_priv_mode = 2'b11; + endcase + endfunction + + function automatic logic [XLEN-1:0] read_csr(input logic [11:0] addr); + logic [XLEN-1:0] data_out; + if (csr_is_pmpcfg(addr)) begin + data_out = read_pmpcfg_csr(addr); + end else if (csr_is_pmpaddr(addr)) begin + data_out = read_pmpaddr_csr(addr); + end else if (csr_is_mhpmcounter(addr) || csr_is_hpmcounter(addr)) begin + data_out = mhpmcounter_reg[hpmcounter_index(addr)]; + end else if (csr_is_mhpmevent(addr)) begin + data_out = '0; + end else begin + unique case (addr) + CSR_FFLAGS: data_out = {{(XLEN-5){1'b0}}, fflags_reg}; + CSR_FRM: data_out = {{(XLEN-3){1'b0}}, frm_reg}; + CSR_FCSR: data_out = compose_fcsr(); + CSR_MSTATUS: data_out = mstatus_reg; + CSR_MISA: data_out = MISA_VALUE; + CSR_MEDELEG: data_out = medeleg_reg; + CSR_MIDELEG: data_out = mideleg_reg; + CSR_MIE: data_out = mie_reg; + CSR_MTVEC: data_out = mtvec_reg; + CSR_MSCRATCH: data_out = mscratch_reg; + CSR_MEPC: data_out = mepc_reg; + CSR_MCAUSE: data_out = mcause_reg; + CSR_MTVAL: data_out = mtval_reg; + CSR_MIP: data_out = mip_value; + CSR_MTINST: data_out = mtinst_reg; + CSR_MTVAL2: data_out = mtval2_reg; + CSR_MCOUNTEREN: data_out = mcounteren_reg; + CSR_MCOUNTINHIBIT: data_out = mcountinhibit_reg; + CSR_MENVCFG: data_out = menvcfg_reg; + CSR_MSTATEEN0: data_out = mstateen_reg[0]; + CSR_MSTATEEN1: data_out = mstateen_reg[1]; + CSR_MSTATEEN2: data_out = mstateen_reg[2]; + CSR_MSTATEEN3: data_out = mstateen_reg[3]; + CSR_MVENDORID: data_out = VENDOR_ID; + CSR_MARCHID: data_out = ARCH_ID; + CSR_MIMPID: data_out = IMPL_ID; + CSR_MHARTID: data_out = HART_ID; + CSR_MCONFIGPTR: data_out = '0; + CSR_SSTATUS: data_out = sstatus_reg; + CSR_SIE: data_out = sie_value; + CSR_STVEC: data_out = stvec_reg; + CSR_SCOUNTEREN: data_out = scounteren_reg; + CSR_SENVCFG: data_out = legalize_senvcfg(senvcfg_reg); + CSR_SSTATEEN0: data_out = sstateen_reg[0]; + CSR_SSTATEEN1: data_out = sstateen_reg[1]; + CSR_SSTATEEN2: data_out = sstateen_reg[2]; + CSR_SSTATEEN3: data_out = sstateen_reg[3]; + CSR_SSCRATCH: data_out = sscratch_reg; + CSR_SEPC: data_out = sepc_reg; + CSR_SCAUSE: data_out = scause_reg; + CSR_STVAL: data_out = stval_reg; + CSR_SIP: data_out = sip_value; + CSR_SATP: data_out = satp_reg; + CSR_HSTATUS: data_out = hstatus_reg; + CSR_HEDELEG: data_out = hedeleg_reg; + CSR_HIDELEG: data_out = hideleg_reg; + CSR_HIE: data_out = hie_reg; + CSR_HTIMEDELTA: data_out = htimedelta_reg; + CSR_HCOUNTEREN: data_out = hcounteren_reg; + CSR_HGEIE: data_out = hgeie_reg; + CSR_HENVCFG: data_out = henvcfg_reg; + CSR_HSTATEEN0: data_out = hstateen_reg[0]; + CSR_HSTATEEN1: data_out = hstateen_reg[1]; + CSR_HSTATEEN2: data_out = hstateen_reg[2]; + CSR_HSTATEEN3: data_out = hstateen_reg[3]; + CSR_HTVAL: data_out = htval_reg; + CSR_HIP: data_out = hip_reg; + CSR_HVIP: data_out = hvip_reg; + CSR_HTINST: data_out = htinst_reg; + CSR_HGATP: data_out = hgatp_reg; + CSR_HGEIP: data_out = '0; + CSR_VSSTATUS: data_out = vsstatus_reg; + CSR_VSIE: data_out = vsie_reg; + CSR_VSTVEC: data_out = vstvec_reg; + CSR_VSSCRATCH: data_out = vsscratch_reg; + CSR_VSEPC: data_out = vsepc_reg; + CSR_VSCAUSE: data_out = vscause_reg; + CSR_VSTVAL: data_out = vstval_reg; + CSR_VSIP: data_out = vsip_reg; + CSR_VSATP: data_out = vsatp_reg; + CSR_MCYCLE: data_out = mcycle_reg; + CSR_MINSTRET: data_out = minstret_reg; + CSR_CYCLE: data_out = mcycle_reg; + CSR_INSTRET: data_out = minstret_reg; + CSR_TIME: data_out = time_reg; + default: data_out = '0; + endcase + end + return data_out; + endfunction + +endmodule diff --git a/rv64cpu/rtl/core/privileged/pmp_checker.sv b/rv64cpu/rtl/core/privileged/pmp_checker.sv new file mode 100644 index 0000000..361e507 --- /dev/null +++ b/rv64cpu/rtl/core/privileged/pmp_checker.sv @@ -0,0 +1,181 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/pmp_checker.sv +// Description: RISC-V Physical Memory Protection access checker. + +module pmp_checker + import rv64_pkg::*; + import csr_pkg::*; +#( + parameter int PHYS_ADDR_WIDTH = 56 +)( + input logic req_valid, + input logic [XLEN-1:0] req_paddr, + input logic [3:0] req_size, + input logic req_is_fetch, + input logic req_is_store, + input logic [1:0] priv_mode, + input pmpcfg_entry_t pmpcfg_entries [PMP_ENTRY_COUNT], + input pmpaddr_entry_t pmpaddr_entries [PMP_ENTRY_COUNT], + output logic access_fault +); + + localparam logic [1:0] PRIV_MODE_M = 2'b11; + + typedef struct packed { + logic valid; + logic [XLEN-1:0] lower_bound; + logic [XLEN-1:0] upper_bound; + } pmp_region_t; + + function automatic logic [XLEN-1:0] pmpaddr_to_paddr(input pmpaddr_entry_t pmpaddr); + logic [XLEN-1:0] paddr; + paddr = pmpaddr << 2; + if (PHYS_ADDR_WIDTH < XLEN) begin + paddr[XLEN-1:PHYS_ADDR_WIDTH] = '0; + end + return paddr; + endfunction + + function automatic int unsigned napot_trailing_ones(input pmpaddr_entry_t pmpaddr); + int unsigned ones; + ones = 0; + for (int bit_idx = 0; bit_idx < (PHYS_ADDR_WIDTH - 2); bit_idx++) begin + if (pmpaddr[bit_idx]) begin + ones++; + end else begin + break; + end + end + return ones; + endfunction + + function automatic pmp_region_t pmp_region(input int unsigned entry_idx); + pmp_region_t region; + logic [1:0] address_matching; + int unsigned ones; + logic [XLEN-1:0] napot_mask; + logic [XLEN-1:0] napot_base; + + region.valid = 1'b0; + region.lower_bound = '0; + region.upper_bound = '0; + address_matching = pmpcfg_entries[entry_idx][PMPCFG_A_HI:PMPCFG_A_LO]; + + unique case (address_matching) + PMP_MODE_OFF: begin + region.valid = 1'b0; + end + PMP_MODE_TOR: begin + region.valid = 1'b1; + region.lower_bound = (entry_idx == 0) ? '0 : pmpaddr_to_paddr(pmpaddr_entries[entry_idx - 1]); + region.upper_bound = pmpaddr_to_paddr(pmpaddr_entries[entry_idx]); + if (region.upper_bound <= region.lower_bound) begin + region.valid = 1'b0; + end + end + PMP_MODE_NA4: begin + region.valid = 1'b1; + region.lower_bound = pmpaddr_to_paddr(pmpaddr_entries[entry_idx]); + region.upper_bound = region.lower_bound + 64'd4; + end + PMP_MODE_NAPOT: begin + region.valid = 1'b1; + ones = napot_trailing_ones(pmpaddr_entries[entry_idx]); + napot_mask = (64'd1 << (ones + 1)) - 64'd1; + napot_base = (pmpaddr_entries[entry_idx] & ~napot_mask) << 2; + if (PHYS_ADDR_WIDTH < XLEN) begin + napot_base[XLEN-1:PHYS_ADDR_WIDTH] = '0; + end + region.lower_bound = napot_base; + region.upper_bound = napot_base + (64'd1 << (ones + 3)); + end + default: begin + region.valid = 1'b0; + end + endcase + + return region; + endfunction + + function automatic logic ranges_overlap( + input logic [XLEN-1:0] access_lower, + input logic [XLEN-1:0] access_upper, + input logic [XLEN-1:0] region_lower, + input logic [XLEN-1:0] region_upper + ); + return (access_lower < region_upper) && (access_upper > region_lower); + endfunction + + function automatic logic range_contains( + input logic [XLEN-1:0] access_lower, + input logic [XLEN-1:0] access_upper, + input logic [XLEN-1:0] region_lower, + input logic [XLEN-1:0] region_upper + ); + return (access_lower >= region_lower) && (access_upper <= region_upper); + endfunction + + function automatic logic permissions_allow( + input pmpcfg_entry_t cfg, + input logic is_fetch, + input logic is_store + ); + if (is_fetch) begin + return cfg[PMPCFG_X_BIT]; + end + if (is_store) begin + return cfg[PMPCFG_W_BIT]; + end + return cfg[PMPCFG_R_BIT]; + endfunction + + always_comb begin + logic [XLEN-1:0] access_lower; + logic [XLEN-1:0] access_upper; + logic [3:0] normalized_size; + logic matched; + pmp_region_t region; + + normalized_size = (req_size == 4'd0) ? 4'd1 : req_size; + access_lower = req_paddr; + access_upper = req_paddr + {{(XLEN-4){1'b0}}, normalized_size}; + matched = 1'b0; + region = '0; + access_fault = 1'b0; + + if (req_valid) begin + for (int unsigned entry_idx = 0; entry_idx < PMP_ENTRY_COUNT; entry_idx++) begin + region = pmp_region(entry_idx); + if (!matched && region.valid && + ranges_overlap(access_lower, access_upper, region.lower_bound, region.upper_bound)) begin + matched = 1'b1; + if (!range_contains(access_lower, access_upper, region.lower_bound, region.upper_bound)) begin + access_fault = 1'b1; + end else if ((priv_mode != PRIV_MODE_M) || pmpcfg_entries[entry_idx][PMPCFG_L_BIT]) begin + access_fault = !permissions_allow(pmpcfg_entries[entry_idx], req_is_fetch, req_is_store); + end + end + end + + if (!matched && (priv_mode != PRIV_MODE_M)) begin + access_fault = 1'b1; + end + end + end + +endmodule diff --git a/rv64cpu/rtl/core/privileged/trap_ctrl.sv b/rv64cpu/rtl/core/privileged/trap_ctrl.sv new file mode 100644 index 0000000..0969fdc --- /dev/null +++ b/rv64cpu/rtl/core/privileged/trap_ctrl.sv @@ -0,0 +1,279 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/trap_ctrl.sv +// Description: Privileged-level Trap Controller (Exception and Interrupt Handling) +// Detects exceptions/interrupts, triggers context saving, and generates global pipeline flush and PC jump signals + +module trap_ctrl + import rv64_pkg::*; + import csr_pkg::*; +( + // Submission signals from the WB stage (guarantee of precise exceptions) + input logic [XLEN-1:0] wb_pc, + input logic trap_illegal_instr, + input logic trap_is_ecall, + input logic trap_is_ebreak, + input logic trap_is_mret, + input logic trap_is_sret, + input logic trap_instr_access_fault, + input logic trap_load_fault, + input logic trap_store_fault, + input logic trap_instr_page_fault, + input logic trap_load_page_fault, + input logic trap_store_page_fault, + input logic [XLEN-1:0] trap_bad_addr, + input logic [INST_WIDTH-1:0] trap_bad_instr, + + // Status read from CSR Unit + input logic csr_mstatus_mie, // Global interrupt enable (MIE) + input logic [XLEN-1:0] csr_mie, // Machine interrupt enable CSR + input logic [XLEN-1:0] csr_sie, // Supervisor interrupt enable CSR view + input logic [XLEN-1:0] csr_mip, // Machine interrupt pending CSR + input logic [XLEN-1:0] csr_sip, // Supervisor interrupt pending CSR view + input logic [XLEN-1:0] csr_mstatus, // Machine status CSR + input logic [XLEN-1:0] csr_mtvec, // Machine trap vector + input logic [XLEN-1:0] csr_stvec, // Supervisor trap vector + input logic [XLEN-1:0] csr_mepc, // Machine exception PC (for MRET) + input logic [XLEN-1:0] csr_sepc, // Supervisor exception PC (for SRET) + input logic [XLEN-1:0] csr_medeleg, // Exception delegation mask + input logic [XLEN-1:0] csr_mideleg, // Interrupt delegation mask + input logic [1:0] csr_priv_mode, // Current privilege level + + // Status update signal sent to the CSR Unit + output logic trap_trigger, // Trigger exception/interrupt context save + output logic [XLEN-1:0] trap_mepc, // Saved PC (MEPC/SEPC selected by trap_to_s_mode) + output logic [XLEN-1:0] trap_mcause, // Cause code (bit63 indicates interrupt) + output logic [XLEN-1:0] trap_mtval, // Additional information saved to mtval/stval + output logic trap_to_s_mode, // 1: delegated to Supervisor mode + output logic mret_en, // Trigger MRET context restore + output logic sret_en, // Trigger SRET context restore + + // Control flow interference sent to the Hazard Unit and IF stage + output logic trap_flush_req, // Global pipeline flush request + output logic trap_pc_sel, // Forcibly take over PC MUX (0: normal, 1: Trap/Mret/Sret) + output logic [XLEN-1:0] trap_next_pc // Target address for forced jump +); + + typedef enum logic [1:0] { + PRIV_MODE_U = 2'b00, + PRIV_MODE_S = 2'b01, + PRIV_MODE_M = 2'b11 + } priv_mode_t; + + localparam logic [XLEN-1:0] INTERRUPT_BIT = {1'b1, {(XLEN-1){1'b0}}}; + localparam int INTERRUPT_M_SOFTWARE_BIT = int'(INTERRUPT_M_SOFTWARE); + localparam int INTERRUPT_M_TIMER_BIT = int'(INTERRUPT_M_TIMER); + localparam int INTERRUPT_M_EXTERNAL_BIT = int'(INTERRUPT_M_EXTERNAL); + localparam int INTERRUPT_S_SOFTWARE_BIT = int'(INTERRUPT_S_SOFTWARE); + localparam int INTERRUPT_S_TIMER_BIT = int'(INTERRUPT_S_TIMER); + localparam int INTERRUPT_S_EXTERNAL_BIT = int'(INTERRUPT_S_EXTERNAL); + localparam int MSTATUS_SIE_BIT = 1; + localparam int MSTATUS_TSR_BIT = 22; + + logic has_sync_exception; + logic [XLEN-1:0] sync_cause; + logic [XLEN-1:0] sync_mtval; + logic [XLEN-1:0] illegal_instr_value; + logic sret_tsr_illegal; + + assign illegal_instr_value = {{(XLEN-INST_WIDTH){1'b0}}, trap_bad_instr}; + assign sret_tsr_illegal = trap_is_sret && + (priv_mode_t'(csr_priv_mode) == PRIV_MODE_S) && + csr_mstatus[MSTATUS_TSR_BIT]; + + always_comb begin + has_sync_exception = 1'b0; + sync_cause = '0; + sync_mtval = '0; + + if (trap_instr_access_fault) begin + has_sync_exception = 1'b1; + sync_cause = EXC_INST_FAULT; + sync_mtval = trap_bad_addr; + end else if (trap_instr_page_fault) begin + has_sync_exception = 1'b1; + sync_cause = EXC_INSTR_PAGE_FAULT; + sync_mtval = trap_bad_addr; + end else if (trap_illegal_instr || sret_tsr_illegal) begin + has_sync_exception = 1'b1; + sync_cause = EXC_INST_ILLEGAL; + sync_mtval = illegal_instr_value; + end else if (trap_is_ebreak) begin + has_sync_exception = 1'b1; + sync_cause = EXC_BREAKPOINT; + sync_mtval = wb_pc; + end else if (trap_is_ecall) begin + has_sync_exception = 1'b1; + case (priv_mode_t'(csr_priv_mode)) + PRIV_MODE_U: sync_cause = EXC_ECALL_FROM_U; + PRIV_MODE_S: sync_cause = EXC_ECALL_FROM_S; + default: sync_cause = EXC_ECALL_FROM_M; + endcase + sync_mtval = '0; + end else if (trap_load_fault) begin + has_sync_exception = 1'b1; + sync_cause = EXC_LOAD_FAULT; + sync_mtval = trap_bad_addr; + end else if (trap_store_fault) begin + has_sync_exception = 1'b1; + sync_cause = EXC_STORE_FAULT; + sync_mtval = trap_bad_addr; + end else if (trap_load_page_fault) begin + has_sync_exception = 1'b1; + sync_cause = EXC_LOAD_PAGE_FAULT; + sync_mtval = trap_bad_addr; + end else if (trap_store_page_fault) begin + has_sync_exception = 1'b1; + sync_cause = EXC_STORE_PAGE_FAULT; + sync_mtval = trap_bad_addr; + end + end + + // Interrupt Detection - Asynchronous, gated by mie and privilege global-enable rules. + logic has_interrupt; + logic [XLEN-1:0] interrupt_cause; + logic machine_interrupts_globally_enabled; + logic supervisor_interrupts_globally_enabled; + logic interrupt_targets_s_mode; + + assign machine_interrupts_globally_enabled = (priv_mode_t'(csr_priv_mode) != PRIV_MODE_M) || + csr_mstatus_mie; + assign supervisor_interrupts_globally_enabled = (priv_mode_t'(csr_priv_mode) == PRIV_MODE_U) || + ((priv_mode_t'(csr_priv_mode) == PRIV_MODE_S) && + csr_mstatus[MSTATUS_SIE_BIT]); + + function automatic logic interrupt_pending_enabled( + input logic [XLEN-1:0] pending, + input logic [XLEN-1:0] enable, + input int cause + ); + interrupt_pending_enabled = pending[cause[5:0]] && enable[cause[5:0]]; + endfunction + + task automatic take_interrupt( + input logic [XLEN-1:0] cause, + input logic to_s_mode, + output logic taken, + output logic [XLEN-1:0] taken_cause, + output logic taken_targets_s_mode + ); + taken = 1'b1; + taken_cause = INTERRUPT_BIT | cause; + taken_targets_s_mode = to_s_mode; + endtask + + function automatic logic s_interrupt_cause_delegated(input int cause); + s_interrupt_cause_delegated = csr_mideleg[cause[5:0]]; + endfunction + + function automatic logic s_interrupt_cause_takeable(input int cause); + if (s_interrupt_cause_delegated(cause)) begin + s_interrupt_cause_takeable = supervisor_interrupts_globally_enabled && + interrupt_pending_enabled(csr_sip, csr_sie, cause); + end else begin + s_interrupt_cause_takeable = machine_interrupts_globally_enabled && + interrupt_pending_enabled(csr_mip, csr_mie, cause); + end + endfunction + + always_comb begin + has_interrupt = 1'b0; + interrupt_cause = '0; + interrupt_targets_s_mode = 1'b0; + + if (machine_interrupts_globally_enabled && + interrupt_pending_enabled(csr_mip, csr_mie, INTERRUPT_M_EXTERNAL_BIT)) begin + take_interrupt(INTERRUPT_M_EXTERNAL, 1'b0, has_interrupt, interrupt_cause, interrupt_targets_s_mode); + end else if (machine_interrupts_globally_enabled && + interrupt_pending_enabled(csr_mip, csr_mie, INTERRUPT_M_SOFTWARE_BIT)) begin + take_interrupt(INTERRUPT_M_SOFTWARE, 1'b0, has_interrupt, interrupt_cause, interrupt_targets_s_mode); + end else if (machine_interrupts_globally_enabled && + interrupt_pending_enabled(csr_mip, csr_mie, INTERRUPT_M_TIMER_BIT)) begin + take_interrupt(INTERRUPT_M_TIMER, 1'b0, has_interrupt, interrupt_cause, interrupt_targets_s_mode); + end else if (s_interrupt_cause_takeable(INTERRUPT_S_EXTERNAL_BIT)) begin + take_interrupt(INTERRUPT_S_EXTERNAL, s_interrupt_cause_delegated(INTERRUPT_S_EXTERNAL_BIT), + has_interrupt, interrupt_cause, interrupt_targets_s_mode); + end else if (s_interrupt_cause_takeable(INTERRUPT_S_SOFTWARE_BIT)) begin + take_interrupt(INTERRUPT_S_SOFTWARE, s_interrupt_cause_delegated(INTERRUPT_S_SOFTWARE_BIT), + has_interrupt, interrupt_cause, interrupt_targets_s_mode); + end else if (s_interrupt_cause_takeable(INTERRUPT_S_TIMER_BIT)) begin + take_interrupt(INTERRUPT_S_TIMER, s_interrupt_cause_delegated(INTERRUPT_S_TIMER_BIT), + has_interrupt, interrupt_cause, interrupt_targets_s_mode); + end + end + + logic [XLEN-1:0] trap_cause_value; + logic [XLEN-1:0] trap_mtval_value; + logic trap_is_interrupt; + + assign trap_is_interrupt = has_interrupt; + assign trap_cause_value = has_interrupt ? interrupt_cause : sync_cause; + assign trap_mtval_value = has_interrupt ? '0 : sync_mtval; + + assign trap_trigger = has_interrupt | has_sync_exception; + + logic [5:0] trap_cause_index; + logic delegate_to_s; + logic [XLEN-1:0] deleg_mask; + logic machine_interrupt_cause; + + assign trap_cause_index = trap_cause_value[5:0]; + assign deleg_mask = trap_is_interrupt ? csr_mideleg : csr_medeleg; + assign machine_interrupt_cause = trap_is_interrupt && + ((trap_cause_index == INTERRUPT_M_SOFTWARE_BIT[5:0]) || + (trap_cause_index == INTERRUPT_M_TIMER_BIT[5:0]) || + (trap_cause_index == INTERRUPT_M_EXTERNAL_BIT[5:0])); + assign delegate_to_s = trap_trigger && (priv_mode_t'(csr_priv_mode) != PRIV_MODE_M) && + ((trap_is_interrupt && interrupt_targets_s_mode) || + (!trap_is_interrupt && deleg_mask[trap_cause_index] && !machine_interrupt_cause)); + assign trap_to_s_mode = delegate_to_s; + + assign trap_mcause = trap_trigger ? trap_cause_value : '0; + assign trap_mtval = trap_trigger ? trap_mtval_value : '0; + assign trap_mepc = trap_trigger ? wb_pc : '0; + + assign mret_en = trap_is_mret & ~trap_trigger; + assign sret_en = trap_is_sret & ~trap_trigger; + + assign trap_flush_req = trap_trigger | mret_en | sret_en; + assign trap_pc_sel = trap_flush_req; + + logic [XLEN-1:0] trap_vector_base; + logic [1:0] trap_vector_mode; + logic [XLEN-1:0] trap_vector_aligned; + + assign trap_vector_base = delegate_to_s ? csr_stvec : csr_mtvec; + assign trap_vector_mode = trap_vector_base[1:0]; + assign trap_vector_aligned = {trap_vector_base[XLEN-1:2], 2'b00}; + + always_comb begin + if (trap_trigger) begin + if ((trap_vector_mode == 2'b01) && trap_is_interrupt) begin + trap_next_pc = trap_vector_aligned + (({{(XLEN-6){1'b0}}, trap_cause_index}) << 2); + end else begin + trap_next_pc = trap_vector_aligned; + end + end else if (mret_en) begin + trap_next_pc = csr_mepc; + end else if (sret_en) begin + trap_next_pc = csr_sepc; + end else begin + trap_next_pc = '0; + end + end + +endmodule diff --git a/rv64cpu/rtl/core/regfile.sv b/rv64cpu/rtl/core/regfile.sv deleted file mode 100644 index dbc3c04..0000000 --- a/rv64cpu/rtl/core/regfile.sv +++ /dev/null @@ -1,67 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/regfile.sv -// Description: 64-bit Register File - -module regfile - import rv64_pkg::*; -( - input logic clk, // Clock signal - input logic rst, // Reset signal active high - - // Read port 1 - input logic [4:0] rs1_addr, // Read address 1 - output logic [XLEN-1:0] rs1_rdata, // Read data 1 - - // Read port 2 - input logic [4:0] rs2_addr, // Read address 2 - output logic [XLEN-1:0] rs2_rdata, // Read data 2 - - // Write port - input logic [4:0] rd_addr, // Write address - input logic [XLEN-1:0] rd_wdata, // Write data - input logic reg_write_en // Write enable signal -); - - // Register file storage - logic [XLEN-1:0] regs [0:31]; - - // Read Logic (combinational logic) - // Internal bypass: If reading the same register as currently being written, - // forward the write data immediately. - assign rs1_rdata = (rs1_addr == 5'b0) ? '0 : - ((rs1_addr == rd_addr) && reg_write_en) ? rd_wdata : - regs[rs1_addr]; - - assign rs2_rdata = (rs2_addr == 5'b0) ? '0 : - ((rs2_addr == rd_addr) && reg_write_en) ? rd_wdata : - regs[rs2_addr]; - - // Write Logic (sequential logic) - always_ff @(posedge clk) begin - if (rst) begin - for (int i = 0; i < 32; i++) begin - regs[i] <= '0; - end - end else begin - if (reg_write_en && rd_addr != 5'b0) begin - regs[rd_addr] <= rd_wdata; - end - end - end - -endmodule diff --git a/rv64cpu/rtl/core/registers/fp_regfile.sv b/rv64cpu/rtl/core/registers/fp_regfile.sv new file mode 100644 index 0000000..67e83f9 --- /dev/null +++ b/rv64cpu/rtl/core/registers/fp_regfile.sv @@ -0,0 +1,41 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// File path: rtl/core/fp_regfile.sv +// Description: 32-entry 64-bit floating-point register file for RV64F/RV64D. + +module fp_regfile + import rv64_pkg::*; +( + input logic clk, + input logic rst, + + input logic [4:0] fs1_addr, + input logic [4:0] fs2_addr, + input logic [4:0] fs3_addr, + output logic [XLEN-1:0] fs1_rdata, + output logic [XLEN-1:0] fs2_rdata, + output logic [XLEN-1:0] fs3_rdata, + + input logic fp_write_en, + input logic [4:0] fd_addr, + input logic [XLEN-1:0] fd_wdata +); + + logic [XLEN-1:0] regs [0:31]; + + assign fs1_rdata = (fp_write_en && (fs1_addr == fd_addr)) ? fd_wdata : regs[fs1_addr]; + assign fs2_rdata = (fp_write_en && (fs2_addr == fd_addr)) ? fd_wdata : regs[fs2_addr]; + assign fs3_rdata = (fp_write_en && (fs3_addr == fd_addr)) ? fd_wdata : regs[fs3_addr]; + + always_ff @(posedge clk) begin + if (rst) begin + for (int unsigned reg_idx = 0; reg_idx < 32; reg_idx++) begin + regs[reg_idx] <= '0; + end + end else if (fp_write_en) begin + regs[fd_addr] <= fd_wdata; + end + end + +endmodule diff --git a/rv64cpu/rtl/core/registers/regfile.sv b/rv64cpu/rtl/core/registers/regfile.sv new file mode 100644 index 0000000..dbc3c04 --- /dev/null +++ b/rv64cpu/rtl/core/registers/regfile.sv @@ -0,0 +1,67 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/regfile.sv +// Description: 64-bit Register File + +module regfile + import rv64_pkg::*; +( + input logic clk, // Clock signal + input logic rst, // Reset signal active high + + // Read port 1 + input logic [4:0] rs1_addr, // Read address 1 + output logic [XLEN-1:0] rs1_rdata, // Read data 1 + + // Read port 2 + input logic [4:0] rs2_addr, // Read address 2 + output logic [XLEN-1:0] rs2_rdata, // Read data 2 + + // Write port + input logic [4:0] rd_addr, // Write address + input logic [XLEN-1:0] rd_wdata, // Write data + input logic reg_write_en // Write enable signal +); + + // Register file storage + logic [XLEN-1:0] regs [0:31]; + + // Read Logic (combinational logic) + // Internal bypass: If reading the same register as currently being written, + // forward the write data immediately. + assign rs1_rdata = (rs1_addr == 5'b0) ? '0 : + ((rs1_addr == rd_addr) && reg_write_en) ? rd_wdata : + regs[rs1_addr]; + + assign rs2_rdata = (rs2_addr == 5'b0) ? '0 : + ((rs2_addr == rd_addr) && reg_write_en) ? rd_wdata : + regs[rs2_addr]; + + // Write Logic (sequential logic) + always_ff @(posedge clk) begin + if (rst) begin + for (int i = 0; i < 32; i++) begin + regs[i] <= '0; + end + end else begin + if (reg_write_en && rd_addr != 5'b0) begin + regs[rd_addr] <= rd_wdata; + end + end + end + +endmodule diff --git a/rv64cpu/rtl/core/trap_ctrl.sv b/rv64cpu/rtl/core/trap_ctrl.sv deleted file mode 100644 index 0969fdc..0000000 --- a/rv64cpu/rtl/core/trap_ctrl.sv +++ /dev/null @@ -1,279 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/trap_ctrl.sv -// Description: Privileged-level Trap Controller (Exception and Interrupt Handling) -// Detects exceptions/interrupts, triggers context saving, and generates global pipeline flush and PC jump signals - -module trap_ctrl - import rv64_pkg::*; - import csr_pkg::*; -( - // Submission signals from the WB stage (guarantee of precise exceptions) - input logic [XLEN-1:0] wb_pc, - input logic trap_illegal_instr, - input logic trap_is_ecall, - input logic trap_is_ebreak, - input logic trap_is_mret, - input logic trap_is_sret, - input logic trap_instr_access_fault, - input logic trap_load_fault, - input logic trap_store_fault, - input logic trap_instr_page_fault, - input logic trap_load_page_fault, - input logic trap_store_page_fault, - input logic [XLEN-1:0] trap_bad_addr, - input logic [INST_WIDTH-1:0] trap_bad_instr, - - // Status read from CSR Unit - input logic csr_mstatus_mie, // Global interrupt enable (MIE) - input logic [XLEN-1:0] csr_mie, // Machine interrupt enable CSR - input logic [XLEN-1:0] csr_sie, // Supervisor interrupt enable CSR view - input logic [XLEN-1:0] csr_mip, // Machine interrupt pending CSR - input logic [XLEN-1:0] csr_sip, // Supervisor interrupt pending CSR view - input logic [XLEN-1:0] csr_mstatus, // Machine status CSR - input logic [XLEN-1:0] csr_mtvec, // Machine trap vector - input logic [XLEN-1:0] csr_stvec, // Supervisor trap vector - input logic [XLEN-1:0] csr_mepc, // Machine exception PC (for MRET) - input logic [XLEN-1:0] csr_sepc, // Supervisor exception PC (for SRET) - input logic [XLEN-1:0] csr_medeleg, // Exception delegation mask - input logic [XLEN-1:0] csr_mideleg, // Interrupt delegation mask - input logic [1:0] csr_priv_mode, // Current privilege level - - // Status update signal sent to the CSR Unit - output logic trap_trigger, // Trigger exception/interrupt context save - output logic [XLEN-1:0] trap_mepc, // Saved PC (MEPC/SEPC selected by trap_to_s_mode) - output logic [XLEN-1:0] trap_mcause, // Cause code (bit63 indicates interrupt) - output logic [XLEN-1:0] trap_mtval, // Additional information saved to mtval/stval - output logic trap_to_s_mode, // 1: delegated to Supervisor mode - output logic mret_en, // Trigger MRET context restore - output logic sret_en, // Trigger SRET context restore - - // Control flow interference sent to the Hazard Unit and IF stage - output logic trap_flush_req, // Global pipeline flush request - output logic trap_pc_sel, // Forcibly take over PC MUX (0: normal, 1: Trap/Mret/Sret) - output logic [XLEN-1:0] trap_next_pc // Target address for forced jump -); - - typedef enum logic [1:0] { - PRIV_MODE_U = 2'b00, - PRIV_MODE_S = 2'b01, - PRIV_MODE_M = 2'b11 - } priv_mode_t; - - localparam logic [XLEN-1:0] INTERRUPT_BIT = {1'b1, {(XLEN-1){1'b0}}}; - localparam int INTERRUPT_M_SOFTWARE_BIT = int'(INTERRUPT_M_SOFTWARE); - localparam int INTERRUPT_M_TIMER_BIT = int'(INTERRUPT_M_TIMER); - localparam int INTERRUPT_M_EXTERNAL_BIT = int'(INTERRUPT_M_EXTERNAL); - localparam int INTERRUPT_S_SOFTWARE_BIT = int'(INTERRUPT_S_SOFTWARE); - localparam int INTERRUPT_S_TIMER_BIT = int'(INTERRUPT_S_TIMER); - localparam int INTERRUPT_S_EXTERNAL_BIT = int'(INTERRUPT_S_EXTERNAL); - localparam int MSTATUS_SIE_BIT = 1; - localparam int MSTATUS_TSR_BIT = 22; - - logic has_sync_exception; - logic [XLEN-1:0] sync_cause; - logic [XLEN-1:0] sync_mtval; - logic [XLEN-1:0] illegal_instr_value; - logic sret_tsr_illegal; - - assign illegal_instr_value = {{(XLEN-INST_WIDTH){1'b0}}, trap_bad_instr}; - assign sret_tsr_illegal = trap_is_sret && - (priv_mode_t'(csr_priv_mode) == PRIV_MODE_S) && - csr_mstatus[MSTATUS_TSR_BIT]; - - always_comb begin - has_sync_exception = 1'b0; - sync_cause = '0; - sync_mtval = '0; - - if (trap_instr_access_fault) begin - has_sync_exception = 1'b1; - sync_cause = EXC_INST_FAULT; - sync_mtval = trap_bad_addr; - end else if (trap_instr_page_fault) begin - has_sync_exception = 1'b1; - sync_cause = EXC_INSTR_PAGE_FAULT; - sync_mtval = trap_bad_addr; - end else if (trap_illegal_instr || sret_tsr_illegal) begin - has_sync_exception = 1'b1; - sync_cause = EXC_INST_ILLEGAL; - sync_mtval = illegal_instr_value; - end else if (trap_is_ebreak) begin - has_sync_exception = 1'b1; - sync_cause = EXC_BREAKPOINT; - sync_mtval = wb_pc; - end else if (trap_is_ecall) begin - has_sync_exception = 1'b1; - case (priv_mode_t'(csr_priv_mode)) - PRIV_MODE_U: sync_cause = EXC_ECALL_FROM_U; - PRIV_MODE_S: sync_cause = EXC_ECALL_FROM_S; - default: sync_cause = EXC_ECALL_FROM_M; - endcase - sync_mtval = '0; - end else if (trap_load_fault) begin - has_sync_exception = 1'b1; - sync_cause = EXC_LOAD_FAULT; - sync_mtval = trap_bad_addr; - end else if (trap_store_fault) begin - has_sync_exception = 1'b1; - sync_cause = EXC_STORE_FAULT; - sync_mtval = trap_bad_addr; - end else if (trap_load_page_fault) begin - has_sync_exception = 1'b1; - sync_cause = EXC_LOAD_PAGE_FAULT; - sync_mtval = trap_bad_addr; - end else if (trap_store_page_fault) begin - has_sync_exception = 1'b1; - sync_cause = EXC_STORE_PAGE_FAULT; - sync_mtval = trap_bad_addr; - end - end - - // Interrupt Detection - Asynchronous, gated by mie and privilege global-enable rules. - logic has_interrupt; - logic [XLEN-1:0] interrupt_cause; - logic machine_interrupts_globally_enabled; - logic supervisor_interrupts_globally_enabled; - logic interrupt_targets_s_mode; - - assign machine_interrupts_globally_enabled = (priv_mode_t'(csr_priv_mode) != PRIV_MODE_M) || - csr_mstatus_mie; - assign supervisor_interrupts_globally_enabled = (priv_mode_t'(csr_priv_mode) == PRIV_MODE_U) || - ((priv_mode_t'(csr_priv_mode) == PRIV_MODE_S) && - csr_mstatus[MSTATUS_SIE_BIT]); - - function automatic logic interrupt_pending_enabled( - input logic [XLEN-1:0] pending, - input logic [XLEN-1:0] enable, - input int cause - ); - interrupt_pending_enabled = pending[cause[5:0]] && enable[cause[5:0]]; - endfunction - - task automatic take_interrupt( - input logic [XLEN-1:0] cause, - input logic to_s_mode, - output logic taken, - output logic [XLEN-1:0] taken_cause, - output logic taken_targets_s_mode - ); - taken = 1'b1; - taken_cause = INTERRUPT_BIT | cause; - taken_targets_s_mode = to_s_mode; - endtask - - function automatic logic s_interrupt_cause_delegated(input int cause); - s_interrupt_cause_delegated = csr_mideleg[cause[5:0]]; - endfunction - - function automatic logic s_interrupt_cause_takeable(input int cause); - if (s_interrupt_cause_delegated(cause)) begin - s_interrupt_cause_takeable = supervisor_interrupts_globally_enabled && - interrupt_pending_enabled(csr_sip, csr_sie, cause); - end else begin - s_interrupt_cause_takeable = machine_interrupts_globally_enabled && - interrupt_pending_enabled(csr_mip, csr_mie, cause); - end - endfunction - - always_comb begin - has_interrupt = 1'b0; - interrupt_cause = '0; - interrupt_targets_s_mode = 1'b0; - - if (machine_interrupts_globally_enabled && - interrupt_pending_enabled(csr_mip, csr_mie, INTERRUPT_M_EXTERNAL_BIT)) begin - take_interrupt(INTERRUPT_M_EXTERNAL, 1'b0, has_interrupt, interrupt_cause, interrupt_targets_s_mode); - end else if (machine_interrupts_globally_enabled && - interrupt_pending_enabled(csr_mip, csr_mie, INTERRUPT_M_SOFTWARE_BIT)) begin - take_interrupt(INTERRUPT_M_SOFTWARE, 1'b0, has_interrupt, interrupt_cause, interrupt_targets_s_mode); - end else if (machine_interrupts_globally_enabled && - interrupt_pending_enabled(csr_mip, csr_mie, INTERRUPT_M_TIMER_BIT)) begin - take_interrupt(INTERRUPT_M_TIMER, 1'b0, has_interrupt, interrupt_cause, interrupt_targets_s_mode); - end else if (s_interrupt_cause_takeable(INTERRUPT_S_EXTERNAL_BIT)) begin - take_interrupt(INTERRUPT_S_EXTERNAL, s_interrupt_cause_delegated(INTERRUPT_S_EXTERNAL_BIT), - has_interrupt, interrupt_cause, interrupt_targets_s_mode); - end else if (s_interrupt_cause_takeable(INTERRUPT_S_SOFTWARE_BIT)) begin - take_interrupt(INTERRUPT_S_SOFTWARE, s_interrupt_cause_delegated(INTERRUPT_S_SOFTWARE_BIT), - has_interrupt, interrupt_cause, interrupt_targets_s_mode); - end else if (s_interrupt_cause_takeable(INTERRUPT_S_TIMER_BIT)) begin - take_interrupt(INTERRUPT_S_TIMER, s_interrupt_cause_delegated(INTERRUPT_S_TIMER_BIT), - has_interrupt, interrupt_cause, interrupt_targets_s_mode); - end - end - - logic [XLEN-1:0] trap_cause_value; - logic [XLEN-1:0] trap_mtval_value; - logic trap_is_interrupt; - - assign trap_is_interrupt = has_interrupt; - assign trap_cause_value = has_interrupt ? interrupt_cause : sync_cause; - assign trap_mtval_value = has_interrupt ? '0 : sync_mtval; - - assign trap_trigger = has_interrupt | has_sync_exception; - - logic [5:0] trap_cause_index; - logic delegate_to_s; - logic [XLEN-1:0] deleg_mask; - logic machine_interrupt_cause; - - assign trap_cause_index = trap_cause_value[5:0]; - assign deleg_mask = trap_is_interrupt ? csr_mideleg : csr_medeleg; - assign machine_interrupt_cause = trap_is_interrupt && - ((trap_cause_index == INTERRUPT_M_SOFTWARE_BIT[5:0]) || - (trap_cause_index == INTERRUPT_M_TIMER_BIT[5:0]) || - (trap_cause_index == INTERRUPT_M_EXTERNAL_BIT[5:0])); - assign delegate_to_s = trap_trigger && (priv_mode_t'(csr_priv_mode) != PRIV_MODE_M) && - ((trap_is_interrupt && interrupt_targets_s_mode) || - (!trap_is_interrupt && deleg_mask[trap_cause_index] && !machine_interrupt_cause)); - assign trap_to_s_mode = delegate_to_s; - - assign trap_mcause = trap_trigger ? trap_cause_value : '0; - assign trap_mtval = trap_trigger ? trap_mtval_value : '0; - assign trap_mepc = trap_trigger ? wb_pc : '0; - - assign mret_en = trap_is_mret & ~trap_trigger; - assign sret_en = trap_is_sret & ~trap_trigger; - - assign trap_flush_req = trap_trigger | mret_en | sret_en; - assign trap_pc_sel = trap_flush_req; - - logic [XLEN-1:0] trap_vector_base; - logic [1:0] trap_vector_mode; - logic [XLEN-1:0] trap_vector_aligned; - - assign trap_vector_base = delegate_to_s ? csr_stvec : csr_mtvec; - assign trap_vector_mode = trap_vector_base[1:0]; - assign trap_vector_aligned = {trap_vector_base[XLEN-1:2], 2'b00}; - - always_comb begin - if (trap_trigger) begin - if ((trap_vector_mode == 2'b01) && trap_is_interrupt) begin - trap_next_pc = trap_vector_aligned + (({{(XLEN-6){1'b0}}, trap_cause_index}) << 2); - end else begin - trap_next_pc = trap_vector_aligned; - end - end else if (mret_en) begin - trap_next_pc = csr_mepc; - end else if (sret_en) begin - trap_next_pc = csr_sepc; - end else begin - trap_next_pc = '0; - end - end - -endmodule diff --git a/rv64cpu/rtl/core/wb_stage.sv b/rv64cpu/rtl/core/wb_stage.sv deleted file mode 100644 index b19cb67..0000000 --- a/rv64cpu/rtl/core/wb_stage.sv +++ /dev/null @@ -1,168 +0,0 @@ -// Richard CPU -// Copyright (C) 2026 Richard Qin - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// File path: rtl/core/wb_stage.sv -// Description: Write Back Stage -// Responsible for writing back to the general-purpose register file after multiplexing the final data (ALU result, memory read data, or PC+4) - -module wb_stage - import rv64_pkg::*; -( - // Inputs from MEM/WB pipeline register - input logic [XLEN-1:0] mem_wb_alu_result, - input logic [XLEN-1:0] mem_wb_mem_rdata, - input logic [XLEN-1:0] mem_wb_fp_result, - input logic [4:0] mem_wb_fp_fflags, - input logic [XLEN-1:0] mem_wb_pc, - input logic [XLEN-1:0] mem_wb_pc_plus_4, - input logic [INST_WIDTH-1:0] mem_wb_instr, - - input logic mem_wb_reg_write_en, - input logic mem_wb_fp_reg_write_en, - input logic [4:0] mem_wb_rd_addr, - input logic [1:0] mem_wb_wb_sel, - - input logic mem_wb_is_csr, - input logic [2:0] mem_wb_csr_op, - input logic [XLEN-1:0] mem_wb_rs1_rdata, - input logic [4:0] mem_wb_rs1_addr, - input logic [XLEN-1:0] mem_wb_imm, - - input logic mem_wb_illegal_instr, - input logic mem_wb_is_ecall, - input logic mem_wb_is_ebreak, - input logic mem_wb_is_mret, - input logic mem_wb_is_sret, - input logic mem_wb_instr_access_fault, - input logic mem_wb_instr_page_fault, - input logic mem_wb_load_fault, - input logic mem_wb_store_fault, - input logic mem_wb_load_page_fault, - input logic mem_wb_store_page_fault, - input logic [XLEN-1:0] mem_wb_fault_addr, - - // Inputs from CSR Unit - input logic [XLEN-1:0] csr_rdata, // Data read from CSR unit - input logic csr_illegal_access,// CSR rejected the request - - // Outputs to Register File - output logic [XLEN-1:0] wb_rd_wdata, - output logic [4:0] wb_rd_addr, - output logic wb_reg_write_en, - output logic [XLEN-1:0] wb_fd_wdata, - output logic [4:0] wb_fd_addr, - output logic wb_fp_write_en, - output logic [4:0] wb_fp_fflags, - output logic wb_fp_state_dirty, - output logic wb_fp_fflags_valid, - - // Outputs to CSR Unit - output logic csr_req, // CSR request valid - output logic [2:0] csr_op, // CSR operation type - output logic [11:0] csr_addr, // CSR address - output logic [XLEN-1:0] csr_rs1_data, // Source data (from rs1_rdata or zimm) - - // Outputs to Exception Handler (Trap Controller) - output logic trap_illegal_instr, - output logic trap_is_ecall, - output logic trap_is_ebreak, - output logic trap_is_mret, - output logic trap_is_sret, - output logic [XLEN-1:0] trap_epc, - output logic trap_instr_access_fault, - output logic trap_instr_page_fault, - output logic trap_load_fault, - output logic trap_store_fault, - output logic trap_load_page_fault, - output logic trap_store_page_fault, - output logic [XLEN-1:0] trap_bad_addr, - output logic [INST_WIDTH-1:0] trap_bad_instr -); - - // -------------------------- - // Exception/Trap Detection - // -------------------------- - logic has_trap; - logic trap_blocker; - assign has_trap = mem_wb_illegal_instr | mem_wb_is_ecall | mem_wb_is_ebreak | - mem_wb_instr_access_fault | mem_wb_load_fault | mem_wb_store_fault | - mem_wb_instr_page_fault | mem_wb_load_page_fault | mem_wb_store_page_fault; - assign trap_blocker = has_trap | csr_illegal_access; - - // -------------------------- - // CSR Signal Routing - // -------------------------- - assign csr_req = mem_wb_is_csr & ~has_trap; - assign csr_op = mem_wb_csr_op; - assign csr_addr = mem_wb_imm[11:0]; - - // For immediate CSR instructions (CSRRWI, CSRRSI, CSRRCI), - // the source data is the zero-extended rs1_addr field. - // mem_wb_csr_op[2] is 1 for immediate variants. - assign csr_rs1_data = (mem_wb_csr_op[2]) ? {59'b0, mem_wb_rs1_addr} : mem_wb_rs1_rdata; - - // -------------------------- - // Data Multiplexer for Register File - // -------------------------- - always_comb begin - if (mem_wb_is_csr) begin - // CSR reads write the previous CSR value to the destination register - wb_rd_wdata = csr_rdata; - end else begin - case (mem_wb_wb_sel) - 2'b00: wb_rd_wdata = mem_wb_alu_result; - 2'b01: wb_rd_wdata = mem_wb_mem_rdata; - 2'b10: wb_rd_wdata = mem_wb_pc_plus_4; - default: wb_rd_wdata = '0; - endcase - end - end - - // -------------------------- - // Register File Write Control - // -------------------------- - // We only write back if the instruction didn't trap. - assign wb_rd_addr = mem_wb_rd_addr; - assign wb_reg_write_en = mem_wb_reg_write_en & ~trap_blocker; - assign wb_fd_addr = mem_wb_rd_addr; - assign wb_fd_wdata = mem_wb_fp_result; - assign wb_fp_write_en = mem_wb_fp_reg_write_en & ~trap_blocker; - assign wb_fp_fflags = mem_wb_fp_fflags; - assign wb_fp_state_dirty = wb_fp_write_en | ((|mem_wb_fp_fflags) & ~trap_blocker); - assign wb_fp_fflags_valid = (|mem_wb_fp_fflags) & ~trap_blocker; - - // -------------------------- - // Trap / Exception Signals - // -------------------------- - assign trap_illegal_instr = (mem_wb_illegal_instr & - ~mem_wb_instr_access_fault & - ~mem_wb_instr_page_fault) | - csr_illegal_access; - assign trap_is_ecall = mem_wb_is_ecall; - assign trap_is_ebreak = mem_wb_is_ebreak; - assign trap_is_mret = mem_wb_is_mret; - assign trap_is_sret = mem_wb_is_sret; - assign trap_epc = mem_wb_pc; - assign trap_instr_access_fault = mem_wb_instr_access_fault; - assign trap_instr_page_fault = mem_wb_instr_page_fault; - assign trap_load_fault = mem_wb_load_fault; - assign trap_store_fault = mem_wb_store_fault; - assign trap_load_page_fault = mem_wb_load_page_fault; - assign trap_store_page_fault = mem_wb_store_page_fault; - assign trap_bad_addr = mem_wb_fault_addr; - assign trap_bad_instr = mem_wb_instr; - -endmodule diff --git a/rv64cpu/rtl/packages/csr_pkg.sv b/rv64cpu/rtl/packages/csr_pkg.sv new file mode 100644 index 0000000..d1f9416 --- /dev/null +++ b/rv64cpu/rtl/packages/csr_pkg.sv @@ -0,0 +1,232 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/include/csr_pkg.sv +// Description: Global enumerations, address definitions, and exception reason constants related to CSR and privilege-level architecture + +package csr_pkg; + import rv64_pkg::*; + + typedef enum logic [2:0] { + CSR_OP_NONE = 3'b000, + CSR_OP_WRITE = 3'b001, + CSR_OP_SET = 3'b010, + CSR_OP_CLEAR = 3'b011 + } csr_op_t; + + // --------------------------------- + // Floating-Point CSRs + // --------------------------------- + localparam logic [11:0] CSR_FFLAGS = 12'h001; // Floating-Point accrued exception flags + localparam logic [11:0] CSR_FRM = 12'h002; // Floating-Point dynamic rounding mode + localparam logic [11:0] CSR_FCSR = 12'h003; // Combined fflags/frm register + + // --------------------------------- + // Machine Mode CSRs (M-Mode) + // --------------------------------- + localparam logic [11:0] CSR_MVENDORID = 12'hF11; // Vendor ID + localparam logic [11:0] CSR_MARCHID = 12'hF12; // Architecture ID + localparam logic [11:0] CSR_MIMPID = 12'hF13; // Implementation ID + localparam logic [11:0] CSR_MHARTID = 12'hF14; // Hart ID + localparam logic [11:0] CSR_MCONFIGPTR= 12'hF15; // Machine Configuration Pointer + + localparam logic [11:0] CSR_MSTATUS = 12'h300; // Machine Status Register + localparam logic [11:0] CSR_MISA = 12'h301; // Machine ISA Register + localparam logic [11:0] CSR_MEDELEG = 12'h302; // Exception Delegation + localparam logic [11:0] CSR_MIDELEG = 12'h303; // Interrupt Delegation + localparam logic [11:0] CSR_MIE = 12'h304; // Machine Interrupt Enable Register + localparam logic [11:0] CSR_MTVEC = 12'h305; // Machine Trap Vector Register + localparam logic [11:0] CSR_MCOUNTEREN= 12'h306; // Machine Counter Enable Register + + localparam logic [11:0] CSR_MENVCFG = 12'h30A; // Machine Environment Configuration + localparam logic [11:0] CSR_MSTATEEN0 = 12'h30C; // Machine State Enable 0 + localparam logic [11:0] CSR_MSTATEEN1 = 12'h30D; // Machine State Enable 1 + localparam logic [11:0] CSR_MSTATEEN2 = 12'h30E; // Machine State Enable 2 + localparam logic [11:0] CSR_MSTATEEN3 = 12'h30F; // Machine State Enable 3 + + localparam logic [11:0] CSR_PMPCFG0 = 12'h3A0; // PMP Configuration Register 0 + localparam logic [11:0] CSR_PMPCFG15 = 12'h3AF; // PMP Configuration Register 15 + localparam logic [11:0] CSR_PMPADDR0 = 12'h3B0; // PMP Address Register 0 + localparam logic [11:0] CSR_PMPADDR63 = 12'h3EF; // PMP Address Register 63 + + localparam logic [11:0] CSR_MSCRATCH = 12'h340; // Machine Scratch Register + localparam logic [11:0] CSR_MEPC = 12'h341; // Machine Exception Program Counter + localparam logic [11:0] CSR_MCAUSE = 12'h342; // Machine Cause Register + localparam logic [11:0] CSR_MTVAL = 12'h343; // Machine Trap Value Register + localparam logic [11:0] CSR_MIP = 12'h344; // Machine Interrupt Pending Register + localparam logic [11:0] CSR_MTINST = 12'h34A; // Machine Trap Instruction + localparam logic [11:0] CSR_MTVAL2 = 12'h34B; // Machine Second Trap Value + + localparam logic [11:0] CSR_MCOUNTINHIBIT = 12'h320; // Machine Counter-Inhibit Register + localparam logic [11:0] CSR_MCYCLE = 12'hB00; // Machine Cycle Counter + localparam logic [11:0] CSR_MINSTRET = 12'hB02; // Machine Instruction Retired Counter + localparam logic [11:0] CSR_MHPMCOUNTER3 = 12'hB03; // Machine HPM Counter 3 + localparam logic [11:0] CSR_MHPMCOUNTER31 = 12'hB1F; // Machine HPM Counter 31 + localparam logic [11:0] CSR_MHPMEVENT3 = 12'h323; // Machine HPM Event Selector 3 + localparam logic [11:0] CSR_MHPMEVENT31 = 12'h33F; // Machine HPM Event Selector 31 + + // --------------------------------- + // Supervisor Mode CSRs (S-Mode) + // --------------------------------- + localparam logic [11:0] CSR_SSTATUS = 12'h100; // Supervisor Status Register + localparam logic [11:0] CSR_SIE = 12'h104; // Supervisor Interrupt Enable Register + localparam logic [11:0] CSR_STVEC = 12'h105; // Supervisor Trap Vector Register + localparam logic [11:0] CSR_SCOUNTEREN= 12'h106; // Supervisor Counter Enable Register + localparam logic [11:0] CSR_SENVCFG = 12'h10A; // Supervisor Environment Configuration + localparam logic [11:0] CSR_SSTATEEN0 = 12'h10C; // Supervisor State Enable 0 + localparam logic [11:0] CSR_SSTATEEN1 = 12'h10D; // Supervisor State Enable 1 + localparam logic [11:0] CSR_SSTATEEN2 = 12'h10E; // Supervisor State Enable 2 + localparam logic [11:0] CSR_SSTATEEN3 = 12'h10F; // Supervisor State Enable 3 + localparam logic [11:0] CSR_SSCRATCH = 12'h140; // Supervisor Scratch Register + localparam logic [11:0] CSR_SEPC = 12'h141; // Supervisor Exception Program Counter + localparam logic [11:0] CSR_SCAUSE = 12'h142; // Supervisor Cause Register + localparam logic [11:0] CSR_STVAL = 12'h143; // Supervisor Trap Value Register + localparam logic [11:0] CSR_SIP = 12'h144; // Supervisor Interrupt Pending Register + localparam logic [11:0] CSR_SATP = 12'h180; // Supervisor Address Translation and Protection Register + + // --------------------------------- + // Hypervisor CSRs (H-Extension) + // --------------------------------- + localparam logic [11:0] CSR_HSTATUS = 12'h600; // Hypervisor Status + localparam logic [11:0] CSR_HEDELEG = 12'h602; // Hypervisor Exception Delegation + localparam logic [11:0] CSR_HIDELEG = 12'h603; // Hypervisor Interrupt Delegation + localparam logic [11:0] CSR_HIE = 12'h604; // Hypervisor Interrupt Enable + localparam logic [11:0] CSR_HTIMEDELTA = 12'h605; // Hypervisor Timer Delta + localparam logic [11:0] CSR_HCOUNTEREN = 12'h606; // Hypervisor Counter Enable + localparam logic [11:0] CSR_HGEIE = 12'h607; // Hypervisor Guest External Interrupt Enable + localparam logic [11:0] CSR_HENVCFG = 12'h60A; // Hypervisor Environment Configuration + localparam logic [11:0] CSR_HSTATEEN0 = 12'h60C; // Hypervisor State Enable 0 + localparam logic [11:0] CSR_HSTATEEN1 = 12'h60D; // Hypervisor State Enable 1 + localparam logic [11:0] CSR_HSTATEEN2 = 12'h60E; // Hypervisor State Enable 2 + localparam logic [11:0] CSR_HSTATEEN3 = 12'h60F; // Hypervisor State Enable 3 + localparam logic [11:0] CSR_HTVAL = 12'h643; // Hypervisor Trap Value + localparam logic [11:0] CSR_HIP = 12'h644; // Hypervisor Interrupt Pending + localparam logic [11:0] CSR_HVIP = 12'h645; // Hypervisor Virtual Interrupt Pending + localparam logic [11:0] CSR_HTINST = 12'h64A; // Hypervisor Trap Instruction + localparam logic [11:0] CSR_HGATP = 12'h680; // Hypervisor Guest Address Translation and Protection + localparam logic [11:0] CSR_HGEIP = 12'hE12; // Hypervisor Guest External Interrupt Pending + + localparam logic [11:0] CSR_VSSTATUS = 12'h200; // Virtual Supervisor Status + localparam logic [11:0] CSR_VSIE = 12'h204; // Virtual Supervisor Interrupt Enable + localparam logic [11:0] CSR_VSTVEC = 12'h205; // Virtual Supervisor Trap Vector + localparam logic [11:0] CSR_VSSCRATCH = 12'h240; // Virtual Supervisor Scratch + localparam logic [11:0] CSR_VSEPC = 12'h241; // Virtual Supervisor Exception PC + localparam logic [11:0] CSR_VSCAUSE = 12'h242; // Virtual Supervisor Cause + localparam logic [11:0] CSR_VSTVAL = 12'h243; // Virtual Supervisor Trap Value + localparam logic [11:0] CSR_VSIP = 12'h244; // Virtual Supervisor Interrupt Pending + localparam logic [11:0] CSR_VSATP = 12'h280; // Virtual Supervisor Address Translation and Protection + + // --------------------------------- + // User Mode CSRs (U-Mode / URW) + // --------------------------------- + localparam logic [11:0] CSR_CYCLE = 12'hC00; // Cycle Counter + localparam logic [11:0] CSR_TIME = 12'hC01; // Timer Counter + localparam logic [11:0] CSR_INSTRET = 12'hC02; // Instruction Retired Counter + localparam logic [11:0] CSR_HPMCOUNTER3 = 12'hC03; // HPM Counter 3 + localparam logic [11:0] CSR_HPMCOUNTER31 = 12'hC1F; // HPM Counter 31 + + // --------------------------------- + // Exception Cause Codes (mcause.Interrupt == 0) + // --------------------------------- + localparam logic [XLEN-1:0] EXC_INST_ADDR_MISALIGNED = 64'd0; // Instruction Address Misaligned + localparam logic [XLEN-1:0] EXC_INST_FAULT = 64'd1; // Instruction Access Fault + localparam logic [XLEN-1:0] EXC_INST_ILLEGAL = 64'd2; // Illegal Instruction + localparam logic [XLEN-1:0] EXC_BREAKPOINT = 64'd3; // Breakpoint + localparam logic [XLEN-1:0] EXC_LOAD_ADDR_MISALIGNED = 64'd4; // Load Address Misaligned + localparam logic [XLEN-1:0] EXC_LOAD_FAULT = 64'd5; // Load Access Fault + localparam logic [XLEN-1:0] EXC_STORE_ADDR_MISALIGNED = 64'd6; // Store Address Misaligned + localparam logic [XLEN-1:0] EXC_STORE_FAULT = 64'd7; // Store Access Fault + localparam logic [XLEN-1:0] EXC_ECALL_FROM_U = 64'd8; // Environment Call from U-Mode + localparam logic [XLEN-1:0] EXC_ECALL_FROM_S = 64'd9; // Environment Call from S-Mode + localparam logic [XLEN-1:0] EXC_ECALL_FROM_M = 64'd11; // Environment Call from M-Mode + localparam logic [XLEN-1:0] EXC_INSTR_PAGE_FAULT = 64'd12; // Instruction Page Fault + localparam logic [XLEN-1:0] EXC_LOAD_PAGE_FAULT = 64'd13; // Load Page Fault + localparam logic [XLEN-1:0] EXC_STORE_PAGE_FAULT = 64'd15; // Store Page Fault + + localparam logic [XLEN-1:0] MEDELEG_WRITABLE_MASK = + (64'h1 << EXC_INST_ADDR_MISALIGNED) | + (64'h1 << EXC_INST_FAULT) | + (64'h1 << EXC_INST_ILLEGAL) | + (64'h1 << EXC_BREAKPOINT) | + (64'h1 << EXC_LOAD_ADDR_MISALIGNED) | + (64'h1 << EXC_LOAD_FAULT) | + (64'h1 << EXC_STORE_ADDR_MISALIGNED) | + (64'h1 << EXC_STORE_FAULT) | + (64'h1 << EXC_ECALL_FROM_U) | + (64'h1 << EXC_ECALL_FROM_S) | + (64'h1 << EXC_INSTR_PAGE_FAULT) | + (64'h1 << EXC_LOAD_PAGE_FAULT) | + (64'h1 << EXC_STORE_PAGE_FAULT); + + localparam logic [XLEN-1:0] MEDELEG_READ_ONLY_ONE_MASK = '0; + + // --------------------------------- + // Interrupt Cause Codes (mcause.Interrupt == 1) + // --------------------------------- + // User Mode Interrupts + localparam logic [XLEN-1:0] INTERRUPT_U_SOFTWARE = 64'd0; // User Mode Software Interrupt + localparam logic [XLEN-1:0] INTERRUPT_U_TIMER = 64'd4; // User Mode Timer Interrupt + localparam logic [XLEN-1:0] INTERRUPT_U_EXTERNAL = 64'd8; // User Mode External Interrupt + // Supervisor Mode Interrupts + localparam logic [XLEN-1:0] INTERRUPT_S_SOFTWARE = 64'd1; // Supervisor Mode Software Interrupt + localparam logic [XLEN-1:0] INTERRUPT_S_TIMER = 64'd5; // Supervisor Mode Timer Interrupt + localparam logic [XLEN-1:0] INTERRUPT_S_EXTERNAL = 64'd9; // Supervisor Mode External Interrupt + // Machine Mode Interrupts + localparam logic [XLEN-1:0] INTERRUPT_M_SOFTWARE = 64'd3; // Machine Mode Software Interrupt + localparam logic [XLEN-1:0] INTERRUPT_M_TIMER = 64'd7; // Machine Mode Timer Interrupt + localparam logic [XLEN-1:0] INTERRUPT_M_EXTERNAL = 64'd11; // Machine Mode External Interrupt + + localparam logic [XLEN-1:0] MIDELEG_WRITABLE_MASK = + (64'h1 << INTERRUPT_S_SOFTWARE) | + (64'h1 << INTERRUPT_S_TIMER) | + (64'h1 << INTERRUPT_S_EXTERNAL); + + // --------------------------------- + // Physical Memory Protection (PMP) + // --------------------------------- + localparam int PMP_ENTRY_COUNT = 64; + localparam int PMP_CFG_CSR_COUNT = 16; + localparam int PMP_CFG_ENTRIES_PER_RV64_CSR = 8; + + typedef logic [7:0] pmpcfg_entry_t; + typedef logic [XLEN-1:0] pmpaddr_entry_t; + + localparam int PMPCFG_R_BIT = 0; + localparam int PMPCFG_W_BIT = 1; + localparam int PMPCFG_X_BIT = 2; + localparam int PMPCFG_A_LO = 3; + localparam int PMPCFG_A_HI = 4; + localparam int PMPCFG_L_BIT = 7; + + localparam logic [1:0] PMP_MODE_OFF = 2'b00; + localparam logic [1:0] PMP_MODE_TOR = 2'b01; + localparam logic [1:0] PMP_MODE_NA4 = 2'b10; + localparam logic [1:0] PMP_MODE_NAPOT = 2'b11; + + // --------------------------------- + // Environment Configuration / State Enable + // --------------------------------- + localparam int ENVCFG_FIOM_BIT = 0; + localparam int ENVCFG_CBIE_LO = 4; + localparam int ENVCFG_CBIE_HI = 5; + localparam int ENVCFG_CBCFE_BIT = 6; + localparam int ENVCFG_CBZE_BIT = 7; + localparam int ENVCFG_PMM_LO = 32; + localparam int ENVCFG_PMM_HI = 33; + localparam int STATEEN0_ENVCFG_BIT = 62; + localparam int STATEEN_SE0_BIT = 63; + +endpackage diff --git a/rv64cpu/rtl/packages/rv64_pkg.sv b/rv64cpu/rtl/packages/rv64_pkg.sv new file mode 100644 index 0000000..afa7979 --- /dev/null +++ b/rv64cpu/rtl/packages/rv64_pkg.sv @@ -0,0 +1,162 @@ +// Richard CPU +// Copyright (C) 2026 Richard Qin + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// File path: rtl/core/include/rv64_pkg.sv +// Description: RISC-V 64-bit base instruction set global definition package + +package rv64_pkg; + + localparam XLEN = 64; + localparam INST_WIDTH = 32; + + // RISC-V Base Opcode Map (inst[6:0]) + // Reference: RISC-V Unprivileged ISA V20240411, Table 34 + typedef enum logic [6:0] { + // Loads + OPCODE_LOAD = 7'b000_0011, // LB/LH/LW/LD/LBU/LHU/LWU + OPCODE_LOAD_FP = 7'b000_0111, // FLW/FLD + + // Misc-Mem + OPCODE_MISC_MEM = 7'b000_1111, // FENCE / FENCE.I (Zifencei) + + // Immediate ALU + OPCODE_OP_IMM = 7'b001_0011, // ADDI/SLTI/ANDI/ORI/XORI/SLLI/SRLI/SRAI... + OPCODE_OP_IMM_32 = 7'b001_1011, // ADDIW/SLLIW/SRLIW/SRAIW (RV64 only) + + // Upper Immediate + OPCODE_AUIPC = 7'b001_0111, // AUIPC + OPCODE_LUI = 7'b011_0111, // LUI + + // Stores + OPCODE_STORE = 7'b010_0011, // SB/SH/SW/SD + OPCODE_STORE_FP = 7'b010_0111, // FSW/FSD + + // Atomic (A extension) + OPCODE_AMO = 7'b010_1111, // LR/SC/AMOSWAP/AMOADD/... + + // Register ALU + OPCODE_OP = 7'b011_0011, // ADD/SUB/AND/OR/XOR/SLL/SRL/SRA/SLT/SLTU/MUL/DIV... + OPCODE_OP_32 = 7'b011_1011, // ADDW/SUBW/SLLW/SRLW/SRAW/MULW/DIVW... (RV64 only) + + // Floating-point fused multiply-add + OPCODE_MADD = 7'b100_0011, // FMADD.S/D + OPCODE_MSUB = 7'b100_0111, // FMSUB.S/D + OPCODE_NMSUB = 7'b100_1011, // FNMSUB.S/D + OPCODE_NMADD = 7'b100_1111, // FNMADD.S/D + + // Floating-point arithmetic + OPCODE_OP_FP = 7'b101_0011, // FADD/FSUB/FMUL/FDIV/FSQRT/FCMP/FCVT/FMV/FCLASS... + + // Branches + OPCODE_BRANCH = 7'b110_0011, // BEQ/BNE/BLT/BGE/BLTU/BGEU + + // Jumps + OPCODE_JALR = 7'b110_0111, // JALR + OPCODE_JAL = 7'b110_1111, // JAL + + // System + OPCODE_SYSTEM = 7'b111_0011, // ECALL/EBREAK/CSR*/MRET/SRET/WFI/SFENCE.VMA + + // Custom (reserved for extensions) + OPCODE_CUSTOM_0 = 7'b000_1011, + OPCODE_CUSTOM_1 = 7'b010_1011, + OPCODE_CUSTOM_2 = 7'b101_1011, + OPCODE_CUSTOM_3 = 7'b111_1011 + } opcode_t; + + // ALU Operation Encoding + // Derived from funct3 + funct7[5] of R-type / I-type instructions + typedef enum logic [4:0] { + ALU_ADD = 5'd0, // Add + ALU_SUB = 5'd8, // Subtract + ALU_SLL = 5'd1, // Shift Left Logical + ALU_SLT = 5'd2, // Set Less Than (signed) + ALU_SLTU = 5'd3, // Set Less Than (unsigned) + ALU_XOR = 5'd4, // Exclusive Or + ALU_SRL = 5'd5, // Shift Right Logical + ALU_SRA = 5'd13, // Shift Right Arithmetic + ALU_OR = 5'd6, // Or + ALU_AND = 5'd7, // And + ALU_MUL = 5'd16, // Low XLEN bits of signed multiplication + ALU_MULH = 5'd17, // High XLEN bits of signed x signed multiplication + ALU_MULHSU = 5'd18, // High XLEN bits of signed x unsigned multiplication + ALU_MULHU = 5'd19, // High XLEN bits of unsigned x unsigned multiplication + ALU_DIV = 5'd20, // Signed division + ALU_DIVU = 5'd21, // Unsigned division + ALU_REM = 5'd22, // Signed remainder + ALU_REMU = 5'd23 // Unsigned remainder + } alu_op_t; + + + // Branch Operation Encoding + // Derived from funct3 of B-type instructions + typedef enum logic [2:0] { + BRANCH_EQ = 3'b000, // BEQ: Branch if Equal + BRANCH_NE = 3'b001, // BNE: Branch if Not Equal + BRANCH_LT = 3'b100, // BLT: Branch if Less Than (signed) + BRANCH_GE = 3'b101, // BGE: Branch if Greater Than or Equal (signed) + BRANCH_LTU = 3'b110, // BLTU: Branch if Less Than (unsigned) + BRANCH_GEU = 3'b111 // BGEU: Branch if Greater Than or Equal (unsigned) + } branch_op_t; + + typedef enum logic [3:0] { + AMO_NONE = 4'd0, + AMO_LR = 4'd1, + AMO_SC = 4'd2, + AMO_SWAP = 4'd3, + AMO_ADD = 4'd4, + AMO_XOR = 4'd5, + AMO_AND = 4'd6, + AMO_OR = 4'd7, + AMO_MIN = 4'd8, + AMO_MAX = 4'd9, + AMO_MINU = 4'd10, + AMO_MAXU = 4'd11 + } amo_op_t; + + typedef enum logic [4:0] { + FP_OP_NONE = 5'd0, + FP_OP_ADD = 5'd1, + FP_OP_SUB = 5'd2, + FP_OP_MUL = 5'd3, + FP_OP_DIV = 5'd4, + FP_OP_SQRT = 5'd5, + FP_OP_SGNJ = 5'd6, + FP_OP_SGNJN = 5'd7, + FP_OP_SGNJX = 5'd8, + FP_OP_MIN = 5'd9, + FP_OP_MAX = 5'd10, + FP_OP_CVT_F2I = 5'd11, + FP_OP_CVT_I2F = 5'd12, + FP_OP_CVT_F2F = 5'd13, + FP_OP_MV_F2X = 5'd14, + FP_OP_MV_X2F = 5'd15, + FP_OP_EQ = 5'd16, + FP_OP_LT = 5'd17, + FP_OP_LE = 5'd18, + FP_OP_CLASS = 5'd19, + FP_OP_MADD = 5'd20, + FP_OP_MSUB = 5'd21, + FP_OP_NMSUB = 5'd22, + FP_OP_NMADD = 5'd23 + } fp_op_t; + + typedef enum logic [1:0] { + FP_FMT_S = 2'd0, + FP_FMT_D = 2'd1 + } fp_fmt_t; + +endpackage diff --git a/rv64cpu/rtl/perips/.gitkeep b/rv64cpu/rtl/perips/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/rtl/soc/.gitkeep b/rv64cpu/rtl/soc/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/scripts/.gitkeep b/rv64cpu/scripts/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/sw/baremetal/cache_test/.gitkeep b/rv64cpu/sw/baremetal/cache_test/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/sw/baremetal/hello_uart/.gitkeep b/rv64cpu/sw/baremetal/hello_uart/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/sw/baremetal/timer_irq/.gitkeep b/rv64cpu/sw/baremetal/timer_irq/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/sw/common/.gitkeep b/rv64cpu/sw/common/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/tb/common/.gitkeep b/rv64cpu/tb/common/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/tb/compliance/.gitkeep b/rv64cpu/tb/compliance/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/tb/core/.gitkeep b/rv64cpu/tb/core/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/tb/soc/.gitkeep b/rv64cpu/tb/soc/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/tb/unit/.gitkeep b/rv64cpu/tb/unit/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/rv64cpu/waves/.gitkeep b/rv64cpu/waves/.gitkeep deleted file mode 100644 index e69de29..0000000 -- cgit v1.2.3