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?

Reply via email to