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

Почему не происходит вход в ветку if ?

Играюсь с CDC на STM32F070.

 

В функции CDC_Control_FS(...) файла usbd_cdc_if.c есть длинная структура switch-case. Её условие CDC_SET_LINE_CODING срабатывает, если происходит попытка передать в STM32-CDC конфигурацию UART (битрейт, количество стоп-битов и всё такое).

 

Для кое-каких нужд я объявил в main.c глобальную переменную reconf_flag. В функции main() перед бесконечным циклом я присвоил ей ноль. А в самом бесконечном цикле я при помощи if(reconf_flag) проверяю, не поднялся ли флаг.

 

Затем в usbd_cdc_if.c я объявил reconf_flag как extern. И в условии CDC_SET_LINE_CODING я присваиваю этой переменной единицу.

 

Код, однако, не работает. Сначала я раскидал GPIOx->ODR по тексту, то есть отлаживал при помощи загорающихся LED: в условие CDC_SET_LINE_CODING программа заходит, в if(reconf_flag) внутри main() - нет. Затем прошёл отладчиком, поставив на if(reconf_flag) и на присвоение внутри CDC_SET_LINE_CODING точки останова. Видно, как сначала reconf_flag равен нулю, затем, как в CDC_Control_FS(...) ей присваивается единица, а затем уже в main() эта единица осознаётся отладчиком. Но! В тело if(reconf_flag) вход не происходит!

 

Я посмотрел ассемблер на точке останова if(reconf_flag), он показывает 0x800037c: b.n 0x800037c

 

У меня включена оптимизация -Os (optimization for size), без которой код просто не залезет в STM32F070 (у него маловато памяти). И я полагаю, что оптимизатор слишком вольно интерпретирует if(reconf_flag), перед которым происходит reconf_flag=0;

 

Я думаю, оптимизатор видит, что в main.c переменная reconf_flag больше нигде не изменяется и вфигачивает этот ноль жёстко в условие if(0), которое никогда не отрабатывается.

 

1) Если оно так, то как сказать компилятору не делать этого?
2) Если оно не так, то почему не происходит вход в условие?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Доброго вечера.

Объявите свою переменную как volatile, и всё станет работать как вы хотели.

Без volatile компилятор (когда собирает ваш main.c) видит, что вы проинициализировали переменную в 0, нигде не ставите в "не 0", и проверяете её на "не 0". Так как компилятор ничего не знает и знать не может о вашей многопоточности (в прерываниях), он корректно делает вывод о том, что переменная не изменяется, и выкидывает проверку.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 2/11/2023 at 8:12 PM, engel65536 said:

Доброго вечера.

Объявите свою переменную как volatile, и всё станет работать как вы хотели.

Без volatile компилятор (когда собирает ваш main.c) видит, что вы проинициализировали переменную в 0, нигде не ставите в "не 0", и проверяете её на "не 0". Так как компилятор ничего не знает и знать не может о вашей многопоточности (в прерываниях), он корректно делает вывод о том, что переменная не изменяется, и выкидывает проверку.

Большое спасибо!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

foo.h

#ifndef _FOO_H
#define _FOO_H

extern int flag;

#endif

foo.c

#include "foo.h"

int flag;

void foo(void)
{
    flag = 0;

    while(!flag) boo();
}

boo.c

#include "foo.h"

void boo(void)
{
    if(somewhat_happened) flag = 1;
}

Есть предположение, что flag не будет соптимизирован и без volatile.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

16 hours ago, xemul said:

Есть предположение, что flag не будет соптимизирован и без volatile.

Да, но здесь у вас явный вызов функции из другой единицы компиляции. Компилятор  Код ТС наверняка можно переписать так, чтобы и у него работало таким образом, но это будет несколько синтетический костыль, как мне кажется.

И небольшой нюанс: в итоге без volatile в такой задаче (когда физически причиной взведения флага является событие, фиксируемое в прерывании) обойтись не получится - в вашем примере этот volatile будет где-то внутри somewhat_happened. Если же его не сделать, то всегда остаётся риск, что:

а) при сборке boo.c возникнет ровно та же самая проблема, что и в изначальном вопросе;

б) при включении multi-file compilation всё может соптимизироваться в ничто.

При multi-file compilation (такое есть как минимум у IAR) все c-файлы слепляются в одну кучу и подаются так на вход компилятора - соответственно, тела всех функций видны отовсюду, и руки комплиятора существенно развязываются.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 hours ago, engel65536 said:

Да, но здесь у вас явный вызов функции из другой единицы компиляции.

Я буквально следовал условиям ТС. Про прерывания, multi-file compilation и IAR у него нет ни слова.

On 2/11/2023 at 8:08 PM, flammmable said:

Для кое-каких нужд я объявил в main.c глобальную переменную reconf_flag.

Затем в usbd_cdc_if.c я объявил reconf_flag как extern.

Если Вам будет не лень поэкспериментировать про volatile и "ровно ту же самую проблему", и выложить здесь .map'ы с флагами компиляции для прояснения, то ...

ЗЫЖ есть проблема - у мну нет и не будет IAR.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

48 minutes ago, xemul said:

Про прерывания

Каюсь, про прерывания я придумал сам - мне казалось, что этот USB CDC-драйвер так построен. Так это на самом деле или нет - я уже помню, давно его ковырял.

49 minutes ago, xemul said:

multi-file compilation и IAR

Этого нет у ТС, но это я написал к тому, что здесь может быть мина замедленного действия, когда сейчас оно заработало, а потом через пару лет, когда скрипт сборки изменится, оно упадёт, и придётся очень больно и долго искать, что не так.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, xemul сказал:

Про прерывания, multi-file compilation и IAR у него нет ни слова.

"write code that is guaranteed to work, not code that doesn't seem to break". Иными словами "старайтесь делать хорошо, плохо получится само"

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 2/13/2023 at 1:44 PM, engel65536 said:

Каюсь, про прерывания я придумал сам - мне казалось, что этот USB CDC-драйвер так построен.

Нет, вы ничего не придумали, USB-CDC именно так и построен. Ваш совет мне помог. Ешё раз, большое вам спасибо! :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

5 hours ago, Сергей Борщ said:

"write code that is guaranteed to work, not code that doesn't seem to break". Иными словами "старайтесь делать хорошо, плохо получится само"

ЗдОрово, и я о том же.
Необходимость квалификатора volatile по содержанию исходного поста никак не просматривается.

(Прилетел пакет, где-то его распарсили. Непосредственно в прерывании? Это точно про "старайтесь делать хорошо"?)

У ТС (была?) явная ошибка в описании внешней переменной. Я всего лишь показал, как это исправить.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В такой задаче более уместны барьеры памяти, а не volatile. Ну или какие-то примитивы синхронизации уже содержащие в себе барьер.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Т.к. предыдущий пример был громоздким, савиршеннонипанятным и просто fooooo, попробую ещё раз с картинками, арфистками и домино.
К тому же спич пошёл про "write code that is guaranteed to work". 

/* boo.h */
#ifndef _BOO_H
#define _BOO_H

extern int flag;

#endif
/* main.c */
#include <stdio.h>

#ifdef WITH_EXTERN_IN_MAIN
#include "boo.h"
#endif

extern void boo(void);

void main(void);
void bingo(int);

int flag;

void main(void)
{
    int cnt;

    flag = 0;

    for(cnt = 0; flag != 1; cnt++)
    {
        if(flag == 1) bingo(cnt);
        else puts("Считаем...");
        
        boo();
    }
    while(1) continue;
}

void bingo(int prize)
{
    if(prize == 1) puts("Получилось: один!!!111");
}
/* boo.c */
#include "boo.h"

void boo(void)
{
    if((int)(2.0 * 2.0) == (int)(pow(2.0, 2.0) + 0.5))  /* типа somewhat_happened для multi-file compilation */
        flag = 1;
}

Невооружённым глазом видно, что с -DWITH_EXTERN_IN_MAIN после любого вменяемого компилятора (полагаю, даже pre-C89) с любой оптимизацией в выхлопе будет

Quote

Считаем...
Получилось: один!!!111

Без -DWITH_EXTERN_IN_MAIN имеем случай ТС. Без оптимизации выхлоп будет идентичным. Но с любой оптимизацией...
Не, ну понятно, volatile от оптимизации спасёт, и "code is guaranteed to work".

(вот уж не помню, в этой же конфе лет дцать тому чел по совету уверовал во всемогущий volatile? "Пони бегают по кругу")
 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...