This is the 31st changelog for Knurling-rs, our push to sustainably build better tooling for developing and debugging Rust software for embedded systems. Knurling-rs includes a suite of tools that make it easier to develop, log, debug, and test your embedded Rust libraries and applications!
Knurling-rs is supported by our sponsors on GitHub. If you're interested in seeing more projects like this, consider becoming a sponsor today!
Highlights 🎉
dirbaio work
Many of you noticed the work Dirbaio put into defmt
in the recent weeks. He set out to improve the code size and wire size of firmware using defmt
. The tracking issue for this was "RFC: Traits and wire format 2.0" (defmt#492), which summarizes the discussions from various previous issues and puts them into one coherent design.
His effort resulted consisted of multiple pull requests:
- defmt#505: Logger trait v2
- defmt#507: Remove code-size-costly optimizations
- defmt#521: Remove u24
- defmt#524: Do not depend on custom
InternalFormatter
for tests - defmt#508: Format trait v2
- defmt#539: Add optional rzCOBS encoding+framing
Additionally he also cared about separating the "crate version" from the "wire format version" (defmt#287), which was implemented in defmt#540.
Let's take a look how he approached these improvements!
Traits and wire format 2.0
trait Logger
The trait Logger
defines how data is moved from the device, where the application runs, to the host, where logs will be formatted and displayed.
Previously you needed to implement two traits for your defmt-loggers: trait Write
for defining how to write bytes to the host, and trait Logger
for the acquire-release mechanism.
Now both of these traits are combined into one unified trait Logger
, which handles both acquire-release and writing to the host.
pub unsafe trait Logger {
/// Acquire the global logger in the current execution context.
fn acquire();
/// Releases the global logger in the current execution context.
unsafe fn release();
/// Writes `bytes` to the destination.
unsafe fn write(bytes: &[u8]);
unsafe fn flush(); // not of concern right now
}
This has the advantage of smaller code size, because the dyn Write
doesn't need be stored anymore during log calls.
This change also included to update the transport crates defmt-rtt
, defmt-itm
and defmt-semihosting
to take advantage of this and adapting the #[global_logger]
-macro.
trait Format
Dirbaio also updated the format trait, in order to fix #455, #457, #458.
pub trait Format {
/// Writes the defmt representation of `self` to `fmt`.
fn format(&self, fmt: Formatter);
// omitting provided methods
}
/// Handle to a defmt logger.
#[derive(Copy, Clone)]
pub struct Formatter<'a> {
pub(crate) _phantom: PhantomData<&'a ()>,
}
The biggest change is that the struct defmt::Formatter
is now zero-sized, which positively impacts the code size.
Encoding + framing
Previously we encoded the defmt-stream using a mixture of leb128-encoding and bool compression. Dirbaio removed these optimizations, because of their negative impact on the code size. This results in a good code size gain, but a small bump in the wire size. But no worries! This increase in wire size is offset by the new rzCOBS-encoding.
rz… what? "Reverse-Zerocompressing-COBS encoding (rzCOBS) is a variant of rCOBS which additionally compresses messages known to contain many zero bytes." (see rzcobs crate for more details).
Since the defmt wire stream is known to contain many zero bytes, rzCOBS is a good fit, because it can compress this kind of data quite fast. Additionally it supports framing, which will allow recovering from a corrupted stream!
rzCOBS is fast, but still slower than using no compression at all. Therefore users of defmt
can select if they want rzCOBS or the raw data. This works via cargo-feature-flags, with a default of rzCOBS.
[dependencies]
# rzCOBS
defmt = { version = "0.3", features = ["encoding-rzcobs"] }
# raw data
defmt = { version = "0.3", features = ["encoding-raw"] }
(No, defmt 0.3 didn't release yet, but it will be the first version containing this change. If you already want to try this out now you can use the git-version of defmt.)
Having rzCOBS allows us to drop the u24
unsigned integer type, which was helpful before, but now isn't required anymore.
Improvements 🦀
defmt
- #581 Add
impl Format for alloc::Layout
. Thanks to newAM! - #550
defmt::flush()
- #572
defmt-decoder
:impl FromStr<<String>> for Encoding
- #570 Support referring to
Self
in bitflags constants - #568 Encoding docs. Thanks to Dirbaio!
probe-run
- #254 Add support for measuring the program's stack usage
- #248 Print dedicated message on control + c
- #250 Backtrace options. Thank you, BriocheBerlin!
- #253 feat: add help message for JtagNoDeviceConnected, Thanks to numero-744!
Internal Improvements 🧽
probe-run
- #263 set blocking mode to 0b10 (BLOCK_IF_FULL)
- #257 Split "set rtt to blocking-mode" into function
- #259 Update snapshot tests
Fixes 🔨
defmt
- #579 defmt-rtt: fix check for blocking RTT
- #577 Fix typo in
cfg
of encoding-feature-compile_error!
- #578
qemu
: Allow dead code
probe-run
- #260 fix ci on nightly
Version Update Notification 🆙
probe-run v0.2.6
panic-probe v0.2.1
Sponsor this work
Knurling-rs is mainly funded through GitHub sponsors. Sponsors get early access to the tools we are building and help us to support and grow the knurling tools and courses. Thank you to all of the people already sponsoring our work through the Knurling project!