-1

I've moving over from 8 bit bare-metal registers, and having to learn some new C Kungfu to wrap my head around the CMSIS Core approach.

I have a snippet of code here from a Peripheral Access Layer from a ARM Cortex M vendor. They create this SN_WDT_TYPE structure, which you can use to set watch-dog timer registers using their notation.

Why do they use the unions? I haven't seen this kind of syntax before.

If you use unions to create structures like that, do you they go several layers deep with pointers? Memory management with the unions? Is there some C syntax thing I'm missing here?

This might be CMSIS specific, does anyone know what the ": 1" is doing in those struct declarations...? I know the __IO ties back to some CMSIS definition of read/write.

HELPFUL STUFF I FOUND AFTER COMMENTS & ANSWERS:

ARM'S CMSCIS PERIPHERAL NAMING CONVENTION -- This example code doesn't seem to confirm too gracefully...

ARM'S BITFIELD COMPILER NOTES ON STRUCTS & UNIONS

/**
  * @brief Watchdog Timer (SN_WDT)
  */

typedef struct {                                    /*!< SN_WDT Structure                                                      */
  
  union {
    __IO uint32_t  CFG;                             /*!< Offset:0x00 WDT Configuration Register                                */
    
    struct {
      __IO uint32_t  WDTEN      :  1;               /*!< WDT enable                                                            */
      __IO uint32_t  WDTIE      :  1;               /*!< WDT interrupt enable                                                  */
      __IO uint32_t  WDTINT     :  1;               /*!< WDT interrupt flag                                                    */
           uint32_t             : 13;
      __O  uint32_t  WDKEY      : 16;               /*!< Watchdog register key                                                 */
    } CFG_b;                                        /*!< BitSize                                                               */
  };
  
  union {
    __IO uint32_t  CLKSOURCE;                       /*!< Offset:0x04 WDT Clock Source Register                                 */
    
    struct {
      __IO uint32_t  CLKSOURCE  :  2;               /*!< WDT clock source                                                      */
           uint32_t             : 14;
      __O  uint32_t  WDKEY      : 16;               /*!< Watchdog register key                                                 */
    } CLKSOURCE_b;                                  /*!< BitSize                                                               */
  };
  
  union {
    __IO uint32_t  TC;                              /*!< Offset:0x08 WDT Timer Constant Register                               */
    
    struct {
      __IO uint32_t  TC         :  8;               /*!< Watchdog timer constant reload value                                  */
           uint32_t             :  8;
      __O  uint32_t  WDKEY      : 16;               /*!< Watchdog register key                                                 */
    } TC_b;                                         /*!< BitSize                                                               */
  };
  
  union {
    __O  uint32_t  FEED;                            /*!< Offset:0x0C WDT Feed Register                                         */
    
    struct {
      __O  uint32_t  FV         : 16;               /*!< Watchdog feed value                                                   */
      __O  uint32_t  WDKEY      : 16;               /*!< Watchdog register key                                                 */
    } FEED_b;                                       /*!< BitSize                                                               */
  };
} SN_WDT_Type;
Leroy105
  • 139
  • 5
  • it is a implementation defined feature (meaning dont use it this way) that currently happens to work with gcc and other popular compilers. it is a lazy way to try to bitfield (something implementation defined that you should never ever use this way) the register as well as have a whole register depending on the access. should never use structs across compile domains, and unions are not defined to work this way. so two problems here. but for now it is a fad, until it breaks. – old_timer Jan 15 '18 at 21:47
  • its a ghee whiz way to get full register access based on one part of the union and individual parts of the register based on the other path through the union. blah.FEED for the whole register or blah.FEED_b.WDKEY for partial access to the resource. – old_timer Jan 15 '18 at 21:48
  • :1 is bitfields part of the c language just like unions and structs in general. the one part of the C language you should rarely if ever have a reason to use, never use it across compile domains, that is just a ticking time bomb (for most folks it takes 10-20 years for it to go off). – old_timer Jan 15 '18 at 21:49
  • @old_timer, I hear you. I just don't see unions being used like that in classic C examples. I got far enough ahead to see that the ": 1" is a bitfield delimiter (not sure the exact nomenclature) that specificies you can use one bit even though it is a 32bit declarartion. EEGADS. Glad I'm not totally stupid that this is a syntax oddity. – Leroy105 Jan 15 '18 at 21:51
  • @old_timer -- I'm coming at CMSIS for the first time from an 8051. I feel like the whole thing is pretty "gee whiz"! I get the standardization for building more complex systems, but man you get a lot of ugliness in the code with chunks like this to digest for the first time around the rodeo... – Leroy105 Jan 15 '18 at 22:10
  • Cross-posted on [EE](https://electronics.stackexchange.com/questions/350196/cmsis-peripheral-definitions-structs-with-unions-syntax). – Lundin Jan 16 '18 at 09:56

1 Answers1

3

The union allows you to access the hardware register as either a 32 bit word or as the bit fields contained in the register. Both representations have their uses. Perhaps you are missing the bit field syntax as it is not used in most application level coding. Bit field layout is compiler specific, but CMSIS headers are built to work with the intended compilers.

andy mango
  • 1,526
  • 1
  • 8
  • 13
  • Could I bother you for few examples? Is SN_WDT.CFG valid? Is CLKSOURCE valid? Or is SN_WDT.CLKSOURCE valid, or should it be something else? – Leroy105 Jan 15 '18 at 21:55
  • Typically, CMSIS headers define a pointer to a peripheral by casting a physical address, i.e. – andy mango Jan 15 '18 at 23:14
  • Typically, CMSIS headers define a pointer to a peripheral by casting a physical address, i.e. "#define SN_WDT (SN_WDT_Type *)(0x40000000)" (where the 0x40000000 number is just an example here). Then SN_WDT->CFG is the value of the whole register and SN_WDT->CFG_b.WDTEN is the value of the WDTEN bit within the CFG register. Note the use of anonymous unions to shorten the path to the desired value. – andy mango Jan 15 '18 at 23:21
  • Right on. That anonymous union is the weird part... I totally get it. – Leroy105 Jan 16 '18 at 01:04