I have a uint64 variable which often only requires high or low 32 bit access. I am using a 32-bit ARM Cortex M0, and to help with speed and I am trying to overlap the uint64 var
You could get fancy and use assembly to achieve what you want, or use C++.
In assembly
EXPORT AB
EXPORT A
EXPORT B
AB
A SPACE 4
B SPACE 4
In C:
extern uint64_t AB;
extern uint32_t A;
extern uint32_t B;
Then do what you want.
In C++, something like this:
union MyUnionType
{
uint64_t ab;
struct
{
uint32_t a;
uint32_t b;
};
} ;
MyUnionType MyUnion;
uint64_t &ab = MyUnion.ab;
uint32_t &a = MyUnion.a;
uint32_t &b = MyUnion.b;
But, to be honest, this is all wasted effort. Accessing a
or MyUnion.a
is going to have the same code generated by the compiler. It knows the size and offsets of everything, so it's not going to calculate anything at runtime, but just load from the right address it knows ahead of time.
To clarify what I've learned:
It turns out C doesn't allow global members of anonymous structs/unions. Oh well. But using named structs/unions generates efficient code anyway, as the member offset is known at compile time and can be added without incurring extra instructions.
Regarding using shifts & masks instead of a union, this may work well for reads, but for writes it results in extra instructions (9 vs 5) and pointless accesses to the low uint32. On the other hand, it is more endian portable than a union, but that isn't important in my application.
union status {
struct { uint32_t user, system; };
uint64_t all;
};
volatile union status status;
status.system |= 1u; // write to high uint32 member directly
2301 movs r3, #1
4A02 ldr r2, 0x00002910
6851 ldr r1, [r2, #4]
430B orrs r3, r1
6053 str r3, [r2, #4]
status.all |= ((uint64_t)1)<<32; // write to full uint64
2001 movs r0, #1
4905 ldr r1, 0x00002910
680C ldr r4, [r1]
684D ldr r5, [r1, #4]
4328 orrs r0, r5
1C22 adds r2, r4, #0
1C03 adds r3, r0, #0
600A str r2, [r1] // this is not atomic, and pointless
604B str r3, [r1, #4] // this is the important part
You define a union without an instance, which means that there is no union object containing the members. You could probably do something like this:
main.h:
typedef union {
uint64_t ab;
struct { uint32_t a, b; };
} u_t;
extern u_t u;
main.c:
u_t u = { .a = 1; };
And if you really wanted to (in main.h):
#define a u.a
#define b u.b
#define ab u.ab
If you do use #define
s, be wary that they affect any declaration/use of the identifiers (a, b, ab), even those in a different scope. I recommend that instead you just access the values explictly via the u
object (as u.a
, u.b
, u.ab
).
I have removed volatile
from the declarations because I highly doubt you really needed it. But you can certainly add it back if you wish.
(Note: the question originally had code split across two files, main.h and main.c. My answer correspondingly has code for two files. However, these could be combined into one easily).
There is actually little to no reason to use a union. Instead, use shift/mask, possibly in inline
functions to extract the two halves:
static inline uint32_t upper(uint64_t val)
{ return (uint32_t)(val >> 32); }
static inline uint32_t lower(uint64_t val)
{ return (uint32_t)val; }
This will be very likely optimized by the compiler to the same code as the union-approach.
However, as you are refering to anonymous struct/union members: Omitting the name is a feature of the struct/union which includes the member, not the member being a struct/union. So you can use:
union {
uint64_t v64;
struct {
uint32_t l32;
uint32_t h32;
}; // here the name can been omitted
} my_flex_var;
Problems are:
uint64_t
.Normal usage of a volatile is to load/store it always with full size. If that is not the case, a race condition might uccur and you are in the world of locking/mutex, etc. which makes things very much more complicated. If the two fields are only loosely related, you are very likely better off with two 32 bit variables or a struct
thereof.
Typical usage:
volatile uint64_t v64;
void int_handler(void)
{
uint64_t vt = v64;
uint32_t l = lower(vt);
uint32_t h = higher(vt);
}
This ensures the variable is read only once. With a proper compiler, the assignments to l
, h
will not generate any code, but the registers with vt
are used. That depends, of course on your code, but even if there is some overhead, that will be negligible.
(Sitenote: This is from my own practise as a long time embedded programmer)
It's simply not possible to do like that. A global variable (like the ones you declare in the header file) are not the same as members of an anonymous structure or union, that simply wont work.
And having anonymous structures or unions won't help with pointer arithmetic, the structure will still sit somewhere in memory, and the compiler uses offsets from the structures base-address to find out where the members are. However, since both the base-address and member offsets are know at time of compilation, the compiler will generally be able to generate code to access the member directly, just like any other variable. Look at the generated code if you are unsure.
So skip the nonsense with anonymous structures, define them properly in the header files, and declare variables of those structures in the header files too, while defining those variables in some source file.
So for the header file:
union you_union
{
uint64_t ab;
struct { uint32_t a, b; };
};
extern union your_union your_union;
And in a source file
union your_union your_union;
No, of course you can't access members of a non-instantiated type. Where would the data be stored?
You should just make the union
visible, and trust the compiler to generate efficient accesses. I don't think there's going to be any "pointer arithmetic", at least not at run-time.