看了很多资料,用的isp下载器,但是把下载器拔下在重新上电还是这样
watchdog一般是一個硬件模塊(其實可以當做是一個定時器),其作用是,在嵌入式操作系統中,很多應用情況是系統長期運行且無人看守,導致程序跑飛,所以難免怕萬一出現系統死機,那就悲劇了,這時,watchdog就會自動幫你重啟系統。
那么其是如何實現此功能的呢?簡單解釋一下其實現原理:
watchdog硬件的邏輯就是,其硬件上有個記錄超時功能,然后要求用戶需要每隔一段時間(此時間可以根據自己需求而配置)去對其進行一定操作,比如往里面寫一些固定的值,俗稱“喂狗”,那么看門狗發現超時了,即過了返么長時間你還不給看門狗喂食,那么看門狗就認為你系統是死機了,出問題了,看門狗就幫你reset重啟系統。
為何在要系統初始化的時候關閉watchdog
了解了watchdog的原理后,此問題就很容易理解了。如果不禁用watchdog,那么就要單獨寫程序去定期“喂狗”,那多麻煩,多無聊啊。畢竟咱此處叧是去用uboot初始化必要的硬件資源和系統資源而已,完全用丌到返個watchdog的機制。需要用到,那也是你linux
內核跑起來了,是你系統關心的事情,和我uboot沒啥關系的,所以肯定此處要去關閉watchdog(的reset功能)了。
看門狗(watchdog)包括一個4分頻的預分頻器和一個32位的計數器,時鍾通過預分頻器輸入定時器。定時器遞減計數,遞減的最小值為0XFF。如果設置一個小於0XFF的值,系統會將0XFF裝入計數器,因此最小看門狗間隔為t(pclk)X256X4。
門狗的用途是使微控制器在進入錯誤狀態后的一定時間內復位。當看門狗使能時,如果用戶程序沒有在周期時間內喂狗(重裝),看門狗會產生一個系統復位。
1.如果沒有周期性重裝,則產生片內復位。
3.由軟件使能,但要求禁止硬件復位或看門狗復位/中斷~。
4.錯誤/不完整的喂狗時序會導致復位/中斷(如果使能)。
5.指示看門狗復位的標志。
6.帶內部預分頻器的可編程32位定時器。
7.可選擇t(pclk)X4的倍數的時間周期。
基本操作:看門狗應當根據下面的方法來使用
1.在WDTC寄存器中設置看門狗定時器的固定裝載值;
2.在WDMOD寄存器中設置模式;
3.通過向WDFEED寄存器順序寫入0XAA和0X55啟動看門狗;
4.在看門狗向下溢出之前應當再次喂狗,以防止復位/中斷~!
當看門狗計數器想下溢出時,程序計數器將從0x開始,和外部復位一樣。可以檢查看門狗超時標志WDTOF來確定看門狗是否產生復位條件,WDTOF標志必須由軟件清零。
類似的看門狗相關的數據手冊 電路圖
MTK 看門狗詳細解析
在手機Soc
Chip中,里面的AP跑着linux操作系統軟件,而任何軟件都可能存在各種問題,如果遇到了這些異常,軟件可能陷入死循環,導致手機變成“磚頭”,如果沒有其他硬件輔助,那么只能斷電(拔電池)然后重新開機才行。為了避免出現這種情況,芯片內部增加了一個看門狗模塊,這個模塊專門檢測CPU運行狀態,只要出現卡死就復位系統。
timer,就是看門狗模塊,看門狗其實就是一個可以在一定時間內被復位的計數器。當看門狗啟動后,計數器開始自動計數,經過一定時間,如果計數沒有被復位,計數器達到指定數值就會發出復位信號,很多設備包括CPU接到這個信號而復位重啟(俗稱“被狗咬”)。為了保證看門狗不發出復位信號,就需要在看門狗允許的時間間隔內對看門狗計數器清零(俗稱“喂狗”),計數器重新計數。如果系統正常並保證按時“喂狗”,那么就相安無事。一旦程序故障卡死,沒有“喂狗”,系統“被咬”復位。
嵌入式系統中主要可以分為兩種類型的看門狗:
Soc芯片內部集成WDT,這是Soc常用的設計。當然PC上可能用獨立的看門狗芯片。
軟件模擬看門狗,只要有個timer就可以模擬。
可以看到WDT在RGU里只是其中一個模塊,還有其他模塊可以產生復位信號,比如thermal,當溫度過高會觸發IRQ或直接復位,這也是一種硬件保護措施。復位信號經過Mixer后會分出2個信號,grst_b會復位芯片內部模塊,還有1個信號通過芯片管腳WATCHDOG復位外部芯片。有時為了debug,也可以測量WATCHDOG
pin腳(正常是高電平,有復位信號時低電平),看這次重啟是否是WDT等觸發的。
WDT_SWRST:寫該寄存器直接觸發復位
這里介紹一個dual mode功能,通常CPU發生卡死,我們需要知道卡死位置,分析原因,然后改進,而不希望直接復位。設計的方法是:WDT超時后先不發復位信號,而是先送出IRQ(如上面的框圖),WDT
IRQ通常配置成CPU的FIQ,如果CPU只是軟件卡死(內核死鎖,中斷過多等),會響應該FIQ,然后我們在FIQ里收集異常信息和CPU寄存器信息,然后再走正常的panic流程。重啟后我們就有信息分析此次WDT timeout的原因了。為了保證IRQ發出后,CPU不卡死,WDT再次計時,如果在panic流程又卡死了,就會由WDT發出復位信號復位。
簡單講:WDT超時先發出IRQ,WDT重新計時,如果再次超時WDT直接發出復位信號。可以看到WDT超時后分2個階段,第1階段發出IRQ用於收集異常信息,第2階段直接復位。通常我們在第1階段就完成異常信息收集並通過WDT_SWRST寄存器完成復位,而不用等到第2階段的。這種情況我們稱為HWT(Hardware
Watchdog
事實上任何事情總有意外,比如CPU因為PMIC提供的電壓低,或硬件故障等原因導致WDT超時發出的IRQ在CPU端得不到響應,WDT只能通過第2階段復位芯片。這種情況我們稱為HW reboot。HW reboot通常和硬件有較大關系。比如:
這里的wdtk-x就是對應CPU的喂狗進程。以上的一套設計可以保證各個CPU卡死都可以通過看門狗復位。
喂狗間隔是20s,而超時時間是30s,也就是說最長能容忍卡住的時間是10s(卡一小會還是可以的),超過這個時間,系統就會復位了。這里還有問題,由於喂狗進程之間沒有同步,是否有可能存在剛開始一起喂狗,之后漸漸出現先后呢?誤差肯定有的,但在任何30s時間里,喂狗進程都會喂1次狗的(因為喂狗間隔20s,每個CPU肯至少會喂1次狗)。而只要CPU卡死超過10s就復位了。
當只剩1個CPU時,並且該CPU要進入睡眠,此時kernel進入suspend狀態,WDT模塊當然也要關閉的,喚醒時再重啟開啟。那睡眠后的流程卡死怎么辦?那已超出WDT管轄的范圍了,需要用其他硬件保證,不在該文章討論范圍。
這里我們沒有用到kernel原生的WDT驅動,而是前面章節講的那套機制。具體代碼位置:
系統重啟(比如adb reboot)是通過WDT來完成復位的,在驅動里提供了()函數,這個函數通過寫WDT_SWRST寄存器完成軟件復位。基本上重啟的log都長這樣:
紅線以上是正常的log,寫完WDT_SWRST寄存器基本就復位了,但有些平台並不是立即生效,需要過幾百毫秒才完成復位,因此有可能看到紅線以下的log,這是正常的。
WDT啟動后就開始計時了,如果沒人喂狗就會觸發FIQ,然后在硬復位。因此需要提供了()函數,調用一次就將計數清0。
如果有驅動需要長時間關中斷運行,比如開機時的TP固件升級,就需要在里面添加喂狗動作,防止HWT。
有時為了調試,我們需要關閉看門狗,驅動里也提供了開啟/關閉函數:()。如果你要關閉WDT,可以直接在代碼里調用這個函數,那么WDT就永久關閉了。
WDT驅動比較簡單,基本都是控制WDT硬件寄存器達到所需效果。重點還在於WDK驅動(喂狗模塊)。
wdk是喂狗模塊,由於只有一個WDT,而需要保護多個CPU不卡死,根據前面設計的原理來設計wdk驅動,驅動具體位置:
各個CPU的()沒有同步,因此各自喂狗的時間不定,但只要喂狗間隔不超過30s就沒有事。
如果喂狗進程沒有及時喂狗,WDT就會超時觸發FIQ,而分析解決HWT的問題很依賴WDT FIQ能輸出什么信息。因此必須要熟悉WDT FIQ流程。32bit/64bit kernel處理FIQ方式不一樣,因此要分開講解。32bit kernel進階篇: coredump分析 => GNU tools)和vmlinux(必須是當時一起編譯出來的,如果后面有更新過,addr2line得出的結果可能錯誤)。比如:
全部轉換后就可以看到完整的調用棧了,有助於我們分析哪里卡住。
kernel發生異常或HWT,當時的系統是不穩定的,無法保證panic或上面的WDT FIQ流程能順利走完。因此我們使用fiq step標記走到了哪里,如上面的WDT
請結合代碼理清一遍流程,知道哪條log從哪個函數打印出來。如果需要加log調試,也知道該加在哪個位置。
學完WDT/WDK相關內容並不代表就可以獨立解決HWT/HW REBOOT的問題了,還需要很多kernel基礎知識支撐才行,因為任何模塊都不是獨立存在,在后面的案例分析就會有更深刻的體會了。
WDT/WDK驅動都在里面初始化,而WDT通常很早就完成初始化,WDK驅動較為靠后,這中間夾着其他驅動,如果其他驅動因為異常而卡較久時間,就有可能到30s都沒有喂狗而超時。那原因和解決方法有哪些呢?主要有:此時WDK已初始化好,而喂狗進程都是優先級最高的實時進程,其他任何進程都無法卡到喂狗進程。那誰可以卡到喂狗進程呢?換句話說,誰可以影響到系統調度?答案有:
WDT第1階段超時將觸發FIQ,如果CPU沒有響應或CPU響應了但沒有在第2階段超時時間內完成重啟,就好導致第2階段超時發出復位信號復位整個系統,生成的db類型就是HW reboot。
可以看到HW reboot定義很簡單,這樣導致了許多問題都導向了HW reboot,而處理問題的方法都不一樣。那有哪些異常會導致HW reboot?有如下:
基於HW reboot產生原因的復雜性。我們要學會如何區分呢?首先我們要了解哪些信息可以給我們參考。HW reboot沒走任何軟件流程,是直接復位的,無法知道CPU處於什么狀態,因為重啟后什么都丟了,不像kernel panic或HWT有詳盡的CPU寄存器和調用棧參考。其實HW reboot還是有些硬件模塊記錄了當時的情況。
如果wdt status為1或5且fiq step為0,那么這個就是HW reboot了。如何調試呢?好像這些信息無法幫我們找到問題。我們繼續看還有哪些調試信息。
上面記錄的地址是物理地址,需要查找datasheet看是哪個模塊的寄存器。然后檢查代碼,看看last pc所在函數是否有可能當時的power/clock被關閉了。
這些信息顯示了系統當時的狀態,比如是否進入sodi,當時dvfs檔位狀態等等。因為系統的復雜性,這些信息也算是提供一些參考吧。
還有一個重要的信息就是hot plug信息,每個CPU都有,不過CPU0比較特殊,因為不會發生hot plug(在新平台上,CPU0也會發生hot plug),因此有3個參數,其他CPU都只有1個參數:
最后的參考資料是last kmsg,看看是否存在異常。如果沒有看到異常,就看最后log停在哪里。猜測和哪個模塊相關,然后進一步調試。
有了以上的調試信息,我們就可以開始分析了。純粹的HW reboot一般和HW相關性較大。結合以上信息還不足以定位,通常還需要更多信息,比如復現次數等,大致分析如下:
僅1次復現,如果以上的調試信息看不出問題點就只能后面在關注是否再次發生了。
可復現的話,需要關注復現路徑,發生問題的時間點,結合多次復現的db統一分析,看是否有相似性。
隨機問題,基本導向HW故障,還需要進一步做信息調查:
非常重要,是HW穩定性的保證 | |
非常重要,保證memory工作穩定性 | |
正常: 可能是SW問題 異常: 可能是HW問題 |
驗證是否SMT焊接不良 |
查看波形是否有異常drop等現象 |
HW reboot是一個復雜的問題,需要積累經驗及擴大知識面。還要積累各種調試手段,比如DVFS開關,hotplug開關等,這樣調試起來才比較順手。
解決這類問題,首先要對kernel初始化流程有所了解。否則即使知道HWT,也無法找到問題點。
看到TSP初始化時間間隔是很長的(16s),檢查TSP驅動,發現在kernel啟動之后都會對TP做更新rom的動作,導致花費很長時間。
修正TP的驅動,僅在必要時才更新rom,並且更新時主動調用喂狗函數喂狗,避免超時。
我們來看一個lk執行時間長導致剛進kernel就HWT的例子。問題:項目剛開始,遇到板子下載軟件后重啟,幾片板子都是這樣,沒有裝外設(屏、TP都不裝)
設定wdt前,也表明lk執行時間過長,接近超時),我們看下是否是lk執行時間接近超時呢?
就可以看到哪個階段花的時間長了,如果不夠細致,還可以自己添加log分析。
tools)和vmlinux(必須是當時一起編譯出來的,如果后面有更新過,addr2line得出的結果可能錯誤)。比如:
hotplug時會用到,比如CPU1要下電,會將其他CPU進入stop_machine_cpu_stop()停止運行,同時關閉中斷,CPU1完成task遷移等動作再恢復。所以問題出在CPU0上。CPU0當時在handle_IRQ(),正在處理中斷,PC所在的位置不像是被卡住的樣子。可以排除死鎖可能。
175s往前推,CPU0倒是很活躍,那為何沒有喂狗呢?結合當前的調用棧,懷疑是IRQ過多引起,我們查看下IRQ信息:
us(1s)的時間內,236中斷觸發了228次。這個頻率從該中斷行為來看已經不正常了,如果中斷執行時間長點,CPU0就無法做任何事,只能不停得處理中斷了。這就是該題HWT的原因。
需要對kernel基本模塊有了解,比如stop machine機制。不一定是之前就了解,可以在解決這題的時候,詳細查找stop machine機制資料並結合代碼學習。
看到調用棧被中斷嵌套2次,明顯大量中斷引起無法正常調度,無法正常喂狗,引起HWT的,可以參考前面的章節《HWT-中斷頻繁觸發》。
AccessPermissionControl,管理各個HW模塊之前的訪問權限。造成訪問違例的原因有:
了解log是哪些模塊輸出的,以及為何輸出這些log。然后了解模塊的運作(知其然,知其所以然),這樣處理問題就得心應手了。
看到436行是一個while(1);的死循環,而且前面還關閉了中斷,直接導致系統無法調度卡死。為什么這里有個死循環呢?
這是一個功能,hang_detect進程用於同步SWT的,在android層中,如果發生了SWT,android會發生重啟,但如果在SWT過程中因為某些原因卡住,這直接導致手機變成磚頭,用戶體驗很差。因此我們在SWT每走幾步就和hang_detect進程交流,設定超時時間hang_detect_counter。如果SWT在某一步中卡住,底層的hang_detect就會超時,進而跑入上面的代碼里,等待HWT。簡單來講:避免SWT卡死,我們將SWT卡死轉化為HWT。
不過這個功能僅在USER版本有效。ENG版本還是卡死,但是系統還在運行,我們可以通過adb shell等接入android,可以查看系統的情況,比如是否是surfaceflinger卡死或EMMC卡死等。
所以呢,這個問題應該分析SWT卡住的原因,而不是去看HWT了。USER版本直接發生HWT,我們只有db可以看,信息有限,分析問題通常很困難。一般都需要切換為ENG版本復現,問題發生后保留現場,插上USB防止掉電。請Mediatek工程師直接在手機上分析(可以現場分析或遠程遙控分析)。
在L1版本之后拿掉了上面的死循環,改為直接調用了BUG(),也就是說,后面版本就將不到這類異常了。
問題:開機后界面黑屏,按電源鍵無作用,之后重啟
HWT一個常見的卡死是死鎖,正常情況下拿到鎖后盡量盡快執行完所需任務后就釋放掉鎖,以免持有過久帶來一系列問題。
這題是碰巧CPU2的調用棧明顯看出是它拿了鎖。如果調用棧無法反應誰持有鎖該怎么辦?還好ENG版本的spinlock有死鎖檢測機制,持有鎖被記錄owner和函數,如果獲取鎖失敗,並且在1s內一直無法獲取鎖,系統會直接印出該鎖被誰持有。有助於我們進一步調試。
從調用棧上看,是卡住power off流程里面,卡在某把鎖里,這把spinlock被誰拿走,目前不明顯,kernel log沒有印出拿鎖的進程調用棧,無從分析。
這里需要檢查下power off的代碼邏輯了。我們一路檢查,有一個地方發現:
通過檢查代碼發現,這個函數會對所有online cpu發送ipi中斷:IPI_CPU_STOP,收到中斷后,所有online cpu都會執行對應的ipi_cpu_stop函數。這個函數的作用是:
將CPU停下來,並且關閉了irq和fiq,沒人再打斷這個cpu了,除了security irq(會切換到tee)。
這個是否是引起這題的關鍵呢?是的,因為可能存在這樣的場景:
也就是說,誰調用了smp_send_stop函數,那么之后不應該拿任何鎖,否則可能造成死鎖。
除了關機流程會調用到smp_send_stop函數,重啟流程也會,machine_restart函數會調用到smp_send_stop函數,因此關機和重啟后面的流程不能拿任何一把鎖。
開關機和重啟壓力測試一直都是常規測試項,在添加代碼到關機、重啟流程時,要特別注意不要使用任何lock。
看到CPU0等待調度的進程已經有71個,更明顯看出CPU0卡住了。問題肯定出在CPU0調用棧上,看到
看到中斷是關閉的,所以無法調度,也就說明為何有71個進程等待調度但又沒有調度起來了。
很有可能算出負值,引起卡死呢。后面發現kernel已經修復了這個問題:
結合調度信息,調用棧,其他CPU調用棧,一一抽絲剝繭深入分析,最終得出答案。另外原生kernel也可能有問題,不要放棄任何懷疑。
熟悉使用trace32(可以查看trace32使用教程)、gdb查看全局變量,局部變量等信息,是分析問題的關鍵。
CPU4最后卡住的位置是smp_call_function_single(),這個函數的目的是通知其他CPU做完事后再返回,如果其他CPU沒有做完就一直等待。那CPU4是通知哪顆CPU呢?看參數是cpu=6,也就是說,如果CPU6沒有做完事,那么CPU4就一直等待。所以要看CPU6在干什么了。
可惜的是CPU6沒有調用棧,無法知道在干什么。其實還有一處信息可以知道CPU6在干什么,那就是last pc,這個信息記錄在SYS_REBOOT_REASON文件里,內容如下:
這題和《HWT-死鎖卡死》不一樣的地方是看不到出問題的CPU的調用棧,只能通過last pc知道當時的情況。
問題:長按HOME建彈出快捷方式,按返回鍵反應遲鈍,多操作幾次手機會異常重啟
HWT本質上就是調度問題,因此濫用實時進程或實時進程長時間占用CPU,有可能引起系統無法及時響應。
這個問題的解決核心就是要找到誰引起了rt throttle,找到了就好處理了。
所以往系統里添加實時進程或提升進程優先級為實時都要非常小心。
這題並不是因為卡住引起HWT,因此分析當前的各個CPU的調用棧是無任何意義的。應該先查清check bit和kick bit異常原因。
越來越多的手機支持TEE,因此也帶來了新的問題。以往沒有卡在TEE的案例,而后面則可能存在越來越多的類似TEE引起的問題要調試。
可以看到這個調用棧里2次調用了printk,直接導致了spinlock嵌套,那么printk是否有針對這個情況做處理?有的,代碼如下:
上面的代碼注釋已經寫的很清除了,printk已經考慮到存在spinlock嵌套的情況(不可避免),因此做了規避處理,直接調用zap_locks()函數,這個函數如下:
這個鎖也存在嵌套情況,那么就需要好好檢查調用棧了。先看看這把鎖是什么,看到try_to_wake_up()函數代碼:
客戶測試一周后反饋說,打開了這些宏沒辦法復現了,就只能讓他們重新用原來的軟件再抓取log,希望在log中有些完整的調用棧,幾天后客戶復現了現象。
上面這段文字很明顯,是說被wake up的進程正在被某個CPU schedule出去,再用trace32看下哪個進程正在運行kswapd0進程,可以看出是CPU1進程,從CPU1的backtrace也能看出。
需要理清各個CPU對應spin lock的關系,梳理代碼流程就可以明白死鎖之間的關系。
80%左右,user版本復現率高於eng版本 |
有的機器不復現,有的機器概率很高 |
所有patch是否打上 |
是否只有antutu發生問題 |
由於ETT沒有跑過,因此請跑完ETT在確認問題,發現問題依舊。另外vproc確認也不像是有問題的樣子。
另外一條線索,PCB有2版,第1版容易復現,第2版不容易復現,但可惜也沒有排查出問題。
buck電路之后,檢查到buck電感不符合spec,導致buck輸出電流不夠。則會引起CPU無法正常工作,PC/SP/FP等寄存器亂跑。
從db/log分析到最后問題找到,中間的道路是崎嶇的。我們要盡量抓住任何可能的疑點(盡量發散),然后一一排查,最后找到問題點。而這過程中使用的調試手段,可能各個平台都不一樣,但分析思路都差不多。
另外還有一點,HW reboot必須可復現才行,否則無法采取任何調試措施。比如這題如果僅復現1次,是根本無法找到問題點的。
|
在编写STM32程序时,经常会需要在中断里进行延时,有的人会使用变量递减的方式,但是需要进行精确延时的情况,就必须要用到定时器,而内核中的滴答定时器SysTick自然就成了不二之选,也就是常用的delay_ms/delay_us函数
但是,往往在中断使用delay函数,特别是在写大工程时,却经常遇到各种奇奇怪怪的bug,比如显示屏异常,串口数据异常,WIFI蓝牙异常等等,只要是涉及到通讯且在通讯中使用了delay延时的设备,均有可能出现异常,最严重的当然就是死机
其实网上也有许许多多的人在咨询这个问题
但是得到的回答无一都是因为中断中延时占了资源,中断中不能停留太长时间等待,所以中断中一定不能使用delay
但是我给出的答案是:中断中可以用delay函数,只需要修改delay函数!
举一个最简单的例子,在外部中断中检测按键按下时使用delay函数消抖
中断程序如下(此处使用的是STM32F0系列,其它系列同理)
不懂的同学可以先看下这篇博客:
程序逻辑中,理论上是主程序串口一直发送运行信息,当按键按下时,LED电平翻转
但是,实际上跑在单片机里的现象是,一旦按下按键,电脑上的串口就再也收不到信息,而按键中断反转LED的功能却是正常的
主程序几乎99%的时间都是在delay函数中的while语句,等待滴答定时器数到0。而在等待的过程中,中断触发了,中断里进入了delay函数,正常执行后,退出时将systick关闭了,使得退出中断后在主程序的delay函数里卡死在while里(systick已经关闭了,不再向下记数)
其实,这个问题从本质上讲无疑就是因为systick定时器资源只有一个,而却被两个进程同时调用产生的冲突
为什么是同时调用呢,因为有了中断,如果不使用中断,那主程序中该函数只会被单次调用
所以在中断中不能使用delay?非也
我们从底层看delay函数,在主程序中跑delay时被中断再进入delay函数时会产生什么冲突呢
1是主程序中的delay计数值会被改变为中断中延时的数值,2是中断后定时器会被关掉
那么我们将delay函数修改如下
加入了delaying_times变量用来防止定时器被关闭
但这并不完美,如果在中断里,程序如果在执行其它内容时消耗了一些时间后再进入delay函数,那么就有可能出现一种情况,在进入delay函数时,计数器已经溢出,也就是CTRL的第16位已经变为0,那么此时记录下来的last_systick_val是重装载后的计数器数值(定时器的计数器是自动重装载计数器)
于是我又加入了一个变量
上面的情况下是只考虑到只进入了一次中断的
那如果我在执行中断中的delay函数时,又发生了一次甚至n次的更高优先级中断并且进入delay函数了呢
这就涉及到一个递归的概念了,我直接摆出代码吧
代码部分需要读者们自己好好理解理解,作者最终的这个程序理论上是可行的,但没有经过大数据的测验,自测在一次中断的情况下是完全无问题的,如果有什么疑问欢迎在评论区留言,如果觉得好用就给该文章点个赞罢~