aboutsummaryrefslogtreecommitdiff
path: root/rv64cpu
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-11-07 02:59:33 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-11-07 02:59:33 +0800
commitbc3ec25107314be3cd816c0b669d82338e892806 (patch)
treeff3e69ce6be1218be8bd0dd04e0c75760776d7dc /rv64cpu
parentc67ba709482821f37d63601b001ac07e6db867b6 (diff)
downloadrcc-project-bc3ec25107314be3cd816c0b669d82338e892806.tar.gz
rcc-project-bc3ec25107314be3cd816c0b669d82338e892806.tar.xz
checkpoint: preserve current RV64 CPU development state
Diffstat (limited to 'rv64cpu')
-rw-r--r--rv64cpu/rtl/core/alu.sv58
-rw-r--r--rv64cpu/rtl/core/compressed_decoder.sv234
-rw-r--r--rv64cpu/rtl/core/csr_unit.sv1066
-rw-r--r--rv64cpu/rtl/core/decoder.sv277
-rw-r--r--rv64cpu/rtl/core/ex_mem_reg.sv181
-rw-r--r--rv64cpu/rtl/core/ex_stage.sv8
-rw-r--r--rv64cpu/rtl/core/fp_regfile.sv41
-rw-r--r--rv64cpu/rtl/core/fpu/fpu_unit.sv2392
-rw-r--r--rv64cpu/rtl/core/hazard_unit.sv18
-rw-r--r--rv64cpu/rtl/core/id_ex_reg.sv137
-rw-r--r--rv64cpu/rtl/core/id_stage.sv49
-rw-r--r--rv64cpu/rtl/core/if_id_reg.sv29
-rw-r--r--rv64cpu/rtl/core/if_stage.sv3
-rw-r--r--rv64cpu/rtl/core/include/csr_pkg.sv115
-rw-r--r--rv64cpu/rtl/core/include/rv64_pkg.sv78
-rw-r--r--rv64cpu/rtl/core/mem_stage.sv225
-rw-r--r--rv64cpu/rtl/core/mem_wb_reg.sv137
-rw-r--r--rv64cpu/rtl/core/mmu/mmu_top.sv359
-rw-r--r--rv64cpu/rtl/core/mmu/ptw.sv416
-rw-r--r--rv64cpu/rtl/core/mmu/tlb.sv32
-rw-r--r--rv64cpu/rtl/core/pmp_checker.sv181
-rw-r--r--rv64cpu/rtl/core/richard_core.sv584
-rw-r--r--rv64cpu/rtl/core/trap_ctrl.sv127
-rw-r--r--rv64cpu/rtl/core/wb_stage.sv53
-rwxr-xr-xrv64cpu/scripts/run_compliance.py132
-rw-r--r--rv64cpu/tb/CMakeLists.txt29
-rw-r--r--rv64cpu/tb/compliance/CMakeLists.txt7
-rw-r--r--rv64cpu/tb/compliance/compliance_runner.cc238
-rw-r--r--rv64cpu/tb/core/tb_core.cc40
-rw-r--r--rv64cpu/tb/unit/tb_alu.cc45
-rw-r--r--rv64cpu/tb/unit/tb_csr.cc225
-rw-r--r--rv64cpu/tb/unit/tb_decoder.cc51
-rw-r--r--rv64cpu/tb/unit/tb_hazard_unit.cc21
-rw-r--r--rv64cpu/tb/unit/tb_mem_stage.cc97
-rw-r--r--rv64cpu/tb/unit/tb_mmu_top.cc236
-rw-r--r--rv64cpu/tb/unit/tb_ptw.cc206
-rw-r--r--rv64cpu/tb/unit/tb_tlb.cc421
-rw-r--r--rv64cpu/tb/unit/tb_trap_ctrl.cc98
38 files changed, 7735 insertions, 911 deletions
diff --git a/rv64cpu/rtl/core/alu.sv b/rv64cpu/rtl/core/alu.sv
index abfc238..08635ce 100644
--- a/rv64cpu/rtl/core/alu.sv
+++ b/rv64cpu/rtl/core/alu.sv
@@ -31,6 +31,18 @@ module alu
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;
@@ -58,6 +70,52 @@ module alu
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
diff --git a/rv64cpu/rtl/core/compressed_decoder.sv b/rv64cpu/rtl/core/compressed_decoder.sv
new file mode 100644
index 0000000..ccbb043
--- /dev/null
+++ b/rv64cpu/rtl/core/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/csr_unit.sv b/rv64cpu/rtl/core/csr_unit.sv
index 3aefe10..a25e17a 100644
--- a/rv64cpu/rtl/core/csr_unit.sv
+++ b/rv64cpu/rtl/core/csr_unit.sv
@@ -20,11 +20,13 @@ 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_0050_112D
+ 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,
@@ -39,21 +41,37 @@ module csr_unit
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
);
@@ -65,11 +83,72 @@ module csr_unit
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;
@@ -83,23 +162,54 @@ module csr_unit
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] sie_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] sip_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;
@@ -115,8 +225,13 @@ module csr_unit
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;
@@ -132,7 +247,9 @@ module csr_unit
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 & (is_write_cmd | ((is_set_cmd | is_clear_cmd) & has_non_zero_data));
+ 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;
@@ -142,28 +259,46 @@ module csr_unit
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 && (is_write_cmd || is_set_cmd || is_clear_cmd) && !csr_is_writable(csr_addr_canonical));
- assign csr_illegal_access = csr_req && (!csr_addr_supported || csr_write_illegal);
+ 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 <= '0;
- medeleg_reg <= '0;
+ mstatus_reg <= reset_mstatus_value();
+ medeleg_reg <= legalize_medeleg('0);
mideleg_reg <= '0;
mie_reg <= '0;
mtvec_reg <= '0;
@@ -173,84 +308,200 @@ module csr_unit
mcause_reg <= '0;
mtval_reg <= '0;
mip_reg <= '0;
- sstatus_reg <= '0;
- sie_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;
- sip_reg <= '0;
satp_reg <= '0;
- mcycle_reg <= '0;
- minstret_reg <= '0;
- time_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
- mcycle_reg <= mcycle_reg + 64'd1;
- time_reg <= time_reg + 64'd1;
- if (!trap_taken) 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
- unique case (csr_addr_canonical)
- CSR_MSTATUS: begin
- logic [XLEN-1:0] next_data;
- next_data = csr_write_next(mstatus_reg, csr_rs1_data, csr_op);
- mstatus_reg <= next_data;
- sstatus_reg <= mirror_mstatus_to_sstatus(sstatus_reg, next_data);
- end
- CSR_MEDELEG: medeleg_reg <= csr_write_next(medeleg_reg, csr_rs1_data, csr_op);
- CSR_MIDELEG: mideleg_reg <= csr_write_next(mideleg_reg, csr_rs1_data, csr_op);
- CSR_MIE: mie_reg <= 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 <= csr_write_next(mip_reg, csr_rs1_data, csr_op);
- CSR_MCOUNTEREN: mcounteren_reg <= csr_write_next(mcounteren_reg, 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 <= next_sstatus;
- mstatus_reg <= apply_sstatus_to_mstatus(mstatus_reg, next_sstatus);
- end
- CSR_SIE: sie_reg <= csr_write_next(sie_reg, csr_rs1_data, csr_op);
- CSR_STVEC: stvec_reg <= csr_write_next(stvec_reg, csr_rs1_data, csr_op);
- CSR_SCOUNTEREN: scounteren_reg <= csr_write_next(scounteren_reg, 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: sip_reg <= csr_write_next(sip_reg, csr_rs1_data, csr_op);
- CSR_SATP: satp_reg <= csr_write_next(satp_reg, csr_rs1_data, csr_op);
- 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];
- 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];
- 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];
- end
- default: ;
- endcase
+ 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
@@ -275,8 +526,8 @@ module csr_unit
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 <= next_mstatus;
- sstatus_reg <= mirror_mstatus_to_sstatus(sstatus_reg, next_mstatus);
+ 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;
@@ -291,19 +542,25 @@ module csr_unit
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);
- mstatus_reg <= next_mstatus;
- sstatus_reg <= mirror_mstatus_to_sstatus(sstatus_reg, next_mstatus);
+ 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;
- sstatus_reg <= next_sstatus;
- mstatus_reg <= apply_sstatus_to_mstatus(mstatus_reg, next_sstatus);
+ 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
@@ -319,6 +576,134 @@ module csr_unit
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,
@@ -337,70 +722,364 @@ module csr_unit
function automatic bit csr_is_writable(input logic [11:0] addr);
case (addr)
- CSR_MISA,
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;
- 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_MCOUNTEREN,
- CSR_MVENDORID,
- CSR_MARCHID,
- CSR_MIMPID,
- CSR_MHARTID,
- CSR_SSTATUS,
- CSR_SIE,
- CSR_STVEC,
- CSR_SCOUNTEREN,
- CSR_SSCRATCH,
- CSR_SEPC,
- CSR_SCAUSE,
- CSR_STVAL,
- CSR_SIP,
- CSR_SATP,
- CSR_MCYCLE,
- CSR_MINSTRET,
- CSR_CYCLE,
- CSR_INSTRET,
- CSR_TIME,
- CSR_FFLAGS,
- CSR_FRM,
- CSR_FCSR:
- supported = 1'b1;
- default:
- supported = 1'b0;
- endcase
+ 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];
- return next_data;
+ 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(
@@ -408,11 +1087,17 @@ module csr_unit
input logic [XLEN-1:0] mstatus_data
);
logic [XLEN-1:0] next_data;
- next_data = 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];
- return 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.
@@ -442,43 +1127,94 @@ module csr_unit
function automatic logic [XLEN-1:0] read_csr(input logic [11:0] addr);
logic [XLEN-1:0] data_out;
- 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_reg;
- CSR_MCOUNTEREN: data_out = mcounteren_reg;
- 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_SSTATUS: data_out = sstatus_reg;
- CSR_SIE: data_out = sie_reg;
- CSR_STVEC: data_out = stvec_reg;
- CSR_SCOUNTEREN: data_out = scounteren_reg;
- 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_reg;
- CSR_SATP: data_out = satp_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
+ 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
diff --git a/rv64cpu/rtl/core/decoder.sv b/rv64cpu/rtl/core/decoder.sv
index 8e5e18f..c39a637 100644
--- a/rv64cpu/rtl/core/decoder.sv
+++ b/rv64cpu/rtl/core/decoder.sv
@@ -15,15 +15,18 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// File path: rtl/core/decoder.sv
-// Description: Instruction decoder for RV64I base ISA + Zicsr + Zifencei
+// Description: Instruction decoder for RV64IMAFD base ISA + Zicsr + Zifencei
// Translates 32-bit instructions into control signals for the pipeline.
-// M/A/F/D/C extensions are marked with TODO stubs.
+// 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
@@ -43,6 +46,14 @@ module decoder
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
@@ -83,6 +94,7 @@ module decoder
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];
@@ -90,6 +102,7 @@ module decoder
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
@@ -145,6 +158,14 @@ module decoder
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;
@@ -302,9 +323,17 @@ module decoder
alu_src2_sel = 1'b0; // rs2
if (funct7 == 7'b0000001) begin
- // M extension (MUL/DIV/REM)
- // TODO: M extension — set alu_op to MUL/DIV variants
- illegal_instr = 1'b1; // placeholder until M extension is implemented
+ 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
@@ -330,9 +359,14 @@ module decoder
alu_src2_sel = 1'b0; // rs2
if (funct7 == 7'b0000001) begin
- // M extension 32-bit (MULW/DIVW/REMW)
- // TODO: M extension word variants
- illegal_instr = 1'b1;
+ 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
@@ -372,18 +406,27 @@ module decoder
illegal_instr = 1'b1;
end
12'b0001000_00010: begin // SRET
- if (priv_ge(priv_mode, PRIV_MODE_S))
- is_sret = 1'b1;
+ 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
- 12'b0001000_00101: is_wfi = 1'b1; // WFI
default: begin
if (funct7 == 7'b0001001) begin
- if (priv_ge(priv_mode, PRIV_MODE_S))
- is_sfence_vma = 1'b1; // SFENCE.VMA
- else
+ 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
@@ -408,21 +451,213 @@ module decoder
// -----------------
// AMO — A extension (LR/SC/AMO*)
OPCODE_AMO: begin
- // TODO: A extension
- illegal_instr = 1'b1;
+ 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,
- OPCODE_STORE_FP,
+ 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,
+ 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
- // TODO: F/D extension
- illegal_instr = 1'b1;
+ 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
// -----------------
diff --git a/rv64cpu/rtl/core/ex_mem_reg.sv b/rv64cpu/rtl/core/ex_mem_reg.sv
index 783dced..b8e5b9e 100644
--- a/rv64cpu/rtl/core/ex_mem_reg.sv
+++ b/rv64cpu/rtl/core/ex_mem_reg.sv
@@ -41,15 +41,30 @@ module ex_mem_reg
// 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,
@@ -60,6 +75,8 @@ module ex_mem_reg
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,
@@ -79,15 +96,30 @@ module ex_mem_reg
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,
@@ -98,6 +130,8 @@ module ex_mem_reg
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,
@@ -111,12 +145,17 @@ module ex_mem_reg
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;
@@ -124,75 +163,117 @@ module ex_mem_reg
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_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;
+ 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_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;
+ 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_rs1_addr <= ex_rs1_addr;
- mem_pc <= ex_pc;
- mem_imm <= ex_imm;
- mem_instr <= ex_instr;
-
- mem_is_load <= ex_is_load;
- mem_is_store <= ex_is_store;
- mem_mem_size <= ex_mem_size;
- 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_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;
+ 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
diff --git a/rv64cpu/rtl/core/ex_stage.sv b/rv64cpu/rtl/core/ex_stage.sv
index 48d4833..287bfd7 100644
--- a/rv64cpu/rtl/core/ex_stage.sv
+++ b/rv64cpu/rtl/core/ex_stage.sv
@@ -63,14 +63,20 @@ module ex_stage
// 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)
+ 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
diff --git a/rv64cpu/rtl/core/fp_regfile.sv b/rv64cpu/rtl/core/fp_regfile.sv
new file mode 100644
index 0000000..67e83f9
--- /dev/null
+++ b/rv64cpu/rtl/core/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/fpu/fpu_unit.sv b/rv64cpu/rtl/core/fpu/fpu_unit.sv
new file mode 100644
index 0000000..a1962e1
--- /dev/null
+++ b/rv64cpu/rtl/core/fpu/fpu_unit.sv
@@ -0,0 +1,2392 @@
+// Richard CPU
+// Copyright (C) 2026 Richard Qin
+
+// File path: rtl/core/fpu/fpu_unit.sv
+// Description: RV64F/RV64D floating-point execution unit.
+
+module fpu_unit
+ import rv64_pkg::*;
+(
+ input fp_op_t fp_op,
+ input fp_fmt_t fp_fmt,
+ input logic [2:0] rm,
+ input logic [2:0] funct3,
+ input logic [4:0] rs2_addr,
+ input logic [XLEN-1:0] fs1_data,
+ input logic [XLEN-1:0] fs2_data,
+ input logic [XLEN-1:0] fs3_data,
+ input logic [XLEN-1:0] int_rs1_data,
+
+ output logic [XLEN-1:0] fp_result,
+ output logic [XLEN-1:0] int_result,
+ output logic [4:0] fflags,
+ output logic illegal_instr
+);
+
+ localparam logic [31:0] CANONICAL_NAN_S = 32'h7FC0_0000;
+ localparam logic [63:0] CANONICAL_NAN_D = 64'h7FF8_0000_0000_0000;
+
+ logic [31:0] s1_raw;
+ logic [31:0] s2_raw;
+ logic [31:0] s3_raw;
+ logic [31:0] s1;
+ logic [31:0] s2;
+ logic [31:0] s3;
+ logic [63:0] d1;
+ logic [63:0] d2;
+ logic [63:0] d3;
+
+ assign s1_raw = fs1_data[31:0];
+ assign s2_raw = fs2_data[31:0];
+ assign s3_raw = fs3_data[31:0];
+ assign s1 = (fs1_data[63:32] == 32'hFFFF_FFFF) ? s1_raw : CANONICAL_NAN_S;
+ assign s2 = (fs2_data[63:32] == 32'hFFFF_FFFF) ? s2_raw : CANONICAL_NAN_S;
+ assign s3 = (fs3_data[63:32] == 32'hFFFF_FFFF) ? s3_raw : CANONICAL_NAN_S;
+ assign d1 = fs1_data;
+ assign d2 = fs2_data;
+ assign d3 = fs3_data;
+
+ function automatic logic is_nan_s(input logic [31:0] value);
+ return (&value[30:23]) && (|value[22:0]);
+ endfunction
+
+ function automatic logic is_snan_s(input logic [31:0] value);
+ return is_nan_s(value) && (value[22] == 1'b0);
+ endfunction
+
+ function automatic logic is_nan_d(input logic [63:0] value);
+ return (&value[62:52]) && (|value[51:0]);
+ endfunction
+
+ function automatic logic is_snan_d(input logic [63:0] value);
+ return is_nan_d(value) && (value[51] == 1'b0);
+ endfunction
+
+ function automatic logic [9:0] class_s(input logic [31:0] value);
+ logic sign;
+ logic [7:0] exp;
+ logic [22:0] frac;
+ sign = value[31];
+ exp = value[30:23];
+ frac = value[22:0];
+ class_s = '0;
+ if (&exp && (frac != '0)) begin
+ class_s[is_snan_s(value) ? 8 : 9] = 1'b1;
+ end else if (sign && (&exp) && (frac == '0)) begin
+ class_s[0] = 1'b1;
+ end else if (sign && (exp != '0)) begin
+ class_s[1] = 1'b1;
+ end else if (sign && (exp == '0) && (frac != '0)) begin
+ class_s[2] = 1'b1;
+ end else if (sign && (exp == '0) && (frac == '0)) begin
+ class_s[3] = 1'b1;
+ end else if (!sign && (exp == '0) && (frac == '0)) begin
+ class_s[4] = 1'b1;
+ end else if (!sign && (exp == '0) && (frac != '0)) begin
+ class_s[5] = 1'b1;
+ end else if (!sign && (exp != '0) && !(&exp)) begin
+ class_s[6] = 1'b1;
+ end else if (!sign && (&exp) && (frac == '0)) begin
+ class_s[7] = 1'b1;
+ end
+ endfunction
+
+ function automatic logic [9:0] class_d(input logic [63:0] value);
+ logic sign;
+ logic [10:0] exp;
+ logic [51:0] frac;
+ sign = value[63];
+ exp = value[62:52];
+ frac = value[51:0];
+ class_d = '0;
+ if (&exp && (frac != '0)) begin
+ class_d[is_snan_d(value) ? 8 : 9] = 1'b1;
+ end else if (sign && (&exp) && (frac == '0)) begin
+ class_d[0] = 1'b1;
+ end else if (sign && (exp != '0)) begin
+ class_d[1] = 1'b1;
+ end else if (sign && (exp == '0) && (frac != '0)) begin
+ class_d[2] = 1'b1;
+ end else if (sign && (exp == '0) && (frac == '0)) begin
+ class_d[3] = 1'b1;
+ end else if (!sign && (exp == '0) && (frac == '0)) begin
+ class_d[4] = 1'b1;
+ end else if (!sign && (exp == '0) && (frac != '0)) begin
+ class_d[5] = 1'b1;
+ end else if (!sign && (exp != '0) && !(&exp)) begin
+ class_d[6] = 1'b1;
+ end else if (!sign && (&exp) && (frac == '0)) begin
+ class_d[7] = 1'b1;
+ end
+ endfunction
+
+ function automatic logic less_s(input logic [31:0] lhs, input logic [31:0] rhs);
+ logic lhs_neg;
+ logic rhs_neg;
+ lhs_neg = lhs[31];
+ rhs_neg = rhs[31];
+ if ((lhs[30:0] == '0) && (rhs[30:0] == '0)) begin
+ return 1'b0;
+ end
+ if (lhs_neg != rhs_neg) begin
+ return lhs_neg;
+ end
+ return lhs_neg ? (lhs[30:0] > rhs[30:0]) : (lhs[30:0] < rhs[30:0]);
+ endfunction
+
+ function automatic logic less_d(input logic [63:0] lhs, input logic [63:0] rhs);
+ logic lhs_neg;
+ logic rhs_neg;
+ lhs_neg = lhs[63];
+ rhs_neg = rhs[63];
+ if ((lhs[62:0] == '0) && (rhs[62:0] == '0)) begin
+ return 1'b0;
+ end
+ if (lhs_neg != rhs_neg) begin
+ return lhs_neg;
+ end
+ return lhs_neg ? (lhs[62:0] > rhs[62:0]) : (lhs[62:0] < rhs[62:0]);
+ endfunction
+
+ function automatic logic equal_s(input logic [31:0] lhs, input logic [31:0] rhs);
+ if ((lhs[30:0] == '0) && (rhs[30:0] == '0)) begin
+ return 1'b1;
+ end
+ return lhs == rhs;
+ endfunction
+
+ function automatic logic equal_d(input logic [63:0] lhs, input logic [63:0] rhs);
+ if ((lhs[62:0] == '0) && (rhs[62:0] == '0)) begin
+ return 1'b1;
+ end
+ return lhs == rhs;
+ endfunction
+
+ function automatic logic [2:0] effective_rm(input logic [2:0] rm_value);
+ effective_rm = (rm_value <= 3'b100) ? rm_value : 3'b000;
+ endfunction
+
+ function automatic logic round_increment(
+ input logic [2:0] round_mode,
+ input logic sign,
+ input logic [127:0] quotient,
+ input logic [127:0] remainder,
+ input logic [127:0] half
+ );
+ logic greater_than_half;
+ logic equal_to_half;
+
+ greater_than_half = remainder > half;
+ equal_to_half = remainder == half;
+
+ unique case (effective_rm(round_mode))
+ 3'b000: round_increment = greater_than_half || (equal_to_half && quotient[0]); // RNE
+ 3'b001: round_increment = 1'b0; // RTZ
+ 3'b010: round_increment = sign && (remainder != '0); // RDN
+ 3'b011: round_increment = !sign && (remainder != '0); // RUP
+ 3'b100: round_increment = greater_than_half || equal_to_half; // RMM
+ default: round_increment = 1'b0;
+ endcase
+ endfunction
+
+ function automatic logic [63:0] invalid_integer_result(
+ input logic is_signed,
+ input logic is_long,
+ input logic sign,
+ input logic is_nan
+ );
+ if (is_signed) begin
+ if (sign && !is_nan) begin
+ invalid_integer_result = is_long ? 64'h8000_0000_0000_0000 : 64'hFFFF_FFFF_8000_0000;
+ end else begin
+ invalid_integer_result = is_long ? 64'h7FFF_FFFF_FFFF_FFFF : 64'h0000_0000_7FFF_FFFF;
+ end
+ end else begin
+ if (sign && !is_nan) begin
+ invalid_integer_result = '0;
+ end else begin
+ invalid_integer_result = 64'hFFFF_FFFF_FFFF_FFFF;
+ end
+ end
+ endfunction
+
+ function automatic logic [63:0] cvt_s_to_int(
+ input logic [31:0] value,
+ input logic [1:0] int_fmt,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic sign;
+ logic [7:0] exp;
+ logic [22:0] frac;
+ logic [23:0] significand;
+ logic signed [12:0] unbiased_exp;
+ logic is_signed;
+ logic is_long;
+ logic invalid;
+ logic inexact;
+ logic [127:0] magnitude;
+ logic [127:0] quotient;
+ logic [127:0] remainder;
+ logic [127:0] divisor;
+ logic [127:0] half;
+ logic [127:0] rounded_magnitude;
+ logic [63:0] positive_limit;
+ logic [63:0] negative_limit;
+ logic [63:0] unsigned_limit;
+ logic [63:0] integer_value;
+ int shift;
+
+ sign = value[31];
+ exp = value[30:23];
+ frac = value[22:0];
+ significand = (exp == '0) ? {1'b0, frac} : {1'b1, frac};
+ unbiased_exp = $signed({5'b0, exp}) - 13'sd127;
+ is_signed = (int_fmt == 2'd0) || (int_fmt == 2'd2);
+ is_long = (int_fmt == 2'd2) || (int_fmt == 2'd3);
+ invalid = 1'b0;
+ inexact = 1'b0;
+ magnitude = '0;
+ quotient = '0;
+ remainder = '0;
+ divisor = '0;
+ half = '0;
+ rounded_magnitude = '0;
+ positive_limit = is_long ? 64'h7FFF_FFFF_FFFF_FFFF : 64'h0000_0000_7FFF_FFFF;
+ negative_limit = is_long ? 64'h8000_0000_0000_0000 : 64'h0000_0000_8000_0000;
+ unsigned_limit = is_long ? 64'hFFFF_FFFF_FFFF_FFFF : 64'h0000_0000_FFFF_FFFF;
+ integer_value = '0;
+ flags = '0;
+
+ if (is_nan_s(value) || (&exp)) begin
+ invalid = 1'b1;
+ end else if (significand == '0) begin
+ rounded_magnitude = '0;
+ end else begin
+ if (unbiased_exp >= 13'sd23) begin
+ shift = int'(unbiased_exp) - 23;
+ if (shift >= 104) begin
+ invalid = 1'b1;
+ end else begin
+ rounded_magnitude = {104'b0, significand} << shift;
+ end
+ end else begin
+ shift = 23 - int'(unbiased_exp);
+ if (shift > 24) begin
+ quotient = '0;
+ remainder = {104'b0, significand};
+ divisor = '0;
+ half = '0;
+ inexact = remainder != '0;
+ unique case (effective_rm(round_mode))
+ 3'b010: rounded_magnitude = (sign && inexact) ? 128'd1 : 128'd0;
+ 3'b011: rounded_magnitude = (!sign && inexact) ? 128'd1 : 128'd0;
+ default: rounded_magnitude = '0;
+ endcase
+ end else begin
+ divisor = 128'd1 << shift;
+ quotient = {104'b0, significand} >> shift;
+ remainder = {104'b0, significand} & (divisor - 128'd1);
+ half = divisor >> 1;
+ inexact = (remainder != '0);
+ rounded_magnitude = quotient +
+ (round_increment(round_mode, sign, quotient, remainder, half) ? 128'd1 : 128'd0);
+ end
+ end
+ end
+
+ if (!invalid) begin
+ if (is_signed) begin
+ if ((!sign && (rounded_magnitude > {64'b0, positive_limit})) ||
+ (sign && (rounded_magnitude > {64'b0, negative_limit}))) begin
+ invalid = 1'b1;
+ end
+ end else begin
+ if ((sign && (rounded_magnitude != '0)) ||
+ (rounded_magnitude > {64'b0, unsigned_limit})) begin
+ invalid = 1'b1;
+ end
+ end
+ end
+
+ if (invalid) begin
+ flags[4] = 1'b1;
+ return invalid_integer_result(is_signed, is_long, sign, is_nan_s(value));
+ end
+
+ if (inexact) begin
+ flags[0] = 1'b1;
+ end
+
+ integer_value = rounded_magnitude[63:0];
+ if (sign) begin
+ integer_value = (~integer_value) + 64'd1;
+ end
+
+ if (!is_long) begin
+ return {{32{integer_value[31]}}, integer_value[31:0]};
+ end
+
+ return integer_value;
+ endfunction
+
+ function automatic logic [63:0] cvt_d_to_int(
+ input logic [63:0] value,
+ input logic [1:0] int_fmt,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic sign;
+ logic [10:0] exp;
+ logic [51:0] frac;
+ logic [52:0] significand;
+ logic signed [12:0] unbiased_exp;
+ logic is_signed;
+ logic is_long;
+ logic invalid;
+ logic inexact;
+ logic [127:0] quotient;
+ logic [127:0] remainder;
+ logic [127:0] divisor;
+ logic [127:0] half;
+ logic [127:0] rounded_magnitude;
+ logic [63:0] positive_limit;
+ logic [63:0] negative_limit;
+ logic [63:0] unsigned_limit;
+ logic [63:0] integer_value;
+ int shift;
+
+ sign = value[63];
+ exp = value[62:52];
+ frac = value[51:0];
+ significand = (exp == '0) ? {1'b0, frac} : {1'b1, frac};
+ unbiased_exp = $signed({2'b0, exp}) - 13'sd1023;
+ is_signed = (int_fmt == 2'd0) || (int_fmt == 2'd2);
+ is_long = (int_fmt == 2'd2) || (int_fmt == 2'd3);
+ invalid = 1'b0;
+ inexact = 1'b0;
+ quotient = '0;
+ remainder = '0;
+ divisor = '0;
+ half = '0;
+ rounded_magnitude = '0;
+ positive_limit = is_long ? 64'h7FFF_FFFF_FFFF_FFFF : 64'h0000_0000_7FFF_FFFF;
+ negative_limit = is_long ? 64'h8000_0000_0000_0000 : 64'h0000_0000_8000_0000;
+ unsigned_limit = is_long ? 64'hFFFF_FFFF_FFFF_FFFF : 64'h0000_0000_FFFF_FFFF;
+ integer_value = '0;
+ flags = '0;
+
+ if (is_nan_d(value) || (&exp)) begin
+ invalid = 1'b1;
+ end else if (significand == '0) begin
+ rounded_magnitude = '0;
+ end else begin
+ if (unbiased_exp >= 13'sd52) begin
+ shift = int'(unbiased_exp) - 52;
+ if (shift >= 75) begin
+ invalid = 1'b1;
+ end else begin
+ rounded_magnitude = {75'b0, significand} << shift;
+ end
+ end else begin
+ shift = 52 - int'(unbiased_exp);
+ if (shift > 53) begin
+ quotient = '0;
+ remainder = {75'b0, significand};
+ inexact = remainder != '0;
+ unique case (effective_rm(round_mode))
+ 3'b010: rounded_magnitude = (sign && inexact) ? 128'd1 : 128'd0;
+ 3'b011: rounded_magnitude = (!sign && inexact) ? 128'd1 : 128'd0;
+ default: rounded_magnitude = '0;
+ endcase
+ end else begin
+ divisor = 128'd1 << shift;
+ quotient = {75'b0, significand} >> shift;
+ remainder = {75'b0, significand} & (divisor - 128'd1);
+ half = divisor >> 1;
+ inexact = (remainder != '0);
+ rounded_magnitude = quotient +
+ (round_increment(round_mode, sign, quotient, remainder, half) ? 128'd1 : 128'd0);
+ end
+ end
+ end
+
+ if (!invalid) begin
+ if (is_signed) begin
+ if ((!sign && (rounded_magnitude > {64'b0, positive_limit})) ||
+ (sign && (rounded_magnitude > {64'b0, negative_limit}))) begin
+ invalid = 1'b1;
+ end
+ end else begin
+ if ((sign && (rounded_magnitude != '0)) ||
+ (rounded_magnitude > {64'b0, unsigned_limit})) begin
+ invalid = 1'b1;
+ end
+ end
+ end
+
+ if (invalid) begin
+ flags[4] = 1'b1;
+ return invalid_integer_result(is_signed, is_long, sign, is_nan_d(value));
+ end
+
+ if (inexact) begin
+ flags[0] = 1'b1;
+ end
+
+ integer_value = rounded_magnitude[63:0];
+ if (sign) begin
+ integer_value = (~integer_value) + 64'd1;
+ end
+
+ if (!is_long) begin
+ return {{32{integer_value[31]}}, integer_value[31:0]};
+ end
+
+ return integer_value;
+ endfunction
+
+ function automatic int highest_bit_64(input logic [63:0] value);
+ highest_bit_64 = -1;
+ for (int bit_idx = 0; bit_idx < 64; bit_idx++) begin
+ if (value[bit_idx]) begin
+ highest_bit_64 = bit_idx;
+ end
+ end
+ endfunction
+
+ function automatic int highest_bit_53(input logic [52:0] value);
+ highest_bit_53 = -1;
+ for (int bit_idx = 0; bit_idx < 53; bit_idx++) begin
+ if (value[bit_idx]) begin
+ highest_bit_53 = bit_idx;
+ end
+ end
+ endfunction
+
+ function automatic logic [31:0] overflow_s_result(input logic sign, input logic [2:0] round_mode);
+ unique case (effective_rm(round_mode))
+ 3'b001: overflow_s_result = {sign, 8'hFE, 23'h7F_FFFF};
+ 3'b010: overflow_s_result = sign ? 32'hFF80_0000 : 32'h7F7F_FFFF;
+ 3'b011: overflow_s_result = sign ? 32'hFF7F_FFFF : 32'h7F80_0000;
+ default: overflow_s_result = {sign, 8'hFF, 23'b0};
+ endcase
+ endfunction
+
+ function automatic logic [63:0] overflow_d_result(input logic sign, input logic [2:0] round_mode);
+ unique case (effective_rm(round_mode))
+ 3'b001: overflow_d_result = {sign, 11'h7FE, 52'hF_FFFF_FFFF_FFFF};
+ 3'b010: overflow_d_result = sign ? 64'hFFF0_0000_0000_0000 : 64'h7FEF_FFFF_FFFF_FFFF;
+ 3'b011: overflow_d_result = sign ? 64'hFFEF_FFFF_FFFF_FFFF : 64'h7FF0_0000_0000_0000;
+ default: overflow_d_result = {sign, 11'h7FF, 52'b0};
+ endcase
+ endfunction
+
+ function automatic logic [27:0] shift_right_jam_28(
+ input logic [27:0] value,
+ input int shift
+ );
+ logic sticky;
+
+ if (shift <= 0) begin
+ return value;
+ end
+ if (shift >= 28) begin
+ return (value != '0) ? 28'd1 : 28'd0;
+ end
+
+ sticky = 1'b0;
+ for (int bit_idx = 0; bit_idx < 28; bit_idx++) begin
+ if (bit_idx < shift) begin
+ sticky = sticky | value[bit_idx];
+ end
+ end
+ return (value >> shift) | (sticky ? 28'd1 : 28'd0);
+ endfunction
+
+ function automatic logic shifted_out_28(
+ input logic [27:0] value,
+ input int shift
+ );
+ logic sticky;
+
+ if (shift <= 0) begin
+ return 1'b0;
+ end
+ if (shift >= 28) begin
+ return value != '0;
+ end
+
+ sticky = 1'b0;
+ for (int bit_idx = 0; bit_idx < 28; bit_idx++) begin
+ if (bit_idx < shift) begin
+ sticky = sticky | value[bit_idx];
+ end
+ end
+ return sticky;
+ endfunction
+
+ function automatic logic [27:0] shift_right_jam_48(
+ input logic [47:0] value,
+ input int shift
+ );
+ logic sticky;
+ logic [27:0] shifted;
+
+ if (shift <= 0) begin
+ return value[27:0];
+ end
+ if (shift >= 48) begin
+ return (value != '0) ? 28'd1 : 28'd0;
+ end
+
+ sticky = 1'b0;
+ for (int bit_idx = 0; bit_idx < 48; bit_idx++) begin
+ if (bit_idx < shift) begin
+ sticky = sticky | value[bit_idx];
+ end
+ end
+ shifted = 28'((value >> shift));
+ return shifted | (sticky ? 28'd1 : 28'd0);
+ endfunction
+
+ function automatic logic [56:0] shift_right_jam_57(
+ input logic [56:0] value,
+ input int shift
+ );
+ logic sticky;
+
+ if (shift <= 0) begin
+ return value;
+ end
+ if (shift >= 57) begin
+ return (value != '0) ? 57'd1 : 57'd0;
+ end
+
+ sticky = 1'b0;
+ for (int bit_idx = 0; bit_idx < 57; bit_idx++) begin
+ if (bit_idx < shift) begin
+ sticky = sticky | value[bit_idx];
+ end
+ end
+ return (value >> shift) | (sticky ? 57'd1 : 57'd0);
+ endfunction
+
+ function automatic logic [64:0] shift_right_jam_65(
+ input logic [64:0] value,
+ input int shift
+ );
+ logic sticky;
+
+ if (shift <= 0) begin
+ return value;
+ end
+ if (shift >= 65) begin
+ return (value != '0) ? 65'd1 : 65'd0;
+ end
+
+ sticky = 1'b0;
+ for (int bit_idx = 0; bit_idx < 65; bit_idx++) begin
+ if (bit_idx < shift) begin
+ sticky = sticky | value[bit_idx];
+ end
+ end
+ return (value >> shift) | (sticky ? 65'd1 : 65'd0);
+ endfunction
+
+ function automatic logic [127:0] shift_right_jam_128(
+ input logic [127:0] value,
+ input int shift
+ );
+ logic sticky;
+
+ if (shift <= 0) begin
+ return value;
+ end
+ if (shift >= 128) begin
+ return (value != '0) ? 128'd1 : 128'd0;
+ end
+
+ sticky = 1'b0;
+ for (int bit_idx = 0; bit_idx < 128; bit_idx++) begin
+ if (bit_idx < shift) begin
+ sticky = sticky | value[bit_idx];
+ end
+ end
+ return (value >> shift) | (sticky ? 128'd1 : 128'd0);
+ endfunction
+
+ function automatic logic [56:0] shift_right_jam_128_to_57(
+ input logic [127:0] value,
+ input int shift
+ );
+ logic sticky;
+ logic [56:0] shifted;
+
+ if (shift <= 0) begin
+ return value[56:0];
+ end
+ if (shift >= 128) begin
+ return (value != '0) ? 57'd1 : 57'd0;
+ end
+
+ sticky = 1'b0;
+ for (int bit_idx = 0; bit_idx < 128; bit_idx++) begin
+ if (bit_idx < shift) begin
+ sticky = sticky | value[bit_idx];
+ end
+ end
+ shifted = 57'(value >> shift);
+ return shifted | (sticky ? 57'd1 : 57'd0);
+ endfunction
+
+ function automatic logic [31:0] round_pack_s(
+ input logic sign,
+ input logic signed [10:0] unbiased_exp,
+ input logic [27:0] significand,
+ input logic inexact_before_round,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic signed [10:0] result_unbiased_exp;
+ logic [27:0] result_sig;
+ logic round_any;
+ logic round_up;
+ logic [24:0] rounded_sig;
+ logic [7:0] result_exp;
+ logic [22:0] result_frac;
+ logic tiny_before_round;
+ int subnormal_shift;
+
+ flags = '0;
+ result_unbiased_exp = unbiased_exp;
+ result_sig = significand;
+ result_sig[0] = result_sig[0] | inexact_before_round;
+ tiny_before_round = 1'b0;
+
+ if (result_unbiased_exp < -11'sd126) begin
+ tiny_before_round = 1'b1;
+ subnormal_shift = -126 - int'(result_unbiased_exp);
+ result_sig = shift_right_jam_28(result_sig, subnormal_shift);
+ result_unbiased_exp = -11'sd126;
+ end
+
+ round_any = |result_sig[2:0];
+ unique case (effective_rm(round_mode))
+ 3'b000: round_up = result_sig[2] && (result_sig[1] || result_sig[0] || result_sig[3]);
+ 3'b001: round_up = 1'b0;
+ 3'b010: round_up = sign && round_any;
+ 3'b011: round_up = !sign && round_any;
+ 3'b100: round_up = result_sig[2];
+ default: round_up = 1'b0;
+ endcase
+
+ rounded_sig = {1'b0, result_sig[26:3]} + (round_up ? 25'd1 : 25'd0);
+ if (round_any) begin
+ flags[0] = 1'b1;
+ end
+ if (tiny_before_round && flags[0]) begin
+ flags[1] = 1'b1;
+ end
+
+ if (rounded_sig[24]) begin
+ rounded_sig = rounded_sig >> 1;
+ result_unbiased_exp = result_unbiased_exp + 11'sd1;
+ end
+
+ if (result_unbiased_exp > 11'sd127) begin
+ flags[2] = 1'b1;
+ flags[0] = 1'b1;
+ return overflow_s_result(sign, round_mode);
+ end
+
+ if ((result_unbiased_exp == -11'sd126) && !rounded_sig[23]) begin
+ result_exp = 8'b0;
+ result_frac = rounded_sig[22:0];
+ if (flags[0]) begin
+ flags[1] = 1'b1;
+ end
+ end else begin
+ result_exp = 8'(result_unbiased_exp + 11'sd127);
+ result_frac = rounded_sig[22:0];
+ end
+
+ return {sign, result_exp, result_frac};
+ endfunction
+
+ function automatic logic [63:0] round_pack_d(
+ input logic sign,
+ input logic signed [12:0] unbiased_exp,
+ input logic [56:0] significand,
+ input logic inexact_before_round,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic signed [12:0] result_unbiased_exp;
+ logic [56:0] result_sig;
+ logic round_any;
+ logic round_up;
+ logic [53:0] rounded_sig;
+ logic [10:0] result_exp;
+ logic [51:0] result_frac;
+ logic tiny_before_round;
+ int subnormal_shift;
+
+ flags = '0;
+ result_unbiased_exp = unbiased_exp;
+ result_sig = significand;
+ result_sig[0] = result_sig[0] | inexact_before_round;
+ tiny_before_round = 1'b0;
+
+ if (result_unbiased_exp < -13'sd1022) begin
+ tiny_before_round = 1'b1;
+ subnormal_shift = -1022 - int'(result_unbiased_exp);
+ result_sig = shift_right_jam_57(result_sig, subnormal_shift);
+ result_unbiased_exp = -13'sd1022;
+ end
+
+ round_any = |result_sig[2:0];
+ unique case (effective_rm(round_mode))
+ 3'b000: round_up = result_sig[2] && (result_sig[1] || result_sig[0] || result_sig[3]);
+ 3'b001: round_up = 1'b0;
+ 3'b010: round_up = sign && round_any;
+ 3'b011: round_up = !sign && round_any;
+ 3'b100: round_up = result_sig[2];
+ default: round_up = 1'b0;
+ endcase
+
+ rounded_sig = {1'b0, result_sig[55:3]} + (round_up ? 54'd1 : 54'd0);
+ if (round_any) begin
+ flags[0] = 1'b1;
+ end
+ if (tiny_before_round && flags[0]) begin
+ flags[1] = 1'b1;
+ end
+
+ if (rounded_sig[53]) begin
+ rounded_sig = rounded_sig >> 1;
+ result_unbiased_exp = result_unbiased_exp + 13'sd1;
+ end
+
+ if (result_unbiased_exp > 13'sd1023) begin
+ flags[2] = 1'b1;
+ flags[0] = 1'b1;
+ return overflow_d_result(sign, round_mode);
+ end
+
+ if ((result_unbiased_exp == -13'sd1022) && !rounded_sig[52]) begin
+ result_exp = 11'b0;
+ result_frac = rounded_sig[51:0];
+ if (flags[0]) begin
+ flags[1] = 1'b1;
+ end
+ end else begin
+ result_exp = 11'(result_unbiased_exp + 13'sd1023);
+ result_frac = rounded_sig[51:0];
+ end
+
+ return {sign, result_exp, result_frac};
+ endfunction
+
+ function automatic logic [31:0] fp_addsub_s(
+ input logic [31:0] lhs,
+ input logic [31:0] rhs,
+ input logic subtract,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic lhs_sign;
+ logic rhs_sign;
+ logic result_sign;
+ logic [7:0] lhs_exp;
+ logic [7:0] rhs_exp;
+ logic [22:0] lhs_frac;
+ logic [22:0] rhs_frac;
+ logic lhs_is_zero;
+ logic rhs_is_zero;
+ logic lhs_is_inf;
+ logic rhs_is_inf;
+ logic signed [10:0] lhs_unbiased_exp;
+ logic signed [10:0] rhs_unbiased_exp;
+ logic signed [10:0] result_unbiased_exp;
+ logic [27:0] lhs_sig;
+ logic [27:0] rhs_sig;
+ logic [27:0] result_sig;
+ logic [27:0] larger_sig;
+ logic [27:0] smaller_sig;
+ logic signed [10:0] larger_exp;
+ logic signs_match;
+ logic alignment_inexact;
+ int exp_diff;
+
+ flags = '0;
+ lhs_sign = lhs[31];
+ rhs_sign = rhs[31] ^ subtract;
+ lhs_exp = lhs[30:23];
+ rhs_exp = rhs[30:23];
+ lhs_frac = lhs[22:0];
+ rhs_frac = rhs[22:0];
+ lhs_is_zero = (lhs[30:0] == '0);
+ rhs_is_zero = (rhs[30:0] == '0);
+ lhs_is_inf = (&lhs_exp) && (lhs_frac == '0);
+ rhs_is_inf = (&rhs_exp) && (rhs_frac == '0);
+ signs_match = lhs_sign == rhs_sign;
+ alignment_inexact = 1'b0;
+
+ if (is_nan_s(lhs) || is_nan_s(rhs)) begin
+ flags[4] = is_snan_s(lhs) || is_snan_s(rhs);
+ return CANONICAL_NAN_S;
+ end
+
+ if (lhs_is_inf || rhs_is_inf) begin
+ if (lhs_is_inf && rhs_is_inf && !signs_match) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_S;
+ end
+ return lhs_is_inf ? {lhs_sign, 8'hFF, 23'b0} : {rhs_sign, 8'hFF, 23'b0};
+ end
+
+ if (lhs_is_zero && rhs_is_zero) begin
+ result_sign = signs_match ? lhs_sign : (effective_rm(round_mode) == 3'b010);
+ return {result_sign, 31'b0};
+ end
+ if (lhs_is_zero) begin
+ return {rhs_sign, rhs[30:0]};
+ end
+ if (rhs_is_zero) begin
+ return lhs;
+ end
+
+ lhs_unbiased_exp = (lhs_exp == '0) ? -11'sd126 : ($signed({3'b0, lhs_exp}) - 11'sd127);
+ rhs_unbiased_exp = (rhs_exp == '0) ? -11'sd126 : ($signed({3'b0, rhs_exp}) - 11'sd127);
+ lhs_sig = {1'b0, (lhs_exp == '0) ? 1'b0 : 1'b1, lhs_frac, 3'b000};
+ rhs_sig = {1'b0, (rhs_exp == '0) ? 1'b0 : 1'b1, rhs_frac, 3'b000};
+
+ if ((lhs_unbiased_exp > rhs_unbiased_exp) ||
+ ((lhs_unbiased_exp == rhs_unbiased_exp) && (lhs_sig >= rhs_sig))) begin
+ exp_diff = int'(lhs_unbiased_exp) - int'(rhs_unbiased_exp);
+ larger_sig = lhs_sig;
+ alignment_inexact = shifted_out_28(rhs_sig, exp_diff);
+ smaller_sig = shift_right_jam_28(rhs_sig, exp_diff);
+ larger_exp = lhs_unbiased_exp;
+ result_sign = lhs_sign;
+ end else begin
+ exp_diff = int'(rhs_unbiased_exp) - int'(lhs_unbiased_exp);
+ larger_sig = rhs_sig;
+ alignment_inexact = shifted_out_28(lhs_sig, exp_diff);
+ smaller_sig = shift_right_jam_28(lhs_sig, exp_diff);
+ larger_exp = rhs_unbiased_exp;
+ result_sign = rhs_sign;
+ end
+
+ result_unbiased_exp = larger_exp;
+ if (signs_match) begin
+ result_sig = larger_sig + smaller_sig;
+ if (result_sig[27]) begin
+ result_sig = shift_right_jam_28(result_sig, 1);
+ result_unbiased_exp = result_unbiased_exp + 11'sd1;
+ end
+ end else begin
+ result_sig = larger_sig - smaller_sig;
+ if (result_sig == '0) begin
+ result_sign = effective_rm(round_mode) == 3'b010;
+ return {result_sign, 31'b0};
+ end
+ while (!result_sig[26] && (result_unbiased_exp > -11'sd126)) begin
+ result_sig = result_sig << 1;
+ result_unbiased_exp = result_unbiased_exp - 11'sd1;
+ end
+ end
+
+ return round_pack_s(result_sign, result_unbiased_exp, result_sig, alignment_inexact, round_mode, flags);
+ endfunction
+
+ function automatic logic [63:0] fp_addsub_d(
+ input logic [63:0] lhs,
+ input logic [63:0] rhs,
+ input logic subtract,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic lhs_sign;
+ logic rhs_sign;
+ logic result_sign;
+ logic [10:0] lhs_exp;
+ logic [10:0] rhs_exp;
+ logic [51:0] lhs_frac;
+ logic [51:0] rhs_frac;
+ logic lhs_is_zero;
+ logic rhs_is_zero;
+ logic lhs_is_inf;
+ logic rhs_is_inf;
+ logic signed [12:0] lhs_unbiased_exp;
+ logic signed [12:0] rhs_unbiased_exp;
+ logic signed [12:0] result_unbiased_exp;
+ logic [56:0] lhs_sig;
+ logic [56:0] rhs_sig;
+ logic [56:0] result_sig;
+ logic [56:0] larger_sig;
+ logic [56:0] smaller_sig;
+ logic signed [12:0] larger_exp;
+ logic signs_match;
+ int exp_diff;
+
+ flags = '0;
+ lhs_sign = lhs[63];
+ rhs_sign = rhs[63] ^ subtract;
+ lhs_exp = lhs[62:52];
+ rhs_exp = rhs[62:52];
+ lhs_frac = lhs[51:0];
+ rhs_frac = rhs[51:0];
+ lhs_is_zero = (lhs[62:0] == '0);
+ rhs_is_zero = (rhs[62:0] == '0);
+ lhs_is_inf = (&lhs_exp) && (lhs_frac == '0);
+ rhs_is_inf = (&rhs_exp) && (rhs_frac == '0);
+ signs_match = lhs_sign == rhs_sign;
+
+ if (is_nan_d(lhs) || is_nan_d(rhs)) begin
+ flags[4] = is_snan_d(lhs) || is_snan_d(rhs);
+ return CANONICAL_NAN_D;
+ end
+
+ if (lhs_is_inf || rhs_is_inf) begin
+ if (lhs_is_inf && rhs_is_inf && !signs_match) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_D;
+ end
+ return lhs_is_inf ? {lhs_sign, 11'h7FF, 52'b0} : {rhs_sign, 11'h7FF, 52'b0};
+ end
+
+ if (lhs_is_zero && rhs_is_zero) begin
+ result_sign = signs_match ? lhs_sign : (effective_rm(round_mode) == 3'b010);
+ return {result_sign, 63'b0};
+ end
+ if (lhs_is_zero) begin
+ return {rhs_sign, rhs[62:0]};
+ end
+ if (rhs_is_zero) begin
+ return lhs;
+ end
+
+ lhs_unbiased_exp = (lhs_exp == '0) ? -13'sd1022 : ($signed({2'b0, lhs_exp}) - 13'sd1023);
+ rhs_unbiased_exp = (rhs_exp == '0) ? -13'sd1022 : ($signed({2'b0, rhs_exp}) - 13'sd1023);
+ lhs_sig = {1'b0, (lhs_exp == '0) ? 1'b0 : 1'b1, lhs_frac, 3'b000};
+ rhs_sig = {1'b0, (rhs_exp == '0) ? 1'b0 : 1'b1, rhs_frac, 3'b000};
+
+ if ((lhs_unbiased_exp > rhs_unbiased_exp) ||
+ ((lhs_unbiased_exp == rhs_unbiased_exp) && (lhs_sig >= rhs_sig))) begin
+ exp_diff = int'(lhs_unbiased_exp) - int'(rhs_unbiased_exp);
+ larger_sig = lhs_sig;
+ smaller_sig = shift_right_jam_57(rhs_sig, exp_diff);
+ larger_exp = lhs_unbiased_exp;
+ result_sign = lhs_sign;
+ end else begin
+ exp_diff = int'(rhs_unbiased_exp) - int'(lhs_unbiased_exp);
+ larger_sig = rhs_sig;
+ smaller_sig = shift_right_jam_57(lhs_sig, exp_diff);
+ larger_exp = rhs_unbiased_exp;
+ result_sign = rhs_sign;
+ end
+
+ result_unbiased_exp = larger_exp;
+ if (signs_match) begin
+ result_sig = larger_sig + smaller_sig;
+ if (result_sig[56]) begin
+ result_sig = shift_right_jam_57(result_sig, 1);
+ result_unbiased_exp = result_unbiased_exp + 13'sd1;
+ end
+ end else begin
+ result_sig = larger_sig - smaller_sig;
+ if (result_sig == '0) begin
+ result_sign = effective_rm(round_mode) == 3'b010;
+ return {result_sign, 63'b0};
+ end
+ while (!result_sig[55] && (result_unbiased_exp > -13'sd1022)) begin
+ result_sig = result_sig << 1;
+ result_unbiased_exp = result_unbiased_exp - 13'sd1;
+ end
+ end
+
+ return round_pack_d(result_sign, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [63:0] fp_mul_d(
+ input logic [63:0] lhs,
+ input logic [63:0] rhs,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic result_sign;
+ logic [10:0] lhs_exp;
+ logic [10:0] rhs_exp;
+ logic [51:0] lhs_frac;
+ logic [51:0] rhs_frac;
+ logic lhs_is_zero;
+ logic rhs_is_zero;
+ logic lhs_is_inf;
+ logic rhs_is_inf;
+ logic signed [12:0] lhs_unbiased_exp;
+ logic signed [12:0] rhs_unbiased_exp;
+ logic signed [12:0] result_unbiased_exp;
+ logic [52:0] lhs_sig;
+ logic [52:0] rhs_sig;
+ logic [105:0] product;
+ logic [56:0] result_sig;
+ int lhs_msb;
+ int rhs_msb;
+
+ flags = '0;
+ result_sign = lhs[63] ^ rhs[63];
+ lhs_exp = lhs[62:52];
+ rhs_exp = rhs[62:52];
+ lhs_frac = lhs[51:0];
+ rhs_frac = rhs[51:0];
+ lhs_is_zero = (lhs[62:0] == '0);
+ rhs_is_zero = (rhs[62:0] == '0);
+ lhs_is_inf = (&lhs_exp) && (lhs_frac == '0);
+ rhs_is_inf = (&rhs_exp) && (rhs_frac == '0);
+
+ if (is_nan_d(lhs) || is_nan_d(rhs)) begin
+ flags[4] = is_snan_d(lhs) || is_snan_d(rhs);
+ return CANONICAL_NAN_D;
+ end
+
+ if ((lhs_is_inf && rhs_is_zero) || (rhs_is_inf && lhs_is_zero)) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_D;
+ end
+
+ if (lhs_is_inf || rhs_is_inf) begin
+ return {result_sign, 11'h7FF, 52'b0};
+ end
+
+ if (lhs_is_zero || rhs_is_zero) begin
+ return {result_sign, 63'b0};
+ end
+
+ if (lhs_exp == '0) begin
+ lhs_msb = highest_bit_53({1'b0, lhs_frac});
+ lhs_unbiased_exp = 13'(lhs_msb - 1074);
+ lhs_sig = {1'b0, lhs_frac} << (52 - lhs_msb);
+ end else begin
+ lhs_unbiased_exp = $signed({2'b0, lhs_exp}) - 13'sd1023;
+ lhs_sig = {1'b1, lhs_frac};
+ end
+
+ if (rhs_exp == '0) begin
+ rhs_msb = highest_bit_53({1'b0, rhs_frac});
+ rhs_unbiased_exp = 13'(rhs_msb - 1074);
+ rhs_sig = {1'b0, rhs_frac} << (52 - rhs_msb);
+ end else begin
+ rhs_unbiased_exp = $signed({2'b0, rhs_exp}) - 13'sd1023;
+ rhs_sig = {1'b1, rhs_frac};
+ end
+
+ product = lhs_sig * rhs_sig;
+ if (product[105]) begin
+ result_unbiased_exp = lhs_unbiased_exp + rhs_unbiased_exp + 13'sd1;
+ result_sig = shift_right_jam_128_to_57({22'b0, product}, 50);
+ end else begin
+ result_unbiased_exp = lhs_unbiased_exp + rhs_unbiased_exp;
+ result_sig = shift_right_jam_128_to_57({22'b0, product}, 49);
+ end
+
+ return round_pack_d(result_sign, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [31:0] fp_mul_s(
+ input logic [31:0] lhs,
+ input logic [31:0] rhs,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic result_sign;
+ logic [7:0] lhs_exp;
+ logic [7:0] rhs_exp;
+ logic [22:0] lhs_frac;
+ logic [22:0] rhs_frac;
+ logic lhs_is_zero;
+ logic rhs_is_zero;
+ logic lhs_is_inf;
+ logic rhs_is_inf;
+ logic signed [10:0] lhs_unbiased_exp;
+ logic signed [10:0] rhs_unbiased_exp;
+ logic signed [10:0] result_unbiased_exp;
+ logic [23:0] lhs_sig;
+ logic [23:0] rhs_sig;
+ logic [47:0] product;
+ logic [27:0] result_sig;
+ int lhs_msb;
+ int rhs_msb;
+
+ flags = '0;
+ result_sign = lhs[31] ^ rhs[31];
+ lhs_exp = lhs[30:23];
+ rhs_exp = rhs[30:23];
+ lhs_frac = lhs[22:0];
+ rhs_frac = rhs[22:0];
+ lhs_is_zero = (lhs[30:0] == '0);
+ rhs_is_zero = (rhs[30:0] == '0);
+ lhs_is_inf = (&lhs_exp) && (lhs_frac == '0);
+ rhs_is_inf = (&rhs_exp) && (rhs_frac == '0);
+
+ if (is_nan_s(lhs) || is_nan_s(rhs)) begin
+ flags[4] = is_snan_s(lhs) || is_snan_s(rhs);
+ return CANONICAL_NAN_S;
+ end
+
+ if ((lhs_is_inf && rhs_is_zero) || (rhs_is_inf && lhs_is_zero)) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_S;
+ end
+
+ if (lhs_is_inf || rhs_is_inf) begin
+ return {result_sign, 8'hFF, 23'b0};
+ end
+
+ if (lhs_is_zero || rhs_is_zero) begin
+ return {result_sign, 31'b0};
+ end
+
+ if (lhs_exp == '0) begin
+ lhs_msb = highest_bit_64({41'b0, lhs_frac});
+ lhs_unbiased_exp = 11'(lhs_msb - 149);
+ lhs_sig = {1'b0, lhs_frac} << (23 - lhs_msb);
+ end else begin
+ lhs_unbiased_exp = $signed({3'b0, lhs_exp}) - 11'sd127;
+ lhs_sig = {1'b1, lhs_frac};
+ end
+
+ if (rhs_exp == '0) begin
+ rhs_msb = highest_bit_64({41'b0, rhs_frac});
+ rhs_unbiased_exp = 11'(rhs_msb - 149);
+ rhs_sig = {1'b0, rhs_frac} << (23 - rhs_msb);
+ end else begin
+ rhs_unbiased_exp = $signed({3'b0, rhs_exp}) - 11'sd127;
+ rhs_sig = {1'b1, rhs_frac};
+ end
+
+ product = lhs_sig * rhs_sig;
+ if (product[47]) begin
+ result_unbiased_exp = lhs_unbiased_exp + rhs_unbiased_exp + 11'sd1;
+ result_sig = shift_right_jam_48(product, 21);
+ end else begin
+ result_unbiased_exp = lhs_unbiased_exp + rhs_unbiased_exp;
+ result_sig = shift_right_jam_48(product, 20);
+ end
+
+ return round_pack_s(result_sign, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [31:0] fp_div_s(
+ input logic [31:0] lhs,
+ input logic [31:0] rhs,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic result_sign;
+ logic [7:0] lhs_exp;
+ logic [7:0] rhs_exp;
+ logic [22:0] lhs_frac;
+ logic [22:0] rhs_frac;
+ logic lhs_is_zero;
+ logic rhs_is_zero;
+ logic lhs_is_inf;
+ logic rhs_is_inf;
+ logic signed [10:0] lhs_unbiased_exp;
+ logic signed [10:0] rhs_unbiased_exp;
+ logic signed [10:0] result_unbiased_exp;
+ logic [23:0] lhs_sig;
+ logic [23:0] rhs_sig;
+ logic [63:0] dividend;
+ logic [63:0] quotient;
+ logic [23:0] remainder;
+ logic [27:0] result_sig;
+ int lhs_msb;
+ int rhs_msb;
+
+ flags = '0;
+ result_sign = lhs[31] ^ rhs[31];
+ lhs_exp = lhs[30:23];
+ rhs_exp = rhs[30:23];
+ lhs_frac = lhs[22:0];
+ rhs_frac = rhs[22:0];
+ lhs_is_zero = (lhs[30:0] == '0);
+ rhs_is_zero = (rhs[30:0] == '0);
+ lhs_is_inf = (&lhs_exp) && (lhs_frac == '0);
+ rhs_is_inf = (&rhs_exp) && (rhs_frac == '0);
+
+ if (is_nan_s(lhs) || is_nan_s(rhs)) begin
+ flags[4] = is_snan_s(lhs) || is_snan_s(rhs);
+ return CANONICAL_NAN_S;
+ end
+
+ if ((lhs_is_zero && rhs_is_zero) || (lhs_is_inf && rhs_is_inf)) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_S;
+ end
+
+ if (lhs_is_inf || rhs_is_zero) begin
+ if (rhs_is_zero && !lhs_is_zero && !lhs_is_inf) begin
+ flags[3] = 1'b1;
+ end
+ return {result_sign, 8'hFF, 23'b0};
+ end
+
+ if (lhs_is_zero || rhs_is_inf) begin
+ return {result_sign, 31'b0};
+ end
+
+ if (lhs_exp == '0) begin
+ lhs_msb = highest_bit_64({41'b0, lhs_frac});
+ lhs_unbiased_exp = 11'(lhs_msb - 149);
+ lhs_sig = {1'b0, lhs_frac} << (23 - lhs_msb);
+ end else begin
+ lhs_unbiased_exp = $signed({3'b0, lhs_exp}) - 11'sd127;
+ lhs_sig = {1'b1, lhs_frac};
+ end
+
+ if (rhs_exp == '0) begin
+ rhs_msb = highest_bit_64({41'b0, rhs_frac});
+ rhs_unbiased_exp = 11'(rhs_msb - 149);
+ rhs_sig = {1'b0, rhs_frac} << (23 - rhs_msb);
+ end else begin
+ rhs_unbiased_exp = $signed({3'b0, rhs_exp}) - 11'sd127;
+ rhs_sig = {1'b1, rhs_frac};
+ end
+
+ if (lhs_sig >= rhs_sig) begin
+ result_unbiased_exp = lhs_unbiased_exp - rhs_unbiased_exp;
+ dividend = {40'b0, lhs_sig} << 26;
+ end else begin
+ result_unbiased_exp = lhs_unbiased_exp - rhs_unbiased_exp - 11'sd1;
+ dividend = {40'b0, lhs_sig} << 27;
+ end
+
+ quotient = dividend / {40'b0, rhs_sig};
+ remainder = 24'(dividend % {40'b0, rhs_sig});
+ result_sig = 28'(quotient);
+ if (remainder != '0) begin
+ result_sig[0] = 1'b1;
+ end
+
+ return round_pack_s(result_sign, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [63:0] fp_div_d(
+ input logic [63:0] lhs,
+ input logic [63:0] rhs,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic result_sign;
+ logic [10:0] lhs_exp;
+ logic [10:0] rhs_exp;
+ logic [51:0] lhs_frac;
+ logic [51:0] rhs_frac;
+ logic lhs_is_zero;
+ logic rhs_is_zero;
+ logic lhs_is_inf;
+ logic rhs_is_inf;
+ logic signed [12:0] lhs_unbiased_exp;
+ logic signed [12:0] rhs_unbiased_exp;
+ logic signed [12:0] result_unbiased_exp;
+ logic [52:0] lhs_sig;
+ logic [52:0] rhs_sig;
+ logic [127:0] dividend;
+ logic [127:0] quotient;
+ logic [52:0] remainder;
+ logic [56:0] result_sig;
+ int lhs_msb;
+ int rhs_msb;
+
+ flags = '0;
+ result_sign = lhs[63] ^ rhs[63];
+ lhs_exp = lhs[62:52];
+ rhs_exp = rhs[62:52];
+ lhs_frac = lhs[51:0];
+ rhs_frac = rhs[51:0];
+ lhs_is_zero = (lhs[62:0] == '0);
+ rhs_is_zero = (rhs[62:0] == '0);
+ lhs_is_inf = (&lhs_exp) && (lhs_frac == '0);
+ rhs_is_inf = (&rhs_exp) && (rhs_frac == '0);
+
+ if (is_nan_d(lhs) || is_nan_d(rhs)) begin
+ flags[4] = is_snan_d(lhs) || is_snan_d(rhs);
+ return CANONICAL_NAN_D;
+ end
+
+ if ((lhs_is_zero && rhs_is_zero) || (lhs_is_inf && rhs_is_inf)) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_D;
+ end
+
+ if (lhs_is_inf || rhs_is_zero) begin
+ if (rhs_is_zero && !lhs_is_zero && !lhs_is_inf) begin
+ flags[3] = 1'b1;
+ end
+ return {result_sign, 11'h7FF, 52'b0};
+ end
+
+ if (lhs_is_zero || rhs_is_inf) begin
+ return {result_sign, 63'b0};
+ end
+
+ if (lhs_exp == '0) begin
+ lhs_msb = highest_bit_53({1'b0, lhs_frac});
+ lhs_unbiased_exp = 13'(lhs_msb - 1074);
+ lhs_sig = {1'b0, lhs_frac} << (52 - lhs_msb);
+ end else begin
+ lhs_unbiased_exp = $signed({2'b0, lhs_exp}) - 13'sd1023;
+ lhs_sig = {1'b1, lhs_frac};
+ end
+
+ if (rhs_exp == '0) begin
+ rhs_msb = highest_bit_53({1'b0, rhs_frac});
+ rhs_unbiased_exp = 13'(rhs_msb - 1074);
+ rhs_sig = {1'b0, rhs_frac} << (52 - rhs_msb);
+ end else begin
+ rhs_unbiased_exp = $signed({2'b0, rhs_exp}) - 13'sd1023;
+ rhs_sig = {1'b1, rhs_frac};
+ end
+
+ if (lhs_sig >= rhs_sig) begin
+ result_unbiased_exp = lhs_unbiased_exp - rhs_unbiased_exp;
+ dividend = {75'b0, lhs_sig} << 55;
+ end else begin
+ result_unbiased_exp = lhs_unbiased_exp - rhs_unbiased_exp - 13'sd1;
+ dividend = {75'b0, lhs_sig} << 56;
+ end
+
+ quotient = dividend / {75'b0, rhs_sig};
+ remainder = 53'(dividend % {75'b0, rhs_sig});
+ result_sig = 57'(quotient);
+ if (remainder != '0) begin
+ result_sig[0] = 1'b1;
+ end
+
+ return round_pack_d(result_sign, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [31:0] fp_sqrt_s(
+ input logic [31:0] value,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic sign;
+ logic [7:0] exp;
+ logic [22:0] frac;
+ logic signed [10:0] unbiased_exp;
+ logic signed [10:0] result_unbiased_exp;
+ logic [23:0] sig;
+ logic [63:0] radicand;
+ logic [63:0] trial;
+ logic [63:0] root;
+ logic [63:0] remainder;
+ logic [27:0] result_sig;
+ int sig_msb;
+
+ flags = '0;
+ sign = value[31];
+ exp = value[30:23];
+ frac = value[22:0];
+
+ if (is_nan_s(value)) begin
+ flags[4] = is_snan_s(value);
+ return CANONICAL_NAN_S;
+ end
+
+ if ((&exp) && (frac == '0)) begin
+ if (sign) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_S;
+ end
+ return value;
+ end
+
+ if (value[30:0] == '0) begin
+ return value;
+ end
+
+ if (sign) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_S;
+ end
+
+ if (exp == '0) begin
+ sig_msb = highest_bit_64({41'b0, frac});
+ unbiased_exp = 11'(sig_msb - 149);
+ sig = {1'b0, frac} << (23 - sig_msb);
+ end else begin
+ unbiased_exp = $signed({3'b0, exp}) - 11'sd127;
+ sig = {1'b1, frac};
+ end
+
+ if (unbiased_exp[0]) begin
+ radicand = {39'b0, sig, 1'b0} << 29;
+ result_unbiased_exp = (unbiased_exp - 11'sd1) >>> 1;
+ end else begin
+ radicand = {40'b0, sig} << 29;
+ result_unbiased_exp = unbiased_exp >>> 1;
+ end
+
+ root = '0;
+ for (int bit_idx = 26; bit_idx >= 0; bit_idx--) begin
+ trial = root | (64'd1 << bit_idx);
+ if ((trial * trial) <= radicand) begin
+ root = trial;
+ end
+ end
+
+ remainder = radicand - (root * root);
+ result_sig = 28'(root);
+ if (remainder != '0) begin
+ result_sig[0] = 1'b1;
+ end
+
+ return round_pack_s(1'b0, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [63:0] fp_sqrt_d(
+ input logic [63:0] value,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic sign;
+ logic [10:0] exp;
+ logic [51:0] frac;
+ logic signed [12:0] unbiased_exp;
+ logic signed [12:0] result_unbiased_exp;
+ logic [52:0] sig;
+ logic [127:0] radicand;
+ logic [127:0] trial;
+ logic [127:0] root;
+ logic [127:0] remainder;
+ logic [56:0] result_sig;
+ int sig_msb;
+
+ flags = '0;
+ sign = value[63];
+ exp = value[62:52];
+ frac = value[51:0];
+
+ if (is_nan_d(value)) begin
+ flags[4] = is_snan_d(value);
+ return CANONICAL_NAN_D;
+ end
+
+ if ((&exp) && (frac == '0)) begin
+ if (sign) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_D;
+ end
+ return value;
+ end
+
+ if (value[62:0] == '0) begin
+ return value;
+ end
+
+ if (sign) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_D;
+ end
+
+ if (exp == '0) begin
+ sig_msb = highest_bit_53({1'b0, frac});
+ unbiased_exp = 13'(sig_msb - 1074);
+ sig = {1'b0, frac} << (52 - sig_msb);
+ end else begin
+ unbiased_exp = $signed({2'b0, exp}) - 13'sd1023;
+ sig = {1'b1, frac};
+ end
+
+ if (unbiased_exp[0]) begin
+ radicand = {74'b0, sig, 1'b0} << 58;
+ result_unbiased_exp = (unbiased_exp - 13'sd1) >>> 1;
+ end else begin
+ radicand = {75'b0, sig} << 58;
+ result_unbiased_exp = unbiased_exp >>> 1;
+ end
+
+ root = '0;
+ for (int bit_idx = 55; bit_idx >= 0; bit_idx--) begin
+ trial = root | (128'd1 << bit_idx);
+ if ((trial * trial) <= radicand) begin
+ root = trial;
+ end
+ end
+
+ remainder = radicand - (root * root);
+ result_sig = 57'(root);
+ if (remainder != '0) begin
+ result_sig[0] = 1'b1;
+ end
+
+ return round_pack_d(1'b0, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [31:0] fp_fma_s(
+ input logic [31:0] mul_lhs,
+ input logic [31:0] mul_rhs,
+ input logic [31:0] addend,
+ input logic negate_product,
+ input logic negate_addend,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic lhs_sign;
+ logic rhs_sign;
+ logic addend_sign;
+ logic product_sign;
+ logic result_sign;
+ logic [7:0] lhs_exp;
+ logic [7:0] rhs_exp;
+ logic [7:0] addend_exp;
+ logic [22:0] lhs_frac;
+ logic [22:0] rhs_frac;
+ logic [22:0] addend_frac;
+ logic lhs_is_zero;
+ logic rhs_is_zero;
+ logic addend_is_zero;
+ logic lhs_is_inf;
+ logic rhs_is_inf;
+ logic addend_is_inf;
+ logic invalid_multiply;
+ logic signed [10:0] lhs_unbiased_exp;
+ logic signed [10:0] rhs_unbiased_exp;
+ logic signed [10:0] addend_unbiased_exp;
+ logic signed [10:0] product_unbiased_exp;
+ logic signed [10:0] common_unbiased_exp;
+ logic signed [10:0] result_unbiased_exp;
+ logic [23:0] lhs_sig;
+ logic [23:0] rhs_sig;
+ logic [23:0] addend_sig;
+ logic [47:0] product;
+ logic [47:0] normalized_product;
+ logic [64:0] product_ext;
+ logic [64:0] addend_ext;
+ logic [64:0] aligned_product;
+ logic [64:0] aligned_addend;
+ logic [64:0] result_ext;
+ logic [64:0] rounded_source;
+ logic [27:0] result_sig;
+ int lhs_msb;
+ int rhs_msb;
+ int addend_msb;
+ int exp_diff;
+
+ flags = '0;
+ lhs_sign = mul_lhs[31];
+ rhs_sign = mul_rhs[31];
+ addend_sign = addend[31] ^ negate_addend;
+ product_sign = lhs_sign ^ rhs_sign ^ negate_product;
+ lhs_exp = mul_lhs[30:23];
+ rhs_exp = mul_rhs[30:23];
+ addend_exp = addend[30:23];
+ lhs_frac = mul_lhs[22:0];
+ rhs_frac = mul_rhs[22:0];
+ addend_frac = addend[22:0];
+ lhs_is_zero = (mul_lhs[30:0] == '0);
+ rhs_is_zero = (mul_rhs[30:0] == '0);
+ addend_is_zero = (addend[30:0] == '0);
+ lhs_is_inf = (&lhs_exp) && (lhs_frac == '0);
+ rhs_is_inf = (&rhs_exp) && (rhs_frac == '0);
+ addend_is_inf = (&addend_exp) && (addend_frac == '0);
+ invalid_multiply = (lhs_is_inf && rhs_is_zero) || (rhs_is_inf && lhs_is_zero);
+
+ if (is_nan_s(mul_lhs) || is_nan_s(mul_rhs) || is_nan_s(addend) || invalid_multiply) begin
+ flags[4] = invalid_multiply || is_snan_s(mul_lhs) || is_snan_s(mul_rhs) || is_snan_s(addend);
+ return CANONICAL_NAN_S;
+ end
+
+ if (lhs_is_inf || rhs_is_inf) begin
+ if (addend_is_inf && (product_sign != addend_sign)) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_S;
+ end
+ return {product_sign, 8'hFF, 23'b0};
+ end
+
+ if (addend_is_inf) begin
+ return {addend_sign, 8'hFF, 23'b0};
+ end
+
+ if (lhs_is_zero || rhs_is_zero) begin
+ if (addend_is_zero) begin
+ result_sign = (product_sign == addend_sign) ? product_sign : (effective_rm(round_mode) == 3'b010);
+ return {result_sign, 31'b0};
+ end
+ return {addend_sign, addend[30:0]};
+ end
+
+ if (addend_is_zero) begin
+ return fp_mul_s({mul_lhs[31] ^ negate_product, mul_lhs[30:0]}, mul_rhs, round_mode, flags);
+ end
+
+ if (lhs_exp == '0) begin
+ lhs_msb = highest_bit_64({41'b0, lhs_frac});
+ lhs_unbiased_exp = 11'(lhs_msb - 149);
+ lhs_sig = {1'b0, lhs_frac} << (23 - lhs_msb);
+ end else begin
+ lhs_unbiased_exp = $signed({3'b0, lhs_exp}) - 11'sd127;
+ lhs_sig = {1'b1, lhs_frac};
+ end
+
+ if (rhs_exp == '0) begin
+ rhs_msb = highest_bit_64({41'b0, rhs_frac});
+ rhs_unbiased_exp = 11'(rhs_msb - 149);
+ rhs_sig = {1'b0, rhs_frac} << (23 - rhs_msb);
+ end else begin
+ rhs_unbiased_exp = $signed({3'b0, rhs_exp}) - 11'sd127;
+ rhs_sig = {1'b1, rhs_frac};
+ end
+
+ if (addend_exp == '0) begin
+ addend_msb = highest_bit_64({41'b0, addend_frac});
+ addend_unbiased_exp = 11'(addend_msb - 149);
+ addend_sig = {1'b0, addend_frac} << (23 - addend_msb);
+ end else begin
+ addend_unbiased_exp = $signed({3'b0, addend_exp}) - 11'sd127;
+ addend_sig = {1'b1, addend_frac};
+ end
+
+ product = lhs_sig * rhs_sig;
+ if (product[47]) begin
+ product_unbiased_exp = lhs_unbiased_exp + rhs_unbiased_exp + 11'sd1;
+ normalized_product = product;
+ end else begin
+ product_unbiased_exp = lhs_unbiased_exp + rhs_unbiased_exp;
+ normalized_product = product << 1;
+ end
+
+ product_ext = {4'b0, normalized_product, 13'b0};
+ addend_ext = {4'b0, addend_sig, 37'b0};
+
+ if (product_unbiased_exp >= addend_unbiased_exp) begin
+ common_unbiased_exp = product_unbiased_exp;
+ exp_diff = int'(product_unbiased_exp) - int'(addend_unbiased_exp);
+ aligned_product = product_ext;
+ aligned_addend = shift_right_jam_65(addend_ext, exp_diff);
+ end else begin
+ common_unbiased_exp = addend_unbiased_exp;
+ exp_diff = int'(addend_unbiased_exp) - int'(product_unbiased_exp);
+ aligned_product = shift_right_jam_65(product_ext, exp_diff);
+ aligned_addend = addend_ext;
+ end
+
+ result_unbiased_exp = common_unbiased_exp;
+ if (product_sign == addend_sign) begin
+ result_ext = aligned_product + aligned_addend;
+ result_sign = product_sign;
+ if (result_ext[61]) begin
+ result_ext = shift_right_jam_65(result_ext, 1);
+ result_unbiased_exp = result_unbiased_exp + 11'sd1;
+ end
+ end else if (aligned_product >= aligned_addend) begin
+ result_ext = aligned_product - aligned_addend;
+ result_sign = product_sign;
+ end else begin
+ result_ext = aligned_addend - aligned_product;
+ result_sign = addend_sign;
+ end
+
+ if (result_ext == '0) begin
+ result_sign = effective_rm(round_mode) == 3'b010;
+ return {result_sign, 31'b0};
+ end
+
+ while (!result_ext[60] && (result_unbiased_exp > -11'sd126)) begin
+ result_ext = result_ext << 1;
+ result_unbiased_exp = result_unbiased_exp - 11'sd1;
+ end
+
+ rounded_source = shift_right_jam_65(result_ext, 34);
+ result_sig = rounded_source[27:0];
+ return round_pack_s(result_sign, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [63:0] fp_fma_d(
+ input logic [63:0] mul_lhs,
+ input logic [63:0] mul_rhs,
+ input logic [63:0] addend,
+ input logic negate_product,
+ input logic negate_addend,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic lhs_sign;
+ logic rhs_sign;
+ logic addend_sign;
+ logic product_sign;
+ logic result_sign;
+ logic [10:0] lhs_exp;
+ logic [10:0] rhs_exp;
+ logic [10:0] addend_exp;
+ logic [51:0] lhs_frac;
+ logic [51:0] rhs_frac;
+ logic [51:0] addend_frac;
+ logic lhs_is_zero;
+ logic rhs_is_zero;
+ logic addend_is_zero;
+ logic lhs_is_inf;
+ logic rhs_is_inf;
+ logic addend_is_inf;
+ logic invalid_multiply;
+ logic signed [12:0] lhs_unbiased_exp;
+ logic signed [12:0] rhs_unbiased_exp;
+ logic signed [12:0] addend_unbiased_exp;
+ logic signed [12:0] product_unbiased_exp;
+ logic signed [12:0] common_unbiased_exp;
+ logic signed [12:0] result_unbiased_exp;
+ logic [52:0] lhs_sig;
+ logic [52:0] rhs_sig;
+ logic [52:0] addend_sig;
+ logic [105:0] product;
+ logic [105:0] normalized_product;
+ logic [127:0] product_ext;
+ logic [127:0] addend_ext;
+ logic [127:0] aligned_product;
+ logic [127:0] aligned_addend;
+ logic [127:0] result_ext;
+ logic [127:0] rounded_source;
+ logic [56:0] result_sig;
+ int lhs_msb;
+ int rhs_msb;
+ int addend_msb;
+ int exp_diff;
+
+ flags = '0;
+ lhs_sign = mul_lhs[63];
+ rhs_sign = mul_rhs[63];
+ addend_sign = addend[63] ^ negate_addend;
+ product_sign = lhs_sign ^ rhs_sign ^ negate_product;
+ lhs_exp = mul_lhs[62:52];
+ rhs_exp = mul_rhs[62:52];
+ addend_exp = addend[62:52];
+ lhs_frac = mul_lhs[51:0];
+ rhs_frac = mul_rhs[51:0];
+ addend_frac = addend[51:0];
+ lhs_is_zero = (mul_lhs[62:0] == '0);
+ rhs_is_zero = (mul_rhs[62:0] == '0);
+ addend_is_zero = (addend[62:0] == '0);
+ lhs_is_inf = (&lhs_exp) && (lhs_frac == '0);
+ rhs_is_inf = (&rhs_exp) && (rhs_frac == '0);
+ addend_is_inf = (&addend_exp) && (addend_frac == '0);
+ invalid_multiply = (lhs_is_inf && rhs_is_zero) || (rhs_is_inf && lhs_is_zero);
+
+ if (is_nan_d(mul_lhs) || is_nan_d(mul_rhs) || is_nan_d(addend) || invalid_multiply) begin
+ flags[4] = invalid_multiply || is_snan_d(mul_lhs) || is_snan_d(mul_rhs) || is_snan_d(addend);
+ return CANONICAL_NAN_D;
+ end
+
+ if (lhs_is_inf || rhs_is_inf) begin
+ if (addend_is_inf && (product_sign != addend_sign)) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_D;
+ end
+ return {product_sign, 11'h7FF, 52'b0};
+ end
+
+ if (addend_is_inf) begin
+ return {addend_sign, 11'h7FF, 52'b0};
+ end
+
+ if (lhs_is_zero || rhs_is_zero) begin
+ if (addend_is_zero) begin
+ result_sign = (product_sign == addend_sign) ? product_sign : (effective_rm(round_mode) == 3'b010);
+ return {result_sign, 63'b0};
+ end
+ return {addend_sign, addend[62:0]};
+ end
+
+ if (addend_is_zero) begin
+ return fp_mul_d({mul_lhs[63] ^ negate_product, mul_lhs[62:0]}, mul_rhs, round_mode, flags);
+ end
+
+ if (lhs_exp == '0) begin
+ lhs_msb = highest_bit_53({1'b0, lhs_frac});
+ lhs_unbiased_exp = 13'(lhs_msb - 1074);
+ lhs_sig = {1'b0, lhs_frac} << (52 - lhs_msb);
+ end else begin
+ lhs_unbiased_exp = $signed({2'b0, lhs_exp}) - 13'sd1023;
+ lhs_sig = {1'b1, lhs_frac};
+ end
+
+ if (rhs_exp == '0) begin
+ rhs_msb = highest_bit_53({1'b0, rhs_frac});
+ rhs_unbiased_exp = 13'(rhs_msb - 1074);
+ rhs_sig = {1'b0, rhs_frac} << (52 - rhs_msb);
+ end else begin
+ rhs_unbiased_exp = $signed({2'b0, rhs_exp}) - 13'sd1023;
+ rhs_sig = {1'b1, rhs_frac};
+ end
+
+ if (addend_exp == '0) begin
+ addend_msb = highest_bit_53({1'b0, addend_frac});
+ addend_unbiased_exp = 13'(addend_msb - 1074);
+ addend_sig = {1'b0, addend_frac} << (52 - addend_msb);
+ end else begin
+ addend_unbiased_exp = $signed({2'b0, addend_exp}) - 13'sd1023;
+ addend_sig = {1'b1, addend_frac};
+ end
+
+ product = lhs_sig * rhs_sig;
+ if (product[105]) begin
+ product_unbiased_exp = lhs_unbiased_exp + rhs_unbiased_exp + 13'sd1;
+ normalized_product = product;
+ end else begin
+ product_unbiased_exp = lhs_unbiased_exp + rhs_unbiased_exp;
+ normalized_product = product << 1;
+ end
+
+ product_ext = {9'b0, normalized_product, 13'b0};
+ addend_ext = {9'b0, addend_sig, 66'b0};
+
+ if (product_unbiased_exp >= addend_unbiased_exp) begin
+ common_unbiased_exp = product_unbiased_exp;
+ exp_diff = int'(product_unbiased_exp) - int'(addend_unbiased_exp);
+ aligned_product = product_ext;
+ aligned_addend = shift_right_jam_128(addend_ext, exp_diff);
+ end else begin
+ common_unbiased_exp = addend_unbiased_exp;
+ exp_diff = int'(addend_unbiased_exp) - int'(product_unbiased_exp);
+ aligned_product = shift_right_jam_128(product_ext, exp_diff);
+ aligned_addend = addend_ext;
+ end
+
+ result_unbiased_exp = common_unbiased_exp;
+ if (product_sign == addend_sign) begin
+ result_ext = aligned_product + aligned_addend;
+ result_sign = product_sign;
+ if (result_ext[119]) begin
+ result_ext = shift_right_jam_128(result_ext, 1);
+ result_unbiased_exp = result_unbiased_exp + 13'sd1;
+ end
+ end else if (aligned_product >= aligned_addend) begin
+ result_ext = aligned_product - aligned_addend;
+ result_sign = product_sign;
+ end else begin
+ result_ext = aligned_addend - aligned_product;
+ result_sign = addend_sign;
+ end
+
+ if (result_ext == '0) begin
+ result_sign = effective_rm(round_mode) == 3'b010;
+ return {result_sign, 63'b0};
+ end
+
+ while (!result_ext[118] && (result_unbiased_exp > -13'sd1022)) begin
+ result_ext = result_ext << 1;
+ result_unbiased_exp = result_unbiased_exp - 13'sd1;
+ end
+
+ rounded_source = shift_right_jam_128(result_ext, 63);
+ result_sig = rounded_source[56:0];
+ return round_pack_d(result_sign, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [63:0] pack_uint_to_s(
+ input logic sign,
+ input logic [63:0] magnitude,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ int msb_index;
+ int shift;
+ logic [127:0] quotient;
+ logic [127:0] remainder;
+ logic [127:0] divisor;
+ logic [127:0] half;
+ logic [24:0] rounded_sig;
+ logic [7:0] exp_field;
+ logic [31:0] result_s;
+
+ flags = '0;
+ if (magnitude == '0) begin
+ return {32'hFFFF_FFFF, sign, 31'b0};
+ end
+
+ msb_index = highest_bit_64(magnitude);
+ exp_field = 8'(msb_index + 127);
+ if (msb_index <= 23) begin
+ rounded_sig = {1'b0, magnitude[23:0] << (23 - msb_index)};
+ end else begin
+ shift = msb_index - 23;
+ divisor = 128'd1 << shift;
+ quotient = {64'b0, magnitude} >> shift;
+ remainder = {64'b0, magnitude} & (divisor - 128'd1);
+ half = divisor >> 1;
+ rounded_sig = {1'b0, quotient[23:0]} +
+ (round_increment(round_mode, sign, quotient, remainder, half) ? 25'd1 : 25'd0);
+ if (remainder != '0) begin
+ flags[0] = 1'b1;
+ end
+ if (rounded_sig[24]) begin
+ rounded_sig = rounded_sig >> 1;
+ exp_field = exp_field + 8'd1;
+ end
+ end
+
+ result_s = {sign, exp_field, rounded_sig[22:0]};
+ return {32'hFFFF_FFFF, result_s};
+ endfunction
+
+ function automatic logic [63:0] pack_uint_to_d(
+ input logic sign,
+ input logic [63:0] magnitude,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ int msb_index;
+ int shift;
+ logic [127:0] quotient;
+ logic [127:0] remainder;
+ logic [127:0] divisor;
+ logic [127:0] half;
+ logic [53:0] rounded_sig;
+ logic [10:0] exp_field;
+
+ flags = '0;
+ if (magnitude == '0) begin
+ return {sign, 63'b0};
+ end
+
+ msb_index = highest_bit_64(magnitude);
+ exp_field = 11'(msb_index + 1023);
+ if (msb_index <= 52) begin
+ rounded_sig = {1'b0, magnitude[52:0] << (52 - msb_index)};
+ end else begin
+ shift = msb_index - 52;
+ divisor = 128'd1 << shift;
+ quotient = {64'b0, magnitude} >> shift;
+ remainder = {64'b0, magnitude} & (divisor - 128'd1);
+ half = divisor >> 1;
+ rounded_sig = {1'b0, quotient[52:0]} +
+ (round_increment(round_mode, sign, quotient, remainder, half) ? 54'd1 : 54'd0);
+ if (remainder != '0) begin
+ flags[0] = 1'b1;
+ end
+ if (rounded_sig[53]) begin
+ rounded_sig = rounded_sig >> 1;
+ exp_field = exp_field + 11'd1;
+ end
+ end
+
+ return {sign, exp_field, rounded_sig[51:0]};
+ endfunction
+
+ function automatic logic [63:0] cvt_int_to_fp(
+ input logic [63:0] int_value,
+ input logic [1:0] int_fmt,
+ input logic to_double,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic is_signed;
+ logic is_long;
+ logic sign;
+ logic [63:0] source_value;
+ logic [63:0] magnitude;
+
+ is_signed = (int_fmt == 2'd0) || (int_fmt == 2'd2);
+ is_long = (int_fmt == 2'd2) || (int_fmt == 2'd3);
+ source_value = is_long ? int_value :
+ (is_signed ? {{32{int_value[31]}}, int_value[31:0]} : {32'b0, int_value[31:0]});
+ sign = is_signed && source_value[63];
+ magnitude = sign ? ((~source_value) + 64'd1) : source_value;
+
+ if (to_double) begin
+ return pack_uint_to_d(sign, magnitude, round_mode, flags);
+ end
+
+ return pack_uint_to_s(sign, magnitude, round_mode, flags);
+ endfunction
+
+ function automatic logic [63:0] cvt_s_to_d(
+ input logic [31:0] value,
+ output logic [4:0] flags
+ );
+ logic sign;
+ logic [7:0] exp;
+ logic [22:0] frac;
+ int frac_msb;
+ logic [10:0] exp_d;
+ logic [51:0] frac_d;
+ logic [22:0] subnormal_tail_s;
+
+ flags = '0;
+ sign = value[31];
+ exp = value[30:23];
+ frac = value[22:0];
+
+ if (is_nan_s(value)) begin
+ flags[4] = is_snan_s(value);
+ return CANONICAL_NAN_D;
+ end
+ if (&exp) begin
+ return {sign, 11'h7FF, 52'b0};
+ end
+ if ((exp == '0) && (frac == '0)) begin
+ return {sign, 63'b0};
+ end
+ if (exp == '0) begin
+ frac_msb = -1;
+ for (int bit_idx = 0; bit_idx < 23; bit_idx++) begin
+ if (frac[bit_idx]) begin
+ frac_msb = bit_idx;
+ end
+ end
+ exp_d = 11'(frac_msb - 149 + 1023);
+ subnormal_tail_s = frac ^ (23'(1) << frac_msb);
+ frac_d = {29'b0, subnormal_tail_s} << (52 - frac_msb);
+ return {sign, exp_d, frac_d};
+ end
+
+ exp_d = 11'(int'(exp) - 127 + 1023);
+ frac_d = {frac, 29'b0};
+ return {sign, exp_d, frac_d};
+ endfunction
+
+ function automatic logic [63:0] cvt_d_to_s(
+ input logic [63:0] value,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic sign;
+ logic [10:0] exp;
+ logic [51:0] frac;
+ logic [52:0] significand;
+ logic signed [13:0] unbiased_exp;
+ int frac_msb;
+ int shift;
+ logic [127:0] quotient;
+ logic [127:0] remainder;
+ logic [127:0] divisor;
+ logic [127:0] half;
+ logic [24:0] rounded_sig;
+ logic [7:0] exp_s;
+ logic [31:0] result_s;
+
+ flags = '0;
+ sign = value[63];
+ exp = value[62:52];
+ frac = value[51:0];
+
+ if (is_nan_d(value)) begin
+ flags[4] = is_snan_d(value);
+ return {32'hFFFF_FFFF, CANONICAL_NAN_S};
+ end
+ if (&exp) begin
+ return {32'hFFFF_FFFF, sign, 8'hFF, 23'b0};
+ end
+ if ((exp == '0) && (frac == '0)) begin
+ return {32'hFFFF_FFFF, sign, 31'b0};
+ end
+
+ if (exp == '0) begin
+ frac_msb = highest_bit_53({1'b0, frac});
+ unbiased_exp = 14'(frac_msb - 1074);
+ significand = {1'b0, frac} << (52 - frac_msb);
+ end else begin
+ unbiased_exp = $signed({3'b0, exp}) - 14'sd1023;
+ significand = {1'b1, frac};
+ end
+
+ if (unbiased_exp > 14'sd127) begin
+ flags[2] = 1'b1;
+ flags[0] = 1'b1;
+ return {32'hFFFF_FFFF, overflow_s_result(sign, round_mode)};
+ end
+
+ if (unbiased_exp >= -14'sd126) begin
+ exp_s = 8'(unbiased_exp + 14'sd127);
+ shift = 29;
+ divisor = 128'd1 << shift;
+ quotient = {75'b0, significand} >> shift;
+ remainder = {75'b0, significand} & (divisor - 128'd1);
+ half = divisor >> 1;
+ rounded_sig = {1'b0, quotient[23:0]} +
+ (round_increment(round_mode, sign, quotient, remainder, half) ? 25'd1 : 25'd0);
+ if (remainder != '0) begin
+ flags[0] = 1'b1;
+ end
+ if (rounded_sig[24]) begin
+ rounded_sig = rounded_sig >> 1;
+ exp_s = exp_s + 8'd1;
+ if (exp_s == 8'hFF) begin
+ flags[2] = 1'b1;
+ flags[0] = 1'b1;
+ return {32'hFFFF_FFFF, overflow_s_result(sign, round_mode)};
+ end
+ end
+ result_s = {sign, exp_s, rounded_sig[22:0]};
+ return {32'hFFFF_FFFF, result_s};
+ end
+
+ shift = 29 + ((-126) - int'(unbiased_exp));
+ if (shift >= 128) begin
+ quotient = '0;
+ remainder = {75'b0, significand};
+ rounded_sig = '0;
+ if (remainder != '0) begin
+ flags[1] = 1'b1;
+ flags[0] = 1'b1;
+ unique case (effective_rm(round_mode))
+ 3'b010: rounded_sig = sign ? 25'd1 : 25'd0;
+ 3'b011: rounded_sig = !sign ? 25'd1 : 25'd0;
+ default: rounded_sig = '0;
+ endcase
+ end
+ end else begin
+ divisor = 128'd1 << shift;
+ quotient = {75'b0, significand} >> shift;
+ remainder = {75'b0, significand} & (divisor - 128'd1);
+ half = divisor >> 1;
+ rounded_sig = {1'b0, quotient[23:0]} +
+ (round_increment(round_mode, sign, quotient, remainder, half) ? 25'd1 : 25'd0);
+ if (remainder != '0) begin
+ flags[1] = 1'b1;
+ flags[0] = 1'b1;
+ end
+ end
+
+ if (rounded_sig[23]) begin
+ result_s = {sign, 8'd1, 23'b0};
+ end else begin
+ result_s = {sign, 8'b0, rounded_sig[22:0]};
+ end
+ return {32'hFFFF_FFFF, result_s};
+ endfunction
+
+ always_comb begin
+ logic invalid_s;
+ logic invalid_d;
+ logic cmp_value;
+ logic [31:0] result_s;
+ logic [63:0] result_d;
+ logic [63:0] result_int;
+ logic [4:0] result_flags;
+
+ fp_result = '0;
+ int_result = '0;
+ fflags = '0;
+ illegal_instr = 1'b0;
+ invalid_s = is_nan_s(s1) || is_nan_s(s2);
+ invalid_d = is_nan_d(d1) || is_nan_d(d2);
+ cmp_value = 1'b0;
+ result_s = '0;
+ result_d = '0;
+ result_int = '0;
+ result_flags = '0;
+
+ unique case (fp_op)
+ FP_OP_NONE: begin
+ fp_result = '0;
+ int_result = '0;
+ fflags = '0;
+ end
+
+ FP_OP_ADD, FP_OP_SUB: begin
+ if (fp_fmt == FP_FMT_S) begin
+ result_s = fp_addsub_s(s1, s2, fp_op == FP_OP_SUB, rm, result_flags);
+ fp_result = {32'hFFFF_FFFF, result_s};
+ fflags = result_flags;
+ end else begin
+ result_d = fp_addsub_d(d1, d2, fp_op == FP_OP_SUB, rm, result_flags);
+ fp_result = result_d;
+ fflags = result_flags;
+ end
+ end
+
+ FP_OP_MUL: begin
+ if (fp_fmt == FP_FMT_S) begin
+ result_s = fp_mul_s(s1, s2, rm, result_flags);
+ fp_result = {32'hFFFF_FFFF, result_s};
+ fflags = result_flags;
+ end else begin
+ result_d = fp_mul_d(d1, d2, rm, result_flags);
+ fp_result = result_d;
+ fflags = result_flags;
+ end
+ end
+
+ FP_OP_DIV: begin
+ if (fp_fmt == FP_FMT_S) begin
+ result_s = fp_div_s(s1, s2, rm, result_flags);
+ fp_result = {32'hFFFF_FFFF, result_s};
+ fflags = result_flags;
+ end else begin
+ result_d = fp_div_d(d1, d2, rm, result_flags);
+ fp_result = result_d;
+ fflags = result_flags;
+ end
+ end
+
+ FP_OP_SQRT: begin
+ if (fp_fmt == FP_FMT_S) begin
+ result_s = fp_sqrt_s(s1, rm, result_flags);
+ fp_result = {32'hFFFF_FFFF, result_s};
+ fflags = result_flags;
+ end else begin
+ result_d = fp_sqrt_d(d1, rm, result_flags);
+ fp_result = result_d;
+ fflags = result_flags;
+ end
+ end
+
+ FP_OP_MADD, FP_OP_MSUB, FP_OP_NMSUB, FP_OP_NMADD: begin
+ if (fp_fmt == FP_FMT_S) begin
+ unique case (fp_op)
+ FP_OP_MADD: result_s = fp_fma_s(s1, s2, s3, 1'b0, 1'b0, rm, result_flags);
+ FP_OP_MSUB: result_s = fp_fma_s(s1, s2, s3, 1'b0, 1'b1, rm, result_flags);
+ FP_OP_NMSUB: result_s = fp_fma_s(s1, s2, s3, 1'b1, 1'b0, rm, result_flags);
+ default: result_s = fp_fma_s(s1, s2, s3, 1'b1, 1'b1, rm, result_flags);
+ endcase
+ fp_result = {32'hFFFF_FFFF, result_s};
+ fflags = result_flags;
+ end else begin
+ unique case (fp_op)
+ FP_OP_MADD: result_d = fp_fma_d(d1, d2, d3, 1'b0, 1'b0, rm, result_flags);
+ FP_OP_MSUB: result_d = fp_fma_d(d1, d2, d3, 1'b0, 1'b1, rm, result_flags);
+ FP_OP_NMSUB: result_d = fp_fma_d(d1, d2, d3, 1'b1, 1'b0, rm, result_flags);
+ default: result_d = fp_fma_d(d1, d2, d3, 1'b1, 1'b1, rm, result_flags);
+ endcase
+ fp_result = result_d;
+ fflags = result_flags;
+ end
+ end
+
+ FP_OP_SGNJ, FP_OP_SGNJN, FP_OP_SGNJX: begin
+ if (fp_fmt == FP_FMT_S) begin
+ unique case (fp_op)
+ FP_OP_SGNJ: fp_result = {32'hFFFF_FFFF, s2[31], s1[30:0]};
+ FP_OP_SGNJN: fp_result = {32'hFFFF_FFFF, ~s2[31], s1[30:0]};
+ default: fp_result = {32'hFFFF_FFFF, s1[31] ^ s2[31], s1[30:0]};
+ endcase
+ end else begin
+ unique case (fp_op)
+ FP_OP_SGNJ: fp_result = {d2[63], d1[62:0]};
+ FP_OP_SGNJN: fp_result = {~d2[63], d1[62:0]};
+ default: fp_result = {d1[63] ^ d2[63], d1[62:0]};
+ endcase
+ end
+ end
+
+ FP_OP_MV_X2F: begin
+ fp_result = (fp_fmt == FP_FMT_S) ? {32'hFFFF_FFFF, int_rs1_data[31:0]} : int_rs1_data;
+ end
+
+ FP_OP_MV_F2X: begin
+ int_result = (fp_fmt == FP_FMT_S) ? {{32{s1_raw[31]}}, s1_raw} : d1;
+ end
+
+ FP_OP_CLASS: begin
+ int_result = (fp_fmt == FP_FMT_S) ? {54'b0, class_s(s1)} : {54'b0, class_d(d1)};
+ end
+
+ FP_OP_EQ, FP_OP_LT, FP_OP_LE: begin
+ if (fp_fmt == FP_FMT_S) begin
+ if (invalid_s) begin
+ fflags[4] = is_snan_s(s1) || is_snan_s(s2) || (fp_op != FP_OP_EQ);
+ cmp_value = 1'b0;
+ end else begin
+ cmp_value = (fp_op == FP_OP_EQ) ? equal_s(s1, s2) :
+ (fp_op == FP_OP_LT) ? less_s(s1, s2) :
+ (less_s(s1, s2) || equal_s(s1, s2));
+ end
+ end else begin
+ if (invalid_d) begin
+ fflags[4] = is_snan_d(d1) || is_snan_d(d2) || (fp_op != FP_OP_EQ);
+ cmp_value = 1'b0;
+ end else begin
+ cmp_value = (fp_op == FP_OP_EQ) ? equal_d(d1, d2) :
+ (fp_op == FP_OP_LT) ? less_d(d1, d2) :
+ (less_d(d1, d2) || equal_d(d1, d2));
+ end
+ end
+ int_result = {63'b0, cmp_value};
+ end
+
+ FP_OP_MIN, FP_OP_MAX: begin
+ if (fp_fmt == FP_FMT_S) begin
+ if (is_nan_s(s1) && is_nan_s(s2)) begin
+ fp_result = {32'hFFFF_FFFF, CANONICAL_NAN_S};
+ fflags[4] = is_snan_s(s1) || is_snan_s(s2);
+ end else if (is_nan_s(s1)) begin
+ fp_result = {32'hFFFF_FFFF, s2};
+ fflags[4] = is_snan_s(s1);
+ end else if (is_nan_s(s2)) begin
+ fp_result = {32'hFFFF_FFFF, s1};
+ fflags[4] = is_snan_s(s2);
+ end else if ((s1[30:0] == '0) && (s2[30:0] == '0)) begin
+ fp_result = {32'hFFFF_FFFF, (fp_op == FP_OP_MIN) ? {s1[31] | s2[31], 31'b0} :
+ {s1[31] & s2[31], 31'b0}};
+ end else if (fp_op == FP_OP_MIN) begin
+ fp_result = {32'hFFFF_FFFF, less_s(s1, s2) ? s1 : s2};
+ end else begin
+ fp_result = {32'hFFFF_FFFF, less_s(s1, s2) ? s2 : s1};
+ end
+ end else begin
+ if (is_nan_d(d1) && is_nan_d(d2)) begin
+ fp_result = CANONICAL_NAN_D;
+ fflags[4] = is_snan_d(d1) || is_snan_d(d2);
+ end else if (is_nan_d(d1)) begin
+ fp_result = d2;
+ fflags[4] = is_snan_d(d1);
+ end else if (is_nan_d(d2)) begin
+ fp_result = d1;
+ fflags[4] = is_snan_d(d2);
+ end else if ((d1[62:0] == '0) && (d2[62:0] == '0)) begin
+ fp_result = (fp_op == FP_OP_MIN) ? {d1[63] | d2[63], 63'b0} :
+ {d1[63] & d2[63], 63'b0};
+ end else if (fp_op == FP_OP_MIN) begin
+ fp_result = less_d(d1, d2) ? d1 : d2;
+ end else begin
+ fp_result = less_d(d1, d2) ? d2 : d1;
+ end
+ end
+ end
+
+ FP_OP_CVT_F2I: begin
+ if (fp_fmt == FP_FMT_S) begin
+ result_int = cvt_s_to_int(s1, rs2_addr[1:0], rm, result_flags);
+ end else begin
+ result_int = cvt_d_to_int(d1, rs2_addr[1:0], rm, result_flags);
+ end
+ int_result = result_int;
+ fflags = result_flags;
+ end
+
+ FP_OP_CVT_I2F: begin
+ result_d = cvt_int_to_fp(int_rs1_data, rs2_addr[1:0], fp_fmt == FP_FMT_D, rm, result_flags);
+ fp_result = result_d;
+ fflags = result_flags;
+ end
+
+ FP_OP_CVT_F2F: begin
+ if (fp_fmt == FP_FMT_D) begin
+ result_d = cvt_s_to_d(s1, result_flags);
+ fp_result = result_d;
+ if (is_nan_s(s1)) begin
+ result_flags[4] = is_snan_s(s1);
+ end
+ end else begin
+ result_d = cvt_d_to_s(d1, rm, result_flags);
+ fp_result = result_d;
+ if (is_nan_d(d1)) begin
+ result_flags[4] = is_snan_d(d1);
+ end
+ end
+ fflags = result_flags;
+ end
+
+ default: begin
+ fp_result = (fp_fmt == FP_FMT_S) ? {32'hFFFF_FFFF, CANONICAL_NAN_S} : CANONICAL_NAN_D;
+ int_result = '0;
+ fflags[4] = 1'b1;
+ end
+ endcase
+ end
+
+endmodule
diff --git a/rv64cpu/rtl/core/hazard_unit.sv b/rv64cpu/rtl/core/hazard_unit.sv
index aef550c..ee90356 100644
--- a/rv64cpu/rtl/core/hazard_unit.sv
+++ b/rv64cpu/rtl/core/hazard_unit.sv
@@ -36,6 +36,7 @@ module hazard_unit
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,
@@ -44,6 +45,8 @@ module hazard_unit
// -----------------------
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
// -----------------------
@@ -96,7 +99,7 @@ module hazard_unit
// Operand A (ex_rs1_addr)
always_comb begin
- if (mem_reg_write_en && (mem_rd_addr != 5'b0) && (mem_rd_addr == ex_rs1_addr)) 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
@@ -107,7 +110,7 @@ module hazard_unit
// Operand B (ex_rs2_addr)
always_comb begin
- if (mem_reg_write_en && (mem_rd_addr != 5'b0) && (mem_rd_addr == ex_rs2_addr)) 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
@@ -126,6 +129,12 @@ module hazard_unit
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)
// -----------------------
@@ -168,8 +177,9 @@ module hazard_unit
ex_mem_stall_en = 1'b1;
end
- // (2) Load-Use stall: freeze IF and IF/ID, insert bubble into ID/EX
- if (load_use_stall && !mem_stall_req) begin
+ // (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;
diff --git a/rv64cpu/rtl/core/id_ex_reg.sv b/rv64cpu/rtl/core/id_ex_reg.sv
index 9375bb4..777cc46 100644
--- a/rv64cpu/rtl/core/id_ex_reg.sv
+++ b/rv64cpu/rtl/core/id_ex_reg.sv
@@ -34,13 +34,18 @@ module id_ex_reg
// 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
@@ -56,6 +61,14 @@ module id_ex_reg
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,
@@ -64,6 +77,8 @@ module id_ex_reg
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,
@@ -78,13 +93,18 @@ module id_ex_reg
// 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
@@ -100,6 +120,14 @@ module id_ex_reg
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,
@@ -108,6 +136,8 @@ module id_ex_reg
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,
@@ -121,11 +151,16 @@ module id_ex_reg
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;
@@ -137,22 +172,33 @@ module id_ex_reg
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_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;
+ 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;
@@ -163,27 +209,42 @@ module id_ex_reg
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_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;
+ 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;
@@ -195,20 +256,30 @@ module id_ex_reg
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_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;
+ 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
diff --git a/rv64cpu/rtl/core/id_stage.sv b/rv64cpu/rtl/core/id_stage.sv
index 6a8bab9..9003b42 100644
--- a/rv64cpu/rtl/core/id_stage.sv
+++ b/rv64cpu/rtl/core/id_stage.sv
@@ -26,21 +26,33 @@ module id_stage
// 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)
@@ -56,6 +68,14 @@ module id_stage
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)
@@ -76,17 +96,24 @@ module id_stage
// 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),
@@ -109,6 +136,14 @@ module id_stage
.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),
@@ -145,4 +180,18 @@ module id_stage
.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
index 904f038..0ea4fae 100644
--- a/rv64cpu/rtl/core/if_id_reg.sv
+++ b/rv64cpu/rtl/core/if_id_reg.sv
@@ -32,24 +32,39 @@ module if_id_reg
// 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 [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_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_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_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
diff --git a/rv64cpu/rtl/core/if_stage.sv b/rv64cpu/rtl/core/if_stage.sv
index af6b34a..c3d417b 100644
--- a/rv64cpu/rtl/core/if_stage.sv
+++ b/rv64cpu/rtl/core/if_stage.sv
@@ -28,6 +28,7 @@ module if_stage
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
);
@@ -39,7 +40,7 @@ module if_stage
logic [XLEN-1:0] next_pc;
always_comb begin
- next_pc = pc_reg + 64'd4;
+ next_pc = pc_reg + (if_instr_compressed ? 64'd2 : 64'd4);
end
// Sequential Logic: Update PC Register
diff --git a/rv64cpu/rtl/core/include/csr_pkg.sv b/rv64cpu/rtl/core/include/csr_pkg.sv
index a908538..d1f9416 100644
--- a/rv64cpu/rtl/core/include/csr_pkg.sv
+++ b/rv64cpu/rtl/core/include/csr_pkg.sv
@@ -41,6 +41,7 @@ package csr_pkg;
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
@@ -50,14 +51,32 @@ package csr_pkg;
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)
@@ -66,6 +85,11 @@ package csr_pkg;
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
@@ -74,11 +98,45 @@ package csr_pkg;
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)
@@ -98,6 +156,23 @@ package csr_pkg;
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)
// ---------------------------------
@@ -114,4 +189,44 @@ package csr_pkg;
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
index 372cc1b..afa7979 100644
--- a/rv64cpu/rtl/core/include/rv64_pkg.sv
+++ b/rv64cpu/rtl/core/include/rv64_pkg.sv
@@ -79,17 +79,25 @@ package rv64_pkg;
// ALU Operation Encoding
// Derived from funct3 + funct7[5] of R-type / I-type instructions
- typedef enum logic [3:0] {
- ALU_ADD = 4'b0000, // Add
- ALU_SUB = 4'b1000, // Subtract
- ALU_SLL = 4'b0001, // Shift Left Logical
- ALU_SLT = 4'b0010, // Set Less Than (signed)
- ALU_SLTU = 4'b0011, // Set Less Than (unsigned)
- ALU_XOR = 4'b0100, // Exclusive Or
- ALU_SRL = 4'b0101, // Shift Right Logical
- ALU_SRA = 4'b1101, // Shift Right Arithmetic
- ALU_OR = 4'b0110, // Or
- ALU_AND = 4'b0111 // And
+ 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;
@@ -104,5 +112,51 @@ package rv64_pkg;
BRANCH_GEU = 3'b111 // BGEU: Branch if Greater Than or Equal (unsigned)
} branch_op_t;
-endpackage
+ 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
index 558d6d6..d5a97dc 100644
--- a/rv64cpu/rtl/core/mem_stage.sv
+++ b/rv64cpu/rtl/core/mem_stage.sv
@@ -22,21 +22,33 @@
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,
@@ -47,6 +59,8 @@ module mem_stage
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,
@@ -65,14 +79,22 @@ module mem_stage
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,
@@ -83,12 +105,16 @@ module mem_stage
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
@@ -97,9 +123,31 @@ module mem_stage
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,
@@ -116,13 +164,67 @@ module mem_stage
return result;
endfunction
- 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);
+ 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 & ~load_addr_misaligned;
- assign dmem_write_req = mem_is_store & ~store_addr_misaligned;
+ 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
// --------------------------
@@ -130,7 +232,9 @@ module mem_stage
// For a simple synchronous SRAM, this might be 0, but for cache it will depend on hits/misses
// --------------------------
always_comb begin
- if ((mem_is_load || mem_is_store) && (!dmem_req_ready || (mem_is_load && !dmem_rsp_valid))) 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;
@@ -144,30 +248,32 @@ module mem_stage
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) begin
+ if (mem_is_store || amo_writes_memory) begin
case (mem_mem_size)
// SB: Store Byte
3'b000: begin
- store_data_aligned = {56'b0, mem_rs2_rdata[7:0]} << (align_offset * 8);
+ 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, mem_rs2_rdata[15:0]} << (align_offset * 8);
+ 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, mem_rs2_rdata[31:0]} << (align_offset * 8);
+ 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 = mem_rs2_rdata;
+ store_data_aligned = amo_writes_memory ? amo_write_data : selected_store_data;
write_strobe = 8'b1111_1111;
end
default: begin
@@ -213,15 +319,88 @@ module mem_stage
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 = load_data_aligned;
+ 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;
@@ -231,13 +410,21 @@ module mem_stage
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_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;
- assign mem_wb_store_fault = store_addr_misaligned;
- assign mem_wb_fault_addr = (load_addr_misaligned | store_addr_misaligned) ? mem_alu_result : '0;
+ 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
index 6e8e13f..be0aec1 100644
--- a/rv64cpu/rtl/core/mem_wb_reg.sv
+++ b/rv64cpu/rtl/core/mem_wb_reg.sv
@@ -36,10 +36,14 @@ module mem_wb_reg
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,
@@ -50,12 +54,16 @@ module mem_wb_reg
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,
// -----------------------
@@ -65,10 +73,14 @@ module mem_wb_reg
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,
@@ -79,72 +91,97 @@ module mem_wb_reg
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_instr <= '0;
- wb_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_is_ecall <= '0;
- wb_is_ebreak <= '0;
- wb_is_mret <= '0;
- wb_is_sret <= '0;
- wb_load_fault <= '0;
- wb_store_fault <= '0;
- wb_fault_addr <= '0;
+ 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_is_csr <= '0;
- wb_illegal_instr <= '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_fault_addr <= '0;
+ 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_instr <= mem_instr;
- wb_reg_write_en <= mem_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_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_fault_addr <= mem_fault_addr;
+ 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
diff --git a/rv64cpu/rtl/core/mmu/mmu_top.sv b/rv64cpu/rtl/core/mmu/mmu_top.sv
new file mode 100644
index 0000000..169bf81
--- /dev/null
+++ b/rv64cpu/rtl/core/mmu/mmu_top.sv
@@ -0,0 +1,359 @@
+// 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 <https://www.gnu.org/licenses/>.
+
+// File path: rtl/core/mmu/mmu_top.sv
+// Description: MMU top-level integrating iTLB + dTLB + shared PTW
+
+module mmu_top
+ import rv64_pkg::*;
+#(
+ parameter int ITLB_ENTRIES = 16,
+ parameter int DTLB_ENTRIES = 32,
+ parameter int ASID_WIDTH = 16
+)(
+ input logic clk,
+ input logic rst,
+
+ // CSR/context inputs
+ input logic [XLEN-1:0] satp_value,
+ input logic [1:0] priv_mode,
+ input logic sum,
+ input logic mxr,
+
+ // i-side translation request
+ input logic i_req_valid,
+ output logic i_req_ready,
+ input logic [XLEN-1:0] i_req_vaddr,
+ output logic i_resp_valid,
+ output logic i_resp_hit,
+ output logic i_resp_page_fault,
+ output logic [XLEN-1:0] i_resp_vaddr,
+ output logic [XLEN-1:0] i_resp_paddr,
+
+ // d-side translation request
+ input logic d_req_valid,
+ output logic d_req_ready,
+ input logic [XLEN-1:0] d_req_vaddr,
+ input logic d_req_is_store,
+ output logic d_resp_valid,
+ output logic d_resp_hit,
+ output logic d_resp_page_fault,
+ output logic [XLEN-1:0] d_resp_paddr,
+
+ // sfence / invalidation
+ input logic flush_all,
+ input logic flush_asid_valid,
+ input logic [ASID_WIDTH-1:0] flush_asid,
+ input logic flush_vpn_valid,
+ input logic [44:0] flush_vpn,
+
+ // PTW memory interface
+ output logic ptw_mem_req_valid,
+ input logic ptw_mem_req_ready,
+ output logic [XLEN-1:0] ptw_mem_req_addr,
+ input logic ptw_mem_resp_valid,
+ input logic [XLEN-1:0] ptw_mem_resp_data
+);
+
+ localparam logic [3:0] SATP_MODE_BARE = 4'd0;
+
+ typedef enum logic [0:0] {
+ PTW_SRC_I = 1'b0,
+ PTW_SRC_D = 1'b1
+ } ptw_src_t;
+
+ logic [ASID_WIDTH-1:0] req_asid;
+ logic [3:0] req_mode;
+
+ logic i_tlb_resp_valid;
+ logic i_tlb_resp_hit;
+ logic [XLEN-1:0] i_tlb_resp_paddr;
+ logic i_tlb_resp_page_fault;
+ logic i_tlb_resp_need_ptw;
+
+ logic d_tlb_resp_valid;
+ logic d_tlb_resp_hit;
+ logic [XLEN-1:0] d_tlb_resp_paddr;
+ logic d_tlb_resp_page_fault;
+ logic d_tlb_resp_need_ptw;
+
+ logic i_pending_q;
+ logic d_pending_q;
+ logic [XLEN-1:0] i_pending_vaddr_q;
+ logic [XLEN-1:0] d_pending_vaddr_q;
+ logic [1:0] i_pending_priv_q;
+ logic [1:0] d_pending_priv_q;
+ logic i_pending_sum_q;
+ logic d_pending_sum_q;
+ logic i_pending_mxr_q;
+ logic d_pending_mxr_q;
+ logic [ASID_WIDTH-1:0] i_pending_asid_q;
+ logic [ASID_WIDTH-1:0] d_pending_asid_q;
+ logic [3:0] i_pending_mode_q;
+ logic [3:0] d_pending_mode_q;
+ logic d_pending_is_store_q;
+
+ logic ptw_req_valid;
+ logic ptw_req_ready;
+ logic ptw_resp_valid;
+ logic ptw_resp_fault;
+ logic [XLEN-1:0] ptw_resp_paddr;
+
+ logic ptw_refill_valid;
+ logic [44:0] ptw_refill_vpn;
+ logic [43:0] ptw_refill_ppn;
+ logic [2:0] ptw_refill_level;
+ logic [3:0] ptw_refill_mode;
+ logic [ASID_WIDTH-1:0] ptw_refill_asid;
+ logic ptw_refill_g;
+ logic ptw_refill_u;
+ logic ptw_refill_r;
+ logic ptw_refill_w;
+ logic ptw_refill_x;
+ logic ptw_refill_a;
+ logic ptw_refill_d;
+
+ logic ptw_busy_q;
+ ptw_src_t ptw_owner_q;
+ ptw_src_t ptw_owner_next;
+
+ logic i_accept;
+ logic d_accept;
+ logic i_fast_resp_valid;
+ logic d_fast_resp_valid;
+ logic i_ptw_resp_sel;
+ logic d_ptw_resp_sel;
+
+ logic i_refill_valid;
+ logic d_refill_valid;
+
+ assign req_mode = satp_value[63:60];
+ assign req_asid = satp_value[59 -: ASID_WIDTH];
+
+ assign i_req_ready = !i_pending_q;
+ assign d_req_ready = !d_pending_q;
+ assign i_accept = i_req_valid && i_req_ready;
+ assign d_accept = d_req_valid && d_req_ready;
+
+ assign i_fast_resp_valid = i_accept && (i_tlb_resp_hit || i_tlb_resp_page_fault);
+ assign d_fast_resp_valid = d_accept && (d_tlb_resp_hit || d_tlb_resp_page_fault);
+
+ assign i_ptw_resp_sel = ptw_resp_valid && (ptw_owner_q == PTW_SRC_I);
+ assign d_ptw_resp_sel = ptw_resp_valid && (ptw_owner_q == PTW_SRC_D);
+
+ assign i_resp_valid = i_fast_resp_valid || i_ptw_resp_sel;
+ assign i_resp_hit = i_fast_resp_valid ? i_tlb_resp_hit : (i_ptw_resp_sel && !ptw_resp_fault);
+ assign i_resp_page_fault = i_fast_resp_valid ? i_tlb_resp_page_fault : (i_ptw_resp_sel && ptw_resp_fault);
+ assign i_resp_vaddr = i_fast_resp_valid ? i_req_vaddr : i_pending_vaddr_q;
+ assign i_resp_paddr = i_fast_resp_valid ? i_tlb_resp_paddr : ptw_resp_paddr;
+
+ assign d_resp_valid = d_fast_resp_valid || d_ptw_resp_sel;
+ assign d_resp_hit = d_fast_resp_valid ? d_tlb_resp_hit : (d_ptw_resp_sel && !ptw_resp_fault);
+ assign d_resp_page_fault = d_fast_resp_valid ? d_tlb_resp_page_fault : (d_ptw_resp_sel && ptw_resp_fault);
+ assign d_resp_paddr = d_fast_resp_valid ? d_tlb_resp_paddr : ptw_resp_paddr;
+
+ always_comb begin
+ if (d_pending_q) begin
+ ptw_owner_next = PTW_SRC_D;
+ end else begin
+ ptw_owner_next = PTW_SRC_I;
+ end
+ end
+
+ assign ptw_req_valid = !ptw_busy_q && (d_pending_q || i_pending_q);
+
+ assign i_refill_valid = ptw_refill_valid && (ptw_owner_q == PTW_SRC_I);
+ assign d_refill_valid = ptw_refill_valid && (ptw_owner_q == PTW_SRC_D);
+
+ always_ff @(posedge clk) begin
+ if (rst) begin
+ i_pending_q <= 1'b0;
+ d_pending_q <= 1'b0;
+ ptw_busy_q <= 1'b0;
+ ptw_owner_q <= PTW_SRC_I;
+ i_pending_vaddr_q <= '0;
+ d_pending_vaddr_q <= '0;
+ i_pending_priv_q <= '0;
+ d_pending_priv_q <= '0;
+ i_pending_sum_q <= 1'b0;
+ d_pending_sum_q <= 1'b0;
+ i_pending_mxr_q <= 1'b0;
+ d_pending_mxr_q <= 1'b0;
+ i_pending_asid_q <= '0;
+ d_pending_asid_q <= '0;
+ i_pending_mode_q <= SATP_MODE_BARE;
+ d_pending_mode_q <= SATP_MODE_BARE;
+ d_pending_is_store_q <= 1'b0;
+ end else begin
+ if (i_accept && i_tlb_resp_need_ptw) begin
+ i_pending_q <= 1'b1;
+ i_pending_vaddr_q <= i_req_vaddr;
+ i_pending_priv_q <= priv_mode;
+ i_pending_sum_q <= sum;
+ i_pending_mxr_q <= mxr;
+ i_pending_asid_q <= req_asid;
+ i_pending_mode_q <= req_mode;
+ end
+
+ if (d_accept && d_tlb_resp_need_ptw) begin
+ d_pending_q <= 1'b1;
+ d_pending_vaddr_q <= d_req_vaddr;
+ d_pending_priv_q <= priv_mode;
+ d_pending_sum_q <= sum;
+ d_pending_mxr_q <= mxr;
+ d_pending_asid_q <= req_asid;
+ d_pending_mode_q <= req_mode;
+ d_pending_is_store_q <= d_req_is_store;
+ end
+
+ if (ptw_req_valid && ptw_req_ready) begin
+ ptw_busy_q <= 1'b1;
+ ptw_owner_q <= ptw_owner_next;
+ end
+
+ if (ptw_resp_valid) begin
+ ptw_busy_q <= 1'b0;
+ if (ptw_owner_q == PTW_SRC_I) begin
+ i_pending_q <= 1'b0;
+ end else begin
+ d_pending_q <= 1'b0;
+ end
+ end
+ end
+ end
+
+ tlb #(
+ .ENTRIES(ITLB_ENTRIES),
+ .ASID_WIDTH(ASID_WIDTH)
+ ) u_itlb (
+ .clk (clk),
+ .rst (rst),
+ .req_valid (i_accept),
+ .req_vaddr (i_req_vaddr),
+ .req_asid (req_asid),
+ .req_mode (req_mode),
+ .req_priv (priv_mode),
+ .req_is_fetch (1'b1),
+ .req_is_store (1'b0),
+ .req_sum (sum),
+ .req_mxr (mxr),
+ .resp_valid (i_tlb_resp_valid),
+ .resp_hit (i_tlb_resp_hit),
+ .resp_paddr (i_tlb_resp_paddr),
+ .resp_page_fault (i_tlb_resp_page_fault),
+ .resp_need_ptw (i_tlb_resp_need_ptw),
+ .refill_valid (i_refill_valid),
+ .refill_vpn (ptw_refill_vpn),
+ .refill_ppn (ptw_refill_ppn),
+ .refill_level (ptw_refill_level),
+ .refill_mode (ptw_refill_mode),
+ .refill_asid (ptw_refill_asid),
+ .refill_g (ptw_refill_g),
+ .refill_u (ptw_refill_u),
+ .refill_r (ptw_refill_r),
+ .refill_w (ptw_refill_w),
+ .refill_x (ptw_refill_x),
+ .refill_a (ptw_refill_a),
+ .refill_d (ptw_refill_d),
+ .flush_all (flush_all),
+ .flush_asid_valid (flush_asid_valid),
+ .flush_asid (flush_asid),
+ .flush_vpn_valid (flush_vpn_valid),
+ .flush_vpn (flush_vpn),
+ .flush_mode (req_mode)
+ );
+
+ tlb #(
+ .ENTRIES(DTLB_ENTRIES),
+ .ASID_WIDTH(ASID_WIDTH)
+ ) u_dtlb (
+ .clk (clk),
+ .rst (rst),
+ .req_valid (d_accept),
+ .req_vaddr (d_req_vaddr),
+ .req_asid (req_asid),
+ .req_mode (req_mode),
+ .req_priv (priv_mode),
+ .req_is_fetch (1'b0),
+ .req_is_store (d_req_is_store),
+ .req_sum (sum),
+ .req_mxr (mxr),
+ .resp_valid (d_tlb_resp_valid),
+ .resp_hit (d_tlb_resp_hit),
+ .resp_paddr (d_tlb_resp_paddr),
+ .resp_page_fault (d_tlb_resp_page_fault),
+ .resp_need_ptw (d_tlb_resp_need_ptw),
+ .refill_valid (d_refill_valid),
+ .refill_vpn (ptw_refill_vpn),
+ .refill_ppn (ptw_refill_ppn),
+ .refill_level (ptw_refill_level),
+ .refill_mode (ptw_refill_mode),
+ .refill_asid (ptw_refill_asid),
+ .refill_g (ptw_refill_g),
+ .refill_u (ptw_refill_u),
+ .refill_r (ptw_refill_r),
+ .refill_w (ptw_refill_w),
+ .refill_x (ptw_refill_x),
+ .refill_a (ptw_refill_a),
+ .refill_d (ptw_refill_d),
+ .flush_all (flush_all),
+ .flush_asid_valid (flush_asid_valid),
+ .flush_asid (flush_asid),
+ .flush_vpn_valid (flush_vpn_valid),
+ .flush_vpn (flush_vpn),
+ .flush_mode (req_mode)
+ );
+
+ ptw #(
+ .ASID_WIDTH(ASID_WIDTH)
+ ) u_ptw (
+ .clk (clk),
+ .rst (rst),
+ .req_valid (ptw_req_valid),
+ .req_ready (ptw_req_ready),
+ .req_vaddr ((ptw_owner_next == PTW_SRC_D) ? d_pending_vaddr_q : i_pending_vaddr_q),
+ .req_asid ((ptw_owner_next == PTW_SRC_D) ? d_pending_asid_q : i_pending_asid_q),
+ .req_mode ((ptw_owner_next == PTW_SRC_D) ? d_pending_mode_q : i_pending_mode_q),
+ .req_priv ((ptw_owner_next == PTW_SRC_D) ? d_pending_priv_q : i_pending_priv_q),
+ .req_is_fetch (ptw_owner_next == PTW_SRC_I),
+ .req_is_store ((ptw_owner_next == PTW_SRC_D) ? d_pending_is_store_q : 1'b0),
+ .req_sum ((ptw_owner_next == PTW_SRC_D) ? d_pending_sum_q : i_pending_sum_q),
+ .req_mxr ((ptw_owner_next == PTW_SRC_D) ? d_pending_mxr_q : i_pending_mxr_q),
+ .satp_value (satp_value),
+ .mem_req_valid (ptw_mem_req_valid),
+ .mem_req_ready (ptw_mem_req_ready),
+ .mem_req_addr (ptw_mem_req_addr),
+ .mem_resp_valid(ptw_mem_resp_valid),
+ .mem_resp_data (ptw_mem_resp_data),
+ .resp_valid (ptw_resp_valid),
+ .resp_fault (ptw_resp_fault),
+ .resp_paddr (ptw_resp_paddr),
+ .refill_valid (ptw_refill_valid),
+ .refill_vpn (ptw_refill_vpn),
+ .refill_ppn (ptw_refill_ppn),
+ .refill_level (ptw_refill_level),
+ .refill_mode (ptw_refill_mode),
+ .refill_asid (ptw_refill_asid),
+ .refill_g (ptw_refill_g),
+ .refill_u (ptw_refill_u),
+ .refill_r (ptw_refill_r),
+ .refill_w (ptw_refill_w),
+ .refill_x (ptw_refill_x),
+ .refill_a (ptw_refill_a),
+ .refill_d (ptw_refill_d)
+ );
+
+endmodule
diff --git a/rv64cpu/rtl/core/mmu/ptw.sv b/rv64cpu/rtl/core/mmu/ptw.sv
new file mode 100644
index 0000000..6447b5a
--- /dev/null
+++ b/rv64cpu/rtl/core/mmu/ptw.sv
@@ -0,0 +1,416 @@
+// 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 <https://www.gnu.org/licenses/>.
+
+// File path: rtl/core/mmu/ptw.sv
+// Description: Page-table walker for Sv39. Sv48/Sv57/Sv64 are explicit stubs.
+
+module ptw
+ import rv64_pkg::*;
+#(
+ parameter int ASID_WIDTH = 16
+)(
+ input logic clk,
+ input logic rst,
+
+ input logic req_valid,
+ output logic req_ready,
+ input logic [XLEN-1:0] req_vaddr,
+ input logic [ASID_WIDTH-1:0] req_asid,
+ input logic [3:0] req_mode,
+ input logic [1:0] req_priv,
+ input logic req_is_fetch,
+ input logic req_is_store,
+ input logic req_sum,
+ input logic req_mxr,
+ input logic [XLEN-1:0] satp_value,
+
+ output logic mem_req_valid,
+ input logic mem_req_ready,
+ output logic [XLEN-1:0] mem_req_addr,
+ input logic mem_resp_valid,
+ input logic [XLEN-1:0] mem_resp_data,
+
+ output logic resp_valid,
+ output logic resp_fault,
+ output logic [XLEN-1:0] resp_paddr,
+
+ output logic refill_valid,
+ output logic [44:0] refill_vpn,
+ output logic [43:0] refill_ppn,
+ output logic [2:0] refill_level,
+ output logic [3:0] refill_mode,
+ output logic [ASID_WIDTH-1:0] refill_asid,
+ output logic refill_g,
+ output logic refill_u,
+ output logic refill_r,
+ output logic refill_w,
+ output logic refill_x,
+ output logic refill_a,
+ output logic refill_d
+);
+
+ 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;
+ localparam logic [3:0] SATP_MODE_SV57 = 4'd10;
+ localparam logic [3:0] SATP_MODE_SV64 = 4'd11; // Reserved/future stub.
+
+ typedef enum logic [2:0] {
+ PTW_IDLE,
+ PTW_ISSUE,
+ PTW_WAIT,
+ PTW_DONE
+ } ptw_state_t;
+
+ ptw_state_t state_q;
+
+ logic [XLEN-1:0] req_vaddr_q;
+ logic [ASID_WIDTH-1:0] req_asid_q;
+ logic [3:0] req_mode_q;
+ logic [1:0] req_priv_q;
+ logic req_is_fetch_q;
+ logic req_is_store_q;
+ logic req_sum_q;
+ logic req_mxr_q;
+
+ logic [43:0] table_ppn_q;
+ logic [2:0] level_q;
+
+ logic pte_v;
+ logic pte_r;
+ logic pte_w;
+ logic pte_x;
+ logic pte_u;
+ logic pte_g;
+ logic pte_a;
+ logic pte_d;
+ logic [43:0] pte_ppn;
+
+ logic leaf_pte;
+ logic bad_pte;
+ logic superpage_misaligned;
+
+ logic [44:0] req_vpn_q;
+ logic [XLEN-1:0] next_paddr;
+
+ logic resp_fault_q;
+ logic [XLEN-1:0] resp_paddr_q;
+ logic refill_valid_q;
+
+ logic [44:0] refill_vpn_q;
+ logic [43:0] refill_ppn_q;
+ logic [2:0] refill_level_q;
+ logic [3:0] refill_mode_q;
+ logic [ASID_WIDTH-1:0] refill_asid_q;
+ logic refill_g_q;
+ logic refill_u_q;
+ logic refill_r_q;
+ logic refill_w_q;
+ logic refill_x_q;
+ logic refill_a_q;
+ logic refill_d_q;
+
+ function automatic logic is_supported_mode(input logic [3:0] mode);
+ return (mode == SATP_MODE_SV39);
+ endfunction
+
+ function automatic int vpn_stride(input logic [3:0] mode);
+ case (mode)
+ SATP_MODE_SV39: vpn_stride = 9;
+ default: vpn_stride = 0;
+ endcase
+ endfunction
+
+ function automatic logic [44:0] vaddr_to_vpn(
+ input logic [XLEN-1:0] vaddr,
+ input logic [3:0] mode
+ );
+ logic [44:0] vpn;
+ begin
+ vpn = '0;
+ case (mode)
+ SATP_MODE_SV39: vpn[26:0] = vaddr[38:12];
+ default: vpn = '0;
+ endcase
+ return vpn;
+ end
+ endfunction
+
+ function automatic logic [9:0] vpn_index(
+ input logic [XLEN-1:0] vaddr,
+ input logic [3:0] mode,
+ input logic [2:0] level
+ );
+ logic [9:0] idx;
+ begin
+ idx = '0;
+ case (mode)
+ SATP_MODE_SV39: idx = {1'b0, vaddr[12 + level * 9 +: 9]};
+ default: idx = '0;
+ endcase
+ return idx;
+ end
+ endfunction
+
+ function automatic logic vaddr_is_canonical(
+ input logic [XLEN-1:0] vaddr,
+ input logic [3:0] mode
+ );
+ case (mode)
+ SATP_MODE_SV39: vaddr_is_canonical = (vaddr[63:39] == {25{vaddr[38]}});
+ default: vaddr_is_canonical = 1'b1;
+ endcase
+ endfunction
+
+ function automatic logic [5:0] page_shift(
+ input logic [3:0] mode,
+ input logic [2:0] level
+ );
+ int stride;
+ begin
+ stride = vpn_stride(mode);
+ return 6'(12 + level * stride);
+ end
+ endfunction
+
+ function automatic logic allow_access(
+ input logic is_fetch,
+ input logic is_store,
+ input logic [1:0] priv,
+ input logic sum,
+ input logic mxr,
+ input logic u,
+ input logic r,
+ input logic w,
+ input logic x,
+ input logic a,
+ input logic d
+ );
+ logic is_load;
+ logic readable;
+ logic priv_u;
+ logic priv_ok;
+ begin
+ is_load = ~is_fetch && ~is_store;
+ readable = r || (mxr && x);
+ priv_u = (priv == 2'b00);
+
+ if (u) begin
+ if (priv_u) begin
+ priv_ok = 1'b1;
+ end else begin
+ priv_ok = ~is_fetch && sum;
+ end
+ end else begin
+ priv_ok = ~priv_u;
+ end
+
+ return a && priv_ok && (
+ (is_fetch && x) ||
+ (is_store && w && d) ||
+ (is_load && readable)
+ );
+ end
+ endfunction
+
+ assign pte_v = mem_resp_data[0];
+ assign pte_r = mem_resp_data[1];
+ assign pte_w = mem_resp_data[2];
+ assign pte_x = mem_resp_data[3];
+ assign pte_u = mem_resp_data[4];
+ assign pte_g = mem_resp_data[5];
+ assign pte_a = mem_resp_data[6];
+ assign pte_d = mem_resp_data[7];
+ assign pte_ppn = mem_resp_data[53:10];
+
+ assign leaf_pte = pte_r || pte_x;
+ assign bad_pte = (!pte_v) || (pte_w && !pte_r);
+
+ always_comb begin
+ int stride;
+ int lower_bits;
+ logic [63:0] ppn_mask;
+ stride = vpn_stride(req_mode_q);
+ lower_bits = level_q * stride;
+ ppn_mask = '0;
+ superpage_misaligned = 1'b0;
+ if (leaf_pte && (lower_bits > 0)) begin
+ if (lower_bits >= 44) begin
+ ppn_mask = 64'h0000_0FFF_FFFF_FFFF;
+ end else begin
+ ppn_mask = (64'd1 << lower_bits) - 1;
+ end
+ superpage_misaligned = |({20'b0, pte_ppn} & ppn_mask);
+ end
+ end
+
+ always_comb begin
+ logic [5:0] shift;
+ logic [XLEN-1:0] low_mask;
+ int shift_index;
+ shift = page_shift(req_mode_q, level_q);
+ shift_index = int'(shift);
+ if (shift_index >= XLEN) begin
+ low_mask = {XLEN{1'b1}};
+ end else begin
+ low_mask = ({{(XLEN-1){1'b0}}, 1'b1} << shift_index) - 1;
+ end
+ next_paddr = (({{(XLEN-56){1'b0}}, pte_ppn, 12'b0}) & ~low_mask) | (req_vaddr_q & low_mask);
+ end
+
+ assign req_ready = (state_q == PTW_IDLE);
+ assign mem_req_valid = (state_q == PTW_ISSUE);
+ assign mem_req_addr = {{(XLEN-56){1'b0}}, table_ppn_q, 12'b0} +
+ ({{(XLEN-10){1'b0}}, vpn_index(req_vaddr_q, req_mode_q, level_q)} << 3);
+
+ assign resp_valid = (state_q == PTW_DONE);
+ assign resp_fault = resp_fault_q;
+ assign resp_paddr = resp_paddr_q;
+
+ assign refill_valid = refill_valid_q && (state_q == PTW_DONE);
+ assign refill_vpn = refill_vpn_q;
+ assign refill_ppn = refill_ppn_q;
+ assign refill_level = refill_level_q;
+ assign refill_mode = refill_mode_q;
+ assign refill_asid = refill_asid_q;
+ assign refill_g = refill_g_q;
+ assign refill_u = refill_u_q;
+ assign refill_r = refill_r_q;
+ assign refill_w = refill_w_q;
+ assign refill_x = refill_x_q;
+ assign refill_a = refill_a_q;
+ assign refill_d = refill_d_q;
+
+ always_ff @(posedge clk) begin
+ if (rst) begin
+ state_q <= PTW_IDLE;
+ req_vaddr_q <= '0;
+ req_asid_q <= '0;
+ req_mode_q <= SATP_MODE_BARE;
+ req_priv_q <= '0;
+ req_is_fetch_q <= 1'b0;
+ req_is_store_q <= 1'b0;
+ req_sum_q <= 1'b0;
+ req_mxr_q <= 1'b0;
+ table_ppn_q <= '0;
+ level_q <= '0;
+ req_vpn_q <= '0;
+ resp_fault_q <= 1'b0;
+ resp_paddr_q <= '0;
+ refill_valid_q <= 1'b0;
+ refill_vpn_q <= '0;
+ refill_ppn_q <= '0;
+ refill_level_q <= '0;
+ refill_mode_q <= SATP_MODE_BARE;
+ refill_asid_q <= '0;
+ refill_g_q <= 1'b0;
+ refill_u_q <= 1'b0;
+ refill_r_q <= 1'b0;
+ refill_w_q <= 1'b0;
+ refill_x_q <= 1'b0;
+ refill_a_q <= 1'b0;
+ refill_d_q <= 1'b0;
+ end else begin
+ case (state_q)
+ PTW_IDLE: begin
+ refill_valid_q <= 1'b0;
+ if (req_valid) begin
+ req_vaddr_q <= req_vaddr;
+ req_asid_q <= req_asid;
+ req_mode_q <= req_mode;
+ req_priv_q <= req_priv;
+ req_is_fetch_q <= req_is_fetch;
+ req_is_store_q <= req_is_store;
+ req_sum_q <= req_sum;
+ req_mxr_q <= req_mxr;
+ req_vpn_q <= vaddr_to_vpn(req_vaddr, req_mode);
+
+ if (!is_supported_mode(req_mode) || !vaddr_is_canonical(req_vaddr, req_mode)) begin
+ resp_fault_q <= 1'b1;
+ resp_paddr_q <= '0;
+ state_q <= PTW_DONE;
+ end else begin
+ table_ppn_q <= satp_value[43:0];
+ case (req_mode)
+ SATP_MODE_SV39: level_q <= 3'd2;
+ default: level_q <= '0;
+ endcase
+ state_q <= PTW_ISSUE;
+ end
+ end
+ end
+
+ PTW_ISSUE: begin
+ if (mem_req_ready) begin
+ state_q <= PTW_WAIT;
+ end
+ end
+
+ PTW_WAIT: begin
+ if (mem_resp_valid) begin
+ if (bad_pte) begin
+ resp_fault_q <= 1'b1;
+ resp_paddr_q <= '0;
+ refill_valid_q <= 1'b0;
+ state_q <= PTW_DONE;
+ end else if (leaf_pte) begin
+ if (superpage_misaligned ||
+ !allow_access(req_is_fetch_q, req_is_store_q, req_priv_q, req_sum_q, req_mxr_q,
+ pte_u, pte_r, pte_w, pte_x, pte_a, pte_d)) begin
+ resp_fault_q <= 1'b1;
+ resp_paddr_q <= '0;
+ refill_valid_q <= 1'b0;
+ end else begin
+ resp_fault_q <= 1'b0;
+ resp_paddr_q <= next_paddr;
+ refill_valid_q <= 1'b1;
+ refill_vpn_q <= req_vpn_q;
+ refill_ppn_q <= pte_ppn;
+ refill_level_q <= level_q;
+ refill_mode_q <= req_mode_q;
+ refill_asid_q <= req_asid_q;
+ refill_g_q <= pte_g;
+ refill_u_q <= pte_u;
+ refill_r_q <= pte_r;
+ refill_w_q <= pte_w;
+ refill_x_q <= pte_x;
+ refill_a_q <= pte_a;
+ refill_d_q <= pte_d;
+ end
+ state_q <= PTW_DONE;
+ end else if (level_q == 0) begin
+ resp_fault_q <= 1'b1;
+ resp_paddr_q <= '0;
+ refill_valid_q <= 1'b0;
+ state_q <= PTW_DONE;
+ end else begin
+ table_ppn_q <= pte_ppn;
+ level_q <= level_q - 1'b1;
+ state_q <= PTW_ISSUE;
+ end
+ end
+ end
+
+ PTW_DONE: begin
+ state_q <= PTW_IDLE;
+ end
+
+ default: state_q <= PTW_IDLE;
+ endcase
+ end
+ end
+
+endmodule
diff --git a/rv64cpu/rtl/core/mmu/tlb.sv b/rv64cpu/rtl/core/mmu/tlb.sv
index e4696fd..06eb947 100644
--- a/rv64cpu/rtl/core/mmu/tlb.sv
+++ b/rv64cpu/rtl/core/mmu/tlb.sv
@@ -15,7 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// File path: rtl/core/mmu/tlb.sv
-// Description: Parameterized L1 TLB (Sv32/Sv48/Sv57)
+// Description: Parameterized L1 TLB (Bare + Sv39, higher modes stubbed)
module tlb
import rv64_pkg::*;
@@ -69,9 +69,10 @@ module tlb
);
localparam logic [3:0] SATP_MODE_BARE = 4'd0;
- localparam logic [3:0] SATP_MODE_SV32 = 4'd1;
+ localparam logic [3:0] SATP_MODE_SV39 = 4'd8;
localparam logic [3:0] SATP_MODE_SV48 = 4'd9;
localparam logic [3:0] SATP_MODE_SV57 = 4'd10;
+ localparam logic [3:0] SATP_MODE_SV64 = 4'd11; // Reserved/future stub.
typedef struct packed {
logic valid;
@@ -105,24 +106,19 @@ module tlb
int unsigned shift_index;
function automatic logic is_supported_mode(input logic [3:0] mode);
- return (mode == SATP_MODE_BARE) || (mode == SATP_MODE_SV32) ||
- (mode == SATP_MODE_SV48) || (mode == SATP_MODE_SV57);
+ return (mode == SATP_MODE_BARE) || (mode == SATP_MODE_SV39);
endfunction
function automatic int vpn_width(input logic [3:0] mode);
case (mode)
- SATP_MODE_SV32: vpn_width = 20;
- SATP_MODE_SV48: vpn_width = 36;
- SATP_MODE_SV57: vpn_width = 45;
+ SATP_MODE_SV39: vpn_width = 27;
default: vpn_width = 0;
endcase
endfunction
function automatic int vpn_stride(input logic [3:0] mode);
case (mode)
- SATP_MODE_SV32: vpn_stride = 10;
- SATP_MODE_SV48,
- SATP_MODE_SV57: vpn_stride = 9;
+ SATP_MODE_SV39: vpn_stride = 9;
default: vpn_stride = 0;
endcase
endfunction
@@ -160,15 +156,23 @@ module tlb
begin
vpn = '0;
case (mode)
- SATP_MODE_SV32: vpn[19:0] = vaddr[31:12];
- SATP_MODE_SV48: vpn[35:0] = vaddr[47:12];
- SATP_MODE_SV57: vpn[44:0] = vaddr[56:12];
+ SATP_MODE_SV39: vpn[26:0] = vaddr[38:12];
default: vpn = '0;
endcase
vaddr_to_vpn = vpn;
end
endfunction
+ function automatic logic vaddr_is_canonical(
+ input logic [XLEN-1:0] vaddr,
+ input logic [3:0] mode
+ );
+ case (mode)
+ SATP_MODE_SV39: vaddr_is_canonical = (vaddr[63:39] == {25{vaddr[38]}});
+ default: vaddr_is_canonical = 1'b1;
+ endcase
+ endfunction
+
function automatic logic [5:0] page_shift(
input logic [3:0] mode,
input logic [2:0] level
@@ -262,7 +266,7 @@ module tlb
hit_d = 1'b0;
if (req_valid) begin
- if (!is_supported_mode(req_mode)) begin
+ if (!is_supported_mode(req_mode) || !vaddr_is_canonical(req_vaddr, req_mode)) begin
resp_page_fault = 1'b1;
end else if (req_mode == SATP_MODE_BARE || req_priv == 2'b11) begin
// Bare mode or M-mode bypasses translation.
diff --git a/rv64cpu/rtl/core/pmp_checker.sv b/rv64cpu/rtl/core/pmp_checker.sv
new file mode 100644
index 0000000..361e507
--- /dev/null
+++ b/rv64cpu/rtl/core/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 <https://www.gnu.org/licenses/>.
+
+// 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/richard_core.sv b/rv64cpu/rtl/core/richard_core.sv
index a841253..51c64f8 100644
--- a/rv64cpu/rtl/core/richard_core.sv
+++ b/rv64cpu/rtl/core/richard_core.sv
@@ -20,6 +20,7 @@
module richard_core
import rv64_pkg::*;
+ import csr_pkg::*;
(
input logic clk,
input logic rst,
@@ -37,11 +38,19 @@ module richard_core
input logic [63:0] dmem_rdata,
// Interface 3: External Interrupt Sources
- input logic ext_timer_interrupt,
- input logic ext_software_interrupt,
- input logic ext_external_interrupt
+ 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
);
localparam int CSR_MSTATUS_MIE_BIT = 3;
+ localparam int CSR_MSTATUS_SUM_BIT = 18;
+ localparam int CSR_MSTATUS_MXR_BIT = 19;
+ localparam int CSR_MSTATUS_TVM_BIT = 20;
+ localparam int CSR_MSTATUS_TW_BIT = 21;
+ localparam int CSR_MSTATUS_TSR_BIT = 22;
// -----------------------
// Internal Wire Declarations
@@ -56,16 +65,25 @@ module richard_core
// (2) IF Stage Outputs
logic [XLEN-1:0] if_pc_out;
+ logic [INST_WIDTH-1:0] if_instr_to_decode;
+ logic [INST_WIDTH-1:0] if_compressed_expanded_instr;
+ logic if_instr_compressed;
+ logic [2:0] if_instr_len;
+ logic if_compressed_illegal;
// imem interface (ports)
// (3) IF/ID Pipeline Register Outputs
logic [XLEN-1:0] if_id_pc;
logic [31:0] if_id_instr;
+ logic [2:0] if_id_instr_len;
// (4) ID Stage Outputs
logic [XLEN-1:0] id_pc_out;
+ logic [2:0] id_instr_len;
logic [XLEN-1:0] id_rs1_rdata, id_rs2_rdata;
+ logic [XLEN-1:0] id_fs1_rdata, id_fs2_rdata, id_fs3_rdata;
logic [4:0] id_rs1_addr, id_rs2_addr;
+ logic [4:0] id_fs3_addr;
logic [XLEN-1:0] id_imm;
alu_op_t id_alu_op;
logic id_alu_src1_sel, id_alu_src2_sel;
@@ -73,6 +91,12 @@ module richard_core
branch_op_t id_branch_op;
logic id_is_load, id_is_store;
logic [2:0] id_mem_size;
+ logic id_is_amo;
+ amo_op_t id_amo_op;
+ logic id_is_fp, id_fp_reg_write_en, id_fp_store_data_sel;
+ fp_op_t id_fp_op;
+ fp_fmt_t id_fp_fmt;
+ logic [2:0] id_fp_rm;
logic id_reg_write_en;
logic [4:0] id_rd_addr;
logic [1:0] id_wb_sel;
@@ -84,8 +108,11 @@ module richard_core
// (5) ID/EX Pipeline Register Outputs
logic [XLEN-1:0] ex_pc_in;
logic [INST_WIDTH-1:0] ex_instr_in;
+ logic [2:0] ex_instr_len_in;
logic [XLEN-1:0] ex_rs1_rdata_in, ex_rs2_rdata_in;
+ logic [XLEN-1:0] ex_fs1_rdata_in, ex_fs2_rdata_in, ex_fs3_rdata_in;
logic [4:0] ex_rs1_addr_in, ex_rs2_addr_in;
+ logic [4:0] ex_fs3_addr_in;
logic [XLEN-1:0] ex_imm_in;
alu_op_t ex_alu_op_in;
logic ex_alu_src1_sel_in, ex_alu_src2_sel_in;
@@ -93,6 +120,12 @@ module richard_core
branch_op_t ex_branch_op_in;
logic ex_is_load_in, ex_is_store_in;
logic [2:0] ex_mem_size_in;
+ logic ex_is_amo_in;
+ amo_op_t ex_amo_op_in;
+ logic ex_is_fp_in, ex_fp_reg_write_en_in, ex_fp_store_data_sel_in;
+ fp_op_t ex_fp_op_in;
+ fp_fmt_t ex_fp_fmt_in;
+ logic [2:0] ex_fp_rm_in;
logic ex_reg_write_en_in;
logic [4:0] ex_rd_addr_in;
logic [1:0] ex_wb_sel_in;
@@ -103,21 +136,41 @@ module richard_core
// (6) EX Stage Outputs (and Forwarding Muxes)
logic [XLEN-1:0] ex_rs1_rdata_fwd, ex_rs2_rdata_fwd;
+ logic [XLEN-1:0] ex_fs1_rdata_fwd, ex_fs2_rdata_fwd, ex_fs3_rdata_fwd;
+ logic [XLEN-1:0] mem_forward_data;
logic [XLEN-1:0] ex_alu_result_out;
+ logic [XLEN-1:0] ex_result_to_mem;
+ logic [XLEN-1:0] ex_fp_result_out;
+ logic [XLEN-1:0] ex_fp_int_result_out;
+ logic [4:0] ex_fp_fflags_out;
+ logic ex_fp_illegal_out;
logic ex_branch_taken_out;
logic [XLEN-1:0] ex_branch_target_out;
+ logic ex_redirect_taken;
+ logic [XLEN-1:0] ex_redirect_target;
// (7) EX/MEM Pipeline Register Outputs
logic [XLEN-1:0] mem_pc_in;
logic [INST_WIDTH-1:0] mem_instr_in;
+ logic [2:0] mem_instr_len_in;
logic [XLEN-1:0] mem_alu_result_in;
logic mem_branch_taken_in;
logic [XLEN-1:0] mem_branch_target_in;
logic [XLEN-1:0] mem_rs1_rdata_in, mem_rs2_rdata_in;
+ logic [XLEN-1:0] mem_fs1_rdata_in, mem_fs2_rdata_in, mem_fs3_rdata_in;
logic [4:0] mem_rs1_addr_in;
+ logic [4:0] mem_fs3_addr_in;
logic [XLEN-1:0] mem_imm_in;
logic mem_is_load_in, mem_is_store_in;
logic [2:0] mem_mem_size_in;
+ logic mem_is_amo_in;
+ amo_op_t mem_amo_op_in;
+ logic mem_is_fp_in, mem_fp_reg_write_en_in, mem_fp_store_data_sel_in;
+ fp_op_t mem_fp_op_in;
+ fp_fmt_t mem_fp_fmt_in;
+ logic [2:0] mem_fp_rm_in;
+ logic [XLEN-1:0] mem_fp_result_in;
+ logic [4:0] mem_fp_fflags_in;
logic mem_reg_write_en_in;
logic [4:0] mem_rd_addr_in;
logic [1:0] mem_wb_sel_in;
@@ -129,10 +182,14 @@ module richard_core
// (8) MEM Stage Outputs (to MEM/WB Register)
logic [XLEN-1:0] mw_reg_alu_result_in;
logic [XLEN-1:0] mw_reg_mem_rdata_in;
+ logic [XLEN-1:0] mw_reg_fp_result_in;
+ logic [4:0] mw_reg_fp_fflags_in;
logic [XLEN-1:0] mw_reg_pc_in;
logic [INST_WIDTH-1:0] mw_reg_instr_in;
+ logic [2:0] mw_reg_instr_len_in;
logic [7:0] mw_reg_dmem_wstrb_in;
logic mw_reg_reg_write_en_in;
+ logic mw_reg_fp_reg_write_en_in;
logic [4:0] mw_reg_rd_addr_in;
logic [1:0] mw_reg_wb_sel_in;
logic mw_reg_is_csr_in;
@@ -142,6 +199,7 @@ module richard_core
logic [XLEN-1:0] mw_reg_imm_in;
logic mw_reg_illegal_instr_in, mw_reg_is_ecall_in, mw_reg_is_ebreak_in, mw_reg_is_mret_in, mw_reg_is_sret_in;
logic mw_reg_load_fault_in, mw_reg_store_fault_in;
+ logic mw_reg_load_page_fault_in, mw_reg_store_page_fault_in;
logic [XLEN-1:0] mw_reg_fault_addr_in;
logic mem_stall_req;
@@ -149,8 +207,12 @@ module richard_core
logic [XLEN-1:0] wb_pc_in;
logic [XLEN-1:0] wb_alu_result_in;
logic [XLEN-1:0] wb_mem_rdata_in;
+ logic [XLEN-1:0] wb_fp_result_in;
+ logic [4:0] wb_fp_fflags_in;
logic [INST_WIDTH-1:0] wb_instr_in;
+ logic [2:0] wb_instr_len_in;
logic wb_reg_write_en_in;
+ logic wb_fp_reg_write_en_in;
logic [4:0] wb_rd_addr_in;
logic [1:0] wb_wb_sel_in;
logic wb_is_csr_in;
@@ -160,12 +222,19 @@ module richard_core
logic [XLEN-1:0] wb_imm_in;
logic wb_illegal_instr_in, wb_is_ecall_in, wb_is_ebreak_in, wb_is_mret_in, wb_is_sret_in;
logic wb_load_fault, wb_store_fault;
+ logic wb_load_page_fault, wb_store_page_fault;
logic [XLEN-1:0] wb_fault_addr_in;
// (10) WB Stage Outputs (to RegFile & CSR Unit)
logic [XLEN-1:0] wb_rd_wdata;
logic [4:0] wb_rd_addr;
logic wb_reg_write_en_out;
+ logic [XLEN-1:0] wb_fd_wdata;
+ logic [4:0] wb_fd_addr;
+ logic wb_fp_write_en;
+ logic [4:0] wb_fp_fflags;
+ logic wb_fp_state_dirty;
+ logic wb_fp_fflags_valid;
logic csr_req;
logic [2:0] csr_op;
logic [11:0] csr_addr;
@@ -178,16 +247,24 @@ module richard_core
logic [XLEN-1:0] csr_sstatus_value;
logic [XLEN-1:0] csr_medeleg_value;
logic [XLEN-1:0] csr_mideleg_value;
+ logic [XLEN-1:0] csr_mie_value;
+ logic [XLEN-1:0] csr_sie_value;
+ logic [XLEN-1:0] csr_mip_value;
+ logic [XLEN-1:0] csr_sip_value;
logic [XLEN-1:0] csr_mepc_value;
logic [XLEN-1:0] csr_sepc_value;
logic [XLEN-1:0] csr_satp_value;
+ logic [2:0] csr_frm_value;
logic [XLEN-1:0] csr_trap_vector_value;
logic csr_trap_to_s_mode;
logic csr_illegal_access;
+ pmpcfg_entry_t csr_pmpcfg_entries [PMP_ENTRY_COUNT];
+ pmpaddr_entry_t csr_pmpaddr_entries [PMP_ENTRY_COUNT];
// (11) Exception / Trap bus
logic trap_illegal_instr, trap_is_ecall, trap_is_ebreak, trap_is_mret, trap_is_sret;
logic [XLEN-1:0] trap_epc;
+ logic trap_instr_access_fault;
// (12) Trap Controller + interrupt wiring
logic trap_load_fault, trap_store_fault;
@@ -207,13 +284,145 @@ module richard_core
logic trap_ctrl_mret_en;
logic trap_ctrl_sret_en;
logic csr_mstatus_mie;
+ logic csr_mstatus_sum;
+ logic csr_mstatus_mxr;
+ logic if_instr_access_fault;
+ logic if_instr_page_fault;
+ logic if_id_instr_access_fault;
+ logic if_id_instr_page_fault;
+ logic ex_instr_access_fault_in;
+ logic ex_instr_page_fault_in;
+ logic mem_instr_access_fault_in;
+ logic mem_instr_page_fault_in;
+ logic mw_reg_instr_access_fault_in;
+ logic mw_reg_instr_page_fault_in;
+ logic wb_instr_access_fault;
+ logic wb_instr_page_fault;
+ logic dmem_access_fault;
+ logic dmem_load_access_fault;
+ logic dmem_store_access_fault;
+ logic dmem_load_page_fault;
+ logic dmem_store_page_fault;
+ logic [3:0] dmem_access_size;
- // Page-fault paths remain stubbed until the MMU surfaces violations.
- assign trap_instr_page_fault = 1'b0;
- assign trap_load_page_fault = 1'b0;
- assign trap_store_page_fault = 1'b0;
+ logic mmu_i_req_valid;
+ logic mmu_i_resp_valid;
+ logic mmu_i_resp_page_fault;
+ logic [XLEN-1:0] mmu_i_resp_vaddr;
+ logic [XLEN-1:0] mmu_i_resp_paddr;
+ logic mmu_d_req_valid;
+ logic mmu_d_resp_valid;
+ logic mmu_d_resp_page_fault;
+ logic [XLEN-1:0] mmu_d_resp_paddr;
+ logic ptw_mem_req_valid;
+ logic ptw_mem_req_ready;
+ logic [XLEN-1:0] ptw_mem_req_addr;
+ logic ptw_mem_resp_valid;
+ logic [XLEN-1:0] ptw_mem_resp_data;
+ logic ptw_mem_resp_valid_q;
+ logic [XLEN-1:0] ptw_mem_resp_data_q;
+ logic raw_dmem_read_en;
+ logic raw_dmem_write_en;
+ logic [XLEN-1:0] raw_dmem_addr;
+ logic [XLEN-1:0] raw_dmem_wdata;
+ logic [7:0] raw_dmem_wstrb;
+ logic data_access_needs_translation;
+ logic data_translation_ready;
+ logic data_translation_page_fault;
+ logic data_memory_port_available;
+ logic if_translation_ready;
+ logic if_translation_page_fault;
+ logic mmu_stall_req;
+ logic dmem_req_is_store;
assign csr_mstatus_mie = csr_mstatus_value[CSR_MSTATUS_MIE_BIT];
+ assign csr_mstatus_sum = csr_mstatus_value[CSR_MSTATUS_SUM_BIT];
+ assign csr_mstatus_mxr = csr_mstatus_value[CSR_MSTATUS_MXR_BIT];
+ assign ex_redirect_taken = ex_branch_taken_out | ex_is_fence_i_in;
+ assign ex_redirect_target = ex_is_fence_i_in ?
+ (ex_pc_in + {{(XLEN-3){1'b0}}, ex_instr_len_in}) :
+ ex_branch_target_out;
+
+ always_comb begin
+ unique case (mem_mem_size_in)
+ 3'b000, 3'b100: dmem_access_size = 4'd1;
+ 3'b001, 3'b101: dmem_access_size = 4'd2;
+ 3'b010, 3'b110: dmem_access_size = 4'd4;
+ 3'b011: dmem_access_size = 4'd8;
+ default: dmem_access_size = 4'd1;
+ endcase
+ end
+
+ assign mmu_i_req_valid = !rst;
+ assign dmem_req_is_store = mem_is_store_in | (mem_is_amo_in && (mem_amo_op_in != AMO_LR));
+
+ assign mmu_d_req_valid = (mem_is_load_in | mem_is_store_in | mem_is_amo_in) &
+ ~mem_instr_access_fault_in & ~mem_instr_page_fault_in;
+
+ assign if_translation_ready = mmu_i_resp_valid & (mmu_i_resp_vaddr == if_pc_out);
+ assign if_translation_page_fault = if_translation_ready & mmu_i_resp_page_fault;
+ assign if_instr_page_fault = if_translation_page_fault;
+
+ assign data_access_needs_translation = mmu_d_req_valid;
+ assign data_translation_ready = !data_access_needs_translation | mmu_d_resp_valid;
+ assign data_translation_page_fault = data_access_needs_translation & mmu_d_resp_valid &
+ mmu_d_resp_page_fault;
+ assign data_memory_port_available = !ptw_mem_req_valid;
+ assign dmem_load_page_fault = (mem_is_load_in | (mem_is_amo_in && (mem_amo_op_in == AMO_LR))) &
+ data_translation_page_fault;
+ assign dmem_store_page_fault = dmem_req_is_store & data_translation_page_fault;
+ assign mmu_stall_req = !if_translation_ready;
+
+ assign ptw_mem_req_ready = 1'b1;
+ assign ptw_mem_resp_valid = ptw_mem_resp_valid_q;
+ assign ptw_mem_resp_data = ptw_mem_resp_data_q;
+
+ always_ff @(posedge clk) begin
+ if (rst) begin
+ ptw_mem_resp_valid_q <= 1'b0;
+ ptw_mem_resp_data_q <= '0;
+ end else begin
+ ptw_mem_resp_valid_q <= ptw_mem_req_valid & ptw_mem_req_ready;
+ if (ptw_mem_req_valid & ptw_mem_req_ready) begin
+ ptw_mem_resp_data_q <= dmem_rdata;
+ end
+ end
+ end
+
+ mmu_top u_mmu_top (
+ .clk (clk),
+ .rst (rst),
+ .satp_value (csr_satp_value),
+ .priv_mode (csr_priv_mode_cur),
+ .sum (csr_mstatus_sum),
+ .mxr (csr_mstatus_mxr),
+ .i_req_valid (mmu_i_req_valid),
+ .i_req_ready (),
+ .i_req_vaddr (if_pc_out),
+ .i_resp_valid (mmu_i_resp_valid),
+ .i_resp_hit (),
+ .i_resp_page_fault (mmu_i_resp_page_fault),
+ .i_resp_vaddr (mmu_i_resp_vaddr),
+ .i_resp_paddr (mmu_i_resp_paddr),
+ .d_req_valid (mmu_d_req_valid),
+ .d_req_ready (),
+ .d_req_vaddr (mem_alu_result_in),
+ .d_req_is_store (dmem_req_is_store),
+ .d_resp_valid (mmu_d_resp_valid),
+ .d_resp_hit (),
+ .d_resp_page_fault (mmu_d_resp_page_fault),
+ .d_resp_paddr (mmu_d_resp_paddr),
+ .flush_all (mem_is_sfence_vma_in),
+ .flush_asid_valid (1'b0),
+ .flush_asid ('0),
+ .flush_vpn_valid (1'b0),
+ .flush_vpn ('0),
+ .ptw_mem_req_valid (ptw_mem_req_valid),
+ .ptw_mem_req_ready (ptw_mem_req_ready),
+ .ptw_mem_req_addr (ptw_mem_req_addr),
+ .ptw_mem_resp_valid (ptw_mem_resp_valid),
+ .ptw_mem_resp_data (ptw_mem_resp_data)
+ );
// ---------------------------------------------------------
// (A) Hazard Unit Implementation
@@ -225,12 +434,15 @@ module richard_core
.ex_rs2_addr (ex_rs2_addr_in),
.ex_rd_addr (ex_rd_addr_in),
.ex_reg_write_en (ex_reg_write_en_in),
- .ex_is_load (ex_is_load_in),
- .ex_branch_taken (ex_branch_taken_out),
- .ex_branch_target (ex_branch_target_out),
+ .ex_is_load (ex_is_load_in | ex_is_amo_in),
+ .ex_is_csr (ex_is_csr_in),
+ .ex_branch_taken (ex_redirect_taken),
+ .ex_branch_target (ex_redirect_target),
.mem_rd_addr (mem_rd_addr_in),
.mem_reg_write_en (mem_reg_write_en_in),
- .mem_stall_req (mem_stall_req),
+ .mem_result_late (mem_wb_sel_in == 2'b01),
+ .mem_is_csr (mem_is_csr_in),
+ .mem_stall_req (mem_stall_req | mmu_stall_req),
.trap_flush_req (trap_flush_req),
.trap_pc_sel (trap_pc_sel),
.trap_target_pc (trap_next_pc),
@@ -259,20 +471,55 @@ module richard_core
.pc_redirect_sel (hu_pc_redirect_sel),
.pc_redirect_target (hu_pc_redirect_target),
.pc_redirect_is_trap(hu_pc_redirect_is_trap),
+ .if_instr_compressed(if_instr_compressed),
.if_pc (if_pc_out)
);
- assign imem_addr = if_pc_out;
+ assign imem_addr = (if_translation_ready && !mmu_i_resp_page_fault) ? mmu_i_resp_paddr : if_pc_out;
+
+ assign if_instr_compressed = (imem_rdata[1:0] != 2'b11);
+ assign if_instr_len = if_instr_compressed ? 3'd2 : 3'd4;
+
+ compressed_decoder u_compressed_decoder (
+ .compressed_instr (imem_rdata[15:0]),
+ .expanded_instr (if_compressed_expanded_instr),
+ .illegal_instr (if_compressed_illegal)
+ );
+
+ always_comb begin
+ if_instr_to_decode = imem_rdata;
+ if (if_instr_compressed) begin
+ if_instr_to_decode = if_compressed_illegal ? 32'h0000_0000 : if_compressed_expanded_instr;
+ end
+ end
+
+ pmp_checker u_pmp_checker_if (
+ .req_valid (if_translation_ready && !mmu_i_resp_page_fault),
+ .req_paddr (mmu_i_resp_paddr),
+ .req_size (4'd4),
+ .req_is_fetch (1'b1),
+ .req_is_store (1'b0),
+ .priv_mode (csr_priv_mode_cur),
+ .pmpcfg_entries (csr_pmpcfg_entries),
+ .pmpaddr_entries (csr_pmpaddr_entries),
+ .access_fault (if_instr_access_fault)
+ );
if_id_reg u_if_id_reg (
.clk (clk),
.rst (rst),
.stall_en (hu_if_id_stall_en),
.flush_en (hu_if_id_flush_en),
- .if_pc (if_pc_out),
- .if_instr (imem_rdata),
- .id_pc (if_id_pc),
- .id_instr (if_id_instr)
+ .if_pc (if_pc_out),
+ .if_instr (if_instr_to_decode),
+ .if_instr_len (if_instr_len),
+ .if_instr_access_fault (if_instr_access_fault),
+ .if_instr_page_fault (if_instr_page_fault),
+ .id_pc (if_id_pc),
+ .id_instr (if_id_instr),
+ .id_instr_len (if_id_instr_len),
+ .id_instr_access_fault (if_id_instr_access_fault),
+ .id_instr_page_fault (if_id_instr_page_fault)
);
// ---------------------------------------------------------
@@ -283,15 +530,27 @@ module richard_core
.rst (rst),
.if_pc (if_id_pc),
.if_instr (if_id_instr),
+ .if_instr_len (if_id_instr_len),
.csr_priv_mode (csr_priv_mode_cur),
+ .csr_mstatus_tsr (csr_mstatus_value[CSR_MSTATUS_TSR_BIT]),
+ .csr_mstatus_tw (csr_mstatus_value[CSR_MSTATUS_TW_BIT]),
+ .csr_mstatus_tvm (csr_mstatus_value[CSR_MSTATUS_TVM_BIT]),
.wb_reg_write_en (wb_reg_write_en_out),
.wb_rd_addr (wb_rd_addr),
.wb_rd_wdata (wb_rd_wdata),
+ .wb_fp_write_en (wb_fp_write_en),
+ .wb_fd_addr (wb_fd_addr),
+ .wb_fd_wdata (wb_fd_wdata),
.id_pc (id_pc_out),
+ .id_instr_len (id_instr_len),
.id_rs1_rdata (id_rs1_rdata),
.id_rs2_rdata (id_rs2_rdata),
+ .id_fs1_rdata (id_fs1_rdata),
+ .id_fs2_rdata (id_fs2_rdata),
+ .id_fs3_rdata (id_fs3_rdata),
.id_rs1_addr (id_rs1_addr),
.id_rs2_addr (id_rs2_addr),
+ .id_fs3_addr (id_fs3_addr),
.id_imm (id_imm),
.id_alu_op (id_alu_op),
.id_alu_src1_sel (id_alu_src1_sel),
@@ -303,6 +562,14 @@ module richard_core
.id_is_load (id_is_load),
.id_is_store (id_is_store),
.id_mem_size (id_mem_size),
+ .id_is_amo (id_is_amo),
+ .id_amo_op (id_amo_op),
+ .id_is_fp (id_is_fp),
+ .id_fp_reg_write_en (id_fp_reg_write_en),
+ .id_fp_store_data_sel(id_fp_store_data_sel),
+ .id_fp_op (id_fp_op),
+ .id_fp_fmt (id_fp_fmt),
+ .id_fp_rm (id_fp_rm),
.id_reg_write_en (id_reg_write_en),
.id_rd_addr (id_rd_addr),
.id_wb_sel (id_wb_sel),
@@ -325,11 +592,16 @@ module richard_core
.stall_en (hu_id_ex_stall_en),
.flush_en (hu_id_ex_flush_en),
.id_instr (if_id_instr),
+ .id_instr_len (id_instr_len),
.id_pc (id_pc_out),
.id_rs1_rdata (id_rs1_rdata),
.id_rs2_rdata (id_rs2_rdata),
+ .id_fs1_rdata (id_fs1_rdata),
+ .id_fs2_rdata (id_fs2_rdata),
+ .id_fs3_rdata (id_fs3_rdata),
.id_rs1_addr (id_rs1_addr),
.id_rs2_addr (id_rs2_addr),
+ .id_fs3_addr (id_fs3_addr),
.id_imm (id_imm),
.id_alu_op (id_alu_op),
.id_alu_src1_sel (id_alu_src1_sel),
@@ -341,13 +613,23 @@ module richard_core
.id_is_load (id_is_load),
.id_is_store (id_is_store),
.id_mem_size (id_mem_size),
+ .id_is_amo (id_is_amo),
+ .id_amo_op (id_amo_op),
+ .id_is_fp (id_is_fp),
+ .id_fp_reg_write_en (id_fp_reg_write_en),
+ .id_fp_store_data_sel(id_fp_store_data_sel),
+ .id_fp_op (id_fp_op),
+ .id_fp_fmt (id_fp_fmt),
+ .id_fp_rm (id_fp_rm),
.id_reg_write_en (id_reg_write_en),
.id_rd_addr (id_rd_addr),
.id_wb_sel (id_wb_sel),
- .id_is_csr (id_is_csr),
- .id_csr_op (id_csr_op),
- .id_illegal_instr (id_illegal_instr),
- .id_is_ecall (id_is_ecall),
+ .id_is_csr (id_is_csr),
+ .id_csr_op (id_csr_op),
+ .id_illegal_instr (id_illegal_instr),
+ .id_instr_access_fault (if_id_instr_access_fault),
+ .id_instr_page_fault (if_id_instr_page_fault),
+ .id_is_ecall (id_is_ecall),
.id_is_ebreak (id_is_ebreak),
.id_is_mret (id_is_mret),
.id_is_sret (id_is_sret),
@@ -357,10 +639,15 @@ module richard_core
.id_is_fence_i (id_is_fence_i),
.ex_pc (ex_pc_in),
.ex_instr (ex_instr_in),
+ .ex_instr_len (ex_instr_len_in),
.ex_rs1_rdata (ex_rs1_rdata_in),
.ex_rs2_rdata (ex_rs2_rdata_in),
+ .ex_fs1_rdata (ex_fs1_rdata_in),
+ .ex_fs2_rdata (ex_fs2_rdata_in),
+ .ex_fs3_rdata (ex_fs3_rdata_in),
.ex_rs1_addr (ex_rs1_addr_in),
.ex_rs2_addr (ex_rs2_addr_in),
+ .ex_fs3_addr (ex_fs3_addr_in),
.ex_imm (ex_imm_in),
.ex_alu_op (ex_alu_op_in),
.ex_alu_src1_sel (ex_alu_src1_sel_in),
@@ -372,13 +659,23 @@ module richard_core
.ex_is_load (ex_is_load_in),
.ex_is_store (ex_is_store_in),
.ex_mem_size (ex_mem_size_in),
+ .ex_is_amo (ex_is_amo_in),
+ .ex_amo_op (ex_amo_op_in),
+ .ex_is_fp (ex_is_fp_in),
+ .ex_fp_reg_write_en (ex_fp_reg_write_en_in),
+ .ex_fp_store_data_sel(ex_fp_store_data_sel_in),
+ .ex_fp_op (ex_fp_op_in),
+ .ex_fp_fmt (ex_fp_fmt_in),
+ .ex_fp_rm (ex_fp_rm_in),
.ex_reg_write_en (ex_reg_write_en_in),
.ex_rd_addr (ex_rd_addr_in),
.ex_wb_sel (ex_wb_sel_in),
- .ex_is_csr (ex_is_csr_in),
- .ex_csr_op (ex_csr_op_in),
- .ex_illegal_instr (ex_illegal_instr_in),
- .ex_is_ecall (ex_is_ecall_in),
+ .ex_is_csr (ex_is_csr_in),
+ .ex_csr_op (ex_csr_op_in),
+ .ex_illegal_instr (ex_illegal_instr_in),
+ .ex_instr_access_fault (ex_instr_access_fault_in),
+ .ex_instr_page_fault (ex_instr_page_fault_in),
+ .ex_is_ecall (ex_is_ecall_in),
.ex_is_ebreak (ex_is_ebreak_in),
.ex_is_mret (ex_is_mret_in),
.ex_is_sret (ex_is_sret_in),
@@ -391,11 +688,20 @@ module richard_core
// ---------------------------------------------------------
// (D) EX Stage - Execution & Forwarding
// ---------------------------------------------------------
+
+ always_comb begin
+ unique case (mem_wb_sel_in)
+ 2'b00: mem_forward_data = mem_alu_result_in;
+ 2'b01: mem_forward_data = mw_reg_mem_rdata_in;
+ 2'b10: mem_forward_data = mem_pc_in + 64'd4;
+ default: mem_forward_data = '0;
+ endcase
+ end
// Operand A Forwarding Mux
always_comb begin
case (hu_forward_a_sel)
- 2'b01: ex_rs1_rdata_fwd = mem_alu_result_in; // From MEM
+ 2'b01: ex_rs1_rdata_fwd = mem_forward_data; // From MEM
2'b10: ex_rs1_rdata_fwd = wb_rd_wdata; // From WB
default: ex_rs1_rdata_fwd = ex_rs1_rdata_in; // From RegFile
endcase
@@ -404,12 +710,42 @@ module richard_core
// Operand B Forwarding Mux
always_comb begin
case (hu_forward_b_sel)
- 2'b01: ex_rs2_rdata_fwd = mem_alu_result_in; // From MEM
+ 2'b01: ex_rs2_rdata_fwd = mem_forward_data; // From MEM
2'b10: ex_rs2_rdata_fwd = wb_rd_wdata; // From WB
default: ex_rs2_rdata_fwd = ex_rs2_rdata_in; // From RegFile
endcase
end
+ always_comb begin
+ if (mem_fp_reg_write_en_in && (mem_rd_addr_in == ex_rs1_addr_in)) begin
+ ex_fs1_rdata_fwd = mw_reg_fp_result_in;
+ end else if (wb_fp_write_en && (wb_fd_addr == ex_rs1_addr_in)) begin
+ ex_fs1_rdata_fwd = wb_fd_wdata;
+ end else begin
+ ex_fs1_rdata_fwd = ex_fs1_rdata_in;
+ end
+ end
+
+ always_comb begin
+ if (mem_fp_reg_write_en_in && (mem_rd_addr_in == ex_rs2_addr_in)) begin
+ ex_fs2_rdata_fwd = mw_reg_fp_result_in;
+ end else if (wb_fp_write_en && (wb_fd_addr == ex_rs2_addr_in)) begin
+ ex_fs2_rdata_fwd = wb_fd_wdata;
+ end else begin
+ ex_fs2_rdata_fwd = ex_fs2_rdata_in;
+ end
+ end
+
+ always_comb begin
+ if (mem_fp_reg_write_en_in && (mem_rd_addr_in == ex_fs3_addr_in)) begin
+ ex_fs3_rdata_fwd = mw_reg_fp_result_in;
+ end else if (wb_fp_write_en && (wb_fd_addr == ex_fs3_addr_in)) begin
+ ex_fs3_rdata_fwd = wb_fd_wdata;
+ end else begin
+ ex_fs3_rdata_fwd = ex_fs3_rdata_in;
+ end
+ end
+
ex_stage u_ex_stage (
.ex_pc (ex_pc_in),
.ex_rs1_rdata (ex_rs1_rdata_fwd),
@@ -427,29 +763,65 @@ module richard_core
.ex_branch_target (ex_branch_target_out)
);
+ fpu_unit u_fpu_unit (
+ .fp_op (ex_fp_op_in),
+ .fp_fmt (ex_fp_fmt_in),
+ .rm ((ex_fp_rm_in == 3'b111) ? csr_frm_value : ex_fp_rm_in),
+ .funct3 (ex_instr_in[14:12]),
+ .rs2_addr (ex_rs2_addr_in),
+ .fs1_data (ex_fs1_rdata_fwd),
+ .fs2_data (ex_fs2_rdata_fwd),
+ .fs3_data (ex_fs3_rdata_fwd),
+ .int_rs1_data (ex_rs1_rdata_fwd),
+ .fp_result (ex_fp_result_out),
+ .int_result (ex_fp_int_result_out),
+ .fflags (ex_fp_fflags_out),
+ .illegal_instr (ex_fp_illegal_out)
+ );
+
+ assign ex_result_to_mem = (ex_is_fp_in && ex_reg_write_en_in) ?
+ ex_fp_int_result_out : ex_alu_result_out;
+
ex_mem_reg u_ex_mem_reg (
.clk (clk),
.rst (rst),
.stall_en (hu_ex_mem_stall_en),
- .flush_en (1'b0),
- .ex_alu_result (ex_alu_result_out),
+ .flush_en (trap_flush_req),
+ .ex_alu_result (ex_result_to_mem),
.ex_branch_taken (ex_branch_taken_out),
.ex_branch_target (ex_branch_target_out),
.ex_rs1_rdata (ex_rs1_rdata_fwd),
.ex_rs2_rdata (ex_rs2_rdata_fwd),
+ .ex_fs1_rdata (ex_fs1_rdata_fwd),
+ .ex_fs2_rdata (ex_fs2_rdata_fwd),
+ .ex_fs3_rdata (ex_fs3_rdata_fwd),
.ex_rs1_addr (ex_rs1_addr_in),
+ .ex_fs3_addr (ex_fs3_addr_in),
.ex_pc (ex_pc_in),
.ex_imm (ex_imm_in),
.ex_instr (ex_instr_in),
+ .ex_instr_len (ex_instr_len_in),
.ex_is_load (ex_is_load_in),
.ex_is_store (ex_is_store_in),
.ex_mem_size (ex_mem_size_in),
+ .ex_is_amo (ex_is_amo_in),
+ .ex_amo_op (ex_amo_op_in),
+ .ex_is_fp (ex_is_fp_in),
+ .ex_fp_reg_write_en (ex_fp_reg_write_en_in),
+ .ex_fp_store_data_sel(ex_fp_store_data_sel_in),
+ .ex_fp_op (ex_fp_op_in),
+ .ex_fp_fmt (ex_fp_fmt_in),
+ .ex_fp_rm (ex_fp_rm_in),
+ .ex_fp_result (ex_fp_result_out),
+ .ex_fp_fflags (ex_fp_fflags_out),
.ex_reg_write_en (ex_reg_write_en_in),
.ex_rd_addr (ex_rd_addr_in),
.ex_wb_sel (ex_wb_sel_in),
.ex_is_csr (ex_is_csr_in),
.ex_csr_op (ex_csr_op_in),
- .ex_illegal_instr (ex_illegal_instr_in),
+ .ex_illegal_instr (ex_illegal_instr_in | (ex_is_fp_in & ex_fp_illegal_out)),
+ .ex_instr_access_fault(ex_instr_access_fault_in),
+ .ex_instr_page_fault(ex_instr_page_fault_in),
.ex_is_ecall (ex_is_ecall_in),
.ex_is_ebreak (ex_is_ebreak_in),
.ex_is_mret (ex_is_mret_in),
@@ -463,19 +835,36 @@ module richard_core
.mem_branch_target (mem_branch_target_in),
.mem_rs1_rdata (mem_rs1_rdata_in),
.mem_rs2_rdata (mem_rs2_rdata_in),
+ .mem_fs1_rdata (mem_fs1_rdata_in),
+ .mem_fs2_rdata (mem_fs2_rdata_in),
+ .mem_fs3_rdata (mem_fs3_rdata_in),
.mem_rs1_addr (mem_rs1_addr_in),
+ .mem_fs3_addr (mem_fs3_addr_in),
.mem_pc (mem_pc_in),
.mem_imm (mem_imm_in),
.mem_instr (mem_instr_in),
+ .mem_instr_len (mem_instr_len_in),
.mem_is_load (mem_is_load_in),
.mem_is_store (mem_is_store_in),
.mem_mem_size (mem_mem_size_in),
+ .mem_is_amo (mem_is_amo_in),
+ .mem_amo_op (mem_amo_op_in),
+ .mem_is_fp (mem_is_fp_in),
+ .mem_fp_reg_write_en (mem_fp_reg_write_en_in),
+ .mem_fp_store_data_sel(mem_fp_store_data_sel_in),
+ .mem_fp_op (mem_fp_op_in),
+ .mem_fp_fmt (mem_fp_fmt_in),
+ .mem_fp_rm (mem_fp_rm_in),
+ .mem_fp_result (mem_fp_result_in),
+ .mem_fp_fflags (mem_fp_fflags_in),
.mem_reg_write_en (mem_reg_write_en_in),
.mem_rd_addr (mem_rd_addr_in),
.mem_wb_sel (mem_wb_sel_in),
.mem_is_csr (mem_is_csr_in),
.mem_csr_op (mem_csr_op_in),
.mem_illegal_instr (mem_illegal_instr_in),
+ .mem_instr_access_fault(mem_instr_access_fault_in),
+ .mem_instr_page_fault(mem_instr_page_fault_in),
.mem_is_ecall (mem_is_ecall_in),
.mem_is_ebreak (mem_is_ebreak_in),
.mem_is_mret (mem_is_mret_in),
@@ -489,25 +878,54 @@ module richard_core
// ---------------------------------------------------------
// (E) MEM Stage - Memory Access
// ---------------------------------------------------------
+ pmp_checker u_pmp_checker_dmem (
+ .req_valid (data_access_needs_translation && mmu_d_resp_valid && !mmu_d_resp_page_fault),
+ .req_paddr (mmu_d_resp_paddr),
+ .req_size (dmem_access_size),
+ .req_is_fetch (1'b0),
+ .req_is_store (dmem_req_is_store),
+ .priv_mode (csr_priv_mode_cur),
+ .pmpcfg_entries (csr_pmpcfg_entries),
+ .pmpaddr_entries (csr_pmpaddr_entries),
+ .access_fault (dmem_access_fault)
+ );
+
+ assign dmem_load_access_fault = dmem_access_fault &
+ (mem_is_load_in | (mem_is_amo_in && (mem_amo_op_in == AMO_LR)));
+ assign dmem_store_access_fault = dmem_access_fault & dmem_req_is_store;
+
mem_stage u_mem_stage (
+ .clk (clk),
+ .rst (rst),
.mem_alu_result (mem_alu_result_in),
.mem_branch_taken (mem_branch_taken_in),
.mem_branch_target (mem_branch_target_in),
.mem_rs1_rdata (mem_rs1_rdata_in),
.mem_rs2_rdata (mem_rs2_rdata_in),
+ .mem_fs2_rdata (mem_fs2_rdata_in),
.mem_rs1_addr (mem_rs1_addr_in),
.mem_pc (mem_pc_in),
.mem_imm (mem_imm_in),
.mem_instr (mem_instr_in),
+ .mem_instr_len (mem_instr_len_in),
.mem_is_load (mem_is_load_in),
.mem_is_store (mem_is_store_in),
.mem_mem_size (mem_mem_size_in),
+ .mem_is_amo (mem_is_amo_in),
+ .mem_amo_op (mem_amo_op_in),
+ .mem_is_fp (mem_is_fp_in),
+ .mem_fp_reg_write_en (mem_fp_reg_write_en_in),
+ .mem_fp_store_data_sel(mem_fp_store_data_sel_in),
+ .mem_fp_result (mem_fp_result_in),
+ .mem_fp_fflags (mem_fp_fflags_in),
.mem_reg_write_en (mem_reg_write_en_in),
.mem_rd_addr (mem_rd_addr_in),
.mem_wb_sel (mem_wb_sel_in),
.mem_is_csr (mem_is_csr_in),
.mem_csr_op (mem_csr_op_in),
.mem_illegal_instr (mem_illegal_instr_in),
+ .mem_instr_access_fault(mem_instr_access_fault_in),
+ .mem_instr_page_fault(mem_instr_page_fault_in),
.mem_is_ecall (mem_is_ecall_in),
.mem_is_ebreak (mem_is_ebreak_in),
.mem_is_mret (mem_is_mret_in),
@@ -516,19 +934,29 @@ module richard_core
.mem_is_sfence_vma (mem_is_sfence_vma_in),
.mem_is_fence (mem_is_fence_in),
.mem_is_fence_i (mem_is_fence_i_in),
- .dmem_read_req (dmem_read_en),
- .dmem_write_req (dmem_write_en),
- .dmem_addr (dmem_addr),
- .dmem_wdata (dmem_wdata),
- .dmem_wstrb (mw_reg_dmem_wstrb_in),
- .dmem_req_ready (1'b1),
- .dmem_rsp_valid (1'b1),
+ .dmem_read_req (raw_dmem_read_en),
+ .dmem_write_req (raw_dmem_write_en),
+ .dmem_addr (raw_dmem_addr),
+ .dmem_wdata (raw_dmem_wdata),
+ .dmem_wstrb (raw_dmem_wstrb),
+ .dmem_req_ready (data_translation_ready && !data_translation_page_fault &&
+ data_memory_port_available),
+ .dmem_rsp_valid (data_translation_ready && !data_translation_page_fault &&
+ data_memory_port_available),
.dmem_rdata (dmem_rdata),
+ .dmem_load_access_fault(dmem_load_access_fault),
+ .dmem_store_access_fault(dmem_store_access_fault),
+ .dmem_load_page_fault(dmem_load_page_fault),
+ .dmem_store_page_fault(dmem_store_page_fault),
.mem_wb_alu_result (mw_reg_alu_result_in),
.mem_wb_mem_rdata (mw_reg_mem_rdata_in),
+ .mem_wb_fp_result (mw_reg_fp_result_in),
+ .mem_wb_fp_fflags (mw_reg_fp_fflags_in),
.mem_wb_pc (mw_reg_pc_in),
.mem_wb_instr (mw_reg_instr_in),
+ .mem_wb_instr_len (mw_reg_instr_len_in),
.mem_wb_reg_write_en (mw_reg_reg_write_en_in),
+ .mem_wb_fp_reg_write_en(mw_reg_fp_reg_write_en_in),
.mem_wb_rd_addr (mw_reg_rd_addr_in),
.mem_wb_wb_sel (mw_reg_wb_sel_in),
.mem_wb_is_csr (mw_reg_is_csr_in),
@@ -537,28 +965,52 @@ module richard_core
.mem_wb_rs1_addr (mw_reg_rs1_addr_in),
.mem_wb_imm (mw_reg_imm_in),
.mem_wb_illegal_instr(mw_reg_illegal_instr_in),
+ .mem_wb_instr_access_fault(mw_reg_instr_access_fault_in),
+ .mem_wb_instr_page_fault(mw_reg_instr_page_fault_in),
.mem_wb_is_ecall (mw_reg_is_ecall_in),
.mem_wb_is_ebreak (mw_reg_is_ebreak_in),
.mem_wb_is_mret (mw_reg_is_mret_in),
.mem_wb_is_sret (mw_reg_is_sret_in),
.mem_wb_load_fault (mw_reg_load_fault_in),
.mem_wb_store_fault (mw_reg_store_fault_in),
+ .mem_wb_load_page_fault(mw_reg_load_page_fault_in),
+ .mem_wb_store_page_fault(mw_reg_store_page_fault_in),
.mem_wb_fault_addr (mw_reg_fault_addr_in),
.mem_stall_req (mem_stall_req)
);
- assign dmem_size = mem_mem_size_in;
+ always_comb begin
+ if (ptw_mem_req_valid) begin
+ dmem_read_en = 1'b1;
+ dmem_write_en = 1'b0;
+ dmem_addr = ptw_mem_req_addr;
+ dmem_wdata = '0;
+ dmem_size = 3'b011;
+ end else begin
+ dmem_read_en = raw_dmem_read_en && data_translation_ready &&
+ !data_translation_page_fault;
+ dmem_write_en = raw_dmem_write_en && data_translation_ready &&
+ !data_translation_page_fault;
+ dmem_addr = data_access_needs_translation ? mmu_d_resp_paddr : raw_dmem_addr;
+ dmem_wdata = raw_dmem_wdata;
+ dmem_size = mem_mem_size_in;
+ end
+ end
mem_wb_reg u_mem_wb_reg (
.clk (clk),
.rst (rst),
.stall_en (1'b0),
- .flush_en (trap_flush_req),
+ .flush_en (trap_flush_req | mem_stall_req),
.mem_pc (mw_reg_pc_in),
.mem_alu_result (mw_reg_alu_result_in),
.mem_mem_rdata (mw_reg_mem_rdata_in),
+ .mem_fp_result (mw_reg_fp_result_in),
+ .mem_fp_fflags (mw_reg_fp_fflags_in),
.mem_instr (mw_reg_instr_in),
+ .mem_instr_len (mw_reg_instr_len_in),
.mem_reg_write_en (mw_reg_reg_write_en_in),
+ .mem_fp_reg_write_en (mw_reg_fp_reg_write_en_in),
.mem_rd_addr (mw_reg_rd_addr_in),
.mem_wb_sel (mw_reg_wb_sel_in),
.mem_is_csr (mw_reg_is_csr_in),
@@ -567,18 +1019,26 @@ module richard_core
.mem_rs1_addr (mw_reg_rs1_addr_in),
.mem_imm (mw_reg_imm_in),
.mem_illegal_instr (mw_reg_illegal_instr_in),
+ .mem_instr_access_fault(mw_reg_instr_access_fault_in),
+ .mem_instr_page_fault(mw_reg_instr_page_fault_in),
.mem_is_ecall (mw_reg_is_ecall_in),
.mem_is_ebreak (mw_reg_is_ebreak_in),
.mem_is_mret (mw_reg_is_mret_in),
.mem_is_sret (mw_reg_is_sret_in),
.mem_load_fault (mw_reg_load_fault_in),
.mem_store_fault (mw_reg_store_fault_in),
+ .mem_load_page_fault (mw_reg_load_page_fault_in),
+ .mem_store_page_fault(mw_reg_store_page_fault_in),
.mem_fault_addr (mw_reg_fault_addr_in),
.wb_pc (wb_pc_in),
.wb_alu_result (wb_alu_result_in),
.wb_mem_rdata (wb_mem_rdata_in),
+ .wb_fp_result (wb_fp_result_in),
+ .wb_fp_fflags (wb_fp_fflags_in),
.wb_instr (wb_instr_in),
+ .wb_instr_len (wb_instr_len_in),
.wb_reg_write_en (wb_reg_write_en_in),
+ .wb_fp_reg_write_en (wb_fp_reg_write_en_in),
.wb_rd_addr (wb_rd_addr_in),
.wb_wb_sel (wb_wb_sel_in),
.wb_is_csr (wb_is_csr_in),
@@ -587,12 +1047,16 @@ module richard_core
.wb_rs1_addr (wb_rs1_addr_in),
.wb_imm (wb_imm_in),
.wb_illegal_instr (wb_illegal_instr_in),
+ .wb_instr_access_fault(wb_instr_access_fault),
+ .wb_instr_page_fault (wb_instr_page_fault),
.wb_is_ecall (wb_is_ecall_in),
.wb_is_ebreak (wb_is_ebreak_in),
.wb_is_mret (wb_is_mret_in),
.wb_is_sret (wb_is_sret_in),
.wb_load_fault (wb_load_fault),
.wb_store_fault (wb_store_fault),
+ .wb_load_page_fault (wb_load_page_fault),
+ .wb_store_page_fault (wb_store_page_fault),
.wb_fault_addr (wb_fault_addr_in)
);
@@ -602,9 +1066,12 @@ module richard_core
wb_stage u_wb_stage (
.mem_wb_alu_result (wb_alu_result_in),
.mem_wb_mem_rdata (wb_mem_rdata_in),
+ .mem_wb_fp_result (wb_fp_result_in),
+ .mem_wb_fp_fflags (wb_fp_fflags_in),
.mem_wb_pc (wb_pc_in),
- .mem_wb_pc_plus_4 (wb_pc_in + 64'd4),
+ .mem_wb_pc_plus_4 (wb_pc_in + {{(XLEN-3){1'b0}}, wb_instr_len_in}),
.mem_wb_reg_write_en(wb_reg_write_en_in),
+ .mem_wb_fp_reg_write_en(wb_fp_reg_write_en_in),
.mem_wb_rd_addr (wb_rd_addr_in),
.mem_wb_wb_sel (wb_wb_sel_in),
.mem_wb_is_csr (wb_is_csr_in),
@@ -614,18 +1081,28 @@ module richard_core
.mem_wb_imm (wb_imm_in),
.mem_wb_instr (wb_instr_in),
.mem_wb_illegal_instr(wb_illegal_instr_in),
+ .mem_wb_instr_access_fault(wb_instr_access_fault),
+ .mem_wb_instr_page_fault(wb_instr_page_fault),
.mem_wb_is_ecall (wb_is_ecall_in),
.mem_wb_is_ebreak (wb_is_ebreak_in),
.mem_wb_is_mret (wb_is_mret_in),
.mem_wb_is_sret (wb_is_sret_in),
.mem_wb_load_fault (wb_load_fault),
.mem_wb_store_fault (wb_store_fault),
+ .mem_wb_load_page_fault(wb_load_page_fault),
+ .mem_wb_store_page_fault(wb_store_page_fault),
.mem_wb_fault_addr (wb_fault_addr_in),
.csr_rdata (csr_rdata),
.csr_illegal_access (csr_illegal_access),
.wb_rd_wdata (wb_rd_wdata),
.wb_rd_addr (wb_rd_addr),
.wb_reg_write_en (wb_reg_write_en_out),
+ .wb_fd_wdata (wb_fd_wdata),
+ .wb_fd_addr (wb_fd_addr),
+ .wb_fp_write_en (wb_fp_write_en),
+ .wb_fp_fflags (wb_fp_fflags),
+ .wb_fp_state_dirty (wb_fp_state_dirty),
+ .wb_fp_fflags_valid (wb_fp_fflags_valid),
.csr_req (csr_req),
.csr_op (csr_op),
.csr_addr (csr_addr),
@@ -636,8 +1113,12 @@ module richard_core
.trap_is_mret (trap_is_mret),
.trap_is_sret (trap_is_sret),
.trap_epc (trap_epc),
+ .trap_instr_access_fault(trap_instr_access_fault),
+ .trap_instr_page_fault(trap_instr_page_fault),
.trap_load_fault (trap_load_fault),
.trap_store_fault (trap_store_fault),
+ .trap_load_page_fault(trap_load_page_fault),
+ .trap_store_page_fault(trap_store_page_fault),
.trap_bad_addr (trap_bad_addr),
.trap_bad_instr (trap_bad_instr)
);
@@ -656,21 +1137,37 @@ module richard_core
.trap_ctrl_mtval (trap_ctrl_mtval),
.trap_ctrl_mret_en (trap_ctrl_mret_en),
.trap_ctrl_sret_en (trap_ctrl_sret_en),
+ .m_timer_interrupt(m_timer_interrupt),
+ .m_software_interrupt(m_software_interrupt),
+ .m_external_interrupt(m_external_interrupt),
+ .s_timer_interrupt (s_timer_interrupt),
+ .s_software_interrupt(s_software_interrupt),
+ .s_external_interrupt(s_external_interrupt),
+ .fp_state_dirty (wb_fp_state_dirty),
+ .fp_fflags_valid (wb_fp_fflags_valid),
+ .fp_fflags (wb_fp_fflags),
.csr_rdata (csr_rdata),
.csr_mstatus (csr_mstatus_value),
.csr_sstatus (csr_sstatus_value),
.csr_medeleg (csr_medeleg_value),
.csr_mideleg (csr_mideleg_value),
+ .csr_mie (csr_mie_value),
+ .csr_sie (csr_sie_value),
+ .csr_mip (csr_mip_value),
+ .csr_sip (csr_sip_value),
.csr_mtvec (csr_mtvec_base),
.csr_stvec (csr_stvec_base),
.csr_mepc (csr_mepc_value),
.csr_sepc (csr_sepc_value),
.csr_satp (csr_satp_value),
+ .csr_frm (csr_frm_value),
.csr_priv_mode (csr_priv_mode_cur),
.csr_trap_vector (),
.csr_trap_to_s_mode (),
.csr_trap_vector_next (csr_trap_vector_value),
.csr_trap_to_s_mode_next(csr_trap_to_s_mode),
+ .csr_pmpcfg_entries (csr_pmpcfg_entries),
+ .csr_pmpaddr_entries (csr_pmpaddr_entries),
.csr_illegal_access (csr_illegal_access)
);
@@ -681,6 +1178,7 @@ module richard_core
.trap_is_ebreak (trap_is_ebreak),
.trap_is_mret (trap_is_mret),
.trap_is_sret (trap_is_sret),
+ .trap_instr_access_fault(trap_instr_access_fault),
.trap_load_fault (trap_load_fault),
.trap_store_fault (trap_store_fault),
.trap_instr_page_fault(trap_instr_page_fault),
@@ -688,10 +1186,12 @@ module richard_core
.trap_store_page_fault(trap_store_page_fault),
.trap_bad_addr (trap_bad_addr),
.trap_bad_instr (trap_bad_instr),
- .ext_timer_interrupt (ext_timer_interrupt),
- .ext_software_interrupt (ext_software_interrupt),
- .ext_external_interrupt (ext_external_interrupt),
.csr_mstatus_mie (csr_mstatus_mie),
+ .csr_mie (csr_mie_value),
+ .csr_sie (csr_sie_value),
+ .csr_mip (csr_mip_value),
+ .csr_sip (csr_sip_value),
+ .csr_mstatus (csr_mstatus_value),
.csr_mtvec (csr_mtvec_base),
.csr_stvec (csr_stvec_base),
.csr_mepc (csr_mepc_value),
diff --git a/rv64cpu/rtl/core/trap_ctrl.sv b/rv64cpu/rtl/core/trap_ctrl.sv
index 55278d9..0969fdc 100644
--- a/rv64cpu/rtl/core/trap_ctrl.sv
+++ b/rv64cpu/rtl/core/trap_ctrl.sv
@@ -29,6 +29,7 @@ module trap_ctrl
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,
@@ -37,13 +38,13 @@ module trap_ctrl
input logic [XLEN-1:0] trap_bad_addr,
input logic [INST_WIDTH-1:0] trap_bad_instr,
- // Interrupt pin from external source (asynchronous signal)
- input logic ext_timer_interrupt, // M-Mode Machine Timer Interrupt (MTIP)
- input logic ext_software_interrupt, // M-Mode Software Interrupt (MSIP)
- input logic ext_external_interrupt, // M-Mode External Interrupt (MEIP)
-
// 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)
@@ -74,20 +75,40 @@ module trap_ctrl
} 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_illegal_instr) begin
+ 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;
@@ -111,10 +132,6 @@ module trap_ctrl
has_sync_exception = 1'b1;
sync_cause = EXC_STORE_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_load_page_fault) begin
has_sync_exception = 1'b1;
sync_cause = EXC_LOAD_PAGE_FAULT;
@@ -126,25 +143,76 @@ module trap_ctrl
end
end
- // Interrupt Detection - Asynchronous, Protected by MIE
+ // 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;
-
- if (csr_mstatus_mie) begin
- if (ext_external_interrupt) begin
- has_interrupt = 1'b1;
- interrupt_cause = INTERRUPT_BIT | INTERRUPT_M_EXTERNAL;
- end else if (ext_software_interrupt) begin
- has_interrupt = 1'b1;
- interrupt_cause = INTERRUPT_BIT | INTERRUPT_M_SOFTWARE;
- end else if (ext_timer_interrupt) begin
- has_interrupt = 1'b1;
- interrupt_cause = INTERRUPT_BIT | INTERRUPT_M_TIMER;
- end
+ 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
@@ -161,10 +229,17 @@ module trap_ctrl
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 delegate_to_s = trap_trigger && (priv_mode_t'(csr_priv_mode) != PRIV_MODE_M) && deleg_mask[trap_cause_index];
+ 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;
diff --git a/rv64cpu/rtl/core/wb_stage.sv b/rv64cpu/rtl/core/wb_stage.sv
index d9bfd93..b19cb67 100644
--- a/rv64cpu/rtl/core/wb_stage.sv
+++ b/rv64cpu/rtl/core/wb_stage.sv
@@ -24,11 +24,14 @@ module wb_stage
// 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,
@@ -43,8 +46,12 @@ module wb_stage
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
@@ -55,6 +62,12 @@ module wb_stage
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
@@ -69,8 +82,12 @@ module wb_stage
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
);
@@ -81,7 +98,8 @@ module wb_stage
logic has_trap;
logic trap_blocker;
assign has_trap = mem_wb_illegal_instr | mem_wb_is_ecall | mem_wb_is_ebreak |
- mem_wb_load_fault | mem_wb_store_fault;
+ 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;
// --------------------------
@@ -119,19 +137,32 @@ module wb_stage
// 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 | 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_load_fault = mem_wb_load_fault;
- assign trap_store_fault = mem_wb_store_fault;
- assign trap_bad_addr = mem_wb_fault_addr;
- assign trap_bad_instr = mem_wb_instr;
+ 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/scripts/run_compliance.py b/rv64cpu/scripts/run_compliance.py
index 6932a15..8ec87c6 100755
--- a/rv64cpu/scripts/run_compliance.py
+++ b/rv64cpu/scripts/run_compliance.py
@@ -36,15 +36,24 @@ ARCH_TEST_DIR = PROJECT_ROOT / "sw" / "riscv-arch-test"
BUILD_DIR = PROJECT_ROOT / "build"
SUITES = {
- "rv64i": "I",
- "rv64im": "I,M",
- "rv64ia": "I,A",
- "rv64if": "I,F",
- "rv64id": "I,D",
- "rv64ic": "I,C",
- "rv64priv": "S,Sm",
- "rv64g": "I,M,A,F,D,C",
- "rv64full": "I,M,A,F,D,C,S,Sm",
+ "i": "I",
+ "m": "M",
+ "a": "Zaamo,Zalrsc",
+ "zicsr": "Zicsr",
+ "zifencei": "Zifencei",
+ "z": "Zicsr,Zifencei",
+ "f": "F",
+ "d": "D",
+ "c": "Zca,Zcd",
+ "priv_s": "S,Sm",
+ "priv_sv_base": "Sv,SvPMP,Svade,Svbare",
+ "priv_sv_optional": "SvZicbo,Svadu,SvaduPMP,Svnapot,Svpbmt",
+ "priv_sv": "Sv,SvPMP,SvZicbo,Svade,Svadu,SvaduPMP,Svbare,Svnapot,Svpbmt",
+ "priv_pmp": "PMPSm,PMPZca,PMPmisaligned",
+ "priv_all": "S,Sm,Sv,SvPMP,SvZicbo,Svade,Svadu,SvaduPMP,Svbare,Svnapot,Svpbmt,PMPSm,PMPZca,PMPmisaligned",
+ "g": "I,M,Zaamo,Zalrsc,F,D,Zicsr,Zifencei",
+ "gc": "I,M,Zaamo,Zalrsc,F,D,Zicsr,Zifencei,Zca,Zcd",
+ "full": "I,M,Zaamo,Zalrsc,F,D,Zicsr,Zifencei,Zca,Zcd,S,Sm,Sv,SvPMP,SvZicbo,Svade,Svadu,SvaduPMP,Svbare,Svnapot,Svpbmt,PMPSm,PMPZca,PMPmisaligned",
}
@@ -76,6 +85,44 @@ def discover_elfs(work_dir: Path, config_name: str) -> List[Path]:
return sorted(elf_root.rglob("*.elf"))
+def parse_extensions(extensions: str) -> List[str]:
+ return [ext.strip() for ext in extensions.split(",") if ext.strip()]
+
+
+def elf_extension_key(elf: Path, elf_root: Path, selected: set[str]) -> Optional[str]:
+ rel = elf.relative_to(elf_root)
+ parts = rel.parts
+ if len(parts) < 2:
+ return None
+ if parts[0] == "priv":
+ for part in parts[1:]:
+ if part in selected:
+ return part
+ return None
+ if parts[0].startswith("rv") and parts[1] in selected:
+ return parts[1]
+ return None
+
+
+def filter_elfs_by_extensions(elf_files: List[Path], elf_root: Path, extensions: str) -> List[Path]:
+ selected = set(parse_extensions(extensions))
+ filtered = []
+ for elf in elf_files:
+ if elf_extension_key(elf, elf_root, selected) is not None:
+ filtered.append(elf)
+ return filtered
+
+
+def count_elfs_by_extension(elf_files: List[Path], elf_root: Path, extensions: str) -> Dict[str, int]:
+ selected = set(parse_extensions(extensions))
+ counts = {ext: 0 for ext in parse_extensions(extensions)}
+ for elf in elf_files:
+ key = elf_extension_key(elf, elf_root, selected)
+ if key is not None:
+ counts[key] += 1
+ return counts
+
+
def find_reference_signature(elf_path: Path, work_dir: Path) -> Optional[Path]:
candidates = [
elf_path.with_suffix(".signature"),
@@ -83,6 +130,13 @@ def find_reference_signature(elf_path: Path, work_dir: Path) -> Optional[Path]:
elf_path.with_suffix(".ref"),
elf_path.parent / "signatures" / f"{elf_path.stem}.signature",
]
+
+ elf_root = next((parent for parent in elf_path.parents if parent.name == "elfs"), None)
+ if elf_root is not None:
+ rel = elf_path.relative_to(elf_root)
+ candidates.append(elf_root.parent / "build" / rel.with_suffix(".sig"))
+ candidates.extend(sorted((work_dir / "common").glob(f"*/build/{rel.with_suffix('.sig')}")))
+
for candidate in candidates:
if candidate.exists():
return candidate
@@ -118,6 +172,12 @@ def ensure_submodule_ready(arch_test_dir: Path):
def ensure_binary(sim_bin: Path, build_dir: Path, verbose: bool):
if sim_bin.exists():
return sim_bin
+
+ matches = [p for p in build_dir.rglob(sim_bin.name) if p.is_file() and os.access(p, os.X_OK)]
+ if matches:
+ matches.sort(key=lambda p: len(str(p)))
+ return matches[0]
+
print(f"Simulation binary not found: {sim_bin}")
print("Attempting to build compliance_runner target...")
result = run_cmd(["cmake", "--build", str(build_dir), "--target", "compliance_runner"], verbose=verbose)
@@ -131,10 +191,7 @@ def ensure_binary(sim_bin: Path, build_dir: Path, verbose: bool):
return sim_bin
matches = [p for p in build_dir.rglob(sim_bin.name) if p.is_file() and os.access(p, os.X_OK)]
- if len(matches) == 1:
- print(f"Using discovered simulation binary: {matches[0]}")
- return matches[0]
- if len(matches) > 1:
+ if matches:
matches.sort(key=lambda p: len(str(p)))
print(f"Using discovered simulation binary: {matches[0]}")
return matches[0]
@@ -225,7 +282,22 @@ def run_suite(
print(f"Generating ELFs for suite={suite_name}, extensions={extensions}")
generate_elfs(ARCH_TEST_DIR, config_file, work_dir, extensions, jobs, verbose)
- elf_files = discover_elfs(work_dir, config_name)
+ elf_root = work_dir / config_name / "elfs"
+ discovered_elfs = discover_elfs(work_dir, config_name)
+ elf_files = filter_elfs_by_extensions(discovered_elfs, elf_root, extensions)
+
+ if max_tests == 0:
+ elf_counts = count_elfs_by_extension(elf_files, elf_root, extensions)
+ missing_extensions = [ext for ext, count in elf_counts.items() if count == 0]
+ if missing_extensions:
+ print("No generated ELF files for requested extensions:")
+ for ext in missing_extensions:
+ print(f" {ext}")
+ print("Generated ELF counts:")
+ for ext, count in elf_counts.items():
+ print(f" {ext:16s} {count}")
+ return False
+
if max_tests > 0:
elf_files = elf_files[:max_tests]
@@ -246,6 +318,8 @@ def run_suite(
rel = elf.relative_to(work_dir / config_name / "elfs")
sig_path = signature_root / rel.with_suffix(".signature")
sig_path.parent.mkdir(parents=True, exist_ok=True)
+ if sig_path.exists():
+ sig_path.unlink()
if verbose:
print(f"[{idx}/{len(elf_files)}] {rel}")
@@ -267,13 +341,19 @@ def run_suite(
print(f"FAIL {rel} (signature not generated)")
continue
- if run_result.returncode not in (0, 3):
+ ref_sig = find_reference_signature(elf, work_dir)
+ if run_result.returncode != 0:
failed += 1
- if not verbose:
- print(f"FAIL {rel} (runner exit={run_result.returncode})")
+ if ref_sig is None:
+ no_reference += 1
+ print(f"FAIL {rel} (runner exit={run_result.returncode}, no reference signature)")
+ else:
+ if compare_text_files(sig_path, ref_sig):
+ print(f"FAIL {rel} (runner exit={run_result.returncode}, signature matched {ref_sig})")
+ else:
+ print(f"FAIL {rel} (runner exit={run_result.returncode}, signature mismatch against {ref_sig})")
continue
- ref_sig = find_reference_signature(elf, work_dir)
if ref_sig is None:
no_reference += 1
if require_reference:
@@ -287,7 +367,10 @@ def run_suite(
passed += 1
else:
failed += 1
- print(f"FAIL {rel} (signature mismatch against {ref_sig})")
+ if run_result.returncode != 0:
+ print(f"FAIL {rel} (runner exit={run_result.returncode}, signature mismatch against {ref_sig})")
+ else:
+ print(f"FAIL {rel} (signature mismatch against {ref_sig})")
print("=" * 60)
print(f"Suite: {suite_name}")
@@ -315,13 +398,18 @@ def main():
default="build/compliance_work",
help="Working directory for generated ELFs",
)
- parser.add_argument("--max-cycles", type=int, default=2000, help="Cycles per test for compliance runner")
+ parser.add_argument("--max-cycles", type=int, default=500000, help="Cycles per test for compliance runner")
parser.add_argument("--max-tests", type=int, default=0, help="Limit number of ELFs to run (0 = all)")
parser.add_argument("--jobs", type=int, default=0, help="Parallel jobs for ACT test generation")
parser.add_argument(
"--require-reference",
action="store_true",
- help="Fail tests when reference signature is missing",
+ help=argparse.SUPPRESS,
+ )
+ parser.add_argument(
+ "--allow-missing-reference",
+ action="store_true",
+ help="Pass tests without reference signatures when the runner exits successfully",
)
parser.add_argument(
"--keep-work",
@@ -363,7 +451,7 @@ def main():
max_cycles=args.max_cycles,
max_tests=args.max_tests,
jobs=args.jobs,
- require_reference=args.require_reference,
+ require_reference=not args.allow_missing_reference,
keep_work=args.keep_work,
skip_generate=args.skip_generate,
verbose=args.verbose,
diff --git a/rv64cpu/tb/CMakeLists.txt b/rv64cpu/tb/CMakeLists.txt
index 1312860..33a6224 100644
--- a/rv64cpu/tb/CMakeLists.txt
+++ b/rv64cpu/tb/CMakeLists.txt
@@ -81,6 +81,7 @@ add_verilator_target(
SOURCES ${RTL_CORE_DIR}/include/rv64_pkg.sv
${RTL_CORE_DIR}/decoder.sv
${RTL_CORE_DIR}/regfile.sv
+ ${RTL_CORE_DIR}/fp_regfile.sv
${RTL_CORE_DIR}/id_stage.sv
INCLUDE_DIRS ${RTL_COMMON_INCLUDES}
TB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/unit/tb_id_stage.cc
@@ -189,6 +190,27 @@ add_verilator_target(
TB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/unit/tb_tlb.cc
)
+add_verilator_target(
+ NAME tb_ptw
+ TOP_MODULE ptw
+ SOURCES ${RTL_CORE_DIR}/include/rv64_pkg.sv
+ ${RTL_CORE_DIR}/mmu/ptw.sv
+ INCLUDE_DIRS ${RTL_COMMON_INCLUDES}
+ TB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/unit/tb_ptw.cc
+)
+
+add_verilator_target(
+ NAME tb_mmu_top
+ TOP_MODULE mmu_top
+ SOURCES ${RTL_CORE_DIR}/include/rv64_pkg.sv
+ ${RTL_CORE_DIR}/mmu/tlb.sv
+ ${RTL_CORE_DIR}/mmu/ptw.sv
+ ${RTL_CORE_DIR}/mmu/mmu_top.sv
+ INCLUDE_DIRS ${RTL_COMMON_INCLUDES}
+ TB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/unit/tb_mmu_top.cc
+ EXTRA_FLAGS -Wno-VARHIDDEN
+)
+
# ═══════════════════════════════════════════════════════════════════════════════════
# Core Integration Test
# ═══════════════════════════════════════════════════════════════════════════════════
@@ -199,13 +221,16 @@ add_verilator_target(
SOURCES ${RTL_CORE_DIR}/include/rv64_pkg.sv
${RTL_CORE_DIR}/alu.sv
${RTL_CORE_DIR}/branch_unit.sv
+ ${RTL_CORE_DIR}/compressed_decoder.sv
${RTL_CORE_DIR}/decoder.sv
${RTL_CORE_DIR}/regfile.sv
+ ${RTL_CORE_DIR}/fp_regfile.sv
${RTL_CORE_DIR}/if_stage.sv
${RTL_CORE_DIR}/if_id_reg.sv
${RTL_CORE_DIR}/id_stage.sv
${RTL_CORE_DIR}/id_ex_reg.sv
${RTL_CORE_DIR}/ex_stage.sv
+ ${RTL_CORE_DIR}/fpu/fpu_unit.sv
${RTL_CORE_DIR}/ex_mem_reg.sv
${RTL_CORE_DIR}/mem_stage.sv
${RTL_CORE_DIR}/mem_wb_reg.sv
@@ -213,6 +238,10 @@ add_verilator_target(
${RTL_CORE_DIR}/hazard_unit.sv
${RTL_CORE_DIR}/include/csr_pkg.sv
${RTL_CORE_DIR}/csr_unit.sv
+ ${RTL_CORE_DIR}/pmp_checker.sv
+ ${RTL_CORE_DIR}/mmu/tlb.sv
+ ${RTL_CORE_DIR}/mmu/ptw.sv
+ ${RTL_CORE_DIR}/mmu/mmu_top.sv
${RTL_CORE_DIR}/trap_ctrl.sv
${RTL_CORE_DIR}/richard_core.sv
INCLUDE_DIRS ${RTL_COMMON_INCLUDES}
diff --git a/rv64cpu/tb/compliance/CMakeLists.txt b/rv64cpu/tb/compliance/CMakeLists.txt
index 0139f54..677cae6 100644
--- a/rv64cpu/tb/compliance/CMakeLists.txt
+++ b/rv64cpu/tb/compliance/CMakeLists.txt
@@ -12,13 +12,16 @@ add_verilator_target(
SOURCES ${RTL_CORE_DIR}/include/rv64_pkg.sv
${RTL_CORE_DIR}/alu.sv
${RTL_CORE_DIR}/branch_unit.sv
+ ${RTL_CORE_DIR}/compressed_decoder.sv
${RTL_CORE_DIR}/decoder.sv
${RTL_CORE_DIR}/regfile.sv
+ ${RTL_CORE_DIR}/fp_regfile.sv
${RTL_CORE_DIR}/if_stage.sv
${RTL_CORE_DIR}/if_id_reg.sv
${RTL_CORE_DIR}/id_stage.sv
${RTL_CORE_DIR}/id_ex_reg.sv
${RTL_CORE_DIR}/ex_stage.sv
+ ${RTL_CORE_DIR}/fpu/fpu_unit.sv
${RTL_CORE_DIR}/ex_mem_reg.sv
${RTL_CORE_DIR}/mem_stage.sv
${RTL_CORE_DIR}/mem_wb_reg.sv
@@ -26,6 +29,10 @@ add_verilator_target(
${RTL_CORE_DIR}/hazard_unit.sv
${RTL_CORE_DIR}/include/csr_pkg.sv
${RTL_CORE_DIR}/csr_unit.sv
+ ${RTL_CORE_DIR}/pmp_checker.sv
+ ${RTL_CORE_DIR}/mmu/tlb.sv
+ ${RTL_CORE_DIR}/mmu/ptw.sv
+ ${RTL_CORE_DIR}/mmu/mmu_top.sv
${RTL_CORE_DIR}/trap_ctrl.sv
${RTL_CORE_DIR}/richard_core.sv
INCLUDE_DIRS ${RTL_COMMON_INCLUDES}
diff --git a/rv64cpu/tb/compliance/compliance_runner.cc b/rv64cpu/tb/compliance/compliance_runner.cc
index b29662a..ba52647 100644
--- a/rv64cpu/tb/compliance/compliance_runner.cc
+++ b/rv64cpu/tb/compliance/compliance_runner.cc
@@ -27,10 +27,12 @@
#include <iostream>
#include <map>
#include <string>
+#include <sstream>
#include <vector>
#include <verilated.h>
#include "Vrichard_core.h"
+#include "Vrichard_core___024root.h"
namespace {
@@ -38,6 +40,7 @@ struct Options {
uint64_t max_cycles = 2000;
std::string elf_path;
std::string signature_path;
+ uint64_t trace_pc_count = 0;
bool show_help = false;
};
@@ -49,11 +52,21 @@ struct Symbols {
class ByteMemory {
public:
+ static constexpr uint64_t UART_THR = 0x10000000ULL;
+ static constexpr uint64_t UART_LSR = 0x10000005ULL;
+ static constexpr uint64_t CLINT_MTIME = 0x0200BFF8ULL;
+
void write_byte(uint64_t addr, uint8_t value) {
+ if (addr == UART_THR) {
+ return;
+ }
mem_[addr] = value;
}
uint8_t read_byte(uint64_t addr) const {
+ if (addr == UART_LSR) {
+ return 0x20;
+ }
auto it = mem_.find(addr);
if (it == mem_.end()) {
return 0;
@@ -186,6 +199,10 @@ Options parse_args(int argc, char** argv) {
opts.signature_path = argv[++i];
continue;
}
+ if (arg == "--trace-pc" && (i + 1) < argc) {
+ opts.trace_pc_count = static_cast<uint64_t>(std::stoull(argv[++i]));
+ continue;
+ }
}
return opts;
}
@@ -194,7 +211,46 @@ void print_help() {
std::cout
<< "compliance_runner (bootstrap)\n"
<< "Usage:\n"
- << " compliance_runner --elf PATH [--max-cycles N] [--signature PATH]\n";
+ << " compliance_runner --elf PATH [--max-cycles N] [--signature PATH] [--trace-pc N]\n";
+}
+
+std::string hex64(uint64_t value) {
+ std::ostringstream out;
+ out << "0x" << std::hex << std::setfill('0') << std::setw(16) << value;
+ return out.str();
+}
+
+void dump_selected_gprs(const Vrichard_core* top, const char* prefix) {
+ const auto& regs = top->rootp->richard_core__DOT__u_id_stage__DOT__u_regfile__DOT__regs;
+ std::cerr << prefix << ": selected GPRs\n"
+ << " x2/sp=" << hex64(regs[2])
+ << " x3/gp=" << hex64(regs[3])
+ << " x4/tp=" << hex64(regs[4])
+ << " x5/t0=" << hex64(regs[5]) << "\n"
+ << " x6/t1=" << hex64(regs[6])
+ << " x7/t2=" << hex64(regs[7])
+ << " x8/s0=" << hex64(regs[8])
+ << " x9/s1=" << hex64(regs[9]) << "\n"
+ << " x10/a0=" << hex64(regs[10])
+ << " x11/a1=" << hex64(regs[11])
+ << " x20/s4=" << hex64(regs[20])
+ << " x21/s5=" << hex64(regs[21]) << "\n";
+ std::cerr << " mstatus=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__mstatus_reg)
+ << " sstatus=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__sstatus_reg)
+ << " mcause=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__mcause_reg)
+ << " mepc=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__mepc_reg)
+ << "\n";
+ std::cerr << " priv=" << static_cast<unsigned>(top->rootp->richard_core__DOT__u_csr_unit__DOT__priv_mode_q)
+ << " medeleg=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__medeleg_reg)
+ << " mideleg=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__mideleg_reg)
+ << " mtvec=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__mtvec_reg)
+ << " stvec=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__stvec_reg)
+ << "\n";
+ std::cerr << " mstateen0=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__mstateen_reg[0])
+ << " sstateen0=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__sstateen_reg[0])
+ << " menvcfg=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__menvcfg_reg)
+ << " senvcfg=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__senvcfg_reg)
+ << "\n";
}
bool load_elf64(const std::string& path, ByteMemory* memory, Symbols* symbols) {
@@ -303,20 +359,34 @@ bool load_elf64(const std::string& path, ByteMemory* memory, Symbols* symbols) {
return true;
}
-void tick(Vrichard_core* top, ByteMemory* memory) {
+void drive_memory_response(Vrichard_core* top, ByteMemory* memory) {
top->imem_rdata = memory->read_u32(top->imem_addr);
top->dmem_rdata = top->dmem_read_en ? memory->read_u64(top->dmem_addr & ~0x7ULL) : 0;
- top->ext_timer_interrupt = 0;
- top->ext_software_interrupt = 0;
- top->ext_external_interrupt = 0;
+}
+
+void tick(Vrichard_core* top, ByteMemory* memory) {
+ drive_memory_response(top, memory);
+ top->m_timer_interrupt = 0;
+ top->m_software_interrupt = 0;
+ top->m_external_interrupt = 0;
+ top->s_timer_interrupt = 0;
+ top->s_software_interrupt = 0;
+ top->s_external_interrupt = 0;
top->clk = 0;
top->eval();
+ drive_memory_response(top, memory);
+ top->eval();
top->clk = 1;
top->eval();
if (top->dmem_write_en) {
memory->write_store(top->dmem_addr, top->dmem_wdata, top->dmem_size);
+ if ((top->dmem_addr <= ByteMemory::CLINT_MTIME + 7ULL) &&
+ (top->dmem_addr + 7ULL >= ByteMemory::CLINT_MTIME)) {
+ top->rootp->richard_core__DOT__u_csr_unit__DOT__time_reg =
+ memory->read_u64(ByteMemory::CLINT_MTIME);
+ }
}
}
@@ -377,10 +447,152 @@ int main(int argc, char** argv) {
bool saw_tohost = false;
bool test_pass = false;
+ uint64_t store_trace_printed = 0;
+ uint64_t read_trace_printed = 0;
+ uint64_t wb_trace_printed = 0;
+ uint64_t trap_trace_printed = 0;
+ std::string uart_trace;
+ std::vector<uint64_t> pc_trace;
+ if (opts.trace_pc_count != 0) {
+ pc_trace.reserve(static_cast<size_t>(opts.trace_pc_count));
+ }
+
for (uint64_t i = 0; i < opts.max_cycles; ++i) {
+ if (opts.trace_pc_count != 0) {
+ if (pc_trace.size() == opts.trace_pc_count) {
+ pc_trace.erase(pc_trace.begin());
+ }
+ pc_trace.push_back(top->imem_addr);
+ }
tick(top, &memory);
+ if (opts.trace_pc_count != 0 &&
+ top->dmem_read_en &&
+ (top->dmem_addr >= 0x000000008004cd20ULL) &&
+ (top->dmem_addr < 0x000000008004cd40ULL) &&
+ read_trace_printed < 32) {
+ std::cerr << "load cycle=" << i
+ << " pc=" << hex64(top->imem_addr)
+ << " addr=" << hex64(top->dmem_addr)
+ << " rdata=" << hex64(top->dmem_rdata)
+ << " size=" << static_cast<unsigned>(top->dmem_size)
+ << "\n";
+ ++read_trace_printed;
+ }
+ if (opts.trace_pc_count != 0 && top->dmem_write_en && top->dmem_addr == ByteMemory::UART_THR) {
+ uart_trace.push_back(static_cast<char>(top->dmem_wdata & 0xFFULL));
+ }
+ if (opts.trace_pc_count != 0 &&
+ top->dmem_write_en &&
+ top->dmem_addr >= 0x00000000800115e0ULL &&
+ top->dmem_addr < 0x0000000080011608ULL) {
+ std::cerr << "scratch-store cycle=" << i
+ << " pc=" << hex64(top->imem_addr)
+ << " addr=" << hex64(top->dmem_addr)
+ << " data=" << hex64(top->dmem_wdata)
+ << " size=" << static_cast<unsigned>(top->dmem_size)
+ << "\n";
+ }
+ if (opts.trace_pc_count != 0 &&
+ top->rootp->richard_core__DOT__csr_req &&
+ (((top->rootp->richard_core__DOT__wb_instr_in >> 20) & 0xFFFU) == 0x341 ||
+ ((top->rootp->richard_core__DOT__wb_instr_in >> 20) & 0xFFFU) == 0x141 ||
+ ((top->rootp->richard_core__DOT__wb_instr_in >> 20) & 0xFFFU) == 0x300 ||
+ ((top->rootp->richard_core__DOT__wb_instr_in >> 20) & 0xFFFU) == 0x100)) {
+ std::cerr << "csr cycle=" << i
+ << " pc=" << hex64(top->rootp->richard_core__DOT__wb_pc_in)
+ << " addr=0x" << std::hex << std::setw(3) << std::setfill('0')
+ << ((top->rootp->richard_core__DOT__wb_instr_in >> 20) & 0xFFFU)
+ << " op=" << std::dec
+ << static_cast<unsigned>((top->rootp->richard_core__DOT__wb_instr_in >> 12) & 0x7U)
+ << " rs1=" << hex64(top->rootp->richard_core__DOT__csr_rs1_data)
+ << " mepc_old=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__mepc_reg)
+ << " sepc_old=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__sepc_reg)
+ << " instr=0x" << std::hex << std::setw(8) << std::setfill('0')
+ << top->rootp->richard_core__DOT__wb_instr_in
+ << std::dec << std::setfill(' ') << "\n";
+ }
+ if (opts.trace_pc_count != 0 &&
+ (top->rootp->richard_core__DOT__trap_ctrl_mret_en ||
+ top->rootp->richard_core__DOT__trap_ctrl_sret_en)) {
+ std::cerr << "xret cycle=" << i
+ << " pc=" << hex64(top->rootp->richard_core__DOT__wb_pc_in)
+ << " mret=" << static_cast<unsigned>(top->rootp->richard_core__DOT__trap_ctrl_mret_en)
+ << " sret=" << static_cast<unsigned>(top->rootp->richard_core__DOT__trap_ctrl_sret_en)
+ << " target=" << hex64(top->rootp->richard_core__DOT__hu_pc_redirect_target)
+ << " mepc=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__mepc_reg)
+ << " sepc=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__sepc_reg)
+ << "\n";
+ }
+ if (opts.trace_pc_count != 0 &&
+ top->rootp->richard_core__DOT__wb_pc_in == 0x000000008000e1b4ULL &&
+ trap_trace_printed < 128) {
+ const auto& regs = top->rootp->richard_core__DOT__u_id_stage__DOT__u_regfile__DOT__regs;
+ const uint64_t sig_ptr = regs[6];
+ std::cerr << "trap-check cycle=" << i
+ << " t1=" << hex64(sig_ptr)
+ << " cause=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__mcause_reg)
+ << " mepc=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__mepc_reg)
+ << " stored_msig=" << hex64(memory.read_u64(0x00000000800115f0ULL))
+ << " exp_status=" << hex64(memory.read_u64(sig_ptr))
+ << " exp_cause=" << hex64(memory.read_u64(sig_ptr + 8))
+ << " exp_epc=" << hex64(memory.read_u64(sig_ptr + 16))
+ << "\n";
+ ++trap_trace_printed;
+ }
+ if (opts.trace_pc_count != 0 &&
+ (top->rootp->richard_core__DOT__wb_pc_in == 0x000000008000eb84ULL ||
+ top->rootp->richard_core__DOT__wb_pc_in == 0x000000008000eb88ULL ||
+ top->rootp->richard_core__DOT__wb_pc_in == 0x000000008000ebc8ULL ||
+ top->rootp->richard_core__DOT__wb_pc_in == 0x000000008000ebccULL ||
+ top->rootp->richard_core__DOT__wb_pc_in == 0x000000008000ebecULL ||
+ top->rootp->richard_core__DOT__wb_pc_in == 0x000000008000ec60ULL) &&
+ trap_trace_printed < 256) {
+ const auto& regs = top->rootp->richard_core__DOT__u_id_stage__DOT__u_regfile__DOT__regs;
+ std::cerr << "strap-debug cycle=" << i
+ << " pc=" << hex64(top->rootp->richard_core__DOT__wb_pc_in)
+ << " sp=" << hex64(regs[2])
+ << " t1=" << hex64(regs[6])
+ << " t2=" << hex64(regs[7])
+ << " s0=" << hex64(regs[8])
+ << " s1=" << hex64(regs[9])
+ << " a0=" << hex64(regs[10])
+ << " a1=" << hex64(regs[11])
+ << " sscratch=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__sscratch_reg)
+ << " scause=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__scause_reg)
+ << " sepc=" << hex64(top->rootp->richard_core__DOT__u_csr_unit__DOT__sepc_reg)
+ << " mem[Strap_sig]=" << hex64(memory.read_u64(0x00000000800115f0ULL))
+ << " mem[Ssig_bgn]=" << hex64(memory.read_u64(0x00000000800115d0ULL))
+ << " mem[Sbase]=" << hex64(memory.read_u64(0x00000000800115b0ULL))
+ << "\n";
+ ++trap_trace_printed;
+ }
+ if (opts.trace_pc_count != 0 &&
+ top->rootp->richard_core__DOT__wb_reg_write_en_out &&
+ (top->rootp->richard_core__DOT__wb_rd_addr_in == 8 ||
+ top->rootp->richard_core__DOT__wb_rd_addr_in == 4 ||
+ top->rootp->richard_core__DOT__wb_rd_addr_in == 13 ||
+ top->rootp->richard_core__DOT__wb_rd_addr_in == 20) &&
+ wb_trace_printed < 64) {
+ std::cerr << "wb cycle=" << i
+ << " pc=" << hex64(top->rootp->richard_core__DOT__wb_pc_in)
+ << " rd=x" << static_cast<unsigned>(top->rootp->richard_core__DOT__wb_rd_addr_in)
+ << " data=" << hex64(top->rootp->richard_core__DOT__wb_rd_wdata)
+ << " instr=0x" << std::hex << std::setw(8) << std::setfill('0')
+ << top->rootp->richard_core__DOT__wb_instr_in
+ << std::dec << std::setfill(' ') << "\n";
+ ++wb_trace_printed;
+ }
if (top->dmem_write_en && symbols.tohost != 0) {
const uint64_t addr = top->dmem_addr;
+ if (opts.trace_pc_count != 0 && store_trace_printed < 128) {
+ std::cerr << "store cycle=" << i
+ << " pc=" << hex64(top->imem_addr)
+ << " addr=" << hex64(addr)
+ << " data=" << hex64(top->dmem_wdata)
+ << " size=" << static_cast<unsigned>(top->dmem_size)
+ << "\n";
+ ++store_trace_printed;
+ }
const bool overlaps_tohost = (addr <= symbols.tohost + 3ULL) && (addr + 7ULL >= symbols.tohost);
if (overlaps_tohost) {
const uint32_t status = static_cast<uint32_t>(memory.read_u64(symbols.tohost) & 0xFFFFFFFFULL);
@@ -400,9 +612,23 @@ int main(int argc, char** argv) {
dump_signature(memory, symbols, opts.signature_path);
- delete top;
+ if (opts.trace_pc_count != 0 && !uart_trace.empty()) {
+ std::cerr << "uart output:\n" << uart_trace << "\n";
+ }
if (!saw_tohost) {
+ if (!pc_trace.empty()) {
+ std::cerr << "timeout: last " << pc_trace.size() << " fetch PCs\n";
+ for (uint64_t pc : pc_trace) {
+ std::cerr << " " << hex64(pc) << "\n";
+ }
+ dump_selected_gprs(top, "timeout");
+ }
+ delete top;
return 3;
}
+ if (opts.trace_pc_count != 0 && !test_pass) {
+ dump_selected_gprs(top, "fail");
+ }
+ delete top;
return test_pass ? 0 : 1;
}
diff --git a/rv64cpu/tb/core/tb_core.cc b/rv64cpu/tb/core/tb_core.cc
index 6877d8d..da758c1 100644
--- a/rv64cpu/tb/core/tb_core.cc
+++ b/rv64cpu/tb/core/tb_core.cc
@@ -352,17 +352,23 @@ public:
Vrichard_core* top;
MemoryModel mem;
uint64_t sim_time;
- bool ext_timer_interrupt_level;
- bool ext_software_interrupt_level;
- bool ext_external_interrupt_level;
+ bool m_timer_interrupt_level;
+ bool m_software_interrupt_level;
+ bool m_external_interrupt_level;
+ bool s_timer_interrupt_level;
+ bool s_software_interrupt_level;
+ bool s_external_interrupt_level;
#if VM_TRACE
VerilatedVcdC* tfp;
#endif
CoreSim() : sim_time(0),
- ext_timer_interrupt_level(false),
- ext_software_interrupt_level(false),
- ext_external_interrupt_level(false) {
+ m_timer_interrupt_level(false),
+ m_software_interrupt_level(false),
+ m_external_interrupt_level(false),
+ s_timer_interrupt_level(false),
+ s_software_interrupt_level(false),
+ s_external_interrupt_level(false) {
top = new Vrichard_core;
#if VM_TRACE
Verilated::traceEverOn(true);
@@ -401,9 +407,12 @@ public:
top->dmem_rdata = 0;
}
- top->ext_timer_interrupt = ext_timer_interrupt_level;
- top->ext_software_interrupt = ext_software_interrupt_level;
- top->ext_external_interrupt = ext_external_interrupt_level;
+ top->m_timer_interrupt = m_timer_interrupt_level;
+ top->m_software_interrupt = m_software_interrupt_level;
+ top->m_external_interrupt = m_external_interrupt_level;
+ top->s_timer_interrupt = s_timer_interrupt_level;
+ top->s_software_interrupt = s_software_interrupt_level;
+ top->s_external_interrupt = s_external_interrupt_level;
// Rising edge
top->clk = 0;
@@ -432,13 +441,20 @@ public:
}
void set_interrupt_lines(bool timer, bool software, bool external) {
- ext_timer_interrupt_level = timer;
- ext_software_interrupt_level = software;
- ext_external_interrupt_level = external;
+ m_timer_interrupt_level = timer;
+ m_software_interrupt_level = software;
+ m_external_interrupt_level = external;
}
void clear_interrupts() {
set_interrupt_lines(false, false, false);
+ set_supervisor_interrupt_lines(false, false, false);
+ }
+
+ void set_supervisor_interrupt_lines(bool timer, bool software, bool external) {
+ s_timer_interrupt_level = timer;
+ s_software_interrupt_level = software;
+ s_external_interrupt_level = external;
}
};
diff --git a/rv64cpu/tb/unit/tb_alu.cc b/rv64cpu/tb/unit/tb_alu.cc
index 04eaaed..0ed5059 100644
--- a/rv64cpu/tb/unit/tb_alu.cc
+++ b/rv64cpu/tb/unit/tb_alu.cc
@@ -22,16 +22,24 @@
#include <verilated.h>
enum AluOp {
- ALU_ADD = 0b0000,
- ALU_SUB = 0b1000,
- ALU_SLL = 0b0001,
- ALU_SLT = 0b0010,
- ALU_SLTU = 0b0011,
- ALU_XOR = 0b0100,
- ALU_SRL = 0b0101,
- ALU_SRA = 0b1101,
- ALU_OR = 0b0110,
- ALU_AND = 0b0111
+ ALU_ADD = 0,
+ ALU_SUB = 8,
+ ALU_SLL = 1,
+ ALU_SLT = 2,
+ ALU_SLTU = 3,
+ ALU_XOR = 4,
+ ALU_SRL = 5,
+ ALU_SRA = 13,
+ ALU_OR = 6,
+ ALU_AND = 7,
+ ALU_MUL = 16,
+ ALU_MULH = 17,
+ ALU_MULHSU = 18,
+ ALU_MULHU = 19,
+ ALU_DIV = 20,
+ ALU_DIVU = 21,
+ ALU_REM = 22,
+ ALU_REMU = 23
};
void test_alu(Valu* alu, uint8_t op, uint64_t rs1, uint64_t rs2, uint64_t expected, const char* test_name) {
@@ -104,10 +112,25 @@ int main(int argc, char** argv) {
// SLT Edge Cases
test_alu(alu, ALU_SLT, 0x8000000000000000ULL, 0x7FFFFFFFFFFFFFFFULL, 1, "SLT_Min_max");
test_alu(alu, ALU_SLT, 0x7FFFFFFFFFFFFFFFULL, 0x8000000000000000ULL, 0, "SLT_Max_Min");
+
+ // M extension
+ test_alu(alu, ALU_MUL, 7, 6, 42, "MUL_Positive");
+ test_alu(alu, ALU_MUL, 0xFFFFFFFFFFFFFFFFULL, 2, 0xFFFFFFFFFFFFFFFEULL, "MUL_NegativeLow");
+ test_alu(alu, ALU_MULH, 0xFFFFFFFFFFFFFFFFULL, 2, 0xFFFFFFFFFFFFFFFFULL, "MULH_Negative");
+ test_alu(alu, ALU_MULHU, 0xFFFFFFFFFFFFFFFFULL, 2, 1, "MULHU_UnsignedHigh");
+ test_alu(alu, ALU_DIV, (uint64_t)-21, 4, (uint64_t)-5, "DIV_SignedTruncTowardZero");
+ test_alu(alu, ALU_DIVU, 21, 4, 5, "DIVU");
+ test_alu(alu, ALU_REM, (uint64_t)-21, 4, (uint64_t)-1, "REM_Signed");
+ test_alu(alu, ALU_REMU, 21, 4, 1, "REMU");
+ test_alu(alu, ALU_DIV, 123, 0, 0xFFFFFFFFFFFFFFFFULL, "DIV_ByZero");
+ test_alu(alu, ALU_REM, 123, 0, 123, "REM_ByZero");
+ test_alu(alu, ALU_DIV, 0x8000000000000000ULL, 0xFFFFFFFFFFFFFFFFULL,
+ 0x8000000000000000ULL, "DIV_Overflow");
+ test_alu(alu, ALU_REM, 0x8000000000000000ULL, 0xFFFFFFFFFFFFFFFFULL, 0, "REM_Overflow");
std::cout << "----------------------------------" << std::endl;
std::cout << "\033[32mAll tests passed!\033[0m" << std::endl;
std::cout << "----------------------------------" << std::endl;
delete alu;
return 0;
-} \ No newline at end of file
+}
diff --git a/rv64cpu/tb/unit/tb_csr.cc b/rv64cpu/tb/unit/tb_csr.cc
index 8391759..e86ad9b 100644
--- a/rv64cpu/tb/unit/tb_csr.cc
+++ b/rv64cpu/tb/unit/tb_csr.cc
@@ -37,16 +37,21 @@ constexpr uint16_t CSR_MIDELEG = 0x303;
constexpr uint16_t CSR_MIE = 0x304;
constexpr uint16_t CSR_MTVEC = 0x305;
constexpr uint16_t CSR_MCOUNTEREN= 0x306;
+constexpr uint16_t CSR_MENVCFG = 0x30A;
+constexpr uint16_t CSR_MSTATEEN0 = 0x30C;
+constexpr uint16_t CSR_MCOUNTINHIBIT = 0x320;
constexpr uint16_t CSR_MSCRATCH = 0x340;
constexpr uint16_t CSR_MEPC = 0x341;
constexpr uint16_t CSR_MCAUSE = 0x342;
constexpr uint16_t CSR_MTVAL = 0x343;
+constexpr uint16_t CSR_MIP = 0x344;
constexpr uint16_t CSR_MCYCLE = 0xB00;
constexpr uint16_t CSR_MINSTRET = 0xB02;
constexpr uint16_t CSR_SSTATUS = 0x100;
constexpr uint16_t CSR_SIE = 0x104;
constexpr uint16_t CSR_STVEC = 0x105;
constexpr uint16_t CSR_SCOUNTEREN= 0x106;
+constexpr uint16_t CSR_SENVCFG = 0x10A;
constexpr uint16_t CSR_SSCRATCH = 0x140;
constexpr uint16_t CSR_SEPC = 0x141;
constexpr uint16_t CSR_SCAUSE = 0x142;
@@ -58,6 +63,7 @@ constexpr uint16_t CSR_INSTRET = 0xC02;
constexpr uint16_t CSR_FFLAGS = 0x001;
constexpr uint16_t CSR_FRM = 0x002;
constexpr uint16_t CSR_FCSR = 0x003;
+constexpr uint16_t CSR_MVENDORID = 0xF11;
constexpr uint64_t EXC_INST_ILLEGAL = 2ULL;
constexpr uint64_t EXC_ECALL_FROM_U = 8ULL;
@@ -69,18 +75,49 @@ constexpr uint64_t INTERRUPT_BIT = 1ULL << 63;
constexpr uint64_t INTERRUPT_M_SOFTWARE = 3ULL;
constexpr uint64_t INTERRUPT_M_TIMER = 7ULL;
constexpr uint64_t INTERRUPT_M_EXTERNAL = 11ULL;
+constexpr uint64_t INTERRUPT_S_SOFTWARE = 1ULL;
+constexpr uint64_t INTERRUPT_S_TIMER = 5ULL;
+constexpr uint64_t INTERRUPT_S_EXTERNAL = 9ULL;
constexpr int MSTATUS_MIE_BIT = 3;
constexpr int MSTATUS_MPIE_BIT = 7;
constexpr int MSTATUS_MPP_LO = 11;
constexpr int MSTATUS_MPP_HI = 12;
+constexpr int MSTATUS_TVM_BIT = 20;
+constexpr int MSTATUS_TW_BIT = 21;
+constexpr int MSTATUS_TSR_BIT = 22;
+constexpr int MSTATUS_UXL_LO = 32;
+constexpr int MSTATUS_SXL_LO = 34;
constexpr int SSTATUS_SIE_BIT = 1;
constexpr int SSTATUS_SPIE_BIT = 5;
constexpr int SSTATUS_SPP_BIT = 8;
+constexpr int MCOUNTINHIBIT_CY_BIT = 0;
+constexpr int MCOUNTINHIBIT_IR_BIT = 2;
+constexpr int STATEEN0_ENVCFG_BIT = 62;
+
+constexpr uint64_t STATUS_XLEN_64 = 2ULL;
+constexpr uint64_t MSTATUS_RESET_VALUE = (STATUS_XLEN_64 << MSTATUS_UXL_LO) |
+ (STATUS_XLEN_64 << MSTATUS_SXL_LO);
+constexpr uint64_t SSTATUS_RESET_VALUE = (STATUS_XLEN_64 << MSTATUS_UXL_LO);
constexpr uint64_t PRIV_ENCODE_U = 0ULL;
constexpr uint64_t PRIV_ENCODE_S = 1ULL;
constexpr uint64_t PRIV_ENCODE_M = 3ULL;
+constexpr uint64_t MEDELEG_WRITABLE_MASK =
+ (1ULL << EXC_ECALL_FROM_U) |
+ (1ULL << EXC_ECALL_FROM_S) |
+ (1ULL << EXC_LOAD_FAULT) |
+ (1ULL << EXC_STORE_FAULT);
+constexpr uint64_t MEDELEG_READ_ONLY_ONE_MASK = (1ULL << EXC_INST_ILLEGAL);
+constexpr uint64_t MIDELEG_WRITABLE_MASK =
+ (1ULL << INTERRUPT_S_SOFTWARE) |
+ (1ULL << INTERRUPT_S_TIMER) |
+ (1ULL << INTERRUPT_S_EXTERNAL);
+constexpr uint64_t MCOUNTINHIBIT_WRITABLE_MASK =
+ (1ULL << MCOUNTINHIBIT_CY_BIT) |
+ (1ULL << MCOUNTINHIBIT_IR_BIT) |
+ (0x1FFFFFFFULL << 3);
+constexpr uint64_t COUNTEREN_WRITABLE_MASK = 0xFFFFFFFFULL;
}
void tick(Vcsr_unit* dut) {
@@ -106,6 +143,12 @@ void drive_defaults(Vcsr_unit* dut) {
dut->trap_ctrl_mtval = 0;
dut->trap_ctrl_mret_en = 0;
dut->trap_ctrl_sret_en = 0;
+ dut->m_timer_interrupt = 0;
+ dut->m_software_interrupt = 0;
+ dut->m_external_interrupt = 0;
+ dut->s_timer_interrupt = 0;
+ dut->s_software_interrupt = 0;
+ dut->s_external_interrupt = 0;
}
void apply_reset(Vcsr_unit* dut) {
@@ -185,6 +228,9 @@ void advance_cycles(Vcsr_unit* dut, int cycles) {
}
void force_priv_mode_via_mret(Vcsr_unit* dut, uint64_t encoded_mode) {
+ if (dut->csr_priv_mode != PRIV_ENCODE_M) {
+ trigger_trap(dut, 0x100ULL, EXC_INST_ILLEGAL, 0ULL, false);
+ }
uint64_t mstatus = read_csr(dut, CSR_MSTATUS);
mstatus &= ~(0x3ULL << MSTATUS_MPP_LO);
mstatus |= (encoded_mode << MSTATUS_MPP_LO);
@@ -231,10 +277,10 @@ int main(int argc, char** argv) {
std::cout << "----------------------------------\n";
// Reset sanity
- assert(read_csr(dut, CSR_MSTATUS) == 0);
+ assert(read_csr(dut, CSR_MSTATUS) == MSTATUS_RESET_VALUE);
assert(read_csr(dut, CSR_MIE) == 0);
assert(read_csr(dut, CSR_MEPC) == 0);
- assert(read_csr(dut, CSR_SSTATUS) == 0);
+ assert(read_csr(dut, CSR_SSTATUS) == SSTATUS_RESET_VALUE);
assert(read_csr(dut, CSR_SATP) == 0);
std::cout << "[PASS] Reset clears CSR state" << std::endl;
@@ -247,30 +293,62 @@ int main(int argc, char** argv) {
// CSRRS / CSRRC behavior
csr_op(dut, CSR_MIE, CSR_OP_WRITE, 0);
- csr_op(dut, CSR_MIE, CSR_OP_SET, 0xBuLL);
- assert(read_csr(dut, CSR_MIE) == 0xBuLL);
- csr_op(dut, CSR_MIE, CSR_OP_CLEAR, 0x3ULL);
- assert(read_csr(dut, CSR_MIE) == 0x8ULL);
+ csr_op(dut, CSR_MIE, CSR_OP_SET, (1ULL << INTERRUPT_M_SOFTWARE) | (1ULL << INTERRUPT_M_TIMER));
+ assert(read_csr(dut, CSR_MIE) == ((1ULL << INTERRUPT_M_SOFTWARE) | (1ULL << INTERRUPT_M_TIMER)));
+ csr_op(dut, CSR_MIE, CSR_OP_CLEAR, (1ULL << INTERRUPT_M_SOFTWARE));
+ assert(read_csr(dut, CSR_MIE) == (1ULL << INTERRUPT_M_TIMER));
csr_op(dut, CSR_MIE, CSR_OP_SET, 0x0ULL);
- assert(read_csr(dut, CSR_MIE) == 0x8ULL);
+ assert(read_csr(dut, CSR_MIE) == (1ULL << INTERRUPT_M_TIMER));
std::cout << "[PASS] CSRRS/CSRRC honor masks and ignore zero data" << std::endl;
- // Read-only CSRs ignore write attempts
+ csr_op(
+ dut,
+ CSR_MSTATUS,
+ CSR_OP_WRITE,
+ (1ULL << MSTATUS_TVM_BIT) | (1ULL << MSTATUS_TW_BIT) | (1ULL << MSTATUS_TSR_BIT)
+ );
+ assert((read_csr(dut, CSR_MSTATUS) & ((1ULL << MSTATUS_TVM_BIT) |
+ (1ULL << MSTATUS_TW_BIT) |
+ (1ULL << MSTATUS_TSR_BIT))) == 0ULL);
+ std::cout << "[PASS] Unsupported mstatus trap-control bits are read-zero" << std::endl;
+
+ // Hardwired WARL CSRs may ignore writes without taking an illegal-instruction trap.
const uint64_t misa_ro_snapshot = read_csr(dut, CSR_MISA);
csr_op(dut, CSR_MISA, CSR_OP_WRITE, 0ULL);
assert(read_csr(dut, CSR_MISA) == misa_ro_snapshot);
- std::cout << "[PASS] Read-only CSRs reject writes" << std::endl;
+ std::cout << "[PASS] Hardwired WARL CSRs ignore unsupported writes" << std::endl;
- // Canonical aliases for cycle/minstret
- csr_op(dut, CSR_CYCLE, CSR_OP_WRITE, 0x40ULL);
+ // Machine counters are writable through machine CSR addresses; user aliases are read-only.
+ csr_op(dut, CSR_MCYCLE, CSR_OP_WRITE, 0x40ULL);
assert(read_csr(dut, CSR_MCYCLE) == 0x40ULL);
+ csr_op(dut, CSR_CYCLE, CSR_OP_WRITE, 0x100ULL);
+ assert(read_csr(dut, CSR_MCYCLE) == 0x41ULL);
advance_cycles(dut, 5);
- assert(read_csr(dut, CSR_MCYCLE) == 0x45ULL);
+ assert(read_csr(dut, CSR_MCYCLE) == 0x46ULL);
const uint64_t instret_before = read_csr(dut, CSR_MINSTRET);
advance_cycles(dut, 3);
assert(read_csr(dut, CSR_MINSTRET) == instret_before + 3);
std::cout << "[PASS] Cycle/instret counters tick correctly" << std::endl;
+ csr_op(dut, CSR_MCOUNTINHIBIT, CSR_OP_WRITE, ~0ULL);
+ assert(read_csr(dut, CSR_MCOUNTINHIBIT) == MCOUNTINHIBIT_WRITABLE_MASK);
+ const uint64_t inhibited_cycle = read_csr(dut, CSR_MCYCLE);
+ const uint64_t inhibited_instret = read_csr(dut, CSR_MINSTRET);
+ advance_cycles(dut, 3);
+ assert(read_csr(dut, CSR_MCYCLE) == inhibited_cycle);
+ assert(read_csr(dut, CSR_MINSTRET) == inhibited_instret);
+ csr_op(dut, CSR_MCOUNTINHIBIT, CSR_OP_WRITE, 0ULL);
+ advance_cycles(dut, 1);
+ assert(read_csr(dut, CSR_MCYCLE) == inhibited_cycle + 1);
+ assert(read_csr(dut, CSR_MINSTRET) == inhibited_instret + 1);
+ std::cout << "[PASS] mcountinhibit controls machine counters" << std::endl;
+
+ csr_op(dut, CSR_MCOUNTEREN, CSR_OP_WRITE, ~0ULL);
+ assert(read_csr(dut, CSR_MCOUNTEREN) == COUNTEREN_WRITABLE_MASK);
+ csr_op(dut, CSR_SCOUNTEREN, CSR_OP_WRITE, ~0ULL);
+ assert(read_csr(dut, CSR_SCOUNTEREN) == COUNTEREN_WRITABLE_MASK);
+ std::cout << "[PASS] counteren CSRs keep only implemented counter bits" << std::endl;
+
// Trap should freeze minstret for that cycle
uint64_t mstatus_cfg = read_csr(dut, CSR_MSTATUS) | (1ULL << MSTATUS_MIE_BIT);
csr_op(dut, CSR_MSTATUS, CSR_OP_WRITE, mstatus_cfg);
@@ -346,21 +424,54 @@ int main(int argc, char** argv) {
std::cout << "[PASS] medeleg routes ecall to supervisor" << std::endl;
force_priv_mode_via_mret(dut, PRIV_ENCODE_M);
+ csr_op(dut, CSR_MEDELEG, CSR_OP_WRITE, ~0ULL);
+ assert((read_csr(dut, CSR_MEDELEG) & MEDELEG_WRITABLE_MASK) == MEDELEG_WRITABLE_MASK);
+ assert((read_csr(dut, CSR_MEDELEG) & MEDELEG_READ_ONLY_ONE_MASK) == MEDELEG_READ_ONLY_ONE_MASK);
+ assert((read_csr(dut, CSR_MEDELEG) & (1ULL << 11)) == 0ULL);
+ csr_op(dut, CSR_MEDELEG, CSR_OP_WRITE, 0ULL);
+ assert((read_csr(dut, CSR_MEDELEG) & MEDELEG_READ_ONLY_ONE_MASK) == MEDELEG_READ_ONLY_ONE_MASK);
+ csr_op(dut, CSR_MIDELEG, CSR_OP_WRITE, ~0ULL);
+ assert((read_csr(dut, CSR_MIDELEG) & MIDELEG_WRITABLE_MASK) == MIDELEG_WRITABLE_MASK);
+ assert((read_csr(dut, CSR_MIDELEG) & (1ULL << INTERRUPT_M_TIMER)) == 0ULL);
+ std::cout << "[PASS] Delegation CSRs expose only writable standard bits" << std::endl;
+
// Basic supervisor CSRs touching for completeness
- csr_op(dut, CSR_SIE, CSR_OP_WRITE, 0x55ULL);
+ const uint64_t s_interrupt_mask = (1ULL << INTERRUPT_S_SOFTWARE) |
+ (1ULL << INTERRUPT_S_TIMER) |
+ (1ULL << INTERRUPT_S_EXTERNAL);
+ csr_op(dut, CSR_SIE, CSR_OP_WRITE, ~0ULL);
csr_op(dut, CSR_SSCRATCH, CSR_OP_WRITE, 0xAAULL);
csr_op(dut, CSR_SEPC, CSR_OP_WRITE, 0x1234ULL);
csr_op(dut, CSR_SCAUSE, CSR_OP_WRITE, 0x21ULL);
- csr_op(dut, CSR_SIP, CSR_OP_WRITE, 0x3ULL);
+ csr_op(dut, CSR_SIP, CSR_OP_WRITE, ~0ULL);
csr_op(dut, CSR_SATP, CSR_OP_WRITE, 0x8000000000000000ULL);
- assert(read_csr(dut, CSR_SIE) == 0x55ULL);
+ assert(read_csr(dut, CSR_SIE) == s_interrupt_mask);
assert(read_csr(dut, CSR_SSCRATCH) == 0xAAULL);
assert(read_csr(dut, CSR_SEPC) == 0x1234ULL);
assert(read_csr(dut, CSR_SCAUSE) == 0x21ULL);
- assert(read_csr(dut, CSR_SIP) == 0x3ULL);
+ assert(read_csr(dut, CSR_SIP) == s_interrupt_mask);
assert(read_csr(dut, CSR_SATP) == 0x8000000000000000ULL);
std::cout << "[PASS] Supervisor space CSR read/write" << std::endl;
+ csr_op(dut, CSR_MIE, CSR_OP_WRITE, 0ULL);
+ csr_op(dut, CSR_SIE, CSR_OP_WRITE, (1ULL << INTERRUPT_S_TIMER));
+ assert(read_csr(dut, CSR_SIE) == (1ULL << INTERRUPT_S_TIMER));
+ assert(read_csr(dut, CSR_MIE) == (1ULL << INTERRUPT_S_TIMER));
+
+ csr_op(dut, CSR_MIP, CSR_OP_WRITE, 0ULL);
+ dut->s_timer_interrupt = 1;
+ dut->eval();
+ assert(read_csr(dut, CSR_SIP) == (1ULL << INTERRUPT_S_TIMER));
+ assert(read_csr(dut, CSR_MIP) == (1ULL << INTERRUPT_S_TIMER));
+ dut->s_timer_interrupt = 0;
+
+ dut->m_external_interrupt = 1;
+ dut->eval();
+ assert((read_csr(dut, CSR_MIP) & (1ULL << INTERRUPT_M_EXTERNAL)) != 0ULL);
+ assert((read_csr(dut, CSR_SIP) & (1ULL << INTERRUPT_M_EXTERNAL)) == 0ULL);
+ dut->m_external_interrupt = 0;
+ std::cout << "[PASS] Interrupt pending and enable CSRs expose delegated views" << std::endl;
+
// Writing SSTATUS should mirror MSTATUS subset bits
csr_op(dut, CSR_SSTATUS, CSR_OP_WRITE, (1ULL << SSTATUS_SIE_BIT));
assert(((read_csr(dut, CSR_MSTATUS) >> SSTATUS_SIE_BIT) & 1ULL) == 1ULL);
@@ -457,8 +568,9 @@ int main(int argc, char** argv) {
});
run_csr_scenario(dut, "nested_return_sequence", [&](Vcsr_unit* target) {
- force_priv_mode_via_mret(target, PRIV_ENCODE_U);
+ force_priv_mode_via_mret(target, PRIV_ENCODE_S);
csr_op(target, CSR_SSTATUS, CSR_OP_WRITE, (1ULL << SSTATUS_SIE_BIT));
+ force_priv_mode_via_mret(target, PRIV_ENCODE_U);
const uint64_t first_trap_pc = 0xB00ULL;
trigger_trap(target, first_trap_pc, EXC_ECALL_FROM_U, 0ULL, true);
@@ -477,7 +589,9 @@ int main(int argc, char** argv) {
assert(target->csr_priv_mode == PRIV_ENCODE_U);
// Enable machine interrupts so the subsequent machine trap captures pre-trap MIE=1
+ force_priv_mode_via_mret(target, PRIV_ENCODE_M);
csr_op(target, CSR_MSTATUS, CSR_OP_SET, (1ULL << MSTATUS_MIE_BIT));
+ force_priv_mode_via_mret(target, PRIV_ENCODE_U);
const uint64_t second_trap_pc = 0xB80ULL;
trigger_trap(target, second_trap_pc, EXC_INST_ILLEGAL, 0ULL, false);
uint64_t mstatus_snapshot = read_csr(target, CSR_MSTATUS);
@@ -499,8 +613,8 @@ int main(int argc, char** argv) {
run_csr_scenario(dut, "illegal_csr_access_guard", [&](Vcsr_unit* target) {
force_priv_mode_via_mret(target, PRIV_ENCODE_M);
assert(target->csr_priv_mode == PRIV_ENCODE_M);
- const uint64_t misa_snapshot = read_csr(target, CSR_MISA);
- target->csr_addr = CSR_MISA;
+ const uint64_t vendor_snapshot = read_csr(target, CSR_MVENDORID);
+ target->csr_addr = CSR_MVENDORID;
target->csr_op = CSR_OP_WRITE;
target->csr_rs1_data = 0x1234ULL;
target->csr_req = 1;
@@ -510,12 +624,79 @@ int main(int argc, char** argv) {
target->csr_rs1_data = 0;
target->csr_addr = 0;
assert(target->csr_illegal_access);
- assert(read_csr(target, CSR_MISA) == misa_snapshot);
+ assert(read_csr(target, CSR_MVENDORID) == vendor_snapshot);
assert(!target->csr_trap_to_s_mode);
assert(target->csr_priv_mode == PRIV_ENCODE_M);
assert(target->csr_trap_vector_next == read_csr(target, CSR_MTVEC));
tick(target);
assert(!target->csr_illegal_access);
+
+ force_priv_mode_via_mret(target, PRIV_ENCODE_S);
+ assert(target->csr_priv_mode == PRIV_ENCODE_S);
+ target->csr_addr = CSR_MSTATUS;
+ target->csr_op = CSR_OP_WRITE;
+ target->csr_rs1_data = 0ULL;
+ target->csr_req = 1;
+ target->eval();
+ assert(target->csr_illegal_access);
+ tick(target);
+ target->csr_req = 0;
+ target->csr_op = 0;
+ target->csr_rs1_data = 0;
+ target->csr_addr = 0;
+ target->eval();
+ assert(!target->csr_illegal_access);
+
+ force_priv_mode_via_mret(target, PRIV_ENCODE_M);
+ csr_op(target, CSR_MCOUNTEREN, CSR_OP_WRITE, 0ULL);
+ force_priv_mode_via_mret(target, PRIV_ENCODE_S);
+ target->csr_addr = CSR_CYCLE;
+ target->csr_op = 0;
+ target->csr_rs1_data = 0ULL;
+ target->csr_req = 1;
+ target->eval();
+ assert(target->csr_illegal_access);
+ tick(target);
+ target->csr_req = 0;
+ target->csr_addr = 0;
+ target->eval();
+
+ force_priv_mode_via_mret(target, PRIV_ENCODE_M);
+ csr_op(target, CSR_MCOUNTEREN, CSR_OP_WRITE, 1ULL);
+ csr_op(target, CSR_SCOUNTEREN, CSR_OP_WRITE, 0ULL);
+ force_priv_mode_via_mret(target, PRIV_ENCODE_U);
+ target->csr_addr = CSR_CYCLE;
+ target->csr_op = 0;
+ target->csr_rs1_data = 0ULL;
+ target->csr_req = 1;
+ target->eval();
+ assert(target->csr_illegal_access);
+ tick(target);
+ target->csr_req = 0;
+ target->csr_addr = 0;
+ target->eval();
+
+ force_priv_mode_via_mret(target, PRIV_ENCODE_M);
+ csr_op(target, CSR_MSTATEEN0, CSR_OP_WRITE, 0ULL);
+ force_priv_mode_via_mret(target, PRIV_ENCODE_S);
+ target->csr_addr = CSR_SENVCFG;
+ target->csr_op = CSR_OP_WRITE;
+ target->csr_rs1_data = 1ULL;
+ target->csr_req = 1;
+ target->eval();
+ assert(target->csr_illegal_access);
+ tick(target);
+ target->csr_req = 0;
+ target->csr_op = 0;
+ target->csr_rs1_data = 0;
+ target->csr_addr = 0;
+ target->eval();
+
+ force_priv_mode_via_mret(target, PRIV_ENCODE_M);
+ csr_op(target, CSR_MSTATEEN0, CSR_OP_WRITE, (1ULL << STATEEN0_ENVCFG_BIT));
+ force_priv_mode_via_mret(target, PRIV_ENCODE_S);
+ csr_op(target, CSR_SENVCFG, CSR_OP_WRITE, 1ULL);
+ assert((read_csr(target, CSR_SENVCFG) & 1ULL) == 1ULL);
});
run_csr_scenario(dut, "delegation_cross_over", [&](Vcsr_unit* target) {
@@ -535,8 +716,8 @@ int main(int argc, char** argv) {
const std::array<DelegationProbe, 4> probes = {{
{"medeleg_set", false, EXC_ECALL_FROM_U, true, 0xD000ULL},
{"medeleg_clear", false, EXC_ECALL_FROM_U, false, 0xD040ULL},
- {"mideleg_set", true, INTERRUPT_M_TIMER, true, 0xD080ULL},
- {"mideleg_clear", true, INTERRUPT_M_TIMER, false, 0xD0C0ULL}
+ {"mideleg_set", true, INTERRUPT_S_TIMER, true, 0xD080ULL},
+ {"mideleg_clear", true, INTERRUPT_S_TIMER, false, 0xD0C0ULL}
}};
for (const auto& probe : probes) {
diff --git a/rv64cpu/tb/unit/tb_decoder.cc b/rv64cpu/tb/unit/tb_decoder.cc
index 25a3d69..1dcfb82 100644
--- a/rv64cpu/tb/unit/tb_decoder.cc
+++ b/rv64cpu/tb/unit/tb_decoder.cc
@@ -225,6 +225,55 @@ int main(int argc, char** argv) {
CHECK(dut, alu_op, 8, "SUBW"); // ALU_SUB
CHECK(dut, is_word_op, 1, "SUBW");
+ decode(dut, 0x023100B3, "MUL x1, x2, x3");
+ CHECK(dut, alu_op, 16, "MUL");
+ CHECK(dut, reg_write_en, 1, "MUL");
+ CHECK(dut, illegal_instr,0, "MUL");
+
+ decode(dut, 0x023140B3, "DIV x1, x2, x3");
+ CHECK(dut, alu_op, 20, "DIV");
+ CHECK(dut, illegal_instr,0, "DIV");
+
+ decode(dut, 0x023160B3, "REM x1, x2, x3");
+ CHECK(dut, alu_op, 22, "REM");
+ CHECK(dut, illegal_instr,0, "REM");
+
+ decode(dut, 0x023100BB, "MULW x1, x2, x3");
+ CHECK(dut, alu_op, 16, "MULW");
+ CHECK(dut, is_word_op, 1, "MULW");
+ CHECK(dut, illegal_instr,0, "MULW");
+
+ decode(dut, 0x0021A0AF, "AMOADD.W x1, x2, (x3)");
+ CHECK(dut, is_amo, 1, "AMOADD.W");
+ CHECK(dut, amo_op, 4, "AMOADD.W");
+ CHECK(dut, mem_size, 2, "AMOADD.W");
+ CHECK(dut, wb_sel, 1, "AMOADD.W");
+ CHECK(dut, reg_write_en, 1, "AMOADD.W");
+ CHECK(dut, illegal_instr,0, "AMOADD.W");
+
+ decode(dut, 0x100332AF, "LR.D x5, (x6)");
+ CHECK(dut, is_amo, 1, "LR.D");
+ CHECK(dut, amo_op, 1, "LR.D");
+ CHECK(dut, mem_size, 3, "LR.D");
+ CHECK(dut, rs2_addr, 0, "LR.D");
+ CHECK(dut, illegal_instr,0, "LR.D");
+
+ decode(dut, 0x1884B3AF, "SC.D x7, x8, (x9)");
+ CHECK(dut, is_amo, 1, "SC.D");
+ CHECK(dut, amo_op, 2, "SC.D");
+ CHECK(dut, mem_size, 3, "SC.D");
+ CHECK(dut, illegal_instr,0, "SC.D");
+
+ decode(dut, 0x0EB6252F, "AMOSWAP.W.AQRL x10, x11, (x12)");
+ CHECK(dut, is_amo, 1, "AMOSWAP.W.AQRL");
+ CHECK(dut, amo_op, 3, "AMOSWAP.W.AQRL");
+ CHECK(dut, illegal_instr,0, "AMOSWAP.W.AQRL");
+
+ decode(dut, 0x105332AF, "Illegal LR.D with rs2 != x0");
+ CHECK(dut, is_amo, 0, "Illegal LR.D");
+ CHECK(dut, reg_write_en, 0, "Illegal LR.D");
+ CHECK(dut, illegal_instr,1, "Illegal LR.D");
+
// 19. CSRRW x1, mstatus(0x300), x2
// imm=001100000000 rs1=00010 funct3=001 rd=00001 op=1110011
// Tests: is_csr, csr_op=funct3, reg_write=1
@@ -301,4 +350,4 @@ int main(int argc, char** argv) {
delete dut;
return (fail_count > 0) ? 1 : 0;
-} \ No newline at end of file
+}
diff --git a/rv64cpu/tb/unit/tb_hazard_unit.cc b/rv64cpu/tb/unit/tb_hazard_unit.cc
index d43ccc5..6314ab8 100644
--- a/rv64cpu/tb/unit/tb_hazard_unit.cc
+++ b/rv64cpu/tb/unit/tb_hazard_unit.cc
@@ -45,6 +45,7 @@ void reset(Vhazard_unit* dut) {
dut->ex_branch_target = 0;
dut->mem_rd_addr = 0;
dut->mem_reg_write_en = 0;
+ dut->mem_result_late = 0;
dut->mem_stall_req = 0;
dut->trap_flush_req = 0;
dut->trap_pc_sel = 0;
@@ -139,6 +140,26 @@ int main(int argc, char** argv) {
dut->eval();
check(dut->forward_b_sel == 1, "FwdB_MEM_Priority_Over_WB");
+ reset(dut);
+ dut->ex_rs1_addr = 3;
+ dut->mem_rd_addr = 3;
+ dut->mem_reg_write_en = 1;
+ dut->mem_result_late = 1;
+ dut->wb_rd_addr = 3;
+ dut->wb_reg_write_en = 1;
+ dut->eval();
+ check(dut->forward_a_sel == 2, "LateResult_ForwardA_From_WB");
+
+ reset(dut);
+ dut->ex_rs2_addr = 3;
+ dut->mem_rd_addr = 3;
+ dut->mem_reg_write_en = 1;
+ dut->mem_result_late = 1;
+ dut->wb_rd_addr = 3;
+ dut->wb_reg_write_en = 1;
+ dut->eval();
+ check(dut->forward_b_sel == 2, "LateResult_ForwardB_From_WB");
+
// -----------------------
// 7. No forward to x0 (hardwired zero)
// -----------------------
diff --git a/rv64cpu/tb/unit/tb_mem_stage.cc b/rv64cpu/tb/unit/tb_mem_stage.cc
index 2747ca9..417cb95 100644
--- a/rv64cpu/tb/unit/tb_mem_stage.cc
+++ b/rv64cpu/tb/unit/tb_mem_stage.cc
@@ -28,6 +28,15 @@ void tick(Vmem_stage* dut) {
dut->eval();
}
+void clock_tick(Vmem_stage* dut) {
+ dut->clk = 0;
+ dut->eval();
+ dut->clk = 1;
+ dut->eval();
+ dut->clk = 0;
+ dut->eval();
+}
+
void check(bool cond, const char* name) {
if (cond) {
std::cout << "\033[32m[PASS] " << name << "\033[0m" << std::endl;
@@ -47,15 +56,21 @@ int main(int argc, char** argv) {
std::cout << "----------------------------------" << std::endl;
// 1. Initial / Default states
+ dut->clk = 0;
+ dut->rst = 1;
dut->mem_alu_result = 0;
dut->mem_rs2_rdata = 0;
dut->mem_is_load = 0;
dut->mem_is_store = 0;
+ dut->mem_is_amo = 0;
+ dut->mem_amo_op = 0;
dut->mem_mem_size = 0;
dut->dmem_req_ready = 1;
dut->dmem_rsp_valid = 1;
dut->dmem_rdata = 0;
+ clock_tick(dut);
+ dut->rst = 0;
tick(dut);
check(dut->dmem_read_req == 0, "No_Read_Req");
@@ -186,7 +201,87 @@ int main(int argc, char** argv) {
tick(dut);
check(dut->mem_wb_mem_rdata == 0xAABBCCDDEEFF0011ULL, "LD_Data");
- // 5. Stall Logic
+ dut->mem_is_load = 0;
+
+ // 5. Atomic memory operations
+ dut->mem_is_amo = 1;
+ dut->mem_mem_size = 2; // AMO.W
+ dut->mem_amo_op = 4; // AMO_ADD
+ dut->mem_alu_result = 0x3004;
+ dut->mem_rs2_rdata = 3;
+ dut->dmem_rdata = 0x0000000500000000ULL;
+ dut->dmem_req_ready = 1;
+ dut->dmem_rsp_valid = 1;
+ tick(dut);
+ check(dut->dmem_read_req == 1, "AMOADDW_ReadReq");
+ check(dut->dmem_write_req == 0, "AMOADDW_NoWriteDuringRead");
+ check(dut->mem_stall_req == 1, "AMOADDW_StallDuringRead");
+ clock_tick(dut);
+
+ tick(dut);
+ check(dut->dmem_read_req == 0, "AMOADDW_NoReadDuringWrite");
+ check(dut->dmem_write_req == 1, "AMOADDW_WriteReq");
+ check(dut->dmem_wdata == 0x0000000800000000ULL, "AMOADDW_WriteData");
+ check(dut->dmem_wstrb == 0xF0, "AMOADDW_Strobe");
+ check(dut->mem_wb_mem_rdata == 5, "AMOADDW_ReturnOldValue");
+ check(dut->mem_stall_req == 0, "AMOADDW_NoStallDuringWrite");
+ clock_tick(dut);
+
+ dut->mem_mem_size = 3; // AMO.D
+ dut->mem_amo_op = 1; // AMO_LR
+ dut->mem_alu_result = 0x4000;
+ dut->dmem_rdata = 0x123456789ABCDEF0ULL;
+ tick(dut);
+ check(dut->dmem_read_req == 1, "LRD_ReadReq");
+ check(dut->dmem_write_req == 0, "LRD_NoWrite");
+ check(dut->mem_wb_mem_rdata == 0x123456789ABCDEF0ULL, "LRD_ReturnData");
+ clock_tick(dut);
+
+ dut->mem_amo_op = 2; // AMO_SC
+ dut->mem_rs2_rdata = 0xCAFEBABE11223344ULL;
+ tick(dut);
+ check(dut->dmem_read_req == 0, "SCD_NoRead");
+ check(dut->dmem_write_req == 1, "SCD_SuccessWrite");
+ check(dut->dmem_wdata == 0xCAFEBABE11223344ULL, "SCD_WriteData");
+ check(dut->mem_wb_mem_rdata == 0, "SCD_SuccessStatus");
+ clock_tick(dut);
+
+ tick(dut);
+ check(dut->dmem_write_req == 0, "SCD_FailNoWrite");
+ check(dut->mem_wb_mem_rdata == 1, "SCD_FailStatus");
+
+ dut->mem_amo_op = 1; // AMO_LR
+ dut->mem_alu_result = 0x5000;
+ dut->dmem_rdata = 0x1122334455667788ULL;
+ tick(dut);
+ check(dut->dmem_read_req == 1, "LRD_BeforeStore_ReadReq");
+ clock_tick(dut);
+
+ dut->mem_is_amo = 0;
+ dut->mem_is_store = 1;
+ dut->mem_mem_size = 0; // SB
+ dut->mem_alu_result = 0x5003;
+ dut->mem_rs2_rdata = 0xAA;
+ tick(dut);
+ check(dut->dmem_write_req == 1, "StoreAfterLR_WriteReq");
+ clock_tick(dut);
+
+ dut->mem_is_store = 0;
+ dut->mem_is_amo = 1;
+ dut->mem_mem_size = 3; // AMO.D
+ dut->mem_amo_op = 2; // AMO_SC
+ dut->mem_alu_result = 0x5000;
+ dut->mem_rs2_rdata = 0x0102030405060708ULL;
+ tick(dut);
+ check(dut->dmem_write_req == 1, "SCD_AfterStore_WriteReq");
+ check(dut->mem_wb_mem_rdata == 0, "SCD_AfterStore_SuccessStatus");
+
+ dut->mem_is_amo = 0;
+ dut->mem_is_store = 0;
+
+ // 6. Stall Logic
+ dut->mem_is_load = 1;
+ dut->mem_mem_size = 3;
dut->dmem_req_ready = 0; // Cache miss / AXI busy
dut->dmem_rsp_valid = 0;
tick(dut);
diff --git a/rv64cpu/tb/unit/tb_mmu_top.cc b/rv64cpu/tb/unit/tb_mmu_top.cc
new file mode 100644
index 0000000..5df90ef
--- /dev/null
+++ b/rv64cpu/tb/unit/tb_mmu_top.cc
@@ -0,0 +1,236 @@
+#include <cassert>
+#include <cstdint>
+#include <iostream>
+#include <unordered_map>
+
+#include <verilated.h>
+
+#include "Vmmu_top.h"
+
+namespace {
+
+constexpr uint8_t MODE_BARE = 0;
+constexpr uint8_t MODE_SV39 = 8;
+constexpr uint8_t MODE_SV48 = 9;
+
+constexpr uint64_t FLAG_V = 1ULL << 0;
+constexpr uint64_t FLAG_R = 1ULL << 1;
+constexpr uint64_t FLAG_W = 1ULL << 2;
+constexpr uint64_t FLAG_X = 1ULL << 3;
+constexpr uint64_t FLAG_A = 1ULL << 6;
+constexpr uint64_t FLAG_D = 1ULL << 7;
+
+int g_pass_count = 0;
+
+void check(bool cond, const char* name) {
+ if (cond) {
+ std::cout << "\033[32m[PASS] " << name << "\033[0m\n";
+ ++g_pass_count;
+ } else {
+ std::cout << "\033[31m[FAIL] " << name << "\033[0m\n";
+ assert(false);
+ }
+}
+
+uint64_t make_satp(uint8_t mode, uint16_t asid, uint64_t ppn) {
+ return (uint64_t(mode) << 60) | (uint64_t(asid) << 44) | (ppn & ((1ULL << 44) - 1ULL));
+}
+
+uint64_t make_pte(uint64_t ppn, uint64_t flags) {
+ return ((ppn & ((1ULL << 44) - 1ULL)) << 10) | (flags & 0x3ffULL);
+}
+
+void tick(Vmmu_top* dut) {
+ dut->clk = 0;
+ dut->eval();
+ dut->clk = 1;
+ dut->eval();
+}
+
+void reset(Vmmu_top* dut) {
+ dut->rst = 1;
+ dut->i_req_valid = 0;
+ dut->d_req_valid = 0;
+ dut->d_req_is_store = 0;
+ dut->flush_all = 0;
+ dut->flush_asid_valid = 0;
+ dut->flush_asid = 0;
+ dut->flush_vpn_valid = 0;
+ dut->flush_vpn = 0;
+ dut->ptw_mem_req_ready = 1;
+ dut->ptw_mem_resp_valid = 0;
+ dut->ptw_mem_resp_data = 0;
+ tick(dut);
+ tick(dut);
+ dut->rst = 0;
+ tick(dut);
+}
+
+struct StepResult {
+ bool issued;
+ uint64_t req_addr;
+ bool i_resp_valid;
+ bool i_resp_hit;
+ bool i_resp_page_fault;
+ uint64_t i_resp_paddr;
+ bool d_resp_valid;
+ bool d_resp_hit;
+ bool d_resp_page_fault;
+ uint64_t d_resp_paddr;
+};
+
+StepResult step(Vmmu_top* dut, bool inject_resp, uint64_t resp_data) {
+ dut->ptw_mem_req_ready = 1;
+ dut->ptw_mem_resp_valid = inject_resp ? 1 : 0;
+ dut->ptw_mem_resp_data = resp_data;
+
+ dut->clk = 0;
+ dut->eval();
+ const bool issued = dut->ptw_mem_req_valid && dut->ptw_mem_req_ready;
+ const uint64_t req_addr = dut->ptw_mem_req_addr;
+ StepResult result{
+ issued,
+ req_addr,
+ bool(dut->i_resp_valid),
+ bool(dut->i_resp_hit),
+ bool(dut->i_resp_page_fault),
+ dut->i_resp_paddr,
+ bool(dut->d_resp_valid),
+ bool(dut->d_resp_hit),
+ bool(dut->d_resp_page_fault),
+ dut->d_resp_paddr,
+ };
+
+ dut->clk = 1;
+ dut->eval();
+
+ return result;
+}
+
+void add_sv39_mapping(
+ std::unordered_map<uint64_t, uint64_t>& memory,
+ uint64_t root_ppn,
+ uint64_t vaddr,
+ uint64_t l1_ppn,
+ uint64_t l0_ppn,
+ uint64_t leaf_ppn,
+ uint64_t leaf_flags
+) {
+ const uint64_t vpn2 = (vaddr >> 30) & 0x1ffULL;
+ const uint64_t vpn1 = (vaddr >> 21) & 0x1ffULL;
+ const uint64_t vpn0 = (vaddr >> 12) & 0x1ffULL;
+ memory[(root_ppn << 12) + (vpn2 << 3)] = make_pte(l1_ppn, FLAG_V);
+ memory[(l1_ppn << 12) + (vpn1 << 3)] = make_pte(l0_ppn, FLAG_V);
+ memory[(l0_ppn << 12) + (vpn0 << 3)] = make_pte(leaf_ppn, leaf_flags);
+}
+
+StepResult run_until_response(
+ Vmmu_top* dut,
+ const std::unordered_map<uint64_t, uint64_t>& memory,
+ bool want_d
+) {
+ bool inject_resp = false;
+ uint64_t next_resp = 0;
+ StepResult last{};
+
+ for (int cycle = 0; cycle < 100; ++cycle) {
+ last = step(dut, inject_resp, next_resp);
+ if ((!want_d && last.i_resp_valid) || (want_d && last.d_resp_valid)) {
+ return last;
+ }
+
+ inject_resp = false;
+ next_resp = 0;
+ if (last.issued) {
+ const auto it = memory.find(last.req_addr);
+ next_resp = (it == memory.end()) ? 0 : it->second;
+ inject_resp = true;
+ }
+ }
+
+ check(false, "MMU_ResponseTimeout");
+ return last;
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ Verilated::commandArgs(argc, argv);
+ auto* dut = new Vmmu_top;
+
+ std::cout << "----------------------------------\n";
+ std::cout << " MMU Top Unit Test\n";
+ std::cout << "----------------------------------\n";
+
+ dut->satp_value = make_satp(MODE_BARE, 0, 0);
+ dut->priv_mode = 1;
+ dut->sum = 0;
+ dut->mxr = 0;
+ reset(dut);
+
+ dut->i_req_valid = 1;
+ dut->i_req_vaddr = 0x80001234ULL;
+ dut->d_req_valid = 1;
+ dut->d_req_vaddr = 0x90005678ULL;
+ dut->d_req_is_store = 0;
+ dut->eval();
+ check(dut->i_resp_valid && dut->i_resp_hit && dut->i_resp_paddr == 0x80001234ULL, "Bare_I_Bypass");
+ check(dut->d_resp_valid && dut->d_resp_hit && dut->d_resp_paddr == 0x90005678ULL, "Bare_D_Bypass");
+ dut->i_req_valid = 0;
+ dut->d_req_valid = 0;
+ tick(dut);
+
+ dut->satp_value = make_satp(MODE_SV48, 1, 0x100);
+ dut->i_req_valid = 1;
+ dut->i_req_vaddr = 0x0000000040401000ULL;
+ dut->eval();
+ check(dut->i_resp_valid && dut->i_resp_page_fault, "Sv48_Stub_IFault");
+ dut->i_req_valid = 0;
+ tick(dut);
+
+ const uint64_t root_ppn = 0x100;
+ const uint64_t i_va = 0x0000000040401234ULL;
+ const uint64_t d_va = 0x0000000080505678ULL;
+ const uint64_t i_leaf_ppn = 0x45670;
+ const uint64_t d_leaf_ppn = 0x56780;
+ std::unordered_map<uint64_t, uint64_t> memory;
+ add_sv39_mapping(memory, root_ppn, i_va, 0x210, 0x220, i_leaf_ppn,
+ FLAG_V | FLAG_R | FLAG_X | FLAG_A);
+ add_sv39_mapping(memory, root_ppn, d_va, 0x230, 0x240, d_leaf_ppn,
+ FLAG_V | FLAG_R | FLAG_W | FLAG_A | FLAG_D);
+
+ dut->satp_value = make_satp(MODE_SV39, 1, root_ppn);
+ dut->i_req_valid = 1;
+ dut->i_req_vaddr = i_va;
+ StepResult i_resp = run_until_response(dut, memory, false);
+ check(i_resp.i_resp_hit && !i_resp.i_resp_page_fault, "Sv39_I_Walk_Hit");
+ check(i_resp.i_resp_paddr == ((i_leaf_ppn << 12) | (i_va & 0xfffULL)), "Sv39_I_Walk_Paddr");
+ dut->i_req_valid = 0;
+ tick(dut);
+
+ dut->d_req_valid = 1;
+ dut->d_req_vaddr = d_va;
+ dut->d_req_is_store = 1;
+ StepResult d_resp = run_until_response(dut, memory, true);
+ check(d_resp.d_resp_hit && !d_resp.d_resp_page_fault, "Sv39_D_Walk_Hit");
+ check(d_resp.d_resp_paddr == ((d_leaf_ppn << 12) | (d_va & 0xfffULL)), "Sv39_D_Walk_Paddr");
+
+ dut->d_req_valid = 0;
+ tick(dut);
+ dut->i_req_valid = 1;
+ dut->i_req_vaddr = i_va;
+ dut->d_req_valid = 1;
+ dut->d_req_vaddr = d_va;
+ dut->d_req_is_store = 1;
+ dut->eval();
+ check(dut->i_resp_valid && dut->i_resp_hit, "Sv39_I_TLB_Hit");
+ check(dut->d_resp_valid && dut->d_resp_hit, "Sv39_D_TLB_Hit");
+ check(dut->ptw_mem_req_valid == 0, "Sv39_TLB_Hit_NoPTW");
+
+ std::cout << "----------------------------------\n";
+ std::cout << "All " << g_pass_count << " MMU top tests passed!\n";
+ std::cout << "----------------------------------\n";
+
+ delete dut;
+ return 0;
+}
diff --git a/rv64cpu/tb/unit/tb_ptw.cc b/rv64cpu/tb/unit/tb_ptw.cc
new file mode 100644
index 0000000..f32de41
--- /dev/null
+++ b/rv64cpu/tb/unit/tb_ptw.cc
@@ -0,0 +1,206 @@
+#include <cassert>
+#include <cstdint>
+#include <iostream>
+#include <unordered_map>
+
+#include <verilated.h>
+
+#include "Vptw.h"
+
+namespace {
+
+constexpr uint8_t MODE_BARE = 0;
+constexpr uint8_t MODE_SV39 = 8;
+constexpr uint8_t MODE_SV48 = 9;
+constexpr uint8_t MODE_SV57 = 10;
+constexpr uint8_t MODE_SV64 = 11;
+
+constexpr uint64_t FLAG_V = 1ULL << 0;
+constexpr uint64_t FLAG_R = 1ULL << 1;
+constexpr uint64_t FLAG_W = 1ULL << 2;
+constexpr uint64_t FLAG_X = 1ULL << 3;
+constexpr uint64_t FLAG_A = 1ULL << 6;
+constexpr uint64_t FLAG_D = 1ULL << 7;
+
+int g_pass_count = 0;
+
+void check(bool cond, const char* name) {
+ if (cond) {
+ std::cout << "\033[32m[PASS] " << name << "\033[0m\n";
+ ++g_pass_count;
+ } else {
+ std::cout << "\033[31m[FAIL] " << name << "\033[0m\n";
+ assert(false);
+ }
+}
+
+uint64_t make_satp(uint8_t mode, uint16_t asid, uint64_t ppn) {
+ return (uint64_t(mode) << 60) | (uint64_t(asid) << 44) | (ppn & ((1ULL << 44) - 1ULL));
+}
+
+uint64_t make_pte(uint64_t ppn, uint64_t flags) {
+ return ((ppn & ((1ULL << 44) - 1ULL)) << 10) | (flags & 0x3ffULL);
+}
+
+void tick(Vptw* dut) {
+ dut->clk = 0;
+ dut->eval();
+ dut->clk = 1;
+ dut->eval();
+}
+
+void reset(Vptw* dut) {
+ dut->rst = 1;
+ dut->req_valid = 0;
+ dut->mem_req_ready = 1;
+ dut->mem_resp_valid = 0;
+ dut->mem_resp_data = 0;
+ tick(dut);
+ tick(dut);
+ dut->rst = 0;
+ tick(dut);
+}
+
+void launch(
+ Vptw* dut,
+ uint64_t vaddr,
+ uint8_t mode,
+ bool fetch,
+ bool store,
+ uint64_t satp
+) {
+ for (int i = 0; i < 8 && !dut->req_ready; ++i) {
+ dut->req_valid = 0;
+ dut->mem_resp_valid = 0;
+ tick(dut);
+ }
+
+ dut->req_valid = 1;
+ dut->req_vaddr = vaddr;
+ dut->req_asid = 1;
+ dut->req_mode = mode;
+ dut->req_priv = 1;
+ dut->req_is_fetch = fetch;
+ dut->req_is_store = store;
+ dut->req_sum = 0;
+ dut->req_mxr = 0;
+ dut->satp_value = satp;
+ tick(dut);
+ dut->req_valid = 0;
+}
+
+void run_walk(Vptw* dut, const std::unordered_map<uint64_t, uint64_t>& memory) {
+ bool inject_resp = false;
+ uint64_t next_resp_data = 0;
+
+ for (int cycle = 0; cycle < 80; ++cycle) {
+ dut->mem_req_ready = 1;
+ dut->mem_resp_valid = inject_resp ? 1 : 0;
+ dut->mem_resp_data = next_resp_data;
+
+ dut->clk = 0;
+ dut->eval();
+
+ if (dut->resp_valid) {
+ dut->mem_resp_valid = 0;
+ return;
+ }
+
+ const bool issued = dut->mem_req_valid && dut->mem_req_ready;
+ const uint64_t req_addr = dut->mem_req_addr;
+
+ dut->clk = 1;
+ dut->eval();
+
+ inject_resp = false;
+ next_resp_data = 0;
+ if (issued) {
+ const auto it = memory.find(req_addr);
+ next_resp_data = (it == memory.end()) ? 0 : it->second;
+ inject_resp = true;
+ }
+ }
+
+ check(false, "PTW_Timeout");
+}
+
+void add_sv39_mapping(
+ std::unordered_map<uint64_t, uint64_t>& memory,
+ uint64_t root_ppn,
+ uint64_t vaddr,
+ uint64_t l1_ppn,
+ uint64_t l0_ppn,
+ uint64_t leaf_ppn,
+ uint64_t leaf_flags
+) {
+ const uint64_t vpn2 = (vaddr >> 30) & 0x1ffULL;
+ const uint64_t vpn1 = (vaddr >> 21) & 0x1ffULL;
+ const uint64_t vpn0 = (vaddr >> 12) & 0x1ffULL;
+ memory[(root_ppn << 12) + (vpn2 << 3)] = make_pte(l1_ppn, FLAG_V);
+ memory[(l1_ppn << 12) + (vpn1 << 3)] = make_pte(l0_ppn, FLAG_V);
+ memory[(l0_ppn << 12) + (vpn0 << 3)] = make_pte(leaf_ppn, leaf_flags);
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ Verilated::commandArgs(argc, argv);
+ auto* dut = new Vptw;
+
+ std::cout << "----------------------------------\n";
+ std::cout << " PTW Unit Test\n";
+ std::cout << "----------------------------------\n";
+
+ reset(dut);
+
+ launch(dut, 0x0000000040401000ULL, MODE_SV48, false, false, make_satp(MODE_BARE, 1, 0x100));
+ run_walk(dut, {});
+ check(dut->resp_fault == 1, "Sv48_Stub_Fault");
+
+ launch(dut, 0x0000000040401000ULL, MODE_SV57, false, false, make_satp(MODE_BARE, 1, 0x100));
+ run_walk(dut, {});
+ check(dut->resp_fault == 1, "Sv57_Stub_Fault");
+
+ launch(dut, 0x0000000040401000ULL, MODE_SV64, false, false, make_satp(MODE_BARE, 1, 0x100));
+ run_walk(dut, {});
+ check(dut->resp_fault == 1, "Sv64_Stub_Fault");
+
+ const uint64_t root_ppn = 0x100;
+ const uint64_t va = 0x0000000040401234ULL;
+ const uint64_t leaf_ppn = 0x45670;
+ std::unordered_map<uint64_t, uint64_t> mem_ok;
+ add_sv39_mapping(mem_ok, root_ppn, va, 0x210, 0x220, leaf_ppn,
+ FLAG_V | FLAG_R | FLAG_W | FLAG_A | FLAG_D);
+
+ launch(dut, va, MODE_SV39, false, false, make_satp(MODE_SV39, 1, root_ppn));
+ run_walk(dut, mem_ok);
+ check(dut->resp_fault == 0, "Sv39_Success_NoFault");
+ check(dut->resp_paddr == ((leaf_ppn << 12) | (va & 0xfffULL)), "Sv39_Success_Paddr");
+ check(dut->refill_valid == 1, "Sv39_Success_Refill");
+ check(dut->refill_level == 0, "Sv39_Success_Level0");
+
+ std::unordered_map<uint64_t, uint64_t> mem_no_x;
+ add_sv39_mapping(mem_no_x, root_ppn, va, 0x230, 0x240, 0x50000, FLAG_V | FLAG_R | FLAG_A);
+ launch(dut, va, MODE_SV39, true, false, make_satp(MODE_SV39, 1, root_ppn));
+ run_walk(dut, mem_no_x);
+ check(dut->resp_fault == 1, "Sv39_Fetch_NoX_Fault");
+ check(dut->refill_valid == 0, "Sv39_Fetch_NoX_NoRefill");
+
+ std::unordered_map<uint64_t, uint64_t> mem_store_d0;
+ add_sv39_mapping(mem_store_d0, root_ppn, va, 0x250, 0x260, 0x60000,
+ FLAG_V | FLAG_R | FLAG_W | FLAG_A);
+ launch(dut, va, MODE_SV39, false, true, make_satp(MODE_SV39, 1, root_ppn));
+ run_walk(dut, mem_store_d0);
+ check(dut->resp_fault == 1, "Sv39_Store_D0_Fault");
+
+ launch(dut, 0x0000008000000000ULL, MODE_SV39, false, false, make_satp(MODE_SV39, 1, root_ppn));
+ run_walk(dut, {});
+ check(dut->resp_fault == 1, "Sv39_NonCanonical_Fault");
+
+ std::cout << "----------------------------------\n";
+ std::cout << "All " << g_pass_count << " PTW tests passed!\n";
+ std::cout << "----------------------------------\n";
+
+ delete dut;
+ return 0;
+}
diff --git a/rv64cpu/tb/unit/tb_tlb.cc b/rv64cpu/tb/unit/tb_tlb.cc
index f244fbe..0a57738 100644
--- a/rv64cpu/tb/unit/tb_tlb.cc
+++ b/rv64cpu/tb/unit/tb_tlb.cc
@@ -1,19 +1,3 @@
-// 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 <https://www.gnu.org/licenses/>.
-
-
#include <cassert>
#include <cstdint>
#include <iostream>
@@ -25,9 +9,10 @@
namespace {
constexpr uint8_t MODE_BARE = 0;
-constexpr uint8_t MODE_SV32 = 1;
+constexpr uint8_t MODE_SV39 = 8;
constexpr uint8_t MODE_SV48 = 9;
constexpr uint8_t MODE_SV57 = 10;
+constexpr uint8_t MODE_SV64 = 11;
int g_pass_count = 0;
@@ -41,10 +26,6 @@ void check(bool cond, const char* name) {
}
}
-void eval_comb(Vtlb* dut) {
- dut->eval();
-}
-
void tick(Vtlb* dut) {
dut->clk = 0;
dut->eval();
@@ -52,27 +33,11 @@ void tick(Vtlb* dut) {
dut->eval();
}
-uint64_t make_paddr(uint64_t ppn, uint64_t vaddr) {
- return (ppn << 12) | (vaddr & 0xFFFULL);
-}
-
-uint64_t sv48_vpn(uint64_t vaddr) {
- return (vaddr >> 12) & ((1ULL << 36) - 1ULL);
-}
-
-uint64_t sv32_vpn(uint64_t vaddr) {
- return (vaddr >> 12) & ((1ULL << 20) - 1ULL);
-}
-
-uint64_t sv57_vpn(uint64_t vaddr) {
- return (vaddr >> 12) & ((1ULL << 45) - 1ULL);
-}
-
void clear_req(Vtlb* dut) {
dut->req_valid = 0;
dut->req_vaddr = 0;
dut->req_asid = 0;
- dut->req_mode = MODE_SV48;
+ dut->req_mode = MODE_SV39;
dut->req_priv = 1;
dut->req_is_fetch = 0;
dut->req_is_store = 0;
@@ -85,7 +50,7 @@ void clear_refill(Vtlb* dut) {
dut->refill_vpn = 0;
dut->refill_ppn = 0;
dut->refill_level = 0;
- dut->refill_mode = MODE_SV48;
+ dut->refill_mode = MODE_SV39;
dut->refill_asid = 0;
dut->refill_g = 0;
dut->refill_u = 0;
@@ -102,34 +67,7 @@ void clear_flush(Vtlb* dut) {
dut->flush_asid = 0;
dut->flush_vpn_valid = 0;
dut->flush_vpn = 0;
- dut->flush_mode = MODE_SV48;
-}
-
-void apply_flush_all(Vtlb* dut) {
- clear_flush(dut);
- dut->flush_all = 1;
- tick(dut);
- dut->flush_all = 0;
- eval_comb(dut);
-}
-
-void apply_flush_asid(Vtlb* dut, uint16_t asid) {
- clear_flush(dut);
- dut->flush_asid_valid = 1;
- dut->flush_asid = asid;
- tick(dut);
- clear_flush(dut);
- eval_comb(dut);
-}
-
-void apply_flush_vpn(Vtlb* dut, uint64_t vpn, uint8_t mode) {
- clear_flush(dut);
- dut->flush_vpn_valid = 1;
- dut->flush_vpn = vpn;
- dut->flush_mode = mode;
- tick(dut);
- clear_flush(dut);
- eval_comb(dut);
+ dut->flush_mode = MODE_SV39;
}
void reset(Vtlb* dut) {
@@ -140,77 +78,42 @@ void reset(Vtlb* dut) {
tick(dut);
tick(dut);
dut->rst = 0;
- eval_comb(dut);
+ dut->eval();
+}
+
+uint64_t sv39_vpn(uint64_t vaddr) {
+ return (vaddr >> 12) & ((1ULL << 27) - 1ULL);
}
-void do_refill(
- Vtlb* dut,
- uint64_t vpn,
- uint64_t ppn,
- uint8_t level,
- uint8_t mode,
- uint16_t asid,
- bool g,
- bool u,
- bool r,
- bool w,
- bool x,
- bool a,
- bool d
-) {
+void refill(Vtlb* dut, uint64_t vpn, uint64_t ppn, bool r, bool w, bool x, bool a, bool d) {
clear_refill(dut);
dut->refill_valid = 1;
dut->refill_vpn = vpn;
dut->refill_ppn = ppn;
- dut->refill_level = level;
- dut->refill_mode = mode;
- dut->refill_asid = asid;
- dut->refill_g = g;
- dut->refill_u = u;
+ dut->refill_level = 0;
+ dut->refill_mode = MODE_SV39;
+ dut->refill_asid = 1;
dut->refill_r = r;
dut->refill_w = w;
dut->refill_x = x;
dut->refill_a = a;
dut->refill_d = d;
tick(dut);
- dut->refill_valid = 0;
- eval_comb(dut);
-}
-
-void issue_load(Vtlb* dut, uint64_t vaddr, uint16_t asid, uint8_t mode) {
- clear_req(dut);
- dut->req_valid = 1;
- dut->req_vaddr = vaddr;
- dut->req_asid = asid;
- dut->req_mode = mode;
- dut->req_priv = 1; // S mode
- dut->req_is_fetch = 0;
- dut->req_is_store = 0;
- eval_comb(dut);
+ clear_refill(dut);
+ dut->eval();
}
-void issue_req(
- Vtlb* dut,
- uint64_t vaddr,
- uint16_t asid,
- uint8_t mode,
- uint8_t priv,
- bool is_fetch,
- bool is_store,
- bool sum,
- bool mxr
-) {
+void request(Vtlb* dut, uint64_t vaddr, uint8_t mode, bool fetch, bool store, bool mxr = false) {
clear_req(dut);
dut->req_valid = 1;
dut->req_vaddr = vaddr;
- dut->req_asid = asid;
+ dut->req_asid = 1;
dut->req_mode = mode;
- dut->req_priv = priv;
- dut->req_is_fetch = is_fetch;
- dut->req_is_store = is_store;
- dut->req_sum = sum;
+ dut->req_priv = 1;
+ dut->req_is_fetch = fetch;
+ dut->req_is_store = store;
dut->req_mxr = mxr;
- eval_comb(dut);
+ dut->eval();
}
} // namespace
@@ -225,255 +128,43 @@ int main(int argc, char** argv) {
reset(dut);
- // 1) Bare mode bypass
- clear_req(dut);
- dut->req_valid = 1;
- dut->req_mode = MODE_BARE;
- dut->req_vaddr = 0x1234'5678'9ABC'DEF0ULL;
- dut->req_priv = 1;
- eval_comb(dut);
+ request(dut, 0x123456789abcdef0ULL, MODE_BARE, false, false);
check(dut->resp_hit == 1, "Bare_Bypass_Hit");
check(dut->resp_page_fault == 0, "Bare_Bypass_NoFault");
- check(dut->resp_need_ptw == 0, "Bare_Bypass_NoPTW");
- check(dut->resp_paddr == dut->req_vaddr, "Bare_Bypass_PaddrEqualsVaddr");
-
- // 2) Unsupported mode faults
- clear_req(dut);
- dut->req_valid = 1;
- dut->req_mode = 4;
- dut->req_vaddr = 0x8000'0000ULL;
- eval_comb(dut);
- check(dut->resp_page_fault == 1, "UnsupportedMode_PageFault");
-
- // 3) Miss => need PTW
- issue_load(dut, 0x0000'0000'8040'1000ULL, 1, MODE_SV48);
- check(dut->resp_hit == 0, "Miss_NoHit");
- check(dut->resp_need_ptw == 1, "Miss_NeedPTW");
- check(dut->resp_page_fault == 0, "Miss_NoFault");
-
- // 4) Refill then hit
- const uint64_t va1 = 0x0000'0000'8040'1234ULL;
- const uint64_t vpn1 = sv48_vpn(va1);
- const uint64_t ppn1 = 0x12345ULL;
- do_refill(dut, vpn1, ppn1, 0, MODE_SV48, 1, false, false, true, true, false, true, true);
- issue_load(dut, va1, 1, MODE_SV48);
- check(dut->resp_hit == 1, "Refill_Hit");
- check(dut->resp_page_fault == 0, "Refill_NoFault");
- check(dut->resp_paddr == make_paddr(ppn1, va1), "Refill_PaddrMatch");
-
- // 5) ASID mismatch miss when non-global
- issue_load(dut, va1, 2, MODE_SV48);
- check(dut->resp_hit == 0, "AsidMismatch_Miss");
- check(dut->resp_need_ptw == 1, "AsidMismatch_NeedPTW");
-
- // 6) Global entry ignores ASID
- const uint64_t va2 = 0x0000'0000'9000'3456ULL;
- const uint64_t vpn2 = sv48_vpn(va2);
- const uint64_t ppn2 = 0x23456ULL;
- do_refill(dut, vpn2, ppn2, 0, MODE_SV48, 9, true, false, true, false, true, true, true);
- issue_load(dut, va2, 99, MODE_SV48);
- check(dut->resp_hit == 1, "Global_AsidIgnore_Hit");
-
- // 7) Store permission requires W and D
- const uint64_t va3 = 0x0000'0000'A000'2222ULL;
- const uint64_t vpn3 = sv48_vpn(va3);
- do_refill(dut, vpn3, 0x34567ULL, 0, MODE_SV48, 3, false, false, true, true, false, true, false);
- clear_req(dut);
- dut->req_valid = 1;
- dut->req_vaddr = va3;
- dut->req_asid = 3;
- dut->req_mode = MODE_SV48;
- dut->req_priv = 1;
- dut->req_is_store = 1;
- eval_comb(dut);
- check(dut->resp_page_fault == 1, "Store_DBitFault");
-
- // 8) MXR allows load from execute-only page
- const uint64_t va4 = 0x0000'0000'B000'0100ULL;
- const uint64_t vpn4 = sv48_vpn(va4);
- do_refill(dut, vpn4, 0x45678ULL, 0, MODE_SV48, 4, false, false, false, false, true, true, true);
- clear_req(dut);
- dut->req_valid = 1;
- dut->req_vaddr = va4;
- dut->req_asid = 4;
- dut->req_mode = MODE_SV48;
- dut->req_priv = 1;
- dut->req_mxr = 1;
- eval_comb(dut);
- check(dut->resp_hit == 1, "MXR_LoadFromXOnly_Hit");
- check(dut->resp_page_fault == 0, "MXR_LoadFromXOnly_NoFault");
+ check(dut->resp_paddr == 0x123456789abcdef0ULL, "Bare_Bypass_Paddr");
+
+ request(dut, 0x0000000040401000ULL, MODE_SV48, false, false);
+ check(dut->resp_page_fault == 1, "Sv48_Stub_Fault");
+ request(dut, 0x0000000040401000ULL, MODE_SV57, false, false);
+ check(dut->resp_page_fault == 1, "Sv57_Stub_Fault");
+ request(dut, 0x0000000040401000ULL, MODE_SV64, false, false);
+ check(dut->resp_page_fault == 1, "Sv64_Stub_Fault");
+
+ const uint64_t va = 0x0000000040401234ULL;
+ const uint64_t ppn = 0x45670ULL;
+ request(dut, va, MODE_SV39, false, false);
+ check(dut->resp_hit == 0, "Sv39_Miss_NoHit");
+ check(dut->resp_need_ptw == 1, "Sv39_Miss_NeedPTW");
+ check(dut->resp_page_fault == 0, "Sv39_Miss_NoFault");
+
+ refill(dut, sv39_vpn(va), ppn, true, true, false, true, true);
+ request(dut, va, MODE_SV39, false, false);
+ check(dut->resp_hit == 1, "Sv39_Refill_Hit");
+ check(dut->resp_paddr == ((ppn << 12) | (va & 0xfffULL)), "Sv39_Refill_Paddr");
+
+ refill(dut, sv39_vpn(0x0000000040500000ULL), 0x50000ULL, true, true, false, true, false);
+ request(dut, 0x0000000040500000ULL, MODE_SV39, false, true);
+ check(dut->resp_page_fault == 1, "Sv39_Store_D0_Fault");
+
+ refill(dut, sv39_vpn(0x0000000040600000ULL), 0x60000ULL, false, false, true, true, false);
+ request(dut, 0x0000000040600000ULL, MODE_SV39, false, false, true);
+ check(dut->resp_hit == 1, "Sv39_MXR_LoadFromXOnly");
+
+ request(dut, 0x0000008000000000ULL, MODE_SV39, false, false);
+ check(dut->resp_page_fault == 1, "Sv39_NonCanonical_Fault");
- // 9) Superpage match (Sv48 level=1) ignores low VPN[8:0]
- const uint64_t va5a = 0x0000'0000'C123'4000ULL;
- const uint64_t va5b = 0x0000'0000'C123'8ABCULL; // same high VPN, different low VPN bits
- const uint64_t vpn5 = sv48_vpn(va5a);
- do_refill(dut, vpn5, 0x56780ULL, 1, MODE_SV48, 5, false, false, true, true, true, true, true);
- issue_load(dut, va5b, 5, MODE_SV48);
- check(dut->resp_hit == 1, "Superpage_Level1_Hit");
-
- // 10) Flush all invalidates
- apply_flush_all(dut);
- issue_load(dut, va1, 1, MODE_SV48);
- check(dut->resp_hit == 0, "FlushAll_Invalidates");
-
- // 11) Flush by ASID only invalidates matching non-global entries
- const uint64_t va6a = 0x0000'0000'D000'1111ULL;
- const uint64_t va6b = 0x0000'0000'D000'2222ULL;
- do_refill(dut, sv48_vpn(va6a), 0x60100ULL, 0, MODE_SV48, 7, false, false, true, true, false, true, true);
- do_refill(dut, sv48_vpn(va6b), 0x60200ULL, 0, MODE_SV48, 8, false, false, true, true, false, true, true);
- apply_flush_asid(dut, 7);
- issue_load(dut, va6a, 7, MODE_SV48);
- check(dut->resp_hit == 0, "FlushAsid_TargetInvalidated");
- issue_load(dut, va6b, 8, MODE_SV48);
- check(dut->resp_hit == 1, "FlushAsid_OtherAsidPreserved");
-
- // 12) Flush by VPN only invalidates matching VPN in the selected mode
- const uint64_t va7a = 0x0000'0000'E100'0123ULL;
- const uint64_t va7b = 0x0000'0000'E200'0456ULL;
- do_refill(dut, sv48_vpn(va7a), 0x70100ULL, 0, MODE_SV48, 9, false, false, true, true, false, true, true);
- do_refill(dut, sv48_vpn(va7b), 0x70200ULL, 0, MODE_SV48, 9, false, false, true, true, false, true, true);
- apply_flush_vpn(dut, sv48_vpn(va7a), MODE_SV48);
- issue_load(dut, va7a, 9, MODE_SV48);
- check(dut->resp_hit == 0, "FlushVpn_TargetInvalidated");
- issue_load(dut, va7b, 9, MODE_SV48);
- check(dut->resp_hit == 1, "FlushVpn_OtherVpnPreserved");
-
- // 13) Sv32 superpage boundary: level=1 ignores low VPN[9:0]
- const uint64_t va32a = 0x0000000081234000ULL;
- const uint64_t va32b = 0x00000000813FFABCULL;
- do_refill(dut, sv32_vpn(va32a), 0x31000ULL, 1, MODE_SV32, 11, false, false, true, true, false, true, true);
- issue_load(dut, va32b, 11, MODE_SV32);
- check(dut->resp_hit == 1, "Sv32_Superpage_Level1_Hit");
- check(dut->resp_page_fault == 0, "Sv32_Superpage_NoFault");
-
- // 14) Sv57 superpage boundary: level=2 ignores low VPN[17:0]
- const uint64_t va57a = 0x0012'3456'7800'4000ULL;
- const uint64_t va57b = 0x0012'3456'7BFF'8ABCULL;
- do_refill(dut, sv57_vpn(va57a), 0x4A1234ULL, 2, MODE_SV57, 12, false, false, true, true, false, true, true);
- issue_load(dut, va57b, 12, MODE_SV57);
- check(dut->resp_hit == 1, "Sv57_Superpage_Level2_Hit");
- check(dut->resp_page_fault == 0, "Sv57_Superpage_NoFault");
-
- // 15) Flush VPN mode filter: Sv32 flush must not touch Sv48 entry with same low VPN
- const uint64_t va8 = 0x0000'0000'8123'4567ULL;
- const uint64_t vpn32 = sv32_vpn(va8);
- const uint64_t vpn48_same_low = vpn32;
- do_refill(dut, vpn32, 0x81000ULL, 0, MODE_SV32, 13, false, false, true, true, false, true, true);
- do_refill(dut, vpn48_same_low, 0x82000ULL, 0, MODE_SV48, 13, false, false, true, true, false, true, true);
- apply_flush_vpn(dut, vpn32, MODE_SV32);
- issue_load(dut, va8, 13, MODE_SV32);
- check(dut->resp_hit == 0, "FlushVpn_ModeFilter_Sv32Invalidated");
- issue_load(dut, va8, 13, MODE_SV48);
- check(dut->resp_hit == 1, "FlushVpn_ModeFilter_Sv48Preserved");
-
- // 16) S-mode access to U=1 page requires SUM for data access
- const uint64_t va9 = 0x0000'0000'8333'0100ULL;
- do_refill(dut, sv48_vpn(va9), 0x83000ULL, 0, MODE_SV48, 14, false, true, true, true, false, true, true);
- issue_req(dut, va9, 14, MODE_SV48, 1, false, false, false, false);
- check(dut->resp_page_fault == 1, "SUM_SmodeLoadUPage_Sum0_Fault");
- issue_req(dut, va9, 14, MODE_SV48, 1, false, false, true, false);
- check(dut->resp_hit == 1, "SUM_SmodeLoadUPage_Sum1_Hit");
- check(dut->resp_page_fault == 0, "SUM_SmodeLoadUPage_Sum1_NoFault");
-
- // 17) SUM must not allow S-mode instruction fetch from U page
- issue_req(dut, va9, 14, MODE_SV48, 1, true, false, true, false);
- check(dut->resp_page_fault == 1, "SUM_SmodeFetchUPage_AlwaysFault");
-
- // 18) U-mode cannot access U=0 page
- const uint64_t va10 = 0x0000'0000'8444'0200ULL;
- do_refill(dut, sv48_vpn(va10), 0x84000ULL, 0, MODE_SV48, 15, false, false, true, true, true, true, true);
- issue_req(dut, va10, 15, MODE_SV48, 0, false, false, false, false);
- check(dut->resp_page_fault == 1, "Umode_LoadSupervisorPage_Fault");
-
- // 19) U-mode can load U=1 page when permissions allow
- const uint64_t va11 = 0x0000'0000'8555'0300ULL;
- do_refill(dut, sv48_vpn(va11), 0x85000ULL, 0, MODE_SV48, 16, false, true, true, false, false, true, true);
- issue_req(dut, va11, 16, MODE_SV48, 0, false, false, false, false);
- check(dut->resp_hit == 1, "Umode_LoadUserPage_Hit");
- check(dut->resp_page_fault == 0, "Umode_LoadUserPage_NoFault");
-
- // 20) MXR matrix: execute-only page load faults when mxr=0, passes when mxr=1
- const uint64_t va12 = 0x0000'0000'8666'0400ULL;
- do_refill(dut, sv48_vpn(va12), 0x86000ULL, 0, MODE_SV48, 17, false, false, false, false, true, true, true);
- issue_req(dut, va12, 17, MODE_SV48, 1, false, false, false, false);
- check(dut->resp_page_fault == 1, "MXR_LoadXOnly_Mxr0_Fault");
- issue_req(dut, va12, 17, MODE_SV48, 1, false, false, false, true);
- check(dut->resp_hit == 1, "MXR_LoadXOnly_Mxr1_Hit");
- check(dut->resp_page_fault == 0, "MXR_LoadXOnly_Mxr1_NoFault");
-
- // 21) A-bit required for any access type (load/fetch)
- const uint64_t va13 = 0x0000'0000'8777'0500ULL;
- do_refill(dut, sv48_vpn(va13), 0x87000ULL, 0, MODE_SV48, 18, false, false, true, true, true, false, true);
- issue_req(dut, va13, 18, MODE_SV48, 1, false, false, false, false);
- check(dut->resp_page_fault == 1, "ABit_LoadFault_WhenA0");
- issue_req(dut, va13, 18, MODE_SV48, 1, true, false, false, false);
- check(dut->resp_page_fault == 1, "ABit_FetchFault_WhenA0");
-
- // 22) ASID-targeted flush must preserve global entries
- const uint64_t va14 = 0x0000'0000'8888'0600ULL;
- do_refill(dut, sv48_vpn(va14), 0x88000ULL, 0, MODE_SV48, 33, true, false, true, true, false, true, true);
- apply_flush_asid(dut, 33);
- issue_load(dut, va14, 99, MODE_SV48);
- check(dut->resp_hit == 1, "FlushAsid_GlobalPreserved");
-
- // 23) Refill pressure should eventually evict oldest entry (round-robin)
- apply_flush_all(dut);
- const uint64_t va15 = 0x0000'0000'9000'1000ULL;
- do_refill(dut, sv48_vpn(va15), 0x90001ULL, 0, MODE_SV48, 40, false, false, true, true, false, true, true);
- for (int n = 0; n < 32; ++n) {
- const uint64_t va_fill = 0x0000'0001'0000'0000ULL + (static_cast<uint64_t>(n) << 12);
- const uint64_t ppn_fill = 0x91000ULL + static_cast<uint64_t>(n);
- do_refill(dut, sv48_vpn(va_fill), ppn_fill, 0, MODE_SV48, static_cast<uint16_t>(50 + n), false, false, true, true, false, true, true);
- }
- issue_load(dut, va15, 40, MODE_SV48);
- check(dut->resp_hit == 0, "ReplacementPressure_OldestEvicted");
-
- // 24) Newest refill remains accessible after pressure
- const uint64_t va16 = 0x0000'0001'0001'F000ULL;
- do_refill(dut, sv48_vpn(va16), 0xA001FULL, 0, MODE_SV48, 77, false, false, true, true, false, true, true);
- issue_load(dut, va16, 77, MODE_SV48);
- check(dut->resp_hit == 1, "ReplacementPressure_NewestEntryHits");
-
- // 25) flush_vpn should invalidate Sv48 level-1 superpage for any VPN within same range
- apply_flush_all(dut);
- const uint64_t va17_base = 0x0000'0001'2340'0000ULL;
- const uint64_t va17_same_superpage = 0x0000'0001'2341'2ABCULL;
- const uint64_t va17_other_superpage = 0x0000'0001'2440'0555ULL;
- do_refill(dut, sv48_vpn(va17_base), 0xB1000ULL, 1, MODE_SV48, 88, false, false, true, true, false, true, true);
- do_refill(dut, sv48_vpn(va17_other_superpage), 0xB2000ULL, 1, MODE_SV48, 88, false, false, true, true, false, true, true);
- apply_flush_vpn(dut, sv48_vpn(va17_same_superpage), MODE_SV48);
- issue_load(dut, va17_base, 88, MODE_SV48);
- check(dut->resp_hit == 0, "FlushVpn_Sv48L1_SameSuperpageInvalidated");
- issue_load(dut, va17_other_superpage, 88, MODE_SV48);
- check(dut->resp_hit == 1, "FlushVpn_Sv48L1_OtherSuperpagePreserved");
-
- // 26) flush_vpn should invalidate Sv57 level-2 superpage with 18-bit VPN wildcard
- apply_flush_all(dut);
- const uint64_t va18_base = 0x0012'3456'7800'1000ULL;
- const uint64_t va18_same_superpage = 0x0012'3456'7BFF'8FFFULL;
- const uint64_t va18_other_superpage = 0x0012'345A'0000'2000ULL;
- do_refill(dut, sv57_vpn(va18_base), 0xC1234ULL, 2, MODE_SV57, 99, false, false, true, true, false, true, true);
- do_refill(dut, sv57_vpn(va18_other_superpage), 0xC5678ULL, 2, MODE_SV57, 99, false, false, true, true, false, true, true);
- apply_flush_vpn(dut, sv57_vpn(va18_same_superpage), MODE_SV57);
- issue_load(dut, va18_base, 99, MODE_SV57);
- check(dut->resp_hit == 0, "FlushVpn_Sv57L2_SameSuperpageInvalidated");
- issue_load(dut, va18_other_superpage, 99, MODE_SV57);
- check(dut->resp_hit == 1, "FlushVpn_Sv57L2_OtherSuperpagePreserved");
-
- // 27) flush_vpn with exact-page entry must not over-invalidate neighboring pages
- apply_flush_all(dut);
- const uint64_t va19_a = 0x0000'0000'9900'1000ULL;
- const uint64_t va19_b = 0x0000'0000'9900'2000ULL;
- do_refill(dut, sv48_vpn(va19_a), 0xD1000ULL, 0, MODE_SV48, 120, false, false, true, true, false, true, true);
- do_refill(dut, sv48_vpn(va19_b), 0xD2000ULL, 0, MODE_SV48, 120, false, false, true, true, false, true, true);
- apply_flush_vpn(dut, sv48_vpn(va19_a), MODE_SV48);
- issue_load(dut, va19_a, 120, MODE_SV48);
- check(dut->resp_hit == 0, "FlushVpn_Level0_OnlyTargetInvalidated");
- issue_load(dut, va19_b, 120, MODE_SV48);
- check(dut->resp_hit == 1, "FlushVpn_Level0_NeighborPreserved");
-
- std::cout << "\n----------------------------------\n";
- std::cout << "TLB tests passed: " << g_pass_count << "\n";
+ std::cout << "----------------------------------\n";
+ std::cout << "All " << g_pass_count << " TLB tests passed!\n";
std::cout << "----------------------------------\n";
delete dut;
diff --git a/rv64cpu/tb/unit/tb_trap_ctrl.cc b/rv64cpu/tb/unit/tb_trap_ctrl.cc
index f0b667c..62867df 100644
--- a/rv64cpu/tb/unit/tb_trap_ctrl.cc
+++ b/rv64cpu/tb/unit/tb_trap_ctrl.cc
@@ -35,12 +35,19 @@ constexpr uint64_t EXC_STORE_FAULT = 7ULL;
constexpr uint64_t INTERRUPT_M_SOFTWARE = 3ULL;
constexpr uint64_t INTERRUPT_M_TIMER = 7ULL;
constexpr uint64_t INTERRUPT_M_EXTERNAL = 11ULL;
+constexpr uint64_t INTERRUPT_S_SOFTWARE = 1ULL;
+constexpr uint64_t INTERRUPT_S_TIMER = 5ULL;
+constexpr uint64_t INTERRUPT_S_EXTERNAL = 9ULL;
constexpr uint64_t INTERRUPT_BIT = 1ULL << 63;
constexpr uint64_t PRIV_MODE_U = 0ULL;
constexpr uint64_t PRIV_MODE_S = 1ULL;
constexpr uint64_t PRIV_MODE_M = 3ULL;
+uint64_t mie_bit(uint64_t interrupt_cause) {
+ return 1ULL << interrupt_cause;
+}
+
void drive_defaults(Vtrap_ctrl* dut) {
dut->wb_pc = 0;
dut->trap_illegal_instr = 0;
@@ -56,11 +63,11 @@ void drive_defaults(Vtrap_ctrl* dut) {
dut->trap_bad_addr = 0;
dut->trap_bad_instr = 0;
- dut->ext_timer_interrupt = 0;
- dut->ext_software_interrupt = 0;
- dut->ext_external_interrupt = 0;
-
dut->csr_mstatus_mie = 0;
+ dut->csr_mie = 0;
+ dut->csr_sie = 0;
+ dut->csr_mip = 0;
+ dut->csr_sip = 0;
dut->csr_mtvec = 0x800ULL;
dut->csr_stvec = 0x400ULL;
dut->csr_mepc = 0x1000ULL;
@@ -150,16 +157,19 @@ int main(int argc, char** argv) {
check(dut->trap_mtval == 0xABCDEF0020ULL, "StoreFault_Mtval");
// ---------------------------------------------------------------------
- // 2) Interrupt priority + delegation to supervisor
+ // 2) Interrupt priority + enable masking
// ---------------------------------------------------------------------
drive_defaults(dut);
dut->csr_mstatus_mie = 1;
+ dut->csr_mie = mie_bit(INTERRUPT_M_EXTERNAL) |
+ mie_bit(INTERRUPT_M_SOFTWARE) |
+ mie_bit(INTERRUPT_M_TIMER);
dut->csr_priv_mode = PRIV_MODE_M;
dut->csr_mtvec = 0x300ULL | 0x1ULL; // vectored mode
dut->wb_pc = 0x4000ULL;
- dut->ext_timer_interrupt = 1;
- dut->ext_software_interrupt = 1;
- dut->ext_external_interrupt = 1;
+ dut->csr_mip = mie_bit(INTERRUPT_M_EXTERNAL) |
+ mie_bit(INTERRUPT_M_SOFTWARE) |
+ mie_bit(INTERRUPT_M_TIMER);
dut->eval();
const uint64_t expected_vectored_pc = 0x300ULL + (INTERRUPT_M_EXTERNAL << 2);
check(dut->trap_trigger, "InterruptPriority_Triggers");
@@ -169,22 +179,80 @@ int main(int argc, char** argv) {
drive_defaults(dut);
dut->csr_mstatus_mie = 0;
- dut->ext_external_interrupt = 1;
+ dut->csr_mie = mie_bit(INTERRUPT_M_EXTERNAL);
+ dut->csr_mip = mie_bit(INTERRUPT_M_EXTERNAL);
dut->eval();
- check(!dut->trap_trigger, "InterruptMasked_NoTrigger");
+ check(!dut->trap_trigger, "InterruptMaskedByGlobalMie_NoTrigger");
+
+ drive_defaults(dut);
+ dut->csr_priv_mode = PRIV_MODE_M;
+ dut->csr_mstatus_mie = 1;
+ dut->csr_mie = 0;
+ dut->csr_mip = mie_bit(INTERRUPT_M_EXTERNAL);
+ dut->eval();
+ check(!dut->trap_trigger, "InterruptMaskedByMie_NoTrigger");
+
+ drive_defaults(dut);
+ dut->csr_priv_mode = PRIV_MODE_S;
+ dut->csr_mstatus_mie = 0;
+ dut->csr_mie = mie_bit(INTERRUPT_M_EXTERNAL);
+ dut->csr_mtvec = 0x440ULL;
+ dut->wb_pc = 0x6780ULL;
+ dut->csr_mip = mie_bit(INTERRUPT_M_EXTERNAL);
+ dut->eval();
+ check(dut->trap_trigger, "MachineInterruptBelowM_IgnoresMstatusMie");
+ check(!dut->trap_to_s_mode, "MachineInterruptBelowM_NoDelegationWithoutMideleg");
+ check(dut->trap_mcause == (INTERRUPT_BIT | INTERRUPT_M_EXTERNAL), "MachineInterruptBelowM_Mcause");
+ check(dut->trap_next_pc == 0x440ULL, "MachineInterruptBelowM_JumpsToMtvec");
drive_defaults(dut);
dut->csr_priv_mode = PRIV_MODE_S;
dut->csr_mstatus_mie = 1;
- dut->csr_stvec = 0x880ULL | 0x1ULL; // vectored supervisor mode
+ dut->csr_mie = mie_bit(INTERRUPT_M_TIMER);
+ dut->csr_mtvec = 0x880ULL | 0x1ULL; // vectored machine mode
+ dut->csr_stvec = 0x400ULL | 0x1ULL;
dut->csr_mideleg = (1ULL << INTERRUPT_M_TIMER);
dut->wb_pc = 0xABCDULL;
- dut->ext_timer_interrupt = 1;
+ dut->csr_mip = mie_bit(INTERRUPT_M_TIMER);
dut->eval();
const uint64_t expected_sv_pc = 0x880ULL + (INTERRUPT_M_TIMER << 2);
- check(dut->trap_to_s_mode, "InterruptDelegation_ToSupervisor");
- check(dut->trap_next_pc == expected_sv_pc, "InterruptDelegation_UsesStvec");
- check(dut->trap_mcause == (INTERRUPT_BIT | INTERRUPT_M_TIMER), "InterruptDelegation_Mcause");
+ check(!dut->trap_to_s_mode, "MachineInterrupt_NoDelegation");
+ check(dut->trap_next_pc == expected_sv_pc, "MachineInterrupt_UsesMtvec");
+ check(dut->trap_mcause == (INTERRUPT_BIT | INTERRUPT_M_TIMER), "MachineInterrupt_Mcause");
+
+ drive_defaults(dut);
+ dut->csr_priv_mode = PRIV_MODE_S;
+ dut->csr_mstatus = (1ULL << 1);
+ dut->csr_sie = mie_bit(INTERRUPT_S_TIMER);
+ dut->csr_sip = mie_bit(INTERRUPT_S_TIMER);
+ dut->csr_mideleg = mie_bit(INTERRUPT_S_TIMER);
+ dut->csr_stvec = 0x900ULL | 0x1ULL;
+ dut->wb_pc = 0xABCEULL;
+ dut->eval();
+ const uint64_t expected_s_timer_pc = 0x900ULL + (INTERRUPT_S_TIMER << 2);
+ check(dut->trap_to_s_mode, "SupervisorInterruptDelegated_ToSupervisor");
+ check(dut->trap_next_pc == expected_s_timer_pc, "SupervisorInterruptDelegated_UsesStvec");
+ check(dut->trap_mcause == (INTERRUPT_BIT | INTERRUPT_S_TIMER), "SupervisorInterruptDelegated_Mcause");
+
+ drive_defaults(dut);
+ dut->csr_priv_mode = PRIV_MODE_S;
+ dut->csr_mstatus = 0;
+ dut->csr_sie = mie_bit(INTERRUPT_S_TIMER);
+ dut->csr_sip = mie_bit(INTERRUPT_S_TIMER);
+ dut->csr_mideleg = mie_bit(INTERRUPT_S_TIMER);
+ dut->eval();
+ check(!dut->trap_trigger, "SupervisorInterruptMaskedBySstatusSie_NoTrigger");
+
+ drive_defaults(dut);
+ dut->csr_priv_mode = PRIV_MODE_U;
+ dut->csr_mstatus = 0;
+ dut->csr_sie = mie_bit(INTERRUPT_S_EXTERNAL);
+ dut->csr_sip = mie_bit(INTERRUPT_S_EXTERNAL);
+ dut->csr_mideleg = mie_bit(INTERRUPT_S_EXTERNAL);
+ dut->csr_stvec = 0x940ULL;
+ dut->eval();
+ check(dut->trap_to_s_mode, "SupervisorInterruptBelowS_IgnoresSstatusSie");
+ check(dut->trap_next_pc == 0x940ULL, "SupervisorInterruptBelowS_UsesStvec");
// ---------------------------------------------------------------------
// 3) MRET/SRET redirect without new traps