Skip to content

Latest commit

 

History

History
186 lines (146 loc) · 12.1 KB

TriceVsPrintfSimilaritiesAndDifferences.md

File metadata and controls

186 lines (146 loc) · 12.1 KB

Trice Similarities and differences to printf usage

(Read only you are interested in)

1. Printf-like functions

...have a lot of things to do: Copy format string from FLASH memory into a RAM buffer and parse it for format specifiers. Also parse the variadic parameter list and convert each parameter according to its format specifier into a character sequences, what includes several divisions - costly function calls. Concatenate the parts to a new string and deliver it to the output, what often means copying again. A full featured printf library consumes plenty space and time and many open source projects try to make it better in this or that way. Never ever call a printf-like function in time critical code, like an interrupt - it would crash your target in most cases.

2. Trice IDs

  • Each Trice caries an ID as runtime replacement for the format string.
  • This ID is automatically generated (controllable) and the source code takes it as first parameter inside the TRICE macro after the optional target timestamp and context.
  • The format specifier string is not compiled into the target code. It goes together with the ID into a reference list file til.json

3. Trice values bit width

  • No need to explicit express the value bit width.
  • The default parameter width for the TRICE macro is 32 bit. It is adaptable for 8- or 16-bit MCUs:
    • Adapt settings inside triceConfig.h.
    • Use -defaultTRICEBitwidth switch during logging.
  • The macros TRICE8, TRICE16, TRICE32, TRICE64 are usable too, to define the bit width explicit.
    • This leads for the smaller bit widths to a data packing and less needed space and bandwidth.
  • The fastest TRICE macro execution is, when MCU bit width matches the TRICEmacro bit width.

4. Many value parameters

  • No need to explicit express the values count.
  • Up to 12 values are supported directly. Example:
    • TRICE( "%p | %04x %04x %04x %04x %04x %04x %04x %04x %04x | %f\n", p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], aFloat(x));
    • To support more than 12 values for each TRICE macro, the Trice code on target and host is straightforward extendable up to a total payload of 1008 bytes.
  • Each macro can be prolonged with the used parameter count, for example TRICE8_3 or TRICE_2 to improve compile time checks.
    • This length code extension can be done automatically using trice u -addParamCount.
  • There is no variadic values scanning during runtime. The C preprocessor does the work.

5. float and double values

These types are mixable with integer types but need to be covered by converter function.

  • float types use the aFloat() function and need a minimal value bit width of 32, to secure correct data transfer.

    • Example:
     float x = 7.2;
     TRICE32( "%f", aFloat(x));
  • double types use the aDouble() function and need need a value bit width of 64, to secure correct data transfer.

    • Example:
     double y = 7.2;
     TRICE64( "float %f and double %f", aFloat(x), aDouble(y));
  • Both functions are simple and fast:

// aFloat returns passed float value x as bit pattern in a uint32_t type.
static inline uint32_t aFloat( float x ){
    union {
        float f;
        uint32_t u;
    } t;
    t.f = x;
    return t.u;
}

// aDouble returns passed double value x as bit pattern in a uint64_t type.
static inline uint64_t aDouble( double x ){
    union {
        double d;
        uint64_t u;
    } t;
    t.d = x;
    return t.u;
}

6. Runtime generated strings transfer

  • The %s format specifier is not directly supported by the TRICE macro.

  • Strings, known at compile time should be a part of a format string to reduce runtime overhead.

  • Strings created at runtime, need a special TRICE_S macro, which accepts exactly one type %s format specifier. Generated strings are allowed to a size of 1000 bytes each, if the configured Trice buffer size matches.

    • Example:
     char s[] = "Hello again!";
     TRICE_S( "A runtime string %20s\n", s;

7. Extended format specifier possibilities

  • Because the format string is interpreted by the trice tool written in Go, the Go capabilities partial usable.

7.1. Trice format specifier

  • The TRICE macros are used in C code.
  • The format strings are interpreted by the trice tool, which is written in Go.
  • The C and Go format specifier are not equal but similar.
  • Therefore a Trice adaption is internally performed.

7.2. Overview Table

Format Specifier Type C Go T remark
signed decimal integer d d d Supported.
unsigned decimal integer u - u The trice tool changes %u into %d and treats value as unsigned.
signed decimal integer i d i The trice tool changes %i into %d and treats value as signed.
signed octal integer - o o With trice log -unsigned=false value is treated as signed.
unsigned octal integer o - o With trice log value is treated as unsigned.
signed octal integer with 0o prefix - O O With trice log -unsigned=false value is treated as signed.
unsigned octal integer with 0o prefix - - O With trice log value is treated as unsigned.
signed hexadecimal integer lowercase - x x With trice log -unsigned=false value is treated as signed.
unsigned hexadecimal integer lowercase x - x With trice log value is treated as unsigned.
signed hexadecimal integer uppercase - X X With trice log -unsigned=false value is treated as signed.
unsigned hexadecimal integer uppercase X - X With trice log value is treated as unsigned.
signed binary integer - b b With trice log -unsigned=false value is treated as signed.
unsigned binary integer - - b With trice log value is treated as unsigned.
decimal floating point, lowercase f f f aFloat(value)|aDouble(value)
decimal floating point, uppercase - F F aFloat(value)|aDouble(value)
scientific notation (mantissa/exponent), lowercase e e e aFloat(value)|aDouble(value)
scientific notation (mantissa/exponent), uppercase E E E aFloat(value)|aDouble(value)
the shortest representation of %e or %f g g g aFloat(value)|aDouble(value)
the shortest representation of %E or %F G G G aFloat(value)|aDouble(value)
a character as byte c - c Value can contain ASCII character.
a character represented by the corresponding Unicode code point c c c Value can contain UTF-8 characters if the C-File is edited in UTF-8 format.
a quoted character - q q Supported.
Unicode escape sequence - U - Not supported (yet) by Trice.
the word true or false - t t Supported.
a string s s s Use TRICE_S macro with one and only one runtime generated string.
value in default format - v - Not supported.
Go-syntax representation of the value - #v - Not supported.
a Go-syntax representation of the type of the value - T - Not supported.
pointer address p p p Supported.
a double %% prints a single % % % % Supported.
nothing printed n - - Not supported.
  • Long story short: Use the -unsigned=false switch when you like to see hex numbers and the like as signed values.
  • Look in triceCheck.c for exampe code producing this:

./ref/TriceCheckOutput.gif

8. UTF-8 Support

This is gratis, if you edit your source files containing the format strings in UTF-8:

./ref/UTF-8Example.PNG

The target does not even "know" about that, because it gets only the Trice IDs.

9. Switch the language without changing a bit inside the target code

Once the til.json list is done the user can translate it in any language and exchanging the list switches to an other language.

10. Format tags prototype %[flags][width][.precision][length] specifier examples

  • Because the interpretation is done inside the trice tool written in Go these all should work:
    • %-d
    • %064b
    • %+9.3f
    • %+#012.12g
    • %+'#012.12E
    • %e
    • %9.f
  • Anyway tests are needed (Issue #211).