After reset, RV_PLIC doesn't generate any interrupts to any targets even if
interrupt sources are set, as all priorities and thresholds are 0 by default and
all IE
values are 0. Software should configure the above three registers.
PRIO0
.. PRIO31
registers are unique. So, only one of the targets
shall configure them.
// Pseudo-code below
void plic_init() {
// Configure priority
// Note that PRIO0 register doesn't affect as intr_src_i[0] is tied to 0.
for (int i = 0; i < N_SOURCE; ++i) {
*(PRIO + i) = value(i);
}
}
void plic_threshold(tid, threshold) {
*(THRESHOLD + tid) = threshold;
}
void plic_enable(tid, iid) {
// iid: 0-based ID
int offset = ceil(N_SOURCE / 32) * tid + (iid >> 5);
*(IE + offset) = *(IE + offset) | (1 << (iid % 32));
}
If software receives an interrupt request, it is recommended to follow the steps
shown below (assuming target 0 which uses CC0
for claim/complete).
- Claim the interrupts right after entering to the interrupt service routine
by reading the
CC0
register. - Determine which interrupt should be serviced based on the values read from
the
CC0
register. - Execute ISR, clearing the originating peripheral interrupt.
- Write Interrupt ID to
CC0
- Repeat as necessary for other pending interrupts.
It is possible to have multiple interrupt events claimed. If software claims one interrupt request, then the process module advertises any pending interrupts with lower priority unless new higher priority interrupt events occur. If a higher interrupt event occurs after previous interrupt is claimed, the RV_PLIC IP advertises the higher priority interrupt. Software may utilize an event manager inside a loop so that interrupt claiming and completion can be separated.
void interrupt_service() {
uint32_t tid = /* ... */;
uint32_t iid = *(CC + tid);
if (iid == 0) {
// Interrupt is claimed by one of other targets.
return;
}
do {
// Process interrupts...
// ...
// Finish.
*(CC + tid) = iid;
iid = *(CC + tid);
} while (iid != 0);
}
As a reference, default interrupt service routines are auto-generated for each IP, and are documented here.
The RV_PLIC in the top level is generated by topgen tool so that the number of interrupt sources may be different.
- IE: CEILING(N_SOURCE / DW) X N_TARGET Each bit enables corresponding interrupt source. Each target has IE set.
- PRIO: N_SOURCE Universal set across all targets. Lower n bits are valid. n is determined by MAX_PRIO parameter
- THRESHOLD: N_TARGET Priority threshold per target. Only priority of the interrupt greater than threshold can raise interrupt notification to the target.
- IP: CEILING(N_SOURCE / DW) Pending bits right after the gateways. Read-only
- CC: N_TARGET Claim by read, complete by write