Author: P R Sivakumar, Founder and CEO, Maven Silicon

Understanding RISC‑V traps is essential for Chip Designers building RISC‑V CPUs, microcontrollers, and complex SoCs. It’s equally important for Embedded Engineers who develop and debug software stacks — firmware, OS kernels, trap handlers, device drivers. In RISC‑V, traps are at the heart of exception handling, system calls, and interrupt-driven I/O. So, in this article I explain everything, especially from a chip designer’s perspective - from what traps are to how they are set up and used in bare-metal, RTOS and OS-based hardware systems, with RISC-V assembly examples.
What is a Trap in RISC-V?
A trap is any event that causes the CPU to stop normal instruction flow and jump to a trap handler running in a more privileged mode.
Two types:
|
Type |
Synchronous? |
Cause Example |
|
Exception |
Yes |
Illegal opcode, ecall |
|
Interrupt |
No |
Timer tick, UART receive, GPIO event |
When a trap occurs, the CPU:
- Saves the PC into mepc or sepc
- Records the cause in mcause or scause
- Optionally stores fault info in mtval or stval
- Updates the fields mpp/spp, mpie/spie and mie/sie of mstatus
- Jumps to the address in mtvec or stvec – Executes the trap handler
- Executes the mret/sret instruction to resume the interrupted thread : pc=mepc/sepc
To explore more and dive deep into RISC-V traps, refer to my RISC-V Traps YouTube video
Trap Setup Process
Step 1: Trap Vector Setup
The trap vector tells the CPU where to go when a trap happens.

- Direct mode → all traps go to the same address
- Vectored mode → traps jump to a base + (cause × 4)
Step 2: Enabling Interrupts
To receive interrupts, you need:
- Global enable in mstatus
- Specific enable in mie

Step 3: Delegating to Lower Privilege (Optional)
In OS-based systems, Machine Mode can delegate traps to Supervisor Mode.

Step 4: PLIC Setup for External Interrupts
The Platform-Level Interrupt Controller routes device interrupts.

Real-World Scenarios
[A] Bare-Metal – External Interrupt


How It Works
- Trap Vector Setup
- mtvec points to our trap_vector in direct mode (all traps jump here).
- Enabling Interrupts
- Set MEIE bit in mie → enables machine external interrupts.
- Set MIE in mstatus → globally allows interrupts.
- PLIC Setup
- Enables a specific interrupt ID (e.g., UART, GPIO).
- Sets priority threshold to 0 so all priorities are accepted.
- Interrupt Flow
- wfi halts until an interrupt occurs.
- When a device triggers, PLIC signals M-mode → CPU jumps to trap_vector.
- mcause high bit = interrupt (1), low bits = cause code (11 = MEI).
- Claim the interrupt ID from PLIC, handle the device, then complete the claim.
- Returning
- mret restores mepc and resumes main code.
[B] OS-Based – System Call with ecall
The OS uses traps to switch from user mode to kernel mode.

The OS trap handler reads a7 to determine the syscall and services it before sret.
[C] RTOS - M-mode Firmware delegates interrupts to S-mode RTOS
How S-mode RTOS interrupt handling works on RISC-V
- M-mode firmware (like OpenSBI or a custom stub) is still required because hardware boots in M-mode.
- This firmware configures:
- mideleg → delegates certain interrupts (like external or timer) to S-mode.
- medeleg → delegates exceptions to S-mode.
- stvec → points to the S-mode trap handler.
- Then it switches to S-mode and jumps into the RTOS kernel.
- This firmware configures:
- RTOS runs entirely in S-mode.
- All interrupts you’ve delegated now go directly to the S-mode trap handler without trapping to M-mode first.
- The S-mode trap handler:
- Reads scause (cause of trap/interrupt).
- Reads sepc (return address).
- Services the interrupt (e.g., from a PLIC).
- Returns with sret.
- PLIC & Timer
- If using the Platform-Level Interrupt Controller (PLIC), the PLIC is memory-mapped and can be accessed from S-mode (if M-mode firmware allows).
- For timers, the CLINT (Core Local Interruptor) can also be configured for S-mode interrupts (via mideleg).
Below is a simplified RISC-V assembly sequence that would be part of an S-mode RTOS kernel:


How It Works:
- M-mode firmware sets mideleg so external interrupts go to S-mode.
Example:
li t0, (1 << 9) # Delegate Supervisor External Interrupt
csrs mideleg, t0
- RTOS kernel (in S-mode) sets up stvec, sie, and sstatus.
- On interrupt:
- CPU jumps to supervisor_trap_vector (because of stvec).
- scause is checked:
- Bit 31 → interrupt, else exception.
- Code 9 → Supervisor External Interrupt.
- Device serviced → PLIC claim/complete done → sret.
Trap Flow Recap
- Normal execution
- Event occurs (exception or interrupt)
- CPU saves state into CSRs (mepc/sepc, mcause/scause, mtval/stval)
- PC → trap vector (mtvec/stvec)
- Handler runs (bare-metal or OS)
- mret / sret restores state and returns to main thread
RISC-V Traps Summary
- In RISC-V, trap means a transfer of control to trap handler.
- Three kinds of event which cause the CPU to force a trap: System Call[ecall], Exception[Illegal instruction], and Device Interrupt[Timer/External Interrupt]
- In bare-metal embedded systems, traps are usually executed in machine mode - using the RISC-V Machine ISA CSRs: mstatus, mie, mcause, mepc, mtval, mtvec
- In OS-based systems, traps could also be delegated to OS kernel, and executed in supervisor mode – using the RISC-V Supervisor ISA CSRs: sstatus, sie, scause, sepc, stval, stvec
Maven Silicon’s Advanced RISC-V Online Executive Certification Courses - RISC-V IP Design, RISC-V IP Verification, and RISC-V SoC Design empower chip designers to dive deep into the RISC-V ISA and design powerful CPUs, embedded microcontrollers, and complex SoCs.