Что делает компилятор C с битовыми полями? - PullRequest
8 голосов
/ 21 июля 2010

Я работаю над встроенным проектом (цель PowerPC, компилятор Freescale Metrowerks Codewarrior), в котором регистры отображаются в памяти и определены в хороших битовых полях, чтобы упростить изменение отдельных битовых флагов.

В данный моментмы используем эту функцию для очистки флагов прерываний и управления передачей данных.Хотя я еще не заметил никаких ошибок, мне было любопытно, если это безопасно.Есть ли какой-нибудь способ для безопасного использования битовых полей, или мне нужно обернуть каждое из них в DISABLE_INTERRUPTS ... ENABLE_INTERRUPTS?

Чтобы уточнить: заголовок, поставляемый с микро, имеет поля типа

union {
        vuint16_t R;
        struct {
            vuint16_t MTM:1;        /* message buffer transmission mode */
            vuint16_t CHNLA:1;      /* channel assignement */
            vuint16_t CHNLB:1;      /* channel assignement */
            vuint16_t CCFE:1;       /* cycle counter filter enable */
            vuint16_t CCFMSK:6;     /* cycle counter filter mask */
            vuint16_t CCFVAL:6;     /* cycle counter filter value */
        } B;
    } MBCCFR;

Я предполагаю, что установка битового поля не является атомарной.Это правильное предположение?Какой код на самом деле генерирует компилятор для битовых полей?Выполнение маски самостоятельно с использованием поля R (raw) может помочь запомнить, что операция не является атомарной (легко забыть, что присвоение типа CAN_A.IMASK1.B.BUF00M = 1 не является атомарным).

Ваш советценится.

Ответы [ 6 ]

3 голосов
/ 21 июля 2010

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

Так много о том, как битовые поля обрабатываются компилятором, определяется реализацией (включая то, как обрабатываются поля, охватывающие границы байтов или слов, проблемы с бесконечностью и как именно осуществляется получение, установка и очистка битов).См. C / C ++: порядок и выравнивание поля принудительных битов

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

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

Эти функции могут быть изменены для архитектур, поддерживающих атомарный доступ на битовом уровне (например, ARM Cortex M3)бит-полоса "адресации).Я не знаю, поддерживает ли PowerPC что-то подобное - M3 - единственный процессор, с которым я имел дело, который поддерживает его в общем виде.И даже битовая полоса M3 поддерживает 1-битный доступ;если вы имеете дело с полем шириной 6 бит, вам придется вернуться к сценарию чтения / изменения / записи.

3 голосов
/ 21 июля 2010

Да, ваше предположение верно, в том смысле, что вы не можете предполагать атомарность.На конкретной платформе вы можете получить ее как дополнительную, но ни в коем случае не можете положиться на нее.

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

3 голосов
/ 21 июля 2010

Правильно предположить, что установка битовых полей не является атомарной. Стандарт C не совсем ясно, как должны быть реализованы битовые поля, и различные компиляторы используют их по-разному.

Если вы действительно заботитесь только о своей целевой архитектуре и компиляторе, разберите некоторый объектный код.

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

Вы всегда можете написать функцию-обертку-установщика для атомарных битов, если вы беспокоитесь о том, что будущие кодеры (включая вас) будут сбиты с толку.

3 голосов
/ 21 июля 2010

Атомность зависит от цели и компилятора. Например, AVR-GCC пытается обнаружить доступ к битам и, если возможно, выдать установленные биты или сбросить команды. Проверьте выходные данные ассемблера, чтобы убедиться ...

РЕДАКТИРОВАТЬ: Вот ресурс для атомарных инструкций по PowerPC прямо изо рта лошади:

http://www.ibm.com/developerworks/library/pa-atom/

0 голосов
/ 21 июля 2010

Я почти уверен, что на powerpc это не атомарно, но если ваша цель - одноядерная система, вы можете просто:

void update_reg_from_isr(unsigned * reg_addr, unsigned set, unsigned clear, unsigned toggle) {
   unsigned reg = *reg_addr;
   reg |= set;
   reg &= ~clear;
   reg ^= toggle;
   *reg_addr = reg;
}

void update_reg(unsigned * reg_addr, unsigned set, unsigned clear, unsigned toggle) {
   interrupts_block();
   update_reg_from_isr(reg_addr, set, clear, toggle);
   interrupts_enable();
}

Не помню, чтобы обработчики прерываний в powerpc были прерываемыми, но если они есть, то вам всегда следует использовать вторую версию.

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

Я однажды прочитал, как реализовать блокировки в powerpc - он велел процессору следить за шиной памяти по определенному адресу, пока вы выполняли некоторые операции, а затем проверял в конце этих операций, чтобы увидеть, был ли адрес наблюдения был написан другим ядром. Если этого не произошло, то ваша операция была успешной; если это было так, вы должны были повторить операцию. Это было в документе, написанном для разработчиков компиляторов, библиотек и ОС. Я не помню, где я нашел это (вероятно, где-то на IBM.com), но небольшая охота должна показать это. Вероятно, в нем также есть информация о том, как сделать атомарный битовый поворот.

0 голосов
/ 21 июля 2010

Это полностью зависит от архитектуры и компилятора, являются ли операции с битовыми полями атомарными или нет.Мой личный опыт говорит: не используйте битовые поля, если вам не нужно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...