Open-source project
rust-embedded/svd2rust avatar
rust-embedded/svd2rust

svd2rust writes Rust register maps from SVD files for Cortex-M chips

Generate Rust register maps (`struct`s) from SVD files

863 stars164 forksRustApache-2.0

At a glance

What is it?
svd2rust is a code generator in the rust-embedded ecosystem that reads an SVD description of a microcontroller and writes Rust structs for its registers, so firmware does not need hand-written memory maps or C headers. It is a generator rather than a runtime library, it guarantees the generated code compiles on stable Rust 1.85 and later, and it is dual licensed.
Who is it for?
Adopt svd2rust when you are writing Rust firmware for a Cortex-M microcontroller whose vendor publishes an SVD file, and you want a typed register map that does not depend on C headers or unsafe pointer arithmetic. Do not adopt it if your target is not described by an SVD file, or if you need a complete peripheral access crate with clock configuration and interrupt setup, because this project generates the register layer and leaves the rest to the surrounding stack.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 61 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The register map is generated, not written by hand

svd2rust generates Rust register maps, in the form of structs, from SVD files. The description in the repository is that short, and the topic tags say the rest: arm, cmsis-svd, code-generator, cortex-m, microcontroller.

The problem it removes is concrete. A microcontroller has a memory mapped set of peripheral registers, and every firmware project needs a way to reach them. Vendors publish those registers in two ways, an SVD file that describes addresses, sizes, bit fields and reset values in XML, and C headers that declare them as macros. The C header route forces you to write volatile pointer access or bind the headers through a foreign function interface, both of which put C conventions and unsafe code in the middle of a Rust firmware project. The SVD route lets a generator produce native Rust structs with named fields for individual bits.

The project is developed and maintained by the Tools team of the rust-embedded working group, the same team that keeps a set of embedded tooling crates under one roof, and it is one of the crates the group publishes to crates.io with published API documentation on docs.rs.

This is a build time tool rather than a runtime dependency. Your firmware depends on the generated code, and the generator itself is a development dependency or a command you run by hand.

The repository carries a CHANGELOG.md, a CODE_OF_CONDUCT.md, a Cargo.lock and a workspace with a second member, svd2rust-regress, which is the regression testing helper.

The generated code has its own Rust version guarantee

The README distinguishes two Rust versions, and confusing them is the kind of mistake that costs an afternoon.

The crate itself declares rust-version = 1.74 in Cargo.toml, and the badge in the README reads rustc 1.74+. That is the minimum version for building the generator.

The generated code has its own rule, stated in its own section. The generated code is guaranteed to compile on stable Rust 1.85.0 and up. If you hit compilation errors on any stable version newer than 1.85.0, the README asks you to open an issue.

The distinction matters in practice because the two versions move for different reasons. The generator's own MSRV is about what the tool needs to build. The generated code's rule is a promise about the output, and it is the one your firmware team depends on, since the output is checked into your repository and compiled with whatever toolchain your project pins.

The wording of that guarantee is also unusual and worth reading carefully. It is a guarantee that the output compiles on 1.85.0 and later stable versions, plus an instruction to report failures. It is not a statement about which language features the generated code uses or how long the guarantee lasts, so a project with an unusual toolchain policy should treat the README text as the contract rather than assuming forward compatibility.

The Cargo.toml also marks the crate as edition 2021, which is the edition the generator itself is written in.

Cargo features decide whether you get a binary or a library

The feature list in Cargo.toml is short and it tells you how the crate is meant to be consumed.

toml
[features]
default = ["bin", "json", "yaml"]
bin = ["dep:clap", "dep:env_logger", "serde", "dep:irx-config"]
json = ["dep:serde_json"]
yaml = ["dep:serde_yaml"]

The binary feature pulls in clap for command line parsing, env_logger for logging, serde and irx-config, which is used with its cmd, toml-parser and yaml features. The json and yaml features add the two serialisation formats, and both are on by default, so a standard install can read SVD files in either format.

The binary itself is declared as a target with the name svd2rust at src/main.rs, and it is gated behind the bin feature with required-features. That gating is what lets the crate be used as a library, or with a reduced feature set, without pulling in the command line machinery.

The package metadata reflects the intended audience. The categories are command-line-utilities, embedded, hardware-support and no-std, and the keywords are svd, embedded, register, map and generator. The no-std category is the one to note: the crate is expected to be usable in a no_std context, which matters because firmware for small microcontrollers has no standard library, and a generator that dragged in the standard library would not fit the ecosystem.

The package version in the manifest is 0.37.1, and the crate is published to crates.io under the name svd2rust.

svd-parser reads the file, quote and syn write the Rust

The dependency list shows the shape of the tool, and it is a good place to look if you want to know what the generator trusts.

Two crates do the reading. svd-parser at version 0.14.9 is used with its expand feature, and svd-rs at 0.14.12 is used with serde, so the parsed document can be serialised. The expand feature on the parser is worth noting, because it is the part that resolves the derived and repeated register definitions that vendor SVD files use to avoid repeating the same block of registers for every peripheral instance.

On the writing side, quote and proc-macro2 build the Rust token stream, syn 2.0 is pulled in with the full and extra-traits features to parse and manipulate syntax trees, inflections handles pluralisation when generating names, and anyhow and thiserror 2.0 carry the error types. regex and url round out the list.

This is a build time toolchain doing source generation, not a runtime library that ends up in your firmware binary. The generated code is what ships.

The workspace section confirms the two-member layout, with svd2rust-regress as the additional member and the root package as the default member.

The command line configuration comes from irx-config, which brings the cmd, toml-parser and yaml features, so the tool reads its configuration from files rather than from flags alone.

Test your changes against svd2rust-regress first

The README gives contributors a specific workflow, and it is the one piece of project-specific instruction the documentation contains.

svd2rust-regress is described as a helper program for regression testing changes against svd2rust, and the README says it can be used locally to check modifications of svd2rust before submitting a pull request. The details live in the helper's own README at ci/svd2rust-regress/README.md.

The logic behind it is the usual one for a code generator. If svd2rust changes the shape of the code it emits, every downstream project using that chip sees the change too, and the damage is not always visible in the generator's own unit tests. A regression helper regenerates the outputs for a set of devices and lets you look at the difference, which is where an unwanted change in the register map becomes obvious.

That is the practical test to run before opening a pull request, and it is also the reason the workspace has a second member: the helper is part of the same build, with its own dependencies and its own lockfile entry.

For the maintainers, the code of conduct applies to contributions, and the README states that the maintainer of the crate, the Tools team, promises to intervene to uphold it. The repository also carries a continuous integration workflow, which is the environment in which the regression helper runs for every change.

What svd2rust does not build for you

The scope is the register layer, and understanding the boundary keeps you from reaching for the wrong tool.

The output is register maps as structs. It is not a complete peripheral access crate. A finished firmware project usually needs more than a struct per peripheral: clock configuration, pin multiplexing, interrupt setup and vector table placement are part of the surrounding stack, and none of that comes out of an SVD file because an SVD file describes register layout rather than how a chip is brought up.

The second boundary is the input. You need an SVD file for your part, and not every vendor publishes one, and where they do, the quality varies, because an SVD file is often written by hand rather than generated from the same source as the reference manual. If your chip has no SVD file, this generator cannot help, and a hand-written or community register map is the only route.

The third boundary is the target family. The topics and the description point at ARM and Cortex-M, and there is nothing in the README about other architectures.

The alternative worth naming is the C header route with a foreign function interface generator. That approach gives you the vendor's own definitions rather than generated ones, which is good when you want to match the reference manual exactly, and it puts volatile pointer access and unsafe calls in your Rust code. svd2rust takes the opposite position: it reads a machine readable description and emits typed Rust structs, so you get field access by name and no foreign function interface at all, at the price of depending on the quality of the SVD file.

Dual licensing and the contribution rule

The licence is the simplest part of the project to get right, and it is deliberately generous.

The README states that the project is licensed under either of the Apache License, Version 2.0, with the file LICENSE-APACHE in the repository, or the MIT license, with LICENSE-MIT, at your option. The manifest agrees, with the license field set to MIT OR Apache-2.0.

The contribution clause follows the standard pattern. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work, as defined in the Apache-2.0 licence, is dual licensed as above with no additional terms or conditions. In practice that means a pull request lands under both licences unless you say otherwise in the pull request description.

For an embedded project, the dual licence is useful in a specific way. Apache-2.0 carries an explicit patent grant, which many corporate review processes look for, while MIT is the licence most permissive downstream projects already have on file. Offering both means a company that runs a licence checklist can usually pick the one it already accepts without a legal exception.

The release history is unhurried and the crate is still on 0.x. Version 0.36.1 was published on 2025-04-04, 0.37.0 on 2025-08-15 and 0.37.1 on 2025-10-19, and the last push to the default branch was on 2026-08-23. The repository is not archived. Since generated code ends up committed into your firmware repository, the version of the generator is worth pinning deliberately rather than tracking the branch.

Editorial conclusion

Adopt svd2rust when you are writing Rust firmware for a Cortex-M microcontroller whose vendor publishes an SVD file, and you want a typed register map that does not depend on C headers or unsafe pointer arithmetic. Do not adopt it if your target is not described by an SVD file, or if you need a complete peripheral access crate with clock configuration and interrupt setup, because this project generates the register layer and leaves the rest to the surrounding stack. Before you commit, check the MSRV rule in the README, since the generated code is only guaranteed on stable Rust 1.85.0 and later, and run svd2rust-regress locally as the README instructs, so you can compare your changes against the existing outputs before opening a pull request.

Frequently asked questions

What does svd2rust generate?

It generates Rust register maps in the form of structs from SVD files, the XML descriptions microcontrollers use to publish their peripheral registers. The API documentation is published on docs.rs, and the crate is developed by the Tools team of the rust-embedded working group.

Which Rust version does svd2rust need?

The crate itself declares rust-version 1.74, but the generated code is guaranteed to compile on stable Rust 1.85.0 and later. The README asks that compilation errors on any newer stable version be reported as an issue.

How do I get svd2rust?

It is published on crates.io as svd2rust, with the binary target built from src/main.rs behind the bin feature, which is enabled by default along with json and yaml. The package documentation is at docs.rs/svd2rust.

What licence applies to svd2rust?

You may use it under either the Apache License 2.0 or the MIT licence, at your option, and the repository ships LICENSE-APACHE and LICENSE-MIT. Contributions are dual licensed the same way unless the author is told otherwise.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. README
  4. Releases
  5. rust-embedded/svd2rust on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/rust-embedded-svd2rust.svg)](https://hysenlabs.com/projects/rust-embedded-svd2rust)

Was this useful?