背景
某個專案使用的介質是 spinor
, 其 bootloader
需要從 flash
中載入 os
。
啟動速度是一個關鍵指標,需要深入優化。其他部分的優化暫且略過,此篇主要記錄對 nor
讀速度的優化過程。
瞭解現狀
接到啟動速度優化的任務之後, 首先是瞭解情況。
當前的 bootloader
實測讀速度只有約 4M/s
。
為了加快速度已經嘗試過
spinor
驅動改為使用四線讀命令讀取資料。速度並沒有明顯改善。待確認改動是否生效。spinor
驅動改為使用dma
搬運資料。尚未修改成功。
計算上限
既然是要深入優化,那知道終點在哪還是很有必要的。
整個讀取過程,資料主要是從 spinor
到達 soc
的 spi
控制器,再由 cpu
或 dma
搬運到 dram
中的目標位置。
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
。
當然較真一點,傳送讀命令給 flash
也需要時間,讓我們來算下。
一般一個讀命令需要 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
。
代入一些具體資料可得
每次讀取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
來搬運資料,正常讀取過程中,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
暫存器竟然這麼耗時。
改用 DMA
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: https://www.cnblogs.com/zqb-all/p/12824908.html
公眾號:https://sourl.cn/4X3jE7