Skip to content

Commit 5e21399

Browse files
authored
Add Raspberry Pi 4B & 5 baremetal examples (#67)
Provide new examples demonstrating how to run Swift Embedded on the Raspberry Pi 4B and 5 single-board computers, in a baremetal fashion, without an underlying operating system. The provided examples showcase how to blink the onboard green LED using Swift MMIO.
1 parent ac91524 commit 5e21399

File tree

19 files changed

+509
-0
lines changed

19 files changed

+509
-0
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Build Raspberry Pi Baremetal Examples
2+
3+
on:
4+
push:
5+
branches: ["main"]
6+
pull_request:
7+
branches: ["main"]
8+
schedule:
9+
# Build on Mondays at 9am PST every week
10+
- cron: '0 17 * * 1'
11+
jobs:
12+
build-rpi-baremetal:
13+
runs-on: ubuntu-24.04
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
example: [rpi5-blink, rpi4b-blink]
18+
swift: [swift-DEVELOPMENT-SNAPSHOT-2024-12-04-a]
19+
20+
steps:
21+
- name: Checkout repo
22+
uses: actions/checkout@v4
23+
24+
- name: Install apt dependencies
25+
run: sudo apt-get -qq update && sudo apt-get -qq -y install make llvm
26+
27+
- name: Install ${{ matrix.swift }}
28+
run: |
29+
wget -q https://download.swift.org/development/ubuntu2404/${{ matrix.swift }}/${{ matrix.swift }}-ubuntu24.04.tar.gz
30+
tar xzf ${{ matrix.swift }}-ubuntu24.04.tar.gz
31+
export PATH="`pwd`/${{ matrix.swift }}-ubuntu24.04/usr/bin/:$PATH"
32+
echo "PATH=$PATH" >> $GITHUB_ENV
33+
which swiftc
34+
swiftc --version
35+
36+
- name: Build ${{ matrix.example }}
37+
run: |
38+
cd ${{ matrix.example }}
39+
make

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ Each example in this repository contains build and deployment instructions, howe
4040
| [pico-blink](./pico-blink) | Raspberry Pi Pico | None | Blink an LED repeatedly. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/1186214/f2c45c18-f9a4-48b4-a941-1298ecc942cb"> |
4141
| [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. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/26223064/a4949a2e-1887-4325-8f5f-a681963c93d7"> |
4242
| [pico2-neopixel](./pico2-neopixel) | Raspberry Pi Pico 2 | None | Control Neopixel LEDs using the RP2350 PIO. | <img width="300" src="pico2-neopixel/assets/images/example.jpg"> |
43+
| [rpi4b-blink](./rpi4b-blink) | Raspberry Pi 4B | None | Blink the Pi's status green LED repeatedly using Swift MMIO. | <img width="300" src="rpi4b-blink/assets/rpi4.png"> |
44+
| [rpi5-blink](./rpi5-blink) | Raspberry Pi 5 | None | Blink the Pi's status green LED repeatedly with Swift MMIO. | <img width="300" src="rpi5-blink/assets/raspi5.png"> |
4345
| [stm32-blink](./stm32-blink) | STM32F746G-DISCO | None | Blink an LED repeatedly. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/1186214/739e98fd-a438-4a64-a7aa-9dddee25034b"> |
4446
| [stm32-lcd-logo](./stm32-lcd-logo) | STM32F746G-DISCO | None | Animate the Swift Logo on the built-in LCD. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/1186214/9e117d81-e808-493e-a20c-7284ea630f37"> |
4547
| [stm32-neopixel](./stm32-neopixel) | STM32F746G-DISCO | None | Control NeoPixel LEDs using SPI. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/1186214/9c5d8f74-f8aa-4632-831e-212a3e35e75a"> |

rpi4b-blink/Makefile

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
SWIFT_EXEC ?= $(shell if [ "$(shell uname)" = "Darwin" ]; then xcrun -f swift; else which swift; fi)
2+
CLANG ?= $(shell if [ "$(shell uname)" = "Darwin" ]; then xcrun -f clang; else which clang; fi)
3+
LLVM_OBJCOPY ?= $(shell if [ "$(shell uname)" = "Darwin" ]; then xcrun -f llvm-objcopy; else which llvm-objcopy; fi)
4+
5+
BUILDROOT := $(shell $(SWIFT_EXEC) build --triple aarch64-none-none-elf -Xswiftc -Xfrontend -Xswiftc -disable-stack-protector --show-bin-path)
6+
7+
.PHONY: all clean
8+
9+
all: kernel8.img
10+
11+
kernel8.img: kernel8.elf
12+
@echo "💾 Converting to binary kernel image with llvm-objcopy..."
13+
$(LLVM_OBJCOPY) -O binary kernel8.elf kernel8.img
14+
@echo ""
15+
@echo "🥳 Done! kernel8.img was saved to this directory."
16+
17+
kernel8.elf: $(BUILDROOT)/libMainApp.a $(BUILDROOT)/Support.build/boot.S.o link.ld
18+
@echo "🔗 Linking with clang..."
19+
$(CLANG) --target=aarch64-elf -o kernel8.elf $< $^ -fuse-ld=lld -nostdlib -Wl,--unresolved-symbols=ignore-in-object-files -Wl,-T ./link.ld
20+
@echo ""
21+
22+
$(BUILDROOT)/libMainApp.a $(BUILDROOT)/Support.build/boot.S.o:
23+
@echo "🛠️ Building with Swift Package Manager..."
24+
$(SWIFT_EXEC) build --triple aarch64-none-none-elf -Xswiftc -Xfrontend -Xswiftc -disable-stack-protector
25+
@echo ""
26+
27+
clean:
28+
rm -rf kernel8.elf kernel8.img .build

rpi4b-blink/Package.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// swift-tools-version: 6.1
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "RPi4B-Blink",
8+
platforms: [
9+
.macOS(.v14)
10+
],
11+
products: [
12+
.library(
13+
name: "MainApp",
14+
type: .static,
15+
targets: ["MainApp"])
16+
],
17+
dependencies: [
18+
.package(
19+
url: "https://github.com/apple/swift-mmio.git",
20+
branch: "swift-embedded-examples")
21+
],
22+
targets: [
23+
.target(
24+
name: "MainApp",
25+
dependencies: [
26+
.product(name: "MMIO", package: "swift-mmio")
27+
],
28+
swiftSettings: [
29+
.enableExperimentalFeature("Embedded"),
30+
.unsafeFlags(["-Xfrontend", "-function-sections"]),
31+
]
32+
),
33+
.target(name: "Support"),
34+
35+
]
36+
)

rpi4b-blink/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# rpi4b-blink
2+
3+
<img src="assets/rpi4.png">
4+
5+
## Requirements
6+
7+
- A Raspberry Pi 4B board
8+
- 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.
9+
- 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`.
10+
11+
## How to build and run this example:
12+
13+
- Make sure you have a recent nightly Swift toolchain that has Embedded Swift support.
14+
- Build the program, then copy the kernel image to the SD card.
15+
``` console
16+
$ cd rpi4b-blink
17+
$ export TOOLCHAINS='<toolchain-identifier>' # Your Swift nightly toolchain identifier
18+
$ make
19+
$ cp kernel8.img /Volumes/bootfs
20+
```
21+
- If your original OS is not 64-bit, make sure to set `arm_64bit=1` in `config.txt`.
22+
- Place the SD card in your Raspberry Pi 4B, and connect it to power.
23+
- After the boot sequence, the green (ACT) led will start blinking in a regular pattern.
24+
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift project authors.
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
import MMIO
13+
14+
@Register(bitWidth: 32)
15+
struct GPSET1 {
16+
@ReadWrite(bits: 10..<11, as: Bool.self)
17+
var set: SET
18+
}
19+
20+
@Register(bitWidth: 32)
21+
struct GPCLR1 {
22+
@ReadWrite(bits: 10..<11, as: Bool.self)
23+
var clear: CLEAR
24+
}
25+
26+
@Register(bitWidth: 32)
27+
struct GPFSEL4 {
28+
@ReadWrite(bits: 6..<7, as: Bool.self)
29+
var fsel42b1: FSEL42b1
30+
@ReadWrite(bits: 7..<8, as: Bool.self)
31+
var fsel42b2: FSEL42b2
32+
@ReadWrite(bits: 8..<9, as: Bool.self)
33+
var fsel42b3: FSEL42b3
34+
}
35+
36+
@RegisterBlock
37+
struct GPIO {
38+
@RegisterBlock(offset: 0x200020)
39+
var gpset1: Register<GPSET1>
40+
@RegisterBlock(offset: 0x20002c)
41+
var gpclr1: Register<GPCLR1>
42+
@RegisterBlock(offset: 0x200010)
43+
var gpfsel4: Register<GPFSEL4>
44+
}
45+
46+
let gpio = GPIO(unsafeAddress: 0xFE00_0000)
47+
48+
func setLedOutput() {
49+
gpio.gpfsel4.modify {
50+
// setFunction Select 42 (fsel42) to 001
51+
$0.fsel42b1 = true
52+
$0.fsel42b2 = false
53+
$0.fsel42b3 = false
54+
}
55+
}
56+
57+
func ledOn() {
58+
gpio.gpset1.modify {
59+
$0.set = true
60+
}
61+
}
62+
63+
func ledOff() {
64+
gpio.gpclr1.modify {
65+
$0.clear = true
66+
}
67+
}
68+
69+
@main
70+
struct Main {
71+
72+
static func main() {
73+
setLedOutput()
74+
75+
while true {
76+
ledOn()
77+
for _ in 1..<100000 {} // just a delay
78+
ledOff()
79+
for _ in 1..<100000 {} // just a delay
80+
}
81+
}
82+
}

rpi4b-blink/Sources/Support/boot.S

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift project authors.
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
.section ".text.boot"
13+
14+
.global _start
15+
16+
_start:
17+
// Check processor ID is zero (executing on main core), else hang
18+
mrs x1, mpidr_el1
19+
and x1, x1, #3
20+
cbz x1, 2f
21+
// We're not on the main core, so hang in an infinite wait loop
22+
1: wfe
23+
b 1b
24+
2: // We're on the main core!
25+
26+
// Set stack to start below our code
27+
ldr x1, =_start
28+
mov sp, x1
29+
30+
// Clean the BSS section
31+
ldr x1, =__bss_start // Start address
32+
ldr w2, =__bss_size // Size of the section
33+
3: cbz w2, 4f // Quit loop if zero
34+
str xzr, [x1], #8
35+
sub w2, w2, #1
36+
cbnz w2, 3b // Loop if non-zero
37+
38+
// Jump to Swift!
39+
4: bl main
40+
// Halt if Swift returns
41+
b 1b

rpi4b-blink/Sources/Support/include/boot.h

Whitespace-only changes.

rpi4b-blink/assets/rpi4.png

31.3 MB
Loading

rpi4b-blink/link.ld

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
SECTIONS
2+
{
3+
. = 0x80000; /* Kernel load address for AArch64 */
4+
.text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) }
5+
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) }
6+
PROVIDE(_data = .);
7+
.data : { *(.data .data.* .gnu.linkonce.d*) }
8+
.bss (NOLOAD) : {
9+
. = ALIGN(16);
10+
__bss_start = .;
11+
*(.bss .bss.*)
12+
*(COMMON)
13+
__bss_end = .;
14+
}
15+
_end = .;
16+
17+
/DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
18+
}
19+
__bss_size = (__bss_end - __bss_start)>>3;

rpi5-blink/Makefile

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
SWIFT_EXEC ?= $(shell if [ "$(shell uname)" = "Darwin" ]; then xcrun -f swift; else which swift; fi)
2+
CLANG ?= $(shell if [ "$(shell uname)" = "Darwin" ]; then xcrun -f clang; else which clang; fi)
3+
LLVM_OBJCOPY ?= $(shell if [ "$(shell uname)" = "Darwin" ]; then xcrun -f llvm-objcopy; else which llvm-objcopy; fi)
4+
5+
BUILDROOT := $(shell $(SWIFT_EXEC) build --triple aarch64-none-none-elf -Xswiftc -Xfrontend -Xswiftc -disable-stack-protector --show-bin-path)
6+
7+
.PHONY: all clean
8+
9+
all: kernel8.img
10+
11+
kernel8.img: kernel8.elf
12+
@echo "💾 Converting to binary kernel image with llvm-objcopy..."
13+
$(LLVM_OBJCOPY) -O binary kernel8.elf kernel8.img
14+
@echo ""
15+
@echo "🥳 Done! kernel8.img was saved to this directory."
16+
17+
kernel8.elf: $(BUILDROOT)/libMainApp.a $(BUILDROOT)/Support.build/boot.S.o link.ld
18+
@echo "🔗 Linking with clang..."
19+
$(CLANG) --target=aarch64-elf -o kernel8.elf $< $^ -fuse-ld=lld -nostdlib -Wl,--unresolved-symbols=ignore-in-object-files -Wl,-T ./link.ld
20+
@echo ""
21+
22+
$(BUILDROOT)/libMainApp.a $(BUILDROOT)/Support.build/boot.S.o:
23+
@echo "🛠️ Building with Swift Package Manager..."
24+
$(SWIFT_EXEC) build --triple aarch64-none-none-elf -Xswiftc -Xfrontend -Xswiftc -disable-stack-protector
25+
@echo ""
26+
27+
clean:
28+
rm -rf kernel8.elf kernel8.img .build

rpi5-blink/Package.resolved

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"originHash" : "193ca3f107e2c8dd2da5d091f6259f64b2cbfd6776d1c26bbcfb195b3a0b5045",
3+
"pins" : [
4+
{
5+
"identity" : "swift-argument-parser",
6+
"kind" : "remoteSourceControl",
7+
"location" : "https://github.com/apple/swift-argument-parser.git",
8+
"state" : {
9+
"revision" : "41982a3656a71c768319979febd796c6fd111d5c",
10+
"version" : "1.5.0"
11+
}
12+
},
13+
{
14+
"identity" : "swift-mmio",
15+
"kind" : "remoteSourceControl",
16+
"location" : "https://github.com/apple/swift-mmio.git",
17+
"state" : {
18+
"branch" : "swift-embedded-examples",
19+
"revision" : "06d96ed4916739f2edafde87f3951b2d2a04df65"
20+
}
21+
},
22+
{
23+
"identity" : "swift-syntax",
24+
"kind" : "remoteSourceControl",
25+
"location" : "https://github.com/swiftlang/swift-syntax.git",
26+
"state" : {
27+
"revision" : "0687f71944021d616d34d922343dcef086855920",
28+
"version" : "600.0.1"
29+
}
30+
}
31+
],
32+
"version" : 3
33+
}

rpi5-blink/Package.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// swift-tools-version: 6.1
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "RPi5-Blink",
8+
platforms: [
9+
.macOS(.v14)
10+
],
11+
products: [
12+
.library(
13+
name: "MainApp",
14+
type: .static,
15+
targets: ["MainApp"])
16+
],
17+
dependencies: [
18+
.package(
19+
url: "https://github.com/apple/swift-mmio.git",
20+
branch: "swift-embedded-examples")
21+
],
22+
targets: [
23+
.target(
24+
name: "MainApp",
25+
dependencies: [
26+
.product(name: "MMIO", package: "swift-mmio")
27+
],
28+
swiftSettings: [
29+
.enableExperimentalFeature("Embedded"),
30+
.unsafeFlags(["-Xfrontend", "-function-sections"]),
31+
]
32+
),
33+
.target(name: "Support"),
34+
35+
]
36+
)

0 commit comments

Comments
 (0)