Skip to content

Commit

Permalink
Merge branch 'main' into RPi-4B-BCM2711
Browse files Browse the repository at this point in the history
  • Loading branch information
rauhul authored Jan 3, 2025
2 parents dc4024f + 8795b3f commit 92dd75b
Show file tree
Hide file tree
Showing 22 changed files with 780 additions and 119 deletions.
10 changes: 10 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[flake8]

ignore =
# These are needed to make our license headers pass the linting
E265,
E266,

# 10% larger than the standard 80 character limit. Conforms to the black
# standard and Bugbear's B950.
max-line-length = 88
2 changes: 1 addition & 1 deletion .github/workflows/build-esp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
branches: ["main"]
schedule:
# Build on Mondays at 9am PST every week
- cron: '0 17 * * 1'
- cron: '0 17 * * 1'

jobs:
build-esp:
Expand Down
67 changes: 67 additions & 0 deletions .github/workflows/build-nuttx.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Build NuttX Examples

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
schedule:
# Build on Mondays at 9am PST every week
- cron: '0 17 * * 1'

jobs:
build-nuttx:
runs-on: ubuntu-24.04

strategy:
fail-fast: false
matrix:
example: [nuttx-riscv-blink]
swift: [swift-DEVELOPMENT-SNAPSHOT-2024-12-22-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 \
bison flex gettext texinfo libncurses5-dev libncursesw5-dev \
gperf automake libtool pkg-config build-essential gperf genromfs \
libgmp-dev libmpc-dev libmpfr-dev libisl-dev binutils-dev libelf-dev \
libexpat-dev gcc-multilib g++-multilib u-boot-tools util-linux \
kconfig-frontends ninja-build
- name: Install CMake 3.30.2
run: |
ARCH=`uname -m`
curl -sL https://github.com/Kitware/CMake/releases/download/v3.30.2/cmake-3.30.2-linux-$ARCH.tar.gz -O
tar xzf cmake-3.30.2-linux-$ARCH.tar.gz
export PATH="`pwd`/cmake-3.30.2-linux-$ARCH/bin:$PATH"
echo "PATH=$PATH" >> $GITHUB_ENV
cmake --version
- name: Install RISC-V toolchain
run: |
mkdir -p riscv-none-elf-gcc && \
curl -s -L "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v13.2.0-2/xpack-riscv-none-elf-gcc-13.2.0-2-linux-x64.tar.gz" \
| tar -C riscv-none-elf-gcc --strip-components 1 -xz
export PATH="$PATH:`pwd`/riscv-none-elf-gcc/bin/"
echo "PATH=$PATH" >> $GITHUB_ENV
riscv-none-elf-gcc --version
- 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
swiftc --version
- name: Config ${{ matrix.example }}
working-directory: ${{ matrix.example }}
run: cmake -B build -GNinja -DBOARD_CONFIG=rv-virt:leds_swift -DENABLE_NUTTX_TRACE=ON

- name: Build ${{ matrix.example }}
working-directory: ${{ matrix.example }}
run: cmake --build build
29 changes: 19 additions & 10 deletions .github/workflows/build-pico-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ on:
branches: ["main"]
schedule:
# Build on Mondays at 9am PST every week
- cron: '0 17 * * 1'
- cron: '0 17 * * 1'

jobs:
build-pico-sdk:
runs-on: ubuntu-22.04
container: swiftlang/swift:nightly-main-jammy
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
Expand All @@ -21,13 +20,14 @@ jobs:
board: pico
- name: pico-w-blink-sdk
board: pico_w
swift: [swift-DEVELOPMENT-SNAPSHOT-2024-12-04-a]

steps:
- name: Checkout repo
uses: actions/checkout@v4

- name: Install apt dependencies
run: apt-get -qq update && apt-get -qq -y install curl ninja-build python3
run: sudo apt-get -qq update && sudo apt-get -qq -y install curl ninja-build python3

- name: Install CMake 3.30.2
run: |
Expand All @@ -38,19 +38,28 @@ jobs:
echo "PATH=$PATH" >> $GITHUB_ENV
cmake --version
- name: Install GNU ARM toolchain
run: |
ARCH=`uname -m`
curl -sL https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-$ARCH-arm-none-eabi.tar.xz -O
tar xf arm-gnu-toolchain-13.3.rel1-$ARCH-arm-none-eabi.tar.xz
- 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="$PATH:`pwd`/${{ matrix.swift }}-ubuntu24.04/usr/bin/"
echo "PATH=$PATH" >> $GITHUB_ENV
which swiftc
swiftc --version
- name: Clone Pico SDK
run: |
git clone https://github.com/raspberrypi/pico-sdk.git
cd pico-sdk
git submodule update --init --recursive
cd ..
- name: Download GNU ARM toolchain
run: |
ARCH=`uname -m`
curl -sL https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-$ARCH-arm-none-eabi.tar.xz -O
tar xf arm-gnu-toolchain-13.3.rel1-$ARCH-arm-none-eabi.tar.xz
- name: Set Pico environment variables
run: |
ARCH=`uname -m`
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-zephyr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
branches: ["main"]
schedule:
# Build on Mondays at 9am PST every week
- cron: '0 17 * * 1'
- cron: '0 17 * * 1'

jobs:
build-zephyr:
Expand Down
18 changes: 11 additions & 7 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,26 @@ on:
branches: ["main"]

jobs:
lint:
name: Lint
validate_format_config:
name: Validate Format Config
runs-on: ubuntu-latest
container: swift:6.0-jammy

steps:
- name: Checkout repo
uses: actions/checkout@v4

- name: Install apt dependencies
run: apt-get -qq update && apt-get -qq -y install curl
run: sudo apt-get -qq update && sudo apt-get -qq -y install curl

- name: Compare against swift-mmio swift-format config
run: |
curl -sL https://raw.githubusercontent.com/apple/swift-mmio/refs/heads/main/.swift-format -o .swift-format-mmio
diff .swift-format .swift-format-mmio
- name: Lint
run: swift-format lint --recursive --strict .
soundness:
name: Soundness
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main
with:
api_breakage_check_enabled: false # this repo doesn't vend any API
license_header_check_enabled: false # feature: https://github.com/swiftlang/github-workflows/issues/78
license_header_check_project_name: "Swift.org" # bug: https://github.com/swiftlang/github-workflows/issues/76
unacceptable_language_check_enabled: false # unfortunately many hardware specs use terms like master/slave in their documentation
21 changes: 12 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,22 @@ Each example in this repository contains build and deployment instructions, howe

| Name | Platform | SDK | Description | Photo |
| ---- | -------- | --- | ----------- | ----- |
| [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"> |
| [stm32-uart-echo](./stm32-uart-echo) | STM32F746G-DISCO | None | Echo user input using UART. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/1186214/97d3c465-9a07-4b86-9654-0c2aaaa43b3d">|
| [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"> |
| [esp32-led-blink-sdk](./esp32-led-blink-sdk) | ESP32-C6-Bug | ESP-IDF SDK | Blink an LED repeatedly with Swift & the ESP-IDF. | <img width="300" src="esp32-led-blink-sdk/assets/images/ledon.jpg"> |
| [esp32-led-strip-sdk](./esp32-led-strip-sdk) | ESP32-C6-DevKitC-1 | ESP-IDF SDK | Control NeoPixel LEDs with Swift & the ESP-IDF. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/1186214/15f8a3e0-953e-426d-ad2d-3902baf859be"> |
| [nrfx-blink-sdk](./nrfx-blink-sdk) | nRF52840-DK | Zephyr SDK | Blink an LED repeatedly with Swift & Zephyr. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/1186214/ae3ff153-dd33-4460-8a08-4eac442bf7b0"> |
| [nuttx-riscv-blink] | QEMU | NuttX | Blink a virualized led in QEMU using the Apache NuttX RTOS | |
| [pico-blink-sdk](./pico-blink-sdk) | Raspberry Pi Pico, Pico 2 | Pico SDK | Blink an LED repeatedly with Swift & the Pico SDK. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/1186214/f2c45c18-f9a4-48b4-a941-1298ecc942cb"> |
| [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"> |
| [nrfx-blink-sdk](./nrfx-blink-sdk) | nRF52840-DK | Zephyr SDK | Blink an LED repeatedly with Swift & Zephyr. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/1186214/ae3ff153-dd33-4460-8a08-4eac442bf7b0"> |
| [esp32-led-strip-sdk](./esp32-led-strip-sdk) | ESP32-C6-DevKitC-1 | ESP-IDF SDK | Control NeoPixel LEDs with Swift & the ESP-IDF. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/1186214/15f8a3e0-953e-426d-ad2d-3902baf859be"> |
| [esp32-led-blink-sdk](./esp32-led-blink-sdk) | ESP32-C6-Bug | ESP-IDF SDK | Blink an LED repeatedly with Swift & the ESP-IDF. | <img width="300" src="esp32-led-blink-sdk/assets/images/ledon.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"> |
| [stm32-uart-echo](./stm32-uart-echo) | STM32F746G-DISCO | None | Echo user input using UART. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/1186214/97d3c465-9a07-4b86-9654-0c2aaaa43b3d">|

Note that the SDK integration examples (Pico SDK, Zephyr SDK, etc.) are not recommendations or endorsement, the same is true for build system choice (Make, CMake, SwiftPM, shell scripts). Embedded Swift aims to be versatile and to allow for integration into more existing SDKs and build systems, and the example projects are merely showing the possibilities.
Note that the SDK integration examples (Pico SDK, Zephyr SDK, etc.) are not recommendations or endorsement, the same is true for build system choice (Make, CMake, SwiftPM, shell scripts). Embedded Swift aims to be versatile and allowing integration into existing SDKs and build systems, and the example projects show some of the possibilities.

## Community Examples

Expand All @@ -59,6 +60,8 @@ Note that the SDK integration examples (Pico SDK, Zephyr SDK, etc.) are not reco
| ---- | -------- | ----------- |
| [swift-matter-examples](https://github.com/apple/swift-matter-examples) | ESP32 | An Embedded Swift Matter application running on ESP32-C6. |
| [swift-flipperzero-hello](https://github.com/Sameesunkaria/swift-flipperzero-hello) | Flipper Zero | A demonstration of running Swift apps on the Flipper Zero. |
| [EmbeddedSwift nRF52 Examples](https://github.com/nelcea/EmbeddedSwift-nRF52-Examples) | nRF52840 (Development Kit) | A collection of examples using Embedded Swift on top of nRF Connect SDK (Zephyr). |
| [Swatak](https://github.com/nelcea/EmbeddedSwift-nRF52-Swatak) | nRF52840 (Seeed Studio XIAO) | A reaction time game inspired by BATAK© boards, implemented in Embedded Swift using nRF Connect SDK. |
| [swift-picosystem-example](https://github.com/jerrodputman/swift-picosystem-example) | PicoSystem | An Embedded Swift demo running on the Pimoroni PicoSystem |
| [PlaydateKit](https://github.com/finnvoor/PlaydateKit) | Playdate | A full featured framework for building Playdate games using Embedded Swift. |
| [swift-playdate-examples](https://github.com/apple/swift-playdate-examples) | Playdate | An Embedded Swift game running on Playdate by Panic. |
Expand Down
68 changes: 45 additions & 23 deletions Tools/macho2bin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,45 @@
# See https://swift.org/LICENSE.txt for license information

#
# macho2bin -- Converts a statically-linked executable Mach-O into a flat "BIN" file suitable for flashing as a single
# contiguous blob onto some embedded devices. Note that this format assumes the embedded device can boot from a state
# where the entire firmware (all segments) are flashed contigously into one smalle address range. This is true for e.g.
# the STM32F746 devices if we place the vector table at 0x00200000, and code and data right after it, as the vector
# table also contains a pointer to the initial PC. This setup might not work for other devices.
# macho2bin -- Converts a statically-linked executable Mach-O into a flat "BIN" file
# suitable for flashing as a single contiguous blob onto some embedded devices. Note
# that this format assumes the embedded device can boot from a state where the entire
# firmware (all segments) are flashed contigously into one smalle address range. This
# is true for e.g. the STM32F746 devices if we place the vector table at 0x00200000,
# and code and data right after it, as the vector table also contains a pointer to the
# initial PC. This setup might not work for other devices.
#
# Usage:
# $ macho2bin.py <input> <output> --base-address <base-address> --segments <segment-list>
# $ macho2bin.py <input> <output> --base-address <base-address> --segments
# <segment-list>
#
# Example:
# $ macho2bin.py ./blink ./blink.bin --base-address 0x00200000 --segments '__TEXT,__DATA,__VECTORS'
# $ macho2bin.py ./blink ./blink.bin --base-address 0x00200000 --segments
# '__TEXT,__DATA,__VECTORS'
#
# Requirements and notes:
# * The output BIN file is a flat contiguous representation of the segments (--segments) based on their VM addresses.
# * The BIN file's first byte corresponds to the specified base address (--base-address).
# * The output BIN file is a flat contiguous representation of the segments
# (--segments) based on their VM addresses.
# * The BIN file's first byte corresponds to the specified base address
# (--base-address).
# * Any gaps between segments are filled with zero bytes.
# * Because of that, you want the input Mach-O to have all segments "close", and not have gaps.
# * Because of that, you want the input Mach-O to have all segments "close", and not
# have gaps.
#

import argparse
import os

from macholib import MachO
from macholib import mach_o


def main():
parser = argparse.ArgumentParser()
parser.add_argument('input')
parser.add_argument('output')
parser.add_argument('--base-address', required=True)
parser.add_argument('--segments', required=True)
parser.add_argument("input")
parser.add_argument("output")
parser.add_argument("--base-address", required=True)
parser.add_argument("--segments", required=True)
args = parser.parse_args()
args.base_address = int(args.base_address, 16)
args.segments = args.segments.split(",")
Expand All @@ -49,17 +58,23 @@ def main():
for command in mh.commands:
if isinstance(command[1], mach_o.segment_command):
(_, segment, sections) = command
segname = segment.segname.decode().strip('\0')
if segname not in args.segments: continue
segname = segment.segname.decode().strip("\0")
if segname not in args.segments:
continue

with open(args.input, "rb") as f:
f.seek(mh.offset + segment.fileoff)
data = f.read(segment.filesize)
segments.append({"vmaddr": segment.vmaddr, "data": data, "name": segname})

segments.append(
{"vmaddr": segment.vmaddr, "data": data, "name": segname}
)

segments = sorted(segments, key=lambda x: x["vmaddr"])

assert segments[0]["vmaddr"] == args.base_address, f"first segment's vmaddr 0x{segments[0]['vmaddr']:08x} does not match the passed --base-address 0x{args.base_address:08x}"
assert segments[0]["vmaddr"] == args.base_address, (
f"first segment's vmaddr 0x{segments[0]['vmaddr']:08x} does not match the"
f" passed --base-address 0x{args.base_address:08x}"
)

if os.path.exists(args.output):
os.unlink(args.output)
Expand All @@ -69,15 +84,22 @@ def main():
for segment in segments:
gap = segment["vmaddr"] - vmaddr
if gap != 0:
print(f"Writing gap of size {gap} (0x{gap:0x}) at vmaddr 0x{vmaddr:08x}")
f.write(b'\0' * gap)
print(
f"Writing gap of size {gap} (0x{gap:0x}) at vmaddr 0x{vmaddr:08x}"
)
f.write(b"\0" * gap)
assert gap >= 0
vmaddr = segment["vmaddr"]
print(f"Writing segment {segment['name']} size {len(segment['data'])} (0x{len(segment['data']):x}) at vmaddr 0x{vmaddr:08x}")
print(
f"Writing segment {segment['name']} size"
f" {len(segment['data'])} (0x{len(segment['data']):x}) at vmaddr"
f" 0x{vmaddr:08x}"
)
f.write(segment["data"])
vmaddr = segment["vmaddr"] + len(segment["data"])

print(f"Produced {args.output} with {vmaddr - args.base_address} bytes")

if __name__ == '__main__':

if __name__ == "__main__":
main()
Loading

0 comments on commit 92dd75b

Please sign in to comment.