BSC does not play nicely with enums whose labels do not start at 0 and increase linearly. Instead, in such cases, it generates a whole bunch of conditions to "legalise" any read values, which causes an explosion of logic in places like the ROB. Thus, use this ugly (but still typed) alternative that, other than naming conventions enforced by BSC, looks almost the same as an enum.
1345 lines
53 KiB
Plaintext
1345 lines
53 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 :: *;
|
|
|
|
// ================================================================
|
|
// 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
|
|
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
|
|
|
|
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 not 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);
|
|
// 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 not 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);
|
|
// 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;
|
|
// Capability cause register
|
|
Reg #(CapException) mccsr_reg <- mkCsrReg(unpack(0));
|
|
|
|
`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 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);
|
|
|
|
rule incCycle;
|
|
mcycle_ehr[1] <= mcycle_ehr[1] + 1;
|
|
endrule
|
|
|
|
// Function for getting a csr given an index
|
|
function Reg#(Data) get_csr(CSR csr);
|
|
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;
|
|
// 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: csr_capcause(mccsr_reg);
|
|
`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: readOnlyReg(64'b0);
|
|
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: 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
|
|
`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
|
|
`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);
|
|
minstret_ehr[1] <= minstret_ehr[1] + zeroExtend(x);
|
|
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
|
|
|
|
endmodule
|