Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Вопрос по volatile
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Страницы: 1, 2
Alt.F4
Здравствуйте.
В очередной раз начитался про volatile + чудные компиляторы и насмотревшись "глупых" примеров родился вопрос: стоит ли указывать переменные и/или кольцевые буферы, используемые в прерывании, как volatile, если ни одна из переменных при объявлении не принимает значение, все необходимые начальные значения прописываются в отдельной функции (все изменения таких переменных в любом случае делаются атомарно, с запретом прерываний)?
Т.е. компилятор при работе с такими переменными (например, байтом флагов) в принципе не располагает информацией о их значениях, чтобы как-то вырезать условия с их проверкой, как изображается в примерах с неиспользованием volatile.
Спасибо.
Lagman
Для 51 в кейле такое объяснение http://www.keil.com/support/man/docs/c51/c51_le_volatile.htm
XVR
Чтения этих переменных делается напрямую или тоже через интерфейсные функции? Если напрямую, то volitalie нужен. Если нет - то еще не факт, что он не нужен sm.gif
Alt.F4
Если вообще смотреть на проблемы в примерах, то они возникают из-за ситуации, когда компилятор на старте выполнения кода функции располагает инфой о значении переменных и уже исходя из этого исключает определенные участки кода.
Поэтому, если сперва читаем, а уже выходя из функции пишем, то по идее volatile не нужен.

Кстати, обращение к переменным volatile компилятор может произвольно перемещать относительно переменных не-volatile и, я думаю, в определенных ситуациях на этом можно словить не мало приколов.
jcxz
Цитата(Alt.F4 @ Jan 14 2017, 12:00) *
В очередной раз начитался про volatile + чудные компиляторы и насмотревшись "глупых" примеров родился вопрос: стоит ли указывать переменные и/или кольцевые буферы, используемые в прерывании, как volatile, если ни одна из переменных при объявлении не принимает значение, все необходимые начальные значения прописываются в отдельной функции (все изменения таких переменных в любом случае делаются атомарно, с запретом прерываний)?

Атомарность тут не при чём.
Компилятор может например перенести значение переменной в регистр. И будете Вы в неё писать хоть атомарно, хоть нет, обработчик прерывания этого не знает когда захочет её прочитать из памяти где осталось старое значение.
SlavaV
Тогда и я своё видение изложу

В документацию не лазил пишу на память могу где-то в чем-то ошибиться.

Ключевое слово volatile говорит компилятору не оптимизировать работу с этой переменной, поясняется это следующим образом: переменная может быть отражена на область памяти изменяемую аппаратно или на пространство ввода вывода.

PS а компилятор про ваш код знает всё, если эта переменная пишется/читается в прерывании то именно она будет использована хоть с указанием ключевого слова volatile хоть без него и не важно где она расположена в памяти или регистре, ведь код обработки прерывания также компилируется линкуется в единое целое.
jcxz
Цитата(SlavaV @ Jan 16 2017, 17:43) *
PS а компилятор про ваш код знает всё, если эта переменная пишется/читается в прерывании то именно она будет использована хоть с указанием ключевого слова volatile хоть без него и не важно где она расположена в памяти или регистре, ведь код обработки прерывания также компилируется линкуется в единое целое.

Как "именно она будет использована" в ISR если она находится в регистре???
Компилятор о коде не может "знать всё" хотя бы потому, что код вообще может не проходить через компилятор, а быть получен из объектников/библиотек.
SlavaV
Переменная не "путешествует" во время выполнения из памяти в регистр или обратно, если компилятор определил ей место в памяти или в регистре то она там и будет, но если ей определенно место в регистре то компилятор код добавит при входе в ISR "push" при выходе "pop" и то при условии если этот регистр используется во время обработки прерывания.

PS может я чего не знаю, но почему в процедуре обработки прерывания нельзя обращаться к переменным расположенным в регистре?
jcxz
Цитата(SlavaV @ Jan 16 2017, 18:14) *
Переменная не "путешествует" во время выполнения из памяти в регистр или обратно, если компилятор определил ей место в памяти или в регистре то она там и будет, но если ей определенно место в регистре то компилятор код добавит при входе в ISR "push" при выходе "pop" и то при условии если этот регистр используется во время обработки прерывания.

PS может я чего не знаю, но почему в процедуре обработки прерывания нельзя обращаться к переменным расположенным в регистре?

Каким образом обращаться-то??? К переменной, расположенной в регистре в фоновой задаче и выгруженной куда-то в стек при активации прерывания.
Переменная именно что "путешествует". Она может находиться в памяти, а через такт - уже в регистре, а потом опять в памяти. Это и есть работа оптимизатора.
Пример:
int x;
void f()
{
x += N1; //1
...
x += N2; //2
...
x += N3; //3
}
В точке 1 компилятор может загрузить x в какой-либо регистр и все обращения внутри f() к этой переменной выполнять с этим с регистром, и выгрузить её в ОЗУ только перед выходом из функции.
Или не перед выходом, а на любом участке выполнения, где сочтёт это оптимальным.
Это вполне допустимое поведение.
А теперь представьте что будет, если где-то во время выполнения f() вызвался ISR, который обратился к переменной x.
SlavaV
int x;
void f()
{
x += N1; //1
...
x += N2; //2
...
x += N3; //3
}

добавим к вашему коду:

ISR fff()
{
x++;
}
вы считаете разработчиков компилятора недалёкими людьми которые не предполагают, что прерывание может возникнуть в любой момент времени?

PS описанное вами поведение переменной ни на практике ни в теории я не видел ни разу.
На этом я удаляюсь из обсуждения
Сергей Борщ
QUOTE (SlavaV @ Jan 16 2017, 17:55) *
вы считаете разработчиков компилятора недалёкими людьми которые не предполагают, что прерывание может возникнуть в любой момент времени?
Нет - мы знаем, что они умные люди и понимают, что программист хочен получить малнькую и быструю программу, а не большую и медленную. Также они понимают, что только программист знает, что должна делать его программа. Поэтому они придумали volatile.
QUOTE (SlavaV @ Jan 16 2017, 17:55) *
PS описанное вами поведение переменной ни на практике ни в теории я не видел ни разу.
Или вы мало видели, или никогда не включали оптимизацию.
gerber
Цитата(SlavaV @ Jan 16 2017, 18:55) *
вы считаете разработчиков компилятора недалёкими людьми которые не предполагают, что прерывание может возникнуть в любой момент времени?

Компилятор (и примкнувший к нему линкер) вообще ничего не знают про прерывания. Есть только функции, расположенные по определенным адресам. А то, что эти функции могут обрабатывать прерывания, и прерывать другие функции в любом месте, знает только разработчик на основе архитектуры контроллера, под который он пишет программу.
А в вашем примере и volatile не спасет, так как суммирование x и чисел N1,N2,N3 неатомарно.
Alt.F4
Снова приведен пример, как и во всех статьях. В реальности такую ситуации с тупым инкрементом или другой подобной логикой не встретить, имхо.
Думается мне, что проблема высосана из пальца и требует ну совсем исключительных условий для объявления volatile.
jcxz
Цитата(gerber @ Jan 16 2017, 19:55) *
А в вашем примере и volatile не спасет, так как суммирование x и чисел N1,N2,N3 неатомарно.

Атомарность - это совсем другой вопрос. Я же ничего не писал про тип доступа из ISR. ISR может просто доступ на чтение сделать, чему неатомарность никак не помешает.

Цитата(Alt.F4 @ Jan 16 2017, 20:10) *
Снова приведен пример, как и во всех статьях. В реальности такую ситуации с тупым инкрементом или другой подобной логикой не встретить, имхо.
Думается мне, что проблема высосана из пальца и требует ну совсем исключительных условий для объявления volatile.

"Тупой инкремент" там для примера. На его месте могут быть любые другие операции модификации x. Перемежающиеся другими операциями если Вы не поняли.
Если Вы не встречали такую ситуацию в реале, то смею предположить, что вообще не занимаетесь программированием.
Например: элементарный буфер данных, в который фоновая программа пишет данные, передвигая после каждой записи указатель позиции, а ISR, читает эту позицию и на основании её значения - данные из буфера.
Вот записали Вы данные, передвинули позицию, дальше например можете в цикле ждать когда установится некий флажок (устанавливаемый в ISR и прописанный отдельно, с volatile),
а ISR смотрит - позиция-то не изменилась, значит данных нет. А в это время новая позиция просто в регистре лежит. Так и будет фоновый процесс бесконечно ждать.
XVR
Пример (https://gcc.godbolt.org/)
Код (компилятор ARM gcc 5.4.1, опции -O3)
Код
int flag;

void isr() {flag=1;}

void wait()
{
  flag = 0;
  while(!flag);
}

Предположим, что процедура isr вызывется из приерывания (по таймеру)
Результат:
Код
isr():
        mov     r2, #1
        ldr     r3, .L2
        str     r2, [r3]
        bx      lr
.L2:
        .word   .LANCHOR0
wait():
        mov     r2, #0
        ldr     r3, .L7
        str     r2, [r3]
.L5:
        b       .L5
.L7:
        .word   .LANCHOR0
flag:

Как видим wait заканчивается бесконечным циклом.

Добавим volatile

Код
isr():
        mov     r2, #1
        ldr     r3, .L2
        str     r2, [r3]
        bx      lr
.L2:
        .word   .LANCHOR0
wait():
        mov     r3, #0
        ldr     r2, .L9
        str     r3, [r2]
.L5:
        ldr     r3, [r2]
        cmp     r3, #0
        beq     .L5
        bx      lr
.L9:
        .word   .LANCHOR0
flag:


Бесконечный цикл исчез

Alt.F4
Цитата
... Дальше например можете в цикле ждать когда установится некий флажок ...

Если не использовать блокирующие операции, то и проблемы не будет.
jcxz
Цитата(Alt.F4 @ Jan 17 2017, 10:57) *
Если не использовать блокирующие операции, то и проблемы не будет.

Это что за операции такие??
RobFPGA
Приветствую!

Не забывайте о том что не только на ISR заканчивается внешний мир для переменной.
Переменная может обновляется и через DMA и из других ядер. Так что без volatile ну ни как.
И это отнюдь не "высосанный из пальца" пример, а вполне обычная практика.

Удачи! Rob.
Alt.F4
jcxz, вот именное такие: "например можете в цикле ждать когда установится некий флажок" (цитирую Вас же).

RobFPGA, по идее вообще без разницы, где эта переменная будет изменяться, что-то мы ходим по кругу...

Проблема: компилятор оптимизируя вырезает кусок кода считая его лишним.
На основании чего он это делает? Он знает, что некая переменная имеет в текущий момент определенное состояние.
Значит, если на входе функции мы не присваиваем ей какое либо значение и затем не проверяем эту переменную на другие значения (что для компилятора кажется безумным и он это режет), то и volatile не нужен.
jcxz
Цитата(Alt.F4 @ Jan 18 2017, 00:45) *
Значит, если на входе функции мы не присваиваем ей какое либо значение и затем не проверяем эту переменную на другие значения (что для компилятора кажется безумным и он это режет), то и volatile не нужен.

Похоже пациент безнадёжен....
Попробуйте когда-нить заглянуть в реальные результаты компиляции при полной оптимизации. Вы как видно никогда туда не заглядывали или у Вас нет опыта отладки вообще.
Уже 100500 раз тут повторили, что на входе в эту функцию, компилятор вполне может переместить значение переменной в регистр (или какое другое место) и другая задача/ISR/другое_процессорное_ядро никогда об этом не узнает и будет обращаться к ней по адресу в памяти, а внутри функции - обращение к этой переменной в регистре (хоть по записи хоть по чтению). Так как при оптимизации кода, оптимизатор не должен (и не может) анализировать весь ход выполнения программы и не может знать что обращения к этой переменной возможны из разных мест одновременно. Об этом должен знать программист и должен явно указать это посредством volatile.
Kabdim
А если включить lto то оптимизироваться будет совсем не одна функция, а кусок какой сказать заранее не выйдет. И уж там оптимизатор оторвется по полной.
Alt.F4
jcxz, сейчас я тоже начну вас оскорблять sm.gif
Изменять все переменные, используемые в прерывании, необходимо атомарно (или вы не в курсе?), и нам в принципе без разницы, куда компилятор запишет значение во время преобразований, хоть в регистр, хоть будет "держать в уме".

Пока по прежнему нет реального примера, кроме блокирующих операций, где компилятор может сделать не так, как предполагалось.
В статьях обычно об этом пишут как-то абстрактно, а никто детально не желает разобраться, как действительно все работает.
jcxz
Цитата(Alt.F4 @ Jan 18 2017, 13:53) *
jcxz, сейчас я тоже начну вас оскорблять sm.gif
Изменять все переменные, используемые в прерывании, необходимо атомарно (если вы не в курсе), и нам в принципе без разницы, куда компилятор запишет значение во время преобразований, хоть в регистр, хоть будет "держать в уме".

Да при чём тут атомарность-то???
Вы хотя-бы прочитали и поняли что я написал? Изменений переменной в фоновом программе может вообще не быть.
RobFPGA
Приветствую!

Цитата(Alt.F4 @ Jan 18 2017, 13:53) *
Изменять все переменные, используемые в прерывании, необходимо атомарно (если вы не в курсе), и нам в принципе без разницы, куда компилятор запишет значение во время преобразований, хоть в регистр, хоть будет "держать в уме".

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

Volatile к атомарности никакого отношения не имеет! Volatile указывает компилятору что значение переменной может изменится ВНЕ текущего контекста зоны видимости переменной. Поэтому компилятор НЕ вправе оптимизировать ЧТЕНИЕ актуального состояния этой переменной.
Пример Вам уже приводили - чтение переменной в цикле. И не только как блокировка, а например очень часто при опросе регистров состояния периферии, биты которых меняются аппаратно.

Удачи! Rob.
Alt.F4
jcxz, читаю "на входе в эту функцию, компилятор вполне может переместить значение переменной в регистр"
И каким боком из-за этого переменная может не изменяться вообще? В текущий момент пусть функция не видит изменения, при следующем входе снова прочитает по адресу и все увидит. Где проблема?

RobFPGA, об этом речи и не идет, атомарность делает сам программист, конечно.
Ок, добавляем еще одну ситуацию, кроме блокирующих - чтение регистров ввода/вывода, хотя они уже по умолчанию идут как volatile, и программисту об этом не надо заботиться.
RobFPGA
Приветствую!
Цитата(Alt.F4 @ Jan 18 2017, 14:24) *
jcxz, читаю "на входе в эту функцию, компилятор вполне может переместить значение переменной в регистр"
И каким боком из-за этого переменная может не изменяться вообще? В текущий момент пусть функция не видит изменения, при следующем входе снова прочитает по адресу и все увидит. Где проблема?

RobFPGA, об этом речи и не идет, атомарность делает сам программист, конечно.
Ок, добавляем еще одну ситуацию, кроме блокирующих - чтение регистров ввода/вывода, хотя они уже по умолчанию идут как volatile, и программисту об этом не надо заботиться.

Это если за Вас уже позаботились сделав правильный BSP, а если эта периферия сидит например во внешней FPGA которая отмаплена на память?
И не только регистры, а например буфера данных которые пишутся через DMA, а проц их только читает для обработки.
И что с оптимизирует компилятор в этом случае если НИГДЕ в Вашей программе нет ЗАПИСИ в этот буфер/переменную?

Удачи! Rob.
Baser
Цитата(Alt.F4 @ Jan 18 2017, 13:24) *
jcxz, читаю "на входе в эту функцию, компилятор вполне может переместить значение переменной в регистр"
И каким боком из-за этого переменная может не изменяться вообще? В текущий момент пусть функция не видит изменения, при следующем входе снова прочитает по адресу и все увидит. Где проблема?

Если ваше прерывание будет только читать эту временно перемещенную переменную, то ничего не произойдет, она прочтет новое значение позже.
А вот если прерывание тоже изменяет это значение, то это значение пропадет, так как функция при выходе сохранит в эту переменную свое значение.

А если глобально про volatile, то я не слишком часто его применяю, только там, где вышеприведенные эффекты могут иметь место или есть опасность выкидывания компилятором моего кода.
Обычно анализирую возможность поведения каждой переменной, и если гиблые состояния видны, применяю.
А если простая передача данных по флагам из/в прерывания, то и так все работает.
Alt.F4
RobFPGA, думаю, если буфер объявлен глобально, то компилятор не вырежет код, даже если "НИГДЕ в Вашей программе нет ЗАПИСИ в этот буфер/переменную".
Имхо, компилятор работает исключительно локально, относительно кода функции, и не проверяет, в каком месте и пишутся ли вообще данные в глобальные переменные.

Цитата
А вот если прерывание тоже изменяет это значение, то это значение пропадет, так как функция при выходе сохранит в эту переменную свое значение.
Ну как бы изменения делаем атомарно, это уже обсудили.
aiwa
Цитата(Alt.F4 @ Jan 17 2017, 23:45) *
RobFPGA, по идее вообще без разницы, где эта переменная будет изменяться, что-то мы ходим по кругу...

Переменная не может где-то изменяться. Она находится всегда там, где определена изначально: глобальная - в памяти, регистровая - в регистре, временная в стеке.
И только так. Никаких "путешествий" она не производит по стекам и регистрам она не производит: в стек и регистры компилятор записывает копии значения переменных.
Компилятор производит компиляцию исходя из предположения по умолчанию, что копия значения переменной всегда тождественна оригинальному, и как раз
служебное слово volatile говорит компилятору, что это не так и переменная может поменять свое значение в прерываниии.
jcxz
Цитата(Alt.F4 @ Jan 18 2017, 14:24) *
И каким боком из-за этого переменная может не изменяться вообще? В текущий момент пусть функция не видит изменения, при следующем входе снова прочитает по адресу и все увидит. Где проблема?

У меня возникает ощущение что Вы троллите всех тут....
Как можно не понимать очевидных вещей??
Пример (жизненный, написал специально для Вас):
CODE
enum {F_NONE, F_SLEEP_STATE, F_TIMER, F_SERVICE_REQUEST_1, F_SERVICE_REQUEST_2};
int flag = F_NONE;
void f()
{
if (flag == F_NONE) {
//ТОЧКА1
ENTR_CRT_SECTION(); //входим в критическую секцию (просто запрет прерывания)
if (flag == F_NONE) {
flag = F_SLEEP_STATE;
EXIT_CRT_SECTION(); //покидаем критическую секцию
Sleep();
} else EXIT_CRT_SECTION();
}
int i = flag;
flag = F_NONE;
switch (flag) {
case F_TIMER: return;
case F_SERVICE_REQUEST_1:
... //обслуживание сервиса 1
break;
case F_SERVICE_REQUEST_2:
... //обслуживание сервиса 2
break;
}
}
void isr1()
{
PeripheralIntReqReset1();
if (flag == F_SLEEP_STATE) WakeUp();
else if (flag != F_NONE && flag != F_TIMER) return;
flag = F_SERVICE_REQUEST_1;
}
void isr2()
{
PeripheralIntReqReset2();
if (flag == F_SLEEP_STATE) WakeUp();
else if (flag != F_NONE && flag != F_TIMER) return;
flag = F_SERVICE_REQUEST_2;
}
void isrTimer()
{
if (flag == F_SLEEP_STATE) WakeUp();
else if (flag != F_NONE) return;
flag = F_TIMER;
}

Теперь представьте что эта функция вызывается периодически (суперцикл или корпоративная многозадачность).
Включена максимальная оптимизация и на входе в функцию f() при первом обращении компилятор перенёс переменную flag в регистр, а в месте ТОЧКА1 происходит какое-либо прерывание, которое пишет запрос обслуживания в flag.
Так вот - при компиляции с макс. оптимизацией, этот код будет сбоить (терять запросы на обслуживание). Но если к определению flag добавить volatile, всё будет прекрасно работать с любым уровнем оптимизации.
Чтобы пресечь возможные доводы типа "искусственный пример", допустим: isr1()/isr2() к примеру - это ISR-ы о готовности данных к чтению в FIFO-буфере некоей периферии, которая однократно выставляет флаг готовности при достижении некоего уровня заполнения FIFO. Эти данные надо считать. Причём считать до прихода след. данных чтобы избежать потерь.
И можно 1000 подобных примеров придумать и с более сложными зависимостями.

Цитата(Alt.F4 @ Jan 18 2017, 15:33) *
думаю, если буфер объявлен глобально, то компилятор не вырежет код, даже если "НИГДЕ в Вашей программе нет ЗАПИСИ в этот буфер/переменную".

Опять неправда.
Если записи нет, а есть только чтения, то компилятор заменит все чтения этой переменной загрузкой константы в регистр.
А потом компоновщик увидит, что к этой переменной нет ни одного обращения и удалит её за ненадобностью.
Baser
Цитата(Alt.F4 @ Jan 18 2017, 14:33) *
Ну как бы изменения делаем атомарно, это уже обсудили.

Ну как бы вам уже несколько человек на пальцах объяснили, что атомарность тут не причем sm.gif
Попробую еще раз:
Если глобальная переменная не объявлена volatile, то ваша функция из главного цикла, работая с этой переменной,
имеет право (но не обязана) поместить её копию в регистр или в стек.
И внутри функции работать с этой копией (атомарно или как хочет) согласно вашей программы.
И только при выходе из функции записать эту копию в основное глобальное место.
Т.е. во время работы функции могут быть две копии (и различные!).

Прерывания этого не знают и работают с переменной из глобальной памяти.
И функция при выходе результат работы прерывания затирает.

А если объявить эту глобальную переменную volatile, то компилятору будет запрещено делать копии и работать с ними.
Все изменения этой переменной будут происходить только по основному месту хранения.
DASM
Господа никогда не понимал таких споров. Процесс важен или результат? Сюрпризы возможны, не проще ли дать volatile и забыть?
RobFPGA
Приветствую!
Цитата(Baser @ Jan 18 2017, 17:06) *
Ну как бы вам уже несколько человек на пальцах объяснили, что атомарность тут не причем sm.gif
...
Если глобальная переменная не объявлена volatile, то ваша функция из главного цикла, работая с этой переменной,
имеет право (но не обязана) поместить её копию в регистр или в стек.
И внутри функции работать с этой копией (атомарно или как хочет) согласно вашей программы.
И только при выходе из функции записать эту копию в основное глобальное место.
Т.е. во время работы функции могут быть две копии (и различные!).

Прерывания этого не знают и работают с переменной из глобальной памяти.
И функция при выходе результат работы прерывания затирает.

А если объявить эту глобальную переменную volatile, то компилятору будет запрещено делать копии и работать с ними.
Все изменения этой переменной будут происходить только по основному месту хранения.

Простите но это фигня "... а не заливная рыба ..." wacko.gif Вы тоже путаете атомарность и Volatile.
Глобальная переменная может быт считана в регистр для работы с ней, но копироваться при этом в стек она не будет.

Еще раз "Volatile указывает компилятору что значение переменной может изменится ВНЕ текущего контекста зоны видимости переменной"
Другими словами - меняет правило расчета "срока жизни" переменной в текущем контексте кода.
Это значит что если в текущем контексте функции/таска/isr требуется несколько чтений из переменной то ВСЕ они БУДУТ РЕАЛЬНО выполнены из актуального местоположения переменной. И не важно - будет ли изменятся или нет переменная этом контексте.
Например если var не volatile и Вы хотите:
Код
   sum += var;
  sum += var;

компилятор может сделать одно чтение var затем умножение на 2 и сложение с sum так как срок жизни переменной не заканчивается
если будет volatile - то ВСЕГДА будет 2 чтения из памяти.

Вот еще пример - иногда требуется только сам факт чтения/записи переменной (без реальных значений) для выполнения какого либо действия в периферии. Без volatile такое "бессмысленное расточительство" режется на корню "слишком умным" компилятором.

Удачи! Rob.


Укушенный воблой
Цитата(jcxz @ Jan 16 2017, 14:19) *
Атомарность тут не при чём.
Компилятор может например перенести значение переменной в регистр. И будете Вы в неё писать хоть атомарно, хоть нет, обработчик прерывания этого не знает когда захочет её прочитать из памяти где осталось старое значение.

Если переменная не объявлена как регистЕр, то никуда компилятор её не перенесёт. Не вводите людей в заблуждение.

А если я работаю с переменной через указатель на неё, то что?
Я должен такую переменную объявлять как волатайл?

Цитата(gerber @ Jan 16 2017, 17:55) *
Компилятор (и примкнувший к нему линкер) вообще ничего не знают про прерывания. Есть только функции, расположенные по определенным адресам. А то, что эти функции могут обрабатывать прерывания, и прерывать другие функции в любом месте, знает только разработчик на основе архитектуры контроллера, под который он пишет программу.
А в вашем примере и volatile не спасет, так как суммирование x и чисел N1,N2,N3 неатомарно.

Есть такая штука, называется "сохранение контекста прерванной задачи".
Хотя конечно есть двухбайтовые команды работы напрямую с РВВ, обращение к которым должно происходить в соседних тактах процессора.
Тогда ой. Нужно запрещать прерывания перед такой критической последовательностью команд.
Иначе вообше может произойти катастрофа если прерывание разорвет двухбайтовую последовательность команд

Цитата(Alt.F4 @ Jan 17 2017, 22:45) *
jcxz, вот именное такие: "например можете в цикле ждать когда установится некий флажок" (цитирую Вас же).

RobFPGA, по идее вообще без разницы, где эта переменная будет изменяться, что-то мы ходим по кругу...

Проблема: компилятор оптимизируя вырезает кусок кода считая его лишним.
На основании чего он это делает? Он знает, что некая переменная имеет в текущий момент определенное состояние.
Значит, если на входе функции мы не присваиваем ей какое либо значение и затем не проверяем эту переменную на другие значения (что для компилятора кажется безумным и он это режет), то и volatile не нужен.

Именно так. Именно для этого введен волатйл. Чтобы запретить компилятору "оптимизировать" (чаще всего просто удалять) куски кода, которые содержат якобы не изменяемые и не используемые переменные
Baser
Цитата(RobFPGA @ Jan 18 2017, 17:08) *
Простите но это фигня "... а не заливная рыба ..." wacko.gif Вы тоже путаете атомарность и Volatile.

Ничего не имею против ваших разъяснений работы volatile, но доводов того, что "моя заливная рыба" это фигня, я у вас не нашел. laughing.gif

Атомарность это запрещение различных прерываний, исключений, переключения задач и т.д. на каком-то участке кода.
Это никак не запрещает компилятору в функции работать с переменной атомарно, но при этом работать с её копией.

Цитата
Глобальная переменная может быт считана в регистр для работы с ней, но копироваться при этом в стек она не будет.

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

Цитата(DASM @ Jan 18 2017, 16:32) *
Господа никогда не понимал таких споров. Процесс важен или результат?

Для достижения желаемого результата неплохо бы разбираться в особенностях процесса rolleyes.gif
zltigo
Цитата(Укушенный воблой @ Jan 18 2017, 18:14) *
Если переменная не объявлена как регистЕр, то никуда компилятор её не перенесёт.

Вы, как обычно, совершенно ничего не смыслите в том, о чем пытаетесь писать. Разумеется компилятор может и делает это. Да и квалификатор register уже давно у большинства компиляторов просто игнорируется.
Цитата
Не вводите людей в заблуждение.

Это по Вас.
Цитата
Именно для этого введен волатйл. Чтобы запретить компилятору "оптимизировать" (чаще всего просто удалять) куски кода, которые содержат якобы не изменяемые и не используемые переменные

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


Цитата(Baser @ Jan 18 2017, 18:38) *
Атомарность это запрещение различных прерываний, исключений, переключения задач и т.д. на каком-то участке кода.

Никак нет. То, что описали, это критическая секция, точнее один из вариантов ее реализации.
Соответственно и все, что дальше написали это не о чем sad.gif


Цитата(Baser @ Jan 18 2017, 16:06) *
А если объявить эту глобальную переменную volatile, то компилятору будет запрещено делать копии и работать с ними.
Все изменения этой переменной будут происходить только по основному месту хранения.

Расскажите это создателям ARM ядра и множества других с Load-Store sm.gif. Они просто не умеют, так, как Вы представляете.
Baser
Цитата(zltigo @ Jan 18 2017, 19:06) *
Никак нет. То, что описали, это критическая секция, точнее один из вариантов ее реализации.
Соответственно и все, что дальше написали это не о чем sad.gif

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

Цитата(zltigo @ Jan 18 2017, 19:06) *
Расскажите это создателям ARM ядра и множества других с Load-Store sm.gif. Они просто не умеют, так, как Вы представляете.

Наверно, здесь был не точен. Конечно, volatile не создаст атомарный доступ к переменной, но хотя бы предотвратит создание компилятором её "долгоживущих" копий.
Укушенный воблой
А если я работаю с переменной через указатель на неё, то что?
Я должен такую переменную объявлять как волатайл?

Ведь компилятор не может проследить все случаи где я получаю доступ к переменной через указатель на неё
aiwa
Цитата(Укушенный воблой @ Jan 18 2017, 20:31) *
А если я работаю с переменной через указатель на неё, то что?
Я должен такую переменную объявлять как волатайл?

Правила те же: указатель должен быть объявлен как указатель на volatile-переменную.
Естественно компилятор сгенерирует соответствующий код: перед каждым использованием переменной он будет считывать ее значение заново.


Цитата(DASM @ Jan 18 2017, 16:32) *
Господа никогда не понимал таких споров. Процесс важен или результат? Сюрпризы возможны, не проще ли дать volatile и забыть?

"Споры" нужны для того, чтобы понять при каких ситуациях "сюрпризы возможны".
Укушенный воблой
Цитата(aiwa @ Jan 18 2017, 22:36) *
Правила те же: указатель должен быть объявлен как указатель на volatile-переменную.

А сама переменная?
Сама переменная должна объявляться как волатАЙл если её используют через указатель?
Что-то то я не видел в реальном коде разных разработчиков этого

Причем используют неявно. Без использования операции "взятие адреса" (амперсанд)
XVR
Цитата(Укушенный воблой @ Jan 19 2017, 07:18) *
А сама переменная?
Сама переменная должна объявляться как волатАЙл если её используют через указатель?
Нет. Компилятор вещь достаточно умная - он понимает что такое указатель и к чему может привести взятие адреса переменной (для этой переменной)
Он даже понимает кое что в том, куда этот указатель может указывать - см ключевое слово restrict и опцию ansi alias ( http://www.atmos.washington.edu/~ovens/ifo...nsi_alias_f.htm )

Alt.F4
jcxz, а мне Ваши примеры напоминают троллинг, повторение описанного ранее.
Я не вижу причин для сбоя в Вашем коде. В описанной ситуации функция не увидит возведение флажка, при втором входе в функцию все будет Ок. Повторюсь: где проблема?
Да и пример я бы назвал "странным" (читай так делать нельзя), т.к. оба прерывания используют один и тот же байт-флаг и если они сработают друг за другом, то Вы не будете знать, что сработало первое.

Цитата
Если записи нет, а есть только чтения, то компилятор заменит все чтения этой переменной загрузкой константы в регистр.
А потом компоновщик увидит, что к этой переменной нет ни одного обращения и удалит её за ненадобностью.
Какой еще константы, если она переменная? Как это нет обращения, Вы же ее читаете.

Цитата
И функция при выходе результат работы прерывания затирает.
Если переменная изменяется в функции, то это делать надо атомарно, и тогда не будет описанной Вами проблемы. Если что, то volatile не спасет от написанного Вами.

Цитата
Вот еще пример - иногда требуется только сам факт чтения/записи переменной (без реальных значений) для выполнения какого либо действия в периферии. Без volatile такое "бессмысленное расточительство" режется на корню "слишком умным" компилятором.
Хороший пример, добавляем в копилку. Хотя можно объявить переменную глобально или extern, тоже будет работать.

Цитата
"Споры" нужны для того, чтобы понять при каких ситуациях "сюрпризы возможны".

Пока имеем следующие ситуации требущие объявления volatile:
1) Переменные в блокирующих операций (ожидание определенного значения в цикле)
2) Все системные регистры, в том числе ввода/вывода (хотя они де-факто volatile)
3) Переменные в которые "просто читаем" из какого-либо регистра (как вариант, используем глобальную или extern переменную)


Еще пару примеров и можно делать мини-FAQ по теме вопроса sm.gif
jcxz
Цитата(Alt.F4 @ Jan 19 2017, 09:54) *
jcxz, а мне Ваши примеры напоминают троллинг, повторение описанного ранее.
Я не вижу причин для сбоя в Вашем коде. В описанной ситуации функция не увидит возведение флажка, при втором входе в функцию все будет Ок. Повторюсь: где проблема?

Очевидно что Вы и читать исходники не умеете. Посмотрите ещё раз на тот код. Вы ничего не поняли.
Я там вроде всё предельно разжевал, дальше некуда.
Сергей Борщ
QUOTE (Alt.F4 @ Jan 19 2017, 08:54) *
Пока имеем следующие ситуации требущие объявления volatile:
Не надо изобретать велосипед. Есть два критения:
1) если переменная используется в двух потоках (читай - в основном цикле и прерывании). Компилятор обязан при каждом обращении на чтение вычитывать ее из ОЗУ и при каждом обращении на запись класть обратно.
2) если важен порядок доступа к этой переменной (читай - регистры периферии). Компилятор обязан делать то же самое, что и п.1, кроме этого не имеет права менять местами обращения к этой переменной относительно обращений к другим volatile.

QUOTE (Укушенный воблой @ Jan 19 2017, 06:18) *
Сама переменная должна объявляться как волатАЙл если её используют через указатель?
Какая разница, как к ней обращаются? Если она попадает под указзанные выше два критерия - должна быть volatile. Все. И указатель должен иметь тип "указатель на volatile". Зачем так сделано - подумайте самостоятельно.
jcxz
Цитата(Alt.F4 @ Jan 19 2017, 09:54) *
Да и пример я бы назвал "странным" (читай так делать нельзя), т.к. оба прерывания используют один и тот же байт-флаг и если они сработают друг за другом, то Вы не будете знать, что сработало первое.

Специально для Вас разжёвываю: функционал isr1 и isr2 взаимосвязан - если произошло одно, то выставление второго не нужно и даже запрещено (нужно знать какое было первое). И процедура сервиса, будучи запущенной, проверит и обслужит события.
Если совсем уже до детского уровня разжевать, например: это прерывания от двух счётчиков, запущенных от одного события, считающих с разной частотой, кто досчитает первым - тот и должен послать Int. Процедуре сервиса важно знать кто был первый, но обработка - общая.
Alt.F4
Цитата
Очевидно что Вы и читать исходники не умеете.
А Вы убеждать не умеете. Использование одного и того же байта-флага в разных прерываниях описали, хорошо, но конкретной причины для дальнейшего сбоя по прежнему нет.

Цитата
1) если переменная используется в двух потоках (читай - в основном цикле и прерывании). Компилятор обязан при каждом обращении на чтение вычитывать ее из ОЗУ и при каждом обращении на запись класть обратно.
По идее так должно быть, но не обязательно же?
Просто скорость реакции уменьшится (обновленные данные внешней операцией будут получены при повторном чтении переменной).

Я не против volatile, просто хочется понять, когда без него ну совсем не обойтись и код точно не будет работать.


jcxz
Цитата(Укушенный воблой @ Jan 18 2017, 21:31) *
А если я работаю с переменной через указатель на неё, то что?
Я должен такую переменную объявлять как волатайл?
Ведь компилятор не может проследить все случаи где я получаю доступ к переменной через указатель на неё

Попробуйте как-нить написать:
c = *p;
...//пара операций
if (*p) ...
увидите что при наличии оптимизации, компилятор часто не будет делать второе чтение из памяти, а использует копию переменной в регистре, ранее прочитанную.
Он не знает и не должен знать, что Вы возможно в параллельной задаче ОС или ISR можете её изменить. Об этом должен думать программист.
Тем более если такое написано в теле цикла - компилятор тем более *p считает в регистр и всё время цикла будет её там хранить.
А вот уже если в этом теле цикла вы сделаете *p++, то тут поведение компилятора различно: если в этом цикле нет обращений через указатели к памяти, и стоит оптимизация "по скорости" то скорей всего он и модификацию выполнит в регистре, а сохранение в память - уже после выхода из цикла.
Если же обращения есть, то может сделать и так и так - в зависимости от компилятора от степени оптимизации.
К тому же в некоторых компиляторах для указателей можно задавать специальные модификаторы, говорящие что работа с данными через них не имеет "side effects", т.е. - что указатель указывает на свои отдельные данные, которые никак не пересекаются с данными других указателей и областями хранения переменных используемых в этой функции.
Это позволяет компилятору выполнять оптимизацию работы с этими указателями по-максимуму, например: загрузка данных из области этих указателей может выполняться заранее
(например в коде есть несколько обращений: p[1], p[2], p[3],... размазанных по циклу, компилятор может загрузить эти данные сразу в одном месте или даже вообще до входа в цикл или загружать данные для следующего прохода цикла в предыдущем проходе, менять кол-во проховдов цикла, частично или полностью разворачивать его, делая предварительную загрузку, а сохранение в такие указатели вообще - в последующих проходах цикла).
Это позволяет конвееризировать обработку данных, что очень ярко видно на процессорах, умеющих распараллеливать вычисления в несколько потоков, имеющих несколько АЛУ, например - DSP. DSP-шные оптимизаторы выполняют как правило такое преобразование кода, которое и не снилось на ARM-системах. Так как там в разы больше регистров и способов адресации памяти.
Укушенный воблой
Цитата(XVR @ Jan 19 2017, 06:34) *
Нет. Компилятор вещь достаточно умная - он понимает что такое указатель и к чему может привести взятие адреса переменной (для этой переменной)

Вы не читатель?
Так я повторю:
Цитата(Укушенный воблой @ Jan 19 2017, 04:18) *
Причем используют неявно. Без использования операции "взятие адреса" (амперсанд)

XVR
Цитата(Укушенный воблой @ Jan 19 2017, 20:24) *
Вы не читатель?
Так я повторю:
Сама переменная должна объявляться как волатАЙл если её используют через указатель?
Причем используют неявно. Без использования операции "взятие адреса" (амперсанд)
Так не бывает sm.gif
Приведите пример.

Укушенный воблой
Цитата(XVR @ Jan 19 2017, 20:21) *
Так не бывает sm.gif

Зачем?
С Вами и так все ясно.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.