Host-based formatting
Host-based formatting is a technology that reduces the on-target code and data volumes associated with typical applications where semihosting is involved.
Classic semihosting
Many embedded-system development tools offer support for semihosting in which a host computer system provides I/O and other services to an embedded target. Semihosting is typically used during product development, and it is not included as part of an embedded system's code when the respective product is released.
Typically, semihosting provides very simple I/O functions, such as the reading and writing of characters to the terminal and to files. In an embedded system, such an implementation requires target code that implements C-based format specifications used in calls to printf
and scanf
. This code is typically bulky, even after elimination of the floating-point I/O: 2k to 4k of code that will not be found in the production code can be brought into the application just to support printf
.
Host-based formatting
For MCUs with very small amounts of flash and RAM, debug messages delivered via semihosting can be an unacceptable burden, as they consume most of the the target's flash and RAM memory space, thereby leaving very little space for the application. For instance, MSP430 processors can have as little as 256 bytes of RAM and 1 KiB of flash memory. Usually, a PCB would be populated with a larger device for product development in order to enable larger debug builds to be programmed into the flash memory. However, some device families do not offer larger memories that could be used for development; therefore, reducing the debug code footprints is essential for ensuring that the application fits.
Advantages
Host-based formatting shifts the burden of formatting from the target system (where resources are limited) to the host system, where resources are plentiful. The host system supports all formatting specifications (such as width, precision, text alignment, floating point, and so on), simply because it has ample resources to do so. In this scenario, the target footprint for printf
, for instance, consists of no more than a few instructions to issue a semihosting call and have a debug agent perform the semihosting operation.
Implementation details
The following is the implementation of printf
using the SEGGER Runtime Library with host-based formatting for semihosting:
int printf(const char *fmt, ...) {
return __do_debug_operation(DEBOP_PRINTF, &fmt);
}
The debugger or host agent identifies the semihosting request from the target (see the Semihosting page for details) and proceeds to service the request. It will first identify the parameters of the semihosting call: the debug operation and the incoming C arguments. With access to all of the memory via the debug probe, the debugger/agent can find the address of the format string, read the format string from memory under debug control, and format the output "virtually" on the host—taking arguments from the target's memory or registers (as needed) under debug control.
Worked example
With host-based formatting, the call...
printf("There are %u bottles of %s on the wall", 99, "beer");
...would be processed, in detail, as follows:
- Target calls
printf
with a format-string address in a register and additional arguments that are typically passed on a processor stack. - Target execution enters
printf
and immediately requests a semihosting call (e.g., using a breakpoint). - Host recognizes semihosting requests and proceeds to service them with the target halted.
- Host decodes the semihosting operation, e.g., register
R0
, and identifies it as aprintf
request. - Host reads, e.g., register
R1
, to find the address of the format string. - Host reads the format string from the target memory into the host memory up to and including the terminating null character.
- Host starts formatting the string and sending
There are
to the console. - Host recognizes
%u
format specifier and reads a word (corresponding to the argument) from the processor stack under debug control (in this case 99). - Host formats the argument, 99, and writes
99
to the console. - Host continues and copies
bottles of
to the console. - Host recognizes
%s
format specifier and reads a pointer value from the processor stack under debug control. - Host reads zero-terminated string using that pointer as the base address and copies it
beer
to the console. - Host continues and copies
on the wall
to the console. - Host finds the end of the control string and writes a "success" result to, e.g., register
R0
. - Host restarts program execution on the target.
- Target continues execution.
The semihosting call is now complete.