On Mon, 13 Sept 2021 at 07:55, Rafał Pietrak via Gcc <gcc@gcc.gnu.org> wrote: > > Hi everybody, > > I've been using GCC for a varety of ARM micro controller project. With > embedded systems, one often has to access specific memory locations, > which contain hardware registers, that control device behavior. > "traditionally" or "commonly" that's codded by programmers using > "#define", thus relaying on preprocessor to put specific compiler > constructs, that do the job. > > IMHO, this is wrong exactly because of the involvement of preprocessor. > Taking as an example exerpts from libopencm3, like: > >> USB_SET_EP_RX_STAT(addr, USB_EP_RX_STAT_VALID) > one can understand, that such "code" can actually translate to quite > anything. > > Getting to the point, here is code exerpt of by own STM32 usart driver: > ------------------------------------------------------------------ > #define USART_RE 11 > #define USART_RENEIE 8 > #define USART_TE 10 > #define USART_TXEIE 6 > #define USART_UE 0 > > extern struct usart_s { > uint sr; > uint dr; // offset 0x04 > uint brr; // offset 0x08 > volatile union { uint cr1; // offset 0x0C > struct cr_s { uint sbk:1, rwu:1, > re:1, te:1, idleie:1, rxneie:1, > tcie:1, txeie:1, peie:1, ps:1, > pce:1, wake:1, m:1, ue:1;} cr1_s; > }; > } USART1; > > static void EXIT(void *tty) { > volatile struct usart_s *d = &USART1; > > #if VARIANT_TRADITIONAL > d->cr1 &= ~(1 << USART_RE) & ~(1 << USART_RXNEIE) > & ~(1 << USART_TE) & ~(1 << USART_TXEIE) > & ~(1 << USART_UE); > #elif VARIANT_WORKING > struct cr_s a = (struct cr_s) { > .re = 1, > .te = 1, > .rxneie = 1, > .txeie = 1, > .ue = 1 }; > int *b = (int *) &a; > d->cr1 &= ~(*b);
This is a strict aliasing violation. You should either use a union or memcpy to get the value as an int. > #elif VARIANT_BETTER > (union cr_u) d->cr1 &= ~ { > .re = 1, > .te = 1, > .rxneie = 1, > .txeie = 1, > .ue = 1 }; > #elif VARIANT_THE_BEST > d->cr1_s &= ~ { .re = 1, > .te = 1, > .rxneie = 1, > .txeie = 1, > .ue = 1 }; > #endif > } > ---------------------------------------------------------------- > > In function EXIT, you can see four variants of "the same code". First > fragment is codded just like todays common practice is. This is BAD > coding, because debugger has no way of presenting "the real thing". Have you tried -ggdb3 which does preserve macro information? > Using gcc-8-branch revision 273027 (arm-none-eabi-gcc linux cross > compiler) I was able to code the very same thing by VARIANT_WORKING, > which is far better to read (IMHO). The disadvantage is, that I had to > use variable "b" to fool gcc syntax parser. No direct multiple "type > overwrite" worked for me. > > Then again, it would be even better (and easier to read the code), > should gcc syntax parser recognised VARIANT_BETTER or VARIANT_THE_BEST > as "syntactic sugar" to generate code, that currently gets issued by > VARIANT_WORKING (being the same as issued by VARIANT_TRADITIONAL, which > is very OK). You can define an inline function that initializes the struct, then returns the negation of the int value. I very much doubt GCC will add an extension to make ~ work on structs. What would it even mean if sizeof(struct cr_s) == 3 or == 1024?