錯(cuò)排查:老嵌入式項(xiàng)目為何離不開(kāi)AC5)
簡(jiǎn)介ARMCC編譯器是ARM官方推出的集成開(kāi)發(fā)工具鏈之一面向使用Cortex-A/R/M等內(nèi)核進(jìn)行嵌入式開(kāi)發(fā)的工程師能夠解決從源碼編寫(xiě)、編譯優(yōu)化到鏈接調(diào)試的完整流程問(wèn)題。壓縮包內(nèi)含650個(gè)文件大小56.83MB除了編譯器、匯編器、鏈接器及調(diào)試工具等可執(zhí)行程序還收錄了大量標(biāo)準(zhǔn)庫(kù)頭文件、庫(kù)文件、鏈接映射文件與匯編源文件便于查閱ARMCC底層實(shí)現(xiàn)細(xì)節(jié)。資源內(nèi)容覆蓋ARM指令集支持、O0至O3多級(jí)優(yōu)化、嵌入式鏈接編輯、CMSIS統(tǒng)一硬件接口、硬件浮點(diǎn)運(yùn)算、代碼尺寸壓縮以及多線程與實(shí)時(shí)性處理等關(guān)鍵知識(shí)點(diǎn)同時(shí)提供了豐富的錯(cuò)誤和警告報(bào)告機(jī)制可幫助開(kāi)發(fā)者精準(zhǔn)定位代碼問(wèn)題。適合初學(xué)者系統(tǒng)學(xué)習(xí)嵌入式編譯原理也可作為工程師在移動(dòng)設(shè)備、物聯(lián)網(wǎng)、汽車電子與工業(yè)控制等領(lǐng)域搭建ARM開(kāi)發(fā)環(huán)境的備用工具包。目前已有4216人學(xué)習(xí)下載是一份極具實(shí)用價(jià)值的ARM開(kāi)發(fā)參考資料。1. ARMCC v5到底是個(gè)什么角色為什么老項(xiàng)目離不開(kāi)它1.1 ARMCC在工具鏈里的準(zhǔn)確位置“ARM Compiler Version 5”這串名字聽(tīng)起來(lái)很像官方文檔里某個(gè)古早的術(shù)語(yǔ)但你要是做嵌入式開(kāi)發(fā)肯定早就被它折騰過(guò)。它就是圈子里天天喊的ARMCC、AC5。整套工具鏈由四件事組成armcc負(fù)責(zé)把C/C源碼編譯成目標(biāo)文件armasm處理匯編源文件armlink做鏈接生成可執(zhí)行映像fromelf負(fù)責(zé)把ELF文件轉(zhuǎn)成hex、bin這類燒錄格式。四件套長(zhǎng)期集成在Keil MDK里所以多數(shù)人的日常是打開(kāi)Keil點(diǎn)一下Build編譯器在后臺(tái)默默跑完整套流程你基本感覺(jué)不到它存在。AC5的老是那種一看就有歷史感的老。它的編譯器前端是ARM公司自己的閉源實(shí)現(xiàn)而后來(lái)的ARM Compiler 6則完全基于LLVM/Clang構(gòu)建命令行工具也換成了armclang。打個(gè)比方AC5像一個(gè)性格穩(wěn)定但脾氣固執(zhí)的老工程師AC6像一個(gè)思維更開(kāi)放、效率更高但要求更嚴(yán)格的年輕工程師。老工程師的“脾氣”已經(jīng)被無(wú)數(shù)項(xiàng)目摸透了這正是AC5一直火到現(xiàn)在的原因。1.2 老項(xiàng)目離不開(kāi)AC5的真實(shí)原因“ARM公司都主推AC6了為什么網(wǎng)上還全是arm compiler 5.06下載的搜索”這個(gè)問(wèn)題我被人問(wèn)過(guò)無(wú)數(shù)遍答案濃縮成三個(gè)字兼容性。第一老板子老庫(kù)。大量MCU廠商在AC5時(shí)代發(fā)布的外設(shè)庫(kù)、例程、驅(qū)動(dòng)壓根沒(méi)考慮過(guò)AC6會(huì)來(lái)。最典型的是STM32標(biāo)準(zhǔn)外設(shè)庫(kù)以及一大票國(guó)產(chǎn)Cortex-M0/M3芯片的早期SDK。你把整個(gè)工程拖到AC6下編譯不是這里紅字就是那里警告改起來(lái)比重新寫(xiě)還痛苦。第二量產(chǎn)項(xiàng)目求穩(wěn)。編譯器是工具鏈里最底層的環(huán)節(jié)優(yōu)化策略和代碼生成一變整個(gè)系統(tǒng)的時(shí)序、內(nèi)存占用、代碼體積都會(huì)跟著變。已經(jīng)發(fā)貨幾年的控制器固件負(fù)責(zé)人不會(huì)為了“新編譯器更好”就去冒險(xiǎn)重新編譯驗(yàn)證功能回歸成本遠(yuǎn)比想象中高。第三老編譯器能力并不差。AC5雖然老但對(duì)Cortex-M0/M3/M4這些主流內(nèi)核的優(yōu)化能力沒(méi)什么短板-O2、-O3、-Ospace、-Otime等主力優(yōu)化項(xiàng)都齊全。跑裸機(jī)或者小型RTOS的產(chǎn)品AC5的輸出完全夠用。這三個(gè)原因疊在一起AC5在2025年的今天依然活躍在老產(chǎn)品維護(hù)、方案復(fù)制、二手設(shè)備翻新項(xiàng)目里一點(diǎn)都不奇怪。2. Keil報(bào)錯(cuò)“Missing: Compiler version 5”的完整排解流程2.1 報(bào)錯(cuò)的真實(shí)原因與典型場(chǎng)景這個(gè)報(bào)錯(cuò)我?guī)缀趺恐芏荚谌豪锟慈擞龅健>唧w畫(huà)面是從同事那里拷來(lái)的工程或者從GitHub拉下來(lái)的老Demo打開(kāi)Keil一按Build輸出窗口直接來(lái)一句“Missing: compiler version 5”或者更啰嗦一點(diǎn)提示Target Target 1 uses ARM-Compiler Compiler Version 5 for compilation which is not installed。光看字面就能明白工程要求用Compiler Version 5但你這臺(tái)電腦上沒(méi)裝。為什么沒(méi)裝大概率是因?yàn)槟阊b的是MDK 5.37或更高版本。從5.37開(kāi)始Keil不再把AC5放進(jìn)默認(rèn)安裝列表新裝的MDK只帶AC6。老工程在保存時(shí)把“我使用Compiler Version 5”寫(xiě)進(jìn)了工程配置新環(huán)境識(shí)別后對(duì)不上號(hào)直接罷工。還有一種情況是誰(shuí)在遷移電腦時(shí)漏裝了編譯器本體或者手動(dòng)卸載過(guò)老版本。搞清楚這一點(diǎn)解決問(wèn)題就很快了。2.2 下載安裝ARMCC 5.06 Update 7補(bǔ)丁包關(guān)鍵一步是把AC5補(bǔ)回來(lái)。推薦安裝的版本是“ARM Compiler 5.06 update 7 (build 960)”這是整個(gè)5.06系列的最后一個(gè)補(bǔ)丁bug收得最全。網(wǎng)絡(luò)熱詞里總有人問(wèn)arm compiler 5.06 update 6 (build 750)下載那個(gè)也能用但如果能選優(yōu)先u7。獲取途徑不復(fù)雜搜索“ARM Compiler 5.06 update 7 (build 960)”就能在ARM官網(wǎng)和Keil官網(wǎng)的下載頁(yè)找到安裝包。雙擊運(yùn)行一路Next即可但這里我自己動(dòng)手裝過(guò)太多回給你三個(gè)提醒默認(rèn)安裝路徑通常指向C:\Keil_v5\ARM\ARMCC如果你的MDK裝在D盤或E盤安裝器大概率能自動(dòng)識(shí)別識(shí)別不了就手動(dòng)指到MDK所在磁盤但路徑里不要有中文和特殊符號(hào)。裝完后去bin目錄確認(rèn)一下里面必須能看到armcc.exe。如果這個(gè)文件不存在說(shuō)明裝錯(cuò)位置了趕緊重裝。安裝前后把Keil關(guān)掉裝完再重新打開(kāi)工程讓Keil重新掃描一遍工具鏈。2.3 在Keil MDK里切換編譯器并驗(yàn)證補(bǔ)丁裝好后打開(kāi)Options for Target切到Target選項(xiàng)卡在中間位置的ARM Compiler下拉框里選擇“Use default compiler version 5”或者直接選V5.06 update 7 (build 960)點(diǎn)確定。這里有個(gè)操作細(xì)節(jié)必須強(qiáng)調(diào)編譯器切換后先執(zhí)行Clean Target把之前用其他編譯器生成的中間文件清掉再重新Build。否則殘留的.o文件可能和新編譯器生成的代碼混在一起冒出一些奇怪的鏈接報(bào)錯(cuò)比如unresolved symbol浪費(fèi)時(shí)間排查。驗(yàn)證是否成功就看編譯輸出窗口最前面的C編譯器路徑是否指向armcc并且版本號(hào)顯示5.06 update 7。如果下拉框里壓根沒(méi)有V5選項(xiàng)去Keil安裝目錄打開(kāi)TOOLS.INI文件檢查里面是否有一條帶ARMCC路徑的記錄。通常正規(guī)安裝包會(huì)自動(dòng)寫(xiě)進(jìn)去如果沒(méi)識(shí)別到十有八九是裝錯(cuò)了目錄。3. ARMCC v5的版本號(hào)與匹配規(guī)則別裝串了3.1 從5.06u6到5.06u7版本號(hào)怎么讀ARMCC的版本體系很多人搞混工程備注里寫(xiě)“ARMCC 5.06”實(shí)際裝的是5.06u3隊(duì)友用的卻是5.06u7兩邊編譯結(jié)果可能就有差異。這里的update后綴不是隨便加的它對(duì)應(yīng)著官方發(fā)布的維護(hù)補(bǔ)丁包。網(wǎng)上最常見(jiàn)的兩個(gè)版本我整理成了表格常見(jiàn)叫法build版本ARMCC 5.06 update 6build 750ARMCC 5.06 update 7build 960搜“arm compiler 5.06u7 下載”的人最多因?yàn)榇蠹倚枰木褪沁@個(gè)最終版本。不過(guò)要記住一個(gè)原則不是越新越好而是和團(tuán)隊(duì)對(duì)齊最好。如果整個(gè)團(tuán)隊(duì)都在u6上驗(yàn)證過(guò)工程你一個(gè)人裝u7語(yǔ)法細(xì)節(jié)和優(yōu)化策略的差異也可能帶來(lái)新坑但如果沒(méi)人有要求直接裝u7總沒(méi)錯(cuò)反正AC5已經(jīng)停更u7就是集大成者。3.2 ARMCC與ARMCLANG的五個(gè)核心差異AC5和AC6的差異不只是命令名變了一個(gè)字母本質(zhì)是整套編譯器的前后端實(shí)現(xiàn)都換了。我整理了一張對(duì)比表對(duì)比項(xiàng)ARMCC V5ARMCLANG V6編譯器命令armccarmclangC標(biāo)準(zhǔn)支持C90/C99為主C11、C14/C17典型預(yù)定義宏__CC_ARMclang、__ARMCC_VERSION內(nèi)聯(lián)匯編寫(xiě)法__asm { ... }標(biāo)準(zhǔn)asm(...)需要兼容處理警告與錯(cuò)誤策略相對(duì)寬松默認(rèn)更嚴(yán)格老代碼易報(bào)錯(cuò)為什么老代碼在AC5下編譯順暢到了AC6就一堆錯(cuò)第一個(gè)原因是標(biāo)準(zhǔn)支持。老代碼大量使用C90風(fēng)格比如在函數(shù)中部聲明變量AC5編譯器前端容忍度很高AC6更現(xiàn)代碰到不符合標(biāo)準(zhǔn)的寫(xiě)法就會(huì)警告甚至報(bào)錯(cuò)。第二個(gè)原因是預(yù)定義宏。許多外設(shè)庫(kù)底層靠__CC_ARM這個(gè)宏判斷編譯器環(huán)境AC6不再定義它條件編譯直接跳進(jìn)未知分支于是出現(xiàn)“莫名其妙的缺定義”。第三個(gè)原因是內(nèi)聯(lián)匯編。AC5里很常見(jiàn)的__asm { NOP }寫(xiě)法在AC6下需要用asm volatile (nop)或者CMSIS封裝函數(shù)替代改起來(lái)非常細(xì)碎。3.3 芯片內(nèi)核與編譯器的匹配建議編譯器版本和芯片內(nèi)核不是隨便搭配的。AC5.06 update 7雖然名義上支持到Cortex-M23/M33這類ARMv8-M內(nèi)核但官方在新內(nèi)核上的專項(xiàng)優(yōu)化、調(diào)試支持都集中在AC6。說(shuō)直白點(diǎn)如果你做M0/M3/M4/M7這些老內(nèi)核AC5穩(wěn)得一批要是選了M33或者更新內(nèi)核直接上AC6別在老編譯器里硬撐。反過(guò)來(lái)說(shuō)老版本MDK也只能配合一定范圍的AC5。MDK 5.20那個(gè)時(shí)代最高就支援到AC5.06u2你硬裝u7反而可能不被識(shí)別新版MDK裝老補(bǔ)丁包倒沒(méi)問(wèn)題就是要注意別選到和新Keil組件打架的版本。總的原則是MDK版本決定能掛載的編譯器范圍工程里的組件版本決定編譯器世代三者對(duì)齊了才能順利編譯。4. ARMCC v5實(shí)戰(zhàn)避坑清單與遷移建議4.1 老工程編譯的3個(gè)高頻坑先說(shuō)License。AC5的許可證通常跟著MDK走絕大多數(shù)用戶不會(huì)遇到單獨(dú)激活問(wèn)題。但如果你在命令行直接調(diào)用armcc而不是通過(guò)Keil界面編譯就可能碰到找不到許可證的情況。這時(shí)候先檢查環(huán)境變量是否指到了MDK的安裝目錄再確認(rèn)授權(quán)是否在有效期內(nèi)。第二個(gè)坑是路徑。編譯器安裝目錄和工程目錄都不要出現(xiàn)中文、空格、特殊符號(hào)。很多工程喜歡放在桌面或者帶日期的文件夾里路徑一深鏈接器就容易報(bào)“cannot open file”。AC5對(duì)路徑的容忍度比現(xiàn)代工具鏈低很多工程路徑能簡(jiǎn)化就簡(jiǎn)化。第三個(gè)坑是多版本混裝。同一個(gè)MDK裝了多個(gè)AC5版本后下拉框會(huì)列出好幾個(gè)選項(xiàng)。從5.06u6切到u7必須Clean Target再重新編譯不同編譯器生成的中間文件混用輕則出現(xiàn)鏈接錯(cuò)誤重則編譯能過(guò)但運(yùn)行行為異常這才是最可怕的。4.2 新項(xiàng)目到底該不該繼續(xù)用AC5每個(gè)來(lái)問(wèn)我的工程師我都會(huì)反問(wèn)他三個(gè)問(wèn)題。如果項(xiàng)目要復(fù)用大量老庫(kù)、老例程、老同事留下的代碼資產(chǎn)直接用AC5。改語(yǔ)法的時(shí)間成本遠(yuǎn)大于編譯器性能提升帶來(lái)的好處這條沒(méi)什么好猶豫的。如果產(chǎn)品要做五到十年的長(zhǎng)期維護(hù)優(yōu)先AC6。新中間件、新CMSIS、新SDK都在往AC6靠攏現(xiàn)在堅(jiān)持AC5等于提前背上了兼容性的債以后想換更麻煩。如果只是個(gè)人學(xué)習(xí)、跑開(kāi)源例程、做方案驗(yàn)證心里別糾結(jié)。目前網(wǎng)上大量例程還是AC5為主先跑起來(lái)理解代碼邏輯比糾結(jié)編譯器版本重要得多。4.3 從AC5遷移到AC6的三個(gè)落地步驟真正決定遷移的話按三步走最穩(wěn)。第一步替換底層框架。把啟動(dòng)文件startup_xxx.s和CMSIS相關(guān)的core_cm*.h、system_xxx.c統(tǒng)一更新到當(dāng)前MDK版本自帶的新文件。這個(gè)動(dòng)作能解決八成的匯編和底層宏報(bào)錯(cuò)舊版啟動(dòng)文件里的偽指令在AC6的匯編階段經(jīng)常被殺。第二步清理源碼語(yǔ)法。老庫(kù)里的內(nèi)聯(lián)匯編、__CC_ARM條件編譯、舊的register關(guān)鍵字、一些不合C99的寫(xiě)法都需要逐文件處理。建議先把編譯器warning全部打開(kāi)按警告逐條清理而不是只盯著error。很多隱藏問(wèn)題都在warning里。第三步嚴(yán)格功能回歸。AC6的優(yōu)化器比AC5激進(jìn)得多未初始化變量、位域操作、函數(shù)內(nèi)static變量、volatile使用不規(guī)范的代碼在高優(yōu)化級(jí)別下行為可能悄悄變化。遷移后不能只編譯通過(guò)就算結(jié)束必須完整跑測(cè)試用例尤其是時(shí)間敏感的外設(shè)驅(qū)動(dòng)和通信協(xié)議部分。如果工期緊張最穩(wěn)的辦法是保持AC5等產(chǎn)品進(jìn)入下一代開(kāi)發(fā)時(shí)再整體切。維護(hù)老產(chǎn)品時(shí)不要為了“先進(jìn)”去動(dòng)底子風(fēng)險(xiǎn)完全不可控。我自己手里維護(hù)的幾個(gè)量產(chǎn)老產(chǎn)品一直用ARMCC 5.06 update 7環(huán)境搭好后多年沒(méi)動(dòng)過(guò)一點(diǎn)問(wèn)題都沒(méi)有。很多初學(xué)者總愛(ài)問(wèn)“AC5是不是該淘汰了”我的回答很直接工具沒(méi)有新舊之分只有合適不合適。你手里的老工程、老芯片、老SDK還離不開(kāi)它那它就有實(shí)實(shí)在在的價(jià)值。最后分享一個(gè)習(xí)慣在每個(gè)工具鏈相關(guān)的工程根目錄放一個(gè)readme寫(xiě)清楚Keil MDK版本號(hào)、ARMCC準(zhǔn)確版本和安裝路徑。這樣不管過(guò)多久、換幾人接手都不會(huì)再為“Missing: compiler version 5”這種問(wèn)題浪費(fèi)半天時(shí)間。本文還有配套的精品資源點(diǎn)擊獲取