CMSIS - Definiciones periféricas - ¿Estructuras con sintaxis de uniones? [cerrado]

0

He pasado de los registros de 8 bits de metal desde cero y he tenido que aprender un poco de Kungfu C para envolver mi cabeza en torno al enfoque de CMSIS Core.

Aquí tengo un fragmento de código de una capa de acceso periférico de un proveedor de ARM Cortex M. Crean esta estructura SN_WDT_TYPE, que puedes usar para configurar los registros del temporizador de vigilancia usando su notación.

¿Por qué usan los sindicatos? No he visto este tipo de sintaxis antes.

Si usas uniones para crear estructuras como esa, ¿van a varias capas de profundidad con punteros? ¿Gestión de la memoria con los sindicatos? ¿Hay algo de sintaxis de C que me falta?

Esto podría ser específico de CMSIS, ¿alguien sabe qué hace el ": 1" en esas declaraciones de estructura ...? Sé que el __IO se relaciona con alguna definición de lectura / escritura de CMSIS.

CONVENCIÓN DE NOMBRAMIENTO PERIFÉRICO DE ARM'S CMSCIS : este código de ejemplo no parece confirmar con demasiada gracia ...

/* ================================================================================ */
/* ================                     SN_WDT                     ================ */
/* ================================================================================ */


/**
  * @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;
    
pregunta Leroy105

2 respuestas

2

Utilizan uniones para que pueda acceder al registro a través de un acceso de 32 bits o de bits individuales. Desafortunadamente, esta es una forma muy común de declarar mapas de registro. Desafortunado porque se basa en toneladas de comportamiento mal especificado.

Para empezar, las uniones y estructuras pueden contener relleno en cualquier lugar, lo que las hace inadecuadas para la asignación de memoria directa. Cuando utilice uniones / estructuras para tales propósitos, siempre debe protegerse contra el relleno accidental. Los diferentes sistemas tienen diferentes requisitos de alineación y, por lo tanto, un relleno diferente.

Luego están los campos de bits, que en sí mismos es un mecanismo de lenguaje con poco o ningún soporte por parte del estándar C. Siempre que utilice campos de bits, confíe en un comportamiento mal definido o en extensiones no estándar.

Todo esto es muy malo. Nunca debes escribir código como el publicado. Preferiblemente, en su lugar se deben usar macros de máscara de bits y operadores de bit-bit, ya que son 100% estándar, determinísticos y portátiles.

    
respondido por el Lundin
2

esto es lo que están tratando de hacer.

typedef union
{
    unsigned int reg;

    struct {
      unsigned int a:2;
      unsigned int b:14;
      unsigned int c:16;
    } bits;
} MYUN;

volatile MYUN *myun;


unsigned int fun1 ( unsigned int x, unsigned int y )
{
    myun=(volatile MYUN *)0x1000;
    myun->bits.b = x;
    myun->bits.c = y;
    return(myun->reg);
}

00000000 <fun1>:
   0:   e3a02a01    mov r2, #4096   ; 0x1000
   4:   e59f3034    ldr r3, [pc, #52]   ; 40 <fun1+0x40>
   8:   e5832000    str r2, [r3]
   c:   e59fc030    ldr r12, [pc, #48]  ; 44 <fun1+0x44>
  10:   e5923000    ldr r3, [r2]
  14:   e000000c    and r0, r0, r12
  18:   e1c3310c    bic r3, r3, r12, lsl #2
  1c:   e1833100    orr r3, r3, r0, lsl #2
  20:   e5823000    str r3, [r2]
  24:   e5923000    ldr r3, [r2]
  28:   e1a03803    lsl r3, r3, #16
  2c:   e1a03823    lsr r3, r3, #16
  30:   e1833801    orr r3, r3, r1, lsl #16
  34:   e5823000    str r3, [r2]
  38:   e5920000    ldr r0, [r2]
  3c:   e12fff1e    bx  lr
  40:   00000000    
  44:   00003fff    

la dirección 0x40 será completada por el enlazador para mantener la dirección donde vive myun. Puede ver que r2 a lo largo es la dirección que especificamos para lo que apunta (apuntando una unión / estructura a través de un dominio de compilación en hardware, muy mala idea en general con o sin campos de bits).

Los estándares C / C ++ actuales intentan asegurar que los campos de bits sean lineales, no colocarán los bits c entre los bits a y b. Pero lo que se define como implementación, es decir, quien escribe el compilador o el backend puede hacer lo que quiera, es si los bits a son los msbits o los lsbits, así como los bits c que la mitad de esa palabra de 32 bits (asumiendo el tamaño de int aquí para simplificar El ejemplo, fácilmente confirmado). En teoría, los sindicatos comparten la memoria entre los elementos, pero dejan bastante claro que eso no significa que estén alineados. existe cierto enunciado, pero está claro que un int sin signo de dos bits y un int sin signo de tamaño completo no son del mismo tipo, por lo que no tienen que alinearse, dejan claro que todo lo que suceda después no tiene que alinearse. padding in structs está definido por la implementación, por lo que es una mala idea utilizarlos en dominios de compilación.

Vemos que para escribir los bits b, estos bits son cero [15: 2] 14 bits como se define, luego se aplican los x bits allí, luego se guardan, ya que esto es volátil.

Luego lea de nuevo 0x1000, y enmascare y aplique los bits y al [31:16] del destino.

por último, devolviendo lo que se leyó a 0x1000 como un valor total de 32 bits.

Los campos de bits son una forma perezosa de evitar el enmascaramiento y el desplazamiento, pero no son confiables, donde el enmascaramiento y el desplazamiento no fallarán a menos que el compilador esté totalmente dañado.

El truco de unión es otra forma de pereza para poder ver o usar todo el valor como una variable (32 bits) en lugar de las partes.

Se trata de trucos de lenguaje C de ghee whiz para ahorrar en la escritura tal vez, con un gran riesgo. Un hábito como este si sigues haciéndolo fallará. Ahora, algunas personas pueden optar por hacer esto como una forma de seguridad en el trabajo, código que falla periódicamente y que necesita mantenimiento en lugar de escribirlo una vez y listo. Por lo general, solo funciona si la gerencia y los compañeros de trabajo no saben lo que está haciendo.

Es un trabajo en mi máquina, tu máquina, tu compilador en un día en particular contra un objetivo en particular. El mismo compilador o familia contra un objetivo diferente puede cambiar las definiciones del campo de bits, y señalar uniones o estructuras contra la memoria como un hábito fallará eventualmente debido al relleno o la alineación.

El enmascaramiento y el desplazamiento no están definidos en la implementación y no varían de compilador a compilador o de destino a destino (para compiladores sin buggy).

Al final del día, ambos enfoques generan enmascaramiento y desplazamiento, por lo que elegir el que sea confiable sobre el que no lo es sería prudente.

    
respondido por el old_timer

Lea otras preguntas en las etiquetas