我是靠谱客的博主 包容服饰,最近开发中收集的这篇文章主要介绍海思(Hi3521a)uboot详细分析(5)——uboot启动第一阶段start.S文件分析1.设置CPU SVC模式 2.关闭mmu和缓存3.启动流程判断4.关闭地址重映射5.使能指令缓存6.重定向异常向量表到内部RAM7.重定向uboot到外部DDR8.设置栈空间9.清除bss段10.跳转到C程序入口致谢:,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

    从《链接文件u-boot.lds分析》中我们看到链接脚本的代码段链接的第一个文件是arch/arm/cpu/hi3521a/start.o,也就是说程序运行最开始是从start.S文件开始执行的。
    start.S在这里完成uboot的第一阶段的启动,它的内容包括:

  1. 设置CPU SVC模式
  2. 关闭mmu和缓存
  3. 启动流程判断
  4. 关闭地址重映射
  5. 使能指令缓存
  6. 重定向异常向量表到内部RAM
  7. 重定向uboot到外部DDR
  8. 设置栈空间
  9. 清除bss段
  10. 跳转到C程序入口

1.设置CPU SVC模式 

1.1头文件包含 

#include <config.h>
#include <version.h>
  • 头文件包含,这里的config.h 是/include/config.h 
  • 这个文件不是原来就有的,是uboot配置的的时候生成的
  • 它的内容是:
  • #include <config_defaults.h>
  • #include <configs/hi3521a.h>
  • #include <asm/config.h>
  • 这里面都是一些条件编译的宏,根据不同的设备配置不同而不同
  • <version.h>。include/version.h中包含了include/version_autogenerated.h,
  • 这个头文件就是配置过程中自动生成的。版本号信息来自于Makefile中的配置值
  • 在uboot启动过程中会串口打印出uboot的版本号,那个版本号信息就是从这来的。

1.2程序入口

.globl _start
_start: b	reset
  • 由uboot.lds链接脚本,我们知道整个程序的入口取决于中ENTRY声明的地方。
  • 在uboot.lds中有ENTRY(_start),因此_start符号所在的文件就是整个程序的起始文件,_start所在的代码就是整个程序的起始代码。
  • .globl XX 语法:给XX外部连接的属性,一般为了在别的文件中引用这个符号
  • _start后面加上一个冒号' :',表示_start是一个标号
  • _start: b    reset 程序开始,跳到reset标号去执行,执行完后不返回

1.3异常向量表的构建

	ldr	pc, _undefined_instruction
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq
	ldr	pc, _fiq
  • LDR指令用于从存储器中将一个32位的字数据传送到目的寄存器中。该指令通常用于从存储器中读取32位的字数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。
  • 以第一个_undefined_instruction为例,就是将地址为_undefined_instruction中的一个word的值,赋值给pc
_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
  • 以_undefined_instruction为例,就是,此处分配了一个word=32bit=4字节的地址空间,里面存放的值是undefined_instruction。
  • 在后面的代码,我们可以看到,undefined_instruction也是一个标号,即一个地址值,对应着就是在发生“未定义指令”的时候,系统所要去执行的代码。
  • 这里定义的中断分别是:
  1. 未定义指令异常,0x04
  2. 软中断异常,0x08
  3. 内存操作异常,0x0c
  4. 数据异常,0x10
  5. 未适用,0x14
  6. 慢速中断异常,0x18
  7. 快速中断异常,0x1c,

1.4设置标号地址

_pad:			.word 0x12345678 /* now 16*4=64 */
  • _pad是一个标号,没有看到有地方使用它
__blank_zone_start:
.fill 1024*4,1,0
__blank_zone_end:
  • 反复拷贝1个字节的0,拷贝1024*4次,也就是填充4KB的0数据
  • 通过反汇编我们知道__blank_zone_start的值为0x80800040,__blank_zone_end的值为0x80801040,可以知道它的范围刚好是0x1000=4096=4KB
.globl _blank_zone_start
_blank_zone_start:
.word __blank_zone_start

.globl _blank_zone_end
_blank_zone_end:
.word __blank_zone_end
  • 设置外部引用标号:_blank_zone_start,_blank_zone_end,这样他们就可以在其它汇编和C语言中被调用
    .balignl 16,0xdeadbeef
  • 设置16字节对齐,如果没有16字节对齐,则使用0xdeadbeef中的数值来填充。这个数值没有什么特别的意义,只是对应英文单词deadbeef(坏牛肉)
_TEXT_BASE:
	.word	TEXT_BASE
	
.globl _armboot_start
_armboot_start:
	.word _start
  • TEXT_BASE就是Makefile配置阶段写入的TEXT_BASE值
  • TEXT_BASE = 0x80800000 在/board/hi3521a/config.mk中定义
  • 这个是指明链接地址的值,也就是程序应该开始运行的地址,在uboot重定位的时候会使用到。经过链接脚本链接过后_armboot_start的地址与_start的地址都等于0x80800000
/*
 * These are defined in the board-specific linker script.
 */
.globl _bss_start
_bss_start:
	.word __bss_start

.globl _bss_end
_bss_end:
	.word _end
  • 这里将bss段的开始地址和结束地址设置一个外部标号,bss段的开始位置和结束位置在这里是不确定的,通过u-boot.lds可知,bss的位置与前面的代码段和数据段的长度有关    
  • 通过反汇编,我们查看到_bss_start=0x8084abc4,_bss_end=0x8089f700
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
	.word	0x0badc0de

/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
	.word 0x0badc0de
#endif
  • CONFIG_USE_IRQ 在海思设备上并没有使用中断
  • 所以上面这段代码无效
_clr_remap_fmc_entry:
    .word   FMC_TEXT_ADRS + do_clr_remap - TEXT_BASE
  • FMC 是flash memory controller 
  • FMC_TEXT_ADRS=FMC_MEM_BASE=0x14000000
  • TEXT_BASE = 0x80800000 在/board/hi3521a/config.mk中定义    
  • 芯片手册上描述:
  • SFC NAND/NOR MEMORY起始地址为0x14000000,大小为16MB
  • DDR 存储地址空间起始地址为:0x8000_0000,大小为2GB
  • 通过反汇编我们查看到
  • do_clr_remap=0x8080113c
  • _clr_remap_fmc_entry=0x1400113c
  • 这里是指向了SFC NAND/NOR MEMORY地址空间

1.5.设置cpu SVC模式

/*
 * the actual reset code
 */

reset:
	/*
	 * set the cpu to SVC32 mode
	 */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f
	orr	r0, r0, #0xd3
	msr	cpsr,r0
  • 将程序状态寄存器(cpsr)的值存到寄存器r0
  • 将r0寄存器的低五位清零
  • 将r0与0xd3或之后赋值给r0
  • 将寄存器r0里的值赋值程序状态寄存器(cpsr)
  • 上面的这四句命令是将CPU设置为禁止FIQ IRQ,ARM状态,SVC模式

2.关闭mmu和缓存

	/*
	 * Invalidate L1 I/D
	 */
	mov	r0, #0			@ set up for MCR
	mcr	p15, 0, r0, c8, c7, 0	@ invalidate TLBs
	mcr	p15, 0, r0, c7, c5, 0	@ invalidate icache
  • 关闭一级缓存的指令和数据缓存功能
	/*
	 * disable MMU stuff and caches
	 */
	mrc	p15, 0, r0, c1, c0, 0
	bic	r0, r0, #0x00002000	@ clear bits 13 (--V-)
	bic	r0, r0, #0x00000007	@ clear bits 2:0 (-CAM)
	orr	r0, r0, #0x00000002	@ set bit 1 (--A-) Align
	orr	r0, r0, #0x00000800	@ set bit 11 (Z---) BTB
	mcr	p15, 0, r0, c1, c0, 0
  • 关闭MMU功能

3.启动流程判断

3.1正常模式流程

	/*
	 *  read system register REG_SC_GEN2
         *  check if ziju flag
	 */
	ldr	r0, =SYS_CTRL_REG_BASE
    ldr	r1, [r0, #REG_SC_GEN2]
	ldr	r2, =0x7a696a75          /* magic for "ziju" */
	cmp	r1, r2
	bne	normal_start_flow
  • 将SYS_CTRL_REG_BASE这个宏表示的值赋值给r0寄存器
  • 将r0寄存器的值加上REG_SC_GEN2宏地址上的值赋值给r1寄存器
  • 将0x7a696a75 赋值给r2
  • 将r1与r2寄存器中的值做对比
  • 如果不相等 执行函数normal_start_flow,执行完之后再返回这里
  • 如果相等,继续往下执行,将sp寄存器的值赋值给r1寄存器
  • 将r1寄存器的值存到r0+REG_SC_GEN2值表示的地址中去
  • 这里SYS_CTRL_REG_BASE=0x12050000,REG_SC_GEN2=0x0140,这个寄存器的功能海思的官方手册上并没有给出说明,所以这里不好猜测它主要是做什么的,但是基本上重启都是会进入normal_start_flow模式的。

3.2ziju模式流程

	mov	r1, sp                   /* save sp */
	str	r1, [r0, #REG_SC_GEN2]  /* clear ziju flag */
	/* init PLL/DDRC/pin mux/... */
	ldr	r0, _blank_zone_start
	ldr	r1, _TEXT_BASE
	sub	r0, r0, r1
	ldr	r1, =RAM_START_ADRS
	add	r0, r0, r1
	mov	r1, #0x0                 /* flags: 0->normal 1->pm */
	bl	init_registers           /* init PLL/DDRC/... */

	/* after ziju, we need ddr traning */
#ifdef CONFIG_DDR_TRAINING_V2
	ldr	sp, =STACK_TRAINING
	ldr	r0, =REG_BASE_SCTL
	bl	start_ddr_training       /* DDR training */
#endif

	ldr	r0, =SYS_CTRL_REG_BASE
    	ldr	r1, [r0, #REG_SC_GEN2]
	mov	sp, r1		         /* restore sp */
    	ldr	r1, [r0, #REG_SC_GEN3]
	mov	pc, r1                   /* return to bootrom */
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	b	.                        /* bug here */
  • 海思在u-boot-2010.06archarmincludeasmarch-hi3521aplatform.h中有定义:
  • #define REG_SC_GEN0            0x0138
  • #define REG_SC_GEN1            0x013c
  • #define REG_SC_GEN2            0x0140
  • #define REG_SC_GEN3            0x0144
  • #define REG_SC_GEN4            0x0148
  • 但是海思的用户手册上并没有给出这几个寄存器的描述,因此从代码上并不能知道面的自举操作实现了些什么功能。
  • 从代码上来猜测,RAM_START_ADRS是内部SRAM的开始地址,__blank_zone_start的大小是4K,在hi3520dv400上这个值是5k。
  • 综上猜测,这里的ziju模式应该是使用HiBurn进行uboot升级的时候,先将4K或是5k的uboot代码下载到内部SRAM,然后再通过下载的前面一点点uboot代码,初始化ddr,初始化DDR之后,再将所有的uboot程序下载到ddr中,从ddr中使用命令进行uboot烧入flash的操作。

通过这里也可以分析出两个问题:

  1. 使用hiburn升级uboot的时候,如果只升级了4~5k程序(通过进度判断)就提示失败,那有可能是ddr初始化失败了。
  2. 所升级的uboot程序对DDR的操作有问题,导致DDR不能正常被初始化。

3.3.正常启动

normal_start_flow:

       	@if running not boot from  spi/nand/ddr ram,
		@we skipping boot_type checking.
    	mov    r0, pc, lsr#24
    	cmp    r0, #0x0
    	bne    do_clr_remap
  • 将PC寄存器的值逻辑右移24位,然后再赋值给r0寄存器
  • 将r0寄存器的值与0做对比
  • 如果r0寄存器的值不等于零,则执行do_clr_remap函数,执行完后不返回。
  • 海思hi3521a的uboot有三种种启动方式:
  • spi nor/nand flash
  • 内部RAM
  • 外部DDR启动 
  • PC指针地址逻辑右移24位等于0,也就是只有PC指针的地址小于或等于0x8FFFFF时才成立,这种情况应该只有在内部RAM中运行才会出现。
  • 正常启动的时候,海思hi3521a芯片这里的PC指针是指向spi flash的地址空间范围0x1400_0000-0x1400_FFFF。

3.4检查启动类型

check_boot_type:
        ldr     r0, =SYS_CTRL_REG_BASE
        ldr     r0, [r0, #REG_SYSSTAT]
        mov     r6, r0, lsr#4
		and     r6, #0x1
        cmp     r6, #0			@ [4] = 0 FMC /* spi nor | spi nand */
        ldreq   pc, _clr_remap_fmc_entry

		@otherwise, [31]=1 means boot from bootrom, err
		beq	bug
  • 通过SYSSTAT寄存器的bootrom_sel控制启动类型。
  • 如果是spi nor或是spi nand 启动,程序会进入到这里执行,如果是bootrom启动则不会。
  • 正常这里应该是不会进来执行的。

4.关闭地址重映射

do_clr_remap:
        /* do clear remap */
    	ldr     r4, =SYS_CTRL_REG_BASE
		ldr 	r0, [r4, #REG_SC_CTRL]

    	@Set clear remap bit.
    	orr 	r0, #(1<<8)
    	str 	r0, [r4, #REG_SC_CTRL]
  • 将SYS_CTRL_REG_BASE内存地址的值赋值给r4寄存器
  • 将r4加REG_SC_CTRL表示的地址的值赋值给r0寄存器
  • 将(1<<8)赋值给与r0或之后赋值给r0寄存器
  • 将r0寄存器的值存到r4加REG_SC_CTRL的地址表示的地址中
  • SYS_CTRL_REG_BASE=0x12050000;REG_SC_CTRL=0 表示hi3521a中的SC_CTRL系统控制寄存器,该寄存器的第8位为地址重映射清除选择位(remapclear):
  • 0:保持 Remap 状态。
  • 1:清除 Remap。
  • 地址重映射时:    0x00000000地址指向启动地址空间。
  • 地址重映射撤销后:0x00000000地址空间指向片内 RAM
  • 上面的代码是清除重映射。
	/*
	 * Set ACTLR.SMP to 1
	 * This is a bug on Cortex-A7 MPCORE. see buglist of Cortex-A7
	 * The D-caches are disabled when ACTLR.SMP is set to 0 regardless of
	 * the value of the cache enable bit. so we must set SMP bit of ACTLR
	 * register before enable D-cache
	 */
	mrc     p15, 0, r0, c1, c0, 1
	orr     r0, #(1 << 6)
	mcr     p15, 0, r0, c1, c0, 1
  • 修复一个Cortex-A7的BUG,具体内容不用管它

5.使能指令缓存

	@enable I-Cache now
 	mrc p15, 0, r0, c1, c0, 0
	orr r0, r0, #0x00001000 /* set bit 12 (I) I-Cache */
	mcr p15, 0, r0, c1, c0, 0
	isb
  • 使能指令缓存
	@Check wether I'm running in dynamic mem bank
	mov r0, pc, lsr#28
	cmp r0, #8
	bleq    relocate
  • 将PC寄存器的值逻辑右移28位后赋值给r0寄存器    
  • 将pc寄存器的值与8相比较
  • 如果PC寄存器的值与8相等,调到relocate执行,执行结束后返回到这里
  • 这里是判断是否是在DDR中运行,因为DDR的地址空间是0x8000_0000到0xFFFF_FFFF的范围,uboot的运行地址一定在0x8000_0000~0x8FFF_FFFF,所以逻辑右移28位之后就一定是等于8
  • 在这里如果是正常启动,PC值是在0x14000000开始的16M地址空间
  • 最后的那个跳转函数不会执行。

6.重定向异常向量表到内部RAM

	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
  • 将_blank_zone_start地址的值读入到r0寄存器
  • 将_TEXT_BASE地址的值读入到r1寄存器
  • 将r1-r0的值存入r0
  • 将_start地址的值读入到r1
  • 将r1+r0的值存入r0
  • 将0赋值给r1
  • 跳转到init_registers去执行,执行完返回
  • _blank_zone_start和_TEXT_BASE用的是远连接,正常启动时:
  • blank_zone_start=0x80800040_TEXT_BASE=0x80800000
  • _start使用的是中长度寻址,实际正常启动时_start的地址范围在0x1400_0000之后的16M之内
  • 这一段的作用是:如果不是从内存启动,那么需要为uboot重定向到DDR做准备。
  • 假如是flash正常启动,在执行上面代码之后,r0寄存器中的地址是指向flash中异常中断结束的地址,r1寄存器的值为0,
  • init_registers标号是在lowlevel_init.S文件中定义的,是一些比较底层的初始化,官方没有给出说明,看不懂 -_- 略过~
#ifdef CONFIG_DDR_TRAINING_V2
	ldr	sp, =STACK_TRAINING
	ldr	r0, =REG_BASE_SCTL
	bl	start_ddr_training       /* DDR training */
#endif
  • CONFIG_DDR_TRAINING_V2=1  该功能有使能
  • STACK_TRAINING=0x04014000 数据手册查不到
  • REG_BASE_SCTL=0x12050000  系统控制寄存器
  • start_ddr_training 是一个C语言函数,定义在lowlevel_init_v300.c 
  • DDR training:DR布线,完全按等长约束就没有ddr training的说法。当布线去掉等长约束或放宽约束条件,就要做ddr training,以保证时序的完整性,使信号的建立&保持时间窗口一致。ddr training是调整Addr/Cmd信号对CLK,DQ信号对DQS的延时。由于没做等长约束,信号有长,有短,就会导致信号有快,慢之差(信号在1000mil走线耗时约160~180ps,相对FR-4的板材),ddr training就是找到一套参数,使信号的建立与保持时间充足。并保存且写到配置中。

6.1重定向异常向量表

#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate:
	@copy arm exception table in 0 address
	adrl	r0, _start
	mov	r1, #0
	mov	r2, #0x100		/* copy arm Exception table to 0 addr */
	add     r2, r0, r2
  • 将_start的地址值赋值给r0寄存器
  • 将r1寄存器的值设置为0
  • 将r2寄存器的值设置为0x100
  • 将r2寄存器的值加r0寄存器的值赋值给r2    
  • 注意:
  • 这里的_start,在正常启动的时候,_start的地址是0x1400_0000,是flash的地址空间,r1是0地址,在地址重映射时,地址指向启动空间,地址重映射撤销后:此地址
  • 空间指向片内 RAM。前面我们取消了地址映射,这里应该是指向片内RAM
copy_exception_table:
	ldmia   r0!, {r3 - r10}
	stmia   r1!, {r3 - r10}
	cmp     r0, r2
	ble     copy_exception_table
  • 从R0所指的源数据区装载8个字数据到r3-R10中,每次装载1个字后R0中地址加1,最后更新R0中地址
  • 将R3-R10的8个字数据存入R1所指的目的数据区,每次装载1个字后R1中地址加1,最后更新R1中地址
  • 如果r0与r2寄存器数值表示的地址小于或者等于则循环执行copy_exception_table函数
  • 这里是从r0开始的地址拷贝0x100个字节到r1的地址中去,由于r0,r1寄存器的地址这里会自动加以,所以最终会出现r0大于r2结束循环
  • 注意:ble是指Branch if Less than or Equal,即小于或等于跳转;
  • 上面这一段实现的功能就是,将_start开始的100字节复制到地址为0的内部RAM中去.从反汇编中可以知道,异常向量表的地址是存在开始的0x00~0x40的空间,这里实际是将异常向量表赋值到片内RAM中去了。

7.重定向uboot到外部DDR

	@ relocate U-Boot to RAM
	adrl r0, _start		@ r0 <- current position of code
	ldr	r1, _TEXT_BASE		@ test if we run from flash or RAM
	cmp	r0, r1			@ don't reloc during debug
	beq	stack_setup
  • 将_start的地址复制给r0寄存器
  • 将_TEXT_BASE的地址赋值给r1寄存器
  • 比较r0与r1寄存器里的地址是否相同
  • 如果相同跳转到stack_setup去执行,执行完返回。
  • _start的地址使用的是adrl来加载,实际该值是flash的指令地址,也就是0x14000000
  • _TEXT_BASE的地址使用的是ldr来加载,正常启动时这个值就是我们连接的起始地址0x80800000
  • 只有从外部DDR启动的时候r0才会与r1相等,其它时候不会相等。
  • 正常启动执行到这里r0=0x14000000,r1=0x80800000
	ldr	r2, _armboot_start
	ldr	r3, _bss_start
	sub	r2, r3, r2		@ r2 <- size of armboot
	add	r2, r0, r2		@ r2 <- source end address
  • 将_armboot_start标号表示的地址赋值给r2寄存器
  • 将_bss_start标号的地址赋值给r3寄存器
  • 将r3减r2地址的值赋值给r2
  • 将r0加r2的值赋值给r2
  • _armboot_start和_bss_start使用的都是ldr加载,
  • _armboot_start=0x80800000,_bss_start=0x8084abc4(该值通过反汇编查询得到)
  • 正常启动到这里寄存器r2的值为r2=0x14000000+(0x8084abc4-0x80800000)
  • r2的地址实际表示flash中uboot bss段的开始地址(重定向时不需要bss段)。
copy_loop:				@ copy 32 bytes at a time
	ldmia	r0!, {r3 - r10}		@ copy from source address [r0]
	stmia	r1!, {r3 - r10}		@ copy to   target address [r1]
	cmp	r0, r2			@ until source end addreee [r2]
	ble	copy_loop
#endif	/* CONFIG_SKIP_RELOCATE_UBOOT */
  • r0=0x14000000,r1=0x80800000,与上面复制异常中断表到内部RAM中类似,这里是将flash中的uboot(除了bss段)全部复制到外部的DDR中去

8.设置栈空间

	/* Set up the stack */
stack_setup:
	ldr	r0, _TEXT_BASE		@ upper 128 KiB: relocated uboot
	sub	r0, r0, #CONFIG_SYS_MALLOC_LEN @ malloc area
	sub	r0, r0, #CONFIG_SYS_GBL_DATA_SIZE @ bdinfo
  • _TEXT_BASE 代码启动地址    
  • CONFIG_SYS_MALLOC_LEN = 0x40000 +128*1024 = (256+128)*1024 = 384KB
  • CONFIG_SYS_GBL_DATA_SIZE=128 Byte    
#ifdef CONFIG_USE_IRQ
	sub	r0, r0, #(CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ)
#endif
  • 在hi3521a中CONFIG_USE_IRQ没有被定义
	sub	sp, r0, #12		@ leave 3 words for abort-stack
	and	sp, sp, #~7		@ 8 byte alinged for (ldr/str)d
  • r0=0x80800000 - 0x60000(384K) - 0x80(byte)
  • sp=r0 - sp
  • 设置sp 8字节对齐

9.清除bss段

	/* Clear BSS (if any). Is below tx (watch load addr - need space) */
clear_bss:
	ldr	r0, _bss_start		@ find start of bss segment
	ldr	r1, _bss_end		@ stop here
	mov	r2, #0x0		@ clear value
  • 将bss段的开始位置放到r0寄存器中
  • 将bss段的结束位置放到r1寄存器中
  • 将数值0放置到r2寄存器    
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
  • 将r2寄存器的值(0)保存到r0寄存器所指向的地址中去
  • 比较r0与r1寄存器中的值是否相等
  • 将r0寄存器的值加4(地址加4字节)
  • 如果r0与r1表示的地址不相等,则循环到clbss_l处执行
  • 这一段代码的作用是:使用0填充_bss_start开始到_bss_end结束的地址空间。

10.跳转到C程序入口

	ldr	pc, _start_armboot	@ jump to C code

_start_armboot: .word start_armboot
  • 跳转到C语言阶段
  • start_armboot 函数是在arch/arm/lib/board.c 中被定义的
  • 注意这里使用ldr来跳转,这是个长跳转,由原来的spi flash 中运行直接跳转到外部DDR中运行,uboot后面都是在DDR中运行。
bug:

	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	b	.			/* bug here */
  • bug 标号定义,b.表示运行到最后进入了死循环。

  uboot的第一阶段启动到这里就结束了,后面调用C语言实现第二阶段的启动,在start.S 文件后面的汇编代码是一些异常中断的处理定义,这里不再介绍。其它内容可以参考博客《序言与目录》

致谢:

    本文内容有参考下列内容:

  • https://re-eject.gbadev.org/files/GasARMRef.pdf
  • https://www.crifan.com/files/doc/docbook/uboot_starts_analysis/release/webhelp/

----------------------------------------------------------------2022.08.28----------------------------------------------------------------

   新的文章|公|内容和附件工程文件

已更新|众|在博客首页和:

  |号|:liwen01

----------------------------------------------------------------2022.08.28----------------------------------------------------------------

最后

以上就是包容服饰为你收集整理的海思(Hi3521a)uboot详细分析(5)——uboot启动第一阶段start.S文件分析1.设置CPU SVC模式 2.关闭mmu和缓存3.启动流程判断4.关闭地址重映射5.使能指令缓存6.重定向异常向量表到内部RAM7.重定向uboot到外部DDR8.设置栈空间9.清除bss段10.跳转到C程序入口致谢:的全部内容,希望文章能够帮你解决海思(Hi3521a)uboot详细分析(5)——uboot启动第一阶段start.S文件分析1.设置CPU SVC模式 2.关闭mmu和缓存3.启动流程判断4.关闭地址重映射5.使能指令缓存6.重定向异常向量表到内部RAM7.重定向uboot到外部DDR8.设置栈空间9.清除bss段10.跳转到C程序入口致谢:所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部