又重新回到原点了,但是此时运行的环境是sdram中,好再次分析.
前面的都是相同的,但是在lowlevel_init中会有不同。
/* when we already run in ram, we don't need to relocate U-Boot.
* and actually, memory controller must be configured before U-Boot
* is running in ram.
*/
ldr r0, =0x00ffffff
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
会判断当前运行的环境是内部的RAM还是外部的RAM,现在运行的环境是外部的RAM,那么会跳过系统时钟的初始化以及内存的初始化,会重新初始化串口,webee210的板子上串口初始化后会打印2次的ok,这一点也验证了这个地方会重新运行。
接着回到调用函数的地方。start.s中:
/* Set stackpointer in internal RAM to call board_init_f */
call_board_init_f:
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
ldr r0,=0x00000000
#if defined(CONFIG_WEBEE210) || defined(CONFIG_MINI210)
adr r4, _start
ldr r5,_TEXT_BASE
cmp r5,r4
beq board_init_in_ram
此时我们是跑在了外部的ram中,这里还会进行判断,假如是外部的RAM,直接跳转到board_init_in_ram处:
board_init_in_ram:
bl board_init_f
好吧,调到board_init_f中了,Board.c (arch\arm\lib)
bd_t *bd;
init_fnc_t **init_fnc_ptr;
gd_t *id;
ulong addr, addr_sp;
/* Pointer is writable since we allocated a register for it */
gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07); //gd webee 210 上的地址是
// gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
memset((void *)gd, 0, sizeof(gd_t)); //清0
gd->mon_len = _bss_end_ofs; //uboot的长度,bss段的结束地址
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { //初始化函数指针
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
其中init_sequence定义如下:
init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT) // 有定义 初始化和CPU相关的ARCH
arch_cpu_init, /* basic arch cpu dependent setup */
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_F) //未定义
board_early_init_f,
#endif
timer_init, /* initialize timer */
#ifdef CONFIG_FSL_ESDHC //未定义
get_clocks,
#endif
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
#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_I2C) || defined(CONFIG_SOFT_I2C) //未定义
init_func_i2c,
#endif
dram_init, /* configure available RAM banks */
NULL,
};
@1:arch_cpu_init:
#ifdef CONFIG_ARCH_CPU_INIT
int arch_cpu_init(void)
{
s5p_set_cpu_id();
return 0;
}
#endif
s5p_set_cpu_id函数从cpu内部读取了相关的id值到
s5p_cpu_id的全局的变量。
@2:
int timer_init(void)
{
/* PWM Timer 4 */
pwm_init(4, MUX_DIV_2, 0);
pwm_config(4, 0, 0);
pwm_enable(4);
return 0;
}
初始化了pwm4的定时器,具体细节不讲了。
@3:env_init
int env_init(void)
{
//carl_Wang add
#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];
gd->env_valid = 1;
#endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
return (0);
}
看见了一堆的代码,但是经过查找之后,发现没用的很多,有用的是
#else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
gd已经知道了,是一个全局的变量,gd->env_addr 赋值了默认的env 参数表,同时置位env_valid有效。好看一下这个env参数表
即gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
const uchar default_environment[] = {
#ifdef CONFIG_BOOTARGS
"bootargs=" CONFIG_BOOTARGS "\0"
#endif
#ifdef CONFIG_BOOTCOMMAND
"bootcmd=" CONFIG_BOOTCOMMAND "nand read 0x30007fc0 0x100000 0x500000;bootm 0x30007fc0\0"
#endif
#ifdef CONFIG_RAMBOOTCOMMAND
"ramboot=" CONFIG_RAMBOOTCOMMAND "\0"
#endif
#ifdef CONFIG_NFSBOOTCOMMAND
"nfsboot=" CONFIG_NFSBOOTCOMMAND "\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_LOADS_ECHO
"loads_echo=" MK_STR(CONFIG_LOADS_ECHO) "\0"
#endif
#ifdef CONFIG_ETHADDR
"ethaddr=" MK_STR(CONFIG_ETHADDR) "\0"
#endif
#ifdef CONFIG_ETH1ADDR
"eth1addr=" MK_STR(CONFIG_ETH1ADDR) "\0"
#endif
#ifdef CONFIG_ETH2ADDR
"eth2addr=" MK_STR(CONFIG_ETH2ADDR) "\0"
#endif
#ifdef CONFIG_ETH3ADDR
"eth3addr=" MK_STR(CONFIG_ETH3ADDR) "\0"
#endif
#ifdef CONFIG_ETH4ADDR
"eth4addr=" MK_STR(CONFIG_ETH4ADDR) "\0"
#endif
#ifdef CONFIG_ETH5ADDR
"eth5addr=" MK_STR(CONFIG_ETH5ADDR) "\0"
#endif
#ifdef CONFIG_IPADDR
"ipaddr=" MK_STR(CONFIG_IPADDR) "\0"
#endif
#ifdef CONFIG_SERVERIP
"serverip=" MK_STR(CONFIG_SERVERIP) "\0"
#endif
#ifdef CONFIG_SYS_AUTOLOAD
"autoload=" CONFIG_SYS_AUTOLOAD "\0"
#endif
#ifdef CONFIG_PREBOOT
"preboot=" CONFIG_PREBOOT "\0"
#endif
#ifdef CONFIG_ROOTPATH
"rootpath=" MK_STR(CONFIG_ROOTPATH) "\0"
#endif
#ifdef CONFIG_GATEWAYIP
"gatewayip=" MK_STR(CONFIG_GATEWAYIP) "\0"
#endif
#ifdef CONFIG_NETMASK
"netmask=" MK_STR(CONFIG_NETMASK) "\0"
#endif
#ifdef CONFIG_HOSTNAME
"hostname=" MK_STR(CONFIG_HOSTNAME) "\0"
#endif
#ifdef CONFIG_BOOTFILE
"bootfile=" MK_STR(CONFIG_BOOTFILE) "\0"
#endif
#ifdef CONFIG_LOADADDR
"loadaddr=" MK_STR(CONFIG_LOADADDR) "\0"
#endif
#ifdef CONFIG_CLOCKS_IN_MHZ
"clocks_in_mhz=1\0"
#endif
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
"pcidelay=" MK_STR(CONFIG_PCI_BOOTDELAY) "\0"
#endif
#ifdef CONFIG_EXTRA_ENV_SETTINGS
CONFIG_EXTRA_ENV_SETTINGS
#endif
"\0"
};
这个有点类似于linux中的devicetree机制,根据“”中的字符串可以取到后面的相应的命令或者参数。注意一下bootcmd 的指令,启动linux时会用到。。。。。
@4:init_baudrate
初始化波特率,看一下env参数表中是否有这个命令,在 Webee210.h (include\configs) 中定义了
#define CONFIG_BAUDRATE 115200
所以gd->baudrate = 115200
static int init_baudrate(void)
{
char tmp[64]; /* long enough for environment variables */
int i = getenv_f("baudrate", tmp, sizeof(tmp));
gd->baudrate = (i > 0)
? (int) simple_strtoul(tmp, NULL, 10)
: CONFIG_BAUDRATE;
return (0);
}
@5: serial_init Serial.c (common)
int serial_init (void)
{
//carl_wang
if (!(gd->flags & GD_FLG_RELOC) || !serial_current) {
struct serial_device *dev = default_serial_console ();
return dev->init ();
}
return serial_current->init ();
}
struct serial_device 是一个关于串行设备的一些属性以及方法的(面向对象的思想)结构体,可以使用它构成一个链表。
struct serial_device {
char name[NAMESIZE];
int (*init) (void);
int (*uninit) (void);
void (*setbrg) (void);
int (*getc) (void);
int (*tstc) (void);
void (*putc) (const char c);
void (*puts) (const char *s);
#if CONFIG_POST & CONFIG_SYS_POST_UART
void (*loop) (int);
#endif
struct serial_device *next;
};
dev = default_serial_console()获取到了关于默认设备的实例,
__weak struct serial_device *default_serial_console(void)
{
#if defined(CONFIG_SERIAL0) //定义了串口0
return &s5p_serial0_device; //实例化
#elif defined(CONFIG_SERIAL1)
return &s5p_serial1_device;
#elif defined(CONFIG_SERIAL2)
return &s5p_serial2_device;
#elif defined(CONFIG_SERIAL3)
return &s5p_serial3_device;
#else
#error "CONFIG_SERIAL? missing."
#endif
}
这里获取的是s5p_serial0_device,所以调用dev->init则是调用了serial_init_dev,dev_index则是0
/*
* Initialise the serial port with the given baudrate. The settings
* are always 8 data bits, no parity, 1 stop bit, no start bits.
*/
int serial_init_dev(const int dev_index)
{
//carl_wang add
struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
/* reset and enable FIFOs, set triggers to the maximum */
writel(0, &uart->ufcon);
writel(0, &uart->umcon);
/* 8N1 */
writel(0x3, &uart->ulcon);
/* No interrupts, no DMA, pure polling */
writel(0x245, &uart->ucon);
serial_setbrg_dev(dev_index);
return 0;
}
@6:console_init_f,实际上只是置为标位,此时
gd->have_console = 1;
/* Called before relocation - use serial functions */
int console_init_f(void)
{
gd->have_console = 1;
#ifdef CONFIG_SILENT_CONSOLE //未定义
if (getenv("silent") != NULL)
gd->flags |= GD_FLG_SILENT;
#endif
return 0;
}
@7:display_banner 哎,就是log输出点版本信息,这里还log输出了uboot的起始地址,bss 段起始地址,bss段的结束地址。
打印出的log如下:
U-Boot 2011.06 (Dec 15 2015 - 22:32:20) for Webee_210_V2
U-Boot code: 33E00000 -> 33E66A30 BSS: -> 33E9CAFC
static int display_banner(void)
{
printf("\n\n%s\n\n", version_string);
printf("U-Boot code: %08lX -> %08lX BSS: -> %08lX\n",
_TEXT_BASE,
_bss_start_ofs + _TEXT_BASE, _bss_end_ofs + _TEXT_BASE);
#ifdef CONFIG_MODEM_SUPPORT
debug("Modem Support enabled\n");
#endif
#ifdef CONFIG_USE_IRQ
debug("IRQ Stack: %08lx\n", IRQ_STACK_START);
debug("FIQ Stack: %08lx\n", FIQ_STACK_START);
#endif
return (0);
}
@8:print_cpuinfo
log出了一点信息,其中你想写的,还有是芯片的频率 。
#ifdef CONFIG_DISPLAY_CPUINFO
int print_cpuinfo(void)
{
char buf[32];
printf("\n");
printf("##############################################\n");
printf("# #\n");
printf("# i love wunana #\n");
printf("# #\n");
printf("##############################################\n\n");
//printf("CPU:\tS5P%X@%sMHz\n",
// s5p_cpu_id, strmhz(buf, get_arm_clk()));
printf("CPU:\tS5PV210@%sMHz\n",
strmhz(buf, get_arm_clk()));
return 0;
}
#endif
@9:checkboard:没有特殊的作用,还是log
#ifdef CONFIG_DISPLAY_BOARDINFO
int checkboard(void)
{
//carl_wang
printf("\nBoard: Webee_210_V2\n");
return (0);
}
#endif
@10 dram_init 其实是初始化了
gd->ram_size = 512M
int dram_init(void)
{
/* Since we have discontinuous RAM configuration, just put
* bank1 here for relocation
*/
gd->ram_size = get_ram_size((long *)PHYS_SDRAM_1, PHYS_SDRAM_1_SIZE+PHYS_SDRAM_2_SIZE);
return 0;
}
ok,假如中间有任何错误的化都会挂起等待。接着往下走
#if defined(CONFIG_SYS_MEM_TOP_HIDE)
/*
* Subtract specified amount of memory to hide so that it won't
* get "touched" at all by U-Boot. By fixing up gd->ram_size
* the Linux kernel should now get passed the now "corrected"
* memory size and won't touch it either. This should work
* for arch/ppc and arch/powerpc. Only Linux board ports in
* arch/powerpc with bootwrapper support, that recalculate the
* memory size from the SDRAM controller setup will have to
* get fixed.
*/
gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
#endif
addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;
#ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
/* reserve kernel log buffer */
addr -= (LOGBUFF_RESERVE);
debug("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN,
addr);
#endif
#endif
其实就有一句话被编译到了,addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size; 其中CONFIG_SYS_SDRAM_BASE 定义是 0x30000000,而ram_size是 536870912
ok 往下
#ifdef CONFIG_PRAM //未定义
/*
* reserve protected RAM
*/
i = getenv_r("pram", (char *)tmp, sizeof(tmp));
reg = (i > 0) ? simple_strtoul((const char *)tmp, NULL, 10) :
CONFIG_PRAM;
addr -= (reg << 10); /* size is in kB */
debug("Reserving %ldk for protected RAM at %08lx\n", reg, addr);
#endif /* CONFIG_PRAM */
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) //会编译到
/* reserve TLB table */
addr -= (4096 * 4);
/* round down to next 64 kB limit */
addr &= ~(0x10000 - 1);
gd->tlb_addr = addr;
printf("TLB table at: %08lx\n", addr);
#endif
/* round down to next 4 kB limit */
addr &= ~(4096 - 1);
printf("Top of RAM usable for U-Boot at: %08lx\n", addr);
这个就是给TLB table分出了一些空间,然后禁止访问。
接着往下
#ifdef CONFIG_LCD //未定义
#ifdef CONFIG_FB_ADDR
gd->fb_base = CONFIG_FB_ADDR;
debug("Top the fb_base addr is : %08lx\n", gd->fb_base);
#else
/* reserve memory for LCD display (always full pages) */
addr = lcd_setmem(addr);
gd->fb_base = addr;
debug("Top the fb_base addr is : %08lx\n", gd->fb_base);
#endif /* CONFIG_FB_ADDR */
#endif /* CONFIG_LCD */
/*
* reserve memory for U-Boot code, data & bss
* round down to next 4 kB limit
*/
addr -= gd->mon_len;
addr &= ~(4096 - 1);
debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);
这个又是在为uboot预留空间,然后进行4Kb的四舍五入。
往下:
#ifndef CONFIG_SPL_BUILD //会编译到
/*
* reserve memory for malloc() arena
*/
addr_sp = addr - TOTAL_MALLOC_LEN;
debug("Reserving %dk for malloc() at: %08lx\n",
TOTAL_MALLOC_LEN >> 10, addr_sp);
/*
* (permanently) allocate a Board Info struct
* and a permanent copy of the "global" data
*/
addr_sp -= sizeof (bd_t);
bd = (bd_t *) addr_sp;
gd->bd = bd;
debug("Reserving %zu Bytes for Board Info at: %08lx\n",
sizeof (bd_t), addr_sp);
#ifdef CONFIG