Host-based formatting

From SEGGER Knowledge Base
Jump to navigation Jump to search

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 a printf 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.