PLIC

From OSDev.wiki
Jump to navigation Jump to search
This page is a stub.
You can help the wiki by accurately adding more contents to it.

The PLIC is the RISC-V equivalent of the PIC for x86 platforms. Without it, no external interrupts can be received (on "standard" platforms, anyways).

Quickstart

First, figure out the start address & size of the PLIC MMIO region & map it. In the DTB there should be an entry like this:

plic@c000000 {
	phandle = < 0x03 >;
	riscv,ndev = < 0x35 >;
	reg = < 0x00 0xc000000 0x00 0x210000 >;
	interrupts-extended = < 0x02 0x0b 0x02 0x09 >;
	interrupt-controller;
	compatible = "riscv,plic0";
	#interrupt-cells = < 0x01 >;
	#address-cells = < 0x00 >;
};

Then, determine which interrupt source(s) to enable. For example, to receive interrupts from the UART device, interrupt 0x0A must be enabled:

uart@10000000 {
	interrupts = < 0x0a >;
	interrupt-parent = < 0x03 >;
	clock-frequency = < 0x384000 >;
	reg = < 0x00 0x10000000 0x00 0x100 >;
	compatible = "ns16550a";
};

Interrupts are enabled on a per-context basis. The exact meaning of a context is implementation-dependent but generally even-numbered contexts (0, 2, ...) refer to M-mode of hart 0, 1, ... while odd-numbered contexts (1, 3, ...) refer to S-mode of hart 0, 1, ...

For example, to enable interrupt 0x0A for S-mode in hart 0 the 10th bit at base + 0x2000 + 0x80 * context + source / 32 must be toggled. Note that the registers are all 32 bits!

The priority and priority thresholds must be set to actually enable the interrupt. base + 0x0 + 0x4 * source must be set to any non-zero value and base + 0x200000 + 0x1000 * context to any value lower than the priority.

Finally, enable any interrupts on the UART device:

uart_base[0] = 0x00; // Disable DLAB
uart_base[1] = 0x01; // Enable data available interrupts

If the trap handler is set up properly, an external interrupt should be triggered when any characters are sent.

Handling interrupts

To begin handling an interrupt, the interrupt must be claimed by reading base + 0x200000 + 0x4 + 0x1000 * context, which will return the source. This automatically clears the pending bit.

The interrupt must be claimed before returning to userspace! Not doing so will cause it to be triggered again immediately.

When the interrupt handler has finished, write the source to the same address to indicate it is completed.

Note: the interrupt should not be marked as completed until whatever caused it has actually been dealt with, e.g. don't mark an interrupt from UART as completed until all the data has been read.

See Also

External Links