Landin library reference discuss source

Importable 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

compiler_barrier

function platform/riscv/riscv.ldn:69 ↑ index

When compiler.arch == rv32

public compiler_barrier: () -> none

Prevent compiler memory knowledge from moving across this point. This emits no device-completion protocol.

completion_barrier

function platform/riscv/riscv.ldn:63 ↑ index

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.

device_barrier

function platform/riscv/riscv.ldn:57 ↑ index

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.

disable_interrupts

function platform/riscv/riscv.ldn:14 ↑ index

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.

disable_lines

function platform/riscv/riscv.ldn:43 ↑ index

When compiler.arch == rv32

public disable_lines: (lines: u32) -> none

Clear the bits of LINES in mie.

enable_lines

function platform/riscv/riscv.ldn:38 ↑ index

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.

interrupt_mask

function platform/riscv/riscv.ldn:28 ↑ index

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.

restore_interrupts

function platform/riscv/riscv.ldn:21 ↑ index

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.

wait_for_interrupt

function platform/riscv/riscv.ldn:51 ↑ index

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
compiler/tests/fixtures/runtime/library-platform-riscv/main.ldn36 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