FPU — блок вычислений с плавающей точкой
Без FPU микроконтроллер считает float программно, целочисленными командами. Блок FPU делает это аппаратно. В ядре Cortex-M4F он необязательный, работает только с одинарной точностью и после сброса выключен — отсюда и типичные вопросы.
Обновлено Редакция GAW
FPU (Floating Point Unit) — аппаратный блок микроконтроллера для операций с числами с плавающей точкой. Без него, по нашему комментарию, компилятор заменяет действия с float вызовами библиотечных функций на целочисленных командах; с ним операция выполняется отдельной командой процессора.
FPU в Cortex-M4F
У ядра Cortex-M4 FPU необязательный: он есть в вариантах Cortex-M4F и реализует расширение FPv4-SP (ARM DUI 0553B, разд. 4.6):
- Только одинарная точность. Сложение, вычитание, умножение, деление, умножение с накоплением, квадратный корень, преобразования между целыми, фиксированной и плавающей точкой — для 32-битных чисел по IEEE 754.
- 32 регистра одинарной точности; для загрузки, сохранения и пересылки их можно использовать как 16 двойных слов.
- Системные регистры: CPACR (доступ), FPCCR (управление контекстом), FPCAR, FPSCR (состояние), FPDSCR.
Отсюда наш вывод: double на Cortex-M4F считается программно и медленно, поэтому в коде для STM32 константы пишут как 1.5f, а для дробной арифметики без FPU применяют числа с фиксированной точкой.
Включение: регистр CPACR
После сброса FPU выключен (разд. 4.6.6). Доступ к нему задают поля CP10 и CP11 регистра CPACR по адресу 0xE000ED88:
| Значение поля | Доступ |
|---|---|
| 0b00 | запрещён — любая команда FPU вызывает отказ NOCP UsageFault |
| 0b01 | только из привилегированного режима |
| 0b10 | зарезервировано |
| 0b11 | полный |
Пример ARM: записать 0xF в биты 20–23 CPACR, затем выполнить DSB и ISB; писать в CPACR можно только в привилегированном режиме. В проектах на CMSIS это делает функция SystemInit из кода запуска: у STM32G4 она открывает CP10 и CP11, если у кристалла есть FPU (__FPU_PRESENT = 1) и компилятор генерирует команды FPU (__FPU_USED = 1). Если FPU не включён, программа падает на первой же команде с float — это разобрано в ответе «Почему STM32 падает в HardFault на float» и в термине HardFault.
Контекст FPU и прерывания
Регистры FPU тоже нужно сохранять при входе в прерывание. Бит ASPEN регистра FPCCR включает аппаратное сохранение и восстановление контекста FPU при входе в исключение и выходе из него, а бит LSPEN — автоматическое ленивое сохранение (табл. 4-51). Значение FPCCR после сброса — 0xC0000000, то есть, по нашему прочтению, оба бита установлены. Об общем времени реакции на прерывание — термин «Задержка прерывания».
Где встречается
FPU одинарной точности есть, например, в STM32G4 (в заголовке stm32g431xx.h __FPU_PRESENT = 1) — см. страницу STM32G4. Наличие FPU у конкретного кристалла проверяют по даташиту или по этому макросу CMSIS.
Частые вопросы
Считает ли FPU Cortex-M4 числа double?
Нет: FPv4-SP в Cortex-M4F поддерживает операции одинарной точности; регистры можно лишь загружать и пересылать как 16 двойных слов (DUI 0553B, разд. 4.6). Отсюда наш вывод: double на Cortex-M4 вычисляется программно и заметно медленнее float, поэтому константы пишут с суффиксом f — 1.5f.
Почему программа падает в HardFault на первой операции с float?
FPU выключен после сброса. Пока поля CP10 и CP11 в CPACR равны 0b00, любая команда FPU вызывает отказ NOCP UsageFault, а если обработчик UsageFault не включён, отказ поднимается до HardFault (DUI 0553B, табл. 4-50 и разд. 2.4.2). Обычно FPU включает SystemInit из CMSIS — подробно в ответе о HardFault на float.
Что такое ленивое сохранение контекста FPU?
Режим, в котором регистры FPU при входе в прерывание сохраняются автоматически, но лениво — его включает бит LSPEN регистра FPCCR (DUI 0553B, табл. 4-51). По значению сброса FPCCR 0xC0000000 биты ASPEN и LSPEN после сброса установлены (наше прочтение таблицы).
Источники
- ARM. Cortex-M4 Devices Generic User Guide (ARM DUI 0553B) — разд. 4.6: необязательный FPU в устройствах Cortex-M4F, расширение FPv4-SP; одинарная точность — сложение, вычитание, умножение, деление, умножение с накоплением, квадратный корень, преобразования; IEEE 754-2008; 32 регистра одинарной точности, доступные и как 16 двойных слов для загрузки, сохранения и пересылки; табл. 4-49: CPACR 0xE000ED88 (сброс 0x00000000), FPCCR 0xE000EF34 (сброс 0xC0000000), FPCAR, FPSCR, FPDSCR; табл. 4-50: поля CP10 и CP11 — 0b00 доступ запрещён, отказ NOCP UsageFault, 0b01 только привилегированный, 0b11 полный; табл. 4-51: ASPEN — установка CONTROL<2> при команде FPU и аппаратное сохранение контекста FPU при входе в исключение и выходе, LSPEN — автоматическое ленивое сохранение; разд. 2.4.2: отказ поднимается до HardFault, если его обработчик не включён; разд. 4.6.6: FPU выключен после сброса, пример 4-1 — запись 0xF в биты 20–23 CPACR, DSB, ISB, только в привилегированном режиме
- ARM. CMSIS 5.9.0, core_cm4.h — макрос __FPU_USED = 1, только если компилятор генерирует команды FPU и у устройства __FPU_PRESENT = 1; для GCC — при __VFP_FP__ и без __SOFTFP__
- STMicroelectronics. cmsis-device-g4 v1.2.6, system_stm32g4xx.c и stm32g431xx.h — SystemInit: при __FPU_PRESENT == 1 и __FPU_USED == 1 устанавливает CP10 и CP11 в полный доступ (SCB->CPACR |= (3UL << 20) | (3UL << 22)); stm32g431xx.h: __FPU_PRESENT 1
Нашли ошибку?
Смотрите также
- Почему STM32F3, G4 или L4 падает в HardFault на операции с float?FPU выключен после сброса: CP10 и CP11 в CPACR до первой операции с float
- HardFaultИсключение Cortex-M при необработанной ошибке
- Код запуска (startup)Что выполняется до main: стек, .data, .bss
- Формат QQm.n: дробные числа в целых, Q15 и Q31
- STM32G4Cortex-M4 с FPU, до 170 МГц: G431/G441, G471–G484, G491/G4A1 — линейки и распиновки
- Задержка прерыванияВремя от запроса до обработчика: Cortex-M3 и AVR