一、u-boot.lds
1 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") 2 OUTPUT_ARCH(arm) 3 ENTRY(_start) 4 SECTIONS 5 { 6 . = 0x00000000; 7 . = ALIGN(4); 8 .text : 9 { 10 *(.__image_copy_start) 11 *(.vectors) 12 arch/arm/cpu/armv7/start.o (.text*) 13 *(.text*) 14 } 15 . = ALIGN(4); 16 .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } 17 . = ALIGN(4); 18 .data : { 19 *(.data*) 20 } 21 . = ALIGN(4); 22 . = .; 23 . = ALIGN(4); 24 .u_boot_list : { 25 KEEP(*(SORT(.u_boot_list*))); 26 } 27 . = ALIGN(4); 28 .image_copy_end : 29 { 30 *(.__image_copy_end) 31 } 32 .rel_dyn_start : 33 { 34 *(.__rel_dyn_start) 35 } 36 .rel.dyn : { 37 *(.rel*) 38 } 39 .rel_dyn_end : 40 { 41 *(.__rel_dyn_end) 42 } 43 .end : 44 { 45 *(.__end) 46 } 47 _image_binary_end = .; 48 . = ALIGN(4096); 49 .mmutable : { 50 *(.mmutable) 51 } 52 .bss_start __rel_dyn_start (OVERLAY) : { 53 KEEP(*(.__bss_start)); 54 __bss_base = .; 55 } 56 .bss __bss_base (OVERLAY) : { 57 *(.bss*) 58 . = ALIGN(4); 59 __bss_limit = .; 60 } 61 .bss_end __bss_limit (OVERLAY) : { 62 KEEP(*(.__bss_end)); 63 } 64 .dynsym _image_binary_end : { *(.dynsym) } 65 .dynbss : { *(.dynbss) } 66 .dynstr : { *(.dynstr*) } 67 .dynamic : { *(.dynamic*) } 68 .plt : { *(.plt*) } 69 .interp : { *(.interp*) } 70 .gnu.hash : { *(.gnu.hash) } 71 .gnu : { *(.gnu*) } 72 .ARM.exidx : { *(.ARM.exidx*) } 73 .gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) } 74 }
其中,
变量_start定义在文件arch/arm/lib/vectors.S中,为0x87800000,此文件被用来定义中断向量表,其中还包含有变量.vectors和ax
通过链接文件可以确定vectors.S在start.S之前运行
变量__image_copy_start在System.map和u-boot.map中均有定义,此处以u-boot.map为例:
其他变量也可以在System.map和u-boot.map中找到定义
而且我们可以通过u-boot.map中函数所属的文件夹确定其粗略位置,如后面章节需要用到的_main()函数就定义在arch/arm/lib/文件夹内
接下来我将分析start.S,因其内容较多,因此以功能分节
二、进入Supervisor模式,屏蔽IRQ和FIQ
1 b reset vectors.S 2 -> b save_boot_params start.S 3 -> ENTRY(save_boot_params) start.S 4 -> b save_boot_params_ret start.S
其中,save_boot_params_ret()函数定义如下:
1 mrs r0, cpsr @ 将cpsr寄存器值存储至r0寄存器中 2 and r1, r0, #0x1f @ 将r0寄存器值的低5位存储至r1寄存器中 3 teq r1, #0x1a @ 判断r1寄存器值是否等于0x1a(0b11010,Hyp模式) 4 bicne r0, r0, #0x1f @ 如果不相等,则将r0寄存器值的低5位置0 5 orrne r0, r0, #0x13 @ 并且与0x13(0b10011,Supervisor模式)进行或运算 6 orr r0, r0, #0xc0 @ 将r0寄存器值的bit[7:6]置0 7 msr cpsr,r0
cpsr的格式在《ARMv7架构参考手册》中B1.3.3一节
其低五位为M[4:0]定义在B1.3.1一节
bit[7]为IRQ屏蔽位,bit[6]为FIQ屏蔽位
三、向量表重定位
/* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */ /* CP15寄存器在《ARMv7架构参考手册》的Table B3-33中有定义,如下图所示 */ /* 参考《ARMv7架构参考手册》的B4.1.130一节 */ mrc p15, 0, r0, c1, c0, 0 @ 将CP15协处理器c1中SCTLR值存储至r0寄存器中 /* Source Insight提示arch/arm/include/asm/system.h中有其定义:#define CR_V (1 << 13)*/ bic r0, #CR_V @ 设置向量表地址为0,让向量表可以重定位 mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTLR Register /* Set vector address in CP15 VBAR register */ ldr r0, =_start @ _start = 0x87800000 /* 参考《ARMv7架构参考手册》的B4.1.156一节 */ mcr p15, 0, r0, c12, c0, 0 @ Set VBAR,设置向量表基地址
在上图中,读者可能会注意到的PL1 or higher
这个是ARM所划分的特权等级,在ARM v7-A架构中有PL0、PL1和PL2三种等级,PL0包含User,PL2包含Hyp,PL1包含其他模式,如下图所示:
四、设置CP15协处理器
bl cpu_init_cp15
-> ENTRY(cpu_init_cp15)
1 /* 参考《ARMv7架构参考手册》的Table B3-38 */ 2 mov r0, #0 @ set up for MCR 3 mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs,B4.2.3 PL2或Monitor Mode 4 mcr p15, 0, r0, c7, c5, 0 @ invalidate icache,B4.2.1 PL1 or higher 5 mcr p15, 0, r0, c7, c5, 6 @ invalidate branch predictors array,Table D14-3 6 mcr p15, 0, r0, c7, c10, 4 @ DSB 7 mcr p15, 0, r0, c7, c5, 4 @ ISB 8 9 /* 10 * disable MMU stuff and caches 11 */ 12 mrc p15, 0, r0, c1, c0, 0 13 bic r0, r0, #0x00002000 @ clear bits 13 (--V-) 14 bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM) 15 orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align 16 orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB 17 orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache 18 mcr p15, 0, r0, c1, c0, 0 19 20 mov r5, lr @ Store my Caller 21 mrc p15, 0, r1, c0, c0, 0 @ r1 has Read Main ID Register (MIDR) 22 mov r3, r1, lsr #20 @ get variant field 23 and r3, r3, #0xf @ r3 has CPU variant 24 and r4, r1, #0xf @ r4 has CPU revision 25 mov r2, r3, lsl #4 @ shift variant field for combined value 26 orr r2, r4, r2 @ r2 has combined CPU variant + revision 27 28 mov pc, r5 @ back to my caller
五、设置栈顶指针sp
bl cpu_init_crit start.S
-> ENTRY(cpu_init_crit)
-> b lowlevel_init
-> ENTRY(lowlevel_init) arch/arm/cpu/armv7/lowlevel_init.S
-> ldr sp, =CONFIG_SYS_INIT_SP_ADDR
-> bl s_init /* 空函数 */
使用内部RAM设置栈顶
1 ldr sp, =CONFIG_SYS_INIT_SP_ADDR 2 /* 8bit对齐 */ 3 bic sp, sp, #7
CONFIG_SYS_INIT_SP_ADDR值确定过程如下:
ldr sp, =CONFIG_SYS_INIT_SP_ADDR
-> #define CONFIG_SYS_INIT_SP_ADDR \ include/configs/mx6ull_lioker.h
-> (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)
-> #define CONFIG_SYS_INIT_RAM_ADDR IRAM_BASE_ADDR
-> #define CONFIG_SYS_INIT_SP_OFFSET \
-> (CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE)
-> #define CONFIG_SYS_INIT_RAM_SIZE IRAM_SIZE
也就是CONFIG_SYS_INIT_SP_ADDR = IRAM_BASE_ADDR + IRAM_SIZE - GENERATED_GBL_DATA_SIZE
IRAM_BASE_ADDR和IRAM_SIZE根据mx6ull_lioker.h头文件和如下的grep命令确定定义在arch/arm/include/asm/arch/imx-regs.h中
$ grep "IRAM_BASE_ADDR" * -nR
arch/arm/include/asm/arch/为arch/arm/include/asm/arch-mx6/的链接文件
GENERATED_GBL_DATA_SIZE根据如下的grep命令确定定义在include/generated/generic-asm-offsets.h中,grep命令如下:
上述变量定义如下:
1 #define IRAM_BASE_ADDR 0x00900000 2 3 /* CONFIG_MX6UL在.config中定义,因此IRAM_SIZE = 0x00040000 */ 4 #if !(defined(CONFIG_MX6SX) || defined(CONFIG_MX6UL) || \ 5 defined(CONFIG_MX6SLL) || defined(CONFIG_MX6SL)) 6 #define IRAM_SIZE 0x00040000 7 #else 8 #define IRAM_SIZE 0x00020000 9 #endif 10 11 #define GENERATED_GBL_DATA_SIZE 256
最终,CONFIG_SYS_INIT_SP_ADDR = 0x0091FF00
至此,可以确定函数调用流程如下:
save_boot_params_ret /* 进入SVC模式,关闭IRQ和FIQ,向量表重定位 */
-> cpu_init_cp15 /* 初始化CP15协处理器 */ -> cpu_init_crit -> lowlevel_init /* 设置栈顶指针CONFIG_SYS_INIT_SP_ADDR = 0x0091FF00 */ -> s_init /* 空函数 */ -> _main
cpu_init_cp15()和cpu_init_crit()函数执行完后,开始执行指令bl _main
六、struct gd_t gd设置
在第二节中,我们已经确定_main()函数定义在arch/arm/lib/文件夹内
在arch/arm/lib/文件夹内执行grep命令,从而确定_main()函数定义在arch/arm/lib/crt0.S文件中
函数定义如下,代码中已删除不执行部分,而且因篇幅问题,本节只介绍gd相关设置
1 ENTRY(_main) 2 ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) /* sp = 0x0091FF00 */ 3 bic sp, sp, #7 /* 8字节对齐 */ 4 5 mov r0, sp /* r0 = 0x0091FF00,作为board_init_f_alloc_reserve()函数的输入参数 */ 6 bl board_init_f_alloc_reserve /* common/init/board_init.c */ 7 mov sp, r0 /* sp = 0x0091FA00 */ 8 /* set up gd here, outside any C code */ 9 mov r9, r0 /* r9 = 0x0091FA00,arch/arm/include/asm/global_data.h定义r9存储gd地址 */ 10 bl board_init_f_init_reserve /* common/init/board_init.c */ 11 12 mov r0, #0 13 bl board_init_f /* common/board_f.c */ 14 15 #if ! defined(CONFIG_SPL_BUILD) 16 ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp = 0x9ef41e90 */ 17 bic sp, sp, #7 /* 8字节对齐,仍为0x9ef41e90 */ 18 19 ldr r9, [r9, #GD_BD] /* r9 = gd->bd = 0x9ef41fb0 */ 20 sub r9, r9, #GD_SIZE /* new GD is below bd,0x9ef41eb8 */ 21 22 adr lr, here /* 相对寻址,lr = here */ 23 ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off = 0 */ 24 add lr, lr, r0 25 26 ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr = 0x9ff44000 */ 27 b relocate_code /* 代码重定位 */ 28 here: 29 bl relocate_vectors /* 向量重定位 */ 30 31 bl c_runtime_cpu_setup /* 禁用icache */ 32 #endif 33 #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK) 34 ldr r0, =__bss_start /* this is auto-relocated! */ 35 ldr r1, =__bss_end /* this is auto-relocated! */ 36 mov r2, #0x00000000 /* prepare zero to clear BSS */ 37 38 clbss_l: 39 cmp r0, r1 /* 清除BSS段,while not at end of BSS */ 40 strlo r2, [r0] /* clear 32-bit BSS word */ 41 addlo r0, r0, #4 /* move to next */ 42 blo clbss_l 43 44 #if ! defined(CONFIG_SPL_BUILD) 45 bl coloured_LED_init /* 空函数 */ 46 bl red_led_on /* 空函数 */ 47 #endif 48 /* call board_init_r(gd_t *id, ulong dest_addr) */ 49 mov r0, r9 /* r0用来存储全局变量gd的起始地址 */ 50 ldr r1, [r9, #GD_RELOCADDR] /* r1用来存储重定位的起始地址 */ 51 ldr pc, =board_init_r /* this is auto-relocated! */ 52 /* we should not return here. */ 53 #endif 54 55 ENDPROC(_main)
1. board_init_f_alloc_reserve()和board_init_f_init_reserve()函数调用过程如下:
bl board_init_f_alloc_reserve /* board_init_f_alloc_reserve(top = 0x91FF00) */ -> top -= CONFIG_SYS_MALLOC_F_LEN; /* 0x91FF00 - 0x400 = 0x91FB00 */ -> top = rounddown(top-sizeof(struct global_data), 16); /* GD_SIZE = 248 */ -> top = top-GD_SIZE - ((top-GD_SIZE) % 16); /* 16字节对齐,top = 0x91FA00 */ -> return top; /* r0 = 0x91FA00 */ mov sp, r0 bl board_init_f_init_reserve /* board_init_f_init_reserve(base = 0x91FA00) */ -> struct global_data *gd_ptr; -> gd_ptr = (struct global_data *)base; -> base += roundup(sizeof(struct global_data), 16); /* GD_SIZE = 248 */ -> base += (GD_SIZE + (16 - 1)) /16) * 16; /* 0x91FA00 + 256 = 0x91FB00 */ -> gd->malloc_base = base; /* gd->malloc_base = 0x91FB00 */ -> base += CONFIG_SYS_MALLOC_F_LEN; /* 0x91FB00 + 0x400 = 0x91FF00 */
完成后的内存分布如下图所示:
之前所提到的gd是一个全局变量,用于存储CPU主频、环境变量地址、RAM地址和大小等数据
2. board_init_f()函数定义如下:
1 void board_init_f(ulong boot_flags/* = 0 */) { 2 gd->flags = boot_flags; 3 gd->have_console = 0; 4 5 /* initcall_run_list()定义在lib/initcall.c中 6 * init_sequence_f[]定义在common/board_f.c中 7 */ 8 if (initcall_run_list(init_sequence_f)) /* 初始化序列 */ 9 hang(); /* for ( ; ; ) ; */ 10 }
其中,
initcall_run_list()函数定义如下:
1 int initcall_run_list(const init_fnc_t init_sequence[]) { 2 const init_fnc_t *init_fnc_ptr; 3 4 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { 5 int ret = (*init_fnc_ptr)(); /* 依次调用init_sequence[]中的函数 */ 6 if (ret) { 7 return -1; 8 } 9 } 10 return 0; 11 }
init_sequence_f[]定义如下:
1 /** 2 * 函数中包含有gd成员设置的注释与其他注释不会在同一种缩进格式下 3 * gd成员设置我会在第三点中进行陈述 4 */ 5 static init_fnc_t init_sequence_f[] = { 6 setup_mon_len, /* 获取uboot长度/大小 */ 7 initf_malloc, /* malloc内存池大小 */ 8 initf_console_record, /* 空函数 */ 9 arch_cpu_init, /* basic arch cpu dependent setup */ 10 initf_dm, /* 初始化驱动模型 */ 11 arch_cpu_init_dm, /* 空函数 */ 12 mark_bootstage, /* need timer, go after init dm */ 13 board_early_init_f, /* 初始化UART_IOMUX */ 14 timer_init, /* 初始化定时器并设置gd成员 */ 15 board_postclk_init, /* Set VDDSOC to 1.175V */ 16 get_clocks, /* 获取sdhc_clk */ 17 env_init, /* 获取保存环境变量的地址 */ 18 init_baud_rate, /* 根据环境变量获取波特率 */ 19 serial_init, /* 启动UART并设置gd成员 */ 20 console_init_f, /* UART设置完成,将之前暂存的信息打印出来 */ 21 display_options, /* 打印uboot版本信息 */ 22 display_text_info, /* 打印uboot主要代码段的起始地址 */ 23 print_cpuinfo, /* 打印CPU信息 */ 24 show_board_info, /* 打印单板信息 */ 25 INIT_FUNC_WATCHDOG_INIT /* 初始化看门狗,空函数 */ 26 INIT_FUNC_WATCHDOG_RESET /* 复位看门狗,空函数 */ 27 init_func_i2c, /* 初始化I2C */ 28 announce_dram_init, /* 打印"DRAM: " */ 29 dram_init, /* 获取RAM大小 */ 30 INIT_FUNC_WATCHDOG_RESET 31 INIT_FUNC_WATCHDOG_RESET 32 INIT_FUNC_WATCHDOG_RESET 33 34 setup_dest_addr, /* setup_dest_addr()至display_new_sp()均为gd设置 */ 35 reserve_round_4k, 36 reserve_mmu, 37 reserve_trace, 38 reserve_uboot, 39 reserve_malloc, 40 reserve_board, 41 setup_machine, 42 reserve_global_data, 43 reserve_fdt, 44 reserve_arch, 45 reserve_stacks, 46 setup_dram_config, 47 show_dram_config, 48 display_new_sp, 49 INIT_FUNC_WATCHDOG_RESET 50 reloc_fdt, /* 空函数 */ 51 setup_reloc, /* 代码重定位 */ 52 NULL, 53 };
3. struct gd_t gd成员设置:
1 /* 为缩减长度,在此并未提供函数名 */ 2 gd->malloc_base = base; /* 0x0091FA00 */ 3 4 gd->flags = boot_flags; /* 0 */ 5 gd->have_console = 0; 6 7 gd->mon_len = (ulong)&__bss_end - (ulong)_start; /* 0x878abab8-0x87800000=0xabab8(其值因代码长度而变化) */ 8 9 gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN; /* 0x400 */ 10 gd->malloc_ptr = 0; 11 12 gd->arch.tbl = 0; 13 gd->arch.tbu = 0; 14 15 gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC2_CLK); /* SD卡外设时钟:198000000 */ 16 17 gd->env_addr = (ulong)&default_environment[0]; /* default_environment地址:0x8784454E */ 18 gd->env_valid = 1; 19 /* 在环境变量中查找baudrate,没有则使用CONFIG_BAUDRATE(115200) */ 20 gd->baudrate = getenv_ulong("baudrate", 10, CONFIG_BAUDRATE); 21 22 gd->flags |= GD_FLG_SERIAL_READY; /* 0x00100 */ 23 24 gd->have_console = 1; 25 26 gd->ram_size = imx_ddr_size(); /* DRAM大小:0x20000000 = 512M */ 27 28 gd->ram_top = CONFIG_SYS_SDRAM_BASE; /* DRAM起始地址:0x80000000 */ 29 gd->ram_top += get_effective_memsize(); /* 0x80000000 + gd->ram_size = 0xA0000000 */ 30 gd->ram_top = board_get_usable_ram_top(gd->mon_len);/* DRAM结束地址:0xA0000000 */ 31 gd->relocaddr = gd->ram_top; 32 33 gd->relocaddr = determine_mp_bootpg(NULL); /* 0xA0000000 */ 34 35 gd->relocaddr &= ~(4096 - 1); 36 37 gd->arch.tlb_size = PGTABLE_SIZE; /* 4096*4=0x4000 */ 38 gd->relocaddr -= gd->arch.tlb_size; /* 0x9fffc000 */ 39 gd->relocaddr &= ~(0x10000 - 1); /* 0x9fff0000 */ 40 gd->arch.tlb_addr = gd->relocaddr; 41 42 gd->relocaddr -= gd->mon_len; /* 0x9fff0000 - 0xabab8 = 0x9ff44548 */ 43 gd->relocaddr &= ~(4096 - 1); /* 0x9ff44000 */ 44 gd->start_addr_sp = gd->relocaddr; 45 46 /* TOTAL_MALLOC_LEN = 16 * 0x100000 */ 47 gd->start_addr_sp = gd->start_addr_sp - TOTAL_MALLOC_LEN; /* 0x9ef42000 */ 48 49 gd->start_addr_sp -= sizeof(bd_t); /* 0x9ef41fb0 */ 50 gd->bd = (bd_t *)map_sysmem(gd->start_addr_sp, sizeof(bd_t)); 51 52 gd->start_addr_sp -= sizeof(gd_t); /* 0x9ef41eb8 */ 53 gd->new_gd = (gd_t *)map_sysmem(gd->start_addr_sp, sizeof(gd_t)); 54 55 gd->start_addr_sp -= 16; /* 0x9ef41ea8 */ 56 gd->start_addr_sp &= ~0xf; /* 0x9ef41ea0 */ 57 gd->irq_sp = gd->start_addr_sp; 58 gd->start_addr_sp -= 16; /* 0x9ef41e90 */ 59 60 gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE; /* 0x80000000 */ 61 gd->bd->bi_dram[0].size = get_effective_memsize(); /* gd->bd->bi_dram[0].size = gd->ram_size = 0x20000000 */
在此,我只分析15行和26行代码:
gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC2_CLK)调用过程:
mxc_get_clock(MXC_ESDHC2_CLK) -> return get_usdhc_clk(1); -> u32 cscmr1 = __raw_readl(&imx_ccm->cscmr1); /* cscmr1 = *0x20c401c = 0x4900100 */ -> u32 cscdr1 = __raw_readl(&imx_ccm->cscdr1); /* cscdr1 = *0x20c4024 = 0x6490b00 */ -> usdhc_podf = (cscdr1 & (0x7 << 16)) >> 16; /* usdhc_podf = 1 */ -> clk_sel = cscmr1 & (1 << 17); /* clk_sel = 0 */ -> root_freq = mxc_get_pll_pfd(PLL_BUS, 2); /* root_freq = 0x179a7b00,为get_usdhc_clk(1)返回值 */ -> div = __raw_readl(&imx_ccm->analog_pfd_528); /* div = 0x5058505b */ -> freq = (u64)decode_pll(PLL_BUS, 24000000); /* freq = 0x1f78a400 */ -> u32 div = __raw_readl(&imx_ccm->analog_pll_528); -> div &= 0x01; /* 1 */ -> return 24000000 * (20 + (div << 1)); /* 24000000 * 22 = 528000000 = 0x1f78a400 */ -> return lldiv(freq * 18, (div & 0x3f)) >> 0); /* (528000000 * 18, 0x1b) */ -> do_div(freq * 18, 0x1b); -> __div64_32(&n, 0x1b);
gd->ram_size = imx_ddr_size()调用过程:
dram_init() -> gd->ram_size = imx_ddr_size(); -> struct esd_mmdc_regs *mem = (struct esd_mmdc_regs *)MEMCTL_BASE; /* 0x21b0000 */ -> unsigned ctl = readl(&mem->ctl); /* ctl = 0x84180000 */ -> unsigned misc = readl(&mem->misc); /* misc = 0x40201740 */ -> int bits = 11 + 0 + 0 + 1; /* row + col + bank + width */ -> bits += ESD_MMDC_CTL_GET_ROW(ctl); /* ((0x84180000 >> 24) & 7) = 4 */ -> bits += col_lookup[ESD_MMDC_CTL_GET_COLUMN(ctl)]; /* col_lookup [((0x84180000 >> 20) & 7) = 1] = 10 */ -> bits += bank_lookup[ESD_MMDC_MISC_GET_BANK(misc)]; /* bank_lookup[((0x40201740 >> 5 ) & 1) = 0] = 3 */ -> bits += ESD_MMDC_CTL_GET_WIDTH(ctl); /* ((0x84180000 >> 16) & 3) = 0 */ -> bits += ESD_MMDC_CTL_GET_CS1(ctl); /* ((0x84180000 >> 30) & 1) = 0 */ -> return 1 << bits; /* 1 << 29 = 521M,bits = 12 + 4 + 10 + 3 + 0 + 0 = 29 */
gd所涉及的内存布局如下图所示:
_main()函数中调用的和init_sequence_f[]中定义的其他函数我将在接下来几章中进行介绍