http//www.elecfans.com 电子发烧友 http//bbs.elecfans.com ARM 处理器中 ARM 和 Thumb 状态的切换 (Interworking) 潘朝霞北京交通大学电气学院王毅北京交通大学电气学院 摘要 主要介绍了在 ARM 处理器中,ARM/Thumb 状态切换的原因和方法 在基于 ARM 处理器的嵌入式开发中, 为了增强系统的灵活性以及提高系统的整体性能经常需要使用 16 位的 Thumb 指令, 所以需要在 ARM 和 Thumb 状态之间来切换 (Interworking) 微处理器状态, 这部分内容也是实际项目设计中需要重点考虑的内容 关键字 Interworking,ARM/THUMB,Veneer Exchange the State of the ARM/Thumb on the ARM Chip ABSTRACT The reasons and the means about exchange the state of the ARM/Thumb Interworking were introduced in the paper. In the development of the RISC application based on the ARM chip, sometimes, in order to improved the system facility and performance, we might use THUMB instruction in our application code. The difference between the V4T and V5T were shown. Keywords Interworking,ARM/THUMB,Veneer 引言 近年来,32 位 RISC 芯片性价比快速提高, 使得基于 32 位处理器 ( 特别是 ARM) 的嵌入式应用迅猛地上升 在 32 位控制器领域,ARM 架构的芯片占据了 60%--70% 的市场 在 ARM 体系中有一些特定功能称为 ARM 体系的变种 ( variant), 其中支持 Thumb 指令集, 称为 T 变种 这样 ARM 微处理器就有两种工作状态 ARM/Thumb, 并可在两种状态之间切换 只要遵循 ATPCS 调用规则,Thumb 子程序和 ARM 子程序就可以互相调用 在这种嵌入式系统软件开发中, 为了增强系统的灵活性以及提高系统的整体性能经常需要使用 16 位的 Thumb 指令 如何有效 准确地使用 ARM/Thumb 状态切换 (Interworking) 是关系到整个系统成败的关键环节, 也是在具体项目开发过程中相对比较难掌握的内容 本文主要介绍 ARM 体系结构中的 ARM/Thumb 状态切换 (Interworking) 1. ARM/Thumb 指令的性能比较在 ARM 处理器中, 内核同时支持 32 位的 ARM 指令和 16 位的 Thumb 令 对于 ARM 指令来说, 所有的指令长度都是 32 位, 并且执行周期大多为单周期, 指令都是有条 件执行的 而 THUMB 指令的特点如下 指令执行条件经常不会使用 ; 源寄存器与目标寄存器经常是相同的 ; 使用的寄存器数量比较少 ; 常数的值比较小 ; 内核中的桶式移位器 (barrel shifter) 经常是不使用的 ; 也就是说 16 位的 Thumb 指令一般可以完成和 32 位 ARM 相同的任务 当用户使用 C 程序来处理应用时, 如果编译为 Thumb 指令, 那么它的目标代码大小只有编译为 ARM 指令时的 65% 左右, 这样就增加了指令密度 从另一方面来看, 处理器在这两种状态下的性能是依赖于指令执行的存储器的宽度的 下面的图一具体说明二者的性能比较 可以看出, 在存储器是 32 位的情况下,ARM 性能较好, 这时因为同样的代码编译的结果 Thumb 指令将会比 ARM 多, Thumb 指令仍旧花费指令周期来从 32-bit 块内存预取 在 16-bit 内存上, 即使有比 ARM 多的代码, 这时 Thumb 性能也较好, 因为 Thumb 每一条指令预取需要一个周期而每条 ARM 指令需要两个周期 另外在 16-bit 内存上,Thumb 的性能降低了 ; 这是 1
http//www.elecfans.com 电子发烧友 http//bbs.elecfans.com 因为数据去操作和特殊的堆栈操作, 即使在 Thumb 下, 堆栈操作仍是 32-bit 操作, 导致低的性能在 16-bit 内存架构上 一个改进的方法是提供 32-bit 的内存来放置堆栈 在这种情况下的性能提高到了 32-bit 内存架构的水平 主要的差别是因为使用的整型的 (32-bit) 全局数据将仍被存储在 16-bit 内存上 另外, 与 ARM 代码相比较, 使用 Thumb 代码, 存储器的功耗会降低约 30% 30000 25000 20000 15000 10000 5000 0 32bit 16bit 16bit with Dhrystone 2.1/sec 32bit @20Mhz stack 图一显然,ARM 指令集和 Thumb 指令集各有其优点, 若对系统的执行效率有较高的要求, 应使用 32 位的存储系统和 ARM 指令集, 若对系统的成本及功耗有较高的要求, 则应使用 16 为的存储系统和 Thumb 指令集 当然, 若两者结合使用, 充分发挥其各自的优点, 会取得更好的效果 2. 切换 (Interwoking) 的基本概念及切换时的子函数调用在我们的实际系统应用中, 因为 ARM/Thumb 指令具有不同的特点, 所以不同的场合开发人员会有不同的选择 Thumb 指令低密度及在窄存储器时性能高的特点使得它在大多数基于 C 代码的系统中有非常广泛的应用, 但是有些场合中系统只能使用 ARM 指令, 比如 如果对于速度有比较高的要求,ARM 指令在宽存储器中会提供更高的性能 ; 某些功能只能由 ARM 指令来实现, 比 如 访问 CPSR 寄存器来使能 / 禁止中断或者改变处理器工作模式 ; 访问协处理器 CP15; 执行 C 代码不支持的 DSP 算术指令 ; 异常中断 (Exception) 处理 在进入异常中断后, 内核自动切换到 ARM 状态 即在异常中断处理程序入口的一些指令是 ARM 指令, 然后根据需要程序可以切换到 Thumb 状态, 在异常中断处理程序返回前, 程序再切换到 ARM 状态 ARM 处理器总是从 ARM 状态开始执行 因而, 如果要在调试器中运行 Thumb 程序, 必须为该 Thumb 程序添加一个 ARM 程序头, 然后再切换到 Thumb 状态, 调用该 Thumb 程序 所以在实际系统中, 内核状态需要经常的切换 (Interworking) 来满足系统性能需求 具体的切换是通过 ARM Branch Exchange 即 BX 指令来实现的 指令格式为 THUMB Thumb 状态 BX Rn ARM 状态 BX<condition> Rn 其中 Rn 可以是寄存器 R0 R15 中的任意一个 指令可以通过将寄存器 Rn 的内容拷贝到程序计数器 PC 来完成在 4Gbyte 地址空间中的绝对跳转, 而状态切换是由寄存器 Rn 的最低位来指定的, 如果操作数寄存器的状态位 Bit0=0, 则进入 ARM 状态, 如果 Bit0=1, 则进入 Thumb 状态, 图二给出了具体得切换过程 31 1 0 Rn 0-ARM 状态 BX 1-Thumb 状态 31 1 0 0 目标地址 图二 下面是某系统中使用的程序切换实例 CODE32 //ARM 状态下的代码 LDR R0, =Into_Thumb+1 // 产生跳转地址并且设置最低位 2
http//www.elecfans.com 电子发烧友 http//bbs.elecfans.com BX R0 //Branch Exchange 进入 Thumb 状态 CODE16 //Thumb 状态下的子函数 LDR R3, =Back_to_ARM // 产生字对齐的跳转地址, 最低位被清除 BX R3 //Branch Exchange 返回到 ARM 状态 CODE32 //ARM 状态下的子函数 Bach_to_ARM 在上面的程序中,CODE16/CODE32 伪指令告诉汇编编译器后面的指令序列分别为 Thumb/ARM 指令 在非 Interworking 函数调用中, 调用函数使用 (Branch with Link) 指令, 即将返回地址保存在连接寄存器 LR 中, 同时跳转到被调用的子函数程序入口 从子函数返回时执行指令 MOV PC, LR( 当然也可能是其他形式的指令, 如出栈指令 ) 将 LR 值直接放入 PC 中, 从而返回到调用函数中的下一条指令的地址, 然后继续执行程序 在 Interworking 函数的调用中, 需要在编译时对此函数所在的源程序指定编译开关选项 -apcs / interwork, 即保证程序遵守 ARM/Thumb 程序混合使用的 ATPCS 规则 一般来说, 这时生成的目标代码会增加 2% 左右 这样在编译器 (compiler) 处理这个函数时就会用 BX 指令取代 MOV PC,LR 指令, 而且连接器 (linker) 会自动的产生一小段代码 (veneers) 来改变处理器状态 (ARM/Thumb), 具体过程如图 3 所示 函数一连接器产生函数二的代码 (veneer) BX LR BX 图三 编译 / 连接命令为 armcc -apcs/interwork arm_code.c o arm_code.o tcc -apcs/interwork thumb_code.c o thumb_code.o armlink arm_code.o thumb_code.o 对于 C/C++ 程序来说, 当编译时如果增加 apcs/interwork 选项, 那就是告诉连接器自动增加一小段代码 (veneer) 来实现函数调用时 ARM/Thumb 的状态切换 但是对于使用 C 程序中的 Interwork 选项, 需要注意的是 对于一个 C /C++ 源程序中不能同时包含 ARM/Thumb 指令 ; 如果 C/C++ 程序间接的调用另一种指令系统下的子程序, 编译该程序时需要增加 -apcs/interwork 选项 ; 如果调用程序和被调用程序是不同的指令, 而被调用程序是 Non-Interworking 代码, 这时不要使用函数指针来调用该被调用程序 下面的图四显示了 C/C++ 程序在增加编译选项 -apcs/interwork 时将代码分别编译为 ARM/THUMB 指令时的情况 由于在 Thumb 状态下不能直接使用 POP LR, 所以使用了暂时寄存器 R3 对于汇编程序来说, 如果本代码是被调用的函数, 则需按照以下步骤处理 增加 apcs/interwork 选项 ; 用 BX 来返回 ; EXPORT 本函数名 ; 如果本代码是调用函数, 那就只需要用 指令来实现子函数的调用即可, 也就是正常的处理 当然, 用户也可以自己来编写这些状态切换程序, 这样执行代码的效率会更高些 对于 C/C++ 程序和汇编程序的相互调用同样需要遵守以上的规则 另外, 在实际应用中, 如果要在 ARM/Thumb 状态间来切换程序, 最好的办法是所有的函数在编译时都增加 apcs/interwork 选项 3
http//www.elecfans.com 电子发烧友 http//bbs.elecfans.com C code Void func(void) { sub } ARM code Thumb code func func STMFD sp!,{r4-r11,1r} PUSH {r4-r11,1r} LDMFD sp!,{r4-r11,pc} POP {r4-r11,pc} 增加编译选项 apcs/interwork 后返回指令的变化 LDMFD sp!,{r4-r11,1r} POP {r4-r7} BX 1r POP {r3} BX r3 图四其中 Thumb 状态下因为不能直接使用 POP LR 指令, 所以使用了暂时存储器 r3 3.V5TE 架构中的扩展前面所提到的内容是针对 ARM 微处理器内核为 V4T 架构时的切换情况, 而对于 V5TE 架构的 ARM 内核, 除了完全支持 V4T 架构的代码 ( 具有 veneers) 外, 代码在连接时不再增加 veneers, 而是使用新的指令 X(Branch and Link with Exchang) 来实现状态切换 这条指令完成完成的任务是 在跳转时将返回的指令地址保存在 LR 寄存器中, 同时将 PC 中的最低位的值拷贝到 CPSR 寄存器中的 T 位, 从而改变处理器状态 (Exchange) 一般来说, 对于调用函数使用 X 指令即可, 被调用函数则与 V4T 架构相同, 也是使用 BX 指令来返回 用 ARM 处理器的 ARM/Thumb 指令则是一种比较有效的方法 总的来说, 在切换中程序需要遵守支持 ARM 程序和 Thumb 程序混合使用的 ATPCS 故如何在 ARM/THUMB 状态间切换是一个开发过程中的关键环节, 需要开发人员有比较深刻的理解, 本文也正是从这个角度出发来阐述这方面的基本知识的 参考文献 1 ARM 体系结构与编程杜春雷 2003 清华大学出版社 2 ARM 应用系统开发详解李驹光聂雪媛等 2003 清华大学出版社 结束语 虽然 ARM 状态有其无可比拟的优点, 但是对于实时性 复杂程度要求越来越高的嵌入式系统应用, 应用 Thumb 状态是不可避免的 而且, 如何有效地提高系统的性能是开发人员必须面对的一个任务, 灵活地使 4
http//www.elecfans.com 电子发烧友 http//bbs.elecfans.com 作者介绍 潘朝霞女 1975 年出生山西文水人北京交通大学硕士研究方向 电力系统监测 诊断 控制及智能管理 通信地址 北京交通大学电气工程学院 46# 单位 北京交通大学电气工程学院 邮编 100044 手机 13641061916 E-mailzhaoxiapan@163.com 5