Работая над проектом встроенных систем с использованием 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 для коллекций регистров, но, похоже, это тоже не так.
Потому что нет 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
которые определяют контроль доступа.
Если вы объявите с помощью const, стандарт C ++ будет обязывать вас инициализировать содержимое переменной. В случае с регистром микроконтроллера, вы не хотите этого делать.
Как упомянуто @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);