Bare-Metal Swift on M5Stack NanoC6: No C, No Assembly, No ESP-IDF

Inspired by @kishikawakatsumi 's Raspberry Pi Pico examples, I wanted to try the same approach on a different architecture. This project runs pure Swift on the ESP32-C6 (RISC-V) — from the 2nd-stage bootloader to the application — with zero lines of C or assembly.

Target hardware: M5Stack NanoC6 (ESP32-C6, RISC-V, 320KB SRAM)

What's in it

  • Pure Swift 2nd-stage bootloader — disables watchdogs, reads SPI flash via direct register manipulation, configures Flash MMU,
    loads segments, and jumps to the app. No ROM function calls.
  • Pure Swift application — LED blink + USB Serial output, all through hardware registers.
  • Pure Swift build tools — elf2image, write-flash, gen-partition-table replace Python-based esptool entirely.

What made this possible

Embedded Swift + _Volatile module for safe volatile register access, and apple/swift-mmio for type-safe MMIO definitions generated from SVD files.

@c attribute (Swift 6.3) lets Swift functions satisfy C symbol expectations without libc:

@c(posix_memalign)
func posixMemalign(_ memptr: UnsafeMutablePointer<UnsafeMutableRawPointer?>,
_ alignment: Int, _ size: Int) -> Int32 {
// Bump allocator growing upward from _heap_start
}

@_extern(c) gives direct access to linker script symbols (_sbss, _ebss, _heap_start), and @main struct serves as the entry point via -e main — no startup assembly needed.

Build

SwiftPM + Make, cross-compiling to riscv32-none-none-eabi. Requires a Swift 6.3 snapshot with Embedded Swift support.

Sources/*.swift → swiftc (RISC-V) → ELF → elf2image.swift → .bin → write-flash.swift → device

11 Likes

Same comment I just made on @kishikawakatsumi's post applies here:

We would love to link to these examples from swift-embedded-examples, are you interested in adding your repo to the community section of the embedded-examples README?

1 Like

That would be great, I'd be honored! Please feel free to add it.