diff --git a/.github/workflows/build-rpi-baremetal.yml b/.github/workflows/build-rpi-baremetal.yml
new file mode 100644
index 00000000..1a3a833b
--- /dev/null
+++ b/.github/workflows/build-rpi-baremetal.yml
@@ -0,0 +1,39 @@
+name: Build Raspberry Pi Baremetal Examples
+
+on:
+ push:
+ branches: ["main"]
+ pull_request:
+ branches: ["main"]
+ schedule:
+ # Build on Mondays at 9am PST every week
+ - cron: '0 17 * * 1'
+jobs:
+ build-rpi-baremetal:
+ runs-on: ubuntu-24.04
+ strategy:
+ fail-fast: false
+ matrix:
+ example: [rpi5-blink, rpi4b-blink]
+ swift: [swift-DEVELOPMENT-SNAPSHOT-2024-12-04-a]
+
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Install apt dependencies
+ run: sudo apt-get -qq update && sudo apt-get -qq -y install make llvm
+
+ - name: Install ${{ matrix.swift }}
+ run: |
+ wget -q https://download.swift.org/development/ubuntu2404/${{ matrix.swift }}/${{ matrix.swift }}-ubuntu24.04.tar.gz
+ tar xzf ${{ matrix.swift }}-ubuntu24.04.tar.gz
+ export PATH="`pwd`/${{ matrix.swift }}-ubuntu24.04/usr/bin/:$PATH"
+ echo "PATH=$PATH" >> $GITHUB_ENV
+ which swiftc
+ swiftc --version
+
+ - name: Build ${{ matrix.example }}
+ run: |
+ cd ${{ matrix.example }}
+ make
diff --git a/README.md b/README.md
index ff7051a8..86e2bbc6 100644
--- a/README.md
+++ b/README.md
@@ -40,6 +40,8 @@ Each example in this repository contains build and deployment instructions, howe
| [pico-blink](./pico-blink) | Raspberry Pi Pico | None | Blink an LED repeatedly. |
|
| [pico-w-blink-sdk](./pico-w-blink-sdk) | Raspberry Pi Pico W | Pico SDK | Blink an LED to signal 'SOS' in Morse code repeatedly with Swift & the Pico SDK. |
|
| [pico2-neopixel](./pico2-neopixel) | Raspberry Pi Pico 2 | None | Control Neopixel LEDs using the RP2350 PIO. |
|
+| [rpi4b-blink](./rpi4b-blink) | Raspberry Pi 4B | None | Blink the Pi's status green LED repeatedly using Swift MMIO. |
|
+| [rpi5-blink](./rpi5-blink) | Raspberry Pi 5 | None | Blink the Pi's status green LED repeatedly with Swift MMIO. |
|
| [stm32-blink](./stm32-blink) | STM32F746G-DISCO | None | Blink an LED repeatedly. |
|
| [stm32-lcd-logo](./stm32-lcd-logo) | STM32F746G-DISCO | None | Animate the Swift Logo on the built-in LCD. |
|
| [stm32-neopixel](./stm32-neopixel) | STM32F746G-DISCO | None | Control NeoPixel LEDs using SPI. |
|
diff --git a/rpi4b-blink/Makefile b/rpi4b-blink/Makefile
new file mode 100644
index 00000000..da37442b
--- /dev/null
+++ b/rpi4b-blink/Makefile
@@ -0,0 +1,28 @@
+SWIFT_EXEC ?= $(shell if [ "$(shell uname)" = "Darwin" ]; then xcrun -f swift; else which swift; fi)
+CLANG ?= $(shell if [ "$(shell uname)" = "Darwin" ]; then xcrun -f clang; else which clang; fi)
+LLVM_OBJCOPY ?= $(shell if [ "$(shell uname)" = "Darwin" ]; then xcrun -f llvm-objcopy; else which llvm-objcopy; fi)
+
+BUILDROOT := $(shell $(SWIFT_EXEC) build --triple aarch64-none-none-elf -Xswiftc -Xfrontend -Xswiftc -disable-stack-protector --show-bin-path)
+
+.PHONY: all clean
+
+all: kernel8.img
+
+kernel8.img: kernel8.elf
+ @echo "💾 Converting to binary kernel image with llvm-objcopy..."
+ $(LLVM_OBJCOPY) -O binary kernel8.elf kernel8.img
+ @echo ""
+ @echo "🥳 Done! kernel8.img was saved to this directory."
+
+kernel8.elf: $(BUILDROOT)/libMainApp.a $(BUILDROOT)/Support.build/boot.S.o link.ld
+ @echo "🔗 Linking with clang..."
+ $(CLANG) --target=aarch64-elf -o kernel8.elf $< $^ -fuse-ld=lld -nostdlib -Wl,--unresolved-symbols=ignore-in-object-files -Wl,-T ./link.ld
+ @echo ""
+
+$(BUILDROOT)/libMainApp.a $(BUILDROOT)/Support.build/boot.S.o:
+ @echo "🛠️ Building with Swift Package Manager..."
+ $(SWIFT_EXEC) build --triple aarch64-none-none-elf -Xswiftc -Xfrontend -Xswiftc -disable-stack-protector
+ @echo ""
+
+clean:
+ rm -rf kernel8.elf kernel8.img .build
\ No newline at end of file
diff --git a/rpi4b-blink/Package.swift b/rpi4b-blink/Package.swift
new file mode 100644
index 00000000..f004c0b8
--- /dev/null
+++ b/rpi4b-blink/Package.swift
@@ -0,0 +1,36 @@
+// swift-tools-version: 6.1
+// The swift-tools-version declares the minimum version of Swift required to build this package.
+
+import PackageDescription
+
+let package = Package(
+ name: "RPi4B-Blink",
+ platforms: [
+ .macOS(.v14)
+ ],
+ products: [
+ .library(
+ name: "MainApp",
+ type: .static,
+ targets: ["MainApp"])
+ ],
+ dependencies: [
+ .package(
+ url: "https://github.com/apple/swift-mmio.git",
+ branch: "swift-embedded-examples")
+ ],
+ targets: [
+ .target(
+ name: "MainApp",
+ dependencies: [
+ .product(name: "MMIO", package: "swift-mmio")
+ ],
+ swiftSettings: [
+ .enableExperimentalFeature("Embedded"),
+ .unsafeFlags(["-Xfrontend", "-function-sections"]),
+ ]
+ ),
+ .target(name: "Support"),
+
+ ]
+)
diff --git a/rpi4b-blink/README.md b/rpi4b-blink/README.md
new file mode 100644
index 00000000..b68de6a5
--- /dev/null
+++ b/rpi4b-blink/README.md
@@ -0,0 +1,24 @@
+# rpi4b-blink
+
+
+
+## Requirements
+
+- A Raspberry Pi 4B board
+- An SD Card, with a Raspberry Pi OS installed (this way, we don't need to create the configuration files from scratch). You may backup `kernel8.img` and `config.txt` if you need the Linux install later, since we will change these files.
+- LLVM installed (`brew install llvm`) and added to PATH. This is needed to convert the resulted ELF file to binary image format using `llvm-objcopy`.
+
+## How to build and run this example:
+
+- Make sure you have a recent nightly Swift toolchain that has Embedded Swift support.
+- Build the program, then copy the kernel image to the SD card.
+``` console
+$ cd rpi4b-blink
+$ export TOOLCHAINS='' # Your Swift nightly toolchain identifier
+$ make
+$ cp kernel8.img /Volumes/bootfs
+```
+- If your original OS is not 64-bit, make sure to set `arm_64bit=1` in `config.txt`.
+- Place the SD card in your Raspberry Pi 4B, and connect it to power.
+- After the boot sequence, the green (ACT) led will start blinking in a regular pattern.
+
diff --git a/rpi4b-blink/Sources/MainApp/MainApp.swift b/rpi4b-blink/Sources/MainApp/MainApp.swift
new file mode 100644
index 00000000..eaef412f
--- /dev/null
+++ b/rpi4b-blink/Sources/MainApp/MainApp.swift
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the Swift open source project
+//
+// Copyright (c) 2024 Apple Inc. and the Swift project authors.
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+//
+//===----------------------------------------------------------------------===//
+
+import MMIO
+
+@Register(bitWidth: 32)
+struct GPSET1 {
+ @ReadWrite(bits: 10..<11, as: Bool.self)
+ var set: SET
+}
+
+@Register(bitWidth: 32)
+struct GPCLR1 {
+ @ReadWrite(bits: 10..<11, as: Bool.self)
+ var clear: CLEAR
+}
+
+@Register(bitWidth: 32)
+struct GPFSEL4 {
+ @ReadWrite(bits: 6..<7, as: Bool.self)
+ var fsel42b1: FSEL42b1
+ @ReadWrite(bits: 7..<8, as: Bool.self)
+ var fsel42b2: FSEL42b2
+ @ReadWrite(bits: 8..<9, as: Bool.self)
+ var fsel42b3: FSEL42b3
+}
+
+@RegisterBlock
+struct GPIO {
+ @RegisterBlock(offset: 0x200020)
+ var gpset1: Register
+ @RegisterBlock(offset: 0x20002c)
+ var gpclr1: Register
+ @RegisterBlock(offset: 0x200010)
+ var gpfsel4: Register
+}
+
+let gpio = GPIO(unsafeAddress: 0xFE00_0000)
+
+func setLedOutput() {
+ gpio.gpfsel4.modify {
+ // setFunction Select 42 (fsel42) to 001
+ $0.fsel42b1 = true
+ $0.fsel42b2 = false
+ $0.fsel42b3 = false
+ }
+}
+
+func ledOn() {
+ gpio.gpset1.modify {
+ $0.set = true
+ }
+}
+
+func ledOff() {
+ gpio.gpclr1.modify {
+ $0.clear = true
+ }
+}
+
+@main
+struct Main {
+
+ static func main() {
+ setLedOutput()
+
+ while true {
+ ledOn()
+ for _ in 1..<100000 {} // just a delay
+ ledOff()
+ for _ in 1..<100000 {} // just a delay
+ }
+ }
+}
diff --git a/rpi4b-blink/Sources/Support/boot.S b/rpi4b-blink/Sources/Support/boot.S
new file mode 100644
index 00000000..0a8f8c39
--- /dev/null
+++ b/rpi4b-blink/Sources/Support/boot.S
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the Swift open source project
+//
+// Copyright (c) 2024 Apple Inc. and the Swift project authors.
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+//
+//===----------------------------------------------------------------------===//
+
+.section ".text.boot"
+
+.global _start
+
+_start:
+ // Check processor ID is zero (executing on main core), else hang
+ mrs x1, mpidr_el1
+ and x1, x1, #3
+ cbz x1, 2f
+ // We're not on the main core, so hang in an infinite wait loop
+1: wfe
+ b 1b
+2: // We're on the main core!
+
+ // Set stack to start below our code
+ ldr x1, =_start
+ mov sp, x1
+
+ // Clean the BSS section
+ ldr x1, =__bss_start // Start address
+ ldr w2, =__bss_size // Size of the section
+3: cbz w2, 4f // Quit loop if zero
+ str xzr, [x1], #8
+ sub w2, w2, #1
+ cbnz w2, 3b // Loop if non-zero
+
+ // Jump to Swift!
+4: bl main
+ // Halt if Swift returns
+ b 1b
diff --git a/rpi4b-blink/Sources/Support/include/boot.h b/rpi4b-blink/Sources/Support/include/boot.h
new file mode 100644
index 00000000..e69de29b
diff --git a/rpi4b-blink/assets/rpi4.png b/rpi4b-blink/assets/rpi4.png
new file mode 100644
index 00000000..1df6f1c3
Binary files /dev/null and b/rpi4b-blink/assets/rpi4.png differ
diff --git a/rpi4b-blink/link.ld b/rpi4b-blink/link.ld
new file mode 100644
index 00000000..dfbf0227
--- /dev/null
+++ b/rpi4b-blink/link.ld
@@ -0,0 +1,19 @@
+SECTIONS
+{
+ . = 0x80000; /* Kernel load address for AArch64 */
+ .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) }
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) }
+ PROVIDE(_data = .);
+ .data : { *(.data .data.* .gnu.linkonce.d*) }
+ .bss (NOLOAD) : {
+ . = ALIGN(16);
+ __bss_start = .;
+ *(.bss .bss.*)
+ *(COMMON)
+ __bss_end = .;
+ }
+ _end = .;
+
+ /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
+}
+__bss_size = (__bss_end - __bss_start)>>3;
diff --git a/rpi5-blink/Makefile b/rpi5-blink/Makefile
new file mode 100644
index 00000000..da37442b
--- /dev/null
+++ b/rpi5-blink/Makefile
@@ -0,0 +1,28 @@
+SWIFT_EXEC ?= $(shell if [ "$(shell uname)" = "Darwin" ]; then xcrun -f swift; else which swift; fi)
+CLANG ?= $(shell if [ "$(shell uname)" = "Darwin" ]; then xcrun -f clang; else which clang; fi)
+LLVM_OBJCOPY ?= $(shell if [ "$(shell uname)" = "Darwin" ]; then xcrun -f llvm-objcopy; else which llvm-objcopy; fi)
+
+BUILDROOT := $(shell $(SWIFT_EXEC) build --triple aarch64-none-none-elf -Xswiftc -Xfrontend -Xswiftc -disable-stack-protector --show-bin-path)
+
+.PHONY: all clean
+
+all: kernel8.img
+
+kernel8.img: kernel8.elf
+ @echo "💾 Converting to binary kernel image with llvm-objcopy..."
+ $(LLVM_OBJCOPY) -O binary kernel8.elf kernel8.img
+ @echo ""
+ @echo "🥳 Done! kernel8.img was saved to this directory."
+
+kernel8.elf: $(BUILDROOT)/libMainApp.a $(BUILDROOT)/Support.build/boot.S.o link.ld
+ @echo "🔗 Linking with clang..."
+ $(CLANG) --target=aarch64-elf -o kernel8.elf $< $^ -fuse-ld=lld -nostdlib -Wl,--unresolved-symbols=ignore-in-object-files -Wl,-T ./link.ld
+ @echo ""
+
+$(BUILDROOT)/libMainApp.a $(BUILDROOT)/Support.build/boot.S.o:
+ @echo "🛠️ Building with Swift Package Manager..."
+ $(SWIFT_EXEC) build --triple aarch64-none-none-elf -Xswiftc -Xfrontend -Xswiftc -disable-stack-protector
+ @echo ""
+
+clean:
+ rm -rf kernel8.elf kernel8.img .build
\ No newline at end of file
diff --git a/rpi5-blink/Package.resolved b/rpi5-blink/Package.resolved
new file mode 100644
index 00000000..1a6f57c0
--- /dev/null
+++ b/rpi5-blink/Package.resolved
@@ -0,0 +1,33 @@
+{
+ "originHash" : "193ca3f107e2c8dd2da5d091f6259f64b2cbfd6776d1c26bbcfb195b3a0b5045",
+ "pins" : [
+ {
+ "identity" : "swift-argument-parser",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-argument-parser.git",
+ "state" : {
+ "revision" : "41982a3656a71c768319979febd796c6fd111d5c",
+ "version" : "1.5.0"
+ }
+ },
+ {
+ "identity" : "swift-mmio",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-mmio.git",
+ "state" : {
+ "branch" : "swift-embedded-examples",
+ "revision" : "06d96ed4916739f2edafde87f3951b2d2a04df65"
+ }
+ },
+ {
+ "identity" : "swift-syntax",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/swiftlang/swift-syntax.git",
+ "state" : {
+ "revision" : "0687f71944021d616d34d922343dcef086855920",
+ "version" : "600.0.1"
+ }
+ }
+ ],
+ "version" : 3
+}
diff --git a/rpi5-blink/Package.swift b/rpi5-blink/Package.swift
new file mode 100644
index 00000000..a01f42ef
--- /dev/null
+++ b/rpi5-blink/Package.swift
@@ -0,0 +1,36 @@
+// swift-tools-version: 6.1
+// The swift-tools-version declares the minimum version of Swift required to build this package.
+
+import PackageDescription
+
+let package = Package(
+ name: "RPi5-Blink",
+ platforms: [
+ .macOS(.v14)
+ ],
+ products: [
+ .library(
+ name: "MainApp",
+ type: .static,
+ targets: ["MainApp"])
+ ],
+ dependencies: [
+ .package(
+ url: "https://github.com/apple/swift-mmio.git",
+ branch: "swift-embedded-examples")
+ ],
+ targets: [
+ .target(
+ name: "MainApp",
+ dependencies: [
+ .product(name: "MMIO", package: "swift-mmio")
+ ],
+ swiftSettings: [
+ .enableExperimentalFeature("Embedded"),
+ .unsafeFlags(["-Xfrontend", "-function-sections"]),
+ ]
+ ),
+ .target(name: "Support"),
+
+ ]
+)
diff --git a/rpi5-blink/README.md b/rpi5-blink/README.md
new file mode 100644
index 00000000..49c200b2
--- /dev/null
+++ b/rpi5-blink/README.md
@@ -0,0 +1,25 @@
+# rpi5-blink
+
+
+
+## Requirements
+
+- A Raspberry Pi 5 board
+- An SD Card, with a Raspberry Pi OS installed (this way, we don't need to create the configuration files from scratch). You may backup `kernel8.img` and `kernel_2712.img` if you need the Linux install later, since we will change these files.
+- LLVM installed (`brew install llvm`) and added to PATH. This is needed to convert the resulted ELF file to binary image format using `llvm-objcopy`.
+
+## How to build and run this example:
+
+- Make sure you have a recent nightly Swift toolchain that has Embedded Swift support.
+- Build the program, then copy the kernel image to the SD card.
+``` console
+$ cd rpi5-blink
+$ export TOOLCHAINS='' # Your Swift nightly toolchain identifier
+$ make
+$ cp kernel8.img /Volumes/bootfs # Copy kernel image to SD card
+$ rm /Volumes/bootfs/kernel_2712.img # Delete this kernel image so our kernel8.img is used
+$ # You can also rename our kernel8.img to kernel_2712.img, or set it to anything you want and specify "kernel=[your-img-name]" in config.txt.
+```
+- Place the SD card in your Raspberry Pi 5, and connect it to power.
+- After the boot sequence, the green (ACT) led will start blinking in a regular pattern.
+
diff --git a/rpi5-blink/Sources/MainApp/MainApp.swift b/rpi5-blink/Sources/MainApp/MainApp.swift
new file mode 100644
index 00000000..7bbae377
--- /dev/null
+++ b/rpi5-blink/Sources/MainApp/MainApp.swift
@@ -0,0 +1,56 @@
+import MMIO
+
+@Register(bitWidth: 32)
+struct GIOIODIR {
+ @ReadWrite(bits: 9..<10, as: Bool.self)
+ var direction: DIRECTION
+}
+
+@Register(bitWidth: 32)
+struct GIODATA {
+ @ReadWrite(bits: 9..<10, as: Bool.self)
+ var value: VALUE
+}
+
+@RegisterBlock
+struct GPIO {
+ @RegisterBlock(offset: 0x00008)
+ var gioiodir: Register
+ @RegisterBlock(offset: 0x00004)
+ var giodata: Register
+}
+
+let gpio = GPIO(unsafeAddress: 0x10_7d51_7c00)
+
+func setLedOutput() {
+ gpio.gioiodir.modify {
+ $0.direction = false // 0 is output, 1 is input
+ }
+}
+
+func ledOn() {
+ gpio.giodata.modify {
+ $0.value = true // pin on
+ }
+}
+
+func ledOff() {
+ gpio.giodata.modify {
+ $0.value = false // pin off
+ }
+}
+
+@main
+struct Main {
+
+ static func main() {
+ setLedOutput()
+
+ while true {
+ ledOn()
+ for _ in 1..<100000 {} // just a delay
+ ledOff()
+ for _ in 1..<100000 {} // just a delay
+ }
+ }
+}
diff --git a/rpi5-blink/Sources/Support/boot.S b/rpi5-blink/Sources/Support/boot.S
new file mode 100644
index 00000000..0a8f8c39
--- /dev/null
+++ b/rpi5-blink/Sources/Support/boot.S
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the Swift open source project
+//
+// Copyright (c) 2024 Apple Inc. and the Swift project authors.
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+//
+//===----------------------------------------------------------------------===//
+
+.section ".text.boot"
+
+.global _start
+
+_start:
+ // Check processor ID is zero (executing on main core), else hang
+ mrs x1, mpidr_el1
+ and x1, x1, #3
+ cbz x1, 2f
+ // We're not on the main core, so hang in an infinite wait loop
+1: wfe
+ b 1b
+2: // We're on the main core!
+
+ // Set stack to start below our code
+ ldr x1, =_start
+ mov sp, x1
+
+ // Clean the BSS section
+ ldr x1, =__bss_start // Start address
+ ldr w2, =__bss_size // Size of the section
+3: cbz w2, 4f // Quit loop if zero
+ str xzr, [x1], #8
+ sub w2, w2, #1
+ cbnz w2, 3b // Loop if non-zero
+
+ // Jump to Swift!
+4: bl main
+ // Halt if Swift returns
+ b 1b
diff --git a/rpi5-blink/Sources/Support/include/boot.h b/rpi5-blink/Sources/Support/include/boot.h
new file mode 100644
index 00000000..e69de29b
diff --git a/rpi5-blink/assets/raspi5.png b/rpi5-blink/assets/raspi5.png
new file mode 100644
index 00000000..3fb4451f
Binary files /dev/null and b/rpi5-blink/assets/raspi5.png differ
diff --git a/rpi5-blink/link.ld b/rpi5-blink/link.ld
new file mode 100644
index 00000000..dfbf0227
--- /dev/null
+++ b/rpi5-blink/link.ld
@@ -0,0 +1,19 @@
+SECTIONS
+{
+ . = 0x80000; /* Kernel load address for AArch64 */
+ .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) }
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) }
+ PROVIDE(_data = .);
+ .data : { *(.data .data.* .gnu.linkonce.d*) }
+ .bss (NOLOAD) : {
+ . = ALIGN(16);
+ __bss_start = .;
+ *(.bss .bss.*)
+ *(COMMON)
+ __bss_end = .;
+ }
+ _end = .;
+
+ /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
+}
+__bss_size = (__bss_end - __bss_start)>>3;