ARM移植之BootLoader(4)
vivi Bootloader的第二阶段又分成了八个小阶段,在main函数中分别调用这几个小阶段的相关函数:
int main(int argc, char *argv[]) { int ret; /* * Step 1: */ putstr("rn"); putstr(vivi_banner); reset_handler(); /* * Step 2: */ ret = board_init(); if (ret) { putstr("Failed a board_init() procedurern"); error(); } /* * Step 3: */ mem_map_init(); mmu_init(); putstr("Succeed memory mapping.rn"); /* * Now, vivi is running on the ram. MMU is enabled. */ /* * Step 4: */ /* initialize the heap area*/ ret = heap_init(); if (ret) { putstr("Failed initailizing heap regionrn"); error(); } /* Step 5: */ ret = mtd_dev_init(); /* Step 6: */ init_priv_data(); /* Step 7: */ misc(); init_builtin_cmds(); /* Step 8: */ boot_or_vivi(); return 0; } |
STEP1的putstr(vivi_banner)语句在串口输出一段字符说明vivi的版本、作者等信息,vivi_banner定义为:
const char *vivi_banner = "VIVI version " VIVI_RELEASE " (" VIVI_COMPILE_BY "@" VIVI_COMPILE_HOST ") (" VIVI_COMPILER ") " UTS_VERSION "rn"; |
reset_handler进行相应的复位处理:
void reset_handler(void) { int pressed; pressed = is_pressed_pw_btn(); if (pressed == PWBT_PRESS_LEVEL) { DPRINTK("HARD RESETrn"); hard_reset_handle(); } else { DPRINTK("SOFT RESETrn"); soft_reset_handle(); } } |
hard_reset_handle会clear内存,而软件复位处理则什么都不做:
static void hard_reset_handle(void) { clear_mem((unsigned long)USER_RAM_BASE, (unsigned long)USER_RAM_SIZE); } |
STEP2进行板初始化,设置时间和可编程I/O口:
int board_init(void) { init_time(); set_gpios(); return 0; } |
STEP3进行内存映射及MMU初始化:
void mem_map_init(void) { #ifdef CONFIG_S3C2410_NAND_BOOT mem_map_nand_boot(); #else mem_map_nor(); #endif cache_clean_invalidate(); tlb_invalidate(); } |
S3C2410A的MMU初始化只需要调用通用的arm920 MMU初始化函数:
static inline void arm920_setup(void) { unsigned long ttb = MMU_TABLE_BASE; __asm__( /* Invalidate caches */ "mov r0, #0n" "mcr p15, 0, r0, c7, c7, 0n" /* invalidate I,D caches on v4 */ "mcr p15, 0, r0, c7, c10, 4n" /* drain write buffer on v4 */ "mcr p15, 0, r0, c8, c7, 0n" /* invalidate I,D TLBs on v4 */ /* Load page table pointer */ "mov r4, %0n" "mcr p15, 0, r4, c2, c0, 0n" /* load page table pointer */ /* Write domain id (cp15_r3) */ "mvn r0, #0n" /* Domains 0, 1 = client */ "mcr p15, 0, r0, c3, c0, 0n" /* load domain access register */ /* Set control register v4 */ "mrc p15, 0, r0, c1, c0, 0n" /* get control register v4 */ /* Clear out unwanted bits (then put them in if we need them) */ /* .RVI ..RS B... .CAM */ "bic r0, r0, #0x3000n" /* ..11 .... .... .... */ "bic r0, r0, #0x0300n" /* .... ..11 .... .... */ "bic r0, r0, #0x0087n" /* .... .... 1... .111 */ /* Turn on what we want */ /* Fault checking enabled */ "orr r0, r0, #0x0002n" /* .... .... .... ..1. */ #ifdef CONFIG_CPU_D_CACHE_ON "orr r0, r0, #0x0004n" /* .... .... .... .1.. */ #endif #ifdef CONFIG_CPU_I_CACHE_ON "orr r0, r0, #0x1000n" /* ...1 .... .... .... */ #endif /* MMU enabled */ "orr r0, r0, #0x0001n" /* .... .... .... ...1 */ "mcr p15, 0, r0, c1, c0, 0n" /* write control register */ : /* no outputs */ : "r" (ttb) ); } |
STEP4设置堆栈;STEP5进行mtd设备的初始化,记录MTD分区信息;STEP6设置私有数据;STEP7初始化内建命令。
STEP8启动一个SHELL,等待用户输出命令并进行相应处理。在SHELL退出的情况下,启动操作系统:
#define DEFAULT_BOOT_DELAY 0x30000000 void boot_or_vivi(void) { char c; int ret; ulong boot_delay; boot_delay = get_param_value("boot_delay", &ret); if (ret) boot_delay = DEFAULT_BOOT_DELAY; /* If a value of boot_delay is zero, * unconditionally call vivi shell */ if (boot_delay == 0) vivi_shell(); /* * wait for a keystroke (or a button press if you want.) */ printk("Press Return to start the LINUX now, any other key for vivin"); c = awaitkey(boot_delay, NULL); if (((c != r) && (c != n) && (c != ))) { printk("type "help" for help.n"); vivi_shell(); } run_autoboot(); return; } |
SHELL中读取用户从串口输出的命令字符串,执行该命令:
void vivi_shell(void) { #ifdef CONFIG_SERIAL_TERM serial_term(); #else #error there is no terminal. #endif } void serial_term(void) { char cmd_buf[MAX_CMDBUF_SIZE]; for (;;) { printk("%s> ", prompt); getcmd(cmd_buf, MAX_CMDBUF_SIZE); /* execute a user command */ if (cmd_buf[0]) exec_string(cmd_buf); } } |
5.电路板调试
在电路板的调试过程中,我们首先要在ADT新建的工程中添加第一阶段的汇编代码head.S文件,修改Link脚本,将代码和数据映射到S3C2410A自带的0x40000000开始的4KB内存空间内:
SECTIONS { . = 0x40000000; .text : { *(.text) } Image_RO_Limit = .; Image_RW_Base = .; .data : { *(.data) } .rodata : { *(.rodata) } Image_ZI_Base = .; .bss : { *(.bss) } Image_ZI_Limit = .; __bss_start__ = .; __bss_end__ = .; __EH_FRAME_BEGIN__ = .; __EH_FRAME_END__ = .; PROVIDE (__stack = .); end = .; _end = .; .debug_info 0 : { *(.debug_info) } .debug_line 0 : { *(.debug_line) } .debug_abbrev 0 : { *(.debug_abbrev)} .debug_frame 0 : { *(.debug_frame) } } |
借助万用表、示波器等仪器仪表,调通SDRAM,并将vivi中自带的串口、NAND FLASH驱动添加到工程中,调试通过板上的串口和FLASH。如果板电路的原理与三星公司DEMO板有差距,则vivi中硬件的操作要进行相应的修改。 全部调试通过后,修改vivi源代码,重新编译vivi,将其烧录入NAND FLASH就可以在复位后启动这个Bootloader了。
调试板上的新增硬件时,宜在ADT中添加相应的代码,在不加载操作系统的情况下,单纯地操作这些硬件。如果电路板设计有误,要进行飞线和割线等处理。
6.小结
本章讲解了ARM汇编、Bootloader的功能,Bootloader的调试环境及ARM电路板的调试方法。
评论