Serg_el 0 1 апреля, 2014 Опубликовано 1 апреля, 2014 · Жалоба Коллеги! Правильно ли я понимаю, что чтение переменной в основном цикле, значение которой изменяется в прерывании, можно считать атомарным? Т.е. к примеру существует структура, элементами которой являются: 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 - это сложные указатели на структуры и для того, чтобы получить в итоге адрес, нужно несколько ассемблерных команд. Гарантируется ли в это случае компилятором сохранение всех участвующих регистров в стеке? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
demiurg_spb 0 1 апреля, 2014 Опубликовано 1 апреля, 2014 · Жалоба Правильно ли я понимаю, что чтение переменной в основном цикле, значение которой изменяется в прерывании, можно считать атомарным?Нет не так. Не можно считать атомарным, а должно быть атомарным. В худшем случае будет винегрет из полей структуры (часть старые, часть новые), а целостность самих полей не пострадает, пока не станете портировать на контроллер с другой архитектурой и разрядностью. Также интересно...Не о том вы думаете... Не надо думать ни о стеке ни о указателях. Главное, чтобы волатильные агрегатные переменные, значимые для логики вашей программы, читались-писались атомарно. Всё. Например: волатильная комплексно сопряжённая пара чисел не имеет смысла к существованию, если доступ к ней в целом не атомарен. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Serg_el 0 1 апреля, 2014 Опубликовано 1 апреля, 2014 · Жалоба Нет не так. Не можно считать атомарным, а должно быть атомарным. Не о том вы думаете... Не надо думать ни о стеке ни о указателях. Главное, чтобы волатильные агрегатные переменные, значимые для логики вашей программы, читались-писались атомарно. Всё. Например: комплексно сопряжённая пара чисел не имеет смысла к существованию, если доступ к ней в целом не атомарен. А что же по поводу сохранения всех регистров, участвующих в копировании в стек? Т.е. я на самом деле провел эксперимент, инкрементировал переменную в прерывании с периодом 40 мкс, в основном цикле считывал ее значение и если разность больше двух, то менял состояние порта. Состояние ни разу не менялось, т.е. переменная "не портилась" при чтении, а ассемблерном коде видно, что участвующие регистры сохраняются и восстанавливаются в прерывании. PS Раньше просто не заморачивался, делал обертку из запрета/разрешения прерывания и все. Но пока не возникла потребность в более частых прерываниях, а переменные, которые надо скопировать, не увеличились в количестве. Про LDREXW знаю и скорее всего буду использовать, главное знать, что это обязательно при чтении без модификации и записи. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
demiurg_spb 0 1 апреля, 2014 Опубликовано 1 апреля, 2014 · Жалоба А что же по поводу сохранения всех регистров, участвующих в копировании в стек? Т.е. я на самом деле провел эксперимент, инкрементировал переменную в прерывании с периодом 40 мкс, в основном цикле считывал ее значение и если разность больше двух, то менял состояние порта. Состояние ни разу не менялось, т.е. переменная "не портилась" при чтении, а ассемблерном коде видно, что участвующие регистры сохраняются и восстанавливаются в прерывании. Это хорошо, что ISR не портит регистры контекста. Так и задумывалось:) Ваш вопрос для меня не понятен. Попробуйте пожалуйста описать чего вы опасаетесь другими словами. Кстати, копия переменной не нужна вовсе, т.к. на CM3 доступ к 32-ух битным переменным атомарен от рождения (в смысле чтения и записи), а вот например её инкремент нифига не атомарная операция, а LOAD-MODIFY-STORE (аж 3 шага, которые не должны быть прерваны соответствующим ISR). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Serg_el 0 1 апреля, 2014 Опубликовано 1 апреля, 2014 · Жалоба Это хорошо, что 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 - часть сложной структуры? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
demiurg_spb 0 1 апреля, 2014 Опубликовано 1 апреля, 2014 · Жалоба Повторюсь, не нужно блокировать прерывания при записи 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 ... } } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Serg_el 0 1 апреля, 2014 Опубликовано 1 апреля, 2014 · Жалоба Повторюсь, не нужно блокировать прерывания при записи int - это и так атомарная операция. Да, никакой разницы нет, является ли int частью структуры или нет. Т.е. для CM3 вопрос атомарности возникает только для чтения/записи переменных long long? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 243 1 апреля, 2014 Опубликовано 1 апреля, 2014 · Жалоба Опасаюсь того, что при копировании значения из переменной, которая изменяется в прерывании, в переменную, которая работает только в основном цикле произойдет порча какого-либо регистра и я считаю не то значение. Боюсь этого потому, что нашел в доках, что сохраняются R0-R3, но не нашел, что и другие тоже. У вас каша в голове. Атомарность и сохранение контекста при прерываниях - совершенно не связанные между собой вещи. Будет-ли эта вещь красной если она круглая? как вы думаете? Т.е. для CM3 вопрос атомарности возникает только для чтения/записи переменных long long? Да. Чтения и записи long long в общем случае могут быть не атомарными (хотя не обязательно есть LDRD/STRD). Любые операции чтения или записи переменных с такой разрядностью, для которой нет соотв. команд в системе команд CPU (или эти команды не используются компилятором), буду неатомарными. Также почти все операции чтения-модификации-записи в Cortex-M3 будут неатомарными. Исключение - операции установки/сброса одиночных (или даже смежных для LDRD/STRD) битов в слове, реализуемые через bitbanding. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Golikov 0 1 апреля, 2014 Опубликовано 1 апреля, 2014 · Жалоба Да вы вообще не о том думаете. Какая разница вам про регистры? Если пишите на C, то нефиг думать про ASM пока не припрет временем или чем-то подобным. Во время возникновения прерывания все что надо будет сохранено, а потом восстановлено обратно. Если внутри обработчика потребуются другие регистры то компилятор их сам сохранит, а до выхода восстановит. Чтобы там ни было, по выходу из функции прерывания ничего не испортиться. Вопрос атомарности связан с другим: вот допустим есть счетчик который считает A = A + 1; и есть таймер который его сбрасывает, А = 0; процесс счета такой <---0 выбираем из памяти А <---1 увеличиваем выбранное значение А на 1 <---2 сохраняем полученное значение обратно в А <---3 процесс сброса Схраняем по адресу А, значение 0. если прерывание произойдет (вызовется обработчик) на 0, или 3, все пройдет штатно а если на 1 или 2, то что будет? А сброситься в 0, и по возвращению обратно пройдут прочие процедуры с сохраненным до прерывания значением и А не изменится... Это мелкое зло, для лонг переменных которые пишутся во много тактов, может так случится что пол переменной сменится и сохранится, а вторая часть испортится. Атомарность изменения переменной важно с точки зрения того, что в процесс ее изменения не может вклинится прерывание, поскольку этот момент случаен, то и результат случаен. А если взять наш первоначальный пример, но в прерывании А не будет сбрасываться, а будет только читаться и выводиться наружу, например, то даже вызванное в 1 и 2 прерывание, все отработает штатно, ничего не испортится, до тех пор пока чтение и запись переменной будет в 1 такт, и не будет шансов попасть между чтением первой половины и второй, например... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Serg_el 0 1 апреля, 2014 Опубликовано 1 апреля, 2014 · Жалоба Всем спасибо за комментарии! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
amaora 25 1 апреля, 2014 Опубликовано 1 апреля, 2014 · Жалоба Это такой ABI, один набор регистров сохраняется внутри вызываемой функции, другой вне, тем кто эту функцию вызывает. Т.к. ISR вызывает не тот кто выполнялся в момент события, сохранение второго набора регистров берет на себя апаратная часть которая и делат вызов ISR. Любой нормальный компилятор будет генерировать код который сохраняет первый набор регистров если они используюся или могут быть использованы в фкнкции. Про атомароность правильно заметили, что это другой вопрос. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
demiurg_spb 0 2 апреля, 2014 Опубликовано 2 апреля, 2014 · Жалоба Всем спасибо за комментарии!Всегда пожалуйста! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
KnightIgor 2 2 апреля, 2014 Опубликовано 2 апреля, 2014 · Жалоба Коллеги! Правильно ли я понимаю, что чтение переменной в основном цикле, значение которой изменяется в прерывании, можно считать атомарным? Т.е. к примеру существует структура, элементами которой являются: Как уже отвечали ниже, определенные инструкции обеспечивают атомарность операций чтения/записи элементарных данных. Это, однако, частные случаи. В общем же необходимо обеспечивать "атомарность" целого блока действий. Например, быстрый прием потока данных по прерываниям по UART в очередь (FIFO) и выборка этих данных в основной программе. В таком случае возникает проблема "атомарности" даже не чтения, а модификации целого ряда указателей и счетчиков. Обрамлять такие действия критической секцией с запретом прерываний - это по меньшей мере нарваться на "фе-е-е" крутых программеров на форуме. Поэтому подойдите к проблеме комплексно с самого начала, решив ее для себя один раз и навсегда. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Golikov 0 2 апреля, 2014 Опубликовано 2 апреля, 2014 · Жалоба туманно вы в конце написали а главное решения то не подсказали:)... того что раз и навсегда... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
KnightIgor 2 2 апреля, 2014 Опубликовано 2 апреля, 2014 (изменено) · Жалоба туманно вы в конце написали а главное решения то не подсказали:)... того что раз и навсегда... Направление пути ясно, Google в помощь. Ну, а если разойтись-таки на многа букаф, то повторю то, что я уже как-то писал в форуме по поводу незапрета прерываний в основной программе (мы не говорим об ОСях и в них встроенные механизмы разграничения доступа). Принцип (на котором у меня надежно работают FIFO) заключается в использовании свойства NVIC вызывать прерывания до тех пор, пока соответствующий флаг не сброшен. Впрочем, и другие архитектуры в основном также работают, но не всегда: в MSC-51 с UART может не прокатить. Стояла как всегда задача реализации FIFO с приемом от последовательного устройства (UART|SPI) по прерыванию и выборки данных в основной программе. Чтобы не запрещать прерывания в основной программе я применил следующее решение: 1. Заводится флаг критической секции. 2. Основная программа окружает критическую секцию, в которой она модифицирует общие с прерыванием ресурсы FIFO (указатели/счетчики), следующими действиями: 2.1. Установить флаг. 2.2. Выполнить действия. 2.3. Сбросить флаг. 2.4. (Пере)разрешить прерывание от источника данных. Прерывание работает так: 3. проверить флаг критической секции. 4. Если установлен - НЕ сбрасывать свои биты прерывания, ЗАПРЕТИТЬ свое прерывание и выйти. Если сброшен - выполнить надлежащие действия над данными и указателями/счетчиками, сбросить биты прерывания, выйти. Что получится, если в прерывании флаг критической секции обнаружен установленным, и прерывание запретило само себя? Основная программа, которая этот флаг установила, продолжит выполнять действия, ничего не зная о прерывании, сбросит флаг критической секции, после чего, как в п. 2.4, переразрешит прерывание. Прерывание тут же вызовется снова, обнаружит сброшеный флаг и произведет соответствующие модицикации. Конечно, принципиально от запрета прерывания избавиться не получается, но его запрещает не основная программа непонятно когда и зачем (и невовремя), а само прерывание, и только себя, и только, когда попало внутрь критической секции основного цикла. Получается как бы отложенная обработка прерывания. Изменено 2 апреля, 2014 пользователем KnightIgor Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться