海思Hi35xx uboot启动分析总结

Posted liwen01

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了海思Hi35xx uboot启动分析总结相关的知识,希望对你有一定的参考价值。

前言

在嵌入式linux设备中,uboot的最终目的就是启动kernel。对于uboot而言,没有人把它引导起来,所以uboot首先需要把自己加载起来,然后再去引导kernel的启动,这也就可以大致的分为Uboot启动的第一阶段和第二阶段。

(一)start.S第一阶段启动总结

在海思hi3251a官方的《Hi3521A_PINOUT_CN》手册上有上电锁存管脚BOOTROM_SEL,这个是引脚用来定义是从BOOTROM启动还是从spi flash启动 。

(1)BOOTROM启动

当启动模式为从 BOOTROM 启动时,海思的BOOTROM的程序会去初始化串口,然后与海思的HiBurn工具建立通信

如果能建立通信,则启动HiBurn升级uboot程序流程,如果不能与HiBurn建立连接,等待一段时间之后它就转去从外部的spi flash启动

(2)HiBurn 烧入原理

HiBurn烧入的基本原理是,HiBurn工具与BOOTROM程序建立连接之后,先下载uboot程序的开始4KB数据到海思芯片的内部RAM

然后通过下载的那一小部分uboot代码去初始化外部的DDR,如果DDR初始化成功,HiBurn再将剩下的uboot程序下载到外部的DDR中去

最后是在DDR中启动uboot,如果要进行烧入操作,是通过DDR中的Uboot程序,发送uboot命令将DDR中的uboot程序烧入到外部的flash中去,这样就实现了uboot程序的升级。

(3)spi flash启动

海思的hi3521a芯片支持从 SPI NOR Flash 和 SPI NAND Flash 直接启动,它的启动也就是我们start.s中的大部分实现。主要做的工作有:

  1. 初始化CPU相关功能(关缓存,关MMU,设置SVC模式等)
  2. 将异常中断向量表重定向到内部RAM
  3. 将uboot代码重定向到外部DDR中
  4. 设置栈空间
  5. 清空BSS段
  6. 最后调用C语言接口去执行C语言代码实现第二阶段的启动。
    第一阶段在执行之后,在DDR中的映射空间的数据分布如下:

(二)start_armboot第二阶段启动总结

在uboot的启动的第二阶段,主要做的是:

  1. 初始化变量和结构体
  2. 重定向环境变量
  3. 初始化堆管理器
  4. 初始化环境变量
  5. 进入命令处理循环

在命令处理循环中,默认Uboot是超时等待用户命令输入

如果有检测到命令的输入,则执行相应的命令,如果超时没有命令输入,uboot默认执行的bootm命令进行kernel启动引导。

这里涉及到两个比较总要的点:环境变量的重定向和uboot命令处理。

(1)环境变量重定向

在uboot第二阶段的时候,uboot会去检测flash中环境变量的地址中环境变量是否有效,如果有效,则使用flash中的环境变量值来初始化uboot运行中的环境变量,如果flash中的环境变量无效,则使用默认的环境变量来初始化运行中的uboot。

在uboot的用户命令操作过程中,如果有对环境变量进行修改,都是修改的内存中的那一份环境变量,如果要将修改的环境变量保存到flash中去,则需要运行命令saveenv
uboot在flash的分布如下:

uboot.bin烧入到flash的0地址位置,boot.bin里面的数据就是按我们连接脚本中的数据那样分布。在0x80000(512KB)开始的位置就是环境变量的在flash的位置,其大小为256KB。

(2)uboot命令处理

在启动的最后阶段,就是执行uboot的bootm命令,主要就是uboot将kernel程序从flash中复制到内存去,然后就是检验kernel镜像文件的有效性,设置uboot传递给kernel的参数,设置kernel启动的环境(比如关缓存,关中断,CPU处于SVC模式等)

最后就是通过一个函数指针指向kernel的启动地址,调用该函数实现uboot到kernel的运行。

在这个过程中,启动参数和机器码同时传递给kernel,关于参数传递,实际在这里是通过寄存器间参数的地址传递给了kernel。

(3)参数传递

C语言进行函数调用的时候,常常会传递给被调用的函数一些参数,对于这些C语言级别的参数,被编译器翻译成汇编语言的时候,就要找个地方存放一下,并且让被调用的函数能够访问,否则就没发实现传递参数了。对于找个地方放一下,分两种情况。

  1. 一种情况是,本身传递的参数就很少,就可以通过寄存器传送参数。因为在前面的保存现场的动作中,已经保存好了对应的寄存器的值,那么此时,这些寄存器就是空闲的,可以供我们使用的了,那就可以放参数,而参数少的情况下,就足够存放参数了,比如参数有2个,那么就用r0和r1存放即可。

  2. 另外一种情况是,如果参数太多,寄存器不够用,那么就得把多余的参数堆栈中了。即,可以用堆栈来传递所有的或寄存器放不下的那些多余的参数。

---------------------------End---------------------------

长按识别二维码
关注 liwen01 公众号

从零开始在海思芯片上部署ubootkernelrootfsmpp

前言

本文介绍的是拿到海思芯片的开发板和海思的SDK开发包,在裸机上部署uboot、kernel、rootfs、mpp的过程,其中不涉及uboot、kernel、rootfs、mpp的细节,主要是介绍如何使用开发包快速的将海思芯片跑起来。(这里的裸机是指海思芯片就是出厂的状态,连uboot都没有)。本文大体上是从逻辑上讲解如何部署海思SDK,并不能保证你能按着文章讲解的顺序就能畅通无阻的完成SDK的部署。光是编译uboot、kernel、rootfs就可能遇到很多错误,每个人的开发环境不同,遇到的错误也不一样,在这里不讲解可能遇到的错误,有问题就去百度。

1.芯片介绍

我用的是HI3518EV200这款芯片,现在来看这款芯片的性能并不算好,但是只用该芯片做单纯录像的监控设备,性能还是足够的,如果想要跑算法就显得性能不足了。该款芯片内置64M的内存,可以不用外接内存就可以运行,SDRAM的地址范围:80000000-83FFFFFF,我的开发板是外接的16M的spi flash,将来uboot、kernel、rootfs都烧录到spi flash中。

2.部署的思路

(1)首先是搭建开发环境,我是用PC端来开发的,要自己装虚拟机搭建环境,比较麻烦,公司开发都是服务器上进行开发,不用自己搭建环境;
(2)熟悉SDK包,了解目录的结构,找到与编译生成uboot、kernel、rootf1s相关的内容,看文档和编译脚本。
(3)在虚拟机中编译生成uboot、kernel、rootf1s的镜像,;
(4)将uboot、kernel、rootf1s烧录进海思芯片里;
(5)部署mpp:将海思相关的.ko文件和.so文件都拷贝到设备的目录里加载好;
(6)编译SDK包里的sample程序,如果示例程序能在海思芯片上正常运行,说明此次部署已经成功;
(7)后期展望:按照SDK去部署系统是比较基础的,没有针对设备进行优化,比如uboot、kernel、rootfs都是可以针对设备进行精简的,这样可以节省内存,用不上的.ko和.so也不必拷贝到设备里。

3.环境搭建

(1)编译环境:Windows+VMware+Ubuntu;
(2)在虚拟机里搭建好samba服务器、tftp服务器、nfs服务器,调试好网络,确认设备可以通过网络连接上虚拟机;
(3)确认PC和设备可以通过终端模拟软件进行串口通信;

3.SDK开发包的使用:

(1)游览《Hi3518EV20X/Hi3516CV200 U-boot 移植应用开发指南》和《Hi3518EV20X/Hi3516CV200 Linux开发环境用户指南》,里面讲解的是开发环境的搭建和U-boot的移植;
(2)将“Hi3518E_SDK_V1.0.3.0.tgz”压缩包拷贝到Linux环境中进行解压,可以通过共享文件夹或者Samba服务器,推荐共享文件夹;
(3)将压缩包解压开后,可以看到两个脚本文件:sdk.cleanup和sdk.unpack。先执行source sdk.cleanup清除掉整个目录里的中间文件、解压文件、目标文件,确保目录里的文件都是干净的;然后执行source sdk.unpack,将目录里的压缩包都解压开。

4.制作uboot、kernel

(1)安装交叉工具编译链:交叉工具编译链在“Hi3518E_SDK_V1.0.3.0/osdrv/opensource/toolchain/”路径下,有arm-hisiv300-linux和arm-hisiv400-linux两个交叉工具编译链,arm-hisiv300-linux对应uclibc库,arm-hisiv400-linux对应glibc库,自己根据需求选择一个安装。在交叉工具编译链的目录下有个cross.install.v300脚本文件,执行该脚本文件会自动安装交叉工具编译链。该脚本会安装交叉工具编译链并创建链接符号,我们需要将创建的那个路径导出到环境变量PATH里。具体的细节查看cross.install.v300文件。对交叉工具编译链安装不熟悉的可以参考《交叉编译工具链的安装以及介绍》
(2)安装32位的兼容包:交叉工具编译链是32位的,我的Ubuntu是64位的,所以需要安装lib32z1,因为编译还涉及C++编译还需要装lib32stdc++6-4.8-dbg,推荐使用aptitude进行安装。
(3)将“Hi3518E_SDK_V1.0.3.0/osdrv/opensource/uboot/u-boot-2010.06/tools”路径下的mkimage拷贝到**/usr/local/lib目录下,这是制作镜像需要用到的。
(4)制作uboot、kernel、rootfs:“Hi3518E_SDK_V1.0.3.0/osdrv”目录里的内容就是制作镜像的,Makefile文件是编译脚本,该Makefile的使用方法看同目录下的
readme_cn.txt**文件。理论上来说,按照readme_cn.txt的说明可以直接一条命令就可以生成uboot、kernel、rootfs三个镜像文件,实际编译过程会遇到很多问题。遇到问题就研究Makefile文件或者百度,我反正当时被折磨的够呛。
(5)最终得到三个镜像文件:rootfs_hi3518ev200_64k.jffs2u-boot-hi3518ev200.binuImage_hi3518ev200
特别提醒:制作rootfs时注意文件系统的格式和flash的块大小,这些在readme_cn.txt都有说明。

5.烧写uboot

1.uboot的烧写参考博客《海思烧录工具HiTool的使用方法以及烧录uboot》《用HiTool烧写uboot到spi flash的原理》
2.将uboot烧写到0x0起始地址处;

6.烧写kernel、rootfs

已经部署好uboot就不用再使用HiTool进行烧录了,用uboot进行烧写更方便,下面将uboot、kernel、rootfs都再重新烧写一遍

6.1 分区表:

分区名              分区大小            起始地址              截至地址
bootloader:    1M                      0x00000000        0x00100000
kernel:            3M                     0x00100000        0x00400000
rootfs:               12M                   0x00400000         0x01000000
解析:spi flash总共有16M,uboot有266kb,kerner有2.6M,rootfs有4.8M。所以uboot分配1M的地址空间,kernel分配3M,剩下的12M全部分配给rootfs。分区的原则有两个:首先是每个分区要能放得下镜像文件,还有就是有扩展余地,适当多分配一点。

6.2 搭建好tftp下载的环境:

在前期的环境搭建中就在虚拟机中搭建好了tftp服务器,现在只需要将uboot中的serverip改为虚拟机的ip地址,确保开发板可以ping通虚拟机。将uboot、kernel、rootfs的镜像放到虚拟机的tftp目录下,接下来就利用tftp下载烧录镜像。

6.3 烧录的说明:

本次是利用uboot的命令进行烧录,因为前面已经将uboot烧录到了开发板,但是为了烧录的完整性,接下来还会把uboot重新烧录一遍。这里有一些隐含的知识点:1.HI3518EV200芯片的内置内存的地址范围是80000000-83FFFFFF;2.spi flash的起始地址是0x0,且只外接了一块spi flash;spi flash的写操作顺序:选中、擦除、写入;3.uboot、kernel、rootfs烧录到flash的位置要严格按照分区表进行;4.海思芯片的内存是分为系统内存和mmz内存,系统内存由kernel管理,mmz由海思内部管理;5.接下来的操作都是进入到uboot里输入命令进行烧录;

6.3 重新烧录uboot

tftp更新并重新烧写uboot的命令序列:
(1)mw.b 0x82000000 ff 0x100000 //擦除内存:在0x82000000起始地址处按字节写入ff,长度为0x100000(1M)
(2)tftp 0x82000000 u-boot-hi3518ev200.bin //通过tftp命令,将虚拟机tftp目录下的uboot镜像烧录到0x82000000起始的内存处
(3)sf probe 0 //选中spi flash,因为只外接了一片flash,所以是probe 0
(4)sf erase 0x0 0x100000 //从0x0起始,擦除1M大小的地址空间
(5)sf write 0x82000000 0x0 0x100000 //将从0x82000000地址处开始,长度为1M大小的数据写入到0x0地址起始处。也就是把刚才写到内存里的uboot镜像写入到flash中

6.4 烧录kernel

tftp更新并重新烧写kernel的命令序列:
(1)mw.b 0x82000000 ff 0x300000
(2)tftp 0x82000000 uImage_hi3518ev200
(3)sf probe 0
(4)sf erase 0x100000 0x300000
(5)sf write 0x82000000 0x100000 0x300000
备注:以上命令不再解释,和烧录uboot是一样的,只是烧写的地址不一样,结合分区表来看就很容易理解。

6.5 烧录rootfs

tftp更新并重新烧写rootfs的命令序列:
(1)mw.b 0x82000000 ff 0xc00000
(2)tftp 0x82000000 rootfs_hi3518ev200_64k.jffs2
(3)sf probe 0
(4)sf erase 0x400000 0xc00000
(5)sf write 0x82000000 0x400000 0xc00000
备注:以上命令不再解释,和烧录uboot是一样的,只是烧写的地址不一样,结合分区表来看就很容易理解。

6.6设置正确的bootcmd和bootargs

6.6.1 设置bootcmd

命令:set bootcmd ‘sf probe 0;sf read 0x82000000 0x100000 0x300000;bootm 0x82000000’
解析:这是uboot启动的命令,作用是加载kernel。
(1)sf probe 0:选中spi flash;
(2)sf read 0x82000000 0x100000 0x300000:从0x100000地址处读取0x300000(3M)大小的数据到0x82000000地址处。就是将保存到flash的kernel读取到内存中,刚好和我们的烧录是逆过程。
(3)bootm 0x82000000:加载kernel。

6.6.2 设置bootargs

命令set bootargs mem=32M console=ttyAMA0,115200 root=/dev/mtdblock2 rootfstype=jffs2 mtdparts=hi_sfc:1024K(boot),3072K(kernel),12288K(rootfs)
解析:这是启动uboot的传参,指定一些关键的参数。
(1)mem=32M:这里指明系统分配内存32M(总共有64M的内存);
(2)console=ttyAMA0,115200:系统控制台的波特率是115200
(3)root=/dev/mtdblock2:rootfs在第二个分区(按0、1、2来计数的),对应分区表的第三个分区;
(4)rootfstype=jffs2 :根文件系统的格式是jffs2;
(5)mtdparts=hi_sfc:1024K(boot),3072K(kernel),12288K(rootfs):系统有三个分区:uboot占1M、kernel占3M、rootfs占12M,刚好分配完16M的flash;

7.部署mpp

mpp的部署参考博客《海思芯片部署MPP并验证功能》

以上是关于海思Hi35xx uboot启动分析总结的主要内容,如果未能解决你的问题,请参考以下文章

经验分享华为海思Hi35xx系列ARM32交叉编译opencv

海思hi35xx 开发学习:视频输入

海思HI35xx平台串口配置方法

海思HI35XX之----视频处理单元各通道间的关系

海康摄像头 海思AI芯片(Hi35XX): 图像jpg转.bgr

FFmpeg开发笔记:ffmpeg在移植到海思HI35xx平台之将ffmpeg库引入到sample的demo中