In the following code from core_cm4.h why is there a double cast ((uint32_t)(int32_t)IRQn)
?
For example in the following function:
__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn)
{
NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL));
}
What is the purpose of this?
Since CM4 software implements negative interrupt sources for core, you have to cast value first to 32bit signed integer and later to unsigned 32bit to make proper right shift with padding zeros on the left side of number.
CM4 uses -15 to -1 as CM4-Core sources and from 0 to next as vendor specific sources.
By looking at that snippet, it is clear that whoever wrote it was confused over how implicit type promotion rules work. In addition, the >> 5UL
looks very fishy and when I see that I immediately suspect that this code base suffers from a poor understanding of MISRA-C; possibly they are using a bad static analyser which spits out false positives.
A visit at Github proves my suspicion to be correct, there's comments stating that the intention is to follow MISRA-C:2004.
MISRA-C demands that no dangerous, implicit type promotions may occur. The casts are some failed attempts to silence a static analyser, without an understanding why the tool gave those warnings.
Correct, MISRA-C (2004 and 2012) compliant code would be this:
NVIC->ISER[((uint32_t)IRQn>>5UL)] = (1UL << ((uint32_t)IRQn & 0x1FUL));
(MISRA-C has a requirement that complex sub-expressions must use parenthesis despite what operator precedence there is.)
This code is still messy, so it should preferably be rewritten for readability, into something that will produce exactly the same machine code:
uint32_t ISER_index = ((uint32_t)IRQn >> 5UL);
uint32_t shift_n = ((uint32_t)IRQn & 0x1FUL);
NVIC->ISER[ISER_index] = (1UL << shift_n);
Side note:
MISRA-C:2004 demanded that the result of the shift expression should be immediately cast to the "underlying type" (MISRA term). Thus one could also have written (IRQn_Type)(IRQn >> 5UL)
and it would still be MISRA-C:2004 compliant.
However, there is nothing in MISRA preventing you to adding your own cast to a different type, such as uint32_t
, before the shift. This is a better thing to do, since it eliminates the implicit promotion entirely.
For those who are confused about how the implicit type promotions work in C, see Implicit type promotion rules.
Assuming IRQn
is an integer (any signed integer type) in the range you say, then (uint32_t)(int32_t)IRQn
is exactly the same as (uint32_t)IRQn
.
The author of the code possibly didn't understand C type conversion rules; which are based on values, not representations. Converting -3
, for example, to uint32_t
always gives UINT32_MAX - 2
regardless of which data type the -3
was. It's the value of "negative 3" that matters.
(The other answer explains the difference between using a cast and no cast at all).