注册名:

密码:

个人注册

企业注册

商务申请

商务管理平台

企业管理平台

个人管理平台

我的工控博客

中国工控网www.chinakong.com

首页 | 新闻中心 | 工控论坛 | 经验视点 | 工控商务 | 电气手册 | 工控博客 | 招聘求职 | 网上调查 | 企业中心 | 供求信息 | 资料中心 | 工控书店

所在位置:工控博客苑 -- 莱得科技 -- 深入剖析barebox(U-BOOT-II)在i.MX27上的移植

中国工控网搜索:

杨先生

     学历:硕士研究生
     职称:高级工程师
     年龄:50岁
       新闻信息(0/0)
       工作图片(0)
       技术论文(3/1)
       交流论坛(0/0)
       留言信箱(0)
       浏览人次:4153

深入剖析barebox(U-BOOT-II)在i.MX27上的移植

发表评论(0)    作者:杨勇    发布时间:2011年10月31日  

    深入剖析barebox(U-BOOT-II)在i.MX27上的移植
   
   
    Barebox是一个在uboot的基础上发展起来的一个新的功能强大的bootloader,它有着非常直观的设备模型和友好的编程接口,使用方便、功能强大。以下使用平台为成都莱得科技的i.MX27开发板上,平台稳定并应用到数十个客户的产品中。以下是详细的移植步骤。
    文章版权属于成都莱得科技有限责任公司所有,转载请注明出处。
    网址:www.nidetech.com,联系电话:18080873876,邮箱:nidetech@163.com,技术交流QQ:1460879610
   
    1 、barebox的特色和亮点
    1.1 POSIX File API
    barebox使用open/close/read/write/lseek 函数,并且提供了设备文件的模型,可以通过使用像linux下被人熟知的这些API函数来操作设备。
    1.2 Shell
    提供了标准的shell 命令,例如 cd/ls/cat/echo …等等
    1.3 Envionment Filesystem
    barebox可以像操作文件一样的将环境变量保存在一个flash中,并且可以加载到ram中来。环境变量被模拟成了一个文件,可以通过barebox下简单的文本编辑器来编辑和保存环境变量文件。
    1.4 Filesystem Support
    当barebox启动后,环境变量被挂载到/env/目录下,所有的设备被挂载到/dev目录下,我们可以通过标准的API函数来操作这些设备文件,就像在linux下一样。并且还可以通过mount指令挂载其他文件系统
    1.5 Driver Model(borrowed from Linux)
    提供了像linux一样的驱动开发模型,在板级支持文件中定义所有的设备名,然后在驱动中通过相同的名字负责探测和管理这些设备。
    1.6 Clocksource
    提供了在linux下熟知的clocksource函数,来用统一的管理时钟,例如clk_get,clk_enable等
    1.7 Sandbox
    你可以通过将barebox选择成为sandbox来方便的进行barebox开发,他会将barebox编译成为在linux用户空间上的一个POSIX应用程序。方便开发和调试。
    1.8 Device Parameters
    barebox提供了一个参数模型,每个设备都可以指定他们自己的参数,可以通过<devid>.<param> = “….”这样的方式来修改设备的参数,例如可以通过eth0.ipaddr=xxx.xxx.xxx.xxx来修改第一块网卡的ip地址。
    1.9 Getopt
    barebox实现了一个简单的getopt,提供了一种比通过位置来传递参数更方便的模式。
    1.10 Integrated Editor
    内部集成了一个文本编辑器
    1.11 集成了许多方便调试的命令和功能
     md –s /dev/mem [寄存器的物理地址] 可以查看任意寄存器和物理地址空间中的值。
     mw 可以设置某个寄存器或者物理内存地址的值。
     gpio_set_value/gpio_get_value 可以设置某个gpio的输出。
    1.12 支持网络更新自身、内核、文件系统、环境变量。
    1. 可以支持通过串口或者网络更新barebox自身
    2. 可以通过网络下载zImage,uImage,raw格式的内核
    3. 支持jffs2,ubifs等格式的文件系统映像
    2 、barebox的目录结构和移植相关文件
    arch/*/ -> contains architecture specific parts
    arch/*/mach-*/ -> SoC specific code
    drivers/serial -> drivers
    drivers/net
    drivers/...
    include/asm-* -> architecture specific includes
    include/asm-*/arch-* -> SoC specific includes
    fs/ -> filesystem support and filesystem drivers
    lib/ -> generic library functions (getopt, readline and the
    like)
    common/ -> common stuff
    commands/ -> many things previously in common/cmd_*, one command
    per file
    net/ -> Networking stuff
    scripts/ -> Kconfig system
    Documentation/ -> Parts of the documentation, also doxygen
   
    移植相关的文件:按启动顺序
    arch/arm/lib/barebox.lds.S -> 针对arm体系下,Barebox的链接脚本,定义了barebox映像在内存中的布局
    arch/arm/cpu/start.c -> arm体系,barebox针对arm体系的入口函数,负责arm体系的初始化
    arch/arm/boards/xxx/ lowlevel_init.[c/S] -> 针对某个具体ARM芯片的低层初始化文件,板级初始化文件
    common/startup.c -> 与体系架构无关的 barebox 启动代码,加载各个驱动,并且启动barebox的shell,等待用户输入。
    3 、barebox的详细启动流程
     i.MX27冷启动后,nand flash控制器中有2K 的SRAM会被用作boot ram,根据硬件设计如果支持从nandflash启动的话,mx27会将NAND FLASH中的前2K的数据自动拷贝到NAND FLASH 控制器中的 RAM中,并且自动跳转到该2K RAM的开始地址 0xD8000000开始执行。因为我们的barebox是被atk烧写工具烧写到nandflash的0地址处,所以barebox的前2K代码会被拷贝到0xD8000000,barebox从而获得被调用的机会。
     那么barebox中首先得以执行的函数或者文件在什么地方啦?这就要查看barebox的链接脚本了,对于每种cpu架构,barebox在其架构名所在的文件夹中都有一个针对该架构的链接文件,对于arm来说,就是arch/arm/lib/barebox.lds.S文件,我们来详细分析一下该文件,弄清楚barebox在运行时候的映像分布情况
   
    arch/arm/lib/barebox.lds.S
    该头文件中定义了一些节的定义宏,方便barebox来定义输出节名
    #include <asm-generic/barebox.lds.h>
   
    OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
    OUTPUT_ARCH(arm)
    ENTRY指示出了Barebox的入口点,也就是在barebox被执行的第一条指令的地方。
    ENTRY(exception_vectors)
    以下描述了barebox中各个代码和数据的分布情况
    SECTIONS
    {
    定义了barebox最终需要重定位的地址,在我们的开发板中定义为0xa7f00000,也就是硬件上设计的SDRAM的最后1M地址处,这里说的地址都是物理地址,没有经过mmu转化的。
    . = TEXT_BASE;
    这个是对于支持内部启动的imx系列cpu才支持的flash header比如mx51/53等,对于mx27来说,这个宏是个空的,也就是没有定义,所以我们不需要去管
    PRE_IMAGE
    代码段需要以4字节对齐,以便取指令
    . = ALIGN(4);
    定义了代码段,对于mx27来说代码段的地址等于TEXT_BASE
    .text :
    {
    _stext = .; 将代码段和只读数据段的起始处 == TEXT_BASE
    _text = .; 和上面的_stext一样
    *(.text_entry*) 所有的代码段
    #ifdef CONFIG_ARCH_EP93XX
    /* the EP93xx expects to find the pattern 'CRUS' at 0x1000 */
    . = 0x1000;
    LONG(0x53555243) /* 'CRUS' */
    #endif
    *(.text_bare_init*)
    *(.text*)
    }
   
    . = ALIGN(4);
    .rodata : { *(.rodata*) }
   
    #ifdef CONFIG_ARM_UNWIND
    /*
    * Stack unwinding tables
    */
    . = ALIGN(8);
    .ARM.unwind_idx : {
    __start_unwind_idx = .;
    *(.ARM.exidx*)
    __stop_unwind_idx = .;
    }
    .ARM.unwind_tab : {
    __start_unwind_tab = .;
    *(.ARM.extab*)
    __stop_unwind_tab = .;
    }
    #endif
    _etext = .; /* End of text and rodata section */
   
    . = ALIGN(4);
    .data : { *(.data*) }
   
    . = ALIGN(4);
    .got : { *(.got*) }
   
    . = .;
    __barebox_cmd_start = .;
    .barebox_cmd : { BAREBOX_CMDS }
    __barebox_cmd_end = .;
   
    __barebox_initcalls_start = .;
    .barebox_initcalls : { INITCALLS }
    __barebox_initcalls_end = .;
   
    __usymtab_start = .;
    __usymtab : { BAREBOX_SYMS }
    __usymtab_end = .;
   
    . = ALIGN(4);
    __bss_start = .;
    .bss : { *(.bss*) }
    __bss_stop = .;
    _end = .;
    _barebox_image_size = __bss_start - _text;
    }
    从上面的链接脚本可以看出barebox首先被执行的是 exception_vectors
    该函数在 arch/arm/cpu/start.c
    void __naked __section(.text_entry) exception_vectors(void)
    {
    __asm__ __volatile__ (
    "b reset\n" /* reset */
    #ifdef CONFIG_ARM_EXCEPTIONS
    "ldr pc, =undefined_instruction\n" /* undefined instruction */
    "ldr pc, =software_interrupt\n" /* software interrupt (SWI) */
    "ldr pc, =prefetch_abort\n" /* prefetch abort */
    "ldr pc, =data_abort\n" /* data abort */
    "1: bne 1b\n" /* (reserved) */
    "ldr pc, =irq\n" /* irq (interrupt) */
    "ldr pc, =fiq\n" /* fiq (fast interrupt) */
    #else
    "1: bne 1b\n" /* undefined instruction */
    "1: bne 1b\n" /* software interrupt (SWI) */
    "1: bne 1b\n" /* prefetch abort */
    "1: bne 1b\n" /* data abort */
    "1: bne 1b\n" /* (reserved) */
    "1: bne 1b\n" /* irq (interrupt) */
    "1: bne 1b\n" /* fiq (fast interrupt) */
    #endif
    ".word 0x65726162\n" /* 'bare' */
    ".word 0x00786f62\n" /* 'box' */
    ".word _text\n" /* text base. If copied there,
    * barebox can skip relocation
    */
    ".word _barebox_image_size\n" /* image size to copy */
    );
    }
    由此可见第一条指令就是b reset, barebox然后跳转到了reset函数
    该函数在 arch/arm/cpu/start.c
   
    board_init_lowlevel:
   
    mov r10, lr
    /* ahb lite ip interface */
    writel(0x20040304, AIPI1_PSR0)
    writel(0xDFFBFCFB, AIPI1_PSR1)
    writel(0x00000000, AIPI2_PSR0)
    writel(0xFFFFFFFF, AIPI2_PSR1)
   
    /* skip sdram initialization if we run from ram [0xa0000000--0xc0000000 is ram space]*/
    cmp pc, #0xa0000000
    bls 1f
    cmp pc, #0xc0000000
    bhi 1f
   
    mov pc,r10
   
    1:
    writel(IMX_PLL_PD(0) |
    IMX_PLL_MFD(51) |
    IMX_PLL_MFI(7) |
    IMX_PLL_MFN(35), MPCTL0) /* 399 MHz */
   
    writel(IMX_PLL_PD(1) |
    IMX_PLL_MFD(12) |
    IMX_PLL_MFI(9) |
    IMX_PLL_MFN(3), SPCTL0) /* SPLL = 2 * 26 * 4.61538 MHz = 240 MHz */
   
    writel(CSCR_MPLL_RESTART | CSCR_SPLL_RESTART | CSCR_ARM_SRC_MPLL |
    CSCR_MCU_SEL | CSCR_SP_SEL | CSCR_FPM_EN | CSCR_MPEN |
    CSCR_SPEN | CSCR_ARM_DIV(0) | CSCR_AHB_DIV(1) | CSCR_USB_DIV(3) |
    CSCR_SD_CNT(3) | CSCR_SSI2_SEL | CSCR_SSI1_SEL | CSCR_H264_SEL |
    CSCR_MSHC_SEL, CSCR)
   
    sdram_init
    #ifdef CONFIG_NAND_IMX_BOOT
    ldr sp, =0xa0f00000 /* Setup a temporary stack in SDRAM [ram start+15M offset] */
   
    ldr r0, =IMX_NFC_BASE /* start of NFC SRAM */
    ldr r2, =IMX_NFC_BASE + 0x1000 /* end of NFC SRAM */
   
    /* skip NAND boot if not running from NFC space */
    cmp pc, r0
    bls ret #if(pc < r0) goto ret
    cmp pc, r2
    bhi ret ##if(pc > r2) goto ret
   
    /* Move ourselves out of NFC SRAM */
    ldr r1, =TEXT_BASE #0xa7f0_0000
   
    copy_loop:
    ldmia r0!, {r3-r9} /* copy from source address [r0] */
    stmia r1!, {r3-r9} /* copy to target address [r1] */
    cmp r0, r2 /* until source end addreee [r2] */
    ble copy_loop
   
    ldr pc, =1f /* Jump to SDRAM */
    1:
    bl nand_boot /* Load barebox from NAND Flash */
   
    ldr r1, =IMX_NFC_BASE - TEXT_BASE
    sub r10, r10, r1 /* adjust return address from NFC SRAM */
    /* to SDRAM */
   
    #endif /* CONFIG_NAND_IMX_BOOT */
   
    ret:
    mov pc,r10
   
    Reset函数流程如下
     ARM进入svc32 mode 因为arm芯片内部有许多寄存器都只能在SVC32mode下才能访问,否则会产生abort异常,或则读取不到数据。比如mx27内部的AIPI模块
     禁用MMU和cache 必须采用物理地址访问而且不能进行cache。
     调用board_init_lowlevel函数进行板级初始化
     调用board_init_lowlevel_return进行代码的重定位
    board_init_lowlevel函数在 arch/arm/boards/你的电路板目录下面/lowlevel.c文件中,流程如下
     保护lr地址到r10中,以便返回
     配置AIPI接口,设置外设模块总线宽度
     判断当前程序是否在RAM中执行,如果已经在RAM中则直接返回,因为有可能barebox是通过tftp下载到ram中,然后再跳转执行的,因为已经完成了后面的初始化了,所以直接返回。
     设置MPLL和SPLL时钟 分别为400M和240M
     设置时钟源寄存器,从而设置各个外设的时钟
     初始化SDRAM
     判断自己是否在NANDFLASH的内部RAM中,如果是则将自己拷贝2K到SDRAM中的TEXT_BASE处
     拷贝完毕后,跳转到SDRAM中接着从跳转前的偏移位置处开始执行,也就是bl nand_boot,至于为什么要这样做,是因为接下来马上要从NANDFLASH中拷贝剩余的barebox代码到SDRAM中,而一旦拷贝就需要用到NAND FLASH控制器中的SRAM也就是boot ram,而拷贝前我们代码正是在这段ram中,所以我们要把这段ram让出来。
     现在我们的pc指针已经在sdram中了,接着调用nand_boot函数将nandflash中的barebox完整的拷贝到sdram的TEXT_BASE处。拷贝的大小是256K。
     接着计算出NANDFLASH控制器中RAM的起始地址和SDRAM的TEXT_BASE的偏移地址,然后将第一步保存的lr地址减去计算出来的偏移地址就得到了,board_init_lowlevel函数经过重新拷贝到sdram中后应该返回的地址。然后跳转到该地址去
     跳转到board_init_lowlevel_return 继续执行
     那么你可能会问了,既然mx27在冷启动的时候只能复制nandflash中的前2k代码出来,那么我们怎么才能保证exception_vectors和reset函数,还有board_init_lowlevel函数,以及nand_boot函数在前面的2K代码内。这还得看链接文件
    .text :
    {
    _stext = .;
    _text = .;
    *(.text_entry*)
    *(.text_bare_init*)
    *(.text*)
    }
    看看几个函数的申明
    void __naked __section(.text_entry) exception_vectors(void)
    说明exception_vectors在*(.text_entry*)节中
    void __naked __bare_init reset(void)
    void __bare_init nand_boot(void)
    .section ".text_bare_init","ax”
    .globl board_init_lowlevel
    board_init_lowlevel:
    #define __bare_init __section(.text_bare_init.text)
    说明这三个函数在*(.text_bare_init*)节中,这样就保证了这4个关键函数在最前面的2K,不会将他们链接到其他地方去。
   
     board_init_lowlevel_return函数主要是继续地址重定位,当barebox的代码定位到TEXT_BASE处,为什么前面的board_init_lowlevel中以及进行了重定位了,这里还需要重定位啦?这是因为board_init_lowlevel中的重定位操作是可以配置的,也就是某些平台可能没有启用NAND FLASH启动方式,所以这里需要再检查一下是否以及重定位了,如果没有在进行一次重定位操作,然后跳转到start_barebox中,完成了最底层的初始化加载操作。
     start_barebox 函数在common/startup.c中,主要按照预先设置的顺序调用核心模块以及负责加载驱动等等,所有在这里被调用的函数都属于barebox.lds.S中
    __barebox_initcalls_start = .;
    .barebox_initcalls : { INITCALLS }
    __barebox_initcalls_end = .;
   
    #define INITCALLS \
    KEEP(*(.initcall.0)) \
    KEEP(*(.initcall.1)) \
    KEEP(*(.initcall.2)) \
    KEEP(*(.initcall.3)) \
    KEEP(*(.initcall.4)) \
    KEEP(*(.initcall.5)) \
    KEEP(*(.initcall.6)) \
    KEEP(*(.initcall.7)) \
    KEEP(*(.initcall.8)) \
    KEEP(*(.initcall.9)) \
    KEEP(*(.initcall.10)) \
    KEEP(*(.initcall.11))
   
    #define pure_initcall(fn) __define_initcall("0",fn,0)
   
    #define core_initcall(fn) __define_initcall("1",fn,1)
    #define postcore_initcall(fn) __define_initcall("2",fn,2)
    #define console_initcall(fn) __define_initcall("3",fn,3)
    #define postconsole_initcall(fn) __define_initcall("4",fn,4)
    #define mem_initcall(fn) __define_initcall("5",fn,5)
    #define mmu_initcall(fn) __define_initcall("6",fn,6)
    #define postmmu_initcall(fn) __define_initcall("7",fn,7)
    #define coredevice_initcall(fn) __define_initcall("8",fn,8)
    #define fs_initcall(fn) __define_initcall("9",fn,9)
    #define device_initcall(fn) __define_initcall("10",fn,10)
    #define late_initcall(fn) __define_initcall("11",fn,11)
   
    和linux驱动一样的处理方式。所以我们只要熟知linux内核,理解起barebox就很容易了,他里面很多东西都是借鉴的linux内核。
    从上面可以看出core_initcall所定义的函数调用的优先级别最高,比如
    core_initcall(clocksource_init); clocksource_init函数是delay系列函数实现的基础,而该函数是非常基础的函数,所以我们要将clocksource_init定义为高优先级函数。至于我们的设备驱动则是device_initcall(fn),可以看出他的优先级是比较低的了,可以看出设备驱动的加载必须是再barebox的核心组件都初始化完毕后再加载的。
    core_initcall(arm_mem_malloc_init);负责malloc、free函数的实现
    postcore_initcall(getc_buffer_flush);
    postcore_initcall(env_push_context);
    postcore_initcall(init_cwd);
    postcore_initcall(net_init);
    postcore_initcall(hist_init);
    在完成init系列函数的初始化后,barebox开始从flash中加载环境变量,并且将环境变量挂载到/env目录下,包括
    Env/config 配置文件
    Env/bin/init 根据配置文件中的内容执行一些初始化
    Env/bin/boot 加载内核的一些列脚本
    最后进入shell死循环,等待用户的输入。如果用户不输入,超时时间到达后,自动执行env/bin/boot脚本,完成内核的加载。Barebox至此启动完成。
    4 、移植步骤
     从官网下载并且解压barebox
     cd ~
     git clone git://git.pengutronix.de/git/barebox.git barebox
     cd barebox
     在arch/arm/boards下建立硬件电路板对应的工程文件夹,比如我们的电路板叫nidetech-mx27
     mkdir –p arch/arm/boards/nidetech-mx27
     复制和你开发板最相近的板级支持工程文件到你的工程目录,我们的开发板跟pca100比较相近,所以我选择这个工程作为模板
     cd arch/arm/boards/nidetech-mx27
     cp arch/arm/boards/phycard-i.MX27/* ./ -a
     复制默认的环境变量文件夹到工程目录
     cp defaultenv/config arch/arm/boards/nidetech-mx27/env
     修改Kconfig和Makefile以支持新的电路板nidetech-mx27
    根据cpu的平台选择相应的Kconfig来修改,例如nidetech-mx27是采用的是freescale的imx系列cpu,那么我们就要修改如下文件
    Arch/arm/mach-imx/Kconfig
    在其中为自己的电路板添加一项
    因为我们的cpu是imx27,所以找到红色处,再其后面再添加一项基于mx27的电路板
    if ARCH_IMX27
    choice
    prompt "i.MX27 Board Type"
    config MACH_NIDETECH_MX27
    bool "nidetech-i.MX27"
    select MACH_HAS_LOWLEVEL_INIT
    help
    Say Y here if you are using Phytec's nidetech-i.MX27 equipped
    with a Freescale i.MX27 Processor
   
    a.定义boardinfo,boardinfo就是barebox启动后打印出来的板子的名称信息
    config BOARDINFO
    default "Nidetech i.MX27" if MACH_NIDETECH_MX27
    b. 定义默认的代码段基地址 TEXT_BASE
    config ARCH_TEXT_BASE
    default 0xa7f00000 if MACH_PCA100
    c.定义自己的mach-type
    在 arch/arm/tools/mach-types文件中添加自己的mach-type
    d.修改makefile文件,支持新工程的编译
    在arch/arm/Makefile中添加
    Board-$(CONFIG_MACH_NIDETECH_MX27) := nidetech-mx27(工程目录名)
     配置并编译,barebox支持menuconfig,通过可视化菜单来选择
    电路板的相关配置
    make ARCH=arm CROSS_COMPILE=arm-v5te-linux-gnueabi- menuconfig
     设置System Type
   

chinakong.com中国工控网


    在这里可以选择你自己的电路板类型nidetech-i.MX27
    如果需要支持从NAND FLASH中启动bootloader的话,必须选择support starting barebox from nand
     General Setting
   

chinakong.com中国工控网


   
   

chinakong.com中国工控网


   
    如果你想支持tab键自动补齐命令和文件名的话,必须选择Enable command line editing 和 enable auto completion
    选择activate first console on startup
    如果想使用单独的默认的环境变量的话,选择compile in default environment,并且制定默认环境的路径在你新建板子的工程目录下。
    make ARCH=arm CROSS_COMPILE=arm-v5te-linux-gnueabi- 来编译。编译成功的话,可以在根目录下发现barebox.bin文件。
    5 、运行
    1. 用atktool工具将barebox.bin烧入到nandflash的地址0x0
    2. barebox的环境变量介绍
    barebox的环境变量保持在/env/config文件中。我们可以用edit命令来编辑该文件
    edit env/config
    用ctrl+d保存修改并退出,ctrl+c取消修改并退出
    eth0.ipaddr=192.168.50.127 :设备ip地址
    eth0.netmask=255.255.255.0 :设备的子网掩码
    eth0.gateway=192.168.50.1 :设备的网关
    eth0.serverip=192.168.50.87 : 服务器ip,通常是内核或者nfs服务器的地址
    eth0.ethaddr=00:11:22:33:44:aa :设备的mac地址
    ip = none :不在内核中设置ip地址 dhcp通过dhcp来获取设备ip
    # can be either 'nfs', 'tftp', 'nor' or 'nand'
    kernel_loc=nand :内核的加载路径,可以支持tftp加载serverip的上的内核,或者从nand中启动
    # can be either 'net', 'nor', 'nand' or 'initrd'
    rootfs_loc=nand :rootfs的加载路径,可以支持从nfs启动,或则从本机的nand启动
    支持zImage和uImage,raw,raw_lzo类型的内核
    # The image type of the kernel. Can be uimage, zimage, raw, or raw_lzo
    #kernelimage_type=zimage
    #kernelimage=zImage-$machine
    kernelimage_type=uimage
    kernelimage=uImage-$machine
    #kernelimage_type=raw
    #kernelimage=Image-$machine
    #kernelimage_type=raw_lzo
    #kernelimage=Image-$machine.lzo
   
    修改后,如果需要保存到flash中,则必须输入save命令即可。
   
    如何更新barebox,内核,以及文件系统
    Update –t barebox –d nand
    通过tftp从服务器上取得barebox.bin文件,并且重新写入到barebox所在的分区。
    Update –t kernel –d nand : 下载并烧写内核
    Update –t rootfs –d nand :下载并烧写文件系统
    6 、Barebox链接技术详细分析
    Barebox的链接文件
    arch\arm\mach-imx\include\mach\barebox.lds.h 定义了内部启动模式下的映像头部信息
    #ifdef CONFIG_ARCH_IMX_INTERNAL_BOOT
    #define PRE_IMAGE \
    .pre_image : { \
    KEEP(*(.flash_header_start*)) \
    . = 0x100; \
    KEEP(*(.flash_header_0x0100*)) \
    KEEP(*(.dcd_entry_0x0100*)) \
    KEEP(*(.image_len_0x0100*)) \
    . = 0x400; \
    KEEP(*(.flash_header_0x0400*)) \
    KEEP(*(.dcd_entry_0x0400*)) \
    KEEP(*(.image_len_0x0400*)) \
    . = 0x1000; \
    KEEP(*(.flash_header_0x1000*)) \
    KEEP(*(.dcd_entry_0x1000*)) \
    KEEP(*(.image_len_0x1000*)) \
    . = 0x2000; \
    }
    #endif
    include\asm-generic\ barebox.lds 中定义了链接节定义宏,可以方便的定义一些预定义节
    被定义在这些节中的函数在common/startup.c中的startup函数中被调用,调用的顺序是从小到大也就是.initcall.0到.initcall.11这样可以保证优先级最高的函数最先被调用
    #define INITCALLS \
    KEEP(*(.initcall.0)) \
    KEEP(*(.initcall.1)) \
    KEEP(*(.initcall.2)) \
    KEEP(*(.initcall.3)) \
    KEEP(*(.initcall.4)) \
    KEEP(*(.initcall.5)) \
    KEEP(*(.initcall.6)) \
    KEEP(*(.initcall.7)) \
    KEEP(*(.initcall.8)) \
    KEEP(*(.initcall.9)) \
    KEEP(*(.initcall.10)) \
    KEEP(*(.initcall.11))
    这里定义了barebox的命令函数节,barebox中所有的命令函数被定义到了这个节中
    #define BAREBOX_CMDS KEEP(*(SORT_BY_NAME(.barebox_cmd*)))
    #define BAREBOX_SYMS KEEP(*(__usymtab))
    Barebox的时钟源分析
    Arch/arm/mach-imx/clocksource.c
    通用定时器1被用作时钟源
    static int clocksource_init (void)
    {
    int i;
    uint32_t val;
    /* setup GP Timer 1 */ 软复位定时器
    writel(TCTL_SWR, timer_base + GPT_TCTL);
   
    #ifdef CONFIG_ARCH_IMX21
    PCCR1 |= PCCR1_GPT1_EN;
    #endif
    #ifdef CONFIG_ARCH_IMX27
    PCCR0 |= PCCR0_GPT1_EN; 启用gpt1的ipg时钟
    PCCR1 |= PCCR1_PERCLK1_EN; 启用外设1时钟
    #endif
    #ifdef CONFIG_ARCH_IMX25
    writel(readl(IMX_CCM_BASE + CCM_CGCR1) | (1 << 19),
    IMX_CCM_BASE + CCM_CGCR1);
    #endif
    for (i = 0; i < 100; i++)
    writel(0, timer_base + GPT_TCTL); /* We have no udelay by now */
    writel(0, timer_base + GPT_TPRER);
    val = readl(timer_base + GPT_TCTL);
    val |= TCTL_FRR | (1 << TCTL_CLKSOURCE) | TCTL_TEN; /* Freerun Mode, PERCLK1 input */
    writel(val, timer_base + GPT_TCTL);
    将时钟频率转化成倍数以便以后转化
    cs.mult = clocksource_hz2mult(imx_get_gptclk(), cs.shift);
    init_clock(&cs);
    clock_register_client(&imx_clock_notifier);
    return 0;
    }
    core_initcall(clocksource_init); 该函数在以优先级3startup.c中被调用
   
    1.barebox一般不用clk_get和clk_enable来获取和设置时钟,而是直接在板级初始化文件中来初始化外设的时钟,或者在驱动中启动外设的时钟,为什么不用标准的clk_xx函数来管理啊?
   

chinakong.com中国工控网


   

chinakong.com中国工控网


    MPLL和SPLL的输入是内部FPM或者标准外部OSC经过倍频后产生的,每个都可以选择其时钟源,要么为FPM要么为extosc[26M/27M]
    MPLL : 主要用于产生系统时钟和cpu时钟,以及AHB和IPG时钟,以及大部分芯片内部的外设的divider的时钟源,包括PER1-PER4的divider时钟,以及NFC的divider的时钟,其余的如i2c、rtc、gpio、wdg、kpp等都采用ipg时钟来工作。
    SPLL :主要用于产生特殊外设的时钟,比如作为USB/SSI1/SSI2/H264/MSHD的divider的输入时钟信号
    AHB时钟:由mpll时钟分频后产生[ahbdiv 1-4 分之 mpll]
    ARM时钟:由mpll时钟经armdiv分频后产生,其中还可以选择是直接对mpll分频还是mpll*2/3后再分频。
    IPG时钟 : 等与AHB时钟分频后获得,所有外设都有AHB时钟,而且外设的APB时钟都是一样频率,主要用这个时钟来进行ARM和外设直接的通信,比如读取寄存器,传输数据等等,以及外设的工作时钟。还有一个时钟是某些外设特有的时钟,比如定时器需要一个计数的时钟,SSI需要一个传输的时钟,PER1—PER4需要的时钟,SSI1/2,H264,的时钟,
    一般来说低速外设是IPG时钟,高速是AHB时钟,要看设备是挂载什么总线上的,是IP还是AHB。某些外设出了工作时钟外还需要特定的外设时钟或则像素时钟等等。所以一个外设至少需要1个时钟,要么是IPG要么是AHB,其余的时钟需要根据设备的具体需要而定。
    其中SSI1DIV、SSI2DIV、MSHCDIV、H264DIV的时钟输入可以编程选择MPLL或者SPLL
    每个外设有1个或者多个时钟需要启用,有些是IPG时钟,有些是AHB时钟,还有其他外设时钟等等。是ipg/ahb/perclk的组合。其中ipg是必须的,是arm和外设进行寄存器读写的时钟,ahb是高速外设传输的时钟,外设时钟是低速外设的时钟。
    GPT 使用PERCLK1作为其计数器工作的时钟,用IPG时钟作为和ARM通信的时钟进行寄存器读写操作。还有模块工作的时钟,没有这个时钟模块就没有办法工作。或则这样来认为。并且arm核无法和module通信。
    FCLK 就是cpu工作的频率
    HCLK 就是AHB总线的工作频率 AHB连接高速的外设,也是系统时钟 AHB CLK
    PCLK 就是 APB 总线工作频率 APB总线连接低速的外设 IPGCLK
    rtc/wdg/gpio/dmac/rtic/i2c/fec/kpp
   
   
   
   
   
   
   
 

暂时没有评论

    发表评论

登陆网站发表评论

用户名:

密码:

注册 | 忘了密码

关于我们     免责声明     服务项目     广告联系     友情链接     联系方式     意见反馈     设为首页     加入收藏

 ©2023-2025 中国工控网(www.chinakong.com) 版权所有 豫ICP备17046657号

管理员信箱:chinakong98@163.com  服务热线:13525974529

洛阳博德工控自动化技术有限公司

中国    洛阳