ARM CORTEX A9 微处理器 体系结构(二)寄存器组织、异常中断
ARM处理器共有37个寄存器,被分为若干个组(BANK),这些寄器包括:
● 31个通用寄存器,包括程序计数器(PC指针),均为32位的寄存器。
● 6个状态寄存器,用以标识CPU的工作状态及程序的运行状态,均为32位,目前只使用了其中的一部分。
这些寄存器不能被同时访问,但是在任何时候,通用寄存器R0-R14,程序计数器PC,一个或者两个状态寄存器是可以访问的
1 ARM下寄存器组织
通用寄存器R0-R14,程序计数器PC,一个或者两个状态寄存器
加起来8+52+2+25+1=31个通用
对于CORTEX -A
还有MON HYP
IRQ和SVC用的R14 R13 一样
banked 分组寄存器,灰色的表示跟用户模式一样的地址
1.1 通用寄存器
- 1、不分组寄存器(R0-R7)
- 2、分组寄存器(R8-R14)
- 程序计数器R15(PC)
(1)不分组通用寄存器 R0-R7
R0-R7是不分组寄存器。真正的通用。这意味着在所有处理器模式下,访问的都是同一个物理寄存器。不分组寄存器没有被系统用于特别的用途,任何可采用通用寄存器的应用场合都可以使用未分组寄存器。
因此在中断或者异常处理进行模式转换时,由于不同的处理模式受用相同的物理寄存器,可能会造成寄存器中数据的破坏
(2)分组通用寄存器 R8-R14
就是根据工作模式进行分组,到不同的地址
访问时需要指定它们的模式,
如:R13_<mode>,R14_<mode>;其中:<mode>可以从usr、svc、abt、und、irq和fiq六种模式中选取一个。
分组寄存器R8-R12
寄存器R8~Rl2可分为两组物理寄存器。
一组用于FIQ模式,另一组用于除FIQ以外的其他模式。
1、第1组 FIQ模式分组寄存器R8-R12 。访问R8_fiq~R12_fiq,允许快速中断处理。为FIQ的快提供条件
2、第二组 FIQ以外的分组寄存器R8-R12。访问R8_usr~R12_usr,寄存器R8~R12没有任何指定的特殊用途。
分组寄存器R13、R14
可分为6个分组的物理寄存器。1个用于用户模式和系统模式,而其他5个分别用于svc、abt、und、irq和fiq五种异常模式。
1、寄存器R13通常做堆栈指针SP
通常R13应当被初始化成指向异常模式分配的堆栈。
在入口处,异常处理程序将用到的其他寄存器的值保存到堆栈中;
返回时,重新将这些值加载到寄存器。
这种异常处理方法保证了异常出现后不会导致执行程序的状态不可靠。
在其他情况下,将R13当做通用寄存器。
2、寄存器R14
链接寄存器(Link Register-LR),也称为LR,
2个功能
-
指向子程序的返回地址。
step1 程序由1->2,跳转到标号Label BL Label下一条指令地址 存入 LR (R14) step2 程序2执行完 ,MOV PC LR,把LR的内容放到PC,返回程序1
当中断或异常出现时,或当中断或异常程序执行BL指令时,
相应的分组寄存器R14_svc、R14_irq、R14_fiq、R14_abt和R14_und
用来保存R15的返回值。
在其他情况下,将R14当做通用寄存器。
总结上述
User、Sys、IRQ、Supervisor、Abort和Undefined模式每一种都包含两个分组的寄存器R13和R14的映射,允许每种模式都有自己的堆栈和链接寄存器。
FIQ模式有7个分组的寄存器R8~R14,映射为R8_fiq~R14_fiq。
(3)程序计数器 R15
寄存器R15被用作程序计数器,也称为PC。
其值等于正在取指的指令地址,即为当前正在执行的指令的地址+偏移量8(因为在取地址和执行之间多了一个译码的阶段)。不同的arm流水线,偏移量不同。
访问的都是同一个物理寄存器
① 读程序计数器。指令读出的R15的值是指令地址加上8字节。由于ARM指令始终是字对齐的,所以读出结果值的位[1:0]总是0(在Thumb状态下,情况有所变化)。
② 写程序计数器。写R15的通常结果是将写到R15中的值作为指令地址,并以此地址发生转移。
同理,由于ARM指令要求字对齐,通常希望写到R15中值的位[1:0]=0b00。
由于ARM体系结构采用了多级流水线技术,对于ARM指令集而言,PC总是指向当前指令的下两条指令的地址,即PC的值为当前指令的地址值加8个字节。
1.2 状态寄存器
- CPSR
- SPSR_svc
- SPSR_abt
- SPSR_und
- SPSR_irq
- SPSR_fiq
CPSR
ARM所有工作模式下都可以访问程序状态寄存器CPSR。CPSR包含
- 条件码标志、
- Application Program Status Register (APSR) 标志
- 中断禁止位、
- 当前处理器模式
- 处理器工作状态 ARM, Thumb, ThumbEE, or Jazelle.
- 字节顺序
- 其他状态和控制信息。
1)条件码标志位(Condition Code Flags)
它们的内容可被算术或逻辑运算的结果所改变,并且可以决定某条指令是否被执行。
在ARM状态下,绝大多数的指令都是有条件执行的。
通常条件码标志通过执行比较指令(CMN、CMP、TEQ、TST)、一些算术运算、逻辑运算和传送指令进行修改。
条件码标志的通常含义如下:
-
N:如果结果是带符号二进制补码,那么,若结果为负数,则N=1;若结果为正数或0,则N=0。
-
Z:若指令的结果为0,则置1(通常表示比较的结果为“相等”),否则置0。
-
C:可用如下4种方法之一设置:
一-加法(包括比较指令CMN)。 若加法产生进位(即无符号溢出), 则C置1; 否则置0。 一-减法(包括比较指令CMP)。 若减法产生借位(即无符号溢出), 则C置0; 否则置1。 一-对于结合移位操作的非加法/减法指令, C置为移出值的最后1位。 一-对于其他非加法/减法指令, C通常不改变。
V:可用如下两种方法设置,即
一-对于加法或减法指令,
当发生带符号溢出时,
V置1,认为操作数和结果是补码形式的带符号整数。
一-对于非加法/减法指令,
V通常不改变。
C无符号数进位、V有符号数溢出
8bits 0~255 /-128~127
2)控制位
J、I、F、T A IT GE和M[4:0]用作控制位。当异常出现时改变控制位。处理器在特权模式下时也可由软件改变。
a.中断禁止位
I:置1,则禁止IRQ中断;
F:置1,则禁止FIQ中断。
CPSR在每个异常模式下都有一个对应的物理寄存器——程序状态保存寄存器SPSR。当异常出现时,SPSR用于保存CPSR的值,以便异常返回后恢复异常发生时的工作状态。
Thumb寄存器映射
2 异常
2.1 定义
处理器正常执行的程序流 因为意外事件被暂时中止,并且使处理器*进入一个由有特权的特殊指令执行的事件
2.2 分类
(1)按照是否同步
-
同步异常
由内部事件引起 -
异步异常
外部事件,与处理器指令执行不相关的事件,通常与硬件信号相关,又称中断
包括
(2)按照原因
可以分为4大类
interrupt
trap
通常在用户程序和内核之间,提供系统调用
故障
由故障引起,调用故障处理程序
终止
硬件错误,DRAM SRAM位损坏
2.3 ARM 异常
共有5类异常模式,7种异常类型
多个异常,按优先级顺序处理
处理异常之前需要保留处理器状态(哪些)
分类
优先级
进入模式、地址(异常向量)
当异常发生时,处理器会把PC设置为一个固定存储器地址。这些固定的地址就是异常向量。这一地址放在被称为向量表(vector table)的特定地址范围内。向量表的入口是一些跳转指令,跳转到专门处理某个异常或中断的子程序。
arm 启动代码
异常的响应过程
CPSR PC只有一个,SPSR\LR每个异常状态都有
当异常发生时,分组寄存器r14和SPSR用于保存处理器状态CPSR
R14_<exception_mode> = return link
SPSR_<exception_mode> = CPSR
CPSR[4∶0] = exception mode number
CPSR[5] = 0 /*进入ARM状态*/
If <exception_mode> = = reset or FIQ then
CPSR[6] = 1 /*屏蔽快速中断FIQ*/
CPSR[7] = 1 /*屏蔽外部中断IRQ*/
PC = exception vector address
异常返回时,SPSR内容恢复到CPSR,连接寄存器r14的内容恢复到程序计数器PC。
异常进入
当一个异常出现以后,ARM微处理器会执行以下几步操作:
①将下一条指令的地址存入相应异常类型的连接寄存器LR,以便程序在处理异常返回时能从正确的位置重新开始执行。
Q :是怎么知道哪个异常的
若异常是从ARM状态进入,LR寄存器中保存的是下一条指令的地址(当前PC+4(Thumb)或PC+8(ARM),与异常的类型有关);
若异常是从Thumb状态进入,则在LR寄存器中保存当前PC的偏移量。
② 将CPSR状态传送到相应异常类型的SPSR中。
③ 根据异常类型,强制设置CPSR的运行模式位。
④ 强制PC从相关的异常向量地址取下一条指令执行,跳转到相应的异常处理程序。
注:
- 还可以设置中断禁止位,以禁止中断嵌套发生。
- 如果异常发生时,处理器处于Thumb状态,则当异常向量地址加载入PC时,处理器自动切换到ARM状态。
eg
异常返回
异常处理完毕之后,ARM微处理器会执行以下几步操作从异常返回:
① 将连接寄存器LR的值减去相应的偏移量(为返回地址)后送到PC中。
② 将SPSR内容送回CPSR中。
③ 若在进入异常处理时设置了中断禁止位,要在此清除CPSR的中断设置位。
可以认为应用程序总是从复位异常处理程序开始执行的,因此复位异常处理程序不需要返回。
eg
指令
应用程序中的异常处理
在应用程序的设计中,异常处理采用的方式是在异常向量表中的特定位置放置一条跳转指令,跳转到异常处理程序。当ARM处理器发生异常时,程序计数器PC会被强制设置为对应的异常向量,从而跳转到异常处理程序,当异常处理完成以后,返回到主程序继续执行。
含义
1)复位
当处理器的复位电平有效时,产生复位异常,ARM处理器立刻停止执行当前指令。复位后,ARM处理器在禁止中断的管理模式下,程序跳转到复位异常处理程序处执行(从地址0x00000000或0xFFFF0000开始执行指令)。
复位异常处理特殊对待
主要功能
设置异常中断向量表。
初始化数据栈和寄存器。
初始化存储系统,如系统中的MMU等。
初始化关键的I/O设备。
使能中断。
处理器切换到合适的模式。
初始化C变量,跳转到应用程序执行。
R14_svc
R14_svc = UNPREDICTABLE value
SPSR_svc = UNPREDICTABLE value
CPSR[4∶0] = 0b10011 /*进入特权模式*/
CPSR[5] = 0 /*处理器进入ARM状态*/
CPSR[6] = 1 /*禁止快速中断*/
CPSR[7] = 1 /*禁止外设中断*/
If high vectors configured then
PC = 0xffff0000
Else
PC = 0x00000000
2)未定义指令异常
当ARM处理器或协处理器遇到不能处理的指令时,产生未定义指令异常。
当ARM处理器执行协处理器指令时,它必须等待任一外部协处理器应答后,才能真正执行这条指令。若协处理器没有响应,就会出现未定义指令异常。若试图执行未定义的指令,也会出现未定义指令异常。未定义指令异常可用于在没有物理协处理器(硬件)的系统上,对协处理器进行软件仿真,或在软件仿真时进行指令扩展。
例如,在一个不包含浮点运算的系统中,CPU遇到浮点运算指令时,将发生未定义指令异常中断,在该未定义指令异常中断的处理程序中可以通过其他指令序列仿真浮点运算指令。
r14_und
r14_und = address of next instruction after the undefined instruction
SPSR_und = CPSR
CPSR[4∶0] = 0b11011 /*进入未定义指令模式*/
CPSR[5] = 0 /*处理器进入ARM状态*/
/*CPSR[6]保持不变*/
CPSR[7] = 1 /*禁止外设中断*/
If high vectors configured then
PC = 0xffff0004
Else
PC = 0x00000004
返回
3)软件中断异常(SoftWare Interrupt,SWI)
软件中断异常由执行SWI指令产生,可使用该异常机制实现系统功能调用,用于用户模式下的程序调用特权操作指令,以请求特定的管理(操作系统)函数。
r14_svc\ SPSR_svc
r14_svc = address of next instruction after the SWI instruction
SPSR_svc = CPSR
CPSR[4∶0] = 0b10011 /*进入特权模式*/
CPSR[5] = 0 /*处理器进入ARM状态*/
/*CPSR[6]保持不变*/
CPSR[7] = 1 /*禁止外设中断*/
If high vectors configured then
PC = 0xffff0008
Else
PC = 0x00000008
4)指令预取中止
是由系统存储器报告,若处理器预取指令的地址不存在,或该地址不允许当前指令访问,存储器会向处理器发出存储器中止(Abort)信号,但当预取的指令被执行时,才会产生指令预取中止异常。
如果系统中不包含MMU,指令预取异常中断处理程序只是简单地报告错误并退出;
若包含MMU,引起异常的指令的物理地址被存储到内存中。
r14_abt = address of the aborted instruction + 4
SPSR_abt = CPSR
CPSR[4∶0] = 0b10111 /*进入特权模式*/
CPSR[5] = 0 /*处理器进入ARM状态*/
/*CPSR[6]保持不变*/
CPSR[7] = 1 /*禁止外设中断*/
If high vectors configured then
PC = 0xffff000C
Else
PC = 0x0000000C
5)数据中止(数据访问存储器中止)
数据异常时由存储器发出数据中止信号,它由存储器访问指令Load/Store产生。
若处理器数据访问指令的地址不存在,或该地址不允许当前指令访问时,产生数据中止异常。
存储器系统发出存储器中止信号。响应数据访问(加载或存储)**中止,标记数据为无效。在后面的任何指令或异常改变CPU状态之前,数据中止异常发生。
r14_abt
r14_abt = address of the aborted instruction + 8
SPSR_abt = CPSR
CPSR[4∶0] = 0b10111
CPSR[5] = 0
/*CPSR[6]保持不变*/
CPSR[7] = 1 /*禁止外设中断*/
If high vectors configured then
PC = 0xffff000C10
Else
PC = 0x00000010
6)外部中断请求(IRQ)异常
当处理器的外部中断请求引脚有效,且CPSR中的I位为0时,产生IRQ异常。
系统的外设可通过该异常请求中断服务。IRQ异常的优先级比FIQ异常的低。当进入FIQ处理时,会屏蔽掉IRQ异常。
r14_irq
r14_irq = address of next instruction to be executed + 4
SPSR_irq = CPSR
CPSR[4∶0] = 0b10010 /*进入特权模式*/
CPSR[5] = 0 /*处理器进入ARM状态*/
/*CPSR[6]保持不变*/
CPSR[7] = 1 /*禁止外设中断*/
If high vectors configured then
PC = 0xffff0018
Else
PC = 0x00000018
7)快速中断请求(FIQ)异常
当处理器的快速中断请求引脚有效,且CPSR中的F位为0时,产生FIQ异常。FIQ支持数据传送和通道处理,并有足够的私有寄存器。
r14_fiq = address of next instruction to be executed + 4
SPSR_fiq = CPSR
CPSR[4∶0] = 0b10001 /*进入FIQ模式*/
CPSR[5] = 0
CPSR[6] = 1
CPSR[7] = 1
If high vectors configured then
PC= 0xffff001c
Else
PC = 0x0000001c
PLUS
问题2:为什么在异常向量表中,用b指令跳转而不是用ldr绝对跳转?
答:因为使用b指令跳转比绝对跳转(ldr pc,XXXX)效率高,正因为效率高,所以把__stubs_start~__stubs_end之间的代码考到了0xffff0200起始处。
注意:
因为b跳转指令只能在+/-32MB之内跳转,所以必须拷贝到0xffff0000附近。
b指令是相对于当前PC的跳转,当汇编器看到 B 指令后会把要跳转的标签转化为相对于当前PC的偏移量写入指令码。
问题4: 中断为什么必须进入svc模式?
一个最重要原因是:
如果一个中断模式(例如从usr进入irq模式,在irq模式中)中重新允许了中断,并且在这个中断例程中使用了BL指令调用子程序,BL指令会自动将子程序返回地址保存到当前模式的sp(即r14_irq)中,这个地址随后会被在当前模式下产生的中断所破坏,因为产生中断时CPU会将当前模式的PC保存到r14_irq,这样就把刚刚保存的子程序返回地址冲掉。为了避免这种情况,中断例程应该切换到SVC或者系统模式,这样的话,BL指令可以使用r14_svc来保存子程序的返回地址。
问题7:什么是High vector?
A:在Linux3.1.0,arch/arm/include/asm/system.hline121 有定义如下:
#if LINUX_ARM_ARCH >=4
#define vectors_high() (cr_alignment & CR_V)
#else
#define vectors_high() (0)
#endif
意思就是,如果使用的ARM架构大于等于4,则定义vectors_high()=cr_alignment&CR_V,该值就等于0xffff0000
在Linux3.1.0,arch/arm/include/asm/system.hline33有定义如下:
#define CR_V (1 << 13) /* Vectors relocated to 0xffff0000 */
arm下规定,在0x00000000或0xffff0000的地址处必须存放一张跳转表。
问题8:中断向量表是如何存放到0x00000000或0xffff0000地址的?
A:Uboot执行结束后会把Linux内核拷贝到内存中开始执行,linux内核执行的第一条指令是linux/arch/arm/kernel/head.S,此文件中执行一些参数设置等操作后跳入linux/init/main.c文件的start_kernel函数,此函数调用一系列初始化函数,其中trip_init()函数实现向量表的设定操作。
下一篇: 2440的时钟初始化