Raspberry pico 是一款雙核cortex-m0的處理器,在RT-Thread提供的bsp中目前是默認采用libcpu/arm/cortex-m0,其并沒有對多核進行支持。在Coremark的測試中pico的性能很一般,只用一個核心實在是太浪費了,所以下面用一種不太優(yōu)雅的方式基本實現(xiàn)Pico的SMP,簡單測試沒有問題,當然由于萌新對于內(nèi)核的理解程度有限,總是可能存在一些問題,不過總算跑起來了不是~
移植SMP
在這之前,官方的文件完全沒有支持Cortex-M的多核。
首先是幾個基本的函數(shù)對接:
rt_hw_cpu_id
:最首先需要實現(xiàn)的一個也是最容易的實現(xiàn)的一個,直接訪問pico的sio
就可以
rt_hw_interrupt_disable/enable
: 在SMP框架當中,關(guān)閉中斷不只是屏蔽中斷,其還要通過spinlock來保證對資源訪問的互斥,對于此rtt在rthw
通過宏定義將其替換,并且重新命名原來的中斷控制函數(shù)
rt_hw_spin_lock_xxx
:自旋鎖,用于多核之間的資源保護,在rp2040中芯片提供硬件spinlock使用,這一部分同樣使用pico-sdk的api即可,選擇unsafe
版本
rt_hw_secondary_cpu_up
:在主CPU啟動后,運行調(diào)度器,調(diào)度器會調(diào)用main線程運行,main線程運行前會首先調(diào)用該api來啟動第二個核心。Rp2040兩個核心其實是上電以后同時啟動的,CPU-1會在bootrom中被攔截下來進入等待狀態(tài),我們可以通過sio的fifo來喚醒第二個核心,pico-sdk中提供了api,可以直接指定CPU-1喚醒后執(zhí)行的函數(shù)。在喚醒過程中同時使能兩個CPU的SIO中斷,用來進行IPI_Handler.
在需要調(diào)度的時候,CPU之間可能會互相通知讓其進行調(diào)度,該部分通過rt_hw_ipi_send
和rt_hw_ipi_handler
對接,
上面對接的函數(shù)都比較基礎(chǔ),其次是對接上下文的匯編代碼部分,這一部分就不是特別順利了。簡單梳理一下Cortex-M的調(diào)度流程,rt_schedule
獲取最高優(yōu)先級的任務(wù)然后使能PendSV
中斷并在全局變量中保存調(diào)度信息,最后在完成高優(yōu)先級中斷(或者直接進行PendSV
)后進行實際的上下文切換,在SMP中基本同理,但是由于RT-Thread的SMP是針對Cortex-A提供的,這里出現(xiàn)了一些問題。
首先在調(diào)度中必須關(guān)注一個函數(shù),rt_cpus_lock_status_restore(thread)
,其將要調(diào)度的線程綁定到當前的cpu上,調(diào)用該函數(shù)的位置是一個關(guān)鍵問題
在Cortex-A中其在rt_hw_context_switch
中被調(diào)用,這對于Cortex-A是可行的,因為在非中斷情況下A核會直接進行線程切換而不需要PendSV,但是對于Cortex-M核心放在這個位置會存在下面一個問題:PendSV是中斷,所以需要使能中斷才能運行,因此在rt_hw_context_switch
后立馬就有一個rt_hw_interrupt_enable
,如果M核工作在非SMP框架下這是沒有問題的,但是在SMP框架下當前的線程已經(jīng)變了,而rt_hw_interrupt_enable
是同當前線程綁定的,所以這里會導(dǎo)致CPU的scheduler_lock_nest,cpus_lock_nest
錯亂,從而導(dǎo)致調(diào)度器不能正常工作
基于上面的描述,我考慮把rt_cpus_lock_status_restore
放在PendSV
中進行調(diào)用,這樣就可以保證scheduler_lock_nest
工作的正確性,但是導(dǎo)致一個更大的問題!!!在rt_schedule
函數(shù)中,如果中斷還沒有使能的情況下重復(fù)調(diào)用rt_schedule
(systick中多層中斷)會導(dǎo)致已經(jīng)被標記為RUNNING
的線程無法正常被加入到就緒列表中。因為在上一次的rt_schedule
中線程已經(jīng)被移除了,其等待在PendSV
中綁定到當前CPU的時候rt_schedule
再次到來,其應(yīng)該被重新加入到就緒列表(如果優(yōu)先級低的話),但是schudler
是基于當前CPU上的線程來管理的,由于之前被調(diào)度的線程當前還沒有綁定,所以線程變成游離狀態(tài)而無法被調(diào)度,就會出現(xiàn)下面的情況:
所以rt_cpus_lock_status_restore(thread)
只能在rt_hw_context_switch
中被調(diào)用,但這種情況下我們需要處理scheduler_nest
和cpus_lock_nest
錯亂的問題,由于SMP框架將nest
綁定到線程上,但實際上鎖針對的還是CPU,我也認為將太綁定到CPU上更合適,為了不修改內(nèi)核源碼的情況下實現(xiàn),我在rt_hw_context_switch
中將當前cpu線程的nest
綁定到需要調(diào)度的線程上,這樣就等價于把nest
綁定到CPU上,此時就可以正常工作了。
解決上述問題后知剩下最后一個問題,我們前文的討論都是基于非中斷情況下的,對于Cortex-M而言中斷中的調(diào)度和非中斷中的調(diào)度是一致的,都是基于PendSV
實現(xiàn)的,所以我們rt_hw_context_switch,rt_hw_context_interrupt_switch
用一套一樣的代碼就可以,但是在SMP框架中這兩個部分具有兩個調(diào)度函數(shù),在中斷中調(diào)用rt_schedule
,SMP框架會直接跳過當前調(diào)度并且給當前CPU打上中斷調(diào)度標記,最后在離開中斷的時候調(diào)用rt_scheduler_do_irq_switch(void *context)
來實現(xiàn),對于Cortex-A的中斷結(jié)構(gòu)來說這是沒有問題的,只要保證switch能夠在本次調(diào)度過程中直接切換就行,但是對于Cortex-M這樣就不太合適,我們可以把NVIC弄成統(tǒng)一IRQ的樣子,但是我覺得直接廢棄rt_scheduler_do_irq_switch
更加合適。
為了使得調(diào)度器不知道我們在中斷狀態(tài),我把rt_interrupt_enter/leave
注釋掉了(應(yīng)該在涉及內(nèi)核調(diào)度的中斷中全部采用這種辦法),這樣irq_nest
就一直是0
,調(diào)度器也不會去調(diào)用do_irq了,其實我們不用這個處理方法也能夠工作的,但是中斷中就沒法調(diào)度了,實時性也沒法保障。按照我的理解在Cortex-M中這樣的處理并不會有太大的問題,但是總不太好是吧~
最后基于上面全部的修改,RP2040的SMP能夠正常工作,小燈能夠按照正常閃爍。
最后
我對于RT-Thread的理解還很有限,萌新,有很多問題我可能預(yù)料不到,這樣的實現(xiàn)方式我也覺得不太優(yōu)雅,不過總算是跑起來了(肝了兩天還是有點累emmm)。后續(xù)會優(yōu)化整理并且再經(jīng)過一段時間的測試,或許能夠喜提自己的第一個RT-Thread PR ~
最后是關(guān)于SMP,我不明白為什么把nest
綁定到thread而不是cpu上,因為總還是在鎖cpu,其次rt-thread的smp似乎是專門給A核設(shè)計,目前的多核MCU也有蠻多,希望可以提供一些相關(guān)支持。
Attention please!!(2022-5-12): 評論區(qū)有提供bsp的壓縮包
只是一個可以玩玩的狀態(tài),目前可以確定的是存在和調(diào)度相關(guān)的bug會導(dǎo)致系統(tǒng)崩潰(目前測試在shell反復(fù)調(diào)用list_thread可能崩潰,可能和kservice有關(guān))。
另外如果線程在調(diào)度器啟動前被創(chuàng)建,即INIT_BORAD_EXPORT方式創(chuàng)建則一切正常(list_thread不會崩潰),在main中創(chuàng)建就可能出現(xiàn)崩潰,希望各位大佬可以給點調(diào)試思路。
由于最近事情很多比較忙綠,沒有時間調(diào)試和閱讀代碼,但會在后續(xù)一段時間(六月-七月)調(diào)試完善smp調(diào)度并在后續(xù)添加完善pico的驅(qū)動支持,希望感興趣的同學(xué)一起交流哈。(也在看看rtthread v5.0的消息哈)
來源:RTThread物聯(lián)網(wǎng)操作系統(tǒng)
-
SMP
+關(guān)注
關(guān)注
0文章
71瀏覽量
19631
發(fā)布評論請先 登錄
相關(guān)推薦
評論