zoukankan      html  css  js  c++  java
  • bootloader

    [Makefile]
    [1] 规则
        目标: 依靠
        [TAB]命令(命令名 参数 依靠 目标)
        
    [2] 难点
        1. 自己主动变量
           作用域在一个规则中, 如: $@(目标, 每条规则都仅仅有一个目标), $<(第一个依赖), $^(全部依赖)
           例:
           hello.o world.o: hello.h
           等价于
           hello.o: hello.h
           world.o: hello.h
           
        2. 模式规则
           %.o: %.c
           [TAB]$(CC) $(CFLAGS) -c -o $@ $<
           
        3. 自己主动推倒
           要生成的目标没有显示的规则,这时会去找通用规则(模式规则)。找通用规则的过程叫自己主动推导
           寻找规则的过程:
           1. 在Makefile中,寻找生成hello.o的规则。假设找到...
              如:
              hello.o: hello.c 
              
           2. 在Makefile中, 寻找模式规则, 假设找到匹配的模式规则, 须要查看当前文件夹下是否有模式规则须要的依赖文件
              假设有。则使用模式规则生成目标。假设没有继续寻找下一个模式规则...
              如:
              %.o: %.c
              %.o: %.S
              
           3. 在Make程序内部,寻找模式规则(隐式规则),生成目标
           
    [ARMproject]
    [1] 文件夹
        board         跟主板相关的配置和源代码代码(主板的初始化代码)
        common        主程序源代码及配置
        cpu           启动代码及配置
        drivers       主板无关的设备驱动及配置
        include       头文件
        lib           库源代码及配置
        
    [2] 源文件
        *.c           C语言源码
        *.S           带预处理命令的汇编源码
        map.lds       链接脚本
        Makefile      编译规则
        config.mk     编译命令及编译參数(是Makefile的一部分), 会影响编译命令和參数的硬件文件夹,都须要config.mk
        
    [3] 目标文件
        fsc100        ELF格式的可运行文件
        fsc100.bin    Binary格式的可运行文件
        fsc100.dis    ELF格式的可运行文件反汇编文件
        fsc100.map    具体的符号表文件
        System.map    符号表文件
        
    [4] project解说(Makefile和config.mk)
        Makefile和config.mk编译源码为可运行文件
        注意:每一个模块打包成静态库的优点:
              1. 便于管理
              2. 链接成可运行程序时,没有被启动代码调用的代码不会被链接到可运行文件里
        
    [5] 怎样加入驱动模块?
        1. 加入源码
        2. 加入Makefile
           拷贝别的文件夹下的Makefile。改动下列变量的值:
           LIB                 终于生成的*.a
           SOBJS               *.S源码编译出来的*.o
           COBJS               *.c源码编译出来的*.o
           START               启动代码生成的*.o


        3. 改动顶层文件夹下的Makefile,加入新文件夹生成的目标文件到project
           OBJS                启动代码的目标文件(*.o)
           LIBS                非启动代码的目标文件(*.a)
           
    [u-boot]
    [1] 文件夹
        cpu(cpu/名称/...)                  启动代码、cpu内部驱动和配置
        board(board/芯片厂家/主板名/...)   启动代码中要使用的硬件的驱动代码,主板的初始化代码
        common                             命令输入、解析和运行代码
        net                                网络协议
        drivers                            通用硬件设备驱动
        fs                                 文件系统源代码
        disk                               磁盘分区驱动
        lib_arm                            arm架构的cpu都须要的代码,主板的初始化代码和库代码 
        tools                              工具软件源代码  
        doc                                作者写的源码说明
        README                             简单的u-boot介绍、文件夹树说明、配置项说明
        
    [2] 文件
        1. 源文件
           *.c/*.S/*.h                     源码文件
           Makefile                        编译规则(有源码的文件夹下都会有)
           config.mk                       编译命令和编译參数(cpu、board、lib_cpu架构、nand_spl)
           *.lds                           链接脚本
           rules.mk                        产生*.c(*.S): xxx.h ...
           mkconfig                        配置的shell脚本
           见《文件作用表.bmp》
           
        2. 目标文件
           u-boot                          ELF可运行文件
           u-boot.bin                      Binary可运行文件
           u-boot.map                      具体的符号表
           System.map                      符号表
        
    [3] 特点
        1. 支持非常多CPU
        2. 支持非常多主板
        3. 支持非常多通用硬件
        
        4. project管理和代码比較混乱
        何一款嵌入式产品。都不会用到全部的源代码,所以用u-boot的源代码生成目标文件的步骤:
        1. 选择须要的源码
        2. 编译选取的源码
        
    [4] 编译(必须掌握)
        1. 配置--依据主板的硬件配置和须要的命令。选取须要的源码
           # make fsc100(主板名)_config
           
        2. 编译--编译选取的源码
           # make
           
        3. 清除编译
           # make clean
           
        4. 清除配置
           # make clobber
        
    [5] 配置原理
        1. 过程
           见《u-boot配置流程图》
           
        2. 结果
           include/config.mk                                     选取源码所在的文件夹
           include/config.h                                      选取源码
              include/configs/fsc100.h                           主板的配置项
                 include/config_cmd_default.h                    编译到u-boot可运行程序的命令的配置项
                 (include/config_cmd_all.h                       u-boot源代码中支持的全部命令的配置项)
                 
         3. 原理
            (1) 配置命令
               # make fsc100_config
               
               命令原理
               fsc100_config: unconfig
               @$(MKCONFIG) $(@:_config=) arm arm_cortexa8 fsc100 samsung s5pc1xx
               @mkconfig fsc100(主板名) arm(架构) arm_cortexa8(cpu名) fsc100 samsung(芯片厂家) s5pc1xx(SOC名)
               
            (2) 配置项原理 
                1. 控制源码文件是否编译到u-boot中的配置项--选取文件
                   例: 在include/configs/fsc100.h定义:
                       #define CONFIG_DRIVER_DM9000  1  (顶层文件夹下的Makefile会转换CONFIG_DRIVER_DM9000宏定义为一个Makefile变量)
                                                     1  (CONFIG_DRIVER_DM9000 = y) 
                                                     0  (CONFIG_DRIVER_DM9000 = 0) 
                                                     
                       宏定义(include/configs/fsc100.h)    被转化为(include/autoconf.mk)                 
                       #define CONFIG_DRIVER_DM9000  1     CONFIG_DRIVER_DM9000 = y     
                       
                       Makefile
                          config.mk
                            include/autoconf.mk
                                            
                   在drivers/net/Makefile中引用
                     CONFIG_DRIVER_DM9000=y (include/autoconf.mk)   
                     
                    COBJS-$(CONFIG_DRIVER_DM9000) += dm9000x.o
                    ...
                    
                    COBJS := $(COBJS-y)
                    OBJS := $(COBJS)
                      $(LIBS): .depend $(OBJS)
                            $(AR) ...
                            
                   研究方法:
                   1. 查找CONFIG_DRIVER_DM9000
                      grep CONFIG_DRIVER_DM9000 * -r
                      在autoconf.mk文件里发现"CONFIG_DRIVER_DM9000=y”c
                            
                   2. 找出autoconf.mk文件生成方法
                      grep autoconf.mk * -r
                      在Makefile中找到生成autoconf.mk的规则


                   3. 查看生成autoconf.mk的规则
                   
               2. 条件编译原理选取源码--选取文件里的一部分代码
                  例: 在include/configs/fsc100.h定义:
                      #define CONFIG_CMD_PING  1
                      
               3. 提供数据给源码的配置项
                  宏替换原理
                  例: 在include/configs/fsc100.h定义:
                      #define CONFIG_DM9000_BASE 0x88000000
                      在drivers/net/dm9000x.c里面引用
                 
    [6] 编译原理
        1. 改动代码段的执行地址
           (1) board/$(VENDOR)/$(BOARD)/config.mk
               TEXT_BASE = 代码段执行地址
           
           (2) 链接脚本
               不推荐
        
        2.  子文件夹下的Makefile
           拷贝别的文件夹下的Makefile,改动下列变量的值:
           LIB                 终于生成的*.a
           SOBJS               *.S源码编译出来的*.o
           COBJS               *.c源码编译出来的*.o
           START               启动代码生成的*.o
           
        3. 顶层文件夹的Makefile
           OBJS                启动代码的目标文件(也能够在链接脚本中实现)
           LIBS                非启动代码的目标文件
           
    [7] 怎样加入模块到project中?(必须掌握)
        1. 加入源码
           例:
           drivers/uart/uart.c(记得在start_armboot函数中调用这个文件里的函数,否则uart.c中的函数不会被链接到终于的可运行代码中)
           
        2. 子文件夹下的Makefile
           拷贝别的文件夹下的Makefile,改动下列变量的值:
           LIB                 终于生成的*.a
           SOBJS               *.S源码编译出来的*.o
           COBJS               *.c源码编译出来的*.o
           START               启动代码生成的*.o
           
           例:
           LIB := libuart.a
           SOBJS := 
           COBJS := uart.o(非启动代码的*.c编译出来的目标文件)
           START := uart.o(启动代码编译出来的目标文件)
           
         3. 改动顶层文件夹下的Makefile,加入新文件夹生成的目标文件到project
            (1) 假设加入的模块是非启动代码, 加入模块的Makefile生成的目标文件到project
                (将目标文件放入顶层文件夹下的Makfile的LIBS变量中)
                例:
                LIBS += drivers/uart/libuart.a
                
            (2) 假设加入的模块是启动代码,方法例如以下:
                1. 加入模块的Makefile生成的目标文件到project
                   (将目标文件放入顶层文件夹下的Makfile的OBJS变量中)
                   例:
                   OBJS += drivers/uart/uart.o
                
                   $(OBJS):...                 (应该是标志的u-boot做好的)
                      $(MAKE) -c $(dir $@) $(notdir $@)
                   
                 2. 加入模块的Makefile生成的目标文件到project
                    (1) 将目标文件放入顶层文件夹下的Makfile的OBJS(LIBS)变量中
                        例:
                        LIBS += drivers/uart/libuart.a
                        
                    (2) 在链接脚本中改动链接顺序
                        例:
                        .text :                      (不推荐)
                        {
                           cpu/arm_cortexa8/start.o (.text)
                           drivers/uart/uart.o      (.text)
                           *.o                      (.text)
                        }    


    [8] 源码编辑软件
        vi + ctags                     查看源码
        1. 进入顶层文件夹。然后打开随意的文件。然后按F9新建tags索引文件
        2. 查找定义: 把光标移动要查找的标识符,然后按ctrl+]
        3. 回退: ctrl + o
        
        sourceinside
        建立project,加入全部文件到project
        
    [9] 源码分析
        (1) 启动操作系统
            第一阶段: 自启动
                      硬件初始化
                         CPU初始化
                            关闭中断进入SVC模式
                            关闭MMU和Cache
                         
                         SOC初始化
                             假设有,须要关闭看门狗
                             假设须要加快代码的运行速度, 初始化PLL(clock control)
                             UART初始化
                             假设须要用到DMA,就初始化
                             Nandflash控制器初始化(为读驱动做准备)
                             
                         主板初始化(驱动)
                             初始化SDRAM
                             驱动Nandflash读操作
                      
                      软件环境建立
                         自复制到SDRAM
                         预留malloc动态分配区
                         给全局数据预留内存
                         栈内存分配(每种模式下都须要分配自己的栈)
                         .BSS段清0
                         跳转到SDRAM中执行
                     主板初始化(全部软硬件模块的初始化都须要在这里完毕)
                      
            第二阶段: 命令运行阶段
                      接收命令输入
                      解析命令
                      运行命令
                         协议栈
                         文件系统
                         软件驱动
                         硬件驱动
                         
            第三阶段: 启动操作系统(bootm)
                      建立操作系统执行环境
                        linux内核启动前提条件:
                          1. 关闭中断,进入SVC
                          2. 关闭MMU和Cache
                          3. R0     0                             不是强制
                          4. R1     arch number(machine type id)  强制
                          5. R2     内核參数指针(atags list)      不必须
                          
                         传递參数给内核
                            atags list
                            struct传递方式(内存地址 + 0x100 BSP确定)
                          
                      跳转到内核代码执行
                      
            
                cpu/arm_cortexa8/start.S(reset --->cpu_init_crit)
             -->board/samsung/fsc100/lowlevel_init.S(lowlevel_init-->system_clock_init)
                                                          -->uart_asm_init
                                                          -->dma_init
                                                          -->nand_pin_mux
                                                          ?-->board/samsung/fsc100/mem_setup.S(mem_ctrl_asm_init)
                                                          -->wakeup_reset
             ?-->board/samsung/fsc100/nand_cp.c(stack_setup)
             -->copy_uboot_to_ram
             -->board/samsung/fsc100/nand_cp.c(stack_setup)
             -->lib_arm/board.c(start_armboot)
                                -->common/main.c(main_loop)
       
        
          reset                            复位异常启动的正常代码
          cpu_init_crit                    关闭MMU和cache
          lowlevel_init                    关闭看门狗、初始化SRAM接口、初始化中断控制器、初始化PLL
          system_clock_init                初始化PLL
          dma_init                         初始化dma
          nand_pin_mux                     初始化nandflash的多功能管脚
          nand_asm_init                    nandflash初始化
          mem_ctrl_asm_init                DDR控制器初始化
          wakeup_reset                     唤醒复位
          stack_setup                      栈初始化
          start_armboot                    软硬件初始化
          main_loop                        初始化及命令循环
                 
            
        (2) 关键代码理解
             /*******************************************/
               // 推断u-boot当前执行位置(iRAM SDRAM)
               ldr r0, =0xff0fffff
               r0: 0xff0fffff         (20-23bit)

            @清除除20-23bit以外的全部位
            @u-boot仅仅有可能执行在iRAM和SDRAM
            @假设执行在iRAM中, pc的值为(0x20000 - 0x38000)
            @     r1:0x00020000
            @BIC  r0:0xff0fffff 
            -------------------
            @        0x00000000
            
            @假设执行在SDRAM中, pc的值为(0x2ff80000-...)
            @     r1:0x2ff80000
            @BIC  r0:0xff0fffff 
            -------------------
            @        0x00f00000
            
            bic r1, pc, r0

            @ 读代替码段的起始地址
            @ 0x2ff80000-->r2
            ldr r2, _TEXT_BASE(0x2ff80000)
            
            @     r2:0x2ff80000
            @BIC  r0:0xff0fffff 
            -------------------
            @        0x00f00000
       
            bic r2, r2, r0

            @ 推断代码是否执行在SDRAM中
            @ 假设R1==R2, 在SDRAM中执行, 说明当前从USB启动
            @ 假设R1!=R2, 在iRAM中执行。 说明当前从Nandflash启动
            cmp r1, r2
               beq 1f
               
            /*******************************************/
               代码一:   
               val = test;
               // test-->R0  ldr R0, test
               // R0-->val   str r0, val
               tmp = test;
               // R0-->tmp   str r0, tmp
               
               代码二:
               val = test;
               // test-->R0  ldr R0, test
               // R0-->val   str r0, val
               __asm__ __volatile__("":::"memory");
               tmp = test;   
               // test-->R0  ldr R0, test
               // R0-->tmp   str r0, val
               
              /*******************************************/
               #使用方法
               定义:
               #define TOSTR(name) #name
               使用:
               TOSTR(go)     <----> "go"
               
               ##使用方法
               定义:
               #define U_BOOT_CMD(name) __u_boot_cmd_##name
               使用:
               U_BOOT_CMD(go) <----> __u_boot_cmd_go
               cmd_tbl_t __u_boot_cmd_go __attribute__ ((unused, section(".u_boot_cmd"))) = {"go", ...}
               
    [10] 移植
         1. project配置
            // $ make fsc100(主板名)_config
            (1) 加入配置
                在參考板(芯片厂家提供的开发板)的配置命令:
                smdkc100_config: unconfig(删除曾经配置)
                   @$(MKCONFIG) $(@:_config=) arm arm_cortexa8 smdkc100 samsung s5pc1xx
                   @mkconfig smdkc100(主板名) arm(架构) arm_cortexa8(cpu名) smdkc100 samsung(芯片厂家) s5pc1xx(SOC名)
                   
                以下加入同样命令,而且改动主板名:
                fsc100_config: unconfig(删除曾经配置)
                   @$(MKCONFIG) $(@:_config=) arm arm_cortexa8 fsc100 samsung s5pc1xx
                
           2. 加入主板文件夹(board/samsung/smdkc100)
              拷贝“芯片厂家开发板的主板文件夹”到 “新的主板文件夹”
              $ cp board/VENDOR/BOARD/ board/VENDOR/新主板名/ -a
              $ mv board/VENDOR/BOARD/BOARD.c board/VENDOR/新主板名/新主板名.c
              改动Makefile,内容例如以下:
              COBJS := BOARD.o ...
              改为
              COBJS := 新主板名.o ...
              
           3. 加入主板配置文件
              拷贝“芯片厂家开发板的主板配置”到“新的主板配置”
              $cp include/configs/BOARD.h include/configs/新主板名.h
              
           4. 改动改交叉编译器(顶层文件夹Makefile)
              CROSS_COMPILE ?

    = arm-cortex_a8-linux-gnueabi-
           
         2. 代码移植
            (1) 启动代码移植
                1. 硬件初始化
                   CPU初始化(新主板和芯片厂家开发板芯片同样,cpu同样。所以做移植不过更改cpu/CPU/start.S)
                      关闭中断进入SVC模式(确认)
                      关闭MMU和Cache(确认)
                      
                   SOC初始化 (cpu/CPU/start.S 或 cpu/CPU/芯片名/...(确认) 或 board/VENDOR/新主板名/...)
                      假设须要。关闭看门狗(确认)
                      假设须要加快代码运行速度。初始化PLL(clk control)(确认/加入)
                      UART初始化(确认)
                      假设须要。初始化DMA(概率非常小)(加入)
                      Nandflash初始化(为读驱动做准备)(确认)
                      
                   主板初始化(驱动)(board/VENDOR/新主板名/...)
                      初始化SDRAM(内存类型发生变化。须要改动内存的初始化參数)
                      驱动Nand的读操作(Nand类型发生变化。须要改动)
                   
                2. 软件环境建立
                      自复制到SDRAM(flash类型或代码段位置变化。须要改动)
                      预留malloc动态分配区(确认,改动大小)
                      给全局数据预留内存(确认)
                      栈分配(预留)内存(每种模式下都须要分配自己的栈)(栈位置变化,须要改动)
                      BSS段清0(确认)
                      跳转到SDRAM中执行(确认)
                      主板初始化(u-boot中须要使用的软件模块和硬件驱动模块的初始化都在这里完毕)(须要依据硬件信息改动)
                 效果:能够执行到命令提示符
                 注意:可能须要加入模块。方法见《[7] 怎样加入模块到project中?》
                 
            (2) 命令移植(实现命令运行代码)
                接收命令输入
                解析命令
                运行命令
                   协议栈(依据命令须要加入)
                   文件系统(依据命令须要加入)
                   软件驱动(依据命令须要加入)
                   硬件驱动(依据命令须要加入)
                   
                include/config_cmd_default.h                   编译到u-boot可运行程序的命令的配置项
                include/config_cmd_all.h                       u-boot源代码中支持的全部命令的配置项
                注意:可能须要加入模块,方法见《[7] 怎样加入模块到project中?》
                
            (3) 启动操作系统(bootm)
                改动board/VENDOR/新主板名/新主板名.c文件里的代码。例如以下:
                int board_init(void)
                {
                   ....
                   gd->bd->bi_arch_number = 主板ID(来自于内核);
                gd->bd->bi_boot_params = 内核參数存放位置(物理内存的首地址+0x100);
                ....
             }


                int dram_init(void)
                {
                gd->bd->bi_dram[0].start = 物理内存的首地址;
                gd->bd->bi_dram[0].size = 物理内存大小;
      ...
                }


               

    版权声明:本文博客原创文章。博客,未经同意,不得转载。

  • 相关阅读:
    迭代器(Iterator)的使用
    xml转array
    linux 常用命令
    yii2 httpClient的用法
    将普通用户添加到sudo
    yii2 注册一个新事件(trigger Event)
    解决IDEA输入法输入中文候选框不显示问题(亲测谷歌拼音完美解决问题)
    5个用/不用GraphQL的理由
    Linux 用户必须知道的 14 个常用 Linux 终端快捷键
    java执行系统命令, 返回执行结果
  • 原文地址:https://www.cnblogs.com/blfshiye/p/4655424.html
Copyright © 2011-2022 走看看