日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁(yè) > > 小麥大叔
[導(dǎo)讀]位域和volatile大家再熟悉不過(guò)了:前者用于將指定類型的整形變量按照我們的意愿像蛋糕一樣切分成或大或小的若干份;后者用于告訴編譯器“絕不允許對(duì)被修飾的變量動(dòng)手動(dòng)腳(做優(yōu)化)”,因?yàn)樵凇熬幾g器不知道的情況下”,這個(gè)變量的值是可能會(huì)因?yàn)楦鞣N原因被更新或者是改變的。

【寫在前面的話】

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


【正文】

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

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


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

  • 時(shí)間空出來(lái)了,我就可以做更多別的事情了唄……

  • 時(shí)間空出來(lái)了,我好像沒別的事情做,那就……睡一會(huì)兒?jiǎn)h……


然而,我們廣大的可愛的朋友們用實(shí)際行動(dòng)告訴我們:

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

//! 我故意不用STM32的例子,以防止更多的人受到冒犯//! 一個(gè)串口發(fā)送單個(gè)字符的例子,這個(gè)代碼是我自己寫的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ù)的討論更加簡(jiǎn)單直接,我想重復(fù)下很多你們“肯定”注意到了的“廢話”:

  • 外設(shè)是可以跟CPU同時(shí)工作的

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

    • 正因?yàn)槿绱?,定義外設(shè)寄存器的時(shí)候要用volatile來(lái)修飾


接下來(lái),我再來(lái)介紹一些很多人一般不會(huì)注意到的事實(shí):

  • 寄存器的訪問(wèn)是有對(duì)齊限制的

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

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


  • 寄存器的訪問(wèn)是有大小限制的

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

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


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


NO,NO,NO,你太天真了。讓我們來(lái)看一個(gè)案例(同時(shí)為了防止人們對(duì)號(hào)入座,以下當(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){    //! 使用位域來(lái)直接訪問(wèn) SEL[0:7]    EXAMPLE_REG.SEL = chSelection;}

在這個(gè)代碼里我們用位域定義了一個(gè)寄存器叫 EXAMPLE_REG,它的地址是0x4000-0000,其 BIT0~BIT7是一個(gè)叫做 SEL的8bit無(wú)符號(hào)整型位域。這里, volatile正確告訴了編譯器“不要對(duì)操作進(jìn)行優(yōu)化”,而 uint32_t則正確的告訴了編譯器SEL所寄宿的整形類型是一個(gè)WORD—— “飛龍騎臉 怎么輸”?

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

為了方便大家理解,這里逐條解釋如下:

MOV r1,#0x40000000 ; 將地址值 0x40000000 存入r1LDR r2,[r1,#0x00]  ; 將 r1 當(dāng)作指針變量,讀取偏移量為0x00的一個(gè)word到r2中BFI r2,r0,#0,#8    ; 將保存在r0中由用戶傳入的值提取低8位覆蓋r2的低8位STR r2,[r1,#0x00]  ; 將 r1 當(dāng)作指針變量,寫入r2中的WORD到目標(biāo)地址BX    lr             ; 返回上一級(jí)函數(shù)

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



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


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

等一等?且不論之前的“讀改寫”被成功的“優(yōu)化掉了”(這個(gè)是沒有問(wèn)題的,因?yàn)樵镜募拇嫫鞫x中,我們就沒有給出剩下28bit的內(nèi)容,這等于告訴編譯器我們對(duì)這部分值是不在乎的,所以這里編譯器也沒有對(duì)剩下的28bit做“讀改寫”保護(hù)),

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

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

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

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

  • 這是編譯器的bug么?實(shí)錘了么?

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


先別急,我們?cè)賮?lái)看看定義本身:

typedef struct { volatile uint32_t SEL : 8;} example_reg_t

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

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

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

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

果然,不僅讀改寫回來(lái)了,針對(duì)寄存器訪問(wèn)的大小也乖乖變回了uint32_t。


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

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




【后記】

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

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

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

LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動(dòng)電源

在工業(yè)自動(dòng)化蓬勃發(fā)展的當(dāng)下,工業(yè)電機(jī)作為核心動(dòng)力設(shè)備,其驅(qū)動(dòng)電源的性能直接關(guān)系到整個(gè)系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動(dòng)勢(shì)抑制與過(guò)流保護(hù)是驅(qū)動(dòng)電源設(shè)計(jì)中至關(guān)重要的兩個(gè)環(huán)節(jié),集成化方案的設(shè)計(jì)成為提升電機(jī)驅(qū)動(dòng)性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機(jī) 驅(qū)動(dòng)電源

LED 驅(qū)動(dòng)電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個(gè)照明設(shè)備的使用壽命。然而,在實(shí)際應(yīng)用中,LED 驅(qū)動(dòng)電源易損壞的問(wèn)題卻十分常見,不僅增加了維護(hù)成本,還影響了用戶體驗(yàn)。要解決這一問(wèn)題,需從設(shè)計(jì)、生...

關(guān)鍵字: 驅(qū)動(dòng)電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動(dòng)電源的公式,電感內(nèi)電流波動(dòng)大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計(jì) 驅(qū)動(dòng)電源

電動(dòng)汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動(dòng)汽車的核心技術(shù)之一是電機(jī)驅(qū)動(dòng)控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機(jī)驅(qū)動(dòng)系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動(dòng)汽車的動(dòng)力性能和...

關(guān)鍵字: 電動(dòng)汽車 新能源 驅(qū)動(dòng)電源

在現(xiàn)代城市建設(shè)中,街道及停車場(chǎng)照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進(jìn)步,高亮度白光發(fā)光二極管(LED)因其獨(dú)特的優(yōu)勢(shì)逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動(dòng)電源 LED

LED通用照明設(shè)計(jì)工程師會(huì)遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動(dòng)電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動(dòng)電源的電磁干擾(EMI)問(wèn)題成為了一個(gè)不可忽視的挑戰(zhàn)。電磁干擾不僅會(huì)影響LED燈具的正常工作,還可能對(duì)周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來(lái)解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動(dòng)電源

開關(guān)電源具有效率高的特性,而且開關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機(jī)重量也有所下降,所以,現(xiàn)在的LED驅(qū)動(dòng)電源

關(guān)鍵字: LED 驅(qū)動(dòng)電源 開關(guān)電源

LED驅(qū)動(dòng)電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動(dòng)LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動(dòng)電源
關(guān)閉