uboot启动——lowlevel_init函数详解

1、将lr寄存器中的值压栈

push	{
    
    lr}

lr寄存器保存的是函数返回地址,每个模式下只有一个lr寄存器,如果涉及多重函数调用,则lr寄存器会被覆盖,导致返回地址丢失。在之前已经初始化栈,所以这里可以将lr寄存器压栈,将来函数返回的时候再弹栈。

2、检查复位的状态

	/* check reset status  */
	ldr	r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET) //0xE0100000 + 0xa000
	ldr	r1, [r0]
	bic	r1, r1, #0xfff6ffff
	cmp	r1, #0x10000
	beq	wakeup_reset_pre
	cmp	r1, #0x80000
	beq	wakeup_reset_from_didle

在这里插入图片描述>0xE010a000是寄存器的地址,查询数据手册可以得到该寄存器的描述。根据不同的复位状态调用不同的唤醒函数,实现快速启动。比如:启动是断电重启,那所以的初始化都必须做;从睡眠模式启动,则底层的某些初始化就可以跳过;

3、I/O相关的检查

	/* IO Retention release */
	ldr	r0, =(ELFIN_CLOCK_POWER_BASE + OTHERS_OFFSET) //0xE0100000 + 0xe00
	ldr	r1, [r0]
	ldr	r2, =IO_RET_REL
	orr	r1, r1, r2
	str	r1, [r0]

这也是分析寄存器的值来检查I/O状态,不太熟悉,感兴趣的可以根据地址去数据手册查寄存器说明。

4、关看门狗

	/* Disable Watchdog */
	ldr	r0, =ELFIN_WATCHDOG_BASE	/* 0xE2700000 */
	mov	r1, #0
	str	r1, [r0]

关看门狗,不希望在启动阶段去喂狗,也不想因为看门狗导致重启。

5、SRAM和SROM相关的初始化

/* SRAM(2MB) init for SMDKC110 */
	/* GPJ1 SROM_ADDR_16to21 */
	ldr	r0, =ELFIN_GPIO_BASE
	
	ldr	r1, [r0, #GPJ1CON_OFFSET]
	bic	r1, r1, #0xFFFFFF
	ldr	r2, =0x444444
	orr	r1, r1, r2
	str	r1, [r0, #GPJ1CON_OFFSET]

	ldr	r1, [r0, #GPJ1PUD_OFFSET]
	ldr	r2, =0x3ff
	bic	r1, r1, r2
	str	r1, [r0, #GPJ1PUD_OFFSET]

	/* GPJ4 SROM_ADDR_16to21 */
	ldr	r1, [r0, #GPJ4CON_OFFSET]
	bic	r1, r1, #(0xf<<16)
	ldr	r2, =(0x4<<16)
	orr	r1, r1, r2
	str	r1, [r0, #GPJ4CON_OFFSET]

	ldr	r1, [r0, #GPJ4PUD_OFFSET]
	ldr	r2, =(0x3<<8)
	bic	r1, r1, r2
	str	r1, [r0, #GPJ4PUD_OFFSET]


	/* CS0 - 16bit sram, enable nBE, Byte base address */
	ldr	r0, =ELFIN_SROM_BASE	/* 0xE8000000 */
	mov	r1, #0x1
	str	r1, [r0]

SRAM和SROM相关的初始化,不熟,想了解根据地址去数据手册查寄存器说明。

6、看发板供电锁存

	ldr	r0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET)
	ldr	r1, [r0]
	orr	r1, r1, #0x300	
	orr	r1, r1, #0x1	
	str	r1, [r0]

参考博客:《开发板的上电锁存》

7、判断当前运行在iRAM还是DDR

	ldr	r0, =0xff000fff
	bic	r1, pc, r0		/* r0 <- current base addr of code */
	ldr	r2, _TEXT_BASE		/* r1 <- original base addr in ram */
	bic	r2, r2, r0		/* r0 <- current base addr of code */
	cmp     r1, r2                  /* compare r0, r1                  */
	beq     1f			/* r0 == r1 then skip sdram init   */
	/* init system clock */
	bl system_clock_init
	/* Memory initialize */
	bl mem_ctrl_asm_init
1:
	/* for UART */
	bl uart_asm_init
	bl tzpc_init //不熟,跳过

把当前地址和链接地址进行比较,判断当前是在iRAM还是在DDR中。
(1)_TEXT_BASE 是链接地址,pc是当前运行地址,通过比较地址的几个高位判断当前所处位置。
(2)如果比较相等则说明已经重定位,此时代码是在链接地址处,本次启动是热启动,跳过后面的时钟和内存初始化;
(3)如果不相等则说明此次是冷启动,要执行时钟和内存的初始化;
(4)冷启动可以理解成断电重启;热启动可以理解成从低功耗或者休眠状态启动;
判断的依据:
(1)首先此段代码是在BL1中,此时还没有执行BL2的重定位,也没有执行内存初始化代码;
(2)链接地址是指向内存的,PC的值和链接地址比较相等说明此时代码运行在内存,说明已经完成BL2的重定位,内存也初始化完成,本次启动肯定不是冷启动;
(3)如果本次代码都运行在内存中,说明内存依据可用,那就没有必要再去初始化内存和时钟,这样可以缩短启动时间。

8、外设控制器初始化

	/* init system clock */
	bl system_clock_init

	/* Memory initialize */
	bl mem_ctrl_asm_init
	
1:
	/* init UART */
	bl uart_asm_init //该函数会串口输出大写的O,和后面输出的K组成成OK,是调试用的

	//不熟,跳过
	bl tzpc_init

9、再次检查复位状态和禁止ABB

	/* check reset status  */	
	ldr	r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
	ldr	r1, [r0]
	bic	r1, r1, #0xfffeffff
	cmp	r1, #0x10000
	beq	wakeup_reset_pre

	/* ABB disable */
	ldr	r0, =0xE010C300
	orr	r1, r1, #(0x1<<23)
	str	r1, [r0]

	/* Print 'K' */
	ldr	r0, =ELFIN_UART_CONSOLE_BASE
	ldr	r1, =0x4b4b4b4b
	str	r1, [r0, #UTXH_OFFSET]

10、函数返回

pop	{
    
    pc}

将之前保存的lr寄存器的值出栈,写到PC寄存器,实现函数返回。

おすすめ

転載: blog.csdn.net/weixin_42031299/article/details/121325981