// Copyright (c) 2017 Massachusetts Institute of Technology // Portions Copyright (c) 2019-2020 Bluespec, Inc. // //- // RVFI_DII + CHERI modifications: // Copyright (c) 2020 Jessica Clarke // Copyright (c) 2020 Peter Rugg // Copyright (c) 2020 Jonathan Woodruff // All rights reserved. // // This software was developed by SRI International and the University of // Cambridge Computer Laboratory (Department of Computer Science and // Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the // DARPA SSITH research programme. // // This work was supported by NCSC programme grant 4212611/RFA 15971 ("SafeBet"). //- // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, copy, // modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. `include "ProcConfig.bsv" import Types::*; import ProcTypes::*; import DefaultValue::*; import ConcatReg::*; import ConfigReg::*; import Ehr::*; import Fifos::*; import Vector::*; import FIFO::*; import GetPut::*; import BuildVector::*; import CHERICap::*; import CHERICC_Fat::*; import ISA_Decls_CHERI::*; // ================================================================ // BSV additional libs import Cur_Cycle :: *; // `ifdef PERFORMANCE_MONITORING import PerformanceMonitor :: *; import StatCounters::*; // `endif // ================================================================ // Project imports from Toooba import SoC_Map :: *; // ================================================================ // Information returned on traps and mret/sret/uret typedef Bit#(SizeOf#(Exception)) Cause; typedef struct { CapPipe new_pcc; `ifdef INCLUDE_TANDEM_VERIF // The fields below are for tandem verification only Bit #(2) prv; Data status; Data cause; CapPipe epcc; Data tval; `endif } Trap_Updates deriving (Bits, FShow); typedef struct { CapPipe new_pcc; `ifdef INCLUDE_TANDEM_VERIF // The fields below are for tandem verification only Bit #(2) prv; Data status; `endif } RET_Updates deriving (Bits, FShow); // ================================================================ interface CsrFile; // Read method Data rd(CSR csr); method CapReg scrRd(SCR csr); // normal write by CSRXXX inst to any CSR method Action csrInstWr(CSR csr, Data x); // normal write by RWSpecialCap inst to any SCR method Action scrInstWr(SCR csr, CapReg x); // normal write by FPU inst to FPU CSR method Bool fpuInstNeedWr(Bit#(5) fflags, Bool fpu_dirty); method Action fpuInstWr(Bit#(5) fflags); // FPU must become dirty `ifdef INCLUDE_TANDEM_VERIF // Returns new fcsr and mstatus (pure function) method Tuple2 #(Bit #(5), Data) fpuInst_csr_updates (Bit #(5) fflags, Bool init_for_way0, Bit #(5) old_fflags, Data old_mstatus); method Data getMIP; `endif // The WARL transform performed during CSRRx writes to a CSR method Data warl_xform (CSR csr, Data x); // Methods for handling traps method Maybe#(Interrupt) pending_interrupt; method ActionValue#(Trap_Updates) trap(Trap t, CapPipe pcc, Addr faultAddr, Bit #(32) orig_inst); method ActionValue#(RET_Updates) sret; method ActionValue#(RET_Updates) mret; // Outputs for CSRs that the rest of the processor needs to know about method VMInfo vmI; method VMInfo vmD; method CsrDecodeInfo decodeInfo; // Updating minstret CSR outside of normal CSR write instructions. This // increment will see the effect of normal CSR write. method Action incInstret(SupCnt x); // update copy of mtime method Action setTime(Data t); // MSIP/MTIP bits for external world (e.g., for MMIO and timer interrupt). // XXX These methods should only be called when the processor backend // pipeline does not contain any CSRXXX inst or corresponding interrupt // inst (the inst which is turned into an interrupt). This ensures that // CSRXXX and interrupt are handled atomically. MSIP/MTIP should not // affect other insts (e.g., address translation of loads/stores), // synchronous exceptions and other types of interrupts. method Bit#(1) getMSIP; method Action setMSIP(Bit#(1) v); method Action setMTIP(Bit#(1) v); // Bluespec: external interrupts targeting machine and supervisor modes method Action setMEIP (Bit #(1) v); method Action setSEIP (Bit #(1) v); // performance stats is collected or not method Bool doPerfStats; // send/recv updates on stats CSR globally method ActionValue#(Bool) sendDoStats; method Action recvDoStats(Bool s); // terminate method ActionValue#(void) terminate; `ifdef INCLUDE_GDB_CONTROL // Read dpc method CapReg dpc_read (); // Update dpc method Action dpc_write (CapReg pc); // Check whether to enter Debug Mode based on dcsr.{ebreakm, ebreaks, ebreaku} method Bit #(1) dcsr_break_bit; // Read dcsr[2], the step bit method Bit #(1) dcsr_step_bit; // Update 'cause' in DCSR // Is invoked by logic that stops a hart, to enter Debug Mode (* always_ready *) method Action dcsr_cause_write (Bit #(3) dcsr_cause); `endif // `ifdef PERFORMANCE_MONITORING (* always_ready, always_enabled *) method Action send_performance_events (Vector #(No_Of_Evts, Bit #(Report_Width)) evts); // `endif endinterface // Fancy Reg functions function Reg#(Bit#(n)) truncateReg(Reg#(Bit#(m)) r) provisos (Add#(a__,n,m)); return (interface Reg; method Bit#(n) _read = truncate(r._read); method Action _write(Bit#(n) x) = r._write({truncateLSB(r._read), x}); endinterface); endfunction function Reg#(Bit#(n)) truncateRegLSB(Reg#(Bit#(m)) r) provisos (Add#(a__,n,m)); return (interface Reg; method Bit#(n) _read = truncateLSB(r._read); method Action _write(Bit#(n) x) = r._write({x, truncate(r._read)}); endinterface); endfunction function Reg#(Bit#(n)) zeroExtendReg(Reg#(Bit#(m)) r) provisos (Add#(a__,m,n)); return (interface Reg; method Bit#(n) _read = zeroExtend(r._read); method Action _write(Bit#(n) x) = r._write(truncate(x)); endinterface); endfunction function Reg#(t) readOnlyReg(t r); return (interface Reg; method t _read = r; method Action _write(t x) = noAction; endinterface); endfunction // module version of readOnlyReg for convenience module mkReadOnlyReg#(t x)(Reg#(t)); return readOnlyReg(x); endmodule function Reg#(t) regFromReadOnly(ReadOnly#(t) r); return (interface Reg; method t _read = r._read; method Action _write(t x); noAction; endmethod endinterface); endfunction function Reg#(t) addWriteSideEffect(Reg#(t) r, Action a); return (interface Reg; method t _read = r._read; method Action _write(t x); r._write(x); a; endmethod endinterface); endfunction // `ifdef PERFORMANCE_MONITORING interface PerfCountersVec; interface Vector#(No_Of_Ctrs, Reg#(Data)) counter_vec; interface Vector#(No_Of_Ctrs, Reg#(Data)) event_vec; interface Reg#(Bit#(No_Of_Ctrs)) inhibit; method Action send_performance_events (Vector #(No_Of_Evts, Bit#(Report_Width)) evts); endinterface (* synthesize *) module mkPerfCountersToooba (PerfCountersVec); PerfCounters_IFC #(No_Of_Ctrs, Counter_Width, Report_Width, No_Of_Evts) perf_counters <- mkPerfCounters; Vector#(No_Of_Ctrs, Reg#(Data)) counters = ?; let writeCounterFF <- mkFIFO; rule forwardWriteCounter; match {.i, .x} <- toGet(writeCounterFF).get; perf_counters.write_counter(i, x); endrule let writeCounterSelFF <- mkFIFO; rule forwardWriteCounterSel; match {.i, .x} <- toGet(writeCounterSelFF).get; perf_counters.write_ctr_sel(i, x); endrule let writeCounterInhibitFF <- mkFIFO; rule forwardWriteCounterInhibit; let x <- toGet(writeCounterInhibitFF).get; perf_counters.write_ctr_inhibit(x); endrule for (Bit#(TLog#(No_Of_Ctrs)) i = 0; i < 29; i = i + 1) counters[i] = interface Reg; method Action _write(Data x) = writeCounterFF.enq(tuple2(i,x)); method Data _read = perf_counters.read_counters[i]; endinterface; Vector#(No_Of_Ctrs, Reg#(Data)) events = ?; for (Bit#(TLog#(No_Of_Ctrs)) i = 0; i < 29; i = i + 1) events[i] = interface Reg; method Action _write(Data x) = writeCounterSelFF.enq(tuple2(i,truncate(x))); method Data _read = zeroExtend(perf_counters.read_ctr_sels[i]); endinterface; interface counter_vec = counters; interface event_vec = events; interface inhibit = interface Reg; method Action _write(Bit#(No_Of_Ctrs) x) = writeCounterInhibitFF.enq(x); method Bit#(No_Of_Ctrs) _read = (perf_counters.read_ctr_inhibit); endinterface; method send_performance_events = perf_counters.send_performance_events; endmodule // `endif function Bool has_csr_permission(CSR csr, Bit#(2) prv, Bool write); Bit#(12) csr_index = pack(csr); return ((prv >= csr_index[9:8]) && (!write || (csr_index[11:10] != 2'b11))); endfunction // non-standard terminate CSR interface Terminate; interface Reg#(Data) reg_ifc; method ActionValue#(void) terminate; endinterface module mkTerminate(Terminate); FIFO#(void) terminateQ <- mkFIFO1; interface Reg reg_ifc; method Action _write(Data x); terminateQ.enq(?); $display( "[Terminate CSR] being written (val = %x), ", "send terminate signal to host", x ); endmethod method Data _read = 0; endinterface method terminate = toGet(terminateQ).get; endmodule // stats CSR: there is only one copy in the whole multiprocessor, so any write // to stats CSR will be broadcasted interface StatsCsr; interface Reg#(Data) reg_ifc; method Bool doPerfStats; // send/recv updates on stats CSR globally method ActionValue#(Bool) sendDoStats; method Action recvDoStats(Bool s); endinterface module mkStatsCsr(StatsCsr); Reg#(Bool) doStats <- mkConfigReg(True); FIFO#(Bool) writeQ <- mkFIFO1; interface Reg reg_ifc; method Data _read = zeroExtend(pack(doStats)); method Action _write(Data x); writeQ.enq(unpack(truncate(x))); endmethod endinterface method Bool doPerfStats = doStats; method ActionValue#(Bool) sendDoStats; writeQ.deq; return writeQ.first; endmethod method Action recvDoStats(Bool s); doStats <= s; endmethod endmodule function Reg#(Data) scrToCsr(Reg#(CapReg) scr) = interface Reg method _write (x) = action CapPipe scr_pipe = cast(scr); scr <= cast(setAddr(scr_pipe, x).value); endaction; method Data _read; CapPipe scr_pipe = cast(scr); return getAddr(scr_pipe); endmethod endinterface; // same as EHR except that read port 0 is not ordered with other methods. Read // port 1 will still get bypassing from write port 0. module mkConfigEhr#(t init)(Ehr#(n, t)) provisos(Bits#(t, w)); Ehr#(n, t) data <- mkEhr(init); Wire#(t) read <- mkBypassWire; (* fire_when_enabled, no_implicit_conditions *) rule setRead; read <= data[0]; endrule Ehr#(n, t) ifc = ?; ifc[0] = (interface Reg; method _read = read._read; method _write = data[0]._write; endinterface); for(Integer i = 1; i < valueOf(n); i = i+1) begin ifc[i] = (interface Reg; method _read = data[i]._read; method _write = data[i]._write; endinterface); end return ifc; endmodule module mkCsrFile #(Data hartid)(CsrFile); RiscVISASubset isa = defaultValue; // To save from bypassing logic, CSR reads will get stale value let mkCsrReg = mkConfigReg; let mkCsrEhr = mkConfigEhr; // current prv level (this is not a csr...) Reg#(Bit#(2)) prv_reg <- mkCsrReg(prvM); // Machine level CSRs // mstatus Reg#(Bit#(2)) xs_reg <- mkReadOnlyReg(0); // XXX no extension Reg#(Bit#(2)) fs_reg <- (isa.f || isa.d) ? mkCsrReg(2'b00) : mkReadOnlyReg(0); Reg#(Bit#(1)) sd_reg = readOnlyReg( ((xs_reg == 2'b11) || (fs_reg == 2'b11)) ? 1 : 0 ); function Bit #(1) fn_sd_val (Bit #(2) xs_val, Bit #(2) fs_val); return (((xs_val == 2'b11) || (fs_val == 2'b11)) ? 1 : 0); endfunction Reg#(Bit#(2)) sxl_reg = readOnlyReg(getXLBits); Reg#(Bit#(2)) uxl_reg = readOnlyReg(getXLBits); Reg#(Bit#(1)) tsr_reg <- mkCsrReg(0); Reg#(Bit#(1)) tw_reg <- mkCsrReg(0); Reg#(Bit#(1)) tvm_reg <- mkCsrReg(0); Reg#(Bit#(1)) mxr_reg <- mkCsrReg(0); Reg#(Bit#(1)) sum_reg <- mkCsrReg(0); Reg#(Bit#(1)) mprv_reg <- mkCsrReg(0); Reg#(Bit#(2)) mpp_reg <- mkCsrReg(0); Reg#(Bit#(1)) spp_reg <- mkCsrReg(0); Vector#(4, Reg#(Bit#(2))) prev_prv_vec = vec( // prev_prv_vec[x]: privilege mode before trapping into mode x readOnlyReg(prvU), // upp concatReg2(readOnlyReg(1'b0), spp_reg), // spp readOnlyReg(2'b0), // reserved mpp_reg ); Vector#(4, Reg#(Bit#(1))) ie_vec = replicate( readOnlyReg(0) // ie_vec[x]: interrupt enable for mode x ); ie_vec[prvU] <- mkCsrReg(0); ie_vec[prvS] <- mkCsrReg(0); ie_vec[prvM] <- mkCsrReg(0); Vector#(4, Reg#(Bit#(1))) prev_ie_vec = replicate( readOnlyReg(0) // prev_ie_vec[x]: ie_vec[x] before trapping into mode x ); prev_ie_vec[prvU] <- mkCsrReg(0); prev_ie_vec[prvS] <- mkCsrReg(0); prev_ie_vec[prvM] <- mkCsrReg(0); Reg#(Data) mstatus_csr = concatReg24( sd_reg, readOnlyReg(27'b0), sxl_reg, uxl_reg, readOnlyReg(9'b0), tsr_reg, tw_reg, tvm_reg, mxr_reg, sum_reg, mprv_reg, xs_reg, fs_reg, mpp_reg, readOnlyReg(2'b0), spp_reg, prev_ie_vec[prvM], readOnlyReg(1'b0), prev_ie_vec[prvS], prev_ie_vec[prvU], ie_vec[prvM], readOnlyReg(1'b0), ie_vec[prvS], ie_vec[prvU] ); function Data fn_mstatus_val (Bit #(2) sxl_val, Bit #(2) uxl_val, Bit #(1) tsr_val, Bit #(1) tw_val, Bit #(1) tvm_val, Bit #(1) mxr_val, Bit #(1) sum_val, Bit #(1) mprv_val, Bit #(2) xs_val, Bit #(2) fs_val, Bit #(2) mpp_val, Bit #(1) spp_val, Bit #(1) prev_ie_vec_prvM_val, Bit #(1) prev_ie_vec_prvS_val, Bit #(1) prev_ie_vec_prvU_val, Bit #(1) ie_vec_prvM_val, Bit #(1) ie_vec_prvS_val, Bit #(1) ie_vec_prvU_val); return {fn_sd_val (xs_val, fs_val), 27'b0, sxl_val, uxl_val, 9'b0, tsr_val, tw_val, tvm_val, mxr_val, sum_val, mprv_val, xs_val, fs_val, mpp_val, 2'b0, spp_val, prev_ie_vec_prvM_val, 1'b0, prev_ie_vec_prvS_val, prev_ie_vec_prvU_val, ie_vec_prvM_val, 1'b0, ie_vec_prvS_val, ie_vec_prvU_val}; endfunction // misa Reg#(Data) misa_csr = readOnlyReg({getXLBits, 36'b0, getExtensionBits(isa)}); // medeleg: some exceptions don't exist, fix corresponding bits to 0 Reg#(Bit#(3)) medeleg_28_26_reg <- mkCsrReg(0); // CHERI causes 0x1a-0x1c Reg#(Bit#(1)) medeleg_15_reg <- mkCsrReg(0); // cause 15 Reg#(Bit#(3)) medeleg_13_11_reg <- mkCsrReg(0); // case 13-11 Reg#(Bit#(10)) medeleg_9_0_reg <- mkCsrReg(0); // cause 9-0 Reg#(Data) medeleg_csr = concatReg8( readOnlyReg(35'b0), medeleg_28_26_reg, readOnlyReg(10'b0), medeleg_15_reg, readOnlyReg(1'b0), medeleg_13_11_reg, readOnlyReg(1'b0), medeleg_9_0_reg ); // mideleg: some interrupts don't exist, fix corresponding bits to 0 Reg#(Bit#(1)) mideleg_11_reg <- mkCsrReg(0); Reg#(Bit#(3)) mideleg_9_7_reg <- mkCsrReg(0); Reg#(Bit#(3)) mideleg_5_3_reg <- mkCsrReg(0); Reg#(Bit#(2)) mideleg_1_0_reg <- mkCsrReg(0); Reg#(Data) mideleg_csr = concatReg8( readOnlyReg(52'b0), mideleg_11_reg, readOnlyReg(1'b0), mideleg_9_7_reg, readOnlyReg(1'b0), mideleg_5_3_reg, readOnlyReg(1'b0), mideleg_1_0_reg ); // mie Vector#(4, Reg#(Bit#(1))) external_int_en_vec = replicate(readOnlyReg(0)); external_int_en_vec[prvU] <- mkCsrReg(0); external_int_en_vec[prvS] <- mkCsrReg(0); external_int_en_vec[prvM] <- mkCsrReg(0); Vector#(4, Reg#(Bit#(1))) timer_int_en_vec = replicate(readOnlyReg(0)); timer_int_en_vec[prvU] <- mkCsrReg(0); timer_int_en_vec[prvS] <- mkCsrReg(0); timer_int_en_vec[prvM] <- mkCsrReg(0); Vector#(4, Reg#(Bit#(1))) software_int_en_vec = replicate(readOnlyReg(0)); software_int_en_vec[prvU] <- mkCsrReg(0); software_int_en_vec[prvS] <- mkCsrReg(0); software_int_en_vec[prvM] <- mkCsrReg(0); Reg#(Data) mie_csr = concatReg13( readOnlyReg(52'b0), external_int_en_vec[prvM], readOnlyReg(1'b0), external_int_en_vec[prvS], readOnlyReg(1'b0), // only if misa.N: external_int_en_vec[prvU], timer_int_en_vec[prvM], readOnlyReg(1'b0), timer_int_en_vec[prvS], readOnlyReg(1'b0), // only if misa.N: timer_int_en_vec[prvU], software_int_en_vec[prvM], readOnlyReg(1'b0), software_int_en_vec[prvS], readOnlyReg(1'b0) // only if misa.N: software_int_en_vec[prvU] ); // mcounteren Reg#(Bit#(1)) mcounteren_ir_reg <- mkCsrReg(0); Reg#(Bit#(1)) mcounteren_tm_reg <- mkCsrReg(0); Reg#(Bit#(1)) mcounteren_cy_reg <- mkCsrReg(0); Reg#(Data) mcounteren_csr = concatReg5( readOnlyReg(32'b0), readOnlyReg(~29'b0), // hpmcounter 3-31 currently always accessible in S mode mcounteren_ir_reg, mcounteren_tm_reg, mcounteren_cy_reg ); // mscratch Reg#(Data) mscratch_csr <- mkCsrReg(0); // mcause Reg#(Bit#(1)) mcause_interrupt_reg <- mkCsrReg(0); Reg#(Cause) mcause_code_reg <- mkCsrReg(0); Reg#(Data) mcause_csr = concatReg3( mcause_interrupt_reg, readOnlyReg(0), mcause_code_reg ); function Data fn_mcause_val (Bit #(1) mcause_interrupt_val, Cause mcause_code_val); return { mcause_interrupt_val, 'b0, mcause_code_val }; endfunction // mtval (mbadaddr in spike) Reg#(Data) mtval_csr <- mkCsrReg(0); // Capability cause register Reg#(Data) mccsr_csr <- mkReadOnlyReg(64'b11); // mip Vector#(4, Reg#(Bit#(1))) external_int_pend_vec = replicate(readOnlyReg(0)); external_int_pend_vec[prvU] <- mkCsrReg(0); external_int_pend_vec[prvS] <- mkCsrReg(0); external_int_pend_vec[prvM] <- mkCsrReg(0); // TODO: bug (writeable by CSRRx)? Vector#(4, Reg#(Bit#(1))) timer_int_pend_vec = replicate(readOnlyReg(0)); timer_int_pend_vec[prvU] <- mkCsrReg(0); timer_int_pend_vec[prvS] <- mkCsrReg(0); timer_int_pend_vec[prvM] <- mkCsrReg(0); // TODO: bug (writeable by CSRRx)? Vector#(4, Reg#(Bit#(1))) software_int_pend_vec = replicate(readOnlyReg(0)); software_int_pend_vec[prvU] <- mkCsrReg(0); software_int_pend_vec[prvS] <- mkCsrReg(0); software_int_pend_vec[prvM] <- mkCsrReg(0); // TODO: bug (writeable by CSRRx)? Reg#(Data) mip_csr = concatReg13( readOnlyReg(52'b0), // External interrupts readOnlyReg(external_int_pend_vec[prvM]), // MEIP is read-only to software readOnlyReg(1'b0), external_int_pend_vec[prvS], readOnlyReg(1'b0), // only if misa.N: external_int_pend_vec[prvU], // Timer interrupts readOnlyReg(timer_int_pend_vec[prvM]), // MTIP is read-only to software readOnlyReg(1'b0), timer_int_pend_vec[prvS], readOnlyReg(1'b0), // only if misa.N: timer_int_pend_vec[prvU], // Software interrupts readOnlyReg(software_int_pend_vec[prvM]), // MSIP is read-only to software readOnlyReg(1'b0), software_int_pend_vec[prvS], readOnlyReg(1'b0) // only if misa.N: software_int_pend_vec[prvU] ); // MIP and MIE fields are WARL (Write Any Read Legal) // We support M-privilege and S-privilege bits only; // this mask allows only those bits through. Data mip_mie_warl_mask = zeroExtend (12'h_222); // minstret Ehr#(2, Data) minstret_ehr <- mkCsrEhr(0); Reg#(Data) minstret_csr = minstret_ehr[0]; // mcycle Ehr#(2, Data) mcycle_ehr <- mkCsrEhr(0); Reg#(Data) mcycle_csr = mcycle_ehr[0]; // mvendorid Reg#(Data) mvendorid_csr = readOnlyReg(0); // marchid Reg#(Data) marchid_csr = readOnlyReg(0); // mimpid Reg#(Data) mimpid_csr = readOnlyReg(0); // mhartid Reg#(Data) mhartid_csr = readOnlyReg(hartid); // Supervisor level CSRs // sstatus: restricted view of mstatus Reg#(Data) sstatus_csr = concatReg17( sd_reg, readOnlyReg(29'b0), uxl_reg, readOnlyReg(12'b0), mxr_reg, sum_reg, readOnlyReg(1'b0), xs_reg, fs_reg, readOnlyReg(4'b0), spp_reg, readOnlyReg(2'b0), prev_ie_vec[prvS], prev_ie_vec[prvU], readOnlyReg(2'b0), ie_vec[prvS], ie_vec[prvU] ); function Data fn_sstatus_val (Bit #(2) uxl_val, Bit #(1) mxr_val, Bit #(1) sum_val, Bit #(2) xs_val, Bit #(2) fs_val, Bit #(1) spp_val, Bit #(1) prev_ie_vec_prvS_val, Bit #(1) prev_ie_vec_prvU_val, Bit #(1) ie_vec_prvS_val, Bit #(1) ie_vec_prvU_val); return {fn_sd_val (xs_val, fs_val), 27'b0, 2'b0, uxl_val, 12'b0, mxr_val, sum_val, 1'b0, xs_val, fs_val, 4'b0, spp_val, 2'b0, prev_ie_vec_prvS_val, prev_ie_vec_prvU_val, 2'b0, ie_vec_prvS_val, ie_vec_prvU_val}; endfunction // sie: restricted view of mie Reg#(Data) sie_csr = concatReg9( readOnlyReg(54'b0), external_int_en_vec[prvS], readOnlyReg(1'b0), // only if misa.N: external_int_en_vec[prvU], readOnlyReg(2'b0), timer_int_en_vec[prvS], readOnlyReg(1'b0), // only if misa.N: timer_int_en_vec[prvU], readOnlyReg(2'b0), software_int_en_vec[prvS], readOnlyReg(1'b0) // only if misa.N: software_int_en_vec[prvU] ); // scounteren Reg#(Bit#(1)) scounteren_ir_reg <- mkCsrReg(0); Reg#(Bit#(1)) scounteren_tm_reg <- mkCsrReg(0); Reg#(Bit#(1)) scounteren_cy_reg <- mkCsrReg(0); Reg#(Data) scounteren_csr = concatReg5( readOnlyReg(32'b0), readOnlyReg(~29'b0), // hpmcounter 3-31 currently always accessible in U mode scounteren_ir_reg, scounteren_tm_reg, scounteren_cy_reg ); // sscratch Reg#(Data) sscratch_csr <- mkCsrReg(0); // scause Reg#(Bit#(1)) scause_interrupt_reg <- mkCsrReg(0); Reg#(Cause) scause_code_reg <- mkCsrReg(0); Reg#(Data) scause_csr = concatReg3( scause_interrupt_reg, readOnlyReg('b0), scause_code_reg ); function Data fn_scause_val (Bit #(1) scause_interrupt_val, Cause scause_code_val); return { scause_interrupt_val, 0, scause_code_val }; endfunction // stval (sbadaddr in spike) Reg#(Data) stval_csr <- mkCsrReg(0); // Capability cause register Reg#(Bit#(1)) global_cap_load_gen_s_reg <- mkCsrReg(0); Reg#(Bit#(1)) global_cap_load_gen_u_reg <- mkCsrReg(0); Reg#(Data) sccsr_csr = concatReg4 (readOnlyReg(60'b0), global_cap_load_gen_u_reg, global_cap_load_gen_s_reg, readOnlyReg(2'b11)); // sip: restricted view of mip Reg#(Data) sip_csr = concatReg9( readOnlyReg(54'b0), external_int_pend_vec[prvS], readOnlyReg(1'b0), // only if misa.N: external_int_pend_vec[prvU], readOnlyReg(2'b0), timer_int_pend_vec[prvS], readOnlyReg(1'b0), // only if misa.N: timer_int_pend_vec[prvU], readOnlyReg(2'b0), software_int_pend_vec[prvS], readOnlyReg(1'b0) // only if misa.N: software_int_pend_vec[prvU] ); // SIP and SIE fields are WARL (Write Any Read Legal) // We support S-privilege bits only; // this mask allows only those bits through. Data sip_sie_warl_mask = zeroExtend (12'h_222); // satp (sptbr in spike): FIXME we only support Bare and Sv39, so we hack // the encoding of mode[3:0] field. Only mode[3] is relevant, other bits // are always 0 Reg#(Bit#(1)) vm_mode_sv39_reg <- mkCsrReg(0); Reg#(Bit#(4)) vm_mode_reg = concatReg2(vm_mode_sv39_reg, readOnlyReg(3'b0)); Reg#(Asid) asid_reg <- mkCsrReg(0); Reg#(Bit#(16)) full_asid_reg = zeroExtendReg(asid_reg); Reg#(Bit#(44)) ppn_reg <- mkCsrReg(0); Reg#(Data) satp_csr = concatReg3(vm_mode_reg, full_asid_reg, ppn_reg); // User level CSRs // According to spike, any write to fflags/frm/fcsr will set fs_reg as // dirty, regardless of whether the write truly changes value or not. // Besides, any non-zero FP exception flags will also make fs_reg dirty. // fflags: if we directly change fflags_reg (instead of fflags_csr), then // we must set fs_reg manually Reg#(Bit#(5)) fflags_reg <- mkCsrReg(0); Reg#(Data) fflags_csr = addWriteSideEffect( zeroExtendReg(fflags_reg), fs_reg._write(2'b11) ); // frm: if we directly change frm_reg (instead of frm_csr), then we must // set fs_reg manually Reg#(Bit#(3)) frm_reg <- mkCsrReg(0); Reg#(Data) frm_csr = addWriteSideEffect( zeroExtendReg(frm_reg), fs_reg._write(2'b11) ); // fcsr Reg#(Data) fcsr_csr = addWriteSideEffect( zeroExtendReg(concatReg2(frm_reg, fflags_reg)), fs_reg._write(2'b11) ); // cycle Reg#(Data) cycle_csr = readOnlyReg(mcycle_csr); // time Reg#(Data) time_reg <- mkCsrReg(0); Reg#(Data) time_csr = readOnlyReg(time_reg); // instret Reg#(Data) instret_csr = readOnlyReg(minstret_csr); // terminate (non-standard) Terminate terminate_module <- mkTerminate; Reg#(Data) terminate_csr = terminate_module.reg_ifc; // whether performance stats is collected StatsCsr stats_module <- mkStatsCsr; Reg#(Data) stats_csr = stats_module.reg_ifc; Reg #(Data) rg_tselect <- mkConfigReg (0); // Note: ISA test rv64mi-p-breakpoint assumes tdata1's reset value == 0 // Until we implement trigger functionality, // force 'tdata1.type' field ([xlen-1:xlen-4]) to zero // meaning: 'There is no trigger at this tselect' Reg #(Bit #(4)) rg_tdata1_type <- mkReadOnlyReg (0); Reg #(Bit #(1)) rg_tdata1_dmode <- mkCsrReg (0); Reg #(Bit #(59)) rg_tdata1_data <- mkCsrReg (0); Reg #(Data) rg_tdata1 = concatReg3 (rg_tdata1_type, rg_tdata1_dmode, rg_tdata1_data); Reg #(Data) rg_tdata2 <- mkConfigRegU; Reg #(Data) rg_tdata3 <- mkConfigRegU; `ifdef INCLUDE_GDB_CONTROL // DCSR is 32b even in RV64 Bit #(32) dcsr_reset_value = {4'h4, // [31:28] xdebugver 12'h0, // [27:16] reserved 1'h0, // [15] ebreakm 1'h0, // [14] reserved 1'h0, // [13] ebreaks 1'h0, // [12] ebreaku 1'h0, // [11] stepie 1'h0, // [10] stopcount 1'h0, // [9] stoptime 3'h0, // [8:6] cause // WARNING: 0 is non-standard 1'h0, // [5] reserved 1'h1, // [4] mprven 1'h0, // [3] nmip // non-maskable interrupt pending 1'h0, // [2] step 2'h3}; // [1:0] prv (machine mode) // RV64: dcsr's upper 32b zeroExtended/ignored Reg #(Data) rg_dcsr <- mkConfigReg (zeroExtend (dcsr_reset_value)); `ifndef BSIM Reg #(CapReg) rg_dpc <- mkConfigReg (setAddrUnsafe(almightyCap,soc_map_struct.pc_reset_value)); `else Reg #(CapReg) rg_dpc <- mkConfigRegU; Reg #(Bool) rg_dpc_is_init <- mkReg (False); (* fire_when_enabled, no_implicit_conditions *) rule init_rg_dpc (!rg_dpc_is_init); rg_dpc <= setAddrUnsafe (almightyCap, soc_map_struct.pc_reset_value); rg_dpc_is_init <= True; endrule `endif Reg #(Data) rg_dscratch0 <- mkConfigRegU; Reg #(Data) rg_dscratch1 <- mkConfigRegU; `endif // `ifdef PERFORMANCE_MONITORING PerfCountersVec perf_counters <- mkPerfCountersToooba; //Reg #(Bit #(2)) rg_ctr_inhib_lsb <- mkReg (0); //Wire #(Bit #(2)) w_ctr_inhib_lsb <- mkWire; //Bit #(3) ctr_inhibit_lsb = { rg_ctr_inhib_lsb [1], 0, rg_ctr_inhib_lsb [0] }; //Word ctr_inhibit = zeroExtend ({ perf_counters.read_ctr_inhibit, ctr_inhibit_lsb }); //CSR_Addr no_of_ctrs = fromInteger (valueOf (No_Of_Ctrs)); // `endif `ifdef SECURITY // sanctum machine CSRs // ### Enclave virtual base and mask // (per-core) registers // ( defines a virtual region for which enclave page tables are used in // place of OS-controlled page tables) // (machine-mode non-standard read/write) Reg#(Data) mevbase_csr <- mkCsrReg(maxBound); // impossible base & mask, Reg#(Data) mevmask_csr <- mkCsrReg(0); // so no enclave accesses are possible // ### Enclave page table base // (per core) register // ( pointer to a separate page table data structure used to translate enclave // virtual addresses) // (machine-mode non-standard read/write) Reg#(Bit#(44)) eppn_reg <- mkCsrReg(0); Reg#(Data) meatp_csr = zeroExtendReg(eppn_reg); // ### DRAM bitmap // (per core) registers (OS and Enclave) // ( white-lists the DRAM regions the core is allowed to access via OS and // enclave virtual addresses) // (machine-mode non-standard read/write) Reg#(Data) mmrbm_csr <- mkCsrReg(maxBound); Reg#(Data) memrbm_csr <- mkCsrReg(0); // ### Protected region base and mask // (per core) registers (OS and Enclave) // ( these are used to prevent address translation into a specific range of // physical addresses, for example to protect the security monitor from all software) // (machine-mode non-standard read/write) Reg#(Data) mparbase_csr <- mkCsrReg(maxBound); Reg#(Data) mparmask_csr <- mkCsrReg(0); Reg#(Data) meparbase_csr <- mkCsrReg(0); Reg#(Data) meparmask_csr <- mkCsrReg(0); // ### Turn on/off speculation Reg#(Bit#(2)) mspec_reg <- mkCsrReg(mSpecAll); Reg#(Data) mspec_csr = zeroExtendReg(mspec_reg); // sanctum user CSR // ### true random number // For now, we skip secure boot, keep TRNG = 0 Reg#(Data) trng_csr <- mkReadOnlyReg(0); //mkTRNG; `endif //SCRs Reg#(CapReg) ddc_reg <- mkCsrReg(defaultValue); // User level SCRs with accessSysRegs // Reg#(CapReg) utcc_reg <- mkCsrReg(defaultValue); // Reg#(CapReg) utdc_reg <- mkCsrReg(nullCap); // Reg#(CapReg) uScratchC_reg <- mkCsrReg(nullCap); // Reg#(CapReg) uepcc_reg <- mkCsrReg(defaultValue); // System level SCRs with accessSysRegs Reg#(CapReg) stcc_reg <- mkCsrReg(defaultValue); Reg#(CapReg) stdc_reg <- mkCsrReg(nullCap); Reg#(CapReg) sScratchC_reg <- mkCsrReg(nullCap); Ehr#(2, CapReg) sepcc_reg <- mkConfigEhr(defaultValue); // Machine level SCRs with accessSysRegs Reg#(CapReg) mtcc_reg <- mkCsrReg(defaultValue); Reg#(CapReg) mtdc_reg <- mkCsrReg(nullCap); Reg#(CapReg) mScratchC_reg <- mkCsrReg(nullCap); Ehr#(2, CapReg) mepcc_reg <- mkConfigEhr(defaultValue); // `ifdef PERFORMANCE_MONITORING // Performance monitoring Reg#(Bit#(1)) mcountinhibit_cy_reg <- mkCsrReg(0); Reg#(Bit#(1)) mcountinhibit_ir_reg <- mkCsrReg(0); Reg#(Data) mcountinhibit_reg = concatReg5(readOnlyReg(32'h00000000), perf_counters.inhibit, mcountinhibit_ir_reg, readOnlyReg(1'b0), mcountinhibit_cy_reg); // `endif rule incCycle; // `ifdef PERFORMANCE_MONITORING if(!unpack(mcountinhibit_cy_reg)) mcycle_ehr[1] <= mcycle_ehr[1] + 1; // `else // mcycle_ehr[1] <= mcycle_ehr[1] + 1; // `endif endrule // Function for getting a csr given an index function Reg#(Data) get_csr(CSR csr); Reg#(Data) ret = readOnlyReg(64'b0); // `ifdef PERFORMANCE_MONITORING let c = csr.addr; if ((csrAddrMHPMCOUNTER3.addr <= c) && (c <= csrAddrMHPMCOUNTER31.addr)) ret = perf_counters.counter_vec[c-csrAddrMHPMCOUNTER3.addr]; if ((csrAddrMHPMEVENT3.addr <= c) && (c <= csrAddrMHPMEVENT31.addr)) ret = perf_counters.event_vec[c - csrAddrMHPMEVENT3.addr]; if ((csrAddrHPMCOUNTER3.addr <= c) && (c <= csrAddrHPMCOUNTER31.addr)) ret = perf_counters.counter_vec[c-csrAddrHPMCOUNTER3.addr]; // `endif return (case (csr) // User CSRs csrAddrFFLAGS: fflags_csr; csrAddrFRM: frm_csr; csrAddrFCSR: fcsr_csr; csrAddrCYCLE: cycle_csr; csrAddrTIME: time_csr; csrAddrINSTRET: instret_csr; csrAddrTERMINATE: terminate_csr; csrAddrSTATS: stats_csr; // Supervisor CSRs csrAddrSSTATUS: sstatus_csr; csrAddrSIE: sie_csr; csrAddrSTVEC: scrToCsr(stcc_reg); // Only accessed by debugger. CPU accesses decoded into cspecialrw csrAddrSCOUNTEREN: scounteren_csr; csrAddrSSCRATCH: sscratch_csr; csrAddrSEPC: scrToCsr(sepcc_reg[1]); // Only accessed by debugger. CPU accesses decoded into cspecialrw csrAddrSCAUSE: scause_csr; csrAddrSTVAL: stval_csr; csrAddrSIP: sip_csr; csrAddrSATP: satp_csr; csrAddrSCCSR: sccsr_csr; // Machine CSRs csrAddrMSTATUS: mstatus_csr; csrAddrMISA: misa_csr; csrAddrMEDELEG: medeleg_csr; csrAddrMIDELEG: mideleg_csr; csrAddrMIE: mie_csr; csrAddrMTVEC: scrToCsr(mtcc_reg); // Only accessed by debugger. CPU accesses decoded into cspecialrw csrAddrMCOUNTEREN: mcounteren_csr; csrAddrMSCRATCH: mscratch_csr; csrAddrMEPC: scrToCsr(mepcc_reg[1]); // Only accessed by debugger. CPU accesses decoded into cspecialrw csrAddrMCAUSE: mcause_csr; csrAddrMTVAL: mtval_csr; csrAddrMIP: mip_csr; csrAddrMCYCLE: mcycle_csr; csrAddrMINSTRET: minstret_csr; csrAddrMVENDORID: mvendorid_csr; csrAddrMARCHID: marchid_csr; csrAddrMIMPID: mimpid_csr; csrAddrMHARTID: mhartid_csr; csrAddrMCCSR: mccsr_csr; // `ifdef PERFORMANCE_MONITORING //csrAddrMCOUNTERINHIBIT: perf_counters.inhibit; csrAddrMCOUNTERINHIBIT: mcountinhibit_reg; // `endif `ifdef SECURITY csrAddrMEVBASE: mevbase_csr; csrAddrMEVMASK: mevmask_csr; csrAddrMEATP: meatp_csr; csrAddrMMRBM: mmrbm_csr; csrAddrMEMRBM: memrbm_csr; csrAddrMPARBASE: mparbase_csr; csrAddrMPARMASK: mparmask_csr; csrAddrMEPARBASE: meparbase_csr; csrAddrMEPARMASK: meparmask_csr; csrAddrMSPEC: mspec_csr; csrAddrTRNG: trng_csr; `endif csrAddrTSELECT: rg_tselect; csrAddrTDATA1: rg_tdata1; csrAddrTDATA2: rg_tdata2; csrAddrTDATA3: rg_tdata3; `ifdef INCLUDE_GDB_CONTROL csrAddrDCSR: rg_dcsr; // TODO: take NMI into account (cf. Piccolo/Flute) csrAddrDPC: scrToCsr(rg_dpc); csrAddrDSCRATCH0: rg_dscratch0; csrAddrDSCRATCH1: rg_dscratch1; `endif default: ret; endcase); endfunction // Function for getting a csr given an index function Reg#(CapReg) get_scr(SCR scr); return (case (scr) // User SCRs scrAddrDDC: ddc_reg; // User CSRs with accessSysRegs // scrAddrUTCC: utcc_reg; // scrAddrUTDC: utdc_reg; // scrAddrUScratchC: uScratchC_reg; // scrAddrUEPCC: uepcc_reg; // System CSRs with accessSysRegs scrAddrSTCC: stcc_reg; scrAddrSTDC: stdc_reg; scrAddrSScratchC: sScratchC_reg; scrAddrSEPCC: sepcc_reg[1]; // Machine CSRs with accessSysRegs scrAddrMTCC: mtcc_reg; scrAddrMTDC: mtdc_reg; scrAddrMScratchC: mScratchC_reg; scrAddrMEPCC: mepcc_reg[1]; endcase); endfunction // ================================================================ // This function is the WARL (Write Any Read Legal) transform // performed during CSR writes. Currently it duplicates the logic // in the _write method of CSRs; ideally this function should be // separate from the _write method, which should remain as an // ordinary _write. The WARL'd value is needed for Tandem // Verification. function Data fv_warl_xform (CSR csr, Data x); Asid x_asid = truncate (x [59:44]); Bit #(16) asid = zeroExtend (x_asid); return ( case (csr) // Machine CSRs csrAddrMISA: {getXLBits, 36'b0, getExtensionBits(isa)}; csrAddrMVENDORID: 0; csrAddrMARCHID: 0; csrAddrMIMPID: 0; csrAddrMHARTID: hartid; csrAddrMSTATUS: fn_mstatus_val (getXLBits, // sxl getXLBits, // uxl x [22], // tsr x [21], // tw x [20], // tvm x [19], // mxr x [18], // sum x [17], // mprv 2'b0, // xs ((isa.f || isa.d) ? x [14:13] : 2'b0), // fs x [12:11], // mpp x [8], // spp x [7], // prev_ie_vec[prvM] x [5], // prev_ie_vec[prvS] x [4], // prev_ie_vec[prvU] x [3], // ie_vec[prvM] x [1], // ie_vec[prvS] x [0]); // ie_vec[prvU] csrAddrMEDELEG: { 35'b0, x[28:26], 10'b0, x[15], 1'b0, x[13:12], x[11], 1'b0, x[9:0]}; csrAddrMIDELEG: { 52'b0, x[11], 1'b0, x[9:8], x[7], 1'b0, x[5:4], x[3], 1'b0, x[1:0]}; csrAddrMIP: ((mip_csr & (~ mip_mie_warl_mask)) | (x & mip_mie_warl_mask)); csrAddrMIE: (x & mip_mie_warl_mask); csrAddrMCOUNTEREN: { 61'b0, x[2:0]}; csrAddrMCAUSE: { x[63], 59'b0, x[3:0] }; csrAddrTDATA1: { 4'b0, x [59:0] }; // Force tdata.type == 0 ("no trigger at this tselect") // Supervisor level CSRs csrAddrSSTATUS: fn_sstatus_val (getXLBits, // uxl x [19], // mxr x [18], // sum 2'b0, // xs ((isa.f || isa.d) ? x [14:13] : 2'b0), // fs x [8], // spp x [5], // prev_ie_vec[prvS] x [4], // prev_ie_vec[prvU] x [1], // ie_vec[prvS] x [0]); // ie_vec[prvU] csrAddrSIP: ((sip_csr & (~ sip_sie_warl_mask)) | (x & sip_sie_warl_mask)); csrAddrSIE: (x & sip_sie_warl_mask); csrAddrSCOUNTEREN: { 61'b0, x[2:0]}; csrAddrSCAUSE: { x[63], 59'b0, x[3:0] }; csrAddrSATP: { x[63], 3'b0, asid, x [43:0] }; // User level CSRs csrAddrFFLAGS: { 59'b0, x [4:0] }; csrAddrFRM: { 61'b0, x [2:0] }; csrAddrFCSR: { 56'b0, x [7:0] }; `ifdef INCLUDE_GDB_CONTROL // Debug Mode CSRs csrAddrDCSR: { 32'b0, x[31:28], 12'b0, x[14], 1'b0, x[13:6], 1'b0, x[4:0] }; `endif default: x; endcase); endfunction // ================================================================ // INTERFACE method Data rd(CSR csr) `ifdef INCLUDE_GDB_CONTROL `ifdef BSIM if (rg_dpc_is_init) `endif `endif ; return get_csr(csr)._read; endmethod method CapReg scrRd(SCR scr); return get_scr(scr)._read; endmethod method Action csrInstWr(CSR csr, Data x) `ifdef INCLUDE_GDB_CONTROL `ifdef BSIM if (rg_dpc_is_init) `endif `endif ; get_csr(csr)._write(x); `ifdef INCLUDE_GDB_CONTROL if (csr == csrAddrDCSR) begin let prv = x [1:0]; prv_reg <= prv; end `endif endmethod method Action scrInstWr(SCR csr, CapReg x); get_scr(csr)._write(x); endmethod method Bool fpuInstNeedWr(Bit#(5) fflags, Bool fpu_dirty); Bool fflags_change = (fflags & fflags_reg) != fflags; // we need to set fs_reg as dirty in two cases // 1. FP reg is written (i.e., fpu_dirty) // 2. FP exception (i.e., fflags) is non-zero (try to match spike) Bool need_set_dirty = fs_reg != 2'b11 && (fpu_dirty || fflags != 0); return fflags_change || need_set_dirty; endmethod method Action fpuInstWr(Bit#(5) fflags); fs_reg <= 2'b11; // FPU must be dirty fflags_reg <= fflags_reg | fflags; endmethod `ifdef INCLUDE_TANDEM_VERIF method Tuple2 #(Bit #(5), Data) fpuInst_csr_updates (Bit #(5) fflags, Bool init_for_way0, Bit #(5) old_fflags, Data old_mstatus); // Note: old_fflags and old_mstatus are accumulated in // sequential program order, and so may differ from fflags_reg // and mstatus_csr, which only change after superscalar-wide // retirement. Bit #(5) old_fflags1 = (init_for_way0 ? fflags_reg : old_fflags); Data old_mstatus1 = (init_for_way0 ? mstatus_csr : old_mstatus); Bit #(5) new_fflags = (old_fflags1 | fflags); Data new_mstatus = { 1'b1, old_mstatus1 [62:15], 2'b11, old_mstatus1 [12:0] }; return tuple2 (new_fflags, new_mstatus); endmethod method Data getMIP; return mip_csr; endmethod `endif method Data warl_xform (CSR csr, Data x); return fv_warl_xform (csr, x); endmethod method Maybe#(Interrupt) pending_interrupt; // first get all the pending interrupts Bit#(InterruptNum) pend_ints = truncate(mie_csr & mip_csr); // now find out all the truly enabled interrupts (that needs handling) Bit#(InterruptNum) enabled_ints = 0; // check interrupts that needs to be handled at M mode: all interrupts // are by default handled at M mode unless it is delegated in // mideleg_csr, we just need to ignore those interrupts if(prv_reg < prvM || (prv_reg == prvM && ie_vec[prvM] == 1)) begin enabled_ints = pend_ints & ~truncate(mideleg_csr); end // check interrupts that needs to be handled at S mode only if no // interrupt needs to be handled at M mode: interrupts handled at S // mode must be delegated in mideleg_csr if (enabled_ints == 0 && (prv_reg < prvS || (prv_reg == prvS && ie_vec[prvS] == 1))) begin enabled_ints = pend_ints & truncate(mideleg_csr); end // According to spike, return the interrupt bit at LSB function Bool isEnabled(Integer i) = (enabled_ints[i] == 1); Vector#(InterruptNum, Integer) idxVec = genVector; if(find(isEnabled, idxVec) matches tagged Valid .i) begin return Valid (unpack(fromInteger(i))); end else begin return Invalid; end endmethod method ActionValue#(Trap_Updates) trap(Trap t, CapPipe pcc, Addr addr, Bit #(32) orig_inst); // figure out trap cause & trap val Bit#(1) cause_interrupt = 0; Cause cause_code = 0; Data trap_val = 0; case(t) matches tagged Exception .e: begin cause_code = pack(e); trap_val = (case(e) excIllegalInst: zeroExtend (orig_inst); excInstAddrMisaligned, excBreakpoint: return getOffset(pcc); // TODO do we want getAddr? excInstAccessFault, excInstPageFault, excLoadAddrMisaligned, excLoadAccessFault, excStoreAddrMisaligned, excStoreAccessFault, excLoadPageFault, excStorePageFault, excLoadCapPageFault, excStoreCapPageFault: return addr; default: return 0; endcase); end tagged CapException .ce: begin cause_code = pack(excCHERIFault); trap_val = zeroExtend({pack(ce.cheri_exc_reg), pack(ce.cheri_exc_code)}); end tagged Interrupt .i: begin cause_code = zeroExtend(pack(i)); cause_interrupt = 1; end endcase // function to figure out next PC function CapPipe getNextPcc(CapPipe tcc); let tvec = getAddr(tcc); // Note this is not actually mtcc: addr rather than offset let mode_low = tvec[0]; // Valid because bottom 2 bits of base must be zero (enforced on write) // tvec[1] must be 1 here. let base_hi = tvec[63:2]; Addr base = {base_hi, 2'b0}; if(mode_low == 1 && cause_interrupt == 1) begin // vector jump: only for interrupt return setAddr(tcc, base + zeroExtend({cause_code, 2'b0})).value; end else begin // direct jump return setAddr(tcc, base).value; end endfunction // check if trap is delegated Bool deleg = prv_reg <= prvS && (case(t) matches tagged Exception .e: return medeleg_csr[pack(e)] == 1; tagged CapException .ce: return medeleg_csr[pack(excCHERIFault)] == 1; tagged Interrupt .i: return mideleg_csr[pack(i)] == 1; endcase); // handle the trap if(deleg) begin // handle in S mode // ie/prv stack prev_prv_vec[prvS] <= prv_reg; prv_reg <= prvS; prev_ie_vec[prvS] <= ie_vec[prvS]; ie_vec[prvS] <= 0; // record trap info sepcc_reg[0] <= cast(pcc); scause_interrupt_reg <= cause_interrupt; scause_code_reg <= cause_code; stval_csr <= trap_val; // return next pc Data sstatus_val = fn_sstatus_val (uxl_reg, mxr_reg, sum_reg, xs_reg, fs_reg, /* spp_reg */ prv_reg [0], /* prev_ie_vec_[prvS] */ ie_vec[prvS], prev_ie_vec [prvU], /* ie_vec [prvS] */ 0, ie_vec [prvU]); Data scause_val = fn_scause_val (cause_interrupt, cause_code); return Trap_Updates {new_pcc: getNextPcc(cast(stcc_reg)) `ifdef INCLUDE_TANDEM_VERIF , prv: prvS, status: sstatus_val, cause: scause_val, epcc: pcc, tval: trap_val `endif }; end else begin // ie/prv stack prev_prv_vec[prvM] <= prv_reg; prv_reg <= prvM; prev_ie_vec[prvM] <= ie_vec[prvM]; ie_vec[prvM] <= 0; // record trap info mepcc_reg[0] <= cast(pcc); mcause_interrupt_reg <= cause_interrupt; mcause_code_reg <= cause_code; mtval_csr <= trap_val; // return next pc Data mstatus_val = fn_mstatus_val (sxl_reg, uxl_reg, tsr_reg, tw_reg, tvm_reg, mxr_reg, sum_reg, mprv_reg, xs_reg, fs_reg, /* mpp */ prv_reg, spp_reg, /* prev_ie_vec [prvM] */ ie_vec [prvM], prev_ie_vec [prvS], prev_ie_vec [prvU], /* ie_vec [prvM] */ 0, ie_vec [prvS], ie_vec [prvU]); Data mcause_val = fn_mcause_val (cause_interrupt, cause_code); return Trap_Updates {new_pcc: getNextPcc(cast(mtcc_reg)) `ifdef INCLUDE_TANDEM_VERIF , prv: prvM, status: mstatus_val, cause: mcause_val, epcc: pcc, tval: trap_val `endif }; end // XXX yield load reservation should be done outside this method endmethod method ActionValue#(RET_Updates) mret; prv_reg <= prev_prv_vec[prvM]; prev_prv_vec[prvM] <= prvU; ie_vec[prvM] <= prev_ie_vec[prvM]; prev_ie_vec[prvM] <= 1; Data mstatus_val = fn_mstatus_val(sxl_reg, uxl_reg, tsr_reg, tw_reg, tvm_reg, mxr_reg, sum_reg, mprv_reg, xs_reg, fs_reg, /* mpp */ prvU, spp_reg, /* prev_ie_vec [prvM] */ 1, prev_ie_vec [prvS], prev_ie_vec [prvU], /* ie_vec [prvM] */ prev_ie_vec[prvM], ie_vec [prvS], ie_vec [prvU]); return RET_Updates {new_pcc: cast(setKind(mepcc_reg[0], getKind(mepcc_reg[0]) == SENTRY ? UNSEALED : getKind(mepcc_reg[0]))) `ifdef INCLUDE_TANDEM_VERIF , prv: prev_prv_vec[prvM], status: mstatus_val `endif }; endmethod method ActionValue#(RET_Updates) sret; prv_reg <= prev_prv_vec[prvS]; prev_prv_vec[prvS] <= prvU; ie_vec[prvS] <= prev_ie_vec[prvS]; prev_ie_vec[prvS] <= 1; // For Tandem Verification, we return the full underlying MSTATUS register Data mstatus_val = fn_mstatus_val(sxl_reg, uxl_reg, tsr_reg, tw_reg, tvm_reg, mxr_reg, sum_reg, mprv_reg, xs_reg, fs_reg, mpp_reg, /* spp_reg */ prvU [0], prev_ie_vec [prvM], /* prev_ie_vec_[prvS] */ 1, prev_ie_vec [prvU], ie_vec [prvM], /* ie_vec [prvS] */ prev_ie_vec[prvS], ie_vec [prvU]); return RET_Updates {new_pcc: cast(setKind(sepcc_reg[0], getKind(sepcc_reg[0]) == SENTRY ? UNSEALED : getKind(sepcc_reg[0]))) `ifdef INCLUDE_TANDEM_VERIF , prv: prev_prv_vec[prvS], status: mstatus_val `endif }; endmethod method VMInfo vmI; // for inst fetch, NO need to consider MPRV Bit#(2) prv = prv_reg; return VMInfo { prv: prv, asid: asid_reg, sv39: prv < prvM && vm_mode_sv39_reg == 1, exeReadable: mxr_reg == 1, userAccessibleByS: sum_reg == 1, basePPN: ppn_reg, globalCapLoadGenU: global_cap_load_gen_u_reg, globalCapLoadGenS: global_cap_load_gen_s_reg `ifdef SECURITY , sanctum_evbase: mevbase_csr, sanctum_evmask: mevmask_csr, sanctum_ebasePPN: eppn_reg, sanctum_mrbm: mmrbm_csr, sanctum_emrbm: memrbm_csr, sanctum_parbase: mparbase_csr, sanctum_parmask: mparmask_csr, sanctum_eparbase: meparbase_csr, sanctum_eparmask: meparmask_csr, // enclave / security monitor should never execute instructions // from untrusted shared region sanctum_authShared: False `endif }; endmethod method VMInfo vmD; // for load/store, need to consider MPRV Bit#(2) prv = (mprv_reg == 1) ? prev_prv_vec[prvM] : prv_reg; return VMInfo { prv: prv, asid: asid_reg, sv39: prv < prvM && vm_mode_sv39_reg == 1, exeReadable: mxr_reg == 1, userAccessibleByS: sum_reg == 1, basePPN: ppn_reg, globalCapLoadGenU: global_cap_load_gen_u_reg, globalCapLoadGenS: global_cap_load_gen_s_reg `ifdef SECURITY , sanctum_evbase: mevbase_csr, sanctum_evmask: mevmask_csr, sanctum_ebasePPN: eppn_reg, sanctum_mrbm: mmrbm_csr, sanctum_emrbm: memrbm_csr, sanctum_parbase: mparbase_csr, sanctum_parmask: mparmask_csr, sanctum_eparbase: meparbase_csr, sanctum_eparmask: meparmask_csr, // enclave / security monitor can read/write untrusted shared // region when speculation is off (either by mspec CSR or in M // mode) // XXX Because of the effects of mprv, we have to use prv_reg here // instead of prv. Otherwise, we may be in M mode, but prv=S, and // still forbid shared accesses sanctum_authShared: mspec_reg != mSpecAll || prv_reg == prvM `endif }; endmethod method CsrDecodeInfo decodeInfo = CsrDecodeInfo { frm: frm_reg, fEnabled: fs_reg != 0, prv: prv_reg, trapVM: tvm_reg == 1, timeoutWait: tw_reg == 1, trapSret: tsr_reg == 1, cycleReadableByS: mcounteren_cy_reg == 1, cycleReadableByU: mcounteren_cy_reg == 1 && scounteren_cy_reg == 1, instretReadableByS: mcounteren_ir_reg == 1, instretReadableByU: mcounteren_ir_reg == 1 && scounteren_ir_reg == 1, timeReadableByS: mcounteren_tm_reg == 1, timeReadableByU: mcounteren_tm_reg == 1 && scounteren_tm_reg == 1 }; method Action incInstret(SupCnt x); // `ifdef PERFORMANCE_MONITORING if(!unpack(mcountinhibit_ir_reg)) minstret_ehr[1] <= minstret_ehr[1] + zeroExtend(x); // `else // minstret_ehr[1] <= minstret_ehr[1] + zeroExtend(x); // `endif endmethod method Action setTime(Data t); time_reg <= t; endmethod method getMSIP = software_int_pend_vec[prvM]._read; method setMSIP = software_int_pend_vec[prvM]._write; method setMTIP = timer_int_pend_vec[prvM]._write; // Bluespec: external interrupts targeting machine and supervisor modes method Action setMEIP (Bit #(1) v); external_int_pend_vec[prvM] <= v; endmethod method Action setSEIP (Bit #(1) v); external_int_pend_vec[prvS] <= v; endmethod method terminate = terminate_module.terminate; // performance stats method doPerfStats = stats_module.doPerfStats; method sendDoStats = stats_module.sendDoStats; method recvDoStats = stats_module.recvDoStats; // ---------------- // Bluespec: // Methods when Debug Module is present `ifdef INCLUDE_GDB_CONTROL // Read dpc method CapReg dpc_read `ifdef BSIM if (rg_dpc_is_init) `endif ; return rg_dpc; endmethod // Update dpc method Action dpc_write (CapReg pc); rg_dpc <= pc; endmethod // Check whether to enter Debug Mode based on dcsr.{ebreakm, ebreaks, ebreaku} method Bit #(1) dcsr_break_bit; return case (prv_reg) prvM: rg_dcsr [15]; prvS: rg_dcsr [13]; prvU: rg_dcsr [12]; endcase; endmethod // Check whether to enter Debug Mode based on dcsr.step method Bit #(1) dcsr_step_bit; return rg_dcsr [2]; endmethod // Update 'cause' in DCSR // Is invoked by logic that stops a hart, to enter Debug Mode method Action dcsr_cause_write (Bit #(3) dcsr_cause); rg_dcsr <= { 32'b0, rg_dcsr [31:9], dcsr_cause, rg_dcsr [5:2], prv_reg }; /* $display ("%0d: %m mkCsrFile.method-dcsr_cause_write: cause %0d, prv %0d", cur_cycle, dcsr_cause, prv_reg); */ endmethod `endif // `ifdef PERFORMANCE_MONITORING method send_performance_events = perf_counters.send_performance_events; // `endif endmodule