基于ARM的嵌入式系统程序开发要点(五)--ARM/Thumb的交互工作
时间:04-05
来源:互联网
点击:
引言
在前面的文章中提到过,很多情况下应用程序需要在ARM跟Thumb状态之间相互切换,这部分就讨论交互工作的实现方法和一些注意问题。
1 需要交互的原因
前面提到过,Thumb指令在某些特殊情况下具有比ARM指令更为出色的表现,主要是在代码长度和窄带宽存储器系统性能两方面。正是因为Thumb指令在特定环境下的优势,它在很多方面得到了广泛应用。但是因为下面一些原因,Thumb又不可能独立地组成一个应用系统。
◇ Thumb指令集在功能上只是ARM指令集的一个子 集,某些功能只能在ARM状态下执行,如CPSR和 协处理器的访问。
◇ 进行异常响应时,处理器会自动进入ARM状态。
◇ 从系统优化考虑,在宽带存储器上不应该放置 Thumb代码,很多窄带系统具有宽带的内部存储器。
◇ 即使是一个单纯的Thumb应用系统,也必须加一 个汇编的交互头程序,因为系统总是自动从ARM 开始启动。
所以,不可避免地会产生ARM与Thumb之间交互的问题。
2 状态切换的实现
处理器在 ARM/Thumb之间的状态切换是通过一条专用的跳转交换指令BX来实现的。BX指令以通用寄存器(R0~R15)为操作数,通过拷贝Rn到PC来实现 4GB空间范围内的一个绝对跳转。BX利用Rn寄存器中存储的目标地址值的最后一位来判断跳转后的状态。如图1所示,是用BX指令实现状态切换。
无论ARM还是Thumb,其指令存储在存储器中都是边界对齐的(4字节或2字节对齐)。因此,在执行跳转过程中,PC寄存器中的最低位肯定被舍弃,不起作用。在BX指令的执行过程中,最低位正好被用作状态判断的标识,不会造成存储器访问不对齐的错误。
图 2 中是一段直接进行状态切换的例程:
下面是一段直接进行状态切换的例程。
;从ARM状态开始
CODE32 ;汇编关键字
ADR R0, Into_Thumb+1 ;得到目标地址,末位置1, ;转向Thumb
BX R0 ;执行 ? ;其它代码
CODE16 ;汇编关键字
Into_Thumb ;Thumb代码段起始地址
… ;Thumb代码
ADR R5, Back_to_ARM ;得到目标地址,末位缺 ;省为0,转向ARM
BX R5 ;执行
… ;其它代码
CODE32 ;汇编关键字
Back_to_ARM ;ARM代码段起始地址
我们知道,在ARM的状态寄存器CPSR 中,bit-5是状态控制位T-bit,决定当前处理器的运行状态。如果直接修改CPSR的状态位,也能够达到改变处理器运行状态的目的。但是这样会带来一个问题,因为ARM采用多级流水线的结构,所以在程序执行过程中,指令流水线上会存在几条预取指令(具体数目视流水线级数而不同)。当修改CPSR的 T-bit后,状态的转变会造成流水线上预取指令的执行错误。而如果用BX指令,则执行后会进行流水线的刷新动作,清除流水线上的残余指令,在新的状态下重新开始指令预取,从而保证状态转变时指令流的正确衔接。
3 ARM/Thumb之间的函数调用
在无交互的子程序调用中,其过程比较简单。实现调用通常只需要一条指令:
BL function
实现返回也只需要从LR恢复PC即可:
MOV PC, LR
函数的调用过程如图3所示。
如果子函数和父函数不在同一种状态下执行,因为状态切换,需要对函数调用作更多的考虑。
① BL不能完成状态切换,需要由BX来切换状态。
② BX不能自动保存返回地址到LR,需要在BX之前先保存好LR。
③ 用“BX LR”来返回,不能使用“MOV PC, LR”,因为这条指令同样不能实现状态切换。返回时要仔细考虑保存在LR中最低位的内容是否正确。
假如用户直接使用汇编语言进行状态交互跳转,上述的几个问题都需要用手工编码加以处理。如果用户使用高级语言进行开发,不需要为ARM/Thumb之间的相互调用增加额外编码,但是最好要对其调用过程加以了解。下面以ARM ADS中的编译工具为例进行说明,如图4所示。
① 两个函数func1()和func2()被编译成不同的指令集(ARM或Thumb)。
注意:func1()和 func2()在这里位于二个不同的源文件中。
② 编译时必须告诉编译器和链接器足够的信息,一方面让编译器能够使用正确的指令码进行编译,另一方面,当在不同的状态之间发生函数调用时,链接器将插入一段链接代码(veneers)来实现状态转换。
上述过程中的一个特点是:func1()还是使用通常的BL指令来进行子程序调用,而 func2()返回时则直接使用“BX LR”,没有对LR进行判断和最低位的设置。这是因为当执行BL指令对LR进行保存时,其最低位会被自动设置,以满足返回时状态切换的需要,可直接使用 “BX LR”。
在上面的例子中,为了让编译器在编译函数func2()时使用BX而不是BL进行返回,必须告诉编译器要按照满足交互工作要求的方式进行编译。在ARM的编译器选项设置中,应选择“-apcs /interwork”。这样,函数的返回指令会被正确设置,并且当链接器进行目标代码的链接时,能够在需要的地方插入正确的链接代码实现状态切换。
当然,插入了链接代码会相应地增加代码长度,通常一段veneer包含3条指令,即12B字节长度。可以用“-info veneers”选项使链接器输出所有veneers的位置和长度信息。
在前面的文章中提到过,很多情况下应用程序需要在ARM跟Thumb状态之间相互切换,这部分就讨论交互工作的实现方法和一些注意问题。
1 需要交互的原因
前面提到过,Thumb指令在某些特殊情况下具有比ARM指令更为出色的表现,主要是在代码长度和窄带宽存储器系统性能两方面。正是因为Thumb指令在特定环境下的优势,它在很多方面得到了广泛应用。但是因为下面一些原因,Thumb又不可能独立地组成一个应用系统。
◇ Thumb指令集在功能上只是ARM指令集的一个子 集,某些功能只能在ARM状态下执行,如CPSR和 协处理器的访问。
◇ 进行异常响应时,处理器会自动进入ARM状态。
◇ 从系统优化考虑,在宽带存储器上不应该放置 Thumb代码,很多窄带系统具有宽带的内部存储器。
◇ 即使是一个单纯的Thumb应用系统,也必须加一 个汇编的交互头程序,因为系统总是自动从ARM 开始启动。
所以,不可避免地会产生ARM与Thumb之间交互的问题。
2 状态切换的实现
处理器在 ARM/Thumb之间的状态切换是通过一条专用的跳转交换指令BX来实现的。BX指令以通用寄存器(R0~R15)为操作数,通过拷贝Rn到PC来实现 4GB空间范围内的一个绝对跳转。BX利用Rn寄存器中存储的目标地址值的最后一位来判断跳转后的状态。如图1所示,是用BX指令实现状态切换。
无论ARM还是Thumb,其指令存储在存储器中都是边界对齐的(4字节或2字节对齐)。因此,在执行跳转过程中,PC寄存器中的最低位肯定被舍弃,不起作用。在BX指令的执行过程中,最低位正好被用作状态判断的标识,不会造成存储器访问不对齐的错误。
图 2 中是一段直接进行状态切换的例程:
下面是一段直接进行状态切换的例程。
;从ARM状态开始
CODE32 ;汇编关键字
ADR R0, Into_Thumb+1 ;得到目标地址,末位置1, ;转向Thumb
BX R0 ;执行 ? ;其它代码
CODE16 ;汇编关键字
Into_Thumb ;Thumb代码段起始地址
… ;Thumb代码
ADR R5, Back_to_ARM ;得到目标地址,末位缺 ;省为0,转向ARM
BX R5 ;执行
… ;其它代码
CODE32 ;汇编关键字
Back_to_ARM ;ARM代码段起始地址
我们知道,在ARM的状态寄存器CPSR 中,bit-5是状态控制位T-bit,决定当前处理器的运行状态。如果直接修改CPSR的状态位,也能够达到改变处理器运行状态的目的。但是这样会带来一个问题,因为ARM采用多级流水线的结构,所以在程序执行过程中,指令流水线上会存在几条预取指令(具体数目视流水线级数而不同)。当修改CPSR的 T-bit后,状态的转变会造成流水线上预取指令的执行错误。而如果用BX指令,则执行后会进行流水线的刷新动作,清除流水线上的残余指令,在新的状态下重新开始指令预取,从而保证状态转变时指令流的正确衔接。
3 ARM/Thumb之间的函数调用
在无交互的子程序调用中,其过程比较简单。实现调用通常只需要一条指令:
BL function
实现返回也只需要从LR恢复PC即可:
MOV PC, LR
函数的调用过程如图3所示。
如果子函数和父函数不在同一种状态下执行,因为状态切换,需要对函数调用作更多的考虑。
① BL不能完成状态切换,需要由BX来切换状态。
② BX不能自动保存返回地址到LR,需要在BX之前先保存好LR。
③ 用“BX LR”来返回,不能使用“MOV PC, LR”,因为这条指令同样不能实现状态切换。返回时要仔细考虑保存在LR中最低位的内容是否正确。
假如用户直接使用汇编语言进行状态交互跳转,上述的几个问题都需要用手工编码加以处理。如果用户使用高级语言进行开发,不需要为ARM/Thumb之间的相互调用增加额外编码,但是最好要对其调用过程加以了解。下面以ARM ADS中的编译工具为例进行说明,如图4所示。
① 两个函数func1()和func2()被编译成不同的指令集(ARM或Thumb)。
注意:func1()和 func2()在这里位于二个不同的源文件中。
② 编译时必须告诉编译器和链接器足够的信息,一方面让编译器能够使用正确的指令码进行编译,另一方面,当在不同的状态之间发生函数调用时,链接器将插入一段链接代码(veneers)来实现状态转换。
上述过程中的一个特点是:func1()还是使用通常的BL指令来进行子程序调用,而 func2()返回时则直接使用“BX LR”,没有对LR进行判断和最低位的设置。这是因为当执行BL指令对LR进行保存时,其最低位会被自动设置,以满足返回时状态切换的需要,可直接使用 “BX LR”。
在上面的例子中,为了让编译器在编译函数func2()时使用BX而不是BL进行返回,必须告诉编译器要按照满足交互工作要求的方式进行编译。在ARM的编译器选项设置中,应选择“-apcs /interwork”。这样,函数的返回指令会被正确设置,并且当链接器进行目标代码的链接时,能够在需要的地方插入正确的链接代码实现状态切换。
当然,插入了链接代码会相应地增加代码长度,通常一段veneer包含3条指令,即12B字节长度。可以用“-info veneers”选项使链接器输出所有veneers的位置和长度信息。
- 基于ARM的除法运算优化策略(01-14)
- 基于ARM的CAN总线智能节点的设计(01-24)
- ARM基础知识教程五 (02-08)
- ARM基础知识教程六(02-08)
- ARM基础知识教程七(02-08)
- ARM基础知识教程八(02-08)