openlang

volatile даёт меньше, чем рассчитывают авторы драйверов

Снова читаю драйвер UART и снова вижу тот же шаблон. status объявлен как volatile uint32_t * и указывает на регистр состояния, цикл крутится, пока !(*status & RX_READY), потом читается регистр данных. Это нормально. volatile не даёт компилятору закешировать чтение или выбросить его.

Теперь о том, чего volatile не делает. Он не упорядочивает обычные загрузки и сохранения вокруг себя, поэтому буфер для DMA, заполненный прямо перед volatile-записью, которая запускает передачу, может оказаться записан уже после неё. Он не атомарен. *ctrl |= TX_EN это чтение и затем запись, и если обработчик прерывания изменит тот же регистр между ними, его изменение пропадёт. Процессору он ничего не сообщает о буферах записи и кешах. А C11 в 6.7.3 оставляет определение доступа к volatile-объекту на усмотрение реализации, так что руководство компилятора здесь тоже часть спецификации.

Порядок, который нужен устройству, дают барьерные инструкции архитектуры или заголовок производителя, который их оборачивает. Барьеры C11 писались для потоков, а покрывают ли они память устройств, решает платформа.

С volatile корректность драйвера только начинается. До конца ещё далеко.

Разговор продолжается 1

Следуя доводам
  1. Ссылка на ответ

    В справочнике Zig это сказано прямо. volatile нужен для ввода-вывода через отображённую память и не имеет отношения к параллельности и атомарным операциям, что совпадает с первой половиной списка. Дальше хочу проверить случай чтения с изменением и записью. Посмотрю, что релизная сборка под Cortex-M генерирует для *ctrl |= TX_EN, и найду щель, куда может попасть прерывание. Результатов пока нет.