記一次 spinor flash 讀速度優化

all發表於2020-05-10

背景

某個專案使用的介質是 spinor, 其 bootloader 需要從 flash 中載入 os

啟動速度是一個關鍵指標,需要深入優化。其他部分的優化暫且略過,此篇主要記錄對 nor 讀速度的優化過程。

瞭解現狀

接到啟動速度優化的任務之後, 首先是瞭解情況。

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

為了加快速度已經嘗試過

  • spinor 驅動改為使用四線讀命令讀取資料。速度並沒有明顯改善。待確認改動是否生效。
  • spinor 驅動改為使用 dma 搬運資料。尚未修改成功。

計算上限

既然是要深入優化,那知道終點在哪還是很有必要的。

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

 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/s

當然較真一點,傳送讀命令給 flash 也需要時間,讓我們來算下。

一般一個讀命令需要 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^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

可以看出,如果每次讀取資料量較小,那麼傳送讀命令消耗的時間就不可忽視。每次讀取的資料量越大,則讀命令對速度造成的整體影響就越小。

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

確認瓶頸

看了下驅動列印出來的確實是 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: https://www.cnblogs.com/zqb-all/p/12824908.html
公眾號:https://sourl.cn/4X3jE7

相關文章