InnocentZero's Treasure Chest

HomeFeedAbout MeList of interesting people

24 Oct 2025

Peripherals and Interrupts in RISCV

Peripherals and Interrupts in RISCV

They are also mapped to the memory. And properties can be changed using that memory area.

The driver can be blocking or non-blocking. If blocking, it takes up a lot of CPU power as it keeps polling the peripheral for data. If it is non-blocking, then there are interrupts then they are detected at hardware level within a clock cycle.

Interrupts

If there is an interrupt, then the program jumps to the supplied interrupt service routine and starts executing from there and returns to the OG instruction once everything is done.

Hardware interrupts are done by a bus manager like the network controller (that straight away receives a bunch of kilobytes) and use DMA (Direct Memory Access) to straight away start using the memory.

There are software interrupts (for accessing devices like IO, sockets, etc.) and hardware interrupts for, well, other stuff.

Apart from that there are exceptions as well that are raised by the CPU when buttfuckery like division by 0 happens.

All the peripherals are connected to the PLIC (Platform Level Interrupt Controller). There is also CLINT (Core Local Interrupt Timer) that gives each core timer interrupts that can be used a scheduler.

Interrupts have modes that define privilege level:

  • Machine level: M mode, most privileged
  • Supervisor level: S mode, OS privilege
  • User level: U mode, all binaries run here

In Intel machines these are called rings.

Interrupt Handling

What caused the interrupt?

mcause has the first bit reserved for the interrupt type (interrupt or error) and the rest 31 bits are used for indicating the subtype of the interrupt.

If the first bit is 1 then it was an interrupt else it was an exception.

To find out what peripheral caused the interrupt, the CPU uses the value given by PLIC.

Filled automatically by the hardware when an interrupt happens.

Where is the interrupt vector address?

mtvec stores the address of the interrupt vector.

If the mode value is set to 1, it is vectored. The pc is set to BASE + 4 * cause.

If mode is set to 0, it is a direct interrupt and all exceptions set pc to BASE.

2 and 3 are reserved for future use.

NOT hardcoded!!

Where to return to?

mepc holds the value of pc when the interrupt occurred.

mret instruction loads the mepc to the pc.

What was the previous status level?

mstatus register has 4 bits that are mostly relevant to us.

MPP[1:0] stores the previous execution mode. These are the 12th and 11th LSB, 0 indexed.

  • User mode: 00
  • Supervisor mode: 01
  • Machine mode: 11

MPIE holds the interrupt enable status in the previous mode. This is used to determine if the previous mode had interrupts enabled for the lower privileges. This is the 7th LSB, same scheme as above.

MIE holds the interrupt enable status in the current mode. 3rd LSB, same scheme.

Nested interrupts

To enable a nested interrupt, copy the data from mepc, mcause and mstatus.

mie register enables interrupts in machine mode if set. It is set to zero at the start of the interrupt to make sure no other interrupts disturb the copying of the data during a nested interrupt.

  • 11th bit for external/DMA interrupt.
  • 7th bit for timer interrupt.
  • 3rd bit for Software interrupt.

These are 0 indexed LSBs.

Pending Interrupts

mip register holds the important bits for any interrupt that wasn't executed in between an instruction.

Bit scheme is the same as above.


Other posts
Creative Commons License
This website by innocentzer0 is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.