Article

Knurling-rs changelog #34

Published on 5 min read
Open Source Project
Knurling icon
Knurling
A tool set to develop embedded applications faster.
Details ❤️ Sponsor

    This is the 34th 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 🎉

    #[derive(Format)] now with better support for third party types

    A common situation that arises when implementing the Format trait for a struct or enum is running into fields that don't implement the Format trait and come from a third party crate.

    #[derive(Format)]
    pub struct MyStruct {
        field0: MyType,
        field1: third_party::Type,
        //~^ error: `third_party::Type` does not
        //          implement the Format trait
    }
    
    #[derive(Format)]
    pub enum MyType { /* .. */ }
    

    Because #[derive(Format)] requires that all fields implement the Format trait, the attribute fails in this case, and because the type comes in a third party crate it's not straightforward to implement the Format trait for it.

    To handle these cases one can use the Debug2Format adapter newtype to get a Format implementation based on the Debug implementation of the type. This, however, required a manual implementation of the Format trait.

    impl defmt::Format for MyStruct {
        fn format(&self, f: defmt::Formatter) {
            defmt::write!(
                f,
                "MyStruct {{ field0: {}, field1: {} }}",
                &self.field0,
                &defmt::Debug2Format(&self.field0),
            )
        }
    }
    

    Thanks to PR #662 such a manual implementation is no longer needed: one can use the #[Debug2Format] field attribute to the same effect.

    #[derive(Format)]
    pub struct MyStruct {
        field0: MyType,
        #[defmt(Debug2Format)] // now it compiles!
        field1: third_party::Type,
    }
    
    #[derive(Format)]
    pub enum MyType { /* .. */ }
    

    Thanks @mattico for the idea and implementation!

    defmt-rtt supports more devices including the RP2040

    defmt-rtt is the main "transport" crate in the defmt ecosystem. It transports defmt-encoded logs emitted from a microcontroller to a second machine where they'll be decoded and printed (or stored for later use).

    Transport crates work by setting up a global logger that must be accessed through a synchronization mechanism. In the case defmt-rtt, it used the cortex-m crate to create a critical section by disabling interrupts. Although this mechanism works fine on single-core devices it is not appropriate for multi-core devices like the RP2040. In order to support such devices, defmt-rtt now uses the critical-section crate as a synchronization mechanism.

    The critical-section crate allows statically registering a critical section mechanism in order to support vendor-specific implementations like the one the RP2040 chip needs due to its lack of atomic instructions (LDREX, STREX) required to implement a Compare-And-Swap (CAS) operation.

    faster --measure-stack

    probe-run gained a runtime stack measurement mechanism last year: --measure-stack. --measure-stack reports the maximum stack usage of a program when it exits. To do that it works in two stages:

    • before the start of the program, probe-run fills the entire stack memory with a known bit pattern. we call this process, "stack painting"
    • on program exit, probe-run scans the stack memory until it finds "a spot" (the address) where the program execution undid the stack painting due to its stack usage. subtracting the start address of the stack from this "watermark" level yields the maximum stack usage of the program

    although this works fairly well the current implementation uses the probe (debug interface commands) to perform the two phases: this is very slow and adds several seconds to the execution of the program (to both the startup and the exit time).

    @Urhengulas has been working on moving the two phases into the target microcontroller itself. The initial results are quite encouraging: performing the "stack painting" on the target is ~170 times faster than the current implementation. We hope that after moving the second phase into the target, the overall time the feature adds to executing programs is small enough that the feature can be enabled by default without the end users noticing the slow down.

    Watch this space!

    defmt in the wild

    We keep an eye on social media and other chat platforms for cool projects using defmt. Like we shared before that the network stack, smoltcp, started using defmt some time ago, this time we want to put pretty-hal-machine in the spotlight.

    Pretty-Hal-Machine (PHM) proposes a different approach to developing no_std driver crates. Instead of writing a no_std test application that runs directly on the microcontroller that interacts with the sensor, actuator, lights, etc. you are writing the driver for, you write a desktop application that has the standard library available and use that to test the no_std driver crate. This is accomplished with a dev board that runs the PHM firmware and exposes a USB interface to control its different peripherals: SPI, I2C and UART at the moment. On the desktop side, PHM is a library that implements the embedded-hal traits and, under the hood, commands the PHM dev board via USB commands.

    We are glad to see that the PHM developers are using defmt and other knurling tools in the development of their PHM firmware and are looking forward to seeing where they take this project.

    Improvements 🦀

    defmt

    • #662 #[derive(Format)] now accepts attribute fields to format fields using the Debug2Format adapter instead of a Format implementation.
    • #656 implement Format for Cell and RefCell
    • #640 use critical-section crate in defmt-rtt

    probe-run

    • #302 make stack painting faster
    • #301 now accepts chip description files (YAML) via --chip-description-path, extending device support beyond probe_rs' device registry
    • #303 now tries to decode defmt logs when --no-flash is used, but emits a warning

    Internal Improvements 🧽

    defmt

    • #661 add tests for Cell types
    • #659 make export::{acquire,release} (internal API) unsafe

    probe-run

    • #299 refactor extract_stack_info

    Version Update Notification 🆙

    • defmt 0.3.1
    • defmt-decoder 0.3.2
    • defmt-json-schema 0.1.0 (new)
    • defmt-macros 0.3.2
    • defmt-parser 0.3.1
    • defmt-print 0.3.2
    • defmt-rtt 0.3.2
    • probe-run 0.3.2

    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!