Rust Tools and Resources for Embedded - Page 1
Forum section: electronics / general

author: uliano
author_rank: Regular Contributor
timestamp: March 30, 2023, 10:04:55 am
content: PLEASE, PLEASE, PLEASE try not to pollute this thread with ad hominem, strawman, funky and flawed edge cases... we already have the otherthreadfor that.It would be nice to collect here only constructive discussion and useful information.FLASHING/RUNNING/LOGGING/DEBUGGINGTwo somewhat overlapping tool-sets competing for sponsors, the first oneprobe-rsis the rusty implementation that replaces gdb (includes a gdb stub for seamless integration)On windows if you want to use J-Link you need to replace its USB drivers withzadigthus preventing the regular Segger tools to connect to it (how can possibly those people think that you ever wanna do that???)beside the debugger itself, two command line tools and a vscode extension are provided:cargo-flash:downloads the code to the MCU, starts it, and returnscargo-embed:downloads the code to the MCU, starts it, open RTT terminal (s) and starts the debugger server (you need to connect to it manually)can save logs -AFTER- you kill the communication, saving does not support defmt (see below)probe-rs-debugger:a vscode extension that allows seamless debugging experience by configuring launch.json, logs are directed to a vscode terminal (I don't know if and how the can be automatically saved). This extension + probe-res functionally replace cortex-debug + openocd which is also a viable alternative, even if I perceive the older (and possibly going to be dismissed) one.To my understanding these are the "original" tools on which the second toolsetknurling toolsis built upon. They provide different resources:probe-run:is a cargo-embed replacement which adds stack back-traces (albeit a false positive warning "(HOST) WARN call stack was corrupted; unwinding could not be completed still appears notwithstanding the fact that itsissuehas been closed since long time) and integrates with vscode extension rust-analyzer by enabling the run|debug button (this doesn't work for me, initially I believed that it was due to the funky macros of RTIC (see below) but then I downloaded an officialtemplatewhich doesn't use RTIC macros, here run|debug appears -only- over # functions. Fun fact: if you delete the target directory and perform a developer-reload-window in vscode the run|debug button appears momentarily). Probe run sends the logs to stdout allowing redirection to an online, host side, application (e.g. for plotting, oh I'm gonna love this so much!). To facilitate writing such applications probe-run can format the defmt (see below) logs in json format.defmt:logging framework optimized for efficiency through compile time encoding of the log format that are decoded host side. This, combined with RTT, allows for the highest logging speed. Inside a vscode defmt messages are clickable leading to the source log statement.flip-link:zero-cost stack overflow protection. (nice and easy)app-template:showcase for defmt logging, flip-link protection and run time panic.defmt-test:didn't have time for thisREAL TIME AND CONCURRENCYUsing plain rust in a MCU where ISR are essential for most projects is kinda pain in the ass as the usual pattern (is there any alternative??) is to initialize ISR resources in the main thread and the borrow checker makes this simple thing difficult. Following this pattern there are no "security" issues as when initalizing interrupts are disabled. TheRTICframework was devised to facilitate this process, it organizes the code in tasks of four kinds:two running at the "main" level one after the otherinit:here you set up all the hardware resources and divides them in local (meant to be used by one and only one task) and shared with different strategies to prevent data racesidle:useful if you want to implement sleep between interrupts or if you are a superloop nostalgicand two interrupt drivenhardware:old style ISRsoftware:these can be scheduled _at or _after some time or duration by exploiting unused interrupt vectorsAll this "magic" takes care for you of the tedious chore of fighting the borrow checker when you don't need it and is based on macros that aggressively rewrite the source code before sending it to the compiler (nothing to do with the dumb cpp #define(s) )Its API are under fast development so stability does not live here, but the major bummer is that (for the moment?) is cortex-m only, however this seems to fit quite well in the ISR driven MCU paradigm for REAL TIME applications.Hereis an application template using defmt.Another option which seem to have a lot of hype (more than RTIC) isEmbassywhich approaches concurrency through coroutines exploiting rust's async/await mechanism which is, you know, a form of multithreading not really REAL TIME. Yes you can spawn different executors each with its own priority (and its own stack!!) but these are no ISR. This and the fact that it comes with loads of communication resources (USB,Bluetooth,LoRa,Ethernet,...) seems to suggest that its major application is on the IoT side of embedded. (This is a very superficial glimpse on Embassy which I didn't have yet the time to investigate in some detail)MISSING- levels of abstractions (many)- useful crates (libraries, drivers,... )- ...INTERIM CONSIDERATIONS.Before convincing others of the superior safety of RUST based solely on its borrow checker, enthusiasts should take the time to guide those who are interested in approaching this language in the field of embedded systems. These individuals may wish to continue working with their hardware and using the paradigm for which it was designed, namely, responding to events through interrupts and creating drivers that take advantage of DMA. Furthermore, enthusiasts should provide information on the tools to use, some of which may not have undergone recent development, and they should strive to ensure that these tools are as bug-free as possible (meaning that at least the features advertised in one page websites should work! and, sadly no, some don't!).

author: tggzzz
author_rank: Super Contributor
timestamp: March 30, 2023, 10:29:09 am
content: Quote from: uliano on March 30, 2023, 10:04:55 amSeconded!Thanks for starting this thread and collating some information. Let us all hope it remains a source ofinformation.

author: uliano
author_rank: Regular Contributor
timestamp: March 31, 2023, 08:01:29 am
content: I'm afraid to have removed the lid of the Pandora's jar, and I don't like at all what I found.Lets start from some silly code, just a few lines more than rtic template, task 1 panics after 100 seconds while task 2 logs the tick count every second.Code:#!#!use rtic411 as _; // global logger + panicking-behavior + memory layout#[rtic::app(device = stm32f4xx_hal::pac, // TODO: Replace `some_hal::pac` with the path to the PACdispatchers = [SPI1,SPI2] // TODO: Replace the `FreeInterrupt1, ...` with free interrupt vectors if software tasks are used)]mod app {use dwt_systick_monotonic::DwtSystick;use rtic411::exit;use stm32f4xx_hal::{prelude::*,};// TODO: Add a monotonic if scheduling will be used#[monotonic(binds = SysTick, default = true)]type DwtMono = DwtSystick<96_000_000>;// Shared resources go here#struct Shared {// TODO: Add resources}// Local resources go here#struct Local {// TODO: Add resources}#fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {defmt::info!("init");// setup clockslet rcc = cx.device.RCC.constrain();let mut dcb = cx.core.DCB;let dwt = cx.core.DWT;let systick = cx.core.SYST;let clocks = rcc.cfgr.use_hse(25.MHz()).sysclk(96.MHz()).freeze();// Setup the monotonic timerlet mono = DwtSystick::new(&mut dcb, dwt, systick, clocks.sysclk().to_Hz());task1::spawn_after(100.secs()).ok();task2::spawn_after(1.secs()).ok();//timer.start(1.secs()).unwrap();(Shared {// Initialization of shared resources go here},Local {//timer// Initialization of local resources go here},init::Monotonics(mono// Initialization of optional monotonic timers go here),)}// Optional idle, can be removed if not needed.#fn idle(_: idle::Context) -> ! {defmt::info!("idle");loop {continue;}}// TODO: Add tasks#fn task1(_cx: task1::Context) {defmt::info!("Hello from task1!");exit();}#fn task2(_cx: task2::Context) {defmt::info!("Hello from task2!");defmt::trace!("time: {}",monotonics::now());task2::spawn_after(1.secs()).ok();}}here dwt_systick_monotonic is used to measure time, according to rticbookit'sQuotethen lookin' into itssourceyou learn that you can configure it in your cargo.toml file activating the "extend" feature to allow for longer durations/interval using 64bits like soCode:dwt-systick-monotonic = {version = "1.1.0", feature = "extend"}then you run your the silly example and, surprise it panics!Code:> cargo run --bin minimalwarning: D:\stuff\rustbedded\rtic411\Cargo.toml: unused manifest key: dependencies.dwt-systick-monotonic.featureCompiling rtic411 v0.1.0 (D:\stuff\rustbedded\rtic411)Finished dev [optimized + debuginfo] target(s) in 0.68sRunning `probe-run --chip STM32F411CEUx target\thumbv7em-none-eabihf\debug\minimal`(HOST) INFO flashing program (16 pages / 16.00 KiB)(HOST) INFO success!────────────────────────────────────────────────────────────────────────────────0 INFO init└─ minimal::app::init @ src\bin\minimal.rs:351 ERROR panicked at 'attempt to multiply with overflow', C:\Users\uliano\.cargo\registry\src\github.com-1ecc6299db9ec823\fugit-0.3.6\src\duration.rs:597:1└─ panic_probe::print_defmt::print @ C:\Users\uliano\.cargo\registry\src\github.com-1ecc6299db9ec823\panic-probe-0.3.0\src\lib.rs:91────────────────────────────────────────────────────────────────────────────────stack backtrace:0: HardFaultTrampoline<exception entry>1: lib::inline::__udfat ./asm/inline.rs:181:52: __udfat ./asm/lib.rs:51:173: cortex_m::asm::udfat C:\Users\uliano\.cargo\registry\src\github.com-1ecc6299db9ec823\cortex-m-0.7.7\src\asm.rs:43:54: rust_begin_unwindat C:\Users\uliano\.cargo\registry\src\github.com-1ecc6299db9ec823\panic-probe-0.3.0\src\lib.rs:72:95: core::panicking::panic_fmtat /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/core/src/panicking.rs:64:146: core::panicking::panicat /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/core/src/panicking.rs:114:57: minimal::app::init8: minimal::app::rtic_ext::main::{{closure}}at src\bin\minimal.rs:6:19: minimal::app::rtic_ext::main::__rtic_init_resourcesat src\bin\minimal.rs:6:110: rtic::export::Priority::new11: mainat src\bin\minimal.rs:6:112: Reset(HOST) WARN call stack was corrupted; unwinding could not be completed(HOST) ERROR the program panickederror: process didn't exit successfully: `probe-run --chip STM32F411CEUx target\thumbv7em-none-eabihf\debug\minimal` (exit code: 22)D:\stuff\rustbedded\rtic411>knowing that integer are checked for overflow when not building in release mode... I can observe that 100 seconds get wrappedCode:> cargo run --bin minimal --releasewarning: D:\stuff\rustbedded\rtic411\Cargo.toml: unused manifest key: dependencies.dwt-systick-monotonic.featureFinished release [optimized + debuginfo] target(s) in 0.09sRunning `probe-run --chip STM32F411CEUx target\thumbv7em-none-eabihf\release\minimal`(HOST) INFO flashing program (10 pages / 10.00 KiB)(HOST) INFO success!────────────────────────────────────────────────────────────────────────────────0 INFO init└─ minimal::app::init @ src\bin\minimal.rs:351 INFO idle└─ minimal::app::idle @ src\bin\minimal.rs:712 INFO Hello from task2!└─ minimal::app::task2 @ src\bin\minimal.rs:873 TRACE time: 96001360 ticks @ (1/96000000)└─ minimal::app::task2 @ src\bin\minimal.rs:884 INFO Hello from task2!└─ minimal::app::task2 @ src\bin\minimal.rs:875 TRACE time: 192007175 ticks @ (1/96000000)└─ minimal::app::task2 @ src\bin\minimal.rs:886 INFO Hello from task2!└─ minimal::app::task2 @ src\bin\minimal.rs:877 TRACE time: 288012990 ticks @ (1/96000000)└─ minimal::app::task2 @ src\bin\minimal.rs:888 INFO Hello from task2!└─ minimal::app::task2 @ src\bin\minimal.rs:879 TRACE time: 384018805 ticks @ (1/96000000)└─ minimal::app::task2 @ src\bin\minimal.rs:8810 INFO Hello from task2!└─ minimal::app::task2 @ src\bin\minimal.rs:8711 TRACE time: 480024620 ticks @ (1/96000000)└─ minimal::app::task2 @ src\bin\minimal.rs:8812 INFO Hello from task2!└─ minimal::app::task2 @ src\bin\minimal.rs:8713 TRACE time: 576030435 ticks @ (1/96000000)└─ minimal::app::task2 @ src\bin\minimal.rs:8814 INFO Hello from task2!└─ minimal::app::task2 @ src\bin\minimal.rs:8715 TRACE time: 672036250 ticks @ (1/96000000)└─ minimal::app::task2 @ src\bin\minimal.rs:8816 INFO Hello from task2!└─ minimal::app::task2 @ src\bin\minimal.rs:8717 TRACE time: 768042065 ticks @ (1/96000000)└─ minimal::app::task2 @ src\bin\minimal.rs:8818 INFO Hello from task2!└─ minimal::app::task2 @ src\bin\minimal.rs:8719 TRACE time: 864047880 ticks @ (1/96000000)└─ minimal::app::task2 @ src\bin\minimal.rs:8820 INFO Hello from task2!└─ minimal::app::task2 @ src\bin\minimal.rs:8721 TRACE time: 960053695 ticks @ (1/96000000)└─ minimal::app::task2 @ src\bin\minimal.rs:8822 INFO Hello from task1!└─ minimal::app::task1 @ src\bin\minimal.rs:81────────────────────────────────────────────────────────────────────────────────stack backtrace:0: lib::inline::__bkptat ./asm/inline.rs:14:51: __bkptat ./asm/lib.rs:51:172: rtic411::exitat src\lib.rs:30:53: minimal::app::task1at src\bin\minimal.rs:82:94: minimal::app::SPI1::{{closure}}at src\bin\minimal.rs:6:15: rtic::export::runat C:\Users\uliano\.cargo\registry\src\github.com-1ecc6299db9ec823\cortex-m-rtic-1.1.4\src\export.rs:68:96: SPI1at src\bin\minimal.rs:6:1<exception entry>7: minimal::app::idlat src\bin\minimal.rs:73:98: mainat src\bin\minimal.rs:6:19: Reset(HOST) WARN call stack was corrupted; unwinding could not be completed(HOST) INFO device halted without errorD:\stuff\rustbedded\rtic411>leading me to think that, nevermind the "extend" feature, here times are specified by 32bit structure.So I turned my attention tofugit, the crate that actually implements interval and durations, where i read:Quotethe hell? didn't I selected the u64 by specifying "extend" in dwt-systick-monotonic? Who brought in fugit with u32 backing store? I didn't knew I was on the edge of the abyss and I looked for a tool to print the building crate dependencies:Code:> cargo treertic411 v0.1.0 (D:\stuff\rustbedded\rtic411)├── cortex-m v0.7.7│ ├── bare-metal v0.2.5│ │ [build-dependencies]│ │ └── rustc_version v0.2.3│ │ └── semver v0.9.0│ │ └── semver-parser v0.7.0│ ├── bitfield v0.13.2│ ├── critical-section v1.1.1│ ├── embedded-hal v0.2.7│ │ ├── nb v0.1.3│ │ │ └── nb v1.1.0│ │ │ └── defmt v0.3.2│ │ │ ├── bitflags v1.3.2│ │ │ └── defmt-macros v0.3.3 (proc-macro)│ │ │ ├── defmt-parser v0.3.1│ │ │ ├── proc-macro-error v1.0.4│ │ │ │ ├── proc-macro-error-attr v1.0.4 (proc-macro)│ │ │ │ │ ├── proc-macro2 v1.0.54│ │ │ │ │ │ └── unicode-ident v1.0.8│ │ │ │ │ └── quote v1.0.26│ │ │ │ │ └── proc-macro2 v1.0.54 (*)│ │ │ │ │ [build-dependencies]│ │ │ │ │ └── version_check v0.9.4│ │ │ │ ├── proc-macro2 v1.0.54 (*)│ │ │ │ ├── quote v1.0.26 (*)│ │ │ │ └── syn v1.0.109│ │ │ │ ├── proc-macro2 v1.0.54 (*)│ │ │ │ ├── quote v1.0.26 (*)│ │ │ │ └── unicode-ident v1.0.8│ │ │ │ [build-dependencies]│ │ │ │ └── version_check v0.9.4│ │ │ ├── proc-macro2 v1.0.54 (*)│ │ │ ├── quote v1.0.26 (*)│ │ │ └── syn v1.0.109 (*)│ │ └── void v1.0.2│ └── volatile-register v0.2.1│ └── vcell v0.1.3├── cortex-m-rtic v1.1.4│ ├── bare-metal v1.0.0│ ├── cortex-m v0.7.7 (*)│ ├── cortex-m-rtic-macros v1.1.6 (proc-macro)│ │ ├── proc-macro-error v1.0.4 (*)│ │ ├── proc-macro2 v1.0.54 (*)│ │ ├── quote v1.0.26 (*)│ │ ├── rtic-syntax v1.0.3│ │ │ ├── indexmap v1.9.3│ │ │ │ └── hashbrown v0.12.3│ │ │ │ [build-dependencies]│ │ │ │ └── autocfg v1.1.0│ │ │ ├── proc-macro2 v1.0.54 (*)│ │ │ ├── quote v1.0.26 (*)│ │ │ └── syn v1.0.109 (*)│ │ └── syn v1.0.109 (*)│ ├── heapless v0.7.16│ │ ├── hash32 v0.2.1│ │ │ └── byteorder v1.4.3│ │ └── stable_deref_trait v1.2.0│ │ [build-dependencies]│ │ └── rustc_version v0.4.0│ │ └── semver v1.0.17│ ├── rtic-core v1.0.0│ └── rtic-monotonic v1.0.0│ [build-dependencies]│ └── version_check v0.9.4├── defmt v0.3.2 (*)├── defmt-rtt v0.3.2│ ├── critical-section v0.2.8│ │ ├── bare-metal v1.0.0│ │ └── critical-section v1.1.1│ └── defmt v0.3.2 (*)├── dwt-systick-monotonic v1.1.0│ ├── cfg-if v1.0.0│ ├── cortex-m v0.7.7 (*)│ ├── fugit v0.3.6│ │ ├── defmt v0.3.2 (*)│ │ └── gcd v2.3.0│ └── rtic-monotonic v1.0.0├── panic-probe v0.3.0│ ├── cortex-m v0.7.7 (*)│ └── defmt v0.3.2 (*)└── stm32f4xx-hal v0.15.0├── bare-metal v1.0.0├── bitflags v1.3.2├── cortex-m v0.7.7 (*)├── cortex-m-rt v0.7.3│ └── cortex-m-rt-macros v0.7.0 (proc-macro)│ ├── proc-macro2 v1.0.54 (*)│ ├── quote v1.0.26 (*)│ └── syn v1.0.109 (*)├── defmt v0.3.2 (*)├── embedded-dma v0.2.0│ └── stable_deref_trait v1.2.0├── embedded-hal v0.2.7 (*)├── embedded-hal v1.0.0-alpha.8│ └── nb v1.1.0 (*)├── embedded-storage v0.2.0├── fugit v0.3.6 (*)├── fugit-timer v0.1.3│ ├── fugit v0.3.6 (*)│ └── nb v1.1.0 (*)├── nb v1.1.0 (*)├── rand_core v0.6.4├── rtic-monotonic v1.0.0├── stm32f4 v0.15.1│ ├── bare-metal v1.0.0│ ├── cortex-m v0.7.7 (*)│ ├── cortex-m-rt v0.7.3 (*)│ └── vcell v0.1.3├── systick-monotonic v1.0.1│ ├── cortex-m v0.7.7 (*)│ ├── fugit v0.3.6 (*)│ └── rtic-monotonic v1.0.0├── time v0.3.20│ └── time-core v0.1.0└── void v1.0.2D:\stuff\rustbedded\rtic411>It's just me freaking out for the multiple usages of many different crates OVER WHICH I CAN'T HAVE ANY CONTROL, or can I?, worse the version may be different in each usage and even worst the same crate has a newer version of itself as a dependency?Sorry, here it is where I call myself out. Even if I had time (my week of forced vacation is turning to an end) and will, I don't even know how and to whom report this gigantic issue./rant alertHow can people using this stuff not be aware of this mess?Maybe now I understand why there is so much focus on safety, could it be a gigantic diversion?/end rant

author: gekkio
author_rank: Newbie
timestamp: March 31, 2023, 11:55:54 am
content: Looks like the extend feature didn't get enabled because there was a typo: in Cargo.toml, you should list it underfeatures, not feature:Code:dwt-systick-monotonic = { version = "1.1.0", features = ["extend"] }If you look closely, you even got a warning about it, which is unfortunately easy to miss, especially if you get a lot of output lines when building your project:Code:> cargo run --bin minimal --releasewarning: D:\stuff\rustbedded\rtic411\Cargo.toml: unused manifest key: dependencies.dwt-systick-monotonic.featureIt's not quite the same thing, but imagine you typoed some very important #define in some C code and the program instead used a default value leading to problems. It would be lovely to get a better error message and/or catch these at compile time, but it's unfortunately not always easy. :'(I've used both RTIC and Embassy and recently had a slightly similar timer overflow issue with the latter, which turned out to be a configuration error caused by my own code. At first I felt annoyed, but in retrospect I think it was great to get a panic in debug mode instead of having the entire timekeeping of the system messed up silently. The main challenge was understanding the real cause of the problem based on the error message, which was a fairly generic overflow panic in some timer code.One useful tip: you can get a fairly verbose but useful explanation of actually enabled features of dependencies by runningcargo tree -e features

author: uliano
author_rank: Regular Contributor
timestamp: March 31, 2023, 12:16:07 pm
content: OMG!!Funny thing is that Rust-analyzer wasn't complaining at all.However now I Have a new problem: the feature is enabled but I cant use anymore 1.secs() as I get:Code:error: could not compile `rtic411` due to 4 previous errorserror: mismatched types--> src\bin\minimal.rs:51:28|51 | task1::spawn_after(100.secs()).ok();| ------------------ ^^^^^^^^^^ expected `u64`, found `u32`| || arguments to this function are incorrect|= note: expected struct `dwt_systick_monotonic::fugit::Duration<u64, 1, 96000000>`found struct `dwt_systick_monotonic::fugit::Duration<u32, _, _>`note: function defined here

author: gekkio
author_rank: Newbie
timestamp: March 31, 2023, 12:36:45 pm
content: My guess is that you have importedfugit::ExtU32in that file, which should be replaced byfugit::ExtU64. One way to think about the error is that the.secs()function currently only exists for the u32 type, so Rust infers the type of the literal100to be u32 instead of u64.In general Rust is much stricter about integer types than C (no implicit int conversions!), but also can infer the intended type of literals correctly as long as there's enough information available for the compiler.In this case you could also use a more explicit literal100_u64to force the type, but this would just lead to a different error since the secs function would simply not exist for the type at all.

author: uliano
author_rank: Regular Contributor
timestamp: March 31, 2023, 12:54:39 pm
content: Quote from: uliano on March 31, 2023, 12:16:07 pmgot away with dwt_systick_monotonic::fugit::Duration::<u64,1, 96_000_000>::from_ticks(96_000_000)it seems that there are 2 different implementation of fugit, one based on u32, brought in byCode:use stm32f4xx_hal::{prelude::*,};and used everywhere, for example here for rate units:Code:let clocks = rcc.cfgr.use_hse(25.MHz()).sysclk(96.MHz()).freeze();the other based on u64 used by fugit.even if I could express a duration in terms of the latter I wasn't able to use shortcut methods (like .secs() )for human readable units.This design with compile time option for the types is very much convoluted.

author: uliano
author_rank: Regular Contributor
timestamp: March 31, 2023, 01:01:52 pm
content: Quote from: gekkio on March 31, 2023, 12:36:45 pmI didn't it was stm32f4xx_hal to import it, and for the good reason that it is using it in u32 version.as for the 1_u64.secs I getno method named `secs` found for type `u64` in the current scopeitems from traits can only be used if the trait is in scopesure, the current scope has u32 version brought in by hal's prelude.

author: uliano
author_rank: Regular Contributor
timestamp: March 31, 2023, 01:20:54 pm
content: this is solved by specifying both u32 and u64 before .secs() & co:Code:use dwt_systick_monotonic::fugit::*;use stm32f4xx_hal::{prelude::*,};...let clocks = rcc.cfgr.use_hse(25_u32.MHz()).sysclk(96_u32.MHz()).freeze();...task2::spawn_after(1_u64.secs()).ok();

author: SiliconWizard
author_rank: Super Contributor
timestamp: March 31, 2023, 08:40:03 pm
content: What does a 'panic' on some MCU lead to?

author: nctnico
author_rank: Super Contributor
timestamp: March 31, 2023, 08:48:12 pm
content: Quote from: SiliconWizard on March 31, 2023, 08:40:03 pm(Watchdog) reset ofcourse...