Skip to content

Latest commit

Β 

History

History
2140 lines (1576 loc) Β· 143 KB

TriceUserGuide.md

File metadata and controls

2140 lines (1576 loc) Β· 143 KB

Trice user guide

_(Read this)

SORRY: This document is long and not 100% consistent. Hints or pull requests are welcome.

Table of Contents

1. Project structure

See FilesAndFolders.md

(back to top)

2. Get started

2.1. Get it

  • Download latest release assets for your system: Compressed source code and binaries.
  • OR Get the repo: x
  • OR use the ./ref/Fork.PNG button

2.2. Install It

  • Place the extracted trice binary somewhere in your PATH.
  • Copy the src folder into your project and add all files.
  • Copy a triceConfig.h from a subfolder in the examples or test folder and optionally adapt it. See file triceDefaultConfig.h for help.
    • Inside the triceCnfig.h file cou can control, if Trice works in direct or deferred mode or both parallel.

2.3. Try it

  • Create a file tryTrice.c and write in it:
#include "trice.h"

int tryIt( void ){
    trice( "Hello! πŸ‘‹πŸ™‚\a\n" ); // A message with sound and without target timestamp.
}

You can also edit any of your existing project files accordingly. Just replace any printf with trice. (Handle float or double numbers and runtime-generated stings, according to TriceVsPrintfSimilaritiesAndDifferences.md. The file _test/testdata/triceCheck.c shows many usage examples. The uppercase TRICE macros are inlining the complete Trice code and the lowercase trice macros are function calls, so most probably you want use trice to keep the overall code size smaller.

  • Create 2 empty files til.json and li.json in your project root.
  • Run trice insert and the trice code line changes to trice( iD(1234), "Hello! πŸ‘‹πŸ™‚\a\n" );.
  • The 2 JSON files are now filled with information.
  • Run trice clean and the trice code line changes back to trice( "Hello! πŸ‘‹πŸ™‚\a\n" );.

You can use trice insert as pre- and trice clean as post-compile step, to not spoil your source code with IDs.

The optional Trice cache technique avoids un-edited file changes at all, what means no Trice releated build speed disadvantages.

See TriceCacheSpec.md for more details and examples/G1B1_inst/build.sh as example.

  • Or, use trice insert in a post-checkout and trice clean in a pre-check-in script to keep just the repository clean of Trice IDs. Using only trice insert as pre-compile step is possible too, especially when the code is used just in a single project and you wish to have it as compiled.
  • When using Trice in libraries for several projects, it may make sense to check-in the libraries with IDs and to use a dedicated ID space for them. See ../_test/testdata/triceCheck.c as an example - especially when building several projects parallel like shown in the examples folder.

A quick setup is possible when using RTT as output channel. Otherwise you need to setup a serial port for Trice data transmission. Other output paths possible too using the auxilary interface.

2.4. Use It

  • In a console, like git bash, type trice help -all. You should see the complete trice tool CLI documentation.
    • DonΒ΄t worry, most of it you will never need.
    • There are only 2 important commands: trice insert and trice log. Call them with the right CLI switches.
      • trice help -insert and trice help -log show partial help.

      • Examples:

        CLI command Description
        touch ./til.json Create an empty til.json file. This is needed only the very first time.
        trice i -src . -src ../myLib Insert IDs to the current and your ../myLib folder. This will read|extend|modify ./til.json and use & create the ./li.json file.
        ... Compile your project
        trice c -src . -src ../myLib Optionally restore the current and your ../myLib folder. This will read|extend|modify ./til.json and use & create the ./li.json file.
        trice l -p com1 -baud 921600 -lf my/path/auto Start Logging over UART and create automatically a new log file in my/path/.
        cat filename.log View a recorded log file.
        trice l -p JLINK -args "..." Start Logging over RTT. Binary log files are collected in ./temp.
        trice l -p FILEBUFFER -args logfile.bin Play a recorded binary log file.
      • It is recommended to add trice insert ... as pre-compile step into the tool chain.

      • Hint: It is possible to add trice clean ... as a post-compile step, so that you can check in your project sources without IDs. That is supported in v0.61.0 and later. This allows to use library sources with trices in different projects and the source code is not spoiled with IDs.

  • The command trice does not make any assumptions about the target processor - 8-bit to 64-bit, supports little and big endianness.
  • The command trice is compiler agnostic - it should work with any compiler.
  • The vsCode editor is free downloadable and free usable, like shown in the examples/F030R8_inst project.
  • The examples and test subfolders contains several vsCode Makefile projects and they are also usable as starting points for your configuration.
  • You can use Trice calls also inside header files but when running trice insert as pre- and trice clean as post-compile step, all files including these headers will be re-compiled every time, what may be too time consuming. Enable the Trice cache then. See TriceCacheSpec.md for more information.

(back to top)

2.5. Port it

Trice should be usable on any MCU with any compiler. On ARM MCUs the easiest way is to use SEGGER J-Link with RTT as output. Setting up UART transmission as alternative or additionally is also no big deal.

Compare folders of one of these folder groups:

Without Instrumentation With Trice Instrumentation
./examples/F030R8_gen ./examples/F030R8_inst
./examples/G0B1_gen ./examples/G0B1_inst
./examples/L432KC_gen_ad_toClang_ed ./examples/L432KC_gen_ad_toClang_ed_instr

This way you see in a quick way any needed adaptions for your target project to port trice to it.

The Readme.md files in the examples folder contain further helpful information.

2.5.1. Target Macros

The easiest and mostly sufficient way to use Trice on the target side is the Trice macro trice which you can mostly use as a printf replacement in legacy code. See TriceVsPrintfSimilaritiesAndDifferences.md for more details. Is uses the TRICE_DEFAULT_PARAMETER_BIT_WIDTH value (usually 32), which is equal for all values. If you wish target stamps use Trice for 16-bit ones or TRice for 32-bit ones.

The macros

  • trice8, trice16, trice32, trice64
  • Trice8, Trice16, Trice32, Trice64
  • TRice8, TRice16, TRice32, TRice64

are always usable and the number 8, 16, 32, 64 specifies the parameter width, which is equal for all values within one macro. They are partially disabled, when the value TRICE_SINGLE_MAX_SIZE is defined to be smaller than 104. For example with TRICE_SINGLE_MAX_SIZE == 8, TRice32 can have no parameter value (4 byte Trice header, 4 byte stamp) and trice8 can have up to 4 parameter values (4 byte Trice header, 4 byte values) That's mainly to get compiler errors rather than runtime errors.

More examples:

Trice Header Stamp max. Values Trice Size
trice8 4 0 0 *1 byte 4
... ... ... ... ...
trice8 4 0 12 *1 byte 16
Trice8 4 2 0 *1 byte 6
... ... ... ... ...
Trice8 4 2 12 *1 byte 18
TRice8 4 4 0 *1 byte 8
... ... ... ... ...
TRice8 4 4 12 *1 byte 20
trice16 4 0 2 *2 byte 8
Trice16 4 2 1 *2 byte 8
trice32 4 0 1 *4 byte 8
Trice32 4 2 1 *4 byte 10
TRice32 4 4 2 *4 byte 16
trice64 4 0 1 *8 byte 12
TRice64 4 4 1 *8 byte 16
... ... ... ... ...
TRice64 4 4 12 *8 byte 104

The value TRICE_DEFAULT_PARAMETER_BIT_WIDTH is the parameter bit with for the macros trice, Trice, TRice (without number). It can make sense to set this value to 16 on smaller machines.

The full uppercase macro TRICE is a Trice macro only using inline code. Because the main design aim was speed, this was the original design. Then it became clear, that several hundred of TRICE macros increase the needed code amount too much and that it is better to have just a function call instead of having inline macros. If speed matters use TRICE(id(0), TRICE(Id(0), TRICE(ID(0) else use trice(iD(0), Trice(iD(0), TRice(iD(0) or mix usage as you like. The lower case macros internally use TRICE like code but each is only a function call and therefore needs less space.

2.5.2. Target Trice Stamps

  • If you wish to have your Trice messages stamped, most probably time stamped, add the 2 hardware specific macros/functions to your project (example in ./examples/F030R8_inst/Core/Inc/triceConfig.h and ./examples/F030R8_inst/Core/Src/stm32f0xx_it.c ). The time base is in your hands and is allowed to be different for the 16-bit and 32-bit stamps. Example:

    //! ms32 is a 32-bit millisecond counter, counting circular in steps of 1 every ms.
    extern uint32_t ms32;
    #define TriceStamp16 (SysTick->VAL) // Counts from 31999 -> 0 in each ms.
    #define TriceStamp32  ms32
  • In the code snippet above the 32-bit timestamp is used for milliseconds and the 16.bit timestamp is used as clock counter what allows fine grained time measurements.

  • In the screenshot below, the 16-bit timestamp is a parallel counter running between 0-9999 milliseconds, which allows to have 16-bit timestamps all the time and only every 10 seconds is a full 32-bit timestamp needed.

    x

  • The trice tool -ts* CLI switches allow customization. With -hs off host time stamps are suppressed.

  • It is also possible to use the stamp option not for time stamps but for any values, like addresses or a voltage or a random number.

Hint: I usually have the 32-bit timestamp as millisecond counter and the 16-bit timestamp as systick counter to measure short execution times.

2.5.3. Trice Checks

  • Optionally copy parts of ./_test/testdata/triceCheck.c to your project if you wish to perform some checks.
    • Do not inlucde this file directly, because it could get changed when updateTestData.sh is executed inside the ./test folder.
    • The only-uppercase TRICE* macros include trice code sequences what can led to a significant code amount if you use plenty of them, whereas the lowercase macros trice, Trice, TRice and their relatives are just function calls and better suited to be used normally.
  • In your source files add line #include "trice.h" at the top.
  • In a function write a trice message like: TRice( "1/11 = %g\n", aFloat( 1.0/11 ) );.
  • In project root:
    • Create empty file: touch til.json.
    • trice insert should perform automatically the following things (The numbers are just examples.):
      • Patch source.c to TRice( iD(12363), "1/11 = %g\n", aFloat( 1.0/11 ) );
        • C & H files containing trice macros, are only modified if needed (missing IDs or changed format strings).
      • Extend til.json
        • If no til.json is found nothing happens. At least an empty file is needed (Safety feature).
  • When the program runs later, it should output something similar to ./ref/1div11.PNG
  • Look into ./TriceVsPrintfSimilaritiesAndDifferences.md for options.
  • Read ./TriceConfigProjectImageSizeOptimization.md if needed.

2.5.4. Communication Ports

  • For RTT the SEGGER source is already included. See ./TriceOverRTT.md for more info.
    • If RTT is used, no hardware specific adaptions needed and it is the fastest possible data transfer. But you cannot use it in the field usually.
    • The direct trice mode is recommended for RTT. The single trice execution is a bit longer then, but the log is completely done in one shot. It takes about 100-150 processor clocks, aka 1-2 microseconds.
      • Info: All deferred trice modes are faster in the runtime execution but the Trice logs appear slightly delayed. You can finetune the Trices down to only 3 Assembler instructions executable within 6 processor clocks. See ./TriceSpeed.md as example.
  • For UART transfer add UART write functionality. The deferred mode is recommended for UART transfer.
  • It is possible to log over several channels parallel and to select an ID range for each channel.
  • An additional device, like local file, GPIO pin or SPI, is possible by providing an appropriate write functionality.
  • See also ./TriceOverOneWire.md.

2.5.5. Target Code Overview

  • ./src: User Interface
File description
trice.h & trice.c trice runtime lib user interface, #include trice.h in project files, where to use TRICE macros. Add trice.c to your embedded device project. Add ./src to your compiler include path.
triceDefaultConfig.h This file contains the most probably settings and serves also as a reference for tuning your project triceConfig.h
  • ./src: Internal Components (only partially needed according to configuration)
File description
cobs.h message packaging, alternatively for tcobs
cobsEncode.c message encoding, alternatively for tcobs
cobsDecode.c message decoding, normally not needed
trice.h trice lib interface
trice.c trice core lib
trice8McuOrder.c trice MCU endianness lib
trice8McuReverse.c trice MCU reverse endianness lib
trice16McuOrder.c trice MCU endianness lib
trice16McuReverse.c trice MCU reverse endianness lib
trice32McuOrder.c trice MCU endianness lib
trice32McuReverse.c trice MCU reverse endianness lib
trice64McuOrder.c trice MCU endianness lib
trice64McuReverse.c trice MCU reverse endianness lib
SEGGER_RTT.h Segger RTT code interface
SEGGER_RTT.c Segger RTT code
tcobs.h message compression and packaging interface
tcobsv1Encode.c message encoding and packaging
tcobsv1Decode.c message decoding and packaging, normally not needed
tcobsv1Internal.h message decoding and packaging internal interface
trice8.h 8-bit trice code interface
trice8.c 8-bit trice code
trice16.h 16-bit trice code interface
trice16.c 16-bit trice code
trice32.h 32-bit trice code interface
trice32.c 32-bit trice code
trice64.h 64-bit trice code interface
trice64.c 64-bit trice code
triceAuxiliary.c trice code for auxiliary interfaces
triceDoubleBuffer.c trice runtime lib extension needed for fastest deferred mode
triceStackBuffer.c trice runtime lib extension needed for direct mode
triceRingBuffer.c trice runtime lib extension needed for recommended deferred mode
xtea.c XTEA message encryption/decryption interface
xtea.c XTEA message encryption/decryption code

(back to top)

2.5.6. User Code Adaption

  • Replace all strings puts with the string trice, when the string follows immediately. For runtime generated strings see triceS.

  • Replace all strings printf with the string trice, when the format string follows immediately.

  • Check for float and double format specifiers in the format strings. The appropriate parameters need to be covered with aFloat() or a double(). Example:

    printf( "%d, %3.2f EUR, %g rate\n", i, price, change );
    trice64( "%d, %3.2f EUR, %g rate\n", i, aFloat(price), aDouble(change) ); 
  • Check for string format specifiers in the format strings. Put each in a separate trice message. Example:

    printf( "name: %16s, surname: %32s, birthday: %4u-%02u-%02u\n", n, s, y, m, d);
    trice( "name: %16s, ", n); trice( "surname: %32s, ", s ); trice( "birthday: %4u-%02u-%02u\n" y, m, d);

The Trice macros are designed for maximal execution speed and therefore we have to pay the price for their limited capabilities.

  • Optionally add tags to get color. Example:

    puts( "A message");
    trice( "msg:A message");
  • Add #include trice.h to all user files using trice.

2.5.7. Limitations

  • The maximum parameter count per trice is 12, but buffer transfer alows up to 32764 bytes payload. See triceB and its relatives.

  • Each trice must fit into a single line in trice versions before v0.61.0.

    • Not ok before v0.61.0 but ok for later versions:

      trice( "hello %u\n", 
              year);
  • But several trices can be in one line.

    • Ok:

      trice( "hello %u\n", year); trice( "good time");
  • Strings directly as parameter are possible now.

    • Ok from v0.61.0 with trice insert and trice clean:

      triceS( "hello %s\n", "world" );
    • Ok always:

      s = "world"; TRICE_S( "hello %s\n", s );
      #define WORLD "world"
      triceS( "hello %s\n", WORLD );

You should be aware that these parameter strings go into the target and slow down the execution. So, whenever a string is known at compile time it shuld be part of the Trice format string.

The Trice source code parser has very limited capabilities, so it cannot handle C-preprocessor string conatenation.

  • Excluded trices are seen by the trice insert process.

    • Example: The following code will be patched and get an ID as well:

      // trice( "Hi!" );
  • All parameters inside one trice have the same bit width. If for example there are a single double and 10 bytes values, the needed trice macro is trice64 providing 8 bytes space for all parameter values, therefore increasing the transmit overhead. With the default TCOBS framing the overhead is marginal because of the compression. Also this can be handled by splitting into 2 trices:

    // 92 bytes: 4 bytes header plus 11 times 8 bytes
    trice64( "%g: %c%c%c%c%c%c%c%c%c%c", aDouble(3.14159), 61, 62, 63, 64, 65, 66, 67, 68, 69, 10 );
    
    // 24 bytes: 4 bytes header plus 1 times 8 bytes plus 4 bytes header plus 8 times 1 byte
    trice64( "%g: ", aDouble(3.14159)); trice8( "%c%c%c%c%c%c%c%c%c%c", 61, 62, 63, 64, 65, 66, 67, 68, 69, 10 );
  • See also 2.6. Avoid it.

2.5.8. Trice (Time) Stamps

  • Trice messages can have no or 16-bit or 32-bit (time) stamps.
    • recommended (function calling) syntax:

      trice( "hello %u\n", year); // no (time) stamp
      Trice( "hello %u\n", year); // 16-bit (time) stamp
      TRice( "hello %u\n", year); // 32-bit (time) stamp
    • legacy (inlining) syntax:

      TRICE( id(0), "hello %u\n", year); // no (time) stamp
      TRICE( Id(0), "hello %u\n", year); // 16-bit (time) stamp
      TRICE( ID(0), "hello %u\n", year); // 32-bit (time) stamp

2.5.9. Trice Parameter Bit Widths

  • The macros trice, Trice, TRice and TRICE use 32-bit parameter values per default. See TRICE_DEFAULT_PARAMETER_BIT_WIDTH inside src/triceDefaultConfig.h to change that.

  • If for example the bit width of all trice parameters is 8-bit, it is writable as trice8 macro, reducing the transmitted byte count per parameter from 4 to 1:

    char b[8] = {1,2,3,4,5,6,7,8};
    
    // 36 bytes: 4 bytes plus 32 (8 times 4) bytes payload 
    trice( "%02x %02x %02x %02x %02x %02x %02x %02x\n", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);`
    
    // 12 bytes: 4 bytes plus 8 (8 times 1) bytes payload 
    trice8( " %02x %02x %02x %02x %02x %02x %02x %02x\n", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);`
    
    // 12 bytes: 4 bytes plus 8 (8 times 1) bytes payload in short notation.
    triceB( "deb: %02x\n", &b, sizeof(b) );

Hint: With the defaut TCOBS framing 8-bit values as 32-bit parameters typically occupy only 2-bytes during transmission.

2.6. Avoid it

2.6.1. Parser Limitation

Because the implemented source code parser for trice insert and trice clean is only a simple one, there is one important limitation:

  • Do not use an unescaped singe double quote in source code comments. Example:
trice( "hi 0" );
// An "allowed" example comment.
trice( "hi 1");
// An \" allowed example comment.
trice( "hi 2");
// A " NOT allowed example comment. This disrupts the parsing.
trice( "hi 3");
// A " NOT allowed example comment. This enables the parsing after a disruption.
trice( "hi 4");

2.6.2. Trice macros in header files

  • There is nothing wrong, when putting trice macros into header files.
  • But: When you use trice insert as pre-build command and trice clean as post build command, those header files get touched on each build and therefore all source code files including them will be re-translated every time.
  • For efficiency avoid that.
  • With inventing the Trice cache this is of no relevance.

2.6.3. Trice macros inside other macros

  • There is nothing wrong, when putting Trice macros into other macros.
  • But: When running the self made macro, the location information of the inner trice macro will point to the self made macro definition and not to its execution location.

(back to top)

3. Build trice tool from Go sources (you can skip that)

  • Install Go.
  • On Windows you need to install TDM-GCC if you wish to execute the CGO tests as well.
    • Take the 64-bit variant when Go is 64-bit or take the 32-bit variant when Go is 32-bit. If mixed installations work I doubt.
    • Recommendation: Minimal online installer.
    • GCC is only needed to test the target C-code on the host.
    • Make sure TDM-GCC is found first in the path, if you have several compilers installed.
    • Other gcc variants could work also but not tested.
  • Open a console inside the trice directory, recommended is the git-bash, when using Windows.
  • Check and install:
ms@DESKTOP-7POEGPB MINGW64 /c/repos/trice (master)
$ go clean -cache

ms@DESKTOP-7POEGPB MINGW64 /c/repos/trice (master)
$ go vet ./...

ms@DESKTOP-7POEGPB MINGW64 /c/repos/trice (master)
$ go test ./...
?       github.com/rokath/trice/cmd/cui [no test files]
ok      github.com/rokath/trice/cmd/stim        0.227s
ok      github.com/rokath/trice/cmd/trice       0.577s
ok      github.com/rokath/trice/internal/args   0.232s
ok      github.com/rokath/trice/internal/charDecoder    0.407s
ok      github.com/rokath/trice/internal/com    1.148s
ok      github.com/rokath/trice/internal/decoder        0.412s [no tests to run]
?       github.com/rokath/trice/internal/do     [no test files]
ok      github.com/rokath/trice/internal/dumpDecoder    0.388s
ok      github.com/rokath/trice/internal/emitter        0.431s
ok      github.com/rokath/trice/internal/id     0.421s
ok      github.com/rokath/trice/internal/keybcmd        0.431s
ok      github.com/rokath/trice/internal/link   0.404s
ok      github.com/rokath/trice/internal/receiver       0.409s
ok      github.com/rokath/trice/internal/tleDecoder     0.398s
?       github.com/rokath/trice/internal/translator     [no test files]
ok      github.com/rokath/trice/internal/trexDecoder    0.391s
ok      github.com/rokath/trice/pkg/cipher      0.377s
ok      github.com/rokath/trice/pkg/endian      0.302s
ok      github.com/rokath/trice/pkg/msg 0.299s
ok      github.com/rokath/trice/pkg/tst 0.406s

To execute the target code tests, you can run test.sh or cd into _test and run go test ./... from there. ATTENTION: These tests run a significant long time (many minutes depending on your machine), because the Go - C border is crossed very often. The last tests can last quite a while, depending on your machine.

ms@DESKTOP-7POEGPB MINGW64 /c/repos/trice (master)
$ go install ./cmd/trice/

Afterwards you should find an executable trice inside $GOPATH/bin/ and you can modify its source code.

(back to top)

4. Embedded system code configuration

Check comments inside triceDefaultConfig.h and adapt your project configuration like shown in triceConfig.h as example.

(back to top)


5. trice tool in logging action

With trice log -port COM12 you can visualize the trices on the PC, if for example COM12 is receiving the data from the embedded device at the 115200 default baudrate.

The following capture output comes from an (old) example project inside ../examples.

life.gif

See ../_test/testdata/triceCheck.c for reference. The Trices can come mixed from inside interrupts (light blue ISR:...) or from normal code. For usage with a RTOS, Trices are protected against breaks (TRICE_ENTER_CRITICAL_SECTION, TRICE_LEAVE_CRITICAL_SECTION). Regard the differences in the read SysTick values inside the GIF above These differences are the MCU clocks needed for one trice (~0,25Β΅s@48MHz).

Use the -color off switch for piping output in a file. More convenient is the -lf auto switch.

(back to top)

6. Encryption

  • You can deliver your device with encrypted trices. This way only the service [wo]men is able to read the Trices.
  • Implemented is XTEA but this is exchangeable.
  • The to 8 byte padded blocks can get encrypted by enabling #define ENCRYPT... inside triceConfig.h. You need to add -password MySecret as trice log switch and you're done.
  • Any password is usable instead of MySecret. Simply add once the -show switch and copy the displayed passphrase into the triceConfig.h file.
  • The encryption takes part before the COBS encoding.
  • TCOBS is usable but not recommended after encryption, because it cannot compress effective arbitrary data.

(back to top)

7. CLI Options for trice tool

The trice tool is very easy to use even it has a plenty of options. Most of them normally not needed. The trice tool can be started in several modes (sub-commands), each with several mandatory or optional switches. Switches can have parameters or not.

trice sub-command -switch1 -switch2 parameter -switch3 ...

Which sub-command switches are usable for each sub-command is shown with trice help -all. This gives also information about their default values.

Info for a special sub-command is shown with trice h -l, trice h -z, ... .

(back to top)

8. Trice command line examples

  • The trice tool has many command line options, but is easy to use with default values.
  • No config file implemented yet. But the command history is usable for example inside the bash, simply enter CTRL-R and start typing trice... and you can select from the history.

8.1. Common information

  • trice h -all shows all options of the current version.
  • trice ver prints version information.
  • trice s shows you all found serial ports for your convenience.
  • trice l -p COM17 could fail if something is wrong. Additional switches are for help tracking the issue:
    • Use log witch -s[howInputBytes] to check if any bytes are received at all. ./ref/ShowInputBytesExample.PNG
    • With -debug you can see the [T]COBS packages and decoded Trice packages. ./ref/DebugSwitchExample.PNG

8.2. Further examples

8.2.1. Automated pre-build insert command example

  • Scan directories ../src, ../lib/src and ./ to insert the IDs there and extend list file ../../../til.json
trice i -v -i ../../../til.json -src ../src -src ../lib/src -src ./

This is a typical line you can add to your project as an automatic pre-compile step.

8.2.2. Some Log examples

  • Log trice messages on COM3 8N1 115200 baud
trice log -i ./myProject/til.json -p=COM3
  • Log trice messages on COM3 8N1 9600 baud and use default til.json
trice l -s COM3 -baud=9600

8.2.3. Logging over a display server

  • Start displayserver on ip 127.0.0.1 (localhost) and port 61497
trice ds
  • Log trice messages on COM3 and display on display server
trice l -ds -p COM3
  • Shutdown remote display server on IP 192.168.1.23 port 45678
trice sd -r 192.168.1.23:45678

8.2.4. Logfile output

trice l -p COM3 -logfile auto

This creates a new logfile 2022-05-16_2216-40_trice.log with the actual timestamp on each trice start.

trice l -p COM3 -logfile trice.log

This creates a new logfile trice.log on first start and appends to it on each next trice start.

Logfiles are text files one can see with 3rd party tools. Example: cat trice.log. They contain also the PC reception timestamps if where enabled.

8.2.5. Binary Logfile

trice l -p COM3 -binaryLogfile auto

This creates a new binary logfile 2022-05-16_2216-40_trice.bin with the actual timestamp on each trice start.

trice l -p COM3 -binaryLogfile trice.bin

This creates a new binary logfile trice.bin on first start and appends to it on each next trice start.

Binary logfiles store the trice messages as they come out of the target in binary form. They are much smaller than normal logfiles, but the trice tool with the til.json is needed for displaying them and the PC timestamps are the displaying time: trice l -p FILEBUFFER -args trice.log.

Binary logfiles are handy in the field for long data recordings.

When using RTT, the data are exchanged over a file interface. These binary logfiles are stored in the project [./temp] folder and accessable for later view: trice l -p FILEBUFFER -args ./temp/logfileName.bin. Of course the host timestamps are the playing time then.

8.2.6. TCP output

trice l -p COM3 -tcp 127.0.0.1:23

This additionally sends trice output to a 3rd party TCP listener, for example like Putty:

./ref/PuttyConfig1.PNG ./ref/PuttyConfig2.PNG ./ref/Putty.PNG

8.2.7. TCP input

trice l -p TCP4 -args "192.168.2.3:45678"

This expects a TCP4 server at IP address 192.168.2.3 with port number 45678 to read binary Trice data from.

8.2.8. Stimulate target with a user command over UART

Sometimes it is handy to stimulate the target during development. For that a 2nd screen is helpful what is possible using the display server option:

./ref/UARTCommandAnimation.gif

8.2.9. Explpore and modify channels and their colors

See file TriceColor.md

8.2.10. Location Information

When running trice insert, a file li.json is created, what you can control with the -li|locationInformation switch. During logging, when li.json is found, automatically the filename and line number is displayed in front of each log line, controllable with the -liFmt switch. This information is correct only with the right version of the li.json file. That is usually the case on the PC during development. Out in the field only the til.json reference is of importance. It serves as an accumulator of all firmware versions and usually the latest version of this file is the best fit. The li.json file should stay with the software developer only and needs no version control in the usual case because it is rebuild with each compilation, when trice i is a prebuild step. When trice clean is used, the file li.json should go into the version management too to secure that identical trices get the same ID back.

(back to top)

9. Limitations

9.1. Permanent Limitations

9.1.1. Limitation TRICE in TRICE not possible

  • No-Good Example:
int f0( void ){ TRICE( "msg:f0\n"); return 0; }
void f1( void ){ TRICE( "No; %d", f0() ); }
  • This will compile normally but corrupt TRICE output.

The reason is: When f1() gets active, the "No" Trice header is created, than the f0() Trice is executed and afterwards the "No" Trice tail is written. This works well during compile time but causes a mismatch during runtime.

  • Workaround:
int f0( void ){ TRICE( "msg:f0\n"); return 0; }
void f1( void ){ int x = f0(); TRICE( "Yes: %d", x ); }

9.2. Current Limitations

9.2.1. String Concatenation Within TRICE Macros Not Possible

String concatenation within TRICE macros does not work. The reason lays inside the way the trice tool parser works:

void f0( void ){ TRICE( "msg:" ## "Hello\n" ); } // ERROR!

To implement this would need to build a trice preprocessor or to run the C preprocessor first and to modify the preprocessor output with the trice tool. That would make things unneccessary complicate and fragile for now.

9.2.2. Limited Trice Parser Capabilities

The Trice tool internal parser has only limited capabilities. In works well in most cases, but could led to problems in some cases. The compiler run will for sure end up with some error messages in the following examples, so the developer can fix the code.

An example, provided by @KammutierSpule, is this:

  • started from a empty li.json/til.json
void trice0_test() {
    Trice0( "OK");
    Trice( InvalidUse );
    Trice( "OK", Variable );
}
  • run trice insert
void trice0_test() {
    Trice0( iD(2740), "OK"); // ok, iD is added
    Trice( InvalidUse ); // no warning or error
    Trice( "OK", Variable ); // id is not added / inserted
}

As said, the compiler will complain about that in any case.

10. Additional hints

10.1. Pre-built executables are available

See https://github.com/rokath/trice/releases.

10.2. Configuration file triceConfig.h

  • When setting up your first project you need a triceConfig.h file.
  • You should not use the ./_test/cgo.../triceConfig.h directly, because these are customized for internal tests with CGO. But you can use their settings as helper for a starting point.
  • Please choose one of the ./examples/*_inst/triceConfig.h files as starting point.
  • Comparing them and understandig the differences helps quick starting.
  • The file triceDefaultConfig.h contains all possible config keys with descriptions.

10.3. Setting up the very first connection

If you see nothing in the beginning, what is normal ;-), add the -s (-showInputBytes) switch to see if any data arrive. There is also a switch -debug showing you the received packages, if you are interested in.

10.4. Avoid buffer overruns

It is your responsibility to produce less data than transmittable. If this is not guarantied, a data loss is not avoidable or you have to slow down the user application. The buffers have an optional overflow protection (TRICE_PROTECT), which is enabled by default. Recommendation: Make the buffer big and emit the maxDepth cyclically, every 10 or 1000 seconds. Then you know the needed size. It is influenced by the max Trice data burst and the buffer switch interval. See ./example/exampleData/triceLogDiagData.c for help.

If the target application produces more Trice data than transmittable, a buffer overrun can let the target crash, because for performance reasons no overflow check is implemented in versions before v0.65.0. Such a check is added now per default using TRICE_PROTECT, but the Trice code can only throw data away in such case. Of course you can disable this protection to get more speed.

Configuring the ring buffer option with TRICE_PROTECT == 0 makes buffer overruns not completely impossible, because due to partial Trice log overwrites, false data are not excluded anymore and overwriting the buffer boundaries is possible, because of wrong length information. Also losses will occur when producing more data than transmittable. This is detectable with the cycle counter. The internal 8-bit cycle counter is usually enabled. If Trice data are lost, the receiver side will detect that because the cycle counter is not as expected. There is a chance of 1/256 that the detection does not work for a single case. You can check the detection by unplugging the trice UART cable for a time. Also resetting the target during transmission should display a cycle error.

Gennerally it is recommended to enable TRICE_PROTECT during development and to disable it for performance, if you are 100% sure, that not more data are producable than transmittable.

Important to know: If the TRICE_PROTECT code inhibits the writing into a buffer, there will be later no cycle error because a non existing Trice cannot cause a cycle error. Therefore the TriceDirectOverflowCount and TriceDeferredOverflowCount values exist, which could be monitored.

10.5. Buffer Macros

(Examples in ../_test/testdata/triceCheck.c)

Macro Name Description
triceS |TriceS |TRiceS |TRICE_S Output of runtime generated 0-terminated strings.
triceN |TriceN |TRiceN |TRICE_N Is for byte buffer output as string until the specified size. It allows limiting the string size to a specific value and does not rely on a terminating 0. If for example len = 7 is given and "Hello\0World\n" is in the buffer, the byte sequence "Hello\0W" is transmitted but the trice tool probably shows only "Hello".
trice8B |Trice8B |TRice8B |TRICE8_B Is for byte buffer output according to the given format specifier for a single byte.
trice16B|Trice16B|TRice16B|TRICE16_B Is for 16-bit buffer output according to the given format specifier for a 16-bit value.
trice32B|Trice32B|TRice32B|TRICE32_B Is for 32-bit buffer output according to the given format specifier for a 32-bit value.
triceB |TriceB |TRiceB |TRICE_B Is buffer output according to the given format specifier for a default unit according to configuration (8

10.6. Logfile viewing

Logfiles, trice tool generated with sub-command switch -color off, are normal ASCII files. If they are with color codes, these are ANSI escape sequences.

  • Simply cat trice.log. One view option is also less -R trice.log. The Linux command less is also available inside the windows git bash.
  • Under Windows one could also download and use ansifilter for logfile viewing. A monospaced font is recommended.
  • See also Color issues under Windows

10.7. Using the trice tool with 3rd party tools

Parallel output as logfile, TCP or binary logfile is possible. See examples above.

10.8. Several targets at the same time

You can connect each target over its transmit channel with an own trice instance and integrate all transmissions line by line in an additional trice instance acting as display server. See https://github.com/rokath/trice#display-server-option.

10.9. Executing go test -race -count 100 ./...

The C-code is executed during some tests. Prerequisite is an installed GCC.

10.10. Direct TRICE Out (TRICE_MODE TRICE_STACK_BUFFER) could cause stack overflow with -o0 optimization

As discussed in issue #294 it can happen, that several TRICE macros within one function call increase the stack usage more than expected, when compiler optimization is totally switched off.

10.11. Cycle Counter

  • The trice tool expects the first cycle counter to start with 0xC0 (=192). If the target is already running and you connect the trice tool then, the first message is marked with "CYCLE: ? not equal expected value 192 - adjusting. Now 1 CycleEvents".
  • If the target is resetted asynchronous, the trice tool receives a cycle counter 192. Most probably the last cycle counter was not 191, so this triggers also a messageΒ  with "CYCLE: 192 not equal expected value ?- adjusting. Now n CycleEvents".
  • In the Trice tool is some heuristics to suppress such obvious false positives.

(back to top)

11. Switching Trice ON and OFF

11.1. Target side compile-time Trice On-Off

  • If your code works well after checking, you can add #define TRICE_OFF 1 just before the #include "trice.h" line and no trice code is generated anymore for that file, so no need to delete or comment out TRICE macros:
#define TRICE_OFF 1
#include "trice.h"
void fn(void) { 
    trice( iD(123), "Hi"); // Will generate code only, when TRICE_OFF == 0.
    trice( "Lo");          // Will generate code only, when TRICE_OFF == 0.
}

With #define TRICE_OFF 1 macros in this file are ignored completely by the compiler, but not by the trice tool. In case of re-constructing the Trice ID List these no code generating macros are regarded and go into (or stay inside) the ID reference list.

  • Hint from @escherstair: With -D TRICE_OFF=1 as compiler option, the trice code diappears completely from the binary.
  • No runtime On-Off switch is implemented for several reasons:
    • Would need a control channel to the target.
    • Would add little performance and code overhead.
    • Would sligtly change target timing (testing).
    • User can add its own switches anywhere.
    • The short TRICE macro code is negligible.
    • The trice output is encryptable, if needed.
  • Because of the low Trice bandwidth needs and to keep the target code as clear as possible the runtime On-Off decision should be done by the trice tool.

(back to top)

11.2. Host side Trice On-Off

  • The PC trice tool offers command line switches to -pick or -ban for trice channels and will be extended with display switches.
  • A trice tool -logLevel switch is usable too.

(back to top)

12. Framing

  • Trice messages are framed binary data, if framing is not disabled.
  • Framing is important for data disruption cases and is done with TCOBS (has included data compression) but the user can force to use COBS, what makes it easier to write an own decoder in some cases or disable framing at all.
    • Change the setting TRICE_FRAMING inside triceConfig.h and use the trice tool -packageFraming switch accordingly.
  • For robustness each Trice can get its own (T)COBS package (TRICE_DEFERRED_TRANSFER_MODE == TRICE_SINGLE_PACK_MODE). That is configurable for transfer data reduction. Use #define TRICE_DEFERRED_TRANSFER_MODE TRICE_MULTI_PACK_MODE inside triceConfig.h (is now default). This allows to reduce the data size a bit by avoiding many 0-delimiter bytes but results in some more data loss in case of data disruptions.

(back to top)

13. Optional XTEA Encryption

  • If XTEA is used, the encrypted packages have a multiple-of-8 byte length containing 1-7 padding bytes.
  • The optional decryption is the next step after unpacking a data frame.
  • Enabling XTEA, automatically switches to COBS framing. There is no need to use the trice tool -packageFraming switch in that case because the trice tool, when getting the CLI switch -password "phrase" automatically assumes COBS encoded data, overwriting the default value for -packageFraming.

(back to top)

14. Endianness

  • To interpret a decoded package, itΒ΄s endianness needs to be known.
  • For efficiency, binary trice data are normally stored and transmitted in MCU endianness and the trice tool expects binary data in little endian format as most MCUs are little endian.
  • On big endian MCUs the compiler switch TRICE_MCU_IS_BIG_ENDIAN needs to be defined as 1 and TRICE_TRANSFER_ORDER_IS_BIG_ENDIAN should have the same value. The trice tool has a CLI switch "triceEndianness" which needs to be set to "bigEndian" then.
  • If trice transmit data are needed to be not in MCU order for some reason, that increases the critical trice storage time and target code amount.
  • De facto different values for TRICE_MCU_IS_BIG_ENDIAN and TRICE_TRANSFER_ORDER_IS_BIG_ENDIAN are mainly used to test the Trice CLI switch -triceEndianness bigEndian automatically.

(back to top)

15. TRICE (Time)Stamps

  • Each Trice message can carry stamp bits, which are free usable like for time, addressing or filtering.

  • By selecting the letter case (trice, Trice, TRice) you decide for each single Trice macro about the stamp size.

  • Default notation (function call):

    notation stamp size remark
    trice( iD(n), "...", ...); 0-bit no stamp at all, shortest footprint
    Trice( iD(n), "...", ...); 16-bit calls internally uint16_t TriceStamp16( void ) for trice message stamping
    TRice( iD(n), "...", ...); 32-bit calls internally uint32_t TriceStamp32( void ) for trice message stamping
  • No upper case macro, like TRICE_S works with the internal iD(n) macro. They need id(n), Id(n) or ID(n). See next table.

  • Legacy notation (code inlining):

    notation stamp size remark
    TRICE( id(n), "...", ...) 0-bit no stamp at all, shortest footprint
    TRICE( Id(n), "...", ...) 16-bit calls internally uint16_t TriceStamp16( void ) for trice message stamping
    TRICE( ID(n), "...", ...) 32-bit calls internally uint32_t TriceStamp32( void ) for trice message stamping

It is up to the user to provide the functions TriceStamp16 and/or TriceStamp32. Normally they return a Β΅s or ms tick count but any values are allowed.

(back to top)

16. Binary Encoding

16.1. Symbols

Symbol Meaning
i ID bit
I iiiiiiii = ID byte
n number bit
z count selector bit
s stamp selector bit
N znnnnnnnn = count selector bit plus 7-bit number byte
c cycle counter bit
C z==0 ? cccccccc : nnnnnnnn = cycle counter byte or number byte extension
t (time)stamp bit
T tttttttt = (time)stamp byte
d data bit
D dddddddd = data byte
... 0 to 32767 data bytes
"..." format string
W bit width 8, 16, 32 or 64 (uW stands for u8, u16, or u64)
x unspecified bit
X =xxxxxxxx unspecified byte

16.2. Package Format

  • Because of the TCOBS or COBS package framing, the package sizes are detectable by the trice tool without additionlal length information.

  • All decoded frames of 0-, 1-, 2- and 3-byte size are considered as user data and ignored by the trice tool.

    bytes Comment
    `` This is an empty package, which can have also a meaning. It is detectable by 2 consecutive 0-delimiter bytes.
    X 1-byte message, reserved for extensions or user data
    X X 2-byte message, reserved for extensions or user data
    X X X 3-byte message, reserved for extensions or user data
  • In decoded frames with >= 4-bytes the first 2 bytes contain 2 stamp selector bits at the most significant position in the known endianness.

  • The 0 stamp selector is usable for any user encoding. The trice tool ignores such packages.

  • The 1, 2 and 3 stamp selector bits are followed by the 14-bit ID.

    16-bit groups Stamp Selector (2 msb) Comment Endianness sizes
    _________ 00xxxxxxX ... 0 >= 4-byte message, reserved for extensions or user data ___ u16 ?...?
    _________ 01iiiiiiI NC ... 1 >= 4-byte message, Trice format without stamp ___ u16 u16 [uW] ... [uW]
    _________ 10iiiiiiI TT NC ... 2 >= 4-byte message, Trice format with 16-bit stamp ___ u16 u16 u16 [uW] ... [uW]
    10iiiiiiI 10iiiiiiI TT NC ... 2 First 16bit are doubled. Info over -d16 trice switch. u16 u16 u16 u16 [uW] ... [uW]
    _________ 11iiiiiiI TT TT NC ... 3 >= 4-byte message, Trice format with 32-bit stamp ___ u16 u32 u16 [uW] ... [uW]
  • The stamp selector 2 encoding has 2 possibilities. When using TRICE_DIRECT_SEGGER_RTT_32BIT_WRITE or encryption, for alignment reasons the first 16bit ID field is doubled. The trice tool discards these 2 doubled bytes when the CLI switch -d16 is given or encryption is active.

  • Default endianness is little endian as most MCUs use little endianness. Otherwise the -triceEndianness=bigEndian CLI switch is needed.

  • The receiving tool evaluates firstly the 2 stamp bits and follows some rules:

    • 0: reserved -> ignore the whole package (discard) or treat it as user data.
    • 1: next 14 bits are the ID followed by 2 bytes u16=NC and optional parameter values. Package size is >= 4 bytes.
    • 2 and -d16 CLI switch not provided: next 14 bits are the ID and convert then u16=TT=stamp16 followed by 2 bytes u16=NC and optional parameter values. Package size is >= 6 bytes.
    • 2 and -d16 CLI switch provided: next 14 bits are the ID, discard 2 following bytes and convert then u16=TT=stamp16 followed by 2 bytes u16=NC and optional parameter values. Package size is >= 8 bytes.
    • 3: next 14 bits are the ID and convert then u32=TTTT=stamp32 followed by 2 bytes u16=NC and optional parameter values. Package size is >= 8 bytes.
  • use ID to get parameters width W=8,16,32,64 from file til.json and and parameters count and convert appropriate.

    • Within one trice message the parameter bit width W does not change.

(back to top)

17. Trice Decoding

The 14-bit IDs are used to display the log strings. These IDs are pointing in two reference files.

17.1. Trice ID list til.json

  • This file integrates all firmware variants and versions and is the key to display the message strings. With the latest version of this file all previous deployed firmware images are usable without the need to know the actual firmware version.
  • The files til.json.h, til.json.c and the like are generated to help writing an own trice decoder tool in your preferred language. Use trice i -v for it. That can be interesting in environments, where Go compiled binaries not executable, like PCs running QNX OS.

17.2. Trice location information file li.json

  • If the generated li.json is available, the trice tool automatically displays file name and line number. But that is accurate only with the exact matching firmware version. That usually is the case right after compiling and of most interest at the developers table.
  • The trice tool will silently not display location information, if the li.json file is not found. For in-field logging, the option -showID string could be used. This allows later an easy location of the relevant source code.
  • An other option is to record the binary trice messages and to play them later with the trice tool using the correct li.json.

(back to top)

18. Trice ID Numbers

18.1. ID number selection

  • The default encoding TREX supports 14-bit IDs, so over 16000 IDs possible. Other encodings can work with other ID sizes.
  • trice("Hi!\n"); ➑ trice i ➑ trice( iD(12345), "Hi!\n"); ➑ trice c ➑ trice("Hi!\n");
  • The ID 12345 is a number assigned to trice( "Hi!\n"); in the above example.
    • It is a so far unused number, according to rules you can control:
      • The -IDMethod switch allows a selection method for new IDs.
        • Per default new IDs determined randomly to keep the chance low, that several developers grab the same ID.
        • Example: trice insert -IDMin 1000 -IDMethod upward will choose the smallest free ID >= 1000.
          • This allows to use the ID space without wholes.
      • The -IDMin and -IDMax switches are usable to control the ID range, a new ID is selected from, making it possible to divide the ID space. Each developer can gets it region.
        • Example: trice insert -IDMin 6000 -IDMax 6999 will choose new randomly IDs only between 6000 and 6999.
  • It is possible to give each trice tag an ID range making it possible to implement Trice tag specific runtime on/off on the target side if that is needed. This could be interesting for routing purposes also. Please run trice help -insert and read about the -IDRange switch for more details.

18.2. ID number usage and stability

  • If you write trice( "msg:%d", 1); again on a 2nd location, the copy gets a different ID, because each Trice gets its own ID.
  • If you change trice( "msg:%d", 1); to trice8( "msg:%d", 1);, to reduce the needed parameter space, a new ID is assigned. That is because the parameter bit width is implicit a part of the now changed Trice. If you change that back, the previous ID is assigned again.
  • If you change trice( "msg:%d", 1); to TRice8( "msg:%d", 1);, to get a 32-bit stamp, the associated ID remains unchanged. That is because the optional stamp is not a part of the Trice itself.
  • IDs stay constant and get only changed to solve conflicts.
  • To make sure, a single ID will not be changed, you could change it manually to a hexadecimal syntax.
    • This lets the trice insert command ignore such TRICE macros and therefore a full til.json rebuild will not add them anymore. Generally this should not be done, because this could cause future bugs.
    • It is possible to assign an ID manually as decimal number. It will be added to the ID list automatically during the next trice i|c if no conflicts occur.
  • If a Trice was deleted inside the source tree (or file removal) the appropriate ID stays inside the ID list.
  • If the same string appears again in the same file this ID is active again.
  • If a trice occurs more than one time, each occurrence gets a different ID. If then 2 of them disappear, their ID numbers stay in til.json. If then one of them comes back, it gets its ID back.

18.3. Trice ID 0

  • The trice ID 0 is a placeholder for "no ID", which is replaced automatically during the next trice insert according to the used trice switches -IDMethod, -IDMin and IDMax.
    • It is sufficient to write the TRICE macros just without the id(0), Id(0), ID(0),. It will be inserted automatically according the -stamp switch.

(back to top)

19. Trice ID management

19.1. Trices inside source code

19.1.1. Trices in source code comments

  • TRICE macros commented out, are visible for the trice insert command and therefore regarded.
    • Example: // TRICE( Id(12345), "Hi!\n" ); is still regarded by the trice i.
  • During trice insert TRICE macros, commented out, are treated in the same way as active TRICE macros. Even after deletion their content stays inside til.json. This is intensionally to get best stability across several firmware versions or variants.
  • The trice tool does treat trice statements inside comments or excluded by compiler switches also.

19.1.2. Different IDs for same Trices

  • When the same Trice is used several times with identical IDs, after copying, and trice insert is called, only one ID survives in the source code.

19.1.3. Same IDs for different Trices

  • If duplicate ID's with different format strings found inside the source tree (case several developers or source code merging) one ID is replaced by a new ID. The probability for such case is low, because of the default random ID generation.
  • Also you can simply copy a Trice statement and modify it without dealing with the ID.
  • The trice tool will detect the 2nd (or 3rd) usage of this ID and assign a new one, also extending the ID list.
  • That is done silently for you during the next trice insert.

(back to top)

20. ID reference list til.json

  • The trice insert command demands a til.json file - it will not work without it. That is a safety feature to avoid unwanted file generations. If you are sure to create a new til.json file, create an empty one: touch til.json.
  • The name til.json is a default one. With the command line parameter -i you can use any filename.
  • It is possible to use several til.json files - for example one for each target project but it is easier to maintain only one *til.json- file for all projects.
  • The ID reference list keeps all obsolete IDs with their format strings allowing compatibility to former firmware versions.
  • One can delete the ID reference list when IDs inside the code. It will be reconstructed automatically from the source tree with the next trice clean command, but history is lost then.
  • Keeping obsolete IDs makes it more comfortable during development to deal with different firmware variants at the same time.

20.1. til.json Version control

  • The ID list should go into the version control repository of your project.
  • To keep it clean from the daily development garbage one could delete the til.json, then check-out again and re-build just before check-in. A small script could do that.
  • For a firmware release it makes sense to remove all unused IDs from til.json.
    • Just delete the til.json contents and run trice clean.
  • An other option is to delete til.json just before a release build and then check-in the new generated til.json.

For the no-ids option deleting til.json should not not be done when the sources are without IDs. That would result in a loss of the complete ID history and a assignment of a complete new set of IDs.

20.2. Long Time Availability

  • You could place a download link for the trice tool and the used til.json list.
  • Link a compressed/encrypted til.json file as resource into the target binary and optionally get it back long years later in a safe way.
  • Optionally add the (compressed/encrypted) ID reference list as resource into the target FLASH memory to be sure not to loose it in the next 200 years.

(back to top)

21. The trice insert Algorithm

21.1. Starting Conditions

  • Before trice i is executed on a source tree, the starting conditions are partially undefined:
    • A trice ID list file til.json file must exist, but it is allowed to be empty.
      • The til.json is a serialized key-value map, where
        • the keys are the IDs i and
        • the values are Trice format string structs (bit width plus format string) named f.
        • When de-serializing, it is not impossible, that an ID is used more than one times. This can only happen, when til.json was edited manually, what normally is not done.
          • The trice tool will report that as error and stop.
        • This ID look-up is the key-value map idToFmt TriceIDLookUp as map[TriceID]TriceFmt.
          • Each ID i as key, points to one and only one f.
          • The TriceFmt structs contains the parameter width and the format string.
        • The idToFmt is reverted then into fmtToId triceFmtLookUp as map[TriceFmt]TriceIDs.
          • TriceIDs is a triceID slice because the identical f can have several ids (no shared IDs).
          • The format struct f look-up map fmtToId is used internally for faster access and always in sync with idToFmt.
      • idToFmt and fmtToId together are named lu.
    • A location information file li.json may exist or not.
      • The li.json is a serialized key-value map idToLocRef TriceIDLookUpLI, a map[TriceID]TriceLI, where
        • the keys are the IDs i and
        • the values are the location information (filename, line and position in line) structs.
      • Each ID as key points to one and only one location information.
  • The til.json IDs may occur in the source tree not at all, once or several times. Also it is not guarantied, that the source tree Trices match the til.json value.
    • That is possible after code edit, for example or code copied or modified.
    • One and only one position is used and relevant, all others are ignored. If no til.json exists on the expected location the user must provide one, at least an empty file.
  • The li.json IDs may occur in the source tree not at all, once or several times. Also it is not guarantied, that the source tree Trices match the li.json value.
    • One and only one position is used and relevant, all others are ignored. If no li.json exists on the expected location trice insert creates one there.
  • The src tree can contain IDs not present inside til.json. This state is seldom, for example after adding sources containing IDs.

21.2. Aims

  • The trice insert main aim is to have a consistent state between til.json, li.json and the source tree with no ID used twice.
  • Also the changes should be minimal.
  • As a general rule lu is only extendable.
  • li is rebuild from scratch.
  • For faster operation files will be processed parallel.
  • To keep the ID management simple, the insert operation acts "per file". That means, that in case a file is renamed or code containing trice statements is copied to an other file, new IDs are generated for the affectes trices.
    • File name changes occur are not that often, so tha should be acceptable.

21.3. Method

21.3.1. insert Initialization

// insertIDsData holds the insert run specific data.
type insertIDsData struct {
    idToFmt    TriceIDLookUp     // idToFmt is a trice ID lookup map and is generated from existing til.json file at the begin of SubCmdIdInsert. This map is only extended during SubCmdIdInsert and goes back into til.json afterwards.
    fmtToId    triceFmtLookUp    // fmtToId is a trice fmt lookup map (reversed idToFmt for faster operation) and kept in sync with idToFmt. Each fmt can have several trice IDs (slice).
    idToLocRef TriceIDLookUpLI   // idToLocInf is the trice ID location information as reference generated from li.json (if exists) at the begin of SubCmdIdInsert and is not modified at all. At the end of SubCmdIdInsert a new li.json is generated from itemToId.
    itemToId   TriceItemLookUpID // itemToId is a trice item lookup ID map, extended from source tree during SubCmdIdInsert after each found and maybe modified trice item.
    idToItem   TriceIDLookupItem // idToItem is a trice ID lookup item map (reversed itemToId for faster operation) and kept in sync with itemToId.
}
  • Create a insertIDsData instance.

  • De-serialize til.json into idToFmt and fmtToId. On error abort and report for manual correction. One result is a slice with used IDs.

  • De-serialize li.json into idToLocRef. On error abort and report for manual correction. As result the slice with used IDs is extended.

    • If li.json contains IDs not already inside til.json, these are reported as warning.
    • idToLocRef stays untouched and is used only in cases when identical f are found.
  • Create a slice IDSpace with numbers IDmin ... IDmax (1 ... 16383, or 1000 ... 1999 if specified in the command line that way)

  • Remove all used IDs from IDSpace.

    • If used IDs outside IDmin and IDmax, for example IDmin=1000, IDmax=1999 and some used IDs are bigger or smaller these are not removable from IDroom what is ok.
  • Create empty itemToId and idToItem.

  • Walk the src and create a source tree map STM with

    • key=Trice+LI and
    • value=ID.
  • During STM creation use these rules:

    • If the next found f src ID == n != 0:
      • If ID n already inside STM set ID = 0 (that is brutal but ok)
      • Otherwise extend STM with ID n and remove n from IDroom
        • It is possible, f is used n times with different IDs, so that is no problem.
        • It is possible, f is used n times with the same ID, so the first occurrence is the winner.
    • If the next found f src ID == 0 (normal case after trice z):
      • Look in flu
        • If not there, create new id and extend STM.
          • The new ID is "new", so forbidden to be inside ilu.
          • If it is accidentally somewhere in the so far unparsed src, we do not know that and therefore do not care about.
            • That is a seldom case and not worth to parse the source tree twice all the time.
          • Patch id into source and extend STM.
        • If the ID slice has len 1 (usually the case), take that n, extend STM and remove f from flu.
          • That is important because f could be copied before.
        • If the ID slice has a len > 1 (several IDs on the same string) check li
          • If li is empty, just remove the first id from the slice and extend STM
          • Loop over slice IDs
            • If a file matches, take the first occurrence, extend STM and remove id from the ID slice
            • If no file matches do the same as when li is empty.
            • That means, after file renaming or code copying between files during trice z state, new IDs are generated for that parts.
              • That is only for same f with several IDs cases
            • File changes during trice i state are ok, because STM is generated with the IDs inside the sources.

Until here the algorithm seem to be ok.

  • STM is not needed but maybe helpful during debugging.

  • STM than is usable to regenerate li.json and to extend til.json

  • If after trice i a trice z and a trice i again is executed, all IDs are expected to be at the same place again. If in between trice i, an optional trice zand a trice i src was edited, most IDs are expected to be at the same place again.

21.4. User Code Patching (trice insert)

  • A Trice ID is inserted by trice insert as shown in the table:

    Unpatched User Code After trice insert Remark
    trice( "Hi!\n"); trice( iD(12345), "Hi!\n"); no stamps
    Trice( "Hi!\n"); Trice( iD(12345), "Hi!\n"); 16-bit stamps
    TRice( "Hi!\n"); TRice( iD(12345), "Hi!\n"); 32-bit stamps
  • Legacy code is handled this way:

    Unpatched User Code After trice insert Remark
    TRICE( "Hi!\n"); TRICE( id(12345), "Hi!\n"); no stamps after trice i -defaultStampSize 0
    TRICE( "Hi!\n"); TRICE( Id(12345), "Hi!\n"); 16-bit stamps after trice i -defaultStampSize 16
    TRICE( "Hi!\n"); TRICE( ID(12345), "Hi!\n"); 32-bit stamps after trice i -defaultStampSize 32
    TRICE( id(0), "Hi!\n"); TRICE( id(12345), "Hi!\n"); no stamps
    TRICE( Id(0), "Hi!\n"); TRICE( Id(12345), "Hi!\n"); 16-bit stamps
    TRICE( ID(0), "Hi!\n"); TRICE( ID(12345), "Hi!\n"); 32-bit stamps
  • A pre-build step trice insert generates the Id(12345) part. Examples:

    • trice i in your project root expects a til.json file there and checks sources and til.json for changes to insert.
    • trice i -v -i ../../../til.json -src ../src -src ../lib/src -src ./ is a typical case as automated pre-build step in your project settings telling trice to scan the project dir and two external directories. Even trice i is fast, it is generally quicker to search only relevant places.

21.5. User Code Patching Examples

  • A Trice ID is modified as shown in these cases:

    • Previously inserted (patched) user code copied to a different location:

      trice(iD(12345), "Hi!\n"); // copied
      trice(iD(12345), "Hi!\n"); // original
      trice(iD(12345), "Hi!\n"); // copied
    • After updating (patching) again:

      trice(iD(12345), "Hi!\n");
      trice(iD( 1233), "Hi!\n"); // re-patched
      trice(iD( 1234), "Hi!\n"); // re-patched
      • If the code is copied inside the same file, the first occurrence after the copy stays unchanged and the following are modified.
      • If the code is copied to other files only, the copies get new IDs.
    • Previously inserted (patched) user code copied and modified:

      trice(iD(12345), "Ha!\n"); // copied and modified
      trice(iD(12345), "Hi!\n"); // original
      trice(iD(12345), "Ha!\n"); // copied and modified
    • After updating (patching) again:

      trice(iD( 2333), "Ha!\n"); // re-patched
      trice(iD(12345), "Hi!\n"); // unchanged
      trice(iD( 1234), "Ha!\n"); // re-patched
    • If the code is copied to other files, it is re-patched.

  • A Trice ID is stays the same if the stamp size is changed. Example:

    trice( iD(12345), "Hi!" ); // original
    TRice( iD(12345), "Hi!" ); // manually changed stamp size and then "trice i" performed.

21.6. User Code Un-Patching

21.7. ID Usage Options

  • Per default the trice insert command chooses randomly a so far unused ID for new format strings and extends til.json.
  • After trice c all src IDs are removed or 0. In this state the src should go into the version management system.

21.8. General ID Management Information

  • The trice ID-instead-of-String idea lives from pre-compile patching of the user code.
  • The user has full control how to deal with that.
  • There are 3 options and the user has to decide which fits best for him.
  • Each format string gets its unique trice ID. If the same format string is used on different source code locations it gets different trice IDs this way allowing a reliable location information.

21.9. Option 1: Let the inserted Trice ID be a Part of the User Code

  • This is the legacy method. It allows unchanged src translation into code without using the trice tool.
  • It is very robust and maybe needed in nasty debugging situations.
  • It allows to reconstruct lost til.json information.
  • Recommendet for small projects.

21.10. Option 2: Cleaning in a Post-build process

  • The code is visually free of IDs all the time.

21.11. Option 3: Cleaning on Repository Check-In

  • The code is visually free of IDs only inside the repository.

(back to top)

22. Changelog

Details
    Date Version Comment
    2022-MAR-15 0.0.0 Initial Draft
    2022-MAR-15 0.1.0 Minor corrections applied.
    2022-MAR-15 0.2.0 Sigil byte encoding clarified.
    2022-MAR-15 0.3.0 Forward versus backward COBS encoding discussion inserted.
    2022-MAR-15 0.4.0 Forward versus backward COBS encoding reworked. Disruption detection added.
    2022-MAR-15 0.5.0 Minor corrections
    2022-MAR-16 0.6.0 TCOBS prime number comment added, simplified
    2022-MAR-17 0.7.0 TCOBS move into a separate TCOBS Specification, Framing more detailed.
    2022-MAR-20 0.7.1 Contributive Trice extension remark added.
    2022-APR-12 0.8.0 TREX mainstream format changed to timestamps immediate after ID.
    2022-MAY-20 0.8.1 Formatting, Spelling
    2022-JUN-19 0.9.0 Implementation hint added to chapter Framing.
    2022-AUG-14 0.10.0 Chapter ID Management added
    2022-AUG-19 0.11.0 Chapter Main Stream Logs changed/extended
    2022-SEP-15 0.11.1 TS32, TS16, NOTS, MOD7 added
    2022-OCT-08 0.11.2 S0...X3 added
    2022-NOV-28 0.11.3 +#337 in Framing
    2022-DEC-11 0.12.0 restructured
    2022-DEC-13 0.13.0 unneeded text removed, some clarifications
    2023-JAN-14 0.14.0 Formatting improved, 1. Trice User Interface - Quick Start added.
    2023-JAN-14 0.15.0 5.1. The trice insert algorithm added
    2023-JAN-21 0.15.1 Corrections
    2023-FEB-25 0.16.0 Many parts reworked and restructured
    2023-JUN-10 0.17.0 trice insert algorithm refined
    2023-AUG-03 0.18.0 update ---> insert
    2024-AUG-18 0.19.0 Mainly updates to current Trice version v0.66.0
    2024-SEP-17 0.20.0 TCP4 input hint added
    2024-SEP-25 0.21.0 Chapter "Target Macros" added
    2024-NOV-11 0.22.0 Whole document re-read aud updated.

    (back to top)