Chinaunix首页 | 论坛 | 博客
  • 博客访问: 707788
  • 博文数量: 255
  • 博客积分: 5000
  • 博客等级: 大校
  • 技术积分: 2811
  • 用 户 组: 普通用户
  • 注册时间: 2010-06-09 13:29
个人简介

IT业行者,行者无疆

文章分类

全部博文(255)

文章存档

2011年(121)

2010年(134)

我的朋友

分类: 嵌入式

2010-09-27 09:18:09

ARM在UC/OS II的移植日记(连载5)

from: http://fangheng1005.blog.163.com/blog/static/3291297200852493353569/

来看zlg的启动代码吧,因为ARM结构的特殊性,ARM只是卖他的内核,具体的芯片是授权给其他厂商生产的,这样有一个好处,就是ARM公司专心于ARM 内核的开发,可以使内核的功能更加强大,性能更强劲,但是也会造成另外一个问题,就是不同公司生产的芯片都得编写针对这个芯片的启动代码,对于应用来说确实会有一定的麻烦,因为启动代码是用汇编来写的,对于汇编,很多人都是可望不可企啊,嘿嘿,下面进入正题吧,分析zlg的启动代码。

Startup.s文件

FIQ_STACK_LEGTH         EQU         0
IRQ_STACK_LEGTH         EQU         9*8             ;every layer need 9 bytes stack , permit 8 layer .每层嵌套需要9个字堆栈,允许8层嵌套
ABT_STACK_LEGTH         EQU         0
UND_STACK_LEGTH         EQU         0

ARM 系统共有几种模式,当你使用FIQ中断的时候必须定义FIQ堆栈的长度,不能为0,否则堆栈会出错的;IRQ的堆栈长度是允许有8层嵌套的,每层需要使用的堆栈个数是9个,具体为什么是9个请看IRQ中断的方式,因为需要实现中断的嵌套,zlg是这样实现的,当是IRQ中断时统一进入一段代码,执行相关的保护然后开中断重新允许中断,从而实现了中断的嵌套,这里有一个重要的问题,当你不需要操作系统管理你的这个IRQ中断时,你可能不进行一个宏的定义,那么会出现一个问题,由于你的IRQ模式的堆栈是9×8,那么你的这个IRQ服务函数如果堆栈超过这个个数,那么后果是你的堆栈将溢出,出现不可预知的后果,所以这里建议应该所有的IRQ中断都由操作系统来管理,或者加大你的IRQ堆栈长度。后面的两个你应该可以自己解释了吧。

NoInt       EQU 0x80

定义一个常数,开关中断的掩码

USR32Mode   EQU 0x10
SVC32Mode   EQU 0x13
SYS32Mode   EQU 0x1f
IRQ32Mode   EQU 0x12
FIQ32Mode   EQU 0x11

这几个也是常数的定义,具体请看后面的使用

IMPORT __use_no_semihosting_swi

;The imported labels   
;引入的外部标号在这声明
IMPORT  FIQ_Exception                   ;Fast interrupt exceptions handler 快速中断异常处理程序
IMPORT  __main                          ;The entry point to the main function C语言主程序入口
IMPORT  TargetResetInit                 ;initialize the target board 目标板基本初始化
IMPORT  SoftwareInterrupt

;The emported labels   
;给外部使用的标号在这声明
EXPORT  bottom_of_heap
EXPORT  StackUsr
   
EXPORT  Reset
EXPORT  __user_initial_stackheap
IMPORT是用来声明外部变量的,这里的程序需要使用这些变量,EXPORT是用来声明外部变量的,供给这个文件以外的函数使用的,当然,不一定是变量,可能是一个函数的名字,一个标志而已。

 

 

 

    CODE32

指示这段汇编代码是32位的汇编代码

    AREA    vectors,CODE,READONLY

vectors是数据块的名字,CODE代表这是一段代码,READONLY代表它是只读的
  ENTRY

入口处,必须定义的哦^_^

;interrupt vectors
;中断向量表
Reset
LDR     PC, ResetAddr
LDR     PC, UndefinedAddr
LDR     PC, SWI_Addr
LDR     PC, PrefetchAddr
LDR     PC, DataAbortAddr
DCD     0xb9205f80

为什么是0xb9205f80呢??ARM有一个规定,要求所有异常地址的执行代码相加起来必须是0,否则认为是无效的代码,ARM内核不会执行这段代码的,就是说你可能写的不是0xb9205f80,但是你的代码编译连接烧录都没有问题,但是ARM就是不执行你的代码,ARM的异常地址共有7个,把你的异常地址的指令编译成二进制之后与0xb9205f80相加就是0了,如果不是0那么请修改0xb9205f80的值。
       LDR     PC, [PC, #-0xff0]

这样定义是可以算出IRQ那个关键寄存器的地址,然后取出它的值存向PC,从而实现中断的跳转
     LDR     PC, FIQ_Addr

ResetAddr           DCD     ResetInit
UndefinedAddr       DCD     Undefined
SWI_Addr            DCD     SoftwareInterrupt
PrefetchAddr        DCD     PrefetchAbort
DataAbortAddr       DCD     DataAbort
Nouse               DCD     0
IRQ_Addr            DCD     0
FIQ_Addr            DCD     FIQ_Handler

个人这么理解,ResetAddr           DCD     ResetInit,就是说本来是ResetInit才是执行代码的地址,DCD是一个伪指令。为什么要这么做呢,那是这样做的话,DCD     0xb9205f80这个值就不在修改了。

 

;未定义指令
Undefined
      Undefined

;取指令中止
PrefetchAbort
      PrefetchAbort

;取数据中止
DataAbort
      DataAbort

以上三个异常的处理方式是死循环,等待看门狗的复位,嘿嘿,如果你狗没开的话你就惨了,呵呵

;快速中断
FIQ_Handler
STMFD   SP!, {R0-R3, LR}

R0-R3是在函数调用时用来传送参数的,必须保存,因为在FIQ_Exception函数中可能有函数的调用,LR是返回地址寄存器,不用说,可定要保存啦
       BL      FIQ_Exception

具体的FIQ函数的功能是在这里实现的
      LDMFD   SP!, {R0-R3, LR}

堆栈的恢复
        SUBS    PC,  LR,  #4

FIQ中断要求这样才能正确的返回

FIQ中断的处理,这个FIQ中断是有错误的,因为上面定义了FIQ模式的堆栈是0,那么这样有可能造成堆栈的出错,除非你没有使能FIQ中断,否则你应该重新定义FIQ模式堆栈的长度,而不是0

 

ResetInit

这是系统复位时的第一入口,也就是启动代码的入口

        BL      InitStack               ; Initialize the stack 初始化堆栈
BL      TargetResetInit         ; Initialize the target board 目标板基本初始化
; Jump to the entry point of C program 跳转到c语言入口
      __main

在__main函数中会调用main函数,即会跑到你的main函数中的

 

 

InitStack   

这是复位之后立刻执行的代码 
MOV     R0, LR

保存返回地址

;Build the SVC stack
;设置中断模式堆栈
MSR     CPSR_c, #0xd2

使ARM内核进入IRQ模式
LDR     SP, StackIrq
;Build the FIQ stack 
;设置快速中断模式堆栈
MSR     CPSR_c, #0xd1

使ARM内核进入FIQ模式
LDR     SP, StackFiq
;Build the DATAABORT stack
;设置中止模式堆栈
MSR     CPSR_c, #0xd7
     LDR     SP, StackAbt
;Build the UDF stack
;设置未定义模式堆栈
MSR     CPSR_c, #0xdb
LDR     SP, StackUnd
;Build the SYS stack
;设置系统模式堆栈
MSR     CPSR_c, #0xdf
LDR     SP, =StackUsr

        MOV     PC, R0

R0保存的是LR的值,通过这个指令就能正确的返回

 

__user_initial_stackheap   
LDR   r0,=bottom_of_heap  
   LDR   r1,=StackUsr   
MOV   pc,lr
    
这里应该是设置预留函数的堆栈空间,不是很清楚     
StackIrq           DCD     IrqStackSpace + (IRQ_STACK_LEGTH - 1)* 4
StackFiq           DCD     FiqStackSpace + (FIQ_STACK_LEGTH - 1)* 4
StackAbt           DCD     AbtStackSpace + (ABT_STACK_LEGTH - 1)* 4
StackUnd           DCD     UndtStackSpace + (UND_STACK_LEGTH - 1)* 4

通知编译器预留各个模式堆栈的空间,ADS的堆栈是向下发展的,故StackIrq 这个标志是地址的最高单元

IrqStackSpace是栈空间的最低地址,通过IrqStackSpace + (IRQ_STACK_LEGTH - 1)* 4操作后就是只想了栈空间的最高地址了,从而满足了ADS栈空间向下发展的要求

 

    IF :DEF: EN_CRP
IF  . >= 0x1fc
INFO    1,"\nThe data at 0x000001fc must be 0x87654321.\nPlease delete some source before this line."
ENDIF
CrpData
WHILE . < 0x1fc
NOP
WEND
CrpData1
DCD     0x87654321          ;
ENDIF

当再ROM地址的0x000001fc处存放的数据是0x87654321时,程序空间将会保护,不允许读取,只能一次性删除,从而起到了保护代码的作用

;
AREA    MyStacks, DATA, NOINIT, ALIGN=2

IrqStackSpace      SPACE   IRQ_STACK_LEGTH * 4  ;Stack spaces for Interrupt ReQuest Mode 中断模式堆栈空间
FiqStackSpace      SPACE   FIQ_STACK_LEGTH * 4  ;Stack spaces for Fast Interrupt reQuest Mode 快速中断模式堆栈空间
AbtStackSpace      SPACE   ABT_STACK_LEGTH * 4  ;Stack spaces for Suspend Mode 中止义模式堆栈空间
UndtStackSpace     SPACE   UND_STACK_LEGTH * 4  ;Stack spaces for Undefined Mode 未定义模式堆栈

通知编译器预留栈空间
AREA    Heap, DATA, NOINIT
bottom_of_heap    SPACE   1

预留一个没有初始化的数据空间,只是预留了一个字节的空间

        AREA    Stacks, DATA, NOINIT
StackUsr

只是定义了一个编号,没有定义空间,但是别的地方应该能正确的定义这个空间的大小

    END

文件结束
;

 

(待续连载6)

阅读(590) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~