少花錢多辦事——這句話抓住了巴克明斯特·富勒(Buckminster Fuller)的短期化概念,在20世紀(jì)90年代在嵌入式開發(fā)領(lǐng)域中引起了熱議,但似乎從未過時。管理者們不斷地擠壓預(yù)算和時間表,以更快、更便宜地交付產(chǎn)品,結(jié)果往往是質(zhì)量受損。與富勒不斷提高質(zhì)量和解決方案的愿景不同,這種方法通常會縮短產(chǎn)品生命周期的測試階段,以滿足積極的計(jì)劃目標(biāo),有時意味著從最終產(chǎn)品中刪除功能(可能會在以后的版本更新中添加)。
讓我們來探索一些技術(shù),這些技術(shù)將幫助開發(fā)人員更快地發(fā)現(xiàn)和修復(fù)缺陷,幫助節(jié)省構(gòu)建材料的資金,并可能避免短期化的挑戰(zhàn)。雖然主要關(guān)注基于Arm的內(nèi)核,但由于許多嵌入式設(shè)備中存在類似的功能,因此許多這些技術(shù)可直接應(yīng)用于其他內(nèi)核。
量化節(jié)約的最簡單方法之一是BML:成本較低的零件需要公司花費(fèi)較少的資金來制造產(chǎn)品。在大多數(shù)嵌入式設(shè)計(jì)中,最昂貴的兩個部分通常是屏幕(如果設(shè)備有一個;大多數(shù)物聯(lián)網(wǎng)設(shè)備沒有)和處理器。當(dāng)你向處理器添加更多內(nèi)存(閃存和RAM)時,處理器的成本會增加。雖然各半導(dǎo)體公司的成本增加的具體程度各不相同,但粗略的經(jīng)驗(yàn)法則是,每增加一倍內(nèi)存,處理器的單位成本就會增加一美元左右。
更糟糕的是,嵌入式開發(fā)工程師通常不太擅長在應(yīng)用程序的設(shè)計(jì)階段預(yù)測內(nèi)存需求。這些對所需內(nèi)存量的最佳“估計(jì)”是處理器選擇的關(guān)鍵因素。鑒于每年的生產(chǎn)量都在數(shù)十萬或數(shù)百萬臺,給BML增加不必要的美元會對公司的底線產(chǎn)生不利影響。
結(jié)果,無數(shù)項(xiàng)目“資源緊張”,這是“我們沒有正確預(yù)測我們的內(nèi)存需求”的代碼。加劇這一問題的是,在項(xiàng)目開始時,BML經(jīng)常被分配給高層管理層。一旦發(fā)生這種情況,成本就變得不可侵犯。這使得人們爭先恐后地減少內(nèi)存占用,或者依靠采購,通過談判其他組件的更好價格來保持BML成本與管理層預(yù)期的相同。為了減少內(nèi)存占用,團(tuán)隊(duì)通常會使用編譯器的優(yōu)化引擎來減少生成的代碼的大小。
提高編譯器優(yōu)化的標(biāo)準(zhǔn)
一些工程師非常不愿意啟動優(yōu)化,因?yàn)樗麄冋J(rèn)為優(yōu)化會給系統(tǒng)帶來錯誤。這種情況很少發(fā)生,根據(jù)我的經(jīng)驗(yàn),大約5%的優(yōu)化器問題是優(yōu)化器的問題。
當(dāng)優(yōu)化級別提高時,編譯器會對C和C++語言的語義非常挑剔。優(yōu)化決策基于對語言規(guī)則的嚴(yán)格解釋。通常,嵌入式開發(fā)工程師們并沒有完全意識到語言和代碼的所有細(xì)微差別,這對他們來說是很自然的。
例如,如果函數(shù)調(diào)用是這樣編寫的:
myFunc(varA、varB、varC、varD);
自然的假設(shè)是從左到右讀取變量:從內(nèi)存中讀取varA,然后是varB,依此類推。
然而,在C或C++中并沒有說一定要這樣。如果有意或無意地將內(nèi)存布置在varB緊挨著varD的位置,那么高度優(yōu)化可能會使用索引寄存器來讀取連續(xù)的內(nèi)存空間,以節(jié)省代碼大小和速度。
在大多數(shù)情況下,這不會對代碼產(chǎn)生影響。但是,如果你在從左到右編寫變量時依賴于被訪問的變量,那么可能會出現(xiàn)這樣的情況:代碼在較低的優(yōu)化水平下運(yùn)行良好,但在較高的優(yōu)化水平上運(yùn)行不佳。在這種情況下,工具供應(yīng)商提供的良好支持結(jié)構(gòu)可以幫助你發(fā)現(xiàn)這些類型的問題,并重寫代碼部分,以更好地進(jìn)行優(yōu)化并正確工作,而不依賴于優(yōu)化設(shè)置。
此外,如果你的代碼可以在高度優(yōu)化時同樣工作,那么它的編寫是正確的,并且經(jīng)過了更好的測試。在嵌入式開發(fā)中,如果代碼不能在更高的優(yōu)化下工作,那么很有可能潛在的缺陷正在等待“咬你”。
好的工具在設(shè)置為大規(guī)模優(yōu)化時可以節(jié)省10-40%的代碼大小。然而,并非所有優(yōu)化轉(zhuǎn)換都是任何一段代碼的好選擇——某些轉(zhuǎn)換實(shí)際上可能會增加某些類型代碼的代碼大小。
目前,有一些資源可用于解決“從編譯器中獲取最少”的問題,這意味著最小的代碼和最短的執(zhí)行時間。節(jié)省這么多的代碼空間可能是在剝離功能以保持設(shè)備大小、由于手動優(yōu)化代碼而錯過計(jì)劃或超出BML預(yù)算之間的區(qū)別。
雖然好的代碼可以在任何級別的優(yōu)化中運(yùn)行相同的代碼,但調(diào)試高度優(yōu)化的代碼是非常棘手的。例如,整個代碼段可以在完全不同的位置合并到其他代碼段中。這就是為什么必須在低優(yōu)化或無優(yōu)化的情況下調(diào)試代碼,并在增加優(yōu)化以運(yùn)行全部測試之前驗(yàn)證代碼是否正常運(yùn)行。
BML中的調(diào)試成本
讓嵌入式調(diào)試變得困難的部分原因是,大多數(shù)嵌入式開發(fā)人員根本不知道他們所有調(diào)試工具。它們傾向于默認(rèn)使用printf語句和代碼斷點(diǎn)。這些默認(rèn)值在試圖隔離硬故障、查找堆棧溢出發(fā)生的位置或查找變量不斷被阻塞的原因時沒有幫助。好消息是,有一些特殊的工具可以幫助發(fā)現(xiàn)這些類型的問題。
處理硬件故障
許多現(xiàn)代MCU具有實(shí)時指令跟蹤功能,允許你跟蹤指令流。在基于Arm的設(shè)備上,用于實(shí)現(xiàn)這一點(diǎn)的技術(shù)是嵌入式跟蹤宏單元(ETM)。參考手冊將顯示設(shè)備是否支持ETM。如果是這樣,請將跟蹤引腳連接到調(diào)試標(biāo)頭,并使用支持跟蹤的調(diào)試器,例如IAR I-jet trace,它可以捕獲實(shí)時指令流并將其顯示在調(diào)試器窗口中。
要查找導(dǎo)致硬件故障的原因,只需滾動跟蹤窗口,找到在進(jìn)入故障處理程序之前執(zhí)行的指令。如果可以可靠地重現(xiàn)錯誤,請?jiān)诠收咸幚沓绦蛱幵O(shè)置斷點(diǎn),并消除跟蹤窗口中的所有滾動–罪魁禍?zhǔn)资歉櫞翱谥械箶?shù)第二條指令。現(xiàn)在原因已經(jīng)知道了,因此可以在問題上設(shè)置斷點(diǎn),并再次運(yùn)行測試用例,以查看導(dǎo)致異常的代碼有什么問題。
但如果你沒有ETM呢?大多數(shù)基于Arm的設(shè)備具有串行線輸出(SWO),允許采樣、低速跟蹤。雖然你沒有得到每一條指令,但這可以提供足夠的跟蹤信息來縮小范圍并找到問題。此外,在嵌入式開發(fā)中,嘗試降低MCU時鐘和/或調(diào)整SWO設(shè)置,以便從調(diào)試器中獲得更精細(xì)的跟蹤信息,以便了解問題發(fā)生的位置。
其他設(shè)備架構(gòu)具有與ETM或SWO類似的功能。因此,使用高質(zhì)量的工具可以利用這些信息,快速隔離和消除問題。此外,可用的支持資源有助于提高SWO的性能,以保護(hù)更多的跟蹤數(shù)據(jù)。
停止堆棧溢出
堆棧溢出或找出變量神秘丟失內(nèi)容的原因如何?使用相同的技術(shù)診斷這兩種情況。
在Arm世界中,大多數(shù)處理器的調(diào)試接口中都有一個數(shù)據(jù)監(jiān)視點(diǎn)和跟蹤(DWT)模塊,可用于快速隔離這些類型的問題。在這種情況下,使用一個數(shù)據(jù)觀察點(diǎn)來找出不好的事情發(fā)生在哪里。每當(dāng)接觸到一段數(shù)據(jù)時,這個觀察點(diǎn)本質(zhì)上就是一個斷點(diǎn)。
將選項(xiàng)配置為僅在數(shù)據(jù)被讀取、寫入或同時讀取和寫入時中斷執(zhí)行。此外,如果數(shù)據(jù)是具有特定位掩碼的特定值,甚至將其限制為僅斷開,這在避免每次訪問數(shù)據(jù)時停止時非常方便。
在堆棧溢出的情況下,嵌入式開發(fā)人員可以在堆棧頂部設(shè)置一個數(shù)據(jù)觀察點(diǎn)。讀取或?qū)懭朐撝挡⒉恢匾?,因?yàn)槎褩T诖a中的該點(diǎn)已經(jīng)損壞。處理器將在堆棧頂部停止執(zhí)行,提供一個完全保留的調(diào)用堆棧,允許查看哪段代碼正在刷新堆棧以及到達(dá)該點(diǎn)的方式,這是確定如何修復(fù)錯誤的關(guān)鍵。
清理破壞的數(shù)據(jù)
對于被破壞的數(shù)據(jù),我們使用基本相同的技術(shù),只是在該變量經(jīng)歷寫入時設(shè)置數(shù)據(jù)觀察點(diǎn)。如果它總是被相同的值阻塞,則進(jìn)一步縮小斷點(diǎn),使其僅在將該值寫入變量時才觸發(fā)。然后,再次運(yùn)行我們的測試用例,找出導(dǎo)致問題的代碼。
同樣,許多其他架構(gòu)(如Renesas RL78、RX和許多其他芯片供應(yīng)商的設(shè)備)具有類似的功能,可用于實(shí)現(xiàn)相同的結(jié)果。有了高質(zhì)量的工具,發(fā)現(xiàn)這些類型的問題變得更容易,并增加了滿足積極的時間表和截止日期的可能性。
讓采購了解你的關(guān)心
用更少的資源做更多的事情似乎是一個矛盾,但使用正確的工具可以很容易地實(shí)現(xiàn)。通過使用編譯器優(yōu)化,你可以將代碼壓縮到盡可能小的空間,以便為應(yīng)用程序使用最便宜的設(shè)備。
優(yōu)化還可以幫助桌面檢查代碼,看看它是否在高優(yōu)化下運(yùn)行,從而在你將代碼簽入構(gòu)建之前找到潛在的代碼缺陷(從而根據(jù)發(fā)布度量計(jì)算每個缺陷)。還可以通過使用完整的工具箱更快地發(fā)現(xiàn)bug,從而縮短測試和修復(fù)周期,更快地完成項(xiàng)目,從而幫助你更高效地進(jìn)行調(diào)試。如果嵌入式開發(fā)人員知道工具箱里有什么工具(以及如何正確使用它們),就可以為企業(yè)節(jié)省每一分錢。