E-Series/src/Cpu/CSRs/CsrFile.bsv
John Terrell 637d71432c
WIP
2023-03-15 07:15:26 -07:00

397 lines
14 KiB
Plaintext

import Cntrs::*;
import GetPut::*;
import ReadOnly::*;
import Trap::*;
import IsaCfg::*;
import MachineInformation::*;
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));
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
};
MachineStatusIfc#(xlen) mstatus <- mkMachineStatus(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.s_mode_supported) 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.u_level_interrupts_supported) 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.u_level_interrupts_supported) 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
let 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 = pack(cfg);
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: begin
let mstatus_ <- mstatus.getMachineStatus.get;
result.value = pack(mstatus_);
end
/*
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.unpack(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);
// CurPriv => MSTATUS::MPP
let mstatus_ <- mstatus.getMachineStatus.get;
mstatus_.mpp = currentPriv;
// MSTATUS::MIE => MSTATUS::MPIE
mstatus_.mpie = mstatus_.mie;
mstatus_.mie = False; // Disable interrupts
mstatus.putMachineStatus.put(mstatus_);
// 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#(xlen) 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, 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