基于Cortex-M4内核的寄存器要点总结

news/2024/10/21 7:53:41/

Cortex-M4的寄存器

        ARM架构中,处理器不能直接操作内存(存储器)中数据,必须先把内存中的数据加载到寄存器组中的寄存器里,处理完后,若有必要,再写回内存。这种方式一般被称作“加载-存储架构”。如果core的寄存器数量充足的话,在进行数据处理时,寄存器组中可以临时存储一些数据变量,而无需每次更新到系统内存及在使用时将它们读回,从而可以有效的提高系统的工作效率。

1.Register Bank

        Cortex-M4有16个用于数据处理和程序控制的寄存器,称为Register Bank,依次被标记为R0~R15。

        R0~R12是32位通用寄存器(general purpose register)。其中前8个寄存器R0~R7被称为low register,因为访问空间的限制,大多数16位指令只能够访问low registers。而high register(R8~R12)则能被32位和几个特殊的16位指令(如MOV(move))访问, R0~R12的初始值是未定义的。

R13,栈指针SP(stack pointer,堆栈指针) 32

  1. 通过PUSH和POP指令访问栈空间内存(即系统的RAM区)。物理上存在两个栈空间指针:主栈指针Main Stack Pointer(MSP, 或SP_main)和进程栈指针Process Stack Pointer(PSP, 或SP_process)。其中MSP是默认的栈指针,系统复位或运行在处理(Handler)模式下时,通过R13访问的是MSP, 而PSP只能在线程(Thread)模式下使用。
  2. 至于使用的是哪个栈指针由CONTROL寄存器控制/配置(仅在Thread Mode下才可设置CONTORL寄存器),但在同一时间只有一个栈指针工作。 MSP和PSP都是32位的寄存器,但它们的低两位总是0,对这两个bit位的写操作不起作用。对于ARM Cortex-M处理器,PUSH和POP指令总是32位的,栈操作的地址也必须对齐到32位的字边界上。
  3. 一般用到嵌入式OS时才会使用PSP(用于thread级app的SP),此时OS内核同应用任务的栈是相互独立的。PSP得初始值未定义,而MSP(用于OS内核的SP)的初始值则需要在复位流程中从存储器的第一个字中取出。
  4. 在编译器中,可以通过r13(R13)或sp(SP)来访问堆栈(具体是MSP和PSP由当时环境决定);也可以通过指定的MRS、MSR指令来访问MSP和PSP。
  5. 注:

    Cortex-M0中堆栈方向是向低地址方向增长,为满堆栈机制。堆栈操作是通过PUSH和POP来完成操作的。(压栈:栈顶地址减小;出栈:栈顶地址增大)

    栈一般放在ARM的 RAM高位区,如某MCU中RAM地址为0x20000000-0x20007fff,共32KByte。栈大小设为4KByte的话,其地址一般就放在0x20007000-0x20007fff,其中0x20007000为绝对栈顶,0x20007ffc为绝对栈底,sp总是指向相对栈顶。第一个PUSH数据被存在绝对栈底(此时绝对栈底也是相对栈顶)。实际上,除了POP指令可以从栈顶中取数据外;MOV指令也可从任意位置取数据,但不会影响栈结构(即不影响其sp)。

    由于ARM寄存器均是32bit,故PUSH和POP指令均是32bit访问,故sp指针总是至少4Byte对齐(低2bit永远为0)。有时编译器也会分配8Byte对齐的栈,这是由于double浮点类型需要占用8Byte,为了处理方便,故将栈设为8Byte对齐。

    在一般函数(无参无局部变量无返回值)嵌套调用时,关于sp的操作。在执行BL FunctionA指令时,LR记录的是BL FunctionA的下一条顺序指令,在进入FunctionA后执行的第一条操作便是PUSH {LR}即将下一条顺序指令压入栈中,然后才开始执行FunctionA函数体。函数体执行结束之后,使用POP {PC}指令将栈顶数据弹到PC中,即可返回继续执行BL FunctionA的下一条顺序指令。函数嵌套调用与函数返回关系如下图:

 

R14,链接寄存器LR (Link Register)

  1. 它被用于在调用函数或者执行子程序时,记录返回地址,在函数或子程序结束时,程序控制可以通过将LR的数值加载到程序计数器(PC)中,从而返回到调用程序处并继续执行。
  2. 在发生函数或子程序调用时,LR的值会被自动更新,如果被调用的函数在执行过程中仍需要调用其他函数, 则需要先把LR的值压栈,保存,否则返回地址将丢失。
  3. 异常处理(中断也是一种异常)期间,LR会被写入一个特别的值EXC_RETURN(异常返回),之后该数值会在异常处理结束时触发异常返回。
  4. 虽然在Cortex-M处理器中,任何函数的返回地址都一定是偶数(由于指令会对齐到半字地址上,因此第0位为0),但LR的第0位仍然是可读可写的,有些跳转/调用操作需要将LR(或正在使用的任何寄存器)的第0位,置1以表示Tbumb状态。

R15,程序计数器PC (Program Counter),可读可写。

  1. 读操作返回当前指令地址+4(由于流水线特性的设计以及对ARM7TDMI的兼容设计)。 写操作(例如,使用数据传输/处理指令)将导致程序执行地址的跳转。
  2. 因为指令必须半字或者整字对齐,所以PC的最低位(LSB)总为0。 但是通过跳转等语句更新PC时,需要将PC的最低位置1,以标识Thumb状态,否则会由于使用不支持的ARM指令(如ARM7TDMI中的32位ARM指令)而触发错误异常。对于C语言编程,不需要考虑这一点,因为编译器已经帮我们完成了这一操作。(对于高级编程语言,包括C和C++编译器会自动将跳转目标的LSB置位)
  3. 多数情况下,跳转和调用由专门的指令实现,利用数据处理指令更新PC的情况较少。不过,在访问位于程序存储器中的字符数据时,PC数值非常有用,因此会经常发现存储器读操作将PC作为基地址寄存器,而地址偏移则由指令中的立即数生成。

2.特殊寄存器

除了上述Register Bank中涉及到的16个寄存器外,Cortex-M4还有一些反应系统状态、控制内核工作模式、异常中断屏蔽掩码等特殊功能的寄存器。 这些特殊的寄存器并没有分配地址映射,可以通过MSR或者MRS读写这些寄存器。

2.1.状态寄存器PSR(Program Status Register)

APSR(Application PSR)、EPSR(Execution PSR)和Interrupt PSR(IPSR)。另外这三个寄存器可以组合为一个寄存器一起访问,标记为xPSR。

注:GE只有基于ARMv7E-M架构的处理器才有,例如Cortex-M4

在ARM的汇编器中,用PSR标识三个状态寄存器的组合。 我们可以单独的访问每个状态寄存器,也可以通过PSR访问他们的组合,参考如下的代码。 但是,需要注意的是,EPSR是不能够直接通过MRSMSR读写的,而IPSR则是一个只读的寄存器

MRS r0, PSR  ; 读组合寄存器的值到r0中

MSR PSR, r0  ; 写r0的值到组合寄存器中

        MRS r0, APSR ; 读应用寄存器的值到r0中

        MSR APSR, r0 ; 写r0的值到应用寄存器中

状态寄存器的位定义如下:

N: 负数标识位(Negative flag)

Z: 零标识位(Zero flag)

C: 进位或者无借位标识位(Carry or NOT borrow flag)

V: 溢出标识位(Overflow flag)

Q: Sticky saturation flag

GE: 大于等于标识位(Greater-Than or Equal flags for each byte lane)

ICT/IT: Interrupt-Continuable Instruction(ICT)bits, IF-THEN instruction status bit for conditional execution.

T: Thumb状态标识位,始终为1;尝试清除该位将触发一个错误异常。

Exception Number: 异常代号,标志着正在处理的异常。

2.2.异常/中断掩码寄存器(PRIMASK, FAULTMASK, 和BASEPRI

  1. 仅在privileged的情况下访问。每个异常/中断都有一个优先级,取值越小优先级越高。默认情况下,这些寄存器的值都为0,所有的中断都是关闭的。
  2. PRIMASK只有最低位有定义,用于打开或屏蔽除复位、NMI(Non-Maskable Interrupt, 不可屏蔽中断)以及硬件错误异常(HardFault Exception)外的所有异常和中断。 同时会把当前正在处理的中断或者异常的优先级提升到level 0,可屏蔽中断的最高优先级。
  3. PRIMASK通常用于执行一些对时序要求严格的任务时,暂时关闭所有中断,实时程序处理完毕后清除PRIMASK,重新开启中断。
  4. FAULTMASK与PRIMADSK类似,并且它可以关闭硬件异常(HardFault Exception),同时把当前异常的优先级提升至level -1。 和PRIMASK不同的是,当中断服务程序返回后FAULTMASK将会自动清除。
  5. 为了提供灵活的中断屏蔽机制,ARMv7-M架构提供了BASEPRI寄存器,可以根据优先级屏蔽中断或者异常。 BASEPRI寄存器的可用位长由具体的CPU厂商实现,Cortex-M4通常有8个或者16个优先级,因此通常为3位或者4位。 BASEPRI中的值为0时,没有作用。BASEPRI非零时,将屏蔽具有相同和更低优先级的中断。(中断优先级号低于阈值不屏蔽,等于或高于阈值屏蔽)
  6. CMSIS-Core定义了一系列的C语言函数用来访问这三个中断屏蔽寄存器:

        x = __get_BASEPRI();

        x = __get_PRIMASK();

        x = __get_FAULTMASK();

        __set_BASEPRI(x);

        __set_PRIMASK(x);

        __set_FAULTMASK(x);

        __disable_irq();    // 置位PRIMASK,关闭中断

        __enable_irq();     // 清除PRIMASK,开启中断

也可以通过汇编语句访问这些寄存器:

MRS r0, BASEPRI  ; 读BASEPRI值到r0

MRS r0, PRIMASK   ; 读PRIMASK的值到r0

MRS r0, FAULTMASK ; 读FAULTMASK的值到r0

MSR BASEPRI, r0   ; 写r0的值到BASEPRI

MSR PRIMASK, r0   ; 写r0的值到PRIMASK

MSR FAULTMASK, r0 ; 写r0的值到FAULTMASK

另外,更改处理器状态(Change Processor State)指令提供了用于修改

PRIMASK和FAULTMASK的简单指令:

CPSIE i ; 清除PRIMASK,使能中断

CPSID i ; 置位PRIMASK,关闭中断

CPSIE f ; 清除FAULTMASK,使能中断

CPSID f ; 置位FAULTMASK,关闭中断

2.3.控制寄存器(CONTROL

  1. CONTROL寄存器主要用来选择栈空间指针寄存器。
  2. CONTROL寄存器只能够在系统具有privileged级权限下被修改,它可以在任何情况下被读取。

      系统复位后,CONTROL的值为0,标识着系统以MSP作为栈空间指针寄存器,而且具有privileged级的访问权限。priveleged的应用程序可以通过写CONTROL寄存器,切换栈空间指针和系统的访问权限。

     但是一旦nPRIV置位后,系统将不能对CONTROL寄存器进行写访问。也就是说,在unpriveleged级权限下的系统不能自由地变更系统的访问权限和栈空间指针。这对于系统的安全性而言是有必要的。

       如果必须切换回privileged的权限,则需要借助中断机制。在中断服务程序中清除nPRIV位,中断服务程序返回后系统就具有了privileged的权限。

       对于使用CMSIS接口的处理器,我们可以通过如下的C语言代码访问CONTROL寄存器:

        x = __get_CONTROL();

        __set_CONTROL(x);

       或者汇编的形式:

        MRS r0, CONTROL ; 读CONTROL中的数据到r0中

        MSR CONTROL, r0 ; 写r0的数据到CONTROL中

在对CONTROL寄存器进行写操作时,有两点需要注意:

  1. 对于有FPU的Cortex-M4处理器,执行浮点数指令时,FPCA位会被自动置位。 如果写操作清除了该位,紧接着产生了中断的话,浮点运算单元中的数据将不会被保存,在中断处理过程中可能修改相关的寄存器,导致中断返回时数据不能正常恢复。
  2. 修改了CONTROL寄存器后,需要执行一个Instruction Synchronization Barrier(ISB)指令, 以保证修改能够应用到以后需要执行的代码中。 而Cortex-M4做了简化,不执行这一操作也没有影响。

3.常用汇编指令

ldr:通常都是作加载指令的,但是它也可以作伪指令,通常有两种不同的表示:

1)ldr pc, =MyHandleIRQ 表示将MyHandleIRQ地址放入pc寄存器中,相当于PC=MyHandleIRQ 。

例如:

ldr r0,=label

//用于加载立即数或一个地址值到指定寄存器中

//如果label是立即数: ldr r0,=0x123 ;将0x123存入r0中

//如果label是个标识符: ldr r0,=label_1 ;将label_1所指向的地址值存入r0中

2)ldr pc,MyHandleIRQ 表示将 MyHandleIRQ地址中的值放入pc寄存器中,相当于PC=*(MyHandleIRQ )。

例如:

ldr r0,[r1] //将r1中的值存到r0中

ldr r1,[r2,#16] //将(r2+16)地址中的内容存到r1中

ldr r1,[r2],#4 //将r2地址中的内容存到r1中,同时r2=r2+4

3)LDMIA R1!,{R0,R4-R12}

LDMIA 中的 IA表示每次访问后递增地址,LD表示加载(load)

R1后面的感叹号“!”表示会自动调节 R1里面存的指针

该代码段表示:任务栈R1的存储地址由低到高,将R1存储地址里面的内容手动加载到 CPU 寄存器 R0,R4-R12里面;

4)STMDB R1!, {R0,R4-R12}

与3)指令相反,ST表示存储(store),D表示decrease,B表示before,

该代码段表示:R1的存储地址由高到低递减,将R0,R4-R12里的内容存储到R1任务栈里面。


http://www.ppmy.cn/news/617416.html

相关文章

ARM 之十四 ARMv9 架构前瞻

在 2021 年的 Arm Vision Day 上(2021年 3 月 30,对应的北京时间应该是 2021 年 3 月 31 日),ARM 展示了其最新的架构:ARMv9。ARMv9 被 ARM 冠以未来 10 年愿景的称号,并将其称为 10 年以来最重要的创新。声…

java装甲特战队,装甲特战队拉阔版的手机游戏攻略、技巧、心得

满意答案 天空hbl号 2014.08.26 采纳率:57% 等级:9 已帮助:1219人 这款游戏玩了好几天,觉得还是很有可玩、可说之处的,特分享一下个人的经验 这个游戏熬的是时间 一、前期不断的演习——挣钱、练经验,11…

lxcfs + cgroup v2 + docker + k8s 实现资源视图限制

制作镜像 cat Dockerfile FROM ubuntu:22.04 USER root RUN apt update -y RUN apt-get -y install lxcfs COPY start.sh / CMD ["/start.sh"][zldlocal2] root@zldlocal2:~# cat start.sh #!/bin/bash # Cleanup nsenter -m/proc/1/ns/mnt fusermount -u /var/li…

国税发票查验|一种简单的发票验真API开发文档

一、接口简介 1、版权说明: 未经翔云人工智能开放平台授权,不得擅自进行使用。 2、调用客户端运行环境 当客户端使用的编程语言为Java时,请使用1.5及以上版本JRE。 3、服务主要功能描述 翔云发票验真服务,实时联网核查验5年内增值…

android 模拟器 root

通过Android Studio创建的手机模拟器,无需任何操作就可以获取root权限。然后还可以通过控制台在Android/sdk/emulator目录下,运行下面的指令来开放remount权限 emulator -writable-system -netdelay none -netspeed full -avd 模拟器的名字

安卓街机模拟器 MAME4droid 源码,只需要自己加入rom 可以发布到安卓市场了。

安卓街机模拟器 MAME4droid 源码,只需要自己加入rom 可以发布到安卓市场了,可以开始自己的赚钱了。为了方便大家赚钱,apk展示的包含万普广告条,也就是只要自己去申请万普广告条,填一下appid ,一行代码不用写…

扩充模拟器的ROM空间

由于长时间的使用模拟器,之后又需要安装比较大的apk包,因此老是提示空间不足,无法安装。 使用参数-partition-size 256 -partition-size 256 意为设置模拟器的ROM大小为256M,当默认模拟器的ROM不够用时使用这个设置比较有效

Android模拟器源码Qemu Study for Android Emulator

这两天看了下android emulator的源代码,位置在android-src/external/qemu里面, 编译和启动的方式很简单; $ ./android-configure.sh $ make $ export ANDROID_SDK_ROOT/path/to/androdi-sdk $ emulator-arm 4.2 你可以对源码进行修改&#x…