00032: #include
00033: #include
1)#include
2)经过分析后,发现start.S中包含的第一个头文件就是:include/configs/hi3519v101.h,这个文件是整个uboot移植时的配置文件。这里面是好多宏。因此这个头文件包含将include/configs/ hi3519v101.h文件和start.S文件关联了起来。因此之后在分析start.S文件时,主要要考虑的就是hi3519v101.h文件。
3)#include
00035: ***************************************************************
00035: **********
00036: *
00037: * Jump vector table as in table 3.1 in [1]
00038: *
00039: ***************************************************************
00043: .globl _start
/*系统复位位置,整个程序入口*/:_start是GNU汇编器的默认入口标签,.globl将_start声明为外部程序可访问的标签,.globl是GNU汇编的保留关键字,前面加点是GNU汇编的语法。.global就是相当于C语言中的Extern,声明此变量,并且告诉链接器此变量是全局的,外部可以访问 .
00044: _start: b reset
---------------------------------------------------------reset代码分析-------------------------------------------------------
00125: /*
00126: * the actual reset code
00127: */
00128:
00129: reset:
00130: /*
00131: * set the cpu to SVC32 mode
00132: */
1.设置SVC模式
对状态寄存器的修改要按照:读-改-写的顺序来执行
00133: mrs r0, cpsr //MRS指令用于将程序状态寄存器的内容传送到通用寄存器中。
00134: bic r0, r0, #0x1f //BIC指令用于清除操作数1的某些位,并把结果放置到目的寄存器中
00135: orr r0, r0, #0xd3//ORR指令用于在两个操作数上迕行逻辑戒运算,幵把结果放置到目的寄存器中
00136: msr cpsr,r0 //MSR指令用于将操作数的内容传送到程序状态寄存器的特定域中。
00137:
00138: /*
00139: * Invalidate L1 I/D
00140: */
2.关闭指令I/D缓存
00141: mov r0, #0 @ set up for MCR//r0清零
00142: mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs// r0放在C8 C7中,这个TLB是虚拟内存管理,后面再说、
00143: mcr p15, 0, r0, c7, c5, 0 @ invalidate icache//使之无效
00145: /*
00146: * disable MMU stuff and caches
00147: */
3.关闭MMU
00148: mrc p15, 0, r0, c1, c0, 0
00149: bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
00150: bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
00151: orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
00152: orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB
00153: mcr p15, 0, r0, c1, c0, 0
00154:
00155: /*
00156: * read system register REG_SC_GEN2
00157: * check if ziju flag
00158: */
4.获取启动信息,先检测是不是自举模式,我猜测这个主要是分辨从PCIE启动还是FLASH启动
海思资料里面没有说明这些东西,无从分析。但整个逻辑应该是这样的:
芯片上电芯片内部的iROM一段代码会自动检测启动模式的引脚并把数值放在一个寄存器中,然后我们用数字去比较看哪个启动模式被激活,然后跳转执行相应的启动模式代码。
00159: ldr r0, =SYS_CTRL_REG_BASE //#define SYS_CTRL_REG_BASE 0x12020000
00160: ldr r1, [r0, #REG_SC_GEN2] // #define REG_SC_GEN2 0x0140-----》记录启动模式的寄存器,数据手册中没有这个寄存器,找不到!
00161: ldr r2, =0x7a696a75 /* magic for "ziju" */
00162: ldr r3, [r0, #REG_SC_GEN20] // #define REG_SC_GEN20 0x0090---》pcie启动的寄存器标志,数据手册中找不到!
00162: /* pcie slave start up flag*/
00163: cmp r3, r2
什么意思呢?就是检测是否pcie启动,如果是pcie启动,那么0x12020090中会标记pcie启动的幻数。如果与幻数一致,则跳入自举之后的操作。如果不相等则继续进行判断!因为pcie启动和flash启动操作是不一样的,这里是做了一个判断。
00164: beq after_ziju
或者说启动分为热启动和冷启动,冷启动一般都是需要自举的,不管是什么模式!热启动是指开启第二次启动,如敲reboot之后的启动,热启动会跳过一些步骤的,比如检测启动模式、DDR初始化等等。。。。貌似和这里没关系!这里认为是pcie启动的情况!使用的也是远跳转!但整个uboot我们默认从spiflash启动!
00165: cmp r1, r2
判断下记录启动模式的寄存器中的数值是否与标记pcie启动幻数是否一致!如果一致则保存sp,清除自举的启动模式标志寄存器!但冷启动一般都是走正常启动流程的!不相等我们继续看!
00166: bne normal_start_flow //跳转到正常启动流程,使用的b,不需要返回!汇编中用bl指令和mov pc,lr来实现子函数调用和返回
------normal_start_flow代码解析------
因为使用的是远跳转,回不来了,代码太多,后面分析
------ normal_start_flow代码解析结束--
00167: mov r1, sp /* save sp */
00168: str r1, [r0, #REG_SC_GEN2] /* clear ziju flag */
--------------------------------after_ziju代码解析-------------------------
00169: after_ziju:
00170: /* init PLL/DDRC/pin mux/... */
00172: ldr r1, _TEXT_BASE //0x8840_0000
00173: sub r0, r0, r1 //r0=0x40
00174: cmp r3, r2 //r2=0X7A696A75, r3=0X12020090,寄存器中的数值相减,如果相等则跳转到pcie的从模式地址
00175: beq pcie_slave_addr//跳转到pcie从模式的地址,也就是作为pcie从模式启动,我们不从pcie启动,继续分析!
00176: ldr r1, =RAM_START_ADRS #define RAM_START_ADRS 0x04010500 数据手册查不到
//上面应该是内部的RAM的起始地址。
00177: ldr sp, =STACK_TRAINING #define STACK_TRAINING 0x04017ff0 数据手册查不到
长跳转到自举模式的DDR初始化代码!从这里我可以推断出,芯片的启动分为两种,一种是自举模式也就是本地的spiflash或nand或emmc等启动,另一种就是pcie启动模式。不同启动模式对应不同的启动流程。但不同启动模式代码是相互交织的,需要分清楚!
00179: pcie_slave_addr:
00180: ldr r1, =0x0
00181: ldr sp, =PCIE_SLV_STACK #define PCIE_SLV_STACK 0x7000
00182: ldr r4, =SYS_CTRL_REG_BASE #define SYS_CTRL_REG_BASE 0x12020000
00183: str r2, [r4, #HI3519V101_SYSBOOT10] #define HI3519V101_SYSBOOT10 0x158 数据手册查不到
r2此时应该是保存了系统启动的一些参数,不知道什么参数,或者说是主控通过pcie加载uboot的地址?
自举模式的ddr初始化代码
00185: add r0, r0, r1 //r0=0x40 r1=0x8840_0000--àr0=0x88400040
00186: mov r1, #0x0 //r1=0,/* flags: 0->normal 1->pm ,使用正常启动*/
00187: bl init_registers /* init PLL/DDRC/... */跳转到PLL和DDRC等的初始化,使用的是bl,还会跳回来!
这个函数是初始化一些寄存器,这些寄存器分了很多,包括中断、网络、哈希功能形式的寄存器,初始化的意思就是给一个值,但这值一般没什么意义,具体的寄存器,后面会再进行配置!
00188:
--------------------------------- after_ziju代码解析结束------------------
00189: /* after ziju, we need ddr traning */
00190: /*#ifdef CONFIG_DDR_TRAINING_V2*/
00191: ldr r0, =REG_BASE_SCTL
#define SYS_CTRL_REG_BASE 0x12020000
#define REG_BASE_SCTL SYS_CTRL_REG_BASE
00192: bl start_ddr_training
/* DDR training:DR布线,完全按等长约束就没有ddr training的说法。
当布线去掉等长约束或放宽约束条件,就要做ddr training,以保证时序的完整性,使信号的建立&保持时间窗口一致。ddr training是调整Addr/Cmd信号对CLK,DQ信号对DQS的延时。由于没做等长约束,信号有长,有短,就会导致信号有快,慢之差(信号在1000mil走线耗时约160~180ps,相对FR-4的板材),ddr training就是找到一套参数,使信号的建立与保持时间充足。并保存且写到配置中。*/
00193: /*#endif*/
00194:
00195: ldr r0, =SYS_CTRL_REG_BASE
#define SYS_CTRL_REG_BASE 0x12020000
00196: ldr r2, =PCIE_SLV_DDR_INIT_FLG
#define PCIE_SLV_DDR_INIT_FLG 0x8080
#define HI3519V101_SYSBOOT9 0x154
#define HI3519V101_SYSBOOT10 0x158
00197: str r2, [r0, #HI3519V101_SYSBOOT9] //写入寄存器
00198: ldr r2, =0x7a696a75 //pcie启动的幻数
00199: ldr r1, [r0, #HI3519V101_SYSBOOT10]
00200: cmp r1, r2 //比较bootrom的启动模式,如果相等则跳转到pcie从模式等待
00201: beq pcie_slave_hold
00202: ldr r1, [r0, #REG_SC_GEN2]
00203: mov sp, r1 /* restore sp */
00204: ldr r1, [r0, #REG_SC_GEN3]
00205: mov pc, r1 /* return to bootrom */
00206: pcie_slave_hold:
00207: ldr r2, [r0, #HI3519V101_SYSBOOT9]
00208: ldr r1, =0x7964616f /* complete flag */
00209: cmp r1, r2
00210: bne pcie_slave_hold
00211: ldr r1, =0x0
00212: str r1, [r0, #HI3519V101_SYSBOOT9]
00213: mov pc, #0x0 //pc指针指向0x0
00214: nop
00215: nop
00216: nop
00217: nop
00218: nop
00219: nop
00220: nop
00221: nop
00222: b . /* bug here */
---------------------------------------------------------reset代码分析结束-------------------------------------------------------
_start后面加上一个冒号' :' ,表示其是一个标号Label,类似于C语言goto后面的标号。
00045: ldr pc, _undefined_instruction/*未定义指令异常,0x04*/
00046: ldr pc, _software_interrupt/*软中断异常,0x08*/
00047: ldr pc, _prefetch_abort/*内存操作异常,0x0c*/
00048: ldr pc, _data_abort /*数据异常,0x10*/
00049: ldr pc, _not_used /*/*未适用,0x14*/
00050: ldr pc, _irq /*慢速中断异常,0x18*/
00051: ldr pc, _fiq /*快速中断异常,0x1c*/一般用在实时性比较高的中断
00052:
00053: _undefined_instruction: .word undefined_instruction
00054: _software_interrupt: .word software_interrupt
00055: _prefetch_abort: .word prefetch_abort
00056: _data_abort: .word data_abort
00057: _not_used: .word not_used
00058: _irq: .word irq
00059: _fiq: .word fiq
00060: _pad: .word 0x12345678 /* now 16*4=64 */
00061: __blank_zone_start:
00062: .fill 1024*5,1,0
.fill 1024*5,1,0 # 供保留1024*5项,每项一个字节,填充0,也就是填充0x1400个字节
00063: __blank_zone_end:
00064:
00065: .globl _blank_zone_start
00066: _blank_zone_start:
00067: .word __blank_zone_start
00068:
00070: .globl _blank_zone_end
00071: _blank_zone_end:
00072: .word __blank_zone_end
00073:
00074: .balignl 16,0xdeadbeef
1).balignl 16,0xdeadbeef. 这一句指令是让当前地址对齐排布,如果当前地址不对齐则自动向后走地址直到对齐,并且向后走的那些内存要用0xdeadbeef来填充。在这个.balignl 16,0xdeadbeef指令之前,一共占了4x15=60个字节的内存, 所以本代码的作者当时就简单的在15这个数上,加了个1,即16,把当前指针往后移到地址为64的位置,然后在前面插上了0xdeadbeef这个特殊的值。
2)0xdeadbeef这是一个十六进制的数字,这个数字很有意思,组成这个数字的十六进制数全是abcdef之中的字母,而且这8个字母刚好组成了英文的dead beef这两个单词,字面意思是坏牛肉。
3)为什么要对齐访问?有时候是效率的要求,有时候是硬件的特殊要求。
00075: /*
00075: ****************************************************************
00075: ********
00076: *
00077: * Startup Code (reset vector)
00078: *
00079: * do important init only if we don't start from memory!
00080: * setup Memory and board specific bits prior to relocation.
00081: * relocate armboot to ram
00082: * setup stack
00083: *
00084: ***************************************************************
00084: **********/
00086: _TEXT_BASE:
00087: .word TEXT_BASE
TEXT_BASE = 0x88400000,在board/hi3519v101/config.mk中可以看到。
00089: .globl _armboot_start
00090: _armboot_start:
00091: .word _start
00092:
00093: /*
00094: * These are defined in the board-specific linker script.
00095: */
00096: .globl _bss_start
00097: _bss_start:
00098: .word __bss_start
__bss_start和__bss_end定义在和开发板相关的u-boot.lds中,_bss_start和__bss_end保存的是__bss_start和__bss_end标号所在的地址。
00100: .globl _bss_end
00101: _bss_end:
00102: .word _end
00103:
00104: #ifdef CONFIG_USE_IRQ
00105: /* IRQ stack memory (calculated at run-time) */
00106: .globl IRQ_STACK_START
00107: IRQ_STACK_START:
00108: .word 0x0badc0de
堆栈指针开始的地方,但目前我们初始化,还不知道,随便放了一个badcode
00110: /* IRQ stack memory (calculated at run-time) */
00111: .globl FIQ_STACK_START
00112: FIQ_STACK_START:
00113: .word 0x0badc0de
00114: #endif
没用到,不分析了。
-----------------------------------------------------------------------------------------------------------------------------
00116: _clr_remap_fmc_entry:
00117: .word FMC_TEXT_ADRS + do_clr_remap - TEXT_BASE
#define FMC_TEXT_ADRS (FMC_MEM_BASE)
#define FMC_MEM_BASE 0x14000000
00118: _clr_remap_ddr_entry:
00119: .word MEM_BASE_DDR + do_clr_remap - TEXT_BASE
#define DDR_MEM_BASE 0x80000000
00120: _clr_remap_ram_entry:
00121: .word RAM_START_ADRS + do_clr_remap - TEXT_BASE
#define RAM_START_ADRS 0x04010500
定义了三个地址入口,等待使用。。。。。
------------------------------------------------------------------------------------------------------
00123: _copy_abort_code:
00124: .word copy_abort_code
复制异常代码到0地址启动。
正常启动如下:
00224: normal_start_flow:
00225: /* init serial and printf a string. */
00226: ldr sp, =STACK_TRAINING
#define STACK_TRAINING 0x04017ff0
00227: bl uart_early_init
跳转到uart.S文件,进行uart的串口初始化
00228: bl msg_main_cpu_startup //输出startup字符串
00229:
00230: /*
00231: * enable cci snoop for GSF and VDMA
00232: */
00233: ldr r0, =CCI_PORT_CTRL_0
00234: mov r3, #CCI_ENABLE_REQ
00235: str r3, [r0]
00236:
00237: 4: ldr r0, =CCI_CTRL_STATUS
00238: ldr r1, [r0]
00239: tst r1, #1
00240: bne 4b
00241:
00242: /*
00243: * enable cci snoop for core 0
00244: */
00245: ldr r0, =CCI_PORT_CTRL_1
00246: mov r3, #CCI_ENABLE_REQ
00247: str r3, [r0]
00248:
00249: 5: ldr r0, =CCI_CTRL_STATUS
00250: ldr r1, [r0]
00251: tst r1, #1
00252: bne 5b
//上面一部分是什么标准协议,不看了
00253:
00254: @if running not boot from nand/spi/emmc,
00255: @we skipping boot_type checking.
00256: mov r0, pc, lsr#24 //检测是否是热启动
00257: cmp r0, #0x0 //检测pc指针的高8位是否为0;
00258: bne check_bootrom_type //如果不相等,则是冷启动,跳转到启动模式检测
00259:
00260: check_boot_type: //冷启动,检测启动模式
00261: ldr r0, =SYS_CTRL_REG_BASE
#define SYS_CTRL_REG_BASE 0x12020000
00262: ldr r0, [r0, #REG_SYSSTAT]
#define REG_SYSSTAT 0x008c
00263: mov r6, r0, lsr#5
00264: and r6, #0x1
00265: cmp r6, #0 @ [5]=0 fmc
00265: /* SPI Nor/Nand and Nand */
00266: ldreq pc, _clr_remap_fmc_entry //检测为SPI Nor/Nand and Nand启动
00267:
00268: @otherwise, [31]=1 means boot from bootrom, err
00269: beq bug
00270: check_bootrom_type:
00271: cmp r0, #0x4
00272: /*
00272: boot from bootrom,we copy the uboot.bin to ram (0x4010500)*/
00273: ldreq pc, _clr_remap_ram_entry //片内RAM启动
00274:
00275: do_clr_remap:
00276: /* do clear remap */
我的理解是:在ROM从0x0用几句指令引导系统之后,把RAM映射到0x0就是Remap。
1.Remap的作用
当ARM处理器上电或者Reset之后,处理器从0x0 取指。因此,必须保证系统上电时,0x0 处有指令可以执行。所以,上电的时候,0x0地址处必定是ROM 或者Flash(NOR)。但是,为了加快启动的速度,也方便可以更改异常向量表,加快中断响应速度,往往把异常向量表映射到更快、更宽(32bit/16bit)的RAM 中。但是异常向量表的开始地址是由ARM架构决定的,必须位于0x0处,因此,必须把RAM映射到0x0。
2.Remap的配置
Remap的实现和ARM处理器的实现相关。
1)如果处理器有专门的寄存器可以完成Remap。那么Remap 是通过Remap 寄存器的相应bit置1 完成的。如Atmel AT91xx
2)如果处理器没有专门的寄存器,但是memory的bank控制寄存器可以用来配置bank的起始地址,那么只要把RAM的起始地址编程为0x0,也可以完成remap。如samsung s3c4510
3)如果上面两种机制都没有,那么Remap就不要做了。因为处理器实现决定了SDRAM对应的bank地址是不能改变的。如Samsung S3c2410.
3.Remap配置前后要做的工作
Remap 前后,不同之处就是RAM 的位置变了。为了达到Remap 的目的,就是加快启动的速度和异常处理速度,一定要初始化异常堆栈和建立异常向量表的。
4.如果象2410那样不能Remap的话怎么办?
2410 不是不能Remap吗?为了加快启动速度,可以这样做
1)使用它的NAND boot 模式。为什么NAND boot 会比较快,那是因为2410 里面有块小石头——“SteppingStone”,一块4KB SRAM,它是映射在0x0 的。启动程序会自动被copy 到这个石头里面。自然异常向量的入口放到这个地方,一样可以达到比NOR boot 快的启动、异常响应速度。
2)如果你对NOR Boot 情有独衷,那么你只好把你的异常向量的入口copy到SDRAM里面,实现所谓的High Vector
00277: ldr r4, =SYS_CTRL_REG_BASE
00278: ldr r0, [r4, #REG_SC_CTRL]
00279:
00280: @Set clear remap bit.
00281: orr r0, #(1<<8)
00282: str r0, [r4, #REG_SC_CTRL]
00283:
00284: /*
00285: * Set ACTLR.SMP to 1
00286: * This is a bug on Cortex-A7 MPCORE. see buglist of Cortex -A7
00287: * The D-caches are disabled when ACTLR.SMP is set to 0 reg
00287: ardless
00288: * of the value of the cache enable bit. so we must set SMP
00288: bit of
00289: * ACTLR register before enable D-cache
00290: */
00291: mrc p15, 0, r0, c1, c0, 1
00292: orr r0, #(1 << 6)
00293: mcr p15, 0, r0, c1, c0, 1
/*以上清除重映射,清除重映射后地址0x000000
00295: @enable I-Cache now //使能I-cache
00296: mrc p15, 0, r0, c1, c0, 0
00297: orr r0, r0, #0x00001000
00297: /* set bit 12 (I) I-Cache */
00298: mcr p15, 0, r0, c1, c0, 0
00299:
00300: @Check wether I'm running in dynamic mem bank
mov r0, pc, lsr#28
cmp r0, #8
blo ddr_init //小于0,跳转到DDR初始化
00304:
no_ddr_init:
adrl r0, _start
b copy_to_ddr //不需要DDR初始化,直接copy到DDR中
00308:
ddr_init:
ldr r0, _blank_zone_start
ldr r1, _TEXT_BASE
sub r0, r0, r1
adrl r1, _start
add r0, r0, r1
mov r1, #0 /* flags: 0->normal 1->pm
bl init_registers
//上面分析过了
/*#ifdef CONFIG_DDR_TRAINING_V2*/
ldr sp, =STACK_TRAINING
ldr r0, =REG_BASE_SCTL
bl start_ddr_training /* DDR training */
/*#endif*/
//上面再DDR初始化中已经看过了
check_boot_mode:
ldr r0, =SYS_CTRL_REG_BASE
ldr r0, [r0, #REG_SYSSTAT]
mov r6, r0, lsr#4
and r6, #0x3
cmp r6, #BOOT_FROM_EMMC //判断是不是EMMC启动
bne copy_flash_to_ddr //如果不是,则将flash代码拷贝到DDR中
#ifdef CONFIG_HIMCI_V200
emmc_boot:
ldr r0, _TEXT_BASE //链接地址
ldr r1, _armboot_start //启动地址
ldr r2, _bss_start //bss段地址
sub r1, r2, r1 //bss段地址减去启动地址,剩下偏移,放入r1
bl emmc_boot_read //跳转emmc_boot_read,重定位并拷贝uboot到DDR中去
b jump_to_ddr //跳转到ddr中,不回来了
#endif
copy_flash_to_ddr:
/* relocate SPI nor/nand Boot to DDR */
cmp r6, #BOOT_FROM_DDR
beq copy_to_ddr //重定位并拷贝uboot到内存中
ldr r0, =FMC_TEXT_ADRS /* 0x1400_0000 */
00345:
copy_to_ddr:
/* now, r0 stores __reset offset from where we getstarted */
00348: ldr r1, _TEXT_BASE /* r1 stores where we wil l copy uboot to */
/* compare source and target address, **if equal no copy to target address */
//如果是冷启动,则需要拷贝,如果是热启动,则不需要拷贝
cmp r0, r1
beq copy_abort_code //热启动,不需要拷贝
ldr r2, _armboot_start //热启动,直接运行
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot * //基本上算是uboot的大小
/* memcpy(r1, r0, r2) */
bl memcpy
jump_to_ddr:
ldr r0, _TEXT_BASE
ldr pc, _copy_abort_code
copy_abort_code:
ldr r1, =0x00000000
mov r2, #0x4000
/* memcpy(r1, r0, r2) */
bl memcpy
/* Set up the stack */
stack_setup: //设置堆栈
ldr r0, _TEXT_BASE @ upper 128 KiB: relocated uboot //链接地址,uboot的起始地址
sub r0, r0, #CONFIG_SYS_MALLOC_LEN @ malloc area //堆栈大小:450kB
sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE @ bdinfo //128B
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 @ leave 3 words for abort-stack
and sp, sp, #~7 @ 8 byte alinged for (ldr/str) d
/* Clear BSS (if any). Is below tx (watch load addr - need space) */
clear_bss: //清理BSS段,这也是为什么栈不需要手动清空,这里帮你清空了
ldr r0, _bss_start @ find start of bss segment
ldr r1, _bss_end @ stop here
mov r2, #0x0 @ clear value
clbss_l:
str r2, [r0] @ clear BSS location
cmp r0, r1 @ are we at the end yet
add r0, r0, #4 @ increment clear index pointer
bne clbss_l @ keep clearing till at end
ldr pc, _start_armboot @ jump to C code //跳转到C语言阶段
_start_armboot: .word start_armboot
//海思的uboot第一阶段做了哪些东西