385 lines
14 KiB
Plaintext
385 lines
14 KiB
Plaintext
import Cntrs::*;
|
|
import GetPut::*;
|
|
import ReadOnly::*;
|
|
import Trap::*;
|
|
|
|
import IsaCfg::*;
|
|
import MachineInformation::*;
|
|
import MachineISA::*;
|
|
import MachineStatus::*;
|
|
import RV_ISA::*;
|
|
|
|
typedef struct {
|
|
Bit#(xlen) value;
|
|
Bool denied;
|
|
} CsrReadResult#(numeric type xlen) deriving(Bits, Eq, FShow);
|
|
|
|
typedef struct {
|
|
Bool denied;
|
|
} CsrWriteResult deriving(Bits, Eq, FShow);
|
|
|
|
interface CsrReadPort#(numeric type xlen);
|
|
method ActionValue#(CsrReadResult#(xlen)) read(RVCSRIndex index);
|
|
endinterface
|
|
|
|
interface CsrWritePort#(numeric type xlen);
|
|
method ActionValue#(CsrWriteResult) write(RVCSRIndex index, Bit#(xlen) value);
|
|
endinterface
|
|
|
|
interface CsrWritePermission;
|
|
method Bool isWriteable(RVCSRIndex index);
|
|
endinterface
|
|
|
|
interface CsrFile#(numeric type xlen);
|
|
method Action incrementCycleCounters;
|
|
method Action incrementInstructionsRetiredCounter;
|
|
|
|
interface CsrReadPort#(xlen) csrReadPort;
|
|
interface CsrWritePort#(xlen) csrWritePort;
|
|
interface CsrWritePermission csrWritePermission;
|
|
|
|
interface TrapController#(xlen) trapController;
|
|
endinterface
|
|
|
|
typedef struct {
|
|
IsaCfg#(xlen) isa_cfg;
|
|
} CsrFileCfg#(numeric type xlen);
|
|
|
|
module mkCsrFile#(CsrFileCfg#(xlen) cfg)(CsrFile#(xlen))
|
|
provisos(
|
|
Add#(xlen, 0, 32),
|
|
Add#(xlen, 0, 64)
|
|
);
|
|
Reg#(RVPrivilegeLevel) currentPriv <- mkReg(priv_MACHINE);
|
|
|
|
// Counters
|
|
Count#(Bit#(64)) cycleCounter <- mkCount(0);
|
|
Count#(Bit#(64)) timeCounter <- mkCount(0);
|
|
Count#(Bit#(64)) retiredCounter <- mkCount(0);
|
|
|
|
// Csrs
|
|
|
|
// MINFO (mvendorid, marchid, mpimpid, mhardit, mconfigptr)
|
|
MachineInformationCfg#(xlen) machineInfoCfg = MachineInformationCfg {
|
|
mvendorid: 0,
|
|
marchid: 0,
|
|
mimpid: 0,
|
|
mhartid: 0,
|
|
mconfigptr: 0
|
|
};
|
|
|
|
MachineStatus_Ifc#(xlen) mstatus <- mkMachineStatus(cfg.isa_cfg);
|
|
MachineISA_Ifc#(xlen) misa <- mkMachineISA(cfg.isa_cfg);
|
|
ReadOnly#(Bit#(xlen)) mcycle <- mkReadOnly(truncate(cycleCounter));
|
|
ReadOnly#(Bit#(xlen)) mtimer <- mkReadOnly(truncate(timeCounter));
|
|
ReadOnly#(Bit#(xlen)) minstret <- mkReadOnly(truncate(retiredCounter));
|
|
|
|
if (valueof(xlen) == 32) begin
|
|
ReadOnly#(Bit#(xlen)) mcycleh <- mkReadOnly(truncateLSB(cycleCounter));
|
|
ReadOnly#(Bit#(xlen)) mtimeh <- mkReadOnly(truncateLSB(timeCounter));
|
|
ReadOnly#(Bit#(xlen)) minstreth <- mkReadOnly(truncateLSB(retiredCounter));
|
|
end
|
|
|
|
Reg#(Bit#(xlen)) mcause <- mkReg(0);
|
|
Reg#(Bit#(xlen)) mtvec <- mkReg('hC0DEC0DE);
|
|
Reg#(Bit#(xlen)) mepc <- mkReg(0);
|
|
Reg#(Bit#(xlen)) mscratch <- mkReg(0);
|
|
Reg#(Bit#(xlen)) mip <- mkReg(0);
|
|
Reg#(Bit#(xlen)) mie <- mkReg(0);
|
|
Reg#(Bit#(xlen)) mtval <- mkReg(0);
|
|
Reg#(Bit#(xlen)) mideleg <- mkReg(0);
|
|
Reg#(Bit#(xlen)) medeleg <- mkReg(0);
|
|
|
|
Reg#(Bit#(xlen)) sideleg <- mkReg(0);
|
|
Reg#(Bit#(xlen)) sedeleg <- mkReg(0);
|
|
|
|
function Bool isWARLIgnore(RVCSRIndex index);
|
|
Bool result = False;
|
|
if ((index >= csr_PMPADDR0 && index <= csr_PMPADDR63) ||
|
|
(index >= csr_PMPCFG0 && index <= csr_PMPCFG15) ||
|
|
index == csr_SATP ||
|
|
index == csr_MIDELEG ||
|
|
index == csr_MEDELEG) begin
|
|
result = True;
|
|
end
|
|
|
|
return result;
|
|
endfunction
|
|
|
|
function RVCSRIndex getIndex(RVPrivilegeLevel privilege_level, RVCSRIndexOffset offset);
|
|
RVCSRIndex index = 0;
|
|
index[9:8] = privilege_level[1:0];
|
|
index[7:0] = offset;
|
|
return index;
|
|
endfunction
|
|
|
|
// Based on fv_new_priv_on_exception from Flute processor.
|
|
function RVPrivilegeLevel getTrapPrivilegeLevel(Trap#(xlen) trap);
|
|
let trap_privilege_level = priv_MACHINE;
|
|
|
|
if (currentPriv < priv_MACHINE) begin
|
|
if (cfg.isa_cfg.extS) begin // S mode supported?
|
|
// See if this trap should be delegated to SUPERVISOR mode
|
|
let delegated = (trap.isInterrupt ?
|
|
(mideleg[trap.cause] == 0 ? False : True) :
|
|
(medeleg[trap.cause] == 0 ? False : True));
|
|
|
|
if (delegated) begin
|
|
trap_privilege_level = priv_SUPERVISOR;
|
|
|
|
// If the current priv mode is U, and user mode traps are supported,
|
|
// then consult sedeleg/sideleg to determine if delegated to USER mode.
|
|
if (currentPriv == priv_USER && cfg.isa_cfg.extU) begin
|
|
delegated = (trap.isInterrupt ?
|
|
(sideleg[trap.cause] == 0 ? False : True) :
|
|
(sedeleg[trap.cause] == 0 ? False : True));
|
|
|
|
if (delegated) begin
|
|
trap_privilege_level = priv_USER;
|
|
end
|
|
end
|
|
end
|
|
end else begin // S mode *NOT* supported
|
|
// If user mode traps are supported, then consult sedeleg/sideleg to determine
|
|
// if delegated to USER mode.
|
|
if (cfg.isa_cfg.extU) begin
|
|
let delegated = (trap.isInterrupt ?
|
|
(mideleg[trap.cause] == 0 ? False : True) :
|
|
(medeleg[trap.cause] == 0 ? False : True));
|
|
|
|
if (delegated) begin
|
|
trap_privilege_level = priv_USER;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return trap_privilege_level;
|
|
endfunction
|
|
|
|
//
|
|
// readInternal
|
|
//
|
|
function ActionValue#(CsrReadResult#(xlen)) readInternal(RVCSRIndex index);
|
|
actionvalue
|
|
CsrReadResult#(xlen) result = CsrReadResult {
|
|
value: 0,
|
|
denied: False
|
|
};
|
|
|
|
if (!isWARLIgnore(index)) begin
|
|
case(index)
|
|
// Machine Information Registers (MRO)
|
|
csr_MVENDORID: result.value = extend(machineInfoCfg.mvendorid);
|
|
csr_MARCHID: result.value = machineInfoCfg.marchid;
|
|
csr_MIMPID: result.value = machineInfoCfg.mimpid;
|
|
csr_MHARTID: result.value = machineInfoCfg.mhartid;
|
|
|
|
csr_MISA: result.value = misa.pack;
|
|
|
|
csr_MCAUSE: result.value = mcause;
|
|
csr_MTVEC: result.value = mtvec;
|
|
csr_MEPC: result.value = mepc;
|
|
csr_MTVAL: result.value = mtval;
|
|
csr_MIDELEG: result.value = mideleg;
|
|
csr_MEDELEG: result.value = medeleg;
|
|
|
|
csr_MSTATUS: result.value <- mstatus.getMachineStatus.get;
|
|
|
|
csr_MCYCLE, csr_CYCLE: begin
|
|
result.value = mcycle;
|
|
end
|
|
|
|
csr_MSCRATCH: result.value = mscratch;
|
|
csr_MIP: result.value = mip;
|
|
csr_MIE: result.value = mie;
|
|
|
|
// !bugbug - TSELECT is hardcoded to all 1s. This is to keep
|
|
// the ISA debug test happy. It *should* report a
|
|
// pass if reading TSELECT failed with a trap (to reflect what's in the spec)
|
|
// This is a bug in the debug test.
|
|
csr_TSELECT: result.value = 'hFFFF_FFFF;
|
|
default: begin
|
|
result.denied = True;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
$display("---- Csr Read Index : ", fshow(index));
|
|
$display("---- Csr Read Result: ", fshow(result));
|
|
return result;
|
|
endactionvalue
|
|
endfunction
|
|
|
|
//
|
|
// writeInternal
|
|
//
|
|
function ActionValue#(CsrWriteResult) writeInternal(RVCSRIndex index, Bit#(xlen) value);
|
|
actionvalue
|
|
let result = CsrWriteResult {
|
|
denied: False
|
|
};
|
|
|
|
// Access and write to read-only Csr check.
|
|
if (!isWARLIgnore(index)) begin
|
|
case(index)
|
|
csr_MCAUSE: mcause <= value;
|
|
csr_MCYCLE: cycleCounter <= zeroExtend(value);
|
|
csr_MEPC: mepc <= value;
|
|
csr_MISA: begin
|
|
// No-Op
|
|
end
|
|
csr_MSCRATCH: mscratch <= value;
|
|
csr_MSTATUS: mstatus.putMachineStatus.put(value);
|
|
csr_MTVAL: mtval <= value;
|
|
csr_MTVEC: mtvec <= value;
|
|
csr_MIE: mie <= value;
|
|
csr_MIP: mip <= value;
|
|
csr_TSELECT: begin
|
|
// No-Op
|
|
end
|
|
default: result.denied = True;
|
|
endcase
|
|
end
|
|
|
|
$display("---- Csr Write Index : ", fshow(index));
|
|
$display("---- Csr Write Value : ", value);
|
|
$display("---- Csr Write Result: ", fshow(result));
|
|
|
|
return result;
|
|
endactionvalue
|
|
endfunction
|
|
|
|
function ActionValue#(CsrReadResult#(xlen)) readWithOffset1(RVPrivilegeLevel privilegeLevel, RVCSRIndexOffset offset);
|
|
actionvalue
|
|
let csrReadResult <- readInternal(getIndex(privilegeLevel, offset));
|
|
return csrReadResult;
|
|
endactionvalue
|
|
endfunction
|
|
|
|
function ActionValue#(CsrWriteResult) writeWithOffset1(RVPrivilegeLevel privilegeLevel, RVCSRIndexOffset offset, Bit#(xlen) value);
|
|
actionvalue
|
|
let csrWriteResult <- writeInternal(getIndex(privilegeLevel, offset), value);
|
|
return csrWriteResult;
|
|
endactionvalue
|
|
endfunction
|
|
|
|
method Action incrementCycleCounters;
|
|
cycleCounter.incr(1);
|
|
timeCounter.incr(1);
|
|
endmethod
|
|
|
|
method Action incrementInstructionsRetiredCounter;
|
|
retiredCounter.incr(1);
|
|
endmethod
|
|
|
|
//
|
|
// CsrReadport
|
|
//
|
|
interface CsrReadPort csrReadPort;
|
|
method ActionValue#(CsrReadResult#(xlen)) read(RVCSRIndex index);
|
|
let result = CsrReadResult {
|
|
value: 0,
|
|
denied: True
|
|
};
|
|
|
|
if (currentPriv >= index[9:8]) begin
|
|
result <- readInternal(index);
|
|
end
|
|
|
|
return result;
|
|
endmethod
|
|
endinterface
|
|
|
|
//
|
|
// CsrWritePort
|
|
//
|
|
interface CsrWritePort csrWritePort;
|
|
method ActionValue#(CsrWriteResult) write(RVCSRIndex index, Bit#(xlen) value);
|
|
let result = CsrWriteResult {
|
|
denied: True
|
|
};
|
|
|
|
if (currentPriv >= index[9:8] && index[11:10] != 'b11) begin
|
|
result <- writeInternal(index, value);
|
|
end
|
|
|
|
return result;
|
|
endmethod
|
|
endinterface
|
|
|
|
//
|
|
// CsrWritePermission
|
|
//
|
|
interface CsrWritePermission csrWritePermission;
|
|
method Bool isWriteable(RVCSRIndex index);
|
|
return (currentPriv >= index[9:8] && index[11:10] != 'b11);
|
|
endmethod
|
|
endinterface
|
|
|
|
//
|
|
// trapController
|
|
//
|
|
interface TrapController trapController;
|
|
method ActionValue#(Bit#(xlen)) beginTrap(Bit#(xlen) trapProgramCounter, Trap#(xlen) trap);
|
|
Bit#(xlen) cause = 0;
|
|
|
|
let trapPrivilegeLevel = getTrapPrivilegeLevel(trap);
|
|
|
|
cause = zeroExtend(trap.cause);
|
|
if (!trap.isInterrupt) begin
|
|
cause[valueOf(xlen)-1] = 1;
|
|
end
|
|
|
|
// PC => MEPC
|
|
writeWithOffset1(trapPrivilegeLevel, csr_EPC, trapProgramCounter);
|
|
|
|
mstatus.beginTrap(currentPriv);
|
|
|
|
// cause => CAUSE
|
|
writeWithOffset1(trapPrivilegeLevel, csr_CAUSE, cause);
|
|
writeWithOffset1(trapPrivilegeLevel, csr_TVAL, trap.tval);
|
|
|
|
let readResult <- readWithOffset1(trapPrivilegeLevel, csr_TVEC);
|
|
Bit#(xlen) vectorTableBase = readResult.value;
|
|
let trapHandler = vectorTableBase;
|
|
|
|
// Check and handle a vectored trap handler table
|
|
if (trapHandler[1:0] == 1) begin
|
|
trapHandler[1:0] = 0;
|
|
if(trap.isInterrupt) begin
|
|
trapHandler = trapHandler + extend(4 * trap.cause);
|
|
end
|
|
end
|
|
|
|
currentPriv <= trapPrivilegeLevel;
|
|
|
|
return trapHandler;
|
|
endmethod
|
|
|
|
method ActionValue#(Bit#(xlen)) endTrap;
|
|
Bit#(xlen) newProgramCounter = 'hDEADDEAD;
|
|
|
|
let readStatus <- readWithOffset1(currentPriv, csr_STATUS);
|
|
if (readStatus.denied == False) begin
|
|
MachineStatus mstatus_ = mstatus_unpack(readStatus.value);
|
|
let newPrivilegeLevel = mstatus_.mpp;
|
|
mstatus_.mie = mstatus_.mpie;
|
|
mstatus_.mpie = False;
|
|
|
|
// Attempt to update MSTATUS. The current privilege level may prevent this.
|
|
|
|
let writeStatus <- writeInternal(csr_MSTATUS, mstatus_pack(mstatus_));
|
|
if (writeStatus.denied == False) begin
|
|
currentPriv <= newPrivilegeLevel;
|
|
readStatus <- readWithOffset1(currentPriv, csr_EPC);
|
|
if (readStatus.denied == False) begin
|
|
newProgramCounter = readStatus.value;
|
|
end
|
|
end
|
|
end
|
|
return newProgramCounter;
|
|
endmethod
|
|
endinterface
|
|
endmodule
|