Inside DULI
DULI is a processor extension, not a scheduler trick. It changes the moment an interrupt is acknowledged: instead of entering the kernel, the hardware activates the target handler together with the protection domains it is allowed to run in.
The isolation–determinism trade-off
A microkernel isolates each component as an unprivileged process, which is what makes modern secure embedded software possible. The interrupt handler of that component, however, is on the wrong side of the boundary:
- In the kernel, the handler runs at kernel speed but the component's untrusted code has to be moved into the most privileged code base — the opposite of isolation.
- In the process, the handler is confined but every activation needs a kernel round trip. Forwarding costs hundreds of cycles, an order of magnitude above a kernel interrupt.
Existing user-level interrupt mechanisms only help when the target process is already running. Intel's UINTR and the RISC-V N extension take a short path while the target is scheduled; when it is dormant, the kernel must switch to it, and the latency doubles into a slow path. The result is a bimodal distribution that improves average throughput but not the worst case. SBI solves determinism for exactly one trusted process per core.
| Approach | Spatial isolation | Temporal isolation | Multi-process | Interrupt latency | Determinism |
|---|---|---|---|---|---|
| TockOS (kernel handlers) | yes | yes | yes | high | medium |
| Hopter | — | — | — | medium | medium |
| CV32RT | — | — | — | low | high |
| RTOSUnit | — | — | — | low | high |
| UINTR / RISC-V-N | yes | — | yes | low (fast path) | low |
| SBI (TrustZone-M) | yes | yes | one process | low | high |
| DULI | yes | yes | yes | low | high |
One interrupt, two domains
Every user-level interrupt is described by an entry in the Interrupt Identification Data (IID). The entry answers three questions at interrupt time:
- is this source user-level or kernel-level?
- which spatial domain does its handler run in — a pointer to an entry in the PMP table, holding the complete PMP configuration for that domain;
- which temporal domain does it run in — a pointer to a 32-bit execution budget, loaded into a hardware countdown timer.
Several interrupts may share a spatial domain, a budget, or both. The IID is the only place where the mapping lives; the kernel can update it without touching the interrupt vector table.
The delivery path
In the V5 variant the IID lookup is folded into the acknowledge cycle and the context is banked, so the modeled entry latency is 11 cycles — 15 cycles measured end-to-end on FPGA at 50 MHz.
Confinement is enforced, not promised
The handler is untrusted by assumption. Two hardware conditions terminate it:
- Spatial violation — the PMP rejects an access outside the domain. The handler is forced to return immediately, without involving the kernel.
- Temporal violation — the budget counter reaches zero. The handler is forced to return, and the remaining budget is zero.
The same rule covers synchronous exceptions: a faulting instruction does not trap into the kernel synchronously (which would create unsafe kernel reentrancy). The cause is recorded in an optional CSR, the handler returns, and the kernel may inspect the record later, after all nested user-level interrupts have unwound.
Because a user-level interrupt can preempt the kernel itself, the kernel-managed PMP set is shadow-banked and the kernel's own timer is paused while the handler executes. The kernel resumes exactly where it left off.
Nesting without an unbounded budget
User-level handlers can preempt one another. The hardware keeps additional register banks for the nested contexts; when they are exhausted, it deterministically spills to a tightly-coupled memory. V5 therefore has a bounded fast path and a graceful fallback: an exhausted bank degrades to the V2 latency instead of failing.
Preemption priority is configured when the handler is registered. The extension is deliberately not a scheduler — handlers are run-to-completion, and the kernel replenishes budgets using a deferrable-server policy.
The software view
The kernel exposes a small API; everything else is hardware:
/* Register a user-level interrupt: vector, handler, scheduling policy */
int_reg(INT_UART0, uart_handler, POLICY_DEFERRABLE);
/* Set its hardware preemption priority and enable it */
int_prio(INT_UART0, 2);
int_ena(INT_UART0);void uart_handler(void)
{
/* Runs unprivileged inside the registered PMP domain */
UART0->DATA = UART0->DATA;
uiret(); /* return to the preempted context */
}The kernel's process manager allocates the IID entry and the PMP domain; its budget manager programs the budget entry and replenishes it every server period. No interrupt-path code is added to the kernel.
Compatibility
DULI adds a handful of CSRs — duictl (global enable and IID base), duistk (spill stack), duiepc (interrupted PC) and the iid* CAM/table registers. Bit 0 of duictl disables the whole extension; the core then behaves as a standard RISC-V processor. Legacy interrupts keep the original vector table and the original entry path, so existing firmware and RTOSes do not need to change.
The prototype targets a single RISC-V core. Multi-core routing and arbitration are explicitly future work.