U-Boot(arm920t/start.S)中重要代码分析

cpu/arm920t/start.S中重要代码分析


cpu/arm920t/start.S中重要代码分析
   
前言:总结了网上多个帖子,而且还从书上手工抄写了许多讲解的部分写了这篇cpu/arm920t/start.S中重要代码分析。

    U-Boot是从cpu/arm920t/start.S开始的,这个文件的任务是设置处理器状态、初始化中断和内存时序等,并确定是否需要对整个U-Boot代码重定位,最终从Flash中跳转到定位好的内存位置执行。

 

开始的一段代码是处理的异常处理向量表
.globl _start             ;系统复位的位置,由u-boot.ld决定
_start: b       reset        ;复位向量  
 ;各个异常向量对应的跳转代码
       ldr   pc, _undefined_instruction       ;未定义的指令异常
       ldr   pc, _software_interrupt       ;软件中断异常
       ldr   pc, _prefetch_abort      ;内存操作异常
       ldr   pc, _data_abort            ;数据异常
       ldr   pc, _not_used          ;未使用0x14
       ldr   pc, _irq             ;普通中断异常
       ldr   pc, _fiq            ;快速中断异常
_undefined_instruction:         .word undefined_instruction
_software_interrupt:             .word software_interrupt
_prefetch_abort:                   .word prefetch_abort
_data_abort:                         .word data_abort
_not_used:                           .word not_used
_irq:                                    .word irq
_fiq:                                    .word fiq
.balignl 16,0xdeadbeef
 
下面的语句很重要:
_TEXT_BASE:
.wordTEXT_BASE       ;这个值在/board/smdk2410/config.mk中定义
.globl _armboot_start
_armboot_start:
.word _start         ;代码段的起始地址也是TEXT_BASE
.globl _bss_start
_bss_start:
.word __bss_start         ;由链接程序ld根据链接脚本确定/board/smdk2410/u-boot.lds
.globl _bss_end
_bss_end:
.word _end      ;由链接程序ld根据链接脚本确定。
 
接下来是系统复位,这个过程可参考处理器手册。
reset:          ;位启动子程序
       mrs   r0,cpsr        ;保存cpsr到r0中
       bic   r0,r0,#0x1f        ;保留后十六位,其余清零
       orr   r0,r0,#0xd3        ;相或
       msr   cpsr,r0
#ifdef CONFIG_INIT_CRITICAL
       bl    cpu_init_crit      ;跳转
#endif
 
       接下来的代码用来关闭看门狗,通过INTMR寄存器屏蔽所有的中断,并配置处理器内部时钟频率,需要仔细研究并深刻理解的是关于重定向(relocate)的代码。
在这里插播一下看门狗的一些说明:看门狗,又叫 watchdog timer,是一个定时器电路, 一般有一个输入,叫喂狗,一个输出到MCU的RST端,MCU正常工作的时候,每隔一端时间输出一个信号到喂狗端,给 WDT 清零,如果超过规定的时间不喂狗,(一般在程序跑飞时),WDT 定时超过,就会给出一个复位信号到MCU,使MCU复位. 防止MCU死机. 看门狗的作用就是防止程序发生死循环,或者说程序跑飞。
relocate:          ;U-Boot重新定位到RAM
       adr   r0, _start     ;装载指令,r0是代码的当前位置
       ldr  r1, _TEXT_BASE  ;装载寄存器,测试判断是从Flash启动还是RAM
       cmp     r0, r1        ;比较r0和r1,调试的时候不要执行重定位
       beq     stack_setup   ;如果r0等于r1,跳过重定位代码
      
;准备重新定位代码
       ldr   r2, _armboot_start
       ldr   r3, _bss_start
       sub   r2, r3, r2      ;r2 得到armboot的大小  
       add   r2, r0, r2     ;r2 得到要复制代码的末尾地址
copy_loop:                                       
       ldmia r0!, {r3-r10} ;过后增加装置命令,从源地址[r0]复制
       stmia r1!, {r3-r10} ;过后增加存储命令,复制到目的地址[r1]
       cmp   r0, r2     ;复制数据块直到源数据末尾地址[r2]
       ble   copy_loop  ;小于等于,则复制
      
       调试阶段的代码是直接在RAM中运行的,而最后需要把这些代码固化到Flash中,因此U-Boot需要自己从Flash转移到RAM中运行。这也是重定向的目的所在。

       其中关键的一步就是通过adr指令得到当前代码的地址信息:

1.               如果U-Boot是从RAM开始运行,则从ard r0,_start得到的地址信息为r0 = _start = _TEXT_BASE = TEXT_BASE = 0x33f8000;

2.               如果U-Boot从Flash开始运行,即从处理器对应的地址运行,则r0=0x0(Flash起始地址),这时将会执行copy_loop标识那段代码。

 
 ;初始化堆栈等
stack_setup:
       ldr   r0, _TEXT_BASE           ;上面是128 KiB重定位的u-boot
       sub   r0, r0, #CFG_MALLOC_LEN  ;向下是内存分配空间
       sub   r0, r0, #CFG_GBL_DATA_SIZE;然后是bdinfo结构体地址空间
#ifdef CONFIG_USE_IRQ
       sub   r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
       sub   sp, r0, #12     ;为abort-stack预留3个字
clear_bss:
       ldr   r0, _bss_start        ;找到bss段起始地址
       ldr   r1, _bss_end          ;bss段末尾地址  
       mov   r2, #0x00000000       ;清零
clbss_l:str r2, [r0]   ;r2装载到[r0]中,bss段地址空间清零循环...
       add   r0, r0, #4     ;每次复制一个机器字长(32位,4字节)
       cmp   r0, r1
       bne   clbss_l        ;如果不等于或小于,跳转到clbss_l继续循环
 
       最终将通过下面的语句跳转到C代码执行。
       ldr   pc, _start_armboot;跳转到start_armboot函数入口,
;_start_armboot字保存函数入口指针
_start_armboot:
        .word start_armboot ;start_armboot函数在lib_arm/board.c

       start_armboot()在lib_arm/board.c中定义,它类似于Linux内核的start_kernel(),它们都是一种系统初始化的接口函数:在内核的start_kernel()中集中完成了内核几乎所有资源的初始化,包括CPU相关的资源和外设接口等;而在 start_armboot()中也要完成一些初始化工作。

你可能感兴趣的:(timer,脚本,Flash,化工,代码分析,linux内核)