-
Notifications
You must be signed in to change notification settings - Fork 98
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Device family HALs in rust #33
Comments
I've been working on this exact problem for about a year now, and you bring up a lot of great points about what's desirable and how hard it will be to accomplish. I believe the only workable approach is to have layers of crates covering architecture-common, vendor-common, subfamily-common, and eventually chip-specific features. There are some use cases where developers will want to target purely high-level traits, but it is just as important to be able to work with highly optimized and tested vendor-specific peripheral drivers that expose all the functionality available. Unfortunately, I don't think SVD is suitable except as a starting point. It simply wasn't designed for that purpose and doesn't support the necessary concepts, not to mention that the quality and style of SVD files varies widely between vendors when they are even available. My view is that vendors see SVD primarily as a convenient output format to use in their GUI device-aware debuggers, not as a source of truth to be used for source generation. I'm also wary about a macro-heavy approach to configuration and peripheral definition. Those are areas where debuggability and understandability are really important, especially for embedded device programmers coming to rust, and complex macro systems can be opaque even for experienced Rust programmers. I think in most cases where someone is targeting an application at multiple boards / MCU models (i.e. not just variants with different amounts of memory), it will make sense to break the higher level application logic into a separate crate and have individual top-level crates that do the board / MCU-specific configuration. So you have something like: Top Level Crate (initialization and configuration) depending on the complexity of your application and how broadly you expect it to be deployed. |
I have done some work in this area. Here's a WIP HAL as a set of traits. You The applications don't directly use registers but the slightly higher level HAL
Rust doesn't have macros in the C sense (a "preprocessor"). By "macro-ing the There have been some recent discussion about using
What set of traits do you have in mind? Depending on how you define them it Something like this has been brought up in the svd2rust repo before in
This sounds like rust-embedded/svd2rust#96. I still prefer less types and re-exports
There's already a common trait between several instances of the same peripheral. // "newtypes"
struct TIM2(tim2::RegisterBlock);
struct TIM3(tim2::RegisterBlock);
struct TIM4(tim2::RegisterBlock);
// the common type: a general purpose timer
mod tim2 {
struct RegisterBlock {
pub CR1: CR1,
// ...
}
}
// the common trait
impl Deref for TIM2 { type Target = tim2::RegisterBlock; /* .. */ }
impl Deref for TIM3 { type Target = tim2::RegisterBlock; /* .. */ }
impl Deref for TIM4 { type Target = tim2::RegisterBlock; /* .. */ }
// instances
const TIM2: Peripheral<TIM2> = ..;
const TIM3: Peripheral<TIM3> = ..;
const TIM4: Peripheral<TIM4> = ..; This lets you write generic code that works with all the instances of a general // generic function
fn foo<T>(tim: &T) where T: Deref<tim2::RegisterBlock> {
let tim2: &tim2::RegisterBlock = tim.deref();
tim2.arr.write(..);
}
// that works with all the instances
foo(&TIM2);
foo(&TIM3);
foo(&TIM4);
What are #device and #family macros? Do you mean |
Hey, thanks for the responses! Pretty much everywhere I put "macro" I should have put conditional compilation ( The top level embedded-hal is an awesome concept for the top level (ie. how to implement/use generic peripherals) abstraction (though I am sure likely to lead to some contention ^_^). The hal implementation in blue pill is also great, but still depends on explicit definition of the f103xx. Totally agree about the elegance of sharing concrete types across families (ie I would argue it is not necessary to erase peripheral names, having them injected into/through the hal(s) from the application level makes it clear what is in use underneath, and allows for board support packages or platform definitions that define useful names for peripherals where required. And within the I didn't realise rust2svd did common traits, on further investigation it looks like the vendor SVD files I am playing with don't use So, what is required to achieve it, and are there any useful small tasks we can undertake to help get there? It looks like a formalisation of the abstraction layers like @jcsoo's above and support for some kind of shared types (SVD / Rust objects / Something else) in svd2rust would be the first blockers? Also, thanks for all the great work you do! Very excited for this future of embedded rust. |
left some tentative next steps for that issue in rust-embedded/svd2rust#96 (comment) |
I feel like this issue should probably be revisited. CC'ing the people I know who are particularly vocal about sharing code within a family: @ryankurte (No priority on this, but I am currently triaging issues. |
some of my comments in #62 (about a higher level abstraction to the hal) apply to this issue. I show how the code for the stm32f1 and stm21f4 differ when setting up a pin. |
Closing this as part of 2024 triage. Today, this is typically the scope of individual HAL implementors, I'd suggest looking at what If you think this was closed incorrectly, please leave a comment and we can revisit this decision in the next weekly WG meeting on Matrix. |
Hey all,
Has anyone looked at / worked out how to build generic HALs / driver libraries that work across a family of cores instead of writing a peripheral implementation for each? IMO it is an important layer of abstraction to make embedded rust easier both to use and maintain, and I haven't seen it mentioned in any of the roadmaps so far or worked out how to achieve it yet.
As an example of what I am talking about, when using silicon labs cores in C we have the vendor provided emlib that includes
<em_device.h>
, through some macro wizardry that ends up being replaced with the processor headers, then each of the peripheral driver functions consumes / works on the types exposed in those headers. For an example, see em_gpio.h and em_gpio.c.So we have one set of drivers to use (and maintain, though in this case by the vendor) that exploit the commonality between device families. This provides a useful abstract interface (ie. SPI mode, frequency rather than registers) and the correct peripherals are either loaded by default where there is one instance (eg. CMU) or injected into the driver where there are multiple (eg. SPI0, SPI1). Also, swapping between MCUs within a family is only a matter of changing the
-DEFM32G210F128
argument on the command line and some pin/instance definitions.The options I have come up with so far to support this in rust are:
The first is imo the most achievable now, but because of the way rust2svd outputs files there is no common trait between a pair of the same peripherals, so I can't see how to abstract across both. The second is IMO the most elegant, in that the crate for a given device could include the family crate and use that to support the SVD output from rust2svd, and applications could then include the family crate and
#cfg
between device crates, though the HAL is going to need to do the same to handle different functions across devices. I guess that would end up looking something like (from the bottom up):#device
,#family
etc macros#efm32device, #efm32family
macrosHowever it's achieved it seems like there are going to be some interdependencies, but I would appreciate any feedback / thoughts / ideas on how to best solve it ^_^
The text was updated successfully, but these errors were encountered: