Read-only write modes defined with "volatile const" in C, but only "volatile" in C ++

While working on an embedded system project using Atmel SAM3X8E, I noticed the following bit of code in some CMSIS header files.

#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 

Why doesn't typedef for C ++ include const ? I saw somewhere mentioning that C ++ does not store integer constant variables in memory at runtime, which, if true, means that const will need to be deleted because the microcontroller registers are mapped to the memory card, but I can't find anything else to say that C ++ does this (although my search was admittedly quite brief). Without much experience with C ++, I also thought that C ++ does not allow const members of the structure, since these typedefs are mainly used in struct typedefs for register collections, but this does not seem to be the case.

+6
source share
3 answers

Since a RoReg object RoReg never created, there is no good reason to omit the const qualifier in a typedef.

Each use of RoReg is either in a macro defining a pointer to a type ...

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

... or a struct that is accessed using a similar macro.

 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 */ 

Even with the const qualifier, the code should behave the same in both C and C ++.

Perhaps the author misinterpreted the standard. To ensure that the C ++ structure has the same layout as in C, it is required that the class β€œhas the same access control (section 11) for all non-static data members”. The author may be wrong const and volatile for access control specifiers. If they were, then you would like all members of the structure to have the same cv-qualifiers to ensure compatibility between C and C ++ layouts (and hardware). But these are public , protected and private , which define access control.

+3
source

If you declare with a constant, the C ++ standard commits you to initialize the contents of the variable. In the case of the microcontroller register, you do not want to do this.

+5
source

As already mentioned by @fanl, const does change the default binding to global variables in C ++ and does not allow you to define a variable without initialization.

But there are better ways to get an external connection than removing const . The use of reserved arrays in the header file associated with Chris is also very fragile. I would say that this code leaves a lot of room for improvement - do not emulate it.

In addition, these variables cannot be defined (which the compiler and linker will call to select an address), they are always accessed through pointers, and the address is fixed in accordance with the memory card.

For headers intended solely for use with C ++, this is how I do it (the memory card matches the TI Stellaris chip).

It looks more complicated, but the optimizing compiler reduces it to a single access statement. And the address offsets are encoded, regardless of the order and filling of the fields inside the structure, so it is much less fragile and easier to check in comparison with the data table.

 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
source

All Articles