- RISC-V Reset and NMI
- RISC-V Interrupts
- RISC-V Synchronous Exceptions
- RISC-V Scratch Registers
- Interrupt Handlers In C
The standard interrupt controller is the machine level interrupt handling done by the core. This is very limited and leaves much to be defined by the platform intergrator. This quick reference deals with that controller. The other available controllers are:
- CLINT and ACLINT (Advanced Core Local Interruptor) - The external logic required to implement
PLIC (Platform Local Interrupt Controller) - Multiplexing of platform level interrupt sources to
RISC-V Reset and NMI
These vectors are implementation defined.
- Non Maskable Interrupt (NMI)
These are the interrupts that are defined by the base ISA.
Assertion and Service
- mcause.Exception_Code (Where Interrupt==1) describes the possible interrupt sources.
- When the interrupt condition is met, a bit in the interrupt pending register (mip) is set.
- To service an interrupt the global interrupt enable (mstatus.mie ) and the per interrupt enable bit (mie) both need to be set.
- If multiple interrupts are pending and enabled simultaneously then the order they are serviced is determined by a fixed priority.
- Interrupts can’t be pre-empted without the ISR writing to (mstatus.mie).
Vectored or Direct
Vectored or direct interrupts are both possible.
mtvec.Mode, the lower 2 bits of the vector.
- When this is 0 (direct), it points to a single interrupt service routine (ISR).
- When this is 1 (vectored), it points to the base of table of ISR entry points (4 bytes per entry).
- Regardless, the address of the ISR is loaded to the PC, so for vectored interrupts the entry should be a jump.
The exception/interrupt handling seems to be designed around a few root interrupt sources, with external multiplexing of platform interrupts.
- The priority assignment is a simple fixed priority scheme. Within the priviledge level, the higher the bit the higher the priority.
- In the standard interrupts there is a sigle external interrupt bit ( mip.meip ). This can be used to service an external interrupt controller.
- Bits 16 and above of the mip/mie registers are platform specific. This can be used to bring platform interrupts in as root interrupt sources.
The entry procedure for a interrupt I into machine mode:
- mcause.interrupt = 1; mcause.exception_code = I
- mstatus.mpie = 1, save previous interrupt enable. (See ISR stack.)
- mstatus.mpp = Previous privilege mode (m, s or u).
- mstatus.mie = 0, interrupts are disabled unless the ISR re-writes this.
- mepc = Interrupted PC, save the return address.
- IF mtvec.Mode == Direct(0) THEN; PC = (mtvec & ~0xF)
- IF mtvec.Mode == Vectored(1) THEN; PC = (mtvec & ~0xF) + (I * 4)
The exit procedure for a machine interrupt when mret is executed.
- mstatus.mie = mstatus.mpie, restore interrupt enable
- Privilege mode = mstatus.mpp, restore privilege
- mstatus.mpp = mstatus.mpie = 0
- PC = mepc
Base + Offset
|msi||3||0x0000000c||m||Machine Software Interrupt||mip.msip = Memory mapped control registers allow this bit to be written to assert a software interrupt. (It is used to provide a software interrupt from other harts).|
|mti||7||0x0000001c||m||Machine Timer Interrupt||mip.mtip = mtime >= mtimecmp (mtimecmp needs to be written to clear the pending bit).|
|mei||11||0x0000002c||m||Machine External Interrupt||mip.meip = External interrupt signal asserted|
|ssi||1||0x00000004||s||Supervisor Software Interrupt||mip.ssi = Implementation defined.|
|sti||5||0x00000014||s||Supervisor Timer Interrupt||mip.sti = Implementation defined.|
|sei||9||0x00000024||s||Supervisor External Interrupt||mip.sei = Implementation defined.|
|usi||0||0x00000000||u||User Software Interrupt||mip.usi = Implementation defined.|
|uti||4||0x00000010||u||User Timer Interrupt||mip.uti = Implementation defined.|
|uei||8||0x00000020||u||User External Interrupt||mip.uei = Implementation defined.|
|platform_defined||16+||0x00000040||m||Optional platform defined interrupt sources.||mip.platform_defined = Implementation defined.|
Supervisor-level interrupts require support of the s mode privilege. The mideleg register needs to set by m mode to enable s mode to take the interrupts.
Supervisor mode CSRs matching machine mode CSRs are used to implement supervisor interrupts and exceptions. Some are a restricted view to the machine mode CSRs, with dedicated bits for supervisor status, while others are replicated to dedicated registers.
|Restricted View CSRs||sstatus, sip, sie|
|Replicated CSRs||scause, sepc, stvec, sscratch|
The entry procedure for a interrupt I into supervisor mode (Assuming mideleg[I] is set):
- scause.interrupt = 1; scause.exception_code = I
- sstatus.spie = 1, save previous interrupt enable. (See ISR stack.)
- sstatus.spp = Previous privilege mode (s or u).
- sstatus.sie = 0, interrupts are disabled unless the ISR re-writes this.
- sepc = Interrupted PC, save the return address.
- IF stvec.mode == Direct THEN; PC = (stvec.Base & ~0xF)
- IF stvec.mode == Vectored THEN; PC = (stvec.Base & ~0xF) + (I * 4)
The exit procedure for a supervisor interrupt when sret is executed.
- sstatus.mie = sstatus.mpie, restore interrupt enable
- Privilege mode = sstatus.spp, restore privilege
- sstatus.spp = sstatus.spie = 0
- PC = sepc
Supervisor interrupts assuming mideleg.ssi, mideleg.sti, mideleg.sei are set:
Base + Offset
|ssi||1||0x00000004||s||Supervisor Software Interrupt||sip.ssip = Implementation dependent, CSR is written by software on the local hart to assert a software interrupt.|
|sti||5||0x00000014||s||Supervisor Timer Interrupt||sip.stip = Implementation dependent, Written my 'm' mode timer interrupt handler, an SEE call needs to be made to clear this).|
|sei||9||0x00000024||s||Supervisor External Interrupt||sip.seip = Implementation dependent, External interrupt signal asserted|
User-level interrupts are optional for implementation. They rely on the n extension. This specification is not yet complete.
User interrupts assuming sideleg.usi, sideleg.uti, sideleg.uei are set:
Base + Offset
|usi||0||0x00000000||u||User Software Interrupt|
|uti||4||0x00000010||u||User Timer Interrupt|
|uei||8||0x00000020||u||User External Interrupt|
Available for Platform Use
mcause.exception_code values greater than 16 are available for platform use, allowing use if bits 16..mxlen-1 in mip and mie. The platform can define a fixed priority scheme. These can be used to implement a fast vectored interrupt scheme.
RISC-V Synchronous Exceptions
These are the exceptions that are defined by the base ISA.
- Synchronous Exceptions are always caused by software execution. e.g. Faults, errors and environment calls.
- Mcause.Exception_Code (Where Interrupt==0) describes the possible interrupt sources.
- Simultaneous exceptions are serviced according to a fixed priority.
The entry procedure for an exception E into machine mode:
- mcause.interrupt = 0; mcause.exception_code = E
- mstatus.mpie = mstatus.mie, save previous interrupt enable
- mstatus.mpp = Previous privilege mode (m, s or u)
- mstatus.mie = 0
- Privilege mode = m
- mtval = Exception specific information related to the cause.
- mepc = PC, Instruction that caused trap.
- PC = (mtvec & ~0xF) (Exceptions are not vectored)
The exit procedure using mret is the same as for interrupts.
As for interrupts exception E can be taken in supervisor or user mode if available and configured via medeleg or sedeleg. The exception needs to be generated in the same or lower privilege level.
Delegation to Lower Privilege Mode
There are two methods to delegate exception E to lower privilege modes:
- Enter in ‘m’ mode. Write mstatus.mpp = lower privilege mode. Execute mret
- Configure medeleg[E]. The exception E will be taken in s mode when it occurs in s mode or lower privilege. (NOT when it occurs in m mode.)
RISC-V Scratch Registers
Scratch CSRs are available for each privilege trap level.
Interrupt Handlers In C
For GCC RISC-V Function Attributes are used to mark a function as an interrupt handler. These are also supported by LLVM
As a parameter user, supervisor, and machine can be specified as the type. (To select the correct return instruction).