
什么是Semihosting(半主机)- ARM处理器与主机之间IO通信机制
Semihosting是一种在Arm处理器上运行的应用程序与调试器的主机进行通信的技术。通过这个机制,跑在ARM处理器上的应用程序可以使用主机的IO设备,比如键盘输入,屏幕输出以及文件的IO等等。如果目标平台(目标开发板)没有这些IO设备,或者为了使用print()函数输出一些debug信息,那将非常有用。在AArch32模式下,应用程序通过使用 SVC(SWI)指令并携带一个特定的 SVC nu
文章目录
Semihosting简介
Semihosting是一种在Arm处理器上运行的应用程序与调试器的主机进行通信的技术。通过这个机制,跑在ARM处理器上的应用程序可以使用主机的IO设备,比如键盘输入,屏幕输出以及文件的IO等等。如果目标平台(目标开发板)没有这些IO设备,或者为了使用print()函数输出一些debug信息,那将非常有用。
- 在AArch32模式下,应用程序通过使用 SVC(SWI)指令并携带一个特定的 SVC number,来触发一个异常,从而实现一个semihosting 请求。操作的类型是通过在通用寄存器R0,R1中传递参数确定的。在ARMv7中,优先使用 SVC指令,早期称为SWI。但是在ARMv6-M 或者 ARMv7-M, 比如 Cortex™-M1 或Cortex-M3 ,它们使用 BKPT指令来产生 semihosting请求。
- 在AArch64模式下,应用程序使用 HLT 指令,并携带一个常数来创建一个semihosting 请求,但是它不会产生异常。
正常情况下,ARM RealView编译器中的C 库函数,比如printf()和scanf()将会触发semihosting 请求。应用程序也可以通过键盘输入、屏幕输出以及文件IO来触发semihosting。详细描述可以参考文档:RealView Compilation Tools Developer Guide
上图是使用SVC指令实现目标板与主机进行pirntf()打印显示的示例:
- 应用程序调用printf函数
- printf函数里使用SVC指令触发异常
- 异常被调试器处理
- 调试器与主机进行通信
- 主机屏幕上显示hello打印
总之,semihosting请求有两种方式创建:
- 使用SVC(SWI) 或者BKPT指令
- 使用HLT指令
与主机上的I/O工具连接的调试器会处理这两种方式产生的请求,并开始与主机进行通信。
下面以Trace32调试器为例,详细介绍32和64模式下的semihosting。
在大多数情况下,semihosting请求是由库函数唤醒,应用程序也可以直接唤醒semihosting请求。ARM A系列和R系列都支持A64, A32, 以及T32指令集,对于M系列,只使用T32指令集。
semihosting的操作请求是通过 trap 指令:SVC, HLT, or BKPT来实现的,关于exception,trap以及interrupt的区别,如下:
异常 Exception , An unusual internal event caused by program during execution 程序在执行过程中引起的异常内部事件。比如页错误page fault, 算术溢出arithmetic underflow
中断 Interrupt , An external event outside of running program。在正在运行的程序之外的事件。
陷阱Trap ,Forced transfer of control to supervisor caused by exception or interrupt,Not all exceptions cause
traps,由于异常和中断,强制将控制权转移给supervisor,并不是所有的异常都会导致traps。
Semihosting Trap指令以及编码总结如下:
Profile | Instruction Set | Instruction | Opcode |
---|---|---|---|
A+R Profile | A64 | HLT #0xF000 | 0xD45E0000 |
A32 | SVC #0x123456 | 0xEF123456 | |
A32 | HLT #0xF000 | 0xE10F0070 | |
T32 | SVC #0xAB | 0xDFAB | |
T32 | HLT #0x3C | 0xBABC | |
M–Profile | T32 | BKPT #0xAB | 0xBEAB |
对于A32和T32的A系列以及R系列而言,可以使用SVC指令和HLT指令,Semihosting的实现必须支持ARM架构中该指令的所有版本。尽管HLT指令在ARMv8中被定义,但是在ARMv7以及更早的版本中也可以使用,当然,这可能需要semihosting agent把它当作 UNDEF 异常来处理。
AArch64 HLT Emulation Mode
使用 HLT 指令可以将 处理器停止,并且不需要设置额外的断点。为了触发semihosting请求,HLT指令需要带一个 0xF000 的立即数,在semihosting需要传递的数据被处理完后,调试器将重启处理器。
在Trace32中,这个模式通过指令TERM.METHOD ARMSWI [<address>]
来使能,并且在 TERM.GATE窗口上显示semihosting的屏幕输出。此外,只有当 TERM.GATE窗口存在,对semihosting请求的处理才会生效。
TERM.HEAPINFO 定义了系统的堆栈位置,C库通过SYS_HEAPINFO semihosting 请求来读取内存信息,并且用它们来做初始化。
在**~~/demo/arm/etc/semihosting_arm_emulation/armv8_aarch64/halt_armv8.cmm**中可以找到AArch64的使用示例。
下图为AArch64模式下的semihosting示意图,左边为target,目标板,右边为PC主机,中间的为调试器。调试器与目标板通过JTAG连接,目标主机通过USB或者以太网接口与调试器连接。Trace32程序在主机上运行,主机上还有显示器键盘以及文件系统等IO设备。
AArch64 DCC Communication Mode (DCC = Debug Communication Channel)
在AArch64下,用户不能为semihosting使用Arm 库,因为HLT指令将会是处理器停止工作。因此处理DCC通信的异常处理器也不会被执行。
在semihosting的Arm 库不能被使用的情况下,用户可以替代性地对semihosting请求使用原生的Trace32格式。并且并不需要SWI处理器(在t32swi.c中),用户可以通过DCC直接发送请求。
在Trace32中,这个模式通过指令TERM.METHOD DCC3
来使能,并且在 TERM.GATE窗口上显示semihosting的屏幕输出。此外,只有当 TERM.GATE窗口存在,对semihosting请求的处理才会生效。 TERM.HEAPINFO 定义了系统的堆栈位置,C库通过SYS_HEAPINFO semihosting 请求来读取内存信息,并且用它们来做初始化。
示例程序见 ~~/demo/arm/etc/semihosting_trace32_dcc
AArch32 SVC(SWI)Emulation Mode
SVC异常将会将程序停止,一个断点将会在SVC异常入口处产生。当程序停止时,调试器开始处理semihosting请求,并提供与主机必要的通信接口,在SVC异常调用时,会将当前PC寄存器中的地址存储到ELR寄存器中,当调试器重启应用程序时,便从ELR中获取地址。其他比如DCC模式,SVC的参数需为0x123456,来表明这是个semihosting 请求。
在Trace32中,这个模式通过指令TERM.METHOD ARMSWI [<address>]
来使能,并且在 TERM.GATE窗口上显示semihosting的屏幕输出。此外,只有当 TERM.GATE窗口存在,对semihosting请求的处理才会生效。
TERM.HEAPINFO 定义了系统的堆栈位置,C库通过SYS_HEAPINFO semihosting 请求来读取内存信息,并且用它们来做初始化。
当使用TERM.METHOD ARMSWI [<address>]
,任何有断点的内存位置都可以用作semihosting 服务入口,而不是SVC调用。应用程序只需要跳转到那个位置。在为请求提供服务之后,程序继续在该地址执行,不是在链接寄存器ELR中的地址。例如可以在该地址放置一个ERET
命令,并在ELR中提交返回地址。由于此方法不使用SVC命令,没有参数(0x123456)将被检查以识别semihosting。
An example for AArch32 can be found in:~~/demo/arm/etc/semihosting_arm_emulation/armv8_aarch32/swisoft_armv8.cmm
AArch32 DCC Communication Mode (DCC = Debug Communication Channel)
SVC异常将会触发一个semihosting异常处理器。它用于基于与主机通信的JTAG接口的DCC。目标程序将不会停止,但是semihosting异常处理器需要被加载或者连接到该程序。这个模式只当目标板提供DCC模式时被使用。
An example (swidcc_x.cmm) and the source of the Arm compatible semihosting handler (t32swi.c,
t32helper_x.c) can be found in ~~/demo/arm/etc/semihosting_arm_dcc
semihosting 性能描述
事实上,semihosting 机制并不能提供高性能的IO系统访问。每次进行semihosting操作,在进行数据传输时,处理器基本上都是处于停止状态的。这所需的时间在一定程度上取决于目标CPU、调试探针/链接(因此Red probe +在PC- link上提供了大大增强的半托管速度)、PC硬件和PC操作系统。所以semihosting 需要一定的时间,这可能会使用户的代码运行得更慢。所以在调试程序性能问题的时候,尽量不要使用printf()打印。
semihosting使用注意事项
当用户使用了semihosting库后,用户的应用程序将不再独立工作——它只会在连接到调试器时工作。
semihosting操作会导致CPU进入“调试状态”,这意味着在目标cpu和主机PC之间的数据传输期间,目标cpu上不会执行任何代码(包括中断)。
因此,如果用户的应用程序使用了中断,那么通常建议在中断活动中避免使用semihosting,对于一般程序来讲就是printf()了。如果仍然需要使用printf,那么建议使用可替代的通信接口,比如UART。
参考文档
https://www2.lauterbach.com/pdf/debugger_armv8v9.pdf
https://community.nxp.com/t5/LPCXpresso-IDE-FAQs/What-is-Semihosting/m-p/475390
https://github.com/ARM-software/abi-aa/blob/2982a9f3b512a5bfdc9e3fea5d3b298f9165c36b/semihosting/semihosting.rst
更多推荐
所有评论(0)