分类: 嵌入式
2015-11-17 17:58:46
原文地址:uboot第二阶段代码详细分析 作者:xutianxi
start_armboot是U-Boot执行的第一个C语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。这里只简要列出了主要执行的函数流程:
void start_armboot (void)
{
init_fnc_t **init_fnc_ptr;
char *s;
#ifndef CFG_NO_FLASH
ulong size;
#endif
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
unsigned long addr;
#endif
/* Pointer is writable since we allocated a register for it */
//获取全局gd指针
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));
//获取bd_info结构体指针
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
gd->flags |= GD_FLG_RELOC; //标志位已经重定向
//整个代码区的长度
monitor_flash_len = _bss_start - _armboot_start;
//调用初始化函数,用来初始化gd结构体
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
#ifndef CFG_NO_FLASH
/* configure available FLASH banks */
//board/smdk2410/flash.c配置flash
//从其实现来看,好像只是配置nor flash
size = flash_init ();//初始化flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS],nor flash基地址和扇区大小
//显示flash信息
display_flash_config (size);
#endif /* CFG_NO_FLASH */
//定义显示类型
#ifdef CONFIG_VFD
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for VFD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
//按页对其方式保留显存
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
size = vfd_setmem (addr);
gd->fb_base = addr;//帧缓冲基地址
#endif /* CONFIG_VFD */
//显示器为LCD,同上
#ifdef CONFIG_LCD
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for LCD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
size = lcd_setmem (addr);
gd->fb_base = addr;
#endif /* CONFIG_LCD */
//初始化CFG_MALLOC_LEN大小空间
/* armboot_start is defined in the board-specific linker script */
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
//初始化nand flash,这是在nand flash启动的s
//在include/configs/smdk2410.h中的command definition中增加CONFIG_COMMANDS和CFG_CMD_NAND命令
#if (CONFIG_CMD_NAND)
puts ("NAND: ");
nand_init(); //board/smdk2410/smdk2410.c,获取nand的基地址和 大小信息
#endif
#ifdef CONFIG_HAS_DATAFLASH
AT
dataflash_print_info();
#endif
/* initialize environment */
//初始化环境参数
env_relocate ();
//framebuffer初始化
#ifdef CONFIG_VFD
/* must do this after the framebuffer is allocated */
drv_vfd_init();
#endif /* CONFIG_VFD */
//通过命令行参数传递获取ip地址
/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
//调用相应驱动函数对硬件设备进行初始化
stdio_init (); /* get the devices list going标准输入输出和设备初始化. */
#ifdef CONFIG_CMC_PU2
load_sernum_ethaddr ();
#endif /* CONFIG_CMC_PU2 */
jumptable_init ();
//初始化串口
console_init_r (); /* fully init console as a device */
#if defined(CONFIG_MISC_INIT_R)
/* miscellaneous platform dependent initialisations */
misc_init_r ();
#endif
/* enable exceptions */
//启用中断
enable_interrupts ();
/* Perform network card initialisation if necessary */
//初始化网卡
#ifdef CONFIG_DRIVER_CS8900
cs8900_get_enetaddr (gd->bd->bi_enetaddr);
#endif
#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN
if (getenv ("ethaddr")) {
smc_set_mac_addr(gd->bd->bi_enetaddr);
}
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN
/* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
#if (CONFIG_COMMANDS & CFG_CMD_NET)
if ((s = getenv ("bootfile")) != NULL) {
copy_filename (BootFile, s, sizeof (BootFile));
}
#endif /* CFG_CMD_NET */
#ifdef BOARD_LATE_INIT
board_late_init ();
#endif
#if (CONFIG_COMMANDS & CFG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
puts ("Net: ");
#endif
eth_initialize(gd->bd);
#endif
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();
}//end start_armboot
//下面详细分析下init_sequence
typedef int (init_fnc_t) (void); //注意这种用法,linux内核中也经常使用
int print_cpuinfo (void);
init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT)
arch_cpu_init, /* basic arch cpu dependent setup //cpu/arm920t/cpu.c中定义,该函数为空,因为没有采用IRQ或FIQ模式*/
#endif
board_init, /* basic board dependent ,初始化时钟频率,配置IO口,初始化全局数据bd(如平台号,传递内核参数的地址) ,setup //board/smdk2410/smdk2410.c */
#if defined(CONFIG_USE_IRQ)
interrupt_init, /* set up exceptions */
#endif
timer_init, /* initialize timer 使用定时器4 cpu/arm920t/s
#ifdef CONFIG_FSL_ESDHC
get_clocks,
#endif
env_init, /* initialize environment common/Env_nand.c 校验环境变量,并获取环境变量的地址 */
init_baudrate, /* initialze baudrate settings 获取波特率环境变量,初始化全局数据的中波特率 /lib_arm/board.c */
serial_init, /* serial communications setup driver/serial/serial_s
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here 打印uboot代码段和数据段地址信息 */
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */
#endif
#if defined(CONFIG_HARD_I
init_func_i
#endif
dram_init, /* configure available RAM ,获取ram的地址和大小,banks board/sunsang/smdk2410/smdk2410.c*/
#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)
arm_pci_init,
#endif
//打印BANK的相关信息
display_dram_config, //lib_arm/board.c
NULL,
};
int board_init (void)
{
;将时间相关的寄存器定义为结构体S
S
S
;设置cpu时钟
/* to reduce PLL lock time, adjust the LOCKTIME register */
clk_power->LOCKTIME = 0xFFFFFF;
/* configure MPLL */
//M_MDIV=0xA1,M_PDIV=0x3,M_SDIV=0x1
//这样系统时钟为
clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);
/* some delay between MPLL and UPLL */
delay (4000);
;USB时钟为
/* configure UPLL */
clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);
/* some delay between MPLL and UPLL */
delay (8000);
;设置GPIO
/* set up the I/O ports */
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;
;初始化bd结构体中的bi_arch_number和bi_boot_params
/* arch number of SMDK2410-Board 平台号,定义在include/asm-arm/mach-types.h */
gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
/* adress of boot parameters,内核启动时,传递给内核的参数 */
gd->bd->bi_boot_params = 0x30000100;
;启用指令和数据cache
;通过对协处理器的操作了实现cache的使能
icache_enable();
dcache_enable();
return 0;
} //end board_init
int timer_init(void)
{
;获取计时控制寄存器
struct s
ulong tmr;
;使用PWM定时器4
/* use PWM Timer 4 because it has no output */
/* prescaler for Timer 4 is 16 */
writel(0x
if (timer_load_val == 0) {
/*
* for 10 ms clock period @ PCLK with 4 bit divider = 1/2
* (default) and prescaler = 16. Should be 10390
* @33.25MHz and 15625 @ 50 MHz
*/
timer_load_val = get_PCLK() / (2 * 16 * 100);
timer_clk = get_PCLK() / (2 * 16);
}
/* load value for 10 ms timeout */
lastdec = timer_load_val;
writel(timer_load_val, &timers->TCNTB4);
/* auto load, manual update of Timer 4 */
tmr = (readl(&timers->TCON) & ~0x0700000) | 0x0600000;
writel(tmr, &timers->TCON);
/* auto load, start Timer 4 启用定时器4做为系统定时器 */
tmr = (tmr & ~0x0700000) | 0x0500000;
writel(tmr, &timers->TCON);
timestamp = 0;
return (0);
} //end timer_init
static int env_init (void)
{
//这个是自己修改的,和源码有所不同
if(pbootflag==1) //by lht 这种情况是从nor启动
{
env_name_spec = "NOR";
env_ptr=(env_t *)CONFIG_ENV_ADDR;
//#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x
return nor_env_init();
}
else // 从nand 启动
{
#if defined(ENV_IS_EMBEDDED) || defined(CONFIG_NAND_ENV_DST)
int crc1_ok = 0, crc2_ok = 0;
env_t *tmp_env1;
#ifdef CONFIG_ENV_OFFSET_REDUND
env_t *tmp_env2;
tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE);
crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
#endif
tmp_env1 = env_ptr;
crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
if (!crc1_ok && !crc2_ok) {
gd->env_addr = 0;
gd->env_valid = 0;
return 0;
} else if (crc1_ok && !crc2_ok) {
gd->env_valid = 1;
}
#ifdef CONFIG_ENV_OFFSET_REDUND
else if (!crc1_ok && crc2_ok) {
gd->env_valid = 2;
} else {
/* both ok - check serial */
if(tmp_env1->flags == 255 && tmp_env2->flags == 0)
gd->env_valid = 2;
else if(tmp_env2->flags == 255 && tmp_env1->flags == 0)
gd->env_valid = 1;
else if(tmp_env1->flags > tmp_env2->flags)
gd->env_valid = 1;
else if(tmp_env2->flags > tmp_env1->flags)
gd->env_valid = 2;
else /* flags are equal - almost impossible */
gd->env_valid = 1;
}
if (gd->env_valid == 2)
env_ptr = tmp_env2;
else
#endif
if (gd->env_valid == 1)
env_ptr = tmp_env1;
gd->env_addr = (ulong)env_ptr->data;
#else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
gd->env_addr = (ulong)&default_environment[0]; //使用默认环境变量,定义在common/env_common.c
gd->env_valid = 1;
#endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
}
return (0);
} //end env_init
//如果参数中设置了波特率则利用参数用设置的波特率,否则利用默认的CONFIG_BAUDRATE(115200)
static int init_baudrate (void)
{
char tmp[64]; /* long enough for environment variables */
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);
} //end init_baudrate
//初始化ram信息,设置起始地址和大小,从include/configs/smdk2410.h中获取这些信息
//,这里只是对gd中的 bi_dram结构中的两个成员赋值,
//也即BANK的起始地址和大小
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;
} // end dram_init
//显示ram信息,其中的宏也是从include/configs/smdk2410.h中读取
static int display_dram_config (void)
{
int i;
#ifdef DEBUG
puts ("RAM Configuration:n");
for(i=0; i
printf ("Bank #%d: %08lx ", i, gd->bd->bi_dram[i].start);
print_size (gd->bd->bi_dram[i].size, "n");
}
#else
ulong size = 0;
for (i=0; i
size += gd->bd->bi_dram[i].size;
}
puts("DRAM: ");
print_size(size, "n");
#endif
return (0);
} //end display_dram_config
//以上都是一些初始化的函数,可以看出以上这些函数都是为了初始化一个全局的结构体变量gd而执行的,
//该变量地址由寄存器r8指向,该结构体定义了开发板的相关硬件配置,在include/asm-arm/global_data.h中
//定义
typedef struct global_data {
bd_t *bd; 板子数据指针
unsigned long flags; //指示标志,如设备已经初始化标志等
unsigned long baudrate; /* 串口初始化标志*/
unsigned long have_console; /* serial_init() was called */
unsigned long env_addr; /* 环境变量的起始地址 */
unsigned long env_valid; /* 校验环境变量有效性的标志位 */
unsigned long fb_base; /* base address of frame buffer 帧缓冲基地址*/
#ifdef CONFIG_VFD
unsigned char vfd_type; /* display type lcd显示设备类型 */
#endif
#ifdef CONFIG_FSL_ESDHC
unsigned long sdhc_clk;
#endif
void **jt; /* jump table */
} gd_t;
typedef struct bd_info {
int bi_baudrate; /* serial console baudrate */
unsigned long bi_ip_addr; /* IP Address IP地址*/
struct environment_s *bi_env;
ulong bi_arch_number; /* unique id for this board 唯一的平台号 */
ulong bi_boot_params; /* where this board expects params 启动参数的地址*/
struct /* RAM configuration ram配置:起始地址和大小*/
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;
typedef struct environment_s {
uint32_t crc; /* CRC32 over data bytes 校验码 */
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
unsigned char flags; /* active/obsolete flags 是否有效 标志位*/
#endif
unsigned char data[ENV_SIZE]; /* Environment data */
/**
#define ENV_SIZE (CONFIG_ENV_SIZE - ENV_HEADER_SIZE)
#define CONFIG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */
#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x030000) /* addr of
environment */
} env_t;
环境变量指针 env_t *env_ptr = (env_t *)(&environment[0]);(common/env_flash.c)
env_ptr指向环境参数区,系统启动时默认的环境参数environment[],定义在common/environment.c中。
参数解释:
bootdelay 定义执行自动启动的等候秒数
baudrate 定义串口控制台的波特率
netmask 定义以太网接口的掩码
ethaddr 定义以太网接口的MAC地址
bootfile 定义缺省的下载文件
bootargs 定义传递给Linux内核的命令行参数
bootcmd 定义自动启动时执行的几条命令
serverip 定义tftp服务器端的IP地址
ipaddr 定义本地的IP地址
stdin 定义标准输入设备,一般是串口
stdout 定义标准输出设备,一般是串口
stderr 定义标准出错信息输出设备,一般是串口
}
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/funy_liu/archive/