Регистры с отображением в памяти только для чтения, определенные с `volatile const` в C, но только` volatile` в Stack Overflow

Работая над проектом встроенных систем с использованием Atmel SAM3X8E, я заметил следующий фрагмент кода в некоторых заголовочных файлах CMSIS.

#ifndef __cplusplus
typedef volatile const uint32_t RoReg; /**< Read only 32-bit register (volatile const unsigned int) */
#else
typedef volatile       uint32_t RoReg; /**< Read only 32-bit register (volatile const unsigned int) */
#endif

Почему typedef для C ++ не включает const? Я видел где-то упоминание о том, что C ++ не хранит целочисленные константные переменные в оперативной памяти, которая, если true, будет означать const пришлось бы удалить из-за того, как регистры микроконтроллера отображаются в памяти, но я не могу найти ничего другого, говорящего, что C ++ делает это (хотя мой поиск был довольно коротким). Не имея большого опыта работы с C ++, я также подумал, что, возможно, C ++ не позволяет const члены структуры, так как эти typedefs в основном используются в struct typedefs для коллекций регистров, но, похоже, это тоже не так.

6

Решение

Потому что нет RoReg объект когда-либо создается, нет веских причин, чтобы опустить const классификатор в typedef.

Каждое использование RoReg находится в макросе, который определяет указатель на тип …

#define REG_WDT_SR (*(RoReg*)0x400E1A58U) /**< \brief (WDT) Status Register */

…или struct объявление, доступ к которому осуществляется с помощью аналогичного макроса.

typedef struct {
WoReg WDT_CR; /**< \brief (Wdt Offset: 0x00) Control Register */
RwReg WDT_MR; /**< \brief (Wdt Offset: 0x04) Mode Register */
RoReg WDT_SR; /**< \brief (Wdt Offset: 0x08) Status Register */
} Wdt;

#define WDT        ((Wdt    *)0x400E1A50U) /**< \brief (WDT) Base Address */

Даже с const квалификатор, код должен вести себя одинаково как на C, так и на C ++.

Возможно, автор неверно истолковал стандарт. Чтобы гарантировать, что структура C ++ имеет ту же структуру, что и в C, требуется, чтобы класс «имел одинаковое управление доступом (пункт 11) для всех нестатических элементов данных». Автор, возможно, ошибся const а также volatile для спецификаторов контроля доступа. Если бы они были, то вы бы хотели, чтобы все члены структуры имели одинаковые cv-квалификаторы, чтобы обеспечить совместимость между макетами C и C ++ (и аппаратными). Но это public, protected, а также private которые определяют контроль доступа.

3

Другие решения

Если вы объявите с помощью const, стандарт C ++ будет обязывать вас инициализировать содержимое переменной. В случае с регистром микроконтроллера, вы не хотите этого делать.

5

Как упомянуто @fanl, const действительно изменяет связывание глобалов по умолчанию в C ++ и не позволяет определить переменную без инициализации.

Но есть лучшие способы получить внешнюю связь, чем удаление const, Использование зарезервированных массивов в заголовочном файле, связанном Крисом, также очень хрупко. Я бы сказал, что этот код оставляет много возможностей для улучшения — не подражайте ему.

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

Для заголовков, предназначенных исключительно для использования C ++, я так и делаю (карта памяти, соответствующая чипу TI Stellaris).

Выглядит сложно, но оптимизирующий компилятор сводит его к одной инструкции на доступ. А смещения адресов кодируются независимо от порядка и заполнения полей внутри структуры, поэтому это гораздо менее хрупко и легче проверить по данным таблицы.

template<uintptr_t extent>
struct memory_mapped_peripheral
{
char data[extent];
volatile       uint32_t* offset( uintptr_t off )       { return reinterpret_cast<volatile       uint32_t*>(data+off); }
volatile const uint32_t* offset( uintptr_t off ) const { return reinterpret_cast<volatile const uint32_t*>(data+off); }
};

struct LM3S_SYSTICK : private memory_mapped_peripheral<0x1000>
{
volatile       uint32_t& CTRL   (void)             { return offset(0x010)[0]; }
volatile       uint32_t& RELOAD (void)             { return offset(0x014)[0]; }
volatile       uint32_t& CURRENT(void)             { return offset(0x018)[0]; }
}* const SYSTICK = reinterpret_cast<LM3S_SYSTICK*>(0xE000E000);

struct LM3S_NVIC : private memory_mapped_peripheral<0x1000>
{
volatile       uint32_t& EN    (uintptr_t i)       { return offset(0x100)[i]; }
volatile       uint32_t& DIS   (uintptr_t i)       { return offset(0x180)[i]; }
volatile       uint32_t& PEND  (uintptr_t i)       { return offset(0x200)[i]; }
volatile       uint32_t& UNPEND(uintptr_t i)       { return offset(0x280)[i]; }
volatile const uint32_t& ACTIVE(uintptr_t i) const { return offset(0x300)[i]; }
volatile       uint32_t& PRI   (uintptr_t i)       { return offset(0x400)[i]; }
volatile       uint32_t& SWTRIG(void)              { return offset(0xF00)[0]; }
}* const NVIC = reinterpret_cast<LM3S_NVIC*>(0xE000E000);
2
По вопросам рекламы [email protected]