u-boot 1.1.6分析:start_armboot()分析一
当跑完start.S和lowlevel_init.S后,就会跳转到start_armboot()这个函数继续运行,这个函数定义在/lib_arm/board.c文件中。
一、全局变量gd初始化
1、为类型为struct gd_t的全局变量gd分配空间,并对其进行初始化;同时为类型为struct bd_t的gd_t->bd变量分配空间,并对其进行初始化。(在当前版本,这两个结构体的大小都为36字节)
2、gd即global data,表示全局变量,存放在r8寄存器中。gd->bd即board data,用来存放开发板的信息。
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); /* compiler optimization barrier needed for GCC >= 3.4 */ __asm__ __volatile__("": : :"memory"); memset ((void*)gd, 0, sizeof (gd_t)); gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); memset (gd->bd, 0, sizeof (bd_t));
二、获取u-boot的代码长度
1、程序的主要分段:.text段(代码段,用来存放程序代码)、.data段(数据段,用来存放已经初始化的全局变量和静态变量)、.bss段(用来存放未初始化的全局变量)
2、根据/board/100ask24x0/u-boot.lds链接脚本可知,.bss段在最后,所以u-boot的代码长度就等于.bss段起始地址减去入口地址
3、之所以不考虑.bss段,是因为.bss段里的内容是已知的,初始化后全为0。
4、注意:此时程序已经运行在SDRAM中的,所以入口地址为0x33f80000。
monitor_flash_len = _bss_start - _armboot_start;
三、根据函数初始化数组,依次调用函数进行初始化
(这里,列出主要用到的初始化函数)
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } }
1、cpu_init()
(1)该函数定义在/cpu/arm920t/cpu.c中
(2)由于在配置头文件中定义了宏CONFIG_USE_IRQ,所以这里会配置IRQ和FIQ的栈大小,以及配置可用的SDRAM的大小
(3)在IRQ栈中,预留了4字节。
#define CFG_ENV_SIZE 0x20000 #define CFG_MALLOC_LEN (CFG_ENV_SIZE + 128*1024) #define CFG_GBL_DATA_SIZE 128 #define PHYS_SDRAM_1 0x30000000 #define CONFIG_STACKSIZE_IRQ (4*1024) #define CONFIG_STACKSIZE_FIQ (4*1024) int cpu_init (void) { #ifdef CONFIG_USE_IRQ IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4; //IRQ_STACK_START = 0x33f3ff7c FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ; //FRQ_STACK_START = 0x33f3ef7c FREE_RAM_END = FIQ_STACK_START - CONFIG_STACKSIZE_FIQ - CONFIG_STACKSIZE; FREE_RAM_SIZE = FREE_RAM_END - PHYS_SDRAM_1; #else FREE_RAM_END = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4 - CONFIG_STACKSIZE; FREE_RAM_SIZE = FREE_RAM_END - PHYS_SDRAM_1; #endif return 0; }
2、board_init()
(1)该函数定义在/board/100ask24x0/100ask24x0.c文件中
(2)配置GPIO口、开发板的芯片ID、启动参数的存放地址
int board_init (void) { S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER(); S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO(); gpio->GPACON = 0x007FFFFF; gpio->GPBCON = 0x00044555; gpio->GPBUP = 0x000007FF; gpio->GPCCON = 0xAAAAAAAA; gpio->GPCUP = 0x0000FFFF; gpio->GPDCON = 0xAAAAAAAA; gpio->GPDUP = 0x0000FFFF; gpio->GPECON = 0xAAAAAAAA; gpio->GPEUP = 0x0000FFFF; gpio->GPFCON = 0x000055AA; gpio->GPFUP = 0x000000FF; gpio->GPGCON = 0xFF95FFBA; gpio->GPGUP = 0x0000FFFF; gpio->GPHCON = 0x002AFAAA; gpio->GPHUP = 0x000007FF; gd->bd->bi_arch_number = MACH_TYPE_S3C2440; gd->bd->bi_boot_params = 0x30000100; return 0; }
3、interrupt_init()
(1)这里的中断初始化实际上是定时器的初始化
(2)根据芯片手册,定时器4是内部定时器,没有输出,所以这里是初始化定时器4
(3)由于周期要使用10ms,所以可根据公式算出相关配置参数 f = PCLK / (prescaler value) / (divider value)
int interrupt_init (void) { S3C24X0_TIMERS * const timers = S3C24X0_GetBase_TIMERS(); timers->TCFG0 = 0x0f00; //定时器4的预分频系数为16 //TCFG1寄存器使用默认值,所以定时器4的分配值为2 if (timer_load_val == 0) { timer_load_val = get_PCLK()/(2 * 16 * 100); //HZ对应的单位为s,所以要除以100得到10ms单位 } lastdec = timers->TCNTB4 = timer_load_val; timers->TCON = (timers->TCON & ~0x0700000) | 0x600000; //自动装载,手动更新计数值 timers->TCON = (timers->TCON & ~0x0700000) | 0x500000; //启动定时器4 timestamp = 0; return (0); }
4、env_init()
(1)环境变量的初始化函数要根据启动方式来确定,由于JZ2440使用的是NAND FLASH启动,并在配置头文件中定义了宏CFG_ENV_IS_IN_NAND,所以该函数定义在/common/env_nand.c文件中。
(2)但由于未定义CONFIG_NAND_U_BOOT(可在顶层的Makefile中定义:@echo "CONFIG_NAND_U_BOOT = y" >> $(obj)include/config.mk),所以环境变量需要进行配置。
(3)环境变量数组default_environment定义在/common/env_common.c中,而具体的宏则是由配置头文件来确定的。
#define CONFIG_BOOTARGS "noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0,115200" #define CONFIG_BOOTCOMMAND "nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0" #define CONFIG_BOOTDELAY 2 #define CONFIG_BAUDRATE 115200 #define CONFIG_ETHADDR 08:00:3e:26:0a:5b #define CONFIG_IPADDR 192.168.1.17 #define CONFIG_SERVERIP 192.168.1.11 #define CONFIG_NETMASK 255.255.255.0 //在这个数组中,只列出JZ2440定义好的环境变量 uchar default_environment[] = { #ifdef CONFIG_BOOTARGS "bootargs=" CONFIG_BOOTARGS "\0" #endif #ifdef CONFIG_BOOTCOMMAND "bootcmd=" CONFIG_BOOTCOMMAND "\0" #endif #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) "bootdelay=" MK_STR(CONFIG_BOOTDELAY) "\0" #endif #if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0) "baudrate=" MK_STR(CONFIG_BAUDRATE) "\0" #endif #ifdef CONFIG_ETHADDR "ethaddr=" MK_STR(CONFIG_ETHADDR) "\0" #endif #ifdef CONFIG_IPADDR "ipaddr=" MK_STR(CONFIG_IPADDR) "\0" #endif #ifdef CONFIG_SERVERIP "serverip=" MK_STR(CONFIG_SERVERIP) "\0" #endif #ifdef CONFIG_NETMASK "netmask=" MK_STR(CONFIG_NETMASK) "\0" #endif }; int env_init(void) { gd->env_addr = (ulong)&default_environment[0]; gd->env_valid = 1; return (0); }
5、init_baudrate()
static int init_baudrate (void) { char tmp[64]; int i = getenv_r ("baudrate", tmp, sizeof (tmp)); gd->bd->bi_baudrate = gd->baudrate = (i > 0) ? (int) simple_strtoul (tmp, NULL, 10) : CONFIG_BAUDRATE; return (0); }
6、serial_init()
(1)不同的SOC的串口寄存器地址不同,所以有不同的初始化函数,JZ2440的初始化函数定义在/cpu/arm920t/s3c24x0/serial.c中
(2)串口初始化完成后,串口才有输出。
#define CONFIG_SERIAL1 1 #ifdef CONFIG_SERIAL1 #define UART_NR S3C24X0_UART0 #endif void serial_setbrg (void) { S3C24X0_UART * const uart = S3C24X0_GetBase_UART(UART_NR); int i; unsigned int reg = 0; reg = get_PCLK() / (16 * gd->baudrate) - 1; /* FIFO enable, Tx/Rx FIFO clear */ uart->UFCON = 0x07; uart->UMCON = 0x0; /* Normal,No parity,1 stop,8 bit */ uart->ULCON = 0x3; /* * tx=level,rx=edge,disable timeout int.,enable rx error int., * normal,interrupt or polling */ uart->UCON = 0x245; uart->UBRDIV = reg; #ifdef CONFIG_HWFLOW uart->UMCON = 0x1; /* RTS up */ #endif for (i = 0; i < 100; i++); } int serial_init (void) { serial_setbrg (); return (0); }
7、console_init_f()
int console_init_f (void) { gd->have_console = 1; return (0); }
8、display_banner()
static int display_banner (void) { printf ("\n\n%s\n\n", version_string); debug ("U-Boot code: %08lX -> %08lX BSS: -> %08lX\n", _armboot_start, _bss_start, _bss_end); debug ("IRQ Stack: %08lx\n", IRQ_STACK_START); debug ("FIQ Stack: %08lx\n", FIQ_STACK_START); return (0); }
9、dram_init()
int dram_init (void) { gd->bd->bi_dram[0].start = PHYS_SDRAM_1; gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; return 0; }
10、display_dram_config()
static int display_dram_config (void) { int i; ulong size = 0; for (i=0; i<CONFIG_NR_DRAM_BANKS; i++) { size += gd->bd->bi_dram[i].size; } puts("DRAM: "); print_size(size, "\n"); return (0); }
到这里,初始化序列就已经跑完了,那么接下来该继续执行start_armboot()的后续代码了。