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).
Related
Suppose I have
typedef struct {
unsigned short bar : 1;
} foo_bf;
typedef union {
unsigned short val;
foo_bf bf;
} foo_t;
How do I correctly assign a value to this bitfield from an type e.g uint16_t?
uint16_t myValue = 1;
foo_t foo;
foo.bf.bar = myValue
Running PC-Lint, this turns into a MISRA error:
Expression assigned to a narrower or different essential type.
I tried to limit the number of used bits without any success.
foo.bf.bar = (myValue 0x1U)
Is there any chance to get it MISRA complient if I have to use a uint16_t value as origin?
MISRA-C's essential type model isn't really applicable to bit-fields. The terms narrower and wider refer to the size in bytes (see 8.10.2). So it isn't obvious if a static analyser should warn here or not, since the rules for essential type do not address bit-fields.
EDIT: I was wrong here, see the answer by Andrew. Appendix D.4 tells how to translate a bit-field type to the matching essential type category.
However, using bit-fields in a MISRA-C application is a bad idea. Bit-fields are very poorly specified by the standard, and therefore non-deterministic and unreliable. Also, MISRA-C 6.1 requires that you document how your compiler supports bit-fields with uint16_t, as that is not one of the standard integer types allowed for bit-fields.
But the real deal-breaker here is Directive 1.1, which requires that all implementation-defined behavior is documented and understood. For a MISRA-C implementation, I once actually tried to document all implementation-defined aspects of bit-fields. Soon I found myself writing a whole essay, because there are so many problems with them. See this for the top of the iceberg.
The work-around for not having to write such a "bit-field behavior book" is to unconditionally ban the use of bit-fields entirely in your coding standard. They are a 100% superfluous feature anyway. Use bit-wise operators instead.
Appendix D.4 of MISRA C:2012 is usefully titled "The essential types of bit fields"
For a bit-field which is implemented with an essentially Boolean type, it is essentially Boolean
For a bit-field which is implemented with a signed type, it is the Signed Type of Lowest Rank which is able to represent the bit field
For a bit-field which is implemented with a unsigned type, it is the Unsigned Type of Lowest Rank which is able to represent the bit field
The Unsigned Type of Lowest Rank of a single-bit unsigned integer would be uint8_t (aka unsigned char) - assuming that the tool does not interpret a single-bit as being boolean...
Beyond observing that this looks like a mis-diagnosis by PC-Lint, a workaround that avoids any possibility of doubt would to cast:
foo.bf.bar = (uint8_t)myValue
As an aside MISRA C:2012 Rule 6.1 gives guidance on the use of types other than signed/unsigned int for bit-fields...
Hi I have one requirement to be done like example first bit of register should be made 0(reset ) and MSB 4 bits need to be made 1 every time.
I can do this in two steps using :
int val=0x0f;
val |=0xf0;
val &=~(1<<0);
printf("val is %d\n",val);
I am expecting two lines of code to be framed in 1 logic.I am trying but looking for some good logic from experts.
I got one logic like val = (val |=0xf0) & ~(1<<0); but cant use this type of logic since some coding standards used are MISRA.
Please can any one give me some better logic.
Thanks in advance.
It is entirely unclear what you mean, so here's some MISRA code review:
int is not MISRA compliant. Use a fixed-width integer type, preferably from stdint.h.
0x0f literals like these are not MISRA compliant. All literals need to have an u or U suffix, even hexadecimal ones.
val |=0xf0; is not MISRA compliant. You aren't allowed to use bit-wise operators on signed types.
val &=~(1<<0) is not MISRA compliant. There is an important MISRA rule stating that the result of the ~ operator must always be cast to the intended type (called underlying type or effective type depending on MISRA version). Furthermore, MISRA does not allow you to use shift operators on signed types.
printf is not MISRA compliant. MISRA doesn't allow stdio.h in production code.
Otherwise, the code is readable and clear. Apart from the fact that int is not 8 bits wide, so your talk about setting the MSBs doesn't make any sense.
Naturally, if you try to merge this non-compliant readable code into an unreadable one-liner, it will remain non-compliant. You have to start by making the original code MISRA compliant. Might be wise to actually run it through your static analyser.
unsigned val = whatever();
val = (val | 0xF0u) & ~1u;
The nested assignment is unnecessary. I don't have a MISRA copy handy, but I think this should pass. The use of unsigned literals and types is because MISRA disallows bitwise operations on signed integers.
I have recently downloaded the MinGw compiler and for some reason it does not display and warning messages when I should be doing a explicit casts.
For example:
double x=9.97;
int y=x;
This seems to be ok with the compiler. I was just wondering do I need to install some other package
There is no cast required when assigning a double to an int. The decimal part is simply truncated. If you want warnings on this you can use -Wconversion or -Wfloat-conversion.
To complement the other answer, a technical explanation about implicit conversion semantics can be found on http://en.cppreference.com/w/c/language/conversion:
A finite value of any real floating type can be implicitly converted to any integer type. Except where covered by boolean conversion above, the rules are:
- The fractional part is discarded (truncated towards zero).
. If the resulting value can be represented by the target type, that value is used
. otherwise, the behavior is undefined
I am doing embedded ARM programming with gcc 4.9. I've been using the -Wconversion switch because it's in my company's default dev tool configuration. I'm using the stdint.h types (uint8_t, uint32_t, etc).
The compiler creates warnings every time I perform a compound assignment or even simple addition. For example:
uint8_t u8 = 0;
uint16_t u16;
// These cause warnings:
u8 += 2;
u8 = u16 >> 8;
The "common method" to fix this is to use casts, as discussed here and here:
u8 = (uint8_t)(u8 + 2);
u8 = (uint8_t)(u16 >> 8);
In addition to this being ugly, I keep running into convincing evidence that casting is generally bad practice.
My questions:
Why is it bad to use typecasts in this way?
Do I lose anything by simply omitting -Wconversion and letting the compiler do implicit conversions for me?
The compiler is showing you potential problem spots, that you should consider changing, but not by casts. The problem in the code that you have is that in C, arithmetic is never done on type that are narrower than int: the compiler always performs an implicit conversion to int for them.
So effectively your code is converting the narrow unsigned types to int, does the operation and then converts them back to the unsigned type.
Generally doing arithmetic with narrow types is not a good idea. Only use them if storage size is a problem (usually that would be larger structures such as arrays). For local, more or less temporary variables these narrow types make no sense.
It's a matter of taste. A cast is never required by C when you assign a value of an arithmetic type to an object of a different arithmetic type. Some people uses the cast to document there is a hidden conversion and that they did it on purpose.
Personally I don't like to put extra useless stuff and I assume the reader knows the basics of C so I tend to avoid the cast (and I don't use -Wconversion which is not a part of -Wall or even -Wextra anyway). Note that in an assignment if a literal on the right hand side cannot fit in the left hand side object gcc usually warns (without -Wall or -Wextra) even if the assignment is not UB.
The problem with casts is that they tell the compiler "shut up, I know what I'm doing", even if you don't. If someone decides that the variable must be able to handle values greater than 255 and changes the type to uint16_t, u8 += 2 would work, but u8 = (uint8_t)(u8 + 2) would be broken. Assuming that the variable isn't named u8 but "numberOfParagraphs", for example, that might be a bug that is very hard to find.
Better to use a bigger type in the first place. Unless you really, really want to store (u8 + 2) & 0xff, in which case you can write it that way and store it into a bigger variable without problems.
(Personally I would like an extension to the language like
(uint8_t)u8 += 2;
with the semantics that casting an lvalue to its own type has no effect and it remains an lvalue while removing the warning, but casting an lvalue to a different type would be an error. This would make it safe to shut up the compiler warning)
My CCS 6.1 ARM compiler (for LM3Sxxxx Stellaris) throws a warning:
"MISRA Rule 10.1. The value of an expression of integer type shall not be implicitly converted to a different underlying type if the expression is
not constant and is a function argument"
for following code:
static uint32_t diagBaseAddress;
void diagCbGetBaseAddress(uint32_t * addr);
...
diagCbGetBaseAddress(&diagBaseAddress); // line where warning is issued
...
I don't see a problem in my code. Is it false positive or my bug?
Yes, it is a false positive. Rule 10.1 of MISRA-C:2004 is concerned about implicit type promotions of integer types. Pointers have nothing to do with that rule.
Though make sure that none of the adjacent code lines contain the actual problem, the tool could simply be pointing at the wrong line.
(I can tell it is the 2004 version because it mentions underlying type. In the 2012 version, the concept of underlying type was replaced.)