當(dāng)前位置:首頁 > 公眾號精選 > 嵌入式大雜燴
[導(dǎo)讀]【寫在前面的話】 在鴿了將近4年之后,我終于良心發(fā)現(xiàn),決定重新恢復(fù)【裸機(jī)思維】公眾號的更新。謝謝大家的長久守候和等待——非常非常抱歉。這段期間,發(fā)生了很多事情,我也憋了很多內(nèi)容想跟更多的朋友分享。作為一個開端,我準(zhǔn)備踏踏實實的從一些小的話題開

【寫在前面的話】

在鴿了將近4年之后,我終于良心發(fā)現(xiàn),決定重新恢復(fù)【裸機(jī)思維】公眾號的更新。謝謝大家的長久守候和等待——非常非常抱歉。這段期間,發(fā)生了很多事情,我也憋了很多內(nèi)容想跟更多的朋友分享。作為一個開端,我準(zhǔn)備踏踏實實的從一些小的話題開始,慢慢恢復(fù)寫作狀態(tài)。《編譯器的玄學(xué)研究報告》就是這樣一個系列,我會為大家分析一些常見的、同時也是最新的、嵌入式編譯器使用中可能會遇到的問題——尤其是那些看似是 玄學(xué)的現(xiàn)象——為大家 庖丁解牛、 由淺入深,不僅給個痛快,也給大家個 明明白白——我最終的目的是希望大家 不懼怕優(yōu)化,不要把編譯器的行為看作是玄學(xué), 最終 人人都擁有屈駕最高優(yōu)化等級的知識和信心。
在正文開始前,給大家提個小問題:你們用過的最高優(yōu)化等級是什么(編譯器是什么)?遇到過什么問題?歡迎大家在評論區(qū)留言。我會篩選最高贊的評論,并嘗試在以后的《編譯器玄學(xué)報告》中為大家解答。


【正文】

位域和volatile大家再熟悉不過了:前者用于將指定類型的整形變量按照我們的意愿像蛋糕一樣切分成或大或小的若干份;后者用于告訴編譯器“絕不允許對被修飾的變量動手動腳(做優(yōu)化)”,因為在“編譯器不知道的情況下”,這個變量的值是可能會因為各種原因被更新或者是改變的。

外設(shè)(peripheral)本質(zhì)上就是大家最近熱炒的“硬件加速器”。在遙遠(yuǎn)的過去,UART、SPI這類外設(shè)其實都只是一個通信協(xié)議,由軟件通過操作GPIO(最多配合引腳上的外中斷)來實現(xiàn)。后來,為了降低CPU的負(fù)擔(dān)(offload CPU)、提高能效比(Energy Efficiency),軟件UART和SPI的硬件加速器被制造了出來——這就是大家熟知的硬件UART和SPI的由來。


說到“降低CPU負(fù)擔(dān)”,實在有個槽不吐不快:外設(shè)存在的意義就是為了“解放CPU”——讓原本通過軟件來實現(xiàn)的功能由硬件來做——不僅做得更好更可靠,而且消耗的能量更少。問題是,當(dāng)CPU解放以后,CPU應(yīng)該做啥呢?或者說多出來的CPU時間、多出來的運(yùn)算性能CPU應(yīng)該用來做啥呢?一般來說,有以下幾個直接的選項:

  • 時間空出來了,我就可以做更多別的事情了唄……

  • 時間空出來了,我好像沒別的事情做,那就……睡一會兒唄……


然而,我們廣大的可愛的朋友們用實際行動告訴我們:

  • 時間空出來了,我就托著腮看著外設(shè),直到它完成工作……唄……

//! 我故意不用STM32的例子,以防止更多的人受到冒犯//! 一個串口發(fā)送單個字符的例子,這個代碼是我自己寫的int stdout_putchar(char txchar){ CMSDK_UART0->DATA = (uint32_t)txchar;    while(CMSDK_UART0->STATE & CMSDK_UART_STATE_TXBF_Msk); //!托腮 return (int) txchar;}


以上內(nèi)容扯遠(yuǎn)了……




為了后續(xù)的討論更加簡單直接,我想重復(fù)下很多你們“肯定”注意到了的“廢話”:
  • 外設(shè)是可以跟CPU同時工作的

  • 外設(shè)寄存器的值在CPU沒有改寫的情況下是會被外設(shè)自己更新的

    • 正因為如此,定義外設(shè)寄存器的時候要用volatile來修飾


接下來,我再來介紹一些很多人一般不會注意到的事實:

  • 寄存器的訪問是有對齊限制的

    • 一個支持WORD對齊訪問的寄存器,如果你直接用Half-WORD的地址去訪問,比如訪問一個4字節(jié)寄存器的高16位,你是很可能會觸發(fā)bus fault

    • 通常,大部分外設(shè)都支持多種訪問對齊形式,比如WORD對齊、Half-WORD對齊和字節(jié)對齊,所以你不太會遇到這類問題。但有些外設(shè)本身設(shè)計比較“樸素”——你可能就會遇到這類沒有蓋上蓋子的下水道。


  • 寄存器的訪問是有大小限制的

    • 一個支持以WORD大小訪問的寄存器(只支持用volatile uint32_t *指針類型來訪問的寄存器),哪怕你地址對齊了到了WORD,如果你用字節(jié)大小去訪問(用volatile uint8_t *指針類型來訪問),你也是很有可能會觸發(fā)bus fault的。

    • 通常,大部分外設(shè)都支持多種大小的訪問,比如WORD大小的訪問、Half-WORD大小的訪問和字節(jié)大小的訪問,所以你不太會遇到這類問題。但是,有些外設(shè)本身設(shè)計比較“樸素”——你可能就會遇到這類沒有蓋上蓋子的下水道。


目前幾乎所有32位處理器中使用的寄存器都是32位的,所以 誰還會用字節(jié)大小去非對齊的訪問32寄存器呢?(何況大部分情況下,寄存器的頭文件都是官方提供的)。


NO,NO,NO,你太天真了。讓我們來看一個案例(同時為了防止人們對號入座,以下當(dāng)事人和代碼都已經(jīng)打碼)

typedef struct { volatile uint32_t SEL : 8;} example_reg_t
#define EXAMPLE_REG_ADDR 0x40000000#define EXAMPLE_REG       (*(example_reg_t*) EXAMPLE_REG_ADDR)
void set_selection_field(uint_fast8_t chSelection){    //! 使用位域來直接訪問 SEL[0:7]    EXAMPLE_REG.SEL = chSelection;}
在這個代碼里我們用位域定義了一個寄存器叫 EXAMPLE_REG,它的地址是0x4000-0000,其 BIT0~BIT7是一個叫做 SEL的8bit無符號整型位域。這里, volatile正確告訴了編譯器“不要對操作進(jìn)行優(yōu)化”,而 uint32_t則正確的告訴了編譯器SEL所寄宿的整形類型是一個WORD—— “飛龍騎怎么輸”?

事實證明,在Arm Compiler 5(也就是大家熟知的armcc)下的確沒有問題,這是生成的代碼:

為了方便大家理解,這里逐條解釋如下:
MOV r1,#0x40000000 ; 將地址值 0x40000000 存入r1LDR r2,[r1,#0x00]  ; 將 r1 當(dāng)作指針變量,讀取偏移量為0x00的一個word到r2中BFI r2,r0,#0,#8    ; 將保存在r0中由用戶傳入的值提取低8位覆蓋r2的低8位STR r2,[r1,#0x00]  ; 將 r1 當(dāng)作指針變量,寫入r2中的WORD到目標(biāo)地址BX    lr             ; 返回上一級函數(shù)

可見,這里的代碼生成完全滿足我們的要求。當(dāng)我們移植同樣的代碼到LLVM或者基于LLVM的Arm Compiler 6下,神奇的一幕發(fā)生了:



注意,這里Arm Compiler 6使用了跟Arm Compiler 5一樣的優(yōu)化等級(-O1),可見原本的5條指令變成了3條,這里逐條解釋如下:


MOV      r1,#0x40000000 ; 將地址值 0x40000000 存入r1STRB     r0,[r1,#0x00]   ; 將 r1 當(dāng)作指針變量,寫入r0中的BYTE到目標(biāo)地址BX       lr              ; 返回上一級函數(shù)

等一等?且不論之前的“讀改寫”被成功的“優(yōu)化掉了”(這個是沒有問題的,因為原本的寄存器定義中,我們就沒有給出剩下24bit的內(nèi)容,這等于告訴編譯器我們對這部分值是不在乎的,所以這里編譯器也沒有對剩下的24bit做“讀改寫”保護(hù)),

  • 為什么uint32_t所明確標(biāo)記的word操作被替換成了byte操作??

  • volatile白加了么?說好的不會優(yōu)化呢?

  • 編譯器你怎么不按套路出牌?

  • 難道位域在Arm Compiler 6不能使用了么?——萬一我的寄存器是只支持WORD大小訪問的怎么辦?

  • 這是編譯器的bug么?實錘了么?

  • Arm Compiler 6果然是垃圾么?果然還是armcc大法好!


先別急,我們再來看看定義本身:

typedef struct { volatile uint32_t SEL : 8;} example_reg_t

注意到?jīng)]有?這里volatile只覆蓋了位域SEL,也就是說我們其實只告訴編譯器uint32_t中只有低8位是volatile的(只有一個字節(jié)是volatile的)——換句話說:“對uint32_t中的第一個字節(jié)的訪問是不允許優(yōu)化的”,而其它部分我們沒有規(guī)定。這是不是意味著,LLVM和Arm Compiler 6編譯器特別較真,它覺得我們本意就是告訴它“要以byte的形式去訪問一個uint32_t整形的第字節(jié)”呢?而且還“不允許優(yōu)化”。

為了驗證這個想法,我們將剩下的部分補(bǔ)齊:

typedef struct { volatile uint32_t SEL : 8; volatile uint32_t : 24;} example_reg_t

重新編譯工程,生成代碼如下:

果然,不僅讀改寫回來了,針對寄存器訪問的大小也乖乖變回了uint32_t。


【玄學(xué)說法】“Arm Compiler 6(armclang)比 Arm Compiler 5 不可靠、容易生成錯誤的代碼” 

際情況】Arm Compiler 6比Arm Compiler 5在語法理解上更嚴(yán)格,而Arm Compiler 5在語法理解上更寬松,并且隱含了一些編譯器自己的“私貨”,大家只不過是先入為主,早已習(xí)慣了armcc而已。




【后記】

armcc并不比Arm Compiler 6更可靠,實際上,作為一個已經(jīng)停止維護(hù)的編譯器 armcc擁有眾多隱藏的天坑,后面有機(jī)會我將向大家展示幾個匪夷所思的armcc編譯器bug,到時候就問你們怕不怕

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉