本文轉(zhuǎn)自公眾號(hào),歡迎關(guān)注
為什么MISRA要求你不要使用位域-本文告訴你真相 (qq.com)
一.前言
做過(guò)嵌入式開(kāi)發(fā)的一般會(huì)看到一條編程規(guī)范:”不要使用位域”,一般都是知其然不其所以然,了解的多一點(diǎn)的可能知道位域是實(shí)現(xiàn)相關(guān)不具備可移植性,那么繼續(xù)追問(wèn)哪些行為是實(shí)現(xiàn)相關(guān)哪些行為導(dǎo)致移植性問(wèn)題? 或者還有人知道,存儲(chǔ)布局,對(duì)齊等行為是實(shí)現(xiàn)相關(guān)會(huì)導(dǎo)致不可移植性。如果再追問(wèn)位域產(chǎn)生的匯編代碼是什么樣的,怎么進(jìn)行讀-修改-寫(xiě)操作的?知道這些內(nèi)容的就更加少之又少了。 讀寫(xiě)肯定不能讀指定位數(shù),只能字節(jié),或者16位,32位這種,那么編譯器到底讀寫(xiě)用什么寬度? 這時(shí)基本大部分人都不知道了。
知其然知其所以然,尤其是嵌入式開(kāi)發(fā)和硬件結(jié)合比較緊密,所以一定要了解細(xì)節(jié),我們這一篇從一個(gè)問(wèn)題引出然后去分析查找原因,只有遇到問(wèn)題然后去分析解決它才會(huì)有更深刻的映像。
二.問(wèn)題分析過(guò)程
問(wèn)題是驅(qū)動(dòng)程序中一個(gè)寄存器的某個(gè)位域修改,導(dǎo)致其他位域的值被修改了。
關(guān)鍵代碼如下,
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.}
執(zhí)行之前該寄存器值為0x00020100
nfc_base[id]->nfc_ena._b.nfc_data_len= len
匯編代碼被優(yōu)化為了寫(xiě)高16位
執(zhí)行完后寄存器低16位變?yōu)榱?
這是因?yàn)榧拇嫫饔布现恢С?2位的寫(xiě)操作,所以寫(xiě)高16位導(dǎo)致低16位清零了,這是硬件決定的。
二.驗(yàn)證
一般想到的就是優(yōu)化相關(guān),加volatile等,我們分別驗(yàn)證下。
3.1不使能編譯器優(yōu)化
編譯器優(yōu)化選項(xiàng)改為”-O0”
代碼不變
依然會(huì)按照16位訪問(wèn),導(dǎo)致低16位被清掉。
所以可以看到這個(gè)和編譯器行為有關(guān),編譯器顯然不是根據(jù)優(yōu)化等級(jí)決定位域的操作寬度,這里而是根據(jù)位域的寬度剛好是16位對(duì)齊,所以優(yōu)化為了16位操作指令。
3.2使用volatile避免編譯器優(yōu)化
#ifndef__IOM
#define__IOM volatile
#endif
所有uint32_t替換為_(kāi)_IOM uint32_t
還是一樣的
顯然匯編代碼的訪問(wèn)寬度也不受volatile影響。
3.3為什么指定了uint32_t和volatile還會(huì)優(yōu)化。
問(wèn)題來(lái)了為什么告訴了編譯器是uint32_t和volatile,為什么其還要一意孤行,要優(yōu)化為16位訪問(wèn)指令呢,答案就是因?yàn)槭菢?biāo)準(zhǔn)沒(méi)有規(guī)定,這是編譯器實(shí)現(xiàn)行為決定的,所以編譯器設(shè)計(jì)者決定的(當(dāng)然也會(huì)有一些現(xiàn)實(shí)考慮的),可能不同編譯器行為不同,這里以GCC為例。
GCC編譯器文檔中可以找到答案
GCC的文檔可以看到如下內(nèi)容,也給出了最好是不使用位域的原因
另外也介紹了位域哪些行為也是編譯器實(shí)現(xiàn)相關(guān)的,所以嵌入式可移植性考慮不要使用位域
那么有沒(méi)有辦法指定編譯按照一定大小訪問(wèn)呢,GCC有編譯選項(xiàng)可以控制見(jiàn)下一節(jié)。
3.4使用編譯器選項(xiàng)-fstrict-volatile-bitfields
可以看到改為了sw指令,按照32位進(jìn)行了操作
四.一些廠家做法
如下可見(jiàn)
4.1CMSIS
core_cmxx.h中定義
CMSIS中進(jìn)行了定義,寄存器個(gè)別使用位域
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)??????????????????????????????????????????????????????????*/
五.總結(jié)
結(jié)論就是正如很多嵌入式編程規(guī)范所描述的(比如MISRA),一般不建議使用位域,因?yàn)樯婕暗轿挥虻脑L問(wèn),存儲(chǔ)等行為都是實(shí)現(xiàn)定義的,不具備可移植性。
嵌入式領(lǐng)域寄存器的定義也最好不要使用位域,到寄存器級(jí)別以寄存器操作為單位即可,每個(gè)寄存器都要使用__IM,__OM,__IOM描述。
如果一定要使用位域可以使用-fstrict-volatile-bitfields選項(xiàng),使用GCC測(cè)試可以保證按照固定指定大小訪問(wèn),但是不保證其他編譯器也支持該選項(xiàng),最好能不使用就不使用位域。
審核編輯黃宇
-
嵌入式
+關(guān)注
關(guān)注
5082文章
19104瀏覽量
304791 -
寄存器
+關(guān)注
關(guān)注
31文章
5336瀏覽量
120228 -
編譯器
+關(guān)注
關(guān)注
1文章
1623瀏覽量
49107 -
MISRA
+關(guān)注
關(guān)注
0文章
21瀏覽量
6963 -
嵌入式編程
+關(guān)注
關(guān)注
0文章
27瀏覽量
10305
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論