···························································· 
第3 章 chapter3 
ARMv8架构基础知识
ARMv8架构是ARM 体系结构的一种,它具有引人注目的特点:首次引入了64位
指令集,从而能够处理更大的内存空间和更复杂的数据计算。此外,ARMv8架构还引入
了全新的执行模式AArch64,它能够运行64位指令集,支持更广泛的操作模式和高级特
性。目前,基于ARMv8架构设计的处理器在各个领域都发挥着重要的作用。
本章将从ARMv8架构的基础概念、寄存器组、A64指令集、ARM64异常处理以及
ARM64内存管理等方面介绍ARMv8架构,旨在帮助读者更好地理解ARMv8架构。
3.1 ARMv8 架构
ARMv8是ARM 公司推出的处理器架构,广泛应用于移动设备、嵌入式系统、物联
网设备和云服务器等领域,了解ARMv8架构可以理解和应用这些设备和系统。许多应
用和系统都是基于ARMv8架构开发的,学习ARMv8架构可以更好地理解和开发这些
应用和系统。同时,了解ARMv8架构也可以提高编程技能和工作竞争力。本节主要对
ARMv8中涉及的主要概念进行梳理,介绍基于ARMv8架构设计的处理器的运行状态
和其支持的数据宽度。
3.1.1 ARMv8 架构介绍
1.ARMv8-A 架构特性 
ARMv8-A 是ARM 公司发布的第一代支持64位处理器的指令集和架构,它在扩充
64位寄存器的同时提供了对上一代架构指令集的兼容,因此它提供了运行32位和64位
应用程序的环境。
ARMv8-A 架构除了提高了处理能力,还引入了很多吸引人的新特性。
. 具有超大物理地址空间,提供超过4GB物理内存。
. 具有64位宽的虚拟地址空间。32位宽的虚拟地址空间只能提供4GB大小的虚
拟地址空间访问,这极大地限制了桌面操作系统和服务器等的性能发挥。64位
宽的虚拟地址空间可以提供更大的访问空间。
. 提供31个64位宽的通用寄存器,可以减少对栈的访问,从而提高性能。

5 0 ◆嵌入式系统开发与应用 
. 提供16KB和64KB的页面,有助于降低TLB的未命中率(missrate)。
. 具有全新的异常处理模型,有助于降低操作系统和虚拟化的实现复杂度。
. 具有全新的加载-获取指令(load-acquireinstruction)、存储-释放指令(storereleaseinstruction),
专门为C++11、C11以及Java内存模型设计。
2.采用ARMv8架构的常见处理器内核
下面介绍市面上常见的采用ARMv8架构的处理器(简称ARMv8处理器)内核。
(1)Cortex-A53处理器内核。ARM 公司第一款采用ARMv8-A 架构的处理器内
核,专门为低功耗设计的处理器。通常可以使用1~4个Cortex-A53处理器组成一个处
理器簇,或者和Cortex-A57或Cortex-A72等高性能处理器组成大/小核架构。
(2)Cortex-A57处理器内核。采用64位ARMv8-A 架构的处理器内核,而且通过
AArch32执行状态,保持与ARMv7架构完全后向兼容。除了ARMv8架构的优势之外, 
Cortex-A57还提高了单个时钟周期的性能,比高性能的Cortex-A15高出20%~40%; 
它还改进了二级高速缓存的设计和内存系统的其他组件,极大地提高了性能。
(3)Cortex-A72处理器内核。2015年年初正式发布的基于ARMv8-A 架构,并在
Cortex-A57处理器上做了大量优化和改进。在相同的移动设备电池寿命限制下, 
Cortex-A72相较于基于Cortex-A15的设备具有3.5倍的性能提升,展现出了优异的整
体功耗效率。
3.1.2 ARMv8 基础概念
ARM 处理器实现的是精简指令集架构。在ARMv8-A架构中有以下基本概念和定义。
(1)处理机(processingelement,PE)。在ARM 公司的官方技术手册中提到的一个
概念,把处理器处理事务的过程抽象为处理机。
(2)执行状态(executionstate)。处理器运行时的环境,包括寄存器的位宽、支持的
指令集、异常模型、内存管理以及编程模型等。ARMv8架构定义了两个执行状态。
① AArch64:64位的执行状态。
. 提供31个64位的通用寄存器。
. 提供64 位的程序计数器(program counter,PC)指针寄存器、栈指针(stack 
pointer,SP)寄存器以及异常链接寄存器(exceptionlinkregister,ELR)。
. 提供A64指令集。
. 定义ARMv8异常模型,支持4个异常等级,即EL0~EL3。
. 提供64位的内存模型。
. 定义一组处理器状态(PSTATE)用来保存PE的状态。
② AArch32:32位的执行状态。
. 提供13个32位的通用寄存器,再加上PC指针寄存器、SP寄存器、链接寄存器
(linkregister,LR)。
. 支持两套指令集,分别是A32和T32指令集(Thumb指令集)。
. 支持ARMv7-A 异常模型,基于PE模式并映射到ARMv8的异常模型中。

第◆3 章 ARMv8 架构基础知识5 1 
. 提供32位的虚拟内存访问机制。
. 定义一组PSTATE用来保存PE的状态。
(3)ARMv8指令集。ARMv8架构根据不同的执行状态提供不同指令集的支持。
. A64指令集:运行在AArch64状态下,提供64位指令集支持。
. A32指令集:运行在AArch32状态下,提供32位指令集支持。
. T32指令集:运行在AArch32状态下,提供16和32位指令集支持。
(4)系统寄存器命名。在AArch64状态下,很多系统寄存器会根据不同的异常等级
提供不同的变种寄存器。
3.1.3 ARMv8 处理器的运行状态
ARMv8处理器支持两种执行状态———AArch64状态和AArch32状态。AArch64 
状态是ARMv8新增的64位执行状态,而AArch32是为了兼容ARMv7架构的32位执
行状态。当处理器运行在AArch64状态下时,运行A64指令集;而当运行在AArch32 
状态下时,可以运行A32指令集或者T32指令集。
如图3-1所示,AArch64状态的异常等级(exceptionlevel)决定了处理器当前运行的
特权级别,类似于ARMv7架构中的特权等级。
图3-1 AArch64状态的异常等级
. EL0:用户特权,用于运行普通用户程序。
. EL1:系统特权,通常用于运行操作系统。
. EL2:运行虚拟化扩展的虚拟监控程序(hypervisor)。
. EL3:运行安全世界中的安全监控器(securemonitor)。
ARMv8架构允许切换应用程序的运行模式。例如在一个运行64位操作系统的
ARMv8处理器中,可以同时运行A64指令集的应用程序和A32指令集的应用程序。但
是在一个运行32位操作系统的ARMv8处理器中就不能运行A64指令集的应用程序
了。当需要运行A32指令集的应用程序时,需要通过一条管理员调用(supervisorcall, 
SVC)指令切换到EL1,操作系统会做任务的切换并返回AArch32的EL0中,这时操作
系统就为这个应用程序准备好了AArch32的运行环境。
3.1.4 ARMv8 架构支持的数据宽度
ARMv8支持如下几种数据宽度。

5 2 ◆嵌入式系统开发与应用
. 字节(byte):8位。
. 半字(halfword):16位。
. 字(word):32位。
. 双字(doubleword):64位。
. 四字(quadword):128位。
不对齐访问有两种情况,一种是指令不对齐访问,另一种是数据不对齐访问。A64 
指令集要求指令存放的位置必须以字(word,32位宽)为单位对齐。访问一条存储位置
不以字为单位对齐的指令会导致PC对齐异常(PCalignmentfault)。
对于数据访问,需要区分不同的内存类型。内存类型是设备内存的不对齐访问会触
发一个对齐异常(alignmentfault)。
对于访问普通内存,除了独占加载/独占存储(load-exclusive/store-exclusive)指令或
者加载-获取/存储-释放(load-acquire/store-release)指令外,对于其他加载或者存储单个
或多个寄存器的所有指令,如果访问地址和要访问数据不对齐,则按照以下两种情况进
行处理。
. 若对应的异常等级中的SCTLR_Elx.A 设置为1,则说明打开了地址对齐检查功
能,那么会触发一个对齐异常。
. 若对应的异常等级中的SCTLR_Elx.A 设置为0,那么处理器支持不对齐访问。
当然,处理器对不对齐访问也有一些限制。
. 不能保证单次原子地完成访问,可能多次复制。
. 不对齐访问比对齐访问需要更多的处理时间。
. 不对齐访问可能会造成中止(abort)。
3.2 ARMv8 寄存器
了解ARMv8寄存器的结构、数量和使用方式,对理解指令集的执行、优化代码、调试
程序以及进行性能分析非常有必要。因此本节通过介绍ARMv8架构的主要寄存器:通
用寄存器、处理状态寄存器、特殊寄存器和系统寄存器,帮助读者认识ARMv8寄存器。
3.2.1 通用寄存器
AArch64运行状态支持31 个64 位的通用寄存器,分别是X0~X30 寄存器,而
AArch32状态支持16个32位的通用寄存器。
通用寄存器除了用于数据运算和存储之外,还可以在函数调用过程中起到特殊作
用,ARM64架构的函数调用标准和规范对此有所约定,如图3-2所示。
在AArch64状态下,使用X表示64位通用寄存器,如X0、X30等。另外,还可以使
用W 表示X寄存器的低32位的数据,如W0表示X0寄存器的低32位数据,W1表示
X1寄存器的低32位数据,如图3-3所示。

第◆3 章 ARMv8 架构基础知识5 3 
图3-2 AArch64状态的31个通用寄存器
图3-3 64位通用寄存器和低32位数据
3.2.2 处理器状态寄存器
在ARMv7架构中,使用当前程序状态寄存器(currentprogramstatusregister, 
CPSR)表示当前的处理器状态(processorstate),而在AArch64中使用PSTATE寄存器
表示,如表3-1所示。
表3-1 PSTATE寄存器
分 类字 段描 述
条件标志位
N 
负数标志位
在结果是有符号的二进制代码的情况下,如果结果为负数,则N=1;如果
结果为非负数,则N=0 
Z 0标志位
如果结果为0,则Z=1;如果结果为非0,则Z=0 
C 
进位标志位
当发生无符号数溢出时,C=1 其他情况下,C=0 
V 
有符号数溢出标志位
对于加/减指令,在操作数和结果是有符号的整数时,如果发生溢出,则V 
=1;如果未发生溢出,则V=0 对于其他指令,V通常不发生变化

续表
5 4 ◆嵌入式系统开发与应用
分 类字 段描 述
运行状态控制
SS 软件单步。该位为1,说明在异常处理中使能了软件单步功能
IL 不合法的异常状态
nRW 
当前执行模式
0:处于AArch64状态
1:处于AArch32状态
EL 
当前异常等级
0:表示EL0 
1:表示EL1 
2:表示EL2 
3:表示EL3 
SP 
选择SP寄存器。当运行在EL0时,处理器选择EL0的SP寄存器,即SP_ 
EL0;当处理器运行在其他异常等级时,处理器可以选择使用SP_EL0或者
对应的SP_ELn寄存器
异常掩码标志位
D 调试位。使能该位可以在异常处理过程中打开调试断点和软件单步等
功能
A 用来屏蔽系统错误(SError) 
I 用来屏蔽IRQ 
F 用来屏蔽FIQ 
访问权限
PAN 
特权不访问(privilegedaccessnever)位是ARMv8.1的扩展特性
1:在EL1或者EL2访问属于EL0的虚拟地址时会触发一个访问权限
错误
0:不支持该功能,需要软件模拟
UAO 
用户特权访问覆盖标志位,是ARMv8.2的扩展特性
1:当运行在EL1或者EL2时,没有特权的加载存储指令可以和有特权
的加载存储指令一样访问内存,如LDTR指令
0:不支持该功能
3.2.3 特殊寄存器
ARMv8架构除了支持31个通用寄存器之外,还提供多个特殊寄存器,如图3-4 
所示。
1.零寄存器
ARMv8架构提供两个零寄存器(zeroregister),这些寄存器的内容全是0,可以用作
源寄存器,也可以用作目标寄存器。WZR寄存器是32位的零寄存器,XZR是64位的零
寄存器。

◆ 

第
3 
章 ARMv8 
架构基础知识55 


图3-
4 
特殊寄存器

2.PC寄存器
PC寄存器通常用来指向当前运行指令的下一条指令的地址,用于控制程序中指令
的运行顺序,但是编程人员不能通过指令直接访问它。

3.SP寄存器
ARMv8架构支持4个异常等级,每一个异常等级都有一个专门的SP寄存器SP_ 
ELn,例如处理器运行在EL1时选择SP_EL1寄存器作为SP寄存器。

.SPEL0:EL0下的SP寄存器。
.SPL1:EL1下的SP寄存器。E(_) 
.SPL2:EL2下的SP寄存器。E(_) 
.SP_L3:EL3下的SP寄存器
。
当处理器运行在比EL0高的异常等级时,处理器可以访问如下寄存器
。E(_) 
.当前异常等级对应的SP寄存器SP_ELn。
.EL0对应的SP寄存器SP_EL0可以当作一个临时寄存器,如Linux内核里使用
该寄存器存放进程的task_struct数据结构的指针。
当处理器运行在EL0时,它只能访问SP_EL0,而不能访问其他高级的SP寄存器。

4.保存处理状态寄存器
当运行一个异常处理器时,处理器的处理状态会保存到保存处理状态寄存器(saved 
processtatusregister,SPSR)中,这个寄存器非常类似于ARMv7架构中的CPSR 。当
异常将要发生时,处理器会把PSTATE寄存器的值暂时保存到SPSR中;当异常处理完
成并返回时,再把SPSR的值恢复到PSTATE寄存器。SPSR的格式如图3-5所示, 
SPSR的重要字段如表3-2所示。


◆

56 
嵌入式系统开发与应用


图3-
5 
SPSR 
的格式
表3-
2 
SPSR 
的重要字段

字段描述
N 负数标志位
Z 零标志位
C 进位标志位
V 有符号数溢出标志位
DIT 与数据无关的指令时序(dataindependenttiming),ARMv8.4的扩展特性
UAO 用户特权访问覆盖标志位,ARMv8.2的扩展特性
PAN 特权模式禁止访问(privilegedacesnever)位,ARMv8.1的扩展特性
SS 表示是否使能软件单步功能。若该位为1,说明在异常处理中使能了软件单步功能
IL 不合法的异常状态
D 调试位。使能该位可以在异常处理过程中打开调试断点和软件单步等功能
A 用来屏蔽系统错误
I 用来屏蔽IRQ 
F 用来屏蔽FIQ 
M[4] 用来表示异常处理过程中处于哪个执行状态,若为0,表示AArch64状态
M[3:0] 异常模式

5.ELR 
寄存器
该寄存器存放了异常返回地址。

6.CurentEL寄存器
该寄存器表示PSTATE寄存器中的EL字段,其中保存了当前异常等级。使用
MRS指令可以读取当前异常等级。

.0:表示EL0 。
.1:表示EL1 。
.2:表示EL2 。

第◆3 章 ARMv8 架构基础知识5 7 
.3:表示EL3。
7.DAIF寄存器
该寄存器表示PSTATE寄存器中的{D,A,I,F}字段。
8.SPSel寄存器
该寄存器表示PSTATE寄存器中的SP字段,用于在SP_EL0和SP_ELn中选择SP 
寄存器。
9.PAN 寄存器
该寄存器表示PSTATE寄存器中的PAN(privilegedaccessnever,特权禁止访问) 
字段。可以通过MRS和MSR指令设置PAN 寄存器。
10.UAO 寄存器
该寄存器表示PSTATE寄存器中的UAO(useraccessoverride,用户访问覆盖)字
段。可以通过MRS和MSR指令设置UAO 寄存器。
11.NZCV 寄存器
该寄存器表示PSTATE寄存器中的{N,Z,C,V }字段。
3.2.4 系统寄存器
除了上面介绍的通用寄存器和特殊寄存器之外,ARMv8架构还定义了很多的系统
寄存器,通过访问和设置这些系统寄存器可以完成对处理器不同功能的配置。在
ARMv7架构中,需要通过访问CP15协处理器间接访问这些系统寄存器,而在ARMv8 
架构中没有协处理器,可直接访问系统寄存器。ARMv8架构支持如下7类系统寄存器。
. 通用系统控制寄存器。
. 调试寄存器。
. 性能监控寄存器。
. 活动监控寄存器。
. 统计扩展寄存器。
. RAS寄存器。
. 通用定时器寄存器。
系统寄存器支持不同异常等级的访问,通常系统寄存器会使用Reg_ELn的方式
表示。.
Reg_ELl:处理器处于EL1、EL2以及EL3时可以访问该寄存器。
. Reg_EL2:处理器处于EL2和EL3时可以访问该寄存器。
. 大部分系统寄存器不支持处理器处于EL0时访问,但也有一些例外,如CTR_ 
EL0寄存器。

5 8 ◆嵌入式系统开发与应用
程序可以通过MRS和MSR指令访问系统寄存器。 
MRS X0, TTBR0_EL1 //把TTBR0_EL1 的值复制到x0 寄存器
MSR TTBR0_EL1, X0 //X0 寄存器的值复制到TTBR0_EL1 
3.3 A64 指令集
指令集是处理器体系结构设计的重点之一。ARM 公司定义和实现的指令集一直在
变化和发展中。ARMv8体系结构最大的改变是增加了一个新的64位指令集,这是早前
ARM 指令集的有益补充和增加,它可以处理64位宽的寄存器和数据,并且使用64位的
指针访问内存。这个新的指令集称为A64指令集,运行在AArch64状态。ARMv8兼容
旧的32位指令集———A32指令集,它运行在AArch32状态。A64和A32指令集并不兼
容,它们是两套不同的指令集,指令编码是不一样的。需要注意的是,A64指令集支持64 
位宽的数据和地址寻址,但其编码宽度是32位,而不是64位。A64指令集具有以下特
点:具有特有的指令编码格式;只能运行在AArch64状态;指令的宽度为32位。
下面以前变基模式的LDR指令为例,介绍A64指令集的编码风格,如图3-6所示。
图3-6 前变基模式的LDR 指令的编码
第0~4位为Rt字段,用来描述目标寄存器Xt,可以从X0~X30中选择。
第5~9位为Rn字段,用来描述基地址寄存器Xn,可以从X0~X30中选择,也可以
选择SP寄存器作为第31个寄存器。
第12~20位为imm9字段,用于偏移量imm。
第21~29位用于指令分类。
第30~31位为size字段,当size为0b11时,表示64位宽数据;当size为0b10时,表
示32位宽数据。
A64指令集可以分为如下几类: 
. 内存加载和存储指令; 
. 多字节内存加载和存储指令; 
. 算术和移位指令; 
. 移位操作指令; 
. 位操作指令; 
. 条件操作指令; 
. 跳转指令; 
. 独占访问指令;

第◆3 章 ARMv8 架构基础知识5 9 
. 内存屏障指令; 
. 异常处理指令; 
. 系统寄存器访问指令。
3.3.1 加载与存储指令
和早期的ARM 体系结构一样,ARMv8体系结构基于指令加载和存储体系结构。
在这种体系结构下,所有的数据处理都需要在通用寄存器中完成,而不能直接在内存中
完成。因此,首先把待处理数据从内存加载到通用寄存器,然后进行数据处理,最后把数
据写入内存。
常见的内存加载指令是LDR 指令,内存写入指令是STR 指令。LDR 指令和STR 
指令的基本格式如下。 
LDR 目标寄存器, <存储器地址> //把存储器地址中的数据加载到目标寄存器中
STR 源寄存器, <存储器地址> //把源寄存器的数据存储到存储器中
1.基地址模式的寻址
基地址模式首先是使用寄存器的值表示一个地址,然后把这个内存地址的内容加载
到通用寄存器中。
以下指令以Xn寄存器中的内容作为内存地址,加载此内存地址的内容到Xt寄
存器。 
LDR Xt, [Xn] 
以下指令是把Xt寄存器中的内容存储到Xn寄存器的内存地址中。 
STR Xt, [Xn] 
2.基地址加偏移量模式的寻址
基地址加偏移量模式是指在基地址的基础上再加上偏移量,从而计算内存地址,并
把这个内存地址的值加载到通用寄存器中,偏移量可以是正数,也可以是负数。
以下指令是把Xn寄存器的内容加上一个偏移量(offset必须是8的倍数),以相加的
结果作为基地址,加载此内存地址的内容到Xt寄存器。 
LDR Xt, [Xn, #offset] 
基地址加偏移量模式的存储指令格式如下。该指令是将Xt寄存器中的值存储到以
Xn寄存器的值加一个偏移量(offset必须是8的倍数)表示的地址中。 
STR Xt, [Xn, #offset] 
3.基地址扩展模式的寻址
基地址扩展模式的命令如下。

6 0 ◆嵌入式系统开发与应用 
LDR <Xt>, [<Xn>, (<Xm>){,<extend>{<amount>}}] 
STR <Xt>, [<Xn>, (<Xm>){,<extend>{<amount>}}] 
Xt:目标寄存器。
Xn:基地址寄存器。
Xm:用来表示偏移的寄存器。
extend:扩展/移位指示符,默认是LSL,也可以是UXTW、SXTW、SXTX。
amount:索引偏移量,amount的值只能是0或者3,如果是其他值,汇编器将报错。
【例3-1】 如下代码使用了基于基地址加偏移量模式。 
LDR X0, [X1] //内存地址为X1 寄存器的值,加载此内存地址的值到X0 寄存器
LDR X0, [X1,#8] 
//内存地址为X1 寄存器的值再加上偏移量(8),加载此内存地址的值到X0 寄存器
LDR X0,[X1,X2] 
//内存地址为X1 寄存器的值加X2 寄存器的值,加载此内存地址的值到X0 寄存器
LDR X0,[X1,X2,LSL #3] 
//内存地址为X1 寄存器的值加(X2 寄存器的值< < 3),加载此内存地址的值到X0 
寄存器
LDR X0,[X1,W2,SXTW] 
//先对W2 的值做有符号的扩展,和X1 寄存器的值相加后,将结果作为内存地址, 
//加载此内存地址的值到X0 寄存器
LDR X0,[X1,W2,SXTW #3] 
//先对W2 的值做有符号的扩展,然后左移3 位,和X1 寄存器的值相加后,将
//结果作为内存地址,加载此内存地址的值到X0 寄存器
4.前变基模式的寻址
前变基模式指先更新偏移量地址,后访问内存地址。前变基模式的指令格式如下。
首先更新Xn|SP寄存器的值为Xn|SP寄存器的值加imm,然后以新的Xn|SP的值为内
存地址,加载该内存地址的值到Xt寄存器。 
LDR <Xt>, [<Xn|SP>, #<imm>]! 
以下指令首先更新Xn|SP的值为Xn|SP寄存器的值加imm,然后把Xt寄存器的值
存储到Xn|SP寄存器的新值为地址的内存单元中。 
STR <Xt>, [<Xn|SP>, #<imm>]! 
5.后变基模式的寻址
后变基模型指先访问内存地址,后更新偏移量地址。首先以Xn|SP寄存器的值为内
存地址,取该内存地址上的值到Xt寄存器,再更新Xn|SP寄存器的值为Xn|SP寄存器
的值加imm。

第◆3 章 ARMv8 架构基础知识6 1 
LDR <Xt>, [<Xn|SP>], #<imm> 
以下指令先将Xt寄存器的值存储到以Xn|SP寄存器的值为地址的内存单元中,然
后更新Xn|SP寄存器的值为Xn|SP寄存器的值加imm。 
STR <Xt>, [<Xn|SP>], #<imm> 
【例3-2】 如下代码使用了前变基模式和后变基模式。 
LDR X0, [X1, #8]! //前变基模式。先更新X1 寄存器的值为X1 寄存器的值加8 
//然后以新的X1 寄存器的值为内存地址,加载该内存地址的值到X0 寄存器中
LDR X0, [X1], #8 
//后变基模式。以X1 寄存器的值为内存地址,加载该内存地址的值到X0 寄存器,然后更
//新X1 寄存器的值为X1 寄存器的值加8 
6.PC相对地址模式的寻址
汇编代码中常常会使用标签(label)标记代码片段。LDR 指令还提供一种访问标签
的地址模式,指令的格式如下。这条指令驱动label所在内存地址的内容到Xt寄存器
中。但是这个label必须在当前PC地址前后1MB的范围内,如果超过这个范围,则汇编
器会报错。 
LDR <Xt>, <label> 
【例3-3】 如下LDR指令会把标签my_data的数据读出来。 
my_data: 
.word 0x40 
ldr x0, my_data //最终X0 寄存器的值为0x40 
【例3-4】 假设当前PC值为0x806E4,那么这条LDR指令读取0x806E4+0x20地
址的内容到X6寄存器中。 
#define MY_LABEL 0x20 
ldr x6,MY_LABEL 
7.多字节内存加载和存储
A32指令集提供LDM 和STM 指令以实现多字节内存加载与存储,而A64指令集
不再提供LDM 和STM 指令,而是提供LDP和STP指令。LDP和STP指令支持3种
寻址模式。
基地址偏移量模式LDP指令的格式如下,它以Xn|SP寄存器的值为基地址,然后读
取Xn|SP寄存器的值加imm 地址的值到Xt1寄器,读取Xn|SP寄存器的值加imm+8 
地址的值到Xt2寄存器中。 
LDP <Xt1>, <Xt2>, [<Xn|SP>{, #<imm>}]

6 2 ◆嵌入式系统开发与应用
基地址偏移量模式STP指令的格式如下,它以Xn|SP寄存器的值为基地址,然后把
Xt1寄存器的内容存储到[Xn|SP+imm]处,把Xt2寄存器的内容存储到[Xn|SP+imm 
+8]处。 
STP <Xt1>, <Xt2>, [<Xn|SP>{, #<imm>}] 
前变基模式LDP指令的格式如下,它先计算Xn寄存器的值加imm,并存储到Xn 
寄存器中,然后以Xn寄存器的最新值作为基地址,读取Xn寄存器的值加imm 地址的值
到Xt1寄存器,读取[Xn+imm+8]的值到Xt2寄存器中。Xn寄存器可以使用SP寄
存器。 
LDP <Xt1>, <Xt2>, [<Xn|SP>, #<imm>]! 
前变基模式STP指令的格式如下,它先计算Xn寄存器的值加imm,并存储到Xn寄
存器中,然后以Xn寄存器的最新值作为基地址,把Xt1寄存器的内容存储到Xn内存地
址处,把Xt2寄存器的值存储到Xn寄存器的值加8对应的内存地址处。 
STP <Xt1>, <Xt2>, [<Xn|SP>, #<imm>]! 
后变基模式LDP指令的格式如下,它以Xn寄存器的值为基地址,读取[Xn+imm] 
的值到Xt1寄存器,读取[Xn+imm+8]的值到Xt2寄存器中,最后更新Xn寄存器。Xn 
寄存器可以使用SP寄存器。 
LDP <Xt1>, <Xt2>, [<Xn|SP>], #<imm> 
后变基模式STP指令格式如下,它以Xn寄存器的值为基地址,把Xt1寄存器的内
容存储到Xn寄存器的值加imm 对应的内存地址处,Xt2寄存器的值存储到[Xn+imm 
+8]处,并更新Xn寄存器。Xn寄存器可以使用SP寄存器。 
STP <Xt1>, <Xt2>, [<Xn|SP>], #<imm> 
【例3-5】 如下代码使用了基地址偏移量模式和前变基模式。 
LDP X3, X7, [X0] 
//以X0 寄存器的值为内存地址,加载此内存地址的值到X3 寄存器中,然后以X0 寄存器的 
// 值加8 作为内存地址,加载此内存地址的值到X7 寄存器中 
LDP X1, X2, [X0, #0x10]! 
//前变基模式。先计算X0+0x10 作为X0 的值,然后以X0 寄存器的值作为内存地址
//加载此内存地址的值到X1 寄存器中,接着以X0 寄存器的值加8 作为内存地址
//加载此内存地址的值到X2 寄存器中
STP X1, X2, [X4] 
//存储X1 寄存器的值到地址为X4 寄存器的值的内存单元中 
// 然后存储X2 寄存器的值到地址为X4 寄存器的值加8 的内存单元中
8.不同位宽的加载与存储指令
LDR和STR指令根据不同的数据位宽有多种变种,如表3-3所示。

第◆3 章 ARMv8 架构基础知识6 3 
表3-3 不同位宽的LDR 和STR 指令
指 令描 述
LDR 数据加载指令
LDRSW 有符号的数据加载指令,单位为字
LDRB 数据加载指令,单位为字节
LDRSB 有符号的数据加载指令,单位为字节
LDRH 数据加载指令,单位为半字
LDRSH 有符号的数据加载指令,单位为半字
STR 数据存储指令
STRB 数据存储指令,单位为字节
STRH 数据存储指令,单位为半字
9.不可扩展的加载和存储指令
LDR指令中的基地址加偏移量模式为可扩展模式,即偏移量按照数据大小来扩展且
是正数,取值范围为0~32760。A64指令集还支持一种不可扩展模式的加载和存储指
令,即偏移量只能按照字节来扩展,可以是正数或者负数,取值范围为-256~255,例如
LDUR指令。因此,可扩展模式和不可扩展模式的区别在于是否按照数据大小进行扩
展,从而扩大寻址范围。
LDUR指令的格式如下。LDUR指令的意思是以Xn|SP寄存器的内容加一个偏移
量(imm)作为内存地址,加载此内存地址的内容(8字节数据)到Xt寄存器。 
LDUR <Xt>, [<Xn|SP>{, #<imm>}] 
同理,不可扩展模式的存储指令为STUR,其指令格式如下。STUR指令是把Xt寄
存器的内容存储到Xn|SP寄存器加上imm 偏移量的地方。 
STUR <Xt>, [<Xn|SP>{, #<imm>}] 
不可扩展模式的LDUR和STUR指令根据数据位宽有多种变种,如表3-4所示。
表3-4 不可扩展模式的LDUR 和STUR 指令
指 令描 述
LDUR 数据加载指令
LDURSW 有符号的数据加载指令,单位为字
LDURB 数据加载指令,单位为字节
LDURSB 有符号的数据加载指令,单位为字节
LDURH 数据加载指令,单位为半字

续表
6 4 ◆嵌入式系统开发与应用
指 令描 述
LDURSH 有符号的数据加载指令,单位为半字
STUB 数据存储指令
STURB 数据存储指令,单位为字节
STURH 数据存储指令,单位为半字
10.独占内存访问指令
ARMv8体系结构提供独占内存访问(exclusivememoryaccess)的指令。LDXR 指
令尝试在内存总线中申请一个独占访问的锁,然后访问一个内存地址。STXR指令往刚
才LDXR指令已经申请独占访问的内存地址中写入新内容。LDXR和STXR指令通常
组合使用以完成一些同步操作,如Linux内核的自旋锁。
另外,ARMv7和ARMv8还提供多字节独占内存访问指令,如表3-5所示。
表3-5 独占内存访问指令
指 令描 述
LDXR 独占内存访问指令。指令的格式如下
LDXR Xt,[Xn|SP{,#0}]; 
STXR 独占内存访问指令。指令的格式如下
STXR Ws,Xt,[Xn|SP{,#0}]; 
LDXP 多字节独占内存访问指令。指令的格式如下
LDXP Xt1,Xt2,[Xn|SP{,#0}]; 
STXP 多字节独占内存访问指令。指令的格式如下
STXP Ws,Xt1,Xt2,[Xn|SP{,#0}]; 
11.隐含加载-获取/存储-释放内存屏障原语
ARMv8体系结构提供一组新的加载和存储指令,其中包含内存屏障原语,如表3-6所示。
表3-6 隐含屏障原语的加载和存储指令
指令描 述
LDAR 加载-获取(load-acquire)指令。LDAR指令后面的读写内存指令必须在LDAR指令之后执行
STLR 存储-释放(store-release)指令。所有的加载和存储指令必须在STLR指令之前完成
12.非特权访问级别的加载和存储指令
ARMv8体系结构实现了一组非特权访问级别的加载和存储指令,它适用于在EL0 
进行的访问,如表3-7所示。

第◆3 章 ARMv8 架构基础知识6 5 
表3-7 非特权访问级别的加载和存储指令
指 令描 述
LDTR 非特权加载指令
LDTRB 非特权加载指令,加载1字节
LDTRSB 非特权加载指令,加载有符号的1字节
LDTRH 非特权加载指令,加载2字节
LDTRSH 非特权加载指令,加载有符号的2字节
LDTRSW 非特权加载指令,加载有符号的4字节
STTR 非特权存储指令,存储8字节
STTRB 非特权存储指令,存储1字节
STTRH 非特权存储指令,存储2字节 
当PSTATE寄存器中的UAO 字段为1时,在EL1和EL2执行这些非特权指令的
效果和执行特权指令是一样的,这个特性是在ARMv8.2的扩展特性中加入的。
3.3.2 算术与移位指令
1.条件操作码 
A64指令集沿用了A32指令集中的条件操作,在PSTATE寄存器中有4个条件标
志位,即N、Z、C、V,如表3-8所示。
表3-8 条件标志位
条件标志位描 述
N 负数标志(上一次运算结果为负值) 
Z 零结果标志(上一次运算结果为零) 
C 进位标志(上一次运算结果发生了无符号数溢出) 
V 溢出标志(上一次运算结果发生了有符号数溢出) 
常见的条件操作后缀如表3-9所示。
表3-9 常见的条件操作后缀
后 缀含义(整数运算) 条件标志位条 件 码
EQ 相等Z=1 0b0000 
NE 不相等Z=0 0b0001 
CS/HS 发生了无符号数溢出C=1 0b0010 
CC/LO 没有发生无符号数溢出C=0 0b0011

续表
6 6 ◆嵌入式系统开发与应用
后 缀含义(整数运算) 条件标志位条 件 码
MI 负数N=1 0b0100 
PL 正数或零N=0 0b0101 
VS 溢出V=1 0b0110 
VC 未溢出V=0 0b0111 
HI 无符号数大于(C==1)&&(Z==0) 0b1000 
LS 无符号数小于或等于(C==0)||(Z==1) 0b1001 
GE 有符号数大于或等于N==V 0b1010 
LT 有符号数小于N! =V 0b1011 
GT 有符号数大于(Z==0)&&(N==V) 0b1100 
LE 有符号数小于或等于(Z==1)||(N! =V) 0b1101 
AL 永远执行— 0b1110 
NV 永不执行— 0b1111 
2.加法与减法指令
1)ADD指令
普通的加法指令有下面几种用法。
. 使用立即数的加法。
. 使用寄存器的加法。
. 使用移位操作的加法。
使用立即数的加法指令格式如下,它的作用是把Xn|SP寄存器的值再加上立即数
imm,把结果写入Xd|SP寄存器。Shift表示可选项,默认表示算术左移操作。 
ADD <Xd|SP>, <Xn|SP>, #<imm>{, <shift>} 
【例3-6】 以下代码是使用立即数的加法指令示例。 
add x0, x1, #1 //把x1 寄存器的值加上立即数1,结果写入x0 寄存器
add x0, x1, #1 LSL 12 
//把立即数1 算术左移12 位,然后加上x1 寄存器的值,结果写入x0 寄存器
使用寄存器的加法指令格式如下。这条指令的作用是先对Rm 寄存器做一些扩展, 
例如左移操作,然后加上Xn|SP寄存器的值,把结果写入Xd|SP寄存器。 
ADD <Xd|SP>, <Xn|SP>, <Rm>{, <extend>{#<amount>}} 
【例3-7】 下面是使用寄存器的加法指令。 
add x0, x1, x2 //x0=x1+x2

第◆3 章 ARMv8 架构基础知识6 7 
add x0, x1, x2, LSL 2 //x0=x1+x2<<2 
【例3-8】 下面也是使用寄存器的加法指令。 
mov xl, #l 
mov x2, #0x108a 
add x0, x1, x2, UXTB 
add x0, x1, x2, SXTB 
上面的示例代码中,第3行的运行结果为0x8B,因为UXTB对X2寄存器的低8位数
据进行了无符号扩展,结果为0x8A,然后加上X1寄存器的值,最终结果为0x8B。在第4行
中,SXTB对X2寄存器的低8位数据进行有符号扩展,结果为0xFFFFFFFFFFFFFF8A,然
后加上X1寄存器的值,最终结果为0xFFFFFFFFFFFFFF8B。
使用移位操作的加法指令的格式如下。这条指令的作用是先对Xm 寄存器做一些
移位操作,然后加上Xn寄存器的值,结果写入Xd寄存器。 
ADD <Xd>,<Xn>,<Xm>{,<shift>#<amount>} 
【例3-9】 以下代码用于实现移位操作加法。 
add x0, x1, x2, LSL 3 //x0=x1+x2<<3 
2)ADDS指令
ADDS指令是ADD指令的变种,唯一的区别是指令执行结果会影响PSTATE寄存
器的N、Z、C、V 标志位,例如当计算结果发生无符号数溢出时,C=1。
【例3-10】 下面的代码使用了ADDS指令。 
mov x1, 0xFFFFFFFFFFFFFFFF 
adds x0, x1, #2 
mrs x2, nzcv 
X1的值(0xFFFFFFFFFFFFFFFF)加上立即数2一定会触发无符号数溢出,最终
X0寄存器的值为1,同时设置PSTATE寄存器的C标志位为1。通过读取NZCV 寄存
器进行判断,最终X2寄存器的值为0x20000000,说明第29位的C 字段置1,如图3-7 
所示。
图3-7 NZCV 寄存器
3)ADC指令
ADC是进位的加法指令,最终的计算结果需要考虑PSTATE寄存器的C标志位。
ADC指令的格式如下。Xd寄存器的值等于Xn寄存器的值再加上Xm 寄存器的值再加
上C,其中,C表示PSTATE寄存器的C标志位。 
ADC <Xd>, <Xn>, <Xm>

6 8 ◆嵌入式系统开发与应用
【例3-11】 如下代码使用了ADC指令。 
mov x1, 0xFFFFFFFFFFFFFFFF 
mov x2, #2 
adc x0, x1, x2 
mrs x3 nzcv 
ADC指令的计算过程是0xFFFFFFFFFFFFFFFF+2+C,因为0xFFFFFFFFFFFFFFFF+ 
2的过程中已经触发了无符号数溢出,C=1,所以最终计算X0寄存器的值为2。若读取
NZCV寄存器,会发现C标志位也被置位了。
4)SUB指令
普通的减法指令与加法指令类似,也有下面几种用法。
. 使用立即数的减法。
. 使用寄存器的减法。
. 使用移位操作的减法。
使用立即数的减法指令格式如下,它的作用是把Xn|SP 寄存器的值减去立即数
imm,结果写入Xd|SP寄存器。 
SUB <Xd|SP>, <Xn|SP>, #<imm>{, <shift>} 
【例3-12】 如下代码使用了SUB指令。 
Sub x0, x1, #1 //把x1 寄存器的值减去立即数1,结果写入x0 寄存器
sub x0, x1, #1, LSL 12 
//把立即数1 算术左移12 位,然后把x1 寄存器中的值减去立即数(1<<12),把结果值
//写入x0 寄存器
使用寄存器的减法指令格式如下。这条指令的作用是先对Rm 寄存器做一些扩展, 
例如左移操作,然后Xn|SP 寄存器的值减去Rm 寄存器的值,把结果写入Xd|SP 寄
存器。 
SUB <Xd|SP>,<Xn|SP>,<Rm>{,<extend>{#<amount>}} 
【例3-13】 如下代码使用了寄存器的减法指令。 
sub x0, xl, x2 //x0=x1-x2 
sub x0, xl, x2, LSL 2 //x0=x1-x2<<2 
【例3-14】 下面的代码也使用了寄存器的减法指令。 
mov x1, #1 
mov x2, #0x108a 
sub x0, x1, x2, UXTB 
sub x0, x1, x2, SXTB 
上面的示例代码中,UXTB对X2寄存器的低8位数据进行了无符号扩展,结果为
0x8A,然后计算1-0x8A 的值,最终结果为0xFFFFFFFFFFFFFF77。