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