EdigE
← ЛЕНТА

STM32U375 перестал засыпать, пока функцию не вынесли из кода

Переход в глубокий сон обычно выглядит скучным и предсказуемым: программа выставляет режим в PWR, готовит источники пробуждения, включает SLEEPDEEP и исполняет WFI. Тем страннее история с STM32U375, где небольшая правка далеко от этого места могла оставить микроконтроллер бодрствовать.

Автор отчёта на ST Community долго ловил эффект токовым анализатором Joulescope. В его проекте функция входа в Standby начала работать стабильно после `__attribute__((noinline))`; симптом также исчезал при запрете оптимизации этой функции. Смысл атрибута здесь не в новом обращении к регистрам PWR. Компилятор перестаёт вклеивать тело функции в вызывающий код и перестраивает карту инструкций в flash.

Сон начинается в Cortex-M и заканчивается в домене питания

### Сон начинается в Cortex-M и заканчивается в домене питания

Для обычного Standby документация STM32U375 требует выбрать LPMS=`10x`, отключить retention для SRAM2, установить SLEEPDEEP и войти через WFI, WFE либо возврат из обработчика прерывания. В этом протоколе нет условия про адрес функции, оптимизацию или `noinline`. В актуальной errata ES0626 также отсутствует известное ограничение, связывающее Standby с выравниванием кода.

Почему один удачный адрес не является workaround

Автор сравнил assembly участка, который готовит сон, и не увидел очевидной функциональной разницы. Изменилась главным образом его позиция после inlining. Отсюда родилась гипотеза о нескольких тактах вокруг WFI: выравнивание выборки инструкций, незавершённая запись на шине, pending interrupt, событие пробуждения или состояние периферийного блока могут попасть в иной временной рисунок. Форумный ответ сотрудника ST допускает такую связь, однако площадка пометила этот ответ как «reviewed answer mostly from AI». Это направление диагностики, а не подтверждённый silicon bug.

### Почему один удачный адрес не является workaround

Low-power routine стоит сделать измеряемой

Пока доказано только наблюдение одного автора: в его сборках раскладка кода коррелировала с входом в Standby, а `noinline` убирал симптом. Из этого нельзя вывести дефект fetch pipeline STM32U375. Для такого вывода нужны минимальный проект, версии компилятора и линкера, ELF map, адрес WFI, ревизия кристалла, регистры PWR, EXTI, NVIC и DBGMCU непосредственно перед сном, а также токовые трассы удачной и сбойной прошивок.

Контекст осложняет более ранняя ветка того же автора. Там похожий отказ в Standby в итоге удалось связать со SPI1: после сброса блока через RCC режим заработал. Это не опровержение свежего наблюдения, но хороший урок против красивой единственной причины. Одинаковый симптом «не уснул» может появиться и от периферии, и от флагов пробуждения, и от порядка записей, и от расположения кода.

### Low-power routine стоит сделать измеряемой

Для повторяемого входа в сон полезно вынести последовательность в маленькую отдельную функцию и проверять её дизассемблирование в release-сборке. Стоит сравнить `-O0`, `-O2` и `-Os`, временно отключить IRQ, снять состояние wake-up flags и pending NVIC, проверить reset domains активных периферийных блоков. Барьеры DSB и ISB имеют смысл добавлять только по правилам архитектуры и конкретной последовательности записей, затем снова смотреть машинный код и токовую трассу.

Здесь красив не сам атрибут `noinline`. Он лишь сделал видимой границу, которую C-код обычно прячет: между записью в регистр, конвейером Cortex-M, шиной, прерыванием и отключением тактирования нет абстрактной стены. Если устройство засыпает только при удачной раскладке бинарника, случайный адрес функции уже стал параметром стенда.