记一次 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

相关文章
相关标签/搜索