E-Series/src/Cpu/CsrFile.bsv
John Terrell fda8dafad7
WIP
2023-04-23 10:45:13 -07:00

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