arch-riscv: Add interrupt handling
Implement the Interrupts SimObject for RISC-V. This basically just handles setting and getting the values of the interrupt-pending and interrupt-enable CSRs according to the privileged ISA reference chapter 3.1.14. Note that it does NOT implement the PLIC as defined in chapter 7, as that is used for handling external interrupts which are defined based on peripherals that are available. Change-Id: Ia1321430f870ff5a3950217266fde0511332485b Reviewed-on: https://gem5-review.googlesource.com/c/14377 Reviewed-by: Jason Lowe-Power <jason@lowepower.com> Maintainer: Jason Lowe-Power <jason@lowepower.com>
This commit is contained in:
@@ -61,13 +61,24 @@ RiscvFault::invoke(ThreadContext *tc, const StaticInstPtr &inst)
|
||||
STATUS status = tc->readMiscReg(MISCREG_STATUS);
|
||||
|
||||
// Set fault handler privilege mode
|
||||
if (pp != PRV_M &&
|
||||
bits(tc->readMiscReg(MISCREG_MEDELEG), _code) != 0) {
|
||||
prv = PRV_S;
|
||||
}
|
||||
if (pp == PRV_U &&
|
||||
bits(tc->readMiscReg(MISCREG_SEDELEG), _code) != 0) {
|
||||
prv = PRV_U;
|
||||
if (isInterrupt()) {
|
||||
if (pp != PRV_M &&
|
||||
bits(tc->readMiscReg(MISCREG_MIDELEG), _code) != 0) {
|
||||
prv = PRV_S;
|
||||
}
|
||||
if (pp == PRV_U &&
|
||||
bits(tc->readMiscReg(MISCREG_SIDELEG), _code) != 0) {
|
||||
prv = PRV_U;
|
||||
}
|
||||
} else {
|
||||
if (pp != PRV_M &&
|
||||
bits(tc->readMiscReg(MISCREG_MEDELEG), _code) != 0) {
|
||||
prv = PRV_S;
|
||||
}
|
||||
if (pp == PRV_U &&
|
||||
bits(tc->readMiscReg(MISCREG_SEDELEG), _code) != 0) {
|
||||
prv = PRV_U;
|
||||
}
|
||||
}
|
||||
|
||||
// Set fault registers and status
|
||||
@@ -116,7 +127,10 @@ RiscvFault::invoke(ThreadContext *tc, const StaticInstPtr &inst)
|
||||
tc->setMiscReg(MISCREG_STATUS, status);
|
||||
|
||||
// Set PC to fault handler address
|
||||
pcState.set(tc->readMiscReg(tvec) >> 2);
|
||||
Addr addr = tc->readMiscReg(tvec) >> 2;
|
||||
if (isInterrupt() && bits(tc->readMiscReg(tvec), 1, 0) == 1)
|
||||
addr += 4 * _code;
|
||||
pcState.set(addr);
|
||||
} else {
|
||||
invokeSE(tc, inst);
|
||||
advancePC(pcState, inst);
|
||||
@@ -126,11 +140,6 @@ RiscvFault::invoke(ThreadContext *tc, const StaticInstPtr &inst)
|
||||
|
||||
void Reset::invoke(ThreadContext *tc, const StaticInstPtr &inst)
|
||||
{
|
||||
if (FullSystem) {
|
||||
tc->getCpuPtr()->clearInterrupts(tc->threadId());
|
||||
tc->clearArchRegs();
|
||||
}
|
||||
|
||||
tc->setMiscReg(MISCREG_PRV, PRV_M);
|
||||
STATUS status = tc->readMiscReg(MISCREG_STATUS);
|
||||
status.mie = 0;
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#ifndef __ARCH_RISCV_FAULTS_HH__
|
||||
#define __ARCH_RISCV_FAULTS_HH__
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "arch/riscv/isa.hh"
|
||||
@@ -53,6 +52,15 @@ enum FloatException : MiscReg {
|
||||
FloatInvalid = 0x10
|
||||
};
|
||||
|
||||
/*
|
||||
* In RISC-V, exception and interrupt codes share some values. They can be
|
||||
* differentiated by an 'Interrupt' flag that is enabled for interrupt faults
|
||||
* but not exceptions. The full fault cause can be computed by placing the
|
||||
* exception (or interrupt) code in the least significant bits of the CAUSE
|
||||
* CSR and then setting the highest bit of CAUSE with the 'Interrupt' flag.
|
||||
* For more details on exception causes, see Chapter 3.1.20 of the RISC-V
|
||||
* privileged specification v 1.10. Codes are enumerated in Table 3.6.
|
||||
*/
|
||||
enum ExceptionCode : MiscReg {
|
||||
INST_ADDR_MISALIGNED = 0,
|
||||
INST_ACCESS = 1,
|
||||
@@ -70,7 +78,18 @@ enum ExceptionCode : MiscReg {
|
||||
INST_PAGE = 12,
|
||||
LOAD_PAGE = 13,
|
||||
STORE_PAGE = 15,
|
||||
AMO_PAGE = 15
|
||||
AMO_PAGE = 15,
|
||||
|
||||
INT_SOFTWARE_USER = 0,
|
||||
INT_SOFTWARE_SUPER = 1,
|
||||
INT_SOFTWARE_MACHINE = 3,
|
||||
INT_TIMER_USER = 4,
|
||||
INT_TIMER_SUPER = 5,
|
||||
INT_TIMER_MACHINE = 7,
|
||||
INT_EXT_USER = 8,
|
||||
INT_EXT_SUPER = 9,
|
||||
INT_EXT_MACHINE = 11,
|
||||
NumInterruptTypes
|
||||
};
|
||||
|
||||
class RiscvFault : public FaultBase
|
||||
@@ -106,6 +125,13 @@ class Reset : public FaultBase
|
||||
StaticInst::nullStaticInstPtr) override;
|
||||
};
|
||||
|
||||
class InterruptFault : public RiscvFault
|
||||
{
|
||||
public:
|
||||
InterruptFault(ExceptionCode c) : RiscvFault("interrupt", true, c) {}
|
||||
InterruptFault(int c) : InterruptFault(static_cast<ExceptionCode>(c)) {}
|
||||
};
|
||||
|
||||
class InstFault : public RiscvFault
|
||||
{
|
||||
protected:
|
||||
|
||||
@@ -31,8 +31,14 @@
|
||||
#ifndef __ARCH_RISCV_INTERRUPT_HH__
|
||||
#define __ARCH_RISCV_INTERRUPT_HH__
|
||||
|
||||
#include <bitset>
|
||||
#include <memory>
|
||||
|
||||
#include "arch/riscv/faults.hh"
|
||||
#include "arch/riscv/registers.hh"
|
||||
#include "base/logging.hh"
|
||||
#include "cpu/thread_context.hh"
|
||||
#include "debug/Interrupt.hh"
|
||||
#include "params/RiscvInterrupts.hh"
|
||||
#include "sim/sim_object.hh"
|
||||
|
||||
@@ -41,10 +47,16 @@ class ThreadContext;
|
||||
|
||||
namespace RiscvISA {
|
||||
|
||||
/*
|
||||
* This is based on version 1.10 of the RISC-V privileged ISA reference,
|
||||
* chapter 3.1.14.
|
||||
*/
|
||||
class Interrupts : public SimObject
|
||||
{
|
||||
private:
|
||||
BaseCPU * cpu;
|
||||
std::bitset<NumInterruptTypes> ip;
|
||||
std::bitset<NumInterruptTypes> ie;
|
||||
|
||||
public:
|
||||
typedef RiscvInterruptsParams Params;
|
||||
@@ -55,64 +67,87 @@ class Interrupts : public SimObject
|
||||
return dynamic_cast<const Params *>(_params);
|
||||
}
|
||||
|
||||
Interrupts(Params * p) : SimObject(p), cpu(nullptr)
|
||||
{}
|
||||
Interrupts(Params * p) : SimObject(p), cpu(nullptr), ip(0), ie(0) {}
|
||||
|
||||
void
|
||||
setCPU(BaseCPU * _cpu)
|
||||
void setCPU(BaseCPU * _cpu) { cpu = _cpu; }
|
||||
|
||||
std::bitset<NumInterruptTypes>
|
||||
globalMask(ThreadContext *tc) const
|
||||
{
|
||||
cpu = _cpu;
|
||||
INTERRUPT mask;
|
||||
STATUS status = tc->readMiscReg(MISCREG_STATUS);
|
||||
if (status.mie)
|
||||
mask.mei = mask.mti = mask.msi = 1;
|
||||
if (status.sie)
|
||||
mask.sei = mask.sti = mask.ssi = 1;
|
||||
if (status.uie)
|
||||
mask.uei = mask.uti = mask.usi = 1;
|
||||
return std::bitset<NumInterruptTypes>(mask);
|
||||
}
|
||||
|
||||
bool checkInterrupt(int num) const { return ip[num] && ie[num]; }
|
||||
bool checkInterrupts(ThreadContext *tc) const
|
||||
{
|
||||
return (ip & ie & globalMask(tc)).any();
|
||||
}
|
||||
|
||||
Fault
|
||||
getInterrupt(ThreadContext *tc) const
|
||||
{
|
||||
assert(checkInterrupts(tc));
|
||||
std::bitset<NumInterruptTypes> mask = globalMask(tc);
|
||||
for (int c = 0; c < NumInterruptTypes; c++)
|
||||
if (checkInterrupt(c) && mask[c])
|
||||
return std::make_shared<InterruptFault>(c);
|
||||
return NoFault;
|
||||
}
|
||||
|
||||
void updateIntrInfo(ThreadContext *tc) {}
|
||||
|
||||
void
|
||||
post(int int_num, int index)
|
||||
{
|
||||
panic("Interrupts::post not implemented.\n");
|
||||
DPRINTF(Interrupt, "Interrupt %d:%d posted\n", int_num, index);
|
||||
ip[int_num] = true;
|
||||
}
|
||||
|
||||
void
|
||||
clear(int int_num, int index)
|
||||
{
|
||||
panic("Interrupts::clear not implemented.\n");
|
||||
DPRINTF(Interrupt, "Interrupt %d:%d cleared\n", int_num, index);
|
||||
ip[int_num] = false;
|
||||
}
|
||||
|
||||
void
|
||||
clearAll()
|
||||
{
|
||||
warn_once("Interrupts::clearAll not implemented.\n");
|
||||
DPRINTF(Interrupt, "All interrupts cleared\n");
|
||||
ip = 0;
|
||||
}
|
||||
|
||||
bool
|
||||
checkInterrupts(ThreadContext *tc) const
|
||||
{
|
||||
warn_once("Interrupts::checkInterrupts just rudimentary implemented");
|
||||
/**
|
||||
* read the machine interrupt register in order to check if interrupts
|
||||
* are pending
|
||||
* should be sufficient for now, as interrupts
|
||||
* are not implemented at all
|
||||
*/
|
||||
if (tc->readMiscReg(MISCREG_IP))
|
||||
return true;
|
||||
MiscReg readIP() const { return (MiscReg)ip.to_ulong(); }
|
||||
MiscReg readIE() const { return (MiscReg)ie.to_ulong(); }
|
||||
void setIP(const MiscReg& val) { ip = val; }
|
||||
void setIE(const MiscReg& val) { ie = val; }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Fault
|
||||
getInterrupt(ThreadContext *tc)
|
||||
void
|
||||
serialize(CheckpointOut &cp)
|
||||
{
|
||||
assert(checkInterrupts(tc));
|
||||
panic("Interrupts::getInterrupt not implemented.\n");
|
||||
SERIALIZE_SCALAR(ip.to_ulong());
|
||||
SERIALIZE_SCALAR(ie.to_ulong());
|
||||
}
|
||||
|
||||
void
|
||||
updateIntrInfo(ThreadContext *tc)
|
||||
unserialize(CheckpointIn &cp)
|
||||
{
|
||||
panic("Interrupts::updateIntrInfo not implemented.\n");
|
||||
long reg;
|
||||
UNSERIALIZE_SCALAR(reg);
|
||||
ip = reg;
|
||||
UNSERIALIZE_SCALAR(reg);
|
||||
ie = reg;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace RiscvISA
|
||||
|
||||
#endif // __ARCH_RISCV_INTERRUPT_HH__
|
||||
|
||||
#endif // __ARCH_RISCV_INTERRUPT_HH__
|
||||
@@ -139,6 +139,12 @@ ISA::readMiscReg(int misc_reg, ThreadContext *tc)
|
||||
warn("Instruction counter disabled.\n");
|
||||
return 0;
|
||||
}
|
||||
case MISCREG_IP:
|
||||
return tc->getCpuPtr()->getInterruptController(tc->threadId())
|
||||
->readIP();
|
||||
case MISCREG_IE:
|
||||
return tc->getCpuPtr()->getInterruptController(tc->threadId())
|
||||
->readIE();
|
||||
default:
|
||||
// Try reading HPM counters
|
||||
// As a placeholder, all HPM counters are just cycle counters
|
||||
@@ -175,7 +181,16 @@ ISA::setMiscReg(int misc_reg, const MiscReg &val, ThreadContext *tc)
|
||||
// Ignore writes to HPM counters for now
|
||||
warn("Ignoring write to %s.\n", CSRData.at(misc_reg).name);
|
||||
} else {
|
||||
setMiscRegNoEffect(misc_reg, val);
|
||||
switch (misc_reg) {
|
||||
case MISCREG_IP:
|
||||
return tc->getCpuPtr()->getInterruptController(tc->threadId())
|
||||
->setIP(val);
|
||||
case MISCREG_IE:
|
||||
return tc->getCpuPtr()->getInterruptController(tc->threadId())
|
||||
->setIE(val);
|
||||
default:
|
||||
setMiscRegNoEffect(misc_reg, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -256,11 +256,25 @@ def template CSRExecute {{
|
||||
} else {
|
||||
DPRINTF(RiscvMisc, "Writing %#x to CSR %s.\n", data,
|
||||
CSRData.at(csr).name);
|
||||
INTERRUPT oldinterrupt = olddata;
|
||||
INTERRUPT newinterrupt = data;
|
||||
switch (csr) {
|
||||
case CSR_FCSR:
|
||||
xc->setMiscReg(MISCREG_FFLAGS, bits(data, 4, 0));
|
||||
xc->setMiscReg(MISCREG_FRM, bits(data, 7, 5));
|
||||
break;
|
||||
case CSR_MIP: case CSR_MIE:
|
||||
if (oldinterrupt.mei == newinterrupt.mei &&
|
||||
oldinterrupt.mti == newinterrupt.mti &&
|
||||
oldinterrupt.msi == newinterrupt.msi) {
|
||||
xc->setMiscReg(CSRData.at(csr).physIndex,data);
|
||||
} else {
|
||||
std::string error = "Interrupt m bits are "
|
||||
"read-only\n";
|
||||
fault = make_shared<IllegalInstFault>(error,
|
||||
machInst);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
xc->setMiscReg(CSRData.at(csr).physIndex, data);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user