4 ms·
> Each HAL implements different things, or the same thing in different ways. They are generated programmatically from core description files that are only avai
by R0b0t1 5y ago
> Each HAL implements different things, or the same thing in different ways.
They are generated programmatically from core description files that are only available to STM. They are designed sloppily because a lot of the implementation work is automated. To really progress in this area FOSS developers are going to need to get ahold of these interface specifications.
> Lack of emulation. You can emulate an arm cpu in qemu, but there’s no tools for mocking out hardware - which means testing has to happen on device (or you need to fragment your application into qemu-testable pieces).
This should be OK and I almost prefer it. Get a JTAG or SWD adapter, learn how to use GDB and the self test hardware, and you'll be fine. Modern MCUs are designed for test and can be used in circuit just fine. You can control any part of the chip via the trace macrocell.
> Shared memory / global initialization is overly complex.
Related to my first point.
- MrBuddyCasino 5y agoDo you mean the register-level description generated from SVD? I thought one works mainly with the higher-level HAL traits, or is that not yet possible?
- zwirbl 5y agoYou would use the traits from embedded-hal to write e.g. a driver for some IC or when implementing functionality in a device agnostic way and then use (or write) a specific HAL driver implementing these
- kennywinker 5y agoIt's great that embedded-hal exists, but somehow the DMA drivers in the stm32f4xx_hal and stm32f1xx_hal crates have a pretty radically different api, which to me means something's a little broken.
- zwirbl 5y agoOne issue with STM devices is the shear amount of peripheral implementations, e.g. at least 3 USART implementations over the years, with all kinds of small differences. Pair this with inconsistent SVD files and you are halfway to the current HAL situation. It's intended for async usage, but take a look at embassy-rs (not yet on crates right now) The embassy contributors put a ton of work into unifying the pac for different versions of the peripherals and called it metapac
- zwirbl 5y agoIt's the PACs (peripheral access crates) that are generated from vendor supplied SVD files, including many patches. These are register definitions Most HALs are actually hand written, often leveraging macros to implement similar functionality for multiple peripheral instances
- nereye 5y agoWell, with Rust tooling that automates the creation of crates from these 'core description files' (aka CMSIS SVD, System View Description), there is a large interest from other CPU vendors too so SVD seems to have become a de facto standard. For example, Espressif has SVD definitions for their CPUs (e.g. ESP32), which are definitely not ARM: https://github.com/espressif/svd https://github.com/espressif/svd, etc.
- kennywinker 5y ago> This should be OK and I almost prefer it. Get a JTAG or SWD adapter, learn how to use GDB and the self test hardware I'd really like to write a test that e.g. confirms that if PB13 goes high for longer than 10ms, and event is triggered. Yes, I can run that code on my MCU - but actually testing it requires pressing buttons. With an emulator, I could easily write code to mock out the behavior of external hardware and confirm that my code is handling that input correctly. > Related to my first point I'm actually talking about a constraint of Rust - not the HAL or PAC. Per https://docs.rust-embedded.org/book/concurrency/index.html https://docs.rust-embedded.org/book/concurrency/index.html the canonical way to use a GPIO from an interrupt is something like: static G_LED: Mutex<RefCell<Option<LEDPIN>>> = Mutex::new(RefCell::new(None)); fn main() { // get the gpio and pass off to mutex let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); // Move the pin into our global storage cortex_m::interrupt::free(|cs| *G_LED.borrow(cs).borrow_mut() = Some(led)); } fn interrupt() { static mut LED: Option<LEDPIN> = None; // load the led from the global mutex let led = LED.get_or_insert_with(|| { cortex_m::interrupt::free(|cs| { // Move LED pin here, leaving a None in its place G_LED.borrow(cs).replace(None).unwrap() }) }); // actually use the led gpio here } If we were genuinely using G_LED from main and from the interrupt, the mutex song-and-dance makes sense, since we want to be careful about how we share use of this resource. But since we just want to set it up in main and use it in the interrupt it's a LOT of confusing overhead/abstraction.
- sdbranam 5y agoCan you recommend any resources on using the self test HW (assuming JTAG/SWD/GDB are all setup, although I'd love to see any recommendations on those as well)? I currently don't know anything about the trace macrocell.
- R0b0t1 5y agoCheck the documentation for GDB and OpenOCD and the overviews from STMicro. You can poke things into memory, like peripheral configurations into peripheral registers. You can have the trace cell generate events on the debug port based on memory access or other events, analogous to Intel/AMD performance counters and iperf/xperf. It also implements breakpoints and code stepping in hardware. To do full mockups you usually need an IO board that can drive pins, but you can also have the trace macrocell jump to code that sets an IO pin's internal driver high and triggers the input side of the IO port.