main.c粗粗看过的,但是并没有象head.S那样分析每一条语句,根据自己的需要真正移植的还是有很多疑惑,主要是每个过程的具体细节怎么实现,比如参数的传递,只知道有有个STRUCT或者TAG可以用,但是具体里面每一个参数,怎么根据自己的系统来设置,放在哪里,怎么传递过去,模模糊糊。现在还是打算仔细看一遍。
按照顺序:
step 1:
reset_handler();主要的思路是以硬件或者软件的方式来控制复位。硬件的判断是检测按键,主要的动作是清零用户RAM;如果没有按键就采用软件复位的方式进行,当然在VIVI中这并没有进行定义,是一个空函数。所以看一下硬件复位的操作:
pressed = is_pressed_pw_btn();
static int is_pressed_pw_btn(void)
{
return read_bt_status();
}
static int read_bt_status(void)
{
ulong status;
//status = ((GPLR & (1 << GPIO_PWBT)) >> GPIO_PWBT);
status = ((PWBT_REG & (1 << PWBT_GPIO_NUM)) >> PWBT_GPIO_NUM);
if (status)
return HIGH;
else
return LOW;
}
应该是通过PWBT_REG寄存器中的位与PWBT_GPIO_NUM相应的按键位相与来判断按键是否按下,但是实际上VIVI中并没有具体定义这个寄存器PWBT_REG和PWBT_GPIO_NUM,所以在实际编译过程中并不能编译这个功能相应模块,否则编译将不能通过,但是可以按照自己的实际情况来设置。
如果实现硬件复位则:
hard_reset_handle(void)
{
#if 0
clear_mem((unsigned long)(DRAM_BASE + VIVI_RAM_ABS_POS), /
(unsigned long)(DRAM_SIZE - VIVI_RAM_ABS_POS));
#endif
clear_mem((unsigned long)USER_RAM_BASE, (unsigned long)USER_RAM_SIZE);
}
主要做的是清0 memory:
void clear_mem(unsigned long base, unsigned long len)
{
__asm__ volatile (
"mov r0, %0/n" /* store base address */
"mov r1, %1/n" /* store length */
"mov r2, #0/n"
"mov r3, r2/n"
"mov r4, r2/n"
"mov r5, r2/n"
"mov r6, r2/n"
"mov r7, r2/n"
"mov r8, r2/n"
"mov r9, r2/n"
" 1: stmia r0!, {r2-r9}/n" /* clear 32 (4 bytes * 8) bytes */
"subs r1, r1, #(8 * 4)/n"
"bne 1b/n"
: /* no outputs */
: "r" (base), "r" (len)
: "r0", "r1", "r2", "r3", "r4",
"r5", "r6", "r7", "r8", "r9"
);
}
step 2:
board_init()做两件事情,一件是初始化PWM,init_time();另一件是设置GPIO,set_gpios()。PWM的使用将在另外一篇总结中进行分析,实际上在VIVI阶段也并不需要用到PWM;GPIO设置是比较重要的,根据手头的板子的电路图进行,具体的操作就没有什么可写的了。
step 3:
mem_map_init():
void mem_map_init(void)
{
#ifdef CONFIG_S3C2410_NAND_BOOT
mem_map_nand_boot();
#else
mem_map_nor();
#endif
cache_clean_invalidate();
tlb_invalidate();
}
主要是建立映射表,但是针对NAND和非NAND启动有点区别:NAND启动,因为前面已经搬移了代码,修改PC到了SDRAM中进行操作;而比如NOR则需要先把VIVI搬移到SDRAM,然后再建立4G线形映射表,同时将SDRAM映射到4G当中。禁用CACHE和TLB表,这些基本都在P15协处理器寄存器中进行。
mmu_init();
void mmu_init(void)
{
arm920_setup();
}
static inline void arm920_setup(void)
{
unsigned long ttb = MMU_TABLE_BASE;
__asm__(
/* Invalidate caches */
"mov r0, #0/n"
"mcr p15, 0, r0, c7, c7, 0/n" /* invalidate I,D caches on v4 */
"mcr p15, 0, r0, c7, c10, 4/n" /* drain write buffer on v4 */
"mcr p15, 0, r0, c8, c7, 0/n" /* invalidate I,D TLBs on v4 */
/* Load page table pointer */
"mov r4, %0/n"
"mcr p15, 0, r4, c2, c0, 0/n" /* load page table pointer */
/* Write domain id (cp15_r3) */
"mvn r0, #0/n" /* Domains 0, 1 = client */
"mcr p15, 0, r0, c3, c0, 0/n" /* load domain access register */
/* Set control register v4 */
"mrc p15, 0, r0, c1, c0, 0/n" /* get control register v4 */
/* Clear out 'unwanted' bits (then put them in if we need them) */
/* .RVI ..RS B... .CAM */
"bic r0, r0, #0x3000/n" /* ..11 .... .... .... */
"bic r0, r0, #0x0300/n" /* .... ..11 .... .... */
"bic r0, r0, #0x0087/n" /* .... .... 1... .111 */
/* Turn on what we want */
/* Fault checking enabled */
"orr r0, r0, #0x0002/n" /* .... .... .... ..1. */
#ifdef CONFIG_CPU_D_CACHE_ON
"orr r0, r0, #0x0004/n" /* .... .... .... .1.. */
#endif
#ifdef CONFIG_CPU_I_CACHE_ON
"orr r0, r0, #0x1000/n" /* ...1 .... .... .... */
#endif
/* MMU enabled */
"orr r0, r0, #0x0001/n" /* .... .... .... ...1 */
"mcr p15, 0, r0, c1, c0, 0/n" /* write control register */
: /* no outputs */
: "r" (ttb) );
}
注释得非常明确了,在前面映射完成的基础上根据需要选择是否打开CACHE,然后使能MMU。MMU使能后根据映射表可以将访问FLASH的PC值映射到SDRAM中,而SDRAM在映射表中仍然映射自己的地址,这样在搬移后无论是否跳转MMU的打开都不会造成映射错误。问题是为什么要这样做,显然这里使用MMU功能只是为了加快VIVI的执行速度,使得在SDARM中进行,但是为什么不象NAND启动后在4K STEPPING STONE中一样,直接在前面就搬移,然后跳转到SDRAM中执行,这样就没必要用到MMU了。而且如果在比较后面的地方才进行搬移的话,那么由于前面的代码包括已经用了C代码万一涉及到绝对跳转,所以在编译连接的时候必须使得连接地址为0,否则在代码也没有搬移的时候,绝对跳转将出错,除非在搬移之前保证没有绝对跳转的指令,而这在已经使用了C语言的的时候就需要特别指定产生代码的类型了,如GCC的FIPC选项,而怎么保证实现FIPC的编译器机理也是比较复杂的,当然编译器确实是可以保证的。这样想来我认为还是在比较靠前的地方去搬移代码,然后跳转到RAM执行是比较乐观的,所以觉得VIVI这部分这样处理不是特别理解他的用意。
另外还有一个问题是VIVI的BSS段的处理似乎不是特别明显,到目前为止我只看到在MEM_test部分进行了RAM清0,由于在设计的时候他是加了宏来选择编译的,就不明白是不是为了BSS段而设计这段清0操作的?为什么要定义这么个宏,还是在其他哪里有BSS段的清0,我是真的没有找到。不管怎么样有几个工作是要明确的,在运行C函数之前,一定要初始化好他的环境,包括DATA,BSS,堆栈。对于DATA,首先在编译连接的时候就要明确他的连接地址是哪里,必须是RAM中的某个位置,因为他们是些可以读写的变量,在FLASH中是无法实现的,然后在C运行前要保证他被搬移到相应RAM位置,搬移也许是跟整个BOOTLOADER一起,也许不是,但是要保证他被搬移过去;BSS段在实际编译出来的文件中是没有的,但是我们必须要知道,他在编译出来的文件中没有是因为,这些全是0的东西没必要使得编译出来的文件增加尺寸而占更多FLASH的空间,但是他们是不可少的部分(当然是说编译出来有BSS段的程序),通常C程序总会有未初始化的全局,所以虽然没有搬移过程,但是在程序运行的前要把这部分对应的RAM清0;堆视需要开辟,栈一般是要开辟的,否则局部变量将没有地方来实现,这里以前也有过一段时间把硬件的堆栈和这里的堆栈弄混过,C程序的堆栈完全是一个用来存放变量的空间,比如定义一个局部变量INT A,首先它是要用到栈的,但是编译器在编译这个句子的时候是一句指示在栈中开辟这么一个变量的句子,堆也差不多,比如MALLOC,而硬件的堆栈相当于一个装载环境内容的容器,一般用在异常的时候,状态上下文;只是在先进后出的顺序上两个栈相似,当然这些是我的理解。