Difference between revisions of "Semihosting"

From SEGGER Wiki
Jump to: navigation, search
m (Implementation on different CPUs)
(Basic operation)
Line 46: Line 46:
 
The way to handle this is using a special hardfault handler, which checks the source of the problem, and allows "skipping" of the BKPT instruction and resumption of program execution.
 
The way to handle this is using a special hardfault handler, which checks the source of the problem, and allows "skipping" of the BKPT instruction and resumption of program execution.
   
=== Implementation on different CPUs===
+
==Implementation==
  +
===Supervisor Call===
==== ARM legacy cores (ARM7, ARM9, ARM11): SVC call====
 
Older ARM cores, such as ARM7, ARM9 and ARM11, used an SVC call for semihosting. Typically, the debug agent will set a break point or use vector catch
+
Legacy ARM cores, such as ARM7, ARM9, ARM11, as well as Cortex-A and Cortex-R devices typically use the supervisor call (SVC instruction, previously SWI) for semihosting.
  +
The debug agent sets a breakpoint or vector catch on the SVC handler.
to hold the CPU once it enters the SVC mode, then perform the host action and continue execution.
 
  +
In ARM Mode, the following instruction is used:
 
  +
On entry of the SVC handler, the debugger checks whether the call should trigger semihosting or not, and if it should, it performs the semihosting operation, does an exception return, and continues execution.
  +
If the supervisor call does not trigger semihosting, execution is continued in the SVC handler.
  +
  +
Semihosting via supervisor call is triggered by the SVC number, which is encoded in the SVC instruction.
  +
  +
In ARM Mode the SVC number can be 24 bit wide. The standard SVC number for semihosting is 0x123456:
 
SVC 0x123456
 
SVC 0x123456
  +
in Thumb mode, the same instruction is used but with another parameter. As in thumb mode the parameter is limited to 8 bits:
 
  +
In Thumb mode the SVC number is only 8 bit wide. The standard SVC number for semihosting is 0xAB:
 
SVC 0xAB
 
SVC 0xAB
See also http://www.keil.com/support/man/docs/armcc/armcc_pge1358787048379.htm
 
   
==== ARM Cortex devices: BKPT====
+
====SVC Handler====
  +
To ensure that the target application keeps running even when no debugger is connected,
A breakpoint is used to hold the CPU. The debug agent recognizes that this is a request for semihosting, not a "regular breakpoint" because it is a breakpoint that has not been set by the debug agent, and there are 2 implementations for it, for ARM or THUMB mode.
 
  +
an SVC handler should be implemented, which also checks whether the call should have triggered semihosting or not.
=====Thumb mode=====
 
  +
In case semihosting should have been triggered, the application might do a simple exception return.
If implemented in Thumb mode, the instruction used is
 
  +
When the SVC handler has been called for any other reason, the application might handle this accordingly.
  +
  +
SVC_Handler:
  +
push {R2,R3}
  +
//
  +
// Check whether we come from Thumb or ARM mode.
  +
// Application's CPSR is saved in SPSR.
  +
//
  +
mrs R2, SPSR
  +
tst R2, #0x20
  +
beq _CheckSemiARM // SPSR.T (Bit 5) not set -> ARM mode
  +
_CheckSemiThumb:
  +
//
  +
// Load immediate of Thumb SVC instruction
  +
//
  +
#if BIG_ENDIAN
  +
ldrb R2, [LR, #-2]
  +
#else
  +
ldrb R2, [LR, #-1]
  +
#endif
  +
//
  +
// Check if it has been a semihosting SVC
  +
//
  +
ldrb R3, =0xAB
  +
cmp R2, R3
  +
pop {R2,R3} // Restore registers (might be used by semihosting or SVC handler)
  +
bne _DoSVC // No semihosting SVC. Continue in SVC handler
  +
b _DoSemihost // Semihosting SVC. Run through debugger breakpoint and return
  +
_CheckSemiARM:
  +
//
  +
// Load immediate of ARM SVC instruction
  +
//
  +
ldr R2, [LR, #-4]
  +
bic R2, R2, #0xFF000000
  +
//
  +
// Check if it has been a semihosting SVC
  +
//
  +
ldr R3, =0x00123456
  +
cmp R2, R3
  +
//
  +
// Restore registers (might be used by semihosting or SVC handler)
  +
//
  +
pop {R2,R3}
  +
bne _DoSVC // No semihosting SVC. Continue in SVC handler
  +
//b _DoSemihost // Semihosting SVC. Run through debugger breakpoint and return
  +
_DoSemihost:
  +
//
  +
// Perform exception return to instruction after SVC.
  +
// Also resets CPSR.
  +
//
  +
subs PC, LR, 0
  +
_DoSVC:
  +
//
  +
// Stay in endless loop
  +
// User SVC handler could start here instead
  +
//
  +
b _DoSVC
  +
.end
  +
  +
===Software Breakpoint===
  +
Cortex-M devices can use the software breakpoint instruction (BKPT) instead of the supervisor call.
  +
  +
When a debug probe is connected, the target halts on execution of the BKPT. The debugger checks whether the breakpint should trigger semihosting or not. If it should, it performs the semihosting operation, skips the BKPT instruction, and lets the target run.
  +
  +
Semihosting via software breakpoint is triggered by the parameter which can be encoded with the BKPT instruction.
  +
  +
BKPT is always a 16 bit instruction with 8 bit parameter. The standard parameter for semihosting is 0xAB:
 
BKPT 0xAB
 
BKPT 0xAB
In Thumb mode, a 16-bit instruction space is used for the breakpoint, with one byte being used to recognize the breakpoint instruction, and the other one not being used, so available as parameter. 0xAB as second byte or parameter marks it as breakpoint for the purpose of semihosting.
 
Cortex-M device always run in Thumb mode (Thumb-2), therefore always use this instruction.
 
   
=====ARM mode=====
+
====Semihosting HardFault Handler====
  +
The disadvantage of the BKPT instruction is, that when no debugger is connected, a hard fault error occurs.
If implemented in ARM mode, the instruction used is
 
  +
The target needs to recover from the hard fault in order to continue execution.
BKPT 0x123456
 
  +
In ARM mode, a 32-bit instruction space is used for the breakpoint, with one byte being used to recognize the breakpoint instruction, and the remaining 3 available as parameter. 0x123456 as parameter marks it as breakpoint for the purpose of semihosting.
 
  +
HardFault_Handler:
  +
//
  +
// This version is for Cortex M3, Cortex M4 and Cortex M4F
  +
// Copy previously selected stack pointer to R0.
  +
//
  +
tst LR, #4 // Check EXC_RETURN in Link register bit 2.
  +
ite EQ
  +
mrseq R0, MSP // Stacking was using MSP.
  +
mrsne R0, PSP // Stacking was using PSP.
  +
//
  +
// Check if hardfault has been caused by software break.
  +
// Otherwise stay in HardFault handler.
  +
//
  +
ldr R1, =0xE000ED2C // NVIC_HFSR
  +
ldr R2, [R1]
  +
lsls R2, R2, 1 // Shift Bit 31 into carry.
  +
_Loop: bcc _Loop // Carry == 0? Hardfault has been caused by other reason. Stay in endless loop.
  +
//
  +
// Reset hardfault status
  +
//
  +
str R2, [R1] // NVIC_HFSR = NVIC_HFSR
  +
//
  +
// Modify return address to return to next instruction after BKPT
  +
//
  +
ldr R1, [R0, #24] // R0 == Current SP
  +
adds R1, #2
  +
str R1, [R0, #24] // *(pStack + 6u) += 2u;
  +
//
  +
// Return
  +
//
  +
bx LR
  +
.end
  +
  +
===Generic Handling===
  +
Supervisor calls and software breakpoints are target-depentent implementations for semihosting.
  +
Some architectures do not implement such functionality. For example the RISC-V instruction set includes a breakpoint instruction, but it does not take an extra parameter which could identify it as a semihosting call.
   
  +
When the developer might need terminal I/O for debugging, a debugger can provide generic handling for semihosting, which does not rely on any target-specific features.
==== RISC-V====
 
At this point, there is no clear standard for RISC-V semihosting.
 
The big problem is that there is no equivalent to a Supervisor call or breakpoint with parameter, and that the behavior of the CPU is not clearly defined
 
when hitting a breakpoint in non-debug mode and is therefore non-recoverable.
 
More to come, please contact SEGGER for up to date information.
 
   
 
== Using Semihosting with J-Link ==
 
== Using Semihosting with J-Link ==

Revision as of 18:07, 19 July 2019

Semihosting is a mechanism that enables code running on an Embedded System (also called the target) to communicate with and use the I/O of the host computer.

Operations

Semihosting allows use of some of the host's resources, such as terminal I/O, File I/O and access to Real time. Not all operations are supported by all implementations, and some are actually a security hazard, therefore potentially not implemented.

Terminal output

In most systems, the important part is Terminal I/O, or rather Terminal out. This is the ability to send text to the host. In probably 90% of the cases, this is what is needed. This functionality is also (not accurately) referred to as printf functionality, since printf() requires an underlying implementation of terminal output. printf() requires a formatter, which at run time creates the string to be output, thus significantly increasing the size of the program (typically by between 3 -20 kb, depending on implementation and supported functionality). Since especially smaller embedded systems can often not afford this increase in Program size, a technology called Host-based formatting is available in some implementations such as the SEGGER Run Time Library.

Semihosting operations defined by ARM

ARM has defined 24 semihosting operations. Not all of these really make sense in an Embedded System. Below is the list of operations as defined by ARM, in alphabetic order, along with the function number assigned:

File operations

  • SYS_CLOSE (0x2) - Closes a file on the host which has been opened by SYS_OPEN.
  • SYS_ERRNO (0x13) - Returns the value of the C library errno variable that is associated with the semihosting implementation.
  • SYS_FLEN (0x0C)

Target time

These functions are not typically needed and implemented.

  • SYS_CLOCK (0x10) - Returns target execution time in centiseconds (10ms). Not normally used.
  • SYS_ELAPSED (0x30) - Returns the number of elapsed target ticks since execution started.

Obsolete

  • SYS_EXIT (0x18)
  • SYS_EXIT_EXTENDED (0x20)

Common semihosting operations

A de-facto standard for semihosting operations has been defined by ARM. This, but now widely adopted, defining the "Common semihosting operations".
Semihosting has been around for a very long time. It was not invented by ARM.

Basic operation

Basically, the target CPU is halted, either by running into a breakpoint instruction or by some other operation which stops program execution and puts the target CPU under control of the debug-agent.
The debug agent can be either the debug probe (e.g. J-Link) or the debugger (such as GDB, SEGGER Ozone or the debugger integrated in Embedded Studio). The debug agent reads one or more register(s) to determine the type of operation to be performed by the host on behalf of the target, then performs the action, then restarts the target. The target is halted for the duration of the semihosting operation.

Ways to halt the target CPU

There are different ways to halt the target CPU, primarily special calls such as "Supervisor calls" or Breakpoints. Ideally, an implementation of semihosting is done in a way that when the system does not run under control of a debugger, it still works. This is really important for systems which use semihosting for things that are optional, not essential, such as debug output (printf) to the host. For example, ARM's Cortex-M implementation has the following problem: It uses a breakpoint instruction, which leads to a hard fault if the CPU is not running in debug mode. The way to handle this is using a special hardfault handler, which checks the source of the problem, and allows "skipping" of the BKPT instruction and resumption of program execution.

Implementation

Supervisor Call

Legacy ARM cores, such as ARM7, ARM9, ARM11, as well as Cortex-A and Cortex-R devices typically use the supervisor call (SVC instruction, previously SWI) for semihosting. The debug agent sets a breakpoint or vector catch on the SVC handler.

On entry of the SVC handler, the debugger checks whether the call should trigger semihosting or not, and if it should, it performs the semihosting operation, does an exception return, and continues execution. If the supervisor call does not trigger semihosting, execution is continued in the SVC handler.

Semihosting via supervisor call is triggered by the SVC number, which is encoded in the SVC instruction.

In ARM Mode the SVC number can be 24 bit wide. The standard SVC number for semihosting is 0x123456:

SVC 0x123456

In Thumb mode the SVC number is only 8 bit wide. The standard SVC number for semihosting is 0xAB:

SVC 0xAB

SVC Handler

To ensure that the target application keeps running even when no debugger is connected, an SVC handler should be implemented, which also checks whether the call should have triggered semihosting or not. In case semihosting should have been triggered, the application might do a simple exception return. When the SVC handler has been called for any other reason, the application might handle this accordingly.

SVC_Handler:
        push {R2,R3}
        //
        // Check whether we come from Thumb or ARM mode.
        // Application's CPSR is saved in SPSR.
        //
        mrs R2, SPSR
        tst R2, #0x20
        beq _CheckSemiARM               // SPSR.T (Bit 5) not set -> ARM mode
_CheckSemiThumb:
        //
        // Load immediate of Thumb SVC instruction
        //
#if BIG_ENDIAN
        ldrb R2, [LR, #-2]
#else
        ldrb R2, [LR, #-1]
#endif
        //
        // Check if it has been a semihosting SVC
        //
        ldrb R3, =0xAB
        cmp R2, R3
        pop {R2,R3}                     // Restore registers (might be used by semihosting or SVC handler)
        bne _DoSVC                      // No semihosting SVC. Continue in SVC handler
        b _DoSemihost                   // Semihosting SVC. Run through debugger breakpoint and return
_CheckSemiARM:
        //
        // Load immediate of ARM SVC instruction
        //
        ldr R2, [LR, #-4]
        bic R2, R2, #0xFF000000
        //
        // Check if it has been a semihosting SVC
        //
        ldr R3, =0x00123456
        cmp R2, R3
        //
        // Restore registers (might be used by semihosting or SVC handler)
        //
        pop {R2,R3}
        bne _DoSVC                      // No semihosting SVC. Continue in SVC handler
        //b _DoSemihost                   // Semihosting SVC. Run through debugger breakpoint and return
_DoSemihost:
        //
        // Perform exception return to instruction after SVC.
        // Also resets CPSR.
        //
        subs PC, LR, 0
_DoSVC:
        //
        // Stay in endless loop
        // User SVC handler could start here instead
        //
        b _DoSVC
        .end

Software Breakpoint

Cortex-M devices can use the software breakpoint instruction (BKPT) instead of the supervisor call.

When a debug probe is connected, the target halts on execution of the BKPT. The debugger checks whether the breakpint should trigger semihosting or not. If it should, it performs the semihosting operation, skips the BKPT instruction, and lets the target run.

Semihosting via software breakpoint is triggered by the parameter which can be encoded with the BKPT instruction.

BKPT is always a 16 bit instruction with 8 bit parameter. The standard parameter for semihosting is 0xAB:

BKPT 0xAB

Semihosting HardFault Handler

The disadvantage of the BKPT instruction is, that when no debugger is connected, a hard fault error occurs. The target needs to recover from the hard fault in order to continue execution.

HardFault_Handler:
        //
        // This version is for Cortex M3, Cortex M4 and Cortex M4F
        // Copy previously selected stack pointer to R0.
        //
        tst    LR, #4           // Check EXC_RETURN in Link register bit 2.
        ite    EQ
        mrseq  R0, MSP          // Stacking was using MSP.
        mrsne  R0, PSP          // Stacking was using PSP.
        //
        // Check if hardfault has been caused by software break.
        // Otherwise stay in HardFault handler.
        //
        ldr    R1, =0xE000ED2C  // NVIC_HFSR
        ldr    R2, [R1]
        lsls   R2, R2, 1        // Shift Bit 31 into carry.
_Loop:  bcc    _Loop            // Carry == 0? Hardfault has been caused by other reason. Stay in endless loop.
        //
        // Reset hardfault status
        //
        str    R2, [R1]         // NVIC_HFSR = NVIC_HFSR
        //
        // Modify return address to return to next instruction after BKPT
        //
        ldr    R1, [R0, #24]    // R0 == Current SP
        adds   R1, #2
        str    R1, [R0, #24]    // *(pStack + 6u) += 2u;
        //
        // Return
        //
        bx     LR
  .end

Generic Handling

Supervisor calls and software breakpoints are target-depentent implementations for semihosting. Some architectures do not implement such functionality. For example the RISC-V instruction set includes a breakpoint instruction, but it does not take an extra parameter which could identify it as a semihosting call.

When the developer might need terminal I/O for debugging, a debugger can provide generic handling for semihosting, which does not rely on any target-specific features.

Using Semihosting with J-Link

Semihosting with Embedded Studio

The implementation of semihosting in Embedded Studio is straight forward. Standard library functions for fopen, fwrite, fread etc. are already implemented and just need to be called. The project wizard is recommended when creating a new project so that all default settings are set correctly.

The following example project opens a file on your host system and writes a string into it triggered by your target device: Semihosting Embedded Studio

Make sure the file path exists, otherwise the project will not run. You can of course change the path by simply editing variable pFilename and rebuild it with Embedded Studio.

Prerequisites for the example project are:

  • Embedded Studio V4.16 or later
  • Cortex-M Trace Reference Board (ST STM32F407VE)
  • J-Link V10 or later

Baremetal Semihosting with Ozone

Ozone is a debugger so it does not come with standard libraries and predefined semihosting code like it does with some IDEs. Thus to make use of semihosting the semihosting calls need to be implemented manually by the user in the target application. The big benefit of this approach without predefined library functions is that you get full control about the complexity of the semihosting implementation so you can write more efficient code as the library overhead is missing.

A baremetal example implementation can be found in the following example project: Semihosting Ozone

The example project will open a file on the host system and write a string to it. Make sure the file path is existing otherwise the project will not run. You can of course change the path by simply editing variable pFilename and rebuild it with Embedded Studio.

Prerequisites for the example project are:

  • Ozone V2.62 or later
  • Embedded Studio V4.16 or later
  • Cortex-M Trace Reference Board (ST STM32F407VE)
  • J-Link V10 or later

This baremetal code can be used with any debug software which supports semihosting handling.

Semihosting with newlib and GNU Arm toolchain

This section will cover how semihosting can be used with Eclipse in combination with GDB and newlib. Generally newlib will only provide a rudimentary setup for semihosting so it will not work out-of-thebox except for printf calls. All callbacks used by e.g. fopen need to be implemented by the user to utilize semihosting.

The following project will show such an example project and create a file at a certain path, fill it with a string and close that file: Semihosting Eclipse

Make sure the file path is existing otherwise the project will not run. You can of course change the path by simply editing variable pFilename and rebuild it with Eclipse. As a base project a default J-Link debug project was created for Cortex-M. Then a debug config with the correct target device was set up. Next make sure that in the debug config semihosting is enabled.

The example project comes also with an Ozone setup that runs out-of-the-box should Eclipse not be the debugger of your choice.

Prerequisites for the example project are:

  • Eclipse Cpp 2019-03
  • Latest GNU ARM Eclipse plugin
  • Cortex-M Trace Reference Board (ST STM32F407VE)
  • J-Link V10 or later
  • (optional) Ozone V2.62 or later

Semihosting with EWARM

The implementation of semihosting in EWARM is generally available but requires some extra settings in your project. Standard library functions for fopen, fwrite, fread etc. are implemented and just need to be called. Under project options semihosting must be enabled as well as the library support must be set to "full" which will drastically increase memory usage of your project.

The project options can be found at the following project settings:

EWARM Library setting.png

Set the library level to "Full" and the library low level implementation to semihosting each.

Note: This will also increase your application size immensely so make sure your target device has enough memory resources left.

The following example project opens a file on your host system and writes a string into it triggered by your target device: Semihosting EWARM

Make sure the file path is existing otherwise the project will not run. You can of course change the path by simply editing variable pFilename and rebuild it with Embedded Studio.

Prerequisites for the example project are:

  • EWARM V8.322 or later
  • Cortex-M Trace Reference Board (ST STM32F407VE)
  • J-Link V10 or later