1
\$\begingroup\$

I find the coding style in the STM32F0 (ARM Cortex-M0 microcontroller) SPL (standard peripheral library) unnecessarily verbose. As an example, here is a snippet of code for configuring the phase locked loop for SYSCLK:

/* Select PLL as system clock source */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;  

Where RCC_CFGR_SW and RCC_CFGR_SW_PLL are macros for integer literals already cast to uint32_t.

#define RCC_CFGR_SW     ((uint32_t)0x00000003) 
#define RCC_CFGR_SW_PLL ((uint32_t)0x00000002)  

And RCC->CFGR is a (memory mapped) instance of a struct of the type

typedef struct
{
    ...
    __IO uint32_t CFGR; /*!< RCC clock configuration register, Address offset: 0x04 */
    ...  
} RCC_TypeDef;  

So the result would be implicitly cast to uint32_t anyway, even giving a warning if something funny happens (unlike what would happen with an explicit cast).


Can these explicit casts be eliminated, especially given that C99 guarantees (6.3.1.3) that

  • When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.

Allowing the simplification of the code to the significantly clearer

//Select the PLL as system clock source
RCC->CFGR &= ~(RCC_CFGR_SW);
RCC->CFGR |= RCC_CFGR_SW_PLL;  

Or could that cause some unforeseen side effects? I'm not really sure, and I presume that the people who wrote that code are more experienced writing embedded C than I am...

I'm asking this question on electronics.stackexchange instead stackoverflow since it's quite specific to low level embedded programming.

\$\endgroup\$
7
  • \$\begingroup\$ Can you guarantee that the outcome of all those expressions is a uint32_t? is 0x00000003 also a uint32_t? Do you really want to worry that it is and think about if or if not it is guaranteed, or do you want to make sure the result is, thus simply tell the compiler htat you want it to be? \$\endgroup\$
    – PlasmaHH
    Sep 19 '16 at 19:40
  • 4
    \$\begingroup\$ "I presume that the people who wrote that code are more experienced writing embedded C than I am" Clearly you haven't got much experience with ST SPLs yet :D \$\endgroup\$
    – Armandas
    Sep 19 '16 at 19:41
  • 1
    \$\begingroup\$ I think stackoverflow would be better. This is exactly what the hardcore C language lawyers deal with, and has been dealing with since 1989 when all the weird C preprocessor+runtime "implicit integer conversions" were standardized. I'm sure you'll get good answers here too though. \$\endgroup\$
    – pipe
    Sep 19 '16 at 19:42
  • \$\begingroup\$ Have a look at the Misra rules to get a feeling on why all those casts might be necessary. \$\endgroup\$
    – Arsenal
    Sep 19 '16 at 19:47
  • 2
    \$\begingroup\$ gotta agree with @Armandas. and i totally disagree with Arsenal. i know straight ANSI C quite well and, assuming that RCC->CFGR is also uint32_t, then the casts in the first two statements are totally redundant. there are a lot of crappy programmers around that do not realize that, besides making code that works, it is also important to make code concise, readable, and easily maintained. \$\endgroup\$ Sep 19 '16 at 20:23
2
\$\begingroup\$

It's been a while since I squandered time reading C standards. But there is a clue from all that which may be helpful, generally. I don't think it is much help here, though. Let me state what I recall and then consider if there are any further comments. (I'm not sure this is the place to look for C compiler writers, though; and they are the ones who'd know the exact details.)

When C converts a signed int to an unsigned int, of the same physical size, the standard requires that the C compiler produce an unsigned result which either (1) has the same equivalent value meaning in the unsigned symbol space, or else; (2) is the signed value modulo \$2^{bitsize}\$. Such conversions are guaranteed a specific mathematical result. One that most people expect, in fact. However, when an unsigned int is converted to a signed int, again of the same physical size, the standard requires that if and only if there exists an equivalent value meaning, then that meaning must be maintained. If, however, the unsigned value exceeds the limit of positive value meaning, then there is no standard result, at all. The C compiler can, I suppose, generate random nonsense if it wants to.

It could be that the left hand doesn't know what the right hand is doing in the coding you are seeing. You are able to go look, of course, and see the obvious. But it's possible that those writing the expressions are trying to be defensive in the sense that someone might later re-write a constant from what used to be an unsigned value to a signed (or by default, signed) value. (C compilers are, or used to be, permitted to make their own default choices when the signed or unsigned specifier was absent. And if they weren't permitted, some certainly did so anyway.)

Casting things to unsigned is always guaranteed a specific result. So it's safer to use. Or once was, when I was reading the standards years ago. Just in case someone used explicitly, or by compiler default, a signed value.

All that said, I can't recall a C compiler that did something crazy when converting an unsigned int to a signed int. In general, assuming the computer hardware used the usual twos complement notation, they all just do the obvious and make them bitwise identical. It's only the interpretation that changes, later. But that doesn't mean that someone who has actually read the standards would care -- they might decide to write as if there was a crazy C compiler out there. Just to prove they'd read the standard.

Now, I haven't considered the code you show in the context of differing size instances. But the above might give you a clue about why you are seeing such stuff there.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.