On core_cm4.h why is there a listing like ((uint32_t) (int32_t) IRQn)?
In the following code from core_cm4.h why is there a double listing ((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?
source to share
Since the CM4 software implements negative interrupt sources for the kernel, you need to specify the value of a signed 32-bit integer first and later an unsigned 32-bit integer value to do the correct right shift with numbering zeros on the left side of the number.
CM4 uses -15 to -1 as CM4-Core sources and 0 to next as vendor specific sources.
source to share
Looking at this snippet, it's clear that whoever wrote it was confused about how the implicit type promotion rules work. Also, >> 5UL
it looks very suspicious, and when I see I immediately suspect that this codebase is suffering from poor understanding of MISRA-C; maybe they are using a bad static analyzer that spits out false positives.
A visit to Github confirms my suspicion that it is correct, and it says the intent is to follow MISRA-C: 2004.
MISRA-C requires that no dangerous, implicit types happen. The throws represent some unsuccessful attempts to silence the static analyzer without understanding why the tool gave these warnings.
Correct, compatible MISRA-C code (2004 and 2012) would be:
NVIC->ISER[((uint32_t)IRQn>>5UL)] = (1UL << ((uint32_t)IRQn & 0x1FUL));
(MISRA-C requires complex subexpressions to use parentheses, even though they take precedence.)
This code is still messy, so it is advisable to rewrite it for readability into something that will give 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 required that the result of the shift expression be immediately passed to the "base type" (MISRA term). So it could be written (IRQn_Type)(IRQn >> 5UL)
and it will still be MISRA-C: 2004 compatible.
However, there is nothing in MISRA to prevent you from adding your own cast to another type, for example uint32_t
, before changing. This is best done because it removes implicit promotion entirely.
For those confused about how implicit type promotions work in C, see Immediate Promotion Rules .
source to share
Assuming that IRQn
is an integer (any sign of an integer type) in the range you say then (uint32_t)(int32_t)IRQn
matches exactly with (uint32_t)IRQn
.
The author of the code may not have understood the C type conversion rules; which are based on values, not views. Converting -3
, for example, to uint32_t
always gives UINT32_MAX - 2
, no matter what data type was -3
. This value "negative 3" matters.
(Another answer explains the difference between use and great act).
source to share