Перейти к содержанию

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, в том числе HardFaultARMv7-M: Cortex-M3, M4
BASEPRI ≠ 0исключения с приоритетом, численно большим или равным BASEPRIARMv7-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.

Источники

  1. 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: исключение входит, только если его приоритет выше пределов, заданных масками, иначе остаётся ожидающим
  2. 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 до ненулевого значения)
  3. STMicroelectronics. RM0090 Reference manual STM32F405/415, STM32F407/417, STM32F427/437, STM32F429/439 (Rev 21) — разд. 12.1.1: 16 программируемых уровней приоритета, используются 4 бита приоритета
  4. Arm. Cortex-M0 Devices Generic User Guide, DUI 0497A, 2009 — разд. 2.1.3: у Cortex-M0 единственный регистр масок — PRIMASK; FAULTMASK и BASEPRI отсутствуют
  5. 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 меняет маску, только если новое значение повышает порог

Нашли ошибку?