您當前的位置:首頁 > 繪畫

我在龍芯3A4000上實現睿頻!

作者:由 FlyGoat 發表于 繪畫時間:2020-02-06

最開始拿到龍芯3A4000 PMON Bootloader程式碼的時候,我是震驚的,一眼看過去又全部是彙編,從DDR訓練到HT和7A初始化,屬實帶工程。真的很佩服龍芯那些工程師,是我用匯編寫這麼大個工程早就寫出114514個Bug了。不過裡面有段彙編出乎我意料,名叫ls132_core。S,結合之前在Lemote看到的資料,推測這就是3A4000內建的GS132管理核的程式碼。一波通讀,說實話看不懂,大致明白了主要實現了DVFS功能,執行時重設PLL和FreqScale暫存器進行調頻,並透過I2C指揮PMIC進行調壓,這不和AMD SMU在做差不多的事麼?之前各種折騰SMU,感嘆於其功能之強大,但是看著這段彙編,實在和強大扯不上半點關係,於是就萌生了重構管理核中程式碼的想法,我稱之為Project Miku。這個管理核,我們稱之為SMC,目前專案已經初具雛形,可以見GitHub。

我在龍芯3A4000上實現睿頻!

首先,頂層設計是必不可少的,參見AMD SMU,其功能包括與主核軟體通訊,控制Sensor Hub,控制HWMon(Thermal),控制DVFS,控制片上匯流排QoS。那麼龍芯的SMC可以達成些哪些功能呢?分析自PMON的彙編程式碼(辣眼睛!),GS132核可以訪問片上IORING設施,包括MISC和HT,也就是可以訪問confbus下的所有暫存器和部分7A暫存器(GS132 只有32Bit訪存能力,個人推測對於64Bit地址可以透過地址視窗的形式重對映訪問,根據從龍芯開源計劃拿到的GS132 RTL程式碼,GS132沒有實現MMU。)。總而言之GS132有能力控制片上I2C用於調壓,UART用於Debug,7A的MISCBUS用於風扇和其他電源管理,以及最重要的時鐘控制。而“工欲成其事,必先利其器”,要在小核上舒舒服服的寫業務邏輯,就必須有成熟的RTOS框架。左顧右盼之下,我選定了國產實時作業系統RT-Thread,一來之前在1C上玩過,大致熟悉其結構,二來採用了Apache開源協議,不會斷絕將來商業應用的可能。

開始我還沒拿到3A4000的機器,就只能做一些外圍工作了,主要是寫寫GS132的RT-Thread BSP。哪知道一上手發現RTT的MIPS框架是一團糟,龍芯君正各自有自己的實現,並且差異很小的晶片也Fork出了多份核心libcpu程式碼,不能忍,遂先進行了一波整理與合併作業,並且向Upstream提交,這樣以後可以很方便的加入新的晶片支援,並且為了適應GS132這種環境,特別加入了CP0。Ebase支援,允許在Link Script中任意放置中斷向量(當然對齊要求還是有的),也增加了可以在QEMU模擬器中執行的bsp以驗證其可靠性。後來也很方便的寫出了GS132的BSP。

拿到機器之後,簡單測試了一下GS132,各種彙編操作串列埠列印OK,確定了他確確實實存在在3A4000裡,就開始上Port的RT-Thread,哪知道現實給了我當頭一棒,根本起不來!PC指向RT-Thread的頭部之後串列埠就徹底偃旗息鼓了。一番推論,我提出了個我自己都不敢相信的可能性,這玩意可能根本沒有記憶體!之前一直覺得和採用GS132的1C101一樣,這玩意片內應該有SRAM。如果沒有記憶體,沒有棧堆,那就沒有用任何作業系統的可能性了。於是寫了一堆彙編做實驗,發現他應該是有記憶體的,並且記憶體應該是直接落到DDR控制器上,和主核共享。那為啥不能用呢?繼續寫彙編做Relocation實驗,最後得出的結論是GS132可以訪存DDR部分主存,但是無法從主存取指,好一個奇葩設計,估計是內部CrossBAR上對取指請求做出了某種限制,無奈之下只能採取常在微控制器上用的Address Layout,將程式碼段與只讀資料段放在ROM中,bss data stack heap放在RAM中,RAM中的資料放在作業系統為韌體保留的地址,並且小心的避開了韌體已經使用的部分。

ok,作業系統起來了,下一個要關心的就是和主核軟體的通訊了。根據彙編版本程式碼,0x1fe0051c處有個Mailbox暫存器,大核和小核都能訪問,說是Mailbox,其實就是個ScratchPad,存進去的東西兩邊都能讀寫而已。大核寫完中斷GS132讓GS132上的軟體來讀取或者直接由小核輪詢,處理完成後再由大核輪詢處理結果。看了龍芯之前的設計,基本也是這個思路,就是有幾個弊端,一是命令的硬體操作也在命令處理部分完成,導致單條命令的處理時間不確定,可能很長以至於核心輪詢超時。二是缺乏可靠的功能探測與版本表達機制,不利於之後擴充套件。眾所周知,在頂層設計欠下的債,以後都要還的。不破不立,因此我拋開了已有設計,參考AMD SMU的體制設計了一套”Service Request”協議進行通訊。引入了Feature Flag,並且確保每個請求都會以最快速度處理返回。

在通訊機制確立之後,作為這種機制的Proof of Conecpt,風扇控制功能和感測器功能都被我很快地完成了,剩下最具挑戰性的DVFS/Boost功能。

對於原先的DVFS實現,我是非常不滿意的,主要問題是 1、動PLL的同時破壞了Stable Conter時鐘,使得理應當全域性穩定的時鐘不再穩定,作業系統只能使用不精確的HPET時鐘。2、一切聽從核心,缺乏對過熱等情況的保護。3、在核心中使用“雙Freq Table制”,與現有CPUFreq框架契合度不高。因此,我提出了第一種設計。始終使PLL置於最高頻率,僅透過FreqScale暫存器來調節當前頻率並輔以電壓調節。然而先期的測試證明我還是Too Young,在高PLL頻率的情況下,僅僅透過FreqScale把頻率拉回來並不能達成降壓的目的,一降壓就宕機。原來,FreqScale和PLL的原理不一樣,他的7/8分頻僅僅是每8個邊沿中摘掉一個邊沿,單個上升沿/下降沿的時間並沒有變化。也就是說,除非Scale小於4/8,片內的時序並不會隨著Scale的變化而放寬,所以對電壓的需求也沒有放寬,僅僅是為了達成減小翻轉率以降低功耗的目的。此路不通,看來針對睿頻重設PLL是必須的。但是,我又希望在睿頻的同時Stable Counter的頻率可以保持不變。還好,Stable Counter也有自己的Scale暫存器,也就是說,透過巧妙的計算,是有辦法讓Stable Counter的頻率保持一致的。比如,睿頻頻率是2000MHz,基礎頻率是1750MHz,那麼我們可以在睿頻狀態下對Stable Counter進行7/8分頻,使Stable Counter保持在1750MHz的頻率。進一步實驗證明了這種方案的可行性。於是到了實現環節,這裡遇到了一個問題,小核操作I2C指揮PMIC一致調不通,我也缺乏相應的裝置進行邏輯分析,於是先擱置了這部分。直到後來做核心中超頻的時候從PMON裡參考來了邏輯才調通。

然後寫出了基礎邏輯,PLL分為兩個狀態,正常狀態和Boost狀態,每個狀態中再根據核心的需求進行FreqScale分頻。然而做出來的原型又遇到了問題,降頻會宕機。先是解決了一個邏輯順序問題,降頻時應該先降頻在調壓防止晶片瞬間處於高頻低電壓態。但是問題還是沒有徹底解決,問題出在在透過FreqScale降頻的時候竟然會宕機?一番分析,發現宕機一般出現在Scale小於2的狀況。雖然可以簡單的過濾掉Scale < 2的狀況,但是不知道原因始終是個隱患。又是一波東問西問,得知原因是在核頻率過低時,可能不能及時返回SCache的一致性請求導致片內超時死鎖。龍芯給出的解決方案是調節晶片配置暫存器,放寬超時時間(但是會影響整體效能)。我思考了一下這個問題,覺得還有個解決方案,就是當晶片整體負載較低時,對Node(包含SCache)也進行分頻,大家都慢就不會超時了。於是在演算法中加入了Idle擋位,當所有核負載都小於2/1 Normal時,將Node也進行4/8分頻,否則不允許單個核的FreqScale低於4/8分頻。

針對“雙表制”的問題,我參考了AMD的Shadow P-State設計,即暴露給核心的頻率表並不是真實頻率表,只是一層影子表,小核根據Shadow Frequency選定合適的PLL和FreqScale組合,以更好的適配cpufreq機制,並且減少對PLL的動作,畢竟操作PLL和電壓還是比較耗時的。

最後就是一些調優,加入溫度保護,調節單個擋位上維持的時間避免頻繁降頻,加入SRAM暫存器配置以適配低電壓情況,進一步降低Idle擋位的電壓以進一步減小待機功耗。並且成功透過加壓的方法,將Boost擋提高到了2200MHz,獲得了可觀的效能提升。

現在SMC的各功能在我這裡測試表現良好,期望可以早日在裝置中實裝投入使用: -)。

GitHub專案:

個人部落格原文:

標簽: GS132  PLL  FreqScale  龍芯  暫存器