求助: 关于S3C2440启动代码的问题。
本帖最后由 lilinly225 于 2013-10-21 18:00 编辑目前正在Windows下学习ARM, 用的开发工具是MDK, 没有用Linux下的交叉编译链工具。现在我在学习启动代码相关的内容。
环境为:裸机,没有操作系统和bootloader。
在我看到的的一个代码中有一段关于入口的代码感觉很诡异,不知道怎么理解。;定义arm汇编程序段,段名叫init段,为只读段
AREA RESET,CODE,READONLY
; ENTRY
EXPORT __ENTRY ;导出__ENTRY标号
__ENTRY
ResetEntry
;1)The code, which converts to Big-endian, should be in little endian code.
;2)The following little endian code will be compiled in Big-Endian mode.
;The code byte order should be changed as the memory bus width.
;3)The pseudo instruction,DCD can not be used here because the linker generates error.
;判断模式改变是否定义过(ASSERT是伪指令,:DEF:lable判断lable是否定义过了)
ASSERT :DEF:ENDIAN_CHANGE
[ ENDIAN_CHANGE ;如果ENDIAN_CHANGE为真,则执行以下
ASSERT:DEF:ENTRY_BUS_WIDTH ;判断是否定义了总线宽度
[ ENTRY_BUS_WIDTH=32 ;如果存储器是32位的总线宽度
b ChangeBigEndian ;DCD 0xea000007
]
[ ENTRY_BUS_WIDTH=16 ;如果存储器是16位的总线宽度
andeq r14,r7,r0,lsl #20 ;DCD 0x0007ea00
]
[ ENTRY_BUS_WIDTH=8 ;如果是存储器是8位总线宽度
streq r0, ;DCD 0x070000ea
]
| ;如果总线宽度没有定义的话,就直接跳转到复位中断
b ResetHandler ;程序执行的地跳跳转指令;正常模式下第一条语句就是这条语句
]
b HandlerUndef ;handler for Undefined mode
b HandlerSWI ;handler for SWI interrupt
b HandlerPabort ;handler for PAbort
b HandlerDabort ;handler for DAbort
b . ;reserved
b HandlerIRQ ;handler for IRQ interrupt
b HandlerFIQ ;handler for FIQ interrupt
;@0x20
b EnterPWDN ; Must be @0x20.;进入powerdown模式按照ARM开发者指南的说法: ENTRY 伪指令表示程序代码的最开始地方(相当于C语言中的main函数,表示程序的入口); 但是这段代码将ENTRY给注释掉
以一个__ENTRY的标识符替代了,并且将这个符号给导出了。
然后我搜索整个工程只在 mmu.c(MMU.c 文件为MMU功能的文件)发现了下面一句:extern char __ENTRY[]; //hzh很显然,这里只是声明了一个外部变量。
后面这个变量名符号 __ENTRY 被用来建立MMU转换页表的时候用了一下。//MMU_SetMTT(int vaddrStart,int vaddrEnd,int paddrStart,int attr)
//MMU_SetMTT(0x00000000,0x07f00000,0x00000000,RW_CNB);//bank0
//虚拟地址 虚拟地址结束 物理地址 映射区间属性
MMU_SetMTT(0x00000000,0x03f00000, (int)__ENTRY,RW_CB);//bank0, hzh然后就没有在任何地方用过了。
而MMU初始化是在 C语言代码的main函数中开始的,也就是说汇编代码的起始点运行的时候,MMU不可能运行,我的理解就是这个设置虚拟地址映射的
过程不可能与汇编代码的起始位置有关。int main(void)
{
rGPBDAT = 0x1e0; //关LED
rGPBCON=0x00015400; //设置GPB5~8为输出
rGPBUP =0x3ff; //禁止上拉
LCD_Init();
Brush_Background(0xffff);
Paint_Bmp(0,0,240,320,gImage_fhand);
MMU_Init();
KeyScan_Test() ;这里还有一个信息是:
上面那个汇编代码段定义是第一个代码段定义 ,这个代码段前面有两个数据段。
我想问的的是:
是不是汇编文件的第一个代码段默认的地址就是为0x00000000,是否是链接到代码段的0x00000000???
否则为什么不需要ENTRY 宏汇编 伪指令。
在ARM DUI 0204IC Real View 4.0版汇编器指南的7-69页中,明确的表示一个程序至少需要一个 ENTRY 入口的。
但是上面的代码没有也可以运行。
下面为整个汇编代码到 __ENTEY 之间的汇编代码。;=========================================
; NAME: 2440INIT.S
; DESC: C start up codes
; Configure memory, ISR ,stacks
; Initialize C-variables
; HISTORY:
; 2002.02.25:kwtark: ver 0.0
; 2002.03.20:purnnamu: Add some functions for testing STOP,Sleep mode
; 2003.03.14:DonGo: Modified for 2440.
;=========================================
GET option.inc
GET memcfg.inc
GET 2440addr.inc
BIT_SELFREFRESH EQU (1<<22)
;Pre-defined constants
USERMODE EQU 0x10 ;用户模式
FIQMODE EQU 0x11 ;快速中断模式
IRQMODE EQU 0x12 ;中断模式
SVCMODE EQU 0x13 ;
ABORTMODE EQU 0x17 ;
UNDEFMODE EQU 0x1b ; 未定义模式
MODEMASK EQU 0x1f ; 模式掩码
NOINT EQU 0xc0 ;
;定义GPB口的,寻址地址
GPBCON EQU 0x56000010
GPBDAT EQU 0x56000014
;各个异常模式的堆栈
UserStack EQU (_STACK_BASEADDRESS-0x3800) ;0x33ff4800 ~
SVCStack EQU (_STACK_BASEADDRESS-0x2800) ;0x33ff5800 ~
UndefStack EQU (_STACK_BASEADDRESS-0x2400) ;0x33ff5c00 ~
AbortStack EQU (_STACK_BASEADDRESS-0x2000) ;0x33ff6000 ~
IRQStack EQU (_STACK_BASEADDRESS-0x1000) ;0x33ff7000 ~
FIQStack EQU (_STACK_BASEADDRESS-0x0) ;0x33ff8000 ~堆栈的顶部
;------------------------------------------------------------------------------
UND_Stack_SizeEQU 0x00000400 ;
SVC_Stack_SizeEQU 0x00000400
ABT_Stack_SizeEQU 0x00000400
FIQ_Stack_SizeEQU 0x00000400
IRQ_Stack_SizeEQU 0x00000400
USR_Stack_SizeEQU 0x00004000
;堆栈的总大小为 0x5400 个字节 = 21504 字节
Stack_Size EQU (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
FIQ_Stack_Size + IRQ_Stack_Size + USR_Stack_Size)
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
Stack_Top EQU Stack_Mem + Stack_Size
Heap_Size EQU 0x00000000
AREA HEAP, NOINIT, READWRITE, ALIGN=3
Heap_Mem SPACE Heap_Size
;-------------------------------------------------------------------------------
;这一段是统一arm的工作状态和对应的软件编译方式(16位编译环境使用tasm.exe编译)。
;arm处理器的工作状态分为两种:32位,arm执行字对齐的arm指令集;16位,arm执行半字
;对齐的Thumb指令集。不同的工作状态,编译方式也不一样。所以下面的程序就是判断
;arm的工作方式来确定它的编译方式。
;Check if tasm.exe(armasm -16 ...@ADS 1.0) is used.
GBLL THUMBCODE ;定义THUMBCODE 这个变量GBLL 声明一个全局逻辑变量并初始化为{FALSE}
[ {CONFIG} = 16 ;"["表示"if","|"表示"else","]"表示"endif",对于CONFIG是在ADS编译中定义的内部变量。
THUMBCODE SETL{TRUE}
CODE32
|
THUMBCODE SETL{FALSE}
] ;如果ARM是在16位的工作状态的话,就使全局变量THUMBCODE设置为ture。
MACRO ;这个是宏定义的关键字
MOV_PC_LR ;作用是子程序返回
[ THUMBCODE ;当目标程序是Thumb时,就要使用BX跳转返回,并转换模式。
bx lr
|
; mov pc,lr ;目标程序是ARM指令集,直接把lr赋给pc就可以了。
bx lr
]
MEND ;宏定义的结束标志。
MACRO
MOVEQ_PC_LR ;这个是带“相等”条件的子程序返回。和上面说的类似。
[ THUMBCODE
bxeq lr
|
moveq pc,lr
]
MEND
;//////////////////////////////////////////////////////////////////////////
MACRO ;这个地方定义一个宏处理器,HANDLER 为宏标识符
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
sub sp,sp,#4
stmfd sp!,{r0}
ldr r0,=$HandleLabel ;load the address of HandleXXX to r0
ldr r0, ;load the contents(service routine start address) of HandleXXX
str r0, ;store the contents(ISR) of HandleXXX to stack
ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)
MEND
;编译器使用下列段来记录各段的起始地址和结束地址
;|Image$RO$Base|(ADS用这种写法), |Image$ER_ROM1$RO$Base|(MDK用这种写法); RO 段起始地址
;|Image$RO$Limit|(ADS用这种写法),|Image$ER_ROM1$RO$Limit| (MDK用这种写法);RO 段结束地址加1
;|Image$RW$Base| (ADS用这种写法),|Image$RW_RAM1$RW$Base|(MDK用这种写法);RW 段起始地址
;|Image$RW$Limit| (ADS用这种写法),|Image$RW_RAM1$ZI$Limit|(MDK用这种写法);RW 段结束地址加1
;|Image$ZI$Base|(ADS用这种写法),|Image$RW_RAM1$ZI$Base| (MDK用这种写法); ZI 段起始地址
;|Image$ZI$Limit| (ADS用这种写法),|Image$RW_RAM1$ZI$Limit|(MDK用这种写法); ZI 段结束地址加1
IMPORT|Image$ER_ROM1$RO$Base| ; Base of ROM code
IMPORT|Image$ER_ROM1$RO$Limit|; End of ROM code (=start of ROM data)
IMPORT|Image$RW_RAM1$RW$Base| ; Base of RAM to initialise
IMPORT|Image$RW_RAM1$ZI$Base| ; Base and limit of area
IMPORT|Image$RW_RAM1$ZI$Limit|; to zero initialise
;引入外部变量mmu的快速总线模式和同步总线模式两个变量
IMPORT MMU_SetAsyncBusMode
IMPORT MMU_SetFastBusMode ;hzh
;引入外部标号我们所熟知的main函数
IMPORT__main ; The main entry of mon program
PRESERVE8
;定义arm汇编程序段,段名叫init段,为只读段
AREA RESET,CODE,READONLY
; ENTRY
EXPORT __ENTRY ;导出__ENTRY标号
__ENTRY
ResetEntry
;1)The code, which converts to Big-endian, should be in little endian code.
;2)The following little endian code will be compiled in Big-Endian mode.
;The code byte order should be changed as the memory bus width.
;3)The pseudo instruction,DCD can not be used here because the linker generates error.
;判断模式改变是否定义过(ASSERT是伪指令,:DEF:lable判断lable是否定义过了)
ASSERT :DEF:ENDIAN_CHANGE
[ ENDIAN_CHANGE ;如果ENDIAN_CHANGE为真,则执行以下
ASSERT:DEF:ENTRY_BUS_WIDTH ;判断是否定义了总线宽度
[ ENTRY_BUS_WIDTH=32 ;如果存储器是32位的总线宽度
b ChangeBigEndian ;DCD 0xea000007
]
[ ENTRY_BUS_WIDTH=16 ;如果存储器是16位的总线宽度
andeq r14,r7,r0,lsl #20 ;DCD 0x0007ea00
]
[ ENTRY_BUS_WIDTH=8 ;如果是存储器是8位总线宽度
streq r0, ;DCD 0x070000ea
]
| ;如果总线宽度没有定义的话,就直接跳转到复位中断
b ResetHandler ;程序执行的地跳跳转指令;正常模式下第一条语句就是这条语句
]
b HandlerUndef ;handler for Undefined mode
b HandlerSWI ;handler for SWI interrupt
b HandlerPabort ;handler for PAbort
b HandlerDabort ;handler for DAbort
b . ;reserved
b HandlerIRQ ;handler for IRQ interrupt
b HandlerFIQ ;handler for FIQ interrupt
;@0x20
b EnterPWDN ; Must be @0x20.;进入powerdown模式
求各位大神解释一下。
另外各位大神,有没有关于MMU和堆栈初始化的相关比较系统的资料,关于裸机设置MMU和堆栈设置的系统的解释的资料, 我搜到的资料都是
一些比较零散的资料,理解起来很难。 本帖最后由 crifan 于 2013-10-21 23:54 编辑
你问题太多了。
其中很多问题,下面的教程,应该可以帮你解答了:
Uboot中start.S源码的指令级的详尽解析
看完后,还有疑问,再问,我再抽空看看能否帮你解答。 回复 2# crifan
谢谢先。
页:
[1]