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
- compiler_barrier function
- completion_barrier function
- device_barrier function
- disable_interrupts function
- interrupt_mask function
- restore_interrupts function
- wait_for_interrupt function
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: () -> nonePrevent compiler memory knowledge from moving across this point. This emits no device-completion protocol.
completion_barrier function
public completion_barrier: () -> noneIssue a data synchronization barrier (DSB) to wait for preceding explicit memory accesses to complete.
device_barrier function
public device_barrier: () -> noneIssue 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)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)Read the current PRIMASK interrupt mask.
restore_interrupts function
public restore_interrupts: (previous: u32) -> noneRestore 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: () -> noneIssue 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.