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.
