Landin library reference source

platform/cpu

Target scope is enforced by the module assertion below.

M-profile interrupt masking, waiting and memory barriers.

Save the mask from disable_interrupts and restore it, including on early returns. NMI and HardFault are not masked. Waiting can wake spuriously; barriers do not prove completion of a device protocol or supply a DMA lifetime discipline.

compiler.assert(compiler.arch == cortex_m0)

Items

Executable example

This complete program is maintained in the repository runtime tests. View source.

import platform/cpu

mut initialized: u32 = 0x670
mut cleared: u32 = 0
mut observed: u32 = 0
mut interrupts: u32 = 0
mut handler_mask: u32 = 99
mut discarded: u32 = 0
mut evaluations: u32 = 0

operand: () -> (carrier: u32) =
    evaluations = evaluations + 1
    carrier = u32(usize(addr discarded))
end operand

skip_assembly: (early: bool) -> (result: u32) =
    result = 41
    _ = assembler.block("movs r1, #99\nstr r1, [r0]", begin
        return when early
        operand()
    end)
    result = 42
end skip_assembly

link(vector: 16)
extern(interrupt) irq: () -> none =
    previous := cpu.disable_interrupts()
    nested := cpu.disable_interrupts()
    cpu.restore_interrupts(nested)
    handler_mask = previous + cpu.interrupt_mask()
    cpu.restore_interrupts(previous)
    interrupts = interrupts + 1
end irq

transport: (item: type, value: u32) -> (result: u32) =
    result = assembler.block("adds r0, #7\nmovs r1, #99\nmovs r4, #88", value)
end transport

--  [1630]'s named operands: several outputs destructured, a block that
--  holds every low register, general passing over a register the text
--  writes, and a signed byte extended to the whole register on the way in.
split: (value: u32) -> (low: u32, high: u32) =
    (l, h) := assembler.block("lsrs {h}, {v}, #16\nuxth {l}, {v}",
                              out l: u32 at general, out h: u32 at general,
                              in v: u32 at general = value)
    low = l
    high = h
end split

every_register: (base: u32) -> (total: u32) =
    (a, b, c, d, e, f, g, h) := assembler.block(
        "adds r1, r0, #1\nadds r2, r0, #2\nadds r3, r0, #3\nadds r4, r0, #4\nadds r5, r0, #5\nadds r6, r0, #6\nadds r7, r0, #7",
        inout a: u32 at r0 = base, out b: u32 at r1, out c: u32 at r2,
        out d: u32 at r3, out e: u32 at r4, out f: u32 at r5,
        out g: u32 at r6, out h: u32 at r7)
    total = a + b + c + d + e + f + g + h
end every_register

avoided: (value: u32) -> (result: u32) =
    result = assembler.block("movs r0, #0\nmovs r1, #0\nadds {r}, {r}, #1",
                             inout r: u32 at general = value)
end avoided

widened: (value: i8) -> (result: i32) =
    result = assembler.block("mov {r}, {v}", out r: i32 at general,
                             in v: i8 at general = value)
end widened

temporary_mask: (early: bool) -> (masked: u32) =
    previous := cpu.disable_interrupts()
    defer cpu.restore_interrupts(previous)
    masked = cpu.interrupt_mask()
    return when early
    masked = masked + cpu.interrupt_mask()
end temporary_mask

start: () -> none =
    observed = 1
    return when initialized <> 0x670 or cleared <> 0
    return when cpu.interrupt_mask() <> 0
    previous := cpu.disable_interrupts()
    return when previous <> 0 or cpu.interrupt_mask() <> 1
    nested := cpu.disable_interrupts()
    return when nested <> 1
    cpu.restore_interrupts(nested)
    return when cpu.interrupt_mask() <> 1
    cpu.restore_interrupts(previous)
    return when cpu.interrupt_mask() <> 0
    return when temporary_mask(true) <> 1 or cpu.interrupt_mask() <> 0
    return when temporary_mask(false) <> 2 or cpu.interrupt_mask() <> 0
    outer := cpu.disable_interrupts()
    return when temporary_mask(true) <> 1 or cpu.interrupt_mask() <> 1
    cpu.restore_interrupts(outer)

    mut value: u32 = 35
    answer := transport(item: u32, value: value)
    return when answer <> 42 or value <> 35
    before := discarded
    _ = assembler.block("movs r1, #73\nstr r1, [r0]", operand())
    return when before <> 0 or discarded <> 73 or evaluations <> 1
    return when skip_assembly(true) <> 41
    return when discarded <> 73 or evaluations <> 1
    return when value <> 35
    (low, high) := split(0x12345678)
    return when low <> 0x5678 or high <> 0x1234
    return when every_register(10) <> 108
    return when avoided(41) <> 42
    return when widened(-3) <> -3

    saved := cpu.disable_interrupts()
    enable: ptr mut u32 = ptr(0xe000e100)
    pending: ptr mut u32 = ptr(0xe000e200)
    compiler.volatile_store(enable, 1)
    compiler.volatile_store(pending, 1)
    cpu.completion_barrier()
    cpu.wait_for_interrupt()
    return when interrupts <> 0 or cpu.interrupt_mask() <> 1
    cpu.restore_interrupts(saved)
    while interrupts == 0 do
        cpu.wait_for_interrupt()
    end while
    return when interrupts <> 1 or handler_mask <> 1
    return when cpu.interrupt_mask() <> 0
    cpu.device_barrier()
    cpu.compiler_barrier()
    observed = 0x670
end start

compiler_barrier function

public compiler_barrier: () -> none

platform/cpu/cpu.ldn:47

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

completion_barrier function

public completion_barrier: () -> none

platform/cpu/cpu.ldn:41

Issue a data synchronization barrier (DSB) to wait for preceding explicit memory accesses to complete.

device_barrier function

public device_barrier: () -> none

platform/cpu/cpu.ldn:35

Issue a device memory barrier (DMB) to order memory accesses. This is not a proof that a device operation completed.

disable_interrupts function

public disable_interrupts: () -> (previous: u32)

platform/cpu/cpu.ldn:8

Disable maskable interrupts and return the previous PRIMASK. Save the result and pass it to restore_interrupts, including on early returns. NMI and HardFault remain unmasked.

interrupt_mask function

public interrupt_mask: () -> (current: u32)

platform/cpu/cpu.ldn:21

Read the current PRIMASK interrupt mask.

restore_interrupts function

public restore_interrupts: (previous: u32) -> none

platform/cpu/cpu.ldn:15

Restore a saved PRIMASK value. Only bit zero is implemented; use the value returned by disable_interrupts to preserve nesting.

wait_for_interrupt function

public wait_for_interrupt: () -> none

platform/cpu/cpu.ldn:29

Issue a completion barrier and wait for an interrupt. Wakeups can be spurious; recheck the condition after waking.

Architectural completion before sleep does not acknowledge a device.