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