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

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 после сброса установлены (наше прочтение таблицы).

Источники

  1. 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, только в привилегированном режиме
  2. ARM. CMSIS 5.9.0, core_cm4.h — макрос __FPU_USED = 1, только если компилятор генерирует команды FPU и у устройства __FPU_PRESENT = 1; для GCC — при __VFP_FP__ и без __SOFTFP__
  3. 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

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