0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

为什么MISRA要求你不要使用位域-本文告诉你真相

嵌入式USB开发 来源:嵌入式Lee 作者:嵌入式Lee 2023-06-21 17:36 次阅读

本文转自公众号,欢迎关注

为什么MISRA要求你不要使用位域-本文告诉你真相 (qq.com)

一.前言

做过嵌入式开发的一般会看到一条编程规范:”不要使用位域”,一般都是知其然不其所以然,了解的多一点的可能知道位域是实现相关不具备可移植性,那么继续追问哪些行为是实现相关哪些行为导致移植性问题? 或者还有人知道,存储布局,对齐等行为是实现相关会导致不可移植性。如果再追问位域产生的汇编代码是什么样的,怎么进行读-修改-写操作的?知道这些内容的就更加少之又少了。 读写肯定不能读指定位数,只能字节,或者16位,32位这种,那么编译器到底读写用什么宽度? 这时基本大部分人都不知道了。

知其然知其所以然,尤其是嵌入式开发和硬件结合比较紧密,所以一定要了解细节,我们这一篇从一个问题引出然后去分析查找原因,只有遇到问题然后去分析解决它才会有更深刻的映像。

二.问题分析过程

问题是驱动程序中一个寄存器的某个位域修改,导致其他位域的值被修改了。

关键代码如下,

1.typedefunionnfc_ena_union{

2. uint32_tw;

3. struct{

4. /*spienable,oncethespitransiscompleted,thisbitwillbeclearedbyHWautomaticlly*/

5. uint32_tnfc_ena:1; //[0]

6. uint32_treserved_0:3; //[1,3]

7. /*swrequesttousedp*/

8. uint32_tnfc_dp_req:1; //[4]

9. uint32_treserved_1:3; //[5,7]

10. /*duetodelayinreceivingdata,nfcdelayonebeattorx*/

11. uint32_tnfc_rx_delay_en:1; //[8]

12. uint32_treserved_2:7; //[9,15]

13. /*spitransdatalength,unitisbyte,oncethespitransiscompleted,thisbitwillbeclearedbyHWautomaticlly*/

14. uint32_tnfc_data_len:16; //[16,31]

15.}_b;

16.}nfc_ena_u;

1./**

2.*fnintnfc_set_datalen(uint8_tid,uint16_tlen)

3.*param[in]idportid

4.*param[in]lendatalen

5.*retval0ok

6.*retval<0 param err

7.*

8.*/

9.NFC_INLINEintnfc_set_datalen(uint8_tid,uint16_tlen)

10.{

11. if(id>=HW_NFC_PORT_MAX)

12.{

13. return-1;

14.}

15.nfc_base[id]->nfc_ena._b.nfc_data_len=len;

16. return0;

17.}

执行之前该寄存器值为0x00020100

34b4c4c2-0f04-11ee-9c1d-dac502259ad0.png

nfc_base[id]->nfc_ena._b.nfc_data_len= len

汇编代码被优化为了写高16位

34d22efe-0f04-11ee-9c1d-dac502259ad0.png

执行完后寄存器低16位变为了0

34f0fab4-0f04-11ee-9c1d-dac502259ad0.png

这是因为寄存器硬件上只支持32位的写操作,所以写高16位导致低16位清零了,这是硬件决定的。

二.验证

一般想到的就是优化相关,加volatile等,我们分别验证下。

3.1不使能编译器优化

编译器优化选项改为”-O0”

代码不变

依然会按照16位访问,导致低16位被清掉。

所以可以看到这个和编译器行为有关,编译器显然不是根据优化等级决定位域的操作宽度,这里而是根据位域的宽度刚好是16位对齐,所以优化为了16位操作指令。

3504cddc-0f04-11ee-9c1d-dac502259ad0.png351f3d70-0f04-11ee-9c1d-dac502259ad0.png

3.2使用volatile避免编译器优化

#ifndef__IOM

#define__IOM volatile

#endif

所有uint32_t替换为__IOM uint32_t

还是一样的

353ad710-0f04-11ee-9c1d-dac502259ad0.png

显然汇编代码的访问宽度也不受volatile影响。

3.3为什么指定了uint32_t和volatile还会优化。

问题来了为什么告诉了编译器是uint32_t和volatile,为什么其还要一意孤行,要优化为16位访问指令呢,答案就是因为是标准没有规定,这是编译器实现行为决定的,所以编译器设计者决定的(当然也会有一些现实考虑的),可能不同编译器行为不同,这里以GCC为例。

GCC编译器文档中可以找到答案

GCC的文档可以看到如下内容,也给出了最好是不使用位域的原因

354f86f6-0f04-11ee-9c1d-dac502259ad0.png

35645612-0f04-11ee-9c1d-dac502259ad0.png

另外也介绍了位域哪些行为也是编译器实现相关的,所以嵌入式可移植性考虑不要使用位域

3576ae5c-0f04-11ee-9c1d-dac502259ad0.png

那么有没有办法指定编译按照一定大小访问呢,GCC有编译选项可以控制见下一节。

3.4使用编译器选项-fstrict-volatile-bitfields

358c8eca-0f04-11ee-9c1d-dac502259ad0.png

可以看到改为了sw指令,按照32位进行了操作

359dc4f6-0f04-11ee-9c1d-dac502259ad0.png

四.一些厂家做法

如下可见

4.1CMSIS

core_cmxx.h中定义

CMSIS中进行了定义,寄存器个别使用位域

1./*IOdefinitions(accessrestrictionstoperipheralregisters)*/
2./**
3.defgroupCMSIS_glob_defsCMSISGlobalDefines
4.
5.<  strong  >IOTypeQualifiers<  /strong  >areused
6.litospecifytheaccesstoperipheralvariables.
7.liforautomaticgenerationofperipheralregisterdebuginformation.
8.*/
9.#ifdef__cplusplus
10.#define__Ivolatile/*!<   Defines 'read only' permissions */
11.#else
12.  #define   __I     volatile const       /*!<   Defines 'read only' permissions */
13.#endif
14.#define     __O     volatile             /*!<   Defines 'write only' permissions */
15.#define     __IO    volatile             /*!<   Defines 'read / write' permissions */
16.
17./* following defines should be used for structure members */
18.#define     __IM     volatile const      /*! Defines 'read only' structure member permissions */
19.#define     __OM     volatile            /*! Defines 'write only' structure member permissions */
20.#define     __IOM    volatile            /*! Defines 'read / write' structure member permissions */

1.
/**
2.  brief  Structure type to access the Instrumentation Trace Macrocell Register (ITM).
3. */
4.typedef struct
5.{
6.  __OM  union
7.  {
8.    __OM  uint8_t    u8;                 /*!<   Offset: 0x000 ( /W)  ITM Stimulus Port 8-bit */
9.    __OM  uint16_t   u16;                /*!<   Offset: 0x000 ( /W)  ITM Stimulus Port 16-bit */
10.    __OM  uint32_t   u32;                /*!<   Offset: 0x000 ( /W)  ITM Stimulus Port 32-bit */
11.  }  PORT [32U];                         /*!<   Offset: 0x000 ( /W)  ITM Stimulus Port Registers */
12.        uint32_t RESERVED0[864U];
13.  __IOM uint32_t TER;                    /*!<   Offset: 0xE00 (R/W)  ITM Trace Enable Register */
14.        uint32_t RESERVED1[15U];
15.  __IOM uint32_t TPR;                    /*!<   Offset: 0xE40 (R/W)  ITM Trace Privilege Register */
16.        uint32_t RESERVED2[15U];
17.  __IOM uint32_t TCR;                    /*!<   Offset: 0xE80 (R/W)  ITM Trace Control Register */
18.        uint32_t RESERVED3[29U];
19.  __OM  uint32_t IWR;                    /*!<   Offset: 0xEF8 ( /W)  ITM Integration Write Register */
20.  __IM  uint32_t IRR;                    /*!<   Offset: 0xEFC (R/ )  ITM Integration Read Register */
21.  __IOM uint32_t IMCR;                   /*!<   Offset: 0xF00 (R/W)  ITM Integration Mode Control Register */
22.        uint32_t RESERVED4[43U];
23.  __OM  uint32_t LAR;                    /*!<   Offset: 0xFB0 ( /W)  ITM Lock Access Register */
24.  __IM  uint32_t LSR;                    /*!<   Offset: 0xFB4 (R/ )  ITM Lock Status Register */
25.        uint32_t RESERVED5[6U];
26.  __IM  uint32_t PID4;                   /*!<   Offset: 0xFD0 (R/ )  ITM Peripheral Identification Register #4 */
27.  __IM  uint32_t PID5;                   /*!<   Offset: 0xFD4 (R/ )  ITM Peripheral Identification Register #5 */
28.  __IM  uint32_t PID6;                   /*!<   Offset: 0xFD8 (R/ )  ITM Peripheral Identification Register #6 */
29.  __IM  uint32_t PID7;                   /*!<   Offset: 0xFDC (R/ )  ITM Peripheral Identification Register #7 */
30.  __IM  uint32_t PID0;                   /*!<   Offset: 0xFE0 (R/ )  ITM Peripheral Identification Register #0 */
31.  __IM  uint32_t PID1;                   /*!<   Offset: 0xFE4 (R/ )  ITM Peripheral Identification Register #1 */
32.  __IM  uint32_t PID2;                   /*!<   Offset: 0xFE8 (R/ )  ITM Peripheral Identification Register #2 */
33.  __IM  uint32_t PID3;                   /*!<   Offset: 0xFEC (R/ )  ITM Peripheral Identification Register #3 */
34.  __IM  uint32_t CID0;                   /*!<   Offset: 0xFF0 (R/ )  ITM Component  Identification Register #0 */
35.  __IM  uint32_t CID1;                   /*!<   Offset: 0xFF4 (R/ )  ITM Component  Identification Register #1 */
36.  __IM  uint32_t CID2;                   /*!<   Offset: 0xFF8 (R/ )  ITM Component  Identification Register #2 */
37.  __IM  uint32_t CID3;                   /*!<   Offset: 0xFFC (R/ )  ITM Component  Identification Register #3 */
38.} ITM_Type;

1./**
2.  brief  Union type to access the Application Program Status Register (APSR).
3. */
4.typedef union
5.{
6.  struct
7.  {
8.    uint32_t _reserved0:27;              /*!<   bit:  0..26  Reserved */
9.    uint32_t Q:1;                        /*!<   bit:     27  Saturation condition flag */
10.    uint32_t V:1;                        /*!<   bit:     28  Overflow condition code flag */
11.    uint32_t C:1;                        /*!<   bit:     29  Carry condition code flag */
12.    uint32_t Z:1;                        /*!<   bit:     30  Zero condition code flag */
13.    uint32_t N:1;                        /*!<   bit:     31  Negative condition code flag */
14.  } b;                                   /*!<   Structure used for bit  access */
15.  uint32_t w;                            /*!<   Type      used for word access */
16.} APSR_Type;

4.2ST

1./**
2.*@briefUniversalSerialBusFullSpeedDevice
3.*/
4.
5.typedefstruct
6.{
7.__IOuint16_tEP0R;/*!<   USB Endpoint 0 register,                   Address offset: 0x00 */ 
8.  __IO uint16_t RESERVED0;            /*!<   Reserved */     
9.  __IO uint16_t EP1R;                 /*!<   USB Endpoint 1 register,                   Address offset: 0x04 */
10.  __IO uint16_t RESERVED1;            /*!<   Reserved */       
11.  __IO uint16_t EP2R;                 /*!<   USB Endpoint 2 register,                   Address offset: 0x08 */
12.  __IO uint16_t RESERVED2;            /*!<   Reserved */       
13.  __IO uint16_t EP3R;                 /*!<   USB Endpoint 3 register,                   Address offset: 0x0C */ 
14.  __IO uint16_t RESERVED3;            /*!<   Reserved */       
15.  __IO uint16_t EP4R;                 /*!<   USB Endpoint 4 register,                   Address offset: 0x10 */
16.  __IO uint16_t RESERVED4;            /*!<   Reserved */       
17.  __IO uint16_t EP5R;                 /*!<   USB Endpoint 5 register,                   Address offset: 0x14 */
18.  __IO uint16_t RESERVED5;            /*!<   Reserved */       
19.  __IO uint16_t EP6R;                 /*!<   USB Endpoint 6 register,                   Address offset: 0x18 */
20.  __IO uint16_t RESERVED6;            /*!<   Reserved */       
21.  __IO uint16_t EP7R;                 /*!<   USB Endpoint 7 register,                   Address offset: 0x1C */
22.  __IO uint16_t RESERVED7[17];        /*!<   Reserved */     
23.  __IO uint16_t CNTR;                 /*!<   Control register,                          Address offset: 0x40 */
24.  __IO uint16_t RESERVED8;            /*!<   Reserved */       
25.  __IO uint16_t ISTR;                 /*!<   Interrupt status register,                 Address offset: 0x44 */
26.  __IO uint16_t RESERVED9;            /*!<   Reserved */       
27.  __IO uint16_t FNR;                  /*!<   Frame number register,                     Address offset: 0x48 */
28.  __IO uint16_t RESERVEDA;            /*!<   Reserved */       
29.  __IO uint16_t DADDR;                /*!<   Device address register,                   Address offset: 0x4C */
30.  __IO uint16_t RESERVEDB;            /*!<   Reserved */       
31.  __IO uint16_t BTABLE;               /*!<   Buffer Table address register,             Address offset: 0x50 */
32.  __IO uint16_t RESERVEDC;            /*!<   Reserved */       
33.} USB_TypeDef;


1./** @defgroup USBD_SCSI_Exported_TypesDefinitions
2.  * @{
3.  */
4.
5.typedef struct _SENSE_ITEM
6.{
7.  char Skey;
8.  union
9.  {
10.    struct _ASCs
11.    {
12.      char ASC;
13.      char ASCQ;
14.    } b;
15.    uint8_t ASC;
16.    char *pData;
17.  } w;
18.} USBD_SCSI_SenseTypeDef;

4.3瑞萨

__I,__O__ROM也是core_cmxx.h中定义,大量使用位域

1.#ifndef__IM/*!<   Fallback for older CMSIS versions                                         */
2.  #define __IM     __I
3. #endif
4. #ifndef __OM                              /*!<   Fallback for older CMSIS versions                                         */
5.  #define __OM     __O
6. #endif
7. #ifndef __IOM                             /*!<   Fallback for older CMSIS versions                                         */
8.  #define __IOM    __IO
9. #endif

1./**
2. * @brief R_BUS_CSa [CSa] (CS Registers)
3. */
4.typedef struct
5.{
6.    __IM uint16_t RESERVED;
7.
8.    union
9.    {
10.        __IOM uint16_t MOD;            /*!<   (@ 0x00000002) Mode Register                                              */
11.
12.        struct
13.        {
14.            __IOM uint16_t WRMOD : 1;  /*!<   [0..0] Write Access Mode Select                                           */
15.            uint16_t             : 2;
16.            __IOM uint16_t EWENB : 1;  /*!<   [3..3] External Wait Enable                                               */
17.            uint16_t             : 4;
18.            __IOM uint16_t PRENB : 1;  /*!<   [8..8] Page Read Access Enable                                            */
19.            __IOM uint16_t PWENB : 1;  /*!<   [9..9] Page Write Access Enable                                           */
20.            uint16_t             : 5;
21.            __IOM uint16_t PRMOD : 1;  /*!<   [15..15] Page Read Access Mode Select                                     */
22.        } MOD_b;
23.    };
24.
25.    union
26.    {
27.        __IOM uint32_t WCR1;             /*!<   (@ 0x00000004) Wait Control Register 1                                    */
28.
29.        struct
30.        {
31.            __IOM uint32_t CSPWWAIT : 3; /*!<   [2..0] Page Write Cycle Wait SelectNOTE: The CSPWWAIT value
32.                                          *   is valid only when the PWENB bit in CSnMOD is set to 1.                   */
33.            uint32_t                : 5;
34.            __IOM uint32_t CSPRWAIT : 3; /*!<   [10..8] Page Read Cycle Wait SelectNOTE: The CSPRWAIT value
35.                                          *   is valid only when the PRENB bit in CSnMOD is set to 1.                   */
36.            uint32_t               : 5;
37.            __IOM uint32_t CSWWAIT : 5;  /*!<   [20..16] Normal Write Cycle Wait Select                                   */
38.            uint32_t               : 3;
39.            __IOM uint32_t CSRWAIT : 5;  /*!<   [28..24] Normal Read Cycle Wait Select                                    */
40.            uint32_t               : 3;
41.        } WCR1_b;
42.    };
43.
44.    union
45.    {
46.        __IOM uint32_t WCR2;           /*!<   (@ 0x00000008) Wait Control Register 2                                    */
47.
48.        struct
49.        {
50.            __IOM uint32_t CSROFF : 3; /*!<   [2..0] Read-Access CS Extension Cycle Select                              */
51.            uint32_t              : 1;
52.            __IOM uint32_t CSWOFF : 3; /*!<   [6..4] Write-Access CS Extension Cycle Select                             */
53.            uint32_t              : 1;
54.            __IOM uint32_t WDOFF  : 3; /*!<   [10..8] Write Data Output Extension Cycle Select                          */
55.            uint32_t              : 1;
56.            __IOM uint32_t AWAIT  : 2; /*!<   [13..12] CS Assert Wait Select                                            */
57.            uint32_t              : 2;
58.            __IOM uint32_t RDON   : 3; /*!<   [18..16] RD Assert Wait Select                                            */
59.            uint32_t              : 1;
60.            __IOM uint32_t WRON   : 3; /*!<   [22..20] WR Assert Wait Select                                            */
61.            uint32_t              : 1;
62.            __IOM uint32_t WDON   : 3; /*!<   [26..24] Write Data Output Wait Select                                    */
63.            uint32_t              : 1;
64.            __IOM uint32_t CSON   : 3; /*!<   [30..28] CS Assert Wait Select                                            */
65.            uint32_t              : 1;
66.        } WCR2_b;
67.    };
68.    __IM uint32_t RESERVED1;
69.} R_BUS_CSa_Type;                      /*!<   Size = 16 (0x10)                                                          */

五.总结

结论就是正如很多嵌入式编程规范所描述的(比如MISRA),一般不建议使用位域,因为涉及到位域的访问,存储等行为都是实现定义的,不具备可移植性。

嵌入式领域寄存器的定义也最好不要使用位域,到寄存器级别以寄存器操作为单位即可,每个寄存器都要使用__IM,__OM,__IOM描述。

如果一定要使用位域可以使用-fstrict-volatile-bitfields选项,使用GCC测试可以保证按照固定指定大小访问,但是不保证其他编译器也支持该选项,最好能不使用就不使用位域。


审核编辑黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式
    +关注

    关注

    4983

    文章

    18286

    浏览量

    288494
  • 寄存器
    +关注

    关注

    30

    文章

    5032

    浏览量

    117745
  • 编译器
    +关注

    关注

    1

    文章

    1577

    浏览量

    48621
  • MISRA
    +关注

    关注

    0

    文章

    18

    浏览量

    6887
  • 嵌入式编程
    +关注

    关注

    0

    文章

    26

    浏览量

    10247
收藏 人收藏

    评论

    相关推荐

    C结构体之

    数据结构,称为“”或“段”。所谓“”是把一个字节中的二进位划分为几个不同的区域, 并说明每个区域的位数。每个
    发表于 12-10 20:35

    MISRA C 2012 学习资料

    `MISRA C 学习资料 `
    发表于 09-17 14:55

    电子硬件设计牛人张飞告诉“11个不要

    我们不知道抄来的电路优点在哪,缺点是什么。不要随便的去抄别人的电路,一定要根据自己的知识来设计这样一个电路。即使看到别人电路时,也要能够分析和理解别人的电路,把电路中的问题找出来,然后变成
    发表于 07-22 21:47

    V399丝印,搞不懂是什么IC 求助,USB视频采集卡上面的,有图片,忘知道的告诉我,求你们了

    V399丝印,搞不懂是什么IC 求助,USB视频采集卡上面的,有图片,忘知道的告诉我,求你们了
    发表于 09-08 17:29

    使用问题

    在支持操作的单片机中,如C51,使用定义变量或者寄存器,操作方便并且节约空间。 问题1:但是很多单片机不支持操作,仍然使用
    发表于 09-16 22:25

    在linux驱动中的妙用

    c语言里规定的的方式进行比特操作,可以对某一进行操作;一般情况下,比如普通的存储数据的区域,
    发表于 08-03 09:54

    Linux桌面环境的优缺点

    打算把每个桌面都试用一遍,但是那很费时间,而且确实有很多桌面环境可供选择,这就是我发表“最优秀的 Linux 桌面以及他们的优缺点”的目的,本文告诉在选择桌面时需要注意些什么,让我们开始吧。
    发表于 07-19 06:30

    MISRA C编程规范标准有什么规则要求

    如何衡量代码是否满足某些标准?MISRA C编程规范标准有什么规则要求
    发表于 04-19 07:20

    是怎样去定义的

    一、描述存放一个开关量或者其他开关状态时,用一二进位即可。为了减少嵌入式内存资源的开销,可以使用进行定义,
    发表于 12-15 07:47

    “代入公差,根据生产实际值,确定最终设计值”具体怎么做?本文告诉

    的,一般可以忽略。但是,并不是说,可以完全不用考虑,当对产品的精密度要求较高时,元器件本身的公差也是产品公差的一部分,也是会对最终产品造成影响的。2、PCB的工艺制程公差。这个,就是朋友们需要特别
    发表于 10-14 17:39

    手机电池剩一格时不要使

    手机电池剩一格时不要使用   一般我们都会有一种想法就是手机的电池电力要全部放完再充电比较好基本上是没错的,因为我们在以前使用的充电电
    发表于 11-10 15:00 1357次阅读

    笔记本最好不要使用屏保

    笔记本最好不要使用屏保  CRT(阴极射线显像管)显示器的显像原理主要是由灯丝加热阴极,阴极发射电子,然后在加速极电场的作用
    发表于 01-18 09:51 672次阅读

    软件编程规范(MISRA-C)

    MISRA-C是一种汽车制造业嵌入式C编码标准。FreeRTOS符合MISRA-C编程规范,就连大名鼎鼎的uCOS也宣称基本符合MISRA-C编程规范。
    发表于 11-02 18:29 25次下载

    文告诉你什么是电机短时运行?

    本文小编告诉大家什么是电机短时运行。
    的头像 发表于 12-14 22:12 1758次阅读

    MISRA合规性的五个实用技巧

    回过头来对违规行为进行详细分析,很容易看到关于整个过程的更大问题。MISRA 的文档“MISRA 合规性:2016”比语言子集本身受到的新闻报道要少得多,但它对于了解您选择的静态分析工具突出显示的信息如何与 MISRA 合规应用
    的头像 发表于 07-22 08:06 1245次阅读
    <b class='flag-5'>MISRA</b>合规性的五个实用技巧