1454 lines
58 KiB
Plaintext
1454 lines
58 KiB
Plaintext
|
|
// 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(False);
|
|
|
|
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(setOffset(scr_pipe, x).value); endaction;
|
|
method Data _read;
|
|
CapPipe scr_pipe = cast(scr);
|
|
return getOffset(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));
|
|
Reg #(CapReg) rg_dpc <- mkConfigReg (setAddrUnsafe(almightyCap,soc_map_struct.pc_reset_value));
|
|
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);
|
|
return get_csr(csr)._read;
|
|
endmethod
|
|
|
|
method CapReg scrRd(SCR scr);
|
|
return get_scr(scr)._read;
|
|
endmethod
|
|
|
|
method Action csrInstWr(CSR csr, Data x);
|
|
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 ();
|
|
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
|