loading

Loading

请输入关键字开始搜索
    首页 技术实践嵌入式

    【2】uboot启动流程

    分类:嵌入式
    字数: (7261)
    阅读: (166)
    0

    本文主要是将如何将tftp嵌入到uboot中,开机启动。
    使用规格如下:

    board hi3519v101 C020
    arch hi3519
    uboot version 2010.06

    1 添加tftp步骤

    上一章更新了uboot的启动流程。而在uboot下做tftp升级,因为海思本身支持ftfp升级,(可以查看main.c 372 行CONFIG_UPDATE_TFTP)。

    故需要做的工作如下:

    1. 在/include/configs/hi3519v101.h中增加  #define UPDATE_TFTP,则在common/main.c中可寻找到关于tftp的升级代码。
    2. 编写common/下update.c代码,当tftp收到代码以后进行升级。
    3. 由于update.c使用fit文件,需要学习解析FIT文件。
    4. 用于update.c使用flash擦写,而3519使用EMMC,需学习擦写EMMC文件。
    5. 重新编写update.c
    6. tftp等待时间有点长,需要修改net/tftp.c中关于tftp的等待时间。

    其中有几个注意点:

    • common下的cmd_XXX.c都是在uboot下使用命令实现的。tftp就是在cmd_net.c中实现,如果不会,可以参考cmd下的代码\
    • 前章节已经说明,但是重新说明一下,3519的存储初始化采用广撒网操作,即定义了nor,nand,emmc的三种存储器初始化,哪个操作成功就算是成功了。
      • 针对于存储的操作,都采用hisi自己写的操作。具体查看drivers/mtd/中hi开头的di驱动。
      • 关于EMMC的驱动,都在drivers/mmc/himciv200.c中。可以查看common/cmd_mmc.c参考mmc工作方式
        #define CONFIG_CMD_SF  //354行,nor flash操作
        #define CONFIG_CMD_NAND //358行,nand flash操作
        #define CONFIG_GENERIC_MMC //377行,EMMC操作

    2 实施步骤

    查看common/main.c中372行已经有TFTP升级了,所以只需要定义CONFIG_UPDATE_TFTP即可。

    1. 在hi3519v101.所以只需要定义CONFIG_UPDATE_TFTP即可

    /*---->include/configs/hi3519v101.h<----------------*/
    #define CONFIG_FIT 1
    #define CONFIG_UPDATE_TFTP 1
    // #define CONFIG_OF_LIBFDT 1
    // #define CONFIG_UPDATE_TFTP_CNT_MAX 1
    // #define CONFIG_UPDATE_TFTP_MSEC_MAX 1

    其中,除了要定义CONFIG_OF_LIBFDT,还要定义CONFIG_FIT。。毕竟我们需要使用FIT文件结构进行升级。
    在实施中,一定要阅读以下几个文档:

    • README 2097行有关于tftp升级的详细介绍
    • doc/README.update 有详细的关于auto tftp update的介绍
    • CONFIG_OF_LIBFDT是关于描述FDT的文件,在README.update中建议打开,但是在实际使用中,我们并没有用到FDT相关功能,打开以后问题多多,所以关闭了
    • CONFIG_UPDATE_TFTP_CNT_MAX和CONFIG_UPDATE_TFTP_MSEC_MAX是关于tftp等待时间,这里定义的只是初始值,在update.c中可以直接定义,而且在tftp.c中关于tftp的等待时间有严格限制,需要修改

    2. 查看update.c代码

    update.c中update_tftp代码非常简单,首先获取要升级的文件,获取内存地址,使用tftp获取文件,使用FIT进行解析。解析完毕后存储到文件中。步骤如下:

    1. 获取要升级的文件名称
    2. 获取要升级存放的内存地址
    3. 调用tftp协议进行文件传输
    4. 解析FIT文件,获取单个文件
    5. 使用擦写文件

    需要做的工作如下:

    1. 设置升级文件名称
    2. 设置内存存放地址,默认为0x82000000
    3. 学习FIT格式
    4. 学习EMMC擦写

    3. FIT文件解析

    关于FIT文件,详细的操作文档在:

    doc/uImage.FIT
    //请详细仔细阅读

    FIT举例如下:

    //FIT是作为内核的一种文件描述方式,其实和xml差不多
    / {     // 根文件“/”是必要的,如同文件系统的根目录,以下都是根节点下子节点,例子中,/下只有images一个子节点
        description = "IPNC auto-tftp firmware";//根节点下关于子节点描述
        #address-cells = <1>; //子节点地址描述,<1>表示只有1个地址
    
        images { //子节点image
            update@1 { //子节点,名称@地址
                description = "u-boot"; //子节点描述符
                data = /incbin/("./uboot.bin"); //子节点存在地址,mkimage会使用
                type = "standalone"; //类型,类型定义很多,查看README
                compression = "none";//是否压缩
                arch = "arm"; //架构
                load = <0x00000000>; //启动地址
                entry = <0x100000>;  //地址大小
                hash@1 {
                    algo = "md5"; //校验规则
                };
            };
    }

    生产FIT,在PC下操作如下:

    mkimage -fl update_firmware.its ./firmware.bin
    //使用mkimage将its的文件描述符打包成firmware.bin升级文件

    4. EMMC擦写

    查看cmd_env代码,为了区分使用nand,nor,emmc去保存,uboot写了三套saveenv的结构体,通过查看当前的存储,调用不同的存储擦写操作,简单粗暴,大写的佩服。

    截取的cmd_mmc.c中擦写mmc的代码
    {
         if (strncmp(argv[1], "write", sizeof("write")) == 0) {
            int dev = simple_strtoul(argv[2], NULL, 10);
            void *addr = (void *)simple_strtoul(argv[3], NULL, 16);
            u32 cnt = simple_strtoul(argv[5], NULL, 16);
            u32 n;
            struct mmc *mmc = find_mmc_device(dev)  
            int blk = simple_strtoul(argv[4], NULL, 16) 
            if (!mmc)
                return 1    
            printf("\nMMC write: dev # %d, block # %d, count %d ... ",
                dev, blk, cnt)  
            mmc_init(mmc)   
            n = mmc->block_dev.block_write(dev, blk, cnt, addr) 
            printf("%d blocks written: %s\n",
                n, (n == cnt) ? "OK" : "ERROR");
            return (n == cnt) ? 0 : 1;
        } else if (strncmp(argv[1], "write.ext4sp",
                        sizeof("write.ext4sp")) == 0) {
    #ifdef CONFIG_EXT4_SPARSE
            int dev = simple_strtoul(argv[2], NULL, 10);
            void *addr = (void *)simple_strtoul(argv[3], NULL, 16);
            u32 blk = simple_strtoul(argv[4], NULL, 16);
            u32 cnt = simple_strtoul(argv[5], NULL, 16);
            struct mmc *mmc = find_mmc_device(dev);
            if (!mmc)
                return 1;
            mmc_init(mmc);
            printf("\nMMC write ext4 sparse: dev # %d, "
                "block # %d, count %d ... \n",
                dev, blk, cnt);
            return ext4_unsparse(mmc, dev, addr, blk, cnt);
    #else
            printf("Not support.\n");
            return 0;
    #endif
    }

    可以查看EMMC有两种写法,一种是write,一种是write.ext4sp,两种写法一定要注意。

    5. update.c编写

    参考代码

    6. tftp等待时间过长

    在使用tftp中,如果网线没有插好,大约有10s等待时间,如果tftp服务器没有开启,大约有20s等待时间。考虑到如果网线没有插,优化10s等待时间意义不大,所以着重优化tftp等待时间。
    通过代码追踪可以看到,阻塞主要发生在net/net.c中

    //----->net/net.c 307行
    NetLoop(proto_t protocol)
    {
        ……
        ……
        // 347行,如果网线没有插上,主要阻塞在eth初始化上,可以优化net/net-drc.c上代码,但是意义不大
        if (eth_init(bd) < 0) {
        eth_halt();
        return(-1);
        }
        ……
        ……
        //384行,在TFTP中有一个数值TftpTimeoutCountMax和TftpTimeout,这两个函数有什么用呢,后续会用到
        //TftpTimeoutCountMax,tftp失败尝试次数
        //TftpTimeout,TFTP timeout处理函数,根据count值重新尝试TFTP
        switch (protocol) {
            case TFTP:
                /* always use ARP to get server ethernet address */
                TftpStart();
                break;
                ……
        }
        ……
        ……
        //487行,此处ARP发包用到了configs/hi3519v101.h中的
        //#define CONFIG_ARP_TIMEOUT        50000UL
        //#define CONFIG_NET_RETRY_COUNT        50
        //进行了50次,50000次电平的发包,当然会慢,注释了相关函数
            ArpTimeoutCheck();
            ………
            ………
            //493行,此处为TFTP time out后的处理
            if (timeHandler && ((get_timer(0) - timeStart) > timeDelta)) {
                thand_f *x;
                …………
        }
        ……
        ……
    }

    所以,想要将时间变短,改变ArpTimeoutCheck()的等待时间即可。
    修改在configs/hi3519v101.h中

    7 Q&A

    1. 每次重新烧写uboot,环境变量不变

    env初始化有两个地方:

    1. arm/lib/board.c中, init_sequence初始化序列中会初始化,但是此时存储类型还没有确定,初始化无用
    2. arm/lib/board.c中433行env_relocate (),初始化存储以后,会根据存储设备,更新关于存储设备的初始化,擦写,读取等操作的动作。此刻再从存储设备中读取环境变量

    在第二次调用的时候,会调用common/env_emmc.c中代码

    //----------------->67行 common/env_emmc.c
    void emmc_env_relocate_spec(void)
    {
        int ret;
        struct mmc *mmc = find_mmc_device(0);
    
        if (!mmc)
            goto error;
    
        mmc_init(mmc);
    
        printf("offset :%x, size %x\n", CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE);
        ret = mmc->block_dev.block_read(0, CONFIG_ENV_OFFSET >> 9,
             CONFIG_ENV_SIZE >> 9, env_ptr);
    
        /* flush cache after read */
        flush_cache((ulong)env_ptr, CONFIG_ENV_SIZE); /* FIXME */
    
        if (ret != (CONFIG_ENV_SIZE >> 9))
            goto error;
    
        if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc)
            goto error;
    
        gd->env_valid = 1;
    
        return;
    
    error:
        puts("*** Warning - bad CRC, using default environment\n\n");
    
        set_default_env();

    得到重要信息,存储在configs/hi3519v101.h中的定义:

    #define CONFIG_ENV_OFFSET 0x80000
    #define CONFIG_ENV_SIZE   0x40000

    可以知道,在uboot 1M的存储空间中,前512K用于存储uboot,后续512K用于存储环境变量。根据需求进行擦写
    问题来了,如果没有tftp升级,那我们应该怎么做呢?哈哈。。有时间做到再说吧。大致在net中增加新的网络驱动,然后在main.c中增加对应代码即可。

    8 测试

    项目 uboot启动时间
    网线不在 11+3+1+1 = 16s
    server IP不存在,TFTP服务未开启 2+3+1+1s =7s
    server IP存在,TFTP服务未开启 2+3+2+1s = 8s
    Server IP存在,TFTP服务开启 根据服务器启动时间

    时间: A+B+C+D

    • A ---初始化与网络初始化
    • B ---arp寻找server
    • C ---tftp尝试
    • D ---bootdelay

    9 patch包

    源文件打包diff

    diff -Naur u-boot-2010.06 u-boot-2010.06-mh > update.patch
    
    diff -Naur 源文件 更改文件 > patch名称
    -N –new-file  在比较目录时,若文件A仅出现在某个目录中,会显示:Onlyin目录;文件A若使用-N参数,则diff会将文件A与一个空白的文件比较。
    -a 将所有文件当作文本文件来处理
    -u 以合并的方式来显示文件内容的不同。
    -r 递归

    atch添加
    在源文件中,执行

    tar -xzvf u-boot-2010.06.tgz
    patch -p0 < update.patch
    
    -pX 表示忽略多少文件夹
    本文发布于2024年11月14日14:42,已经过了409天,若内容或图片失效,请留言反馈
    文章出处: 求索空间
    文章链接: https://blog.askerlab.com/uboot2
    评论列表:
    empty

    暂无评论