Кто любит RISC в жизни, заходим, не стесняемся.
Ответить

Variable optimized out

Пт июн 25, 2021 23:23:16

Доброго времени суток.

Перехожу с аврок на стм32, приходится писать на С. Столкнулся с проблемой: при включении оптимизации компилятора нижеприведенный код (приведен в упрощенном виде) выщёлкивается из итогового образа напрочь.

Код:
void InitRCC_HSE() {
   uint32_t cycle_counter;

   cycle_counter = 0;

   //Ждем заданное количество циклов флага стабильности частоты HSE
   while (READ_BIT(RCC->CR, RCC_CR_HSERDY) == 0U) {
      cycle_counter++;

      if (cycle_counter == HSE_READY_TIMEOUT) {
         //дальше обработка ошибки
      }
   }

Если cycle_counter объявить как volatile - всё работает, но если нет, то цикл при включенной оптмизиации в итоговый код не попадает.
Как же так, условие в while(...) же не статическое, RCC->CR в коде CMSIS объявлено как __IO uint32_t CR; где __IO - алиас для того же volatile, соответственно, его значение на этапе компиляции не определено. Чтобы выкинуть кусок кода, компилятор должен быть уверен, что код никогда не будет выполнен. Здесь же он вполне себе выполняется из-за того, что значение регистра меняется извне, и об этом компилятору дана соответствующая директива (__IO uint32_t CR;).

Вопрос - в чем логика такого поведения компилятора (gcc@STM32CubeIDE).

Re: Variable optimized out

Сб июн 26, 2021 00:02:06

Такой код компилятор выкидывать не должен, мой gcc 10 ничем подобным и не занимается.

Re: Variable optimized out

Сб июн 26, 2021 00:21:55

должен быть квалификатор volatile

Re: Variable optimized out

Сб июн 26, 2021 01:26:32

должен быть квалификатор volatile

Аргументируйте. Тоже не вижу ничего плохого в этом коде.

Re: Variable optimized out

Сб июн 26, 2021 02:36:36

Спасибо всем, кто откликнулся, стал копать на тему того, конкретно какие инструкции вылетают и нашел свою ошибку. Оказалось, что:

а). Да, действительно локальную переменную компилятор оптимизирует без последствий, но не прям выкинув, а используя на участке кода под ее значение один из регистров общего назначения (не выделяя под нее sram), загрузив на старте в него непосредственное значение (HSE_READY_TIMEOUT, в моем случае равном 250), и декрементируя на каждой итерации цикла и сравнивая с нулём для выполнения условного перехода (поэтому в дебаггере я не мог посмотреть на значение переменной, которой нет, поэтому "variable optimized out").

б). Я выбрал слишком маленькое значение этой константы: на другом кварце частота HSE фиксировалась ещё до 80й итерации цикла, поэтому в константе задал с некоторым запасом (250). Оказалось, что новый кварц стабилизируется дольше. На неоптимизированной версии стабилизация происходила чуть раньше 250й итерации цикла, а на оптимизированной - чуть после (повезло :facepalm:), т.к. неоптмизированный код цикла содержал 3 лишних ассемблерных инструкции, соответственно, в абсолютном выражении работал чуть дольше. Поэтому одна версия впритирку пролазила в таймаут стабилизации HSE, а другая - валилась в ошибку. Увеличил таймаут до 2000 - всё стало ок :beer:.

Re: Variable optimized out

Сб июн 26, 2021 07:27:12

Не надо впритирку. Это же защитный механизм. Завтра другая температура, контролер, кварц или ёмкости и опять "не успет". Я бы ещё на пару порядков счётчик увеличил. Какая разница, через 1, 10 или 100 мс вы узнаете о неисправности кварца.

Re: Variable optimized out

Сб июн 26, 2021 13:38:31

Увеличил таймаут до 2000 - всё стало ок :beer:.
250 - пальцем в небо, 2000 - опять наугад. Да ещё и - зависимо от того, насколько оптимальным будет код... :facepalm:
А ведь в даташитах/юзер-мануалах обычно приводят времена стабилизации генераторов.

Добавлено after 6 minutes 12 seconds:
Я бы ещё на пару порядков счётчик увеличил. Какая разница, через 1, 10 или 100 мс вы узнаете о неисправности кварца.
Для некоторых классов устройств регламентируется максимально допустимое время выхода устройства в рабочий режим после подачи питания. Или же - максимально допустимое время начала выполнения какой-то определённой критичной функции (даже если в целом весь функционал устройства ещё не запустился к этому моменту). И 100мс может оказаться слишком много. Да даже и 10.

Re: Variable optimized out

Сб июн 26, 2021 16:55:59

Увеличил таймаут до 2000 - всё стало ок :beer:.
250 - пальцем в небо, 2000 - опять наугад. Да ещё и - зависимо от того, насколько оптимальным будет код... :facepalm:

Согласен, но в даташите тоже указано "Startup time - 2ms" с припиской, что может меняться в больших пределах, в зависимости от качества кварца. Видимо, по фен-шую всё-таки заюзаю SysTick, чтобы отмерять интервалы им, и добавлю конский запас. Спасибо.

Re: Variable optimized out

Сб июн 26, 2021 23:28:49

И 100мс может оказаться слишком много. Да даже и 10.
Кто мешает сделать критическпе вещи, если они действительно есть, до старта кварца? Заодно и время ожидания зря не пройдёт.

Re: Variable optimized out

Вс июн 27, 2021 12:23:50

при включении оптимизации компилятора нижеприведенный код (приведен в упрощенном виде) выщёлкивается из итогового образа напрочь

Макрос в цикле разворачивается до:
Код:
#define READ_BIT(REG, BIT)    ((REG) & (BIT))

При этом первый параметр имеет атрибут volatile на чтение и запись, однако к нему применяется дополнительный оператор "И" в теле макроса, отчего атрибут теряется.
Для того чтобы обойти это ограничение, достаточно не использовать макрос. Так и запись получается короче, и проще.
Код:
cycle_counter = HSE_READY_TIMEOUT;
while ((!(RCC->CR & RCC_CR_HSERDY)) && --cycle_counter);
if(cycle_counter == 0) {флаг не взвёлся}

Re: Variable optimized out

Вс июн 27, 2021 12:36:40

Макрос в цикле разворачивается до:
Код:
#define READ_BIT(REG, BIT)    ((REG) & (BIT))

При этом первый параметр имеет атрибут volatile на чтение и запись, однако к нему применяется дополнительный оператор "И" в теле макроса, отчего атрибут теряется.

Оператор "И" применяется к прочитанному из регистра значению, никакие атрибуты там не теряются.

Re: Variable optimized out

Вс июн 27, 2021 14:18:21

никакие атрибуты там не теряются.

Наследие атрибута сохраняется до первого применения внешнего оператора. Название макроса является именем новой переменной уже без атрибутов (да, они именно так и работают). Кроме того, по документации не взведённый RCC_CR_HSERDY равен нулю - первоначальный код вообще не рабочий, в том числе и ограничитель времени (там вообще ересь).

Re: Variable optimized out

Вс июн 27, 2021 14:21:03

Кто мешает сделать критическпе вещи, если они действительно есть, до старта кварца? Заодно и время ожидания зря не пройдёт.
"Критически важные вещи" могут включать запуск каких-то служб, для запуска которых нужно например чтение из внешнего чипа SPI-флешь (конфиг и т.д.). Хранение этого конфига во флешь может осуществляться в сложных структурах: кольцевые буфера, ФС, ...
Предлагаете писать два набора API для работы с флешь и структурами хранения данных в ней: для работы до старта кварца и для работы с кварцем?? :facepalm:
К тому же - и все эти службы, время запуска которых критично, они тоже должны как-то плавно пережить переключение источника тактирования....

Re: Variable optimized out

Вс июн 27, 2021 15:40:50

Наследие атрибута сохраняется до первого применения внешнего оператора. Название макроса является именем новой переменной уже без атрибутов (да, они именно так и работают).

Первый раз подобное слышу :) Если изначально было:
Код:
while (READ_BIT(RCC->CR, RCC_CR_HSERDY) == 0U)

то после обработки препроцессором получим:
Код:
while (((RCC->CR) & (RCC_CR_HSERDY)) == 0U)

И я это не выдумал, а скопировал из вывода gcc после скармливания ему ключа "-E". Где тут новая переменная?

Re: Variable optimized out

Вс июн 27, 2021 16:25:30

Первый раз подобное слышу :)

Лучше поздно, чем никогда. :)
Я повторюсь, значение атрибута сохраняется до первого оператора присвоения. И оно там не в воздухе висит, для него даже имя есть, точно такое-же как у названия макроса. Это естественное ограничение GCC, от присвоения volatile всем переменным проекта.
В операцию сравнения идёт имя макроса, а не его содержимое.

Re: Variable optimized out

Вс июн 27, 2021 17:02:07

Я повторюсь, значение атрибута сохраняется до первого оператора присвоения. И оно там не в воздухе висит, для него даже имя есть, точно такое-же как у названия макроса. Это естественное ограничение GCC, от присвоения volatile всем переменным проекта.

При разворачивании макроса происходит простая подстановка, в этот момент компилятор еще не знает что такое RCC->CR, определено ли оно где-то и какие там у него атрибуты, а после подстановки, т.е. когда наличие volatile может иметь значение, уже нет никаких макросов...

Re: Variable optimized out

Пн июн 28, 2021 08:08:31

К тому же - и все эти службы, время запуска которых критично, они тоже должны как-то плавно пережить переключение источника тактирования....
Сова на глобусе. Если кварц запускается, в штатном режиме, никаких лишних задержек не будет. А в аварийном режиме действительно КРИТИЧЕСКАЯ служба должна уметь без кварца работать. Иначе никакая она не критическая.

Добавлено after 16 minutes 16 seconds:
В операцию сравнения идёт имя макроса, а не его содержимое.
Надеюсь, это просто жара...

Пн июн 28, 2021 09:11:30

VladislavS, Reflector - Всегда можно проверить на практике.

Re:

Пн июн 28, 2021 09:50:31

Всегда можно проверить на практике.

Я же вчера продемонстрировал как будет выглядеть код после стадии препроцессинга... Можно чуть поменять макрос и тогда получим точно такую строку как ты сам и предлагал. Так, например. Справа промежуточное представление кода, мы просто тормознули компилятор на этой стадии, никто ведь не станет доказывать что одинаковые строки скомпилируются по-разному просто потому, что раньше в одной из них был макрос :) И все компиляторы которые там можно выбрать естественно выдают одно и то же.

Re: Variable optimized out

Пн июн 28, 2021 13:42:46

Если кварц запускается, в штатном режиме, никаких лишних задержек не будет. А в аварийном режиме действительно КРИТИЧЕСКАЯ служба должна уметь без кварца работать. Иначе никакая она не критическая.
Разговор не про "критические службы", а про службы время старта которых, после включения_питания/сброса критично, не должно превышать максимально допустимое. И тупо ждать 100мс чего-то (когда это не нужно), до того как запустить такую службу - слишком расточительно.
Ответить