為什么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
nfc_base[id]->nfc_ena._b.nfc_data_len= len
匯編代碼被優化為了寫高16位
執行完后寄存器低16位變為了0
這是因為寄存器硬件上只支持32位的寫操作,所以寫高16位導致低16位清零了,這是硬件決定的。
二.驗證
一般想到的就是優化相關,加volatile等,我們分別驗證下。
3.1不使能編譯器優化
編譯器優化選項改為”-O0”
代碼不變
依然會按照16位訪問,導致低16位被清掉。
所以可以看到這個和編譯器行為有關,編譯器顯然不是根據優化等級決定位域的操作寬度,這里而是根據位域的寬度剛好是16位對齊,所以優化為了16位操作指令。
3.2使用volatile避免編譯器優化
#ifndef__IOM
#define__IOM volatile
#endif
所有uint32_t替換為__IOM uint32_t
還是一樣的
顯然匯編代碼的訪問寬度也不受volatile影響。
3.3為什么指定了uint32_t和volatile還會優化。
問題來了為什么告訴了編譯器是uint32_t和volatile,為什么其還要一意孤行,要優化為16位訪問指令呢,答案就是因為是標準沒有規定,這是編譯器實現行為決定的,所以編譯器設計者決定的(當然也會有一些現實考慮的),可能不同編譯器行為不同,這里以GCC為例。
GCC編譯器文檔中可以找到答案
GCC的文檔可以看到如下內容,也給出了最好是不使用位域的原因
另外也介紹了位域哪些行為也是編譯器實現相關的,所以嵌入式可移植性考慮不要使用位域
那么有沒有辦法指定編譯按照一定大小訪問呢,GCC有編譯選項可以控制見下一節。
3.4使用編譯器選項-fstrict-volatile-bitfields
可以看到改為了sw指令,按照32位進行了操作
四.一些廠家做法
如下可見
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測試可以保證按照固定指定大小訪問,但是不保證其他編譯器也支持該選項,最好能不使用就不使用位域。
審核編輯黃宇
-
嵌入式
+關注
關注
5082文章
19104瀏覽量
304799 -
寄存器
+關注
關注
31文章
5336瀏覽量
120230 -
編譯器
+關注
關注
1文章
1623瀏覽量
49108 -
MISRA
+關注
關注
0文章
21瀏覽量
6963 -
嵌入式編程
+關注
關注
0文章
27瀏覽量
10305
發布評論請先 登錄
相關推薦
評論