platform/riscv
skip to the library navigationImportable on every target; it declares its items only where compiler.arch == rv32.
RV32 machine-mode interrupt masking, the core's own interrupt lines, waiting and memory barriers, declared on rv32 alone.
Save the state from disable_interrupts and restore it, including on early returns: restore_interrupts sets mstatus.MIE again only if it was set. enable_lines and disable_lines write mie, the machine software, timer and external interrupts on the cores that have them; an ESP32's CPU lines are enabled through its interrupt matrix or CLIC, which are the chip's generated modules.
Waiting can wake spuriously. Barriers do not prove completion of a device protocol or supply a DMA lifetime discipline.
Items
When compiler.arch == rv32
public compiler_barrier: () -> none
Prevent compiler memory knowledge from moving across this point. This emits no device-completion protocol.
When compiler.arch == rv32
public completion_barrier: () -> none
Wait for preceding explicit memory accesses to complete, as far as RISC-V's FENCE orders them.
When compiler.arch == rv32
public device_barrier: () -> none
Order memory and device accesses (a FENCE). This is not a proof that a device operation completed.
When compiler.arch == rv32
public disable_interrupts: () -> (previous: u32)
Clear mstatus.MIE, which masks every machine-mode interrupt, and return its previous state. Save the result and pass it to restore_interrupts, including on early returns.
When compiler.arch == rv32
public disable_lines: (lines: u32) -> none
Clear the bits of LINES in mie.
When compiler.arch == rv32
public enable_lines: (lines: u32) -> none
Set the bits of LINES in mie, enabling those of the core's own interrupts: machine software 0x8, timer 0x80 and external 0x800 on the cores with the standard set. A core whose lines an interrupt matrix or a CLIC enables, the ESP32-C3's and the ESP32-C5's, is enabled through the chip's own registers instead.
When compiler.arch == rv32
public interrupt_mask: () -> (current: u32)
Read whether machine-mode interrupts are enabled: mstatus.MIE, as 8 when they are and 0 when they are not.
When compiler.arch == rv32
public restore_interrupts: (previous: u32) -> none
Restore a state disable_interrupts returned: set mstatus.MIE again only if it was set then, which preserves nesting.
When compiler.arch == rv32
public wait_for_interrupt: () -> none
Order earlier memory and device accesses before the wait, then wait for an interrupt. Wakeups can be spurious, and a pending interrupt wakes the core even while mstatus.MIE is clear; recheck the condition after waking.
Example program
This complete program is one of the compiler's runtime tests. View source.
The program, 36 lines
-- platform/riscv's critical section, nested, on rv32: masking clears -- mstatus.MIE, a nested mask returns it already clear, and restoring in -- reverse order gives back exactly the state the program started with, -- whether reset left interrupts enabled or not. The module declares -- nothing on the other targets (D687), so the program imports it -- everywhere and calls it from the rv32 arm. import platform/riscv fixed if compiler.arch == rv32 then nested_masks_restore: () -> (ok: bool) = before: u32 = riscv.interrupt_mask() outer: u32 = riscv.disable_interrupts() inner: u32 = riscv.disable_interrupts() masked: u32 = riscv.interrupt_mask() riscv.compiler_barrier() riscv.restore_interrupts(inner) still: u32 = riscv.interrupt_mask() riscv.restore_interrupts(outer) after: u32 = riscv.interrupt_mask() riscv.device_barrier() riscv.completion_barrier() ok = outer == before and inner == 0 and masked == 0 and still == 0 and after == before end nested_masks_restore else nested_masks_restore: () -> (ok: bool) = ok = true end nested_masks_restore end if public main: () -> (code: i32) = code = 1 if nested_masks_restore() then code = 42 end if end main