_(Read this)
SORRY: This document is long and not 100% consistent. Hints or pull requests are welcome.
Table of Contents
- Trice user guide
- 1. Project structure
- 2. Get started
- 3. Build
trice
tool from Go sources (you can skip that) - 4. Embedded system code configuration
- 5.
trice
tool in logging action - 6. Encryption
- 7. CLI Options for
trice
tool - 8. Trice command line examples
- 8.1. Common information
- 8.2. Further examples
- 8.2.1. Automated pre-build insert command example
- 8.2.2. Some Log examples
- 8.2.3. Logging over a display server
- 8.2.4. Logfile output
- 8.2.5. Binary Logfile
- 8.2.6. TCP output
- 8.2.7. TCP input
- 8.2.8. Stimulate target with a user command over UART
- 8.2.9. Explpore and modify channels and their colors
- 8.2.10. Location Information
- 9. Limitations
- 10. Additional hints
- 10.1. Pre-built executables are available
- 10.2. Configuration file
triceConfig.h
- 10.3. Setting up the very first connection
- 10.4. Avoid buffer overruns
- 10.5. Buffer Macros
- 10.6. Logfile viewing
- 10.7. Using the
trice
tool with 3rd party tools - 10.8. Several targets at the same time
- 10.9. Executing
go test -race -count 100 ./...
- 10.10. Direct TRICE Out (TRICE_MODE TRICE_STACK_BUFFER) could cause stack overflow with -o0 optimization
- 10.11. Cycle Counter
- 11. Switching Trice ON and OFF
- 12. Framing
- 13. Optional XTEA Encryption
- 14. Endianness
- 15.
TRICE
(Time)Stamps - 16. Binary Encoding
- 17. Trice Decoding
- 18. Trice ID Numbers
- 19. Trice ID management
- 20. ID reference list til.json
- 21. The
trice insert
Algorithm- 21.1. Starting Conditions
- 21.2. Aims
- 21.3. Method
- 21.4. User Code Patching (
trice insert
) - 21.5. User Code Patching Examples
- 21.6. User Code Un-Patching
- 21.7. ID Usage Options
- 21.8. General ID Management Information
- 21.9. Option 1: Let the inserted Trice ID be a Part of the User Code
- 21.10. Option 2: Cleaning in a Post-build process
- 21.11. Option 3: Cleaning on Repository Check-In
- 22. Changelog
- Download latest release assets for your system: Compressed source code and binaries.
- OR Get the repo:
- OR use the button
- 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.
- 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
andli.json
in your project root. - Run
trice insert
and the trice code line changes totrice( iD(1234), "Hello! ππ\a\n" );
. - The 2 JSON files are now filled with information.
- Run
trice clean
and the trice code line changes back totrice( "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 andtrice clean
in a pre-check-in script to keep just the repository clean of Trice IDs. Using onlytrice 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.
- 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
andtrice log
. Call them with the right CLI switches.-
trice help -insert
andtrice 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.- Even if you do not have such hardware, you can compile the
examples/F030R8_inst
project just to get started. - When adding or modifying
trice
macros inside examples/F030R8_inst/Core/Src/main.c and recompiling you should see automatically changed ID numbers inside the code.
- Even if you do not have such hardware, you can compile the
- 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- andtrice 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.
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.
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.
-
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.
-
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.
- 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 macrostrice
,Trice
,TRice
and their relatives are just function calls and better suited to be used normally.
- Do not inlucde this file directly, because it could get changed when
- 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).
- C & H files containing
- Extend
til.json
- If no
til.json
is found nothing happens. At least an empty file is needed (Safety feature).
- If no
- Patch source.c to
- Create empty file:
- When the program runs later, it should output something similar to
- Look into ./TriceVsPrintfSimilaritiesAndDifferences.md for options.
- Read ./TriceConfigProjectImageSizeOptimization.md if needed.
- 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.
./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 |
- The tcobs*.* files are copied from https://github.com/rokath/tcobs/tree/master/Cv1. They are maintained there and extensively tested and probably not a matter of significant change.
- The SEGGER files are copied and you could check for a newer version at https://www.segger.com/downloads/jlink/.
-
Replace all strings
puts
with the stringtrice
, when the string follows immediately. For runtime generated strings seetriceS
. -
Replace all strings
printf
with the stringtrice
, 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()
ora 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) );
- Because double needs 8 bytes the trice macro in this case needs to be trice64 (see Trice Parameter Bit Widths).
-
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.
-
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
andtrice 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.
- 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
-
-
The macros
trice
,Trice
,TRice
andTRICE
use 32-bit parameter values per default. SeeTRICE_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.
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");
- The
trice insert
andtrice clean
will not see thetrice( "hi 3");
line here, but the compiler will mark an error then. - See also issue #427, issue #465 and see also Limited Trice Parser Capabilities.
- There is nothing wrong, when putting trice macros into header files.
- But: When you use
trice insert
as pre-build command andtrice 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.
- 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.
- 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.
Check comments inside triceDefaultConfig.h and adapt your project configuration like shown in triceConfig.h as example.
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.
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.
- 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
astrice 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.
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
, ... .
- 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.
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:
- 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.
- 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
- 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
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.
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.
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:
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.
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:
See file TriceColor.md
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.
- 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 ); }
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.
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.
See https://github.com/rokath/trice/releases.
- 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.
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.
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.
(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 |
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 alsoless -R trice.log
. The Linux commandless
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
Parallel output as logfile, TCP or binary logfile is possible. See examples above.
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.
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.
- 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.
- 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 outTRICE
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.
- 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.
- 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
insidetriceConfig.h
and use the trice tool-packageFraming
switch accordingly.
- Change the setting
- 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
insidetriceConfig.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.
- 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
.
- 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 andTRICE_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
andTRICE_TRANSFER_ORDER_IS_BIG_ENDIAN
are mainly used to test the Trice CLI switch-triceEndianness bigEndian
automatically.
-
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 stampingTRice( iD(n), "...", ...);
32-bit calls internally uint32_t TriceStamp32( void )
for trice message stamping -
No upper case macro, like
TRICE_S
works with the internaliD(n)
macro. They needid(n)
,Id(n)
orID(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 stampingTRICE( 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.
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 |
-
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
and3
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 usingTRICE_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.
- Within one trice message the parameter bit width
The 14-bit IDs are used to display the log strings. These IDs are pointing in two reference files.
- 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. Usetrice i -v
for it. That can be interesting in environments, where Go compiled binaries not executable, like PCs running QNX OS.
- 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
.
- 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 totrice( "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.
- Example:
- The
- It is a so far unused number, according to rules you can control:
- 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.
- 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);
totrice8( "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);
toTRice8( "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 suchTRICE
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.
- This lets the
- 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.
- 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
andIDMax
.- 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.
- It is sufficient to write the TRICE macros just without the
TRICE
macros commented out, are visible for thetrice insert
command and therefore regarded.- Example:
// TRICE( Id(12345), "Hi!\n" );
is still regarded by thetrice i
.
- Example:
- 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.
- 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.
- 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
.
- 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.
- 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
.
- Just delete the til.json contents and run
- 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.
- 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.
- 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
asmap[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.
- The
- A location information file
li.json
may exist or not.- The
li.json
is a serialized key-value mapidToLocRef TriceIDLookUpLI
, amap[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
- A trice ID list file
- 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 thetil.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 theli.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.
- One and only one position is used and relevant, all others are ignored. If no
- The src tree can contain IDs not present inside
til.json
. This state is seldom, for example after adding sources containing IDs.
- The
trice insert
main aim is to have a consistent state betweentil.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.
// 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
intoidToFmt
andfmtToId
. On error abort and report for manual correction. One result is a slice with used IDs. -
De-serialize
li.json
intoidToLocRef
. On error abort and report for manual correction. As result the slice with used IDs is extended.- If
li.json
contains IDs not already insidetil.json
, these are reported as warning. idToLocRef
stays untouched and is used only in cases when identical f are found.
- If
-
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
andidToItem
. -
Walk the src and create a source tree map STM with
- key=
Trice+LI
and - value=ID.
- key=
-
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.
- If not there, create new id and extend STM.
- Look in flu
- If the next found f src ID == n != 0:
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
atrice z
and atrice i
again is executed, all IDs are expected to be at the same place again. If in betweentrice i
, an optionaltrice z
and atrice i
src was edited, most IDs are expected to be at the same place again.
-
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 theId(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. Eventrice i
is fast, it is generally quicker to search only relevant places.
-
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.
- Per default the
trice insert
command chooses randomly a so far unused ID for new format strings and extendstil.json
. - After
trice c
all src IDs are removed or 0. In this state the src should go into the version management system.
- 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.
- 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.
- The code is visually free of IDs all the time.
- The code is visually free of IDs only inside the repository.
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. |