svd2rust: Common traits for identical IP blocks
I want to be able to write generic functions that can operate on any instance of the same peripheral across a vendor’s product line, or any instance of the same peripheral within a given chip.
For a concrete example: as far as I know, ST Micro uses the same bxCAN IP on every chip in the STM32 line that has a CAN peripheral, and on some chips they have multiple copies of the bxCAN peripheral. So I’d like to be able to write generic code that uses a bxCAN peripheral, independent of which STM32 product you’re building for and which instance of the peripheral you’re using.
I think a lot of the relevant information can be inferred directly from the .svd files, by checking whether two peripherals have identical register definitions, ignoring reset values. But I can’t decide how much manual intervention makes sense.
What are your thoughts?
About this issue
- Original URL
- State: open
- Created 7 years ago
- Reactions: 9
- Comments: 30 (13 by maintainers)
Hi everyone! I missed this conversation at the time but coincidentally was working on something similar and would be interested in pushing this issue forward.
I downloaded the official ST SVD files for all the STM32 devices (here) and wrote a bunch of scripts to parse and compare them initially. One main result is this big set of tables. For a representative member of each STM32F family (I did the same for L and H too), you can see which unique peripherals there are and which devices have them, and then drill down to all the chips in that family, and to each register and field and bitfield in each as well. For example, here you can see all STM32F7s have the same CRC peripheral at the same address, but for some reason two of them shift the INIT and POL register addresses.
Overall there are a lot of peripherals that are common or have common supersets. One really helpful way to group them is looking at the ChibiOS STM32 HAL organisation, e.g. it has USARTv1 and USARTv2 and you can see which chips use which drivers.
My attempt at making these at least usable by e.g. macros in drivers (so they’d work with same-named registers even if not the actual same type) was to created ‘patched’ SVD files which automatically fill in and match up details from shared peripherals, fully annotated with descriptions and enums etc. That’s what’s in this repo; in
peripherals/there are files for each shared peripheral design in a yaml format that applies patches to the SVD entries, and indevices/there is a file for each STM32 specifying which SVD file to start from and which peripherals to apply and any per-file fixes as well (there is no shortage of typos or inconsistencies in ST’s SVDs). Then a Makefile generates all the patched SVDs and runs svd2rust against them, creating a crate for each family with a cfg-gated library for each chip. There’s no shared traits between the chips, but they are as consistent as possible given the hardware.For relatively complete examples, check out the STM32F405 – it has a couple of typos to correct, then pulls in a bunch of common and more-specific peripheral files. For example, dac_common_2ch, which includes
dac_common_1ch, and adds some extra fields relating to having a second channel. Because all the fields are fully documented, the output of svd2rust is as useful as possible (including the result of building documentation against the source).I’m not sure this is the best approach to take – it was just an experiment at the time and I’m not using it today. Honestly I wonder if the best approach is actually hand-written traits and libraries (plus generous helpings of automation), perhaps using svd2rust, perhaps not. It would be so nice to have a register-level HAL that worked on all the STM32s, the equivalent of the good C header files being available from ST, both for people who prefer coding against registers directly instead of using higher level abstractions, and for building drivers etc on top of.
Number 429 comes up only when you run script against ALL SVD files for STM32. Which includes STM32W108 with a lot of unique peripherals. Also there is RCC, which is different in almost all devices but is used mainly for initialization. So the situation is not as bad as first looks.
I will try to analyze results for peripherals that are most useful for HAL layer. Maybe they can be unified even more. For example in STM32F1 family UART4 different from UART5 only by DMA support (and only in high-density devices).
Thanks @japaric, the cluster reporting is definitely an improvement… although it didn’t compile as-is for me, because the map’s keys don’t implement
Debug. So I’ve pushed to my repo a version that does compile, and changed the output so I can use tools likesort -k2orawk '$1>2'to answer more questions.I spot-checked several peripherals that had the same name but different definitions across the STM32 line, and found enough cases of fundamentally different fields that I think these results are more or less accurate.
That said, using the cluster reporting, I can see that only 274 peripherals are shared across at least two members of the STM32 product line, so that’s a little more reasonable than 429.
But, for example, the STM32F4 family has a pretty reasonable set of shared peripherals: There are only 72 distinct peripherals used by at least two devices in the F4 family. So maybe @ryankurte’s question about “another layer so it’s vendor -> family -> device” is important here?
Except there are also peripherals that cross a couple of families. For example: CAN has one version for F4 and F7, another for F2, another for F0 and F1, and two variants for F3. In the last case, one variant has a bunch of filter-bank registers that the other one doesn’t, so they are legitimately different, although one is a strict subset of the other.
I’m still not sure where this is going, but I hope the additional data helps somehow. 😄