某个项目使用的介质是 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