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