Skip to content

Commit

Permalink
Add Raspberry Pi 4B & 5 baremetal examples (#67)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
iCMDdev authored Jan 14, 2025
1 parent ac91524 commit 5e21399
Show file tree
Hide file tree
Showing 19 changed files with 509 additions and 0 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/build-rpi-baremetal.yml
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/1186214/f2c45c18-f9a4-48b4-a941-1298ecc942cb"> |
| [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"> |
| [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"> |
| [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"> |
| [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"> |
| [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"> |
| [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"> |
| [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"> |
Expand Down
28 changes: 28 additions & 0 deletions rpi4b-blink/Makefile
Original file line number Diff line number Diff line change
@@ -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
36 changes: 36 additions & 0 deletions rpi4b-blink/Package.swift
Original file line number Diff line number Diff line change
@@ -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"),

]
)
24 changes: 24 additions & 0 deletions rpi4b-blink/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# rpi4b-blink

<img src="assets/rpi4.png">

## 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='<toolchain-identifier>' # 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.

82 changes: 82 additions & 0 deletions rpi4b-blink/Sources/MainApp/MainApp.swift
Original file line number Diff line number Diff line change
@@ -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<GPSET1>
@RegisterBlock(offset: 0x20002c)
var gpclr1: Register<GPCLR1>
@RegisterBlock(offset: 0x200010)
var gpfsel4: Register<GPFSEL4>
}

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
}
}
}
41 changes: 41 additions & 0 deletions rpi4b-blink/Sources/Support/boot.S
Original file line number Diff line number Diff line change
@@ -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
Empty file.
Binary file added rpi4b-blink/assets/rpi4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions rpi4b-blink/link.ld
Original file line number Diff line number Diff line change
@@ -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;
28 changes: 28 additions & 0 deletions rpi5-blink/Makefile
Original file line number Diff line number Diff line change
@@ -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
33 changes: 33 additions & 0 deletions rpi5-blink/Package.resolved
Original file line number Diff line number Diff line change
@@ -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
}
36 changes: 36 additions & 0 deletions rpi5-blink/Package.swift
Original file line number Diff line number Diff line change
@@ -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"),

]
)
Loading

0 comments on commit 5e21399

Please sign in to comment.