記一次 spinor flash 讀速度優化

背景

某個項目使用的介質是 spinor, 其 bootloader 須要從 flash 中加載 oshtml

啓動速度是一個關鍵指標,須要深刻優化。其餘部分的優化暫且略過,此篇主要記錄對 nor 讀速度的優化過程。算法

瞭解現狀

接到啓動速度優化的任務以後, 首先是瞭解狀況。測試

當前的 bootloader 實測讀速度只有約 4M/s優化

爲了加快速度已經嘗試過url

  • spinor 驅動改成使用四線讀命令讀取數據。速度並無明顯改善。待確認改動是否生效。
  • spinor 驅動改成使用 dma 搬運數據。還沒有修改爲功。

計算上限

既然是要深刻優化,那知道終點在哪仍是頗有必要的。code

整個讀取過程,數據主要是從 spinor 到達 socspi 控制器,再由 cpudma 搬運到 dram 中的目標位置。htm

spinor --> spi控制器 --> cpu/dma --> dram

先來考慮第一段的速度,這裏比較好計算。針對當前的 socflash 的組合,從規格書可獲得最高的 spi 時鐘頻率爲 100M = 100 * 10^6,且讀數據可以使用 4 線讀取,即 socflash 之間有 4 根數據線在並行傳輸數據。那麼簡單算下 100 * 10^6 * 4bit = 400 * 10^6 bit/s = 47.68 MB/s , 可知極限速度爲 47.68 MB/sblog

固然較真一點,發送讀命令給 flash 也須要時間,讓咱們來算下。get

通常一個讀命令須要 5 bytes, 即 cmd + addr[3] + dummy,因此實際的極限速度要考慮每發一次讀命令後讀取多少數據。讀命令是單線傳輸的,數據是四線傳輸。假設發一次命令讀 nbytes 數據,則命令和數據所佔時間的比例爲 5:(n/4), 那麼實際用於傳輸數據的 clk 就只有 (n/4) / (5 + n/4) * 100M4 線傳輸的狀況下每一個 clk 可傳輸 4bit,從 bit 換算成 byte 再除以 8,因而速度公式爲 (n/4) / (5 + n/4) * 100M * 4 / 8 , 這裏要注意對於大小 1MB = 1024*1024 Byte, 對於時鐘 100M = 10^6cmd

代入一些具體數據可得

每次讀取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

能夠看出,若是每次讀取數據量較小,那麼發送讀命令消耗的時間就不可忽視。每次讀取的數據量越大,則讀命令對速度形成的總體影響就越小。

後面的部分理論速度暫時沒有很明確的計算方式,那暫時先知道完整的數據是這麼流動的就能夠了。

確認瓶頸

看了下驅動打印出來的確實是 80Mclk4 線的讀命令,雖然還沒調到最高時鐘 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 clk80M 提升到 100M,能夠發現總體讀速度沒有變化,這也能夠佐證當前瓶頸仍然不在 nor 的讀取速度上面。

測個波形看看,果真 clk 線上仍是間歇性的,不過空閒時間比以前少了不少。

dma 的速度能不能改進呢? 這就涉及到具體的芯片了,須要深究下 dma 控制器和 spi 控制器的配置。

優化 dmaspi 控制器的配置後,dmaspi 控制器取數據的速度,終於超過了 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

相關文章
相關標籤/搜索