Когда мне нужно использовать volatile в ISR?

Я делаю встроенную прошивку, где все после инициализации происходит в ISR. У меня есть общие переменные, и мне интересно, в каких случаях они должны быть изменчивыми. Я никогда не блокирую, ожидая смены в другом ISR.

Когда я могу быть уверен, что фактическая память читается или записывается, если не используется volatile? Один раз в ISR?

Дополнение:
Это для ARM Cortex-M0, но на самом деле это вопрос не столько об ISR, сколько об оптимизации компилятора, и поэтому платформа не должна иметь большого значения.


person oyvind    schedule 04.10.2014    source источник
comment
Я подозреваю, что на это нельзя ответить без указания поддерживающей операционной системы или другой информации о контексте, в котором работают ISR. Даже с этой информацией я не смогу вам помочь.   -  person Jonathan Leffler    schedule 04.10.2014
comment
Поскольку стандарт C ничего не говорит о таких вещах, вся проблема полностью зависит (с моей точки зрения) от платформы и того, что делает платформа (компилятор плюс o/s плюс h/w). Является ли код однопоточным или многопоточным; сколько ядер в процессоре; какие общие ресурсы для ISR изменяют (в частности). Общие ресурсы только для чтения, вероятно, в порядке, хотя, если чтение разрушительно, потому что оно собирает данные из какого-то регистра, вы должны относиться к нему с такой же осторожностью, как если бы вы писали. Я признаю, что в последний раз я делал что-то похожее на это 30 лет назад.   -  person Jonathan Leffler    schedule 04.10.2014
comment
Я пытаюсь понять оптимизацию компилятора в общих чертах, поэтому я надеюсь, что ответ на мой вопрос не сильно различается между платформами, поскольку я полагаю, что основные принципы оптимизации переменных в основном одинаковы для разных компиляторов. Например, могу ли я быть уверен, что глобальная переменная будет записана в память хотя бы один раз, если она записана в коде?   -  person oyvind    schedule 04.10.2014
comment
По соглашению с @JonathanLeffler, для окончательного ответа представлено недостаточно информации. Учитывая это, взгляните на это: embedded.com/electronics-blogs/beginner-s-corner/4023801/ Как всегда, GIYF (Google вам в помощь), есть много полезного материала.   -  person Throwback1986    schedule 04.10.2014
comment
Нет — вы не можете полагаться на планирование записи в глобальные переменные относительно других операций. Это произойдет в конце концов, но не обязательно сразу (где в конечном итоге может быть несколько инструкций позже или это может быть дольше). Частично это зависит от того, действует ли у вас взаимное исключение; тайники; есть ли вокруг несколько потоков; есть ли прерывания с более высоким приоритетом, которые могут помешать текущему; сообщили ли вы компилятору, что переменная была volatile. Вы могли шлепнуть volatile на все; однако это не лучший способ программирования.   -  person Jonathan Leffler    schedule 04.10.2014
comment
volatile во встроенной системе в основном используется для описания регистра, и это важно. иногда вы обнаруживаете, что вам нужно сделать reg|=0x1;reg&=~0x1;, и это имеет смысл. без volatile это будет оптимизировано без кода, если только вы не отключите оптимизатор, что иногда вам не понравится. к простому вводу-выводу памяти, я не вижу причины.   -  person Jason Hu    schedule 04.10.2014


Ответы (3)


На вопрос вполне можно ответить, и ответ прост:

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

Вам нужно volatile, чтобы сообщить компилятору, что побочные эффекты доступа могут быть важны для чего-то, что оптимизатор не может проанализировать, например контекст прерывания или контекст другого «потока».

По сути, volatile — это то, как вы говорите компилятору: «Я знаю то, чего не знаешь ты, поэтому не пытайся быть умным здесь».

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

person Chris Stratton    schedule 04.10.2014
comment
мне нравится этот ответ. действительно, встроенная система использует много volatile из-за этого. - person Jason Hu; 04.10.2014

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

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

даже несмотря на то, что компиляторы не должны быть умными с изменчивыми переменными. Он может выполнять некоторые оптимизации в отношении изменчивых точек последовательности (оптимизация по точкам последовательности не разрешена, но оптимизация между точками последовательности разрешена)

person bare_metal    schedule 04.10.2014

Переменные, которые нуждаются в volatile:

1) Обмен данными между ISR и программными данными или другими потоками.
Предпочтительно, чтобы это были флаги, которые указывают блочный доступ к различным структурам данных.

// main() code;
disable_interrupts();
if (flag == 0) {
  flag = 1;
  enable_interrupts();
  Manipulate_data();
  flag = 0;
} else {
  enable_interrupts();
  Cope_with_data_unavailable();
}

2) Аппаратные регистры с отображением памяти.
Эта «память» может измениться в любое время из-за состояния оборудования, и компилятор должен знать, что их значения не обязательно совпадают. Без volatile наивный компилятор будет сэмплировать fred только один раз, что может привести к бесконечному циклу.

volatile int *fred = 0x1234; // Hardware reg at address 0x1234;
while (*fred);
person chux - Reinstate Monica    schedule 04.10.2014