2.1.2 第二阶段(Stage 2)
vivi的第二阶段是从main()函数开始,同一般的C语言程序一样,该函数在/init/main.c文件中,总共可以分为8个步骤。
(1) 函数开始,通过putstr(vivi_banner)打印出vivi的版本。Vivi_banner在/init/version.c
文件中定义
(2) 对开发板进行初始化(board_init函数),board_init是与开发板紧密相关的,这个函
数在/arch/s3c2410/smdk.c文件中。开发板初始化主要完成两个功能,时钟初始化(init_time())和通用IO口设置(set_gpios())。
void set_gpios(void) {
GPACON = vGPACON; GPBCON = vGPBCON; GPBUP = vGPBUP; GPCCON = vGPCCON; GPCUP = vGPCUP; GPDCON = vGPDCON; GPDUP = vGPDUP; GPECON = vGPECON; GPEUP = vGPEUP; GPFCON = vGPFCON; GPFUP = vGPFUP; GPGCON = vGPGCON; GPGUP = vGPGUP; GPHCON = vGPHCON; GPHUP = vGPHUP; EXTINT0 = vEXTINT0; EXTINT1 = vEXTINT1; EXTINT2 = vEXTINT2;
}
其中,GPIO口在smdk2410.h(\\vivi\\include\\platform\\目录下)文件中定义。 (3) 内存映射初始化和内存管理单元的初始化工作:
mem_map_init(); mmu_init();
这两个函数都在/arch/s3c2410/mmu.c文件中。 void mem_map_init(void) {
#ifdef CONFIG_S3C2410_NAND_BOOT mem_map_nand_boot(); #else
mem_map_nor(); #endif
cache_clean_invalidate();
11
tlb_invalidate(); }
如果配置vivi时使用了NAND作为启动设备,则执行mem_map_nand_boot(),否则执行mem_map_nor()。这里要注意的是,如果使用NOR启动,则必须先把vivi代码复制到RAM中。这个过程是由copy_vivi_to_ram()函数来完成的。代码如下: static void copy_vivi_to_ram(void)
{
putstr_hex(\
memcpy((void *)VIVI_RAM_BASE, (void *)VIVI_ROM_BASE, VIVI_RAM_SIZE); }
VIVI_RAM_BASE、VIVI_ROM_BASE、VIVI_RAM_SIZE这些值都可以在
smdk2410.h中查到,并且这些值必须根据自己开发板的RAM实际大小修改。这也是在移植vivi的过程中需要注意的一个地方。
mmu_init()函数中执行了arm920_setup函数。这段代码是用汇编语言实现的,针对arm920t核的处理器。
(4) 初始化堆栈,heap_init()。(定义在\\vivi\\lib\\heap.c文件中)
int heap_init(void) {
return mmalloc_init((unsigned char *)(HEAP_BASE), HEAP_SIZE);
}
(5) 初始化mtd设备,mtd_dev_init()。
int mtd_init(void) { int ret;
#ifdef CONFIG_MTD_CFI
ret = cfi_init();
#endif
#ifdef CONFIG_MTD_SMC
ret = smc_init();
#endif
#ifdef CONFIG_S3C2410_AMD_BOOT
ret = amd_init();
#endif
if (ret) { mymtd = NULL; return ret; } return 0;
}
这几个函数可以在/drivers/mtd/maps/s3c2410_flash.c里找到。 (6) 初始化私有数据,init_priv_data()。(定义在\\vivi\\lib\\priv_data\\rw.c文件中) (7) 初始化内置命令,init_builtin_cmds()。
12
通过add_command函数,加载vivi内置的几个命令。 (8) 启动boot_or_vivi()。
启动成功后,将通过vivi_shell()启动一个shell(如果配置了CONFIG_SERIAL_TERM),此时vivi的任务完成。
13
2.2 Vivi 的初始化
2.2.1 设置异常向量
@ Exception vector table (physical address = 0x00000000) ;异常向量表物理地址 @
@ 0x00: Reset b Reset
@ 0x04: Undefined instruction exception UndefEntryPoint: b HandleUndef
@ 0x08: Software interrupt exception SWIEntryPoint: b HandleSWI
@ 0x0c: Prefetch Abort (Instruction Fetch Memory Abort) PrefetchAbortEnteryPoint: b HandlePrefetchAbort
@ 0x10: Data Access Memory Abort DataAbortEntryPoint: b HandleDataAbort
@ 0x14: Not used NotUsedEntryPoint: b HandleNotUsed
@ 0x18: IRQ(Interrupt Request) exception IRQEntryPoint: b HandleIRQ
@ 0x1c: FIQ(Fast Interrupt Request) exception FIQEntryPoint: b HandleFIQ
下面是固定位置存放环境变量 @
@ VIVI magics
14
;复位;未定义的指令异常;软件中断异常;内存操作异常;数据异常;未使用;慢速中断处理;快速中断处理@
@ 0x20: magic number so we can verify that we only put .long 0 @ 0x24:
.long 0
@ 0x28: where this vivi was linked, so we can put it in memory in the right place
.long _start //_start用来指定链接后的起始装载地址装载到内存中的地址 @ 0x2C: this contains the platform, cpu and machine id .long ((1 << 24) | (6 << 16) | 193) @ 0x30: vivi capabilities .long 0
@ 0x34:
b SleepRamProc @
@ Start VIVI head @
2.2.2 初始化
Reset: //上电后cpu会从0x0地址读取指令执行,也就是b Reset
@ disable watch dog timer mov r1, #0x53000000 mov r2, #0x0 str r2, [r1]
# 121 \ @ disable all interrupts mov r1, #0x4A000000 mov r2, #0xffffffff
str r2, [r1, #0x08] //0x4A000008为中断屏蔽寄存器,将里面赋全1,表示屏蔽这32个中断源
ldr r2, =0x7ff
str r2, [r1, #0x1C] //0x4A00001C为中断子屏蔽寄存器,里面低11位也用来表示屏蔽掉这11个中断源
@ initialise system clocks mov r1, #0x4C000000
mvn r2, #0xff000000 //MVN指令可完成从另一个寄存器、被移位的寄存器、或将一个立即数加载到目的寄存器。与MOV指令不同之处是在传送之前按位被取反了,即把一个被取反的值传送到目的寄存器中。也就是r2=0x00ffffff
str r2, [r1, #0x00] //我们可以在程序开头启动MPLL,在设置MPLL的几个寄存器
15