arm 指令(转载)

ucOS在ARM上的中断管理
SoftwareInterrupt
        LDR     SP, StackSvc            ; 重新设置堆栈指针
        STMFD   SP!, {R0-R3, R12, LR}
        MRS     R3, SPSR                        ; 保存状态
            STMFD   SP!, {R3}
 
        TST     R3, #T_bit              ; 中断前是否是Thumb状态
        LDRNEH  R0, [LR,#-2]            ; 是: 取得Thumb状态SWI号
        BICNE   R0, R0, #0xff00
        LDREQ   R0, [LR,#-4]            ; 否: 取得arm状态SWI号
        BICEQ   R0, R0, #0xFF000000
                                        ; r0 = SWI号,R1指向参数存储位置
        CMP     R0, #1
        LDRLO   PC, =OSCtxSw
        LDREQ   PC, =__OSStartHighRdy   ; SWI 0x01为第一次任务切换;
            LDMFD   SP!, {R3}                ; MRS     R3, SPSR  huifu 状态
            MSR     SPSR_cxsf, R3
        BL      SWI_Exception
               
        LDMFD   SP!, {R0-R3, R12, PC}^
;**************************************************************************
OSCtxSw         LDR     R3, [SP, #24]                       ;获取PC
                 LDR     R1, [SP, #20]                      ;获取R12
                 MRS     R0, CPSR
                 MSR     CPSR_c, #(NoInt | SYS32Mode)
                 MOV     R2,LR
                 STMFD   SP!,{R1-R3}    ; R12, LR,PC
                 STMFD   SP!,{R4-R11}   ;R4-R11
                 MSR     CPSR_c,R0
                 LDMFD   SP!, {R3}       ;  SPSR  huifu 状态
                 LDMFD   SP!, {R4-R7}    ;R0-R3
                 ADD     SP,SP,#8
                 LDR     R2, =OsEnterSum
                          LDR     R2,[R2]
                          MSR     CPSR_c, #(NoInt | SYS32Mode)
                          STMFD   SP!,{R2-R7}   ; OsEnterSum,SPSR, R0-R3
                 LDR     R0, =OSTCBCur
                          LDR     R0, [R0]
                      STR     SP, [R0]
                      BL      OSTaskSwHook        ;调用钩子函数
                         ;OSPrioCur <= OSPrioHighRdy
        LDR     R4, =OSPrioCur
        LDR     R5, =OSPrioHighRdy
        LDRB    R6, [R5]
        STRB    R6, [R4]
                        ;OSTCBCur <= OSTCBHighRdy
        LDR     R6, =OSTCBHighRdy
        LDR     R6, [R6]
        LDR     R4, =OSTCBCur
        STR     R6, [R4]
 OSIntCtxSw_1
                                                    ;获取新任务堆栈指针
        LDR     R4, [R6]
      ADD     SP, R4, #68       ;17寄存器OsEnterSum,CPSR,R0-R12,LR,PC
        LDR     LR, [SP, #-8]
        MSR     CPSR_c, #(NoInt | SVC32Mode)        ;进入管理模式
        MOV     SP, R4                              ;设置堆栈指针
        LDMFD   SP!, {R4, R5}                       ;CPSR,OsEnterSum
                                 ;恢复新任务的OsEnterSum
        LDR     R3, =OsEnterSum
        STR     R4, [R3]
        MSR     SPSR_cxsf, R5                       ;恢复CPSR
        LDMFD   SP!, {R0-R12,LR, PC }^             ;运行新任务
**************************************************************************
OSIntCtxSw
                 ADD     SP,SP,#8                      ;调整STACK
                 LDR     R3, [SP, #24]                       ;获取PC
                 LDR     R1, [SP, #20]                      ;获取R12
                 MRS     R0, CPSR
                 MSR     CPSR_c, #(NoInt | SYS32Mode)
                 MOV     R2,LR
                
                 STMFD   SP!,{R1-R3}    ; R12, LR,PC
                 STMFD   SP!,{R4-R11}   ;R4-R11
                 MSR     CPSR_c,R0
                 LDMFD   SP!, {R3}       ;  SPSR  huifu 状态
                 LDMFD   SP!, {R4-R7}    ;R0-R3
                 ADD     SP,SP,#8
                 MSR     CPSR_c, #(NoInt | SYS32Mode)
                          LDR     R2, =OsEnterSum      ;OsEnterSum=0, SAVE BACK
                          MOV     R1,#0
                          STR     R1,[R2]
                          LDR     R2,[R2]
                          STMFD   SP!,{R2-R7}   ; OsEnterSum,SPSR, R0-R3
                        LDR     R0, =OSTCBCur
                          LDR     R0, [R0]
                      STR     SP, [R0]
                      BL      OSTaskSwHook       ;调用钩子函数
                     ;OSPrioCur <= OSPrioHighRdy
        LDR     R4, =OSPrioCur
        LDR     R5, =OSPrioHighRdy
        LDRB    R6, [R5]
        STRB    R6, [R4]
                        ;OSTCBCur <= OSTCBHighRdy
        LDR     R6, =OSTCBHighRdy
        LDR     R6, [R6]
        LDR     R4, =OSTCBCur
        STR     R6, [R4]
                            ;获取新任务堆栈指针
        LDR     R4, [R6]
        ADD     SP, R4, #68        ;17寄存器OsEnterSum,CPSR,R0-R12,LR,PC
LDR     LR, [SP, #-8]
        MSR     CPSR_c, #(NoInt | SVC32Mode)        ;进入管理模式 NO IRQ
        MOV     SP, R4                              ;设置堆栈指针
LDMFD   SP!, {R4, R5}                       ;CPSR,OsEnterSum
        LDR     R3, =OsEnterSum     ;恢复新任务的OsEnterSum
        STR     R4, [R3]
   
        MSR     SPSR_cxsf, R5                       ;恢复CPSR
        LDMFD   SP!, {R0-R12,LR, PC }^             ;运行新任务
;************************************************************************/
__OSStartHighRdy
        MSR     CPSR_c, #(NoInt | SYS32Mode)
                                                ;告诉uC/OS-II自身已经运行
        LDR     R4, =OSRunning
        MOV     R5, #1
        STRB    R5, [R4]
        BL      OSTaskSwHook                    ;调用钩子函数
        LDR     R6, =OSTCBHighRdy     ;指针数组中的元素,指向一个 OS_TCB
        LDR     R6, [R6]               ;OSTCBHighRdy 处的首地址 (OS_TCB 结构体中的第一个元素:堆栈指针, OSSTK *OSTCBStkPtr) dy
        B       OSIntCtxSw_1                    ;R6 now is 堆栈首址
**************************************************************************
IRQHandler       
                         SUB     LR, LR, #4                      ; 计算返回地址
                       
                          STMFD   SP!, {R0-R3, R12, LR}           ; 保存任务环境
                          MRS     R3, SPSR                        ; 保存状态
                          STMFD   SP!, {R3}
 
                          LDR     R3,  =OSIntNesting            
                          LDRB    R1, [R3]
                          ADD     R1, R1, #1
                          STRB    R1, [R3]          ;;SAVE BACK
                          BL      interrupts        ; 调用c语言的中断处理程序
                  BL      OSIntExit
                         
   LDMFD   SP!, {R3}        ; MRS     R3, SPSR  huifu 状态
                         MSR     SPSR_cxsf, R3
                        LDMFD SP!, {R0-R3, R12, PC}^          ; 不进行任务切换
 
/b
/b
/b
ARM中的异常和中断
http://cache.baidu.com/c?word=ldr%2Cstr%2Cb&url=http%3A//blog%2Ecsdn%2Enet/jiaoxiaomin/archive/2007/09/14/1784690%2Easpx&p=8572d4188c904eae5aa9c4710912c1&user=baidu
ARM 汇编指令的一些总结
ARM 汇编指令很多,但是真正常用的不是很多,而且需要认真琢磨的又更少了。
比较有用的是 MOV B BL LDR STR
还是通过具体汇编代码来学习吧。
      @ disable watch dog timer     
   mov   r1, #0x53000000   // 立即数寻址方式
   mov   r2, #0x0
   str   r2, [r1]       
MOV 没有什么好说的,只要掌握几个寻址方式就可以了,而且 ARM 的寻址方式比 386 的简单很多。立即数寻址方式,立即数要求以“ # 作前缀,对于十六进制的数,还要求在 # 后面加上 0x 或者 & 0x 大家很好理解。有一次我碰到了 &ff 这个数,现在才明白跟 0xff 是一样的。
STR 是比较重要的指令了,跟它对应的是 LDR ARM 指令集是加载 / 存储型的,也就是说它只处理在寄存器中的数据。那么对于系统存储器的访问就经常用到 STR LDR 了。 STR 是把寄存器上的数据传输到指定地址的存储器上。它的格式我个人认为很特殊:
    STR( 条件 源寄存器, < 存储器地址 >
比如 STR R0, [R1] ,意思是 R0-> [R1] ,它把源寄存器写在前面,跟 MOV LDR 都相反。
LDR 应该是非常常见了。 LDR 就是把数据从存储器传输到寄存器上。而且有个伪指令也是 LDR ,因此我有个百思不得其解的问题。看这段代码:
mov r1, #GPIO_CTL_BASE
   add   r1, r1, #oGPIO_F
   ldr   r2,=0x55aa   // 0x55aa 是个立即数啊,前面加个=干什么?
   str   r2, [r1, #oGPIO_CON]
   mov   r2, #0xff
   str   r2, [r1, #oGPIO_UP]
   mov   r2, #0x00
   str   r2, [r1, #oGPIO_DAT]
对于当中的 ldr 那句,我就不明白了,如果你把 = 去掉,是不能通过编译的。我查了一些资料,个人感觉知道了原因:这个 = 应该表示 LDR 不是 ARM 指令,而是伪指令。 作为伪指令的时候, LDR 的格式如下:
    LDR 寄存器, = 数字常量 /Label
它的作用是把一个 32 位的地址或者常量调入寄存器。嗬嗬,那大家可能会问,
MOV r2,#0x55aa 也可以啊。应该是这样的。不过, LDR 是伪指令啊,也就是说编译时编译器会处理它的。怎么处理的呢?——规则如下:如果该数字常量在 MOV 指令范围内,汇编器会把这个指令作为 MOV 。如果不在 MOV 范围中,汇编器把该常量放在程序后面,用 LDR 来读取, PC 和该常量的偏移量不能超过 4KB
这么一说,虽然似懂非懂,但是能够解释这个语句了。
 
 
然后说一下跳转指令。 ARM 有两种跳转方式。
1 mov pc < 跳转地址〉
  这种向程序计数器 PC 直接写跳转地址,能在 4GB 连续空间内任意跳转。
2 )通过 B BL BLX BX 可以完成在当前指令向前或者向后 32MB 的地址空间的跳转(为什么是 32MB 呢?寄存器是 32 位的,此时的值是 24 位有符号数,所以 32MB )。
B 是最简单的跳转指令。要注意的是,跳转指令的实际值不是绝对地址,而是相对地址——是相对当前 PC 值的一个偏移量,它的值由汇编器计算得出。
BL 非常常用。它在跳转之前会在寄存器 LR(R14) 中保存 PC 的当前内容。 BL 的经典用法如下:
       bl NEXT   ; 跳转到NEXT
       ……
    NEXT
       ……
       mov pc, lr    ; 从子程序返回。
 
最后提一下 Thumb 指令。 ARM 体系结构还支持 16 位的 Thumb 指令集。 Thumb 指令集是 ARM 指令集的子集,它保留了 32 位代码优势的同时还大大节省了存储空间。由于 Thumb 指令集的长度只有 16 位,所以它的指令比较多。它和 ARM 各有自己的应用场合。对于系统性能有较高要求,应使用 32 位存储系统和 ARM 指令集;对于系统成本和功耗有较高要求,应使用 16 位存储系统和 ARM 指令集。
对ARM异常(Exceptions)的理解
分类:技术笔记
毕设笔记
1 .对 ARM 异常( Exceptions )的理解
所有的系统引导程序前面中会有一段类似的代码,如下:
.globl _start                    ;系统复位位置
_start: b       reset            ;各个异常向量对应的跳转代码
        ldr     pc, _undefined_instruction ;未定义的指令异常
        ldr     pc, _software_interrupt     ;软件中断异常
        ldr     pc, _prefetch_abort          ;内存操作异常
        ldr     pc, _data_abort               ;数据异常
        ldr     pc, _not_used                  ;未使用
        ldr     pc, _irq                       ;慢速中断异常
        ldr     pc, _fiq                       ;快速中断异常
从中我们可以看出, ARM 支持 7 种异常。问题时发生了异常后 ARM 是如何响应的呢?第一个复位异常很好理解,它放在 0x0 的位置,一上电就执行它,而且我们的程序总是从复位异常处理程序开始执行的,因此复位异常处理程序不需要返回。那么怎么会执行到后面几个异常处理函数呢?
看看书后,明白了 ARM 对异常的响应过程,于是就能够回答以前的这个疑问。
当一个异常出现以后, ARM 会自动执行以下几个步骤:
1 )把下一条指令的地址放到连接寄存器 LR( 通常是 R14) ,这样就能够在处理异常返回时从正确的位置继续执行。
2 )将相应的 CPSR( 当前程序状态寄存器 ) 复制到 SPSR (备份的程序状态寄存器)中。从异常退出的时候,就可以由 SPSR 来恢复 CPSR
(3) 根据异常类型,强制设置 CPSR 的运行模式位。
4 )强制 PC (程序计数器)从相关异常向量地址取出下一条指令执行,从而跳转到相应的异常处理程序中。
至于这些异常类型各代表什么,我也没有深究。因为平常就关心 reset 了,也没有必要弄清楚。
ARM 规定了异常向量的地址:
   b       reset            ; 复位 0x0
ldr pc, _undefined_instruction ;未定义的指令异常 0x4
       ldr     pc, _software_interrupt     ;软件中断异常    0x8
       ldr     pc, _prefetch_abort          ;预取指令    0xc
       ldr     pc, _data_abort               ;数据        0x10
       ldr     pc, _not_used                  ;未使用      0x14
       ldr     pc, _irq                       ;慢速中断异常   0x18
        ldr   pc, _fiq                       ;快速中断异常    0x1c
这样理解这段代码就非常简单了。碰到异常时,PC会被强制设置为对应的异常向量,从而跳转到相应的处理程序,然后再返回到主程序继续执行。
这些引导程序的中断向量,是仅供引导程序自己使用的,一旦引导程序引导Linux内核完毕后,会使用自己的中断向量。
嗬嗬,这又有问题了。比如,ARM发生中断(irq)的时候,总是会跑到0x18上执行啊。那Linux内核又怎么能使用自己的中断向量呢?原因在于Linux内核采用页式存储管理。开通MMU的页面映射以后,CPU所发出的地址就是虚拟地址而不是物理地址。就Linux内核而言,虚拟地址0x18经过映射以后的物理地址就是0xc000 0018。所以Linux把中断向量放到0xc000 0018就可以了。
另外,说一下MMU。说句实话,还不是很明白这个MMU机理。参加Intel培训的时候,李眈说了MMU的两个主要作用:
(1 )安全性:规定访问权限
 (2) 提供地址空间:把不连续的空间转换成连续的。
第2点是不是实现页式存储的意思?
2005 年6月9日
补充一下:  05/06/14

.globl _start ;系统复位位置
_start: b reset ;各个异常向量对应的跳转代码
ldr pc, _undefined_instruction ;未定义的指令异常

……

_undefined_instruction :
.word undefined_instruction

也许有人会有疑问,同样是跳转指令,为什么第一句用的是 b reset;
而后面的几个都是用ldr

为了理解这个问题,我们以未定义的指令异常为例。

当发生了这个异常后,CPU总是跳转到0x4,这个地址是虚拟地址,它映射到哪个物理地址
取决于具体的映射。
ldr pc, _undefined_instruction
相对寻址,跳转到标号_undefined_instruction,然而真正的跳转地址其实是_undefined_instruction的内容——undefined_instruction。那句.word的相当于:
_undefined_instruction dw undefined_instruction (详见毕设笔记3)。
这个地址undefined_instruction到底有多远就难说了,也许和标号_undefined_instruction在同一个页面,也许在很远的地方。不过除了reset,其他的异常是MMU开始工作之后才可能发生的,因此undefined_instruction 的地址也经过了MMU的映射。
在刚加电的时候,CPU从0x0开始执行,MMU还没有开始工作,此时的虚拟地址和物理地址相同;另一方面,重启在MMU开始工作后也有可能发生,如果reset也用ldr就有问题了,因为这时候虚拟地址和物理地址完全不同。

因此,之所以reset用b,就是因为reset在MMU建立前后都有可能发生,而其他的异常只有在MMU建立之后才会发生。用b reset,reset子程序与reset向量在同一页面,这样就不会有问题(b是相对跳转的)。如果二者相距太远,那么编译器会报错的。
/b
/b
/b
ARM基础知识连载之一

ARM处理器共有37个寄存器。其中包括:


**31个通用寄存器,包括程序计数器(PC)在内。这些寄存器都是32位寄存器。


**6个状态寄存器。这些寄存器都是32位寄存器。


ARM处理器共有7种不同的处理器模式,每一种模式中都有一组相应的寄存器组。在任何时刻,可见的寄存器包括15个通用寄存器(R0-R14),一个或两个状态寄存器及程序计数器(PC)。在所有的寄存器中,有些是各模式公用一个物理寄存器,有一些寄存器各模式拥有自己独立的物理寄存器。


****************************************************


通用寄存器


***************************************************8


通用寄存器分为以下三类:备份寄存器、未备份寄存器、程序计数器PC


未备份寄存器


未备份寄存器包括R0-R7。对于每一个未备份寄存器来说,所有处理器模式下都是使用同一个物理寄存器。未备份寄存器没有被系统用于特别的用途,任何可采用通用寄存器的场合都可以使用未备份寄存器。


备份寄存器


对于R8-R12备份寄存器来说,每个寄存器对应两个不同的物理寄存器。系统为将备份寄存器用于任何的特殊用途,但是当中断处理非常简单,仅仅使用R8-R14寄存器时,FIQ处理程序可以不必执行保存和恢复中断现场的指令,从而可以使中断处理非常迅速。


对于R13,R14备份寄存器来说,每个寄存器对应六个不同的物理寄存器,其中的一个是系统模式和用户模式共用的;另外的五个对应于其他的五种处理器模式。采用下面的记号来区分各个物理寄存器:


R13_


其中MODE可以是下面几种模式之一:usr,svc,abt,und,irq,fiq


程序计数器PC


可以作为一般的通用寄存器使用,但有一些指令在使用R15时有一些限制。由于ARM采用了流水线处理器机制,当正确读取了PC的值时,该值为当前指令地址值加上8个字节。也就是说,对于ARM指令集来说,PC指向当前指令的下两条指令的地址。由于ARM指令是字对齐的,PC值的第0位和第一位总为0。


需要注意的是,当使用str/stm保存R15时,保存的可能是当前指令地址值加8个字节,也可能保存的是当前指令地址值加12个字节。到底哪种方式取决于芯片的具体设计。对于用户来说,尽量避免使用STR/STM指令来保存R15的值。
当成功的向R15写入一个数值时,程序将跳转到该地址执行。由于ARM指令是字对齐的,写入R15的值应满足bits[1:0]为0b00,具体要求arm个版本有所不同:
**对于arm3以及更低的版本,写入R15的地址值bits[1:0]被忽略,即写入r15的地址值将与0xFFFF FFFC做与操作。
**对于ARM4以及更高的版本,程序必须保证写入R15的地址值bits[1:0]为0b00,否则将产生不可预知的后果。
对于Thumb指令集来说,指令是班子对齐的,处理器将忽略bit[0]。



***************************************************************


程序状态寄存器


***************************************************************
CPSR(当前程序状态寄存器)在任何处理器模式下被访问。它包含了条件标志位、中断禁止位、当前处理器模式标志以及其他的一些控制和状态位。每一种处理器


模式下都有一个专用的物理状态寄存器,称为SPSR(备份程序状态寄存器)
。当特定的异常中断发生时,这个寄存器用于存放当前程序状态寄存器的内容。在异常中断退出时,可以用SPSR来恢复CPSR。由于用户模式和系统模式不是异常


中断模式,所以他没有SPSR。当用户在用户模式或系统模式访问SPSR,将产生不可预知的后果。
CPSR格式如下所示。SPSR和CPSR格式相同。


31 30 29 28 27 26 7 6 5 4 3 2 1 0
N Z C V Q DNM(RAZ) I F T M4 M3 M2 M1 M0



***条件标志位***
N——本位设置成当前指令运算结果的bit[31]的值。当两个表示的有符号整数运算时,n=1表示运算结果为负数,n=0表示结果为正书或零。


z——z=1表示运算的结果为零;z=0表示运算的结果不为零。对于CMP指令,Z=1表示进行比较的两个数大小相等。


C——下面分四种情况讨论C的设置方法:
在加法指令中(包括比较指令CMP),当结果产生了进位,则C=1,表示无符号运算发生上溢出;其他情况C=0。
在减法指令中(包括减法指令CMP),当运算中发生错位,则C=0,表示无符号运算数发生下溢出;其他情况下C=1。
对于包含移位操作的非加碱运算指令,C中包含最后一次溢出的的位的数值
对于其他非加减运算指令,C位的值通常不受影响
V——对于加减运算指令,当操作数和运算结果为二进制的补码表示的带符号数时,V=1表示符号为溢出;通常其他指令不影响V位。


***Q标识位***
在ARM V5的E系列处理器中,CPSR的bit[27]称为q标识位,主要用于指示增强的dsp指令是否发生了溢出。同样的spsr的bit[27]位也称为q标识位,用于在异常中


断发生时保存和恢复CPSR中的Q标识位。
在ARM V5以前的版本及ARM V5的非E系列的处理器中,Q标识位没有被定义。



***CPSR中的控制位***


CPSR的低八位I、F、T、M[4:0]统称为控制位。当异常中断发生时这些位发生变化。在特权级的处理器模式下,软件可以修改这些控制位。


**中断禁止位:当I=1时禁止IRQ中断,当F=1时禁止FIQ中断


**T控制位:T控制位用于控制指令执行的状态,即说明本指令是ARM指令还是Thumb指令。对于ARM V4以更高版本的T系列ARM处理器,T控制位含义如下:
T=0表示执行ARM指令
T=1表示执行Thumb指令
对于ARM V5以及更高版本的非T系列处理器,T控制位的含义如下
T=0表示执行ARM指令
T=1表示强制下一条执行的指令产生未定指令中断


***M控制位***
M控制位控制处理器模式,具体含义如下:


M[4:0] 处理器模式 可访问的寄存器
ob10000 user pc,r14~r0,CPSR
0b10001 FIQ PC,R14_FIQ-R8_FIQ,R7~R0,CPSR,SPSR_FIQ
0b10010 IRQ PC,R14_IRQ-R13_IRQ,R12~R0,CPSR,SPSR_IRQ
0B10011 SUPERVISOR PC,R14_SVC-R13_SVC,R12~R0,CPSR,SPSR_SVC
0b10111 ABORT PC,R14_ABT-R13_ABT,R12~R0,CPSR,SPSR_ABT
0b11011 UNDEFINEED PC,R14_UND-R8_UND,R12~R0,CPSR,SPSR_UND
0b11111 SYSTEM PC,R14-R0,CPSR(ARM V4以及更高版本)


***CPSR中的其他位***


这些位用于将来扩展。应用软件不要操作这些位。



在ARM体系中通常有以下3种方式控制程序的执行流程:


**在正常执行过程中,每执行一条ARM指令,程序计数器(PC)的值加4个字节;每执行一条Thumb指令,程序计数器寄存器(PC)加2个字节。整个过程是按顺序执行



**跳转指令,程序可以跳转到特定的地址标号处执行,或者跳转到特定的子程序处执行。其中,B指令用于执行跳转操作;BL指令在执行跳转操作同时,保存子程


序的返回地址;BX指令在执行跳转操作同时,根据目标地址为可以将程序切换到Thumb状态;BLX指令执行3个操作,跳转到目标地址处执行,保存子程序的返回


地址,根据目标地址为可以将程序切换到Thumb状态。


**当异常中断发生时,系统执行完当前指令后,将跳转到相应的异常中断处理程序处执行。当异常中断处理程序执行完成后,程序返回到发生中断指令的下条指


令处执行。在进入异常中断处理程序时,要保存被中断程序的执行现场,从异常中断处理程序退出时,要恢复被中断程序的执行现场。



ARM中异常中断的种类


**复位(RESET)**
当处理器复位引脚有效时,系统产生复位异常中断,程序跳转到复位异常中断处理程序处执行。复位异常中断通常用在下面几种情况下:系统加电时;系统复位时;跳转到复位中断向量处执行成为软复位。


**未定义的指令**
当ARM处理器或者是系统中的协处理器认为当前指令未定义时,产生未定义的指令异常中断,可以通过改异常中断机制仿真浮点向量运算。


**软件中断**
这是一个由用户定义的中断指令。可用于用户模式下的程序调用特权操作指令。在实时操作系统中可以通过该机制西线系统功能调用。


**指令与取终止(PrefechAbort)**
如果处理器预取的指令的地址不存在,或者该地址不允许当前指令访问,当被预取的指令执行时,处理器产生指令预取终止异常中断。


**数据访问终止(DATAABORT)
如果数据访问指令的目标地址不存在,,或者该地址不允许当前指令访问,处理器产生数据访问终止异常中断


**外部中断请求(IRQ)**
当处理器的外部中断请求引脚有效,而且CPSR的寄存器的I控制位被清除时,处理器产生外部中断请求异常中断。系统中个外设通过该异常中断请求处理服务。


**快速中断请求(FIQ)**
当处理器的外部快速中断请求引脚有效,而且CPSR的F控制位被清除时,处理器产生外部中断请求异常中断


异常中断向量表及异常中断优先级
中断向量表指定了个异常中断及其处理程序的对应关系。他通常存放在存储地址的低端。在ARM体系中,异常中断向量表的大小为32字节,其中每个异常中断占据4个字节大小,保留了4个字节空间。
每个异常中断对应的中断向量表中的4个字节的空间中存放了一个跳转指令或者一个向PC寄存器中赋值的数据访问指令。通过这两种指令,程序将跳转到相应的异常中断处理程序处执行。当几个异常中断同时发生时,就必须按照一定的次序来处理这些异常中断。


各个异常中断的中断向量地址以及中断的处理优先级
中断向量地址 异常中断类型 异常中断模式 优先级(6最低)
0x00 复位 特权模式 1
0x04 未定义的指令 未定义指令终止模式 6
0x08 软件中断 特权模式 6
0x0C 指令预取终止 终止模式 5
0x10 数据访问终止 终止模式 2
0x14 保留 未使用 未使用
0x18 外部中断请求 IRQ模式 4
0x1C 快速中断请求 FIQ模式 3


在应用程序中安装异常中断处理程序
1.使用跳转指令:可以在异常中断对应异常向量表中特定位置放置一条跳转指令,直接跳转到该异常中断的处理程序。这种方法有一个缺点,即只能在32M空间范围内跳转。
2.使用数据读取指令LDR:使用数据读取指令LDR向程序计数器PC中直接赋值。这种方法分为两步:先将异常中断处理程序的绝对地址存放在存放在距离向量表4KB范围内的一个存储单元中;再使用数据读取指令LDR将该单元的内容读取到程序计数器PC中。


**在系统复位时安装异常中断处理程序**
1.地址0x00处为ROM的情况
使用数据读取指令LDR示例如下所示:
Vector_Init_Block
LDR PC, Reset_Addr
LDR PC, Undefined_Addr
LDR PC, SW_Addr
LDR PC, Prefeth_Addr
LDR PC, Abort_Addr
NOP
LDR PC, IRQ_Addr
LDR PC, FIQ_Addr


Reset_Addr DCD Start_Boot
Undefined_Addr DCD Undefined_Handle
SW_Addr DCD SWI_Handle
Prefeth_Addr DCD Prefeth_Handle
Abort_Addr DCD Abort_Handle
DCD 0
IRQ_Addr DCD IRQ_Handle
FIQ_Addr DCD FIQ_Handle
使用跳转指令的示例如下所示:
Vector_Init_Block
BL Reset_Handle
BL DCD Undefined_Handle
BL SWI_Handle
BL Prefeth_Handle
BL Abort_Handle
NOP
BL IRQ_Handle
BL FIQ_Handle
2.地址0x00处为RAM的情况
地址0x00处为RAM时,中断向量表必须使用数据读取指令直接指向PC中赋值的形式。而且,必须使用下面的代码巴中断向量表从ROM中复制到RAM中地址0x00开始处的存储空间中:

MOV r8,#0
ADR r9,Vector_Init_Block
;复制中断向量表(8字)
LDMIA r9!,(r0-r7)
STMIA r8!,(r0-r7)
;复制保存各中断处理函数地址的表(8字words)
LDMIA r9!,(r0-r7)
STMIA r8!,(r0-r7)

/b
/b
/b

***************************************************************

程序状态寄存器

***************************************************************
CPSR(当前程序状态寄存器)在任何处理器模式下被访问。它包含了条件标志位、中断禁止位、当前处理器模式标志以及其他的一些控制和状态位。每一种处理器

模式下都有一个专用的物理状态寄存器,称为SPSR(备份程序状态寄存器)
。当特定的异常中断发生时,这个寄存器用于存放当前程序状态寄存器的内容。在异常中断退出时,可以用SPSR来恢复CPSR。由于用户模式和系统模式不是异常

中断模式,所以他没有SPSR。当用户在用户模式或系统模式访问SPSR,将产生不可预知的后果。
CPSR格式如下所示。SPSR和CPSR格式相同。

31 30 29 28 27 26 7 6 5 4 3 2 1 0
N Z C V Q DNM(RAZ) I F T M4 M3 M2 M1 M0


***条件标志位***
N——本位设置成当前指令运算结果的bit[31]的值。当两个表示的有符号整数运算时,n=1表示运算结果为负数,n=0表示结果为正书或零。

z——z=1表示运算的结果为零;z=0表示运算的结果不为零。对于CMP指令,Z=1表示进行比较的两个数大小相等。

C——下面分四种情况讨论C的设置方法:
在加法指令中(包括比较指令CMP),当结果产生了进位,则C=1,表示无符号运算发生上溢出;其他情况C=0。
在减法指令中(包括减法指令CMP),当运算中发生错位,则C=0,表示无符号运算数发生下溢出;其他情况下C=1。
对于包含移位操作的非加碱运算指令,C中包含最后一次溢出的的位的数值
对于其他非加减运算指令,C位的值通常不受影响
V——对于加减运算指令,当操作数和运算结果为二进制的补码表示的带符号数时,V=1表示符号为溢出;通常其他指令不影响V位。

***Q标识位***
在ARM V5的E系列处理器中,CPSR的bit[27]称为q标识位,主要用于指示增强的dsp指令是否发生了溢出。同样的spsr的bit[27]位也称为q标识位,用于在异常中

断发生时保存和恢复CPSR中的Q标识位。
在ARM V5以前的版本及ARM V5的非E系列的处理器中,Q标识位没有被定义。


***CPSR中的控制位***

CPSR的低八位I、F、T、M[4:0]统称为控制位。当异常中断发生时这些位发生变化。在特权级的处理器模式下,软件可以修改这些控制位。

**中断禁止位:当I=1时禁止IRQ中断,当F=1时禁止FIQ中断

**T控制位:T控制位用于控制指令执行的状态,即说明本指令是ARM指令还是Thumb指令。对于ARM V4以更高版本的T系列ARM处理器,T控制位含义如下:
T=0表示执行ARM指令
T=1表示执行Thumb指令
对于ARM V5以及更高版本的非T系列处理器,T控制位的含义如下
T=0表示执行ARM指令
T=1表示强制下一条执行的指令产生未定指令中断

***M控制位***
M控制位控制处理器模式,具体含义如下:

M[4:0] 处理器模式 可访问的寄存器
ob10000 user pc,r14~r0,CPSR
0b10001 FIQ PC,R14_FIQ-R8_FIQ,R7~R0,CPSR,SPSR_FIQ
0b10010 IRQ PC,R14_IRQ-R13_IRQ,R12~R0,CPSR,SPSR_IRQ
0B10011 SUPERVISOR PC,R14_SVC-R13_SVC,R12~R0,CPSR,SPSR_SVC
0b10111 ABORT PC,R14_ABT-R13_ABT,R12~R0,CPSR,SPSR_ABT
0b11011 UNDEFINEED PC,R14_UND-R8_UND,R12~R0,CPSR,SPSR_UND
0b11111 SYSTEM PC,R14-R0,CPSR(ARM V4以及更高版本)

***CPSR中的其他位***

这些位用于将来扩展。应用软件不要操作这些位。

/b
/b

在ARM体系中通常有以下3种方式控制程序的执行流程:

**在正常执行过程中,每执行一条ARM指令,程序计数器(PC)的值加4个字节;每执行一条Thumb指令,程序计数器寄存器(PC)加2个字节。整个过程是按顺序执行

**跳转指令,程序可以跳转到特定的地址标号处执行,或者跳转到特定的子程序处执行。其中,B指令用于执行跳转操作;BL指令在执行跳转操作同时,保存子程

序的返回地址;BX指令在执行跳转操作同时,根据目标地址为可以将程序切换到Thumb状态;BLX指令执行3个操作,跳转到目标地址处执行,保存子程序的返回

地址,根据目标地址为可以将程序切换到Thumb状态。

**当异常中断发生时,系统执行完当前指令后,将跳转到相应的异常中断处理程序处执行。当异常中断处理程序执行完成后,程序返回到发生中断指令的下条指

令处执行。在进入异常中断处理程序时,要保存被中断程序的执行现场

/b
/b
http://hi.baidu.com/wzbob/blog/item/1a654fed2877c4d4b31cb1b2.html
 

ARM中异常中断的种类

**复位(RESET)**
当处理器复位引脚有效时,系统产生复位异常中断,程序跳转到复位异常中断处理程序处执行。复位异常中断通常用在下面几种情况下:系统加电时;系统复位时;跳转到复位中断向量处执行成为软复位。

**未定义的指令**
当ARM处理器或者是系统中的协处理器认为当前指令未定义时,产生未定义的指令异常中断,可以通过改异常中断机制仿真浮点向量运算。

**软件中断**
这是一个由用户定义的中断指令。可用于用户模式下的程序调用特权操作指令。在实时操作系统中可以通过该机制西线系统功能调用。

**指令与取终止(PrefechAbort)**
如果处理器预取的指令的地址不存在,或者该地址不允许当前指令访问,当被预取的指令执行时,处理器产生指令预取终止异常中断。

**数据访问终止(DATAABORT)
如果数据访问指令的目标地址不存在,,或者该地址不允许当前指令访问,处理器产生数据访问终止异常中断

**外部中断请求(IRQ)**
当处理器的外部中断请求引脚有效,而且CPSR的寄存器的I控制位被清除时,处理器产生外部中断请求异常中断。系统中个外设通过该异常中断请求处理服务。

**快速中断请求(FIQ)**
当处理器的外部快速中断请求引脚有效,而且CPSR的F控制位被清除时,处理器产生外部中断请求异常中断

异常中断向量表及异常中断优先级
中断向量表指定了个异常中断及其处理程序的对应关系。他通常存放在存储地址的低端。在ARM体系中,异常中断向量表的大小为32字节,其中每个异常中断占据4个字节大小,保留了4个字节空间。
每个异常中断对应的中断向量表中的4个字节的空间中存放了一个跳转指令或者一个向PC寄存器中赋值的数据访问指令。通过这两种指令,程序将跳转到相应的异常中断处理程序处执行。当几个异常中断同时发生时,就必须按照一定的次序来处理这些异常中断。

各个异常中断的中断向量地址以及中断的处理优先级
中断向量地址 异常中断类型 异常中断模式 优先级(6最低)
0x00 复位 特权模式 1
0x04 未定义的指令 未定义指令终止模式 6
0x08 软件中断 特权模式 6
0x0C 指令预取终止 终止模式 5
0x10 数据访问终止 终止模式 2
0x14 保留 未使用 未使用
0x18 外部中断请求 IRQ模式 4
0x1C 快速中断请求 FIQ模式 3

/b
/b
http://hi.baidu.com/wzbob/blog/item/caaa070862f045d162d986b2.html
ARM基础知识连载之五
在应用程序中安装异常中断处理程序
1.使用跳转指令:可以在异常中断对应异常向量表中特定位置放置一条跳转指令,直接跳转到该异常中断的处理程序。这种方法有一个缺点,即只能在32M空间范围内跳转。
2.使用数据读取指令LDR:使用数据读取指令LDR向程序计数器PC中直接赋值。这种方法分为两步:先将异常中断处理程序的绝对地址存放在存放在距离向量表4KB范围内的一个存储单元中;再使用数据读取指令LDR将该单元的内容读取到程序计数器PC中。

**在系统复位时安装异常中断处理程序**
1.地址0x00处为ROM的情况
使用数据读取指令LDR示例如下所示:
Vector_Init_Block
LDR PC, Reset_Addr
LDR PC, Undefined_Addr
LDR PC, SW_Addr
LDR PC, Prefeth_Addr
LDR PC, Abort_Addr
NOP
LDR PC, IRQ_Addr
LDR PC, FIQ_Addr

Reset_Addr DCD Start_Boot
Undefined_Addr DCD Undefined_Handle
SW_Addr DCD SWI_Handle
Prefeth_Addr DCD Prefeth_Handle
Abort_Addr DCD Abort_Handle
DCD 0
IRQ_Addr DCD IRQ_Handle
FIQ_Addr DCD FIQ_Handle
使用跳转指令的示例如下所示:
Vector_Init_Block
BL Reset_Handle
BL DCD Undefined_Handle
BL SWI_Handle
BL Prefeth_Handle
BL Abort_Handle
NOP
BL IRQ_Handle
BL FIQ_Handle
2.地址0x00处为RAM的情况
地址0x00处为RAM时,中断向量表必须使用数据读取指令直接指向PC中赋值的形式。而且,必须使用下面的代码巴中断向量表从ROM中复制到RAM中地址0x00开始处的存储空间中:

MOV r8,#0
ADR r9,Vector_Init_Block
;复制中断向量表(8字)
LDMIA r9!,(r0-r7)
STMIA r8!,(r0-r7)
;复制保存各中断处理函数地址的表(8字words)
LDMIA r9!,(r0-r7)
STMIA r8!,(r0-r7)

 

 

/b

/b

/b

/b

*********************************************

ARM存储系统概述

*********************************************
ARM存储系统的体系结构适应不同的嵌入式应用系统的需要差别很大。最简单的存储系统使用平办事的地址映射机制,就像一些简单的弹片机系统中一样,地址空间的分配方式是固定的,系统各部分都使用物理地址。而一些复杂系统可能包括下面的一种或几种技术,从而提供更为强大的存储系统。

**系统中可能包含多种类型的存储器,如FLASH,ROM,RAM,EEPROM等,不同类型的存储器的速度和宽度等各不相同。
**通过使用CACHE及WRITE BUFFER技术缩小处理器和存储系统速度差别,从而提高系统的整体性能。
**内存管理部件通过内存映射技术实现虚拟空间到物理空间的映射。在系统加电时,将ROM/FLASH影射为地址0,这样可以进行一些初始化处理;当这些初始化完成后将RAM地址影射为0,并把系统程序加载到RAM中运行,这样很好地解决了嵌入式系统的需要。
**引入存储保护机制,增强系统的安全性。
**引入一些机制保证I/O操作应设成内存操作后,各种I/O操作能够得到正确的结果。

**与存储系统相关的程序设计指南**
本节从外部来看ARM存储系统,及ARM存储系统提供的对外接口。本节介绍用户通过这些接口来访问ARM存储系统时需要遵守的规则。

1.地址空间
ARM体系使用单一的和平板地址空间。该地址空间大小为2^32个8位字节,这些字节的单元地址是一个无符号的32位数值,其取值范围为0~2^32-1。ARM地址空间也可以看作是2^30个32位的字单元。这些字单元的地址可以被4整除,也就是说该地址低两位为0b00。地址为A的字数据包括地址为A、A+1、A+3、A+3 4个字节单元的内容。
各存储单元的地址作为32为无符号数,可以进行常规的整数运算。这些运算的结果进行2^32取模。
程序正常执行时,每执行一条ARM指令,当前指令计数器加4个字节;每执行一条Thumb指令,当前指令计数器加2个字节。但是,当地址上发生溢出时,执行结果将是不可预知的。
2.存储器格式
在ARM中,如果地址A是字对齐的,有下面几种:
**地址为A的字单元包括字节单元A,A+1,A+2,A+3。
**地址为A的班子单元包括字节单元A,A+1。
**地址为A+2的半字单元包括字节单元A+2,A=3.
**地址为A的字单元包括半字节单元A,A+2。
在big-endian格式中,对于地址为a的字单元其中字节单元由高位到低位字节顺序为A,A+1,A=2,A+3;这种存储器格式如下所示:

31 24 23 16 15 8 7 0
--------------------------------------------------------------------
字单元A |
--------------------------------------------------------------------
半字单元A | 半字单元A+2 |
--------------------------------------------------------------------
字节单元A | 字节单元A+1 | 字节单元A+2 | 字节单元A+3|
--------------------------------------------------------------------

在little-endian格式中,对于地址为A的字单元由高位到低位字节顺序为A+3,A+2,A+1,A,这种存储格式如下所示

31 24 23 16 15 8 7 0
--------------------------------------------------------------------
字单元A |
--------------------------------------------------------------------
半字单元A+2 | 半字单元A |
--------------------------------------------------------------------
字节单元A+3 |字节单元A+2 | 字节单元A+1 | 字节单元A |
--------------------------------------------------------------------

在ARM系统中没有提供指令来选择存储器格式。如果系统中包含标准的ARM控制协处理器CP15,则CP15的寄存器C1的位[7]决定系统中存储器的格式。当系统复位时,寄存器C1的[7]值为零,这时系统中存储器格式为little-endian格式。如果系统中采用的是big-endian格式,则复位异常中断处理程序中必须设置c1寄存器的[7]位。

3.非对齐的存储访问操作
非对齐:位于arm状态期间,低二位不为0b00;位于Thumb状态期间,最低位不为0b0。
3.1非对齐的指令预取操作
如果系统中指定当发生非对齐的指令预取操作时,忽略地址中相应的位,则由存储系统实现这种忽略。
3.2非对齐的数据访问操作
对于LOAD/STORE操作,系统定义了下面3中可能的结果:
***执行结果不可预知
***忽略字单元地址低两位的值,即访问地址为字单元;忽略半字单元最低位的值,即访问地址为半字单元。
***由存储系统忽略字单元地址中低两位的值,半字单元地址最低位的值。

4.指令预取和自修改代码
当用户读取PC计数器的值时,返回的是当前指令下面的第二条指令的地址。对于ARM指令来说,返回当前指令地址值加8个字节;对于Thumb指令来说,返回值为当前指令地址值加4个字节。
自修改代码指的是代码在执行过程中修改自身。应尽量避免使用。
5.存储器映射的I/O空间
在ARM中,I/O操作通常被影射为存储器操作。通常需要将存储器映射的I/O空间设置成非缓冲的。

/b

/b

/b

/b

*************************************************************

ARM编译器支持的数据类型

************************************************************
数据类型 长度(位) 对齐特性
Char 8 1(字节对齐)
short 16 2(百字对齐)
Int 32 4(字对齐)
Long 32 4(字对齐)
Longlong 64 4(字对齐)
Float 32 4(字对齐)
Double 64 4(字对齐)
Long double 64 4(字对齐)
All pointers 32 4(字对齐)
Bool(C++ only) 32 4(字对齐)

1.整数类型
在ARM体系中,整数类型是以2的补码形式存储的。对于long long类型来说,在little endian内存模式下,其低32位保存在低地址的字单元中,高32为保存在高地址的字单元中;在big endian模式下,其低32位保存在高地址的字单元中,高32为保存在低地址的字单元中。对于整型数据的操作遵守下面的规则:
**所有带符号的整型书的运算是按照二进制的补码进行的。
**带符号的整型数的运算不进行符号的扩展。
**带符号的整型数的右移操作是算数移位。
**制定的移位位数的数是8位的无符号数。
**进行移位操作的数被作为32位数。
**超过31位的逻辑左移的结果为0。
**对于无符号数和有符号的正数来说,超过32位的右移操作结果为0;对于有符号的负数来说,超过32位的右移操作结果为-1。
**整数除法运算的余数和除数有相同的符号。
**当把一个整数截断成位数更短的整数类型的数时,并不能保证所得到的结果的最高位的符号位的正确性。
**整型数据之间的类型转换不会产生异常中断。
**整型数据的溢出不会产生异常中断。
**整型数据除以0将会产生异常中断。
2.浮点数
在ARM体系中,浮点数是按照IEEE标准存储的。
**float类型的数是按照IEEE的单精度数表示的。
**double和long double 是用IEEE的双精度数表示的。
对于浮点数的操作遵守下面的规则:
**遵守正常的IEEE754规则。
**当默认情况下禁止浮点数运算异常中断。
**当发生卷绕时,用最接近的数据来表示。
3.指针类型的数据
下面的规则适用于处数据成员指针以外的其他指针:
**NULL被定义为0。
**相邻的两个存储单元地址相差一。
**在指向函数的指针和指向数据的指针进行数据转换时,编译器将会产生警告信息。
**类型size_t被定义为unsigned int.
**类型ptrdiff_t被定义为signed int。
**两个指针类型的数据相减时,结果可以按照下面的公式得到。
((int)a-(int)b)/(int)sizeof(type pointed to)
这时,只要指针所指的对象不是pack的,其对齐特性能够满足整除的要求。

/b

/b

/b

/b

*****************************************************

ARM编译器中预定义的宏

*****************************************************
ARM编译器预定义了一些宏,这些预定义宏对应一定的数值,有些预定义宏没有对应数值,见下表:

_arm _ 使用编译器armcc,tcc,armcpp,tcpp时

_ARMCC_VERSION Ver 代表编译器版本号,其格式为:
PVtbbb,其中:
P为产品编号(1代表ADS)
V为副版本号(1代表1.1)
T为补丁版本号(0代表1.1)
bbb为build号(比如650)

_APCS_INTERWORK _ 使用编译选项-apcs/interwork时

_APCS_ROPI _ 使用编译选项apcs/ropi时

_RWPI _ 使用编译选项-apcs/rwpi时

_APCS_SWST _ 使用编译选项-apcs/swst时

_BIG_ENDIAN _ 编译器针对目标系统使用big-endian内存模式时

_cplusplus _ 编译器工作与C++模式时

_CC_ARM _ 返回编译器的名称

_DATE_ date 编译源文件的日期

_embedded_cplusplus 编译器工作于EC++模式时

_FEATURE_SINGED_CHAE 使用编译设置选项-zc时设置该预定义宏

_FILE_ name 包含全路径的当前被编译的源文件名称

_func_ name 当前被编译的函数名称

_LINE_ num 当前被编译的代码行号名称

_MOUDLE_ mod 预定义宏_FILE_的文件名称部分

_OPTIMISE_SPACE _ 使用编译选项-OSPACE时

_OPTIMISE_TIME _ 使用编译选项-Otime时

_pretty_func name unmangled的当前函数名称

_sizeof_int 4 sizeof(int),在预处理表达式中可以使用

_sizeof_long 4 sizeof(long),在预处理表达式中可以使用

_sizeof_ptr 4 sizeof(void*)在预处理表达式中可以使用

_SOFTFP _ 编译时使用浮点数

_ _ 在各种编译器模式下

_STDC_VERSION _ 标准的版本信息

_STRICT_ANSI_ _ 使用编译选项-STRICT时

_TARGET_ARCH_xx _ xx代表ARM体系编号

_TARGET_CPU_xx _ xx代表CPU编号

_TARGET_FEATURE_ 当ARM体系支持指令PLD,LDRD,STRD,MCRR,MRRC时
DOUBLEWORD _ 设置该定义宏

_TARGET_FEATURE_ 当系统中包含DSP乘法处理器时,设置该
DSPMUL _ 预定义宏

_TARGET_FEATURE_ 如果目标ARM体系支持半字访问以及有符号的字节数据
HALFWORD _ ,设置该预定义宏

_TARGET_FEATURE_ 如果目标ARM体系支持长乘法指令MULL和
MULTIPLY _ MUAL,设置该预定义宏

_TARGET_FEATURE_ 如果目标ARM体系支持THUMB指令
THUMB _

_TARGET_FPU_xx _ 表示FPU选项,可能取值如下所示:
_TARGET_FPU_VFP
_TARGET_FPU_FPA
_TARGET_FPU_SOFTVFP
_TARGET_FPU_SOFTVFP_VFP
_TARGET_FPU_SOFTFPA
_TARGET_FPU_NONE

_thumb _ 编译器为tcc或tcpp时,设置该预定义宏

_TIME 源文件编译时间

/b

/b

/b

*****************************************************

ARM映像文件

*****************************************************

1.ELF格式文件的结构


1.1映像文件组成部分


**一个映像文件有一个或多个域组成
**每个域包含一个或多个输出段
**每个输出段包含一个或多个输入段
**各输入段中包含了目标文件中的代码和数据

输入段中包含了四类内容:代码、已经初始化的数据、未经初始化的存储区域、内容初始化成0的存储区域。每个输入段有相应的属性,可以为只读的(RO)、可读写的(RW)以及初始化成0的(ZI)。ARM连接器根据个输入段的属性将这些输入段分组,再组成不同的输出段及域。
一个输出段中包含了一系列的具有相同的RO、RW和ZI属性的输入段。输出段的属性与其中包含的输入段的属性相同。在一个输出段的内部,各输入段是按照一定的规则排序的,这将在1.3节油详细地介绍。
一个域中包含1-3个输出段,其中个输出段的属性各不相同。各输出段的排列顺序是由其属性决定的。其中RO属性的输出段排在最前面,其次是RW属性的输出段,最后是ZI属性的输出段。一个域通常映射到一个物理存储器上,如ROM或RAM。


1.2ARM映像文件各组成部分的地址影射


ARM映像文件各组成部分在存储系统中的地址有两种:一种是映像文件位于存储器中时(也就是该映像文件运行之前)的地址,称之为加载地址;一种是映像文件运行时的地址,称之为运行时地址。之所以有这两种地址,是因为映像文件在运行时,其中的有些域是可以移动的新的存储区域。比如,已经初始化的RW属性的数据所在的段运行之前可能保存系统的ROM中,在运行时,他被移动至RAM中。
通常,一个映像文件包含若干个域,各域又包含若干的输出段。ARM连接器需要知道如下的信息,已决定如何生成相应的映像文件。
**分组信息 决定如何将个输入段组织成相应的输出段和域。
**定位信息 决定个域在存储空间地址中的起始地址。
根据映像文件中地址映射的复杂程度,有两种方法来告诉arm连接器这些相关信息。对于映像文件中地址映射关系比较简单的情况,可以使用命令行选项;对于映像文件中地址映射关系比较复杂的情况,可以使用一个配置文件。

2.arm映像文件的入口点


2.1arm映像文件的入口点有两种类型:一种是映像文件运行时的入口点,称为初始入口点(initial entry point),另一种是普通入口点(entry point).
初始入口点是映像文件运行时的入口点,每个映像文件只有一个唯一的初始入口点,它保存在ELF头文件中。如果映像文件是被操作系统加载的,操作系统是通过跳转到该初始入口点处来加载该映像文件。
普通的入口点是在汇编中用ENTRY伪操作定义。他通常用于标志该段代码是通过异常中断处理程序进入的。这样连接器删除无用的段时不会将该段代码删除。一个映像文件中可以定义多个普通入口点。
应该注意的是,初始入口点可以使普通入口点,但也可以不是普通入口点。


2.2定义初始入口点


初始入口点必须满足下面两个条件:
**初始入口点必须位于映像文件的运行时域内。
**饱含初始入口点的运行时域不能被覆盖,他的加载地址和运行地址必须是相同的。
可以使用连接选项-entry address来指定映像文件的初始入口点。这时,address指定了映像文件的初始入口点的地址值。
对于地址0x0处为rom的嵌入式应用系统,可以使用-entry 0x0来指定映像文件的初始入口点。这样当系统复位后,自动跳转到该入口开始执行。
如果映像文件是被一个加载器加载的,该映像文件该映像文件必须包含一个初始化入口点。这种映像文件通常还包含了其他普通入口点,这些普通入口点一般为异常中断处理程序的入口地址。
当用户没有指定-entry address时,连接器根据下面的规则决定映像文件的初始入口点。
**如果输入的目标文件中只有一个普通入口点,该普通入口点被连接器当成映像文件的初始入口点。
**如果输入的目标文件中没有一个普通入口点,或者其中的普通入口点多于一个,则连接器生成的映像文件中不包含初始入口点,并产生警告信息。

2.3普通入口点的用法


普通入口点是在汇编中用ENTRY 伪操作定义。在嵌入式应用中,各异常中断的处理程序入口使用普通入口点标示。这样连接器在删除无用段时不会将该段代码删除。
一个映像文件中可以定义多个普通入口点。没有指定连接选项-entry addres时,如果输入的目标文件中只有一个普通入口点,该入口点被连接器当成映像文件的初始入口点。

/b

/b

/b

*****************************************************

ARM映像文件——2

*****************************************************

3输入段的排序规则
连接器根据输入段的属性来组织这些输入段,具有相同属性的输入段被放到域中一段连续的空间中,组成一个输出段。在一个输出段中,各输入段的起始地址与

输出段的起始地址和该输出段中个输入段的排列顺序有关。
通常情况下,一个输出段中个输入段的排列顺序由下面几个因素决定的。用户可以通过连接选项-first和-last来改变这些规则。
**输入段的属性。
**输入段的名称
**各输入段在连接命令行的输入段列表中的排列顺序
按照输入段的属性,其排列顺序如下所示:
**只读的代码段
**只读的数据段
**可读写的代码段
**其他已经初始化的数据段
**未出世化的数据
对于具有相同属性的输入段,按照其名称来排序。这是输入段名称是区分大小写的,按照其ASCII码顺序进行排序。
对于具有相同属性和名城的输入段,按照其在输入段列表中的顺序进行排序。也就是说,几十个输入段的属性和名称保持不变,如果其在编译时,各输入段在输

入段列表中的排列顺序不同,生成的映像文件也将不同。
可以通过连接选项-first和-last来改变这些规则。如果连接时使用了配置文件,可以在配置文件中通过伪属性FIRST和LAST达到相同的效果。
连接选项-first和-last不能改变根据输入段进行排序的规则,它只能改变根据输入段名称和其在输入列表中的顺序的排序规则。也就是说,如果使用-first指定

一个输入段,只有该输入段所在的输出段位于运行时域的开始位置时,该输入段才能位于整个运行时域的开始位置。
各个输入段排好顺序后,在确定各个输入段的起始地址之前,何以通过填充补丁是个输入段满足地址对齐的要求。

 

 

 

这些是很多书中第一章节的内容。都比较基础,用的会比较多。

 

 

 

 

 

第四章            移植μC/OS-

 

§4.1  LPC2106的介绍

ARM7TDMIS是通用的32位微处理器,它具有高性能和低功耗的特性。ARM结构是基于精简指令集计算机(RISC)原理而设计的,指令集和相关的译码机制比复杂指令集计算机要简单得多,这样使用一个小的、廉价的处理器核就可实现很高的指令吞吐量和实时的中断响应。

由于使用了流水线技术,处理和存储系统的所有部分都可连续工作。通常在执行一条指令的同时对下一条指令进行译码,并将第三条指令从存储器中取出。

ARM7TDMIS处理器使用了一个被称为Thumb的独特结构化策略,它非常适用于那些对存储器有限制或者需要较高代码密度的大批量产品的应用。

Thumb后面一个关键的概念是“超精简指令集”。基本上,ARM7TDMIS处理器具有两个指令集:

 ·标准32ARM指令集;

 ·16Thumb指令集。

Thumb指令集的16位指令长度使其可以达到标准ARM代码两倍的密度,却仍然保持ARM的大多数性能上的优势,这些优势是使用16位寄存器的16位处理器所不具备的。因为Thumb代码和ARM代码一样,在相同的32位寄存器上进行操作。

Thumb代码仅为ARM代码规模的65%,但其性能却相当于连接到16位存储器系统的相同ARM处理器性能的160%。

LPC2106带有一个支持实时仿真和跟踪的 ARM7TDMIS CPU,并嵌入了128KB高速Flash存储器。LPC2106 ARM7TDMIS配置为小端(littleendian)字节顺序。128位宽度的存储器接口和独特的加速结构使32位代码能够在最大时钟频率下运行。

LPC2106主要的特征如下:

·128 KB片内 Flash程序存储器,具有ISP和IAP功能;

· Flash编程时间: 1ms可编程512字节,扇区擦除或整片擦除只需400 ms;

·64KB静态 RAM;

·向量中断控制器;

·仿真跟踪模块,支持实时跟踪;

·RealMonitor模块支持实时调试;

·标准 ARM测试/调试接口,兼容现有工具;

·极小封装:TQFP48(7 mm x 7 mm);

·双UART,其中一个带有完全的调制解调器接口;

·I2 C串行接口;

·SPI串行接口;

·两个定时器,分别具有4路捕获/比较通道;

·多达6路输出的PWM单元;

·实时时钟;

·看门狗定时器;

·通用I/O口;

·CPU操作频率可达60 MHZ;

·双电源:

      -CPU操作电压范围:1.65~1.95 V,即 1.8(1±8.3%)V;

      -I/O电压范围:3.0~3.6 V,即 3.3(1±10%)V。

·两个低功耗模式:空闲和掉电;

·通过外部中断将处理器从掉电模式中唤醒;

·外设功能可单独使能/禁止,实现功耗最优化;

·片内晶振的操作频率范围:10~25 MHZ;

·片内PLL允许CPU以最大速度运行,可以在超过整个晶振操作频率范围的情况使用。

 

§4.2  LPC2106的启动代码

启动代码是芯片复位后进入C语言的main()函数前执行的一段代码,主要是为运行C语言程序提供基本运行环境,如初始化存储器系统等。ARM公司只设计内核,不自己生产芯片,只是把内核授权给其它厂商,其它厂商购买了授权且加入自己的外设后生产出各具特色的芯片。这样就促进了基于ARM处理器核的芯片多元化,但也使得每一种芯片的启动代码差别很大,不易编写出统一的启动代码。ADS(针对ARM处理器核的C语言编译器)的策略是不提供完整的启动代码,启动代码不足部分或者由厂商提供,或者自己编写。启动代码划分为4个文件:Vectors.cInit.sTarget.c Target.hVectors.c包含异常向量表、堆栈初始化及中断服务程序与C程序的接口。Init.s包含统初始化代码,并跳转到ADS提供的初始化代码。Target.c Target.h包含目标板特殊的代码,包括异常处理程序和目标板初始化程序。这样做的目的是为了尽量减少汇编代码,同时把不需要修改的代码独立出来以减少错误。

§4.2.1  Vectors.c文件的编写

§4.2.1.1  中断向量表

Vectors

        LDR     PC, ResetAddr

        LDR     PC, UndefinedAddr

        LDR     PC, SWI_Addr

        LDR     PC, PrefetchAddr

        LDR     PC, DataAbortAddr

        DCD     0xb9205f80

        LDR     PC, [PC, #-0xff0]

        LDR     PC, FIQ_Addr

 

ResetAddr           DCD     Reset

UndefinedAddr       DCD     Undefined

SWI_Addr           DCD     SoftwareInterrupt

PrefetchAddr         DCD     PrefetchAbort

DataAbortAddr       DCD     DataAbort

nouse               DCD     0

IRQ_Addr           DCD     IRQ_Handler

FIQ_Addr           DCD     FIQ_Handler

 

异常是由内部或外部源产生的以引起处理器处理的一个事件。ARM处理器核支持7种类型的异常。异常出现后,CPU强制从异常类型对应的固定存储地址开始执行程序。这个固定的地址就是异常向量。向量从上到下依次为复位、未定义指令异常、软件中断、预取指令中止、预取数据中止、保留的异常、IRQ FIQIRQ向量“LDR   PC, [PC, #-0xff0] 使用的指令与其它向量不同。在正常情况下这条指令所在地址为0X00000018。当CPU执行这条指令但还没有跳转时,PC的值为0X000000200X00000020减去 0X00000FF0 0XFFFFF030,这是向量中断控制器(VIC)的特殊寄存器VICVectAddr。这个寄存器保存当前将要服务的IRQ的中断服务程序的入口,用这一条指令就可以直接跳转到需要的中断服务程序中。至于在保留的异常向量“DCD  0xb9205f80”位置填数据0xb9205f8是为了使向量表中所有的数据32位累加和为0

§4.2.1.2  初始化CPU堆栈

 

InitStack   

        MOV     R0, LR  

        MSR     CPSR_c, #0xd2  ;设置中断模式堆栈

        LDR     SP, StackIrq  

        MSR     CPSR_c, #0xd1 ;设置快速中断模式堆栈

        LDR     SP, StackFiq 

        MSR     CPSR_c, #0xd7 ;设置中止模式堆栈

        LDR     SP, StackAbt 

        MSR     CPSR_c, #0xdb ;设置未定义模式堆栈

        LDR     SP, StackUnd 

        MSR     CPSR_c, #0xdf ;设置系统模式堆栈

        LDR     SP, StackSys

        MOV     PC, R0

 

StackIrq   DCD     (IrqStackSpace + IRQ_STACK_LEGTH * 4 - 4)

StackFiq   DCD     (FiqStackSpace + FIQ_STACK_LEGTH * 4 - 4)

StackAbt   DCD     (AbtStackSpace + ABT_STACK_LEGTH * 4 - 4)

StackUnd  DCD     (UndtStackSpace + UND_STACK_LEGTH * 4 - 4)

StackSys   DCD     (SysStackSpace + SYS_STACK_LEGTH * 4 -4 )

;/* 分配堆栈空间 */

        AREA    MyStacks, DATA, NOINIT

IrqStackSpace   SPACE  IRQ_STACK_LEGTH * 4 ;中断模式堆栈FiqStackSpace   SPACE  FIQ_STACK_LEGTH * 4 ;快速中断模式堆栈

AbtStackSpace   SPACE  ABT_STACK_LEGTH * 4 ;中止义模式堆栈UndtStackSpace  SPACE  UND_STACK_LEGTH * 4 ;未定义模式堆栈

SysStackSpace   SPACE  SYS_STACK_LEGTH * 4 ; 系统模式堆栈

 

因为程序需要切换模式,而且程序退出时CPU的模式已经不再是管理模式而是系统模式LR已经不再保存返回程序地址,所以程序首先把返回地址保存到  R0中,同时使用R0返回。然后程序把处理器模式转化为IRQ模式,并设置IRQ模式的堆栈指针。其中变量Stacklrq保存着IRQ模式的堆栈指针的初始值,Irqstackspace是分配给 IRQ模式的堆栈空间的开始地址,IRQ_STACK_LEGTH是用户定义的常量,用于设置 IRQ模式的堆栈空间的大小。程序使用同样的方法设置FIQ模式堆栈指针、中止模式堆栈指针、未定义堆栈指针和系统模式堆栈指针。

程序使用编译器分配的空间作为堆栈,而不是按照通常的做法把堆栈分配到  RAM的顶端,之所以这样是因为这样做不必知道RAM顶端位置,移植更加方便;编译器给出的占用RAM空间的大小就是实际占用的大小,便于控制RAM的分配。对于 LPC2106来说,中止模式是不需要分配堆栈空间的,这是因为 LPC2106没有外部总线,也没有虚拟内存机制,如果出现取数据中止或取指令中止肯定是程序有问题。而一般情况下也不需要模拟协处理器指令或扩充指令,未定义中止也就意味着程序有错误,也不需要分配堆栈空间。

 

§4.2.1.3   异常处理代码与C语言的接口程序

μC/OS-Ⅱ中断服务子程序流程图如图4-1所示:

 

4-1 中断服务子程序流程图

 

 

异常处理代码与C语言的接口程序如下:

 

MACRO

$IRQ_Label HANDLER $IRQ_Exception

        EXPORT  $IRQ_Label                      ;输出的标号

        IMPORT  $IRQ_Exception                  ;引用的外部标号

 

$IRQ_Label

        SUB     LR, LR, #4                       ;计算返回地址

        STMFD   SP!, {R0-R3, R12, LR}            ;保存任务环境

        MRS     R3, SPSR                        ;保存状态

        STMFD   SP!, {R3}

 

        LDR     R2,  =OSIntNesting               OSIntNesting++

        LDRB    R1, [R2]

        ADD     R1, R1, #1

        STRB    R1, [R2]

       

        BL      $IRQ_Exception          ;调用c语言的中断处理程序

 

        MSR    CPSR_c, #0x92            ;关中断

        BL      OSIntExit

 

        LDR     R0, =OSTCBHighRdy

        LDR     R0, [R0]

        LDR     R1, =OSTCBCur

        LDR     R1, [R1]

        CMP     R0, R1

       

        LDMFD   SP!, {R3}

        MSR     SPSR_cxsf, R3

 

        LDMEQFD SP!, {R0-R3, R12, PC}^          ;不进行任务切换

        LDR     PC, =OSIntCtxSw                 ;进行任务切换

    MEND

Undefined                                      ;未定义指令

        b       Undefined

PrefetchAbort                                   ;取指令中止

        b       PrefetchAbort

DataAbort                                      ;取数据中止

        b       DataAbort

IRQ_Handler      HANDLER  IRQ_Exception         ;中断

FIQ_Handler                                    ;快速中断

        b       FIQ_Handler

Timer0_Handler  HANDLER  Timer0              ;定时器0中断

 

未定义指令异常、取指令中止异常、取数据中止异常均是死循环,其中原因在上一小节已经说明。而快速中断在本应用中并未使用,所以也设置为死循环。LPC2106使用向量中断控制器,各个 IRQ中断的人口不一样,所以使用了一个宏来简化中断服务程序与C语言的接口编写。由ARM处理器核的文档可知,处理器进入IRQ中断服务程序时(LR4)的值为中断返回地址,为了使任务无论在主动放弃CPU时还是中断时堆栈结构都一样,在这里先把LR4。其它的部分与μC/OS-Ⅱ要求的基本一致。ARM处理核在进入中断服务程序时处理器模式变为IRQ模式,与任务的模式不同,它们的堆栈指针SP也不一样,而寄存器应当保存到用户的堆栈中,为了减少不必要的CPU时间和RAM空间的浪费,本移植仅在必要时将处理器的寄存器保存到用户的堆栈中,其它时候还是保存到IRQ模式的堆栈中。同时,从编译器的函数调用规范可知,C语言函数返回时,寄存器R4R11SP不会改变,所以只需要保存CPSRR0R3R12和返回地址LR,在后面保存 CPSR是为了必要时将寄存器保存到用户堆栈比较方便。

在异常处理代码与C语言的接口程序中没有与中断服务子程序流程图中的判断语句对应的语句。判断语句是为了避免在函数OSIntCtxsw()调整堆栈指针,这个调整量是与编译器、编译器选项、μC/OS-Ⅱ配置选项都相关的变量。在这里进行这些处理相对其它处理器结构可能增加的处理器时间很少,但对于ARM来说,由于中断(IRQ)有独立的堆栈,在这里这样做就需要把所有寄存器从中断的堆栈拷贝到任务的堆栈,需要花费比较多的额外时间。而变量OSIntNesting为0时,并不一定会进行任务切换,所以本移植没有与之对应的程序,而在函数OSIntCtxsw()中做这一项工作。这样,仅在需要时才处理这些事物,程序效率得以提高。

在中断调用后,如果需要任务切换,则变量OSTCBHighRdy和变量OSTCBCur的值不同;如果不需要任务切换这两个变量则相同。本移植通过判断这两个变量来决定是进行任务切换,还是不进行任务切换。通过比较,如果需要任务切换则执行“LDR  PC, =OSIntCtxSw”跳转到OSIntCtxSw处进行任务切换;如果不需要任务切换则执行“LDMEQFD  SP!, {R0-R3, R12, PC}^”中断返回。

这里需要对“MSR    CPSR_c, #0x92”说明下,这条指令的作用是关IRQ中断。因为中断(IRQ)模式的LR寄存器在处理器响应中断时用于保存中断返回地址,所以在处理器响应中断时中断(IRQ)模式的LR寄存器不能保存有效数据。而BL指令要用LR寄存器保存BL下一条指令的位置,所以在中断(IRQ)模式时,在BL指令之前必须关中断,在保存LR后才能开中断。

 

§4.2.2  Target.c文件的编写

为了使系统基本能够工作,必须在进人 main()函数前对系统进行一些基本的初始化工作,这些工作由函数TargetResetInit()完成。

 

void TargetResetInit(void)

{

    uint32 i

    uint32 *cp1

    uint32 *cp2

    extern void Vectors(void)

 

     /* 拷贝向量表,保证在flashram中程序均可正确运行 */ 

    cp1 = (uint32 *)Vectors

    cp2 = (uint32 *)0x40000000

    for (i = 0; i < 2 * 8; i++)

    {

        *cp2++ = *cp1++

    }

   

 

 

   MEMMAP = 0x2                  

  

PINSEL0 = (PINSEL0 & 0xFFFF0000) | UART0_PCB_PINSEL_CFG | 0x50;

 

  PLLCON = 1                 /* 设置系统各部分时钟 */

    VPBDIV = 0

    PLLCFG =0x23

    PLLFEED = 0xaa

    PLLFEED = 0x55

    while((PLLSTAT & (1 << 10)) = = 0)

    PLLCON = 3

    PLLFEED = 0xaa

    PLLFEED = 0x55

 

    MAMCR = 2        /* 设置存储器加速模块 */

#if Fcclk < 20000000

    MAMTIM = 1

#else

#if Fcclk < 40000000

    MAMTIM = 2

#else

    MAMTIM = 3

#endif

#endif

   

首先向量表拷贝到RAM底部,加上这部分是为了代码无论从Flash基地址开始编译还是从RAM基地址开始编译程序均运行正确。而把RAM底部映射到向量表“MEMMAP = 0x2”也是为了同一个目的。至于复制16个字而不是8个字,是因为后8个字存储跳转的地址是通过 PC指针间接寻址的,它们与对应指令(在向量表中)相对位置是不能变化的。

因为在进入多任务环境前使用了一些外设,部分外设使用了芯片的引脚,而 LPC2106的所有引脚都是多功能的,所以需要设置引脚功能。同时串口也进行了设置。时钟是芯片各部分正常工作的基础,虽然时钟可以在任何时候设置,但为了避免混乱,最好在进入 main()函数前设置。程序首先使能PLL但不连接PLL,然后设置外设时钟(VPB时钟pclk)与系统时钟(cclk)的分频比。接着设置PLL的乘因子和除因子。设置完成后,使用“PLLFEED = 0xaa;  PLLFEED = 0x55;”的访问序列把数据正确写人硬件,并等待PLL跟踪完成。最后,使能PLL,并使PLL联上系统。本应用外接的晶振频率(Fosc)为11.0592MHz,倍增器的值M=4,所以处理器时钟(Fcclk)为44.2368 MHz。为了使电流控制振荡器频率(Fcco)满足156-320MHz,所以分频器的值P=2,使得Fcco= Fcclk×2×P=176.9472 MHz。取VPB分频器的分频值为1/4,所以外设时钟(Fpclk= Fcclk/4=11.0592 MHz,则记数周期为0.09042μs,定时0.2ms,则记数值为2212个,这些时钟的定义都在config.h文件中。

用户程序最终是要在Flash中运行的,而系统复位时Flash是以最低速度运行,这对发挥芯片的性能极其不利。虽然存储器加速模块可以在任何时候设置,但为了避免混乱,最好在进入 main()函数前设置。首先使存储器加速模块全速工作,然后根据系统主时钟利用条件编译将Flash的访问时钟设置到合适的值。

 

§4.2.3  Init.s文件的编写

由于LPC2106微控制器的存储系统比较简单,所以系统初始化代码也比较简单,代码如下:

 

Reset

        BL      InitStack              ;初始化堆栈

        BL      TargetResetInit         ;目标板基本初始化

 B       __main              ;跳转到c语言入口

 

在芯片复位在芯片复位时程序会跳转到标号Reset处,程序首先调用Initstack初始化各种模式的堆栈,然后调用TargetResetlnit对系统进行基本初始化,最后跳转到ADS提供的启动代码__main。_main ADS提供的启动代码起始位置,它初始化库并最终引导CPU进入main函数。

 

 

 

 

 

 

§4.3  移植μC/OS-Ⅱ

4-1  μC/OS-Ⅱ硬件/软件体系结构

 

4-1说明了μC/OS-Ⅱ的结构以及它与硬件的关系,由此可知与处理器相关的代码在OS_CPU.H(包括用#define设置一些常量的值,声明的数据类型和用#define声明的宏),OS_CPU_C.C(用C语言编写的简单函数)和 OS_CPU_A.ASM(编写的汇编语言函数)。下面逐一介绍这3个文件中的关键部分程序段。

 

§4.3.1  OS_CPU.H文件的编写

§4.3.1.1  定义与编译器无关的数据类型

typedef unsigned char BOOLEAN;        /* 布尔变量*/                                                      typedef  unsigned  char  INT8U;            /* 无符号8位整型变量*/     

typedef  signed    char  INT8S;           /* 有符号8位整型变量*/                       

typedef  unsigned short INT16U;           /* 无符号16位整型变量*/

typedef  signed   short INT16S;           /* 有符号16位整型变量 */                     

typedef  unsigned int   INT32U;          /* 无符号32位整型变量*/                       

typedef  signed   int   INT32S;          /* 有符号32位整型变量*/ typedef  float          FP32;       /* 单精度浮点数(32位长度)*/                

typedef  double         FP64;       /* 双精度浮点数(64位长度)*/                

typedef  INT32U         OS_STK;             /* 堆栈是32位宽度*/

 

§4.3.1.2  与ARM7体系结构相关的一些定义

 

#define  OS_CRITICAL_METHOD     3       /* 选择开、关中断的方式 */

 

实现OS_ENTER_CRITICAL()OS_EXIT_CRITICAL()2个宏的定义有3种方法,本移植采用的是第三种方法,具体参见§4.3.2.2小节。

ARM处理器核具有2个指令集,用户任务还可以有2种处理器模式:用户模式和系统模式,组合起来具有4种方式,各种方式对系统资源有不同的访问控制权限。为了使底层接口函数与处理器状态无关,同时使任务调用相应的函数不需要知道函数位置,本移植使用软中断指令SWI作为底层接口,使用不同的功能号区分不同的函数,同时预留挂接μC/OS-Ⅱ系统服务函数的接口。软中断功能号分配如表4-1所列,未列出的为保留。

 

功能号

接口函数

简述

0x00

void OS_TASK_SW(void)

任务级任务切换函数

0x01

_OSStartHighRdy(void)

运行优先级最高的函数

0x02

void OS_ENTER_CRITICAL(void)

开终端

0x03

void OS_EXIT_CRITICAL(void)

关中断

4-1  软中断功能号分配

 

底层接口函数声明如下:

 

  __swi(0x00) void OS_TASK_SW(void);           /*  任务级任务切换函数*/

  __swi(0x01) void _OSStartHighRdy(void);         /*  运行优先级最高的任*/

__swi(0x02) void OS_ENTER_CRITICAL(void);    /*  关中断*/

__swi(0x03) void OS_EXIT_CRITICAL(void);      /*  开中断*/

 

  用软件中断作为操作系统的底层接口就需要在C语言中使用SWI指令。在ADS中,有一个关键字__swi,用它声明一个不存在的函数,调用这个函数就在调用这个函数的地方插入一条SWI指令,并且可以指定功能号。

 

#define OS_STK_GROWTH    1                   /*  堆栈是从上往下/

 

μC/OS-Ⅱ使用结构常量OS_STKG_ROWTH中指定堆栈的生长方式,置 OS_STKG_ROWTH为 0表示堆栈从下往上长;置OS_STKG_ROWTH为1表示堆栈从上往下长。虽然ARM处理器核对于两种方式均支持,但ADS的C语言编译器仅支持一种方式,即从上往下长,并且是必须是满递减堆栈,所以OS_STKG_ROWTH的值为 1。

 

#define     USR32Mode       0x10              /*  用户模式*/

#define     SYS32Mode       0x1f              /*  系统模式*/

#define     NoInt           0x80

#ifndef     USER_USING_MODE

#define     USER_USING_MODE USR32Mode         /*  任务缺省*/

#endif

 

用户任务具有两种处理器模式(用户模式和系统模式),可以使用两种指令集(ARM指令集和THUMB指令集),这些状态均保存在程序状态寄存器CPSR中,而USER_USING_MODE就是定义任务开始执行时默认的处理器模式和使用的指令集。

 

 

 

§4.3.2  OS_CPU_C.C文件的编写

§4.3.2.1  OSTaskStkInt()的编写

   OSTaskCreate()和OSTaskCreateExt()通过调用OSTaskStkInt()来初始

化任务的堆栈结构,因此,堆栈看起来就像刚发生过中断并将所有的寄存器保存到堆栈中的情形一样。

 

  OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata,

OS_STK *ptos, INT16U opt)

{

    OS_STK *stk

opt   = opt                          

stk   = ptos                                     

    *stk = (OS_STK) task                  /*  pc  */

    *--stk = (OS_STK) task                /*  lr  */

    *--stk = 0                            /*  r12  */

    *--stk = 0                            /*  r11  */

    *--stk = 0                            /*  r10  */

    *--stk = 0                            /*  r9   */

    *--stk = 0                            /*  r8   */

    *--stk = 0                            /*  r7   */

    *--stk = 0                            /*  r6   */

    *--stk = 0                            /*  r5   */

    *--stk = 0                            /*  r4   */

    *--stk = 0                            /*  r3   */

    *--stk = 0                            /*  r2   */

    *--stk = 0                            /*  r1   */

    *--stk = (unsigned int) pdata        /*  r0,第一个参数使用r0传递*/ 

    *--stk = (USER_USING_MODE|0x00)/*  spsr,允许IRQ, FIQ 中断   */

*--stk = 0                    /*  关中断计数器OsEnterSum;    */

 

    return (stk)

}

 

4-2所示为本移植堆栈结构,对照4-2很容易理解OSTaskStkInt()的代码,这里就不做过多的说明了。

4-2  任务堆栈结构

 

 

§4.3.2.2   SWI_ Exception的编写

由软中断的汇编接口程序SoftwareInterrupt(具体参见§4.3.3.1小节)可知,在发生软中断时,除了功能号0和1的软中断,其他软中断都要跳转到软件中断的C语言函数SWI_ Exception处,其结构是一个switch语句把各个功能分开,使各个功能相对独立,程序清单如下所示:

 

 void SWI_Exception(int SWI_Num, int *Regs)

{

 OS_TCB   *ptcb;

  switch(SWI_Num)

    {

     //case 0x00: /* 任务切换函数OS_TASK_SW,参考os_cpu_s.s文件 */

     //  break;

     //case 0x01: /* 启动任务函数OSStartHighRdy,参考os_cpu_s.s文件 */

    //   break;

     case 0x02:  /* 关中断函数OS_ENTER_CRITICAL() */

            __asm

            {

                MRS     R0, SPSR

                ORR     R0, R0, #NoInt

                MSR     SPSR_c, R0

            }

            OsEnterSum++;

            break;

        case 0x03:   /* 开中断函数OS_EXIT_CRITICAL()*/

            if (--OsEnterSum == 0)

            {

                __asm

                {

                    MRS     R0, SPSR

                    BIC     R0, R0, #NoInt

                    MSR     SPSR_c, R0

                }

            }

            break;

    }

}

对于任务切换函数OS_TASK_SW和启动任务函数OSStartHighRdyos_cpu_s.s文件中介绍。这里需要对关中断函数和开中断函数作些解释。与所有的实时内核一样,μC/OS-Ⅱ需要先禁止中断再访问代码的临界段,并且在访问完毕后重新允许中断。这就使得μC/OS-Ⅱ能够保护临界段代码免受多任务或中断服务破坏。实现OS_ENTER_CRITICAL()OS_EXIT_CRITICAL()2个宏的定义有3种方法,本移植采用的是第三种方法,即在OS_CPU.H文件中使OS_CRITICAL_MEHTOD等于3,这种方法是利用某些编译器提供的扩展功能,用户可以得到当前处理器状态字的值,并将其保存在C函数的局部变量之中,这个变量可以用于恢复PSW,而本ARM内核关中断和开中断时,是通过改变程序状态寄存器CPSR中的相应控制位实现。由于使用了软件中断,程序状态寄存器 CPSR保存到程序状态保存寄存器 SPSR中,软件中断退出时会将SPSR恢复到CPSR中。所以程序只要改变程序状态保存寄存器SPSR中相应的控制位就可以了。

§4.3.3   OS_CPU_A.ASM文件的编写

§4.3.3.1   SoftwareInterrupt的编写

当发生软件中断时,程序通过异常向量表跳转到软中断的汇编与C接口程序SoftwareInterrupt处,4-3SoftwareInterrupt的流程图。

4-3 软件中断代码的汇编部分流程图

 

SoftwareInterrupt

        LDR     SP, StackSvc            ; 重新设置堆栈指针

        STMFD   SP!, {R0-R3, R12, LR}

        MOV     R1, SP                ; R1指向参数存储位置

        MRS     R3, SPSR

        TST     R3, #T_bit              ; 中断前是否是Thumb状态

        LDRNEH  R0, [LR,#-2]          ; : 取得Thumb状态SWI

        BICNE   R0, R0, #0xff00

        LDREQ   R0, [LR,#-4]          ; : 取得arm状态SWI

        BICEQ   R0, R0, #0xFF000000

                                  ; r0 = SWI号,R1指向参数存储位置

        CMP      R0, #1

        LDRLO   PC, =OSIntCtxSw

        LDREQ   PC, =__OSStartHighRdy ; SWI 0x01为第一次任务切换

        BL       SWI_Exception

        LDMFD   SP!, {R0-R3, R12, PC}^

StackSvc DCD     (SvcStackSpace+SVC_STACK_LENGTH* 4-4)

 

因为执行任务切换时堆栈指针会指向用户的堆栈,这样下一次进入管理模式就会破坏用户堆栈,从而导致程序执行不正确。所以程序在一开始设置堆栈指针。软中断指令使处理器进入管理模式,而用户程序处于系统/用户模式,其它异常也有自己的处理器模式,都有各自的堆栈指针,不会因为给堆栈指针赋值而破坏其它处理器模式的堆栈而影响其它程序的执行。返回的地址已经存储在连接寄存器LR中而不是存储在堆栈中。由于进人管理模式自动关中断,所以这段程序不会被其它程序同时调用,设置的堆栈指针指向的位置肯定是空闲位置,后一次调用不会影响前一次调用。这样就可以保证“LDR  SP, StackSvc”进行正确的堆栈指针设置。

软中断的功能号是包含在SWI指令当中的。程序通过读取该条指令的相应位段获得。由于ARM处理器核具有两个指令集,两个指令集的指令长度不同,SWI指令的功能号的位段也不同,所以程序先判断在进入软中断前处理器是在什么指令集状态,根据指令集状态的不同取出相应的SWI指令的功能号。然后,程序用功能号与1比较,当功能号符号小于1即为 0时,就跳转到任务切换函数OSIntCtxSw处。当功能号等于1时,就跳转到第一次任务切换OSStartHighRdy处。其它部分是给软件中断的C语言处理函数处理。这里有两个参数,第一个就是功能号,存于R0中;第二个是保存参数和返回值的指针,也就是堆栈中存储用户函数R0R4的位置,就是当前堆栈指针的值,它存于R1中。

这里对功能号0说明一下,功能号0跳转到OSIntCtxsw程序段,而这段程序实际是实现了OS_TASK_SW()函数的功能。OS_TASK_SW()是在μC/OS-Ⅱ从低优先级任务切换到最高优先级任务时被调用的,因为ARM处理器核具有两个指令集,在执行Thumb指令的状态时不是所有寄存器都可见(参考ARM的相关资料),而且任务又可能不在特权模式(不能改变CPSR)。为了兼容任意一种模式,本移植使用软中断指令SWI使处理器进入管理模式和ARM指令状态,并使用功能0实现OS_TASK_SW()的功能,即使用SWI 0X00代替 OSTASKSW(),具体实现方法见下一小节。

 

 

 

§4.3.3.2  OSIntCtxSw的编写

  在μC/OS-Ⅱ中,任务切换只是简单的将处理器寄存器保存到将被挂起的任务的堆栈中,并且将更高优先级的任务从堆栈中恢复出来。处于就绪状态的任务的堆栈结构看起来就像刚发生过中断并将所有的寄存器保存到堆栈中的情形一样。换句话说,μC/OS-Ⅱ要运行处于就绪状态的任务必须要做的事就是将所有处理器寄存器从任务堆栈中恢复出来,并且执行中断的返回。

μC/OS-Ⅱ中,用户级任务调度时会调用宏(或者函数)OS_TASK_SW(),它是在μC/OS-Ⅱ从低优先级任务切换到最高优先级任务时被调用的,μC/OS-Ⅱ建议OS_TASK_SW()通过某种途径最终调用函数OSCtxSw()。函数OSCtxSw()是与系统相关的,μC/OS-Ⅱ提供的OSCtxSw()函数原型如下:

 

OSCtxSw()原型的程序清单

void OSCtxSw(void)

{

保存处理器寄存器;

将当前任务的堆栈指针保存到当前任务的OS_TCB中;

OSTCBCur->OSTCBStkPtr = Stack pointer

  调用用户定义的OSTaskSwHook()

  OSTCBCur  = OSTCBHighRdy

  OSPrioCur = OSPrioHighRdy

  得到需要恢复的任务的堆栈指针;

    Stack pointer = OSTCBHighRdy->OSTCBStkPtr

  将所有处理器寄存器从新任务的堆栈中恢复出来;

  执行中断返回指令;

}

 

μC/OS-Ⅱ中,函数OSIntExit( )被用来在ISR使得更高优先级任务处于就绪状态时,执行任务切换功能。中断退出函数通过调OSIntCtxSw()来从ISR中执行切换功能。函数OSIntCtxSw()是与系统相关的,μC/OS-Ⅱ提供的OSIntCtxSw()函数原型如下:

 

OSIntCtxSw( )原型的程序清单

void OSIntCtxSw(void)

 {

    调用用户定义的OSTaskSwHook( )

    OSTCBCur  = OSTCBHighRdy

    OSPrioCur = OSPrioHighRdy

    得到需要恢复的任务的堆栈指针

    堆栈指针 = OSTCBHighRdy->OSTCBStkPtr

    将所有处理器寄存器从新任务的堆栈中恢复出来;

    执行中断返回指令;

 }

 

对比两个函数原型,除OSCtxSw()原型的程序清单比OSIntCtxSw( )原型的程序清单多了两句外,其它都是一样的。由异常处理代码与C语言的接口程序可知,OS_TASK_SW()实质是软件中断的功能号0,在软件中断中已经把除变量OsEntersum外的所有寄存器保存到管理模式的堆栈中。而由程序可知,当程序执行函数 OSIntCtxsw()时,变量 OsEnterSum也没有保存到 IRQ模式的堆栈中。也就是说,两种情况需要做的工作一样,都可用同一段代码实现。

 

OSIntCtxSw

                                    ;下面为保存任务环境

        LDR     R2, [SP, #20]                  ;获取PC   (1)

        LDR     R12, [SP, #16]                 ;获取R12  (2)

        MRS     R0, CPSR                               (3)

 

        MSR     CPSR_c, #(NoInt | SYS32Mode)            (4)

        MOV     R1, LR                                (5)

        STMFD   SP!, {R1-R2}                  ;保存LR,PC (6)

        STMFD   SP!, {R4-R12}                 ;保存R4-R12 (7)

 

        MSR     CPSR_c, R0                               (8)

        LDMFD   SP!, {R4-R7}                  ;获取R0-R3 (9)

        ADD     SP, SP, #8                     ;出栈R12,PC (10)

       

        MSR     CPSR_c, #(NoInt | SYS32Mode)              (11)

        STMFD   SP!, {R4-R7}                   ;保存R0-R3 (12)

        LDR     R1, =OsEnterSum            ;获取OsEnterSum (13)

        LDR     R2, [R1]                                   (14)

        STMFD   SP!, {R2, R3}         ;保存CPSR,OsEnterSum (15)

 

                              ;保存当前任务堆栈指针到当前任务的TCB

        LDR     R1, =OSTCBCur                            (16)

        LDR     R1, [R1]                                   (17)

        STR     SP, [R1]                                    (18)

 

        BL      OSTaskSwHook                ;调用子函数   (19)

                                        

        LDR     R4, =OSPrioCur                          (20)

        LDR     R5, =OSPrioHighRdy                     (21)

        LDRB    R6, [R5]                                (22)

        STRB    R6, [R4]                                (23)

                                          

        LDR     R6, =OSTCBHighRdy                     (24)

        LDR     R6, [R6]                                (25)

        LDR     R4, =OSTCBCur                         (26)

        STR     R6, [R4]                                 (27)

OSIntCtxSw_1                                     

                                                ;获取新任务堆栈指针

        LDR     R4, [R6]                                (28)

        ADD     SP, R4, #68                             (29)

        LDR     LR, [SP, #-8]                            (30)

        MSR     CPSR_c, #(NoInt | SVC32Mode) ;进入管理模式(31)

        MOV     SP, R4                     ;设置堆栈指针(32)

 

        LDMFD SP!, {R4, R5}        ;CPSR,OsEnterSum    (33)

                                   ;恢复新任务的OsEnterSum

        LDR     R3, =OsEnterSum                       (34)

        STR     R4, [R3]                               (35)

        MSR     SPSR_cxsf, R5              ;恢复CPSR (36)

        LDMFD   SP!, {R0-R12, LR, PC }^     ;运行新任务(37)

 

这部分代码基本按照μC/OS-Ⅱ提供的函数原型编写的,其中程序清单(1)—(18)部分与OSCtxSw()OSIntCtxSw( )的原型是没有对应语句的,寄存器应当保存到任务的堆栈中,但为了节省CPU的时间和RAM的空间,仅在必要的时候才将寄存器保存到任务堆栈。OSTCBCur->OSTCBStkPtr=SP也是在必要的时候才执行的。这部分正是在处理这两件事情,其流程图见图4-4

4-4  OSIntCtxSw部分代码流程图

 

由软中断的汇编与C接口程序可知,在调用OS_TASK_SW( )(即软件中断的功能号0)时,寄存器R0R3R12PC已经保存到当前模式的堆栈中,任务的R4R11SPLR没有发生变化。也就是说寄存器已经保存,但不是保存到任务的堆栈中。同样由异常处理代码与C语言的接口程序可知,在调用OSIntCtxSw( )时,寄存器R0R3R12PC已经保存到当前模式的堆栈中,任务的R4R11SPLR没有发生变化。也就是说寄存器已经保存,但不是保存到任务的堆栈中。当前处理器模式的堆栈结构如图4-5所示。

4-5  当前处理器模式堆栈结构图

在执行(1)—(18)这部分程序时的寄存器和存储器之间的具体关系如图4-6所示,图中的标号为对应的程序段,这里是以调用OS_TASK_SW( )为例子来说明,调用OSIntCtxSw( )的工作过程和调用OS_TASK_SW( ) 的工作过程是一样的,只不过调用OS_TASK_SW( )时处理器当前模式为管理模式,而调用OSIntCtxSw( )时处理器当前模式为IRQ模式。

4-6-1

此时处理器处于管理模式,因为本移植是使用软中断指令SWI使处理器进入管理模式和ARM指令状态,并使用功能0来实现OS_TASK_SW( )的功能。具体参见软中断的汇编与C接口程序代码。

4-6-2

此时是通过执行程序段(4),利用MSR指令直接设置状态寄存器CPSR的模式位,使处理器进入用户/系统模式此时需要注意的是处理器的可见寄存器与管理模式时是有区别的。在保存之后同样是通过执行程序段(8),利用MSR指令返回到管理模式。

4-6-3

进入管理模式以后继续保存数据,当数据保存好以后,要注意调整当前模式堆栈指针因为当前模式的入栈比出栈的数据多,而在堆栈中剩余的数据(R12PC)已经没有用处,所以要进行调整。完成指针调整的为程序段(10)。

4-6-4

  

此时又使处理器进入用户/系统模式,继续把未保存的寄存器内容保存到任务堆栈,这里要注意的是R3保存着任务的CPSR(既当前模式的SPSR),这部分在异常处理代码与C语言的接口程序中完成的。至此,寄存器内容已经完全保存到任务堆栈里了。

从标号OSIntCtxSw_1处开始至程序的最后,是将所有处理器寄存器从新任务的堆栈中恢复出来。处理器执行这段代码时处于ARM状态,但用户任务可能处于Thumb状态。而由ARM的相关资料可知,程序不可以直接改变程序状态寄存器的CPSRT位来改变处理器的状态。但“LDMFD   SP!, {R0-R12, LR, PC }^ 异常返回指令是可以使用的(具体参见ARM的多寄存器加载指令),所以在恢复寄存器时,必须使处理器处于某种异常模式。由于FIQ模式和IRQ模式对应中断,SP指针不能随意改变,而未定义模式和中止模式以后可能会用到,所以本移植选择为管理模式。

在执行(28)—(37)这部分程序时的寄存器和存储器之间的具体关系如图4-7所示,图中的标号为对应的程序段。

4-7-1

由于 OSTCBHirtRdy->OSTCBStkPtr保存的是任务堆栈位置,而寄存器恢复后堆栈指针并不指向这里,所以要调整新任务堆栈指针,这是由程序段(29)完成的。

4-7-2

然后通过执行程序段(31),利用MSR指令直接设置状态寄存器CPSR的模式位,使处理器进入管理模式,先恢复新任务OsEntersum,这是由程序段(34)(35)完成的,然后把新任务的 CPSR恢复到 SPSR这是由程序段(36)实现的。

4-7-3

最后通过中断返回指令恢复R0R12,把SPSR拷贝到CPSR(恢复用户任务的处理器模式和指令集)和执行用户任务(恢复PC指针),这是由程序段(37)实现的。这里需要注意的是程序段(29)和(32)中的SP是不同的处理器寄存器分别为R13R13_SVC

 

§4.3.3.3   OSStartHighRdy的编写

μC/OS-Ⅱ启动多任务环境的函数叫做OSStart()。用户在调用OSStart()之前,必须已经建立了一个或更多任务。OSStart()最终调用OSStartHighRdy()函数运行多任务启动前优先级最高的任务。由软中断的汇编与C接口程序SoftwareInterrupt可知,这是调用软中断的1号功能。这是因为ARM处理器核具有两个指令集,在执行Thumb指令的状态时不是所有寄存器都可见(参考ARM的相关资料),而且任务可能不在特权模式。为了兼容任意一种模式,本移植使用软中断指令SWI使处理器进入管理模式和ARM指令状态,并使用功能1实现OSStartHighRdy的功能。μC/OS-Ⅱ中调用函数 OSStartHighRdy()之前,OSTCBHighRdy指向的是优先级最高的任务的任务控制块。μC/OS-Ⅱ要求处于就绪状态的任务的堆栈结构看起来就像刚发生过中断,并将所有寄存器保存到堆栈中的情形一样。要想运行最高优先级任务,用户要做的是将所有处理器寄存器按顺序从任务堆栈中恢复出来,并且执行中断的返回。μC/OS-Ⅱ提供的OSStartHighRdy函数原型如下:

 

OSStartHighRdy原型的程序清单

void OSStartHighRdy (void)

{

调用用户函数OSTaskSwHook()

OSRunning=True;

获取堆栈指针SP=OSTCBHighRdy->OSTCBStkPtr

  从新任务堆栈中恢复所有寄存器;

  执行中断返回指令;

}

OSStartHighRdy的程序代码如下:

OSStartHighRdy

        MSR     CPSR_c, #(NoInt | SYS32Mode)

                                        ;告诉uC/OS-II自身已经运行

        LDR     R4, =OSRunning

        MOV     R5, #1

        STRB    R5, [R4]

 

        BL      OSTaskSwHook                    ;调用子函数

 

        LDR     R6, =OSTCBHighRdy

        LDR     R6, [R6]

        B       OSIntCtxSw_1

这部分代码是比较严格的按照μC/OS-提供的原型编写的,其中OSIntCtxSw_1的代码在上一小节已经详细介绍过。

这里需要对“MSR  CPSR_c,  #(NoInt | SYS32Mode)”作以说明,在μC/OS-Ⅱ中,需要用户提供周期性信号源,用于实现时间延时和确认超时。必须在多任务系统启动以后再开启时钟节拍器,也就是在调用OSStart()之后。换句话说,在调用OSStart()之后做的第一件事是初始化定时器中断。通常,容易犯的错误是将允许时钟节拍器中断放在系统初始化函数OSInit()之后,在调启动多任务系统启动函数OSStart()之前允许时钟节拍中断。μC/OS-Ⅱ的启动多任务函数OSStart()会在最后调用OSStartHighRdy,而SStartHighRdy的第一条语句就允许中断。这里是将CPSR的第8位置1,允许IRQ中断。

至此已经将μC/OS-Ⅱ移植到了LPC2106,下面可以进行瞬时电压有效值检测的程序的编写。

       

 

 

 

 

   ARM处理器中主要有7个异常(2个中断异常):

      1、复位异常;在以ARM为核的单片机中,常把下列事件作为引起复位的原因。

      • 上电复位:在上电后,复位使内部达到预定的状态,特别是程序跳到初始入口;

      • 复位引脚上的复位脉冲:这是由外部其他控制信号引起的;

      • 对系统电源检测发现过压或欠压;

      • 时钟异常复位。

    ARM处理器复位后,处理器硬件将进行以下操作:

      • 强制进入管理模式;

      • 强制进入ARM状态;

      • 跳转到绝对地址PC=0x00000000处执行;

      • 禁止IRQ中断和FIQ中断。

      复位后,程序
状态寄存器如下:        

......
I
F
T
M4
M3
M2
M1
M0
 
1
1
0
1
0
0
1
1

        上电复位后,进入管理模式,执行操作系统程序,一般用做对系统初始化,例如开中断等;然后切换到用户模式,开始执行正常的用户程序。

        切换到用户模式可使用下列程序:

        MRS R0,CPSR     ;读状态寄存器
        BIC R0,R0,#03   ;把末两位
清0
        MSR CPRS_c,R0   ;把修改后的值加载给状态寄存器,切换结束
        ......          ;用户程序

    2、未定义指令异常;由于ARM使用32位代码,包含的信息量很大,可达2的31方(4G)。ARM指令集不能用尽所有代码。当ARM处理器遇到无法译码的指令时就会发

生未定义指令异常。进入中断处理程序。

    ARM的未定义指令异常有以下两种情况:

    (1)遇到一条无法执行的指令,此指令没有定义;

    (2)执行一条对协处理器的操作指令,在正常情况下,协处理器应该应答,但协处理器没有应答。

    未定义异常中断时,状态寄存器中的F位不变。使用下列指令退出异常中断,返回原程序 。

    • 把下一条指令的地址拷贝给LR;

    • 把程序状态寄存器CPSR拷贝给SPSR_und;

    • 强制进入未定义模式;

    • 强制进入到ARM模式;

    • 跳转到绝对地址PC=0x00000004处执行;

    • 禁止IRQ中断。

    进入中断后,程序状态寄存器如下:

......
I
F
T
M4
M3
M2
M1
M0
 
1
x
0
1
1
0
1
1

     MOVS PC,R14.

     3、软件中断异常;是由指令SWI引起的。程序在执行这一指令后,进入异常中断。处理器响应中断,硬件执行如下的操作。

     • 把下一条指令的地址拷贝给LR;

     • 把程序状态寄存器CPSR拷贝给SPSR_svc;

     • 强制进入管理模式;

     • 强制进入到ARM状态;

     • 跳转到绝对地址PC=0x00000008处执行;

     • 禁止IRQ中断。

     进入中断后的程序状态寄存器如下:

......
I
F
T
M4
M3
M2
M1
M0
 
1
x
0
1
0
0
1
1

     软件中断处理程序完成后,使用下列指令返回到原中断处:MOVS PC,R14.

     4、预取指中止异常;由程序存储器引起的中止异常叫做预取指中止异常;由数据存储器引起的中止异常叫做数据中止异常。由于ARM的指令是3级流水线结构,读

取指令周期是提前进行的,因此把读取指令的过程一般称预取指。如果在取得指令的同时程序存储器发出中止信号,则ARM处理器把这一条指令标记位无效,然后等待执

行。有两种可能如下:

    • 当执行这条指令前程序发生跳转,则这条无效指令不引起异常中断;

    • 当执行到这条指令时,处理器会发生预取指中止异常,引起中断。

    当记进入预取指异常中断时,处理器硬件响应中断,执行以下的操作:

    • 把中断时PC的地址拷贝给LR;

    • 把程序状态寄存器CPSR拷贝给SPSR_abt;

    • 强制进入中止异常模式;

    • 强制进入到ARM状态;

    • 跳转到绝对地址PC=0x0000000C处执行;

    • 禁止IRQ中断。

    进入中断后,程序状态寄存器如下:

......
I
F
T
M4
M3
M2
M1
M0
 
1
x
0
1
0
1
1
1

    预取指中止异常中断返回时,应该执行下列指令:SUBS PC,R14,#4.

    5、数据中止异常;ARM处理器访问数据存储器时,在读取数据的同时数据存储器发出了中止信号,引起数据中止异常。

    数据中止异常中断服务程序返回时,使用下列指令:

    SUBS PC,R14,#8

    上述指令是返回到中断时所执行的指令,目的是再一次从数据存储器中读取数据。如果不再一次读取数据,则执行下一条指令,此时使用下列指令返回:

    SUBS PC,R14,#4

    • 把中断时的PC的地址拷贝给LR;

    • 把程序状态寄存器CPSR拷贝给SPSR_abt;

    • 强制进入中止异常模式;

    • 强制进入到ARM状态;

    • 跳转到绝对地址PC=0x00000010处执行;

    • 禁止IRQ中断。

    进入中断后,程序状态寄存器如下:

......
I
F
T
M4
M3
M2
M1
M0
 
1
x
0
1
0
1
1
1

     6、中断请求(IRQ)异常;例如:定时器中断、串行口通讯中断、外部信号中断和A/D处理中断等。IRQ中断是可屏蔽的。在状态寄存器中的I位就是IRQ的屏蔽位

。当I=1时。则屏蔽IRQ中断,当I=0时,则允许中断。处理器复位后置I为1,关闭中断。

     当发生IRQ中断时,处理器硬件响应中断,执行下列操作:

     • 把中断时的PC的地址值拷贝给LR;

     • 把程序状态寄存器CPSR拷贝给SPSR_irq;

     • 强制进入IRQ异常模式;

     • 强制进入到ARM状态;

     • 跳转到绝对地址PC=0x00000018处执行;

     • 禁止IRQ中断。

     进入中断后,程序状态寄存器如下:

......
I
F
T
M4
M3
M2
M1
M0
 
1
x
0
1
0
0
1
0

    完成中断处理后,程序执行下列返回原中断处:SUBS PC.R14,#4.

    7、快速中断(FIQ)请求异常;FIQ快速中断是可屏蔽的。在状态寄存器中的F位就是FIQ的屏蔽位。当F=1时。则屏蔽FIQ中断,当F=0时,则允许中断。处理器复

位后置F为1,关闭中断。
   
    当发生IRQ中断时,处理器硬件响应中断,执行下列操作:

     • 把中断时的PC的地址值拷贝给LR;

     • 把程序状态寄存器CPSR拷贝给SPSR_fiq;

     • 强制进入FIQ异常模式;

     • 强制进入到ARM状态;

     • 跳转到绝对地址PC=0x0000001C处执行;

     • 禁止FIQ中断。

     进入中断后,程序状态寄存器如下:

......
I
F
T
M4
M3
M2
M1
M0
 
1
x
0
1
0
0
0
1

    完成中断处理后,程序执行下列返回原中断处:SUBS PC.R14,#4

你可能感兴趣的:(C51,AVR;PIC;,ARM,;,MIPS)