Host-based formatting

From SEGGER Wiki
Revision as of 22:59, 3 July 2019 by Paul (talk | contribs) (Worked example)
Jump to: navigation, search

Host-based formatting is a technology that reduces the on-target code and data size associated with typical semihosting.

Classic semihosting

Many embedded system development tools offer support for Semihosting where a host computer system provides I/O and other services to an embedded target. Semihosting is typically used during product development and is not included as part of the embedded system's code when the product is released.

Typical semihosting provides very simple I/O functions such as reading and writing characters to the terminal and to files. Such an implementation requires target code on the embedded system that implements the C-based format specifications used in calls to printf and scanf. This code is typically bulky even after eliminating floating-point I/O: 2k to 4k of code can be brought into the application just to support printf which will not be present in the production code.

Host-based formatting

For MCUs with very small amounts of flash and RAM, debug messages delivered through semihosting can be an unacceptable burden, consuming most of the flash and RAM space of the target thereby leaving very little space for the application. For instance, MSP430 processors can have as little as 256 bytes of RAM and 1 KB of flash. Usually a PCB would be populated with a larger device for product development to enable larger debug builds to be programmed into the flash. However, some device families do not offer larg memory versions that could be used for development and, therefore, reducing the footprint of debug code is essential for the application to fit.

Advantages

Host-based formatting shifts the burden of formatting from the target system, where resources are limited, to the host system. The host system supports all formatting specification (such as width, precision, alignment, floating point, and so on) simply becuase it has ample resources to do so. The target footprint for printf, for instance, is no more than a few instructions to issue a semihosting call and have the debug agent perform the semihosting operation.

Implementation details

The following is the implementation of printf using the SEGGER Runtime Library using 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 memory through the debug probe it 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

The call...

printf("There are %u bottles of beer on the %s", 99, "wall");

...would be processed, in detail, as follows:

  • Target calls printf with format string address in a register and additional arguments typically passed on processor stack.
  • Target execution enters printf and immediately requests a semihosting call (e.g. using breakpoint).
  • Host recognizes semihosting requests and proceeds to service it 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 target memory into host memory, up to and including the terminating null character.
  • Host starts formatting the string, 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 beer on the " to the console.
  • Host recognizes %s format specifier and reads an pointer value from the processor stack under debug control.
  • Host reads string using that pointer as a base and copies "wall" to the console.
  • Host finds end of control string and writes an "OK" result to e.g. register R0.
  • Host restarts program execution on the target.
  • Target continues execution.

The semihosting call is now complete.