RP2040MMIO: A hardware access layer for the RP2040

Accompanying Ole's great work on getting a pure Swift environment up and running on the Raspberry Pi Pico I've been working on implementing a hardware access layer for the RP2040.

Here are the first results: RP2040MMIO.

The package is based on Swift MMIO. It provides access to all of the RP2040's peripherals and their registers. Overall it's about 35k lines of generated Swift source code.

I have not yet managed to compile actual RP2040 code. Currently there's still a bug that prevents macros from cross compiling correctly. But it is possible to write code against the module's API by compiling for macOS and setting up dummy register banks (see RP2040Playground).

A problem I have found is that the package needs ~11 minutes to compile. This is probably due to the amount of macros involved. Debug and release builds do not really differ in compile time.

12 Likes

This is fantastic! Having autogenerated Swift types for all peripherals is a great base for writing our own RP2040 SDK in pure Swift. I can't wait until we can bring this all together and run it on an actual RP2040.

2 Likes

Awesome work! You've kinda run ahead into another part of the project I was not yet planning on announcing/releasing, but I'd rather avoid duplicated work.

MMIO has a private branch containing svd2swift a code generation tool like you've written, which also integrates as a swift package build plugin. I'll try to make that public today/tomorrow and hopefully we can collaborate on that one implementation instead of maintaining two.

As for macro compile performance, this is also something I've been investigating a bit and had some success getting improvements made to the compiler. I filed a radar for this (not that you would have visibility into it :sweat_smile:) rdar://119047550 (Quadratic compilation times with number of macros), which led to two important bug fixes:
[Source manager] Improve the performance of findBufferContainingLocInternal by DougGregor · Pull Request #68971 · apple/swift · GitHub and [Macros] Cache `SourceLocationConverter` in `ExportedSourceFile` by ahoppen · Pull Request #70415 · apple/swift · GitHub.

I'd suggest using a nightly toolchain to make sure you have these changes, as they dramatically improve compile times for very large macro uses.

An additional issue I suspect you're hitting is the fact that swift build and xcbuild both build the macro plugins in debug mode, which it turns out is very very slow, see Build host tools in release even when leaf target is building for debug · Issue #7233 · apple/swift-package-manager · GitHub and rdar://120556252 (Building debug targets using swift macros uses debug variants of the macros)

Lastly, @tshortli (and others?) are working on additional performance improvements to the emit-module step.

I was able to bring a ~10 minute compile down to ~10s with all of these changes combined.

10 Likes

I've pushed svd2swift here WIP: Introduce svd2swift by rauhul · Pull Request #74 · apple/swift-mmio · GitHub. I have additional tests I push a bit later as I work through some complications...

4 Likes

Here's a quick comparison building @nikolai.ruhe's code with a nightly toolchain vs. Swift 5.9.2.

Environment:

  • M1 Pro Macbook Pro, 10 cores, 32 GB RAM
  • macOS 13.6.3
  • Timing swift build (debug build) and swift build -c release (release build)
  • Ran swift package clean before each build
Swift 5.9.2 Nightly 2024-01-14 Factor
Debug 379 s (6:19 min) 309 s (5:09 min) 0.82×
Release 499 s (8:19 min) 404 s (6:44 min) 0.81×

Still (too) long, but a very nice speedup!

Oh boy, that sounds almost too good to be true. Looking forward to these improvements!

1 Like

Yeah thats definitely way too long, but I think segmenting the compile time of swift-syntax out from the target build is useful to isolate the impact of macro expansion.

Note: I manually ran swiftc commands using a release variant of the MMIOMacros executable to bring the compile times way down.

I think swift build could get much much much faster with cross-package builds caching. This would dramatically improve the swift-syntax experience, see Cross package build caching · Issue #7234 · apple/swift-package-manager · GitHub. The main issue I see is resources, Max is doing an amazing job modernizing SPM but it's a big undertaking.

4 Likes

I have just pushed a branch expand-mmio-macros to the RP2040MMIO repo. In this branch all MMIO macros are pre-expanded (so the source does not contain any macros at all).

The previously 36637 lines of code are now 241402 (6,5× more). The compile time is reduced to 23 seconds (nearly 30× faster), though.

@ole Would be nice to do another precise comparison?

1 Like

@rauhul: MMIO has […] svd2swift a code generation tool

This sounds terrific! Great to hear that you're seeing MMIO going in the same direction. I'm eager to explore what you have already done (and happy to throw away my toying around).

I've not yet found time to really dig into what you have posted but I'm definitely looking forward to do that. My code has a lot of deficiencies (like emitting peripherals multiple times instead of arrays).

1 Like

Very cool!

My timings are as follows:

Swift 5.9.2 Factor (vs. above) Nightly 2024-01-14
Debug 47 s 0.12× 77 s
Release 285 s 0.57× 361 s

Environment (same as above):

  • M1 Pro Macbook Pro, 10 cores, 32 GB RAM
  • macOS 13.6.3
  • Timing swift build (debug build) and swift build -c release (release build)
  • Ran swift package clean before each build

So not quite your 23 seconds, but a very substantial speedup, especially for clean debug builds. Interestingly, the nightly compiler was much slower this time, maybe because it has more assertions enabled?

The vast majority of the build time is still spent on building SwiftSyntax because it's a dependency of MMIO.

My takeaways:

  • It's not just the SwiftSyntax build time. When you have lots of macros, macro expansion itself is a major bottleneck (as @rauhul already hinted at above).
  • If SwiftPM were somehow able to figure out that (a) SwiftSyntax is only used by MMIO to provide macros and (b) the client code doesn't use any macros, it could potentially skip building SwiftSyntax altogether. I don't know how feasible this is, though. Figuring out (a) seems possible just by looking at the dependency graph, but figuring out (b) seems impossible without scanning all source files in all targets downstream of the .macro target.
1 Like

Small update so folks dont think this work has stalled. I am waiting on some final approval to pull in a dependency of svd2mmio for testing, then I will open the PR for real and merge it. I'd love help improving the SVD coverage decoding logic if anything is missing.

For example the SVD module currently doesn't parse enumerated values which it definitely should.

I have also prototyped svd2lldb which is an lldb plugin that can be used to print registers by symbolic name (and more) directly in lldb. I would like to productionize this after svd2swift lands.

6 Likes