trice

🟢 super fast 🚀 tiny 🐥 𝘾 printf-look-and-feel ✍ trace code, in ⚡ interrupts ⚡ too ‼️, and real-time PC 💻 logging 👀

View the Project on GitHub rokath/trice

Trice User Manual

+ Speed of Light `printf` Comfort Within Interrupts And Everywhere +
-   (TL;DR)   ->  Too Long; Don't Read - use it as reference only❗

(go to bottom)


Table of Contents

(click to expand)

(back to top)


./ref/TriceCheckOutput.gif


1. Abstract

If you develop software for an embedded system, you need some kind of system feedback. Debuggers are awesome tools, but when it comes to analyze dynamic behavior in the field, they are not usable.

Logging then, usually done with printf-like functions, gets quick a result after having i.e. putchar() implemented. This turns out to be an expensive way in terms of processor clocks and needed FLASH memory, when you regard the library code and all the strings needing FLASH memory space. For small micro-controllers that´s it.

Bigger micro-controllers are coming with embedded trace hardware. To use it, an expensive tool is needed. Useful for analyzing complex systems, but for in-field related issues at least unhandy.

Unhappy with this situation, the developer starts thinking of using digital pins or starts emitting some proprietary LED blinking codes or byte sequences, difficult to interpret.

The Trice technique tries to fill this gap, being minimal invasive for the target and as comfortable as possible. It is the result of a long-year dissatisfaction and several attempts to find a loophole to make embedded programming more fun and this way more effective.

Trice is an unusual software tracer-logger, using internally IDs instead of format strings to get maximum speed but provides the user with a printf-like comfort:

trice("Hello! 👋🙂");

int a = -4;
float x = 3.14159265;
trice("info:π/%d is %f with the bit pattern %032b\n", a, aFloat(x/a), x );

string s = "world";
triceS("msg:A runtime generated string: %s", s);

Replacing a printf library, the Trice target source code occupies 1-4 KB Flash memory and less than 1 KB RAM in dependence of the configuration which is done with a user file named triceConfig.h:

#define TRICE_DEFERRED_OUTPUT 1
#define TRICE_BUFFER TRICE_DOUBLE_BUFFER
#define TRICE_DEFERRED_UARTA 1
#define TRICE_UARTA USART2

The open-source Trice PC tool is executable on all Go platforms, at least:

In the future other ports are possible:

./ref/life0.gif

(back to top)

2. A brief history of Trice

Developing firmware means to deal also with interrupts and often with timing. How do you check, if an interrupt occurred? Ok, increment a counter and display it in a background loop with some printf-like function. What about time measurement? Set a digital output to 1 and 0 and connect a measurement device. Once, developing software for a real-time image processing device, I had no clue where in detail the processing time exploded when the image quality got bad. A spare analog output with a video interrupt synced oscilloscope gave me the needed information, after I changed the analog output on several points in my algorithm. But, hey guys, I want to deal with my programming tasks and do not like all this hassle connecting wires and steer into instruments.

A printf is so cool on a PC, developing software there. But an embedded device often cannot use it for performance reasons. My very first attempt was writing the format string .const offset together with its values in a FIFO during a log statement and to do the printf it in the background. But that is compiler specific. Ok the full string address is better but needs buffer space. Zephyr for example does something like that calling it “deferred logging”.

Then, one day I had the idea to compute short checksums for the format strings in a pre-compile step and to use them as ID in a list together with the format strings. That was a step forward but needed to write a supporting PC program. I did that in C++ in the assumption to get it better done that way. Finally, it worked, but I hated my PC code, as I dislike C++ now because of all its nuts and bolts to handle, accompanied by missing libraries on the next PC. The tool usability was also unhandy and therefore error prone and the need became clear for a full automatized solution. Also, what is, if 2 different format strings accidentally generate the same short checksum? There was a way around, but an ID based message filtering will never be possible that way.

The need became clear for controllable IDs and management options. And there was Go now, an as-fast-as-C language, easy to learn, promising high programming efficiency and portability. It would be interesting to try it out on a real PC project.

Trying to add tags in form of partial Trice macro names was blowing up the header code amount and was a too rigid design. Which are the right tags? One lucky day I came to the conclusion to handle tags just as format string parts like "debug:Here we are!\n" and getting rid of them in the target code this way also giving the user freedom to invent any tags.

An other point in the design was the question how to re-sync after data stream interruption, because that happens often during firmware development. Several encodings where tried out and a proprietary escape sequence format and an alternative flexible data format with more ID bits where working reliable but with COBS things got satisfying. A side result of that trials is the Trice tool option to add different decoders if needed. Now the default Trice message framing is TCOBSv1 which includes short message compression and this way allows very low transmit bandwidths and/or saves storage, when binary Trice data are stored in Flash memory.

There was a learning not to reduce the transmit byte count to an absolute minimum, but to focus more on Trice macro speed and universality. That led to a double buffer on the target side as an alternative to the ring buffer solution. The actual binary encoding, allowing alongside user protocols, is result of the optional target timestamps and location info some users asked for, keeping the target code as light as possible. Float and double number support was implementable for free because this work is done mainly on the host side.

Trice grew, and as it got usable I decided to make it Open Source to say “Thank You” to the community this way.

Learning that Trice is also a baby girl name, our daughter Ida designed the little girl with the pen symbolizing the Trice macro for recording and the eyeglasses standing for the PC tool Trice visualizing the logs.

./ref/TriceGirlS.png

(back to top)

3. How it works - the main idea

Trice performs no costly printf-like functions on the target at all. The Trice macro, instead, just copies an ID together with the optional values to a buffer and is done. In the minimum case this can happen in 6(six!) processor clocks even with target timestamps included. When running on a 64 MHz clock, light can travel about 30 meters in that time.

To achieve that, a pre-compile step is needed, executing a trice insert command on the PC. This is fast enough not to disturb the build process. The Trice tool parses then the source tree for macros like trice( "msg: %d Kelvin\n", k ); and patches them to trice( iD(12345), "msg: %d Kelvin\n", k );, where 12345 is a generated 14-bit identifier (ID) copied into a Trice ID List. During compilation than, the Trice macro is translated to the 12345 ID only, and the optional parameter values. The format string is ignored by the compiler.

The target code is project specific configurable. In direct mode the the stack or a static buffer is used as Trice buffer and the Trice macro execution includes optionally the quick COBS encoding and the data transfer. This more straightforward and slower architecture can be interesting for many cases because it is anyway much faster than printf-like functions calls. Especially when using Trice over RTT a single Trice is executable within ~100 processor clocks. See TRICE_DIRECT_SEGGER_RTT_32BIT_WRITE inside triceDefaultConfig.h and look into the examples folder. In deferred mode a service swaps the Trice double buffer or reads the Trice ring buffer periodically, the configured encoding, default is TCOBS, takes part and with the filled buffer the background transfer is triggered. Out buffer and Trice buffer share the same memory for efficiency.

During runtime the PC Trice tool receives all what happened in the last ~100ms as a package from the UART port. The 0x30 0x39 is the ID 12345 and a map lookup delivers the format string “msg: %d Kelvin\n” and also the bit width information. Now the Trice tool can write target timestamp, set msg color and execute printf("%d Kelvin\n", 0x0000000e);


./ref/triceCOBSBlockDiagram.svg

The Trice tool is a background helper giving the developer focus on its programming task. The once generated ID is not changed anymore without need. If for example the format string gets changed into "msg: %d Kelvin!\n", a new ID is inserted automatically and the reference list gets extended. Obsolete IDs are kept inside the Trice ID List for compatibility with older firmware versions. It could be possible, when merging code, an ID is used twice for different format strings. In that case, the ID inside the reference list wins and the additional source gets patched with a new ID. This maybe unwanted patching is avoidable with proper Trice ID management. The reference list should be kept under source code control.

Moreover, using trice i -cache && make && trice c -cache in a build script makes the IDs invisible to the developer reducing the data noise giving more space to focus on the development task. See build.sh as a working example and the Trice Cache chapter for details.

(back to top)

4. Trice Features (Overview)

4.1. Open source

Target code and PC tool are open source. The MIT license gives full usage freedom. Users are invited to support the further Trice development.

4.2. Easy-to-use

Making it facile for a user to use Trice was the driving point just to have

Trice understands itself as a silent helper in the background to give the developer more focus on its real task. If, for example, trice log is running and you re-flash the target, there is no need to restart the Trice tool. When til.json was updated in an pre-build step, the Trice tool automatically reloads the new data during logging.

The Trice tool comes with many command line switches (trice help -all) for tailoring various needs, but mostly these are not needed. In file ../internal/args/tricehelpall_test.go the expected test output contains this information as well.

Normal Trice tool usage is:

In this example, the user code gets not polluted with Trice IDs - they exists only during the compilation step and the Trice cache makes this invisible for the user and the build system.

4.3. Small size - using Trice frees FLASH memory

Compared to a printf-library code which occupies 1 to over 20 KB FLASH memory, the Trice code is normally smaller but provides full support.

4.4. Execution speed

Can it get faster than 6 clocks only? Only 3 runtime Assembler instructions per Trice needed in the minimum case! Optional target timestamp, critical sections, cycle counter, diagnostics and overflow protection can consume a few more processor clocks, if enabled, but a Trice is still incomparable fast.

4.5. Robustness

When a Trice data stream is interrupted, the optional COBS or TCOBS encoding allows an immediate re-sync with the next COBS/TCOBS package delimiter byte and a default Trice cycle counter gives a high chance to detect lost Trice messages. See also Versions and Variants Trice Stability.

4.6. Minimal Transfer Bytes Amount

A Trice message is 4 bytes long (2 ID bytes and 2 count bytes) plus optional time stamps and/or values. In conjunction with the compressing TCOBS framing the Trice data stream is as small as possible. Use the -debug switch to see the compressed and framed packages alongside the decompressed ones together with the decoded messages.

To see the encoding for each single message #define TRICE_DEFERRED_TRANSFER_MODE TRICE_SINGLE_PACK_MODE inside the project specific triceConfig.h.

Without -debug CLI switch:

ms@MacBook-Pro G0B1_inst % trice log -p /dev/tty.usbmodem0007722641261 -prefix off -li off -hs off -ts off
...
This is a message without values and without stamp.
...

With -debug CLI switch:

ms@MacBook-Pro G0B1_inst % trice log -p /dev/tty.usbmodem0007722641261 -prefix off -li off -hs off -ts off -debug
...
TCOBSv1: c1 74 e2 23 00 
->TRICE: c1 74 e2 00 
This is a message without values and without stamp.
...

The TCOBS encoding cannot compress in the example above, because the data are too small, but here is a significant compression result shown:

ms@MacBook-Pro G0B1_inst % trice log -p /dev/tty.usbmodem0007722641261 -prefix off -hs off -debug
...
TCOBSv1: b8 76 7b 18 84 fe e1 fd e1 fc e1 fb e1 fa e1 00 
->TRICE: b8 76 7b 18 ff ff ff ff fe ff ff ff fd ff ff ff fc ff ff ff fb ff ff ff fa ff ff ff 
_test/testdata/triceCheck.c   805              value=-1, -2, -3, -4, -5, -6
...

When encryption is active, a compression makes no sense, but the TRICE_MULTI_PACK_MODE can help to reduce the total amount of padding bytes, because each encrypted package must have a multiple of 8 as length.

ms@MacBook-Pro G0B1_inst % trice log -p /dev/tty.usbmodem0007722641261 -prefix off -hs off -pw MySecret -pf cobs -debug    
...
cobs: 21 84 b7 60 8b 21 89 1e e3 07 6d dc d9 2d 6f 59 04 8e 50 8f 24 1c a2 63 2e 3d 4a 57 ef 39 63 01 cb 00 
->TRICE: 84 b7 60 8b 21 89 1e e3 07 6d dc d9 2d 6f 59 04 8e 50 8f 24 1c a2 63 2e 3d 4a 57 ef 39 63 01 cb 
-> DEC:  cc b6 63 01 71 02 ff fe cd 76 72 03 ff fe fd ce f6 64 81 00 00 73 04 ff fe fd fc 00 00 00 00 00 
_test/testdata/triceCheck.c   827        0_355 value=-1, -2
_test/testdata/triceCheck.c   828              value=-1, -2, -3
_test/testdata/triceCheck.c   829    0,033_124 value=-1, -2, -3, -4
cobs: 19 50 70 79 d7 75 6f d7 99 dc d8 ec 06 e1 66 e7 a7 c1 0d 96 85 df 19 25 55 00 
->TRICE: 50 70 79 d7 75 6f d7 99 dc d8 ec 06 e1 66 e7 a7 c1 0d 96 85 df 19 25 55 
-> DEC:  cf b6 64 01 74 05 ff fe fd fc fb d0 76 75 06 ff fe fd fc fb fa 00 00 00 
_test/testdata/triceCheck.c   830        0_356 value=-1, -2, -3, -4, -5
_test/testdata/triceCheck.c   831              value=-1, -2, -3, -4, -5, -6
...

4.7. More comfort than printf-like functions but small differences

Trice is usable also inside interrupts and extended format specifier possibilities give options like binary or bool output. Transmitting runtime generated strings could be a need, so a triceS macro exists supporting the %s format specifier for strings up to 32737 bytes long. It is possible to log float/double numbers using %f and its relatives, but the numbers need to be covered with the fast converter function aFloat(x) or aDouble(y). Also UTF-8 encoded strings are implicit supported, if you use UTF-8 for the source code. See chapter Trice Similarities and differences to printf usage for more details.

./ref/UTF-8Example.PNG

4.8. Tags, Color and Log Levels

You can label each Trice with a tag specifier to colorize the output. This is free of any runtime costs because the tags are part of the Trice log format strings, which are not compiled into the target. The Trice tool will strip full lowercase tag descriptors from the format string after setting the appropriate color, making it possible to give each message its color.

Loggers use log levels and offer a setting like “log all above INFO” for example. The Trice tags can cover that but can do better: Inside package emitter.ColorChannels in a single file ./internal/emitter/lineTransformerANSI.go all common log levels defined as Trice tags alongside with user tags. The user can adjust this. The Trice tool has the -pick and -ban switches to control the display in detail. Also a -logLevel switch is usable to determine a display threshold as tag position inside ColorChannels.

If an inside-target log selection is needed (routing), the Trice tool can assign each log tag a separate ID range and a target side ID based log selector can control which IDs are transmitted over which output channel. See chapter Trice ID management or type trice help -insert and look for -IDRange.

./ref/COLOR_output.PNG

4.9. Compile Time Enable/Disable Trice Macros on File or Project Level

After debugging code in a file, there is no need to remove or comment out Trice macros. Write a #define TRICE_OFF 1 just before the #include "trice.h" line and all Trice 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.

#define TRICE_OFF 1 // Disable trice code generation for this file object.
#include "trice.h"

When you wish to build a firmware without any Trice code, it is sufficient to add

C_DEFS += -DTRICE_OFF=1 // Define TRICE_OFF=1 for the whole project.

or similar to your Makefile.

4.10. Target and host timestamps

For each Trice you can have (time) stamps or not:

The optional 16- or 32-bit value carry than the system clock, a millisecond second or an other event counter configured in the project specific triceConfig.h. The Trice tool will automatically recognize and display the stamps in a mode you can control. If several Trice macros form a single line, the Trice tool only displays the target timestamp of the first Trice macro.

Embedded devices often lack a real-time clock and some scenarios can last for weeks. Therefore the Trice tool precedes each Trice line with a PC timestamp, if not disabled. This is the Trice reception time on the PC, what can be some milliseconds later than the target Trice event.

4.11. Target source code location

Some developers like to see the filename.c and line in front of each log line for quick source location. During trice i a file li.json is generated containing the location information. If trice log finds this file, filename and line number are displayed in front of each log line, otherwise not.

Because software is a matter of change it could happen you get obsolete information this way. Therefore the Trice tool log option -showID exists to display the Trice ID in front of each log line what gives a more reliable way for event localization in some cases. Also you can get it for free, because no target code is needed for that.

4.12. Several target devices in one log output

Several Trice tool instances can run parallel on one or different PCs. Each Trice tool instance receives Trices from one embedded device. Instead of displaying the log lines, the Trice tool instances can transmit them over TCP/IP (trice l -p COMx -ds) to a Trice tool instance acting as display server (trice ds). The display server can fold these log lines in one output. For each embedded device a separate Trice line prefix and suffix is definable. This allows comparable time measurements in distributed systems.

4.13. Any byte capable 1-wire connection usable

The usual Trice output device is an UART but also SEGGER-RTT is supported over J-Link or ST-Link devices. Many micro controller boards can act as Trice bridge to a serial port from any port (Trice without UART).

4.14. Scalability

The various Trice ID management options allow the organization also of bigger software systems. 16383 possible different IDs should match also large projects. Just in case: 16-bit for the ID is a not to hard changeable value.

4.15. Portability and Modularity

The Trice tool is written in the open source language Go and is therefore usable on many platforms. That means the automatic code patching and ID handling side with trice insert.

All C-compilers should be usable to compile the target Trice code and there is no hardware dependency despite the byte transmission. MCUs with 8-bit to 64-bit, little or big endian are supported.

Any user program able to read a JSON file, can receive the documented Trice message format, look-up the ID and perform a printf-like action to translate into log strings. The Trice tool with its log switch is a working example.

Using no framing, COBS or TCOBS packages starting with a package descriptor allows alongside user protocols. The other way around is also implementable: In a user protocol embedded Trice messages.

The Trice tool is expandable with several decoders. So it is possible to implement a minimal Trice encoding, if bandwidth matters heavily and control that with switches.

When less RAM usage is more important the target double buffer is replaceable with a ring buffer. So the user will be able to decide at compile time about that. A ring buffer mode is selectable inside triceConfig.h avoiding any buffer by paying a time toll.

The Trice tool supports many command line switches.

4.16. Optional Trice messages encryption

The encryption opportunity makes it possible to test thoroughly a binary with log output and releasing it without the need to change any bit but to make the log output unreadable for a not authorized person. Implemented is the lightweight XTEA as option, what will do for many cases. It should be no big deal to add a different algorithm.

4.17. Trice Protection

When using Trice, data are written into buffers. A buffer overflow is impossible with the default configuration #define TRICE_PROTECT 1 by simply ignoring possible overflow causing Trice statements. Those cases are not detectable by the cycle counter evaluation because non-existing Trice data on the embedded system cannot cause cycle errors. Therefore overflow error counters exists, which the user can watch. In ./examples/exampleData/triceLogDiagData.c an option is shown. Of course this buffer overflow protection costs valuable execution time. If you prefer speed over protection, simply write into your project specific triceConfig.h #define TRICE_PROTECT 0.

4.18. Trice Diagnostics

A trice statement produces 4 bytes buffer data plus optional values data. When for example TRice16("Voltage=%u\n"), x); is called inside the ms system tick interrupt every 5th time, 10 bytes data are generated each 5 milliisecond. This needs a transfer baudrate of at least 20.000 bit/s. A UART running at 115.200 baud can easily handle that. Anyway after 100 ms, a 200 Bytes buffer is filled and the question arises what is the optimal Trice buffer size. A caclulation is error prone, so measuring is better. So configure the buffer sizes bigger than estimated and watch the max depth of their usage. In ./examples/exampleData/triceLogDiagData.c an option is shown. After you optimized your buffer sizes, you can deactivate the Trice diagnostics in your project specific triceConfig.h with #define TRICE_DIAGNOSTICS 0.

4.19. Trice Cache

One may think, automatically cleaning the IDs in the target code with trice c after building and re-inserting them just for the compilation needs file modifications all the time and a permanent rebuild of all files containing Trices will slow down the re-build process. That is true, but by using the Trice cache this is avoidable. Simply one-time create a .trice/cache folder in your home directory and use trice insert -cache and trice clean -cache in your build.sh script. More details you find in chapter Trice Cache for Compilation Speed.

4.20. Avoiding False-Positive Editor Warnings

When the user writes

trice("msg: Hello! 👋🙂\n");

after trice insert this gets

trice(iD(123), "msg: Hello! 👋🙂\n");

and the compiler builds and then with trice clean, this gets again

trice("msg: Hello! 👋🙂\n");

Sophisticated editors may detect the missing ID and warn by underlining the trice command:

x

To avoid this you can add the following line to your project specific triceConfig.h file:

#define TRICE_CLEAN 1

The Trice tool, will change the value to 0 and change it back to 1, when performing the ID insertion and cleaning, when this line occurs inside the triceConfig.h file. This way these false-positive editor warnings are avoidable:

x x

It is recommended to use the Trice cache in conjunction with this to avoid a permanent re-translation of files including Trice code.

TRICE_CLEAN==1 changes all Trice macros into empty ones. It is used only to silence sophisticated editors. In the cleaned state, when the IDs are removed from the files, the editor could underline the Trice macros indicating a false positive.

Do not use TRICE_CLEAN for disabling Trice macros. The triceConfig.h line #define TRICE_CLEAN 0 changes to 1 with every trice clean and to 0 with every trice insert. This line is optional and must not be in a different file. If you want to disable Trice macros use TRICE_OFF.

4.21. Trice Generator

The Trice tool is able to generate colors or code to support various tasks. One interesting option is the Remote Procedure Call support, allowing RPC usage in a network of embedded devices.

Read chapter Trice Generate or type:

trice help -generate

4.22. Versions and Variants Trice Stability

When developing firmware, we get often different versions and variants in the developing process. When, for example, getting an older device back, it could be, we do not know the flashed firmware version at all. Because the Trice tool adds only IDs and their Trices to the project specific til.json file, the complete development history remains in that file. So connecting an old device to the Trice tool will deliver correct output. Of course the location information will be outdated. But when reading the Trice logs the compiled version should get visible and it is no big deal to get the correspondenting li.json from the repository. If not, using the -showID "%6d" Trice log option displays the Trice IDs and you can easily grab the source code file and line.

4.23. Legacy Project Code Integration

When it comes to instrument a legacy project with Trice or to intergrate legacy project files into a Trice instrumented project different approaches are possible:

  1. Use for user specific log statements a different output channel. No special care has to be taken. This is maybe acceptable in some cases.
  2. Replace user specific log statements with Trice statements using a text processor and adapt the float, double or runtime strings handling manually. This is acceptable for small code amounts and when is is no problem to edit the legacy sources.
  3. Get the legacy output packages before transmitting them, add a 2-byte count in little-endian (0-16383) in front and frame them the same way the trice packages get framed (for example with COBS). This will set the 2 most significant bits to 00 and the Trice tool, can get informed via CLI switch to treat those packages accordingly. The user code containing specific logs will work unchanged together with Trice code over the same output channel.
  4. Take advantage of the new support for dynamic trice and triceS macro aliases (Legacy User Code Option: Trice Aliases Adaption](#legacy-user-code-option:-trice-aliases-adaption)).

(back to top)

5. Start with Trice

5.1. Get it

5.2. Install It

5.3. Try 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 Trice Similarities and Differences to printf Usage. 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.

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 Trice Cache for Compilation Speed for more details and examples/G1B1_inst/build.sh as example.

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 auxiliary interface.

5.4. Use It

(back to top)

5.5. Fork It (get a contributor)

If you wish to get a contributor please fork the Trice repository.

5.5.1. ✅ What “forking” means

Forking creates your own copy of someone else’s repository under your account.
You can then:

5.5.2. 🧭 How to Fork (GitHub)

1. Go to the repository you want to fork

Example: https://github.com/rokath/trice

2. Click the **“Fork” button (top-right)**

You’ll be taken to a Create Fork page.

3. Choose options (usually leave defaults)

Click Create Fork.

4. Clone your fork locally

git clone https://github.com/YOUR_USERNAME/trice.git && cd trice

5. (Optional but recommended) Add the original repo as upstream

This lets you pull updates later.

git remote add upstream https://github.com/rokath/trice.git

Check remotes:

git remote -v

6. Keep your fork updated

git fetch upstream git merge upstream/main

Or:

git pull upstream main

5.6. Clone It

1. Make sure Git is installed

Check with:

git --version

If not installed, download from https://git-scm.com

2. Clone the repository

Run this command in your terminal or command prompt:

git clone https://github.com/rokath/trice.git

This creates a local folder named trice with the full project history.

3. (Optional) Enter the project folder

cd trice

5.7. Build It

See Build Trice tool from Go sources.

5.8. Modify It

If for example you wich to change the logging capablilities, like changing/extending CLI switches, thanks to Go this is very easy also if you are not familar with Go. See this example.

5.9. 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 Remarks
./examples/F030_bare ./examples/F030_inst no RTOS
./examples/G0B1_bare ./examples/G0B1_inst FreeRTOS
./examples/L432_bare ./examples/L432_inst FreeRTOS

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

The chapter Example Projects without and with Trice Instrumentation contains further helpful information.

5.9.1. Target Macros

The easiest and mostly sufficient way to use Trice on the target side is the Trice macro

trice("Hello world!"); // without     time stamp
Trice("Hello world!"); // with 16-bit time stamp
TRice("Hello world!"); // with 32-bit time stamp

which you can mostly use as a printf replacement in legacy code. See Trice Similarities and differences to printf usage for more details. Is uses the TRICE_DEFAULT_PARAMETER_BIT_WIDTH value (usually 32), which is equal for all values.

The additional macros

are always usable and the number 8, 16, 32, 64 specifies the parameter width, which is equal for all values within one macro. Trice macros 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.

5.9.2. Target Trice Stamps

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

5.9.3. Trice Checks

5.9.4. Communication Ports

5.9.5. Target Code Overview

File description
trice.h trice runtime lib user interface, #include trice.h in project files, where to use Trice macros. Add ./src to your compiler include path.
triceConfig.h Create this file to overwrite triceDefaultConfig.h as needed.
File description
cobs.h message packaging, alternatively for tcobs
cobsEncode.c message encoding, alternatively for tcobs
cobsDecode.c message decoding, normally not needed
trice.c trice core lib
trice8McuOrder.h trice MCU endianness lib
trice8McuReverse.h trice MCU reverse endianness lib
trice16McuOrder.h trice MCU endianness lib
trice16McuReverse.h trice MCU reverse endianness lib
trice32McuOrder.h trice MCU endianness lib
trice32McuReverse.h trice MCU reverse endianness lib
trice64McuOrder.h trice MCU endianness lib
trice64McuReverse.h 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
triceDefaultConfig.h This file contains the most probably settings and serves also as a reference for tuning your project triceConfig.h
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.h XTEA message encryption/decryption interface
xtea.c XTEA message encryption/decryption code

(back to top)

5.9.6. User Code Adaption

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

5.9.7. Limitations

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 should be part of the Trice format string.

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

5.9.8. Trice (Time) Stamps

5.9.9. Trice Parameter Bit Widths

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

5.10. Avoid it

5.10.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:

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");

5.10.2. Trice macros in header files

5.10.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.

Example: When Functions fnA and fnB are executed, the MY_MESSAGE location information points to file.h and not into the appropriate lines inside file.c.

file.h:

#define MY_MESSAGE trice("msg:Hi\n"); // self made macro

file.c:

void fnA( void ){
  ...
  MY_MESSAGE
  ...
}

void fnB( void ){
  ...
  MY_MESSAGE
  ...
}

5.10.4. Upper case only TRICE macros should be written with id(0), Id(0) or ID(0)

The stamp size 0, 16 or 32 is usually controlled by writing trice, Trice or TRICE or for upper case only Trice macros by using id(0), Id(0) or ID(0). When wrting TRICE("hi"); for example, the Trice CLI switch -defaultStampSize controls the ID insertion, but this is then equal for all new TRICE messages.

(back to top)

6. Trice Trouble Shooting Hints

6.1. Initial Data Transfer Setup Hints

If you do not succeed initially, you can try this:

triceConfig.h:

#define TriceStamp32 0x44434241 // a fixed value

#define TRICE_DIRECT_OUT_FRAMING TRICE_FRAMING_NONE   // default
#define TRICE_DEFERRED_OUT_FRAMING TRICE_FRAMING_NONE // no framing to interpret the byte stream manually

main.c:

int main( void) {
    // system init...
    TriceInit();
    TRice(iD(255), "Fun %x!\n", 0xadded ); // with "fixed" iD(255), 32-bit stamp, and with `\n`
    // system run ...
}
trice log -s -port com1 -v -ts32="att:%08x fix" # enter this (adapted)
#       /-------------------------------------- ID low byte (170)
#       |  /----------------------------------- ID high byte (6 bits=0) with 2 most significant bits set (32-bit stamp follows)
#       |  |       /--------------------------- 32-bit (time) stamp
#       |  |       |      /-------------------- initial cycle counter: 192
#       |  |       |      |  /----------------- payload size
#       |  |       |      |  |       /--------- payload (0x00added) 
#       |  |       |      |  |       |      / - 0-delimiter or next Trice
#       |  |       |      |  |       |      |
#       v  v  vvvvvvvvvvv v  v  vvvvvvvvvvv v
# Input(aa c0 41 42 43 44 c0 04 ed dd 0a 00 ... ) # expected byte stream
# ...
#              main.c    84 44434241 fix   170 Fun added!
# ...

6.2. Short Trouble Shooting Hints

Problem Hint
Missing objcopy in MacOS brew install bunutils
Small GUI Editor for MacOs brew install cotedit Usage: cot (not as root)
Small In-Terminal Editor Linux https://cte.e10labs.com/, tilde, micro, joe, https://craigbarnes.gitlab.io/dte/ (also as root)

(back to top)

7. Trice Cache for Compilation Speed

The trice insert and trice clean commands are parsing and modifying the source code files. Even this is a reasonable fast procedure, this could get time consuming on large projects, especially when using these commands as permanent pre-compile and post-compile steps. It is assumed, that usually between 2 compile steps not all project files are changed. The project files majority will stay unchanged despite the ID insertion and removal. This repeated parsing and modifying of unchanged source code is avoidable with the Trice cache technique. Also it could get annoying to recompile files all the time only because they got Trice IDs removed and inserted. With the Trice cache we get also a solution not to re-compile un-edited files as well.

7.1. Trice Cache Idea

Lets talk about just one source file $HOME/my/src/foo.c and imagine we process many in one shot.

7.2. Trice Cache Logic

When id.TriceCacheEnabled is true (applied -cache CLI switch) and the folder ~/.trice/cache exists, we have

7.3. Trice Cache Remarks

Should the .trice/cache be better located inside the project folder? What, if the user has several projects and several users on the same machine working on projects together? What about libraries containing trice code?

7.4. Trice Cache Tests

Nr Action cCache iCache ID state Edid state Test function
0,1 0:clean 0:inval 0:inval 0:cleaned X:any Test_0_1_0000X_clean_on_invalid_cCache_invalid_iCache_cleaned_file
2,3 0:clean 0:inval 0:inval 1:inserted X:any Test_2_3_00011_clean_on_inalid_cCache_invalid_iCache_inserted_edited_file
4,5 0:clean 0:inval 1:valid 0:cleaned X:any Test_4_5_0010X_clean_on_invalid_cCache_valid_iCache_cleaned_file
6 0:clean 0:inval 1:valid 1:inserted 0:not Test_6_00110_clean_on_invalid_cCache_valid_iCache_inserted_not_edited_file
7 0:clean 0:inval 1:valid 1:inserted 1:yes Test_7_00111_clean_on_invalid_cCache_valid_iCache_inserted_edited_file
8 0:clean 1:valid 0:inval 0:cleaned 0:not Test_8_01000_clean_on_valid_cCache_invalid_iCache_cleaned_not_edited_file
9 0:clean 1:valid 0:inval 0:cleaned 1:yes Test_9_01001_clean_on_valid_cCache_invalid_iCache_cleaned_edited_file
10 0:clean 1:valid 0:inval 1:inserted 0:not Test_10_01011_clean_on_valid_cCache_invalid_iCache_inserted_not_edited_file
11 0:clean 1:valid 0:inval 1:inserted 1:yes Test_11_01011_clean_on_valid_cCache_invalid_iCache_inserted_edited_file
12 0:clean 1:valid 1:valid 0:cleaned 0:not Test_12_01100_clean_on_valid_iCache_valid_cCache_clean_file_not_edited
13 0:clean 1:valid 1:valid 0:cleaned 1:yes Test_13_01101_clean_on_valid_iCache_valid_cCache_clean_file_edited
14 0:clean 1:valid 1:valid 1:inserted 0:not Test_14_01110_clean_on_valid_iCache_valid_cCache_inserted_file_not_edited
15 0:clean 1:valid 1:valid 1:inserted 1:yes Test_15_01111_clean_on_valid_iCache_valid_cCache_inserted_file_edited
16,17 1:insert 0:inval 0:inval 0:cleaned X:any Test_16_17_1000X_insert_on_invalid_cCache_invalid_iCache_cleaned_file
18,19 1:insert 0:inval 0:inval 1:inserted X:any Test_18_19_1001X_insert_on_invalid_cCache_invalid_iCache_inserted_edited_file
20,21 1:insert 0:inval 1:valid 0:cleaned X:any Test_20_21_1010X_insert_on_invalid_cCache_valid_iCache_cleaned_file
22 1:insert 0:inval 1:valid 1:inserted 0:not Test_22_10100_insert_on_invalid_cCache_valid_iCache_inserted_not_edited_file
23 1:insert 0:inval 1:valid 1:inserted 1:yes Test_23_10101_insert_on_invalid_cCache_valid_iCache_inserted_edited_file
24 1:insert 1:valid 0:inval 0:cleaned 0:not Test_24_11000_insert_on_valid_cCache_invalid_iCache_cleaned_not_edited_file
25 1:insert 1:valid 0:inval 0:cleaned 1:yes Test_25_11001_insert_on_valid_cCache_invalid_iCache_cleaned_edited_file
26,27 1:insert 1:valid 0:inval 1:inserted X:any Test_26_27_1010X_insert_on_invalid_cCache_valid_iCache_cleaned_file
28 1:insert 1:valid 1:valid 0:cleaned 0:not Test_28_11100_insert_on_valid_cCache_valid_iCache_cleaned_not_edited_file
29 1:insert 1:valid 1:valid 0:cleaned 1:yes Test_29_11100_insert_on_valid_cCache_valid_iCache_cleaned_edited_file
30 1:insert 1:valid 1:valid 1:inserted 0:not Test_30_11110_insert_on_valid_cCache_valid_iCache_inserted_not_edited_file
31 1:insert 1:valid 1:valid 1:inserted 1:yes Test_31_11111_insert_on_valid_cCache_valid_iCache_inserted_edited_file

7.5. Possible Trice Cache Editor-Issues And How To Get Around

7.6. Activating the Trice Cache

mkdir -p ~/.trice/cache

(back to top)

8. Embedded system code configuration

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

A Trice macro is avoiding all the printf() internal overhead (space and time) but is nearly as easy to use. For example instead of writing

printf("time is %d:%d:%d\n", hour, min, sec);

you can write

trice8("time is %d:%d:%d\n", hour, min, sec);

into a source file of your project. The 8 stands here for 8 bit values (16, 32 and 64 also possible). Values of mixed size up to 32-bit size are allowed in one trice macro, so you can use Trice consequently to match most cases for the prize of little data overhead.

(back to top)


9. 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)

10. Encryption

(back to top)

11. Trice Command Line Interface & Examples

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 a single parameter string 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 help -log for example.

11.1. Common information

11.2. Further examples

11.2.1. Automated pre-build insert command example

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.

11.2.2. Some Log examples

trice log -i ./myProject/til.json -p=COM3
trice l -s COM3 -baud=9600

11.2.3. Logging over a display server

trice ds
trice l -ds -p COM3
trice sd -r 192.168.1.23:45678

The IP address and port are free selectable. Using a display server, allows to watch the logs of one or many MCUs on a local or remote machine with the same or different display servers.

A local Trice instance sends Trice messages to a display server only, when a log line is complete (if consisting of several Trices). By using the CLI switches -prefix and -suffix you can decorate the loglines target specific to distinguish them in the output window(s).

##13.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.

11.2.4. 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.

11.2.5. 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

11.2.6. TCP4 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.

11.2.7. UDP4 input (accepted pull request 529)

The pull request #529 introduces key enhancement:

    IPv4 UDP Receiver
    Adds support for receiving data over IPv4 using UDP. This enables integration with systems that broadcast or transmit telemetry, logs, or other messages over the network.

-port UDP4 Example

To receive Trice logs over IPv4 UDP, use the -port UDP4 option. By default, it listens on 0.0.0.0:17005, which accepts packets on all network interfaces. You can specify a different address or multicast group via -args.

trice log -p UDP4

11.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

11.2.9. Explpore and modify tags and their colors

See chapter Trice Tags and Color.

11.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)

12. Limitations

12.1. Permanent Limitations

12.1.1. Limitation TRICE in TRICE not possible

int f0( void ){ TRICE( "msg:f0\n"); return 0; }
void f1( void ){ TRICE( "No; %d", f0() ); }

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.

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

12.2. Current Limitations

12.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.

12.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:

void trice0_test() {
    Trice0( "OK");
    Trice( InvalidUse );
    Trice( "%u", Variable );
}
void trice0_test() {
    Trice0( iD(2740), "OK"); // ok, iD is added
    Trice( InvalidUse ); // no warning or error
    Trice( "%u", Variable ); // id is not added / inserted
}

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

12.2.3. Special Care Demands

More than 12 printf parameters
Float Numbers
Double numbers
Runtime Generated Strings

The triceS macro is ment to be used with strings not known at compile time.

Usage intention and recommendation: (given by @escherstair)

char runtime_string[50];
fillRuntimeStringFromSomewhere(runtime_string); // the content of runtime_string is filled at run time
triceS( "msg:This part of the string is known at compile time. This part is dynamic: %s\n", runtime_string);

All the string literals (i.e. compile-time known strings) should be put inside the format string. Only the runtime strings should be used as variables in triceS macro for best performance.

(back to top)

13. Additional hints

13.1. Pre-built executables are available

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

13.2. Configuration file triceConfig.h

13.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.

13.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 ./examples/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.

13.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|16|32|64-bit value) - default is #define TRICE_B TRICE8_B.

13.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.

13.7. Using the Trice tool with 3rd party tools

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

13.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.

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

13.9. 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.

13.10. Cycle Counter

(back to top)

14. Switching Trice ON and OFF

14.1. Target side compile-time Trice On-Off

#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.

(back to top)

14.2. Host side Trice On-Off

(back to top)

15. Framing

(back to top)

16. Optional XTEA Encryption

(back to top)

17. Endianness

(back to top)

18. Trice (Time)Stamps

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.

18.1. Target (Time)Stamps Formatting

To get a short overview run trice help -log and read about the CLI switches ts, ts0, ts16, ts32. The ts32 switch supports also “epoch” now as format. That is useful for example, if the binary logs are stored internally in the device flash and read out later. Such usage assumes 1 second as ts32 unit in uint32_t format and the Trice tool displays the UTC time. It is also possible to adapt the displayed format like this for example: trice log -ts32='epoch"06-01-02_15:04:05"'. The additional passed string must match the Go time package capabilities. A few examples:

trice log -port FILEBUFFER -args myLogs.bin -ts32='"Mon Jan _2 15:04:05 2006"'             # ANSIC   
trice log -port FILEBUFFER -args myLogs.bin -ts32='"Mon Jan _2 15:04:05 MST 2006"'         # UnixDate    
trice log -port FILEBUFFER -args myLogs.bin -ts32='"Mon Jan 02 15:04:05 -0700 2006"'       # RubyDate      
trice log -port FILEBUFFER -args myLogs.bin -ts32='"02 Jan 06 15:04 MST")'                 # RFC822    
trice log -port FILEBUFFER -args myLogs.bin -ts32='"02 Jan 06 15:04 -0700")'               # RFC822Z     (RFC822 with numeric zone)     
trice log -port FILEBUFFER -args myLogs.bin -ts32='"Monday, 02-Jan-06 15:04:05 MST")'      # RFC850    
trice log -port FILEBUFFER -args myLogs.bin -ts32='"Mon, 02 Jan 2006 15:04:05 MST")'       # RFC1123     
trice log -port FILEBUFFER -args myLogs.bin -ts32='"Mon, 02 Jan 2006 15:04:05 -0700")'     # RFC1123Z    (RFC1123 with numeric zone)        
trice log -port FILEBUFFER -args myLogs.bin -ts32='"2006-01-02T15:04:05Z07:00")'           # RFC3339    
trice log -port FILEBUFFER -args myLogs.bin -ts32='"2006-01-02T15:04:05.999999999Z07:00")' # RFC3339Nano        
trice log -port FILEBUFFER -args myLogs.bin -ts32='"3:04PM")'                              # Kitchen    

After the year 2106 the Trice tool needs a small modification to correctly compute the epoch time then. Probably I will not be alive anymore to do that then, but, hey, Trice is Open Source!

(back to top)

19. Binary Encoding

19.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

19.2. Package Format

19.2.1. typeX0 Trices

The user can insert any data with a well-defined structure into the Trice data stream. The Trice tool, when interpreting the Trice binary data, will behave on typeX0 Trices according to a passed CLI switch -typeX0.

One possible use case is to have user printi statements parallel to Trices (see Legacy User Code Option Print Buffer Wrapping and Framing. The user needs to prepend a generated printi buffer with its size as 16-bit count (<16384!) for example. See ./_test/userprint_dblB_de_tcobs_ua/TargetActivity.c for an implementation option.

19.2.2. Framing - NONE or with COBS or TCOBS encoding

Summary Information for Trice Data Parsing

Details

Framing NONE Overview Table:

mode packed -pf= encr wr use pad stream remark
di single none32 NONE 32 32 0-3 aligned done
de single none8 NONE 8 8 0 compact done
de single none32 NONE 8 32 0 aligned plan
de multi none8 NONE 8 8 0 compact done
de multi none32 NONE 8 32 0 unknown forbid
de multi none NONE 8 32 0 unknown forbid
di single none64 XTEA 32 32 0-7 aligned done
de single none64 XTEA 8 8 0-7 aligned done
de single none64 XTEA 8 32 0-7 aligned plan
de multi none XTEA 8 8 0-7 unknown forbid
de multi none XTEA 8 32 0-7 unknown forbid

(back to top)

20. Trice Decoding

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

20.1. Trice ID list til.json

20.2. Trice location information file li.json

(back to top)

21. Trice ID Numbers

21.1. ID number selection

21.1.1. Trice tool internal Method to get fast a random ID

21.2. ID number usage and stability

21.3. Trice ID 0

(back to top)

22. Trice ID management

22.1. Trice inside source code

22.1.1. Trice in source code comments

22.1.2. Different IDs for same Trices

22.1.3. Same IDs for different Trices

22.1.4. ID Routing

With the Trice insert CLI switch -IDRange each Trice tag can get a specific ID range assigned and inside the project specific triceConfig.h the user can control, which ID range is routed to specific output channels. Search for _MIN_ID inside triceDefaultConfig.h and extend your search than for example TRICE_UARTA_MIN_ID to explore how to use.

22.1.5. Possibility to create new tags without modifying trice tool source

According to the demand in 541 a CLI switch -ulabel exists now.

Use -ulabel for additional user labels. Try this example in an empty folder:

#include "trice.h"

int main(void){
    trice("msg:hi\n");
    trice("man:hi\n");
    trice("wife:hi\n");
    trice("any:hi\n");
}
touch til.json li.json
trice i -IDMin 1004 -IDMax 6999 -IDRange wife:16000,16009 -IDRange man:1000,1003 -ulabel man -ulabel wife
#include "trice.h"

int main(void){
    trice(iD(5778), "msg:hi\n");
    trice(iD(1002), "man:hi\n");
    trice(iD(16004), "wife:hi\n");
    trice(iD(2184), "any:hi\n");
}

(back to top)

23. Trice version 1.0 Log-level Control

23.1. Trice version 1.0 Compile-time Log-level Control

In Trice version 1.0 is no compile-time log-level control. You can only disable all Trice logs

23.2. Trice version 1.0 Run-time Log-level Control

Because the target Trice code is so fast and generates only a few bytes per log, in Trice version 1.0 is no direct run-time log-level control inside the target code. The user has the Trice CLI switches -ban, -pick and -logLevel, to control, which Trice messages are displayed by the Trice tool.

23.3. Trice Version 1.0 Compile-time - Run-time Log-level Control

During compilation the developer can control which Trice tags, like info in trice( "info:...\n"); get get which ID range. Look for -IDRange in trice h -i output. By defining values like TRICE_UARTA_MIN_ID in the project specific triceConfig.h during compile-time is controllable, which Trice tags get routed to an output device or not.

(back to top)

24. ID reference list til.json

24.1. til.json Version control

--> 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 write a small bash script similar to this (untested):

trice insert -cache # Insert the IDs into the source code.
git restore til.json # Forget the todays garbage.

# Add the todays IDs to the restored til.json and clean the code.
# We have to deactivate the cache to force the file processing to get the new IDs into til.json.
trice clean # Remove the IDs from the source code with deactivated cache.  

24.2. Long Time Availability

(back to top)

25. The Trice Insert Algorithm

25.1. Starting Conditions

@@ To understand this chapter you should look into the Trice tool source code. @@

25.2. Aims

25.3. Method

25.3.1. Trice 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.
}

Until here the algorithm seem to be ok.

25.4. User Code Patching (trice insert)

25.5. User Code Patching Examples

25.6. Exclude folders & files from being parsed (pull request 529)

The pull request #529 introduces key enhancement:

    -exclude Flag
    Introduces a command-line flag -exclude that allows users to specify one or more source addresses to be omitted from scanning or processing. This improves flexibility in environments with known noisy or irrelevant sources.

-exclude Flag Example

The -exclude flag can be used multiple times to omit specific files or directories from scanning. Wildcards are not supported.

trice insert -v -src ./_test/ -exclude _test/src/trice.h -exclude _test/generated/

25.7. ID Usage Options

25.8. General ID Management Information

25.8.1. Option Cleaning in a Post-build process

25.8.2. Option Let the inserted Trice ID be a Part of the User Code

25.8.3. Option Cleaning on Repository Check-In

(back to top)

26. Trice Speed

A Trice macro execution can be as cheap like 3 Assembler instructions or 6 processor clocks:

A more realistic (typical) timing with target location and µs timestamps, critical section and parameters is shown here with the STM32F030 M0 core:

./ref/F030FullTiming.PNG

The MCU is clocked with 48 MHz and a Trice duration is about 2 µs, where alone the internal ReadUs() call is already nearly 1 µs long:

./ref/ReadUsF030.PNG

26.1. Target Implementation Options

All trice macros use internally this sub-macro:

#define TRICE_PUT(x) do{ *TriceBufferWritePosition++ = TRICE_HTOTL(x); }while(0); //! PUT copies a 32 bit x into the TRICE buffer.

The usual case is #define TRICE_HTOTL(x) (x). The uint32_t* TriceBufferWritePosition points to a buffer, which is codified and used with the Trice framing sub-macros TRICE_ENTER and TRICE_LEAVE in dependence of the use case.

26.1.1. Trice Use Cases TRICE_STATIC_BUFFER and TRICE_STACK_BUFFER - direct mode only

  1. Each singe Trice is build inside a common buffer and finally copied inside the sub-macro TRICE_LEAVE.
  2. Disabled relevant interrupts between TRICE_ENTER and TRICE_LEAVE are mantadory for TRICE_STATIC_BUFFER.
  3. Usable for multiple non-blocking physical Trice channels but not recommended for some time blocking channels.
  4. A copy call is executed inside TRICE_LEAVE.

26.1.2. Trice Use Case TRICE_DOUBLE_BUFFER - deferred mode, fastest Trice execution, more RAM needed

  1. Several trices are build in a half buffer.
  2. No stack used.
  3. Disabled interrupts between TRICE_ENTER and TRICE_LEAVE.
  4. Usable for multiple blocking and non-blocking physical Trice channels.
  5. No copy call inside TRICE_LEAVE but optionally an additional direct mode is supported.

26.1.3. Trice Use Case TRICE_RING_BUFFER - deferred mode, balanced Trice execution time and needed RAM

  1. Each single trices is build in a ring buffer segment.
  2. No stack used.
  3. Disabled interrupts between TRICE_ENTER and TRICE_LEAVE.
  4. Usable for multiple blocking and non-blocking physical Trice channels.
  5. No copy call inside TRICE_LEAVE but optionally an additional direct mode is supported.
  6. Allocation call inside TRICE_ENTER

26.2. A configuration for maximum Trice execution speed with the L432_inst example

#define TriceStamp16 (*DWT_CYCCNT) // @64MHz wraps after a bit more than 1ms (MCU clocks)
#define TriceStamp32 (*DWT_CYCCNT) // @64MHz -> 1 µs, wraps after 2^32 µs ~= 1.2 hours

#define TRICE_DEFERRED_UARTA 1
#define TRICE_UARTA USART2

#define TRICE_DEFERRED_OUTPUT 1
#define TRICE_BUFFER TRICE_DOUBLE_BUFFER

#define TRICE_PROTECT 0
#define TRICE_DIAGNOSTICS 0
#define TRICE_CYCLE_COUNTER 0

After running ./build.sh, executing ` arm-none-eabi-objdump.exe -D -S -l out.clang/triceExamples.o` shows:

out.clang/triceExamples.o:     file format elf32-littlearm


Disassembly of section .text.TriceHeadLine:

00000000 <TriceHeadLine>:
TriceHeadLine():
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:10
#include "trice.h"

//! TriceHeadLine emits a decorated name. The name length should be 18 characters.
void TriceHeadLine(char * name) {
	//! This is usable as the very first trice sequence after restart. Adapt it. Use a UTF-8 capable editor like VS-Code or use pure ASCII.
	TriceS("w: Hello! 👋🙂\n\n        ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨        \n        🎈🎈🎈🎈%s🎈🎈🎈🎈\n        🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃        \n\n\n", name);
   0:	f240 0100 	movw	r1, #0
   4:	4602      	mov	r2, r0
   6:	f2c0 0100 	movt	r1, #0
   a:	f643 70f1 	movw	r0, #16369	@ 0x3ff1
   e:	f7ff bffe 	b.w	0 <TriceS>

Disassembly of section .ARM.exidx.text.TriceHeadLine:

00000000 <.ARM.exidx.text.TriceHeadLine>:
   0:	00000000 	andeq	r0, r0, r0
   4:	00000001 	andeq	r0, r0, r1

Disassembly of section .text.SomeExampleTrices:

00000000 <SomeExampleTrices>:
SomeExampleTrices():
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:14
}

//! SomeExampleTrices generates a few Trice example logs and a burst of Trices.
void SomeExampleTrices(int burstCount) {
   0:	b5f0      	push	{r4, r5, r6, r7, lr}
   2:	af03      	add	r7, sp, #12
   4:	e92d 0700 	stmdb	sp!, {r8, r9, sl}
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:15
	TRICE(ID(0), "att:🐁 Speedy Gonzales A  32-bit time stamp\n");
   8:	f240 0100 	movw	r1, #0
   c:	f2c0 0100 	movt	r1, #0
  10:	680d      	ldr	r5, [r1, #0]
  12:	f240 0800 	movw	r8, #0
  16:	f2c0 0800 	movt	r8, #0
  1a:	4604      	mov	r4, r0
  1c:	6829      	ldr	r1, [r5, #0]
  1e:	f8d8 0000 	ldr.w	r0, [r8]
  22:	f06f 0212 	mvn.w	r2, #18
  26:	8041      	strh	r1, [r0, #2]
  28:	0c09      	lsrs	r1, r1, #16
  2a:	1cd3      	adds	r3, r2, #3
  2c:	8081      	strh	r1, [r0, #4]
  2e:	21c0      	movs	r1, #192	@ 0xc0
  30:	8003      	strh	r3, [r0, #0]
  32:	80c1      	strh	r1, [r0, #6]
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:16
	TRICE(ID(0), "att:🐁 Speedy Gonzales B  32-bit time stamp\n");
  34:	682b      	ldr	r3, [r5, #0]
  36:	1c96      	adds	r6, r2, #2
  38:	8143      	strh	r3, [r0, #10]
  3a:	0c1b      	lsrs	r3, r3, #16
  3c:	8106      	strh	r6, [r0, #8]
  3e:	8183      	strh	r3, [r0, #12]
  40:	81c1      	strh	r1, [r0, #14]
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:17
	TRICE(ID(0), "att:🐁 Speedy Gonzales C  32-bit time stamp\n");
  42:	682b      	ldr	r3, [r5, #0]
  44:	1c56      	adds	r6, r2, #1
  46:	8243      	strh	r3, [r0, #18]
  48:	0c1b      	lsrs	r3, r3, #16
  4a:	8206      	strh	r6, [r0, #16]
  4c:	8283      	strh	r3, [r0, #20]
  4e:	82c1      	strh	r1, [r0, #22]
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:18
	TRICE(ID(0), "att:🐁 Speedy Gonzales D  32-bit time stamp\n");
  50:	682b      	ldr	r3, [r5, #0]
  52:	8302      	strh	r2, [r0, #24]
  54:	0c1a      	lsrs	r2, r3, #16
  56:	8343      	strh	r3, [r0, #26]
  58:	8382      	strh	r2, [r0, #28]
  5a:	83c1      	strh	r1, [r0, #30]
  5c:	f64b 7ad4 	movw	sl, #49108	@ 0xbfd4
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:19
	TRICE(Id(0), "att:🐁 Speedy Gonzales E  16-bit time stamp\n");
  60:	682a      	ldr	r2, [r5, #0]
  62:	f6cb 7ad4 	movt	sl, #49108	@ 0xbfd4
  66:	f10a 1318 	add.w	r3, sl, #1572888	@ 0x180018
  6a:	6203      	str	r3, [r0, #32]
  6c:	8482      	strh	r2, [r0, #36]	@ 0x24
  6e:	84c1      	strh	r1, [r0, #38]	@ 0x26
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:20
	TRICE(Id(0), "att:🐁 Speedy Gonzales F  16-bit time stamp\n");
  70:	682a      	ldr	r2, [r5, #0]
  72:	f10a 1317 	add.w	r3, sl, #1507351	@ 0x170017
  76:	6283      	str	r3, [r0, #40]	@ 0x28
  78:	8582      	strh	r2, [r0, #44]	@ 0x2c
  7a:	85c1      	strh	r1, [r0, #46]	@ 0x2e
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:21
	TRICE(Id(0), "att:🐁 Speedy Gonzales G  16-bit time stamp\n");
  7c:	682a      	ldr	r2, [r5, #0]
  7e:	f10a 1316 	add.w	r3, sl, #1441814	@ 0x160016
  82:	6303      	str	r3, [r0, #48]	@ 0x30
  84:	8682      	strh	r2, [r0, #52]	@ 0x34
  86:	86c1      	strh	r1, [r0, #54]	@ 0x36
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:22
	TRICE(Id(0), "att:🐁 Speedy Gonzales H  16-bit time stamp\n");
  88:	682a      	ldr	r2, [r5, #0]
  8a:	f10a 1315 	add.w	r3, sl, #1376277	@ 0x150015
  8e:	8782      	strh	r2, [r0, #60]	@ 0x3c
  90:	f647 72e8 	movw	r2, #32744	@ 0x7fe8
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:23
	TRICE(id(0), "att:🐁 Speedy Gonzales I without time stamp\n");
  94:	f8a0 2040 	strh.w	r2, [r0, #64]	@ 0x40
  98:	f647 72e7 	movw	r2, #32743	@ 0x7fe7
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:24
	TRICE(id(0), "att:🐁 Speedy Gonzales J without time stamp\n");
  9c:	f8a0 2044 	strh.w	r2, [r0, #68]	@ 0x44
  a0:	f647 72e6 	movw	r2, #32742	@ 0x7fe6
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:25
	TRICE(id(0), "att:🐁 Speedy Gonzales K without time stamp\n");
  a4:	f8a0 2048 	strh.w	r2, [r0, #72]	@ 0x48
  a8:	f647 72e5 	movw	r2, #32741	@ 0x7fe5
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:22
	TRICE(Id(0), "att:🐁 Speedy Gonzales H  16-bit time stamp\n");
  ac:	6383      	str	r3, [r0, #56]	@ 0x38
  ae:	87c1      	strh	r1, [r0, #62]	@ 0x3e
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:23
	TRICE(id(0), "att:🐁 Speedy Gonzales I without time stamp\n");
  b0:	f8a0 1042 	strh.w	r1, [r0, #66]	@ 0x42
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:24
	TRICE(id(0), "att:🐁 Speedy Gonzales J without time stamp\n");
  b4:	f8a0 1046 	strh.w	r1, [r0, #70]	@ 0x46
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:25
	TRICE(id(0), "att:🐁 Speedy Gonzales K without time stamp\n");
  b8:	f8a0 104a 	strh.w	r1, [r0, #74]	@ 0x4a
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../exampleData/triceExamples.c:26
	TRICE(id(0), "att:🐁 Speedy Gonzales L without time stamp\n");
  bc:	f8a0 204c 	strh.w	r2, [r0, #76]	@ 0x4c
  c0:	f8a0 104e 	strh.w	r1, [r0, #78]	@ 0x4e
TRice0():
C:\Users\ms\repos\trice_wt_devel\examples\L432_inst/../../src/trice.h:741
	Trice32m_0(tid);
	TRICE_UNUSED(pFmt)
}

...

2024-12-05_L432_inst_maxSpeed.png

As you can see in the highlighted blue timestamp bar, typical 8-10 clocks are needed for one Trice macro. One clock duration @64MHz is 15.625 ns, so we need about 150 ns for a Trice. Light can travel about 50 meter in that time.

26.3. A configuration for normal Trice execution speed with the G0B1_inst example

// hardware specific trice lib settings
#include "main.h"
#define TriceStamp16 TIM17->CNT     // 0...999 us
#define TriceStamp32 HAL_GetTick()  // 0...2^32-1 ms (wraps after 49.7 days)

#define TRICE_BUFFER TRICE_RING_BUFFER

// trice l -p JLINK -args="-Device STM32G0B1RE -if SWD -Speed 4000 -RTTChannel 0" -pf none  -d16 -ts ms
//#define TRICE_DIRECT_OUTPUT 1
//#define TRICE_DIRECT_SEGGER_RTT_32BIT_WRITE 1

// trice log -p com7 -pw MySecret -pf COBS
#define TRICE_DEFERRED_OUTPUT 1
#define TRICE_DEFERRED_XTEA_ENCRYPT 1
#define TRICE_DEFERRED_OUT_FRAMING TRICE_FRAMING_COBS
#define TRICE_DEFERRED_UARTA 1
#define TRICE_UARTA USART2

#include "cmsis_gcc.h"
#define TRICE_ENTER_CRITICAL_SECTION { uint32_t primaskstate = __get_PRIMASK(); __disable_irq(); {
#define TRICE_LEAVE_CRITICAL_SECTION } __set_PRIMASK(primaskstate); }

2024-12-05_G0B1_inst_normalSpeed.png

2024-12-05_G0B1_inst_slowSpeed.png

🛑 The Trice execution time is now over 20 microseconds❗

Still fast enough for many cases but you hopefully have a good knowledge now how to tune Trice best for your application.

(back to top)

27. Trice memory needs

Depending on your target configuration the needed space can differ:

27.1. F030_bare Size

arm-none-eabi-size build/F030_bare.elf
   text    data     bss     dec     hex filename
   2428      12    1564    4004     fa4 build/F030_bare.elf

That is the basic size of an empty generated project just containing some drivers.

27.2. F030_inst Size with TRICE_OFF=1

arm-none-eabi-size build/F030_inst.elf
   text    data     bss     dec     hex filename
   2428      12    1564    4004     fa4 build/F030_inst.elf

This is exactly the same result, proofing that TRICE_OFF 1 is working correctly.

27.3. F030_inst with ring buffer

arm-none-eabi-size out/F030_inst.elf
   text    data     bss     dec     hex filename
   9416      28    2692   12136    2f68 out/F030_inst.elf

This is about 7 KB Flash and 1.2 KB RAM size for the Trice library and we see:

ms@DESKTOP-7POEGPB MINGW64 ~/repos/trice_wt_devel/examples/F030_inst (devel)
$ trice l -p com5 -ts16 "time:     #%6d" -hs off
com5:       triceExamples.c    12      # 65535  Hello! 👋🙂
com5:
com5:         ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
com5:         🎈🎈🎈🎈  𝕹𝖀𝕮𝕷𝕰𝕺-F030R8   🎈🎈🎈🎈
com5:         🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃
com5:
com5:
com5:       triceExamples.c    61              TRICE_DIRECT_OUTPUT == 0, TRICE_DEFERRED_OUTPUT == 1
com5:       triceExamples.c    67              TRICE_DOUBLE_BUFFER, TRICE_MULTI_PACK_MODE
com5:       triceExamples.c    76              _CYCLE == 1, _PROTECT == 1, _DIAG == 1, XTEA == 0
com5:       triceExamples.c    77              _SINGLE_MAX_SIZE=104, _BUFFER_SIZE=172, _DEFERRED_BUFFER_SIZE=1024
com5:       triceExamples.c    29    0,031_804 🐁 Speedy Gonzales a  32-bit time stamp
com5:       triceExamples.c    30    0,031_646 🐁 Speedy Gonzales b  32-bit time stamp
com5:       triceExamples.c    31    0,031_488 🐁 Speedy Gonzales c  32-bit time stamp
com5:       triceExamples.c    32    0,031_330 🐁 Speedy Gonzales d  32-bit time stamp
com5:       triceExamples.c    33      # 31172 🐁 Speedy Gonzales e  16-bit time stamp
com5:       triceExamples.c    34      # 31012 🐁 Speedy Gonzales f  16-bit time stamp
com5:       triceExamples.c    35      # 30852 🐁 Speedy Gonzales g  16-bit time stamp
com5:       triceExamples.c    36      # 30692 🐁 Speedy Gonzales h  16-bit time stamp
com5:       triceExamples.c    42      # 30224 2.71828182845904523536 <- float number as string
com5:       triceExamples.c    43      # 29517 2.71828182845904509080 (double with more ciphers than precision)
com5:       triceExamples.c    44      # 29322 2.71828174591064453125 (float  with more ciphers than precision)
com5:       triceExamples.c    45      # 29145 2.718282 (default rounded float)
com5:       triceExamples.c    46      # 28969 A Buffer:
com5:       triceExamples.c    47      # 28790 32 2e 37 31 38 32 38 31 38 32 38 34 35 39 30 34 35 32 33 35 33 36
com5:       triceExamples.c    48      # 28076 31372e32  31383238  34383238  34303935  35333235
com5:       triceExamples.c    49      # 27430 ARemoteFunctionName(2e32)(3137)(3238)(3138)(3238)(3438)(3935)(3430)(3235)(3533)(3633)
com5:       triceExamples.c    50              5 times a 16 byte long Trice messages, which may not be written all if the buffer is too small:
com5:       triceExamples.c    52      # 26554 i=44444400 aaaaaa00
com5:       triceExamples.c    52      # 26342 i=44444401 aaaaaa01
com5:       triceExamples.c    52      # 26130 i=44444402 aaaaaa02
com5:       triceExamples.c    52      # 25918 i=44444403 aaaaaa03
com5:       triceExamples.c    52      # 25706 i=44444404 aaaaaa04
com5:       triceExamples.c    29    0,031_790 🐁 Speedy Gonzales a  32-bit time stamp
com5:       triceExamples.c    30    0,031_632 🐁 Speedy Gonzales b  32-bit time stamp
com5:       triceExamples.c    31    0,031_474 🐁 Speedy Gonzales c  32-bit time stamp
com5:       triceExamples.c    32    0,031_316 🐁 Speedy Gonzales d  32-bit time stamp
com5:       triceExamples.c    33      # 31158 🐁 Speedy Gonzales e  16-bit time stamp
com5:       triceExamples.c    34      # 30998 🐁 Speedy Gonzales f  16-bit time stamp
com5:       triceExamples.c    35      # 30838 🐁 Speedy Gonzales g  16-bit time stamp
com5:       triceExamples.c    36      # 30678 🐁 Speedy Gonzales h  16-bit time stamp
com5:       triceExamples.c    42      # 30210 2.71828182845904523536 <- float number as string
com5:       triceExamples.c    43      # 29503 2.71828182845904509080 (double with more ciphers than precision)
com5:       triceExamples.c    44      # 29308 2.71828174591064453125 (float  with more ciphers than precision)
com5:       triceExamples.c    45      # 29131 2.718282 (default rounded float)
com5:       triceExamples.c    46      # 28955 A Buffer:
com5:       triceExamples.c    47      # 28776 32 2e 37 31 38 32 38 31 38 32 38 34 35 39 30 34 35 32 33 35 33 36
com5:       triceExamples.c    48      # 28062 31372e32  31383238  34383238  34303935  35333235
com5:       triceExamples.c    49      # 27416 ARemoteFunctionName(2e32)(3137)(3238)(3138)(3238)(3438)(3935)(3430)(3235)(3533)(3633)
com5:       triceExamples.c    50              5 times a 16 byte long Trice messages, which may not be written all if the buffer is too small:
com5:       triceExamples.c    52      # 26540 i=44444400 aaaaaa00
com5:       triceExamples.c    52      # 26328 i=44444401 aaaaaa01
com5:       triceExamples.c    52      # 26116 i=44444402 aaaaaa02
com5:       triceExamples.c    52      # 25904 i=44444403 aaaaaa03
com5:       triceExamples.c    52      # 25692 i=44444404 aaaaaa04
com5:    triceLogDiagData.c    44              triceSingleDepthMax = 108 of 172 (TRICE_BUFFER_SIZE)
com5:    triceLogDiagData.c    67              TriceHalfBufferDepthMax = 388 of  512
com5:       triceExamples.c    29    0,031_344 🐁 Speedy Gonzales a  32-bit time stamp
com5:       triceExamples.c    30    0,031_186 🐁 Speedy Gonzales b  32-bit time stamp

27.4. F030_inst with ring buffer

We need 600 bytes more Flash but could have less RAM used:

   text    data     bss     dec     hex filename
  10060      24    2688   12772    31e4 out/F030_inst.elf
ms@DESKTOP-7POEGPB MINGW64 ~/repos/trice_wt_devel/examples/F030_inst (devel)
$ trice l -p com5 -ts16 "time:     #%6d" -hs off
com5:       triceExamples.c    12      # 65535  Hello! 👋🙂
com5:
com5:         ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
com5:         🎈🎈🎈🎈  𝕹𝖀𝕮𝕷𝕰𝕺-F030R8   🎈🎈🎈🎈
com5:         🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃
com5:
com5:
com5:       triceExamples.c    61              TRICE_DIRECT_OUTPUT == 0, TRICE_DEFERRED_OUTPUT == 1
com5:       triceExamples.c    69              TRICE_RING_BUFFER, TRICE_MULTI_PACK_MODE
com5:       triceExamples.c    76              _CYCLE == 1, _PROTECT == 1, _DIAG == 1, XTEA == 0
com5:       triceExamples.c    77              _SINGLE_MAX_SIZE=104, _BUFFER_SIZE=172, _DEFERRED_BUFFER_SIZE=1024
com5:       triceExamples.c    29    0,031_732 🐁 Speedy Gonzales a  32-bit time stamp
com5:       triceExamples.c    30    0,031_531 🐁 Speedy Gonzales b  32-bit time stamp
com5:       triceExamples.c    31    0,031_330 🐁 Speedy Gonzales c  32-bit time stamp
com5:       triceExamples.c    32    0,031_129 🐁 Speedy Gonzales d  32-bit time stamp
com5:       triceExamples.c    33      # 30928 🐁 Speedy Gonzales e  16-bit time stamp
com5:       triceExamples.c    34      # 30725 🐁 Speedy Gonzales f  16-bit time stamp
com5:       triceExamples.c    35      # 30522 🐁 Speedy Gonzales g  16-bit time stamp
com5:       triceExamples.c    36      # 30319 🐁 Speedy Gonzales h  16-bit time stamp
com5:       triceExamples.c    42      # 29808 2.71828182845904523536 <- float number as string
com5:       triceExamples.c    43      # 29058 2.71828182845904509080 (double with more ciphers than precision)
com5:       triceExamples.c    44      # 28821 2.71828174591064453125 (float  with more ciphers than precision)
com5:       triceExamples.c    45      # 28602 2.718282 (default rounded float)
com5:       triceExamples.c    46      # 28383 A Buffer:
com5:       triceExamples.c    47      # 28162 32 2e 37 31 38 32 38 31 38 32 38 34 35 39 30 34 35 32 33 35 33 36
com5:       triceExamples.c    48      # 27406 31372e32  31383238  34383238  34303935  35333235
com5:       triceExamples.c    49      # 26718 ARemoteFunctionName(2e32)(3137)(3238)(3138)(3238)(3438)(3935)(3430)(3235)(3533)(3633)
com5:       triceExamples.c    50              5 times a 16 byte long Trice messages, which may not be written all if the buffer is too small:
com5:       triceExamples.c    52      # 25757 i=44444400 aaaaaa00
com5:       triceExamples.c    52      # 25502 i=44444401 aaaaaa01
com5:       triceExamples.c    52      # 25247 i=44444402 aaaaaa02
com5:       triceExamples.c    52      # 24992 i=44444403 aaaaaa03
com5:       triceExamples.c    52      # 24737 i=44444404 aaaaaa04
com5:       triceExamples.c    29    0,031_746 🐁 Speedy Gonzales a  32-bit time stamp
com5:       triceExamples.c    30    0,031_545 🐁 Speedy Gonzales b  32-bit time stamp
com5:       triceExamples.c    31    0,031_344 🐁 Speedy Gonzales c  32-bit time stamp
com5:       triceExamples.c    32    0,031_143 🐁 Speedy Gonzales d  32-bit time stamp
com5:       triceExamples.c    33      # 30942 🐁 Speedy Gonzales e  16-bit time stamp
com5:       triceExamples.c    34      # 30739 🐁 Speedy Gonzales f  16-bit time stamp
com5:       triceExamples.c    35      # 30536 🐁 Speedy Gonzales g  16-bit time stamp
com5:       triceExamples.c    36      # 30333 🐁 Speedy Gonzales h  16-bit time stamp
com5:       triceExamples.c    42      # 29822 2.71828182845904523536 <- float number as string
com5:       triceExamples.c    43      # 29072 2.71828182845904509080 (double with more ciphers than precision)
com5:       triceExamples.c    44      # 28835 2.71828174591064453125 (float  with more ciphers than precision)
com5:       triceExamples.c    45      # 28616 2.718282 (default rounded float)
com5:       triceExamples.c    46      # 28397 A Buffer:
com5:       triceExamples.c    47      # 28176 32 2e 37 31 38 32 38 31 38 32 38 34 35 39 30 34 35 32 33 35 33 36
com5:       triceExamples.c    48      # 27420 31372e32  31383238  34383238  34303935  35333235
com5:       triceExamples.c    49      # 26732 ARemoteFunctionName(2e32)(3137)(3238)(3138)(3238)(3438)(3935)(3430)(3235)(3533)(3633)
com5:       triceExamples.c    50              5 times a 16 byte long Trice messages, which may not be written all if the buffer is too small:
com5:       triceExamples.c    52      # 25771 i=44444400 aaaaaa00
com5:       triceExamples.c    52      # 25516 i=44444401 aaaaaa01
com5:       triceExamples.c    52      # 25261 i=44444402 aaaaaa02
com5:       triceExamples.c    52      # 25006 i=44444403 aaaaaa03
com5:       triceExamples.c    52      # 24751 i=44444404 aaaaaa04
com5:    triceLogDiagData.c    44              triceSingleDepthMax = 108 of 172 (TRICE_BUFFER_SIZE)
com5:    triceLogDiagData.c    75              triceRingBufferDepthMax = 324 of 1024
com5:       triceExamples.c    29    0,031_188 🐁 Speedy Gonzales a  32-bit time stamp
com5:       triceExamples.c    30    0,030_987 🐁 Speedy Gonzales b  32-bit time stamp

27.5. A developer setting, only enabling SEGGER_RTT

arm-none-eabi-size out/F030_inst.elf
   text    data     bss     dec     hex filename
   6656      16    2768    9440    24e0 out/F030_inst.elf

About 4 KB Flash needed and we see:

ms@DESKTOP-7POEGPB MINGW64 ~/repos/trice_wt_devel/examples/F030_inst (devel)
$ trice l -p jlink -args "-Device STM32F030R8 -if SWD -Speed 4000 -RTTChannel 0" -pf none -d16 -showID "deb:%5d"
Dec  6 16:14:38.276356  jlink:       triceExamples.c    12       65_535 16369  Hello! 👋🙂
Dec  6 16:14:38.276356  jlink:
Dec  6 16:14:38.276356  jlink:         ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
Dec  6 16:14:38.276356  jlink:         🎈🎈🎈🎈  𝕹𝖀𝕮𝕷𝕰𝕺-F030R8   🎈🎈🎈🎈
Dec  6 16:14:38.276356  jlink:         🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃
Dec  6 16:14:38.276356  jlink:
Dec  6 16:14:38.276356  jlink:
Dec  6 16:14:38.276356  jlink:       triceExamples.c    61              16334 TRICE_DIRECT_OUTPUT == 1, TRICE_DEFERRED_OUTPUT == 0
Dec  6 16:14:38.276356  jlink:       triceExamples.c    63              16333 TRICE_STACK_BUFFER, TRICE_MULTI_PACK_MODE
Dec  6 16:14:38.276920  jlink:       triceExamples.c    76              16327 _CYCLE == 1, _PROTECT == 1, _DIAG == 1, XTEA == 0
Dec  6 16:14:38.277424  jlink:       triceExamples.c    77              16326 _SINGLE_MAX_SIZE=104, _BUFFER_SIZE=172, _DEFERRED_BUFFER_SIZE=1024
Dec  6 16:14:39.181228  jlink:       triceExamples.c    29    0,031_848 16356 🐁 Speedy Gonzales a  32-bit time stamp
Dec  6 16:14:39.181228  jlink:       triceExamples.c    30    0,031_292 16355 🐁 Speedy Gonzales b  32-bit time stamp
Dec  6 16:14:39.181228  jlink:       triceExamples.c    31    0,030_736 16354 🐁 Speedy Gonzales c  32-bit time stamp
Dec  6 16:14:39.181228  jlink:       triceExamples.c    32    0,030_180 16353 🐁 Speedy Gonzales d  32-bit time stamp
Dec  6 16:14:39.181228  jlink:       triceExamples.c    33       29_624 16352 🐁 Speedy Gonzales e  16-bit time stamp
Dec  6 16:14:39.181228  jlink:       triceExamples.c    34       29_066 16351 🐁 Speedy Gonzales f  16-bit time stamp
Dec  6 16:14:39.181228  jlink:       triceExamples.c    35       28_508 16350 🐁 Speedy Gonzales g  16-bit time stamp
Dec  6 16:14:39.181228  jlink:       triceExamples.c    36       27_950 16349 🐁 Speedy Gonzales h  16-bit time stamp
Dec  6 16:14:39.181228  jlink:       triceExamples.c    42       27_086 16344 2.71828182845904523536 <- float number as string
Dec  6 16:14:39.181228  jlink:       triceExamples.c    43       25_906 16343 2.71828182845904509080 (double with more ciphers than precision)
Dec  6 16:14:39.181798  jlink:       triceExamples.c    44       25_305 16342 2.71828174591064453125 (float  with more ciphers than precision)
Dec  6 16:14:39.181798  jlink:       triceExamples.c    45       24_727 16341 2.718282 (default rounded float)
Dec  6 16:14:39.181798  jlink:       triceExamples.c    46       24_148 16340 A Buffer:
Dec  6 16:14:39.181798  jlink:       triceExamples.c    47       23_578 16339 32 2e 37 31 38 32 38 31 38 32 38 34 35 39 30 34 35 32 33 35 33 36
Dec  6 16:14:39.181798  jlink:       triceExamples.c    48       22_394 16338 31372e32  31383238  34383238  34303935  35333235
Dec  6 16:14:39.181798  jlink:       triceExamples.c    49       21_295 16337 ARemoteFunctionName(2e32)(3137)(3238)(3138)(3238)(3438)(3935)(3430)(3235)(3533)(3633)
Dec  6 16:14:39.181798  jlink:       triceExamples.c    50              16200 5 times a 16 byte long Trice messages, which may not be written all if the buffer is too small:
Dec  6 16:14:39.182303  jlink:       triceExamples.c    52       19_555 16335 i=44444400 aaaaaa00
Dec  6 16:14:39.182303  jlink:       triceExamples.c    52       18_941 16335 i=44444401 aaaaaa01
Dec  6 16:14:39.182303  jlink:       triceExamples.c    52       18_327 16335 i=44444402 aaaaaa02
Dec  6 16:14:39.182834  jlink:       triceExamples.c    52       17_713 16335 i=44444403 aaaaaa03
Dec  6 16:14:39.182834  jlink:       triceExamples.c    52       17_099 16335 i=44444404 aaaaaa04
Dec  6 16:14:40.187121  jlink:       triceExamples.c    29    0,031_855 16356 🐁 Speedy Gonzales a  32-bit time stamp
Dec  6 16:14:40.187121  jlink:       triceExamples.c    30    0,031_299 16355 🐁 Speedy Gonzales b  32-bit time stamp
Dec  6 16:14:40.187121  jlink:       triceExamples.c    31    0,030_743 16354 🐁 Speedy Gonzales c  32-bit time stamp
Dec  6 16:14:40.187121  jlink:       triceExamples.c    32    0,030_187 16353 🐁 Speedy Gonzales d  32-bit time stamp
Dec  6 16:14:40.187121  jlink:       triceExamples.c    33       29_631 16352 🐁 Speedy Gonzales e  16-bit time stamp
Dec  6 16:14:40.187121  jlink:       triceExamples.c    34       29_073 16351 🐁 Speedy Gonzales f  16-bit time stamp
Dec  6 16:14:40.187121  jlink:       triceExamples.c    35       28_515 16350 🐁 Speedy Gonzales g  16-bit time stamp
Dec  6 16:14:40.187121  jlink:       triceExamples.c    36       27_957 16349 🐁 Speedy Gonzales h  16-bit time stamp
Dec  6 16:14:40.187121  jlink:       triceExamples.c    42       27_093 16344 2.71828182845904523536 <- float number as string
Dec  6 16:14:40.187121  jlink:       triceExamples.c    43       25_913 16343 2.71828182845904509080 (double with more ciphers than precision)
Dec  6 16:14:40.187121  jlink:       triceExamples.c    44       25_310 16342 2.71828174591064453125 (float  with more ciphers than precision)
Dec  6 16:14:40.187121  jlink:       triceExamples.c    45       24_730 16341 2.718282 (default rounded float)
Dec  6 16:14:40.187121  jlink:       triceExamples.c    46       24_149 16340 A Buffer:
Dec  6 16:14:40.187121  jlink:       triceExamples.c    47       23_577 16339 32 2e 37 31 38 32 38 31 38 32 38 34 35 39 30 34 35 32 33 35 33 36
Dec  6 16:14:40.187630  jlink:       triceExamples.c    48       22_391 16338 31372e32  31383238  34383238  34303935  35333235
Dec  6 16:14:40.187690  jlink:       triceExamples.c    49       21_290 16337 ARemoteFunctionName(2e32)(3137)(3238)(3138)(3238)(3438)(3935)(3430)(3235)(3533)(3633)
Dec  6 16:14:40.187690  jlink:       triceExamples.c    50              16200 5 times a 16 byte long Trice messages, which may not be written all if the buffer is too small:
Dec  6 16:14:40.187690  jlink:       triceExamples.c    52       19_546 16335 i=44444400 aaaaaa00
Dec  6 16:14:40.188195  jlink:       triceExamples.c    52       18_930 16335 i=44444401 aaaaaa01
Dec  6 16:14:40.188195  jlink:       triceExamples.c    52       18_314 16335 i=44444402 aaaaaa02
Dec  6 16:14:40.188195  jlink:       triceExamples.c    52       17_698 16335 i=44444403 aaaaaa03
Dec  6 16:14:40.188195  jlink:       triceExamples.c    52       17_082 16335 i=44444404 aaaaaa04
Dec  6 16:14:41.191648  jlink:    triceLogDiagData.c    21              16382 RTT0_writeDepthMax=325 (BUFFER_SIZE_UP=1024)
Dec  6 16:14:41.191648  jlink:    triceLogDiagData.c    44              16378 triceSingleDepthMax = 108 of 172 (TRICE_BUFFER_SIZE)
Dec  6 16:14:41.191648  jlink:       triceExamples.c    29    0,030_628 16356 🐁 Speedy Gonzales a  32-bit time stamp
Dec  6 16:14:41.191648  jlink:       triceExamples.c    30    0,030_072 16355 🐁 Speedy Gonzales b  32-bit time stamp

“🐁 Speedy Gonzales” needs about 500 MCU clocks.

27.6. A developer setting, only enabling SEGGER_RTT and without deferred output gives after running ./build.sh TRICE_DIAGNOSTICS=0 TRICE_PROTECT=0:

arm-none-eabi-size out/F030_inst.elf
   text    data     bss     dec     hex filename
   5796      16    2736    8548    2164 out/F030_inst.elf

That is nearly 1 KB less Flash needs.

The output:

ms@DESKTOP-7POEGPB MINGW64 ~/repos/trice_wt_devel/examples/F030_inst (devel)
$ trice l -p jlink -args "-Device STM32F030R8 -if SWD -Speed 4000 -RTTChannel 0" -pf none -d16 -showID "deb:%5d"
Dec  6 16:20:10.545274  jlink:       triceExamples.c    12       65_535 16369  Hello! 👋🙂
Dec  6 16:20:10.545274  jlink:
Dec  6 16:20:10.545274  jlink:         ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
Dec  6 16:20:10.545274  jlink:         🎈🎈🎈🎈  𝕹𝖀𝕮𝕷𝕰𝕺-F030R8   🎈🎈🎈🎈
Dec  6 16:20:10.545274  jlink:         🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃
Dec  6 16:20:10.545274  jlink:
Dec  6 16:20:10.545274  jlink:
Dec  6 16:20:10.545274  jlink:       triceExamples.c    61              16334 TRICE_DIRECT_OUTPUT == 1, TRICE_DEFERRED_OUTPUT == 0
Dec  6 16:20:10.545274  jlink:       triceExamples.c    63              16333 TRICE_STACK_BUFFER, TRICE_MULTI_PACK_MODE
Dec  6 16:20:10.545890  jlink:       triceExamples.c    76              16327 _CYCLE == 1, _PROTECT == 0, _DIAG == 0, XTEA == 0
Dec  6 16:20:10.546396  jlink:       triceExamples.c    77              16326 _SINGLE_MAX_SIZE=104, _BUFFER_SIZE=172, _DEFERRED_BUFFER_SIZE=1024
Dec  6 16:20:11.448885  jlink:       triceExamples.c    29    0,031_859 16356 🐁 Speedy Gonzales a  32-bit time stamp
Dec  6 16:20:11.448885  jlink:       triceExamples.c    30    0,031_661 16355 🐁 Speedy Gonzales b  32-bit time stamp
Dec  6 16:20:11.448885  jlink:       triceExamples.c    31    0,031_463 16354 🐁 Speedy Gonzales c  32-bit time stamp
Dec  6 16:20:11.448885  jlink:       triceExamples.c    32    0,031_265 16353 🐁 Speedy Gonzales d  32-bit time stamp
Dec  6 16:20:11.448885  jlink:       triceExamples.c    33       31_067 16352 🐁 Speedy Gonzales e  16-bit time stamp
Dec  6 16:20:11.448885  jlink:       triceExamples.c    34       30_867 16351 🐁 Speedy Gonzales f  16-bit time stamp
Dec  6 16:20:11.448885  jlink:       triceExamples.c    35       30_667 16350 🐁 Speedy Gonzales g  16-bit time stamp
Dec  6 16:20:11.448885  jlink:       triceExamples.c    36       30_467 16349 🐁 Speedy Gonzales h  16-bit time stamp
Dec  6 16:20:11.549660  jlink:       triceExamples.c    42       29_961 16344 2.71828182845904523536 <- float number as string
Dec  6 16:20:11.549660  jlink:       triceExamples.c    43       29_141 16343 2.71828182845904509080 (double with more ciphers than precision)
Dec  6 16:20:11.549660  jlink:       triceExamples.c    44       28_897 16342 2.71828174591064453125 (float  with more ciphers than precision)
Dec  6 16:20:11.549660  jlink:       triceExamples.c    45       28_675 16341 2.718282 (default rounded float)
Dec  6 16:20:11.549660  jlink:       triceExamples.c    46       28_452 16340 A Buffer:
Dec  6 16:20:11.550166  jlink:       triceExamples.c    47       28_238 16339 32 2e 37 31 38 32 38 31 38 32 38 34 35 39 30 34 35 32 33 35 33 36
Dec  6 16:20:11.550247  jlink:       triceExamples.c    48       27_412 16338 31372e32  31383238  34383238  34303935  35333235
Dec  6 16:20:11.550247  jlink:       triceExamples.c    49       26_671 16337 ARemoteFunctionName(2e32)(3137)(3238)(3138)(3238)(3438)(3935)(3430)(3235)(3533)(3633)
Dec  6 16:20:11.550247  jlink:       triceExamples.c    50              16200 5 times a 16 byte long Trice messages, which may not be written all if the buffer is too small:
Dec  6 16:20:11.550754  jlink:       triceExamples.c    52       25_646 16335 i=44444400 aaaaaa00
Dec  6 16:20:11.550754  jlink:       triceExamples.c    52       25_389 16335 i=44444401 aaaaaa01
Dec  6 16:20:11.550754  jlink:       triceExamples.c    52       25_132 16335 i=44444402 aaaaaa02
Dec  6 16:20:11.551285  jlink:       triceExamples.c    52       24_875 16335 i=44444403 aaaaaa03
Dec  6 16:20:11.551285  jlink:       triceExamples.c    52       24_618 16335 i=44444404 aaaaaa04
Dec  6 16:20:12.453968  jlink:       triceExamples.c    29    0,031_859 16356 🐁 Speedy Gonzales a  32-bit time stamp
Dec  6 16:20:12.453968  jlink:       triceExamples.c    30    0,031_661 16355 🐁 Speedy Gonzales b  32-bit time stamp

“🐁 Speedy Gonzales” direct outout needs about 200 MCU clocks and not 500 as before.

27.7. Settings Conclusion

27.8. Legacy Trice Space Example (Old Version)

27.9. Memory Needs for Old Example 1

The following numbers are measured with a legacy encoding, showing that the instrumentation code can be even smaller.

Program Size (STM32-F030R8 demo project) trice instrumentation buffer size compiler optimize for time comment
Code=1592 RO-data=236 RW-data= 4 ZI-data=1028 none 0 off CubeMX generated, no trice
Code=1712 RO-data=240 RW-data=24 ZI-data=1088 core 64 off core added without trices
Code=3208 RO-data=240 RW-data=36 ZI-data=1540 TriceCheckSet() 512 off TRICE_SHORT_MEMORY is 1 (small)
Code=3808 RO-data=240 RW-data=36 ZI-data=1540 TriceCheckSet() 512 on TRICE_SHORT_MEMORY is 0 (fast)

27.10. Memory Needs for Old Example 2

Project Compiler Optimization Link-Time-Optimization Result Remark
MDK-ARM_STM32F030_bareerated CLANG v6.19 -Oz yes Code=1020 RO-data=196 RW-data=0 ZI-data=1024 This is the plain generated project without trice instrumentation.
MDK-ARM_STM32F030_instrumented CLANG v6.19 -Oz yes Code=4726 RO-data=238 RW-data=16 ZI-data=4608 This is with full trice instrumentation with example messages.

(back to top)

28. Trice Project Image Size Optimization

Modern compilers are optimizing out unused code automatically, but you can help to reduce trice code size if your compiler is not perfect.

28.1. Code Optimization -o3 or -oz (if supported)

For debugging it could be helpful to switch off code optimization what increases the code size. A good choice is -o1. See also TRICE_STACK_BUFFER could cause stack overflow with -o0 optimization.

28.2. Compiler Independent Setting (a bit outdated)

Maybe the following is a bit unhandy but it decreases the code amount, build time and the image size.

When having lots of program memory simply let all values be 1. With specific linker optimization unused functions can get stripped out automatically.

It is possible to #define TRICE_SINGLE_MAX_SIZE 12 for example in triceConfig.h. This automaticaly disables all Trice messages with payloads > 8 bytes (Trice size is 4 bytes).

28.3. Linker Option –split-sections (if supported)

In ARM-MDK uVision Project -> Options -> C/C++ -> "One EFL section for each function" allows good optimization and getting rid of unused code without additional linker optimization. This leads to a faster build process and is fine for most cases. It allows excluding unused functions.

28.4. Linker Optimization -flto (if supported)

28.4.1. ARMCC Compiler v5 Linker Feedback

28.4.3. GCC

With GCC use the -flto CLI switch directly.

28.4.4. LLVM ARM Clang

This compiler is much faster and creates the smallest images. Right now it uses the GCC libs and linker.

28.4.5. Other IDE´s and compilers

Please check the manuals and create a pull request or simply let me know.

28.5. Legacy STM32F030 Example Project - Different Build Sizes

28.5.1. ARMCC compiler v5

Compiler Linker Result Comment
o0   Code=46942 RO-data=266 RW-data=176 ZI-data=4896 very big
o1   Code=22582 RO-data=258 RW-data=168 ZI-data=4896  
o3   Code=21646 RO-data=258 RW-data=168 ZI-data=4896  
o0 split sections Code= 7880 RO-data=268 RW-data=156 ZI-data=4892 for debugging
o1 split sections Code= 5404 RO-data=260 RW-data=148 ZI-data=4892 for debugging
o3 split sections Code= 4996 RO-data=260 RW-data=148 ZI-data=4892 good balance
o0 flto Code= 8150 RO-data=266 RW-data=176 ZI-data=4896 builds slower
o1 flto Code= 5210 RO-data=258 RW-data=148 ZI-data=4892 builds slower
o3 flto Code= 4818 RO-data=258 RW-data=148 ZI-data=4892 builds slower, smallest image

(back to top)

29. Trice Tags and Color

29.1. How to get

./ref/COLOR_output.PNG

29.1.1. Output options

./ref/ColorOptions.PNG

29.1.2. Check Alternatives

There are over 1000 possibilities:

./ref/ColorAlternatives.PNG

To see them all run trice generate -color. Only file ../internal/emitter/lineTransformerANSI.go needs to be changed and the Trice tool needs to be rebuild afterwards: go install ./.... If you design a good looking flavour, feel free to propose it.

29.2. Color issues under Windows

Currently console colors are not enabled by default in Win10, so if you see no color but escape sequences on your powershell or cmd window, please refer to Windows console with ANSI colors handling or simply use a Linux like terminal under windows, like git-bash. One option is also to install Microsoft Windows Terminal (Preview) from inside the Microsoft store and to start the Trice tool inside there. Unfortunately this can not be done automatically right now because of missing command line switches. Alacritty is one of other alternatives.

(back to top)

30. Trice without UART

A very performant output path is RTT, if your MCU supports background memory access like the ARM-M ones.

Because the Trice tool needs only to receive, a single target UART-TX pin will do. But it is also possible to use a GPIO-Pin for Trice messages without occupying a UART resource.

(back to top)

31. Trice over RTT

Allows Trice over the debug probe without using a pin or UART.

(back to top)

31.1. For the impatient (2 possibilities)

The default SEGGER tools only suport RTT channel 0.

<h5>Setup TCP4 server providing the trace data</h5>

This is just the SEGGER J-Link server here for demonstration, but if your target device has an TCP4 interface, you can replace this with your target server.

ms@DESKTOP-7POEGPB MINGW64 ~/repos/trice (main)
$ jlink
SEGGER J-Link Commander V7.92g (Compiled Sep 27 2023 15:36:46)
DLL version V7.92g, compiled Sep 27 2023 15:35:10

Connecting to J-Link via USB...O.K.
Firmware: J-Link STLink V21 compiled Aug 12 2019 10:29:20
Hardware version: V1.00
J-Link uptime (since boot): N/A (Not supported by this model)
S/N: 770806762
VTref=3.300V


Type "connect" to establish a target connection, '?' for help
J-Link>connect
Please specify device / core. <Default>: STM32G0B1RE
Type '?' for selection dialog
Device>
Please specify target interface:
  J) JTAG (Default)
  S) SWD
  T) cJTAG
TIF>s
Specify target interface speed [kHz]. <Default>: 4000 kHz
Speed>
Device "STM32G0B1RE" selected.


Connecting to target via SWD
InitTarget() start
SWD selected. Executing JTAG -> SWD switching sequence.
DAP initialized successfully.
InitTarget() end - Took 36.3ms
Found SW-DP with ID 0x0BC11477
DPv0 detected
CoreSight SoC-400 or earlier
Scanning AP map to find all available APs
AP[1]: Stopped AP scan as end of AP map has been reached
AP[0]: AHB-AP (IDR: 0x04770031)
Iterating through AP map to find AHB-AP to use
AP[0]: Core found
AP[0]: AHB-AP ROM base: 0xF0000000
CPUID register: 0x410CC601. Implementer code: 0x41 (ARM)
Found Cortex-M0 r0p1, Little endian.
FPUnit: 4 code (BP) slots and 0 literal slots
CoreSight components:
ROMTbl[0] @ F0000000
[0][0]: E00FF000 CID B105100D PID 000BB4C0 ROM Table
ROMTbl[1] @ E00FF000
[1][0]: E000E000 CID B105E00D PID 000BB008 SCS
[1][1]: E0001000 CID B105E00D PID 000BB00A DWT
[1][2]: E0002000 CID B105E00D PID 000BB00B FPB
Memory zones:
  Zone: "Default" Description: Default access mode
Cortex-M0 identified.
J-Link>

Now the TCP4 server is running and you can start the Trice tool as TCP4 client, which connects to the TCP4 server to receive the binary log data:

$ trice l -p TCP4 -args="127.0.0.1:19021" -til ../examples/G0B1_inst/til.json -li ../examples/G0B1_inst/li.json -d16 -pf none

In this G0B1_inst example we use the additional -d16 and -pf none switches to decode the RTT data correctly.

This is a demonstration and test for the -port TCP4 usage possibility. Using RTT with J-Link is more easy possible as shown in the next point.

31.1.2. Start using JLinkRTTLogger

31.1.3. JLinkRTTLogger Issue

mkdir -p ./temp
rm -f ./temp/trice.bin
touch ./temp/trice.bin
tmux new -s "tricerttlog" -d "JLinkRTTLogger -Device STM32G0B1RE -If SWD -Speed 4000 -RTTChannel 0 ./temp/trice.bin"
trice log -p FILE -args ./temp/trice.bin -pf none -prefix off -hs off -d16 -ts16 "time:offs:%4d µs" -showID "deb:%5d" -i ../../demoTIL.json -li ../../demoLI.json -stat
tmux kill-session -t "tricerttlog"

(back to top)

31.2. Segger Real Time Transfer (RTT)

        -args string
        Use to pass port specific parameters. The "default" value depends on the used port:
        port "COMn": default="", use "TARM" for a different driver. (For baud rate settings see -baud.)
        port "J-LINK": default="-Device STM32F030R8 -if SWD -Speed 4000 -RTTChannel 0 -RTTSearchRanges 0x20000000_0x1000",
                The -RTTSearchRanges "..." need to be written without extra "" and with _ instead of space.
                For args options see JLinkRTTLogger in SEGGER UM08001_JLink.pdf.
        port "ST-LINK": default="-Device STM32F030R8 -if SWD -Speed 4000 -RTTChannel 0 -RTTSearchRanges 0x20000000_0x1000",
                The -RTTSearchRanges "..." need to be written without extra "" and with _ instead of space.
                For args options see JLinkRTTLogger in SEGGER UM08001_JLink.pdf.
        port "BUFFER": default="0 0 0 0", Option for args is any byte sequence.
         (default "default")

(back to top)

<h5>First step (to do if some issues occur - otherwise you can skip it)</h5>

Video

See also https://github.com/stlink-org/stlink

<h5>Second step</h5>

31.3.2. Some SEGGER tools in short

<h5>JLink.exe</h5>

<h5>JLinkRTTLogger.exe</h5>

31.3.3. JLinkRTTClient.exe

31.3.4. JLinkRTTViewer.exe

(back to top)

31.4. Segger RTT

(back to top)

(back to top)

31.6. Additional Notes (leftovers)

(back to top)

31.7. Further development

libusb-1.0.23\examples\bin64> .\listdevs.exe
2109:2811 (bus 2, device 8) path: 6
1022:145f (bus 1, device 0)
1022:43d5 (bus 2, device 0)
0a12:0001 (bus 2, device 1) path: 13
1366:0105 (bus 2, device 10) path: 5
libusb-1.0.23\examples\bin64> .\listdevs.exe
2109:2811 (bus 2, device 8) path: 6
1022:145f (bus 1, device 0)
1022:43d5 (bus 2, device 0)
0a12:0001 (bus 2, device 1) path: 13

(back to top)

31.8. NUCLEO-F030R8 example

Info: https://www.st.com/en/evaluation-tools/nucleo-F030r8.html

./ref/STLinkReflash.PNG

./ref/J-LinkRTT.PNG

(back to top)

31.9. Possible issues

(back to top)

31.10. OpenOCD with Darwin (MacOS)

Terminal 1:

brew install open-ocd
...
cd ./trice/examples/G0B1_inst
openocd -f openocd.cfg
Open On-Chip Debugger 0.12.0
Licensed under GNU GPL v2
For bug reports, read
    http://openocd.org/doc/doxygen/bugs.html
srst_only separate srst_nogate srst_open_drain connect_deassert_srst

Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : J-Link STLink V21 compiled Aug 12 2019 10:29:20
Info : Hardware version: 1.00
Info : VTarget = 3.300 V
Info : clock speed 2000 kHz
Info : SWD DPIDR 0x0bc11477
Info : [stm32g0x.cpu] Cortex-M0+ r0p1 processor detected
Info : [stm32g0x.cpu] target has 4 breakpoints, 2 watchpoints
Info : starting gdb server for stm32g0x.cpu on 3333
Info : Listening on port 3333 for gdb connections
Info : rtt: Searching for control block 'SEGGER RTT'
Info : rtt: Control block found at 0x20001238
Info : Listening on port 9090 for rtt connections
Channels: up=1, down=0
Up-channels:
0: Terminal 1024 0
Down-channels:

Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections

Terminal 2:

ms@MacBook-Pro G0B1_inst % trice l -p TCP4 -args localhost:9090  -pf none -d16
Nov 14 17:32:33.319451  TCP4:       triceExamples.c    10        0_000  Hello! 👋🙂
Nov 14 17:32:33.319463  TCP4:
Nov 14 17:32:33.319463  TCP4:         ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
Nov 14 17:32:33.319463  TCP4:         🎈🎈🎈🎈  NUCLEO-G0B1RE   🎈🎈🎈🎈
Nov 14 17:32:33.319463  TCP4:         🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃
Nov 14 17:32:33.319463  TCP4:
Nov 14 17:32:33.319463  TCP4:
Nov 14 17:32:33.406455  TCP4:       triceExamples.c    16        0_037 2.71828182845904523536 <- float number as string
Nov 14 17:32:33.505116  TCP4:       triceExamples.c    17        0_087 2.71828182845904509080 (double with more ciphers than precision)
Nov 14 17:32:33.607518  TCP4:       triceExamples.c    18        0_117 2.71828174591064453125 (float  with more ciphers than precision)
Nov 14 17:32:33.707851  TCP4:       triceExamples.c    19        0_146 2.718282 (default rounded float)
Nov 14 17:32:33.807685  TCP4:       triceExamples.c    20        0_175 A Buffer:
Nov 14 17:32:33.908202  TCP4:       triceExamples.c    21        0_204 32 2e 37 31 38 32 38 31 38 32 38 34 35 39 30 34 35 32 33 35 33 36
Nov 14 17:32:34.007148  TCP4:       triceExamples.c    22        0_254 31372e32  31383238  34383238  34303935  35333235
Nov 14 17:32:35.007949  TCP4:       triceExamples.c    23        0_301 ARemoteFunctionName(2e32)(3137)(3238)(3138)(3238)(3438)(3935)(3430)(3235)(3533)(3633)
Nov 14 17:32:35.112304  TCP4:       triceExamples.c    24              100 times a 16 byte long Trice messages, which not all will be written because of the TRICE_PROTECT:
Nov 14 17:32:35.307567  TCP4:       triceExamples.c    26        0_379 i=44444400 aaaaaa00
Nov 14 17:32:35.408257  TCP4:       triceExamples.c    27    0,000_002 i=44444400 aaaaaa00
Nov 14 17:32:35.509022  TCP4:       triceExamples.c    26        0_441 i=44444401 aaaaaa01
Nov 14 17:32:35.609439  TCP4:       triceExamples.c    27    0,000_002 i=44444401 aaaaaa01
Nov 14 17:32:35.710201  TCP4:       triceExamples.c    26        0_504 i=44444402 aaaaaa02
...

TODO: Working example with SEGGER_RTT J-Link and Open OCD

(back to top)

32. Writing the Trice logs into an SD-card (or a user specific output)

Related issues/discussions: #253 #405 #425 #447 #537

(back to top)

33. Trice Target Code Implementation

33.1. TRICE Macro structure

33.1.1. TRICE_ENTER

33.1.2. TRICE_PUT

33.1.3. TRICE_LEAVE

33.2. TRICE_STACK_BUFFER

33.3. TRICE_STATIC_BUFFER

33.4. TRICE_DOUBLE_BUFFER

33.5. TRICE_RING_BUFFER

The TRICE_RING_BUFFER allocates incremental ring buffer space and each trice location is read by a deferred task.

33.6. Deferred Out

33.6.1. Double Buffer

33.6.2. Ring Buffer

33.7. Direct Transfer

33.8. Possible Target Code Improvements

There have been 3 similar implementations for trice encode

static size_t triceDirectEncode(   uint8_t* enc, const uint8_t * buf, size_t len );
       size_t TriceDeferredEncode( uint8_t* enc, const uint8_t * buf, size_t len );

unsigned TriceEncryptAndCobsFraming32( uint32_t * const triceStart, unsigned wordCount ){

Now:

size_t TriceEncode( unsigned encrypt, unsigned framing, uint8_t* dst, const uint8_t * buf, size_t len ){
unsigned TriceEncryptAndCobsFraming32( uint32_t * const triceStart, unsigned wordCount ){

Currently there are 3 similar implementations for trice buffer reads

static size_t triceIDAndLen(    uint32_t* pBuf,               uint8_t** ppStart, int*      triceID );
static int    TriceNext(        uint8_t** buf,                size_t* pSize,     uint8_t** pStart,    size_t* pLen );
static int    TriceIDAndBuffer( uint32_t const * const pData, int* pWordCount,   uint8_t** ppStart,   size_t* pLength );
//! \param pTriceID is filled with ID for routing
//! \param pCount is used for double or ring buffer to advance inside the buffer
//! \param dest provides space for the encoded trice
//! \param src is the location of the trice message we want encode
//! \retval is the netto size of the encoded trice data
size_t TriceEncode(int* pTriceID, unsigned int pCount, uint32_t * const dest, uint32_t const * const src );

(back to top)

34. Trice Similarities and Differences to printf Usage

34.1. Printf-like functions

…have a lot of things to do: Copy format string from FLASH memory into a RAM buffer and parse it for format specifiers. Also parse the variadic parameter list and convert each parameter according to its format specifier into a character sequences, what includes several divisions - costly function calls. Concatenate the parts to a new string and deliver it to the output, what often means copying again. A full-featured printf library consumes plenty space and processing time and several open source projects try to make it better in this or that way. Never ever call a printf-like function in time critical code, like an interrupt - it would crash your target in most cases. The trice calls are usable inside interrupts, because they only need a few MCU clocks for execution. Porting legacy code to use it with the Trice library, means mainly to replace Printf-like function calls with trice function calls. See also chapter Legacy User Code Option Print Buffer Wrapping and Framing.

34.2. Trice IDs

34.3. Trice values bit width

34.4. Many value parameters

34.5. Floating Point Values

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


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

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

34.6. Runtime Generated 0-terminated Strings Transfer with triceS

34.7. Runtime Generated counted Strings Transfer with triceN

34.8. Runtime Generated Buffer Transfer with triceB

  s = "abcde 12345"; // assume this as runtime generated string
  triceS( "msg:Show s with triceS: %s\n", s );
  len = strlen(s);
  triceN( "sig:Show s with triceN:%s\n", s, len );
  triceB( "dbg: %02x\n", s, len ); // Show s as colored code sequence in hex code.
  triceB( "msg: %4d\n", s, len ); // Show s as colored code sequence in decimal code.

This gives output similar to: ./ref/TRICE_B.PNG

Channel specifier within the TRICE_B format string are supported in Trice versions >= v0.66.0.

If the buffer is not 8 but 16, 32 or 32 bits wide, the macros TRICE8_B, TRICE16_B, TRICE32_B and TRICE64_B, are usable in the same manner.

34.9. Remote function call syntax support with triceF

The TRICE8_F, TRICE16_F, TRICE32_F, TRICE64_F, macros expect a string without format specifiers which is usable later as a function call. Examples:

trice8F(   "call:FunctionNameW", b8,  sizeof(b8) /sizeof(int8_t) );   //exp: time:            default: call:FunctionNameW(00)(ff)(fe)(33)(04)(05)(06)(07)(08)(09)(0a)(0b)(00)(ff)(fe)(33)(04)(05)(06)(07)(08)(09)(0a)(0b)
TRICE16_F( "info:FunctionNameX", b16, sizeof(b16)/sizeof(int16_t) );  //exp: time: 842,150_450default: info:FunctionNameX(0000)(ffff)(fffe)(3344)
TRice16F(  "call:FunctionNameX", b16, sizeof(b16)/sizeof(int16_t) );  //exp: time: 842,150_450default: call:FunctionNameX(0000)(ffff)(fffe)(3344)
Trice16F(  "call:FunctionNameX", b16, sizeof(b16)/sizeof(int16_t) );  //exp: time:       5_654default: call:FunctionNameX(0000)(ffff)(fffe)(3344)
trice16F(  "call:FunctionNameX", b16, sizeof(b16)/sizeof(int16_t) );  //exp: time:            default: call:FunctionNameX(0000)(ffff)(fffe)(3344)
TRICE32_F( "info:FunctionNameY", b32, sizeof(b32)/sizeof(int32_t) );  //exp: time: 842,150_450default: info:FunctionNameY(00000000)(ffffffff)(fffffffe)(33445555)
TRice32F(  "call:FunctionNameY", b32, sizeof(b32)/sizeof(int32_t) );  //exp: time: 842,150_450default: call:FunctionNameY(00000000)(ffffffff)(fffffffe)(33445555)
Trice32F(  "call:FunctionNameY", b32, sizeof(b32)/sizeof(int32_t) );  //exp: time:       5_654default: call:FunctionNameY(00000000)(ffffffff)(fffffffe)(33445555)
trice32F(  "call:FunctionNameY", b32, sizeof(b32)/sizeof(int32_t) );  //exp: time:            default: call:FunctionNameY(00000000)(ffffffff)(fffffffe)(33445555)
TRICE64_F( "info:FunctionNameZ", b64, sizeof(b64)/sizeof(int64_t) );  //exp: time: 842,150_450default: info:FunctionNameZ(0000000000000000)(ffffffffffffffff)(fffffffffffffffe)(3344555566666666)
TRice64F(  "call:FunctionNameZ", b64, sizeof(b64)/sizeof(int64_t) );  //exp: time: 842,150_450default: call:FunctionNameZ(0000000000000000)(ffffffffffffffff)(fffffffffffffffe)(3344555566666666)
Trice64F(  "call:FunctionNameZ", b64, sizeof(b64)/sizeof(int64_t) );  //exp: time:       5_654default: call:FunctionNameZ(0000000000000000)(ffffffffffffffff)(fffffffffffffffe)(3344555566666666)
trice64F(  "call:FunctionNameZ", b64, sizeof(b64)/sizeof(int64_t) );  //exp: time:            default: call:FunctionNameZ(0000000000000000)(ffffffffffffffff)(fffffffffffffffe)(3344555566666666)

The Trice tool displays the parameter buffer in the shown manner. There is a FunctionPointerList Generator, which generates mainly a function pointer list with associated IDs. This list can get part of the source code of a remote device. Then, when receiving a Trice message, the remote device can execute the assigned function call using the transferred parameters. This way several devices can communicate in an easy and reliable way.

With #define TRICE_F TRICE16_F in the project specific triceConfig.h file the user can specify which should be the bitwidth (16 in this example) for triceF macros. The default value is 8.

Hint: If you add for example "rpc" as tag and call trice log -ban "rpc", the Trice tool will not display the RPC Trices, but all others. That could be helpful, if you have frequent RPCs and do not wish to spoil your log output with them.

34.10. Extended format specifier possibilities

34.10.1. Trice format specifier

34.10.2. Overview Table

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

./ref/TriceCheckOutput.gif

34.11. UTF-8 Support

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

./ref/UTF-8Example.PNG

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

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

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

34.13. Format tags prototype specifier examples

This syntax is supported: %[flags][width][.precision][length]

(back to top)

35. Development Environment Setup

35.1. Common Information

35.2. Important to know

The ARM-Keil µVision IDE does sometimes not recognize external file modifications. That means for example: After editing main.c by adding a trice( "Hi!\n" ) and executing trice insert as pre-compile step it could happen, that an updated trice( iD(12345), "Hi!\n" ) was inserted and correct compiled but the update in main.c is not shown. Simply close and reopen main.c before editing again. This seems to be a ARM-Keil µVision IDE “feature” or be caused Windows not signaling a file change.

35.3. Animation

(The trice IDs occur just during the compilation.)

35.4. Setup Linux PC - Example with Debian12 - KDE Desktop

35.4.1. Basic setup

su
apt install sudo
adduser <your_user_name> sudo
exit
groups
sudo apt update
sudo apt upgrade
sudo apt install build-essential
make --version
gcc --version
git --version
git config --global user.email "you@example.com"
git config --global user.name "Your Name"

35.4.2. Github

35.4.3. vsCode

35.4.4. Go

35.4.5. Gitkraken (or other GUI for git)

35.4.6. arm-none-eabi toolchain (or other target system compiler)

sudo apt install gcc-arm-none-eabi
sudo apt install binutils-arm-none-eabi
sudo apt install gdb-arm-none-eabi
sudo apt install openocd
arm-none-eabi-gcc --version
arm-none-eabi-gcc (15:12.2.rel1-1) 12.2.1 20221205
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  ls -l /usr/bin/ | grep arm-none-eabi
  -rwxr-xr-x 1 root root     1033504 Feb 28  2023 arm-none-eabi-addr2line
  -rwxr-xr-x 2 root root     1066088 Feb 28  2023 arm-none-eabi-ar
  -rwxr-xr-x 2 root root     2095024 Feb 28  2023 arm-none-eabi-as
  -rwxr-xr-x 2 root root     1514496 Dec 22  2022 arm-none-eabi-c++
  -rwxr-xr-x 1 root root     1032992 Feb 28  2023 arm-none-eabi-c++filt
  -rwxr-xr-x 1 root root     1514496 Dec 22  2022 arm-none-eabi-cpp
  -rwxr-xr-x 1 root root       43640 Feb 28  2023 arm-none-eabi-elfedit
  -rwxr-xr-x 2 root root     1514496 Dec 22  2022 arm-none-eabi-g++
  -rwxr-xr-x 2 root root     1514496 Dec 22  2022 arm-none-eabi-gcc
  -rwxr-xr-x 2 root root     1514496 Dec 22  2022 arm-none-eabi-gcc-12.2.1
  -rwxr-xr-x 1 root root       35376 Dec 22  2022 arm-none-eabi-gcc-ar
  -rwxr-xr-x 1 root root       35376 Dec 22  2022 arm-none-eabi-gcc-nm
  -rwxr-xr-x 1 root root       35376 Dec 22  2022 arm-none-eabi-gcc-ranlib
  -rwxr-xr-x 1 root root      749664 Dec 22  2022 arm-none-eabi-gcov
  -rwxr-xr-x 1 root root      585688 Dec 22  2022 arm-none-eabi-gcov-dump
  -rwxr-xr-x 1 root root      610328 Dec 22  2022 arm-none-eabi-gcov-tool
  -rwxr-xr-x 1 root root     1104256 Feb 28  2023 arm-none-eabi-gprof
  -rwxr-xr-x 4 root root     1709968 Feb 28  2023 arm-none-eabi-ld
  -rwxr-xr-x 4 root root     1709968 Feb 28  2023 arm-none-eabi-ld.bfd
  -rwxr-xr-x 1 root root    24982344 Dec 22  2022 arm-none-eabi-lto-dump
  -rwxr-xr-x 2 root root     1054720 Feb 28  2023 arm-none-eabi-nm
  -rwxr-xr-x 2 root root     1180744 Feb 28  2023 arm-none-eabi-objcopy
  -rwxr-xr-x 2 root root     1867744 Feb 28  2023 arm-none-eabi-objdump
  -rwxr-xr-x 2 root root     1066120 Feb 28  2023 arm-none-eabi-ranlib
  -rwxr-xr-x 2 root root      973400 Feb 28  2023 arm-none-eabi-readelf
  -rwxr-xr-x 1 root root     1033280 Feb 28  2023 arm-none-eabi-size
  -rwxr-xr-x 1 root root     1037504 Feb 28  2023 arm-none-eabi-strings
  -rwxr-xr-x 2 root root     1180744 Feb 28  2023 arm-none-eabi-strip
sudo apt install ~/Downloads/JLink_Linux_V812_x86_64.deb
th@P51-DebianKDE:~/Downloads$ JLinkRTTLogger -?
SEGGER J-Link RTT Logger
Compiled Dec 18 2024 15:48:21
(c) 2016-2017 SEGGER Microcontroller GmbH, www.segger.com
         Solutions for real time microcontroller applications

Default logfile path: /home/th/.config/SEGGER

------------------------------------------------------------ 

Available options:
-Device <devicename>
-If <ifname>
-Speed <speed>
-USB <SN>
-IP <SN>
-RTTAddress <RTTAddress>
-RTTSearchRanges "<Rangestart> <RangeSize>[, <Range1Start> <Range1Size>, ...]
"-RTTChannel <RTTChannel>
-JLinkScriptFile <PathToScript>
<OutFilename>

Shutting down... Done.th@P51-DebianKDE:~/Downloads$ 

35.4.8. Beyond Compare (if no other diff tool)

35.5. Setup Windows PC Example

Setting up a PC is for Linux mostly straightforward but Windows PCs are more problematic. The steps shown here are just one example.

35.5.1. Setup Trice

OR

35.5.2. Setup ARM Environment Example

<h5>Install make</h5>

ms@PaulPCWin11 MINGW64 ~/repos/trice/examples (devel)
$ winget install ezwinports.make
The `msstore` source requires that you view the following agreements before using.
Terms of Transaction: https://aka.ms/microsoft-store-terms-of-transaction
The source requires the current machine's 2-letter geographic region to be sent to the backend service to function properly (ex. "US").

Do you agree to all the source agreements terms?
[Y] Yes  [N] No: Y
Found ezwinports: make [ezwinports.make] Version 4.4.1
This application is licensed to you by its owner.
Microsoft is not responsible for, nor does it grant any licenses to, third-party packages.
Downloading https://downloads.sourceforge.net/project/ezwinports/make-4.4.1-without-guile-w32-bin.zip
  ██████████████████████████████   383 KB /  383 KB
Successfully verified installer hash
Extracting archive...
Successfully extracted archive
Starting package install...
Path environment variable modified; restart your shell to use the new value.
Command line alias added: "make"
Successfully installed

ms@PaulPCWin11 MINGW64 ~/repos/trice/examples (devel)
$ make --version
GNU Make 4.4.1
Built for Windows32
Copyright (C) 1988-2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

<h5>Install ARM GCC</h5>

<h5>MacOS</h5>

<h5>Install ARM Clang (optional)</h5>

With the ARM Clang you get quicker compilation runs and smaller images.

<h5>Check Project Makefile (if it already exists)</h5>

# Put ARM Clang first in path temporary to avoid compiler variants issues.
export PATH := C:\bin\ArmClang\bin:$(PATH)

# ARM Clang uses the ARM GNU toolchain libraries and finds them over C_INCLUDE_PATH.
export C_INCLUDE_PATH := C:\bin\ArmGNUToolchain\arm-none-eabi\include

The C:\bin\ArmGNUToolchain\bin: is in fact not needed, because it must be in the path anyway for debugging.

$ make version
/c/bin/ArmGNUToolchain/bin/arm-none-eabi-gcc
arm-none-eabi-gcc (Arm GNU Toolchain 12.3.Rel1 (Build arm-12.35)) 12.3.1 20230626
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

/c/bin/ArmClang/bin/clang
clang version 17.0.0
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: C:\bin\ArmClang\bin

The paths must match with the installation locations.

35.5.3. Setup STM32

<h5>Generate Base Project</h5>

<h5>Update NUCLEO Onboard Debugger (other ST evaluation boards too)</h5>

(https://www.st.com/en/development-tools/stsw-link007.html)

This step is recommended before re-flashing with the J-Link onboard debugger software.

(https://www.segger.com/products/debug-probes/j-link/models/other-j-links/st-link-on-board/)

Using the J-Link onboard debugger software allows parallel debugging and RTT usage.

Unfortunately this is not possible with v3 onboard debugger hardware! But you can use a J-Link hardware instead. Also it is possible to use a v2 onboard debugger from a different evaluation board or a “Bluepill” Development Board Module with ARM Cortex M3 processor”.

35.5.5. Setup VS-Code

35.6. Makefile with Clang too

35.7. Download Locations

35.7.1. Clang

https://releases.llvm.org/download.html -> https://github.com/llvm/llvm-project/releases/tag/llvmorg-16.0.0 (example)

35.7.2. GCC

https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain -> https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads (example))

35.8. Install Locations

Do not use locations containing spaces, like C:\Program Files. Take C:\bin for example. This avoids trouble caused by spaces inside path names.

35.9. Environment Variables

Extend the path variable:

35.10. Build command

35.11. Run & Debug

35.12. Logging

trice l -p JLINK -args="-Device STM32G0B1RE -if SWD -Speed 4000 -RTTChannel 0" -pf none -ts ms -d16 (example)

35.13. Setting up a new project

(back to top)

36. Example Projects without and with Trice Instrumentation

Project Name Description
   
F030_bare This is a minimal STM32CubeMX generated Makefile project adapted to Clang and GCC. It serves as a reference for diff to F030_inst so see quickly the needed instrumentation steps you need for your own project.
F030_inst This is a minimal STM32CubeMX generated Makefile project adapted to Clang and GCC and afterward instrumented with the Trice library. Compare it with F030_bare to see quickly how to instrument your project.
   
G0B1_bare This is a minimal FreeRTOS STM32CubeMX generated Makefile project adapted to Clang and GCC.
G0B1_inst This is a minimal FreeRTOS STM32CubeMX generated Makefile project adapted to Clang and GCC and afterward instrumented with the Trice library.
   
L432_bare This is a minimal FreeRTOS STM32CubeMX generated Makefile project extended to compile also with Clang trying to perform minimal changes. It produces some warnings, because it is not finetuned. The L432_inst project is then a next step performable.
L432_inst This is a minimal FreeRTOS STM32CubeMX generated Makefile project adapted to Clang and GCC and afterward instrumented with the Trice library.
   

(back to top)

36.1. Nucleo-F030R8 Examples

36.1.1. F030_bare

Folder: ../examples/F030_bare/

This is a STMCubeMX generated project without Trice instrumentation for easy compare with F030_inst to figure out the needed changes to set up trice.

Steps performed as potential guide:
PS E:\repos\trice\examples\F030_bare> make -j
mkdir build
arm-none-eabi-gcc -c -mcpu=cortex-m0 -mthumb   -DUSE_FULL_LL_DRIVER -DHSE_VALUE=8000000 -DHSE_STARTUP_TIMEOUT=100 -DLSE_STARTUP_TIMEOUT=5000 -DLSE_VALUE=32768 -DHSI_VALUE=8000000 -DLSI_VALUE=40000 -DVDD_VALUE=3300 -DPREFETCH_ENABLE=1 -DINSTRUCTION_CACHE_ENABLE=0 -DDATA_CACHE_ENABLE=0 -DSTM32F030x8 -ICore/Inc -IDrivers/STM32F0xx_HAL_Driver/Inc -IDrivers/CMSIS/Device/ST/STM32F0xx/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/main.d" -Wa,-a,-ad,-alms=build/main.lst Core/Src/main.c -o build/main.o

...

arm-none-eabi-gcc -x assembler-with-cpp -c -mcpu=cortex-m0 -mthumb   -DUSE_FULL_LL_DRIVER -DHSE_VALUE=8000000 -DHSE_STARTUP_TIMEOUT=100 -DLSE_STARTUP_TIMEOUT=5000 -DLSE_VALUE=32768 -DHSI_VALUE=8000000 -DLSI_VALUE=40000 -DVDD_VALUE=3300 -DPREFETCH_ENABLE=1 -DINSTRUCTION_CACHE_ENABLE=0 -DDATA_CACHE_ENABLE=0 -DSTM32F030x8 -ICore/Inc -IDrivers/STM32F0xx_HAL_Driver/Inc -IDrivers/CMSIS/Device/ST/STM32F0xx/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/startup_stm32F030x8.d" startup_stm32F030x8.s -o build/startup_stm32F030x8.o
arm-none-eabi-gcc build/main.o build/stm32f0xx_it.o build/stm32f0xx_ll_gpio.o build/stm32f0xx_ll_pwr.o build/stm32f0xx_ll_exti.o build/stm32f0xx_ll_usart.o build/stm32f0xx_ll_rcc.o build/stm32f0xx_ll_dma.o build/stm32f0xx_ll_utils.o build/system_stm32f0xx.o build/sysmem.o build/syscalls.o build/startup_stm32F030x8.o  -mcpu=cortex-m0 -mthumb   -specs=nano.specs -TSTM32F030R8Tx_FLASH.ld  -lc -lm -lnosys  -Wl,-Map=build/F030_bare.map,--cref -Wl,--gc-sections -o build/F030_bare.elf
C:/bin/ArmGNUToolchain/bin/../lib/gcc/arm-none-eabi/13.2.1/../../../../arm-none-eabi/bin/ld.exe: warning: build/F030_bare.elf has a LOAD segment with RWX permissions
arm-none-eabi-size build/F030_bare.elf
   text    data     bss     dec     hex filename
   2428      12    1564    4004     fa4 build/F030_bare.elf
arm-none-eabi-objcopy -O ihex build/F030_bare.elf build/F030_bare.hex
arm-none-eabi-objcopy -O binary -S build/F030_bare.elf build/F030_bare.bin
PS E:\repos\trice\examples\F030_bare>
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Cortex Debug",
            "cwd": "${workspaceFolder}",
            "executable": "./build/F030_bare.elf",
            "request": "launch",
            "type": "cortex-debug",
            "runToEntryPoint": "main",
            "servertype": "jlink",
            "device": "STM32F030R8",
            "svdFile": "./STM32F030R8.svd",
            "runToMain": true

        }
    ]
}
Hint

36.1.2. F030_inst

Folder: ../examples/F030_inst/

This is a working example with deferred encrypted out over UART. By uncommenting 2 lines in triceConfig.h, you get also parallel direct out over RTT. For setup see Trice over RTT and adapt steps from F030_bare.

Intrumenting:

(back to top)

36.2. Nucleo-G0B1 Examples

36.2.1. G0B1_bare

Folder: ../examples/G0B1_bare/

<h5>G0B1_bare Description</h5>

<h5>Setting Up G0B1_bare</h5>

36.2.2. G0B1_inst

Folder: ../examples/G0B1_inst/

This is an example with direct out without framing over RTT and deferred out in TCOBS framing over UART.

<h5>Setting Up</h5>

<h5>Instrumenting</h5>

(back to top)

36.3. Nucleo-L432KC Examples

36.3.1. L432_bare

Folder: ../examples/L432_bare/

36.3.2. L432_inst

Folder: ../examples/L432_inst/

Build:

Run ./build.sh for configuration 0 or ./build.sh CONFIGURATION=34 for example.

Deferred Mode for max Speed

The stamps are MCU clocks here, so 🐁 Speedy Gonzales lasts 9 processor clocks here.

ms@DESKTOP-7POEGPB MINGW64 ~/repos/trice_wt_devel/examples/L432_inst (devel)
$ trice l -p com8 -hs off -prefix off
      triceExamples.c    10        0_272  Hello! 👋🙂

        ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
        🎈🎈🎈🎈  𝕹𝖀𝕮𝕷𝕰𝕺-L432KC   🎈🎈🎈🎈
        🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃


        triceConfig.h   369              CONFIGURATION == 34 - UART, no cycle counter, no critical sections.
      triceExamples.c    45              TRICE_DIRECT_OUTPUT == 0, TRICE_DEFERRED_OUTPUT == 1
      triceExamples.c    51              TRICE_DOUBLE_BUFFER, TRICE_MULTI_PACK_MODE
      triceExamples.c    60              _CYCLE == 0, _PROTECT == 0, _DIAG == 0, XTEA == 0
      triceExamples.c    61              _SINGLE_MAX_SIZE=512, _BUFFER_SIZE=580, _DEFERRED_BUFFER_SIZE=4096
      triceExamples.c    15    0,000_731 🐁 Speedy Gonzales
      triceExamples.c    16    0,000_745 🐁 Speedy Gonzales
      triceExamples.c    17    0,000_754 🐁 Speedy Gonzales
      triceExamples.c    18    0,000_763 🐁 Speedy Gonzales
      triceExamples.c    19    0,000_772 🐁 Speedy Gonzales
      triceExamples.c    20    0,000_781 🐁 Speedy Gonzales
      triceExamples.c    21    0,000_790 🐁 Speedy Gonzales
      triceExamples.c    22    0,000_799 🐁 Speedy Gonzales
      triceExamples.c    24        0_981 2.71828182845904523536 <- float number as string
      triceExamples.c    25        1_230 2.71828182845904509080 (double with more ciphers than precision)
      triceExamples.c    26        1_268 2.71828174591064453125 (float  with more ciphers than precision)
      triceExamples.c    27        1_296 2.718282 (default rounded float)
      triceExamples.c    28        1_310 A Buffer:
      triceExamples.c    29        1_348 32 2e 37 31 38 32 38 31 38 32 38 34 35 39 30 34 35 32 33 35 33 36
      triceExamples.c    30        1_603 31372e32  31383238  34383238  34303935  35333235
      triceExamples.c    31        1_799 ARemoteFunctionName(2e32)(3137)(3238)(3138)(3238)(3438)(3935)(3430)(3235)(3533)(3633)
      triceExamples.c    32              10 times a 16 byte long Trice messages, which not all will be written because of the TRICE_PROTECT:
      triceExamples.c    34        2_072 i=44444400 aaaaaa00
      triceExamples.c    34        2_119 i=44444401 aaaaaa01
      triceExamples.c    34        2_166 i=44444402 aaaaaa02

<h5>“Hardware” Changes</h5>

<h5>Using RTT with on-board J-Link and JLinkRTTLogger</h5>

<h5>Using RTT with on-board J-Link and OpenOCD</h5>

<h6>With Windows not possible</h6>

<h6>Darwin (MacOS)</h6>

<h5>Using RTT with on-board ST-Link and OpenOCD</h5>

Terminal 1:

ms@LenovoP51Win11 MINGW64 /e/repos/trice/examples/L432_inst (devel)
$ openocd -f STLinkOpenOCD.cfg
Open On-Chip Debugger 0.12.0 (2024-09-16) [https://github.com/sysprogs/openocd]
Licensed under GNU GPL v2
libusb1 d52e355daa09f17ce64819122cb067b8a2ee0d4b
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
Info : clock speed 100 kHz
Info : STLINK V2J24M11 (API v2) VID:PID 0483:374B
Info : Target voltage: 72.811768
Info : [stm32l4x.cpu] Cortex-M4 r0p1 processor detected
Info : [stm32l4x.cpu] target has 6 breakpoints, 4 watchpoints
Info : [stm32l4x.cpu] Examination succeed
Info : [stm32l4x.cpu] starting gdb server on 3333
Info : Listening on port 3333 for gdb connections
Info : rtt: Searching for control block 'SEGGER RTT'
Info : rtt: Control block found at 0x2000145c
Info : Listening on port 9090 for rtt connections
Channels: up=1, down=3
Up-channels:
0: Terminal 1024 0
Down-channels:
0: Terminal 16 0
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections

Terminal2:

ms@LenovoP51Win11 MINGW64 /e/repos/trice/examples/L432_inst (devel)
$ trice l -p TCP4 -args localhost:9090  -pf none -d16
Nov 16 20:38:12.376056  TCP4:       triceExamples.c    10        1_595  Hello! 👋🙂
Nov 16 20:38:12.376056  TCP4:
Nov 16 20:38:12.376056  TCP4:         ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
Nov 16 20:38:12.376056  TCP4:         🎈🎈🎈🎈  𝕹𝖀𝕮𝕷𝕰𝕺-L432KC   🎈🎈🎈🎈
Nov 16 20:38:12.376056  TCP4:         🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃
Nov 16 20:38:12.376056  TCP4:
Nov 16 20:38:12.376056  TCP4:
Nov 16 20:38:13.891033  TCP4:       triceExamples.c    16       43_439 2.71828182845904523536 <- float number as string
Nov 16 20:38:14.874024  TCP4:       triceExamples.c    17       44_949 2.71828182845904509080 (double with more ciphers than precision)
Nov 16 20:38:15.692614  TCP4:       triceExamples.c    18       45_802 2.71828174591064453125 (float  with more ciphers than precision)
Nov 16 20:38:16.323665  TCP4:       triceExamples.c    19       46_536 2.718282 (default rounded float)

<h5>Using On-board ST-Link and VS-Code Cortex-Debug Extension</h5>

<h6>Fail</h6>

<h6>OK</h6>

(back to top)

37. Trice Generate

37.1. Colors

Support for finding a color style:

generateColors.PNG

See Check Alternatives chapter.

37.2. C-Code

If you intend to get the trice log functionality full or partially as a tlog C-Source and do not wish to parse the til.json file, you can run trice generate -tilH --tilC to create a C-file with header as starting point. That could be interesting for compiling the log functionality into a small separate microcontroller board.

//! \file til.c
//! ///////////////////////////////////////////////////////////////////////////

//! Trice generated code - do not edit!

#include "til.h"

//! triceFormatStringList contains all trice format strings together with id and parameter information.
//!
//! The bitWidth value is not transmitted in the binary data stream and needed for its decoding.
//! The paramCount is de-facto not needed. It is derivable from the received data, see docs/TriceUserManual.md#binary-encoding.
//! It is recommended to check if both values are matching. A negative paramCount indicates, that its value is unknown at compile time.
const triceFormatStringList_t triceFormatStringList[] = {
	/* Trice type (  extended  ) */  //  id, bitWidth, paramCount, format-string
	/*      trice (  trice32_9 ) */ { 14016,  32,  9, "rd:trice %d, %d, %d, %d, %d, %d, %d, %d, %d\n" },
	/*      trice (     trice0 ) */ { 14224,  32,  0, "\n" },
	/*    trice32 (  trice32_1 ) */ { 14337,  32,  1, "msg:%u (%%u)\n" },
	/*     TRICE8 (  TRICE8_10 ) */ { 15063,   8, 10, "rd:TRICE8 %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n" },
	/*     Trice8 (   Trice8_9 ) */ { 15124,   8,  9, "rd:Trice8 %d, %d, %d, %d, %d, %d, %d, %d, %d\n" },
	/*     TRICE8 (   TRICE8_5 ) */ { 15058,   8,  5, "rd:TRICE8 %d, %d, %d, %d, %d\n" },
	/*      TRice (     TRice0 ) */ { 14885,  32,  0, "TEST:yellow+h:black\n" },
	/*    Trice64 (  Trice64_1 ) */ { 15560,  64,  1, "rd:Trice64 %d\n" },
	/*      trice (  trice32_1 ) */ { 15860,  32,  1, "rd:TRICE float %9.f (%%9.f)\n" },
...
	/*  TRICE64_0 (  TRICE64_0 ) */ { 16157,  64,  0, "w: Hello! 👋🙂 \a\n" },
	/*      TRICE (     TRICE0 ) */ { 14658,  32,  0, "interrupt:magenta+i:default+h\n" },
};

//! triceFormatStringListElements holds the compile time computed count of list elements.
const unsigned triceFormatStringListElements = sizeof(triceFormatStringList) / sizeof(triceFormatStringList_t);

37.3. C#-Code

With trice generate -tilCS a starting point for a C-Sharp application is generated:

//! \file til.cs 

// Trice generated code - do not edit!

// There is still a need to exchange the format specifier from C to C# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// See https://stackoverflow.com/questions/33432341/how-to-use-c-language-format-specifiers-in-c-sharp
// and https://www.codeproject.com/Articles/19274/A-printf-implementation-in-C for possible help.

namespace TriceIDList;

	public class TilItem
	{
		public TilItem(int bitWidth, int paramCount, string strg)
		{
			BitWidth = bitWidth;
			ParamCount = paramCount;
			Strg = strg;
		}

		public int BitWidth { get; init; }
		public int ParamCount { get; init; }
		public string Strg { get; init; }
	}

	//! Til contains all trice format strings together with id and parameter information.
	//!
	//! The bitWidth value is not transmitted in the binary data stream and needed for its decoding.
	//! The paramCount is de-facto not needed. It is derivable from the received data, see docs/TriceUserManual.md#binary-encoding.
	//! It is recommended to check if both values are matching. A negative paramCount indicates, that its value is unknown at compile time.
	public static class Til
	{
		public static readonly Dictionary<int, TilItem> TilList= new Dictionary<int, TilItem>
		{ /* triceType ( extended ) */ //   id,     TilItem( bitWidth, paramCount, Strg )
		/*   TRICE_12 ( TRICE32_12 )*/ { 14991, new TilItem( 32, 12, "rd:TRICE_12 %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n" ) },
		/*      TRICE (  TRICE32_1 )*/ { 15636, new TilItem( 32,  1, "WR:write        message, SysTick is %6u\n" ) },
		/*    TRICE_S (    TRICE_S )*/ { 14178, new TilItem( 32, -1, "msg:With TRICE_S:%s\n" ) },
...
		/*    TRICE16 (  TRICE16_2 )*/ { 16056, new TilItem( 16,  2, "rd:TRICE16 %p, %p\n" ) },
    };
}

37.4. Generating a RPC Function Pointer List

When several embedded devices are going to communicate, trice generate -rpcH -rpcC could be helpful.

You will get 2 files similar to:

//! \file tilRpc.h
//! ///////////////////////////////////////////////////////////////////////////

//! Trice generated code - do not edit!

#include <stdint.h>

typedef void (*triceRpcHandler_t)(void* buffer, int count);

typedef struct{
    int id;
    triceRpcHandler_t fn;
} triceRpc_t;

extern triceRpc_t triceRpc[];
extern int triceRpcCount;

/*  TRICE16_F */ void FunctionNameXa( int16_t* p, int cnt );
/*  TRICE32_F */ void FunctionNameYa( int32_t* p, int cnt );
/*   TRICE8_F */ void TryoutBufferFunction( int8_t* p, int cnt );
/*   TRice16F */ void FunctionNameXb( int16_t* p, int cnt );
/*   trice32F */ void FunctionNameYd( int32_t* p, int cnt );
/*    TRice8F */ void FunctionNameWb( int8_t* p, int cnt );
/*   trice64F */ void FunctionNameZd( int64_t* p, int cnt );
/*   Trice16F */ void ARemoteFunctionName( int16_t* p, int cnt );
/*    Trice8F */ void FunctionNameWc( int8_t* p, int cnt );
/*   Trice16F */ void FunctionNameXc( int16_t* p, int cnt );
/*   TRICE8_F */ void TryoutStructFunction( int8_t* p, int cnt );
/*   Trice64F */ void FunctionNameZc( int64_t* p, int cnt );
/*    trice8F */ void FunctionNameWd( int8_t* p, int cnt );
/*   TRice64F */ void FunctionNameZb( int64_t* p, int cnt );
/*  TRICE64_F */ void FunctionNameZa( int64_t* p, int cnt );
/*   trice16F */ void FunctionNameXd( int16_t* p, int cnt );
/*   TRICE8_F */ void FunctionNameWa( int8_t* p, int cnt );
/*   TRice32F */ void FunctionNameYb( int32_t* p, int cnt );
/*   Trice32F */ void FunctionNameYc( int32_t* p, int cnt );

// End of file

//! \file tilRpc.c
//! ///////////////////////////////////////////////////////////////////////////

//! Trice generated code - do not edit!

#include <stdio.h> // needed for __attribute__((weak)) 
#include "tilRpc.h"

//! triceRpc contains all rpc IDs together with their function pointer address.
const triceRpc_t triceRpc[] = {
	/* Trice type */  //  id, function pointer
	/*    TRice8F */ { 14227, FunctionNameWb },
	/*  TRICE32_F */ { 14234, FunctionNameYa },
	/*   TRICE8_F */ { 16179, TryoutBufferFunction },
	/*   Trice16F */ { 14232, FunctionNameXc },
	/*   Trice64F */ { 14240, FunctionNameZc },
	/*   TRice64F */ { 14239, FunctionNameZb },
	/*  TRICE16_F */ { 14230, FunctionNameXa },
	/*   TRICE8_F */ { 16178, TryoutStructFunction },
	/*    Trice8F */ { 14228, FunctionNameWc },
	/*   trice16F */ { 14233, FunctionNameXd },
	/*   trice64F */ { 14241, FunctionNameZd },
	/*   trice32F */ { 14237, FunctionNameYd },
	/*   TRICE8_F */ { 14226, FunctionNameWa },
	/*   TRice16F */ { 14231, FunctionNameXb },
	/*   TRice32F */ { 14235, FunctionNameYb },
	/*   Trice16F */ { 16337, ARemoteFunctionName },
	/*    trice8F */ { 14229, FunctionNameWd },
	/*   Trice32F */ { 14236, FunctionNameYc },
	/*  TRICE64_F */ { 14238, FunctionNameZa }
};

//! triceRpcListElements holds the compile time computed count of list elements.
const unsigned triceRpcElements = sizeof(triceRpc) / sizeof(triceRpc_t);

void TryoutBufferFunction( int8_t* p, int cnt) __attribute__((weak)) {}
void FunctionNameXc( int16_t* p, int cnt) __attribute__((weak)) {}
void FunctionNameZc( int64_t* p, int cnt) __attribute__((weak)) {}
void FunctionNameZb( int64_t* p, int cnt) __attribute__((weak)) {}
void FunctionNameXa( int16_t* p, int cnt) __attribute__((weak)) {}
void TryoutStructFunction( int8_t* p, int cnt) __attribute__((weak)) {}
void FunctionNameWc( int8_t* p, int cnt) __attribute__((weak)) {}
void FunctionNameXd( int16_t* p, int cnt) __attribute__((weak)) {}
void FunctionNameZd( int64_t* p, int cnt) __attribute__((weak)) {}
void FunctionNameYd( int32_t* p, int cnt) __attribute__((weak)) {}
void FunctionNameWa( int8_t* p, int cnt) __attribute__((weak)) {}
void FunctionNameXb( int16_t* p, int cnt) __attribute__((weak)) {}
void FunctionNameYb( int32_t* p, int cnt) __attribute__((weak)) {}
void ARemoteFunctionName( int16_t* p, int cnt) __attribute__((weak)) {}
void FunctionNameWd( int8_t* p, int cnt) __attribute__((weak)) {}
void FunctionNameYc( int32_t* p, int cnt) __attribute__((weak)) {}
void FunctionNameZa( int64_t* p, int cnt) __attribute__((weak)) {}
void FunctionNameWb( int8_t* p, int cnt) __attribute__((weak)) {}
void FunctionNameYa( int32_t* p, int cnt) __attribute__((weak)) {}

// End of file

Assume a project with several devices. You can add these 2 files to all targets and if a special target should execute any functions, simply implement them. These functions on their own can execute other Trice statements to transmit results. If a client executes a RPC function this way, the request is transmitted with the Trice speed. Several target devices (servers) can receive and respond and the client can wait for the first or some of them. That server receiving and client waiting functionality is not part of the Trice library.

(back to top)

38. Testing the Trice Library C-Code for the Target

38.1. General info

This folder is per default named to _test to avoid vsCode slow down. Also, when running go test ./..., the tests in the _test folder are excluded, because they take a long time. Run ./testAll.sh to include them.

The main aim of these tests is to automatic compile and run the target code in different compiler switch variants avoiding manual testing this way.

testAll.sh quick performs just a short test. testAll.sh full runs all tests. That can take hours on a Windows PC because make is executed only as single thread there for stability reasons. On Darwin and Linux systems about an hour test duration should be expected.

For the user it could be helpful to start with a triceConfig.hfile from here and to adapt the Trice tool command line from the matching cgo_test.go if no close match in the examples folder was found.

38.2. How to run the tests

38.3. Tests Details

All folders despite testdata are test folders and the name tf is used as a place holder for them in this document.

To exclude a specific folder temporary, simply rename it to start with an underscore _tf.

The tf are serving for target code testing in different configuration variants on the host machine. The file ./testdata/triceCheck.c is the main file for most tests and serves also as example usage.

_test/testdata/cgoPackage.go is the common main for the generated_cgoPackage.go files and contains the common test code.

The folders tf are Go packages just for tests. They all have the same package name cgot and are not included into the trice tool. The different cgot packages are independent and could have any names. They do not see each other and are used for target code testing independently. When the tests are executed for each package, a separate test binary is build and these run parallel.

The tf/triceConfig.h files differ and correspondent to the tf/cgo_test.go files in the same folder. On test execution, the ./testdata/*.c files are compiled into the trice test executable together with the trice sources ../src using the tf/triceConfig.h file.

The individual tests collect the expected results (//exp: result) together with the line numbers into a slice to execute the test loop on it. The triceLogTest function gets the triceLog function as parameter.

triceLogTest iterates over the results slice and calls for each line the C-function triceCheck. Then the line specific binary data buffer is passed to the triceLog parameter function which “logs” the passed buffer into an actual result string which in turn is compared with the expected result.

The whole process is relatively slow because of the often passed Go - C barrier, but allows automated tests in different configuration variants in one shot.

The testdata\cgoPackage.go file contains a variable testLines = n, which limits the amount of performed trices for each test case to n. Changing this value will heavily influence the test duration. The value -1 is reserved for testing all test lines.

38.4. How to add new test cases

38.5. Test Internals

The ./trice/_test/testdata/*.c and ./trice/src/*.c are compiled together with the actual cgot package into one singe Trice test binary, resulting in as many test binaries as there are test folders. Calling its TestFunction(s) causes the activation of the Trice statement(s) inside triceCheck.c. The ususally into an embedded device compiled Trice code generates a few bytes according to the configuration into a buffer. These bytes are transmitted usually in real life over a (serial) port or RTT. In the tests here, this buffer is then read out by the Trice tool handler function according to the used CLI switches and processed to a log string using the til.json file. This string is then compared to the expected string for the activated line.

Each tf is a Go package, which is not part of any Go application. They all named cgot and are only used independently for testing different configurations. The tf/generated_cgoPackage.go file is identical in all tf. Its master is testdata/cgoPackage.go. After editing the master, running the command ./renewIDs_in_examples_and_test_folder.sh copies the master to all tf and renames it to generated_cgoPackage.go.

The test specific target code configuration is inside tf/trice.Config.h and the appropriate Trice tool CLI switches are in tf/cgo_test.go.

When running go test ./tf, a Trice tool test executable is build, using the Trice tool packages and the tf package cgot, and the function TestLogs is executed. Its internal closure triceLog contains the Trice tool CLI switches and is passed to the ccgot package function triceLogTest together with the number of testLines and the trice mode (directTransfer or deferrerdTransfer).

During the test, the file triceCheck.c is scanned for lines like

break; case __LINE__: TRice( iD(3537), "info:This is a message without values and a 32-bit stamp.\n" ); //exp: time: 842,150_450default: info:This is a message without values and a 32-bit stamp.

Some C-code lines contain Trice statements and comments starting with //exp: followed by the expected Trice tool output for that specific line. The Go testfunction collects these outputs in a slice together with the line numbers. Then for each found line number the execution of the Go function func triceCheck(n int) takes part, which in turn calls the CGO compiled C-function TriceCheck(n). The now activated Trice C-code writes the generated trice bytes in a between C and Go shared buffer using the C-function TriceWriteDeviceCgo. After returning from the Go function func triceCheck(n int) and optionally calling TriceTransfer in deferred mode the Trice tool triceLog() function converts the Trice buffer bytes to the log string and compares the result with the expected data. The between Go and C shared buffer limits the executed Trices per line to one, because they use the same buffer from the beginning. This could be done better with an increment to allow several trices in one single line.

Because each test runs a different configuration, all possible combinations are testable.

38.6. Test Results

ms@DESKTOP-7POEGPB MINGW64 ~/repos/trice (main)
$ ./testAll.sh
Thu, Dec 12, 2024  4:51:26 PM
This can take several minutes ...
?       github.com/rokath/trice/internal/decoder        [no test files]
?       github.com/rokath/trice/internal/do     [no test files]
?       github.com/rokath/trice/internal/translator     [no test files]
?       github.com/rokath/trice/pkg/ant [no test files]
ok      github.com/rokath/trice/cmd/trice       1.392s
ok      github.com/rokath/trice/internal/args   0.415s
ok      github.com/rokath/trice/internal/charDecoder    0.298s
ok      github.com/rokath/trice/internal/com    15.845s
ok      github.com/rokath/trice/internal/dumpDecoder    0.339s
ok      github.com/rokath/trice/internal/emitter        0.326s
ok      github.com/rokath/trice/internal/id     3.088s
ok      github.com/rokath/trice/internal/keybcmd        0.233s
ok      github.com/rokath/trice/internal/link   0.196s
ok      github.com/rokath/trice/internal/receiver       0.246s
?       github.com/rokath/trice/internal/translator     [no test files]
?       github.com/rokath/trice/pkg/ant [no test files]
ok      github.com/rokath/trice/cmd/trice       1.392s
ok      github.com/rokath/trice/internal/args   0.415s
ok      github.com/rokath/trice/internal/charDecoder    0.298s
ok      github.com/rokath/trice/internal/com    15.845s
ok      github.com/rokath/trice/internal/dumpDecoder    0.339s
ok      github.com/rokath/trice/internal/emitter        0.326s
ok      github.com/rokath/trice/internal/id     3.088s
ok      github.com/rokath/trice/internal/keybcmd        0.233s
ok      github.com/rokath/trice/internal/link   0.196s
ok      github.com/rokath/trice/internal/receiver       0.246s
ok      github.com/rokath/trice/internal/trexDecoder    0.264s
ok      github.com/rokath/trice/pkg/cipher      0.230s
ok      github.com/rokath/trice/pkg/endian      0.161s
ok      github.com/rokath/trice/internal/args   0.415s
ok      github.com/rokath/trice/internal/charDecoder    0.298s
ok      github.com/rokath/trice/internal/com    15.845s
ok      github.com/rokath/trice/internal/dumpDecoder    0.339s
ok      github.com/rokath/trice/internal/emitter        0.326s
ok      github.com/rokath/trice/internal/id     3.088s
ok      github.com/rokath/trice/internal/keybcmd        0.233s
ok      github.com/rokath/trice/internal/link   0.196s
ok      github.com/rokath/trice/internal/receiver       0.246s
ok      github.com/rokath/trice/internal/trexDecoder    0.264s
ok      github.com/rokath/trice/pkg/cipher      0.230s
ok      github.com/rokath/trice/pkg/endian      0.161s
ok      github.com/rokath/trice/internal/id     3.088s
ok      github.com/rokath/trice/internal/keybcmd        0.233s
ok      github.com/rokath/trice/internal/link   0.196s
ok      github.com/rokath/trice/internal/receiver       0.246s
ok      github.com/rokath/trice/internal/trexDecoder    0.264s
ok      github.com/rokath/trice/pkg/cipher      0.230s
ok      github.com/rokath/trice/pkg/endian      0.161s
ok      github.com/rokath/trice/pkg/msg 0.157s
ok      github.com/rokath/trice/pkg/tst 0.261s
ok      github.com/rokath/trice/_test/be_dblB_de_tcobs_ua       123.142s
ok      github.com/rokath/trice/_test/be_staticB_di_xtea_cobs_rtt32     123.159s
ok      github.com/rokath/trice/internal/trexDecoder    0.264s
ok      github.com/rokath/trice/pkg/cipher      0.230s
ok      github.com/rokath/trice/pkg/endian      0.161s
ok      github.com/rokath/trice/pkg/msg 0.157s
ok      github.com/rokath/trice/pkg/tst 0.261s
ok      github.com/rokath/trice/_test/be_dblB_de_tcobs_ua       123.142s
ok      github.com/rokath/trice/_test/be_staticB_di_xtea_cobs_rtt32     123.159s
ok      github.com/rokath/trice/pkg/msg 0.157s
ok      github.com/rokath/trice/pkg/tst 0.261s
ok      github.com/rokath/trice/_test/be_dblB_de_tcobs_ua       123.142s
ok      github.com/rokath/trice/_test/be_staticB_di_xtea_cobs_rtt32     123.159s
ok      github.com/rokath/trice/_test/dblB_de_cobs_ua   122.964s
ok      github.com/rokath/trice/_test/dblB_de_multi_cobs_ua     123.308s
ok      github.com/rokath/trice/_test/be_dblB_de_tcobs_ua       123.142s
ok      github.com/rokath/trice/_test/be_staticB_di_xtea_cobs_rtt32     123.159s
ok      github.com/rokath/trice/_test/dblB_de_cobs_ua   122.964s
ok      github.com/rokath/trice/_test/dblB_de_multi_cobs_ua     123.308s
ok      github.com/rokath/trice/_test/dblB_de_cobs_ua   122.964s
ok      github.com/rokath/trice/_test/dblB_de_multi_cobs_ua     123.308s
ok      github.com/rokath/trice/_test/dblB_de_multi_cobs_ua     123.308s
ok      github.com/rokath/trice/_test/dblB_de_multi_nopf_ua     123.244s
ok      github.com/rokath/trice/_test/dblB_de_multi_nopf_ua     123.244s
ok      github.com/rokath/trice/_test/dblB_de_multi_tcobs_ua    123.109s
ok      github.com/rokath/trice/_test/dblB_de_multi_tcobs_ua    123.109s
ok      github.com/rokath/trice/_test/dblB_de_multi_xtea_cobs_ua        123.213s
ok      github.com/rokath/trice/_test/dblB_de_multi_xtea_tcobs_ua       123.001s
ok      github.com/rokath/trice/_test/dblB_de_multi_xtea_cobs_ua        123.213s
ok      github.com/rokath/trice/_test/dblB_de_multi_xtea_tcobs_ua       123.001s
ok      github.com/rokath/trice/_test/dblB_de_nopf_ua   123.092s
ok      github.com/rokath/trice/_test/dblB_de_multi_xtea_tcobs_ua       123.001s
ok      github.com/rokath/trice/_test/dblB_de_nopf_ua   123.092s
ok      github.com/rokath/trice/_test/dblB_de_tcobs_ua  122.324s
ok      github.com/rokath/trice/_test/dblB_de_nopf_ua   123.092s
ok      github.com/rokath/trice/_test/dblB_de_tcobs_ua  122.324s
ok      github.com/rokath/trice/_test/dblB_de_tcobs_ua  122.324s
ok      github.com/rokath/trice/_test/dblB_de_xtea_cobs_ua      123.149s
ok      github.com/rokath/trice/_test/dblB_de_xtea_tcobs_ua     122.883s
ok      github.com/rokath/trice/_test/dblB_de_xtea_cobs_ua      123.149s
ok      github.com/rokath/trice/_test/dblB_de_xtea_tcobs_ua     122.883s
ok      github.com/rokath/trice/_test/dblB_di_nopf_rtt32__de_cobs_ua    246.703s
ok      github.com/rokath/trice/_test/dblB_de_xtea_tcobs_ua     122.883s
ok      github.com/rokath/trice/_test/dblB_di_nopf_rtt32__de_cobs_ua    246.703s
ok      github.com/rokath/trice/_test/dblB_di_nopf_rtt32__de_cobs_ua    246.703s
ok      github.com/rokath/trice/_test/dblB_di_nopf_rtt32__de_multi_cobs_ua      247.125s
ok      github.com/rokath/trice/_test/dblB_di_nopf_rtt32__de_multi_tcobs_ua     246.862s
ok      github.com/rokath/trice/_test/dblB_di_nopf_rtt32__de_tcobs_ua   246.531s
ok      github.com/rokath/trice/_test/dblB_di_nopf_rtt32__de_xtea_cobs_ua       247.072s
ok      github.com/rokath/trice/_test/dblB_di_nopf_rtt8__de_cobs_ua     246.639s
ok      github.com/rokath/trice/_test/dblB_di_nopf_rtt8__de_multi_cobs_ua       246.599s
ok      github.com/rokath/trice/_test/dblB_di_nopf_rtt8__de_multi_tcobs_ua      247.114s
ok      github.com/rokath/trice/_test/dblB_di_nopf_rtt8__de_tcobs_ua    246.851s
ok      github.com/rokath/trice/_test/ringB_de_cobs_ua  123.578s
ok      github.com/rokath/trice/_test/ringB_de_multi_tcobs_ua   123.517s
ok      github.com/rokath/trice/_test/ringB_de_multi_xtea_cobs_ua       123.497s
ok      github.com/rokath/trice/_test/ringB_de_multi_xtea_tcobs_ua      123.379s
ok      github.com/rokath/trice/_test/ringB_de_nopf_ua  123.555s
ok      github.com/rokath/trice/_test/ringB_de_tcobs_ua 123.300s
ok      github.com/rokath/trice/_test/ringB_de_xtea_cobs_ua     123.487s
ok      github.com/rokath/trice/_test/ringB_de_xtea_tcobs_ua    123.846s
ok      github.com/rokath/trice/_test/ringB_di_cobs_rtt32__de_tcobs_ua  247.400s
ok      github.com/rokath/trice/_test/ringB_di_cobs_rtt8__de_tcobs_ua   247.202s
ok      github.com/rokath/trice/_test/ringB_di_nopf_rtt32__de_tcobs_ua  247.204s
ok      github.com/rokath/trice/_test/ringB_di_nopf_rtt32__de_xtea_cobs_ua      246.818s
ok      github.com/rokath/trice/_test/ringB_di_nopf_rtt8__de_tcobs_ua   247.006s
ok      github.com/rokath/trice/_test/ringB_di_tcobs_rtt32__de_tcobs_ua 247.000s
ok      github.com/rokath/trice/_test/ringB_di_xtea_cobs_rtt32__de_xtea_cobs_ua 246.872s
ok      github.com/rokath/trice/_test/special_protect_dblB_de_tcobs_ua  0.444s
ok      github.com/rokath/trice/_test/stackB_di_nopf_aux32      123.819s
ok      github.com/rokath/trice/_test/stackB_di_nopf_aux8       123.830s
ok      github.com/rokath/trice/_test/stackB_di_nopf_rtt32      123.912s
ok      github.com/rokath/trice/_test/stackB_di_nopf_rtt8       123.976s
ok      github.com/rokath/trice/_test/stackB_di_xtea_cobs_rtt8  123.719s
ok      github.com/rokath/trice/_test/staticB_di_nopf_aux32     123.553s
ok      github.com/rokath/trice/_test/staticB_di_nopf_aux8      123.551s
ok      github.com/rokath/trice/_test/staticB_di_nopf_rtt32     123.596s
ok      github.com/rokath/trice/_test/staticB_di_nopf_rtt8      123.618s
ok      github.com/rokath/trice/_test/staticB_di_tcobs_rtt32    123.177s
ok      github.com/rokath/trice/_test/staticB_di_tcobs_rtt8     123.353s
ok      github.com/rokath/trice/_test/staticB_di_xtea_cobs_rtt32        123.126s

real    10m31.130s
user    0m0.000s
sys     0m0.015s

ms@DESKTOP-7POEGPB MINGW64 ~/repos/trice (main)
$

38.7. Special tests

38.8. Test Cases

38.8.1. Folder Naming Convention

Folder Name Part Meaning
testdata This is no test folder. It contains data common to all tests.
_... Folder starting with an undescore _ are excluded when go test ./... is executed.
_di_ direct mode
_de_ deferred mode
special_ a test, not using ./testdata/triceCheck.c
staticB_ static buffer, direct mode only possible
stackB_ stack buffer, direct mode only possible
ringB_ ring buffer, deferred mode and optional parallel direct mode
dblB_ double buffer, deferred mode and optional parallel direct mode
_rtt8_ (simulated) SEGGER_RTT byte transfer
_rtt32_ (simulated) SEGGER_RTT word transfer
__ direct and deferred mode together
_xtea_ with encryption, otherwise without encryption
_tcobs TCOBS package framing
_cobs COBS package framing
_nopf no package framing
_multi_ Usually each Trice is handled separately. In multi mode, groups of available Trices are framed together.
_ua simulated UART A output (for deferred modes)

(back to top)

39. Test Issues

Test folders starting with ERROR_ have issues. These cases are usable on the target. These tests fail for an unknown reason. Probably it is a test implementation issue. Especially when XTEA is used in one output but not in the other, the tests fail.

(back to top)

40. Add-On Hints

40.1. Trice on LibOpenCM3

LibOpenCM3 is a hardware abstraction library for many microcontrollers.

This is an exampe using STM’s STM32F411 Nucleo board.

--> This code uses a legacy Trice version and needs adaption!

40.1.1. Prerequisites

40.1.2. triceConfig.h

/*! \file triceConfig.h
\author Thomas.Hoehenleitner [at] seerose.net
LibOpenCM3 adapatation by Kalle Raiskila.
*******************************************************************************/

#ifndef TRICE_CONFIG_H_
#define TRICE_CONFIG_H_

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include <libopencm3/cm3/cortex.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>

// Local (to this demo) time keeping functions
#include "time.h"

#define TRICE_UART USART2 //!< Enable and set UART for serial output.
// The alternative, TRICE_RTT_CHANNEL is not available with OpenCM3
// #define TRICE_RTT_CHANNEL 0

// Timestamping function to be provided by user. In this demo from time.h
#define TRICE_TIMESTAMP wallclock_ms() // todo: replace with TRICE_TREX_ENCODING stuff

// Enabling next 2 lines results in XTEA TriceEncryption  with the key.
// #define TRICE_ENCRYPT XTEA_KEY( ea, bb, ec, 6f, 31, 80, 4e, b9, 68, e2, fa, ea, ae, f1, 50, 54 ); //!< -password MySecret
// #define TRICE_DECRYPT //!< TRICE_DECRYPT is usually not needed. Enable for checks.

// #define TRICE_BIG_ENDIANNESS //!< TRICE_BIG_ENDIANNESS needs to be defined for TRICE64 macros on big endian devices. (Untested!)

//
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Predefined trice modes: Adapt or creeate your own trice mode.
//
#ifndef TRICE_MODE
#error Define TRICE_MODE to 0, 200 or 201
#endif

//! Direct output to UART or RTT with cycle counter. Trices inside interrupts forbidden. Direct TRICE macro execution.
//! This mode is mainly for a quick tryout start or if no timing constrains for the TRICE macros exist.
//! Only a putchar() function is required - look for triceBlockingPutChar().
//! UART Command line similar to: `trice log -p COM1 -baud 115200`
//! RTT needs additional tools installed - see RTT documentation.
//! J-LINK Command line similar to: `trice log -args="-Device STM32G071RB -if SWD -Speed 4000 -RTTChannel 0 -RTTSearchRanges 0x20000000_0x1000"`
//! ST-LINK Command line similar to: `trice log -p ST-LINK -args="-Device STM32G071RB -if SWD -Speed 4000 -RTTChannel 0 -RTTSearchRanges 0x20000000_0x1000"`
#if TRICE_MODE == 0                     // must not use TRICE_ENCRYPT!
#define TRICE_STACK_BUFFER_MAX_SIZE 128 //!< This  minus TRICE_DATA_OFFSET the max allowed single trice size. Usually ~40 is enough.
#ifndef TRICE_ENTER
#define TRICE_ENTER                                                                          \
	{                                                  /*! Start of TRICE macro */           \
		uint32_t co[TRICE_STACK_BUFFER_MAX_SIZE >> 2]; /* Check TriceDepthMax at runtime. */ \
		uint32_t* TriceBufferWritePosition = co + (TRICE_DATA_OFFSET >> 2);
#endif
#ifndef TRICE_LEAVE
#define TRICE_LEAVE                                                                 \
	{ /*! End of TRICE macro */                                                     \
		unsigned tLen = ((TriceBufferWritePosition - co) << 2) - TRICE_DATA_OFFSET; \
		TriceOut(co, tLen);                                                         \
	}                                                                               \
	}
#endif
#endif // #if TRICE_MODE == 0

//! Double Buffering output to RTT or UART with cycle counter. Trices inside interrupts allowed. Fast TRICE macro execution.
//! UART Command line similar to: `trice log -p COM1 -baud 115200`
//! RTT Command line similar to: `trice l -args="-Device STM32F030R8 -if SWD -Speed 4000 -RTTChannel 0 -RTTSearchRanges 0x20000000_0x1000"`
#if TRICE_MODE == 200
#ifndef TRICE_ENTER
#define TRICE_ENTER TRICE_ENTER_CRITICAL_SECTION //! TRICE_ENTER is the start of TRICE macro. The TRICE macros are a bit slower. Inside interrupts TRICE macros allowed.
#endif
#ifndef TRICE_LEAVE
#define TRICE_LEAVE TRICE_LEAVE_CRITICAL_SECTION //! TRICE_LEAVE is the end of TRICE macro.
#endif
#define TRICE_HALF_BUFFER_SIZE 1000 //!< This is the size of each of both buffers. Must be able to hold the max TRICE burst count within TRICE_TRANSFER_INTERVAL_MS or even more, if the write out speed is small. Must not exceed SEGGER BUFFER_SIZE_UP
#define TRICE_SINGLE_MAX_SIZE 100   //!< must not exeed TRICE_HALF_BUFFER_SIZE!
#endif                              // #if TRICE_MODE == 200

//! Double Buffering output to UART without cycle counter. No trices inside interrupts allowed. Fastest TRICE macro execution.
//! Command line similar to: `trice log -p COM1 -baud 115200`
#if TRICE_MODE == 201
#define TRICE_CYCLE_COUNTER 0       //! Do not add cycle counter, The TRICE macros are a bit faster. Lost TRICEs are not detectable by the trice tool.
#define TRICE_ENTER                 //! TRICE_ENTER is the start of TRICE macro. The TRICE macros are a bit faster. Inside interrupts TRICE macros forbidden.
#define TRICE_LEAVE                 //! TRICE_LEAVE is the end of TRICE macro.
#define TRICE_HALF_BUFFER_SIZE 2000 //!< This is the size of each of both buffers. Must be able to hold the max TRICE burst count within TRICE_TRANSFER_INTERVAL_MS or even more, if the write out speed is small. Must not exceed SEGGER BUFFER_SIZE_UP
#define TRICE_SINGLE_MAX_SIZE 800   //!< must not exeed TRICE_HALF_BUFFER_SIZE!
#endif                              // #if TRICE_MODE == 201

//
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Headline info
//

#ifdef TRICE_HALF_BUFFER_SIZE
#define TRICE_BUFFER_INFO                                                              \
	do {                                                                               \
		TRICE32(Id(0), "att: Trice 2x half buffer size:%4u ", TRICE_HALF_BUFFER_SIZE); \
	} while (0)
#else
#define TRICE_BUFFER_INFO                                                                                 \
	do {                                                                                                  \
		TRICE32(Id(0), "att:Single Trice Stack buf size:%4u", TRICE_SINGLE_MAX_SIZE + TRICE_DATA_OFFSET); \
	} while (0)
#endif

//! This is usable as the very first trice sequence after restart. Adapt and use it or ignore it.
#define TRICE_HEADLINE                                                           \
	TRICE0(Id(0), "s:                                          \n");             \
	TRICE8(Id(0), "s:     NUCLEO-F411RE     TRICE_MODE %3u     \n", TRICE_MODE); \
	TRICE0(Id(0), "s:                                          \n");             \
	TRICE0(Id(0), "s:     ");                                                    \
	TRICE_BUFFER_INFO;                                                           \
	TRICE0(Id(0), "s:     \n");                                                  \
	TRICE0(Id(0), "s:                                          \n");

//
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Compiler Adaption
//

#if defined(__GNUC__) /* gnu compiler ###################################### */

#define TRICE_INLINE static inline //! used for trice code

#define ALIGN4                                 //!< align to 4 byte boundary preamble
#define ALIGN4_END __attribute__((aligned(4))) //!< align to 4 byte boundary post declaration

//! TRICE_ENTER_CRITICAL_SECTION saves interrupt state and disables Interrupts.
#define TRICE_ENTER_CRITICAL_SECTION               \
	{                                              \
		uint32_t old_mask = cm_mask_interrupts(1); \
		{

//! TRICE_LEAVE_CRITICAL_SECTION restores interrupt state.
#define TRICE_LEAVE_CRITICAL_SECTION \
	}                                \
	cm_mask_interrupts(old_mask);    \
	}

#else
#error unknown compliler
#endif // compiler adaptions ##################################################

//
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Optical feedback: Adapt to your device.
//

TRICE_INLINE void ToggleOpticalFeedbackLED(void) {
	// The only user controllable LED available on the
	// Nucleo is LD2, on port A5. This is set up in main.c
	gpio_toggle(GPIOA, GPIO5);
}

//
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// UART interface: Adapt to your device.
//

#ifdef TRICE_UART

//! Check if a new byte can be written into trice transmit register.
//! \retval 0 == not empty
//! \retval !0 == empty
//! User must provide this function.
TRICE_INLINE uint32_t triceTxDataRegisterEmpty(void) {
	uint32_t reg = USART_SR(TRICE_UART);
	return (reg & USART_SR_TXE);
}

//! Write value v into trice transmit register.
//! \param v byte to transmit
//! User must provide this function.
TRICE_INLINE void triceTransmitData8(uint8_t v) {
	usart_send_blocking(TRICE_UART, v);
	ToggleOpticalFeedbackLED();
}

//! Allow interrupt for empty trice data transmit register.
//! User must provide this function.
TRICE_INLINE void triceEnableTxEmptyInterrupt(void) {
	usart_enable_tx_interrupt(TRICE_UART);
}

//! Disallow interrupt for empty trice data transmit register.
//! User must provide this function.
TRICE_INLINE void triceDisableTxEmptyInterrupt(void) {
	usart_disable_tx_interrupt(TRICE_UART);
}

#endif // #ifdef TRICE_UART

///////////////////////////////////////////////////////////////////////////////
// Default TRICE macro bitwidth: 32 (optionally adapt to MCU bit width)
//

#define TRICE_1 TRICE32_1   //!< Default parameter bit width for 1  parameter count TRICE is 32, change for a different value.
#define TRICE_2 TRICE32_2   //!< Default parameter bit width for 2  parameter count TRICE is 32, change for a different value.
#define TRICE_3 TRICE32_3   //!< Default parameter bit width for 3  parameter count TRICE is 32, change for a different value.
#define TRICE_4 TRICE32_4   //!< Default parameter bit width for 4  parameter count TRICE is 32, change for a different value.
#define TRICE_5 TRICE32_5   //!< Default parameter bit width for 5  parameter count TRICE is 32, change for a different value.
#define TRICE_6 TRICE32_6   //!< Default parameter bit width for 6  parameter count TRICE is 32, change for a different value.
#define TRICE_7 TRICE32_7   //!< Default parameter bit width for 7  parameter count TRICE is 32, change for a different value.
#define TRICE_8 TRICE32_8   //!< Default parameter bit width for 8  parameter count TRICE is 32, change for a different value.
#define TRICE_9 TRICE32_9   //!< Default parameter bit width for 9  parameter count TRICE is 32, change for a different value.
#define TRICE_10 TRICE32_10 //!< Default parameter bit width for 10 parameter count TRICE is 32, change for a different value.
#define TRICE_11 TRICE32_11 //!< Default parameter bit width for 11 parameter count TRICE is 32, change for a different value.
#define TRICE_12 TRICE32_12 //!< Default parameter bit width for 12 parameter count TRICE is 32, change for a different value.

//
///////////////////////////////////////////////////////////////////////////////

#ifdef __cplusplus
}
#endif

#endif /* TRICE_CONFIG_H_ */

40.1.3. main.c

/*
 * Demo to test/show TRICE usage in a libopencm3
 * environment.
 */

#include <libopencm3/cm3/systick.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/exti.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/cm3/nvic.h>

#include <stdint.h>
void msleep(uint32_t delay);
uint32_t wallclock_ms(void);

#include "trice.h"

static void hardware_setup(void)
{
	/* Set device clocks from opencm3 provided preset.*/
	const struct rcc_clock_scale *clocks = &rcc_hsi_configs[RCC_CLOCK_3V3_84MHZ];
	rcc_clock_setup_pll( clocks );

	/* Set up driving the LED connected to port A, pin 5. */
	rcc_periph_clock_enable(RCC_GPIOA);
	gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO5);

	/* User-button is connected to port C, pin 13. Set up button push
	 * to cause an interrupt. */
	gpio_mode_setup(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO13);
	rcc_periph_clock_enable(RCC_SYSCFG);  // clock for the EXTI handler
	nvic_enable_irq(NVIC_EXTI15_10_IRQ);
	exti_select_source(EXTI13, GPIOC);
	exti_set_trigger(EXTI13, EXTI_TRIGGER_FALLING);
	exti_enable_request(EXTI13);

	/* USART2 is connected to the nucleo's onboard ST-Link, which forwards
	 * it as a serial terminal over the ST-Link USB connection.
	 * This UART is given to the Trice data. */
	rcc_periph_clock_enable(RCC_USART2);
	usart_set_baudrate(USART2, 115200);
	usart_set_databits(USART2, 8);
	usart_set_stopbits(USART2, USART_STOPBITS_1);
	usart_set_mode(USART2, USART_MODE_TX);
	usart_set_parity(USART2, USART_PARITY_NONE);
	usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE);

	// Enable UART2 interrupts in the system's interrupt controller
	// but do NOT enable generating interrupts in the UART at this
	// time. Let Trice enable them with triceEnableTxEmptyInterrupt()
	nvic_enable_irq(NVIC_USART2_IRQ);
	//usart_enable_tx_interrupt(USART2);
	usart_enable(USART2);

	/* Configure USART TX pin only. We don't get any input via the TRICE
	 * channel, so the RX pin can be left unconnected to the USART2 */
	gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO2);
	gpio_set_af(GPIOA, GPIO_AF7, GPIO2);

	/* Enable systick at a 1mS interrupt rate */
	systick_set_reload(84000);
	systick_set_clocksource(STK_CSR_CLKSOURCE_AHB);
	systick_counter_enable();
	systick_interrupt_enable();
}

//////////////////////////
// Time handling utilities
static volatile uint32_t system_millis;

/* "sleep" for delay milliseconds */
void msleep(uint32_t delay)
{
	uint32_t wake = system_millis + delay;
	while (wake > system_millis);
}

uint32_t wallclock_ms(void)
{
	return system_millis;
}

//////////////////////////
// Interupt handlers
// These are weak symbols in libopencm3
// that get overridden here.

// Trice USART
void usart2_isr(void)
{
	#if TRICE_MODE == 200
	triceServeTransmit();
	#endif
}

// External interrupts on pins 10-15, all ports.
// Only PC13 (User button on Nucleo) is enabled in this program.
void exti15_10_isr(void)
{
	exti_reset_request(EXTI13);
	#if TRICE_MODE == 200
	TRICE(Id(0), "Button press at, %d\n", system_millis);
	#endif
}

// Systick timer set to 1ms
void sys_tick_handler(void)
{
	system_millis++;
	#if TRICE_MODE == 200
	// Start sending what is currently in the Trice transmit buffer
	triceTriggerTransmit();
	#endif
}

int main(void)
{
	hardware_setup();
	TRICE_HEADLINE;
	while (1) {
		msleep(1000);

		// Depending on mode, either print this string to
		// UART (mode 0), or the Trice write buffer (mode 200).
		TRICE(Id(0), "Hello, TRICE, %d\n", 42);

		// TRICE("") with a string parameter only is problematic.
		// See discussion on https://github.com/rokath/trice/issues/279
		// TRICE0("") works in either case
		#ifdef __STRICT_ANSI__
		// if compiled with e.g. --std=c99
		TRICE0(Id(0), "Hello, TRICE\n");
		#else
		TRICE(Id(0), "Hello, TRICE\n");
		TRICE0(Id(0), "Hello, TRICE0()\n");
		#endif

		#if TRICE_MODE == 200
		// Swap Trice transmit/write ping-pong buffers.
		// Stuff printed with TRICE() since the last
		// call to TriceTransfer() will be sent once
		// triceTriggerTransmit() is called.
		TriceTransfer();
		#endif
	}

	return 0;
}

40.1.4. nucleo-f411re.ld

/* Use the LibOpenCM3-provided defaults for the linker details.
 */
MEMORY
{
	rom (rx)  : ORIGIN = 0x08000000, LENGTH = 512K
	ram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}

INCLUDE cortex-m-generic.ld

40.1.5. Makefile

# Makefile for compiling the Trice demo on LibOpenCM3
# for STM32F411-Nucleo boards
CC=arm-none-eabi-gcc
C_FLAGS=-O0 -std=c99 -ggdb3
C_FLAGS+=-mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16
C_FLAGS+=-Wextra -Wshadow -Wimplicit-function-declaration -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes
C_FLAGS+=-fno-common -ffunction-sections -fdata-sections  -MD -Wall -Wundef
C_FLAGS+=-DSTM32F4 -I/home/kraiskil/stuff/libopencm3/include
# These two are for trice.h and triceConfig.h
C_FLAGS+=-I../../pkg/src/ -I.

LFLAGS=-L${OPENCM3_DIR}/lib -lopencm3_stm32f4 -lm -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group
LFLAGS+=-T nucleo-f411re.ld
LFLAGS+=--static -nostartfiles
LFLAGS+=-Wl,-Map=memorymap.txt

all: direct_mode.elf irq_mode.elf
.PHONY: flash clean

# Annotate Trice-enabled code.
# trice does this annotation in-place, so here we take
# a copy before running trice.
# I.e. write TRICE macros in foo.c, and this will generate
# the TRICE( Id(1234) .. ) macros into foo.trice.c
%.trice.c: %.c til.json
	cp -f $< $<.bak
	trice update
	cp -f $< $@
	cp -f $<.bak $<

# trice expects this file to exist, can be empty.
til.json:
	touch til.json

direct_mode.elf: main.trice.c ../../pkg/src/trice.c
	${CC} ${C_FLAGS} $^ -o $@ ${LFLAGS} -DTRICE_MODE=0

flash_direct_mode: direct_mode.elf
	openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg -c "program direct_mode.elf verify reset exit"

irq_mode.elf: main.trice.c ../../pkg/src/trice.c
	${CC} ${C_FLAGS} $^ -o $@ ${LFLAGS} -DTRICE_MODE=200

flash_irq_mode: irq_mode.elf
	openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg -c "program irq_mode.elf verify reset exit"


clean:
	@rm -f *.elf til.json main.trice.c

40.1.6. Usage

40.2. Get all project files containing Trice messages

We check the location information file. Every Trice is registered here.

cat demoLI.json | grep '"File":' | sort | uniq
		"File": "_test/_ringB_protect_de_tcobs_ua/TargetActivity.c",
		"File": "_test/special_dblB_de_tcobs_ua/TargetActivity.c",
		"File": "_test/special_for_debug/TargetActivity.c",
		"File": "_test/special_protect_dblB_de_tcobs_ua/TargetActivity.c",
		"File": "_test/testdata/triceCheck.c",
		"File": "examples/F030_inst/Core/Src/stm32f0xx_it.c",
		"File": "examples/G0B1_inst/Core/Src/main.c",
		"File": "examples/G0B1_inst/Core/Src/stm32g0xx_it.c",
		"File": "examples/L432_inst/Core/Inc/triceConfig.h",
		"File": "examples/L432_inst/Core/Src/main.c",
		"File": "examples/L432_inst/Core/Src/stm32l4xx_it.c",
		"File": "examples/exampleData/triceExamples.c",
		"File": "examples/exampleData/triceLogDiagData.c",

40.3. Building a trice library?

The triceConfig.h is mandatory for the trice code. It controls which parts of the trice code are included. There is no big advantage having a trice library, because it would work only with unchanged settings in the project specific triceConfig.h. Once the trice source files are translated, their objects are rebuilt automatically and only when the triceConfig.h is changed. So only the linker has a bit less to do when it finds a trice library compared to a bunch of trice objects. But does that influence the build time heavily?

The triceConfig.h is the only part of the trice sources which should be modified by the users. It is ment to be a individual part of the user projects. The examples folder shows the usage.

40.4. Possible Compiler Issue when using Trice macros without parameters on old compiler or with strict-C settings

If you encounter a compilation error on trice( "hi"); for example, but not on trice( "%u stars", 5 );, this is probably caused by the way your compiler interprets variadic macros. Simply change to trice0( "hi"); or change your compiler settings. See issue #279 for more details. If your project needs to be translated with strict-C settings for some reason, you have to use the trice0 macros when no values exist for the Trice macros.

(back to top)

41. Trice And Legacy User Code

When it comes to use legacy sources together with Trice, there are several ways doing so, which do not exclude each other:

41.1. Legacy User Code Option Separate Physical Output Channel

Advantages:

Disadvantages:

Details:

41.2. Legacy User Code Option Trice Adaption Edits

Advantages:

Disadvantages:

Details:

41.3. Legacy User Code Option Print Buffer Wrapping and Framing

Trice >= v1.1 feature, see also issue #550

Advantages:

Disadvantages:

Details:

The Trice binary encoding uses states 1, 2, 3 of the 4 states, the 2 Binary Encoding stamp selector bits can have. They located in the starting uint16_t ID value to encode the Trice (time) stamp size. If both bits are zero (state 0), the Trice tool can interpret the incoming data buffer according to a passed CLI switch; in this special case just printing it as string.

If the Trice library and the user print both write to the same output, an easy modification would be, to prepend the user print output with a 2-byte count as long its size is < 16383, so that the 2 most significant bits are zero. Additionally, the this way counted buffer needs the same buffer framing as the Trice binary data.

41.4. Legacy User Code Option Trice Aliases Adaption

Trice >= v1.1 feature, see also accepted pull requests #533 and #536

Advantages:

Disadvantages:

Details:

This cool functionality was contributed by @srgg in pull requests (PR) #533 and #536 (to be considered as one PR only). It allows code integration containing user specific log statements into Trice instrumented projects without the need to rename the user specific log statements.

In the assumption, most user printi statements having only up to 12 integers, those user prints could get covered by adding -alias printi to the trice insert and trice clean commands.

The user printi statements containing floats, doubles, strings could get simply renamed into user prints and then -salias prints will cover them too. That, of course, is a legacy user code change, but it allows to use this slightly modified legacy user code parallel in other projects.

Yes, user printi and user prints need to be defined too. See ./_test/alias_dblB_de_tcobs_ua/triceConfig.h/triceConfig as a simple example and its usage in ./_test/alias_dblB_de_tcobs_ua/TargetActivity.c

This technique allows also to cover legacy user code specific ASSERT macros, as shown in ./_test/aliasassert_dblB_de_tcobs_ua/triceConfig.h and used in the tests ./_test/aliasassert_dblB_de_tcobs_ua/TargetActivity.c.

Despite of these 2 CGO tests the real-world example ./examples/G0B1_inst shows the usage too.

The following sub-chapters are mainly written by @srgg as accompanying documentation to its pull requests.

41.4.1. PR533 Doc

41.4.2. PR533 Summary

This PR introduces support for treating user-defined macros as aliases to trice and triceS within the Trice CLI toolchain. The goal is to enable project-specific logging macros to be processed just like built-in Trice macros — including ID generation, decoding, and binary format support — without requiring projects to directly call trice() or triceS() in their source code.

PR leverages the -exclude source feature added in #529.

41.4.3. PR533 Motivation

Trice uses a source-scanning and ID generation approach, where the toolchain scans for trice(...) and triceS(...) calls, injects numeric trace IDs, and builds a mapping database. However, it currently only supports built-in(hardcoded) macros and allows only global on/off control via compile-time flags.

This makes it difficult to:

41.4.4. What This PR533 Adds

CLI-level aliasing: Developers can now declare custom macros to be treated as trice or triceS equivalents. These user-defined macros will be recognized during scanning, ID injection, and decoding.

41.4.5. PR533 Example

print_macro.h:

#ifndef TRICE_OFF
  #define DEBUG_PRINT(...)  trice(__VA_ARGS__)
  #define DEBUG_PRINT_S(...)  triceS(__VA_ARGS__)
#else
  #define DEBUG_PRINT(...)  Serial.println(__VA_ARGS__)
  #define DEBUG_PRINT_S(...)  Serial.printf(__VA_ARGS__)
#endif

example.c:

#include "trice.h"
#include "print_macro.h"

void setup() {
    Serial.begin(115200);

    while (!Serial) {
        delay(10);
    }

   // Add code here to initialize whatever Trice sender TCP/UDP/UART, etc.
   
  // No argument
  DEBUG_PRINT("DEBUG_PRINT test: no args\n");

  char* str = "Test string";
  DEBUG_PRINT_S("DEBUG_PRINT_S test: %s\n", str);
 }
PR533 Check with Trice

Insert trice IDs:

trice insert -alias DEBUG_PRINT -salias DEBUG_PRINT_S  -exclude ./print_macro.h -v

Flash the MCU and run the trice receiver on your host machine to receive probes (cli command is config and receiver dependent), for UDP4, it can be:

   trice log -p UDP4 -v -pf none
PR533 Check without Trice:

Clean trice IDs, if any:

trice clean -alias DEBUG_PRINT -salias DEBUG_PRINT_S  -exclude ./print_macro.h -v

Flash with -DTRICE_OFF.

41.4.6. PR536 Doc

What This PR536 Adds

This is a follow-up to #533. It enforces the consistent use of the “%s” format in all triceS aliases and fixes that behavior in the newly added test case.

The following simplified example reflects a real use case where custom macros wrap formatting logic:

#define CUSTOM_PRINT_S(id, fmt, ....) triceS(id, "%s", format_message(fmt, ##__VA_ARGS__))
PR536 - The Problem Statement

Determining the Strg argument reliably is challenging due to:

For instance, custom macros like these show the variability:

CUSTOM_ASSERT(false, "Message: %s", msg);
CUSTOM_ASSERT(false, "Message without args");
CUSTOM_ASSERT(false);

Improving this would likely require Clang integration—adding complexity (e.g., full build flags, complete source context)—whereas Trice’s current regex-based approach remains lightweight and simple to use.

PR536 Implementation Details:

matchTrice() was re-implemented to improve robustness. It now:

This approach simplifies the logic and allows the parser to skip invalid or partial matches without aborting, enabling continued scanning of the file for valid constructs.

41.4.7. Alias Example Project

To use the Alias technique with examples/G0B1_inst the following adaptions where made:

G0B1AliasExample.png

(back to top)

42. Future Development

42.1. Trice Log-level Control Specification Draft

Specification Draft

--> IMPORTANT: No breaking changes!

42.1.1. What log levels exist in general, including exotic ones, and what is their exact weighting relative to each other?

🧭 Basic principle

🔢 Common standardized levels (by severity)

Level Name Weight Meaning
0 TRACE lowest Finest-grained details — e.g., every function call or variable change. Used for deep debugging only.
1 DEBUG low Developer-level details about execution flow. No failure.
2 INFO medium Normal operational messages — startup, config loaded, connection established.
3 NOTICE slightly higher Significant but expected events (e.g., user login). Exists in syslog.
4 WARN / WARNING rather high Something unexpected but not yet a failure. System continues running.
5 ERROR high A problem occurred — operation failed, but program still runs.
6 CRITICAL very high A subsystem failure. Urgent attention required.
7 ALERT extremely high Immediate human intervention needed.
8 EMERGENCY / FATAL / PANIC highest System unusable. Shutdown or restart required.

🧩 Rare or exotic variants

Name Origin / Context Severity Description
VERBOSE Windows, Android, C/C++ loggers Between TRACE and DEBUG Very detailed, but not quite as deep as TRACE.
SUCCESS / OK / PASS Test frameworks Between INFO and NOTICE Indicates successful operations.
FAIL Test frameworks ERROR Failed test but not system error.
SECURITY / AUDIT Compliance systems Variable Logs security or compliance events separately.
CONFIG / INIT Embedded / frameworks INFO Configuration or initialization messages.
DEPRECATION Compilers, frameworks WARN Deprecated feature warnings.
ASSERT Debuggers, C/C++ CRITICAL Assertion failure, usually aborts program.
NOTICE / IMPORTANT / EVENT Various Between INFO and WARN Events worth attention but not errors.
OFF Logging frameworks none Turns off all logging.
ALL Logging frameworks lowest Enables every log level.

🧮 Example comparison across systems

Severity Syslog Log4J / Java Python .NET Meaning
0 debug TRACE NOTSET Trace Internal details
1 info DEBUG DEBUG Debug Developer info
2 notice INFO INFO Information Normal ops
3 warning WARN WARNING Warning Unexpected
4 err ERROR ERROR Error Operation failed
5 crit FATAL CRITICAL Critical Severe
6 alert Immediate action
7 emerg System crash

🧠 Suggested numeric scale

Level Weight Meaning
TRACE 10 Ultra-detailed
VERBOSE 20 Very detailed
DEBUG 30 Developer info
INFO 40 Normal operation
NOTICE 50 Significant event
WARN 60 Warning
ERROR 70 Error
CRITICAL 80 Serious problem
ALERT 90 Urgent
EMERGENCY / FATAL / PANIC 100 Total failure

For The Trice project (an embedded logging tool), logging must be:

Here’s a 7-level scheme, embedded-friendly yet compatible with syslog/log4j conventions:

🔧 Recommended Trice Log Level Scale

Macro/Level Name Weight Meaning Typical Use
0 – trice_SILENT OFF / NONE 0 No output at all. Disable logging in release builds.
1 – trice_FATAL FATAL / PANIC 100 System unusable, restart required. Watchdog reset, hard fault, stack overflow.
2 – trice_ERROR ERROR 80 Recoverable error. CRC failure, timeout, file missing.
3 – trice_WARN WARN 60 Unexpected but tolerable. Retry, threshold exceeded.
4 – trice_INFO INFO 40 Regular operation messages. Init complete, connection established.
5 – trice_DEBUG DEBUG 30 Developer-level diagnostics. Variable states, state transitions.
6 – trice_VERBOSE VERBOSE / TRACE 10 Deepest trace level. Function calls, ISR entry, timings.

🎯 Advantages

42.1.2. Compile-time Log-level Control

In Trice Structured Logging Compile-time Information we see, how trice insert ... could modify (temporarily) the source code. With an additional insert switch like -loglevel the shown example could get changed in this way:

User may have written inside val.c:

void doStuff( void ){
    // ...
    trice("info:The answer is %d.\n", 42);
    // ...
}

and a trice insert -loglevel command could change that into (revertable with trice clean):

void doStuff( void ){
    // ...
    trice_INFO(iD(123), "info:The answer is %d.\n", 42);
    // ...
}

The idea here is to modify also the trice macro name into trice_INFO, when a Trice tag “info” or “inf” was found. We could define this way:

#define TRICE_LOG_LEVEL TRICE_LEVEL_INFO

#if TRICE_LOG_LEVEL >= TRICE_LEVEL_INFO
#define trice_INFO(...) trice( __VA_ARGS__)
#else
#define trice_INFO(...) ((void)0)
#endif

#if TRICE_LOG_LEVEL >= TRICE_LEVEL_DEBUG
#define trice_DEBUG(...) trice(__VA_ARGS__)
#else
#define trice_DEBUG(...) ((void)0)
#endif

That results in no code generation for trice("info:The answer is %d.\n", 42); for TRICE_LOG_LEVEL < TRICE_LEVEL_INFO. What we get this way is:

42.1.3. Run-time Log-level Control

Trice logs are very light-weight and usually is no need for their run-time control. Nevertheless there could be a need for that. The very first we need, is a control channel to tell the target device about a changing log-level. See for example chapter Stimulate target with a user command over UART.

When we are able to set a value LogLevel in the target device, we can use this value as an ID threshold in combination with the -IDRange switch. More in detail as an example:

trice insert -loglevel -IDRange debug:1,999 -IDRange info:2000,2999 -IDMin 4000 -IDMax 9999 -IDRange err:10000,10999

It is important to understand, that all other Trice messages get IDs in the range -IDMin and -IDMax and that no range overlapping is allowed.

LogLevel Result
16384 no output
10000 only error messages
4000 normal messages and error messages
2000 all output except info and debug messages
1000 all output except debug messages
0 all output

That implies a small Trice library extension, which gets active only with a LOGLEVELS switch. In that case we get a small additional run-time overhead. What we cannot achieve this way is a tag specific target-side selection, but that would be no big deal to add as well.

(back to top)

42.2. Trice Structured Logging

Specification Draft

Structured logging, in contrast to unformatted logging, automatically adds compile time and runtime data to logs as well as log level information. The user should be able to configure, which data get added and also should have control about the data formatting. The generic data insertion allows later an automatic log file analysis and frees the developer from manually typing defaults, what is also error-prone.

Trice is considerable already a bit as a (very limited) structured logger, if we look at the file and line insertion capability and the timestamp options. The following is about how Trice could get full structured logging capability without making a breaking change.

42.2.1. Trice Structured Logging Compile-time Information

file, line, function, compiler version, module, build time, firmware version, machine name, user name, locale, host OS version, log level, an (unstructured) format string, compiler flags, (locally) defined values…

These data can be strings or numbers.

42.2.2. Trice Structured Logging Runtime Information

uptime, timestamp, hw serial, task ID, stack depth, event count, core ID, position, variables values, parameter values …

In an initial approach we assume, these data do not contain runtime generated strings. If really needed, a derived hash is usable instead for now. Despite of this, runtime generated strings are an important feature and therefore Trice supports triceS, capable to transmit a single string up to 32KB long, and triceS relatives like triceB. We could add compile-time data (as inserted fixed strings) but runtime information can only get as an additional part of the runtime generated string into the structured log. This should be acceptable and we will deal with this later.

42.2.3. Trice Structured Logging Limitations and Special Cases

For performance reasons, Trice was designed to only transmit 0-12 (straight forward extendable) numbers of equal bit-width OR a single runtime generated string. Firstly we look at only “normal” Trice macros trice, Trice, TRice and exclude the special cases triceS, TriceS, TRiceS. Also we consider just trices without specified bit-width, assume 32-bit and exlude cases like trice32_4 firstly.

42.2.4. A Trice Structured Logging Example

User may have written inside val.c:

void doStuff( void ){
    // ...
    trice("info:The answer is %d.\n", 42);
    // ...
}

and (as we know) a trice insert command would change that into (revertable with trice clean):

void doStuff( void ){
    // ...
    trice(iD(123), "info:The answer is %d.\n", 42);
    // ...
}

But a trice insert command with context option will, for example, change that line into (revertable with trice clean):

void doStuff( void ){
    // ...
    trice(iD(456), "[level=info][file=\"val.c\"][line=321][func=doStuff][taskID=%x][fmt=\"The answer is %d.\"][uptime=%08us][temperature=%3.1f°C]\n", getTaskID(), 42, uptime(), aFloat(sensorValue));
    // ...
}

42.2.5. Trice Structured Logging CLI Switches and Variables

To achieve that, 2 structured logging CLI switches -stf and -stv on trice insert and trice clean are usable:

CLI switch meaning
-stf structured logging format
-stv structured logging values

Additionally the Trice tool uses these internal variables (no bash variables!) as replacements during trice insert and trice clean:

Variable Example Comment
$level info The bare trice format string part until the first colon (:), if known as channel/tag value.
$file val.c The file name, where the Trice log occures.
$line 321 The file line, where the Trice log occures.
$func doStuff The function name, where the Trice log occures.
$fmt The asnwer is %d. The bare Trice format string stripped from the channel/tag specifier including the colon (:) according to the Trice rule (lowercase-only ones)
$values 42 The bare Trice statement values.
$usr0 abc | ` ` | xyz A predefined string value with location dependent values (see below).

42.2.6. Trice Structured Logging User Defined Values

This use case is not expected for most cases, but mentioned here to show the possibilities. Adding user specific values like $usr0 can be done in this way:

 88 | ...
 89 | #define XSTR(x) STR(x)
 90 | #define STR(x) #x
 91 | 
 92 | trice("info:hi");
 93 |  
 94 | #define TRICE_ETC "xyz"
 95 | #pragma message "$usr0=" XSTR(TRICE_ETC)
 96 | trice("info:hi");
 97 | 
 98 | #undef TRICE_ETC
 99 | #pragma message "$usr0=" XSTR(TRICE_ETC)
100 | trice("info:hi");
101 | 
102 | #define TRICE_ETC "abc"
103 | #pragma message "$usr0=" XSTR(TRICE_ETC)
104 | trice("info:hi");
105 | ...

This is just a demonstration. The #pragma message "$usr0=" XSTR(TRICE_ETC) line probably is needed only on a few lines in the project. A pre-compile output

$ ./build.sh 2>&1 | grep "pragma message:"
Core/Src/main.c:95:9: note: '#pragma message: $usr0="xyz"'
Core/Src/main.c:99:9: note: '#pragma message: $usr0=""'
Core/Src/main.c:103:9: note: '#pragma message: $usr0="abc"'

could get transferred automatically to the Trice tool, with a user generator script to tell, that normally $usr0="", but $usr0="xyz" for Trices in file main.c from line 95 to 99, that $usr0="abc" is valid for main.c after line 103.

Those things are compiler and user specific and not part of the Trice tool design. But on demand a CLI multi switch -stu can get invented, to inject such information into the trice insert process automatically. With


STF='{"level":"%s","loc":"%s:%d","fmt":"$fmt","etc":"%s"}'
STV='$level, $file, $line, $values, $usr0'

# user script generated begin ################################################
ST0='usr0="xyz":main.c:95'                        # user script generated line
ST1='usr0="":main.c:99'                           # user script generated line
ST2='usr0="abc":main.c:103'                       # user script generated line
STU="-stu $ST0 -stu $ST1 -stu $ST2"               # user script generated line
# user script generated end ##################################################

trice insert $STU -stf $STF -stv $STV

The structured log output would be:

{...}
{"level":"info","loc":"main.c:92","fmt":"hi","etc":""}
{"level":"info","loc":"main.c:96","fmt":"hi","etc":"xyz"}
{"level":"info","loc":"main.c:100","fmt":"hi","etc":""}
{"level":"info","loc":"main.c:104","fmt":"hi","etc":"abc"}
{...}

42.2.7. Trice Structured Logging CLI Switches Usage Options

The in A Trice Structured Logging Example shown trice insert result is possible with

trice insert \
-stf='[level=$level][file=$file][line=$line][func=$func][taskID=%x][fmt=$fmt][uptime=%08us][temperature=%3.1f°C]' \
-stv='getTaskID(), $values, uptime(), aFloat(sensorValue)'

The raw string syntax is mandatory here, to pass the internal Trice tool variables names.

Adding variable values like $line as strings has performance advantages, but on each such value change a new Trice ID is generated then. Those variables are better inserted as values, if the code is under development. A $line value insertion looks like this:

trice insert \
-stf='[level=$level][file=$file][line=%5d][func=$func][taskID=%04x][fmt=$fmt][uptime=%08us][temperature=%3.1f°C]' \
-stv='$line, getTaskID(), $values, uptime(), aFloat(sensorValue)'

It is also possible to use string format specifiers to allow somehow aligned values. For example:

trice insert \
-stf='[level=%-6s][file=%24s][line=%5d][func=%-16s][taskID=%04x][fmt=$fmt][uptime=%08us][temperature=%3.1f°C]' \
-stv='$level, $file, $line, $func, getTaskID(), $values, uptime(), aFloat(sensorValue)'

Or, if you like alignment after the format string, even:

trice insert \
-stf='[level=%-6s][file=%24s][line=%5d][func=%-16s][taskID=%04x][fmt=%64s][uptime=%08us][temperature=%3.1f°C]' \
-stv='$level, $file, $line, $func, getTaskID(), $fmt, $values, uptime(), aFloat(sensorValue)'

The user has full control and could also use any other syntax like a JSON format. Only the format specifiers are requested to match the passed values after the Trice tool internal variables replacement during trice insert, so that the Trice tool can perform a printf during logging.

To achieve a log output in compact JSON with line as string we can use:

trice insert \
-stf='{"level":"$level","file":"$file","line:"$line","taskID":"%04x","fmt":$fmt,"uptime":%08u us"}' \
-stv='getTaskID(), $values, uptime()'

To put things together: Any structured format string design is possible and the user can insert the $line (example) value:

After trice insert we get this (compact JSON) log line according to -stf and -stv:

void doStuff( void ){
    // ...
    trice(iD(789), "{\"level\":\"info\",\"file\":\"val.c\",\"line\":\"321\",\"taskID\":\"%04x\",\"fmt\":\"The answer is %d.\",\"uptime\":\"%08u us\"}\n', getTaskID(), 42, uptime());
    // ...
}

All compile time strings are part of the Trice format string now, which is registered inside the til.json file. The needed Trice byte count stays 4 bytes only plus the 3 times 4 bytes for the runtime parameter values taskID, 42, uptime. The default TCOBS compression will afterwards reduce these 16 bytes to 12 or 13 or so.

A trice clean command will remove the context information completely including the ID. Please keep in mind, that with trice insert as a pre-compile and trice clean as post-compile step, the user all the time sees only the original written code:

void doStuff( void ){
    // ...
    trice("info:The answer is %d.\n", 42);
    // ...
}

The optional -cache switch makes things blazing fast.

The appropriate Trice tool log line output would be similar to

{...}
{"level":"info","file":"val.c","line":"321","taskID":"0123","fmt":"The answer is 42.","uptime":"12345678 us"}
{...}

When stf and stv are empty strings (default), trice insert and trice clean commands will work the ususal way. If they are not empty, the trice insert command will on each Trice statement use a heuristic to check if the context information was inserted already and update it or otherwise insert it. ATTENTION: That will work only, if stf and stv where not changed by the user inbetween. In the same way trice clean would remove the context information only, if stf and stv kept unchanged. If the user wants to change stf and stv during development, first a trice clean is needed. Use a build.sh script like this:

#!/bin/bash

# Run "rm -rf ~/.trice/cache/*" automatically after changing this file !!! 

STF='{"level":"$level","file":"$file","line:"$line","taskID":"%04x","fmt":$fmt,"uptime":%08u us"}'
STV='getTaskID(), $values, uptime()'

trice insert -cache -stf="$STF" -stv="$STV"
# make
trice clean  -cache -stf="$STF" -stv="$STV"

The -cache switch is still experimental - to stay safe, use (here again with $line as string):

#!/bin/bash
STF='{"level":"$level","file":"$file","line:"$line","taskID":"%04x","fmt":$fmt,"uptime":%08u us"}'
STV='getTaskID(), $values, uptime()'

trice insert -stf="$STF" -stv="$STV"
# make
trice clean  -stf="$STF" -stv="$STV"

42.2.8. Trice Structured Logging Level Specific Configuration

Configure the Trice Structured Logging selectively in a way, to provide as much helpful diagnostic info as possible on ERROR level for example. Example script:

#!/bin/bash

# Specify `-stf` and `-stv` differently for different channels/tags.

STL="" # Trice Structured Logging configuration

# Trices with an `ERROR:` tag `trice("err:...", ...);`:
STF_ERROR='ERROR:{"log level":"%-6s","file":"%24s","line:"%5d","func":"%-16s","taskID":"%x","fmt":"$fmt","uptime":"%08u us"}'` # (with location)
STV_ERROR='ERROR:$level, $file, $line, $func, getTaskID(), $values, uptime()'`
STL+=" -stf $STF_ERROR -stv $STV_ERROR "

# Trices with an underscore tag, like `trice("_DEBUG:...", ...);` or `trice("_info:...", ...);`:
STF_underscoreTagStart='_*:{"log level":"%-6s","file":"%24s","line:"%5d","func":"%-16s","fmt":"$fmt","uptime":"%08u us"}'` # (no task ID)
STV_underscoreTagStart='_*:$level, $file, $line, $func, $values, uptime()'`
STL+=" -stf $STF_underscoreTagStart -stv $STV_underscoreTagStart "

# Tices with any other tag:
STF_anyTag='*:{"log level":"%-6s","file":"%24s","line:"%5d","func":"%-16s","fmt":"$fmt"}'` # (no task ID, no uptime)
STV_anyTag='*:$level, $file, $line, $func, $values'`
STL+=" -stf $STF_anyTag -stv $STV_anyTag "

# Trices with no tag at all:
STF_noTag='{"file":"%24s","line:"%5d","fmt":"$fmt"}'` # (only location information)
STV_noTag='$file, $line, $values'`
STL+=" -stf $STF_noTag -stv $STV_noTag "

trice insert $STL ...
source make.sh # build process
trice clean  $STL ...

42.2.9. Trice Structured Logging Assert Macros (TODO)

Configure TriceAssert like macros and this works also with the -salias switch.

(back to top)

42.3. Improving the Trice Tool Internal Parser

42.3.1. Trice Internal Log Code Short Description

Trice v1.0 Code

Hint: To follow this explanation with the debugger, you can open in VSCode the trice folder, klick the Run & Debug Button or press CTRL-SHIFT-D, select trice l -p DUMP and set a breakpoint at func main() in ./cmd/trice/main.go or directly in translator.Translate ./internal/translator/translator.go.

  • In file ./internal/args/handler.go function logLoop calls receiver.NewReadWriteCloser and passes the created rwc object to translator.Translate.
  • There rwc is used to create an appropriate decode object dec passed to decodeAndComposeLoop, which uses func (p *trexDec) Read(b []byte) (n int, err error) then, doing the byte interpretation.
    • Finally n += p.sprintTrice(b[n:]) // use param info is called doing the conversion.
  • Read returns a single Trice conversion result or a single error message in b[:n] or 0 and is called again and again.
  • The returned Trice conversion result is the Trice format string with inserted values, but no timestamp, color or location information. The target timestamp, for this single Trice is hold in decoder.TargetTimestamp. It is used only when a new log line begins, what is the normal case. If a Trice format string ends not with a newline, the following Trice gets part of the same log line and therefore its target time stamp is discarded. Also additional data like the location information only displayed for the first Trice in a log line containing several Trices. Because the color is inherent to the Trice tag and needs no display space it is attached to the following Trices in a log line as well.
  • The after Read following emitter.BanOrPickFilter could remove the Read result.
  • If something is to write, the location information is generated if a new line starts and passed to the with sw := emitter.New(w) created object sw.WriteString method which internally keeps a slice of type []string collecting all parts of an output line.
  • The line p.Line = append(p.Line, ts, p.prefix, sx) adds host timestamp ts, the build p.prefix and the “printed Trice” (sx is here just the location information) to the line slice.
  • In the next step the stored target timestamp decoder.TargetTimestamp is printed into a string and added to the Trice line.
  • Optionally the Trice ID follows, if desired.
  • The in a string printed Trice follows now and if the sw.WriteString method detects a \n at its end, the configured line suffix (usually "") follows and p.completeLine() is called then. It passes the line (slice of strings) to p.lw.WriteLine(p.Line), which adds color, prints to the output device and clears the sw.Line slice for the next line.
Disadvantages of Trice v1.0 Implementation
Aims for a better implementation

42.4. Using Trice on Servers

(back to top)

43. Working with the Trice Git Repository

Action Command
Get a local repository copy. git clone github.com/rokath/trice.git trice
Show current folder pwd
Show repository status. git status
Clean the repo, if needed. git stash push
Show all branches. git branch -a
Switch to main. git switch main
Fetch a pull request as new branch PRIDa. git fetch origin pull/ID/head:PRIDa
List worktree. git worktree list
Add to worktree. git worktree add ../trice_wt_PRIDa PRIDa
Add branch dev to worktree git worktree add ../trice-dev dev
Rstore the repo if needed. git stash pop
Change to new folder. cd ../trice_wt_PRIDa
Show repository status. git status
Test pull request. ./testAll.sh full
Show repository status. git status
Clean pull request. git restore .
Change to previous folder. cd -
Delete worktree branch. git worktree remove ../trice_wt_PRIDa
Delete git branch. git branch -d PRIDa
Log last 3 commits in branch maste git log -3 main
Checkout by hash git checkout <hash>
One Liner Log until shortly before v1.0.0 git log --graph --decorate --all --pretty=format:'%C(bold yellow)%h%Creset %C(bold green)%ad%Creset %C(bold cyan)%d%Creset %C(white)%s%Creset' --date=format:'%Y-%m-%d %H:%M' --since 2025-04-01
One Liner Log for branch devel git log --graph --decorate devel --pretty=format:'%C(bold yellow)%h%Creset %C(bold green)%ad%Creset %C(bold cyan)%d%Creset %C(white)%s%Creset' --date=format:'%Y-%m-%d %H:%M'
One Liner Log with author git log --graph --decorate --all --pretty=format:'%C(bold yellow)%h%Creset %C(bold green)%ad%Creset %C(bold blue)%an%Creset %C(bold cyan)%d%Creset %C(white)%s%Creset' --date=format:'%Y-%m-%d %H:%M'
New worktree detached branch for compare git worktree add --detach ../trice_9995fdc4b 9995fdc4b
Add a special commit worktree ./AddWorktreeFromGitLogLineData.sh <commit-hash> <YYYY-MM-DD> <HH:MM>
Create a bunch of worktrees ./AddWorktreesBetween.sh "<since-date>" "<until-date>" or ./AddWorktreesBetween.sh <older-hash> <newer-hash>
Delete all trice_* worktrees cd ~/repos && rm trice_* && cd trice && git worktree prune && git worktree list
Delete all trice_* branches git branch -D `git branch \| grep -E 'trice_'`
Show all opencommit parameter oco config describe
Show some config settings oco config get OCO_MODEL && oco config get OCO_PROMPT_MODULE && oco config get OCO_EMOJI

43.1. Install opencommit on MacOS


🧰 Prerequisites

Before you begin, make sure you have:


🔑 Step 1 — Get Your OpenAI API Key


⚙️ Step 2 — Install OpenCommit


🔧 Step 3 — Set Up Your API Key on macOS


⚙️ Step 4 — Optional Configuration

export OCO_LANG="en"           # or "de", "fr", etc. 
export OCO_MODEL="gpt-5"       # or another model like "gpt-4-turbo" 
export OCO_PROMPT_MODULE="conventional" 
export OCO_EMOJI=true

Add these to your ~/.zshrc for persistence.


🚀 Step 5 — Use OpenCommit


🔁 Step 6 — (Optional) Install Git Hook


🧩 Step 7 — Troubleshooting

If OpenCommit says:


43.2. Install opencommit on Windows

🧭 Overview

OpenCommit is a tool that uses AI (like GPT models) to automatically generate meaningful Git commit messages based on your code changes.

This guide explains how to install and configure OpenCommit on Windows step by step.


⚙️ Prerequisites


🪄 Installation Steps

1. Install OpenCommit Globally

2. Configure the API Key

3. (Optional) Configure Defaults


🚀 Usage

🔧 Troubleshooting


✅ Example

git add .
oco

Output:

Generated commit message:
...

(back to top)

44. Trice Maintenance

44.1. Trice Project structure (Files and Folders)

Trice Root Folder File Details
.clang-format See GitHub Action clang-format.yml - Check C Code Formatting
.clang-format-ignore See GitHub Action clang-format.yml - Check C Code Formatting
.code_snippets Some legacy helper code for copying where to use
.editorconfig See GitHub Action clang-format.yml - Check C Code Formatting
.git version control data base
.gitattributes See GitHub Action clang-format.yml - Check C Code Formatting
.github/ 📁 The .github Folder — Purpose and Contents
.gitignore git ignores these files
.goreleaser.yaml goreleaser configuration
.idea/ GoLand settings
lychee.toml GitHub Action link-check.yml - Broken Links Check
.markdownlinkcheck.json GitHub Action link-check.yml - Broken Links Check
.markdownlint.yaml Cleaning the Sources
.markdownlintignore Cleaning the Sources
.vscode/ vsCode settings
AUTHORS.md contributors
CHANGELOG.md History
CODE_OF_CONDUCT.md How to communicate
CONTRIBUTING.md Helper
LICENSE.md MIT
README.md Github first page
_config.yml jekyll configuration
_test automatic target code tests
buildTriceTool.sh Build Trice tool from Go sources
build_environment.sh see inside
clang-format.sh See GitHub Action clang-format.yml - Check C Code Formatting
clean-dsstore.sh Ru to remove MacOS artifacts
coverage.out Go test coverage output
cmd/_cui/ (do not use) command user interface tryout code
cmd/_stim/ (do not use) target stimulation tool tryout code
cmd/clang-filter ReadMe
cmd/trice Trice tool command Go sources
demoLI.json location information example
demoTIL.json Trice ID list example
dist/ local distribution files folder created by goreleaser
docs documentation folder with link forwarding
examples/ example target projects
format-dumeng-toc.sh Trice User Manual Maintenance (or any *.md file)
gitAddWorktreeFromGitLogLineData.sh helper to get easy a git worktree folder from any git hash for easy folder compare, see inside
gitAddWorktreesBetween.sh helper to get easy git worktree folders from any time range
gitLogWithBranches.sh helper to get easy a history view
go.mod Go modules file
go.sum Go modules sums
index.md Jekyll index site for RERADME.md
internal/ Trice tool internal Go packages
pkg/ Trice tool common Go packages
renewIDs_in_examples_and_test_folder.sh renew all ID data
src/ C sources for trice instrumentation -> Add to target project
temp/ binary logfiles could appear here
testAll.log Accumulated output of last ./testAll.sh run
testAll.sh run all tests
third_party/ external components
trice_cleanIDs_in_examples_and_test_folder.sh Cleaning the Sources Activating the Trice Cache
trice_environment.sh Cleaning the Sources Activating the Trice Cache
trice_insertIDs_in_examples_and_test_folder.sh Cleaning the Sources Activating the Trice Cache

(back to top)

44.2. 📁 The .github Folder — Purpose and Contents

GitHub automatically recognizes and uses everything contained inside the .github/ directory. This folder defines how the project behaves on GitHub: issue templates, automated workflows, labels, code scanning, greetings, and release automation. Details:

44.2.1. 📁 .github Root

It contains issue templates, labels, workflow automation, code scanning, linting, and the CI/CD release pipeline.

44.2.2. 📂 .github/workflows — GitHub Actions Workflows

The .github/workflows/ folder contains YAML descriptions for various actions, which will be triggered automatically on certain events or are started manually. Every yml file in this directory defines an automated process. These processes run on GitHub’s servers (CI/CD).

These workflows run automatically on pushes and pull requests to main, and can also be triggered manually via the GitHub Actions UI. These files are not executed; they simply inform GitHub how certain workflows behave or should be displayed.

Github Action About
clang-format.yml GitHub Action clang-format.yml - Check C Code Formatting
codeql.yml GitHub Action codeql.yml - Static Code Analysis
coverage.yml GitHub Action coverage.yml - Test Coverage and Coveralls Integration
go.yml GitHub Action go.yml - Building and Testing Go Code
goreleaser.yml GitHub Action goreleaser.yml - Build & Pack Trice Distribution
greetings.yml GitHub Action greetings.yml - Greeting Message
label.yml GitHub Action label.yml - Automatic Labeling Rules
learn-github-actions.yml GitHub Action learn-github-actions.yml - Instructional Workflow
link-check.yml GitHub Action link-check.yml - Broken Links Check
manual.yml GitHub Action manual.ym - To Be Triggered Manually
shellcheck.yml GitHub Action shellcheck.yml - Catching Common Bash Scripts Bugs
shfmt.yml GitHub Action shfmt.yml - Ensure Consistent Shell Scripts Formatting
stale.yml GitHub Action stale.yml - Automatic Stale Issue Handling
superlinter.ym GitHub Action superlinter.yml - Ensure Consistent YAML and Markdown Formatting
pages.yml Github Action pages.yml - Creates The Trice Github Pages
test_goreleaser.yml Github Action test_goreleaser.yml - Checks If Goreleaser Would Succeed

44.2.3. GitHub Action clang-format.yml - Check C Code Formatting

44.2.4. GitHub Action codeql.yml - Static Code Analysis

44.2.5. GitHub Action coverage.yml - Test Coverage and Coveralls Integration

Trice uses Go’s built-in coverage tooling to measure how much of the Go codebase is exercised by automated tests.

Action Command
Generate a coverage profile locally go test ./... -covermode=atomic -coverprofile=coverage.out
Show results as list in terminal go tool cover -func=coverage.out
Show results colored file specific in browser go tool cover -html=coverage.out

Coverage badge:
The README displays the current coverage status for the default branch using the Coveralls badge:

[![Coverage Status](https://coveralls.io/repos/github/rokath/trice/badge.svg?branch=master)](https://coveralls.io/github/rokath/trice?branch=master)

This badge is updated whenever the CI workflow successfully uploads a new coverage report for the master branch.

44.2.6. GitHub Action go.yml - Building and Testing Go Code

44.2.7. GitHub Action goreleaser.yml - Build & Pack Trice Distribution

This workflow runs GoReleaser, the tool that builds and packages Trice for distribution.

See also Trigger a real Trice release via CI (with git tag)

44.2.8. GitHub Action greetings.yml - Greeting Message

A small automation that posts a friendly greeting message when somebody:

44.2.9. GitHub Action label.yml - Automatic Labeling Rules

44.2.10. GitHub Action learn-github-actions.yml - Instructional Workflow

An instructional workflow provided by GitHub. Purpose:

44.2.12. GitHub Action manual.ym - To Be Triggered Manually

A workflow that is designed to be triggered manually (similar to workflow_dispatch workflows). Common use cases:

44.2.13. GitHub Action shellcheck.yml - Catching Common Bash Scripts Bugs

Runs ShellCheck on all *.sh files, catching common bugs in Bash scripts.

44.2.14. GitHub Action shfmt.yml - Ensure Consistent Shell Scripts Formatting

Runs shfmt in diff mode on pull requests to ensure consistent formatting of shell scripts.

44.2.15. GitHub Action stale.yml - Automatic Stale Issue Handling

Automates stale issue handling. Function:

Mark stale issues and pull requests

44.2.16. GitHub Action superlinter.yml - Ensure Consistent YAML and Markdown Formatting

44.2.17. Github Action pages.yml - Creates The Trice Github Pages

This workflow creates the Trice github pages avaliable under rokath.github.io/trice/.

44.2.18. Github Action test_goreleaser.yml - Checks If Goreleaser Would Succeed

Test Goreleaser

44.3. Trice User Manual Maintenance (or any *.md file)

44.4. Cleaning the Sources

In Github are some Actions defined. Some of them get triggered on a git push and perform some checks. To get no fail, some scripts should run before committing:

(back to top)

45. Build and Release the Trice Tool

45.1. Build Trice tool from Go sources

To execute the target code tests, you can run testAll.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 (main)
$ go install ./cmd/trice/

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

After installing Go, in your home folder should exist a folder ./go/bin. Please add it to your path variable. OR: Copy the Trice binary from there into a folder of your path after creating it with go install ./cmd/trice/.... There is now a remommended script ./buildTriceTool.sh. Using it, depending on your system you may need to enter bash ./buildTriceTool.sh, includes the actual Trice repository state into the Trice binary, which is shown with trice version then - useful in case of issues.

45.2. Prepare A Release

Prerequisite: Installed goreleaser.

45.2.1. Check a Goreleaser Release before Publishing

By cloning the Trice repo into an empty folder, you make sure no other files exist in the Trice folder.

mkdir ./tmp
cd /tmp
git clone https://github.com/rokath/trice.git
cd trice
goreleaser release --clean --snapshot --skip=publish

This just generates the artifacts locally in /tmp/trice/dist using the ./trice/.goreleaser.yaml copy in ./temp/trice.

Alternatively you can do in your local Trice clone directly, by removing everything not a part of the Trice repo, if you are sure not to loose any data:

git status
git clean -xfd
goreleaser release --clean --snapshot --skip=publish

Explanation:

What you should see:

If this succeeds, you’ve already tested 90% of what CI will do for a real release. If it fails, fix the problem locally first (missing files, bad paths, etc.) – it would fail the same way in CI.

45.3. Trigger a real Trice release via CI (with git tag)

Letting CI build and publish an official release.

45.3.1. Make sure your workflow reacts to tags

In .github/workflows/goreleaser.yml, you need on: workflow_dispatch: push: tags: - 'v*'.

Commit & push this change (if you haven’t already):

git add .github/workflows/goreleaser.yml git commit -m "Configure GoReleaser workflow to run on tags" git push origin main

45.3.2. Final checks before tagging

In your local trice repo:

If all of that is green, you’re ready to “bless” a version.

45.3.3. Choose a version and create a git tag

Decide on a version, for example:

Create an annotated tag:

git tag -a v0.44.0 -m "Trice v0.44.0"

Check your tags:

git tag

You should see v0.44.0 in the list.

💡 The tag is what GoReleaser uses as the release version (.Tag, .Version, etc.) in your .goreleaser.yaml.
Your ldflags like -X main.version= will use this.

45.3.4. Push the tag to GitHub (this triggers CI)

Now push the tag:

git push origin v0.44.0

This does not push all tags, only v0.44.0.

Because of your workflow’s on: push: tags: 'v*', this automatically starts the GoReleaser workflow in GitHub Actions.

45.3.5. Watch the CI release run on GitHub

  1. Open your browser and go to your repo:

    https://github.com/rokath/trice

  2. Click the “Actions” tab at the top.

  3. In the list of workflows, click on “goreleaser”.

  4. You should see a new run with something like:

  1. Click on that run, then on the job (e.g. goreleaser):

If “Run GoReleaser” is green ✔, the CI release has succeeded.

45.3.6. Check the GitHub Release

Finally, verify the published release:

  1. In your repo, click the “Releases” section (right side or under “Code”).

  2. You should see a new release v0.44.0 created by GoReleaser.

  3. Inside it you’ll find:

This is now your official Trice release built by CI.

(back to top)

46. Scratch Pad


(back to top)