zoukankan      html  css  js  c++  java
  • 小白自制Linux开发板 二. u-boot移植

    上一篇:小白自制Linux开发板 一. 瞎抄原理图与乱画PCB  中我们做了一个小型而没用的开发板,用的是Licheepi Nano的镜像,那从本篇开始我们开始自己构建它的灵魂吧。

    我们都知道,PC在启动的时候,首先是进入BIOS,再根据BIOS中配置信息引导后续的启动操作系统,比如配置Windows启动。

    而对于嵌入式linux中,并没有BIOS,这时候就需要一种类似引导程序来处理。于是就有了BootLoader。

    BootLoader是一段小程序,可以把它想象成PC机linux上的GRUB/LILO引导程序,可以直接从flash或TF卡中运行,来装载内核。它可以初始化硬件设备,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统做好准备。

    1. 嵌入式开发板的启动过程

    一个嵌入式系统从软件角度来看分为三个层次:

    •  引导加载程序

               包括固化在芯片中的boot程序(可选)和BootLoader两大部分,对于固化的boot程序。主要是芯片通过外围电路连接的实际情况选择读入程序的位置,比如:通过TF卡或是SPI以及其他方式启动,至于优先顺序这就要具体看芯片的数据手册,个人没做过具体测试。

    • linux内核

              特定于嵌入式平台的定制内核

    • 文件系统

              包括了系统命令和应用程序

     BootLoader Boot Parameters Kernel Root Filesystem

    BootLoader启动过程可分为单阶段和多阶段(stage1、stage2),其中stage1完成初始化硬件,如CPU寄存器、内存控制器,为stage2准备内存空间。一般stage1是可以直接在nor flash中运行的,并将stage2复制到内存RAM中,设置堆栈,然后跳转到stage2(从这也可以看出stage2是在RAM中运行的,与stage1不同)

    Boot Parameters 顾名思义,就是配置了要启动内核的参数,包含要加载系统内核相关文件的位置,要加载到内存中的位置,定位到文件系统的位置,相关输入输出的呈现等一系列参数。

    kernel 在存放在bootloader之后,对于SoC来说,代码都需要在RAM中运行,这里与MCU不一样的地方就是引入了MMU(内存管理单元)。对于MCU而言,由于其执行速度低,因此运行代码都在ROM中直接运行,而对于Flash而言,其读取速度远不及RAM的速度,因此对于运行速度非常快的SoC而言,所有的代码都需要在RAM中运行。但是这里有一个问题,RAM掉电数据将会丢失,故代码保存不可能放在RAM中,当前所有的嵌入式设备而言,代码保存都是放在ROM中,因此在SoC中运行代码需要将代码搬运到RAM中然后再执行。

    Root Filesystem 由于其执行过程需要对ROM进行读写操作,因此可以不用搬运到RAM中,但是实际过程中内核启动后会产生一个虚拟的文件系统,该文件系统是挂在根文件系统的关键所在,这里不详细讲解。整体来说,大致的过程为,嵌入式设备上电后将执行bootloader,对硬件进行硬件和堆栈初始化,然后搬运内核到RAM中并启动内核,紧接着挂载根文件系统。

    2. 环境配置与参考项目

    系统:Ubuntu 16

    编辑器:VSCode

    参考项目:Lichee-Pi Nano  

    地址:https://wiki.sipeed.com/soft/Lichee/zh/Nano-Doc-Backup/index.html

      

    Lichee-Pi Nano

    需要注意的是一定要选择Nano版本,因为我们开发板使用的主控芯片和Nano的主控是一致的,所以后续我们要编译U-boot,内核都可以参考(bai piao)这里面的配置。

    主控芯片:F1c100s/F1c200s,100s内置32MB DDR1内存,200s内置64MB DDR1内存,200s贵一点,他们都是QFN88封装。

    ARM926ejs内核,主频默认408MHz,据了解做产品出货的一般在600M左右。

    带有100M的SPI接口2个,SDIO接口1个,USB OTG接口,还有CSI摄像头接口,LCD RGB显示屏接口,音频接口,I2C I2S UART PWM等等。

    还有就是他们不支持硬件浮点,所以浮点运算使用软浮点方式。

     F1c100s/F1c200s芯片功能

    3.交叉编译器

       我们通过PC版的Linux自带的gcc编译的程序只能在当前系统架构下的cpu架构(x86)下运行,如果我们想要编写的程序在嵌入式Linux下运行,那么就需要用到对应的编译器。

       我们做的开发板主控芯片F1C200S,内核为ARM9,其架构使用的是ARMv5架构,所以我们也要选用对应的编译器,同样,这样的编译器很多,这里我们使用最常用的arm-linux-gnueabi- ,因为交叉编译器F1C200S必须高于6.0版本,这里我们使用7.2版本

         点击下载

        下载较慢时使用下载工具

    下载完成后解压文件:

    tar -vxjf gcc-linaro-7.2.1-2017.11-x86_64_arm-linux-gnueabi.tar.xz

    然后在/usr/local目录下新建arm-linux-gcc目录

    sudo mkdir /usr/local/arm-linux-gcc

    进入解压目录下:

    cd gcc-linaro-7.2.1-2017.11-x86_64_arm-linux-gnueabi/

    将该目录下的所有文件复制到新建的目录下

    sudo cp -rd * /usr/local/arm-linux-gcc/

    最后需要添加该工具链的环境变量使其可以在任何目录下执行,打开/etc/profile文件

    sudo nano /etc/profile

    在文件末尾添加以下内容

    PATH=$PATH:/usr/local/arm-linux-gcc/bin

    添加完毕,使路径生效

    source /etc/profile

    接下来在终端输入:

    arm-linux-

    然后连按两次Tab键,如图在表示成功:

    如果没有出现,则进行下面操作,安装必要的动态链接库

    sudo apt-get install lib32ncurses5 lib32z1

    至此,我们完成了编译工具的配置。

    4. 编译U-boot

      当Arm开发板上电以后第一个要加载到内存并运行的程序就是BootLoader,BootLoader的同类型程序很多,如U-boot、X-boot、Rt-Thread,这里我们依然选中最常用的U-boot作为目标(因为其他的我也不会呀),

     最新版本的uboot几乎包含当前主流的SoC芯片,前面提到本开发板使用的芯片和licheePI nano相同,大部分硬件也是兼容的,为了快速移植该部分,这里采用licheePI nano的u-boot来进行移植。在终端输入如下命令克隆u-boot:

    git clone https://github.com/Lichee-Pi/u-boot.git -b nano-v2018.01

    克隆完毕文件会保存在当前目录下,进入该目录,

    cd u-boot

    在该文件夹下有很多分支,我们可以查看所有分支,使用如下命令:

    git branch -a

    现在我们使用的是nano开发板,所以将当前分支切换到nano分支,命令如下:

    git checkout nano-v2018.01u-boot

     切换到Nano分支

    默认的没有指定交叉工具链和架构,因此在编译之前需要指定交叉工具链和芯片架构,u-boot的交叉编译器在u-boot 的根目录下中的Makefile文件中定义了。打开文件找到CROSS_COMPILE变量,修改为如下:

    ARCH=arm
    CROSS_COMPILE=arm-linux-gnueabi-

     配制交叉编译环境

    这样我们就能使用我们指定的编译器来编译u-boot了。

    在u-boot项目的config目录下存在对多种板子的配置描述文件,由于每个板子的外设不同,因此编译之前必须要对u-boot进行配置。然而配置是一件比较繁琐的事情,特别是像u-boot这种比较复杂的项目而言,初学者几乎无法完成。幸运的是对于大部分开发板而言,config目录下有其配置好的默认配置文件。进入config目录中,然后执行ls查看当前所有的配置文件

    cd config
    ls

     查看配制文件

    找到licheepi_nano_defconfig 和 licheepi_nano_spiflash_defconfig,前者表示为TF卡启动,后者表示从SPI 设备启动,因为我们做的小板只有从TF卡启动,所以我们需要使用 licheepi_nano_defconfig 

    现在回到上级目录,然后执行

    make licheepi_nano_defconfig
    cd ..
    make make licheepi_nano_defconfig

    这样我们把licheepi_nano_defconfig 作为默认配置项。

    接下来我们就可以用图形界面进行配置了,执行

    make menuconfig

    此时出现图形配置选项,如下图所示

     u-boot Menuconfig配制,注意红框中的配置,我们后续要用到。

    至此我们的u-boot环境配置就完成了,但是我们还有个问题要解决:如何让u-boot引导系统

    我们在PC端安装Windows系统的时候往往需要选择启动顺序,比如需要优先通过光驱或u盘启动等。

    同样在u-boot中也需要这样的配置,当然u-boot比PC配置稍微复杂一丢丢。我们前面提到Linux嵌入式系统结构分布中有个Boot Parameters 部分,这部分就是做引导配置的,那怎么配置呢,总体来说可以分为两部分:

    1. bootcmd,主要用于描述控制Linux内核文件以及其他描述文件加载到内存中位置以及启动Linux内核系统等
    2. bootargs,用于配制文件系统、串口信息等。

    bootcmd

    在最开始提到过,内核一般不在flash中运行,这样就需要将内核搬运到内存中,这个过程需要u-boot来完成。对于mmc (TF卡)而言,在u-boot有专门的命令load mmc,该命令可以将mmc中的代码从flash搬运到指定的地址处。

    当u-boot中环境变量bootdelay计数到0时,此时uboot就会开始执行bootcmd中的命令。

    bootdelay这个环境变量是一个计数器,当u-boot主体运行完毕后,此时bootdelay该变量的值将会开始递减,递减时间为1s,当递减到0时,此时u-boot将会跳转到bootcmd处开始执行bootcmd命令,(你可以简单理解为PC启动后有一两秒时间等待,你可以可以通过F8或Enter键打断进入Bios设置的过程,这个等待时间就由u-boot中的bootdelay来控制)。

    下面我们需要记住这句指令,这就是我们当前制作的开发板需要用到的bootcmd全部内容

    load mmc 0:1 0x80008000 zImage;load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb;bootz 0x80008000 - 0x80c08000;

    如果你需要详细了解这句话那就接着往下看,如果不需要则可以跳到 下面的u-boot参数配置环节

    对于上面命令,我们根据分号拆分为3部分:

    1> load mmc 0:1 0x80008000 zImage;
    2> load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb;
    3> bootz 0x80008000 - 0x80c08000;

    其中两个 load mmc 命令、一个bootz 命令。

    先看第一条:

    load mmc 0:1 0x80008000 zImage

    load mmc有三个参数:第一个参数是mmc(TF卡)分区,第二个参数是内存中目标地址,第三个参数是源文件。

    即上面的命令意思是将mmc的0:1 分区中的zImage复制到内存中的0x80008000地址处。这里的zimage就是Linux内核,后续会提到该文件编译,0:1这个可以这样理解0表示TF卡(TF卡属于mmc存储器的一种),1这表示TF卡的第一个分区(boot分区)后面会提到。

    而对于内存位置 0x80008000 地址位置,将其理解为默认值就行了。这样完成了zImage的加载。

    下面分析第二条命令:

    load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb

    有了上面的加载zImage的说明,可以很轻松的理解上面的命令意思是将mmc的0:1分区中的suniv-f1c100s-licheepi-nano.dtb文件加载到内存中的0x80c08000地址处。对于suniv-f1c100s-licheepi-nano.dtb 这个文件,叫做设备树文件,简单来说就是当前开发板上面所有外设备描述文件,这部分将会在后续内核编译部分进行详细说明。

    对于第三条命令: 

    bootz 0x80008000 - 0x80c08000

    的意思是告诉内核镜像的起始地址为0x80008000,加载的设备树地址为0x80c08000。这里是告诉cpu从这里开始启动Linux, bootz命令的格式是:bootz空格0x80008000空格-空格0x80c08000,注意-左右有空格。

    除了bootz 命令外,有些系统里面还可能存在一个叫做bootm命令,这是是对没有使用设备树内核的镜像启动命令,早期版本的内核没有引入设备树,因此对于早期的内核一般使用的是bootm,其命令格式为bootm内核地址,比如bootm x0x30008000,意思是从0x30008000开始启动内核,启动内核的过程其实是将pc指针指向该地址,这样处理器就会从该地址处运行代码。

     这里我们就完成了bootcmd的说明,接下来我们看另外一个参数。

    bootargs

    bootargs也是u-boot环境变量中一个非常重要的变量,上面已经讲解了内核的启动可以通过bootcmd来完成,那接下来内核启动完毕后必须挂在根文件系统(rootfs)。但是内核并不知道根文件系统的具体位置,我们必须要告诉根文件的位置后内核才能将其挂载,这时就需要有bootargs变量。该变量的作用是告诉内核根文件系统的位置和属性以及必要的配置,

    console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 earlyprintk rw

    同上面分析的方法一样,我们依然将这部分命令拆成几部分来说明。这里需要说明的是,这部分配置信息是由u-boot 直接按照参数字符串方式提供给Linux内核,然后由Linux内核进行执行的,这也说明里为什么格式与bootcmd配置方式不一致。

    console=ttyS0,115200 表示终端为ttyS0即串口0,波特率为115200;

    panic=5 字面意思是恐慌,即linux内核恐慌,其实就是linux不知道怎么执行了,此时内核就需要做一些相关的处理,这里的5表示超时时间,当Linux卡住5秒后仍未成功就会执行Linux恐慌异常的一些操作。

    rootwait 该参数是告诉内核挂在文件系统之前需要先加载相关驱动,这样做的目的是防止因mmc驱动还未加载就开始挂载驱动而导致文件系统挂载失败,所以一般bootargs中都要加上这个参数。

    root=/dev/mmcblk0p2 表示根文件系统的位置在mmc的0:2分区处,/dev是设备文件夹,内核在加载mmc中的时候就会在根文件系统中生成mmcblk0p2设备文件,这个设备文件其实就是mmc的0:2分区(这里对应TF卡的第二个分区:rootfs),这样内核对文件系统的读写操作方式本质上就是读写/dev/mmcblk0p2该设备文件。

    earlyprintk 参数是指在内核加载的过程中打印输出信息,这样内核在加载的时候终端就会输出相应的启动信息。rw表示文件系统的操作属性,此处rw表示可读可写。

    5.u-boot参数配置 

     make menuconfig

     选中   Enable boot arguments 按空格选中,下面会显示:() Boot arguments

    然后选中Boot arguments ,按回车,进入配置窗口,接下来上面解释过的bootargs 参数信息:

    console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 earlyprintk rw

      

     配置bootargs信息

    然后按Tab键选中<OK>,保存并进入主菜单。

    同理配置:Enable a default value for bootcmd 按空格选中,下面会显示:() bootcmd value 配置项,

    选中bootcmd value 进入配置界面,输入bootcmd命令:

    load mmc 0:1 0x80008000 zImage;load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb;bootz 0x80008000 - 0x80c08000;

     配置bootcmd参数

     按Tab键选中<OK>,保存并进入主菜单。

    6.u-boot编译与烧录

    先保存图形配置界面后推出界面,在终端执行make -j4即可对整个u-boot进行编译。

    make -j4

    编译u-boot

    make -j4后面的-j4表示4个核心进行编译,若电脑的处理器是2核心,请使用make -j2进行编译。

    编译完成后会在当前目录生成u-boot-sunxi-with-spl.bin烧录文件。

    根目录下找到 u-boot-sunxi-with-spl.bin 文件

    该文件就是我们最终要烧录的二进制文件。

    在当前目录下会有一个隐藏的文件.config,该文件是u-boot编译后根据各个选项产生的配置文件,这个配置文件记录了所有配置选项的宏开关,编译的时候是根据最终的.config文件来进行编译的,当然编译前是需要有脚本解析.config文件然后进行相应的编译。

    烧录到TF卡

    只要将u-boot-sunxi-with-spl.bin烧录到tf卡的8k偏移处地址就可以了,烧录步骤如下:使用dd命令进行块搬移:

    sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/sdb bs=1024 seek=8

    该命令中:

           if    文件名:输入文件名,缺省为标准输入。即指定源文件。< if=input file >

           of   文件名:输出文件名,缺省为标准输出。即指定目的文件。< of=output file >

           bs  bytes:同时设置读入/输出的块大小为bytes个字节。

           seek  blocks:从输出文件开头跳过blocks个块后再开始复制。

    这里的输出文件(of)为主机电脑的/dev/sdb文件,也就是TF卡,这里也体现了Linux一切皆文件的思想。

    /dev/sdb 这个可以用gparted 软件查看,该软件可以直接用命令安装即可:

    sudo apt-get install gparted

    此时在Ubuntu下面可以看到如下软件:

    安装好GParted软件

     打开软件

    GParted

    在右上角可以看到两个硬盘,/dev/sda 为本地硬盘,/dev/sdb 是我们将要写数据的TF(当然这只是墨云自己的配置使然,具体情况请根据实际情况而定),因此这里的of=/dev/sdb 烧录到8k偏移地址处是指绝对地址,这个绝对地址指的是TF卡的物理地址。这8K的值是由F1C200S 中固化的启动代码决定的,所以照抄即可。

    烧写u-boot

    然后我们正常退出TF卡,然后插入我们自制的开发板,通过USB线连接电脑,

    连接开发板

    打开电脑中的命令行工具,我这里使用Xshell,

    打开Xshell,新建连接:

    配置名称 ,协议选择Serial,

     配置串口

    通过下拉选中com端口,波特率为115200,其他默认即可,点击确定,然后双击主界面左侧会话管理中的刚建立的会话,此时进入连接状态。

    因为在你插入USB通电的时候开发板就已经启动了,所以当你打开串口连接的时候可能未必会看到信息,所以按一下重启键,就可以看到如下的输出信息了,这就是我们的u-boot,执行到u-bbot计数完成后会产生错误,那是因为我们还没有进行系统内核的移植,所以默认就会进入u-boot命令模式。

     

     启动信息

     输入pri命令打印环境变量的所有值,可以找到已经配置的bootcmd 和bootargs

     pri命令结果

    至此完成了u-boot移植的全部内容,对于u-boot的移植方法,在后续移植Linux内核和文件系统时都会用到,都是大同小异的,所以有了本篇的说明,之后操作将会非常简单。

    而关于u-boot的内容事实上非常的复杂繁琐,有兴趣的可以自行去了解到,毕竟作为一个小白的我初衷只是先让小板先跑起来。

     

    参考资料

    Lite200  (lishanwen) --  https://lishanwen.cn/index.php/2021/07/03/lite200/

    全志F1C200S F1C100S 介绍 ( 迪卡魏曼依奇君 https://blog.csdn.net/tunqimai9331/article/details/95938903

    荔枝派Nano 全流程指南 (矽速科技) https://wiki.sipeed.com/soft/Lichee/zh/Nano-Doc-Backup/index.html



    NetAnalyzer下载地址

    NetAnalzyer交流群:39753670 (PS 只提供交流平台,群主基本不说话^_^)

    [转载请保留作者信息  作者:冯天文 ]


  • 相关阅读:
    Leetcode 15 3Sum
    Leetcode 383 Ransom Note
    用i个点组成高度为不超过j的二叉树的数量。
    配对问题 小于10 1.3.5
    字符矩阵的旋转 镜面对称 1.2.2
    字符串统计 连续的某个字符的数量 1.1.4
    USACO twofive 没理解
    1002 All Roads Lead to Rome
    USACO 5.5.1 求矩形并的周长
    USACO 5.5.2 字符串的最小表示法
  • 原文地址:https://www.cnblogs.com/twzy/p/14865952.html
Copyright © 2011-2022 走看看