ARM9 (2440A) 从启动代码到应用程序(Main) 1
来源:互联网 发布:java cas是什么 编辑:程序博客网 时间:2024/06/05 17:52
ARM9(2440A) 从启动代码到应用程序
说一下从启动代码到Main函数的过程,以及到了Main还需要设置些什么,才算是一个完整的应用程序。
启动代码
我们知道,uboot的第一阶段的功能是:(1)定义入口;(2)设置异常向量(exception vector);(3)设置CPU的速度、时钟频率及中断控制寄存器;(4)初始化内存控制器 ;(5)将rom中的程序复制到ram中;(6)初始化堆栈;(7)转到ram中执行;
其实我们要实现的启动代码功能也就是实现这些功能,最后跳转到我们自己的应用程序入口。只是这里的启动代码,我们不用uboot来实现,可以说是根据自己的需求来实现,毕竟uboot代码量不小。
keil下创建工程时自动添加的启动文件实现的功能不多,网上大多人采用的是ADS下面的启动文件2440init.s,这里有关于这个文件的详细注释:2440init详细注释.s(http://wenku.baidu.com/view/de704e4f767f5acfa1c7cd6d.html?re=view)
这个文件实现的功能大致有:
建立异常向量表;
关闭看门狗,设置时钟频率;
初始化各个模式下的堆栈;
拷贝RO代码拷贝到特定地址处;
初始化RW、 ZI数据;
跳转到Main函数;
我们知道,当系统重启时,CPU是从0地址处运行,0x00到0x1c地址处存放的内容为:
地址
指令
异常名称
进入时处理器模式
0x0000,0000
b ResetHandler
复位异常
管理模式
0x0000,0004
b HandlerUndef
未定义指令异常
未定义模式
0x0000,0008
b HandlerSWI
软中断异常
管理模式
0x0000,000c
b HandlerPabort
指令预取异常
中止模式
0x0000,0010
b HandlerDabort
数据中止异常
中止模式
0x0000,0014
b .
保留
----
0x0000,0018
b HandlerIRQ
IRQ中断异常
中断模式
0x0000,001c
b HandlerFIQ
FIQ中断异常
快速中断模式
可以看到,0x00地址处存放的是ResetHandler,系统重启时,CPU从0地址处取指令,然后跳转到ResetHandler处执行,此处就开始了关闭看门狗,初始化时钟,初始化SDRAM,初始化堆栈,拷贝代码到SDRAM中。当然还有最重要的就是建立异常中断向量表。下面以IRQ中断为例简要叙述中断过程,结合的文件是前面提到的2440init.s。从网上看了些这方面的资料,觉得这个网址:庖丁解牛 ARM9 中断处理过程http://xinyingdachl.blog.163.com/blog/static/2327600452014229104931186/解释的比较周全,可以深入的看看。下面的理解也来源于这片文章。还有一篇文章不得不推荐,《Mini2440启动代码的编写(第三版)》,这个对启动代码的一些分析很到位。
当发生IRQ中断时,CPU将会从0x18地址处取指令,从上面的表中可知,0x18地址处存的是HandlerIRQ。HandlerIRQ只是一个标号,后有宏定义
HandlerIRQ HANDLER HandleIRQ。HandleIRQ可以认为它是一个函数指针,这个宏定义之后,程序就跳转到了HandleIRQ所指向的函数地址。而HandleIRQ本身所在的地址在哪呢?在_ISR_STARTADDRESS后面的偏移处。
_ISR_STARTADDRESS
实际地址
HandleReset # 4
_ISR_STARTADDRESS+0x00
HandleUndef # 4
_ISR_STARTADDRESS+0x04
HandleSWI # 4
_ISR_STARTADDRESS+0x08
HandlePabort # 4
_ISR_STARTADDRESS+0x0c
HandleDabort # 4
_ISR_STARTADDRESS+0x10
HandleReserved # 4
_ISR_STARTADDRESS+0x14
HandleIRQ # 4
_ISR_STARTADDRESS+0x18
HandleFIQ # 4
_ISR_STARTADDRESS+0x1c
(注:虽然这里面将HandleReset也写了进去,但是HandleReset并没有执行前面的宏定义,为什么呢?想一下,如果系统复位的时候去_ISR_STARTADDRESS+0x00地址处取复位函数,能正确执行么?肯定不行,因为这个时候,_ISR_STARTADDRESS+0x00地址所在的RAM区有可能还没有被初始化。)
即:HandleIRQ在_ISR_STARTADDRESS+0x18地址处。所以HandleIRQ里面存的是要跳转函数的地址。ARM9有好多中断都挂靠在IRQ这个中断号上,那如果IRQ中断发生了,程序怎么知道是哪个中断源呢?因此又加了一级判断,来判断是哪个中断源发生的。这个时候要将判断IRQ中断源的函数放在HandleIRQ地址上。从2440init.s可知,IsrIRQ中断函数被放到了HandleIRQ处。在IsrIRQ函数里实现判断是哪个中断源发生的中断。下表中的变量仍然是指向函数指针的地址。比如说是HandleEINT0产生了中断,那么在IsrIRQ函数中就会去调用HandleEINT0所指向的函数(这个中断函数需要后续添加进去),从而实现中断调用。;@0x33FF_FF20
实际地址
;@0x33FF_FF60
实际地址
HandleEINT0
_ISR_STARTADDRESS+0x20
HandleLCD
_ISR_STARTADDRESS+0x40
HandleEINT1
_ISR_STARTADDRESS+0x24
HandleDMA0
_ISR_STARTADDRESS+0x44
HandleEINT2
_ISR_STARTADDRESS+0x28
HandleDMA1
_ISR_STARTADDRESS+0x48
HandleEINT3
_ISR_STARTADDRESS+0x2c
HandleDMA2
_ISR_STARTADDRESS+0x4c
HandleEINT4_7
...................................................
HandleDMA3
...................................................
HandleEINT8_23
...................................................
HandleMMC
...................................................
HandleCAM
...................................................
HandleSPI0
...................................................
HandleBATFLT
...................................................
HandleUART1
...................................................
HandleTICK
...................................................
HandleNFCON
...................................................
HandleWDT
...................................................
HandleUSBD
...................................................
HandleTIMER0
...................................................
HandleUSBH
...................................................
HandleTIMER1
...................................................
HandleIIC
...................................................
HandleTIMER2
...................................................
HandleUART0
...................................................
HandleTIMER3
...................................................
HandleSPI1
...................................................
HandleTIMER4
...................................................
HandleRTC
...................................................
HandleUART2
...................................................
HandleADC
...................................................
从上面的简要分析可知,当中断发生时,除了第一级中断是由CPU自己跳转过去的,后面的全由程序设计者自己实现。但是像HandleReset,HandleUndef,HandleSWI,HandlePabort,HandleDabort,HandleFIQ这些中断用不到第二级跳转。
到这里还有一个疑问,就是下面这段宏被放到了哪个位置?
MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
sub sp,sp,#4 ;decrementsp(to store jump address)
stmfd sp!,{r0} ;PUSHthe work register to stack(lr does''t push because it return to originaladdress)
ldr r0,=$HandleLabel;load the address ofHandleXXX to r0
ldr r0,[r0] ;load the contents(service routine start address) of HandleXXX
str r0,[sp,#4] ;store the contents(ISR) of HandleXXX tostack
ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)
MEND
通过调试可以发现,这些
HandlerFIQ HANDLER HandleFIQ
HandlerIRQ HANDLER HandleIRQ
HandlerUndef HANDLER HandleUndef
HandlerSWI HANDLER HandleSWI
HandlerDabort HANDLERHandleDabort
HandlerPabort HANDLERHandlePabort
被放到了0x00地址和ResetHandler之间的位置上。
根据上面可以绘出一个简单的中断对应关系图,如下:应用程序
当一切就绪好后,就可以跳转到主(应用)程序的入口处了。在主程序中填充需要的中断函数,以及有一点很重要的就是初始化MMU和CACHE。关于CACHE也可以不用初始化,但初始化CACHE后速度会提升不少。MMU和CACHE是什么?这里也没有找到一个较好的网址参考,关于这方面的知识网络上非常的多,多看几篇就能明白个大概了。总的来说,MMU是位于CPU和主存SDRAM之间的一个器件,用来将CPU发出的地址或者叫虚拟地址转成主存SDRAM这边真正的物理地址。CACHE是当CPU要访问某一地址的数据或指令时优先去Cache里面找,如果找到了,直接取走,这叫命中;如果没有找到,这叫未命中。然后就将主存对应位置里的数据或指令拷贝到Cache里。Cache分为指令Cache和数据Cache,同样还有逻辑Cache和物理Cache。可参考这个网址看一下:http://www.elecfans.com/emb/197050_2.html
为什么要初始MMU?
如果从Nor Flash启动,系统上电时从Nor Flash第一条指令开始运行,之后将Nor Flash代码拷贝到SDRAM内运行。比如说当IRQ中断发生时,CPU跳转到0x0000,0018地址处读取数据。此时0x0000,0018地址对应的是Nor Flash,因此取数据的速度变慢,但也可以运行。由此可知,在Nor Flash启动模式下,不初始化MMU是可以运行的。
如果从Nand Flash启动,Nand Flash前4kB被CPU读取到SteppingStone内,然后开始运行,之后在SDRAM内运行。比如说当IRQ中断发生时,CPU跳转到0x0000,0018地址处读取数据。此时0x0000,0018地址对应(Nand Flash启动,Nand Flash前4kB映射到片内SRAM内)的是内部的SRAM,因此从SRAM中读取数据,然后继续运行。由此可知,在Nand Flash启动模式下,不初始化MMU是可以运行的。
由上可知,当发生中断时,一个是跳转到Nor Flash内取数据,一个是跳转到片内SRAM取数据,都可以正常的中断跳转。如果我们在仿真调试时,那么在中断函数内打断点是无法停住的,因为调试时代码是在片外SDRAM内的。而中断时跳转的地址要么是在Nor Flash里,要么是在片内SRAM里。虽然不开MMU可以正常运行,但却给调试带来了麻烦。因此如果使用MMU,将0x0000,0000地址附近的地址空间映射到程序代码的运行域地址,比如说将0x0000,0000映射到SDRAM地址0x3000,0000处,那当发生中断时跳转的地址就是片内SDRAM的地址,这样就可以在SDRAM内断点调试。
可以参考网址:S3C2440无MMU_Init不能进中断的原因http://hi.baidu.com/1066033011/item/7f3dea48ac06102410ee1e80
问题:初始化MMU后,无法进入中断,程序仍然跑飞。
一开始,一个串口程序,接收是中断方式,在仿真调试时可以正常触发串口接收中断,进入接收中断函数。后来将ucos程序加入,串口接收是中断方式,并没有运行到ucos相关的代码,串口接收中断无法进入。连2440init.s文件中IsrIRQ标签后的程序都无法运行到。前后对比代码没有发现异样的地方。最后在查看map文件时发现,ZI数据的base和limit相同,但我申请了好几个初始化为0的数组,base和limit不应该相等。最后发现在我的scatter文件中有
RW_RAM1 0x31000000 0x01000000 { ; RW data
.ANY (+RW +ZI)
}
ZI +0 { ; ZI data
.ANY (+ZI)
}
而在启动的初始化文件2440init.s中引用ZI的base和limit时采用|Image$$ZI$$Base|、IMPORT |Image$$ZI$$Limit|,而变量都被放到了RW_RAM1内的ZI区。从而导致没有正确初始化ZI区的数据。后将|Image$$ZI$$Base|、 |Image$$ZI$$Limit|改为|Image$$RW_RAM1$$ZI$$Base| 、|Image$$RW_RAM1$$ZI$$Limit|,串口接收中断函数可以正常进入。
Scatter文件详解可参考http://www.cnblogs.com/Ilmen/p/3453248.html
应用程序运行时应处于哪种模式?
下面内容大部分摘自:《Mini2440启动代码的编写(第三版)》Page15。
ARM9中CPU运行程序所在的模式有:用户模式,快速中断模式,外部中断模式,管理模式,中止模式,未定义模式和系统模式。除用户模式之外的其它模式被称为特权模式,特权模式中除系统模式外的其它模式被称为异常模式。管理、中止、未定义、外部中断和快速中断这5种异常模式由相应的异常进入,用户程序不适合在这些模式下运行。这样留给我们选择的只有用户模式和系统模式,系统模式可以访问所有的系统资源并可以切换到其它模式,而用户模式却无法切换到其它模式,因此程序运行在系统模式下。- ARM9 (2440A) 从启动代码到应用程序(Main) 1
- C51 main()函数和启动代码 --- 从汇编到c51
- android 从应用程序连接到市场代码
- 从main到WinMain
- ARM启动代码(适用于arm9)
- 自用的ARM9(tq2440)的启动代码
- WPF 从Main函数启动
- 从命令行启动应用程序
- 阅读代码从 main 开始
- linux启动的第一个阶段(从开机到main)
- 从A到A+
- 从Entry Point到main函数调用(1)
- 从Entry Point到main函数调用(1)
- 从Entry Point到main函数调用(1)
- 从Entry Point到main函数调用(1-6)
- 从ARM9到A15 手机处理器架构进化历程
- 从ARM9到A15 手机处理器架构进化历程
- 全球手机处理器架构进化论:从ARM9到A15
- 使用ajax gson增强用户体验
- Java 压缩,解压zip文件(支持中文)
- poj 1160 动态规划(一维城市建邮局)
- modelsim-察看错误命令 verror
- 华安泰2014摄影作品征集:盛夏里的青春
- ARM9 (2440A) 从启动代码到应用程序(Main) 1
- Spring AOP 完成日志记录
- [ACM] POJ 3096 Surprising Strings (map的使用)
- 1!+2!+...n!求和程序
- check android activity exist
- OCP 1Z0 053 200
- 让你提前认识软件开发(39):软件研发之殇
- 智能家居通用管理平台(二)-软件架构设计
- 带缓存的HTTP代理服务器(七)