//----------------------------------------------------------------- // uRISC-V CPU // V0.5.0 // github.com/ultraembedded/core_uriscv // Copyright 2015-2021 // // admin@ultra-embedded.com // // License: Apache 2.0 //----------------------------------------------------------------- // Copyright 2015-2021 github.com/ultraembedded // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //----------------------------------------------------------------- module uriscv_csr //----------------------------------------------------------------- // Params //----------------------------------------------------------------- #( parameter SUPPORT_CSR = 1 ,parameter SUPPORT_MCYCLE = 1 ,parameter SUPPORT_MTIMECMP = 1 ,parameter SUPPORT_MSCRATCH = 1 ,parameter SUPPORT_MIP_MIE = 1 ,parameter SUPPORT_MTVEC = 1 ,parameter SUPPORT_MTVAL = 1 ,parameter SUPPORT_MULDIV = 1 ) //----------------------------------------------------------------- // Ports //----------------------------------------------------------------- ( input clk_i ,input rst_i ,input intr_i ,input [31:0] isr_vector_i ,input [31:0] cpu_id_i ,input valid_i ,input [31:0] pc_i ,input [31:0] opcode_i ,input [31:0] rs1_val_i ,input [31:0] rs2_val_i ,output [31:0] csr_rdata_o ,input excpn_invalid_inst_i ,input excpn_lsu_align_i ,input [31:0] mem_addr_i ,output [31:0] csr_mepc_o ,output exception_o ,output [5:0] exception_type_o ,output [31:0] exception_pc_o ); //----------------------------------------------------------------- // Includes //----------------------------------------------------------------- `include "uriscv_defs.v" wire take_interrupt_w; wire exception_w; //----------------------------------------------------------------- // Instruction Decode //----------------------------------------------------------------- wire [2:0] func3_w = opcode_i[14:12]; // R, I, S wire [4:0] rs1_w = opcode_i[19:15]; wire [4:0] rs2_w = opcode_i[24:20]; wire [4:0] rd_w = opcode_i[11:7]; wire type_system_w = (opcode_i[6:2] == 5'b11100); wire type_store_w = (opcode_i[6:2] == 5'b01000); wire inst_csr_w = SUPPORT_CSR && type_system_w && (func3_w != 3'b000 && func3_w != 3'b100); wire inst_csrrw_w = inst_csr_w && (func3_w == 3'b001); wire inst_csrrs_w = inst_csr_w && (func3_w == 3'b010); wire inst_csrrc_w = inst_csr_w && (func3_w == 3'b011); wire inst_csrrwi_w = inst_csr_w && (func3_w == 3'b101); wire inst_csrrsi_w = inst_csr_w && (func3_w == 3'b110); wire inst_csrrci_w = inst_csr_w && (func3_w == 3'b111); wire inst_ecall_w = SUPPORT_CSR && type_system_w && (opcode_i[31:7] == 25'h000000); wire inst_ebreak_w = SUPPORT_CSR && type_system_w && (opcode_i[31:7] == 25'h002000); wire inst_mret_w = SUPPORT_CSR && type_system_w && (opcode_i[31:7] == 25'h604000); wire [11:0] csr_addr_w = valid_i ? opcode_i[31:20] : 12'b0; wire [31:0] csr_data_w = (inst_csrrwi_w || inst_csrrsi_w || inst_csrrci_w) ? {27'b0, rs1_w} : rs1_val_i; wire csr_set_w = (valid_i && !exception_w) ? (inst_csrrw_w || inst_csrrs_w || inst_csrrwi_w || inst_csrrsi_w): 1'b0; wire csr_clr_w = (valid_i && !exception_w) ? (inst_csrrw_w || inst_csrrc_w || inst_csrrwi_w || inst_csrrci_w): 1'b0; //----------------------------------------------------------------- // Execute: CSR Access //----------------------------------------------------------------- reg [31:0] csr_mepc_q; reg [31:0] csr_mepc_r; reg [31:0] csr_mcause_q; reg [31:0] csr_mcause_r; reg [31:0] csr_sr_q; reg [31:0] csr_sr_r; reg [31:0] csr_mcycle_q; reg [31:0] csr_mcycle_r; reg [31:0] csr_mtimecmp_q; reg [31:0] csr_mtimecmp_r; reg [31:0] csr_mscratch_q; reg [31:0] csr_mscratch_r; reg [31:0] csr_mip_q; reg [31:0] csr_mip_r; reg [31:0] csr_mie_q; reg [31:0] csr_mie_r; reg [31:0] csr_mtvec_q; reg [31:0] csr_mtvec_r; reg [31:0] csr_mtval_q; reg [31:0] csr_mtval_r; always @ * begin csr_mepc_r = csr_mepc_q; csr_mcause_r = csr_mcause_q; csr_sr_r = csr_sr_q; csr_mcycle_r = csr_mcycle_q + 32'd1; csr_mtimecmp_r = csr_mtimecmp_q; csr_mscratch_r = csr_mscratch_q; csr_mip_r = csr_mip_q; csr_mie_r = csr_mie_q; csr_mtvec_r = csr_mtvec_q; csr_mtval_r = csr_mtval_q; // External interrupt if (intr_i) csr_mip_r[`IRQ_M_EXT] = 1'b1; // Timer match - generate IRQ if (SUPPORT_MTIMECMP && csr_mcycle_r == csr_mtimecmp_r) csr_mip_r[`SR_IP_MTIP_R] = 1'b1; // Execute instruction / exception if (valid_i) begin // Exception / break / ecall if (exception_w || inst_ebreak_w || inst_ecall_w) begin // Save interrupt / supervisor state csr_sr_r[`SR_MPIE_R] = csr_sr_q[`SR_MIE_R]; csr_sr_r[`SR_MPP_R] = `PRIV_MACHINE; // Disable interrupts and enter supervisor mode csr_sr_r[`SR_MIE_R] = 1'b0; // Save PC of next instruction (not yet executed) csr_mepc_r = pc_i; // Extra info (badaddr / fault opcode) csr_mtval_r = 32'b0; // Exception source if (excpn_invalid_inst_i) begin csr_mcause_r = `MCAUSE_ILLEGAL_INSTRUCTION; csr_mtval_r = opcode_i; end else if (inst_ebreak_w) csr_mcause_r = `MCAUSE_BREAKPOINT; else if (inst_ecall_w) csr_mcause_r = `MCAUSE_ECALL_M; else if (excpn_lsu_align_i) begin csr_mcause_r = type_store_w ? `MCAUSE_MISALIGNED_STORE : `MCAUSE_MISALIGNED_LOAD; csr_mtval_r = mem_addr_i; end else if (take_interrupt_w) csr_mcause_r = `MCAUSE_INTERRUPT; end // MRET else if (inst_mret_w) begin // Interrupt enable pop csr_sr_r[`SR_MIE_R] = csr_sr_r[`SR_MPIE_R]; csr_sr_r[`SR_MPIE_R] = 1'b1; // This CPU only supports machine mode csr_sr_r[`SR_MPP_R] = `PRIV_MACHINE; end else begin case (csr_addr_w) `CSR_MEPC: begin if (csr_set_w && csr_clr_w) csr_mepc_r = csr_data_w; else if (csr_set_w) csr_mepc_r = csr_mepc_r | csr_data_w; else if (csr_clr_w) csr_mepc_r = csr_mepc_r & ~csr_data_w; end `CSR_MCAUSE: begin if (csr_set_w && csr_clr_w) csr_mcause_r = csr_data_w; else if (csr_set_w) csr_mcause_r = csr_mcause_r | csr_data_w; else if (csr_clr_w) csr_mcause_r = csr_mcause_r & ~csr_data_w; end `CSR_MSTATUS: begin if (csr_set_w && csr_clr_w) csr_sr_r = csr_data_w; else if (csr_set_w) csr_sr_r = csr_sr_r | csr_data_w; else if (csr_clr_w) csr_sr_r = csr_sr_r & ~csr_data_w; end `CSR_MTIMECMP: begin if (SUPPORT_MTIMECMP && csr_set_w && csr_data_w != 32'b0) begin csr_mtimecmp_r = csr_data_w; // Clear interrupt pending csr_mip_r[`SR_IP_MTIP_R] = 1'b0; end end `CSR_MSCRATCH: begin if (csr_set_w && csr_clr_w) csr_mscratch_r = csr_data_w; else if (csr_set_w) csr_mscratch_r = csr_mscratch_r | csr_data_w; else if (csr_clr_w) csr_mscratch_r = csr_mscratch_r & ~csr_data_w; end `CSR_MIP: begin if (csr_set_w && csr_clr_w) csr_mip_r = csr_data_w; else if (csr_set_w) csr_mip_r = csr_mip_r | csr_data_w; else if (csr_clr_w) csr_mip_r = csr_mip_r & ~csr_data_w; end `CSR_MIE: begin if (csr_set_w && csr_clr_w) csr_mie_r = csr_data_w; else if (csr_set_w) csr_mie_r = csr_mie_r | csr_data_w; else if (csr_clr_w) csr_mie_r = csr_mie_r & ~csr_data_w; end `CSR_MTVEC: begin if (csr_set_w && csr_clr_w) csr_mtvec_r = csr_data_w; else if (csr_set_w) csr_mtvec_r = csr_mtvec_r | csr_data_w; else if (csr_clr_w) csr_mtvec_r = csr_mtvec_r & ~csr_data_w; end `CSR_MTVAL: begin if (csr_set_w && csr_clr_w) csr_mtval_r = csr_data_w; else if (csr_set_w) csr_mtval_r = csr_mtval_r | csr_data_w; else if (csr_clr_w) csr_mtval_r = csr_mtval_r & ~csr_data_w; end default: ; endcase end end end `ifdef verilator `define HAS_SIM_CTRL `endif `ifdef verilog_sim `define HAS_SIM_CTRL `endif always @ (posedge clk_i ) if (rst_i) begin csr_mepc_q <= 32'b0; csr_mcause_q <= 32'b0; csr_sr_q <= 32'b0; csr_mcycle_q <= 32'b0; csr_mtimecmp_q <= 32'b0; csr_mscratch_q <= 32'b0; csr_mie_q <= 32'b0; csr_mip_q <= 32'b0; csr_mtvec_q <= 32'b0; csr_mtval_q <= 32'b0; end else begin csr_mepc_q <= csr_mepc_r; csr_mcause_q <= csr_mcause_r; csr_sr_q <= csr_sr_r; csr_mcycle_q <= SUPPORT_MCYCLE ? csr_mcycle_r : 32'b0; csr_mtimecmp_q <= SUPPORT_MTIMECMP ? csr_mtimecmp_r : 32'b0; csr_mscratch_q <= SUPPORT_MSCRATCH ? csr_mscratch_r : 32'b0; csr_mie_q <= SUPPORT_MIP_MIE ? csr_mie_r : 32'b0; csr_mip_q <= SUPPORT_MIP_MIE ? csr_mip_r : 32'b0; csr_mtvec_q <= SUPPORT_MTVEC ? csr_mtvec_r : 32'b0; csr_mtval_q <= SUPPORT_MTVAL ? csr_mtval_r : 32'b0; `ifdef HAS_SIM_CTRL if (valid_i && (csr_addr_w == `CSR_DSCRATCH || csr_addr_w == `CSR_SIM_CTRL) && inst_csr_w) begin case (csr_data_w & 32'hFF000000) `CSR_SIM_CTRL_EXIT: begin $finish; $finish; end `CSR_SIM_CTRL_PUTC: begin $write("%c", csr_data_w[7:0]); end endcase end `endif end //----------------------------------------------------------------- // CSR Read Data MUX //----------------------------------------------------------------- reg [31:0] csr_data_r; always @ * begin csr_data_r = 32'b0; case (csr_addr_w) `CSR_MEPC: csr_data_r = csr_mepc_q & `CSR_MEPC_MASK; `CSR_MCAUSE: csr_data_r = csr_mcause_q & `CSR_MCAUSE_MASK; `CSR_MSTATUS: csr_data_r = csr_sr_q & `CSR_MSTATUS_MASK; `CSR_MTVEC: csr_data_r = csr_mtvec_q & `CSR_MTVEC_MASK; `CSR_MTVAL: csr_data_r = csr_mtval_q & `CSR_MTVAL_MASK; `CSR_MTIME, `CSR_MCYCLE: csr_data_r = csr_mcycle_q & `CSR_MTIME_MASK; `CSR_MTIMECMP: csr_data_r = csr_mtimecmp_q & `CSR_MTIMECMP_MASK; `CSR_MSCRATCH: csr_data_r = csr_mscratch_q & `CSR_MSCRATCH_MASK; `CSR_MIP: csr_data_r = csr_mip_q & `CSR_MIP_MASK; `CSR_MIE: csr_data_r = csr_mie_q & `CSR_MIE_MASK; `CSR_MISA: csr_data_r = (SUPPORT_MULDIV ? `MISA_RVM : 32'b0) | `MISA_RV32 | `MISA_RVI; `CSR_MHARTID: csr_data_r = cpu_id_i; default: csr_data_r = 32'b0; endcase end assign csr_rdata_o = csr_data_r; // Interrupt request and interrupt enabled assign take_interrupt_w = SUPPORT_MIP_MIE ? ((|(csr_mip_q & csr_mie_q)) & csr_sr_q[`SR_MIE_R]) : (intr_i & csr_sr_q[`SR_MIE_R]); assign exception_w = valid_i && (take_interrupt_w || excpn_invalid_inst_i || (SUPPORT_CSR && excpn_lsu_align_i)); assign exception_o = exception_w; assign exception_pc_o = SUPPORT_MTVEC ? csr_mtvec_q : SUPPORT_CSR ? isr_vector_i : pc_i + 32'd4; assign csr_mepc_o = csr_mepc_q; //----------------------------------------------------------------- // Debug - exception type (checker use only) //----------------------------------------------------------------- reg [5:0] v_etype_r; always @ * begin v_etype_r = 6'b0; if (csr_mcause_r[`MCAUSE_INT]) v_etype_r = `RV_EXCPN_INTERRUPT; else case (csr_mcause_r) `MCAUSE_MISALIGNED_FETCH : v_etype_r = `RV_EXCPN_MISALIGNED_FETCH; `MCAUSE_FAULT_FETCH : v_etype_r = `RV_EXCPN_FAULT_FETCH; `MCAUSE_ILLEGAL_INSTRUCTION: v_etype_r = `RV_EXCPN_ILLEGAL_INSTRUCTION; `MCAUSE_BREAKPOINT : v_etype_r = `RV_EXCPN_BREAKPOINT; `MCAUSE_MISALIGNED_LOAD : v_etype_r = `RV_EXCPN_MISALIGNED_LOAD; `MCAUSE_FAULT_LOAD : v_etype_r = `RV_EXCPN_FAULT_LOAD; `MCAUSE_MISALIGNED_STORE : v_etype_r = `RV_EXCPN_MISALIGNED_STORE; `MCAUSE_FAULT_STORE : v_etype_r = `RV_EXCPN_FAULT_STORE; `MCAUSE_ECALL_U : v_etype_r = `RV_EXCPN_ECALL_U; `MCAUSE_ECALL_S : v_etype_r = `RV_EXCPN_ECALL_S; `MCAUSE_ECALL_H : v_etype_r = `RV_EXCPN_ECALL_H; `MCAUSE_ECALL_M : v_etype_r = `RV_EXCPN_ECALL_M; `MCAUSE_PAGE_FAULT_INST : v_etype_r = `RV_EXCPN_PAGE_FAULT_INST; `MCAUSE_PAGE_FAULT_LOAD : v_etype_r = `RV_EXCPN_PAGE_FAULT_LOAD; `MCAUSE_PAGE_FAULT_STORE : v_etype_r = `RV_EXCPN_PAGE_FAULT_STORE; endcase end assign exception_type_o = v_etype_r; endmodule