1

Facing Issue related to MISRA C 2012 Rule 11.4 violation. Using PC-Lint Plus for rule check. Keil uVision V5.38.0.0

Error:

conversion between object pointer type 'GPIO_Type *' and integer type 'unsigned int' [MISRA 2012 Rule 11.4, advisory]```
uint *L_uc_byte = (uint *)&GPIO->PIN[0];

Following are the details related to GPIO

#define     __IO    volatile             /*!< Defines 'read / write' permissions */
/** Peripheral GPIO base address */
#define GPIO_BASE                                (0x4008C000u)
/** Peripheral GPIO base pointer */
#define GPIO                                     ((GPIO_Type *)GPIO_BASE)

/** GPIO - Register Layout Typedef */
typedef struct {
  __IO uint8_t B[6][32];                           /**< Byte pin registers for all port 0 and 1 GPIO pins, array offset: 0x0, array step: index*0x20, index2*0x1 */
       uint8_t RESERVED_0[3904];
  __IO uint32_t W[6][32];                          /**< Word pin registers for all port 0 and 1 GPIO pins, array offset: 0x1000, array step: index*0x80, index2*0x4 */
       uint8_t RESERVED_1[3328];
  __IO uint32_t DIR[6];                            /**< Direction registers, array offset: 0x2000, array step: 0x4 */
       uint8_t RESERVED_2[104];
  __IO uint32_t MASK[6];                           /**< Mask register, array offset: 0x2080, array step: 0x4 */
       uint8_t RESERVED_3[104];
  __IO uint32_t PIN[6];                            /**< Port pin register, array offset: 0x2100, array step: 0x4 */
       uint8_t RESERVED_4[104];
  __IO uint32_t MPIN[6];                           /**< Masked port register, array offset: 0x2180, array step: 0x4 */
       uint8_t RESERVED_5[104];
  __IO uint32_t SET[6];                            /**< Write: Set register for port Read: output bits for port, array offset: 0x2200, array step: 0x4 */
       uint8_t RESERVED_6[104];
  __O  uint32_t CLR[6];                            /**< Clear port, array offset: 0x2280, array step: 0x4 */
       uint8_t RESERVED_7[104];
  __O  uint32_t NOT[6];                            /**< Toggle port, array offset: 0x2300, array step: 0x4 */
       uint8_t RESERVED_8[104];
  __O  uint32_t DIRSET[6];                         /**< Set pin direction bits for port, array offset: 0x2380, array step: 0x4 */
       uint8_t RESERVED_9[104];
  __O  uint32_t DIRCLR[6];                         /**< Clear pin direction bits for port, array offset: 0x2400, array step: 0x4 */
       uint8_t RESERVED_10[104];
  __O  uint32_t DIRNOT[6];                         /**< Toggle pin direction bits for port, array offset: 0x2480, array step: 0x4 */
} GPIO_Type;

I have tried multiple things

  1. conversion between object pointer type 'GPIO_Type *' and integer type 'unsigned int' [MISRA 2012 Rule 11.4, advisory]
    uint data = ( ( GPIO->PIN[0] >> 17U ) & 0x03U );
    
  2. conversion between object pointer type 'uint *' (aka 'unsigned int *') and integer type 'volatile uint32_t' (aka 'volatile unsigned int') [MISRA 2012 Rule 11.4, advisory]
    volatile uint L_uc_byte = *( uint * )(GPIO->PIN[0]);
    
  3. conversion between object pointer type 'GPIO_Type *' and integer type 'unsigned int' [MISRA 2012 Rule 11.4, advisory]
    uint L_uc_byte = GPIO->PIN[0];
    
  4. Using address literal as a pointer
    conversion between object pointer type 'uint *' (aka 'unsigned int *') and integer type 'unsigned int' [MISRA 2012 Rule 11.4, advisory]
    uint L_uc_byte = *( uint * )( 0x40002000U + 0x2100U);
    

Need Solution to resolve this rule violation.

rock123A
  • 82
  • 2
  • 11
  • The thing that gives you the warning is the define #define GPIO. You have will have to disable the warning temporarly with pragmas when you use the define GPIO – Fredrik Jul 08 '23 at 07:39
  • In the 4th point GPIO is not used still error is there. Any suggestion on how to resolve that or any reference. – rock123A Jul 08 '23 at 07:42
  • GPIO 0 address is 0x40002000U and reading pin offset is 0x2100U. How can this be used for reading the same. – rock123A Jul 08 '23 at 07:43
  • In MISRA it's not allowed to cast integers to pointers, however when working in an embedded enviroment you have no choice but to do that to acces registers. So you will have to use a PC lint pragma to disable the warning for the lines that cast integers to pointers. – Fredrik Jul 08 '23 at 07:45
  • @rock123A If the integer is first made into a `void*`, any improvement? `#define GPIO_BASE (0x4008C000u)` --> `#define GPIO_BASE ((void *) 0x4008C000u)`? – chux - Reinstate Monica Jul 08 '23 at 07:47
  • @chux-ReinstateMonica ```#define GPIO_BASES ((void *)0x4008C000u) /** Peripheral GPIO base pointer */ #define GPIOS ((GPIO_Type *)GPIO_BASES) conversion from pointer to void to other pointer type ('GPIO_Type *') [MISRA 2012 Rule 11.5, advisory] uint L_uc_byte = GPIOS->PIN[0];``` Getting Error – rock123A Jul 08 '23 at 08:36
  • @rock123A Looks like an [_advisory rule_](https://stackoverflow.com/a/35705550/2410359) that can be ignored per instance. – chux - Reinstate Monica Jul 08 '23 at 08:43
  • @chux-ReinstateMonica If choose to ignore appropriate document need to be provided for the same. So need a solution to resolve. – rock123A Jul 08 '23 at 09:50
  • The rule tells you what is apparent: You want to cast an integer to a pointer. There is no solution without disabling the warning, if you keep that cast, independent of its specific form. You could get away with definition of `GPIO` that **is** a pointer. – the busybee Jul 08 '23 at 12:39
  • We had lots of these in a recent STM32 project, and we globally suppressed them. I think our justification was that it was in 3rd party "black box" code or something. Anyway, the client accepted it. – pmacfarlane Jul 08 '23 at 19:26
  • 2
    @pmacfarlane For actual safety-related software you can't really blame some 3rd party. Everything including libraries has to be MISRA compliant. Which sucks since it means a lot of reinventing the wheel. We just need to keep naming & shaming bad libraries and bad static analysers until they sort out their MISRA compliance. Although in this specific case it is actually the rule itself which can't be used in practice on embedded systems. – Lundin Jul 08 '23 at 19:31
  • @Lundin I know, it wasn't ideal. Apparently ST Micro actually do claim MISRA compliance for their HAL (with some deviations), but we never got them to send us any evidence. But you can read some of their claims [here](https://github.com/STMicroelectronics/STM32CubeL4/issues/5). – pmacfarlane Jul 08 '23 at 19:37
  • @pmacfarlane I know the library and it is far from MISRA compliant. And full of very strange practices overall. This kind of bloatware shouldn't be used off the shelf, use it as a time saver to look at while writing your own drivers. Silicon vendors have a _long_ tradition of providing some of the most horrible source code ever seen and ST are far from alone in this. – Lundin Jul 08 '23 at 19:40
  • Final note - when I say we "globally suppressed them", pc-lint still gives you _some_ granularity, e.g. you can do `-emacro(9078, GPIOA)` to suppress for just `GPIOA`, but still warn for other uses. (Of course we ended up suppressing for all the GPIOs we cared about, but still, better than a blanket suppression in all code.) – pmacfarlane Jul 08 '23 at 19:46

3 Answers3

5
  • You need to nuke that strange uint type from your codebase - don't invent strange non-standard, non-self-documenting integer types. Use standard C uint32_t.

  • Please note that it is best to avoid mixing volatile qualified expression such as reading/writing to registers with other operands or sub expressions (and also another MISRA violation). It's bad for performance purpose and clarity, and the extra side-effect can give unpredicted results. Try to make it so that register access expression only does a write or a read and nothing else.

  • Never perform pointer conversion casts unless you have in-depth knowledge of C. Casting an integer to a uint* which isn't volatile qualified is never correct. In particular it is almost certainly never the correct route to go in case you get warnings.

  • "Magic numbers" such as x >> 17 & 42 shouldn't be used since they make the code unreadable and forces the reader to sit with their nose constantly buried in the MCU manual's register descriptions. Instead use meaningful names. In case you are using a register map from a MCU vendor it typically comes with named constants which you can use.

Details here: How to access a hardware register from firmware?

Furthermore, MISRA-C have some advisory rules that are overly pedantic and not really applicable to embedded systems. One such rule forbids conversions between integers and pointers, but there is just no way to define a register map without performing such casts, so the rule has to be ignored.

The key to MISRA is understanding why they made the rule in the first place. The main issue in this case is alignment, something like uint32_t* data = (uint32_t*)0x0003; would cause a misaligned pointer. If you understand why that is a severe bug, then you understand why MISRA made the rule. Given that you aren't going to provide any home-made addresses like that, you are safe to ignore the rule.

The standard practice is that you need no formal deviation for skipping advisory rules. Though ideally your MISRA-based coding standard should list all advisory rules that are OK to ignore and then they should be disabled accordingly in the static analyser/MISRA checker.

Conclusions:

uint data = ( ( GPIO->PIN[0] >> 17U ) & 0x03U ); change to uint32_t data and name the constants something meaningful. Ideally and for MISRA compliance you should also split this up into several expressions, which is a bit overzealous but should be 100% MISRA compliant:

uint32_t data = GPIO->PIN[0]; // volatile expression as a stand-alone read/write

// just making up some names here, refer to the MCU manual for proper names:
data = (data >> GPIO_POS ) & GPIO_MASK; 

And if ignoring the integer to pointer advisory rule as we ought to:

#define SOME_REGISTER (*(volatile uint32_t*)(0x40002000U + 0x2100U))
...
uint32_t L_uc_byte = SOME_REGISTER;
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • You **can** produce a register map without casing an integer-address to a pointer... by means of configuring the linker address map - but that is typically harder work than using the hardware provider's #include file – Andrew Jul 09 '23 at 19:30
  • Was getting error when using the already defined types. ```parameter 1 of function 'hex_to_ascii(const uint8_t)' has type alias name type difference with previous declaration ('const uint8_t' (aka 'const unsigned char') vs 'const unsigned char') [MISRA 2012 Rule 8.3, required] uchar hex_to_ascii ( const uint8_t L_uc_data ) { use of modifier or type 'unsigned' outside of a typedef [MISRA 2012 Directive 4.6, advisory] uchar hex_to_ascii ( const unsigned char L_uc_data ) { typedef unsigned char uchar;``` So have to define these. – rock123A Jul 10 '23 at 04:23
  • @rock123A I can't really read that unformatted stuff and it seems unrelated to your question, so maybe post a different question about that, including the code? – Lundin Jul 11 '23 at 19:01
2

If you plan to use GPIO on several places, you could at least have the occurence of the MISRA violation to a single place.

Create a peripherl.h file like:

extern GPIO_Type * const GPIO;

Create a peripheral.c file like:

#include "peripheral.h"

#define GPIO_BASE (0x4008C000u) // define as needed
#define ADC_BASE  (0x4008D000u) // define as needed

// add your MISRA tool comment/pragma here
// this should be the only place of violating the rule
GPIO_Type * const GPIO = (GPIO_Type*)GPIO_BASE;
ADC_Type  * const ADC  = (ADC_Type*)ADC_BASE;

Now you can use the GPIO pointer in your code without this violation;

main.c

#include "peripheral.h"

int main(void) {
    // since the GPIO is defined in header as GPIO_Type*
    // and initialized in peripheral.c, there should be
    // no violation here
    uint32_t x = (GPIO->PIN[0] >> 17u) & 0x3u;
    
    return 0;
}
kesselhaus
  • 1,241
  • 8
  • 9
1

MISRA acknowledge that some Guidelines may cause problems for legitimate use cases... that is why we have created the Deviation process, as documented in MISRA Compliance.

Likewise for Advisory Guidelines (such as R.11.4), these can be disapplied.

R.11.4 even includes (within the Rationale) a statement to the effect that violating this Rule may be necessary when addressing memory mapped register.

So the correct approach (over and above Lundin's excellent suggestions) is not to ignore R.11.4, but to disapply it (with justification)

-- See profile for affiliation

Andrew
  • 2,046
  • 1
  • 24
  • 37