Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Пн янв 24, 2022 17:16:51
Охохо, а хотите ещё масла в огонь подолью? Volatile можно ещё на функции применять!
Пн янв 24, 2022 17:20:42
И это правильно и логично.
Пн янв 24, 2022 17:50:27
>TEHb<, только что проверил и на gcc, и на clang пример
отсюда. Не работает volatile применительно к функции в языке С!
Пн янв 24, 2022 18:14:55
Eddy_Em, всё верно говоришь. volatile можно только к методам класса (тобишь C++) применять и влияет он на доступ к this.
Compiler Exploer
Пн янв 24, 2022 18:59:26
да.... а старые компиляторы могли, по крайней мере, мне попадались такое в коде. Странно. Вроде же вполне логично: возвращаемое значение функции может быть бессмысленным с точки зрения оптимизатора, притом вызов как есть функции важен по каким либо-причинам.
Впрочем, это можно легко реализовать, но запись получится длиньше и страньше.
Пн янв 24, 2022 20:36:31
Давно пора привыкнуть, что программа не то что вы о ней думаете, а то что о ней думает компилятор.
Кто объяснит, как сущность размером в 1 байт хранит значения явно большей разрядности?
Compiler ExploerЕстественно, никакая другая память нигде не используется, если что. Скажу больше, там даже байта нет, просто компилятор не может признаться, что у него объект размером меньше байта
Добавлено after 49 minutes 27 seconds:Вроде же вполне логично: возвращаемое значение функции может быть бессмысленным с точки зрения оптимизатора, притом вызов как есть функции важен по каким либо-причинам.
Это называется побочный эффект. Вот именно там где он создаётся и должна стоять volatile. Функции это даром не надо. Компилятор может этот эффект просчитать и воспроизвести без лишней шелухи.
Вт янв 25, 2022 10:02:34
Eddy_Em, как конкретно писали? У меня GCC принимает объявление функции
- Код:
void volatile init()
{
};
и работает с ней так, как я того и ожидаю. Фактически ничего не оптимизируя внутри этой функции.
Вт янв 25, 2022 10:52:55
Брехня!
Как в самой функции пусто, так и её вызова вообще нет. volatile перед функцией относится к типу возвращаемого ей результата. Ко всему что происходит внутри это не имеет отношения.
Вт янв 25, 2022 11:03:23
>TEHb<, какая версия gcc? Я проверял на 10 и 11 — не работает volatile, да и не должен. Не помню я такого, чтобы в стандарте С были бы волатильные функции!
Вт янв 25, 2022 11:22:32
8.3 , если не ошибаюсь. Наверно, стоило сразу уточнить, что речь про компиляцию под ARM, а конкретно Cortex-M4 ядро. Короче, ситуация такая: программа под СТМку и в этом самом ините как раз и идёт настройка всего и вся оборудования. Некоторые узлы требуют или ожидание готовности флага или некоторую фиксированную задержку. Без volatile порядок записи в регистры периферии сохранялся, а вот время "оптимизировалось". Ну и привет, ничего не работает. Добавил волшебное слово и вуаля! Все времена сохранятся как просил. Иначе, делал через прагму с конкретным указанием уровня оптимизации, но это как-то криво показалось.
Ср янв 26, 2022 18:22:03
Ну да, вы правы. Секрет заблуждения оказался в том, что, видимо, дописал квалификатор к одной переменной одновременно с функцией. Ну и получил, конечно же, желаемый эффект. Компилятор при оптимизации на скорость по сравнению с выключенной, всё-таки немного корёжит порядок (собирает LDR-ы паровозиком), но побочки оставляет как его и попросили. Короче, крутил, вертел, и что с volatile на функции, что без, получал одинаковый выхлоп.
P.S.: ну не брехня, но точно ошибка
Ср янв 26, 2022 19:22:14
Тоже полазил, и где-то промелькнуло, что когда-то давно функции волатилили чтобы что-то там с возвратом void было корректно. Как говорится, уже забыл, а что вспомнил переврал, но примерно так
Похоже, решалась какая-то особенность компилятора и достойно теперь лишь археологов
Чт янв 27, 2022 09:21:43
ох какая тема живучая)
Сб янв 29, 2022 19:22:51
А вот, если не влом поясните, как понимать... если переменная используется не явно. вот пример кода:
- Код:
uint32_t fram_log_decode (instance_t *instance, int *none) {
static data_buffer_t log_buffer;
unsigned int record_count = 0, ii, jj, mask;
FRAM_dma_read_Start(0x00000));
FRAM_dma_log_read((uint8_t*)&record_count, sizeof(record_count)));
FRAM_dma_wait_EOT();
for (ii = 0; ii < record_count; ii++) {
FRAM_dma_log_read((uint8_t*)&log_buffer, sizeof(log_buffer)));
итд...
В коде читается SPI FRAM в первых четырёх ячейках хранится 32 битное число записей, которое надо прочитать в переменную record_count, которая тут же используется в цикле for для считывания этих записей, которые идут дальше. проблема в том, что явно я туда не пишу - передаю только указатель на эту переменную. И внутри функции FRAM_dma_log_read там тоже нет явной записи. Как следует из названия, там программируется DMA, который и сделает (а может и не сделает) эту запись. Тогда получается, что формально, я эту переменную обязан обозвать волатильной? И структуру log_buffer тоже?
Сб янв 29, 2022 19:39:46
Да, конечно. Если данные в переменной появляются в результате работы прерывания или dma, то она должна быть volatile. В случае с указателями и массивами компилятору тяжело проследить, что вообще нигде нет обращений и чаще всего всё будет работать. Ваш код с FRAM скорее всего тоже. Но это недоработка компилятора и нет никаких гарантий, что завтра он не станет настолько умным, что всё почикает. Так что, действуйте формально - всё что может поменяться из DMA пусть будет volatile. Спать будете спокойней точно.
Сб янв 29, 2022 21:35:50
А вот не получается спокойно спать. Проблема в том, что есть только одна операция, которая требует чтобы структура log_buffer был волатильной - это чтение из FRAM, а потом идёт разбор этих данных с выводом. И вот там уже волатильность крайне вредна, потому как данные после того как считались поменяться никак не могут до следующей итерации. Нельзя ли как-нибудь этот момент корректно обойти. Может каким хитрым приведением типов? Например, указать что функции FRAM_dma_log_read передаётся указатель на волатильную переменную, а подсунуть обычную, приведенную к волатильному типу (как сейчас привожу к байтовому типу)
Сб янв 29, 2022 22:41:06
Проблема в том, что есть только одна операция, которая требует чтобы структура log_buffer был волатильной - это чтение из FRAM,
Вот тут это как раз до одного места. DMA передаётся тупо адрес, что там по нему лежит ему неинтересно.
а потом идёт разбор этих данных с выводом. И вот там уже волатильность крайне вредна,
Тут надо смотреть что такое data_buffer_t. А то ведь в зависимости от этого вредность может варьироваться от нулевой до вредной. В крайне вредную я не верю, не ходите же вы по ней туда-сюда-обратно. Покажите определение data_buffer_t.
Вс янв 30, 2022 12:11:20
VladislavS писал(а):Тут надо смотреть что такое data_buffer_t.
А там не на что смотреть - просто 23 байта:
- Код:
typedef struct data_buffer {
uint32_t Time; // 4
union{
struct{
int16_t setspeedLeft; // 2
int16_t setspeedRight; // 2
int16_t RealSpeedLeft; // 2
int16_t RealSpeedRight; // 2
int32_t StepsLeft; // 4
int32_t StepsRight; // 4
uint8_t vbat;
uint8_t sensors;
};
struct{
int coordX; // 4
int coordY; // 4
unsigned int segmentLength; // 4
int nodeNum; // 4
};
};
uint8_t whereami;
} __attribute__((packed)) data_buffer_t;
И пока их интерпретация довольно проста - числа выводятся в обычном представлении. Ну, чуть поразвесистей работа с полем sensors и whereami:
Конечно, тут я уже не спешу - считавание лог-файла это не realtime задача - это уже постфактум и тут оптимизация мне не требуется.
Просто сама мысль, как писать правильно?
Вот, допустим, у меня была функция чтения FRAM сделанная по-тупому:
- Код:
unsigned int FRAM_log_read(uint8_t *rd_data_ptr, unsigned int wr_data_size) {
while (wr_data_size--) {
*rd_data_ptr++ = get_byte_from_fram();
}
}
Вроде, там явное присваивание и волатильность совсем не требуется. Но со временем я хочу улучшить работу и делаю это или через прерывания, или ПДП, но сохраняя интерфес вызова этих функций, чтобы не переписывать те куски, где она вызывалась.
Но тут уже всё, явного присваивания нет и компилятор и/или линкер берут и просто выкидывают эти куски кода. Тогда получается, что надо теперь бегать по всем исходникам искать эти вызовы и расставлять модификаторы?
Вс янв 30, 2022 12:50:38
Что-то вы всё усложняете. Функции копирования (не важно циклом, memcpy или DMA) не надо передавать информацию о том что место куда вы копируете volatile. Вы передаёте просто адрес (указатель) и функция по нему скопирует, у неё нет выбора. Для функции копирования volatile сущностью будет ваша FRAM. Всё, забыли про функцию чтения FRAM.
Переходим к log_buffer. Он должен быть volatile. Потому что при копироапнии в него данных через DMA логика компилятора будет такая: о, смотри ка, глобальный буфер, инициализирован нулями, понятно же что при любом чтении из него будет ноль, ну и буду везде его подставлять без чтения, а там глядишь и половину кода можно даже не компилировать, а выкинуть - красота! Нет, говорим мы, там будет лежать для тебя сюрприз, будь добр взять его и честно отработать.
Теперь давай подумаем, ухудшит ли volatile быстродействие? Конечно, по сравнению с полностью выкинутым кодом да. Но по сравнению с правильно работающей программой нет! У вас развесистая структура, доступ к её полям по адресу этой структуры. Если вы все поля будете читать по одному разу - ничего не изменится. Если какие-то поля стоят в выражениях несколько раз, то будут лишние чтения. Но это легко парировать, прочитали поле в локальную временную переменную и подставляйте куда надо. Так часто делают со статусными регистрами в прерываниях, когда надо от наличия разных битов сделать разные действия. Вошли в прерывание, прочитали регистр статуса в локальную переменную и дальше проверяйте её сколько угодно раз, она будет в регистре процессора лежать. Да, вы должны держать в голове, что работаете с volatile сущностью и тогда штраф за её использование можно свести к нулю.
Powered by phpBB © phpBB Group.
phpBB Mobile / SEO by Artodia.