2012年(15)
分类:
2012-05-05 09:35:59
首先应该注意的就是为什么使用head.S而不是用head.s?有了GNU AS和GNU Gcc的基础,不难理解主要原因就是为了使用C预处理器的宏替换和文件包含功能(GNU AS的预处理无法完成此项功能)。这样的好处就是可以使用C预处理器的功能来提高ARM汇编的程序设计环境,更加方便。但是因为ARM汇编和C在宏替换的细节上有所不同,为了区分,引入了__ASSEMBLY__这个变量,这是通过Makefile中AFLAGS来引入的,具体如下:
AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS) |
在后面的头文件中,会看到很多#ifdef __ASSEMBLY__等的操作,就是用来区分这个细节的。在编译汇编文件时,加入AFLAGS选项,所以__ASSEMBLY__传入,也就是定义了__ASSEMBLY__;在编译C文件时,没有用AFLAGS选项,自然也就没有定义__ASSEMBLY__。
Head.S:
#include "config.h"
#include "linkage.h"
#include "machine.h"
include/config.h只是包含一个autoconf.h。如果写一个专用的bootloader,不采用vivi的配置机制,那么配置部分就没有这么复杂了,只需要在include文件夹中包含一个配置头文件即可。现在bootloader的设计有两种趋势,一种是针对特定应用,有特殊要求,也就是“专用”。那么设计时,不需要过多的配置,只需要简单的完成引导内核的功能就可以了。二是普通应用,一般是对基本“通用”的bootloader,比如uboot等,然后根据相应的模版进行移植。这就需要了解uboot等的架构,可以进行定制和功能的增加。uboot完成的不仅仅是一个bootloader的功能,还可以提供调试等功能,所以其角色还包含驻留程序这个功能,也就是uboot真正的角色是monitor。当然,可以不加区分,统称为bootloader。
include/linkage.h就是实现了ENTRY宏的封装。其实这个头文件也仅仅为head.S提供了服务
include/machine.h则是利用条件编译来选择适合自己开发板的头文件,本开发板的头文件是【include/platform/smdk2410.h】,主要是一些寄存器的初始值(以v开头)和一些相关的地址等等的定义。一般开发板不同,都是修改此文件相应的部分。
@ Start of executable code
ENTRY(_start) @程序入口点
ENTRY(ResetEntryPoint)
下面是装载中断向量表,
开始对中断向量表很疑惑。现在的理解比较清晰了,在硬件实现上,会支持中断机制,这个可以参考微机接口原理部分详细理解。现在的中断机制处理的比较智能,对每一种中断会固定一个中断向量,比如说,发生IRQ中断,中断向量地址为0x00000018(当然,这还与ARM9TDMI core有关,其中一个引脚可以把中断向量表配置为高端启动,或者低端启动。你可以通过CP15的register 1的bit 13的V bit来设置,可以查看Datasheet TABLE 2-10来解决。),那么PC要执行的指令就是0x00000018。如果在这个地址放上一个跳转指令(只能使用b或者ldr),那么就可以跳到实际功能代码的实现区了。
ARM
规定,在起始必须有8条跳转指令,你可以用b,也可以用ldr pc,文件名。这样的8条规则的标志被arm定义为bootloader的识别标志,检测到这样的标志后,就可以从该位置启动。这样的做法是因为开始的时候不一定有bootloader,必须有一种识别机制,如果识别到bootloader,那么就从bootloader启动。
@
@ Exception vector table (physical address = 0x00000000) ;异常向量表物理地址
@
@ 0x00: Reset ;复位
b Reset
@ 0x04: Undefined instruction exception ;未定义的指令异常
UndefEntryPoint:
b HandleUndef
@ 0x08: Software interrupt exception ;软件中断异常
SWIEntryPoint:
b HandleSWI
@ 0x
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
@ 0x
FIQEntryPoint:
b HandleFIQ
下面是固定位置存放环境变量
对vivi的这些magic number,虽然设计在这里,不过大部分还是没有使用的。其中0x20和0x24没有使用,在0x2C处,倒是设计了一个magic number,组成的格式如下:bit[31:24]为platform,bit[23:16]为cpu type,bit[15:0]为machine id。关于ARCHITECTURE_MAGIC的定义,在【include/platform/smdk2410.h】
@
@ VIVI magics
@
@ 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
@ 0x
.long ARCHITECTURE_MAGIC
@ 0x30: vivi capabilities
.long 0
#ifdef CONFIG_PM ;vivi没有使用电源管理
@ 0x34:
b SleepRamProc
#endif
#ifdef CONFIG_TEST
@ 0x38:
b hmi
#endif
@
@ Start VIVI head
@
Reset:
@ disable watch dog timer ;禁止看门狗计时器
mov r1, #0x53000000 ;WTCON寄存器地址是0x53000000,清0
mov r2, #0x0
str r2, [r1]
#ifdef CONFIG_S
/**** 在/vivi/include/autoconf.h中#undef CONFIG_S
mov r1, #0x56000000 ;GPACON寄存器地址是
0x56000000
mov r2, #0x00000005
str r2, [r1, #0x70] ;配置GPHCON寄存器
mov r2, #0x00000001
str r2, [r1, #0x78] ;配置GPHUP寄存器
mov r2, #0x00000001
str r2, [r1, #0x74] ;配置GPHDAT寄存器
#endif
@ disable all interrupts ;禁止全部中断
mov r1, #INT_CTL_BASE
mov r2, #0xffffffff
str r2, [r1, #oINTMSK] ;掩码关闭所有中断
ldr r2, =0x7ff
str r2, [r1, #oINTSUBMSK]
@ initialise system clocks ;初始化系统时钟
mov r1, #CLK_CTL_BASE
mvn r2, #0xff000000
str r2, [r1, #oLOCKTIME]
@ldr r2, mpll_50mhz
@str r2, [r1, #oMPLLCON]
#ifndef CONFIG_S
/**** 在/vivi/include/autoconf.h中#undef CONFIG_S
@ 1:2:4
mov r1, #CLK_CTL_BASE
mov r2, #0x3
str r2, [r1, #oCLKDIVN]
mrc p15, 0, r1, c1, c0, 0 @ read ctrl register
orr r1, r1, #0xc0000000 @ Asynchronous
mcr p15, 0, r1, c1, c0, 0 @ write ctrl register
@ now, CPU clock is 200 Mhz ;CPU的频率是200MHz
mov r1, #CLK_CTL_BASE
ldr r2, mpll_200mhz
str r2, [r1, #oMPLLCON]
#else
@ 1:2:2
mov r1, #CLK_CTL_BASE
ldr r2, clock_clkdivn
str r2, [r1, #oCLKDIVN]
mrc p15, 0, r1, c1, c0, 0 @ read ctrl register
orr r1, r1, #0xc0000000 @ Asynchronous
mcr p15, 0, r1, c1, c0, 0 @ write ctrl register
@ now, CPU clock is 100 Mhz ;CPU的频率是100MHz
mov r1, #CLK_CTL_BASE
ldr r2, mpll_100mhz
str r2, [r1, #oMPLLCON]
#endif
bl memsetup ;跳转到memsetup函数
/*****************************
Memsetup函数的实现:
ENTRY(memsetup)
@ initialise the static memory
@ set memory control registers ;设置内存控制寄存器的初值
mov r1, #MEM_CTL_BASE
adrl r2, mem_cfg_val
/*******************
@
@ Data Area
@
@ Memory configuration values
.align 4
mem_cfg_val: ;定义好的13*4=52个字节初值
.long vBWSCON ;在/vivi/include/platform/smdk2410.h中赋值
/****** SDRAM从32位变成16位,需要修改vBWSCON的值 ******/
.long vBANKCON0
.long vBANKCON1
.long vBANKCON2
.long vBANKCON3
/********** 网卡控制器vBANKCON3的值可能需要修改 **************/
.long vBANKCON4
.long vBANKCON5
.long vBANKCON6
/****** SDRAM从32位变成16位,可能需要修改vBANKCON6的值 ******/
.long vBANKCON7
.long vREFRESH
.long vBANKSIZE
/****** SDRAM从64MB变成32MB,需要修改vBANKSIZE的值 ******/
.long vMRSRB6
.long vMRSRB7
********************/
add r3, r1, #52
1: ldr r4, [r2], #4
str r4, [r1], #4
cmp r1, r3
bne 1b ;循环操作,直到13个寄存器赋值完成
mov pc, lr
*******************************/
#ifdef CONFIG_PM ;vivi考虑不需要使用电源管理
@ Check if this is a wake-up from sleep
ldr r1, PMST_ADDR
ldr r0, [r1]
tst r0, #(PMST_SMR)
bne WakeupStart ;查看状态,判断是否需要跳转到WakeupStart
#endif
#ifdef CONFIG_S
@ All LED on ;点亮开发板上的LED
mov r1, #GPIO_CTL_BASE
add r1, r1, #oGPIO_F ;LED使用GPIOF组的管脚
ldr r2,=0x55aa ;使能EINT0,EINT1,EINT2,EINT3,;另四个管脚配置成输出,屏蔽EINT4,5,6,7
str r2, [r1, #oGPIO_CON]
mov r2, #0xff
str r2, [r1, #oGPIO_UP] ;disable the pull-up function
mov r2, #0x00
str r2, [r1, #oGPIO_DAT]
#endif
#if 0
@ SVC
mrs r0, cpsr
bic r0, r0, #0xdf
orr r1, r0, #0xd3
msr cpsr_all, r1
#endif
@ set GPIO for UART ;设置串口
mov r1, #GPIO_CTL_BASE
add r1, r1, #oGPIO_H ;设置GPIO_H组管脚为串口
ldr r2, gpio_con_uart
str r2, [r1, #oGPIO_CON]
ldr r2, gpio_up_uart
str r2, [r1, #oGPIO_UP]
/*************************
@ inital values for GPIO
gpio_con_uart:
.long vGPHCON ;vGPHCON在/vivi/include/platform/smdk2410.h中赋值
;#define vGPHCON 0x0016faaa
;GPIO_H配置为nCTS0,nRTS0, RXD0,TXD0, RXD1,
;TXD1,nCTS1,nRTS1,
/**** 三个串口都使能,可能需要修改#define vGPHCON 0x0016aaaa ****/
gpio_up_uart:
.long Vgphup ;同上#define vGPHUP 0x000007ff
;The pull-up function is disabled.
************************/
bl InitUART ;跳转到InitUART串口初始化函数
/****************************************************
@ Initialize UART
@
@ r0 = number of UART port
InitUART:
ldr r1, SerBase
/*******************
.align 4 ;缺省情况下在vivi中只初始化了UART0
SerBase:
#if defined(CONFIG_SERIAL_UART0)
.long UART0_CTL_BASE ;基地址在/vivi/include/s
#elif defined(CONFIG_SERIAL_UART1)
.long UART1_CTL_BASE
#elif defined(CONFIG_SERIAL_UART2)
.long UART2_CTL_BASE
#else
#error not defined base address of serial
#endif
********************/
mov r2, #0x0
str r2, [r1, #oUFCON]
str r2, [r1, #oUMCON]
mov r2, #0x3
str r2, [r1, #oULCON]
ldr r2, =0x245
str r2, [r1, #oUCON]
#define UART_BRD ((50000000 / (UART_BAUD_RATE * 16)) - 1)
mov r2, #UART_BRD
str r2, [r1, #oUBRDIV]
mov r3, #100
mov r2, #0x0
1: sub r3, r3, #0x1
tst r2, r3
bne 1b
#if 0
mov r2, #'U'
str r2, [r1, #oUTXHL]
1: ldr r3, [r1, #oUTRSTAT]
and r3, r3, #UTRSTAT_TX_EMPTY
tst r3, #UTRSTAT_TX_EMPTY
bne 1b
mov r2, #'0'
str r2, [r1, #oUTXHL]
1: ldr r3, [r1, #oUTRSTAT]
and r3, r3, #UTRSTAT_TX_EMPTY
tst r3, #UTRSTAT_TX_EMPTY
bne 1b
#endif
mov pc, lr
****************************************************/
#ifdef CONFIG_DEBUG_LL ;打印调试信息,缺省未定义
@ Print current Program Counter
ldr r1, SerBase
mov r0, #'\r'
bl PrintChar
mov r0, #'\n'
bl PrintChar
mov r0, #'@'
bl PrintChar
mov r0, pc
bl PrintHexWord
#endif
#ifdef CONFIG_BOOTUP_MEMTEST
@ simple memory test to find some DRAM flaults.
bl memtest
#endif
#ifdef CONFIG_S
bl copy_myself ;跳转到copy_myself函数
/**********************************************
@
@ copy_myself: copy vivi to ram
@
copy_myself:
mov r10, lr
@ reset NAND
mov r1, #NAND_CTL_BASE
ldr r2, =0xf830 @ initial value
str r2, [r1, #oNFCONF]
ldr r2, [r1, #oNFCONF]
bic r2, r2, #0x800 @ enable chip
str r2, [r1, #oNFCONF]
mov r2, #0xff @ RESET command
strb r2, [r1, #oNFCMD]
mov r3, #0 @ wait
1: add r3, r3, #0x1
cmp r3, #0xa
blt 1b
2: ldr r2, [r1, #oNFSTAT] @ wait ready
tst r2, #0x1
beq 2b
ldr r2, [r1, #oNFCONF]
orr r2, r2, #0x800 @ disable chip
str r2, [r1, #oNFCONF]
@ get read to call C functions (for nand_read())
ldr sp, DW_STACK_START @ setup stack pointer
mov fp, #0 @ no previous frame, so fp=0
@ copy vivi to RAM
ldr r0, =VIVI_RAM_BASE
/*********在/vivi/linux/platform/smdk2410.h中定义
#define VIVI_RAM_BASE (DRAM_BASE + DRAM_SIZE - VIVI_RAM_SIZE)
***************************************/
mov r1, #0x0
mov r2, #0x20000 ;0x20000-〉128k字节
bl nand_read_ll ;nand_read_ll在/vivi/arch/s
;r0,r1,r2分别为函数的三个参数
;从NANDFlash的0地址拷贝128k到SDRAM指定处
tst r0, #0x0
beq ok_nand_read
#ifdef CONFIG_DEBUG_LL
bad_nand_read:
ldr r0, STR_FAIL
ldr r1, SerBase
bl PrintWord
1: b 1b @ infinite loop
#endif
ok_nand_read:
#ifdef CONFIG_DEBUG_LL
ldr r0, STR_OK
ldr r1, SerBase
bl PrintWord
#endif
@ verify
mov r0, #0
ldr r1, =0x
mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
go_next:
ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne notmatch
subs r2, r2, #4
beq done_nand_read
bne go_next
notmatch:
#ifdef CONFIG_DEBUG_LL
sub r0, r0, #4
ldr r1, SerBase
bl PrintHexWord
ldr r0, STR_FAIL
ldr r1, SerBase
bl PrintWord
#endif
1: b 1b
done_nand_read:
#ifdef CONFIG_DEBUG_LL
ldr r0, STR_OK
ldr r1, SerBase
bl PrintWord
#endif
mov pc, r10 ;vivi拷贝到SDRAM完成,函数返回
*********************************/
@ jump to ram
ldr r1, =on_the_ram
add pc, r1, #0
nop
nop
1: b 1b @ infinite loop
on_the_ram:
#endif
#ifdef CONFIG_DEBUG_LL
ldr r1, SerBase
ldr r0, STR_STACK
bl PrintWord
ldr r0, DW_STACK_START
bl PrintHexWord
#endif
@ get read to call C functions
ldr sp, DW_STACK_START @ setup stack pointer
mov fp, #0 @ no previous frame, so fp=0
mov a2, #0 @ set argv to NULL
bl main @ call main
mov pc, #FLASH_BASE @ otherwise, reboot
@
@ End VIVI head
@