You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Types generated by svd2rust are too specific to write reasonable HAL implementations.
Causal Factors:
SVD files are often garbage, and garbage in garbage out. Moreover, some reasonable types of abstraction can't be expressed within the SVD schema.
Motivating Example:
STM32F439x.svd has two peripherals within the <groupName> CAN; CAN1 and CAN2. As a consequence of this, the generated RegisterBlock for CAN2 is of type can1::RegisterBlock... which is wrong, because on this chip the two CAN peripherals have different sets of registers (see page 1120 of the reference manual ).
As if that's not bad enough, each CAN peripheral has 28 filter banks, each of which composed of two registers, each of which is rendered to its own type. So to implement a function add_filter that populates the next unused filterbank you have to hardcode the checking and manipulation of 56 registers (not counting the metadata registers that track things like whether a filter is in use)... and it'd be double that if the SVD wasn't incorrectly causing the two CAN peripherals to share a RegisterBlock.
What I would expect from a reasonable API, in this case, is a type hierarchy, a trait can::RegisterBlock, as well as the structs can1::RegisterBlock (which adds registers unique to can1) and can2::RegisterBlock (which adds nothing, only on account of it being a subset of can1). Within can::RegisterBlock I would expect an ordered collection of can::RegisterBlock::FilterBanks (which are themselves composed of the two registers).
So the question is, how can we get there from here?
Naive Proposal 1
Fix the SVD. SVD supports register cluster arrays, use them to group the 28 filterbanks. Sure, you'd still need a HAL implementation for each CAN Peripheral, but that's what macros are for!
There are a lot of problems with this approach:
Rewriting large portions of the SVD is a shit load of work, and it has to be repeated for every version of every SVD.
It can't express "the intersection of the specified perpherals" that could generate a trait. Macros can mitigate much of this problem, but at the cost of going from one code generator (svd2rust) to many (svd2rust plus the hand written macros for each generated crate).
It tends to burn people out. Talking to people on # rust-embedded who have gone down this road, they seem to prefer manually adding layers over the generated crate over fixing the input xml.
My (Naive?) Proposal
Split svd2rust's compilation process into two phases; the first phase populates a map of SVD elements to their respective Tokens, and the second phase is to traverse the map, sending Tokens to the output. After this step there should be no change in svd2rust's behavior.
Create a query engine for parsed svd (ie the results of the svd_parser crate) that resembles jQuery in function and form.
Add a parameter to svd2rust that takes the name of a script, and run each query in the script against the map before it is serialized to the various output vectors.
Use this to generate more flexible MCU crates.
The text was updated successfully, but these errors were encountered:
I definitely agree with the problems, and some kind of scripted / mapping could be really useful.
Not sure that I like the macros for multiple HAL implementations concept so much.
You might be interested in #96 which is around automating common feature extraction etc. from svd files to solve both the duplicated peripherals per device and duplicated peripherals within device family problems.
I think the latest suggestion is a pipeline to pre-process batches of SVDs into more useful form (ie. with clusters and derives), then svd2rust into sensible packages.
Some kind of scripted approach like you propose would be really interesting in the pre-processing stage.
That sounds remarkably similar to what I was proposing above, just with more modularity. It also looks like there hasn't been much activity in 6 months. Is there a way I can help? Perhaps defining a format to serve as your "more useful form"?
Problem:
Types generated by svd2rust are too specific to write reasonable HAL implementations.
Causal Factors:
SVD files are often garbage, and garbage in garbage out. Moreover, some reasonable types of abstraction can't be expressed within the SVD schema.
Motivating Example:
STM32F439x.svd has two peripherals within the
<groupName>
CAN; CAN1 and CAN2. As a consequence of this, the generated RegisterBlock for CAN2 is of typecan1::RegisterBlock
... which is wrong, because on this chip the two CAN peripherals have different sets of registers (see page 1120 of the reference manual ).As if that's not bad enough, each CAN peripheral has 28 filter banks, each of which composed of two registers, each of which is rendered to its own type. So to implement a function
add_filter
that populates the next unused filterbank you have to hardcode the checking and manipulation of 56 registers (not counting the metadata registers that track things like whether a filter is in use)... and it'd be double that if the SVD wasn't incorrectly causing the two CAN peripherals to share aRegisterBlock
.What I would expect from a reasonable API, in this case, is a type hierarchy, a trait
can::RegisterBlock
, as well as the structscan1::RegisterBlock
(which adds registers unique to can1) andcan2::RegisterBlock
(which adds nothing, only on account of it being a subset of can1). Withincan::RegisterBlock
I would expect an ordered collection ofcan::RegisterBlock::FilterBank
s (which are themselves composed of the two registers).So the question is, how can we get there from here?
Naive Proposal 1
Fix the SVD. SVD supports register cluster arrays, use them to group the 28 filterbanks. Sure, you'd still need a HAL implementation for each CAN Peripheral, but that's what macros are for!
There are a lot of problems with this approach:
My (Naive?) Proposal
svd2rust
's compilation process into two phases; the first phase populates a map of SVD elements to their respectiveToken
s, and the second phase is to traverse the map, sendingToken
s to the output. After this step there should be no change in svd2rust's behavior.The text was updated successfully, but these errors were encountered: