什么是Semihosting(半主机)- ARM处理器与主机之间IO通信机制

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产生异常来调用semihosting
上图是使用SVC指令实现目标板与主机进行pirntf()打印显示的示例:

  1. 应用程序调用printf函数
  2. printf函数里使用SVC指令触发异常
  3. 异常被调试器处理
  4. 调试器与主机进行通信
  5. 主机屏幕上显示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指令以及编码总结如下:

ProfileInstruction SetInstructionOpcode
A+R ProfileA64HLT #0xF0000xD45E0000
A32SVC #0x1234560xEF123456
A32HLT #0xF0000xE10F0070
T32SVC #0xAB0xDFAB
T32HLT #0x3C0xBABC
M–ProfileT32BKPT #0xAB0xBEAB

对于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

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
QEmu虚拟机 做嵌入式开发的朋友试试 调试内核很方便 QEMU emulator version 0.13.0, Copyright (c) 2003-2008 Fabrice Bellard usage: qemu [options] [disk_image] 'disk_image' is a raw hard disk image for IDE hard disk 0 Standard options: -h or -help display this help and exit -version display version information and exit -M machine select emulated machine (-M ? for list) -cpu cpu select CPU (-cpu ? for list) -smp n[,maxcpus=cpus][,cores=cores][,threads=threads][,sockets=sockets] set the number of CPUs to 'n' [default=1] maxcpus= maximum number of total cpus, including offline CPUs for hotplug, etc cores= number of CPU cores on one socket threads= number of threads on one CPU core sockets= number of discrete sockets in the system -numa node[,mem=size][,cpus=cpu[-cpu]][,nodeid=node] -fda/-fdb file use 'file' as floppy disk 0/1 image -hda/-hdb file use 'file' as IDE hard disk 0/1 image -hdc/-hdd file use 'file' as IDE hard disk 2/3 image -cdrom file use 'file' as IDE cdrom image (cdrom is ide1 master) -drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i] [,cyls=c,heads=h,secs=s[,trans=t]][,snapshot=on|off] [,cache=writethrough|writeback|none|unsafe][,format=f] [,serial=s][,addr=A][,id=name][,aio=threads|native] [,readonly=on|off] use 'file' as a drive image -set group.id.arg=value set parameter for item of type i.e. -set drive.$id.file=/path/to/image -global driver.property=value set a global default for a driver property -mtdblock file use 'file' as on-board Flash memory image -sd file use 'file' as SecureDigital card image -pflash file use 'file' as a parallel flash image -boot [order=drives][,once=drives][,menu=on|off] 'drives': floppy (a), hard disk (c), CD-ROM (d), network (n) -snapshot write to temporary files
FASMARM v1.42 This package is an ARM assembler add-on for FASM. FASMARM currently supports the full range of instructions for 32-bit and 64-bit ARM processors and coprocessors up to and including v8. Contents: 1. ARM assembly compatibility 2. UAL and pre-UAL syntaxes 3. IT block handling 4. Alternate encodings 5. Output formats 6. Control directives 7. Data definitions 8. Defining registers lists inside macros 9. Half-precision number formatting 10. Variants supported 11. Further information 12. Version history _______________________________________________________________________________ 1. ARM assembly compatibility There are a few restrictions how the ARM instruction set is implemented. The changes are minor and mostly have a minor impact. For the most part the basic instruction outline is the same. Where possible the original style is used but there are some differences: Not everything matches the ARM ADS assembly style, where possible the original style is used but there are some differences 1) label names cannot begin with a digit 2) CPSIE and CPSID formats are changed, use "iflags_aif" form instead of "aif" (eg. "CPSIE iflags_i" instead of "CPSID i") 3) SRS with writeback must have a separating space after the mode number and before "!" (eg. "SRSDB 16 !" instead of "SRSDB 16!") 4) macro, rept, irp, format, if, virtual etc. are all significant changes from the ARM ADS, so you will need to re-write those sections of existing code Original ARM Syntax | fasmarm Syntax ----------------------+---------------------- cpsie a | cpsie iflags_a | srsdb #29! | srsdb #29 ! ;or, | srsdb 29 ! _______________________________________________________________________________ 2. UAL and pre-UAL syntaxes fasmarm supports the original pre-UAL syntax and the newer UAL syntax. These two syntaxes only affect THUMB encodings. UAL stands for: Universal Assembly Language. pre-UAL syntax is selected wi
实验三 UC-OS移植实验 一、实验目的 在内核移植了uCOS-II 的处理器上创建任务。 二、实验内容 1.运行实验十,在超级终端上观察四个任务的切换。 2. 任务1~3,每个控制"红"、"绿"、"蓝"一种颜色的显示,适当增加OSTimeDly()的时间,且优先级高的任务延时时间加长,以便看清三种颜色。 引入一个全局变量 BOOLEAN ac_key,解决完整刷屏问题。 4. #define rUTRSTAT0 (*(volatile unsigned *)0x50000010) #define RdURXH0() (*(volatile unsigned char *)0x50000024) 当键盘有输入时在超级终端上显示相应的字符。 三、实验设备 硬件:ARM嵌入式开发平台、用于ARM920T的JTAG仿真器、PC机Pentium100以上。 软件:PC机操作系统Win2000或WinXP、ARM ADS1.2集成开发环境、仿真器驱动程序、超级终端通讯程序。 四、实验原理 所谓移植,指的是一个操作系统可以在某个微处理器或者微控制器上运行。虽然uCOS-II的大部分源代码是用C语言写成的,仍需要用C语言和汇编语言完成一些与处理器相关的代码。比如:uCOS-II在读写处理器、寄存器时只能通过汇编语言来实现。因为uCOS-II在设计的时候就己经充分考虑了可移植性,所以,uCOS-II的移植还是比较容易的。 要使uCOS一工工可以正常工作,处理器必须满足以下要求: 1)处理器的C编译器能产生可重入代码。 2)在程序中可以打开或者关闭中断。 3)处理器支持中断,并A能产生定时中断(通常在10Hz}1000Hz之间)。 4)处理器支持能够容纳一定量数据的硬件堆栈。 北航ARM9实验报告:实验3uCOS-II实验全文共6页,当前为第1页。5) 处理器有将堆栈指针和其它CPU寄存器存储和读出到堆栈(或者内存)的指令。 北航ARM9实验报告:实验3uCOS-II实验全文共6页,当前为第1页。 uCOS-II进行任务调度的时候,会把当前任务的CPU寄存器存放到此任务的堆栈中,然后,再从另一个任务的堆栈中恢复原来的工作寄存器,继续运行另一个任务。所以,寄存器的入栈和出栈是uCOS一工工多任务调度的基础。 五、实验步骤 1 以实验十为模板,将实验六 inc目录下的LCD320.H 和src目录下的LCD640.C拷到 模板下的相应目录,将LCD640.C加入工程中。 包含以下头文件 #include "inc/lcd320.h"。 改LCD640.C 文件中包含头文件的路径 。 #include "../inc/drv/reg2410.h" 4 声明引用的变量 extern U32 LCDBufferII2[LCDHEIGHT][LCDWIDTH]; 源程序 #include"../ucos-ii/includes.h" /* uC/OS interface */ #include "../ucos-ii/add/osaddition.h" #include "../inc/drivers.h" #include "../inc/sys/lib.h" #include "../src/gui/gui.h" #include "../inc/lcd320.h" #include <string.h> #include <stdio.h> //#include "..inc/lcd320.h" //#pragma import(__use_no_semihosting_swi) // ensure no functions that use semihosting OS_EVENT *MboxSem; ///******************任务定义***************/// /*OS_STK SYS_Task_Stack[STACKSIZE]= {0, }; //system task刷新任务堆栈 #define SYS_Task_Prio 1 void SYS_Task(void *Id);*/ OS_STK task1_Stack[STACKSIZE]={0, }; //Main_Test_Task堆栈 void Task1(void *Id); //Main_Test_Task #define Task1_Prio 12 北航ARM9实验报告:实验3uCOS-II实验全文共6页,当前为第2页。OS_STK task2_Stack[STACKSIZE]={0, }; //test_Test_Task堆栈 北航ARM9实验报告:实验3uCOS-II实验全文共6页,当前为第2页。 void Task2(void *Id); //tes

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SOC罗三炮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值