Skip to content

Commit

Permalink
Automate the build system with a build.rs
Browse files Browse the repository at this point in the history
This replaces the AoT processing we did to generate the Rust modules
locally. It generates them on the fly at build-time, and only for the
selected MCU. The process is mostly the same, just automated. Some
things became unnecessary though, such as the `modrs.patch`. `form` is
no longer run, in order to minimize the number of files and directories,
but rustfmt is, so that the user can read the source from generated
documentation pages. The patches were updated to not have the `_svd`
key, since that's now handled by the build script. Those that ended up
empty were removed.
  • Loading branch information
LuigiPiucco committed May 14, 2024
1 parent f45fbf1 commit 372a7cf
Show file tree
Hide file tree
Showing 50 changed files with 372 additions and 264 deletions.
7 changes: 0 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
/target/
/macros/target/
**/*.rs.bk
Cargo.lock

svd/
.deps/
src/devices/*/*
src/generic.rs
__pycache__/
17 changes: 15 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ keywords = ["avr", "arduino"]
categories = ["no-std", "embedded", "hardware-support"]

include = [
"/src/**/*.rs",
"/LICENSE-*",
"/README.md",
"/build.rs",
"/patch/**/*.yaml",
"/src/**/*.rs",
"/vendor/*.atdf",
"/vendor/LICENSE",
]

[package.metadata.docs.rs]
Expand Down Expand Up @@ -64,8 +67,9 @@ attiny88 = ["device-selected"]
attiny1614 = ["device-selected"]
avr64du32 = ["device-selected"]
avr64du28 = ["device-selected"]
rt = ["avr-device-macros"]
rt = ["avr-device-macros", "critical-section"]

critical-section = ["critical-section-impl"]
critical-section-impl = ["critical-section/restore-state-u8"]

# Unfortunately, we can only build documentation for a single MCU.
Expand All @@ -83,10 +87,19 @@ path = "macros/"
version = "=0.5.4"
optional = true

[build-dependencies]
svd2rust = "0.33.2"
svdtools = "0.3.14"
atdf2svd = "0.4.0"

[dev-dependencies]
nb = "0.1.2"
embedded-hal = "0.2.3"

[lib]
test = false
bench = false

[[example]]
name = "atmega328p"
path = "examples/atmega328p/src/main.rs"
Expand Down
73 changes: 0 additions & 73 deletions Makefile

This file was deleted.

122 changes: 70 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ Add the following to `Cargo.toml`:
```toml
[dependencies.avr-device]
version = "0.5.4"
features = ["atmega32u4"]
features = ["atmega32u4", "rt"]
```

Via the feature you can select which chip you want the register specifications for. The following list is what is currently supported:
Via the features you can select which chip you want the register specifications
for. The following list is what is currently supported:

| ATmega | ATmega USB | ATmega 0,1 Series | AT90 | ATtiny |
| :-------------: | :----------: | :---------------: | :-----------: | :-----------: |
Expand All @@ -32,63 +33,80 @@ Via the feature you can select which chip you want the register specifications f
| `atmega2560` | | | | `attiny2313` |
| `atmega164pa` | | | | `attiny2313a` |

## Build Instructions
The version on `crates.io` is pre-built. The following is only necessary when trying to build this crate from source.

You need to have [atdf2svd][] (= 0.4.0), [svd2rust][] (= 0.28), [form][] (>= 0.8), [rustfmt][](for the *nightly* toolchain) and [svdtools][] (>= 0.1.9) installed:
```bash
cargo install atdf2svd --version 0.4.0
cargo install svd2rust --version 0.28.0
cargo install form
rustup component add --toolchain nightly rustfmt
pip3 install --user svdtools

# check svdtools
svd --version
# if a "command not found" error is printed instead of a version,
# either svdtools is installed incorrectly, or its installation path is missing from the PATH variable.
# Temporary solution to PATH variable issue is to manually add the path. Like so:
export PATH=$PATH:~/.local/bin
```

[atdf2svd]: https://github.com/Rahix/atdf2svd
[svd2rust]: https://github.com/rust-embedded/svd2rust
[form]: https://github.com/djmcgill/form
[rustfmt]: https://github.com/rust-lang/rustfmt
[svdtools]: https://github.com/rust-embedded/svdtools

Next, clone this repo and build the device definitions:
```bash
git clone https://github.com/Rahix/avr-device
cd avr-device
make
# You can build for just one specific chip using
# make atmega32u4
# I suggest building documentation as well
cargo +nightly doc --features <chip> --open
```
The `rt` feature, while optional, provides some minimal
startup/interrupt-handling code most projects will benefit from. It also enables
the `critical-section` feature, without which all peripheral access is
considered `unsafe` and the API becomes more limited.

## Internals
*avr-device* is generated using [`atdf2svd`](https://github.com/Rahix/atdf2svd) and [`svd2rust`](https://github.com/rust-embedded/svd2rust). The vendor-provided *atdf* files can be found in `vendor/`. The intermediate svd files are patched by `svdpatch.py` (Adapted from [`svdpatch.py`](https://github.com/stm32-rs/stm32-rs/blob/master/scripts/svdpatch.py) in [stm32-rs](https://github.com/stm32-rs/stm32-rs)) with device-dependent patches in `patch/`, mainly to improve undescriptive names and missing descriptions.
## Build Instructions
The PACs (Peripheral Access Crates, or really modules, in our case) **are not**
checked into git. Rather, we generate them at build time, via an automated
process implemented in [`build.rs`](./build.rs). It takes the ATDF files
Microchip (former Atmel) provides plus some patches of our own making as inputs,
and outputs a module generated from those device descriptions. These inputs
**are** checked-in. The process is similar to what the `*bindgen` crates
provide, just has more steps. So, in short, building should be a matter of
selecting the features and running cargo.

### Adding a new Chip
To add a new chip, download the *atdf* from <http://packs.download.atmel.com/> (or [avr-mcu/packs/](https://github.com/avr-rust/avr-mcu/tree/master/packs)) and place it in `vendor/`. Be sure to name it like the Rust module that should be generated. Next, you need to integrate it into the base crate and build system. Follow what was done in commit [290613454fbd ("Add basic support for ATmega64")](https://github.com/Rahix/avr-device/commit/290613454fbdc5e4ac98e53deccaf74dafc88963). Please adhere to the alphabetical sorting that is present so far.

Next, you **must** create a `<chipname>.yaml` in `patch/` which has at least the following content:
```yaml
_svd: ../svd/<chipname>.svd
```
If more patches need to be applied (most likely!), they should be added into this file as well. The patching format is documented in the [`svdtools` README](https://github.com/stm32-rs/svdtools#device-and-peripheral-yaml-format). Ideally, try to reuse the exisiting patches in `patch/common/` or `patch/timer/`.

Finally, try building the crate for your MCU with `make <chipname>`.
To add a new chip:

1. Download the ATDF from <http://packs.download.atmel.com/> and place it in
`vendor/`. Be sure to name it like the Rust module that should be generated.
2. Add a feature of the same name to `Cargo.toml` (it should enable
`device-selected`);
3. Add any needed patches to a yaml file with the same name under the `patch`
directory, ideally by including some of the snippets present in
`patch/common` and `patch/timer`; The format is decribed
[here](https://github.com/rust-embedded/svdtools#device-and-peripheral-yaml-format),
but it should not include the top-level `_svd` key, as that's handled by the
build system; If patching is unneeded (most likely it is!), the file can be
ommited.
4. Include the module into the tree, in [`devices.rs`](./src/devices.rs),
following the format used by other modules in that file;
5. Finally, try building the crate for your MCU with
`cargo build --features <mcu>,rt`.
6. Also check the built documentation for inconsistencies, via
`cargo doc --features <mcu>,rt --open` (it will pop up in your browser).

## Internals
Since the vendor does not provide SVDs we can pass to [`svd2rust`][], we
generate one via [`atdf2svd`][]. The sequence is as follows:

1. Check which MCUs are known to the crate
([build.rs:get_available_mcus](./build.rs#L21-L40));
2. Select which to build for by checking enabled features
([build.rs:select_mcu](./build.rs#L42-L62));
3. Generate the Rust module ([build.rs:build_mcu_module](./build.rs#L64-L148));

Substeps are:
1. Register inputs with cargo;
2. Get a temporary directory;
3. Apply `atdf2svd`;
4. If a yaml patch exists, use it via [`svdtools`][] and read the new content
/ else, read the content of the unpatched file to continue;
5. Get the output directory;
6. Apply `svd2rust`;
7. Run [`rustfmt`][] on the module to make it readable in [`docs.rs`][];
4. It will be included from `$OUT_DIR/pac/<mcu>.rs` into the path
`avr_device::devices::<mcu>` (private), and re-exported as
`avr_device::<mcu>` (public).

[`atdf2svd`]: https://github.com/Rahix/atdf2svd
[`svd2rust`]: https://github.com/rust-embedded/svd2rust
[`svdtools`]: https://github.com/rust-embedded/svdtools
[`rustfmt`]: https://github.com/rust-lang/rustfmt
[`docs.rs`]: https://docs.rs/avr-device/latest/avr_device

## License
*avr-device* is licensed under either of

* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
http://opensource.org/licenses/MIT)

at your option.

The vendored *atdf* files are licensed under the Apache License, Version 2.0 ([LICENSE-VENDOR](vendor/LICENSE)).
The vendored *atdf* files are licensed under the Apache License, Version 2.0
([LICENSE-VENDOR](vendor/LICENSE)).
Loading

0 comments on commit 372a7cf

Please sign in to comment.