PRIMASK, FAULTMASK и BASEPRI
Чтобы прочитать переменную, которую меняет прерывание, программу на Cortex-M на время защищают от прерываний. Сделать это можно тремя регистрами, и самый грубый способ — запретить вообще всё — не всегда лучший.
Обновлено Редакция GAW
Содержание
PRIMASK, FAULTMASK и BASEPRI — регистры масок исключений ядра Cortex-M. Они не запрещают прерывания в NVIC, а временно не дают ядру входить в обработчики: исключение с недостаточным приоритетом становится ожидающим и запускается, как только маска снята (DUI 0552A, разд. 2.3.7).
Три регистра
| Регистр | Что запрещает | Где есть |
|---|---|---|
| PRIMASK = 1 | все исключения с настраиваемым приоритетом; NMI и HardFault работают | все Cortex-M |
| FAULTMASK = 1 | все исключения, кроме NMI, в том числе HardFault | ARMv7-M: Cortex-M3, M4 |
| BASEPRI ≠ 0 | исключения с приоритетом, численно большим или равным BASEPRI | ARMv7-M: Cortex-M3, M4 |
По DUI 0552A, разд. 2.1.3. FAULTMASK ядро само сбрасывает при выходе из любого обработчика, кроме NMI. Доступ к регистрам — командами MRS и MSR, PRIMASK и FAULTMASK переключает и команда CPS; непривилегированный код CPS выполнять не может. У Cortex-M0 есть только PRIMASK (DUI 0497A, разд. 2.1.3).
Порог BASEPRI
BASEPRI — 8-битное поле, которое сравнивается с приоритетом так же, как поля регистров приоритетов NVIC: нереализованные младшие биты читаются как ноль. У STM32 реализованы 4 старших бита, поэтому порог уровня 5 записывают как 5 << 4:
uint32_t old = __get_BASEPRI();
__set_BASEPRI(5U << 4); /* levels 5..15 wait, levels 0..4 still run */
shared_counter++;
__set_BASEPRI(old);
Функция CMSIS __set_BASEPRI_MAX() пишет в регистр BASEPRI_MAX: значение меняется, только если новый порог строже текущего, поэтому вложенные секции не ослабляют маску. Какой уровень считать «строже», зависит от группировки приоритетов: вытеснение определяет только группа.
Что выбрать
- PRIMASK (
__disable_irq(),__enable_irq()) — короткая критическая секция, когда общие данные трогают любые прерывания. Восстанавливают прежнее значение, а не включают прерывания безусловно, иначе вложенная секция сломает внешнюю. - BASEPRI — когда срочные прерывания нельзя задерживать. ОС реального времени для Cortex-M3/M4 обычно так и защищают свои структуры, оставляя самые приоритетные прерывания вне ядра ОС.
- FAULTMASK — редкий случай: обработчик отказа, которому нужно поднять свой приоритет до уровня HardFault.
Без масок обходятся атомарные операции LDREX и STREX. Подробно о гонках данных между программой и обработчиками — в статье «Прерывания и конкурентный доступ».
Частые вопросы
Чем BASEPRI лучше __disable_irq()?
__disable_irq() ставит PRIMASK и запрещает все прерывания с настраиваемым приоритетом, в том числе срочные, которым критична задержка. BASEPRI задаёт порог: прерывания с приоритетом численно не меньше порога ждут, а более срочные продолжают работать. Так защищают данные, общие с медленными прерываниями, не задерживая, например, управление двигателем.
Почему запись BASEPRI = 0 не запрещает прерывания?
Ноль в BASEPRI означает «маски нет». Порог задают ненулевым значением, и оно сравнивается с приоритетом в старших битах байта: у STM32 реализовано 4 бита, поэтому уровень 5 записывают как 5 << 4 = 0x50. Тогда ждут все прерывания с приоритетами 5–15, а уровни 0–4 обслуживаются.
Есть ли BASEPRI на Cortex-M0?
Нет. У Cortex-M0 и M0+ из регистров масок есть только PRIMASK, поэтому критическую секцию там делают полным запретом прерываний. BASEPRI и FAULTMASK появились в архитектуре ARMv7-M — у Cortex-M3 и M4.
Источники
- Arm. Cortex-M3 Devices Generic User Guide, DUI 0552A, 2010 — разд. 2.1.3, табл. 2-7 — 2-9: PRIMASK (бит 0) запрещает активацию всех исключений с настраиваемым приоритетом; FAULTMASK (бит 0) — всех исключений, кроме NMI, ядро сбрасывает его при выходе из любого обработчика, кроме NMI; BASEPRI (биты 7:0) — минимальный приоритет обработки: при ненулевом значении не обрабатываются исключения с приоритетом, численно большим или равным BASEPRI, 0x00 — без действия; нереализованные младшие биты читаются как 0; доступ командами MRS и MSR, PRIMASK и FAULTMASK меняет и команда CPS; разд. 2.1.1: непривилегированный код не может выполнять CPS; разд. 2.3.7: исключение входит, только если его приоритет выше пределов, заданных масками, иначе остаётся ожидающим
- Arm. ARMv7-M Architecture Reference Manual, ARM DDI 0403C_errata_v3, 2010 — B1.4.3: три регистра для повышения приоритета — PRIMASK (1 бит), BASEPRI (8 бит), FAULTMASK (1 бит); все сбрасываются в 0 при сбросе, непривилегированные записи игнорируются; при числе приоритетов меньше 256 младшие биты BASEPRI читаются как 0; запись в BASEPRI_MAX меняет BASEPRI, только если повышает приоритет (уменьшает BASEPRI до ненулевого значения)
- STMicroelectronics. RM0090 Reference manual STM32F405/415, STM32F407/417, STM32F427/437, STM32F429/439 (Rev 21) — разд. 12.1.1: 16 программируемых уровней приоритета, используются 4 бита приоритета
- Arm. Cortex-M0 Devices Generic User Guide, DUI 0497A, 2009 — разд. 2.1.3: у Cortex-M0 единственный регистр масок — PRIMASK; FAULTMASK и BASEPRI отсутствуют
- Arm. CMSIS 6, CMSIS-Core cmsis_gcc.h, cmsis_gcc_m.h (v6.4.0) — __enable_irq(), __disable_irq() — команды CPSIE i и CPSID i; __disable_fault_irq(); __set_PRIMASK(), __set_FAULTMASK(), __set_BASEPRI(), __set_BASEPRI_MAX() — запись BASEPRI_MAX меняет маску, только если новое значение повышает порог
Нашли ошибку?
Смотрите также
- Критическая секцияУчасток кода под запретом прерываний
- Атомарная операцияОперация, которую прерывание не застанет наполовину
- NVIC — контроллер прерыванийКонтроллер прерываний Cortex-M: приоритеты и вытеснение
- Группировка приоритетов (PRIGROUP)Деление приоритета на группу вытеснения и подприоритет полем PRIGROUP
- Прерывания и общие данныеISR на AVR и Cortex-M, атомарность, критические секции, кольцевой буфер
- Регистры ядра Cortex-MR0–R12, SP, LR, PC, xPSR и специальные регистры: что в каждом