C/C++教程

ARM(CPU)内部寄存器学习笔记

本文主要是介绍ARM(CPU)内部寄存器学习笔记,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录
  • ARM处理器状态
  • 处理器模式
    • 系统模式 System,Sys
  • ARM内部寄存器
    • ARM状态和Thumb状态下的寄存器访问
      • 一般通用寄存器
      • 不同模式下的寄存器
      • 堆栈指针R13(SP)
      • 链接寄存器R14(LR)
      • 程序计数器R15(PC)
  • 当前程序状态寄存器CPSR
    • CPSR与SPSR
    • CPSR格式
      • 条件代码标志
      • 控制标志位
  • 参考

本文的具体寄存器描述,以NXP LPC2000系列用到的内核ARM7TDMI-S processor为例,参考ARM官网 。

提到ARM内部寄存器,就不得不提到ARM处理器状态和处理器模式,因为不同状态和模式下,访问寄存器的权限是完全不一样的。

ARM处理器状态

嵌入式系统对存储成本和空间要求比较高,为了让用户更好控制代码量,因此设计了2套指令系统:ARM指令集Thumb指令集
ARM指令集:32bit字长,具有完整功能;
Thumb指令集:16bit字长(半字长),能实现ARM指令集绝大部分内容。

  • 二者关系
  1. 功能上,Thumb指令集是ARM指令集的子集。
  2. Thumb指令集具有极高代码密度,平均缩减30%代码量。
  • 2种指令集何时执行?
    有2个处理器状态与这2套指令集对应,而这2种状态由CPSR寄存器的T字段控制:
CPSR[T] = 0 <=> ARM状态
CPSR[T] = 1 <=> Thumb状态
  1. ARM状态
    32bit,处理器执行ARM指令,处理器上电后默认状态。

  2. Thumb状态
    16bit,处理器执行半字方式的Thumb指令。

参见下图对PSR寄存器(Program Status Register)描述,其中CPSR表示当前程序状态寄存器(Current Program Status Register):

PS:

  1. 修改CPSR[T](或者说ARM状态和Thumb状态切换),并不影响处理器模式和其他寄存器内容;
  2. 不要使用MSR指令让处理器切换状态,虽然可以修改T位,但是因为不会清空流水线,所以是不安全的。推荐使用BX指令进行状态切换;
  • 状态切换
    BX指令控制处理器状态切换,程序发生跳转时,流水线会被清空:流水线中原来处理器状态取值和译码的指令,也会被清除,不会引起处理器错误。
; 从ARM状态 => Thumb状态
        CODE32                   ; 下面的指令为ARM指令
        LDR R0,=Lable+1          ; R0[bit0]=1, BX自动将CPSR[T]置1
        BX R0                    ; 切换到Thumb状态, 并跳转到Lable处执行
        CODE16                   ; 下面的指令为Thumb指令
Lable MOVE R1,#12
; 从Thumb状态 => ARM状态
        CODE16                   ; 下面的指令为Thumb指令
        LDR R0,=Lable            ; R0[bit0]=0, BX自动将CPSR[T]置0
        BX R0                    ; 切换到ARM状态, 并跳转到Lable处执行
        CODE32                   ; 下面的指令为ARM指令
Lable MOV R1,#10

处理器模式

ARM处理器支持7种处理器模式,以CPSR_M[4:0]控制位反应处理器正在操作的模式。除用户(user, usr)模式外,其他6种称为特权(privileged)模式,分为2类,分别是:系统(system,sys)模式和异常模式,
其中,异常模式又包括5种模式: 管理(supervisor,svc)模式、中止(abort,abt)模式、未定义(undefined,und)模式、中断(interrupt request,irq)模式、快速中断(fast interrupt request,fiq)模式。

ARM处理器7种模式:

CPSR_M[4:0]表示当前处理器模式:

注:

  1. nM[4:0]是M[4:0]的反转,nM[4:0]信号仅用于诊断和调试;
  2. 只有在特权模式下,才允许对CPSR进行读写。这也是为什么不能直接通过修改CPSR[T],以控制处理器状态切换,而是需要一个流程的原因。

## 异常模式 **异常模式** > 管理模式,中止模式,未定义模式,中断模式,快速中断模式 这5种处理器模式统称异常模式。

每种异常都和一种处理器模式相对应,一旦应用程序发生特定异常中断时,处理器进入相应的异常模式,内核立即跳转到向量表中的某个入口地址,执行相应的处理程序。同时,每种异常模式都有与之对应的寄存器,供相应的异常处理程序用,用来确保处理器进入异常模式时,用户模式下的寄存器不会被破坏。

  • 什么时候进入异常模式?规定:
  1. 处理器复位后进入管理模式,操作系统内核通常处于该模式;
  2. 当处理器访问存储器失败时,进入数据访问中止模式;
  3. 当处理器遇到不支持的指令时,进入未定义模式;
  4. 中断模式与快速中断模式分别对ARM处理器两种不同级别的中断做出响应;
  • 系统模式切换到异常模式

例,切换到管理模式(svc)

MSR CPSR_c, #(NoInt | SVC32Mode) // 相当于 CPSR_c = (NoInt | SVC32Mode) => I = 1, F = 1, M[4:0] = 0b10011(Supervisor)

MSR指令通常用于恢复或改变PSR内容;
CPSR_c 表示CPSR的控制域掩码,而控制域指CSPR[7..0](control bits:I, F, T, M[4..0]);
SVC32Mode 指的管理模式;

CPSR_c,CPSR_f,CPSR_s,CPSR_x含义:

Tips:MSR(写)和MRS(读)指令
MRS指令:专门用于对状态寄存器CPSR和SPSR(Saved PSR)进行读操作。通过读取CSPR,可以获得当前处理器的工作状态。通过读取SPSR,可以获得处理器进入异常前的处理器状态(因为只有异常模式才有SPSR);
MSR指令:专门用于对状态寄存器CPSR和SPSR进行写操作。跟MRS配合,可以实现对CPSR/SPSR的读写操作,以切换处理器模式、允许或禁止IRQ(IRQ disable)和FIQ(FIQ disable)中断等功能;

关于MRS和MSR更详细内容,可参见这篇文章汇编指令-MRS(读)和MSR(写)指令操作CPSR寄存器和SPSR寄存器使用(1) | 博客园

系统模式 System,Sys

用户模式

用户模式是程序正常运行的工作模式。

系统模式与用户模式
系统模式具有跟用户模式完全系统的寄存器,不过系统模式有特权,可以访问所有系统资源,也可以直接进行处理器模式切换。主要供OS的任务使用,当然也允许对CPSR读写。
系统模式下,OS可以访问所有用户模式下相应的寄存器,而不是使用异常模式下相应的寄存器,这样可以保证当异常中断发生时,任务的状态不被破坏。

系统模式与异常模式
系统模式、用户模式都不能由异常进入。要进入系统模式,必须通过修改CPSR实现。

例,从管理模式切换到系统模式

MSR CPSR_c, #(NoInt | SYS32Mode) // 从管理模式切换到系统模式 => I = 1, F = 1, M[4:0] = 0b1111(Mode = System)

ARM内部寄存器

ARM处理器内部共有37个用户可访问32bit寄存器,分别是:

  • 31个通用寄存器
    R0~R15, R13_svc, R14_svc, R13_abt, R14_abt, R13_und, R14_und, R13_irq, R14_irq, R8_fiq, R9_fiq, R10_fiq, R11_fiq, R12_fiq, R13_fiq, R14_fiq

  • 6个状态寄存器(每个只使用其中12bit)
    CPSR, SPSR_svc, SPSR_abt, SPSR_und, SPSR_irq, SPSR_fiq

处理器有7种不同的处理器模式,每种模式都有一组相应的寄存器组,对应不同的后缀名,如R13_svc对应svc模式(管理模式)。
PS:

  1. 每种模式拥有的一组寄存器不相同,并非上面描述的全部;
  2. 这些寄存器不能同时访问,何时访问取决于处理器状态和处理器模式。

ARM状态和Thumb状态下的寄存器访问

可以看到,ARM状态和Thumb状态下,程序员能访问的寄存器是不一样的。
特别地,在ARM状态下,特殊用途寄存器R13(堆栈指针SP),R14(链接寄存器LR),R15(程序计数器PC),尽管可以当做通用寄存器使用,但是编译器通常认为R13始终指向一个有效的堆栈结构,所以一定要注意将R13当做通用寄存器来使用是非常危险的。

ARM状态下,各种模式下能访问的通用寄存器和PC(程序计数器)

Thumb状态下,各种模式下能访问的通用寄存器和PC(程序计数器)


一般通用寄存器

R0~R7 保存数据或地址值,任何处理器模式下通用,都是同一个32bit物理寄存器。

PS:由用户模式进入中断模式后,可能造成寄存器的数据丢失,因此应该先对重要数据进行备份。

R8~R14 所对应的物理寄存器取决于当前的处理器模式,几乎所有允许使用通用寄存器的指令都允许使用R8~R14。具体参见上面图“各种模式下能访问的通用寄存器和PC(程序计数器)”。

不同模式下的寄存器

CSPR的位域M[4:0]控制不同的处理器模式。下图展示了不同模式下、不同状态下各寄存器的可见性:


堆栈指针R13(SP)

堆栈的概念
堆栈实际是指“堆”和“栈” 2个不同概念,但为了符合表达习惯,没有特别指出仅表示“栈”。
堆栈是指

在内存中划分出一段存储空间,这个存储空间就像是一个大的数据仓库,用于暂时保存一些数据。

堆栈操作通常发生在子程序调用、异常发生、程序运行过程中寄存器数量不够时。

  • 程序调用、异常发生: 通常把子程序或异常服务程序将要用到的寄存器内容,保存到堆栈中,子程序或异常服务程序返回时再恢复,以确保原有程序不被破坏。
  • 程序运行过程中寄存器数量不够:如果程序运行过程中,局部变量太多,以至于处理器内部的寄存器无法全部装下,程序员或编译器会使用堆栈来作为数据的暂存空间,将暂未用到的数据压栈,需要用到时再出栈。

链接寄存器R14(LR)

ARM状态下,子程序返回地址将自动放入到R14中。每种异常模式都有专用的R14寄存器,用于保存子程序返回地址,分别是R14_svc,R14_abt,R14_und,R14_irq,R14_fiq。
Thumb状态下,LR会自动链接到ARM状态R14。

R14有2种特殊功能:

  1. 使用BL指令调用子程序时,返回地址将自动存入R14中。在子程序结束时,将R14复制到PC即可实现子程序的返回。。
  • 通常有2种方式实现子程序的返回:
// 方式一
MOV PC,LR
// 方式二
BX LR

ARM7TDMI Technical Reference Manual 也提到异常服务程序的进入和退出


注意:异常不同于普通子程序调用,普通子程序调用一般是可预见的,由程序员设置;异常是不可预料的,一般由硬件或OS内核触发。

  • 子程序入口,用寄存器写指令STMFD保存R14和其他寄存器内容到SP
STMFD SP!,{<register>,LR}

//具体地,如果是保存R0~R7及LR(进栈)
STMFD SP!,{R0-R7,LR} 
//其含义是:
// SP指针变化
SP = SP - 9x4 //R0~7 + LR,共9个寄存器,而SP默认在高地址位置,往低地址方向移动
address = SP
// 寄存器赋值给内存地址的赋值过程
for i = 0 to 7
  memory[address] = Ri
  address = address + 4
memory[address] = LR
  • 子程序结束,用批量寄存器读取指令LDMFD
    LDMFD是STMFD逆过程,即出栈的过程。
LDMFD SP!,[<registers>,PC]

// 具体地,将栈内容恢复至PC及R7~R0
LDMFD SP!,[R0-R7,PC]

  1. 当发生异常中断时,应注意保证异常处理程序不会破坏LR,因为LR保存了程序返回地址。异常处理程序返回后,LR内容会写入PC,同时从SPSR中恢复SPSR来实现。

也就是说,除了子程序(包括异常处理程序)调用时、返回时,其他任何时候,R14可以作为一个通用寄存器。

程序计数器R15(PC)

R15保存程序寄存器PC,R15总是指向正在“取指”的指令。

1. 读R15
ARM指令集是字对齐的,PC保存下一条指令的地址也应该是4的倍数,因此PC[1:0]总是为0。
Thumb指令集半字对齐,PC保存的地址是2的倍数,因此PC[0]总是0。

尽量避免使用STR、STM指令来保存R15,因为不同的芯片使用STR或STM保存R15时,保存的可能是 当前指令地址 + 8byte 或 + 12byte。该偏移,针对具体的芯片是一个常量。

2. 写R15
当执行一条写R15的指令时,写入R15的正常结果值被当成一个指令地址,程序从地址处继续执行,相当于执行一次无条件跳转。
ARM状态下,指令字对齐,写入R15值bit[1:0]必须是0b00,否则结果不可预测;
Thumb状态下,指令半字对齐,写入R15时将忽略bit[0]。

当前程序状态寄存器CPSR

ARM core包含1个CPSR + 5个异常用的SPSR。每种异常模式都有一个对应的SPSR,用于保存异常发生前的CPSR值。CPSR和SPSR都可以用特殊指令MSR、MRS访问(见上文)。

CPSR与SPSR

所有模式共享一个CPSR(程序状态寄存器),ARM core通过使用CPSR监视控制内部操作。
异常模式下,允许访问用于保存CPSR当前值的 SPSR(备份程序状态寄存器),不过每种异常都有相应的SPSR共5个,分别是:SPSR_svc(管理), SPSR_abt(中止), SPSR_und(未定义), SPSR_irq(中断), SPSR_fiq(快速中断)。

CPSR和SPSR在不同模式下对应寄存器,见下图:

注:为什么用户模式和系统模式没有SPSR?
因为用户模式和系统模式不是异常中断,因此没有SPSR。因而在用户模式和系统模式下,不能访问SPSR,否则可能产生不可预知结果。

CPSR格式

条件代码标志

条件代码标志(Condition code flags)包括N, Z, C, V位,可以通过算术运算和逻辑操作设置,也可以通过MSR和LDM指令设置。ARM7TDMI处理器测试这些标志位,以决定是否执行一条指令,这样。
一般地,如果指令带后缀S,表明指令会修改条件代码标志。不过也有些指令总是改变条件代码标志。

各标志位含义:

  1. 负标志N 运算结果的第31位值,记录标志设置操作的结果;
  2. 零标志Z 如果标志设置操作的结果为0,则置位;
  3. 进位标志C 记录无符号加法溢出,减法无借位,循环移位;
  4. 溢出标志V 记录标志设置操作的有符号溢出;
    ARM状态中,所有指令都可以按条件来执行;Thumb状态中,只有分支指令可按条件执行。详见ARM Architecture Reference Manual (conditional execution)

控制标志位

控制标志位(Control bits)包括I, F, T, M4 ~ M0。

  1. I 中断(IRQ)禁止位;
  2. F 快速中断(FIQ)禁止位;
  3. T 状态位;
  4. M4~M0 处理器模式位;
    发生异常时,控制位改变。特权模式下,可用软件操作这些位。

中断禁止标志位I和快速中断禁止标志位F
中断(IRQ)和快速中断(FIQ)是ARM的2个不同模式,本质上来说都是中断,不过FIQ优先级更高,而且FIQ的响应时间也比IRQ更快。发生中断时,FIQ可以插队,如果支持嵌套中断,FIQ可以打断当前正在执行的IRQ处理程序。

为什么FIQ比IRQ快?
因为,

  1. FIQ优先级比IRQ更高;
  2. FIQ有自己独有的寄存器(R8~R14,SPSR等),IRQ需要和其他模式共用寄存器,中断现场的保护和恢复会更快;
  3. FIQ在异常向量表的末尾,FIQ中断处理程序紧接着存放,可以直接执行。而异常向量表只能保存IRQ的首地址,发生中断时,进入IRQ中断处理程序需要一次跳转;

详细可参见ARM中FIQ(快速中断)比IRQ(普通中断)响应快的原因

中断禁止标志位:

  • I为1时,IRQ中断禁止;为0时,IRQ中断使能;
  • F为1时,FIQ中断禁止;为0时,FIQ中断使能;

控制位T
控制位T反应处理器正处于的状态:

  • T为1时,处理器处于Thumb状态;
  • T为0时,处理器处于ARM状态;

模式控制位M[4:0]
见下图

保留位
CPSR保留位被保留,以便将来使用。当改变CPSR标志位和控制位时,请确认没有改变这些保留位,否则可能会导致未定义行为。


参考

[1]周立功, 王祖麟, 陈明计, 严寒亮,等. ARM嵌入式系统基础教程[M]. 北京航空航天大学出版社, 2008.

这篇关于ARM(CPU)内部寄存器学习笔记的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!