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

Коллеги!

 

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

 

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

 

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 - это сложные указатели на структуры и для того, чтобы получить в итоге адрес, нужно несколько ассемблерных команд. Гарантируется ли в это случае компилятором сохранение всех участвующих регистров в стеке?

 

 

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


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

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

 

В худшем случае будет винегрет из полей структуры (часть старые, часть новые),

а целостность самих полей не пострадает, пока не станете портировать на контроллер с другой архитектурой и разрядностью.

 

Также интересно...
Не о том вы думаете... Не надо думать ни о стеке ни о указателях.

Главное, чтобы волатильные агрегатные переменные, значимые для логики вашей программы, читались-писались атомарно. Всё.

Например: волатильная комплексно сопряжённая пара чисел не имеет смысла к существованию, если доступ к ней в целом не атомарен.

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


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

Нет не так. Не можно считать атомарным, а должно быть атомарным.

Не о том вы думаете... Не надо думать ни о стеке ни о указателях.

Главное, чтобы волатильные агрегатные переменные, значимые для логики вашей программы, читались-писались атомарно. Всё.

Например: комплексно сопряжённая пара чисел не имеет смысла к существованию, если доступ к ней в целом не атомарен.

 

 

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

 

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

 

 

PS Раньше просто не заморачивался, делал обертку из запрета/разрешения прерывания и все. Но пока не возникла потребность в более частых прерываниях, а переменные, которые надо скопировать, не увеличились в количестве. Про LDREXW знаю и скорее всего буду использовать, главное знать, что это обязательно при чтении без модификации и записи.

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


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

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

 

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

Это хорошо, что ISR не портит регистры контекста. Так и задумывалось:)

Ваш вопрос для меня не понятен.

Попробуйте пожалуйста описать чего вы опасаетесь другими словами.

 

Кстати, копия переменной не нужна вовсе, т.к. на CM3 доступ к 32-ух битным переменным атомарен от рождения (в смысле чтения и записи),

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

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


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

Это хорошо, что ISR не портит регистры контекста. Так и задумывалось:)

Ваш вопрос для меня не понятен.

Попробуйте пожалуйста описать чего вы опасаетесь другими словами.

 

Опасаюсь того, что при копировании значения из переменной, которая изменяется в прерывании, в переменную, которая работает только в основном цикле произойдет порча какого-либо регистра и я считаю не то значение. Боюсь этого потому, что нашел в доках, что сохраняются 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 - часть сложной структуры?

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


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

Повторюсь, не нужно блокировать прерывания при записи 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
        ...
    }
}

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


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

Повторюсь, не нужно блокировать прерывания при записи int - это и так атомарная операция.

Да, никакой разницы нет, является ли int частью структуры или нет.

 

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

 

 

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


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

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

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

Будет-ли эта вещь красной если она круглая? как вы думаете?

 

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

Да. Чтения и записи long long в общем случае могут быть не атомарными (хотя не обязательно есть LDRD/STRD).

Любые операции чтения или записи переменных с такой разрядностью, для которой нет соотв. команд в системе команд CPU (или эти команды не используются компилятором),

буду неатомарными.

Также почти все операции чтения-модификации-записи в Cortex-M3 будут неатомарными. Исключение - операции установки/сброса одиночных (или даже смежных для LDRD/STRD)

битов в слове, реализуемые через bitbanding.

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


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

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

 

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

 

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

 

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

 

Вопрос атомарности связан с другим:

вот допустим есть счетчик который считает A = A + 1;

и есть таймер который его сбрасывает, А = 0;

 

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

 

<---0

выбираем из памяти А

<---1

увеличиваем выбранное значение А на 1

<---2

сохраняем полученное значение обратно в А

<---3

 

процесс сброса

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

 

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

а если на 1 или 2, то что будет?

 

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

 

 

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

 

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

 

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

 

 

 

 

 

 

 

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


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

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

 

Про атомароность правильно заметили, что это другой вопрос.

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


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

Всем спасибо за комментарии!
Всегда пожалуйста!

 

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


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

Коллеги!

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

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

Как уже отвечали ниже, определенные инструкции обеспечивают атомарность операций чтения/записи элементарных данных. Это, однако, частные случаи. В общем же необходимо обеспечивать "атомарность" целого блока действий. Например, быстрый прием потока данных по прерываниям по UART в очередь (FIFO) и выборка этих данных в основной программе. В таком случае возникает проблема "атомарности" даже не чтения, а модификации целого ряда указателей и счетчиков. Обрамлять такие действия критической секцией с запретом прерываний - это по меньшей мере нарваться на "фе-е-е" крутых программеров на форуме. Поэтому подойдите к проблеме комплексно с самого начала, решив ее для себя один раз и навсегда.

 

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


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

туманно вы в конце написали а главное решения то не подсказали:)... того что раз и навсегда...

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


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

туманно вы в конце написали а главное решения то не подсказали:)... того что раз и навсегда...

Направление пути ясно, 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

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


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

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

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

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

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

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

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

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

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