Background:

瑞芯微在《RV1126/RV1109 低功耗/快速启动产品开发指南》中提到如下内容:

RV1126/RV1109内部有专⻔针对快速启动做了硬件优化设计,可以极⼤地降低快速启动时间,⽐如RV1126/RV1109芯⽚内置硬件解压缩模块-- decom,可以快速解压rootfs和kernel。

所以向测量瑞芯微rv1126内置硬件解压缩模块与gzip解压时间差。

1、 使用硬件解压缩

修改代码添加时间戳:

vi common/spl/spl.c
686 /* cleanup before jump to next stage */
687 void spl_cleanup_before_jump(struct spl_image_info *spl_image)
688 {  ……
719     printf("Total: %ld.%ld ms\n\n", us / 1000, us % 1000);
720     printf("\n jump_tick: %ld.%ld ms\n\n", (ulong)(get_ticks() / 24UL) / 1000, (ulong)(get_ticks() / 24UL) % 1000);
722 }

启动过程中时间戳打印如下:
jump_tick: 159.272 ms

2、关闭解压缩功能

make menuconfigDevice Drivers  --->Multifunction device drivers  --->[ ] Enable misc decompress driver support[ ] Enable misc decompress driver support in SPL

对比配置前后.config关于DECOMPRESS的差别:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lXidLekm-1650866686797)(resources/diff1.jpg)]

修改默认的配置文件:

vi rv1126_decom_dis_defconfig
108 # CONFIG_MISC_DECOMPRESS is not set
109 # CONFIG_SPL_MISC_DECOMPRESS is not set

启动过程中时间戳打印如下:(这种情况下内核无法启动)
jump_tick: 148.547 ms

3、只关闭硬件解压缩模块

make menuconfigDevice Drivers  --->Multifunction device drivers  --->[*] Enable misc decompress driver support                                                          [*] Enable misc decompress driver support in SPL   [ ] Rockchip HardWare Decompress Support[ ] Rockchip HardWare Decompress Support

对比配置前后.config关于DECOMPRESS的差别:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8kP37NY9-1650866686800)(resources/diff2.jpg)]

修改默认的配置文件:

vi rv1126_decom_dis_defconfig
108 CONFIG_MISC_DECOMPRESS=y
109 CONFIG_SPL_MISC_DECOMPRESS=y
……
112 # CONFIG_ROCKCHIP_HW_DECOMPRESS is not set
113 # CONFIG_SPL_ROCKCHIP_HW_DECOMPRESS is not set

启动过程中时间戳打印如下:(这种情况下内核无法启动)
jump_tick: 149.557 ms

4、关闭硬件解压缩模块,使能gzip解压

make menuconfigLibrary routines  --->Compression Support  --->[*] Enable gzip decompression support for SPL build

对比配置前后.config关于GZIP的差别:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AengjWi7-1650866686800)(resources/diff3.jpg)]

修改默认的配置文件:

vi rv1126_decom_dis_defconfig
108 CONFIG_MISC_DECOMPRESS=y
109 CONFIG_SPL_MISC_DECOMPRESS=y
……
188 CONFIG_SPL_GZIP=y

正常这种情况内核应该可以启动,但并没有解压成功,主要报错日志如下:

 Checking kernel 0x00608000 (gzip @0x04800000) ... sha256-skipped +Error: inflate() returned -5kernel: decompress error, ret=-1

Question:

为什么mcu和optee镜像可以解压成功,但内核不行?

Debug:

问了下度娘,找到如下解决办法:
61130 - PetaLinux - Uncompressing Kernel Image … Error: inflate() returned -5 Message During Boot (xilinx.com)
其中提到:
在提取压缩的Linux内核映像时,UBOOT将压缩映像作为缓冲区存储在DDR中。
如果压缩映像位于内存中,缓冲区和映像解压缩到的位置重叠,则引导过程将失败,因为内存空间冲突。
这可以通过增加UBOOT bootm命令用于提取过程的空间来解决。

主要内容就是修改如下位置:

#define CONFIG_SYS_BOOTM_LEN <size>

检索define CONFIG_SYS_BOOTM_LEN有如下内容:

grep -nr '#define CONFIG_SYS_BOOTM_LEN'include/configs/rv1126_common.h:26:#define CONFIG_SYS_BOOTM_LEN     (64 << 20)

但是修改为(128 << 20)之后发现不可行,但大概了解此问题可能是因为解压目的地址内存空间不足。

检索inflate()有如下内容:

grep -nr 'inflate'lib/gunzip.c:312:          printf("Error: inflate() returned %d\n", r);
lib/zlib/inflate.c:328:int ZEXPORT inflate(z_streamp strm, int flush)

对应上之前启动log中的报错:Error: inflate() returned -5

vi lib/gunzip.c
289 int zunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp,
290                         int stoponerr, int offset)
……
309         r = inflate(&s, Z_FINISH);
310         if (stoponerr == 1 && r != Z_STREAM_END &&
311             (s.avail_in == 0 || s.avail_out == 0 || r != Z_BUF_ERROR)) {
312             printf("Error: inflate() returned %d\n", r);
……
320     return err;用ctags追代码发现Z_BUF_ERROR就是-5
#define Z_BUF_ERROR    (-5)

百度一下 报错Z_BUF_ERROR具体原因:
(1条消息) Gzip uncompress错误代码Z_BUF_ERROR_林多的博客-CSDN博客

总结一下导致Z_BUF_ERROR的原因:

  1. source缓冲区长度为0(没有要解压的资源,却调用解压过程)。
  2. dest缓冲区(解压后的资源)长度不够用来解压。

更加印证无法解压内核的原因是目标空间内存不足的原因。

于是看inflate.c的实现,想知道Z_BUF_ERROR具体含义,发现如下内容:

So the only thing the flush parameter actually does is: when flush is set to Z_FINISH, inflate() cannot return Z_OK.   Instead it will return Z_BUF_ERROR if it has not reached the end of the stream。译文如下:
因此flush参数实际做的唯一事情是:当flush被设置为Z_FINISH时,inflation()不能返回Z_OK。相反,如果它还没有到达流的末尾,它将返回Z_BUF_ERROR。

所以认为应该是目的地址所分配的空间不足造成内核还没有被解压完才出错。

检索decompress error有如下内容:

grep -nr 'decompress error'arch/arm/mach-rockchip/fit_misc.c:80:      printf("%s: decompress error, ret=%d\n",

对应上之前启动log中的报错:kernel: decompress error, ret=-1

vi arch/arm/mach-rockchip/fit_misc.c
76     ret = gunzip((void *)(*load_addr), ALIGN(len, FIT_MAX_SPL_IMAGE_SZ),
77              (void *)(*src_addr), (void *)(&len));
78 #endif
79     if (ret) {
80         printf("%s: decompress error, ret=%d\n",
81                fdt_get_name(fit, node, NULL), ret);
82         return ret
83     }

发现打印之前调用了gunzip函数,检索gunzip,看此函数实现:

vi lib/gunzip.c
74 int gunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp)
75 {
76     int offset = gzip_parse_header(src, *lenp);
77
78     printf("\n>>>[%s] %s: %d<<<\n", __FILE__, __func__, __LINE__);
79     printf(">>>dstlen:0x%x<<<\n", dstlen);
80     if (offset < 0)
81         return offset;
82
83 #if defined(CONFIG_MISC_DECOMPRESS) && !defined(CONFIG_SPL_BUILD)
84     int ret;
85
86     ret = misc_decompress_process((ulong)dst, (ulong)src, *lenp,
87                       DECOM_GZIP, true, (u64 *)lenp);
88     if (!ret)
89         return 0;
90
91     printf("hw gunzip failed(%d), fallback to soft gunzip\n", ret);
92 #endif
93     return zunzip(dst, dstlen, src, lenp, 1, offset)
94 }

发现在最后调用了zunzip,发现其中有一个实参名字为dstlen,理解为目的长度。
于是在此打印此参数的值:

vi lib/gunzip.c
78     printf("\n>>>[%s] %s: %d<<<\n", __FILE__, __func__, __LINE__);
79     printf(">>>dstlen:0x%x<<<\n", dstlen);

此时的启动log:

## Checking mcu 0x00108000 (gzip @0x00208000) ... sha256+
>>>[lib/gunzip.c] gunzip: 78<<<
>>>dstlen:0x200000<<<
sha256+ OK
>>>mcu_tick<<<: 112.243 ms
## Checking optee 0x00040000 (gzip @0x00140000) ... sha256+
>>>[lib/gunzip.c] gunzip: 78<<<
>>>dstlen:0x200000<<<
sha256+ OK
## Checking fdt 0x01f00000 ... sha256-skipped + OK
## Checking kernel 0x00608000 (gzip @0x04800000) ... sha256-skipped +
>>>[lib/gunzip.c] gunzip: 78<<<
>>>dstlen:0x600000<<<
Error: inflate() returned -5
kernel: decompress error, ret=-1

通过log发现加载不同镜像时打印的值不同,于是找这个值的传递过程,发现在调用gunzip的处发现一些不太明白的地方。

76     ret = gunzip((void *)(*load_addr), ALIGN(len, FIT_MAX_SPL_IMAGE_SZ),
77              (void *)(*src_addr), (void *)(&len));用ctags追代码看到如下内容:
40 #define ALIGN(x,a)      __ALIGN_MASK((x),(typeof(x))(a)-1)
41 #define __ALIGN_MASK(x,mask)    (((x)+(mask))&~(mask))

于是问了下度娘:
Linux中ALIGN宏的原理 (360doc.com)
内存对齐宏ALIGN_qwaszx523的博客-CSDN博客

以下是个人对ALIGN理解:

首先:align有对齐的意思,其次typeof不是C语言本身的关键词或运算符(sizeof是C标准定义的运算符),它是GCC的一个扩展,作用正如其字面意思,用某种已有东西(变量、函数等)的类型去定义新的变量类型。typeof()中可以是任何有类型的东西,变量就是其本身的类型,函数的返回值就是它自身的类型。typeof一般用于声明变量。在此处 (typeof(x))(a)-1,表明把a转化为x的类型,不考虑类型,上述代码可以简化为如下:

#define ALIGN(x,a)    (((x)+(a)-1)&~(a-1))

上面的计算方法在内核代码中可以经常看到,下面给出几个例子:

    (1) 当想向系统申请len字节的空间时, 想将该空间以size为倍数对齐, 而且要得到是比len大的值, 则使用ALIGN宏:#define ALIGN(len,size) (((len)+(size)-1)&(~((size)-1)))(2) 与页面对齐相关的宏#define PAGE_SIZE        4096#definePAGE_MASK         (~(PAGE_SIZE-1))#define PAGE_ALIGN(addr) -(((addr)+PAGE_SIZE-1)& PAGE_MASK)(3) 与skb分配时对齐相关的宏#define SKB_DATA_ALIGN(X) (((X) + (SMP_CACHE_BYTES -1)) & ~(SMP_CACHE_BYTES - 1))

以上操作都是在进行内存对齐,为什么需要内存对齐?

这是因为操作系统在数据读取的时候,其实并不是一个字节一个字节进行读取的,而是一段一段进行读取,我们假如是4bytes。假如我们要读取一个int,这个int是从第1位到第4位。那么读取的时候会发生什么事情呢?首先我们需要先读第一块数据,然后读取后三位的数据。接下来,读取第二块数据,然后只取第一位的数据。最后将两次的数据组合起来,就是我们想要的一个数据。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9NEvasCM-1650866686801)(resources/align.jpg)]
对于操作系统来说,这种处理数据的方式并不是特别地高效。我们都知道,在计算机领域,有一个特别有名的优化手段,就是空间换时间。我们通过内存对齐,直接跳过部分空的字节,然后一次性读取所需数据。

知道这么做的原因后便追寻这么做的原理,实现原理如下:

有如下两个整数
int a = 14; 
int size = 8;

如果想让14变成8的整数倍应该怎么做?

8的倍数有8和16,而14则处在8和16之间,此时发现8和16二进制表示,其后三位都为0,而8 = 2^3,所以直接将14的后三位清0是不是就会变成8的倍数?而要达到这一目标,只要让14和下面这个数进行与运算就可以了:
11111111 11111111 11111111 11111000

而上面这个数实际就是 ~ (size - 1),我们将该数称为size的对齐掩码size_mask. 
可这样做求出的是8是一个比14小的最大的8的倍数. 如果要求出比14大的最大的8的倍数是不是需要加上8就可以了?

14这个数好像是可以的,可是如果a本身就是8呢, 这样加8不就错了吗, 所以在14的基础上加上 (size – 1), 然后与size的对齐掩码(size_mask)进行与运算就可得出比14大的最小的8的倍数16。

这样, 我们可以定义下面的宏, 用于计算一个数a以size为倍数的前后两个值:

#define alignment_down(a, size) (a & (~(size-1)) )
#define alignment_up(a, size) ((a+size-1) & (~ (size-1)))例如:
a=0, size=8,  则alignment_down(a,size)=0, alignment_up(a,size)=0.
a=6, size=8,  则alignment_down(a,size)=0, alignment_up(a,size)=8.
a=8, size=8,  则alignment_down(a,size)=8, alignment_up(a,size)=8.
a=14, size=8, 则alignment_down(a,size)=8, alignment_up(a,size)=16.

RootCause:

之前分析知是dstlen不够造成解压错误,而dsten的实参是ALIGN(len, FIT_MAX_SPL_IMAGE_SZ),

看代码得知,len是压缩后镜像的大小,FIT_MAX_SPL_IMAGE_SZ是要对齐的size。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dd2nVgJ0-1650866686804)(resources/kernel.jpg)]
查看压缩后kernel镜像大小为4.9M
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2tVv5poB-1650866686806)(resources/zboot.jpg)]
查看未压缩的kernel镜像大小为14M

## Checking kernel 0x00608000 (gzip @0x04800000) ... sha256-skipped +
>>>[lib/gunzip.c] gunzip: 78<<<
>>>dstlen:0x600000<<<
Error: inflate() returned -5
kernel: decompress error, ret=-1

打印看出dstlen只有6M空间,不足够放下解压后的kernel镜像,所以会解压失败。

Solution:

分配空间时改为16M字节对齐即可解决此问题:

vi arch/arm/mach-rockchip/fit_misc.c
79     ret = gunzip((void *)(*load_addr), ALIGN(len, FIT_MAX_SPL_IMAGE_SZ),79     ret = gunzip((void *)(*load_addr), ALIGN(len, SZ_16M),

启动过程中时间戳打印如下:(这种情况下内核已经正常启动)
jump_tick: 1040.326 ms

5、软硬件解压缩时间对比

  1. 硬件解压:>>>jump_tick<<<: 159.272 ms
  2. 关闭硬件解压缩模块,不使能gzip解压:>>>jump_tick<<<: 149.557 ms
  3. gzip解压不加内核校验:>>>jump_tick<<<: 1040.326 ms

根据以上数据推测硬件解压缩所用时间大概为10ms,
软件解压缩时间为890ms,相差880ms左右。

瑞芯微rv1126/1109软硬件解压缩对比---附:关于内存对齐的那些事相关推荐

  1. 瑞芯微RV1126/1109开发流程之模型转换

    1.环境搭建(PC端ubuntu16.04搭建rknn环境) (1)安装anaconda环境(为了便于管理自己的环境建议安装,安装步骤请自行搜索,本人安装anaconda版本为Anaconda3-20 ...

  2. 瑞芯微RV1126/1109开发流程之驱动升级

    1.1126硬件参数读取 (1)CPU温度读取 46300和47100分别代表46.3.47.1 (2)查看1126的NPU (3)查询NPU驱动版本 dmesg | grep -i galcore ...

  3. yolov5-5.0训练模型+瑞芯微rv1126上实现模型部署

    yolov5-5.0训练模型+瑞芯微rv1126上实现模型部署   第一次接触模型训练和在开发板部署,过程曲折,从开始的一脸懵到最后模型部署成功,查阅了不少资料和学习了不少大佬的经验,在这里记录一下过 ...

  4. [基于瑞芯微RV1126调试RTL8818FU WIFI模组支持STA和AP模式]

    基于瑞芯微RV1126调试RTL8818FU WIFI模组支持STA和AP模式 内核menuconfig配置 内核dts配置 文件系统配置和更改 驱动编译 wifi工具编译 libnl库编译 open ...

  5. 瑞芯微rv1126 rtsp+mpp+rga取流

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 瑞芯微rv1126 rtsp+mpp+rga取流 一.mpp解码 二.rga转换 经过测试,使用rtsp+mpp+rga取流1920* ...

  6. 瑞芯微rv1126+yolov3模型转换

    瑞芯微rv1126+yolov3模型转换 文章目录 瑞芯微rv1126+yolov3模型转换 环境准备 一.加载运行docker 1.安装 Docker 2. 加载镜像 二.转换步骤 1.下载yolo ...

  7. 瑞芯微RV1126平台----yolov5输出后处理C++实现

    目录 1.前言 2.代码 2.1 padding resize 代码 2.2 瑞芯微yolov5后处理 2.3 坐标框的后处理 3.完整代码

  8. 瑞芯微rv1126超频笔记

    Print Encode Frame Rate enable print fps log echo 0x100 > /sys/module/rk_vcodec/parameters/mpp_de ...

  9. 瑞芯微RV1126部署yolov5-face_模型转换_输出后处理C++实现

    目录 1.模型转换 1.1 yolov5-face的pt模型转为onnx模型 1.2 yolov5-face的onnx模型转换为rknn模型 2.C++代码 2.1 paddi

  10. 瑞芯微RV1126 BuildRoot开发学习笔记

    1.BuildRoot如何增加一个包? 参考 < The Buildroot User Manual.pdf >第17章 2.BuildRoot如何单独编译某一个包? 如果修改了源码,在编 ...

最新文章

  1. firefox.exe not found problem (VS2005 website)
  2. 【模板】RMQ问题—st表实现
  3. textarea限制输入长度
  4. Linux系统中软件的“四”种安装原理详解:源码包安装、RPM二进制安装、YUM在线安装、脚本安装包...
  5. MySQL5.7新特性——在线收缩undo表空间 (转载)
  6. 关于ajax请求400问题解决
  7. 安卓抓包软件_Packet Capture安卓抓包神器介绍及使用教程
  8. Mac生成ssh,并添加公钥到Github
  9. 65 年来,全英国向他道歉三次,图灵,计算机人不能忘记的男人
  10. 创建第一个windows服务
  11. 信噪比 香农公式_「香农公式」信噪比/香农公式 - seo实验室
  12. uni-app 小程序分享到朋友和朋友圈
  13. linux如何查看路由器ip地址,如何查找路由器IP地址?
  14. Hive - 内表和外表的区别
  15. python计算决策树误差_《统计学习方法》第五章决策树 练习题解答
  16. android系统是什么意思
  17. 什么是操作系统(OS)?都有哪些常见的分类?
  18. 女朋友撒娇让我教她HashMap
  19. KALI attack 实验室
  20. 项目初始化报 404 Not Found - GET https://registry.npmjs.org

热门文章

  1. c语言exe木马,为啥我用c语言写成的exe文件会被360当做木马?
  2. python 中的 Fraction和GCD求最大公约数
  3. “2014网站移动化大赛”已启动,个人网站全面进入“移动”时代?
  4. 选择结构与分支结构 计算器的实现
  5. 2021昆明icpc B 状压+期望dp,一点几何模拟
  6. linux五笔教程,RHEL6 64位操作系统安装极点五笔输法
  7. 第16届东北四省赛题解
  8. EXCEL条件格式小知识:条件判断如何写公式,可多层if
  9. 尘世了了 花开花落昔年同
  10. Linux内核设计与实现 总结笔记(第六章)内核数据结构