《ARM 系列处理器应用技术完全手册》
分类:电子电工
日期:
点击:0
《ARM 系列处理器应用技术完全手册》
作者:华清远见
第 1 章 ARM 系列微处理器简介
专业始于专注 卓识源于远见
‐ 2 ‐
1.1 什么是 ARM
ARM(Advanced RISC Machines)有三种含义,它是一个公司的名称,是一类微处理器的通称,还是一种
技术的名称。
ARM 公司是微处理器行业的一家知名企业,设计了大量高性能、廉价、低耗能的 RISC(Reduced Instruction
Set Computing,精简指令集计算机处理器)芯片,并开发了相关技术和软件。ARM 处理器具有性能高、
成本低和能耗低的特点,适用于嵌入式控制、消费/教育类多媒体、DSP 和移动式应用等领域。
ARM 公司本身不生产芯片,靠转让设计许可,由合作伙伴公司来生产各具特色的芯片。ARM 这种商业模
式的强大之处在于其价格合理,它在全世界范围的合作伙伴超过 100 个,其中包括许多著名的半导体公司。
ARM 公司专注于设计,设计的芯片内核耗电少,成本低,功能强,特有 16/32 位双指令集。ARM 已成为
移动通信、手持计算和多媒体数字消费等嵌入式解决方案的 RISC 实际标准。
1.1.1 ARM 公司历史
1990 年 11 月 ARM 公司成立于英国,原名 Advanced RISC Machine 有限公司,是苹果电脑、Acorn 电
脑集团和 VLSI Technology 的合资企业。Acorn 曾推出世界首个商用单芯片 RISC 处理器,而苹果电脑
当时希望将 RISC 技术应用于自身系统,ARM 微处理器新标准因此应运而生。ARM 公司成功地研制了
首个低成本 RISC 架构,迅速在市场上崭露头角。与此同时,RISC 结构的竞争对手都着眼于提高性能,
发展适合高端工作站处理器的 RISC 结构。
1991 年 ARM 公司推出首个嵌入式 RISC 核心——ARM6™系列处理器后不久,VLSI 率先获得授权,一年
后夏普和 GEC Plessey 也成为授权用户。1993 年德州仪器和 Cirrus Logic 也签署了授权协议。从此 ARM 公
司的知识产权产品和授权用户都急剧增多。1993 年 Nippon Investment and Finance(NIF)成为 ARM 公司
股东后,ARM 公司开始向全球拓展,分别在亚洲、北美洲和欧洲设立了办事处。1998 年 4 月 ARM 公司
在伦敦证券交易所和纳斯达克交易所上市。
ARM 公司现已发展成为一家全球性大公司,公司在英国、法国和美国设有研发中心,在中国、法国、德
国、日本、韩国、以色列、英国和美国建立了销售、行政和技术支持办事处。ARM 中国—安谋咨询上海
有限公司于 2002 年 7 月成立。
1.1.2 ARM 的商业模式
ARM Holdings(伦敦证交所:ARM:纳斯达克:ARMHY)在半导体革新过程中初露峥嵘,被 Dataquest 誉
为世界第一的知识产权供应商。20 世纪 90 年代初,ARM 公司率先推出 32 位 RISC 微处理器芯片系统(SoC)
知识产权公开授权概念。ARM 公司通过出售芯片技术授权而非生产或销售芯片,建立起新型的微处理器
设计、生产和销售商业模式。采用 ARM 技术的微处理器遍及各类电子产品,在汽车电子、消费娱乐、成
像、工业控制、网络、存储安保和无线等领域 ARM 技术无处不在。
ARM 公司知识产权授权用户众多,其中包括世界顶级的半导体公司。全球 20 家最大的半导体厂商中有 19
家是 ARM 公司的用户。这些合作伙伴通过使用 ARM 公司低价、高效的 IP 核技术研制生产微处理器、外
围设备和系统芯片。迄今这些厂商共发售了超过 10 亿个 ARM 微处理器内核。
为支持和增补 ARM 公司的现有 RISC 微处理器内核和 SoC IP,公司开发了功能强大的软件。ARM 公司的
伙伴企业能够获得各种基于软件的 IP、操作系统端口和软件设计服务,从而大大降低产品开发风险,缩短
上市时间。
首先是 ARM PrimeXsys 平台。这是一种取出即用的 IP,以平台的形式为专门应用提供支持。第一个
PrimeXsys 平台是 2001 年 9 月推出的 PrimeXsys Wireless 平台。它是一个高集成度的可扩展平台,包
含了所有必需的硬件、软件和集成工具。ARM 公司的伙伴企业可以利用这个平台轻松开发一系列基于
ARM 处理器的面向应用的设备,既迅速,风险又低。
专业始于专注 卓识源于远见
‐ 3 ‐
ARM 公司推出的另一新技术是 Jazelle,这项技术能将 Java 技术和全球领先的 32 位嵌入式 RISC 结构结合
在一起,使平台开发人员能够在同一处理器上与现有操作系统、中间软件和应用编码同时运行 Java 应用程
序,从而提高性能,降低系统成本,比协处理器和双处理器解决方案能耗更低。
1.2 ARM 体系结构的命名规则
ARM 体系结构是 CPU 产品所使用的一种体系结构,ARM 公司开发了一套拥有知识产权的 RISC 体系结构
的指令集。每个 ARM 处理器都有一个特定的指令集架构,而一个特定的指令集架构又可以由多种处理器
实现。
特定的指令集架构随着嵌入式市场的发展而发展。由于所有产品均采用一个通用的软件体系,所以相同的
软件可在所有产品中运行(理论上如此)。
ARM 产品通常以 ARM【x】【y】【z】【T】【D】【M】【I】【E】【J】【F】【-S】形式出现。表 1.1 显示了 ARM
体系结构的命名规则中这些后缀的具体含义。
表 1.1
ARM 体系结构的命名规则
后 缀 变 量
含 义
x
系列,号如 ARM7、ARM9
y
存储管理/保护单元
z
Cache
T
Thumb16 位译码器
D
JTAG 调试器
M
快速乘法器
I
嵌入式跟踪宏单元
E
增强指令(基于 TDMI)
J
Jazelle 加速
F
向量浮点单元
S
可综合版本
另外,还有一些附加的要点:
① ARM7TDMI 之后的所有 ARM 内核,即使“ARM”标志后没有包含“TDMI”字符,也都默认包含了
TDMI 的功能特性;
② JTAG 是由 IEEE 1149.1 标准测试访问端口和边界扫描结构来描述的,它是 ARM 用来发送和接收处理
器内核与测试仪器之间调试信息的一系列协议;
③ 嵌入式 ICE 宏单元是建立在处理器内部用来设置断点和观察点的调试硬件;
④ 可综合,意味着处理器内核是以源代码形式提供的。这种源代码形式可被编译成一种易于 EDA 工具使
用的形式。
1.3 初识 ARM 系列处理器
ARM 处理器当前有 6 个产品系列:ARM7、ARM9、ARM9E、ARM10E、ARM11 和 SecurCore,其中 ARM11
为最近推出的产品。进一步的产品来自于合作伙伴,例如 Intel Xscale ARM7、ARM9、ARM9E、ARM10E
是 4 个通用处理器系列。每个系列提供一套特定的性能来满足设计者对功耗、性能、体积的需求。SecurCore
是第 5 个产品系列,是专门为安全设备而设计的。
表 1.2 总结了 ARM 各系列处理器所包含的不同类型。
专业始于专注 卓识源于远见
‐ 4 ‐
表 1.2
ARM 各系列处理器所包含的不同类型
ARM 系列
包 含 类 型
ARM7 系列
ARM7EJ-S
ARM7TDMI
ARM7TDMI-S
ARM720T
ARM9/9E 系列
ARM920T
续表
ARM 系列
包 含 类 型
ARM9/9E 系列
ARM922T
ARM926EJ-S
ARM940T
ARM946E-S
ARM966E-S
ARM968E-S
向量浮点运算(Vector Floating Point)系列
VFP9-S
VFP10
ARM10E 系列
ARM1020E
ARM1022E
ARM1026EJ-S
ARM11 系列
ARM1136J-S
ARM1136JF-S
ARM1156T2(F)-S
ARM1176JZ(F)-S
ARM11 MPCore
SecurCore 系列
SC100
SC110
SC200
SC210
其他合作伙伴产品
StrongARM
XScale
Cortex-M3
MBX
本节简要介绍 ARM 各个系列处理器的特点。
1.3.1 ARM7 系列
ARM7 内核采用冯·诺伊曼体系结构,数据和指令使用同一条总线。内核有一条 3 级流水线,执行 ARMv4
指令集。
ARM7 系列处理器主要用于对功耗和成本要求比较苛刻的消费类产品。其最高主频可以到达 130MIPS
(MIPS 指每秒执行的百万条指令数)。ARM7 系列包括 ARM7TDMI、ARM7TDMI-S、ARM7EJ-S 和
ARM720T 4 种类型,主要用于适应不同的市场需求。
ARM7 系列处理器主要具有以下特点:
成熟的大批量的 32 位 RICS 芯片;
最高主频到达 130MIPS;
功耗低;
代码密度高,兼容 16 位微处理器;
专业始于专注 卓识源于远见
‐ 5 ‐
开发工具多、EDA 仿真模型多;
调试机制完善;
提供 0.25µm、0.18µm 及 0.13µm 的生产工艺;
代码与 ARM9 系列、ARM9E 系列以及 ARM10E 系列兼容。
1.3.2 ARM9 系列
ARM9 系列于 1997 年问世。由于采用了 5 级指令流水线,ARM9 处理器能够运行在比 ARM7 更高的时钟
频率上,改善了处理器的整体性能;存储器系统根据哈佛体系结构(程序和数据空间独立的体系结构)重
新设计,区分了数据总线和指令总线。
ARM9 系列的第一个处理器是 ARM920T,包含独立的数据指令 Cache 和 MMU。此处理器能够被用在要
求有虚拟存储器支持的操作系统上。此系列的 ARM922T 是 ARM920T 的变种,只有一半大小的数据指令
Cache。
ARM940T 包含一个更小的数据指令 Cache 和一个 MPU。它是针对不要求运行操作系统的应用而设计的。
ARM920T、ARM940T 都执行 v4T 架构指令。
1.3.3 ARM9E 系列
ARM9 系列的下一个处理器是基于 ARM9E-S 内核的。这个内核是 ARM9 内核带有 E 扩展的一个可综合版
本。它有 ARM946E-S 和 ARM966E-S 两个变种。两者都执行 v5TE 架构指令。它们也支持可选的嵌入式跟
踪宏单元,支持开发者实时跟踪处理器上指令和数据的执行。当调试对时间敏感的程序段时,这种方法非
常重要。
ARM946E-S 包括 TCM、Cache 和一个 MPU。TCM 和 Cache 的大小可配置。该处理器是针对要求有确定
的实时响应的嵌入式控制而设计的。ARM966E-S 有可配置的 TCM,但没有 MPU 和 Cache 扩展。
ARM9 系列的 ARM926EJ-S 内核为可综合的处理器内核,发布于 2000 年。它是针对小型便携式 Java 设备,
诸如 3G 手机和 PDA 应用而设计的。ARM926EJ-S 是第一个包含 Jazelle 技术,可加速 Java 字节码执行的
ARM 处理器内核。它还有一个 MMU、可配置的 TCM 以及具有零或非零等待存储器的数据/指令 Cache。
1.3.4 ARM10 系列
ARM10 发布于 1999 年,具有高性能、低功耗的特点。它所采用的新的体系使其在所有 ARM 产品中具有
最高的 MIPS/MHz。它将 ARM9 的流水线扩展到 6 级,也支持可选的向量浮点单元 VFP,对 ARM10 的流
水线加入了第 7 段。VFP 明显增强了浮点运算性能并与 IEEE 754.1985 浮点标准兼容。
ARM10E 系列处理器采用了新的节能模式,提供了 64 位的 Load/Store 体系,支持包括向量操作的满足 IEEE
754 的浮点运算协处理器,系统集成更加方便,拥有完整的硬件和软件开发工具。ARM10E 系列包括
ARM1020E、ARM1022E 和 ARM1026EJ-S 3 种类型。
1.3.5 ARM11 系列
ARM1136J-S 发布于 2003 年,是针对高性能和高能效应而设计的。ARM1136J-S 是第一个执行 ARMv6 架
构指令的处理器。它集成了一条具有独立的 Load/Stroe 和算术流水线的 8 级流水线。ARMv6 指令包含了
针对媒体处理的单指令流多数据流扩展,采用特殊的设计改善视频处理能力。
专业始于专注 卓识源于远见
‐ 6 ‐
1.3.6 SecurCore 系列
SecurCore 系列处理器提供了基于高性能的 32 位 RISC 技术的安全解决方案。SecurCore 系列处理器除了具
有体积小、功耗低、代码密度高等特点外,还具有它自己特别优势,即提供了安全解决方案支持。下面总
结了 SecurCore 系列的主要特点:
① 支持 ARM 指令集和 Thumb 指令集,以提高代码密度和系统性能;
② 采用软内核技术以提供最大限度的灵活性,可以防止外部对其进行扫描探测;
③ 提供了安全特性,可以抵制攻击;
④ 提供面向智能卡和低成本的存储保护单元 MPU;
⑤ 可以集成用户自己的安全特性和其他的协处理器。
SecurCore 系列包含 SC100、SC110、SC200 和 SC210 4 种类型。
1.3.7 其他系列处理器
StrongARM 处理器最初是 ARM 公司与 Digital Semiconductor 公司合作开发的,现在由 Intel 公司单独许可。
在低功耗、高性能的产品中应用很广泛。它是哈佛架构的,具有独立的数据和指令 Cache,有 MMU(Memory
Management Unit)。StrongARM 是第一个包含 5 级流水线的高性能 ARM 处理器,但它不支持 Thumb 指令
集。
Intel 公司的 Xscale 是 Strong ARM 的后续产品,在性能上有显著改善。它执行 v5TE 架构指令,也是哈佛
结构的,类似于 StrongARM 也包含一个 MMU。
1.3.8 Cortex-M3 和 MPCore
为了适应市场的需要,ARM 推出了两个新的处理器:Cortex-M3 和 MPCore。Cortex-M3 主要针对微控制
器市场,而 MPCore 主要针对高端消费类产品。
Cortex-M3 改进了代码密度,减少了中断延时并有更低的功耗。Cortex-M3 中实现了最新了 Thumb-2 指令
集。MPCore 提供了 Cache 一致性,每个支持 1~4 个 ARM11 核,这种设计为现代消费类产品对性能和功
耗的需求作了很好的平衡。ARM 还引入了 L2Cache 控制器来改进系统的整体性能。
1.4 ARM 系列处理器的应用领域
1.4.1 ARM7 系列
ARM7 系列处理器主要应用于下面一些场合:
个人音频设备(MP3 播放器、WMA 播放器、AAC 播放器);
接入级的无线设备;
喷墨打印机;
数码照相机;
PDA。
专业始于专注 卓识源于远见
‐ 7 ‐
1.4.2 ARM9 系列
ARM9 系列处理器具体应用于下面一些场合:
下一代无线设备,包括视频电话和 PDA 等;
数字消费品,包括机顶盒、家庭网关、MP3 播放器和 MPEG4 播放器;
成像设备,包括打印机、数码照相机和数码摄像机;
汽车、通信和信息系统。
1.4.3 ARM9E 系列
ARM9E 系列处理器具体应用于下面一些场合:
下一代无线设备,包括视频电话和 PDA 等;
数字消费品,包括机顶盒、家庭网关、MP3 播放器和 MPEG4 播放器;
成像设备,包括打印机、数码照相机和数码摄像机;
存储设备,包括 DVD 或 HDD 等;
工业控制,包括电机控制等;
汽车、通信和信息系统的 ABS 和车体控制;
网络设备,包括 VoIP、WirelessLAN 和 xDSL 等。
1.4.4 ARM10E 系列
ARM10E 系列处理器具体应用于下面一些场合:
下一代无线设备,包括视频电话和 PDA、笔记本电脑和互联网设备;
数字消费品,包括机顶盒、家庭网关、MP3 播放器和 MPEG4 播放器;
成像设备,包括打印机、数码照相机和数码摄像机;
汽车、通信和信息系统等;
工业控制,包括马达控制等。
1.4.5 SecureCore 系列
SecureCore 系列处理器主要应用于一些安全产品及应用系统,包括电子商务、电子银行业务、网络、移动
媒体和认证系统等。
1.5 ARM 芯片的特点与选型
1.5.1 不同系列处理器间的比较
表 1.3 显示了 ARM7、ARM9、ARM10 及 ARM11 内核之间属性的比较。有些属性依赖于生产过程和工艺,
具体芯片需参阅其芯片手册。
表 1.3
ARM 系列处理器属性比较
专业始于专注 卓识源于远见
‐ 8 ‐
项 目
ARM7
ARM9
ARM10
ARM11
流水线深度
3 级
5 级
6 级
8 级
典型频率(MHz)
80
150
260
335
功耗(mw/ MHz)
0.06
0.19(+Cache)
0.5(+Cache)
0.4(+Cache)
MIPS/ MHz
0.97
1.1
1.3
1.2
架构
冯诺伊曼
哈佛
哈佛
哈佛
乘法器
8×32
8×32
16×32
16×32
表 1.4 总结了各种处理器的不同功能。
表 1.4
ARM 处理器不同功能特性
CPU 核
MMU/MPU
Cache
Jazelle
Thumb
指令集
E
ARM7TDMI
无
无
否
是
v4T
否
ARM7EJ-S
无
无
是
是
v5TEJ
是
ARM720T
MMU
统一 8KBCache
否
是
v4T
否
ARM920T
MMU
独立 16KB 指令和数据 Cache
否
是
v4T
否
ARM922T
MMU
独立 8KB 指令和数据 Cache
否
是
v4T
否
ARM926EJ-S
MMU
Cache 和 TCM 可配置
是
是
v5TEJ
是
ARM940T
MPU
独立 4KB 指令和数据 Cache
否
是
v4T
否
ARM946E-S
MPU
Cache 和 TCM 可配置
否
是
v5TE
是
ARM966E-S
无
Cache 和 TCM 可配置
否
是
v5TE
是
ARM1020E
MMU
独立 32KB 指令和数据 Cache
否
是
v5TE
是
ARM1022E
MMU
独立 16KB 指令和数据 Cache
否
是
v5TE
是
ARM1026EJ-S
MMU
Cache 和 TCM 可配置
是
是
v5TE
是
ARM1036J-S
MMU
Cache 和 TCM 可配置
是
是
v6
是
ARM1136JF-S
MMU
Cache 和 TCM 可配置
是
是
v6
是
1.5.2 ARM 芯片的选型
随着国内嵌入式应用领域的发展,ARM 芯片必然会获得广泛的重视和应用。但是由于 ARM 芯片有多达十
几种的芯核结构、70 多芯片生产厂家以及千变万化的内部功能配置组合,开发人员在选择方案时会有一定
的困难。所以对 ARM 芯片做对比研究是十分必要的。
1.ARM 芯片选择的一般原则
从应用角度看,在选择 ARM 芯片时应从以下几个方面考虑。
(1)ARM 芯核
如果希望使用 Windows CE 或 Linux 等操作系统以减少软件开发时间,就需要选择 ARM720T 以上带有
MMU(Memory Management Unit)功能的 ARM 芯片,ARM720T、StrongARM、ARM920T、ARM922T、
ARM946T 都带有 MMU 功能。而 ARM7TDMI 没有 MMU,不支持 Windows CE 和大部分的 Linux;但目
前有 uCLinux 等少数几种 Linux 不需要 MMU 的支持。
(2)系统时钟控制器
专业始于专注 卓识源于远见
‐ 9 ‐
系统时钟决定了 ARM 芯片的处理速度。ARM7 的处理速度为 0.97MIPS/MHz,常见的 ARM7 芯片系统主
时钟为 20~133MHz,ARM9 的处理速度为 1.1MIPS/MHz,常见的 ARM9 的系统主时钟为 100~233MHz,
ARM10 最高可以达到 700MHz。不同芯片对时钟的处理不同,有的芯片只有一个主时钟频率,这样的芯片
可能不能同时顾及 UART 和音频时钟准确性,如 Cirrus Logic 的 EP7312 等;有的芯片内部时钟控制器可
以分别为 CPU 核和 USB、UART、DSP、音频等功能部件提供同频率的时钟,如 PHILIPS 公司 SAA7750
等芯片。
(3)内部存储器容量
在不需要大容量存储器时,可以考虑选用有内置存储器的 ARM 芯片。表 1.5 列出了内置存储器的 ARM 芯
片。
表 1.5
内置存储器的 ARM 芯片
芯 片 型 号
供 应 商
Flash 容量
ROM 容量
SDAM 容量
AT91F40162
ATMEL
2MB
256KB
4KB
AT91FR4081
ATMEL
1MB
128KB
SAA7750
Philips
384KB
64KB
PUC3030A
Micornas
256KB
56KB
HMS30C7272
Hynix
192KB
LC67F500
Snayo
640KB
32KB
(4)USB 接口
许多 ARM 芯片内置有 USB 控制器,有些芯片甚至同时有 USB Host 和 USB Slave 控制器。表 1.6 显示了
内置 USB 控制器的 ARM 芯片。
表 1.6
内置 USB 控制器的 ARM 芯片
芯 片 型 号
ARM 内核
供 应 商
USB Slave
USB Host
IIS 接口
S3C2410
ARM920T
Samsung
1
2
1
S3C2400
ARM920T
Samsung
1
2
1
S5N8946
ARM7TDMI
Samsung
1
0
0
L7205
ARM720T
Linkup
1
1
0
L7210
ARM720T
Linkup
1
1
0
EP9312
ARM920T
Cirrus logic
0
3
1
Dragonball MX1
ARM920T
Motorola
1
0
1
SAA7750
ARM720T
Plilips
1
0
1
TMS320DSC2x
ARM7TDMI
TI
1
0
0
PUC3030A
ARM7TDMI
Micronas
1
0
5
ML67100
ARM7TDMI
OKI
1
0
0
ML7051LA
ARM7TDMI
OKI
1
0
0
SA-1100
StrongARM
Intel
1
0
0
续表
芯 片 型 号
ARM 内核
供 应 商
USB Slave
USB Host
IIS 接口
LH7979531
ARM7TDMI
Sharp
1
0
0
GMS320C7201
ARM720T
Hynix
1
0
1
(5)GPIO 数量
在某些芯片供应商提供的说明书中,往往申明的是最大可能的 GPIO 数量,但是有许多引脚是和地址线、
数据线、串口线等引脚复用的。这样在系统设计时需要计算实际可以使用的 GPIO 数量。
(6)中断控制器
专业始于专注 卓识源于远见
‐ 10 ‐
ARM 内核只提供快速中断(FIQ)和标准中断(IRQ)两个中断向量。但各个半导体厂家在设计芯片时加
入了自己定义的中断控制器,以便支持诸如串行口、外部中断、时钟中断等硬件中断。外部中断控制是选
择芯片必须考虑的重要因素,合理的外部中断设计可以很大程度地减少任务调度工作量。例如 PHILIPS 公
司的 SAA7750,所有 GPIO 都可以设置成 FIQ 或 IRQ,并且可以选择上升沿、下降沿、高电平和低电平 4
种中断方式。这使得红外线遥控接收、指轮盘和键盘等任务都可以作为背景程序运行。而 Cirrus Logic 公
司的 EP7312 芯片只有 4 个外部中断源,并且每个中断源都只能是低电平或高电平中断,这样在接收红外
线信号的场合必须用查询方式,浪费大量 CPU 时间。
(7)IIS(Integrate Interface of Sound)接口
即集成音频接口。如果设计音频应用产品,IIS 总线接口是必需的。
(8)nWAIT 信号
这是一个外部总线速度控制信号。不是每个 ARM 芯片都提供这个信号引脚,利用这个信号与廉价的 GAL
芯片就可以实现与符合 PCMCIA 标准的 WLAN 卡和 Bluetooth 卡的接口,而不需要外加高成本的 PCMCIA
专用控制芯片。另外,当需要扩展外部 DSP 协处理器时,此信号也是必需的。
(9)RTC(Real Time Clock)
很多 ARM 芯片都提供实时时钟功能,但方式不同。如 Cirrus Logic 公司的 EP7312 的 RTC 只是一个 32 位
计数器,需要通过软件计算出年月日时分秒;而 SAA7750 和 S3C2410 等芯片的 RTC 直接提供年月日时分
秒格式。
(10)LCD 控制器
有些 ARM 芯片内置 LCD 控制器,有的甚至内置 64KB 彩色 TFT LCD 控制器。在设计 PDA 和手持式显示
记录设备时,选用内置 LCD 控制器的 ARM 芯片(如 S3C2410)较为适宜。
(11)PWM 输出
有些 ARM 芯片有 2~8 路 PWM 输出,可以用于电机控制或语音输出等场合。
(12)ADC 和 DAC
有些 ARM 芯片内置 2~8 通道 8~12 位通用 ADC,可以用于电池检测、触摸屏和温度监测等。PHILIPS
的 SAA7750 更是内置了一个 16 位立体声音频 ADC 和 DAC,并且带耳机驱动。
(13)扩展总线
大部分 ARM 芯片具有外部 SDRAM 和 SRAM 扩展接口,不同的 ARM 芯片可以扩展的芯片数量即片选线
数量不同,外部数据总线有 8 位、16 位或 32 位。为某些特殊应用设计的 ARM 芯片(如德国 Micronas 的
PUC3030A)没有外部扩展功能。
(14)UART 和 IrDA
几乎所有的 ARM 芯片都具有 1~2 个 UART 接口,可以用于和 PC 机通信或用 Angel 进行调试。一般的
ARM 芯片通信波特率为 115200bit/s,少数专为蓝牙技术应用设计的 ARM 芯片的 UART 通信波特率可以
达到 920kbit/s,如 Linkup 公司 L7205。
(15)DSP 协处理器
表 1.7 总结了 ARM+DSP 结构的 ARM 芯片。
表 1.7
ARM+DSP 结构的 ARM 芯片
芯 片 型 号
供 应 商
DSP Core
DSP MIPS
应 用
TMS320DSC2X
TI
16bit C5000
500
数码照相机
Dragonball MX1
Motorola
24bit 56000
MP3 播放器
SAA7750
Philips
24bit EPIC
73
CD-MP3
VWS22100
Philips
16bit OAK
GSM
STLC1502
ST
D950
52
VoIP
GMS30C3201
Hynix
16bit Piccolo
AT75C220
ATMEL
16bit OAK
40
AT75C310
ATMEL
16bit OAK
专业始于专注 卓识源于远见
‐ 11 ‐
AT75C320
ATMEL
16bit OAK
40×2
L7205
Linkup
16bit Piccolo
60×2
无线应用
L7210
Linkup
16bit Piccolo
Quatro
OAK
16bit OAK
(16)内置 FPGA
有些 ARM 芯片内置有 FPGA,适合于通信等领域。表 1.8 总结了 ARM+FPGA 结构的 ARM 芯片。
表 1.8
ARM+FPGA 结构的 ARM 芯片
芯 片 型 号
供 应 商
ARM 芯核
FPGA 门数
引 脚 数
EPXA1
Altera
ARM922T
100000
484
EPXA4
Altera
ARM922T
400000
672
EPXA10
Altera
ARM922T
1000000
1020
TA7S20 系列
Triscend
ARM7TDMI
多种
多种
(17)时钟计数器和看门狗
一般 ARM 芯片都具有 2~4 个 16 位或 32 位时钟计数器和一个看门狗计数器。
(18)电源管理功能
ARM 芯片的耗电量与工作频率成正比,一般 ARM 芯片都有低功耗模式、睡眠模式和关闭模式。
(19)DMA 控制器
有些 ARM 芯片内部集成有 DMA(Direct Memory Access)接口,可以和硬盘等外部设备高速交换数据,
同时减少数据交换时对 CPU 资源的占用。
另外,还可以选择的内部功能部件有:HDLC、SDLC、CD-ROM Decoder、Ethernet MAC、VGA controller、
DC-DC。可以选择的内置接口有:IIC、SPDIF、CAN、SPI、PCI、PCMCIA。
最后需说明的是封装问题。ARM 芯片现在主要的封装有 QFP、TQFP、PQFP、LQFP、BGA、LBGA 等形
式,BGA 封装具有芯片面积小的特点,可以减少 PCB 板的面积,但是需要专用的焊接设备,无法手工焊
接。另外一般 BGA 封装的 ARM 芯片无法用双面板完成 PCB 布线,需要多层 PCB 板布线。
2.多芯核结构 ARM 芯片的选择
为了增强多任务处理能力、数学运算能力、多媒体以及网络处理能力,某些供应商提供的 ARM 芯片内置
多个芯核,目前常见的 ARM+DSP,ARM+FPGA,ARM+ARM 等结构。
(1)多 ARM 芯核
为了增强多任务处理能力和多媒体处理能力,某些 ARM 芯片内置多个 ARM 芯核。例如 Portal player
公司的 PP5002 内部集成了两个 ARM7TDMI 芯核,可以应用于便携式 MP3 播放器的编码器或解码器。
从科胜讯公司(Conexant)分离出来的专门致力于高速通信芯片设计生产的 MinSpeed 公司在其多款高
速通信芯片中集成了 2~4 个 ARM7TDMI 内核。
(2)ARM 芯核+DSP 芯核
为了增强数学运算功能和多媒体处理功能,许多供应商在其 ARM 芯片内增加了 DSP 协处理器。通常加入
的 DSP 芯核有 ARM 公司的 Piccolo DSP 芯核、OAK 公司 16 位定点 DSP 芯核、TI 的 TMS320C5000 系列
DSP 芯核和 Motorola 的 56K DSP 芯核等。见表 1.7。
(3)ARM 芯核+FPGA
为了提高系统硬件的在线升级能力,某些公司在 ARM 芯片内部集成了 FPGA。见表 1.8。
3.选择方案举例
专业始于专注 卓识源于远见
‐ 12 ‐
表 1.9 列举的最佳方案仅供参考,由于 SoC 集成电路的发展非常迅速,今天的最佳方案到明天就可能不是
最佳的了。因此任何时候在选择方案时,都应广泛搜寻一下主要的 ARM 芯片供应商,以找出最适合的芯
片。
表 1.9
最佳应用方案推荐
应 用
第 一 方 案
第 二 方 案
备 注
高档 PDA
S3C2410
Dragon ball MX1
便携 CD MP3 播放器
SAA7750
USB 和 CDROM 解码器
FLASH MP3 播放器
SAA7750
PUC3030A
内置 USB 和 FLASH
WLAN和BT应用产品
L7205,L7210
Dragon ball MX1
高速串口和 PCMCIA 接口
VoiceOver IP
STLC1502
数码照相机
TMS320DSC24
TMS320DSC21
内置高速图像处理 DSP
续表
应 用
第 一 方 案
第 二 方 案
备 注
便携式语音 email 机
AT75C320
AT75C310
内置双 DSP,可以分别处理 MODEM
和语音
GSM 手机
VWS22100
AD20MSP430
专为 GSM 手机开发
ADSL Modem
S5N8946
MTK-20141
电视机顶盒
GMS30C3201
VGA 控制器
3G 移动电话机
MSM6000
OMAP1510
10G 光纤通信
MinSpeed 公司系列 ARM 芯片
多 ARM 核+DSP 核
1.6 ARM 开发工具
用户选用 ARM 处理器开发嵌入式产品时,选择合适的开发工具可以加快开发进度,节省开发成本。根据
功能不同,ARM 应用软件的开发工具分别有编译软件、汇编软件、连接软件、调试软件、评估板、JTAG
仿真器和在线仿真器等,目前世界上大约有四十多家公司提供以上不同种类的开发产品。
Realview 系列开发工具的英文全称为 Realview Developer Suite,是 ARM 公司(www.arm.com)为方便用
户在 ARM 芯片上进行应用软件开发而推出的一整套集成开发工具。该套工具包括软件开发套件和硬件仿
真工具。经过 ARM 公司逐年的维护和更新,目前的最新版本为 3.0。
ARM RVDS 起源于 ARM ADS(ARM Developer Suite),它对一些 ADS 的模块进行了增强并替换了一些
ADS 的组成部分。它支持几乎所有的 ARM 处理器,包括最新的 ARMv6 体系结构。支持的操作系统除了
Windows 外,还有 Linux。
ARM RVDS 主要包括以下几部分。
1.Realview compilation Tools
Realview compilation Tools 由编译器、汇编器和连接器组成。ARM 公司针对 ARM 系列每一种结构都进行
了专门的优化和处理,这一点除了作为 ARM 结构的设计者 ARM 公司外,其他公司都无法办到。
Realview compilation Tools 主要包括以下组件:
ARM/Thumb 汇编器 armasm;
连接器 armlink;
格式转换工具 fromelf;
库管理器 armar;
C 和 C++应用程序库;
专业始于专注 卓识源于远见
‐ 13 ‐
工程管理。
这些工具的使用过程如图 1.1 所示。
以上工具为命令行开发工具,同时也被集成在它的 IDE 开发环境中。
图 1.1 ARM 开发工具组件使用过程
2.集成开发环境
(1)CodeWarrior
CodeWarrior 是 Metrowerks 公司一套比较著名的集成开发环境,是一个直观、易用的环境,集成了很多 ARM
开发工具。CodeWarrior 界面风格独特,如图 1.2 所示。
图 1.2 CodeWarrior 集成开发环境
专业始于专注 卓识源于远见
‐ 14 ‐
CodeWarrior 包含项目管理、代码生成、语法敏感编辑器、C/C++源文件浏览器、类浏览器以及文件比较器
等。项目管理有直观的 GUI,可以通过隐藏底层目录结构来简单地管理复杂的项目。强大的内置编辑器是
编写软件的理想工具。可配置的接口让用户可以根据喜好裁减外形,以提高效率。
(2)AXD
AXD 即 ARM 扩展调试器(ARM extended Debugger)是运行在主机上的嵌入式开发调试工具。其界面如
图 1.3 所示。
图 1.3 AXD 图形界面
AXD 包含新型的 GUI、图形窗口管理、数据显示、命令行接口等组件。它使用户不用改变调试器就可以
选择不同的调试目标,如 ARMulator、Angel 或 Multi_ICE 等,扩展了 ARM 调试目标接口。
3.Multi_ICE
Multi_ICE 是 ARM 公司自己的 JTAG 仿真器,其 JTAG 链时钟可以设置为 5kHz~10MHz。它支持 ARM7、
ARM9、ARM9E、ARM10 等 ARM 系列处理器。
Multi_ICE 主要有以下特点。
快速的下载和单步速度;
用户控制的输入、输出位;
可编程的 JTAG 位传送速率;
开放的接口,允许调试非 ARM 核和 DSP;
网络连接到多个调试器。
联系方式
集团官网:www.hqyj.com 嵌入式学院:www.embedu.org 移动互联网学院:www.3g-edu.org
企业学院:www.farsight.com.cn 物联网学院:www.topsight.cn 研发中心:dev.hqyj.com
集团总部地址:北京市海淀区西三旗悦秀路北京明园大学校内 华清远见教育集团
专业始于专注 卓识源于远见
‐ 15 ‐
北京地址:北京市海淀区西三旗悦秀路北京明园大学校区,电话:010-82600386/5
上海地址:上海市徐汇区漕溪路银海大厦 A 座 8 层,电话:021-54485127
深圳地址:深圳市龙华新区人民北路美丽 AAA 大厦 15 层,电话:0755-22193762
成都地址:成都市武侯区科华北路 99 号科华大厦 6 层,电话:028-85405115
南京地址:南京市白下区汉中路 185 号鸿运大厦 10 层,电话:025-86551900
武汉地址:武汉市工程大学卓刀泉校区科技孵化器大楼 8 层,电话:027-87804688
西安地址:西安市高新区高新一路 12 号创业大厦 D3 楼 5 层,电话:029-68785218
《ARM 系列处理器应用技术完全手册》
作者:华清远见
第 2 章 ARM 体系结构
专业始于专注 卓识源于远见
‐ 2 ‐
2.1 ARM 体系结构的特点
ARM 内核采用精简指令集结构(RISC,Reduced Instruction Set Computer)体系结构。RISC 技术产生于上
世纪 70 年代。其目标是设计出一套能在高时钟频率下单周期执行、简单而有效的指令集,RISC 的设计重
点在于降低硬件执行指令的复杂度,这是因为软件比硬件容易提供更大的灵活性和更高的智能。与其相对
的传统复杂指令级计算机(CISC)则更侧重于硬件执行指令的功能性,使 CISC 指令变得更复杂。
RISC 的设计思想主要有以下特性。
Load/Store 体系结构。
Load/Store 体系结构也称为寄存器/寄存器体系结构或者 RR 系统结构。在这类机器中,操作数和运算结果
不是通过主存储器直接取回而是借用大量标量和矢量寄存器来取回的。与 RR 体系结构相反,还有一种存
储器/存储器体系结构,在这种体系结构中,源操作数的中间值和最后的运算结果是直接从主存储器中取回
的。这类机器的缩写符号是 SS 体系结构。
固定长度指令。
固定长度指令使得机器译码变得比较容易。由于指令简单,需要更多的指令来完成相同的工作,但是随着
存储器存取速度的提高,处理器可以更快地执行较大代码段(即大量指令)。
硬联控制。
RISC 机以硬联控制指令为特点,而 CISC 的微代码指令则相反。使用 CISC(常常是可变长度的)指令集
时处理器的语义效率最大,而简单指令往往容易被机器翻译。像 CISC 那样通过执行较少指令来完成工作
未必省时,因为还要包括微代码译码所需要的时间。因此,由硬件实现指令在执行时间方面提供了更好的
平衡。除此之外,还节省了芯片上用于存储微代码的空间并且消除了翻译微代码所需的时间。
流水线。
指令的处理过程被拆分为几个更小的、能够被流水线并行执行的单元。在理想情况下,流水线每周期前进
一步,可获得更高的吞吐率。
寄存器。
RICS 处理器拥有更多的通用寄存器,每个寄存器都可存放数据或地址。寄存器可为所有的数据操作提供
快速的局部存储访问。
表 2.1 总结了 RISC 和 CISC 之间主要的区别。
表 2.1
RISC 和 CISC 之间主要的区别
指 标
RISC
CISC
指令集
一个周期执行一条指令,通过简单指令
的组合实现复杂操作;指令长度固定
指令长度不固定,执行需要多个周期
流水线
流水线每周期前进一步
指令的执行需要调用微代码的一个微程序
寄存器
更多通用寄存器
用于特定目的的专用寄存器
Load/Store 结构
独立的 Load 和 Store 指令完成数据在寄
存器和外部存储器之间的传输
处理器能够直接处理存储器中的数据
为了使 ARM 指令集能够更好地满足嵌入式应用的需要,ARM 指令集和单纯的 RISC 定义有以下几方面的
不同。
一些特定指令的周期数可变
并非所有的 ARM 指令都是单周期的。例如,多寄存器转载/存储的 Load/Store 指令的周期数就不确定,必
须根据被传送的寄存器个数而定。如果是访问连续的存储器地址,就可以改善性能,因为连续的存储器访
问通常比随机访问要快。同时,代码密度也得到了提高,因为在函数的起始和结尾,多个寄存器的传输是
很常用的操作。
内嵌桶形移位器产生更复杂的指令
专业始于专注 卓识源于远见
‐ 3 ‐
内嵌桶形移位器是一个硬件部件,在一个输入寄存器被一条指令使用之前,内嵌桶形移位器可以处理该寄
存器中的数据。它扩展了许多指令的功能,改善了内核的性能,提高了代码密度。
Thumb 指令集
ARM 处理器根据 RICS 原理设计,但是由于各种原因,在低代码密度上它比其他多数 RICS 要好一些,然
而它的代码密度仍不如某些 CISC 处理器。在代码密度重要的场合,ARM 公司在某些版本的 ARM 处理器
中加入了一个称为 Thumb 结构的新型机构。Thumb 指令集是原来 32 位 ARM 指令集的 16 位压缩形式,并
在指令流水线中使用了动态解压缩硬件。Thumb 代码密度优于多数 CISC 处理器达到的代码密度。
条件执行
只有当某个特定条件满足时指令才会被执行。这个特性可以减少分支指令数目,从而改善性能,提高代码
密度。
DSP 指令
一些功能强大的数字信号处理(DSP)指令被加入到标准的 ARM 指令中,以支持快速的 16×16 位乘法操
作及饱和运算。在某些应用中,传统的方法需要微处理器加上 DSP 才能实现。这些增强指令,使得 ARM
处理器也能够满足这些应用的需要。
综上所述,ARM 体系结构的主要特征如下:
大量的寄存器,它们都可以用于多种用途;
Load/Store 体系结构;
每条指令都条件执行;
多寄存器的 Load/Store 指令;
能够在单时钟周期执行的单条指令内完成一项普通的移位操作和一项普通的 ALU 操作;
通过协处理器指令集来扩展 ARM 指令集,包括在编程模式中增加了新的寄存器和数据类型。
如果把 Thumb 指令集也当作 ARM 体系结构的一部分,那么还可以加上:
在 Thumb 体系结构中以高密度 16 位压缩形式表示指令集。
2.2 流水线
2.2.1 流水线的概念与原理
处理器按照一系列步骤来执行每一条指令。典型的步骤如下:
① 从存储器读取指令(fetch);
② 译码以鉴别它是属于哪一条指令(dec);
③ 从指令中提取指令的操作数(这些操作数往往存在于寄存器中)(reg);
④ 将操作数进行组合以得到结果或存储器地址(ALU);
⑤ 如果需要,则访问存储器以存储数据(mem);
⑥ 将结果写回到寄存器堆(res)。
并不是所有的指令都需要上述每一个步骤,但是,多数指令需要其中的多个步骤。这些步骤往往使用不同
的硬件功能,例如,ALU 可能只在第 4 步中用到。因此,如果一条指令不是在前一条指令结束之前就开始,
那么在每一步骤内处理器只有少部分的硬件在使用。
有一种方法可以明显改善硬件资源的使用率和处理器的吞吐量,这就是当前一条指令结束之前就开始执
行下一条指令,即通常所说的流水线(Pipeline)技术。流水线是 RISC 处理器执行指令时采用的机制。
使用流水线,可在取下一条指令的同时译码和执行其他指令,从而加快执行的速度。可以把流水线看作
是汽车生产线,每个阶段只完成专门的处理器任务。
采用上述操作顺序,处理器可以这样来组织:当一条指令刚刚执行完步骤①并转向步骤②时,下一条指令
就开始执行步骤①。图 2.1 说明了这个过程。从原理上说,这样的流水线应该比没有重叠的指令执行快 6
倍,但由于硬件结构本身的一些限制,实际情况会比理想状态差一些。
专业始于专注 卓识源于远见
‐ 4 ‐
2.2.2 流水线的分类
从 Acorn Computer 公司在 1983~1985 年间开发的第一个 3µm 器件,到 ARM 公司在 1990~1995 年间开发
的 ARM6 和 ARM7,ARM 整数处理器核的组织结构变化很小,这些处理器都是采用 3 级流水线,而这一
时期 CMOS 工艺的发展,几乎将特征尺寸减少了一个数量级。因此,核的性能提高很快,但基本的操作原
理大部分没有变化。
图 2.1 流水线的指令执行过程
从 1995 年以来,ARM 公司推出了几个新的 ARM 核。它们采用 5 级流水线和哈佛架构,获得了显著的高
性能。例如,ARM9 增加了存储器访问段和回写段,这使得 ARM9 的处理能力可达到平均 1.1 Dhrystone1
MISP/MHz,与 ARM7 相比,指令吞吐量提高了约 13%。
注意
在许多高性能处理器内部,一级 Cache 一般都设置有两个,其中,一个是指令 Cache,另一个
是数据 Cache。这样可以减少取指令和读操作数的访问冲突,这种结构被称为哈佛架构。
把主存储器分成两个独立编址的存储器,一个专门存放指令,称为指令存储器,简称指存;另
一个专门存放操作数,称为数据存储器,简称数存。两个存储器可以同时访问,这样就解决了
取指令和读操作数的冲突。如果在此基础上规定在执行指令阶段产生的运算结果只写到通用寄
存器中,不写到主存,那么取指令、分析指令和执行指令就可以同时进行。
ARM10 更是把流水线增加到 6 级。ARM10 的平均处理能力达到 1.3 Dhrystone MISP/MHz,与 ARM7 相比,
指令吞吐量提高了约 34%。
注意
虽然 ARM9 和 ARM10 的流水线不同,但它们都使用了与 ARM7 相同的流水线执行机制,因此
ARM7 上的代码也可以在 ARM9 和 ARM10 上运行。
1.3 级流水线 ARM 组织
3 级流水线 ARM 组织如图 2.2 所示,其主要的组成如下:
① 处理器状态寄存器堆(Rigister Bank)。它有两个读端口和一个写端口,每个端口都可以访问任意寄存
器。另外还有附加的可以访问 PC 的一个读端口和一个写端口。
注意
PC 的附加写端口可以在取指地址增加后更新 PC,读端口可以在数据地址发出之后从新开始取
指。
② 桶形移位寄存器(Barrel Shifter)。它可以把一个操作数移位或循环移位任意位数。
1 Dhrystone 是测量处理器运算能力的最常见基准程序之一,Dhrystone 的计量单位为每秒计算多少次 Dhrystone。
专业始于专注 卓识源于远见
‐ 5 ‐
③ ALU。完成指令集要求的算术或逻辑功能。
地址寄存器
PC 写
PC 读
增值器
处理器状态寄存器堆
桶形移位
寄存器
运算器
数据输出寄存器
数据输入寄存器
指令译码器
和控制逻辑
图 2.2 3 级流水线 ARM 的组织
④ 地址寄存器(Address Register)和增值器(Incrementer)。可选择和保存所用的存储器地址并在需要时
产生顺序地址。
⑤ 数据输出寄存器(data-out register)和数据输入寄存器(data-in register)。用于保存传输到存储器和从
存储器输出的数据。
⑥ 指令译码器和相关的控制逻辑(instruction decode and control)。
例 2.1 显示了一条单周期指令在流水线上的执行过程。
【例 2.1】
ADD r1,r2
指令在流水线上的执行过程如图 2.3 所示。
专业始于专注 卓识源于远见
‐ 6 ‐
寄存器堆
ALU
移位寄存器
寄存器堆
移位寄存器
ALU
图 2.3 单周期指令在流水线上的执行过程
在 ADD 指令中,需要访问两个寄存器操作数,B 总线上的数据移位后与 A 总线上的数据在 ALU 中组合,
再将结果写回寄存器堆。在指令执行过程中,程序计数器的数据放在地址寄存器中,地址寄存器的数据送
入增值器。然后将增值后的数据拷贝到寄存器堆的 r15(程序计数器),同时还拷贝到地址寄存器,作为下
一次取指的地址。
到 ARM7 为止的 ARM 处理器使用简单的 3 级流水线,包括下列流水线级:
取指(fetch):从寄存器装载一条指令。
译码(decode):识别被执行的指令,并为下一个周期准备数据通路的控制信号。在这一级,指令占有
译码逻辑,不占用数据通路。
执行(excute):处理指令并将结果写回寄存器。
图 2.4 显示了 3 级流水线指令执行过程。
取指
译码
执行
图 2.4 3 级流水线
注意
在任一时刻,可能有 3 种不同的指令占有这 3 级中的每一级,因此,每一级中的硬件必须能够
独立操作。
当处理器执行简单的数据处理指令时,流水线使得平均每个时钟周期能完成 1 条指令。但 1 条指令需要 3
个时钟周期来完成,因此,有 3 个时钟周期的延时(latency),但吞吐率(throughput)是每个周期一条指
令。例 2.2 通过一个简单的例子说明了流水线的机制。
【例 2.2】
指令序列为:
ADD r1 r2
SUB r3 r2
CMP r1 r3
流水线指令序列如图 2.5 所示。
专业始于专注 卓识源于远见
‐ 7 ‐
预取 ADD
译码
执行
预取 SUB
译码
执行
预取 CMP
译码
执行
图 2.5 流水线指令顺序
在第一个周期,内核从存储器取出指令 ADD;在第二个周期,内核取出指令 SUB,同时对 ADD 译码;在
第三个周期,指令 SUB 和 ADD 都沿流水线移动,ADD 被执行,而 SUB 被译码,同时又取出 CMP 指令。
可以看出,流水线使得每个时钟周期都可以执行一条指令。
当执行多条指令时,流水线的执行不一定会如图 2.5 那么规则,图 2.6 显示了有 STR 指令的流水线状态。
图 2.6 含有存储器访问指令的流水线状态
图 2.6 中在单周期指令 ADD 后出现了一条数据存储指令 STR。访问主存储器的指令用阴影表示,可以看
出在每个周期都使用了存储器。同样,在每一个周期也使用了数据通路。在执行周期、地址计算和数据传
输周期,数据通路都是被占用的。在译码周期,译码逻辑负责产生下一周期用到的数据通路的控制信号。
注意
对于 STR 这种存储器访问指令,实际是在地址计算时由译码逻辑产生下一周期数据传输所需要
的数据通路控制信号。
在图 2.6 中的指令序列中,处理器的每个逻辑单元在每个指令都是活动的。可以看出流水线的执行与存储
器访问密切相关。存储器访问限制了程序执行必须花费的指令周期数。
ARM 的流水线执行模式导致了一个结果,就是程序计数器 PC(对使用者而言为 r15)必须在当前指令执
行前计数。例如,指令在其第一个周期为下下条指令取指,这就意味着 PC 必须指向当前指令的后 8 个字
节(其后的第 2 条指令)。
当程序中必须用到 PC 时,程序员要特别注意这一点。大多数正常情况下,不用考虑这一点,它由汇编器
或编译器自动处理这些细节。
例 2.3 显示了流水线下程序计数器 PC 的使用情况。
【例 2.3】
指令序列为:
0x8000 LDR pc,[pc,#0]
0x8004 NOP
0x8008 DCD jumpAdress
专业始于专注 卓识源于远见
‐ 8 ‐
当指令 LDR 处于执行阶段时,pc=address+8 即 0x8008。
2.5 级流水线 ARM 组织
所有的处理器都要满足对高性能的要求。直到 ARM7 为止,在 ARM 核中使用的 3 级流水线的性价比是很
高的。但是,为了得到更高的性能,需要重新考虑处理器的组织结构。执行一个给定的程序需要的时间由
下式决定:
Tprog = (Ninst×CPI)/ fclk
式中:
Ninst:表示在程序中执行的 ARM 指令数;
CPI:表示每条指令的平均时钟周期;
fclk:表示处理器的时钟频率。
因为对给定程序(假设使用给定的优化集并用给定的编译器来编译)Ninst 是常数,所以,仅有两种方法来
提供性能。
第一,提高时钟频率。时钟频率的提高,必然引起指令执行周期的缩短,所以要求简化流水线每一级的逻
辑,流水线的级数就要增加。
第二,减少每条指令的平均指令周期数 CPI。这就要求重新考虑 3 级流水线 ARM 中多于 1 个流水线周期
的实现方法,以便使其占有较少的周期,或者减少因指令相关造成的流水线停顿,也可以将两者结合起来。
3 级流水线 ARM 核在每一个时钟周期都访问存储器,或者取指令,或者传输数据。只是抓紧存储器不用
的几个周期来改善系统系统性能,效果是不明显的。为了改善 CPI,存储器系统必须在每个时钟周期中给
出多于一个的数据。方法是在每个时钟周期从单个存储器中给出多于 32 位数据,或者为指令或数据分别
设置存储器。
基于以上原因,较高性能的 ARM 核使用了 5 级流水线,而且具有分开的指令和数据存储器。把指令的执
行分割为 5 部分而不是 3 部分,进而可以使用更高的时钟频率,分开的指令和数据存储器使核的 CPI 明显
减少。
注意
分开的指令和数据存储器。一般是分开的 Cache 连接到统一的指令和数据存储器上。
在 ARM9TDMI 中使用了典型的 5 级流水线。ARM9TDMI 的组织结构如图 2.7 所示。
5 级流水线包括下面的流水线级:
取指(fetch):从存储器中取出指令,并将其放入指令流水线。
译码(decode):指令被译码,从寄存器堆中读取寄存器操作数。在寄存器堆中有 3 个操作数读端口,
因此,大多数 ARM 指令能在 1 个周期内读取其操作数。
执行(execute):将其中一个操作数移位,并在 ALU 中产生结果。如果指令是 Load 或 Store 指令,则
在 ALU 中计算存储器的地址。
缓冲/数据(buffer/data):如果需要则访问数据存储器,否则 ALU 只是简单地缓冲一个时钟周期。
回写(write-back):将指令的结果回写到寄存器堆,包括任何从寄存器读出的数据。
图 2.8 显示了 5 级流水线指令的执行过程。
专业始于专注 卓识源于远见
‐ 9 ‐
指令 Cache
指令译码
寄存器读
乘
+4
移位
+4
ALU
MUX
数据 Cache
寄存器写
移位/符号
扩展
pc+8
取指
指令译码
执行
缓冲/数据
回写
前变址
Load/Store
后变址
图 2.7 5 级流水线的组织结构
图 2.8 5 级流水线
在程序执行过程中,PC 值是基于 3 级流水线操作特性的。5 级流水线中提前 1 级来读取指令操作数,得到
的值是不同的(PC+4 而不是 PC+8)。这产生的代码不兼容是不容许的。但 5 级流水线 ARM 完全仿真 3
级流水线的行为。在取指级增加的 PC 值被直接送到译码级的寄存器,穿过两极之间的流水线寄存器。下
一条指令的 PC+4 等于当前指令的 PC+8,因此,未使用额外的硬件便得到了正确的 r15。
3.6 级流水线 ARM 组织
在 ARM10 中,将流水线的级数增加到 6 级,使系统的平均处理能力达到了 1.3Dhrystone MISP/MHz。图
2.9 显示了 6 级流水线上指令的执行过程。
图 2.9 6 级流水线
2.2.3 影响流水线性能的因素
专业始于专注 卓识源于远见
‐ 10 ‐
1.互锁
在典型的程序处理过程中,经常会遇到这样的情形,即一条指令的结果被用做下一条指令的操作数。如例
2.4 所示。
【例 2.4】
有如下指令序列:
LDR r0,[r0,#0]
ADD r0,r0,r1 ;在 5 级流水线上产生互锁
从例 2.4 中可以看出,流水线的操作产生中断,因为第一条指令的结果在第二条指令取数时还没有产生。
第二条指令必须停止,直到结果产生为止。
2.跳转指令
跳转指令也会破坏流水线的行为,因为后续指令的取指步骤受到跳转目标计算的影响,因而必须推迟。
但是,当跳转指令被译码时,在它被确认是跳转指令之前,后续的取指操作已经发生。这样一来,已
经被预取进入流水线的指令不得不被丢弃。如果跳转目标的计算是在 ALU 阶段完成的,那么,在得到
跳转目标之前已经有两条指令按原有指令流读取。
解决的办法是,如果有可能最好早一些计算转移目标,当然这需要硬件支持;如果转移指令具有固定格式,
那么可以在解码阶段预测跳转目标,从而将跳转的执行时间减少到单个周期。但要注意,由于条件跳转与
前一条指令的条件码结果有关,在这个流水线中,还会有条件转移的危险。
尽管有些技术可以减少这些流水线问题的影响,但是,不能完全消除这些困难。流水线级数越多,问题就
越严重。对于相对简单的处理器,使用 3~5 级流水线效果最好。
显然,只有当所有指令都依照相似的步骤执行时,流水线的效率达到最高。如果处理器的指令非常复杂,
每一条指令的行为都与下一条指令不同,那么就很难用流水线实现。
2.3 ARM 存储器
ARM 处理器内核广泛应用于嵌入式系统和其他行业应用中。为了适应不同系统的需要,ARM 采用了灵活
多样的存储管理体系。从平板式内存映射到灵活方便的 MMU 内存管理单元,用户可以根据自己的需要使
用不同的存储管理策略。
在 ARM 体系结构中可使用的存储管理策略包括:
多类型的存储单元(可以使用 SDRAM、FLASH 等);
Cache;
写缓存;
虚拟内存地址。
另外,内存映射 I/O 机制可以使开发者灵活、方便地增加大量外设。
可以通过下面的几种方法实现对存储系统的管理:
使能 Cache,加快存储器的访问速度;
启动虚拟地址到物理地址的映射;
使用“域管理”策略,对存储单元的访问进行保护;
对 I/O 映射地址空间的访问加以限制。
标准的对 ARM 处理器的存储管理是使用协处理器 CP15 来实现的。ARM 体系的存储系统将在第 15 章详
细介绍。
专业始于专注 卓识源于远见
‐ 11 ‐
2.4 I/O 管理
ARM 系统完成 I/O 功能的标准方法是使用存储器映射 I/O。这种方法使用特定的存储器地址。当从这些地
址加载或向这些地址存储时,它们提供 I/O 功能。某些 ARM 系统也可能有直接存储器访问(DMA,Direct
Memory Access)硬件。
外围设备(如串行线控制器)中包含一些寄存器。在存储器映射系统中,这些寄存器就像特定地址的存储
器一样。(在其他的系统组织中,I/O 功能可能与存储器件有不同的寻址空间。)串行线控制器可能有以下 5
种寄存器。
① 发送数据寄存器(只写):写入这个位置的数据被送往串行线。
② 接受数据寄存器(只读):保存从串行线送来的数据。
③ 控制寄存器(读/写):设置数据速率,管理 RTS(请求发送)和其他类似信号。
④ 中断使能寄存器(读/写):控制中断的硬件事件。
⑤ 状态寄存器(读/写):指示读数据是否有效、写缓存是否满等。
要接受数据,必须用软件适当地设置器件。通常在接收到有效数据或检测到错误时产生一个中断。中断程
序必须将数据复制到缓存器中并进行错误检测。
应该注意的是,存储器映射外围寄存器的行为与存储器不同。连续两次读数据寄存器,即使对该寄存器没
有写操作,其结果也很可能不同。而对真正存储器的读是幂等的(idempotent)(可多次重复读,结果一致)。
对外围寄存器的读操作可能清除当前值,致使下一次读结果不同。这种寄存器称为读敏感(read-sensitive)
的。
当涉及读敏感寄存器时,编程必须小心。特别是不能将这种寄存器的数据复制到 Cache 存储器。
在许多 ARM 系统中,不能在用户模式下访问 I/O 寄存器。要访问这些器件,只能通过监控调用(SWI)
或通过使用这种调用的 C 库函数。
注意
在 ARM 编程中,通常将存储器的 I/O 区域标记为非 Cache 区(uncacheable),并绕过 Cache 访
问。通常 Cache 与读敏感(read-sensitive)器件相互排斥。显示帧缓存器(DisplayFrame Buffers)
也需要仔细考虑,通常也设为不可 Cache 的。
2.5 ARM 开发调试方法
用户选用 ARM 处理器开发嵌入式系统时,选择合适的开发工具可以加快开发进度,节省开发成本。因此
一套含有编辑软件、编译软件、汇编软件、链接软件、调试软件、工程管理及函数库的集成开发环境(IDE)
一般来说是必不可少的,如 ARM 公司的 RealView 开发环境。至于嵌入式实时操作系统、评估板等其他开
发工具则可以根据应用软件规模和开发计划选用。
使用集成开发环境开发基于 ARM 的应用软件,包括编辑、编译、汇编、链接等工作全部在 PC 机上即可
完成,调试工作则需要配合其他的模块或产品方可完成。目前常见的调试方法有以下几种。
1.指令集模拟器
部分集成开发环境提供了指令集模拟器,可方便用户在 PC 机上完成一部分简单的调试工作。但是,由于
指令集模拟器与真实的硬件环境相差很大,因此即使用户使用指令集模拟器调试通过的程序也有可能无法
在真实的硬件环境下运行,用户最终必须在硬件平台上完成整个应用的开发。
2.驻留监控软件
专业始于专注 卓识源于远见
‐ 12 ‐
驻留监控软件(Resident Monitors)是一段运行在目标板上的程序,集成开发环境中的调试软件通过以太
网口、并行端口、串行端口等通信端口与驻留监控软件进行交互,由调试软件发布命令通知驻留监控软件
控制程序的执行、读写存储器、读写寄存器、设置断点等。
利用驻留监控软件是一种比较低廉有效的调试方式,不需要任何其他的硬件调试和仿真设备。ARM 公司
的 Angel 就是该类软件,大部分嵌入式实时操作系统也采用该类软件进行调试,不同的是在嵌入式实时操
作系统中,驻留监控软件是作为操作系统的一个任务存在的。
驻留监控软件的不便之处在于它对硬件设备的要求比较高,一般在硬件稳定之后才能进行应用软件的开
发,同时它占用目标板上的一部分资源,而且不能对程序的全速运行进行完全仿真,所以对一些要求严格
的情况不是很适合。
3.JTAG 仿真器
JTAG 仿真器也称为 JTAG 调试器,是通过 ARM 芯片的 JTAG 边界扫描口进行调试的设备。JTAG 仿真器
比较便宜,连接比较方便,通过现有的 JTAG 边界扫描口与 ARM 处理器核通信,属于完全非插入式(即
不使用片上资源)调试。它无需目标存储器,不占用目标系统的任何端口,而这些是驻留监控软件所必需
的。另外,由于 JTAG 调试的目标程序是在目标板上执行,仿真更接近于目标硬件,因此,许多接口问题,
如高频操作限制、AC 和 DC 参数不匹配、电缆长度的限制等被最小化了。使用集成开发环境配合 JTAG
仿真器进行开发是目前采用最多的一种调试方式。
4.在线仿真器
在线仿真器使用仿真头完全取代目标板上的 ARM 处理器,可以完全仿真 ARM 芯片的行为,提供更进一
步的调试功能。但这类仿真器为了能够全速仿真时钟速度高于 100MHz 的处理器,通常必须采用极其复杂
的设计和工艺,因而其价格比较昂贵。在线仿真器通常用在 ARM 的硬件开发中,在软件的开发中较少使
用。其价格高昂是在线仿真器难以普及的原因。
联系方式
集团官网:www.hqyj.com 嵌入式学院:www.embedu.org 移动互联网学院:www.3g-edu.org
企业学院:www.farsight.com.cn 物联网学院:www.topsight.cn 研发中心:dev.hqyj.com
集团总部地址:北京市海淀区西三旗悦秀路北京明园大学校内 华清远见教育集团
北京地址:北京市海淀区西三旗悦秀路北京明园大学校区,电话:010-82600386/5
上海地址:上海市徐汇区漕溪路银海大厦 A 座 8 层,电话:021-54485127
深圳地址:深圳市龙华新区人民北路美丽 AAA 大厦 15 层,电话:0755-22193762
成都地址:成都市武侯区科华北路 99 号科华大厦 6 层,电话:028-85405115
南京地址:南京市白下区汉中路 185 号鸿运大厦 10 层,电话:025-86551900
武汉地址:武汉市工程大学卓刀泉校区科技孵化器大楼 8 层,电话:027-87804688
专业始于专注 卓识源于远见
‐ 13 ‐
西安地址:西安市高新区高新一路 12 号创业大厦 D3 楼 5 层,电话:029-68785218
《ARM 系列处理器应用技术完全手册》
作者:华清远见
第 3 章 ARM 微处理器的编程模型
专业始于专注 卓识源于远见
‐ 2 ‐
3.1 数据类型
3.1.1 ARM 的基本数据类型
ARM 采用的是 32 位架构,ARM 的基本数据类型有以下 3 种。
Byte:字节,8bit。
Halfword:半字,16bit(半字必须于 2 字节边界对齐)。
Word:字,32bit(字必须于 4 字节边界对齐)。
存储器可以看作是序号为 0~232−1 的线性字节阵列。图 3.1 所示为 ARM 存储器的组织结构。
字3
字2
字1
字节4
字节3
字节2
字节1
半字2
半字1
图 3.1 ARM 存储器组织结构
图 3.1 所示为存储器的一小片区域,其中每一个字节都有惟一的地址。字节可以占用任一位置,图中给出
了几个例子。长度为 1 个字的数据项占用一组 4 字节的位置,该位置开始于 4 的倍数的字节地址(地址最
末两位为 00)。图 3.1 中包含了 3 个这样的例子。半字占有两个字节的位置,该位置开始于偶数字节地址
(地址最末一位为 0)。
注意
① ARM 系统结构 v4 以上版本支持以上 3 种数据类型,v4 以前版本仅支持字节和字。
② 当将这些数据类型中的任意一种声明成 unsigned 类型时,N 位数据值表示范围为 0~2n−1
的非负数,通常使用二进制格式。
③ 当将这些数据类型的任意一种声明成 signed 类型时,N 位数据值表示范围为−2n−1~2n−1−1
的整数,使用二进制的补码格式。
④ 所有数据类型指令的操作数都是字类型的,如“ADD r1,r0,#0x1”中的操作数“0x1”
就是以字类型数据处理的。
⑤ Load/Store 数据传输指令可以从存储器存取传输数据,这些数据可以是字节、半字、字。加
载时自动进行字节或半字的零扩展或符号扩展。对应的指令分别为 LDR/BSTRB(字节操作)、
LDRH/STRH(半字操作)、LDR/STR(字操作)。详见后面的指令参考。
⑥ ARM 指令编译后是 4 个字节(与字边界对齐)。Thumb 指令编译后是 2 个字节(与半字边
界对齐)。
3.1.2 浮点数据类型
浮点运算使用在 ARM 硬件指令集中未定义的数据类型。
尽管如此,但 ARM 公司在协处理器指令空间定义了一系列浮点指令。通常这些指令全部可以通过未定义
指令异常(此异常收集所有硬件协处理器不接受的协处理器指令)在软件中实现,但是其中的一小部分也
可以由浮点运算协处理器 FPA10 以硬件方式实现。
专业始于专注 卓识源于远见
‐ 3 ‐
另外,ARM 公司还提供了用 C 语言编写的浮点库作为 ARM 浮点指令集的替代方法(Thumb 代码只能使
用浮点指令集)。该库支持 IEEE 标准的单精度和双精度格式。C 编译器有一个关键字标志来选择这个历程。
它产生的代码与软件仿真(通过避免中断、译码和浮点指令仿真)相比既快又紧凑。
3.1.3 存储器大/小端
从软件角度看,内存相对于一个大的字节数组,其中每个数组元素(字节)都是可寻址的。
ARM 支持大端模式(big-endian)和小端模式(little-endian)两种内存模式。
图 3.2 和图 3.3 分别显示了内存的大端模式和小端模式。
MSB
LSB
0
31
M+3
M+2
M+1
M+0
图 3.2 大端模式
图 3.3 小端模式
下面的例子显示了使用内存大/小端(big/little endian)的存取格式。
【例 3.1】
程序执行前:
r0=0x11223344
执行指令:
r1=0x100
STR r0,[r1]
LDRB r2,[r1]
执行后:
小端模式下:r2=0x44
大端模式下:r2=0x11
上面的例子向我们提示了一个潜在的编程隐患。在大端模式下,一个字的高地址放的是数据的低位,而在
小端模式下,数据的低位放在内存中的低地址。要小心对待存储器中一个字内字节的顺序。
3.2 处理器工作模式
专业始于专注 卓识源于远见
‐ 4 ‐
ARM 处理器共有 7 种工作模式,如表 3.1 所示。
表 3.1
ARM 处理器的工作模式
处理器工作模式
简 写
描 述
用户模式(User)
usr
正常程序执行模式,大部分任务执行在这种模式下
快速中断模式(FIQ)
fiq
当一个高优先级(fast)中断产生时将会进入这种模式,一
般用于高速数据传输和通道处理
外部中断模式(IRQ)
irq
当一个低优先级(normal)中断产生时将会进入这种模式,
一般用于通常的中断处理
特权模式(Supervisor)
svc
当复位或软中断指令执行时进入这种模式,是一种供操作系
统使用的保护模式
数据访问中止模式(Abort)
abt
当存取异常时将会进入这种模式,用于虚拟存储或存储保护
未定义指令中止模式(Undef)
und
当执行未定义指令时进入这种模式,有时用于通过软件仿真
协处理器硬件的工作方式
系统模式(System)
sys
使用和 User 模式相同寄存器集的模式,用于运行特权级操
作系统任务
除用户模式外的其他 6 种处理器模式称为特权模式(Privileged Modes)。在这些模式下,程序可以访问所
有的系统资源,也可以任意地进行处理器模式切换。其中的 5 种又称为异常模式,分别为:
FIQ(Fast Interrupt reQuest);
IRQ(Interrupt request);
管理(Supervisor);
中止(Abort);
未定义(Undefined)。
处理器模式可以通过软件控制进行切换,也可以通过外部中断或异常处理过程进行切换。
大多数的用户程序运行在用户模式下。当处理器工作在用户模式时,应用程序不能够访问受操作系统保护
的一些系统资源,应用程序也不能直接进行处理器模式切换。当需要进行处理器模式切换时,应用程序可
以产生异常处理,在异常处理过程中进行处理器模式切换。这种体系结构可以使操作系统控制整个系统资
源的使用。
当应用程序发生异常中断时,处理器进入相应的异常模式。在每一种异常模式中都有一组专用寄存器以供
相应的异常处理程序使用,这样就可以保证在进入异常模式时用户模式下的寄存器(保存程序运行状态)
不被破坏。
系统模式,不能有任何异常进入。仅 ARM 体系结构 v4 及以上版本有该模式。它和用户模式具有完全相同
的寄存器。但是系统模式属于特权模式,可以访问所有的系统资源,也可以直接进行处理器模式切换,它
主要供操作系统任务使用。通常操作系统的任务需要访问所有的系统资源,同时该任务仍然使用用户模式
的寄存器组而不是异常模式下相应的寄存器组,这样可以保证当异常中断发生时任务状态不被破坏。
3.3 ARM 寄存器组织
ARM 处理器有 37 个 32 位长的寄存器。
1 个用作 PC(Program Counter)。
1 个用作 CPSR(Current Program Status Register)。
5 个用作 SPSR(Saved Program Status Registers)。
30 个用作通用寄存器。
注意
以上 37 个寄存器中,1 个 CPSR 和 5 个 SPSR 通称为状态寄存器,虽然这些寄存器是 32 位的,
但目前只使用了其中的 12 位。除了这 6 个状态寄存器外,其余的 31 个寄存器又称为通用寄存器。
专业始于专注 卓识源于远见
‐ 5 ‐
ARM 处理器共有 7 种不同的处理器模式,在每一种处理器模式中有一组相应的寄存器组。表 3.2 显示了
ARM 的寄存器组织概要。
表 3.2
寄存器组织概要
User
FIQ
IRQ
SVC
Undef
Abort
R0
User mode
R0~R7,R15,and
CPSR
User mode
R0~R12,R15
and CPSR
User mode
R0~R12,R15
and CPSR
User
mode
R0~R12,R15
and CPSR
User
mode
R0~R12,R15
and CPSR
R1
R2
R3
R4
R5
R6
R7
R8
R8
R9
R9
R10
R10
续表
User
FIQ
IRQ
SVC
Undef
Abort
R11
R11
R12
R12
R13(SP)
R13(SP)
R13
R13
R13
R13
R14(LR)
R14(LR)
R14
R14
R14
R14
R15(PC)
CPSR
SPSR
SPSR
SPSR
SPSR
SPSR
注意
System 模式使用和 User 模式相同的寄存器集
当前处理器的模式决定着哪组寄存器可操作,任何模式都可以存取。
相应的 r0~r12。
相应的 r13(the stack pointer, sp)和 r14(the link register, lr)。
相应的 r15(the program counter, pc)。
相应的 CPSR(current program status register, cpsr)。
特权模式(除 System 模式)还可以存取。
相应的 SPSR(saved program status register)。
3.3.1 通用寄存器
通用寄存器根据其分组与否和使用目的分为以下 3 类。
未分组寄存器(The unbanked registers),包括 r0~r7。
分组寄存器(The banked register),包括 r8~r14。
程序计数器(Program Counter),即 r15。
1.未分组寄存器
专业始于专注 卓识源于远见
‐ 6 ‐
未分组寄存器包括 r0~r7。顾名思义,在所有处理器模式下对于每一个未分组寄存器来说,指的都是同一
个物理寄存器。未分组寄存器没有被系统用于特殊的用途,任何可采用通用寄存器的应用场合都可以使用
未分组寄存器。但由于其通用性,在异常中断所引起的处理器模式切换时,其使用的是相同的物理寄存器,
所以也就很容易使寄存器中的数据被破坏。
2. 分组寄存器
r8~r14 是分组寄存器,它们每一个访问的物理寄存器取决于当前的处理器模式。
对于这些分组寄存器 r8~r12 来说,每个寄存器对应两个不同的物理寄存器。一组用于除 FIQ 模式外的所
有处理器模式,而另一组则专门用于 FIQ 模式。这样的结构设计有利于加快 FIQ 的处理速度。不同模式下
寄存器的使用,要使用寄存器名后缀加以区分,例如,当使用 FIQ 模式下的寄存器时,寄存器 r8 和寄存
器 r9 分别记做 r8_fiq, r9_fiq;当使用用户模式下的寄存器时,寄存器 r8 和 r9 分别记做 r8_usr, r9_usr 等。
在 ARM 体系结构中,r8~r12 没有任何指定的其他的用途,所以当 FIQ 中断到达时,不用保存这些通用寄
存器,也就是说 FIQ 处理程序可以不必执行保存和恢复中断现场的指令,从而可以使中断处理过程非常迅
速。所以 FIQ 模式常被用来处理一些时间紧急的任务,如 DMA 处理。
对于分组寄存器 r13 和 r14 来说,每个寄存器对应 6 个不同的物理寄存器。其中的一个是用户模式和系统
模式公用的,而另外 5 个分别用于 5 种异常模式。访问时需要指定它们的模式。名字形式如下:
r13_
r14_
其中可以是以下几种模式之一:usr、svc、abt、und、irp 及 fiq。
r13 寄存器在 ARM 中常用作堆栈指针,称为 SP。当然,这只是一种习惯用法,并没有任何指令强制性的
使用 r13 作为堆栈指针,用户完全可以使用其他寄存器作为堆栈指针。而在 Thumb 指令集中,有一些指令
强制性的将 r13 作为堆栈指针,如堆栈操作指令。
每一种异常模式拥有自己的 r13。异常处理程序负责初始化自己的 r13,使其指向该异常模式专用的栈地址。在
异常处理程序入口处,将用到的其他寄存器的值保存在堆栈中,返回时,重新将这些值加载到寄存器。通过这
种保护程序现场的方法,异常不会破坏被其中断的程序现场。
寄存器 r14 又被称为连接寄存器(Link Register,LR),在 ARM 体系结构中具有下面两种特殊的作用。
(1)每一种处理器模式用自己的 r14 存放当前子程序的返回地址。当通过 BL 或 BLX 指令调用子程序时,
r14 被设置成该子程序的返回地址。在子程序返回时,把 r14 的值复制到程序计数器 PC。典型的做法是使
用下列两种方法之一。
执行下面任何一条指令。
MOV PC, LR
BX LR
在子程序入口处使用下面的指令将 PC 保存到堆栈中。
STMFD SP!, {,LR}
在子程序返回时,使用如下相应的配套指令返回。
LDMFD SP!, {,PC}
(2)当异常中断发生时,该异常模式特定的物理寄存器 r14 被设置成该异常模式的返回地址,对于有些模
式 r14 的值可能与返回地址有一个常数的偏移量(如数据异常使用 SUB PC, LR,#8 返回)。具体的返回方
式与上面的子程序返回方式基本相同,但使用的指令稍微有些不同,以保证当异常出现时正在执行的程序
的状态被完整保存。
R14 也可以被用作通用寄存器使用。
专业始于专注 卓识源于远见
‐ 7 ‐
注意
当嵌套中断被允许时(即异常可重入),r13 和 r14 的使用要特别小心。例如,在用户模式下一
个 IRQ 中断发生,这时两种模式分别使用不同的 r13 和 r14,换句话讲,用户模式使用 r13_usr
和 r14_usr,而 IRQ 模式使用 r13_irq 和 r14_irq,这样不会造成寄存器使用冲突。但是,当程序
运行在 IRQ 模式下,又有 IRQ 中断进入,此时,第二级中断使用 r13_irq 和 r14_irq,冲掉了第
一级 IRQ 的堆栈指针和返回地址,导致程序异常。
解决办法是在第二级中断发生前,将第一级中断用到的寄存器压栈。
3.3.2 程序计数器 r15
程序计算器 r15 又被记为 PC。它有时可以被和 r0-r14 一样用作通用寄存器,但很多特殊的指令在使用 r15
时有些限制。当违反了这些指令的使用限制时,指令的执行结果是不可预知的。
程序计数器在下面两种情况下用于特殊的目的。
读程序计数器。
写程序计数器。
1.程序计数器读操作
由于 ARM 的流水线机制,指令读出的 r15 的值是指令地址加上 8 个字节。由于 ARM 指令始终是字对齐的,
所以读出的结果位[1∶0]始终是 0(但在 Thumb 状态下,指令为 2 字节对齐,bit[0]=0)。
读 PC 主要用于快速地对临近的指令或数据进行位置无关寻址,包括程序中的位置无关分支。
需要注意的是,当使用指令 STR 或 STM 对 r15 进行保存时,保存的可能是当前指令地址加 8 或当前指令
地址加 12。到底是哪种方式,取决于芯片的具体设计方式。当然,在同一个芯片中,要么采用当前指令地
址加 8,要么采用当前指令地址加 12,不可能有些指令采用当前地址加 8,有些采用当前地址加 12。程序
开发人员应尽量避免使用 STR 或 STM 指令来对 r15 进行操作。当不可避免要使用这种方式时,可以先通
过一小段程序来确定所使用的芯片是使用哪种方式实现的。例如:
SUB R1,PC,#4
;r1 中存放 STR 指令地址
STR PC,[R0]
;将 PC=STR 地址+offset 保存到 r0 中
LDR R0,[R0]
SUB R0,R0,R1
;offset=PC-STR 地址
2.程序计数器写操作
当指令向 r15 写入地址数据时,如果指令成功返回,它将使程序跳转到该地址执行。由于 ARM 指令是字
对齐的,写入 r15 的地址值应满足 bit[1:0]=0b00,具体的规则根据 ARM 版本的不同也有所不同:
对于 ARM 版本 3 以及更低的版本,写入 r15 的地址值 bit[1:0]被忽略,即写入 r15 的地址值将与
0xFFFFFFFC 做与操作。
对于 ARM 版本 4 以及更高的版本,程序必须保证写入 r15 寄存器的地址值的 bit[1:0]为 0b00,否则将
会产生不可预知的结果。
对于 Thumb 指令集来说,指令是半字对齐的。处理器将忽略 bit[0],即写入 r15 寄存器的值在写入前要先
和 0XFFFFFFFE 做与操作。
有些指令对 r15 的操作有特殊的要求。比如,指令 BX 利用 bit[0]来确定需要跳转到的子程序是 ARM 状态
还是 Thumb 状态。
注意
这种读取 PC 值和写入 PC 值的不对称操作需要特别注意。
专业始于专注 卓识源于远见
‐ 8 ‐
3.3.3 程序状态寄存器
当前程序状态寄存器 CPSR(Current Program Status Register)可以在任何处理器模式下被访问,它包含下
列内容。
ALU(Arithmetic Logic Unit)状态标志的备份。
当前的处理器模式。
中断使能标志。
设置处理器的状态(只在 4T 架构)。
每一种处理器模式下都有一个专用的物理寄存器作备份程序状态寄存器 SPSR(Saved Program Status
Register)。当特定的异常中断发生时,这个物理寄存器负责存放当前程序状态寄存器的内容。当异常处理
程序返回时,再将其内容恢复到当前程序状态寄存器。
注意
由于用户模式和系统模式不属于异常中断模式,所以它们没有 SPSR。当在用户模式或系统模
式中访问 SPSR,将会产生不可预知的结果。
CPSR 寄存器(和保存它的 SPSR 寄存器)中的位分配如图 3.4 所示。
0
M0
M1
M2
M3
M4
T
F
I
V
C
Z
N
1
2
3
4
5
6
7
28
29
30
31
模式位
状态位
FIQ使能位
IRQ使能位
溢出标志
进位标志
零标志
负数标志
图 3.4 程序状态寄存器格式
1.标志位
N(Negative)、Z(Zero)、C(Carry)和 V(oVerflow)通称为条件标志位。这些条件标志位会根据程序
中的算术或逻辑指令的执行结果进行修改,而且这些条件标志位可由大多数指令检测以决定指令是否执
行。
在 ARM 4T 架构中,所有的 ARM 指令都可以条件执行,而 Thumb 指令却不能。
各条件标志位的具体含义如下。
N
本位设置成当前指令运行结果的 bit[31]的值。当两个由补码表示的有符号整数运算时,N=1 表示运算的结
果为负数;N=0 表示结果为正数或零。
Z
Z=1 表示运算的结果为零,Z=0 表示运算的结果不为零。
注意
对于 CMP 指令,Z=1 表示进行比较的两个数相等。
C
下面分 4 种情况讨论 C 的设置方法。
① 在加法指令中(包括比较指令 CMN),当结果产生了进位,则 C=1,表示无符号数运算发生上溢出;
其他情况下 C=0。
专业始于专注 卓识源于远见
‐ 9 ‐
② 在减法指令中(包括比较指令 CMP),当运算中发生错位(即无符号数运算发生下溢出),则 C=0,;其
他情况下 C=1。
③ 对于在操作数中包含移位操作的运算指令(非加/减法指令),C 被设置成被移位寄存器最后移出去的位。
④ 对于其他非加/减法运算指令,C 的值通常不受影响。
V
下面分两种情况讨论 V 的设置方法。
① 对于加/减运算指令,当操作数和运算结果都是以二进制的补码表示的带符号的数时,V=1 表示符号位
溢出。
② 对于非加/减法指令,通常不改变标志位 V 的值(具体可参照 ARM 指令手册)。
尽管以上 C 和 V 的定义看起来颇为复杂,但使用时在大多数情况下用一个简单的条件测试指令即可,不需
要程序员计算出条件码的精确值即可得到需要的结果。
注意
下面两种情况会对 CPSR 的条件标志位产生影响。
1.比较指令(CMN、CMP、TEQ、TST)。
2.目的寄存器不是 r15 的算术逻辑运算和数据传输指令。这些指令可以通过在指令末尾加标志
“S”来通知处理器指令的执行结果影响标志位。
【例 3.2】
使用 SUBS 指令从寄存器 r1 中减去常量 1,然后把结果写回到 r1,其中 CPSR 的 Z 位将受到影响。
指令执行前:
CPSR 中 Z=0
r1=0x00000001
SUBS r1,r1,#1
SUB 指令执行结束后:
r1=0x0
CPSR 中 Z=1
目的寄存器是 r15 的带“位设置”的算术和逻辑运算指令,也可以将 SPSR 的值复制到 CPSR 中,这种操
作主要用于从异常中断程序中返回。
用 MSR 指令向 CPSR/SPSR 写进新值。
目的寄存器位 r15 的 MRC 协处理器指令通过这条指令可以将协处理器产生的条件标志位的值传送到 ARM
处理器。
在中断返回时,使用 LDR 指令的变种指令可以将 SPSR 的值复制到 CPSR 中。
2.Q 标志位
在带 DSP 指令扩展的 ARM v5 及更高版本中,bit[27]被指定用于指示增强的 DAP 指令是否发生了溢出,
因此也就被称为 Q 标志位。同样,在 SPSR 中 bit[27]也被称为 Q 标志位,用于在异常中断发生时保存和恢
复 CPSR 中的 Q 标志位。
在 ARM v5 以前的版本及 ARM v5 的非 E 系列处理器中,Q 标志位没有被定义。属于待扩展的位。
3.控制位
CPSR 的低 8 位(I、F、T 及 M[4∶0])统称为控制位。当异常发生时,这些位的值将发生相应的变化。另
外,如果在特权模式下,也可以通过软件编程来修改这些位的值。
① 中断禁止位
专业始于专注 卓识源于远见
‐ 10 ‐
I=1,IRQ 被禁止。
F=1,FIQ 被禁止。
② 状态控制位
T 位是处理器的状态控制位。
T=0,处理器处于 ARM 状态(即正在执行 32 位的 ARM 指令)。
T=1,处理器处于 Thumb 状态(即正在执行 16 位的 Thumb 指令)。
当然,T 位只有在 T 系列的 ARM 处理器上才有效,在非 T 系列的 ARM 版本中,T 位将始终为 0。
③ 模式控制位
M[4∶0]作为位模式控制位,这些位的组合确定了处理器处于哪种状态。表 3.3 列出了其具体含义。
只有表中列出的组合是有效的,其他组合无效。
表 3.3
状态控制位 M[4∶0]
M[4∶0]
处理器模式
可以访问的寄存器
0b10000
User
PC,r14~r0,CPSR
0b10001
FIQ
PC,r14_fiq~r8_fiq,r7~r0,CPSR,SPSR_fiq
0b10010
IRQ
PC,r14_irq~r13_irq,r12~r0,CPSR,SPSR_irq
0b10011
Supervisor
PC,r14_svc~r13_svc,r12~r0,CPSR,SPSR_svc
0b10111
Abort
PC,r14_abt~r13_abt,r12~r0,CPSR,SPSR_abt
0b11011
Undefined
PC,r14_und~r13_und,r12~r0,CPSR,SPSR_und
0b11111
System
PC,r14~r0,CPSR(ARM v4 及更高版本)
注意
由于用户模式(User)和系统模式(System)是非异常模式,所以没有单独的 SPSR 保存程
序状态字。在用户模式或系统模式下,读 SPSR 将返回一个不可预知的值,而写 SPSR 将被
忽略。
3.4 异常中断处理
异常或中断是用户程序中最基本的一种执行流程和形态。这部分主要对 ARM 架构下的异常中断做详细说
明。
ARM 有 7 种类型的异常,按优先级从高到低的排列如下:复位异常(Reset)、数据异常(Data Abort)、快
速中断异常(FIQ)、外部中断异常(IRQ)、预取异常(Prefetch Abort)、软件中断(SWI)和未定义指令异常
(Undefined instruction)。
注意
在 ARM 文档中,使用术语 Exception 来描述异常。Exception 主要是从处理器被动接受异常的
角度出发,而 Interrupt 带有向处理器主动申请的色彩。在本书中,对“异常”和“中断”不做
严格区分,两者都是指请求处理器打断正常的程序执行流程,进入特定程序循环的一种机制。
3.4.1 异常种类
ARM 体系结构中,存在 7 种异常处理。当异常发生时,处理器会把 PC 设置为一个特定的存储器地址。这
一地址放在被称为向量表(vector table)的特定地址范围内。向量表的入口是一些跳转指令,跳转到专门
处理某个异常或中断的子程序。
专业始于专注 卓识源于远见
‐ 11 ‐
存储器映射地址 0x00000000 是为向量表(一组 32 位字)保留的。在有些处理器中,向量表可以选择定位
在存储空间的高地址(从偏移量 0xffff0000 开始)。一些嵌入式操作系统,如 Linux 和 Windows CE 就要利
用这一特性。
表 3.4 列出了 ARM 的 7 种异常。
表 3.4
ARM 的 7 种异常
异 常 类 型
处理器模式
执行低地址
执行高地址
复位异常(Reset)
特权模式
0x00000000
0xFFFF0000
未定义指令异常(Undefined interrupt)
未定义指令中止模式
0x00000004
0xFFFF0004
软中断异常(Software Abort)
特权模式
0x00000008
0xFFFF0008
预取异常(Prefetch Abort)
数据访问中止模式
0x0000000C
0xFFFF000C
数据异常(Data Abort)
数据访问中止模式
0x00000010
0xFFFF0010
外部中断请求 IRQ
外部中断请求模式
0x00000018
0xFFFF0018
快速中断请求 FIQ
快速中断请求模式
0x0000001C
0xFFFF001C
异常处理向量表如图 3.5 所示。
当异常发生时,分组寄存器 r14 和 SPSR 用于保存处理器状态,操作伪指令如下。
R14_ = return link
SPSR_ = CPSR
CPSR[4∶0] = exception mode number
CPSR[5] = 0 /*进入 ARM 状态*/
If = = reset or FIQ then
CPSR[6] = 1
/*屏蔽快速中断 FIQ*/
CPSR[7] = 1
/*屏蔽外部中断 IRQ*/
PC = exception vector address
图 3.5 异常处理向量表
异常返回时,SPSR 内容恢复到 CPSR,连接寄存器 r14 的内容恢复到程序计数器 PC。
专业始于专注 卓识源于远见
‐ 12 ‐
1.复位异常
当处理器的复位引脚有效时,系统产生复位异常中断,程序跳转到复位异常中断处理程序处执行。复位异
常中断通常用在下面两种情况下。
系统上电。
系统复位。
当复位异常时,系统执行下列伪操作。
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
复位异常中断处理程序将进行一些初始化工作,内容与具体系统相关。下面是复位异常中断处理程序的主
要功能。
设置异常中断向量表。
初始化数据栈和寄存器。
初始化存储系统,如系统中的 MMU 等。
初始化关键的 I/O 设备。
使能中断。
处理器切换到合适的模式。
初始化 C 变量,跳转到应用程序执行。
2.未定义指令异常
当 ARM 处理器执行协处理器指令时,它必须等待一个外部协处理器应答后,才能真正执行这条指令。若
协处理器没有相应,则发生未定义指令异常。
未定义指令异常可用于在没有物理协处理器的系统上,对协处理器进行软件仿真,或通过软件仿真实现指
令集扩展。例如,在一个不包含浮点运算的系统中,CPU 遇到浮点运算指令时,将发生未定义指令异常中
断,在该未定义指令异常中断的处理程序中可以通过其他指令序列仿真浮点运算指令。
仿真功能可以通过下面步骤实现。
① 将仿真程序入口地址链接到向量表中未定义指令异常中断入口处(0x00000004 或 0xffff0004),并保存
原来的中断处理程序。
② 读取该未定义指令的 bits[27∶24],判断其是否是一条协处理器指令。如果 bits[27∶24]值为 0b1110 或
0b110x,该指令是一条协处理器指令;否则,由软件仿真实现协处理器功能,可以同过 bits[11∶8]来判断
要仿真的协处理器功能(类似于 SWI 异常实现机制)。
③ 如果不仿真该未定义指令,程序跳转到原来的未定义指令异常中断的中断处理程序执行。
当未定义异常发生时,系统执行下列的伪操作。
r14_und = address of next instruction after the undefined instruction
SPSR_und = CPSR
CPSR[4∶0] = 0b11011
/*进入未定义指令模式*/
CPSR[5] = 0
/*处理器进入 ARM 状态*/
专业始于专注 卓识源于远见
‐ 13 ‐
/*CPSR[6]保持不变*/
CPSR[7] = 1
/*禁止外设中断*/
If high vectors configured then
PC = 0xffff0004
Else
PC = 0x00000004
3.软中断 SWI
软中断异常发生时,处理器进入特权模式,执行一些特权模式下的操作系统功能。软中断异常发生时,处
理器执行下列伪操作。
r14_svc = address of next instruction after the SWI instruction
SPSR_und = 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.预取指令异常
预取指令异常使由系统存储器报告的。当处理器试图去取一条被标记为预取无效的指令时,发生预取异常。
如果系统中不包含 MMU 时,指令预取异常中断处理程序只是简单地报告错误并退出。若包含 MMU,引
起异常的指令的物理地址被存储到内存中。
预取异常发生时,处理器执行下列伪操作。
r14_svc = address of the aborted instruction + 4
SPSR_und = 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 产生。当数据访问指令
的目标地址不存在或者该地址不允许当前指令访问时,处理器产生数据访问中止异常。
当数据访问中止异常发生时,处理器执行下列伪操作。
r14_abt = address of the aborted instruction + 8
专业始于专注 卓识源于远见
‐ 14 ‐
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
当数据访问中止异常发生时,寄存器的值将根据以下规则进行修改。
① 返回地址寄存器 r14 的值只与发生数据异常的指令地址有关,与 PC 值无关。
② 如果指令中没有指定基址寄存器回写,则基址寄存器的值不变。
③ 如果指令中指定了基址寄存器回写,则寄存器的值和具体芯片的 Abort Models 有关,由芯片的生产商
指定。
④ 如果指令只加载一个通用寄存器的值,则通用寄存器的值不变。
⑤ 如果是批量加载指令,则寄存器中的值是不可预知的值。
⑥ 如果指令加载协处理器寄存器的值,则被加载寄存器的值不可预知。
6.外部中断 IRQ
当处理器的外部中断请求引脚有效,而且 CPSR 寄存器的 I 控制位被清除时,处理器产生外部中断 IRQ 异
常。系统中各外部设备通常通过该异常中断请求处理器服务。
当外部中断 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 控制位被清除时,处理器产生快速中断请求 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
专业始于专注 卓识源于远见
‐ 15 ‐
PC= 0xffff001c
Else
PC = 0x0000001c
3.4.2 异常优先级
每一种异常按表 3.5 中设置的优先级得到处理。
表 3.5
异常优先级
优 先 级
异 常
最高 1
复位异常
2
数据中止
3
快速中断请求
4
中断请求
5
预取指令异常
6
软件中断
最低 7
未定义指令
异常可以同时发生,处理器按表 3.5 的优先级顺序处理异常。例如,复位异常的优先级最高,处理器上电
时发生复位异常。所以当产生复位时,它将优先于其他异常得到处理。同样,当一个数据访问中止异常发
生时,它将优先于除复位异常外的其他所有异常。
优先级最低的 2 种异常是软件中断和未定义指令异常。因为正在执行的指令不可能既是一条 SWI 指令,又
是一条未定义指令,所以软件中断异常 SWI 和未定义指令异享有相同的优先级。
3.4.3 处理器模式和异常
每一种异常都会导致内核进入一种特定的模式。表 3.6 显示了 ARM 处理器异常及其对应的模式。此外,
也可以通过编程改变 CPSR,进入任何一种 ARM 处理器模式。
注意
用户和系统模式是仅有的不可通过异常进入的两种模式,也就是说,要进入这两种模式,必须
通过编程改变 CPSR。
表 3.6
ARM 处理器异常及其对应模式
异 常
模 式
用 途
快速中断请求
FIQ
进行快速中断请求处理
外部中断请求
IRQ
进行外部中断请求处理
SWI
SVC
进行操作系统的高级处理
复位
SVC
进行操作系统的高级处理
预取指令中止异常
ABORT
虚存和存储器保护
数据中止异常
ABORT
虚存和存储器保护
未定义指令
Undefined
软件模拟硬件协处理器
3.4.4 异常响应流程
专业始于专注 卓识源于远见
‐ 16 ‐
1.判断处理器状态
当异常发生时,处理器自动切换到 ARM 状态,所以在异常处理函数中要判断在异常发生前处理器是 ARM
状态还是 Thumb 状态。这可以通过检测 SPSR 的 T 位来判断。
通常情况下,只有在 SWI 处理函数中才需要知道异常发生前处理器的状态。所以在 Thumb 状态下,调用
SWI 软中断异常必须注意以下两点。
① 发生异常的指令地址为(lr-2)而不是(lr-4)。
② Thumb 状态下的指令是 16 位的,在判断中断向量号时使用半字加载指令 LDRH。
下面的例子显示了一个标准的 SWI 处理函数,在函数中通过 SPSR 的 T 位判断异常发生前的处理器状态。
T_bit EQU 0x20
; bit 5. SPSR 中的 ARM/Thumb 状态位,
:
:
SWIHandler
STMFD sp!, {r0-r3,r12,lr}
; 寄存器压栈,保护程序现场
MRS r0, spsr
; 读 SPSR 寄存器,判断异常发生前的处理器状态
TST r0, #T_bit
; 检测 SPSR 的 T 位,判断异常发生前是否为 Thumb 状态
LDRNEH r0,[lr,#-2]
; 如果是 Thumb 状态,使用半字加载指令读取发生异常的指令地址
BICNE r0,r0,#0xFF00
; .提取中断向量号.
LDREQ r0,[lr,#-4]
; 如果是 ARM 状态,使用字加载指令,读取发生异常的指令地址
BICEQ r0,r0,#0xFF000000
; 提取中断向量号并将中断向量号存入 r0
; r0 存储中断向量号
CMP r0, #MaxSWI
; 判断中断是否超出范围
LDRLS pc, [pc, r0, LSL#2]
; 如果未超出范围,跳转到软中断向量表 Switable
B SWIOutOfRange
; 如果超出范围,跳转到软中断越界处理程序
switable
DCD do_swi_1
DCD do_swi_2
:
:
do_swi_1
; 1 号软中断处理函数
LDMFD sp!, {r0-r3,r12,pc}^ ; Restore the registers and return.
; 恢复寄存器并返回
do_swi_2
; 2 号软中断处理函数
:
2.向量表
如前面介绍向量表时提到的,每一个异常发生时总是从异常向量表开始跳转。最简单的一种情况是向量表
里面的每一条指令直接跳向对应的异常处理函数。其中快速中断处理函数 FIQ_handler()可以直接从地址
0x1C 处开始,省下一条跳转指令,如图 3.6 所示。
专业始于专注 卓识源于远见
‐ 17 ‐
图 3.6 异常处理向量表
但跳转指令 B 的跳转范围为±32MB,但很多情况下不能保证所有的异常处理函数都定位在向量的 32MB
范围内,需要更大范围的跳转,而且由于向量表空间的限制,只能由一条指令完成。具体实现方法有下面
两种。
(1)MOV PC,#imme_value
这种办法将目标地址直接赋值给 PC。但这种方法受格式限制不能处理任意立即数。这个立即数由一个 8
位数值循环右移偶数位得到。
(2)LDR PC,[PC+offset]
把目标地址先存储在某一个合适的地址空间,然后把这个存储器单元的 32 位数据传送给 PC 来实现跳转。
这种方法对目标地址值没有要求。但是存储目标地址的存储器单元必须在当前指令的±4KB 空间范围内。
注意
在计算指令中引用 offset 数值的时候,要考虑处理器流水线中指令预取对 PC 值的影响。
3.4.5 从异常处理程序中返回
当一个异常处理返回时,一共有 3 件事情需要处理:通用寄存器的恢复、状态寄存器的恢复以及 PC 指针
的恢复。通用寄存器的恢复采用一般的堆栈操作指令即可,下面重点介绍状态寄存器的恢复以及 PC 指针
的恢复。
1.恢复被中断程序的处理器状态
PC 和 CPSR 的恢复可以通过一条指令来实现,下面是 3 个例子。
MOVS PC,LR
SUBS PC,LR,#4
LDMFD SP!,{PC}^
这几条指令是普通的数据处理指令,特殊之处在于它们把程序计数器寄存器 PC 作为目标寄存器,并且带
了特殊的后缀“S”或“^”。其中“S”或“^”的作用就是使指令在执行时,同时完成从 SPSR 到 CPSR 的
拷贝,达到恢复状态寄存器的目的。
专业始于专注 卓识源于远见
‐ 18 ‐
2.异常的返回地址
异常返回时,另一个非常重要的问题就是返回地址的确定。前面提到过,处理器进入异常时会有一个保存
LR 的动作,但是该保持值并不一定是正确中断的返回地址。以一个简单的指令执行流水状态图来对此加
以说明,如图 3.7 所示。
F
D
E
F
D
E
F
D
E
F
D
E
0x8000
A
0x8004
B
0x8008
C
0x800C
D
图 3.7 3 级流水线示例
在 ARM 架构里,PC 值指向当前执行指令地址加 8。也就是说,当执行指令 A(地址 0x8000)时,PC 等
于 0x8000+8=0x8008,即等于指令 C 的地址。假设指令 A 是 BL 指令,则当执行时,会把 PC 值(0x8008)
保存到 LR 寄存器。但是,接下来处理器会对 LR 进行一次自动调整,使 LR=LR-0x4。所以,最终保存
在 LR 里面的是图 3.5 中所示的 B 指令地址。所以当从 BL 返回时,LR 里面正好是正确的返回地址。
同样的跳转机制在所有的 LR 自动保存操作中都存在。当进入中断响应时,处理器对保存的 LR 也进行一
次自动调整,并且跳转动作也是 LR=LR-0x04。由此,就可以对不同异常类型的返回地址依次比较。
假设在指令 B 处(地址 0x8004)发生了异常,进入异常相应后,LR 经过跳转保存的地址值应该是 C 的地
址 0x8008。
(1)软中断异常
如果发生软中断异常,即指令 B 为 SWI 指令,从 SWI 中断返回后下一条执行指令就是 C,正好是 LR 寄
存器保存的地址,所以只有直接把 LR 恢复给 PC 即可。
(2)IRQ 或 FIQ 异常
如果发生的是 IRQ 或 FIQ 异常,因为外部中断请求中断了正在执行的指令 B,当中断返回后,需要重新回
到 B 指令执行,也就是说,返回地址应该是 B(0x8004),需要把 LR 减 4 送 PC。
(3)Data Abort 数据中止异常
在指令 B 处进入数据异常的相应,但导致数据异常的原因却应该是上一条指令 A。当中断处理程序恢复数
据异常后,要回到 A 重新执行导致数据异常的指令,因此返回地址应该是 LR 加 8。
为方便起见,表 3.7 总结了各异常和返回地址的关系
表 3.7
异常和返回地址
异 常
地 址
用 途
复位
-
复位没有定义 LR
数据中止
LR-8
指向导致数据中止异常的指令
FIQ
LR-4
指向发生异常时正在执行的指令
IRQ
LR-4
指向发生异常时正在执行的指令
预取指令中止
LR-4
指向导致预取指令异常的那条指令
SWI
LR
执行 SWI 指令的下一条指令
未定义指令
LR
指向未定义指令的下一条指令
3.4.6 在应用程序中安装异常处理程序
专业始于专注 卓识源于远见
‐ 19 ‐
1.使用汇编语言安装异常处理程序
如果系统启动不依赖于 Debug 或 Debug monitor 软件,可以使用汇编语言在系统启动时直接安装异常处理
程序。
下面的例子显示了系统从 0x0 地址启动,直接安装异常处理程序的方法。
Vector_Init_Block
LDR PC, Reset_Addr
LDR PC, Undefined_Addr
LDR PC, SWI_Addr
LDR PC, Prefetch_Addr
LDR PC, Abort_Addr
NOP
;保留向量
LDR PC, IRQ_Addr
LDR PC, FIQ_Addr
Reset_Addr DCD Start_Boot
Undefined_Addr DCD Undefined_Handler
SWI_Addr DCD SWI_Handler
Prefetch_Addr DCD Prefetch_Handler
Abort_Addr DCD Abort_Handler
DCD 0
;保留向量
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler
有些情况下,系统 0x0 地址不一定是 ROM。如果 0x0 地址为 RAM,那么就系统将中断向量表从 ROM 复
制 RAM,下面的例子显示了这样一个过程。
MOV R8, #0
ADR R9, Vector_Init_Block
LDMIA R9!,{r0-r7}
;复制中断向量表 (8 words)
STMIA R8!,{r0-r7}
LDMIA R9!,{r0-r7}
;复制由伪操作 DCD 定义的地址
STMIA R8!,{r0-r7}
注意
可以使用 Scatter 文件定义加载向量表的地址,这样上述代码的拷贝工作由 C 库函数完成。
2.使用 C 语言安装异常处理程序
程序中有时需要在 main()函数中使用 C 语言安装中断向量表。这就要求指令经编译后的解码能安装在内存
的正确位置。
(1)向量表中使用跳转指令的情况
如果在向量表中使用跳转指令,使用下面的步骤完成向量表的安装。
① 读取异常处理程序的地址。
② 从异常处理程序地址中减去向量表中的偏移。
③ 为适应指令流水线,将上一步得到的地址减 8。
④ 将得到的结果右移 2 位,得到以字为单位的地址偏移量。
⑤ 将结果的高 8 位清零,得到跳转指令的 24 位偏移量。
专业始于专注 卓识源于远见
‐ 20 ‐
⑥ 将上一步得到的结果和 0xea000000(无条件跳转指令编码)做逻辑与操作,从而得到要写到向量表中
的跳转指令的正确编码。
下面的例子显示了这样一个标准过程。
unsigned Install_Handler (unsigned routine, unsigned *vector)
{ unsigned vec, oldvec;
vec = ((routine - (unsigned)vector - 0x8)>>2);
if ((vec & 0xFF000000))
{
/* diagnose the fault */
prinf ("Installation of Handler failed");
exit (1);
}
vec = 0xEA000000 | vec;
oldvec = *vector;
*vector = vec;
return (oldvec);
}
(2)在向量表中使用加载 PC 指令
在向量表中使用加载 PC 指令,按照下面的步骤完成。
① 读取异常处理程序地址。
② 从异常处理程序地址中减去向量表中的偏移。
③ 为适应指令流水线,将上一步得到的地址减 8。
④ 保留结果的后 12 位。
⑤ 将结果与 0xe59ff000(LDR PC, [PC,#offset])做逻辑或操作,从而得到要写到向量表中的跳转指令的正
确编码。
⑥ 将异常处理程序的地址放到相应的存储单元。
下面的例子显示了一个标准的 C 语言过程。
unsigned Install_Handler (unsigned location, unsigned *vector)
{ unsigned vec, oldvec;
vec = ((unsigned)location - (unsigned)vector - 0x8) | 0xe59ff000;
oldvec = *vector;
*vector = vec;
return (oldvec);
}
3.4.7 FIQ 和 IRQ 中断处理函数的设计
1.中断分支
ARM 内核只有两个外部中断输入信号 nFIQ 和 nIRQ。但对于一个系统来说,中断源可能多达几十个。为
此,在系统集成的时候,一般都会有一个异常控制器来处理异常信号,如图 3.8 所示。
专业始于专注 卓识源于远见
‐ 21 ‐
图 3.8 中断系统
这时候用户程序可能存在多个 IRQ/FIQ 的中断处理函数。为了使从向量表开始的跳转始终能找到正确的处
理函数入口,需要设置一套处理机制和方法。
多数情况下是由软件来处理异常分支的,因为软件可以通过读取中断控制器来获得中断源的信息,如图 3.9
所示。
有些芯片可能支持特殊的硬件分支功能,这需要查看具体的芯片说明。
因为软件的灵活性,可以设计出比图 3.9 更好的流程控制方法,如图 3.10 所示。
Int_vector_table 是用户自己开辟的一块存储器空间,里面按次序存放异常处理函数的地址。IRQ_Handler()
从中断控制器获取中断源信息,然后再从 Int_vector_table 中的对应地址单元得到异常处理函数的入口地址,
完成一次异常响应的跳转。这种方法的好处是用户程序在运行过程中,能够很方便地动态改变异常服务内
容。
IRQ_Handler:
Switch(int_source)
{
Case1:
Case2:
……
}
IRQ
Int1_handler ()
Int2_handler ()
图 3.9 软件控制中断分支
IRQ_Handler:
Switch(int_source)
{ Case1:
Case2:
……
CaseN:
}
IRQ
Address of Intn_Handler ()
Address of Int2_Handler ()
Address of Int1_Handler ()
Int1_handler ()
Int2_handler ()
{
Int_vector_table
专业始于专注 卓识源于远见
‐ 22 ‐
图 3.10 灵活的软件分支设计
进入异常处理程序后,用户可以完全按照自己的意愿来进行程序设计,包括调用 Thumb 状态的函数等。但
对于绝大多数的系统来说,有两个步骤必须处理,一是现场保护,二是要把中断控制器中对应的中断状态
标识清除,表明该中断请求已经得到响应,否则,中断函数退出以后,又会被再一次触发,从而进入周而
复始的死循环。
2.ARM 编译器对中断处理函数编写的扩展
考虑到中断处理函数在现场保护和返回地址的处理上与普通函数的不同之处,不能直接把普通函数体连接
到异常向量表上,需要在上面加上一层封装,下面是一个例子。
IRQ_Handler
;中断相应函数
STMFD SP!,{r0-r12,lr}
;保护现场,一般只需要保护{r0-r3,LR}
BL IrqHandler
;进入普通处理函数,C 或汇编均可
……
LDMFD sp!,{r0-r12,LR}
;恢复现场
SUBS pc,lr,#4
;中断返回,注意返回地址
为了方便使用高级语言直接编写异常处理函数,ARM 编译器对此做了特定的扩展,可以使用函数声明关
键字_irq,这样编译出来的函数就可以满足异常响应对现场保护和恢复的需要,并且自动加入 LR 减 4 的处
理,符合 IQR 和 FIQ 中断处理的要求。
下面的例子显示了使用_irq 对中断处理函数产生的影响。
C 语言源程序如下。
__irq void IRQHandler (void)
{
volatile unsigned int *base = (unsigned int *) 0x80000000;
if (*base == 1)
{
/*调用 C 语言中断处理函数*/
C_int_handler();
}
/*清楚中断标志*/
*(base+1) = 0;
}
使用 armcc 编译出的汇编代码如下。
IRQHandler PROC
STMFD sp!,{r0-r4,r12,lr}
MOV r4,#0x80000000
LDR r0,[r4,#0]
SUB sp,sp,#4
CMP r0,#1
BLEQ C_int_handler
MOV r0,#0
STR r0,[r4,#4]
ADD sp,sp,#4
LDMFD sp!,{r0-r4,r12,lr}
SUBS pc,lr,#4
ENDP
专业始于专注 卓识源于远见
‐ 23 ‐
如果不使用_irq 子程序声明关键字,编译出的汇编代码如下。
IRQHandler PROC
STMFD sp!,{r4,lr}
MOV r4,#0x80000000
LDR r0,[r4,#0]
CMP r0,#1
BLEQ C_int_handler
MOV r0,#0
STR r0,[r4,#4]
LDMFD sp!,{r4,pc}
ENDP
3.可重入中断设计
在缺省情况下,ARM 中断是不可重入的。因为一旦进入异常响应状态,ARM 自动关闭中断使能。如果在
异常处理过程中,简单地打开中断使能而发生中断嵌套时,显然新的异常处理将破坏原来的中断现场而导
致出错。但有时需要中断必须是可重入的,因此要通过程序设计来解决这个问题。其中有两个关键问题。
① 新中断使能之前,必须要保护好前一个中断的现场信息。比如 LR_irq 和 SPSR_irq 等,这一点比较容易
做的。
② 中断处理过程中对 BL 进行保护。
在中断处理函数中发生函数调用 BL 是很常见的,假设有下面一种情况。
IRQ_Handler:
……
BL Foo
ADD
其中,
Foo:
STMFD SP!,{r0-r3,LR}
……
LDMFD SP!{r0-r3,PC}
上述程序,在 IRQ 处理函数 IRQ_Handler()中调用了函数 Foo()。若是在 IRQ_Handler()里面中断可重入的
话,可能发生问题,考察下面的情况:当新的中断请求恰好在“BL Foo”指令执行完成后发生。这时候
LR_irq 寄存器(因在 IRQ 模式下,所以是 LR_irq)的值将调整为 BL 指令的下一条指令(ADD)地址,
使其能从 Foo()正确返回;但是因为这时候发生了中断请求,接下来要进行新中断的响应,处理器在新中
断响应过程中也要进行 LR_irq 保存。这次对 LR_irq 的操作发生了冲突,当新中断返回后,往下执行 STMFD
指令,这时候压栈的 LR 已不是原来的 ADD 指令地址,从而使子程序 Foo()无法正确返回。
这个问题无法通过增加额外的现场保护指令来解决。一个办法就是在重新使能中断之前改变处理器模式,
也就是使上面程序的“BL Foo”指令不要运行在 IRQ 模式下。这样当新的中断发生时,就不会造成 LR
寄存器的冲突。考虑 ARM 的所有运行模式,采用 SYSTEM 模式是比较合适的,因为它是特权模式,不是
IRQ 模式,与中断响应无关。
下面的例子显示了标准的 IRQ/FIQ 异常中断处理程序。
PRESERVE8
AREA INTERRUPT, CODE, READONLY
IMPORT C_irq_handler
IRQ
专业始于专注 卓识源于远见
‐ 24 ‐
SUB lr, lr, #4
;跳转返回地址
STMFD sp!, {lr}
;保存返回地址
MRS r14, SPSR
;读取 SPSR
STMFD sp!, {r12, r14}
;保存寄存器
; 清除中断源
MSR CPSR_c, #0x1F
;切换到 SYSTEM 模式,
STMFD sp!, {r0-r3, lr}
;保存 lr_USR 和其他使用到的寄存器
BL C_irq_handler
;跳转到 C 中断处理函数
LDMFD sp!, {r0-r3, lr}
;恢复用户模式寄存器
MSR CPSR_c, #0x92
;切换回 irq 模式
LDMFD sp!, {r12, r14}
MSR SPSR_cf, r14
LDMFD sp!, {pc}^
END
3.4.8 SWI 异常处理函数的设计
本小节主要介绍编写 SWI 处理程序时需要注意的几个问题,包括下面内容。
判断 SWI 中断号。
使用汇编语言编写 SWI 异常处理函数。
使用 C 语言编写 SWI 异常处理函数。
在特权模式下使用 SWI 异常中断处理。
从应用程序中调用 SWI。
从应用程序中动态调用 SWI。
1.判断 SWI 中断号
当发生 SWI 异常,进入异常处理程序时,异常处理程序必须提取 SWI 中断号,从而得到用户请求的特定
SWI 功能。
在 SWI 指令的编码格式中,后 24 位称为指令的“comment field”。该域保存的 24 位数,即为 SWI 指令的
中断号,如图 3.11 所示。
图 3.11 SWI 指令编码格式
第一级的 SWI 处理函数通过 LR 寄存器内容得到 SWI 指令地址,并从存储器中得到 SWI 指令编码。通常
这些工作通过汇编语言、内嵌汇编来完成。
下面的例子显示了提取中断向量号的标准过程。
PRESERVE8
AREA TopLevelSwi, CODE, READONLY
;第一级 SWI 处理函数.
EXPORT SWI_Handler
SWI_Handler
STMFD sp!,{r0-r12,lr}
;保存寄存器
LDR r0,[lr,#-4]
;计算 SWI 指令地址.
专业始于专注 卓识源于远见
‐ 25 ‐
BIC r0,r0,#0xff000000
;提取指令编码的后 24 位
;
; 提取出的中断号放 r0 寄存器,函数返回
;
LDMFD sp!, {r0-r12,pc}^
;恢复寄存器
END
例子中,使用 LR-4 得到 SWI 指令的地址,再通过“BIC r0, r0, #0xFF000000”指令提取 SWI 指令中断号。
2.汇编语言编写 SWI 异常处理函数
最简单的方法是利用得到的中断向量号,使用跳转表直接跳转到实现相应 SWI 功能的处理程序。
下面的例子,使用汇编语言实现了这种跳转。
CMP r0,#MaxSWI
;中断向量范围检测
LDRLS pc, [pc,r0,LSL #2]
B SWIOutOfRange
SWIJumpTable
DCD SWInum0
DCD SWInum1
; 使用 DCD 定义各功能函数入口地址
SWInum0
;0 号中断
B EndofSWI
SWInum1
;1 号中断
B EndofSWI
;
EndofSWI
3.使用 C 语言编写 SWI 异常处理函数
虽然第一级 SWI 处理函数(完成中断向量号的提取)必须用汇编语言完成,但第二级中断处理函数(根据
提取的中断向量号,跳转到具体处理函数)就可以使用 C 语言来完成。
因为第一级的中断处理函数已经将中断号提取到寄存器 r0 中,所以根据 AAPCS 函数调用规则,可以直接
使用 BL 指令跳转到 C 语言函数,而且中断向量号作为第一个参数被传递到 C 函数。
例如汇编中使用了“BL C_SWI_Handler”跳转到 C 语言的第二级处理函数,则第二级的 C 语言函数示例
如下所示。
void C_SWI_handler (unsigned number)
{
switch (number)
{
case 0 : /* SWI number 0 code */
break;
case 1 : /* SWI number 1 code */
break;
...
default : /* Unknown SWI - report error */
专业始于专注 卓识源于远见
‐ 26 ‐
}
}
另外,如果需要传递的参数多于 1 个,那么可以使用堆栈,将堆栈指针作为函数的参数传递给 C 类型的二
级中断处理程序,就可以实现在两级中断之间传递多个参数。
例如:
MOV r1, sp
;将传递的第二个参数(堆栈指针)放到 r1 中
BL C_SWI_Handler
;调用 C 函数
相应的 C 函数的入口变为:
void C_SWI_handler(unsigned number, unsigned *reg)
同时,C 函数也可以通过堆栈返回操作的结果。
4.在特权模式下使用 SWI 异常处理
在特权模式下使用 SWI 异常处理,和 IRQ/FIQ 中断嵌套基本类似。当执行 SWI 指令后,处理器执行下面
操作。
① 处理器进入特权模式。
② 将程序状态字内容 CPSR 保存到 SPSR_svc。
③ 返回地址放入 LR_svc。
如果处理器已经处于特权模式,再发生 SWI 异常,则 LR_svc 和 SPSR_svc 寄存器的值将丢失。
所以在特权模式下,调用 SWI 软中断异常,必须先将 LR_svc 和 SPSR_svc 寄存器的值压栈保护。下面的
例子显示了一个可以在特权模式下调用的 SWI 处理函数。
AREA SWI_Area, CODE, READONLY
PRESERVE8
EXPORT SWI_Handler
IMPORT C_SWI_Handler
T_bit EQU 0x20
SWI_Handler
STMFD sp!,{r0-r3,r12,lr}
;寄存器压栈保护
MOV r1, sp
;堆栈指针放 r1 作为参数传递.
MRS r0, spsr
;读取 spsr.
STMFD sp!, {r0, r3}
;将 spsr 压栈保护
;
;
LDR r0,[lr,#-4]
;计算 SWI 指令地址.
BIC r0,r0,#0xFF000000
;读取 SWI 中断向量号.
; r0 存放中断向量号
; r1 堆栈指针
BL C_SWI_Handler
;调用 C 程序的 SWI 处理函数.
LDMFD sp!, {r0, r3}
;从堆栈中读取 spsr.
MSR spsr_cf, r0
;恢复 spcr
LDMFD sp!, {r0-r3,r12,pc}^
;恢复其他寄存器并返回.
END
5.从应用程序中调用 SWI
专业始于专注 卓识源于远见
‐ 27 ‐
可从汇编语言或 C/C++ 中调用 SWI。
(1)从汇编应用程序中调用 SWI
从汇编语言程序中调用 SWI,只要遵循 AAPCS 标准即可。调用前,设定所有必须的值并发出相关的 SWI。
例如:
MOV r0, #65 ; 将软中断的子功能号放到 r0 中
SWI 0x0
注意
SWI 指令和其他所以 ARM 指令一样,可以被条件执行。
(2)从 C 应用程序中调用 SWI
在 C 或 C++应用程序中调用 SWI,要将 C 语言的子程序用编译器扩展_swi 声明,例如:
__swi(0) void my_swi(int);
……
……
……
my_swi(65);
编译器扩展_swi 确保了 SWI 以内联方式进行编译,而没有额外的开销。但有如下的 AAPCS 限制。
函数调用参数只能使用 r0~r3 传递。
函数返回值只能通过 r0~r3 传递。
向内联的 SWI 函数传递参数和向实际的子函数传递参数基本类似。但返回值的情况比较复杂。如果有两到
四个返回值,则必须告诉编译程序返回值是以结构形式返回的,并使用__value_in_regs 伪操作声明。这是
因为基于结构值的函数通常被处理为一个 void(空)型函数,且第一个自变量必须为存放结果结构的地址。
下面的例子显示了对编号为 0x0、0x1、0x2 和 0x3 的 SWI 软中断的调用。其中,SWI0x0 和 SWI0x1 传递
两个整型参数并返回一个单一结果;SWI0x2 传递 4 个参数并返回一个单一结果;而 SWI0x3 传递 4 个参
数并通过结构体返回 4 个结果。
#include
#include "swi.h"
unsigned *swi_vec = (unsigned *)0x08;
extern void SWI_Handler(void);
int main( void )
{
int result1, result2;
struct four_results res_3;
Install_Handler( (unsigned) SWI_Handler, swi_vec );
printf("result1 = multiply_two(2,4) = %d\n", result1 = multiply_two(2,4));
printf("result2 = multiply_two(3,6) = %d\n", result2 = multiply_two(3,6));
printf("add_two( result1, result2 ) = %d\n", add_two( result1, result2 ));
printf("add_multiply_two(2,4,3,6) = %d\n", add_multiply_two(2,4,3,6));
res_3 = many_operations( 12, 4, 3, 1 );
printf("res_3.a = %d\n", res_3.a );
printf("res_3.b = %d\n", res_3.b );
printf("res_3.c = %d\n", res_3.c );
printf("res_3.d = %d\n", res_3.d );
return 0;
}
__swi(0) int multiply_two(int, int);
__swi(1) int add_two(int, int);
专业始于专注 卓识源于远见
‐ 28 ‐
__swi(2) int add_multiply_two(int, int, int, int);
struct four_results
{
int a;
int b;
int c;
int d;
};
__swi(3) __value_in_regs struct four_results many_operations(int, int, int, int);
(3)应用程序中动态调用 SWI
在某些情形下,需要调用直到运行时才会知道其编号的 SWI。例如,当有很多相关操作可在同一目标上执
行,并且每一个操作都有其自己的 SWI 时,就会发生这种情况。在此情况下,上一小节的方法不适用。
解决的方法有两种。
在运行时得到 SWI 功能号,然后构造出相应的 SWI 指令的编码,将该编码保存在某个存储单元中,将
PC 指针指向该单元,执行指令。
使用一个通用的 SWI 异常中断处理程序,将运行时需要调用的 SWI 功能号作为参数传递给该通用的
SWI 异常处理程序,通用的 SWI 异常中断处理程序根据参数值调用相应的 SWI 处理程序完成需要的操作。
通过汇编语言可以实现第二种解决办法:通过寄存器(通常为 r0 或 r12)传递所需要的操作数,这样可以
重新编写 SWI 处理程序,对相应寄存器中的值进行处理。
但有些情况下,为了节省程序开销,需要直接使用 SWI 中断号对程序调用。例如,操作系统可能会使用单
一的一条 SWI 指令并用寄存器来传递所需运算的编号。这使得其他 SWI 空间可用于特定应用程序的 SWI。
在一个特定的应用程序中,如果从指令中提取 SWI 编号的开销太大,就可使用这个方法。ARM(0x123456)
和 Thumb(0xAB)半主机方式的 SWI 就是这样实现的。
下面的例子显示了如何使用_swi 将 C 函数调用映射到半主机方式的 SWI。
#ifdef __thumb
/* Thumb 状态的 Semihosting 软中断处理*/
#define SemiSWI 0xAB
#else
/* ARM 状态下的 Semihosting 的软中断处理*/
#define SemiSWI 0x123456
#endif
/* 使用 Semihosting 软中断输出一个字符*/
__swi(SemiSWI) void Semihosting(unsigned op, char *c);
#define WriteC(c) Semihosting (0x3,c)
void write_a_character(int ch)
{
char tempch = ch;
WriteC( &tempch );
}
编译程序含有一个机制,用以支持使用 r12 来传递所需运算的值。根据 AAPCS 标准,r12 为 IP 寄存器,
并且专用于函数调用。其他时间内可将其用作暂存寄存器。如前面所述,通用 SWI 参数和返回值通过 r0~
r3 寄存器传递。而 r12 可用于传递通用 SWI 调用的中断功能编号。
下面的例子显示了通用 SWI 的 C 语言程序框架。
__swi_indirect(0x80)
unsigned SWI_ManipulateObject(unsigned operationNumber,
unsigned object,unsigned parameter);
unsigned DoSelectedManipulation(unsigned object,
专业始于专注 卓识源于远见
‐ 29 ‐
unsigned parameter, unsigned operation)
{
return SWI_ManipulateObject(operation, object, parameter);
}
生成的汇编代码如下。
DoSelectedManipulation PROC
STMFD sp!,{r3,lr}
MOV r12,r2
SWI 0x80
LDMFD sp!,{r3,pc}
ENDP
联系方式
集团官网:www.hqyj.com 嵌入式学院:www.embedu.org 移动互联网学院:www.3g-edu.org
企业学院:www.farsight.com.cn 物联网学院:www.topsight.cn 研发中心:dev.hqyj.com
集团总部地址:北京市海淀区西三旗悦秀路北京明园大学校内 华清远见教育集团
北京地址:北京市海淀区西三旗悦秀路北京明园大学校区,电话:010-82600386/5
上海地址:上海市徐汇区漕溪路银海大厦 A 座 8 层,电话:021-54485127
深圳地址:深圳市龙华新区人民北路美丽 AAA 大厦 15 层,电话:0755-22193762
成都地址:成都市武侯区科华北路 99 号科华大厦 6 层,电话:028-85405115
南京地址:南京市白下区汉中路 185 号鸿运大厦 10 层,电话:025-86551900
武汉地址:武汉市工程大学卓刀泉校区科技孵化器大楼 8 层,电话:027-87804688
西安地址:西安市高新区高新一路 12 号创业大厦 D3 楼 5 层,电话:029-68785218
《ARM 系列处理器应用技术完全手册》
作者:华清远见
第 4 章 ARM 指令寻址方式
本章目标
ARM 指令集可以分为跳转指令、数据处理指令、程序状态寄存器传输指令、
Load/Store 指令、协处理器指令和异常中断产生指令。根据适用的指令类型不同,
指令的寻址方式分为:数据处理指令操作数寻址方式和内存访问指令寻址方式。
专业始于专注 卓识源于远见
‐ 2 ‐
4.1 数据处理指令的寻址方式
4.1.1 数据处理指令的寻址方式概要
数据处理指令的基本语法格式如下。
{} {S} ,,
其中有下面 11 种形式,如表 4.1 所示。
表 4.1
的寻址方式
语 法
寻 址 方 式
1
#
立即数寻址
2
寄存器寻址
3
, LSL #
立即数逻辑左移
4
, LSL
寄存器逻辑左移
5
, LSR #
立即数逻辑右移
6
, LSR
寄存器逻辑右移
7
, ASR #
立即数算术右移
8
, ASR
寄存器算术右移
9
, ROR #
立即数循环右移
10
, ROR
寄存器循环右移
11
, RRX
寄存器扩展循环右移
数据处理指令的寻址方式根据的不同,相应的分为 11 种。
4.1.2 指令解码
图 4.1 显示了数据处理指令不同寻址方式下的解码格式。
32 位立即数
31
28 27 26 25 24
21 20 19
16 15
12 11
8
7
0
立即数移位
寄存器移位
opcode
cond
0
0
1
S
Rn
Rd
Rotate_imm
Immed_8
opcode
cond
0
0
0
S
Rn
Rd
shift_imm
shift
0
Rm
opcode
cond
0
0
0
S
Rn
Rd
Rs
shift
0
Rm
1
31
28 27 26 25 24
21 20 19
16 15
12 11
7
6
0
31
28 27 26 25 24
21 20 19
16 15
12 11
8
7
0
5
4
6
5 4 3
图 4.1 数据操作指令编码格式
编码格式中各域含义如下。
:确定具体指令。
专业始于专注 卓识源于远见
‐ 3 ‐
S:标识指令是否影响程序状态寄存器 CPSR 条件标志。
Rd:指令操作的目的寄存器。
Rn:指令第一源操作数。
bit[11∶0]:移位操作,详见本章移位操作一节。
bit[25]:被用来区分是立即数移位操作还是寄存器移位操作。
如果指令编码出现下面情况:bit[25] = 0 并且 bit[4] = 1 并且 bit[7] = 1,则指令并非数据处理指令,它可能
是 Load/Store 指令或算术指令。
4.1.3 移位操作
数据处理指令是在算术逻辑单元 ALU 中完成。ARM 处理器一个显著特征就是可以在操作数进入 ALU 之
前,对操作数进行指定位数的左移或右移操作。这种功能明显增强了数据处理操作的灵活性。
移位操作可能产生进位,更新程序状态寄存器 CPSR 的进位标志 C。移位操作有下面 3 种基本方式。
1.立即数方式
没有任何一条 ARM 指令可以包含一个 32 位的立即数,数据处理指令编码格式中,第二个操作数有 12 位。
指令的编码格式如图 4.1 所示。
指令中的立即数是由一个 8 bit 的常数移动 4 bit 偶数位(0,2,4,…,26,28,30)得到的。所以,每
一条指令都包含一个 8 bit 的常数 X 和移位值 Y,得到的立即数=X 循环右移(2×Y)。
注意
8 位立即数一定要移偶数位。
下面列举了一些有效的立即数。
0xFF、0x104、0xFF0、0x FF00、0x FF000、0x FF000000、0x F000000F
下面是一些无效的立即数。
0x101、0x102、0x FF1、0x FF04、0x FF003、0x FFFFFFFF、0x F000001F
下面是一些应用立即数的指令。
MOV r0,#0
;送 0 到 r0
ADD r3,r3,#1
;r3 的值加 1
CMP r7,#1000
;r7 的值和 1000 比较
BIC r9,r8,#0x FF00
;将 r8 中 8~15 位清零,结果保存在 r9 中
2.寄存器方式
寄存器的值可以被直接用于数据操作指令,如:
MOV r2,r0
;r0 的值送 r2
ADD r4,r3,r2
;r2 加 r3,结果送 r4
CMP r7,r8
;比较 r7 和 r8 的值
3.寄存器移位方式
寄存器的值在被送到 ALU 之前,可以事先经过桶形移位寄存器的处理。预处理和移位发生在同一周期内,
所以有效的使用移位寄存器,可以增加代码的执行效率。
具体的移位(或者循环移位)方式有下面几种。
专业始于专注 卓识源于远见
‐ 4 ‐
ASR:算术右移。
LSL:逻辑左移。
LSR:逻辑右移。
ROR:循环右移。
RRX:扩展的循环右移。
以上 5 种移位方式,移位值均可以由立即数或寄存器指定。下面是一些在指令中使用了移位操作的例子。
ADD r2,r0,r1,LSR #5
MOV r1,r0,LSL #2
RSB r9,r5,r5,LSL #1
SUB r1,r2,r0,LSR #4
MOV r2,r4,ROR r0
4.1.4 寻址方式分类详解
数据处理指令的寻址方式根据的不同,相应的分为 11 种。详见表 4.1。下面对各类寻址方
式进行详细说明。
1.#
(1)编码格式
指令的编码格式如图 4.2 所示。
31
28 27 26 25 24
21 20 19
16 15
12 11
8
7
0
opcode
cond
0
0
1
S
Rn
Rd
Rotate_imm
Immed_8
图 4.2 数据处理指令——立即数寻址编码格式
立即数寻址为数据处理指令提供了一个可直接操作的立即数。立即数的生成方法见前面章节介绍。如果移
位值为 0,则移位进位值为程序状态寄存器 CPSR 的 C 标志位;否则,为 32-bit 立即数的 bit[31]。
(2)操作伪代码
Shifter_operand = immed_8 Rotate_Right (rotate_imm*2)
if rotate_imm == 0 then
shifter_carry_out = C flag
else /* rotate_imm != 0*/
shifter_carry_out = shifter_operand[31]
(3)说明
① 并不是所有的 32-bit 立即数都是可以使用的合法立即数。只有那些通过将一个 8-bit 的立即数循环右移
偶数位可以得到的立即数才可以在指令中使用。
② 有些立即数可以通过不止一种方法得到。由于立即数的构造方法中移位包含了循环操作,而循环移位
操作会影响 CPSR 的条件标志位 C。因此,同一个合法的立即数由于采用了不同的编码方式,将使这些指
令的执行产生不同的结果,这是不能允许的。ARM 汇编器按照下面的规则来生成立即数的编码。
当立即数数值在 0 和 0xFF 范围时,令 immed_8=,immed_4=0。
其他情况下,汇编编译器选择使用 immed_4 数值最小的编码方式。
③ 为了更精确地控制立即数的生成,可以使用下面的语法格式控制立即数的生成。
#,
专业始于专注 卓识源于远见
‐ 5 ‐
其中, = 2*rotate_imm
(4)举例
SUBS r0,r0,#1
;寄存器 r0 中的数值减 1,结果保存到 r0
MOV r0,#0xff00 ; 0xff00 → r0 ;将立即数 0xff00 放入 r0 保存
2.
(1)编码格式
指令的编码格式如图 4.3 所示。
opcode
cond
0
0
0
S
Rn
Rd
00000
000
Rm
31
28 27 26 25 24
21 20 19
16 15
12 11
7
6
0
4 3
图 4.3 数据处理指令——寄存器寻址编码格式
指令的操作数即为寄存器中的数值。移位寄存器的进位为程序状态寄存器 CPSR 的 C 标志位。
指令的语法格式为: {} {S} ,,
(2)操作伪代码
Shifter_operand = Rm
Shifter_carry_out = C Flag
(3)说明
① 从指令的解码格式来看,寄存器寻址方式和使用立即数逻辑左移寻址解码格式是相同的,只是其移位
数为 0。
② 如果指令中的 Rm 或 Rn 指定为程序计数器 r15,则操作数的值为当前指令地址加 8。
(4)举例
MOV r1,r2 ; r2 → r1
SUB r0,r1,r2 ; r1 – r2 → r0
3., LSL #
(1)编码格式
指令的编码格式如图 4.4 所示。
opcode
cond
0
0
0
S
Rn
Rd
shift_imm
000
Rm
31
28 27 26 25 24
21 20 19
16 15
12 11
7
6
0
4 3
图 4.4 数据处理指令——立即数逻辑左移寻址编码格式
指令的操作数为寄存器 Rm 的数值逻辑左移 shift_imm 位。左移的范围在 0 到 31 之间。左移移出的位用 0
补齐。进位标志位是最后移出的位(如果移位数为 0,则为 C 标志位)。
指令的语法格式为: {} {S} ,,,LSL #,其中:
为进行逻辑左移操作的寄存器;
LSL 为逻辑左移操作标识;
为逻辑左移位数,范围为 0~31。
(2)操作伪代码
专业始于专注 卓识源于远见
‐ 6 ‐
if shift_imm == 0 then /*执行寄存器操作*/
shifter_operand = Rm
shifter_carry_out = C flag
else /*移位寄存器大于零*/
shifter_operand = Rm logical_shift_left shift_imm
shifter_carry_out = Rm[32 – shift_imm]
(3)说明
① 如果移位立即数 =0,则该寻址方式为立即数直接寻址。
② 如果指令中的 Rm 或 Rn 指定为程序计数器 r15,则操作数的值为当前指令地址加 8。
(4)举例
SUB r0,r1,r2,LSL #10
;r1 的值减去 r2 的值左移 10bit,结果放到 r0 寄存器
MOV r0,r2,LSL #3
;r2 的值左移 3bit,结果放入 r0,即 r0 = r2×8
4., LSL
(1)编码格式
指令的编码格式如图 4.5 所示。
opcode
cond
0
0
0
S
Rn
Rd
Rs
0001
Rm
31
28 27 26 25 24
21 20 19
16 15
12 11
8
7
0
4 3
图 4.5 数据处理指令——寄存器逻辑左移寻址编码格式
寄存器逻辑左移十分适合寄存器值乘 2 的倍数操作。
这个指令是将寄存器 Rm 的值逻辑左移一定的位数。位移的位数由 Rs 的最低 8 位 bit[7∶0]决定。Rm 移出
的位用 0 补齐。进位值是移位寄存器最后移出的位,如果移位数大于 0,则进位值为 0。
(2)语法格式
{} {S} ,,,LSL
其中:
为指令被移位的寄存器;
LSL 为逻辑左移操作标识;
为包含逻辑左移位数的寄存器。
(3)操作伪代码
if Rs[7:0] = = 0 then
shifter_operand = Rm
shifter_carry_out = C flag
else if Rs[7:0] < 32 then
shifter_operand = Rm logical_shift_left Rs[7:0]
shifter_carry_out = Rm[32 – Rs[7:0]]
else if Rs[7:0] = = 32 then
shifter_operand = 0
shifter_carry_out = Rm[0]
else /*Rs 的后 8 位大于零*/
shifter_operand = 0
shifter_carry_out = 0
(4)说明
专业始于专注 卓识源于远见
‐ 7 ‐
如果程序计数器 r15 被用作 Rd,Rm,Rn 或 Rs 中的任意一个,则指令的执行结果不可预知。
(5)举例
MOV r0,r2,LSL r3
;r2 的值左移 r3 位,结果放入 r0
ANDS r1,r1,r2,LSL r3
;r2 的值左移 r3 位,然后和 r1 相与,结果放入 r1
5., LSR #
(1)编码格式
指令的编码格式如图 4.6 所示。
opcode
cond
0
0
0
S
Rn
Rd
shift_imm
010
Rm
31
28 27 26 25 24
21 20 19
16 15
12 11
7
6
0
4 3
图 4.6 数据处理指令——立即数逻辑右移寻址编码格式
指令的操作数为寄存器 Rm 的值右移位,相当于 Rm 的值除以一个 2 的倍数。
值的范围为 0~31,移位后空出的位添 0。循环器进位值为 Rm 最后移出的位。
(2)语法格式
{} {S} ,,,LSR #
其中:
为被移位的寄存器;
LSR 为逻辑右移操作标识;
为逻辑右移位数,范围为 0~31。
(3)操作伪代码
if shift_imm == 0 then /*执行寄存器操作*/
shifter_operand = 0
shifter_carry_out = Rm[31]
else /*移位立即数大于零*/
shifter_operand = Rm logical_shift_Right shift_imm
shifter_carry_out = Rm[shift_imm - 1]
(4)说明
① shift_imm 的取值范围为 0~31,当 shift_imm=0 时,移位位数为 32,所以移位位数范围为 1~32 位。
② 如果指令中的 Rm 或 Rn 指定为程序计数器 r15,则操作数的值为当前指令地址加 8。
6., LSR
(1)编码格式
指令的编码格式如图 4.7 所示。
opcode
cond
0
0
0
S
Rn
Rd
Rs
0011
Rm
31
28 27 26 25 24
21 20 19
16 15
12 11
8
7
0
4 3
图 4.7 数据处理指令——寄存器逻辑右移寻址编码格式
专业始于专注 卓识源于远见
‐ 8 ‐
此操作将寄存器 Rm 的数值逻辑右移一定的位数。移位的位数由 Rs 的最低 8 位 bit[7∶0]决定。移出的位
由 0 补齐。当 Rs[7∶0]大于 0 而小于 32 时,进位标志 C 由最后移出的位决定,当 Rs[7∶0]大于 32 时,进
位标志位为 0,当 Rs[7∶0]等于 0 时,进位标志不变。
(2)语法格式
{} {S} ,,,LSR
其中:
为指令被移位的寄存器;
LSR 为逻辑右移操作标识;
为包含逻辑右移位数的寄存器。
(3)操作伪代码
if Rs[7:0] = = 0 then
shifter_operand = Rm
shifter_carry_out = C flag
else if Rs[7:0] < 32 then
shifter_operand = Rm logical_shift_Right Rs[7:0]
shifter_carry_out = Rm[Rs[7:0] - 1]
else if Rs[7:0] = = 32 then
shifter_operand = 0
shifter_carry_out = Rm[31]
else /*Rs 的后 8 位大于零*/
shifter_operand = 0
shifter_carry_out = 0
(4)说明
如果程序计数器 r15 被用作 Rd、Rm、Rn 或 Rs 中的任意一个,则指令的执行结果不可预知。
7., ASR #
(1)编码格式
指令的编码格式如图 4.8 所示。
opcode
cond
0
0
0
S
Rn
Rd
shift_imm
100
Rm
31
28 27 26 25 24
21 20 19
16 15
12 11
7
6
0
4 3
图 4.8 数据处理指令——立即数算术右移寻址编码格式
指令的操作数为寄存器 Rm 的数值逻辑右移位。的值范围为 0~31,当
等于 0 时,移位位数为 32,所以移位位数范围为 1~32 位。进位移位操作后,空出的位添 Rm 的最高位
Rm[31]。进位标志为 Rm 最后被移出的数值。
(2)语法格式
{} {S} ,,,ASR #
其中:
为被移位的寄存器;
ASR 为算术右移操作标识;
为算术右移位数,范围为 1~32,当 shift_imm 等于 0 时移位位数为 32。
(3)操作伪代码
专业始于专注 卓识源于远见
‐ 9 ‐
if shift_imm == 0 then /*执行寄存器操作*/
if Rm[31] = = 0 then
shifter_operand = 0
shifter_carry_out = Rm[31]
else /*Rm[31] = = 1*/
shifter_operand = 0xffffffff
shifter_carry_out = Rm[31]
else /*shift_imm > 0*/
shifter_operand = Rm Arithmetic_shift_Right
shifter_carry_out = Rm[shift_imm - 1]
(4)说明
① 如果指令中的 Rm 或 Rn 指定为程序计数器 r15,则操作数的值为当前指令地址加 8。
8., ASR
(1)编码格式
指令的编码格式如图 4.9 所示。
opcode
cond
0
0
0
S
Rn
Rd
Rs
0101
Rm
31
28 27 26 25 24
21 20 19
16 15
12 11
8
7
0
4 3
图 4.9 数据处理指令——寄存器算术右移寻址编码格式
此操作将寄存器 Rm 的数值算术右移一定的位数。移位后空缺的位由 Rm 的符号位(Rm[31])填充。位移的
位数由 Rs 的最低 8 位 bit[7∶0]决定。当 Rs[7∶0]大于零而小于 32 时,指令的操作数为寄存器 Rm 的数值算
术右移 Rs[7∶0]位,进位标志 C 为 Rm 最后被移出的位。
(2)语法格式
{} {S} ,,,ASR
其中:
为指令被移位的寄存器;
ASR 为算术右移操作标识;
为包含算术右移位数的寄存器。
(3)操作伪代码
if Rs[7:0] = = 0 then
shifter_operand = Rm
shifter_carry_out = C flag
else if Rs[7:0] < 32 then
shifter_operand = Rm Arithmeticl_shift_Right Rs[7:0]
shifter_carry_out = Rm[Rs[7:0] - 1]
else
if Rm[31] = =0 then
shifter_operand = 0
shifter_carry_out = Rm[31]
else
shifter_operand = 0xffffffff
shifter_carry_out = Rm[31]
(4)说明
专业始于专注 卓识源于远见
‐ 10 ‐
如果程序计数器 r15 被用作 Rd、Rm、Rn 或 Rs 中的任意一个,则指令的执行结果不可预知。
9., ROR #
(1)编码格式
指令的编码格式如图 4.10 所示。
opcode
cond
0
0
0
S
Rn
Rd
shift_imm
110
Rm
31
28 27 26 25 24
21 20 19
16 15
12 11
7
6
0
4 3
图 4.10 数据处理指令——立即数循环右移寻址编码格式
指令的操作数由寄存器 Rm 的数值循环右移一定的位数得到。移位的位数由 Rs 的最低 8 位 bits[7∶0]决定。
当 Rs[7∶0]=0 时,指令的操作数为寄存器 Rm 的值,循环器的进位值为 CPSR 中的 C 条件标志位;否则,
循环器的进位值为 Rm 最后被移出的位。
(2)语法格式
{} {S} ,,,ROR #
其中:
为被移位的寄存器;
ROR 为循环右移操作标识;
为循环右移位数,范围为 1~31,当 shift_imm 等于 0 时执行 RRX 操作。
(3)操作伪代码
if shift_imm == 0 then /*执行寄存器操作*/
执行 RRX 操作
else
shifter_operand = Rm Rotate_Right shift_imm
shifter_carry_out = Rm[shift_imm - 1]
(4)说明
如果指令中的 Rm 或 Rn 指定为程序计数器 r15,则操作数的值为当前指令地址加 8。
10., ROR
(1)编码格式
指令的编码格式如图 4.11 所示。
opcode
cond
0
0
0
S
Rn
Rd
Rs
0111
Rm
31
28 27 26 25 24
21 20 19
16 15
12 11
8
7
0
4 3
图 4.11 数据处理指令——寄存器循环右移寻址编码格式
指令的操作数由寄存器 Rm 的数值循环右移一定的位数。移位的位数由 Rs 的最低 8 位 bits[7∶0]决定。当
Rs[7∶0]=0 时,指令的操作数为寄存器 Rm 的值,循环器的进位值为 CPSR 中的 C 条件标志位;否则,循
环器的进位值为 Rm 最后被移出的位。
(2)语法格式
{} {S} ,,,ROR
专业始于专注 卓识源于远见
‐ 11 ‐
其中:
为指令被移位的寄存器;
ROR 为循环右移操作标识;
为包含循环右移位数的寄存器。
(3)操作伪代码
if Rs[7:0] = = 0 then
shifter_operand = Rm
shifter_carry_out = C flag
else if Rs[4:0] == 0 then
shifter_operand = Rm
shifter_carry_out = Rm[31]
else
shifter_operand = Rm Rotate_Right Rs[4:0]
shifter_carry_out = Rm[Rs[4:0] - 1]
(4)说明
如果程序计数器 r15 被用作 Rd、Rm、Rn 或 Rs 中的任意一个,则指令的执行结果不可预知。
11., RRX
(1)编码格式
指令的编码格式如图 4.12 所示。
opcode
cond
0
0
0
S
Rn
Rd
00000110
Rm
31
28 27 26 25 24
21 20 19
16 15
12 11
0
4 3
图 4.12 数据处理指令——扩展右移寻址编码格式
指令的操作数为寄存器 Rm 的数值右移一位,并用 CPSR 中的 C 条件标志位填补空出的位。CPSR 中的 C
条件标志位则用移出的位代替。
(2)语法格式
{} {S} ,,,RRX
其中:
为指令被移位的寄存器;
RRX 为扩展的循环右移操作。
(3)操作伪代码
shifter_operand = (C flag logical_shift_left 31) OR (Rm logical_shift_Right 1)
shifter_carry_out = Rm[0]
(4)说明
① 此种寻址方式的编码形式和“ROR #0”一致。
② 如果程序计数器 r15 被用作 Rd、Rm、Rn 或 Rs 中的任意一个,则指令的执行结果不可预知。
③ 可以实现 ADC 指令的功能。
4.2 内存访问指令寻址
根据内存访问指令的分类,内存访问指令的寻址方式可以分为以下几种。
专业始于专注 卓识源于远见
‐ 12 ‐
① 字及无符号字节的 Load/Store 指令的寻址方式。
② 杂类 Load/Store 指令的寻址方式。
③ 批量 Load/Store 指令的寻址方式。
④ 协处理器 Load/Store 指令的寻址方式。
4.2.1 字及无符号字节的 Load/Store 指令的寻址方式
字及无符号字节的 Load/Store 指令语法格式如下:
LDR|STR{}{B}{T} ,
其中共有 9 种寻址方式,如表 4.2 所示。
表 4.2
字及无符合字节的 Load/Store 指令的寻址方式
格 式
模 式
1
[Rn,#±]
立即数偏移寻址
(Immediate offset)
2
[Rn,±Rm]
寄存器偏移寻址
(Register offset)
3
[Rn,Rm,#< offset_12>]
带移位的寄存器偏移寻址
(Scaled register offset)
4
[Rn,#±< offset_12>]!
立即数前索引寻址
(Immediate pre-indexed)
5
[Rn,±Rm]!
寄存器前索引寻址
(Register post-indexed)
6
[Rn,Rm,#< offset_12>]!
带移位的寄存器前索引寻址
(Scaled register pre-indexed)
7
[Rn],#±< offset_12>
立即数后索引寻址
(Immediate post-indeded)
8
[Rn],±
寄存器后索引寻址
(Register post-indexed)
9
[Rn],±,#< offset_12>
带移位的寄存器后索引寻址
(Scaled register post-indexed)
字及无符号字节的 Load/Store 指令的解码格式如图 4.13 所示。
立即数
cond
01
0
P U B W
L
Rn
Rd
offset_12
31
28 27 26 25
20 19
16 15
12 11
0
cond
01
1
P U B W
L
Rn
Rd
00000000
31
28 27 26 25
20 19
16 15
12 11
0
cond
01
1
P U B W
L
Rn
Rd
shi_imm
31
28 27 26 25
20 19
16 15
12 11
0
Rm
shift
L
Rm
4 3
7
6
4 3
5
寄存数
寄存器偏移
图 4.13 字及无符号字节的 Load/Store 指令的解码格式
编码格式中各位的含义如表 4.3 所示。
专业始于专注 卓识源于远见
‐ 13 ‐
表 4.3
字和无符号半字 Load/Store 指令编码格式各位含义
位 标 识
取 值
含 义
P
P=0
使用后索引寻址
P=1
使用偏移地址或前索引寻址(由 W 位决定)
U
U=0
访问的地址=基址寄存器的值-偏移量(offset)
U=1
访问的地址=基址寄存器的值+偏移量(offset)
B
B=0
字访问 Load/Store
B=1
无符号字节访问 Load/Store
W
W=0
如果 P=0,该指令为 LDR、LDRB、STR 或 STRB 指令,且内存访问
指令为正常访问指令;如果 P=1,指令执行不更新基地址
W=1
如果 P=0,该指令为 LDRBT、LDRT、STRBT 或 STRT,且指令为非
特权(用户模式)访问指令;如果 P=1,计算内存地址并更新基地址
L
L=0
Store 指令
L=1
Load 指令
1.[Rn,#±]
(1)编码格式
指令的编码格式如图 4.14 所示。
cond
01
0
1 U B
0
L
Rn
Rd
offset_12
31
28 27 26 25
20 19
16 15
12 11
0
图 4.14 内存访问指令——立即数偏移寻址编码格式
内存访问地址为基址寄存器 Rn 的值加(或减)立即数 offset_12。
编程中,在访问结构体或记录(record)类型的变量时,这些内存的操作指令是十分有效的。另外,在子
程序中也常用这些指令访问本地变量和堆栈。
(2)语法格式
LDR|STR{}{B}{T} ,[,#±]
其中:
Rn 为基址寄存器,该寄存器包含内存访问的基地址;
为 12 位立即数,内存访问地址偏移量。
(3)操作伪代码
If U = = 1 then
Address = Rn + offset_12
Else
Address = Rn – offset_12
(4)说明
① 如果指令中没有指定立即数,使用[],编译器按[,#0]形式编码。
② 如果 Rn 被指定为程序计数器 r15,其值为当前指令地址加 8。
2.[Rn,±Rm]
专业始于专注 卓识源于远见
‐ 14 ‐
(1)编码格式
指令的编码格式如图 4.15 所示。
cond
01
1
1 U B
0
L
Rn
Rd
00000000
31
28 27 26 25
20 19
16 15
12 11
0
Rm
图 4.15 内存访问指令——寄存器偏移寻址编码格式
内存访问地址为基址寄存器 Rn 的值加(或减)偏移寄存器 Rm 的值。
该寻址方式适合使用指针访问字节数组中的数据成员。
(2)语法格式
LDR|STR{}{B}{T} ,[,±]
其中:
Rn 为基址寄存器,该寄存器包含内存访问的基地址;
为偏移地址寄存器,包含内存访问地址偏移量。
(3)操作伪代码
If U = = 1 then
Address = Rn + Rm
Else
Address = Rn – Rm
(4)说明
如果 Rn 被指定为程序计数器 r15,其值为当前指令地址加 8;如果 r15 被用作偏移地址寄存器 Rm 的值,
指令的执行结果不可预知。
3.[Rn,Rm,#< offset_12>]
(1)编码格式
指令的编码格式如图 4.16 所示。
cond
01
1
1 U B
0
L
Rn
Rd
shift_imm
31
28 27 26 25
20 19
16 15
12 11
0
shift
0
Rm
7 6
4 3
5
图 4.16 内存访问指令——带移位的寄存器偏移寻址编码格式
内存地址为 Rn 的值加/减通过移位操作后的 Rm 的值。
当数组中的成员长度大于 1 个字节时,使用该寻址方式可高效率地访问数组成员。
(2)语法格式
语法格式有以下 5 种。
LDR|STR{}{B}{T} ,[,±,LSL #< offset_12>]
LDR|STR{}{B}{T} ,[,±,LSR #< offset_12>]
LDR|STR{}{B}{T} ,[,±,ASR #< offset_12>]
LDR|STR{}{B}{T} ,[,±,ROR #< offset_12>]
LDR|STR{}{B}{T} ,[,±,RRX]
其中:
Rn 为基址寄存器,该寄存器包含内存访问的基地址;
为偏移地址寄存器,包含内存访问地址偏移量;
LSL 表示逻辑左移操作;
专业始于专注 卓识源于远见
‐ 15 ‐
LSR 表示逻辑右移操作;
ASR 表示算术右移操作;
ROR 表示循环右移操作;
RRX 表示扩展的循环右移。
为移位立即数。
(3)操作伪代码
Case shift of
0b00 /*LSL*/
Index = Rm logic_shift_left shift_imm
0b01 /*LSR*/
If shift_imm = = 0 then /*LSR #32*/
Index = 0
Else
Index = Rm logical_shift_right shift_imm
0b10 /*ASR*/
If shift_imm = = 0 then /*ASR #32*/
If Rm[31] = = 1 then
Index = 0xffffffff
Else
Index = 0
Else
Index = Rm Arithmetic_shift_Right shift_imm
0b11 /* ROR or RRX*/
If shift_imm = = 0 then /*RRX*/
Index = (C flag Logical_shift_left 31) OR
(Rm logical_shift_Right 1)
Else /*ROR*/
Index = Rm Rotate_Right shift_imm
Endcase
If U = = 1 then
Address = Rn + index
Else /*U = = 0*/
Address = Rn – index
(4)说明
如果 Rn 被指定为程序计数器 r15,其值为当前指令地址加 8;如果 r15 被用作偏移地址寄存器 Rm 的值,
指令的执行结果不可预知。
4.[Rn,#±< offset_12>]!
(1)编码格式
指令的编码格式如图 4.17 所示。
cond
01
0
1 U B
1
L
Rn
Rd
offset_12
31
28 27 26 25
20 19
16 15
12 11
0
图 4.17 内存访问指令——前索引立即数偏移寻址编码格式
内存地址为基址寄存器 Rn 加/减立即数 offset_8 的值。当指令执行的条件满足时,生成的地址写回基
址寄存器 Rn 中。
专业始于专注 卓识源于远见
‐ 16 ‐
该寻址方式适合访问数组自动进行数组下标的更新。
(2)语法格式
LDR|STR{}{B}{T} ,[,±] !
其中:
Rn 为基址寄存器,该寄存器包含内存访问的基地址;
为 12 位立即数,内存访问地址偏移量;
!设置指令编码中的 W 位,更新指令基址寄存器。
(3)操作伪代码
If U == 1 then
Address = Rn + offset_12
Else
Address = Rn – offset_12
If ConditionPassed{cond} then
Rn = address
(4)说明
① 如果指令中没有指定立即数,使用[],编译器按[,#0] ! 形式编码。
② 如果 Rn 被指定为程序计数器 r15,指令的执行结果不可预知。
5.[Rn,±Rm]!
(1)编码格式
指令的编码格式如图 4.18 所示。
cond
01
1
1 U B
1
L
Rn
Rd
00000000
31
28 27 26 25
20 19
16 15
12 11
0
Rm
图 4.18 内存访问指令——前索引寄存器偏移寻址编码格式
内存访问地址为基址寄存器 Rn 的值加(或减)偏移寄存器 Rm 的值。当指令的执行条件满足时,生
成地地址将写回基址寄存器。
(2)语法格式
LDR|STR{}{B}{T} ,[,±]
其中:
Rn 为基址寄存器,该寄存器包含内存访问的基地址;
为偏移地址寄存器,包含内存访问地址偏移量;
!设置指令编码中的 W 位,更新指令基址寄存器。
(3)操作伪代码
If U = = 1 then
Address = Rn + Rm
Else
Address = Rn – Rm
If ConditionPassed{cond} then
Rn = address
(4)说明
如果 Rn 和 Rm 指定为同一寄存器,指令的执行结果不可预知。
专业始于专注 卓识源于远见
‐ 17 ‐
6.[Rn,±Rm,#< offset_12>]!
(1)编码格式
指令的编码格式如图 4.19 所示。
cond
01
1
1 U B
1
L
Rn
Rd
shift_imm
31
28 27 26 25
20 19
16 15
12 11
0
shift
0
Rm
7 6
4 3
5
图 4.19 内存访问指令——带移位的前索引寄存器偏移寻址编码格式
内存地址为 Rn 的值加/减通过移位操作后的 Rm 的值。当指令的执行条件满足时,生成地地址将写回
基址寄存器。
(2)语法格式
语法格式有以下 5 种。
LDR|STR{}{B}{T} ,[,±,LSL #< offset_12>] !
LDR|STR{}{B}{T} ,[,±,LSR #< offset_12>] !
LDR|STR{}{B}{T} ,[,±,ASR #< offset_12>] !
LDR|STR{}{B}{T} ,[,±,ROR #< offset_12>] !
LDR|STR{}{B}{T} ,[,±,RRX] !
其中:
Rn 为基址寄存器,该寄存器包含内存访问的基地址;
为偏移地址寄存器,包含内存访问地址偏移量;
LSL 表示逻辑左移操作;
LSR 表示逻辑右移操作;
ASR 表示算术右移操作;
ROR 表示循环右移操作;
RRX 表示扩展的循环右移。
为移位立即数。
!设置指令编码中的 W 位,更新指令基址寄存器。
(3)操作伪代码
Case shift of
0b00 /*LSL*/
Index = Rm logic_shift_left shift_imm
0b01 /*LSR*/
If shift_imm = = 0 then /*LSR #32*/
Index = 0
Else
Index = Rm logical_shift_right shift_imm
0b10 /*ASR*/
If shift_imm = = 0 then /*ASR #32*/
If Rm[31] = = 1 then
Index = 0xffffffff
Else
Index = 0
Else
Index = Rm Arithmetic_shift_Right shift_imm
0b11 /* ROR or RRX*/
If shift_imm = = 0 then /*RRX*/
专业始于专注 卓识源于远见
‐ 18 ‐
Index = (C flag Logical_shift_left 31) OR
(Rm logical_shift_Right 1)
Else /*ROR*/
Index = Rm Rotate_Right shift_imm
Endcase
If U = = 1 then
Address = Rn + index
Else /*U = = 0*/
Address = Rn – index
If ConditionPassed{cond} then
Rn = address
(4)说明
① 当 PC 用作基址寄存器 Rn 或 Rm 时,指令执行结果不可预知。
② 当 Rn 和 Rm 是同一个寄存器时,指令的执行结果不可预知。
7.[Rn],#±< offset_12>
(1)编码格式
指令的编码格式如图 4.20 所示。
cond
01
0
1 U B
1
L
Rn
Rd
offset_12
31
28 27 26 25
20 19
16 15
12 11
0
图 4.20 内存访问指令——后索引立即数偏移寻址编码格式
指令使用基址寄存器 Rn 的值作为实际内存访问地址。当指令的执行条件满足时,将基址寄存器的值加/减
偏移量产生新的地址值回写到 Rn 寄存器中。
(2)语法格式
LDR|STR{}{B}{T} ,[],±
其中:
Rn 为基址寄存器,该寄存器包含内存访问的基地址;
为 12 位立即数,内存访问地址偏移量。
(3)操作伪代码
Address = Rn
If conditionPassed{cond} then
If U = = 1 then
Rn = Rn + offset_12
Else
Rn = Rn – offset_12
(4)说明
① LDRBT、LDRT、STRBT 和 STRT 指令只支持后索引寻址。
② 如果 Rn 被指定为程序计数器 r15,指令的执行结果不可预知。
8.[Rn],±
(1)编码格式
专业始于专注 卓识源于远见
‐ 19 ‐
指令的编码格式如图 4.21 所示。
cond
01
1
0 U B
0
L
Rn
Rd
00000000
31
28 27 26 25
20 19
16 15
12 11
0
Rm
图 4.21 内存访问指令——后索引寄存器偏移寻址编码格式
指令访问地址为实际的基址寄存器的值。当指令的执行条件满足时,将基址寄存器的值加/减索引寄存器
Rm 的值回写到 Rn 基址寄存器。
(2)语法格式
LDR|STR{}{B}{T} ,[Rn],±
其中:
Rn 为基址寄存器,该寄存器包含内存访问的基地址;
为偏移地址寄存器,包含内存访问地址偏移量。
(3)操作伪代码
Address = Rn
If conditionPassed{cond} then
If U = = 1 then
Rn = Rn + Rm
Else
Rn = Rn – Rm
(4)说明
① LDRBT、LDRT、STRBT 和 STRT 指令只支持后索引寻址。
② 如果 Rm 和 Rn 指定为同一寄存器,指令的执行结果不可预知。
9.[Rn],±Rm,#< offset_12>]
(1)编码格式
指令的编码格式如图 4.22 所示。
cond
01
1
0 U B
0
L
Rn
Rd
shift_imm
31
28 27 26 25
20 19
16 15
12 11
0
shift
0
Rm
7 6
4 3
5
图 4.22 内存访问指令——带移位的后索引寄存器偏移寻址编码格式
实际的内存访问地址为寄存器 Rn 的值。当指令的执行条件满足时,将基址寄存器值加/减一个地址偏移量
产生新的地址值。
(2)语法格式
语法格式有以下 5 种。
LDR|STR{}{B}{T} ,[],±,LSL #< offset_12>
LDR|STR{}{B}{T} ,[],±,LSR #< offset_12>
LDR|STR{}{B}{T} ,[],±,ASR #< offset_12>
LDR|STR{}{B}{T}