跳转到内容

嵌入式系统/ARM 微处理器

来自维基教科书,开放世界中的开放书籍

ARM 架构是一种广泛使用的 32 位 RISC 处理器架构。事实上,ARM 系列约占所有 32 位 CPU 的 75%,约占所有嵌入式 32 位 CPU 的 90%。ARM 有限公司将几个流行的微处理器核心授权给许多供应商(ARM 不出售物理微处理器)。最初,ARM 代表高级精简指令集机器

ARM 提供的一些核心

  • ARM7TDMI
  • ARM9
  • ARM11

一些基于 ARM 的处理器的示例

  • 英特尔 X-Scale(PXA-255 和 PXA-270),用于 Palm PDA
  • 飞利浦 LPC2000 系列 (ARM7TDMI-S 核心),LPC3000 系列 (ARM9 核心)
  • Atmel AT91SAM7 (ARM7TDMI 核心)
  • 意法半导体 STR710 (ARM7TDMI 核心)
  • 飞思卡尔 MCIMX27 系列 (ARM9 核心)

最便宜的 ARM 处理器(LPC2000 系列)的价格已降至 5 美元以下,低于许多 16 位和 8 位微处理器的价格。

Thumb 调用约定

[编辑 | 编辑源代码]

在 ARM Thumb 代码中,16 个寄存器 r0 - r15 通常在所有 ARM 代码中具有相同的角色

  • r0 - r3,称为 a1 - a4:参数/临时/结果寄存器。
  • r4 - r9,称为 v1 - v6:变量
  • r10,称为 sl:堆栈限制
  • r11,称为 fp:帧指针(通常在 Thumb 代码中不使用)
  • r12,称为 ip
  • r13,称为 sp:堆栈指针
  • r14,称为 lr:链接寄存器
  • r15,称为 pc:程序计数器

ARM Thumb 的标准 C 调用约定是:[1]

子程序保留寄存器

[编辑 | 编辑源代码]

当返回地址放置在 pc (r15) 中时,从子程序返回时,sp、fp、sl 和 v1-v6 寄存器必须包含与调用子程序时相同的

每个执行环境都对堆栈在内存中下降的范围有限制——“最小 sp”。

为了让中断(可能在任何时候发生)有空间工作,在任何时刻,sp 和“最小 sp”之间的内存必须不包含对正在执行的程序

应用程序及其库支持代码负责检测和处理堆栈溢出的系统称为“显式堆栈限制”。在这样的系统中,sl 寄存器必须始终

调用者保留寄存器

[编辑 | 编辑源代码]

子程序可以随意覆盖 a1-a4、ip 和 lr。

返回值

[编辑 | 编辑源代码]

如果子程序返回一个不超过一个字的简单值,则该值必须在 a1 (r0) 中。

如果子程序返回一个简单的浮点值,则该值编码在 a1 中;或者 {a1, a2} 中;或者 {a1, a2, a3} 中,以

典型子程序

[编辑 | 编辑源代码]

Thumb 函数最简单的入口和退出序列是:[1]

an_example_Thumb_subroutine:
    PUSH {save-registers, lr} ; one-line entry sequence
    ; ... first part of function ...
    BL subroutine_name 	;Must be in a space of +/- 4 MB 
    ; ... rest of function goes here, perhaps including other function calls
    ; ...
    POP {save-registers, pc} ; one-line exit sequence

ARM 调用约定

[编辑 | 编辑源代码]

ARM 的标准 C 调用约定由 ARM PLC 详细说明。[2]

32 位 ARM 函数最简单的入口和退出序列与 Thumb 函数非常相似:[3][4][5]

an_example_ARM32_subroutine:
    PUSH {r4-r11, lr} ; one-line function prologue
    ; ... first part of function ...
    BL subroutine_name 	;Must be in a space of +/- 4 MB 
    ; ... rest of function goes here, perhaps including other function calls
    ; ...
    POP {r4-r11, pc} ; one-line exit sequence (function epilogue)

使用相同指令的备用助记符,

an_example_ARM32_subroutine:
    ; Push the return address (in LR) and the work registers
    ; "store multiple registers, full descending"
    STMFD sp!,{r4-r11, lr} ; aka PUSH {r4-r11, lr}
    ; (A "sp" alone would leave the stack pointer unchanged.
    ; We must use "sp!" to update the stack pointer appropriately.)
    ; ... first part of function ...
    BL subroutine_name 	;Must be in a space of +/- 4 MB 
    ; ... rest of function goes here, perhaps including other function calls
    ; ...
    ; Pop the return address (into PC) and the work registers
    ; and return automatically.    
    ; "load multiple registers, full descending"
    LDMFD sp!,{r4-r11, pc} ; aka POP {r4-r11, pc}

BL(分支并链接)指令将返回地址存储在链接寄存器 LR(r14)中,并将程序计数器 PC(r15)加载到子程序地址。典型

子程序保留寄存器

[编辑 | 编辑源代码]

通常,r4-r11 用于保存当前正在执行的例程的局部变量。

寄存器 r4-r11 是“子程序保留寄存器”——当子程序将返回地址放置在 pc (r15) 中时,从子程序返回时,寄存器 r4-r11

典型的子程序(如上所示)会立即将这些寄存器的值压入堆栈。这释放了 r4-r11 以保存当前正在执行的子程序的局部

优化的 ARM 编译器会保存和恢复 r4-r11 和 r14(如果有)的精确子集,这些子集实际上被该子程序修改了,因为保存和

临时寄存器

[编辑 | 编辑源代码]

子程序可以随意覆盖 r0-r3、r12 和链接寄存器 lr (r14)。

前四个寄存器 r0-r3 用于将参数值传递给子程序,并从函数返回结果值。

混合 ARM32 和 Thumb 调用

[编辑 | 编辑源代码]

使用 BL 指令可以轻松进行正常的函数调用。一个人输入

    BL destination_subroutine

汇编器和链接器会自动执行“正确的事情” - 插入适当的(32 位长)ARM32 BL 指令用于 ARM32 到 ARM32 或 ARM32 到 Thumb 的调用,或插入适当的(32 位长)[6] Thumb BL 指令用于 Thumb 到 Thumb 或 Thumb 到 ARM32 的指令。

(一些混合调用和一些长分支需要链接器插入代码,用临时值覆盖 scratch register r12。 链接器究竟是如何做到的可能会令人困惑,尤其是在混合使用 BX 和 BLX 指令时。[7][8]

进一步阅读

[edit | edit source]
  1. a b ARM. ARM 软件开发工具包. 1997. 第 9 章:ARM 过程调用标准。 第 10 章:Thumb 过程调用标准。
  2. "ARM 架构过程调用标准"
  3. RealView 编译工具开发者指南 "在 C、C++ 和 ARM 汇编语言之间调用"
  4. [1] 第 59 页“堆栈和子程序”部分
  5. "为嵌套子程序堆叠寄存器"
  6. [infocenter.arm.com/help/topic/com.arm.doc.ddi0234b/i107462.html "带有链接的 Thumb 分支 (BL)"]
  7. "Arm/Thumb:在 Thumb 代码中使用 BX,调用 Thumb 函数,或跳转到另一个函数中的 Thumb 指令"
  8. "Arm / Thumb 互操作"
华夏公益教科书