// Copyright 2018 ETH Zurich and University of Bologna. // Copyright and related rights are licensed under the Solderpad Hardware // License, Version 0.51 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law // or agreed to in writing, software, hardware and materials distributed under // this 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. // // Author: Florian Zaruba, ETH Zurich // Date: 05.05.2017 // Description: CSR Register File as specified by RISC-V import ariane_pkg::*; module csr_regfile #( parameter logic [63:0] DmBaseAddress = 64'h0, // debug module base address parameter int AsidWidth = 1, parameter int unsigned NrCommitPorts = 2 ) ( input logic clk_i, // Clock input logic rst_ni, // Asynchronous reset active low input logic time_irq_i, // Timer threw a interrupt // send a flush request out if a CSR with a side effect has changed (e.g. written) output logic flush_o, output logic halt_csr_o, // halt requested // commit acknowledge input scoreboard_entry_t [NrCommitPorts-1:0] commit_instr_i, // the instruction we want to commit input logic [NrCommitPorts-1:0] commit_ack_i, // Commit acknowledged a instruction -> increase instret CSR // Core and Cluster ID input logic [63:0] boot_addr_i, // Address from which to start booting, mtvec is set to the same address input logic [63:0] hart_id_i, // Hart id in a multicore environment (reflected in a CSR) // we are taking an exception input exception_t ex_i, // We've got an exception from the commit stage, take its input fu_op csr_op_i, // Operation to perform on the CSR file input logic [11:0] csr_addr_i, // Address of the register to read/write input logic [63:0] csr_wdata_i, // Write data in output logic [63:0] csr_rdata_o, // Read data out input logic dirty_fp_state_i, // Mark the FP sate as dirty input logic csr_write_fflags_i, // Write fflags register e.g.: we are retiring a floating point instruction input logic [63:0] pc_i, // PC of instruction accessing the CSR output exception_t csr_exception_o, // attempts to access a CSR without appropriate privilege // level or to write a read-only register also // raises illegal instruction exceptions. // Interrupts/Exceptions output logic [63:0] epc_o, // Output the exception PC to PC Gen, the correct CSR (mepc, sepc) is set accordingly output logic eret_o, // Return from exception, set the PC of epc_o output logic [63:0] trap_vector_base_o, // Output base of exception vector, correct CSR is output (mtvec, stvec) output riscv::priv_lvl_t priv_lvl_o, // Current privilege level the CPU is in // FPU output riscv::xs_t fs_o, // Floating point extension status output logic [4:0] fflags_o, // Floating-Point Accured Exceptions output logic [2:0] frm_o, // Floating-Point Dynamic Rounding Mode output logic [6:0] fprec_o, // Floating-Point Precision Control // MMU output logic en_translation_o, // enable VA translation output logic en_ld_st_translation_o, // enable VA translation for load and stores output riscv::priv_lvl_t ld_st_priv_lvl_o, // Privilege level at which load and stores should happen output logic sum_o, output logic mxr_o, output logic [43:0] satp_ppn_o, output logic [AsidWidth-1:0] asid_o, // external interrupts input logic [1:0] irq_i, // external interrupt in input logic ipi_i, // inter processor interrupt -> connected to machine mode sw input logic debug_req_i, // debug request in output logic set_debug_pc_o, // Virtualization Support output logic tvm_o, // trap virtual memory output logic tw_o, // timeout wait output logic tsr_o, // trap sret output logic debug_mode_o, // we are in debug mode -> that will change some decoding output logic single_step_o, // we are in single-step mode // Caches output logic icache_en_o, // L1 ICache Enable output logic dcache_en_o, // L1 DCache Enable // Performance Counter output logic [4:0] perf_addr_o, // read/write address to performance counter module (up to 29 aux counters possible in riscv encoding.h) output logic [63:0] perf_data_o, // write data to performance counter module input logic [63:0] perf_data_i, // read data from performance counter module output logic perf_we_o ); // internal signal to keep track of access exceptions logic read_access_exception, update_access_exception; logic csr_we, csr_read; logic [63:0] csr_wdata, csr_rdata; riscv::priv_lvl_t trap_to_priv_lvl; // register for enabling load store address translation, this is critical, hence the register logic en_ld_st_translation_d, en_ld_st_translation_q; logic mprv; logic mret; // return from M-mode exception logic sret; // return from S-mode exception logic dret; // return from debug mode // CSR write causes us to mark the FPU state as dirty logic dirty_fp_state_csr; riscv::csr_t csr_addr; // ---------------- // Assignments // ---------------- assign csr_addr = riscv::csr_t'(csr_addr_i); assign fs_o = mstatus_q.fs; // ---------------- // CSR Registers // ---------------- // privilege level register riscv::priv_lvl_t priv_lvl_d, priv_lvl_q; // we are in debug logic debug_mode_q, debug_mode_d; riscv::status_rv64_t mstatus_q, mstatus_d; riscv::satp_t satp_q, satp_d; riscv::dcsr_t dcsr_q, dcsr_d; logic mtvec_rst_load_q;// used to determine whether we came out of reset logic [63:0] dpc_q, dpc_d; logic [63:0] dscratch0_q, dscratch0_d; logic [63:0] dscratch1_q, dscratch1_d; logic [63:0] mtvec_q, mtvec_d; logic [63:0] medeleg_q, medeleg_d; logic [63:0] mideleg_q, mideleg_d; logic [63:0] mip_q, mip_d; logic [63:0] mie_q, mie_d; logic [63:0] mscratch_q, mscratch_d; logic [63:0] mepc_q, mepc_d; logic [63:0] mcause_q, mcause_d; logic [63:0] mtval_q, mtval_d; logic [63:0] stvec_q, stvec_d; logic [63:0] sscratch_q, sscratch_d; logic [63:0] sepc_q, sepc_d; logic [63:0] scause_q, scause_d; logic [63:0] stval_q, stval_d; logic [63:0] dcache_q, dcache_d; logic [63:0] icache_q, icache_d; logic wfi_d, wfi_q; logic [63:0] cycle_q, cycle_d; logic [63:0] instret_q, instret_d; riscv::fcsr_t fcsr_q, fcsr_d; // ---------------- // CSR Read logic // ---------------- always_comb begin : csr_read_process // a read access exception can only occur if we attempt to read a CSR which does not exist read_access_exception = 1'b0; csr_rdata = 64'b0; perf_addr_o = csr_addr.address[4:0];; if (csr_read) begin unique case (csr_addr.address) riscv::CSR_FFLAGS: begin if (mstatus_q.fs == riscv::Off) begin read_access_exception = 1'b1; end else begin csr_rdata = {59'b0, fcsr_q.fflags}; end end riscv::CSR_FRM: begin if (mstatus_q.fs == riscv::Off) begin read_access_exception = 1'b1; end else begin csr_rdata = {61'b0, fcsr_q.frm}; end end riscv::CSR_FCSR: begin if (mstatus_q.fs == riscv::Off) begin read_access_exception = 1'b1; end else begin csr_rdata = {56'b0, fcsr_q.frm, fcsr_q.fflags}; end end // non-standard extension riscv::CSR_FTRAN: begin if (mstatus_q.fs == riscv::Off) begin read_access_exception = 1'b1; end else begin csr_rdata = {57'b0, fcsr_q.fprec}; end end // debug registers riscv::CSR_DCSR: csr_rdata = {32'b0, dcsr_q}; riscv::CSR_DPC: csr_rdata = dpc_q; riscv::CSR_DSCRATCH0: csr_rdata = dscratch0_q; riscv::CSR_DSCRATCH1: csr_rdata = dscratch1_q; // trigger module registers riscv::CSR_TSELECT:; // not implemented riscv::CSR_TDATA1:; // not implemented riscv::CSR_TDATA2:; // not implemented riscv::CSR_TDATA3:; // not implemented // supervisor registers riscv::CSR_SSTATUS: begin csr_rdata = mstatus_q & ariane_pkg::SMODE_STATUS_READ_MASK; end riscv::CSR_SIE: csr_rdata = mie_q & mideleg_q; riscv::CSR_SIP: csr_rdata = mip_q & mideleg_q; riscv::CSR_STVEC: csr_rdata = stvec_q; riscv::CSR_SCOUNTEREN: csr_rdata = 64'b0; // not implemented riscv::CSR_SSCRATCH: csr_rdata = sscratch_q; riscv::CSR_SEPC: csr_rdata = sepc_q; riscv::CSR_SCAUSE: csr_rdata = scause_q; riscv::CSR_STVAL: csr_rdata = stval_q; riscv::CSR_SATP: begin // intercept reads to SATP if in S-Mode and TVM is enabled if (priv_lvl_o == riscv::PRIV_LVL_S && mstatus_q.tvm) begin read_access_exception = 1'b1; end else begin csr_rdata = satp_q; end end // machine mode registers riscv::CSR_MSTATUS: csr_rdata = mstatus_q; riscv::CSR_MISA: csr_rdata = ISA_CODE; riscv::CSR_MEDELEG: csr_rdata = medeleg_q; riscv::CSR_MIDELEG: csr_rdata = mideleg_q; riscv::CSR_MIE: csr_rdata = mie_q; riscv::CSR_MTVEC: csr_rdata = mtvec_q; riscv::CSR_MCOUNTEREN: csr_rdata = 64'b0; // not implemented riscv::CSR_MSCRATCH: csr_rdata = mscratch_q; riscv::CSR_MEPC: csr_rdata = mepc_q; riscv::CSR_MCAUSE: csr_rdata = mcause_q; riscv::CSR_MTVAL: csr_rdata = mtval_q; riscv::CSR_MIP: csr_rdata = mip_q; riscv::CSR_MVENDORID: csr_rdata = 64'b0; // not implemented riscv::CSR_MARCHID: csr_rdata = ARIANE_MARCHID; riscv::CSR_MIMPID: csr_rdata = 64'b0; // not implemented riscv::CSR_MHARTID: csr_rdata = hart_id_i; riscv::CSR_MCYCLE: csr_rdata = cycle_q; riscv::CSR_MINSTRET: csr_rdata = instret_q; // custom (non RISC-V) cache control riscv::CSR_DCACHE: csr_rdata = dcache_q; riscv::CSR_ICACHE: csr_rdata = icache_q; // Counters and Timers riscv::CSR_CYCLE: csr_rdata = cycle_q; riscv::CSR_INSTRET: csr_rdata = instret_q; riscv::CSR_L1_ICACHE_MISS, riscv::CSR_L1_DCACHE_MISS, riscv::CSR_ITLB_MISS, riscv::CSR_DTLB_MISS, riscv::CSR_LOAD, riscv::CSR_STORE, riscv::CSR_EXCEPTION, riscv::CSR_EXCEPTION_RET, riscv::CSR_BRANCH_JUMP, riscv::CSR_CALL, riscv::CSR_RET, riscv::CSR_MIS_PREDICT, riscv::CSR_SB_FULL, riscv::CSR_IF_EMPTY: csr_rdata = perf_data_i; default: read_access_exception = 1'b1; endcase end end // --------------------------- // CSR Write and update logic // --------------------------- logic [63:0] mask; always_comb begin : csr_update automatic riscv::satp_t sapt; automatic logic [63:0] instret; sapt = satp_q; instret = instret_q; // -------------------- // Counters // -------------------- cycle_d = cycle_q; instret_d = instret_q; if (!debug_mode_q) begin // increase instruction retired counter for (int i = 0; i < NrCommitPorts; i++) begin if (commit_ack_i[i] && !ex_i.valid) instret++; end instret_d = instret; // increment the cycle count if (ENABLE_CYCLE_COUNT) cycle_d = cycle_q + 1'b1; else cycle_d = instret; end eret_o = 1'b0; flush_o = 1'b0; update_access_exception = 1'b0; set_debug_pc_o = 1'b0; perf_we_o = 1'b0; perf_data_o = 'b0; fcsr_d = fcsr_q; priv_lvl_d = priv_lvl_q; debug_mode_d = debug_mode_q; dcsr_d = dcsr_q; dpc_d = dpc_q; dscratch0_d = dscratch0_q; dscratch1_d = dscratch1_q; mstatus_d = mstatus_q; // check whether we come out of reset // this is a workaround. some tools have issues // having boot_addr_i in the asynchronous // reset assignment to mtvec_d, even though // boot_addr_i will be assigned a constant // on the top-level. if (mtvec_rst_load_q) begin mtvec_d = boot_addr_i + 'h40; end else begin mtvec_d = mtvec_q; end medeleg_d = medeleg_q; mideleg_d = mideleg_q; mip_d = mip_q; mie_d = mie_q; mepc_d = mepc_q; mcause_d = mcause_q; mscratch_d = mscratch_q; mtval_d = mtval_q; dcache_d = dcache_q; icache_d = icache_q; sepc_d = sepc_q; scause_d = scause_q; stvec_d = stvec_q; sscratch_d = sscratch_q; stval_d = stval_q; satp_d = satp_q; en_ld_st_translation_d = en_ld_st_translation_q; dirty_fp_state_csr = 1'b0; // check for correct access rights and that we are writing if (csr_we) begin unique case (csr_addr.address) // Floating-Point riscv::CSR_FFLAGS: begin if (mstatus_q.fs == riscv::Off) begin update_access_exception = 1'b1; end else begin dirty_fp_state_csr = 1'b1; fcsr_d.fflags = csr_wdata[4:0]; // this instruction has side-effects flush_o = 1'b1; end end riscv::CSR_FRM: begin if (mstatus_q.fs == riscv::Off) begin update_access_exception = 1'b1; end else begin dirty_fp_state_csr = 1'b1; fcsr_d.frm = csr_wdata[2:0]; // this instruction has side-effects flush_o = 1'b1; end end riscv::CSR_FCSR: begin if (mstatus_q.fs == riscv::Off) begin update_access_exception = 1'b1; end else begin dirty_fp_state_csr = 1'b1; fcsr_d[7:0] = csr_wdata[7:0]; // ignore writes to reserved space // this instruction has side-effects flush_o = 1'b1; end end riscv::CSR_FTRAN: begin if (mstatus_q.fs == riscv::Off) begin update_access_exception = 1'b1; end else begin dirty_fp_state_csr = 1'b1; fcsr_d.fprec = csr_wdata[6:0]; // ignore writes to reserved space // this instruction has side-effects flush_o = 1'b1; end end // debug CSR riscv::CSR_DCSR: begin dcsr_d = csr_wdata[31:0]; // debug is implemented dcsr_d.xdebugver = 4'h4; // privilege level dcsr_d.prv = priv_lvl_q; // currently not supported dcsr_d.nmip = 1'b0; dcsr_d.stopcount = 1'b0; dcsr_d.stoptime = 1'b0; end riscv::CSR_DPC: dpc_d = csr_wdata; riscv::CSR_DSCRATCH0: dscratch0_d = csr_wdata; riscv::CSR_DSCRATCH1: dscratch1_d = csr_wdata; // trigger module CSRs riscv::CSR_TSELECT:; // not implemented riscv::CSR_TDATA1:; // not implemented riscv::CSR_TDATA2:; // not implemented riscv::CSR_TDATA3:; // not implemented // sstatus is a subset of mstatus - mask it accordingly riscv::CSR_SSTATUS: begin mask = ariane_pkg::SMODE_STATUS_WRITE_MASK; mstatus_d = (mstatus_q & ~mask) | (csr_wdata & mask); // hardwire to zero if floating point extension is not present if (!FP_PRESENT) begin mstatus_d.fs = riscv::Off; end // hardwired extension registers mstatus_d.sd = (&mstatus_q.xs) | (&mstatus_q.fs); // this instruction has side-effects flush_o = 1'b1; end // even machine mode interrupts can be visible and set-able to supervisor // if the corresponding bit in mideleg is set riscv::CSR_SIE: begin // the mideleg makes sure only delegate-able register (and therefore also only implemented registers) are written mie_d = (mie_q & ~mideleg_q) | (csr_wdata & mideleg_q); end riscv::CSR_SIP: begin // only the supervisor software interrupt is write-able, iff delegated mask = riscv::MIP_SSIP & mideleg_q; mip_d = (mip_q & ~mask) | (csr_wdata & mask); end riscv::CSR_SCOUNTEREN:; riscv::CSR_STVEC: stvec_d = {csr_wdata[63:2], 1'b0, csr_wdata[0]}; riscv::CSR_SSCRATCH: sscratch_d = csr_wdata; riscv::CSR_SEPC: sepc_d = {csr_wdata[63:1], 1'b0}; riscv::CSR_SCAUSE: scause_d = csr_wdata; riscv::CSR_STVAL: stval_d = csr_wdata; // supervisor address translation and protection riscv::CSR_SATP: begin // intercept SATP writes if in S-Mode and TVM is enabled if (priv_lvl_o == riscv::PRIV_LVL_S && mstatus_q.tvm) update_access_exception = 1'b1; else begin sapt = riscv::satp_t'(csr_wdata); // only make ASID_LEN - 1 bit stick, that way software can figure out how many ASID bits are supported sapt.asid = sapt.asid & {{(16-AsidWidth){1'b0}}, {AsidWidth{1'b1}}}; // only update if we actually support this mode if (sapt.mode == MODE_OFF || sapt.mode == MODE_SV39) satp_d = sapt; end // changing the mode can have side-effects on address translation (e.g.: other instructions), re-fetch // the next instruction by executing a flush flush_o = 1'b1; end riscv::CSR_MSTATUS: begin mstatus_d = csr_wdata; // hardwired zero registers mstatus_d.sd = (&mstatus_q.xs) | (&mstatus_q.fs); mstatus_d.xs = riscv::Off; if (!FP_PRESENT) begin mstatus_d.fs = riscv::Off; end mstatus_d.upie = 1'b0; mstatus_d.uie = 1'b0; // this register has side-effects on other registers, flush the pipeline flush_o = 1'b1; end // MISA is WARL (Write Any Value, Reads Legal Value) riscv::CSR_MISA:; // machine exception delegation register // 0 - 15 exceptions supported riscv::CSR_MEDELEG: begin mask = (1 << riscv::INSTR_ADDR_MISALIGNED) | (1 << riscv::BREAKPOINT) | (1 << riscv::ENV_CALL_UMODE) | (1 << riscv::INSTR_PAGE_FAULT) | (1 << riscv::LOAD_PAGE_FAULT) | (1 << riscv::STORE_PAGE_FAULT); medeleg_d = (medeleg_q & ~mask) | (csr_wdata & mask); end // machine interrupt delegation register // we do not support user interrupt delegation riscv::CSR_MIDELEG: begin mask = riscv::MIP_SSIP | riscv::MIP_STIP | riscv::MIP_SEIP; mideleg_d = (mideleg_q & ~mask) | (csr_wdata & mask); end // mask the register so that unsupported interrupts can never be set riscv::CSR_MIE: begin mask = riscv::MIP_SSIP | riscv::MIP_STIP | riscv::MIP_SEIP | riscv::MIP_MSIP | riscv::MIP_MTIP; mie_d = (mie_q & ~mask) | (csr_wdata & mask); // we only support supervisor and M-mode interrupts end riscv::CSR_MTVEC: begin mtvec_d = {csr_wdata[63:2], 1'b0, csr_wdata[0]}; // we are in vector mode, this implementation requires the additional // alignment constraint of 64 * 4 bytes if (csr_wdata[0]) mtvec_d = {csr_wdata[63:8], 7'b0, csr_wdata[0]}; end riscv::CSR_MCOUNTEREN:; riscv::CSR_MSCRATCH: mscratch_d = csr_wdata; riscv::CSR_MEPC: mepc_d = {csr_wdata[63:1], 1'b0}; riscv::CSR_MCAUSE: mcause_d = csr_wdata; riscv::CSR_MTVAL: mtval_d = csr_wdata; riscv::CSR_MIP: begin mask = riscv::MIP_SSIP | riscv::MIP_STIP | riscv::MIP_SEIP; mip_d = (mip_q & ~mask) | (csr_wdata & mask); end // performance counters riscv::CSR_MCYCLE: cycle_d = csr_wdata; riscv::CSR_MINSTRET: instret = csr_wdata; riscv::CSR_DCACHE: dcache_d = csr_wdata[0]; // enable bit riscv::CSR_ICACHE: icache_d = csr_wdata[0]; // enable bit riscv::CSR_L1_ICACHE_MISS, riscv::CSR_L1_DCACHE_MISS, riscv::CSR_ITLB_MISS, riscv::CSR_DTLB_MISS, riscv::CSR_LOAD, riscv::CSR_STORE, riscv::CSR_EXCEPTION, riscv::CSR_EXCEPTION_RET, riscv::CSR_BRANCH_JUMP, riscv::CSR_CALL, riscv::CSR_RET, riscv::CSR_MIS_PREDICT: begin perf_data_o = csr_wdata; perf_we_o = 1'b1; end default: update_access_exception = 1'b1; endcase end mstatus_d.sxl = riscv::XLEN_64; mstatus_d.uxl = riscv::XLEN_64; // mark the floating point extension register as dirty if (FP_PRESENT && (dirty_fp_state_csr || dirty_fp_state_i)) begin mstatus_d.fs = riscv::Dirty; end // write the floating point status register if (csr_write_fflags_i) begin fcsr_d.fflags = csr_wdata_i[4:0] | fcsr_q.fflags; end // --------------------- // External Interrupts // --------------------- // Machine Mode External Interrupt Pending mip_d[riscv::IRQ_M_EXT] = irq_i[0]; // Machine software interrupt mip_d[riscv::IRQ_M_SOFT] = ipi_i; // Timer interrupt pending, coming from platform timer mip_d[riscv::IRQ_M_TIMER] = time_irq_i; // ----------------------- // Manage Exception Stack // ----------------------- // update exception CSRs // we got an exception update cause, pc and stval register trap_to_priv_lvl = riscv::PRIV_LVL_M; // Exception is taken and we are not in debug mode // exceptions in debug mode don't update any fields if (!debug_mode_q && ex_i.valid) begin // do not flush, flush is reserved for CSR writes with side effects flush_o = 1'b0; // figure out where to trap to // a m-mode trap might be delegated if we are taking it in S mode // first figure out if this was an exception or an interrupt e.g.: look at bit 63 // the cause register can only be 6 bits long (as we only support 64 exceptions) if ((ex_i.cause[63] && mideleg_q[ex_i.cause[5:0]]) || (~ex_i.cause[63] && medeleg_q[ex_i.cause[5:0]])) begin // traps never transition from a more-privileged mode to a less privileged mode // so if we are already in M mode, stay there trap_to_priv_lvl = (priv_lvl_o == riscv::PRIV_LVL_M) ? riscv::PRIV_LVL_M : riscv::PRIV_LVL_S; end // trap to supervisor mode if (trap_to_priv_lvl == riscv::PRIV_LVL_S) begin // update sstatus mstatus_d.sie = 1'b0; mstatus_d.spie = mstatus_q.sie; // this can either be user or supervisor mode mstatus_d.spp = priv_lvl_q[0]; // set cause scause_d = ex_i.cause; // set epc sepc_d = pc_i; // set mtval or stval stval_d = (ariane_pkg::ZERO_TVAL && (ex_i.cause inside { riscv::ILLEGAL_INSTR, riscv::BREAKPOINT, riscv::ENV_CALL_UMODE, riscv::ENV_CALL_SMODE, riscv::ENV_CALL_MMODE } || ex_i.cause[63])) ? '0 : ex_i.tval; // trap to machine mode end else begin // update mstatus mstatus_d.mie = 1'b0; mstatus_d.mpie = mstatus_q.mie; // save the previous privilege mode mstatus_d.mpp = priv_lvl_q; mcause_d = ex_i.cause; // set epc mepc_d = pc_i; // set mtval or stval mtval_d = (ariane_pkg::ZERO_TVAL && (ex_i.cause inside { riscv::ILLEGAL_INSTR, riscv::BREAKPOINT, riscv::ENV_CALL_UMODE, riscv::ENV_CALL_SMODE, riscv::ENV_CALL_MMODE } || ex_i.cause[63])) ? '0 : ex_i.tval; end priv_lvl_d = trap_to_priv_lvl; end // ------------------------------ // Debug // ------------------------------ // Explains why Debug Mode was entered. // When there are multiple reasons to enter Debug Mode in a single cycle, hardware should set cause to the cause with the highest priority. // 1: An ebreak instruction was executed. (priority 3) // 2: The Trigger Module caused a breakpoint exception. (priority 4) // 3: The debugger requested entry to Debug Mode. (priority 2) // 4: The hart single stepped because step was set. (priority 1) // we are currently not in debug mode and could potentially enter if (!debug_mode_q) begin dcsr_d.prv = priv_lvl_o; // trigger module fired // caused by a breakpoint if (ex_i.valid && ex_i.cause == riscv::BREAKPOINT) begin // check that we actually want to enter debug depending on the privilege level we are currently in unique case (priv_lvl_o) riscv::PRIV_LVL_M: begin debug_mode_d = dcsr_q.ebreakm; set_debug_pc_o = dcsr_q.ebreakm; end riscv::PRIV_LVL_S: begin debug_mode_d = dcsr_q.ebreaks; set_debug_pc_o = dcsr_q.ebreaks; end riscv::PRIV_LVL_U: begin debug_mode_d = dcsr_q.ebreaku; set_debug_pc_o = dcsr_q.ebreaku; end default:; endcase // save PC of next this instruction e.g.: the next one to be executed dpc_d = pc_i; dcsr_d.cause = dm::CauseBreakpoint; end // we've got a debug request (and we have an instruction which we can associate it to) // don't interrupt the AMO if (debug_req_i && commit_instr_i[0].valid) begin // save the PC dpc_d = pc_i; // enter debug mode debug_mode_d = 1'b1; // jump to the base address set_debug_pc_o = 1'b1; // save the cause as external debug request dcsr_d.cause = dm::CauseRequest; end // single step enable and we just retired an instruction if (dcsr_q.step && commit_ack_i[0]) begin // valid CTRL flow change if (commit_instr_i[0].fu == CTRL_FLOW) begin // we saved the correct target address during execute dpc_d = commit_instr_i[0].bp.predict_address; // exception valid end else if (ex_i.valid) begin dpc_d = trap_vector_base_o; // return from environment end else if (eret_o) begin dpc_d = epc_o; // consecutive PC end else begin dpc_d = commit_instr_i[0].pc + (commit_instr_i[0].is_compressed ? 'h2 : 'h4); end debug_mode_d = 1'b1; set_debug_pc_o = 1'b1; dcsr_d.cause = dm::CauseSingleStep; end end // go in halt-state again when we encounter an exception if (debug_mode_q && ex_i.valid && ex_i.cause == riscv::BREAKPOINT) begin set_debug_pc_o = 1'b1; end // ------------------------------ // MPRV - Modify Privilege Level // ------------------------------ // Set the address translation at which the load and stores should occur // we can use the previous values since changing the address translation will always involve a pipeline flush if (mprv && satp_q.mode == MODE_SV39 && (mstatus_q.mpp != riscv::PRIV_LVL_M)) en_ld_st_translation_d = 1'b1; else // otherwise we go with the regular settings en_ld_st_translation_d = en_translation_o; ld_st_priv_lvl_o = (mprv) ? mstatus_q.mpp : priv_lvl_o; en_ld_st_translation_o = en_ld_st_translation_q; // ------------------------------ // Return from Environment // ------------------------------ // When executing an xRET instruction, supposing xPP holds the value y, xIE is set to xPIE; the privilege // mode is changed to y; xPIE is set to 1; and xPP is set to U if (mret) begin // return from exception, IF doesn't care from where we are returning eret_o = 1'b1; // return to the previous privilege level and restore all enable flags // get the previous machine interrupt enable flag mstatus_d.mie = mstatus_q.mpie; // restore the previous privilege level priv_lvl_d = mstatus_q.mpp; // set mpp to user mode mstatus_d.mpp = riscv::PRIV_LVL_U; // set mpie to 1 mstatus_d.mpie = 1'b1; end if (sret) begin // return from exception, IF doesn't care from where we are returning eret_o = 1'b1; // return the previous supervisor interrupt enable flag mstatus_d.sie = mstatus_q.spie; // restore the previous privilege level priv_lvl_d = riscv::priv_lvl_t'({1'b0, mstatus_q.spp}); // set spp to user mode mstatus_d.spp = 1'b0; // set spie to 1 mstatus_d.spie = 1'b1; end // return from debug mode if (dret) begin // return from exception, IF doesn't care from where we are returning eret_o = 1'b1; // restore the previous privilege level priv_lvl_d = riscv::priv_lvl_t'(dcsr_q.prv); // actually return from debug mode debug_mode_d = 1'b0; end end // --------------------------- // CSR OP Select Logic // --------------------------- always_comb begin : csr_op_logic csr_wdata = csr_wdata_i; csr_we = 1'b1; csr_read = 1'b1; mret = 1'b0; sret = 1'b0; dret = 1'b0; unique case (csr_op_i) CSR_WRITE: csr_wdata = csr_wdata_i; CSR_SET: csr_wdata = csr_wdata_i | csr_rdata; CSR_CLEAR: csr_wdata = (~csr_wdata_i) & csr_rdata; CSR_READ: csr_we = 1'b0; SRET: begin // the return should not have any write or read side-effects csr_we = 1'b0; csr_read = 1'b0; sret = 1'b1; // signal a return from supervisor mode end MRET: begin // the return should not have any write or read side-effects csr_we = 1'b0; csr_read = 1'b0; mret = 1'b1; // signal a return from machine mode end DRET: begin // the return should not have any write or read side-effects csr_we = 1'b0; csr_read = 1'b0; dret = 1'b1; // signal a return from debug mode end default: begin csr_we = 1'b0; csr_read = 1'b0; end endcase // if we are retiring an exception do not return from exception if (ex_i.valid) begin mret = 1'b0; sret = 1'b0; dret = 1'b0; end end logic interrupt_global_enable; // -------------------------------------- // Exception Control & Interrupt Control // -------------------------------------- always_comb begin : exception_ctrl automatic logic [63:0] interrupt_cause; interrupt_cause = '0; // wait for interrupt register wfi_d = wfi_q; csr_exception_o = { 64'b0, 64'b0, 1'b0 }; // ----------------- // Interrupt Control // ----------------- // TODO(zarubaf): Move interrupt handling to commit stage. // we decode an interrupt the same as an exception, hence it will be taken if the instruction did not // throw any previous exception. // we have three interrupt sources: external interrupts, software interrupts, timer interrupts (order of precedence) // for two privilege levels: Supervisor and Machine Mode // Supervisor Timer Interrupt if (mie_q[riscv::S_TIMER_INTERRUPT[5:0]] && mip_q[riscv::S_TIMER_INTERRUPT[5:0]]) interrupt_cause = riscv::S_TIMER_INTERRUPT; // Supervisor Software Interrupt if (mie_q[riscv::S_SW_INTERRUPT[5:0]] && mip_q[riscv::S_SW_INTERRUPT[5:0]]) interrupt_cause = riscv::S_SW_INTERRUPT; // Supervisor External Interrupt // The logical-OR of the software-writable bit and the signal from the external interrupt controller is // used to generate external interrupts to the supervisor if (mie_q[riscv::S_EXT_INTERRUPT[5:0]] && (mip_q[riscv::S_EXT_INTERRUPT[5:0]] | irq_i[1])) interrupt_cause = riscv::S_EXT_INTERRUPT; // Machine Timer Interrupt if (mip_q[riscv::M_TIMER_INTERRUPT[5:0]] && mie_q[riscv::M_TIMER_INTERRUPT[5:0]]) interrupt_cause = riscv::M_TIMER_INTERRUPT; // Machine Mode Software Interrupt if (mip_q[riscv::M_SW_INTERRUPT[5:0]] && mie_q[riscv::M_SW_INTERRUPT[5:0]]) interrupt_cause = riscv::M_SW_INTERRUPT; // Machine Mode External Interrupt if (mip_q[riscv::M_EXT_INTERRUPT[5:0]] && mie_q[riscv::M_EXT_INTERRUPT[5:0]]) interrupt_cause = riscv::M_EXT_INTERRUPT; // An interrupt i will be taken if bit i is set in both mip and mie, and if interrupts are globally enabled. // By default, M-mode interrupts are globally enabled if the hart’s current privilege mode is less // than M, or if the current privilege mode is M and the MIE bit in the mstatus register is set. // All interrupts are masked in debug mode interrupt_global_enable = (~debug_mode_q) // interrupts are enabled during single step or we are not stepping & (~dcsr_q.step | dcsr_q.stepie) & ((mstatus_q.mie & (priv_lvl_o == riscv::PRIV_LVL_M)) | (priv_lvl_o != riscv::PRIV_LVL_M)); if (interrupt_cause[63] && interrupt_global_enable) begin // we can set the cause here csr_exception_o.cause = interrupt_cause; // However, if bit i in mideleg is set, interrupts are considered to be globally enabled if the hart’s current privilege // mode equals the delegated privilege mode (S or U) and that mode’s interrupt enable bit // (SIE or UIE in mstatus) is set, or if the current privilege mode is less than the delegated privilege mode. if (mideleg_q[interrupt_cause[5:0]]) begin if ((mstatus_q.sie && priv_lvl_o == riscv::PRIV_LVL_S) || priv_lvl_o == riscv::PRIV_LVL_U) csr_exception_o.valid = 1'b1; end else begin csr_exception_o.valid = 1'b1; end end // ----------------- // Privilege Check // ----------------- // if we are reading or writing, check for the correct privilege level this has // precedence over interrupts if (csr_we || csr_read) begin if ((riscv::priv_lvl_t'(priv_lvl_o & csr_addr.csr_decode.priv_lvl) != csr_addr.csr_decode.priv_lvl)) begin csr_exception_o.cause = riscv::ILLEGAL_INSTR; csr_exception_o.valid = 1'b1; end // check access to debug mode only CSRs if (csr_addr_i[11:4] == 8'h7b && !debug_mode_q) begin csr_exception_o.cause = riscv::ILLEGAL_INSTR; csr_exception_o.valid = 1'b1; end end // we got an exception in one of the processes above // throw an illegal instruction exception if (update_access_exception || read_access_exception) begin csr_exception_o.cause = riscv::ILLEGAL_INSTR; // we don't set the tval field as this will be set by the commit stage // this spares the extra wiring from commit to CSR and back to commit csr_exception_o.valid = 1'b1; end // ------------------- // Wait for Interrupt // ------------------- // if there is any interrupt pending un-stall the core // also un-stall if we want to enter debug mode if (|mip_q || debug_req_i || irq_i[1]) begin wfi_d = 1'b0; // or alternatively if there is no exception pending and we are not in debug mode wait here // for the interrupt end else if (!debug_mode_q && csr_op_i == WFI && !ex_i.valid) begin wfi_d = 1'b1; end end // output assignments dependent on privilege mode always_comb begin : priv_output trap_vector_base_o = {mtvec_q[63:2], 2'b0}; // output user mode stvec if (trap_to_priv_lvl == riscv::PRIV_LVL_S) begin trap_vector_base_o = {stvec_q[63:2], 2'b0}; end // if we are in debug mode jump to a specific address if (debug_mode_q) begin trap_vector_base_o = DmBaseAddress + dm::ExceptionAddress; end // check if we are in vectored mode, if yes then do BASE + 4 * cause // we are imposing an additional alignment-constraint of 64 * 4 bytes since // we want to spare the costly addition if ((mtvec_q[0] || stvec_q[0]) && csr_exception_o.cause[63]) begin trap_vector_base_o[7:2] = csr_exception_o.cause[5:0]; end epc_o = mepc_q; // we are returning from supervisor mode, so take the sepc register if (sret) begin epc_o = sepc_q; end // we are returning from debug mode, to take the dpc register if (dret) begin epc_o = dpc_q; end end // ------------------- // Output Assignments // ------------------- always_comb begin // When the SEIP bit is read with a CSRRW, CSRRS, or CSRRC instruction, the value // returned in the rd destination register contains the logical-OR of the software-writable // bit and the interrupt signal from the interrupt controller. csr_rdata_o = csr_rdata; unique case (csr_addr.address) riscv::CSR_MIP: csr_rdata_o = csr_rdata | (irq_i[1] << riscv::IRQ_S_EXT); // in supervisor mode we also need to check whether we delegated this bit riscv::CSR_SIP: begin csr_rdata_o = csr_rdata | ((irq_i[1] & mideleg_q[riscv::IRQ_S_EXT]) << riscv::IRQ_S_EXT); end default:; endcase end // in debug mode we execute with privilege level M assign priv_lvl_o = (debug_mode_q) ? riscv::PRIV_LVL_M : priv_lvl_q; // FPU outputs assign fflags_o = fcsr_q.fflags; assign frm_o = fcsr_q.frm; assign fprec_o = fcsr_q.fprec; // MMU outputs assign satp_ppn_o = satp_q.ppn; assign asid_o = satp_q.asid[AsidWidth-1:0]; assign sum_o = mstatus_q.sum; // we support bare memory addressing and SV39 assign en_translation_o = (satp_q.mode == 4'h8 && priv_lvl_o != riscv::PRIV_LVL_M) ? 1'b1 : 1'b0; assign mxr_o = mstatus_q.mxr; assign tvm_o = mstatus_q.tvm; assign tw_o = mstatus_q.tw; assign tsr_o = mstatus_q.tsr; assign halt_csr_o = wfi_q; assign icache_en_o = icache_q[0] & (~debug_mode_q); assign dcache_en_o = dcache_q[0]; // determine if mprv needs to be considered if in debug mode assign mprv = (debug_mode_q && !dcsr_q.mprven) ? 1'b0 : mstatus_q.mprv; assign debug_mode_o = debug_mode_q; assign single_step_o = dcsr_q.step; // sequential process always_ff @(posedge clk_i or negedge rst_ni) begin if (~rst_ni) begin priv_lvl_q <= riscv::PRIV_LVL_M; // floating-point registers fcsr_q <= 64'b0; // debug signals debug_mode_q <= 1'b0; dcsr_q <= '0; dcsr_q.prv <= riscv::PRIV_LVL_M; dpc_q <= 64'b0; dscratch0_q <= 64'b0; dscratch1_q <= 64'b0; // machine mode registers mstatus_q <= 64'b0; // set to boot address + direct mode + 4 byte offset which is the initial trap mtvec_rst_load_q <= 1'b1; mtvec_q <= '0; medeleg_q <= 64'b0; mideleg_q <= 64'b0; mip_q <= 64'b0; mie_q <= 64'b0; mepc_q <= 64'b0; mcause_q <= 64'b0; mscratch_q <= 64'b0; mtval_q <= 64'b0; dcache_q <= 64'b1; icache_q <= 64'b1; // supervisor mode registers sepc_q <= 64'b0; scause_q <= 64'b0; stvec_q <= 64'b0; sscratch_q <= 64'b0; stval_q <= 64'b0; satp_q <= 64'b0; // timer and counters cycle_q <= 64'b0; instret_q <= 64'b0; // aux registers en_ld_st_translation_q <= 1'b0; // wait for interrupt wfi_q <= 1'b0; end else begin priv_lvl_q <= priv_lvl_d; // floating-point registers fcsr_q <= fcsr_d; // debug signals debug_mode_q <= debug_mode_d; dcsr_q <= dcsr_d; dpc_q <= dpc_d; dscratch0_q <= dscratch0_d; dscratch1_q <= dscratch1_d; // machine mode registers mstatus_q <= mstatus_d; mtvec_rst_load_q <= 1'b0; mtvec_q <= mtvec_d; medeleg_q <= medeleg_d; mideleg_q <= mideleg_d; mip_q <= mip_d; mie_q <= mie_d; mepc_q <= mepc_d; mcause_q <= mcause_d; mscratch_q <= mscratch_d; mtval_q <= mtval_d; dcache_q <= dcache_d; icache_q <= icache_d; // supervisor mode registers sepc_q <= sepc_d; scause_q <= scause_d; stvec_q <= stvec_d; sscratch_q <= sscratch_d; stval_q <= stval_d; satp_q <= satp_d; // timer and counters cycle_q <= cycle_d; instret_q <= instret_d; // aux registers en_ld_st_translation_q <= en_ld_st_translation_d; // wait for interrupt wfi_q <= wfi_d; end end //------------- // Assertions //------------- //pragma translate_off `ifndef VERILATOR // check that eret and ex are never valid together assert property ( @(posedge clk_i) !(eret_o && ex_i.valid)) else begin $error("eret and exception should never be valid at the same time"); $stop(); end `endif //pragma translate_on endmodule