某個項目使用的介質是 spinor
, 其 bootloader
須要從 flash
中加載 os
。html
啓動速度是一個關鍵指標,須要深刻優化。其餘部分的優化暫且略過,此篇主要記錄對 nor
讀速度的優化過程。算法
接到啓動速度優化的任務以後, 首先是瞭解狀況。測試
當前的 bootloader
實測讀速度只有約 4M/s
。優化
爲了加快速度已經嘗試過url
spinor
驅動改成使用四線讀命令讀取數據。速度並無明顯改善。待確認改動是否生效。spinor
驅動改成使用 dma
搬運數據。還沒有修改爲功。既然是要深刻優化,那知道終點在哪仍是頗有必要的。code
整個讀取過程,數據主要是從 spinor
到達 soc
的 spi
控制器,再由 cpu
或 dma
搬運到 dram
中的目標位置。htm
spinor --> spi控制器 --> cpu/dma --> dram
先來考慮第一段的速度,這裏比較好計算。針對當前的 soc
和 flash
的組合,從規格書可獲得最高的 spi
時鐘頻率爲 100M = 100 * 10^6
,且讀數據可以使用 4
線讀取,即 soc
和 flash
之間有 4
根數據線在並行傳輸數據。那麼簡單算下 100 * 10^6 * 4bit = 400 * 10^6 bit/s = 47.68 MB/s
, 可知極限速度爲 47.68 MB/s
。blog
固然較真一點,發送讀命令給 flash
也須要時間,讓咱們來算下。get
通常一個讀命令須要 5 bytes
, 即 cmd + addr[3] + dummy
,因此實際的極限速度要考慮每發一次讀命令後讀取多少數據。讀命令是單線傳輸的,數據是四線傳輸。假設發一次命令讀 nbytes
數據,則命令和數據所佔時間的比例爲 5:(n/4)
, 那麼實際用於傳輸數據的 clk
就只有 (n/4) / (5 + n/4) * 100M
。4
線傳輸的狀況下每一個 clk
可傳輸 4bit
,從 bit
換算成 byte
再除以 8
,因而速度公式爲 (n/4) / (5 + n/4) * 100M * 4 / 8
, 這裏要注意對於大小 1MB = 1024*1024 Byte
, 對於時鐘 100M = 10^6
。cmd
代入一些具體數據可得
每次讀取bytes | 讀速度 |
---|---|
64 | 36.33 MB/s |
256 | 44.23 MB/s |
1024 | 46.77 MB/s |
64k | 47.67 MB/s |
1M | 47.68 MB/s |
能夠看出,若是每次讀取數據量較小,那麼發送讀命令消耗的時間就不可忽視。每次讀取的數據量越大,則讀命令對速度形成的總體影響就越小。
後面的部分理論速度暫時沒有很明確的計算方式,那暫時先知道完整的數據是這麼流動的就能夠了。
看了下驅動打印出來的確實是 80M
的 clk
和 4
線的讀命令,雖然還沒調到最高時鐘 100M
,但當前 4M/s
也徹底對不上,究竟是誰出了問題呢?時鐘不對?四線沒配置成功?驅動存在 bug
? nor flash
物料的問題?
思考一下,要確認究竟是誰的鍋,最簡單明瞭的方式仍是量下波形,無論軟件驅動上怎麼寫,控制器的寄存器怎麼配,最終仍是得反映在波形上纔是最真實的傳輸效果。接上示波器或邏輯分析儀,看看 spi
線上的狀況,是誰的問題就一目瞭然了。
首先量一下 spi clk
線,能夠發現讀數據的過程當中,clk
的信號不是連續的,在有信號時其頻率是正常的,但大部分時間 clk
線上倒是沒有信號的。再量量數據線,能夠確認到確實使用了 4
線讀。
問題很明顯,spi
控制器是在間歇性讀數據,因此雖然讀 nor
的時候是 80M
的時鐘頻率進行讀取,但把 spi
的空閒時間計算進去,均攤下來的總的速度就只有 4M/s
了。
那爲何 spi
控制器會間歇性讀取而不是一直在讀取呢? 這就涉及到剛剛所說的數據流了,spi
控制器自己的 fifo
是有限的,當從 spinor
讀取的數據填滿 fifo
以後,就必須等着 cpu/dma
把數據取走,騰出 fifo
空間來,才能繼續發送指令從 nor
取數據。那麼這段空閒時間,應該就是在等 cpu/dma
取數據了。
有了懷疑方向,那就得看下代碼了。目前驅動中使用的是 cpu
來搬運數據,正常讀取過程當中,cpu
在執行如下代碼
while 待讀取數據計數值大於0 if (查詢spi寄存器,判斷到fifo中存在數據) 讀取spi fifo寄存器數據,寫到dram的buffer中 待讀取數據計數值減1
若是是這裏成爲了瓶頸,那就有兩個地方比較有嫌疑,一是讀取 spi
寄存器,而是寫 dram
。
作點實驗確認下
實驗一,嘗試下把寫 dram
的操做去掉,使用以下操做,並由讀取先後的 log
時間戳來判斷耗時。
while 待讀取數據計數值大於0 if (查詢spi寄存器,判斷到fifo中存在數據) 讀取spi fifo寄存器數據 待讀取數據計數值減1
從新測試下,發現速度沒有明顯變化。
實驗二,嘗試下減小讀 spi
寄存器的操做
while 待讀取數據計數值大於0 讀取spi fifo寄存器數據 待讀取數據計數值減1
從新測試下,發現讀速度翻倍了,達到了 8M/s
,看來果真是這裏成爲了瓶頸。沒想到 cpu
讀個 spi
寄存器居然這麼耗時。
cpu
太慢,那就期望 dma
了。
先來解決 dma
驅動異常問題,瞭解下狀況,原來這個 dma
驅動的支持是從另外一個分支上移植過來的,本來工做正常,到了這個分支就翻車了。
這就是一個找不一樣的問題了,先比較下兩個分支的差別,再將可疑的地方 checkout/cherry-pick
到另外一個分支來驗證。很快找到了關鍵因素 dcache
。
這個分支上是默認打開了 dcache
的,可見舊文 記一個bootloader的cache問題,而這就致使了 dma
驅動工做異常。
簡單點,關掉 dcache
試試,果真 dma
就正常了。測下速度,達到了 21M/s
。
再測試下不關 dcache
,在配置了 dma
描述符以後,刷一次 cache
再啓動 dma
傳輸,也是正常的了。
21M/s
的速度,看來瓶頸仍是在 dma
這裏。此時能夠嘗試將 spi clk
從 80M
提升到 100M
,能夠發現總體讀速度沒有變化,這也能夠佐證當前瓶頸仍然不在 nor
的讀取速度上面。
測個波形看看,果真 clk
線上仍是間歇性的,不過空閒時間比以前少了不少。
dma
的速度能不能改進呢? 這就涉及到具體的芯片了,須要深究下 dma
控制器和 spi
控制器的配置。
優化 dma
和 spi
控制器的配置後,dma
從 spi
控制器取數據的速度,終於超過了 80M
時鐘下的 spinor
讀取速度,將 spi clk
修改成 100M
,測得讀速度約 36M/s
。
前面說到,發送讀命令給 flash
也須要時間,在 os
中受限於 buffer
大小等,可能會限制每次讀取和處理的數據量,但對於 bootloader
來講則徹底能夠一口氣將所需的數據讀入,無需分段。
另外可查看驅動中是否有無用的清空 buffer
之類的操做,一併優化掉。
驅動邏輯和寄存器上沒法優化以後,還想提速,那麼能夠試試提升時鐘。
提升 cpu
時鐘和 dma
時鐘,提升後測得速度約 47M/s
,基本就是理論極限了。
但具體可否提升時鐘仍是得謹慎評估,單個板子能夠正常運行,不意味可以穩定量產。
在讀取速度沒法進一步優化的狀況下,要提升啓動速度,那就得減小讀入的數據量了。
能夠評估下使用壓縮的鏡像來減小讀入的數據量,只要多出的解壓時間不長於節省掉的讀取 flash
時間,那就是划算的。
是否壓縮,選擇哪一種壓縮算法,就跟 io
速度,cpu
解壓速度直接相關了,最好通過實測確認,綜合啓動速度和 flash
佔用來選擇。
若是是帶壓縮的鏡像,那啓動速度就是讀取時間+解壓時間。
讀取時主要是 io
操做,解壓則主要是 cpu
操做。那麼是否有可能實現邊讀取邊解壓,使得總的啓動時間進一步縮短呢?這個待研究
blog: http://www.javashuo.com/article/p-hyjqxzwz-em.html
公衆號:https://sourl.cn/4X3jE7