国产AⅤ无码一区二区三区_青青在线香蕉精品视频在线_2021年国产精品专区丝袜_久久青草费线频观看地址_亚洲A综合一区二区三区

首頁 >  資訊 >  詳情

世界播報(bào):為什么MISRA要求你不要使用位域-本文告訴你真相

2023-06-20 12:09:15來源:嵌入式Lee

本文轉(zhuǎn)自公眾號,歡迎關(guān)注

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

一.前言

做過嵌入式開發(fā)的一般會看到一條編程規(guī)范:”不要使用位域”,一般都是知其然不其所以然,了解的多一點(diǎn)的可能知道位域是實(shí)現(xiàn)相關(guān)不具備可移植性,那么繼續(xù)追問哪些行為是實(shí)現(xiàn)相關(guān)哪些行為導(dǎo)致移植性問題? 或者還有人知道,存儲布局,對齊等行為是實(shí)現(xiàn)相關(guān)會導(dǎo)致不可移植性。如果再追問位域產(chǎn)生的匯編代碼是什么樣的,怎么進(jìn)行讀-修改-寫操作的?知道這些內(nèi)容的就更加少之又少了。 讀寫肯定不能讀指定位數(shù),只能字節(jié),或者16位,32位這種,那么編譯器到底讀寫用什么寬度? 這時(shí)基本大部分人都不知道了。


(相關(guān)資料圖)

知其然知其所以然,尤其是嵌入式開發(fā)和硬件結(jié)合比較緊密,所以一定要了解細(xì)節(jié),我們這一篇從一個(gè)問題引出然后去分析查找原因,只有遇到問題然后去分析解決它才會有更深刻的映像。

二.問題分析過程

問題是驅(qū)動程序中一個(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<0paramerr

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)化為了寫高16位

執(zhí)行完后寄存器低16位變?yōu)榱?

這是因?yàn)榧拇嫫饔布现恢С?2位的寫操作,所以寫高16位導(dǎo)致低16位清零了,這是硬件決定的。

二.驗(yàn)證

一般想到的就是優(yōu)化相關(guān),加volatile等,我們分別驗(yàn)證下。

3.1不使能編譯器優(yōu)化

編譯器優(yōu)化選項(xiàng)改為”-O0”

代碼不變

依然會按照16位訪問,導(dǎo)致低16位被清掉。

所以可以看到這個(gè)和編譯器行為有關(guān),編譯器顯然不是根據(jù)優(yōu)化等級決定位域的操作寬度,這里而是根據(jù)位域的寬度剛好是16位對齊,所以優(yōu)化為了16位操作指令。

3.2使用volatile避免編譯器優(yōu)化

#ifndef__IOM

#define__IOM volatile

#endif

所有uint32_t替換為__IOM uint32_t

還是一樣的

顯然匯編代碼的訪問寬度也不受volatile影響。

3.3為什么指定了uint32_t和volatile還會優(yōu)化。

問題來了為什么告訴了編譯器是uint32_t和volatile,為什么其還要一意孤行,要優(yōu)化為16位訪問指令呢,答案就是因?yàn)槭菢?biāo)準(zhǔn)沒有規(guī)定,這是編譯器實(shí)現(xiàn)行為決定的,所以編譯器設(shè)計(jì)者決定的(當(dāng)然也會有一些現(xiàn)實(shí)考慮的),可能不同編譯器行為不同,這里以GCC為例。

GCC編譯器文檔中可以找到答案

GCC的文檔可以看到如下內(nèi)容,也給出了最好是不使用位域的原因

另外也介紹了位域哪些行為也是編譯器實(shí)現(xiàn)相關(guān)的,所以嵌入式可移植性考慮不要使用位域

那么有沒有辦法指定編譯按照一定大小訪問呢,GCC有編譯選項(xiàng)可以控制見下一節(jié)。

3.4使用編譯器選項(xiàng)-fstrict-volatile-bitfields

可以看到改為了sw指令,按照32位進(jìn)行了操作

四.一些廠家做法

如下可見

4.1CMSIS

core_cmxx.h中定義

CMSIS中進(jìn)行了定義,寄存器個(gè)別使用位域

1./*IOdefinitions(accessrestrictionstoperipheralregisters)*/2./**3.defgroupCMSIS_glob_defsCMSISGlobalDefines4.5.IOTypeQualifiersareused6.litospecifytheaccesstoperipheralvariables.7.liforautomaticgenerationofperipheralregisterdebuginformation.8.*/9.#ifdef__cplusplus10.#define__Ivolatile/*!
4.2ST
1./**2.*@briefUniversalSerialBusFullSpeedDevice3.*/4.5.typedefstruct6.{7.__IOuint16_tEP0R;/*!4.3瑞薩

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

1.#ifndef__IM/*!
五.總結(jié)

結(jié)論就是正如很多嵌入式編程規(guī)范所描述的(比如MISRA),一般不建議使用位域,因?yàn)樯婕暗轿挥虻脑L問,存儲等行為都是實(shí)現(xiàn)定義的,不具備可移植性。

嵌入式領(lǐng)域寄存器的定義也最好不要使用位域,到寄存器級別以寄存器操作為單位即可,每個(gè)寄存器都要使用__IM,__OM,__IOM描述。

如果一定要使用位域可以使用-fstrict-volatile-bitfields選項(xiàng),使用GCC測試可以保證按照固定指定大小訪問,但是不保證其他編譯器也支持該選項(xiàng),最好能不使用就不使用位域。

審核編輯黃宇

關(guān)鍵詞:

[ 相關(guān)文章 ]

[ 相關(guān)新聞 ]