I am working on some C++ code for small microcontrollers (AVR). We use a
struct MCU
to represent the register layout of the MCU. In the past all registers were of type uint8_t
and we use the manufacture provided cpp
-macros to set inidividual bits. This not the C++ way ...
We introduced the ControlRegister
class template: it can be parametrized with the acceptable value type:
template<typename Component, typename BitType, typename ValueType = uint8_t>
struct __attribute__((packed)) ControlRegister final {
typedef Component component_type;
typedef ValueType value_type;
typedef BitType bit_type;
template<typename... Flags>
void inline set(Flags... f) {
static_assert((std::is_same<bit_type, Flags>::value && ... && true), "wrong flag type");
static_assert(sizeof...(Flags) <= 8, "too much flags");
value_type v = (static_cast<value_type>(f) | ... | 0);
assert(Util::numberOfOnes(v) == sizeof... (Flags));
hwRegister = v;
}
template<BitType... Flags>
void inline add() {
static_assert(sizeof...(Flags) <= 8, "too much flags");
constexpr auto v = (static_cast<value_type>(Flags) | ... | 0);
constexpr auto n = Util::numberOfOnes(v);
static_assert(n == sizeof... (Flags), "use different flags");
hwRegister |= v;
}
private:
volatile value_type hwRegister;
};
Some MCU registers have a two-fold usage, e.g. setting a timer prescaler and some other interrupt information. The prescaler bits are used at several places and we thought it would be better to divide the register into two or more parts resembling the structure of the particular register. Since this is essentially the very same memory location the idea to use a union came into mind:
struct MCU final {
struct CompA {
enum class Flags1 {
f1 = (1 << 0),
f2 = (1 << 1)
};
enum class Flags2 {
f1 = (1 << 1),
f2 = (1 << 2)
};
union {
ControlRegister<CompA, Flags1> part1;
ControlRegister<CompA, Flags2> part2;
} __attribute__ ((packed));
};
};
The part2
-member can be the forementioned prescaler component: the same value-type Flags
can be used in several places.
I've read several posts about unions in C++, but I still have no clear inbterpretation if this code fragment yields to UB.
Looking at the assembler-language output of g++ leads to the assumption, that it is ok. But I'm not sure ...!
int main() {
const auto c = reinterpret_cast<MCU::CompA*>(0x28);
using F1 = MCU::CompA::Flags1;
c->part1.set(F1::f1, F1::f1); // writing
using F2 = MCU::CompA::Flags2;
c->part2.set(F2::f1);
c->part2.add<F2::f2>(); // reading
while(true) {}
}