Sound Byte Libs 0.5.1-121-g3358a44
C++ firmware library for audio applications on 32-bit ARM Cortex-M processors
Loading...
Searching...
No Matches
control.hpp
Go to the documentation of this file.
1/**
2 * @file control.hpp
3 * @brief ARM Cortex-M interrupt masking primitives
4 * @ingroup hal
5 *
6 * Two masks, two jobs:
7 *
8 * - BASEPRI raises the execution priority to a ceiling. Handlers above the
9 * ceiling keep running — the audio ISR is never delayed by a critical
10 * section anywhere in main or control code. ScopedControlCritical is the
11 * default critical section.
12 * - PRIMASK stops every configurable exception, audio included. It is for
13 * death paths (assert, fault) and nothing else.
14 *
15 * The rule (research doc §4.3 item 10): nothing above the ceiling touches
16 * state that main or the control tick protect; nothing at or below it uses
17 * PRIMASK. Cortex-M7 r1p1 (the STM32H750) does not carry the r0p1 BASEPRI
18 * erratum 837070, so a plain MSR is correct.
19 *
20 * On targets without BASEPRI (ARMv6-M) or on the host, the BASEPRI
21 * primitives are no-ops: the workbench has no interrupts to mask, and the
22 * bridge invariants are enforced by construction there (single control
23 * context, see FDP-073).
24 */
25
26#ifndef SBL_HW_HAL_INTERRUPTS_CONTROL_HPP_
27#define SBL_HW_HAL_INTERRUPTS_CONTROL_HPP_
28
29#include <cstdint>
31
32#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__)
33#define SBL_HAS_BASEPRI 1
34#endif
35#if defined(__arm__) && !defined(__linux__)
36#define SBL_HAS_PRIMASK 1
37#endif
38
39namespace sbl {
40namespace hal {
41namespace interrupts {
42
43// ---------------------------------------------------------------------------
44// BASEPRI — the critical section that never blocks audio
45// ---------------------------------------------------------------------------
46
47/// Read BASEPRI (0 = no ceiling).
48inline uint32_t get_basepri() {
49#if defined(SBL_HAS_BASEPRI)
50 uint32_t v;
51 __asm volatile("mrs %0, basepri" : "=r"(v));
52 return v;
53#else
54 return 0;
55#endif
56}
57
58/// Write BASEPRI unconditionally (used to restore a saved value).
59inline void set_basepri(uint32_t encoded) {
60#if defined(SBL_HAS_BASEPRI)
61 __asm volatile("msr basepri, %0" : : "r"(encoded) : "memory");
62#else
63 (void)encoded;
64#endif
65}
66
67/// Raise BASEPRI to `encoded` only if that is a higher priority than the
68/// current value — never lowers it, so nested sections compose.
69inline void raise_basepri(uint32_t encoded) {
70#if defined(SBL_HAS_BASEPRI)
71 __asm volatile("msr basepri_max, %0" : : "r"(encoded) : "memory");
72#else
73 (void)encoded;
74#endif
75}
76
77/**
78 * @brief RAII critical section against the control tick and everything below.
79 *
80 * Raises BASEPRI to `kBasepriCeiling` (Level::ControlTick) for its scope and
81 * restores the previous value on exit. Safe to nest. Handlers above the
82 * ceiling — audio, USB/MIDI, other DMA — are unaffected by construction.
83 *
84 * Use it around any multi-word state shared between main and the control
85 * tick. Do not use it from a handler above the ceiling (that handler must
86 * not touch such state at all), and do not reach for PRIMASK instead.
87 */
100
101// ---------------------------------------------------------------------------
102// PRIMASK — everything off. Death paths only.
103// ---------------------------------------------------------------------------
104
105/**
106 * @brief Disable all configurable exceptions and return the previous PRIMASK.
107 *
108 * This blocks the audio ISR. It exists for assert/fault handling, where the
109 * system is already lost; a critical section wants ScopedControlCritical.
110 */
111inline uint32_t disable_and_save() {
112#if defined(SBL_HAS_PRIMASK)
113 uint32_t saved;
114 __asm volatile("mrs %0, primask" : "=r"(saved));
115 __asm volatile("cpsid i" : : : "memory");
116 return saved;
117#else
118 return 0;
119#endif
120}
121
122/// Restore PRIMASK from disable_and_save().
123inline void restore(uint32_t saved_state) {
124#if defined(SBL_HAS_PRIMASK)
125 __asm volatile("msr primask, %0" : : "r"(saved_state) : "memory");
126#else
127 (void)saved_state;
128#endif
129}
130
131/// Current PRIMASK (0 = enabled, 1 = disabled).
132inline uint32_t get_state() {
133#if defined(SBL_HAS_PRIMASK)
134 uint32_t v;
135 __asm volatile("mrs %0, primask" : "=r"(v));
136 return v;
137#else
138 return 0;
139#endif
140}
141
142/// True if configurable exceptions are enabled (PRIMASK clear).
143inline bool are_enabled() { return get_state() == 0; }
144
145} // namespace interrupts
146} // namespace hal
147} // namespace sbl
148
149#endif // SBL_HW_HAL_INTERRUPTS_CONTROL_HPP_
RAII critical section against the control tick and everything below.
Definition control.hpp:88
ScopedControlCritical(const ScopedControlCritical &)=delete
ScopedControlCritical & operator=(const ScopedControlCritical &)=delete
Interrupt system types: execution levels and trigger kinds.
constexpr uint8_t encode(Level level)
Encode a level as the byte written to NVIC->IP[] / SCB->SHP[] / BASEPRI.
Definition types.hpp:62
uint32_t get_basepri()
Read BASEPRI (0 = no ceiling).
Definition control.hpp:48
void restore(uint32_t saved_state)
Restore PRIMASK from disable_and_save().
Definition control.hpp:123
uint32_t disable_and_save()
Disable all configurable exceptions and return the previous PRIMASK.
Definition control.hpp:111
void raise_basepri(uint32_t encoded)
Definition control.hpp:69
constexpr Level kBasepriCeiling
The level a ScopedControlCritical raises BASEPRI to.
Definition types.hpp:56
void set_basepri(uint32_t encoded)
Write BASEPRI unconditionally (used to restore a saved value).
Definition control.hpp:59
uint32_t get_state()
Current PRIMASK (0 = enabled, 1 = disabled).
Definition control.hpp:132
bool are_enabled()
True if configurable exceptions are enabled (PRIMASK clear).
Definition control.hpp:143
Root namespace for all Sound Byte Libs code.
Definition assert.hpp:51