Optimizing the setup

We will take some steps to ease our development process and save some time later.

Reduce binary size

Using cargo size and cargo size --release, we can see that the binary produced in release mode is much smaller than the one produced in debug mode. Note that size doesn't display the debug information since those are never stored in the target memory.

We would like to use --release to keep an optimized binary, but we would like to keep the debug information in case we need to use gdb, or to have a better backtrace in case of panic. Fortunately, we can do that with cargo and require that the release profile:

  • keeps debug symbols;
  • uses link-time-optimization (LTO) to optimize the produced binary even further;
  • generates objects one by one to get an even better optimization.

❎ To do so, add the following section to your program Cargo.toml:

[profile.release]
debug = true      # symbols are nice and they don't increase the size on the target
lto = true        # better optimizations
codegen-units = 1 # better optimizations

From now on, we will always use --release when building binaries and those will be optimized fully and contain debugging symbols.

Make it simplier to run the program

Even though we have configured cargo run so that it runs gdb automatically and uploads our program, we still have to start JLinkGDBServer and JLinkRTTClient. Fortunately, the probe-rs and knurling-rs projects make it easy to develop embedded Rust programs:

  • probe-rs lets you manipulate the probes connected to your computer, such as the probe located on your IoT-node board.
  • defmt (for deferred formatting) is a logging library and set of tools that lets you log events from your embedded programs and transmit them in an efficient binary format. The formatting for the developer consumption will be made by tools running on the host rather than on the target. probe-rs run is able to get defmt traces using a RTT channel and decode and format them.

Many others programs such as cargo flash or cargo embed exist, but we will not need them here.

❎ Stop the Segger JLink tools. Using the probe-rs executable, check if the probe on your board is properly detected.

❎ Use probe-rs run with the appropriate parameters instead of gdb to upload your program onto the board and run it. Replace your runner in .cargo/config.toml by:

runner = "probe-rs run --chip stm32l475vgtx"

❎ Using cargo run --release, look at your program being compiled, uploaded and run on your board. You should see the messages sent over RTT on your screen.

⚠ You can use ctrl-c to quit probe-rs run.

Use defmt for logging

Instead of using RTT directly, we will use defmt to have a better and efficient logging system.

❎ Remove the rtt-target and panic-rtt-target from your dependencies in Cargo.toml.

❎ Add the defmt and defmt-rtt dependencies to your Cargo.toml.

❎ Add the panic-probe dependency to your Cargo.toml with the print-defmt feature.

defmt-rtt is the RTT transport library for defmt. panic-probe with the print-defmt feature will indicate to probe-rs run the panic message to display using defmt and will tell it to stop in case of a panic.

defmt uses a special section in your executable. In .cargo/config.toml, add the following to your existing rustflags in order to include the provided linker file fragment: "-C", "link-arg=-Tdefmt.x".

❎ Modify your code in src/main.rs to include the following changes:

  • Write use panic_probe as _; instead of panic_rtt_target to use the panic-probe crate.
  • Write use defmt_rtt as _; to link with the defmtt-rtt library.
  • Remove use of rtt_target items.
  • Remove rtt_init_print!(), and replace rprintln!() with defmt::info!() to print a message.

❎ Run your program using cargo run --release. Notice that you see the panic information, but you do not see the "Hello, world!" message.

By default, defmt only prints errors. The various log level are trace, debug, info, warn, and error. If you want to see the messages of level info and above (info, warn, and error), you must set the DEFMT_LOG environment variable when building and when running the program. Only the appropriate information will be included at build time and displayed at run time.

❎ Build and run your program using DEFMT_LOG=info cargo run --release. You will see the "Hello, world!" message. Note that you could also have used DEFMT_LOG=trace or DEFMT_LOG=debug if you add more verbose error messages.

❎ Setup the default log level by telling cargo to set the DEFMT_LOG environment variable when using cargo commands. You can do this by adding a [env] section in .cargo/config.toml:

[env]
DEFMT_LOG = "info"

⚠️ Changing the [env] section of .cargo/config.toml will not recompile the program with the new options. Make sure that you use cargo clean when you change the DEFMT_LOG variable.

🎉 Your environment is fully setup in an efficient way. If needed, you can revert to using gdb and Segger JLink tools, but that should be reserved to extreme cases.