реклама на сайте
подробности

 
 
> Атомарность чтения, в Cortex M3
Serg_el
сообщение Apr 1 2014, 08:41
Сообщение #1


Местный
***

Группа: Свой
Сообщений: 302
Регистрация: 13-12-06
Из: Togliatti
Пользователь №: 23 473



Коллеги!

Правильно ли я понимаю, что чтение переменной в основном цикле, значение которой изменяется в прерывании, можно считать атомарным?

Т.е. к примеру существует структура, элементами которой являются:

1) 32-битная переменная, значение которой меняется через некий интерфейс (UART, CAN и т.п.) - изменяется в прерывании получения пакета;
2) копия этой переменной, значение которой может использоваться в основном цикле как угодно (чтение, запись), но к которой нет доступа из прерываний;
3) 32-битная переменная, значение которой уменьшается до 0 в прерывании таймера (время жизни переменной 1) и устанавливается равной некой константе в прерывании получения пакета;

typedef struct
{
volatile unsigned int Value_int;

unsigned int Value;

volatile unsigned int Valid_Timer;

} RxParameterInfo;

В основном цикле, если переменная 3 не равна 0, переменная 2 = переменной 1, иначе = 0.

if (Valid_Timer)
{
Value = Value_int;
}
else
{
Value = 0;
}

Вопрос возник из-за того, что Value = Value_int; состоит из минимум 3х ассемблерных команд. Но как я понимаю при входе в прерывание регистры R0-R2 автоматически сохраняются, а остальные через PUSH.
Т.е. мы в итоге не теряем информации об адресах и в результате присваивания в худшем случае в Value мы будем иметь предыдущее значение Value_int.

Также интересно, если Value или Value_int - это сложные указатели на структуры и для того, чтобы получить в итоге адрес, нужно несколько ассемблерных команд. Гарантируется ли в это случае компилятором сохранение всех участвующих регистров в стеке?

Go to the top of the page
 
+Quote Post
3 страниц V   1 2 3 >  
Start new topic
Ответов (1 - 14)
demiurg_spb
сообщение Apr 1 2014, 09:15
Сообщение #2


неотягощённый злом
******

Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643



Цитата(Serg_el @ Apr 1 2014, 12:41) *
Правильно ли я понимаю, что чтение переменной в основном цикле, значение которой изменяется в прерывании, можно считать атомарным?
Нет не так. Не можно считать атомарным, а должно быть атомарным.

Цитата(Serg_el @ Apr 1 2014, 12:41) *
В худшем случае будет винегрет из полей структуры (часть старые, часть новые),
а целостность самих полей не пострадает, пока не станете портировать на контроллер с другой архитектурой и разрядностью.

Цитата(Serg_el @ Apr 1 2014, 12:41) *
Также интересно...
Не о том вы думаете... Не надо думать ни о стеке ни о указателях.
Главное, чтобы волатильные агрегатные переменные, значимые для логики вашей программы, читались-писались атомарно. Всё.
Например: волатильная комплексно сопряжённая пара чисел не имеет смысла к существованию, если доступ к ней в целом не атомарен.


--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
Go to the top of the page
 
+Quote Post
Serg_el
сообщение Apr 1 2014, 09:20
Сообщение #3


Местный
***

Группа: Свой
Сообщений: 302
Регистрация: 13-12-06
Из: Togliatti
Пользователь №: 23 473



Цитата(demiurg_spb @ Apr 1 2014, 13:15) *
Нет не так. Не можно считать атомарным, а должно быть атомарным.
Не о том вы думаете... Не надо думать ни о стеке ни о указателях.
Главное, чтобы волатильные агрегатные переменные, значимые для логики вашей программы, читались-писались атомарно. Всё.
Например: комплексно сопряжённая пара чисел не имеет смысла к существованию, если доступ к ней в целом не атомарен.



А что же по поводу сохранения всех регистров, участвующих в копировании в стек?

Т.е. я на самом деле провел эксперимент, инкрементировал переменную в прерывании с периодом 40 мкс, в основном цикле считывал ее значение и если разность больше двух, то менял состояние порта. Состояние ни разу не менялось, т.е. переменная "не портилась" при чтении, а ассемблерном коде видно, что участвующие регистры сохраняются и восстанавливаются в прерывании.


PS Раньше просто не заморачивался, делал обертку из запрета/разрешения прерывания и все. Но пока не возникла потребность в более частых прерываниях, а переменные, которые надо скопировать, не увеличились в количестве. Про LDREXW знаю и скорее всего буду использовать, главное знать, что это обязательно при чтении без модификации и записи.
Go to the top of the page
 
+Quote Post
demiurg_spb
сообщение Apr 1 2014, 09:24
Сообщение #4


неотягощённый злом
******

Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643



Цитата(Serg_el @ Apr 1 2014, 13:20) *
А что же по поводу сохранения всех регистров, участвующих в копировании в стек?

Т.е. я на самом деле провел эксперимент, инкрементировал переменную в прерывании с периодом 40 мкс, в основном цикле считывал ее значение и если разность больше двух, то менял состояние порта. Состояние ни разу не менялось, т.е. переменная "не портилась" при чтении, а ассемблерном коде видно, что участвующие регистры сохраняются и восстанавливаются в прерывании.

Это хорошо, что ISR не портит регистры контекста. Так и задумывалосьsm.gif
Ваш вопрос для меня не понятен.
Попробуйте пожалуйста описать чего вы опасаетесь другими словами.

Кстати, копия переменной не нужна вовсе, т.к. на CM3 доступ к 32-ух битным переменным атомарен от рождения (в смысле чтения и записи),
а вот например её инкремент нифига не атомарная операция, а LOAD-MODIFY-STORE (аж 3 шага, которые не должны быть прерваны соответствующим ISR).


--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
Go to the top of the page
 
+Quote Post
Serg_el
сообщение Apr 1 2014, 09:42
Сообщение #5


Местный
***

Группа: Свой
Сообщений: 302
Регистрация: 13-12-06
Из: Togliatti
Пользователь №: 23 473



Цитата(demiurg_spb @ Apr 1 2014, 13:24) *
Это хорошо, что ISR не портит регистры контекста. Так и задумывалосьsm.gif
Ваш вопрос для меня не понятен.
Попробуйте пожалуйста описать чего вы опасаетесь другими словами.


Опасаюсь того, что при копировании значения из переменной, которая изменяется в прерывании, в переменную, которая работает только в основном цикле произойдет порча какого-либо регистра и я считаю не то значение. Боюсь этого потому, что нашел в доках, что сохраняются R0-R3, но не нашел, что и другие тоже. Т.е. почему это делает IAR и гарантирует ли он подобные сохранения я не знаю. Вот запрет/разрешение прерываний это гарантирует. __LDREXW и __STREXW тоже гарантирует, но увеличивает время при копировании переменных. Хочу понять можно ли просто скопировать значение без использования обертки?

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

void SysTick_Handler (void)
{
if (Counter_Timer)
{
Counter_Timer--;
}
}

int main(void)
{
while (1)
{
__disable_irq();
Counter_Timer = 10;
__enable_irq();

if (!Counter_Timer)
{
// Do something
}
}
}

Также допустимо ли это в случае, если Counter_Timer - часть сложной структуры?
Go to the top of the page
 
+Quote Post
demiurg_spb
сообщение Apr 1 2014, 10:31
Сообщение #6


неотягощённый злом
******

Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643



Повторюсь, не нужно блокировать прерывания при записи int - это и так атомарная операция.
Да, никакой разницы нет, является ли int частью структуры или нет.
Код
volatile int Counter_Timer;

void SysTick_Handler(void)
{
    if (Counter_Timer)
        Counter_Timer--;
}

int main(void)
{
    while (1)
    {
        Counter_Timer = 10;

        while (Counter_Timer)
           ; // wait
        ...
    }
}


--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
Go to the top of the page
 
+Quote Post
Serg_el
сообщение Apr 1 2014, 11:12
Сообщение #7


Местный
***

Группа: Свой
Сообщений: 302
Регистрация: 13-12-06
Из: Togliatti
Пользователь №: 23 473



Цитата(demiurg_spb @ Apr 1 2014, 14:31) *
Повторюсь, не нужно блокировать прерывания при записи int - это и так атомарная операция.
Да, никакой разницы нет, является ли int частью структуры или нет.


Т.е. для CM3 вопрос атомарности возникает только для чтения/записи переменных long long?

Go to the top of the page
 
+Quote Post
jcxz
сообщение Apr 1 2014, 12:05
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(Serg_el @ Apr 1 2014, 15:42) *
Опасаюсь того, что при копировании значения из переменной, которая изменяется в прерывании, в переменную, которая работает только в основном цикле произойдет порча какого-либо регистра и я считаю не то значение. Боюсь этого потому, что нашел в доках, что сохраняются R0-R3, но не нашел, что и другие тоже.

У вас каша в голове. Атомарность и сохранение контекста при прерываниях - совершенно не связанные между собой вещи.
Будет-ли эта вещь красной если она круглая? как вы думаете?

Цитата(Serg_el @ Apr 1 2014, 17:12) *
Т.е. для CM3 вопрос атомарности возникает только для чтения/записи переменных long long?

Да. Чтения и записи long long в общем случае могут быть не атомарными (хотя не обязательно есть LDRD/STRD).
Любые операции чтения или записи переменных с такой разрядностью, для которой нет соотв. команд в системе команд CPU (или эти команды не используются компилятором),
буду неатомарными.
Также почти все операции чтения-модификации-записи в Cortex-M3 будут неатомарными. Исключение - операции установки/сброса одиночных (или даже смежных для LDRD/STRD)
битов в слове, реализуемые через bitbanding.
Go to the top of the page
 
+Quote Post
Golikov A.
сообщение Apr 1 2014, 12:06
Сообщение #9


Гуру
******

Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454



Да вы вообще не о том думаете.

Какая разница вам про регистры?

Если пишите на C, то нефиг думать про ASM пока не припрет временем или чем-то подобным.

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

Вопрос атомарности связан с другим:
вот допустим есть счетчик который считает A = A + 1;
и есть таймер который его сбрасывает, А = 0;

процесс счета такой

<---0
выбираем из памяти А
<---1
увеличиваем выбранное значение А на 1
<---2
сохраняем полученное значение обратно в А
<---3

процесс сброса
Схраняем по адресу А, значение 0.

если прерывание произойдет (вызовется обработчик) на 0, или 3, все пройдет штатно
а если на 1 или 2, то что будет?

А сброситься в 0, и по возвращению обратно пройдут прочие процедуры с сохраненным до прерывания значением и А не изменится...


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

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

А если взять наш первоначальный пример, но в прерывании А не будет сбрасываться, а будет только читаться и выводиться наружу, например, то даже вызванное в 1 и 2 прерывание, все отработает штатно, ничего не испортится, до тех пор пока чтение и запись переменной будет в 1 такт, и не будет шансов попасть между чтением первой половины и второй, например...






Go to the top of the page
 
+Quote Post
Serg_el
сообщение Apr 1 2014, 12:48
Сообщение #10


Местный
***

Группа: Свой
Сообщений: 302
Регистрация: 13-12-06
Из: Togliatti
Пользователь №: 23 473



Всем спасибо за комментарии!
Go to the top of the page
 
+Quote Post
amaora
сообщение Apr 1 2014, 16:41
Сообщение #11


Местный
***

Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778



Это такой ABI, один набор регистров сохраняется внутри вызываемой функции, другой вне, тем кто эту функцию вызывает. Т.к. ISR вызывает не тот кто выполнялся в момент события, сохранение второго набора регистров берет на себя апаратная часть которая и делат вызов ISR. Любой нормальный компилятор будет генерировать код который сохраняет первый набор регистров если они используюся или могут быть использованы в фкнкции.

Про атомароность правильно заметили, что это другой вопрос.
Go to the top of the page
 
+Quote Post
demiurg_spb
сообщение Apr 2 2014, 03:30
Сообщение #12


неотягощённый злом
******

Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643



Цитата(Serg_el @ Apr 1 2014, 16:48) *
Всем спасибо за комментарии!
Всегда пожалуйста!


--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
Go to the top of the page
 
+Quote Post
KnightIgor
сообщение Apr 2 2014, 05:20
Сообщение #13


Знающий
****

Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725



Цитата(Serg_el @ Apr 1 2014, 09:41) *
Коллеги!
Правильно ли я понимаю, что чтение переменной в основном цикле, значение которой изменяется в прерывании, можно считать атомарным?
Т.е. к примеру существует структура, элементами которой являются:

Как уже отвечали ниже, определенные инструкции обеспечивают атомарность операций чтения/записи элементарных данных. Это, однако, частные случаи. В общем же необходимо обеспечивать "атомарность" целого блока действий. Например, быстрый прием потока данных по прерываниям по UART в очередь (FIFO) и выборка этих данных в основной программе. В таком случае возникает проблема "атомарности" даже не чтения, а модификации целого ряда указателей и счетчиков. Обрамлять такие действия критической секцией с запретом прерываний - это по меньшей мере нарваться на "фе-е-е" крутых программеров на форуме. Поэтому подойдите к проблеме комплексно с самого начала, решив ее для себя один раз и навсегда.
Go to the top of the page
 
+Quote Post
Golikov A.
сообщение Apr 2 2014, 06:06
Сообщение #14


Гуру
******

Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454



туманно вы в конце написали а главное решения то не подсказалиsm.gif... того что раз и навсегда...
Go to the top of the page
 
+Quote Post
KnightIgor
сообщение Apr 2 2014, 08:28
Сообщение #15


Знающий
****

Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725



Цитата(Golikov A. @ Apr 2 2014, 07:06) *
туманно вы в конце написали а главное решения то не подсказалиsm.gif... того что раз и навсегда...

Направление пути ясно, Google в помощь.
Ну, а если разойтись-таки на многа букаф, то повторю то, что я уже как-то писал в форуме по поводу незапрета прерываний в основной программе (мы не говорим об ОСях и в них встроенные механизмы разграничения доступа).

Принцип (на котором у меня надежно работают FIFO) заключается в использовании свойства NVIC вызывать прерывания до тех пор, пока соответствующий флаг не сброшен. Впрочем, и другие архитектуры в основном также работают, но не всегда: в MSC-51 с UART может не прокатить.

Стояла как всегда задача реализации FIFO с приемом от последовательного устройства (UART|SPI) по прерыванию и выборки данных в основной программе. Чтобы не запрещать прерывания в основной программе я применил следующее решение:
1. Заводится флаг критической секции.
2. Основная программа окружает критическую секцию, в которой она модифицирует общие с прерыванием ресурсы FIFO (указатели/счетчики), следующими действиями:
2.1. Установить флаг.
2.2. Выполнить действия.
2.3. Сбросить флаг.
2.4. (Пере)разрешить прерывание от источника данных.

Прерывание работает так:
3. проверить флаг критической секции.
4. Если установлен - НЕ сбрасывать свои биты прерывания, ЗАПРЕТИТЬ свое прерывание и выйти. Если сброшен - выполнить надлежащие действия над данными и указателями/счетчиками, сбросить биты прерывания, выйти.

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

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

Сообщение отредактировал KnightIgor - Apr 2 2014, 08:30
Go to the top of the page
 
+Quote Post

3 страниц V   1 2 3 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 23rd July 2025 - 20:23
Рейтинг@Mail.ru


Страница сгенерированна за 0.01483 секунд с 7
ELECTRONIX ©2004-2016