On Mon, 13 Sept 2021 at 07:55, Rafał Pietrak via Gcc <[email protected]> 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?