After a test, GDB can write DCSR to restore to Machine privilege, write to PC (DPC) to restore boot value, write MSTATUS to restore to initial value, then can load and run next test.
987 lines
35 KiB
Plaintext
987 lines
35 KiB
Plaintext
|
|
// Copyright (c) 2017 Massachusetts Institute of Technology
|
|
// Portions Copyright (c) 2019-2020 Bluespec, Inc.
|
|
//
|
|
// 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 Fifo::*;
|
|
import Vector::*;
|
|
import FIFO::*;
|
|
import GetPut::*;
|
|
import BuildVector::*;
|
|
//import TRNG::*;
|
|
|
|
// ================================================================
|
|
// BSV additional libs
|
|
|
|
import Cur_Cycle :: *;
|
|
|
|
// ================================================================
|
|
// Project imports from Toooba
|
|
|
|
import SoC_Map :: *;
|
|
|
|
// ================================================================
|
|
|
|
interface CsrFile;
|
|
// Initialize to platform-level reset spec
|
|
method Action init;
|
|
|
|
// Read
|
|
method Data rd(CSR csr);
|
|
// normal write by CSRXXX inst to any CSR
|
|
method Action csrInstWr(CSR csr, Data 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
|
|
|
|
// Methods for handling traps
|
|
method Maybe#(Interrupt) pending_interrupt;
|
|
method ActionValue#(Addr) trap(Trap t, Addr pc, Addr faultAddr);
|
|
method ActionValue#(Addr) sret;
|
|
method ActionValue#(Addr) 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 Addr dpc_read ();
|
|
|
|
// Update dpc
|
|
method Action dpc_write (Addr 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
|
|
|
|
// 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(0) : mkReadOnlyReg(0);
|
|
Reg#(Bit#(1)) sd_reg = readOnlyReg(
|
|
((xs_reg == 2'b11) || (fs_reg == 2'b11)) ? 1 : 0
|
|
);
|
|
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]
|
|
);
|
|
// misa
|
|
Reg#(Data) misa_csr = readOnlyReg({getXLBits, 36'b0, getExtensionBits(isa)});
|
|
// medeleg: some exceptions don't exist, fix corresponding bits to 0
|
|
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 = concatReg6(
|
|
readOnlyReg(48'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], external_int_en_vec[prvU],
|
|
timer_int_en_vec[prvM], readOnlyReg(1'b0),
|
|
timer_int_en_vec[prvS], timer_int_en_vec[prvU],
|
|
software_int_en_vec[prvM], readOnlyReg(1'b0),
|
|
software_int_en_vec[prvS], software_int_en_vec[prvU]
|
|
);
|
|
// mtvec
|
|
Reg#(Bit#(62)) mtvec_base_hi_reg <- mkCsrReg(0); // this is BASE[63:2]
|
|
Reg#(Bit#(1)) mtvec_mode_low_reg <- mkCsrReg(0); // this is MODE[0]
|
|
Reg#(Data) mtvec_csr = concatReg3(
|
|
mtvec_base_hi_reg, readOnlyReg(1'b0), mtvec_mode_low_reg
|
|
);
|
|
// 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);
|
|
// mepc: FIXME Since we don't have C extension, mepc should be 4-byte
|
|
// aligned. However, spike is not checking this, so we don't implement it.
|
|
Reg#(Data) mepc_csr <- mkCsrReg(0);
|
|
// mcause
|
|
Reg#(Bit#(1)) mcause_interrupt_reg <- mkCsrReg(0);
|
|
Reg#(Bit#(4)) mcause_code_reg <- mkCsrReg(0);
|
|
Reg#(Data) mcause_csr = concatReg3(
|
|
mcause_interrupt_reg, readOnlyReg(59'b0), mcause_code_reg
|
|
);
|
|
// 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);
|
|
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);
|
|
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);
|
|
Reg#(Data) mip_csr = concatReg13(
|
|
readOnlyReg(52'b0),
|
|
external_int_pend_vec[prvM], readOnlyReg(1'b0),
|
|
external_int_pend_vec[prvS], external_int_pend_vec[prvU],
|
|
readOnlyReg(timer_int_pend_vec[prvM]), // MTIP is read-only to software
|
|
readOnlyReg(1'b0),
|
|
timer_int_pend_vec[prvS], timer_int_pend_vec[prvU],
|
|
software_int_pend_vec[prvM], readOnlyReg(1'b0),
|
|
software_int_pend_vec[prvS], software_int_pend_vec[prvU]
|
|
);
|
|
// 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]
|
|
);
|
|
// sie: restricted view of mie
|
|
Reg#(Data) sie_csr = concatReg9(
|
|
readOnlyReg(54'b0),
|
|
external_int_en_vec[prvS], external_int_en_vec[prvU],
|
|
readOnlyReg(2'b0),
|
|
timer_int_en_vec[prvS], timer_int_en_vec[prvU],
|
|
readOnlyReg(2'b0),
|
|
software_int_en_vec[prvS], software_int_en_vec[prvU]
|
|
);
|
|
// stvec
|
|
Reg#(Bit#(62)) stvec_base_hi_reg <- mkCsrReg(0); // BASE[63:2]
|
|
Reg#(Bit#(1)) stvec_mode_low_reg <- mkCsrReg(0); // MODE[0]
|
|
Reg#(Data) stvec_csr = concatReg3(
|
|
stvec_base_hi_reg, readOnlyReg(1'b0), stvec_mode_low_reg
|
|
);
|
|
// 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);
|
|
// sepc: FIXME Since we don't have C extension, sepc should be 4-byte
|
|
// aligned. However, spike is not checking this, so we don't implement it.
|
|
Reg#(Data) sepc_csr <- mkCsrReg(0);
|
|
// scause
|
|
Reg#(Bit#(1)) scause_interrupt_reg <- mkCsrReg(0);
|
|
Reg#(Bit#(4)) scause_code_reg <- mkCsrReg(0);
|
|
Reg#(Data) scause_csr = concatReg3(
|
|
scause_interrupt_reg, readOnlyReg(59'b0), scause_code_reg
|
|
);
|
|
// 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], external_int_pend_vec[prvU],
|
|
readOnlyReg(2'b0),
|
|
timer_int_pend_vec[prvS], timer_int_pend_vec[prvU],
|
|
readOnlyReg(2'b0),
|
|
software_int_pend_vec[prvS], software_int_pend_vec[prvU]
|
|
);
|
|
// 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;
|
|
|
|
`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:7] 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 #(Data) rg_dpc <- mkConfigReg (truncate (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
|
|
|
|
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
|
|
CSRfflags: fflags_csr;
|
|
CSRfrm: frm_csr;
|
|
CSRfcsr: fcsr_csr;
|
|
CSRcycle: cycle_csr;
|
|
CSRtime: time_csr;
|
|
CSRinstret: instret_csr;
|
|
CSRterminate: terminate_csr;
|
|
CSRstats: stats_csr;
|
|
// Supervisor CSRs
|
|
CSRsstatus: sstatus_csr;
|
|
CSRsie: sie_csr;
|
|
CSRstvec: stvec_csr;
|
|
CSRscounteren: scounteren_csr;
|
|
CSRsscratch: sscratch_csr;
|
|
CSRsepc: sepc_csr;
|
|
CSRscause: scause_csr;
|
|
CSRstval: stval_csr;
|
|
CSRsip: sip_csr;
|
|
CSRsatp: satp_csr;
|
|
// Machine CSRs
|
|
CSRmstatus: mstatus_csr;
|
|
CSRmisa: misa_csr;
|
|
CSRmedeleg: medeleg_csr;
|
|
CSRmideleg: mideleg_csr;
|
|
CSRmie: mie_csr;
|
|
CSRmtvec: mtvec_csr;
|
|
CSRmcounteren: mcounteren_csr;
|
|
CSRmscratch: mscratch_csr;
|
|
CSRmepc: mepc_csr;
|
|
CSRmcause: mcause_csr;
|
|
CSRmtval: mtval_csr;
|
|
CSRmip: mip_csr;
|
|
CSRmcycle: mcycle_csr;
|
|
CSRminstret: minstret_csr;
|
|
CSRmvendorid: mvendorid_csr;
|
|
CSRmarchid: marchid_csr;
|
|
CSRmimpid: mimpid_csr;
|
|
CSRmhartid: mhartid_csr;
|
|
`ifdef SECURITY
|
|
CSRmevbase: mevbase_csr;
|
|
CSRmevmask: mevmask_csr;
|
|
CSRmeatp: meatp_csr;
|
|
CSRmmrbm: mmrbm_csr;
|
|
CSRmemrbm: memrbm_csr;
|
|
CSRmparbase: mparbase_csr;
|
|
CSRmparmask: mparmask_csr;
|
|
CSRmeparbase: meparbase_csr;
|
|
CSRmeparmask: meparmask_csr;
|
|
CSRmspec: mspec_csr;
|
|
CSRtrng: trng_csr;
|
|
`endif
|
|
|
|
`ifdef INCLUDE_GDB_CONTROL
|
|
CSRdcsr: rg_dcsr; // TODO: take NMI into account (cf. Piccolo/Flute)
|
|
CSRdpc: rg_dpc;
|
|
CSRdscratch0: rg_dscratch0;
|
|
CSRdscratch1: rg_dscratch1;
|
|
`endif
|
|
|
|
default: readOnlyReg(64'b0);
|
|
endcase);
|
|
endfunction
|
|
|
|
// ================================================================
|
|
// INTERFACE
|
|
|
|
method Action init;
|
|
// Note: we initialize only certain CSRs (platform-level spec)
|
|
// Current privilege
|
|
prv_reg <= prvM;
|
|
|
|
// Machine-level CSRs
|
|
mstatus_csr <= 0;
|
|
mie_csr <= 0;
|
|
mip_csr <= 0;
|
|
minstret_csr <= 0;
|
|
mcycle_csr <= 0;
|
|
|
|
// User-level CSRs
|
|
time_reg <= 0;
|
|
|
|
`ifdef INCLUDE_GDB_CONTROL
|
|
// Debug Module CSRs
|
|
rg_dcsr <= zeroExtend (dcsr_reset_value);
|
|
rg_dpc <= truncate (soc_map_struct.pc_reset_value);
|
|
`endif
|
|
endmethod
|
|
|
|
method Data rd(CSR csr);
|
|
return get_csr(csr)._read;
|
|
endmethod
|
|
|
|
method Action csrInstWr(CSR csr, Data x);
|
|
get_csr(csr)._write(x);
|
|
`ifdef INCLUDE_GDB_CONTROL
|
|
if (csr == CSRdcsr) begin
|
|
let prv = x [1:0];
|
|
prv_reg <= prv;
|
|
end
|
|
`endif
|
|
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
|
|
|
|
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#(Addr) trap(Trap t, Addr pc, Addr addr);
|
|
// figure out trap cause & trap val
|
|
Bit#(1) cause_interrupt = 0;
|
|
Bit#(4) cause_code = 0;
|
|
Data trap_val = 0;
|
|
case(t) matches
|
|
tagged Exception .e: begin
|
|
cause_code = pack(e);
|
|
trap_val = (case(e)
|
|
InstAddrMisaligned, Breakpoint: return pc;
|
|
|
|
InstAccessFault, InstPageFault,
|
|
LoadAddrMisaligned, LoadAccessFault,
|
|
StoreAddrMisaligned, StoreAccessFault,
|
|
LoadPageFault, StorePageFault: return addr;
|
|
|
|
default: return 0;
|
|
endcase);
|
|
end
|
|
tagged Interrupt .i: begin
|
|
cause_code = pack(i);
|
|
cause_interrupt = 1;
|
|
end
|
|
endcase
|
|
// function to figure out next PC
|
|
function Addr getNextPc(Bit#(1) mode_low, Bit#(62) base_hi);
|
|
Addr base = {base_hi, 2'b0};
|
|
if(mode_low == 1 && cause_interrupt == 1) begin
|
|
// vector jump: only for interrupt
|
|
return base + zeroExtend({cause_code, 2'b0});
|
|
end
|
|
else begin // direct jump
|
|
return base;
|
|
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 Interrupt .i: return mideleg_csr[pack(i)] == 1;
|
|
default: return False;
|
|
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
|
|
sepc_csr <= pc;
|
|
scause_interrupt_reg <= cause_interrupt;
|
|
scause_code_reg <= cause_code;
|
|
stval_csr <= trap_val;
|
|
// return next pc
|
|
return getNextPc(stvec_mode_low_reg, stvec_base_hi_reg);
|
|
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
|
|
mepc_csr <= pc;
|
|
mcause_interrupt_reg <= cause_interrupt;
|
|
mcause_code_reg <= cause_code;
|
|
mtval_csr <= trap_val;
|
|
// return next pc
|
|
return getNextPc(mtvec_mode_low_reg, mtvec_base_hi_reg);
|
|
end
|
|
// XXX yield load reservation should be done outside this method
|
|
endmethod
|
|
|
|
method ActionValue#(Addr) mret;
|
|
prv_reg <= prev_prv_vec[prvM];
|
|
prev_prv_vec[prvM] <= prvU;
|
|
ie_vec[prvM] <= prev_ie_vec[prvM];
|
|
prev_ie_vec[prvM] <= 1;
|
|
return mepc_csr;
|
|
endmethod
|
|
|
|
method ActionValue#(Addr) sret;
|
|
prv_reg <= prev_prv_vec[prvS];
|
|
prev_prv_vec[prvS] <= prvU;
|
|
ie_vec[prvS] <= prev_ie_vec[prvS];
|
|
prev_ie_vec[prvS] <= 1;
|
|
return sepc_csr;
|
|
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 Addr dpc_read ();
|
|
return rg_dpc;
|
|
endmethod
|
|
|
|
// Update dpc
|
|
method Action dpc_write (Addr 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
|