├── .editorconfig ├── .flake8 ├── .github ├── actions │ └── install-swift │ │ └── action.yml └── workflows │ ├── build-esp.yml │ ├── build-nuttx.yml │ ├── build-rpi-baremetal.yml │ ├── build-rpi-pico-sdk.yml │ ├── build-stm.yml │ ├── build-zephyr.yml │ ├── lint.yml │ └── update-swift-version.yml ├── .gitignore ├── .spi.yml ├── .swift-format ├── .swift-version ├── .swiftformatignore ├── CODEOWNERS ├── Documentation └── README.md ├── LICENSE.txt ├── Package.resolved ├── Package.swift ├── README.md ├── Sources └── EmbeddedSwift │ ├── Documentation.docc │ ├── BuildSystemSupport │ │ ├── IntegrateWithBazel.md-wip │ │ ├── IntegrateWithCMake.md-wip │ │ ├── IntegrateWithMake.md-wip │ │ ├── IntegrateWithSwiftPM.md-wip │ │ └── IntegrateWithXcode.md-wip │ ├── CompilerDetails │ │ ├── ABI.md │ │ └── Status.md │ ├── Documentation.md │ ├── GettingStarted │ │ ├── InstallEmbeddedSwift.md │ │ ├── Introduction.md │ │ ├── LanguageSubset.md │ │ └── WaysToGetStarted.md │ ├── GuidedExamples │ │ ├── PicoGuide.md │ │ ├── STM32BaremetalGuide.md │ │ └── macOSGuide.md │ ├── SDKSupport │ │ ├── Baremetal.md │ │ ├── IntegrateWithESP.md-wip │ │ ├── IntegrateWithPico.md │ │ ├── IntegrateWithZephyr.md │ │ └── IntegratingWithPlatforms.md │ └── UsingEmbeddedSwift │ │ ├── Basics.md │ │ ├── ConditionalCompilation.md │ │ ├── Existentials.md │ │ ├── ExternalDependencies.md │ │ ├── Libraries.md │ │ ├── NonFinalGenericMethods.md │ │ └── Strings.md │ └── Empty.swift ├── Tools ├── SVDs │ ├── README.md │ ├── rp2040.patched.svd │ ├── rp235x.patched.svd │ └── stm32f7x6.patched.svd ├── Toolsets │ ├── pico.json │ ├── pico2.json │ ├── rpi-5-elf.json │ ├── stm32f74x-lcd.json │ └── stm32f74x.json ├── elf2hex.py ├── macho2bin.py └── macho2uf2.py ├── esp32-led-blink-sdk ├── CMakeLists.txt ├── README.md ├── assets │ └── images │ │ ├── ledoff.jpg │ │ └── ledon.jpg ├── diagram.json ├── main │ ├── BridgingHeader.h │ ├── CMakeLists.txt │ ├── Led.swift │ ├── Main.swift │ └── idf_component.yml └── wokwi.toml ├── esp32-led-strip-sdk ├── CMakeLists.txt ├── README.md ├── diagram.json ├── main │ ├── BridgingHeader.h │ ├── CMakeLists.txt │ ├── LedStrip.swift │ ├── Main.swift │ └── idf_component.yml └── wokwi.toml ├── harmony ├── .vscode │ ├── settings.json │ └── tasks.json ├── 3DModels │ └── Assembly.step ├── ACKNOWLEDGEMENTS.md ├── BillOfMaterials.md ├── CMakeLists.txt ├── README.md ├── Sources │ ├── Application │ │ ├── BridgingHeader.h │ │ ├── Button.swift │ │ ├── ButtonTimes.swift │ │ ├── LEDStrip.swift │ │ ├── Logging.swift │ │ ├── Main.swift │ │ ├── QuadratureEncoder.swift │ │ └── Stubs.swift │ ├── Audio │ │ ├── AudioAnalyzer.swift │ │ ├── AudioBuffer.swift │ │ ├── AudioBufferTransport.swift │ │ ├── AudioEngine.swift │ │ ├── AudioI2S.swift │ │ ├── AudioPico.swift │ │ ├── MAX9744.swift │ │ ├── Resampler.swift │ │ ├── Ring.swift │ │ ├── RingBuffer.swift │ │ ├── SpinLock.swift │ │ ├── TPA2016D2.swift │ │ └── Timer.swift │ ├── Bluetooth │ │ ├── A2DP.swift │ │ ├── AVRCP.swift │ │ ├── HCI.swift │ │ ├── SBC.swift │ │ └── SDP.swift │ └── PIOPrograms │ │ ├── I2S.pio │ │ ├── QuadratureEncoder.pio │ │ └── WS2812.pio ├── Tests │ └── AudioTests │ │ └── RingBufferTests.swift ├── assets │ └── hero.jpg └── include │ ├── btstack_config.h │ └── lwipopts.h ├── nrfx-blink-sdk ├── BridgingHeader.h ├── CMakeLists.txt ├── Main.swift ├── README.md ├── Stubs.c ├── prj.conf └── west.yml ├── nuttx-riscv-blink ├── CMakeLists.txt ├── README.md ├── defconfig └── leds_swift │ ├── BridgingHeader.h │ ├── Kconfig │ ├── Make.defs │ ├── Makefile │ └── leds_swift.swift ├── rpi-4b-blink ├── Makefile ├── Package.resolved ├── Package.swift ├── README.md ├── Sources │ ├── Application │ │ └── Application.swift │ └── Support │ │ ├── boot.S │ │ ├── include │ │ └── Support.h │ │ └── linkerscript.ld └── assets │ └── hero.jpg ├── rpi-5-blink ├── Makefile ├── Package.resolved ├── Package.swift ├── README.md ├── Sources │ ├── Application │ │ └── Application.swift │ └── Support │ │ ├── boot.S │ │ ├── include │ │ └── Support.h │ │ └── linkerscript.ld └── assets │ └── hero.jpg ├── rpi-pico-blink-sdk ├── BridgingHeader.h ├── CMakeLists.txt ├── Main.swift └── README.md ├── rpi-pico-blink ├── Makefile ├── Package.swift ├── README.md └── Sources │ ├── Application │ └── Application.swift │ ├── RP2040 │ ├── HAL │ │ ├── Digital.swift │ │ ├── Pins.swift │ │ ├── RP2040.swift │ │ └── Time.swift │ └── Hardware │ │ ├── Clocks.swift │ │ ├── IOBank.swift │ │ ├── PLL.swift │ │ ├── PPB.swift │ │ ├── PadsBank.swift │ │ ├── RP2040Hardware.swift │ │ ├── Resets.swift │ │ ├── SIO.swift │ │ ├── Timer.swift │ │ ├── Watchdog.swift │ │ └── XOSC.swift │ └── Support │ ├── Support.c │ ├── crt0.S │ └── include │ └── Support.h ├── rpi-pico2-neopixel ├── .sourcekit-lsp │ └── config.json ├── Makefile ├── Package.resolved ├── Package.swift ├── README.md ├── Sources │ ├── Application │ │ ├── Application.swift │ │ ├── HSV8Pixel.swift │ │ ├── RGB8Pixel.swift │ │ └── WS2812.swift │ ├── RP2350 │ │ ├── Empty.swift │ │ ├── rp235x.patched.svd │ │ └── svd2swift.json │ └── Support │ │ ├── Support.c │ │ ├── crt0.S │ │ └── include │ │ └── Support.h └── assets │ └── images │ └── example.jpg ├── rpi-picow-blink-sdk ├── BridgingHeader.h ├── CMakeLists.txt ├── Main.swift ├── README.md └── include │ └── lwipopts.h ├── stm32-blink ├── Board.swift ├── BridgingHeader.h ├── Main.swift ├── README.md ├── Registers.swift ├── Startup.c ├── build-elf.sh ├── build-macho.sh └── elf-linkerscript.ld ├── stm32-lcd-logo ├── Makefile ├── Package.resolved ├── Package.swift ├── README.md └── Sources │ ├── Application │ ├── Application.swift │ ├── Geometry │ │ ├── Color.swift │ │ ├── Point.swift │ │ └── Size.swift │ └── HAL │ │ ├── GPIOA+Helpers.swift │ │ ├── LTDC+Helpers.swift │ │ └── RCC+Helpers.swift │ ├── STM32F7X6 │ ├── Device.swift │ ├── Empty.swift │ ├── FLASH.swift │ ├── GPIOA.swift │ ├── GPIOB.swift │ ├── GPIOC.swift │ ├── GPIOD.swift │ ├── GPIOE.swift │ ├── GPIOF.swift │ ├── GPIOG.swift │ ├── GPIOH.swift │ ├── GPIOI.swift │ ├── GPIOJ.swift │ ├── GPIOK.swift │ ├── LTDC.swift │ ├── RCC.swift │ ├── stm32f7x6.patched.svd │ └── svd2swift.json │ └── Support │ ├── Startup.c │ └── include │ └── Support.h ├── stm32-lvgl ├── .gitignore ├── .sourcekit-lsp │ └── config.json ├── Makefile ├── Package.resolved ├── Package.swift ├── README.md ├── Sources │ ├── Application │ │ ├── Clocks.swift │ │ ├── Debug.swift │ │ ├── I2C.swift │ │ ├── Interrupts.swift │ │ ├── Lcd.swift │ │ ├── Main.swift │ │ ├── Pins.swift │ │ ├── Sdram.swift │ │ ├── TouchPanel.swift │ │ └── UIAppLogic.swift │ ├── CLVGL │ │ └── include │ │ │ ├── CLVGL.h │ │ │ ├── lv_conf.h │ │ │ └── module.modulemap │ ├── HostSDLApp │ │ ├── Main.swift │ │ └── UIAppLogic.swift │ ├── Registers │ │ ├── DBGMCU.swift │ │ ├── Device.swift │ │ ├── FLASH.swift │ │ ├── FMC.swift │ │ ├── GPIOA.swift │ │ ├── GPIOB.swift │ │ ├── GPIOC.swift │ │ ├── GPIOD.swift │ │ ├── GPIOE.swift │ │ ├── GPIOF.swift │ │ ├── GPIOG.swift │ │ ├── GPIOH.swift │ │ ├── GPIOI.swift │ │ ├── GPIOJ.swift │ │ ├── GPIOK.swift │ │ ├── I2C1.swift │ │ ├── I2C2.swift │ │ ├── I2C3.swift │ │ ├── I2C4.swift │ │ ├── LTDC.swift │ │ ├── NVIC.swift │ │ ├── PWR.swift │ │ ├── RCC.swift │ │ ├── SCB.swift │ │ ├── STK.swift │ │ ├── SYSCFG.swift │ │ └── USART1.swift │ └── Support │ │ ├── Startup.c │ │ ├── Stubs.c │ │ ├── include │ │ └── Support.h │ │ └── linkerscript.ld ├── clang-arm-toolchain.cmake ├── fetch-dependencies.sh └── toolset.json ├── stm32-neopixel ├── Makefile ├── Package.resolved ├── Package.swift ├── README.md ├── Sources │ ├── Application │ │ ├── Application.swift │ │ ├── GPIO.swift │ │ └── Neopixel │ │ │ ├── HSV8Pixel.swift │ │ │ ├── RGB8Pixel.swift │ │ │ ├── SPINeoPixel.swift │ │ │ ├── SPINeoPixelBit.swift │ │ │ └── SPINeoPixelGRB64Pixel.swift │ ├── STM32F7X6 │ │ ├── DMA1.swift │ │ ├── DMA2.swift │ │ ├── Device.swift │ │ ├── Empty.swift │ │ ├── GPIOA.swift │ │ ├── GPIOB.swift │ │ ├── GPIOI.swift │ │ ├── RCC.swift │ │ ├── SPI1.swift │ │ ├── SPI2.swift │ │ ├── USART1.swift │ │ ├── stm32f7x6.patched.svd │ │ └── svd2swift.json │ └── Support │ │ ├── Support.c │ │ ├── include │ │ └── Support.h │ │ └── startup.S └── schematic.png └── stm32-uart-echo ├── Makefile ├── Package.resolved ├── Package.swift ├── README.md └── Sources ├── Application ├── Application.swift └── GPIO.swift ├── STM32F7X6 ├── Device.swift ├── Empty.swift ├── GPIOA.swift ├── GPIOB.swift ├── RCC.swift ├── USART1.swift ├── stm32f7x6.patched.svd └── svd2swift.json └── Support ├── Support.c ├── include └── Support.h └── startup.S /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | 3 | ignore = 4 | # These are needed to make our license headers pass the linting 5 | E265, 6 | E266, 7 | 8 | # 10% larger than the standard 80 character limit. Conforms to the black 9 | # standard and Bugbear's B950. 10 | max-line-length = 88 11 | -------------------------------------------------------------------------------- /.github/workflows/build-esp.yml: -------------------------------------------------------------------------------- 1 | name: ESP 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | types: [opened, reopened, synchronize, ready_for_review] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | name: Build 13 | runs-on: ubuntu-24.04 14 | container: espressif/idf:latest 15 | 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | example: [esp32-led-blink-sdk, esp32-led-strip-sdk] 20 | 21 | steps: 22 | - name: Checkout repo 23 | uses: actions/checkout@v4 24 | 25 | - name: Install apt dependencies 26 | run: apt-get -qq update && apt-get -qq -y install pkg-config 27 | 28 | - name: Install Swift 29 | uses: ./.github/actions/install-swift 30 | 31 | - name: Build ${{ matrix.example }} 32 | run: | 33 | cd $IDF_PATH 34 | . ./export.sh 35 | cd - 36 | cd ${{ matrix.example }} 37 | idf.py set-target esp32c6 38 | idf.py build 39 | -------------------------------------------------------------------------------- /.github/workflows/build-nuttx.yml: -------------------------------------------------------------------------------- 1 | name: NuttX 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | types: [opened, reopened, synchronize, ready_for_review] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | name: Build 13 | runs-on: ubuntu-24.04 14 | 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | example: [nuttx-riscv-blink] 19 | 20 | steps: 21 | - name: Checkout repo 22 | uses: actions/checkout@v4 23 | 24 | - name: Install apt dependencies 25 | run: | 26 | sudo apt-get -qq update && sudo apt-get -qq -y install \ 27 | bison flex gettext texinfo libncurses5-dev libncursesw5-dev \ 28 | gperf automake libtool pkg-config build-essential gperf genromfs \ 29 | libgmp-dev libmpc-dev libmpfr-dev libisl-dev binutils-dev libelf-dev \ 30 | libexpat-dev gcc-multilib g++-multilib u-boot-tools util-linux \ 31 | kconfig-frontends ninja-build 32 | 33 | - name: Install CMake 3.30.2 34 | run: | 35 | ARCH=`uname -m` 36 | curl -sL https://github.com/Kitware/CMake/releases/download/v3.30.2/cmake-3.30.2-linux-$ARCH.tar.gz -O 37 | tar xzf cmake-3.30.2-linux-$ARCH.tar.gz 38 | export PATH="`pwd`/cmake-3.30.2-linux-$ARCH/bin:$PATH" 39 | echo "PATH=$PATH" >> $GITHUB_ENV 40 | cmake --version 41 | 42 | - name: Install RISC-V toolchain 43 | run: | 44 | mkdir -p riscv-none-elf-gcc && \ 45 | 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" \ 46 | | tar -C riscv-none-elf-gcc --strip-components 1 -xz 47 | export PATH="$PATH:`pwd`/riscv-none-elf-gcc/bin/" 48 | echo "PATH=$PATH" >> $GITHUB_ENV 49 | riscv-none-elf-gcc --version 50 | 51 | - name: Install Swift 52 | uses: ./.github/actions/install-swift 53 | 54 | - name: Build ${{ matrix.example }} 55 | working-directory: ${{ matrix.example }} 56 | run: | 57 | cmake -B build -GNinja -DBOARD_CONFIG=rv-virt:leds_swift -DENABLE_NUTTX_TRACE=ON 58 | cmake --build build 59 | -------------------------------------------------------------------------------- /.github/workflows/build-rpi-baremetal.yml: -------------------------------------------------------------------------------- 1 | name: Raspberry Pi Baremetal 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | types: [opened, reopened, synchronize, ready_for_review] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | name: Build 13 | runs-on: ubuntu-24.04 14 | 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | example: [rpi-4b-blink, rpi-5-blink] 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 Swift 28 | uses: ./.github/actions/install-swift 29 | 30 | - name: Build ${{ matrix.example }} 31 | working-directory: ${{ matrix.example }} 32 | run: make 33 | -------------------------------------------------------------------------------- /.github/workflows/build-stm.yml: -------------------------------------------------------------------------------- 1 | name: STM32 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | types: [opened, reopened, synchronize, ready_for_review] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | name: Build 13 | runs-on: ubuntu-24.04 14 | 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | example: [stm32-blink, stm32-lvgl] 19 | 20 | steps: 21 | - name: Checkout repo 22 | uses: actions/checkout@v4 23 | 24 | - name: Fixup for running locally in act 25 | if: ${{ env.ACT }} 26 | run: echo /opt/acttoolcache/node/18.20.8/x64/bin >> $GITHUB_PATH 27 | 28 | - name: Set up Python 29 | uses: actions/setup-python@v5 30 | with: 31 | python-version: 3.11 32 | 33 | - name: Install uv 34 | uses: astral-sh/setup-uv@v5 35 | 36 | - name: Install Swift 37 | uses: ./.github/actions/install-swift 38 | 39 | - name: Set environment variables 40 | run: | 41 | echo "STM_BOARD=STM32F746G_DISCOVERY" >> $GITHUB_ENV 42 | 43 | - name: Build ${{ matrix.example }} 44 | working-directory: ${{ matrix.example }} 45 | run: | 46 | if [[ -f ./fetch-dependencies.sh ]]; then 47 | ./fetch-dependencies.sh 48 | fi 49 | 50 | if [[ -f ./build-elf.sh ]]; then 51 | ./build-elf.sh 52 | fi 53 | 54 | if [[ -f Makefile ]]; then 55 | make 56 | fi 57 | -------------------------------------------------------------------------------- /.github/workflows/build-zephyr.yml: -------------------------------------------------------------------------------- 1 | name: Zephyr 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | types: [opened, reopened, synchronize, ready_for_review] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | name: Build 13 | runs-on: ubuntu-24.04 14 | 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | example: [nrfx-blink-sdk] 19 | 20 | steps: 21 | - name: Checkout repo 22 | uses: actions/checkout@v4 23 | 24 | - name: Set up Python 25 | uses: actions/setup-python@v5 26 | with: 27 | python-version: 3.11 28 | 29 | - name: Setup Zephyr project 30 | uses: zephyrproject-rtos/action-zephyr-setup@v1 31 | with: 32 | app-path: ${{ matrix.example }} 33 | toolchains: arm-zephyr-eabi 34 | sdk-version: 0.17.0 35 | 36 | - name: Install Swift 37 | uses: ./.github/actions/install-swift 38 | 39 | - name: Build ${{ matrix.example }} 40 | working-directory: ${{ matrix.example }} 41 | run: west build -b nrf52840dk/nrf52840 42 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | types: [opened, reopened, synchronize, ready_for_review] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | validate_format_config: 12 | name: Validate Format Config 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout repo 16 | uses: actions/checkout@v4 17 | 18 | - name: Install apt dependencies 19 | run: sudo apt-get -qq update && sudo apt-get -qq -y install curl 20 | 21 | - name: Compare against swift-mmio swift-format config 22 | run: | 23 | curl -sL https://raw.githubusercontent.com/apple/swift-mmio/refs/heads/main/.swift-format -o .swift-format-mmio 24 | diff .swift-format .swift-format-mmio 25 | 26 | soundness: 27 | name: Soundness 28 | uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main 29 | with: 30 | api_breakage_check_enabled: false # this repo doesn't vend any API 31 | license_header_check_enabled: false # feature: https://github.com/swiftlang/github-workflows/issues/78 32 | license_header_check_project_name: "Swift.org" # bug: https://github.com/swiftlang/github-workflows/issues/76 33 | unacceptable_language_check_enabled: false # unfortunately many hardware specs use terms like master/slave in their documentation 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | **/.DS_Store 3 | */.build 4 | */build 5 | .build/ 6 | build/ 7 | */sdkconfig 8 | */sdkconfig.old 9 | */managed_components 10 | */dependencies.lock 11 | 12 | -------------------------------------------------------------------------------- /.spi.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | external_links: 3 | documentation: "https://docs.swift.org/embedded/documentation/embedded" 4 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | main-snapshot-2025-06-01 -------------------------------------------------------------------------------- /.swiftformatignore: -------------------------------------------------------------------------------- 1 | ./harmony/* 2 | ./stm32-lvgl/* 3 | ./stm32-lcd-logo/Sources/STM32F7X6/* 4 | ./stm32-lvgl/Sources/Registers/* 5 | ./stm32-neopixel/Sources/STM32F7X6/* 6 | ./stm32-uart-echo/Sources/STM32F7X6/* 7 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This file is a list of the people responsible for ensuring that patches for a 2 | # particular part of Swift are reviewed, either by themselves or by someone else. 3 | # They are also the gatekeepers for their part of Swift, with the final word on 4 | # what goes in or not. 5 | # 6 | # The list is sorted by surname and formatted to allow easy grepping and 7 | # beautification by scripts. The fields are: name (N), email (E), web-address 8 | # (W), PGP key ID and fingerprint (P), description (D), and snail-mail address 9 | # (S). 10 | 11 | # N: Kuba Mracek 12 | # E: mracek@apple.com 13 | # D: Everything in swift-embedded-examples not covered by someone else 14 | 15 | ### 16 | 17 | # The following lines are used by GitHub to automatically recommend reviewers. 18 | 19 | * @kubamracek @rauhul 20 | -------------------------------------------------------------------------------- /Documentation/README.md: -------------------------------------------------------------------------------- 1 | Documentation for Embedded Swift 2 | ================================ 3 | 4 | Documentation for Embedded Swift can be found at these locations: 5 | - [in rendered form](https://swiftpackageindex.com/swiftlang/swift-embedded-examples/documentation/embeddedswift) 6 | - [DocC source code](/Sources/EmbeddedSwift/Documentation.docc) 7 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "ffbd64644921de7b1a9cb8b17d9ecb480b14c4b6487677314b16730c1b12221b", 3 | "pins" : [ 4 | { 5 | "identity" : "swift-docc-plugin", 6 | "kind" : "remoteSourceControl", 7 | "location" : "https://github.com/swiftlang/swift-docc-plugin", 8 | "state" : { 9 | "revision" : "85e4bb4e1cd62cec64a4b8e769dcefdf0c5b9d64", 10 | "version" : "1.4.3" 11 | } 12 | }, 13 | { 14 | "identity" : "swift-docc-symbolkit", 15 | "kind" : "remoteSourceControl", 16 | "location" : "https://github.com/swiftlang/swift-docc-symbolkit", 17 | "state" : { 18 | "revision" : "b45d1f2ed151d057b54504d653e0da5552844e34", 19 | "version" : "1.0.0" 20 | } 21 | } 22 | ], 23 | "version" : 3 24 | } 25 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "swift-embedded-examples", 7 | products: [ 8 | .library(name: "EmbeddedSwift", targets: ["EmbeddedSwift"]) 9 | ], 10 | dependencies: [ 11 | .package( 12 | url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.1.0") 13 | ], 14 | targets: [ 15 | .target(name: "EmbeddedSwift") 16 | ]) 17 | -------------------------------------------------------------------------------- /Sources/EmbeddedSwift/Documentation.docc/BuildSystemSupport/IntegrateWithBazel.md-wip: -------------------------------------------------------------------------------- 1 | # Integrate with Bazel 2 | 3 | 🚧 Under construction... 4 | -------------------------------------------------------------------------------- /Sources/EmbeddedSwift/Documentation.docc/BuildSystemSupport/IntegrateWithCMake.md-wip: -------------------------------------------------------------------------------- 1 | # Integrate with CMake 2 | 3 | 🚧 Under construction... 4 | -------------------------------------------------------------------------------- /Sources/EmbeddedSwift/Documentation.docc/BuildSystemSupport/IntegrateWithMake.md-wip: -------------------------------------------------------------------------------- 1 | # Integrate with Make 2 | 3 | 🚧 Under construction... 4 | -------------------------------------------------------------------------------- /Sources/EmbeddedSwift/Documentation.docc/BuildSystemSupport/IntegrateWithSwiftPM.md-wip: -------------------------------------------------------------------------------- 1 | # Integrate with SwiftPM 2 | 3 | 🚧 Under construction... 4 | -------------------------------------------------------------------------------- /Sources/EmbeddedSwift/Documentation.docc/BuildSystemSupport/IntegrateWithXcode.md-wip: -------------------------------------------------------------------------------- 1 | # Integrate with Xcode 2 | 3 | 🚧 Under construction... 4 | 5 | > Warning: Embedded Swift integration with Xcode is still under development and 6 | > subject to change. 7 | -------------------------------------------------------------------------------- /Sources/EmbeddedSwift/Documentation.docc/Documentation.md: -------------------------------------------------------------------------------- 1 | # Embedded Swift 2 | 3 | Embedded Swift is a compilation and language mode that enables development of baremetal, embedded and standalone software in Swift 4 | 5 | @Metadata { 6 | @TechnologyRoot 7 | } 8 | 9 | ## Topics 10 | 11 | ### Getting Started 12 | 13 | - 14 | - 15 | - 16 | 17 | ### Guided Examples 18 | 19 | - 20 | - 21 | - 22 | - 23 | 24 | ### Using Embedded Swift 25 | 26 | - 27 | - 28 | - 29 | - 30 | - 31 | - 32 | - 33 | 34 | ### Build System Support 35 | 36 | - 37 | - 38 | - 39 | - 40 | - 41 | 42 | ### SDK Support 43 | 44 | - 45 | - 46 | - 47 | - 48 | - 49 | 50 | ### Compiler Development and Details 51 | 52 | - 53 | - 54 | -------------------------------------------------------------------------------- /Sources/EmbeddedSwift/Documentation.docc/GettingStarted/InstallEmbeddedSwift.md: -------------------------------------------------------------------------------- 1 | # Install Embedded Swift 2 | 3 | Get the tools needed to use Embedded Swift 4 | 5 | ## Overview 6 | 7 | The best way to install Swift for embedded development is using [`swiftly`](http://github.com/swiftlang/swiftly), the official Swift toolchain installer and manager. Since Embedded Swift is still experimental and not yet supported in public Swift releases, you'll need to use a development toolchain. 8 | 9 | For instructions on how to install `swiftly`, check out the [Getting Started](https://www.swift.org/swiftly/documentation/swiftly/getting-started) guide. 10 | 11 | > Note: Swiftly installs the latest release toolchain during `swiftly init`, if you only plan on using Embedded Swift, you can avoid downloading this toolchain by using the `swiftly init --skip-install` option. 12 | 13 | After installing Swiftly, install the latest main "Development Snapshot" toolchain available for your platform by running: 14 | 15 | ```shell 16 | $ swiftly install main-snapshot 17 | ... 18 | main-snapshot-2025-04-12 installed successfully! 19 | ``` 20 | 21 | To test that you have Swift installed, run `swift --version` from your shell or terminal app. It should say "6.2-dev", meaning you have a "Development Snapshot" toolchain. 22 | 23 | ```shell 24 | $ swift --version 25 | Apple Swift version 6.2-dev (LLVM 81ab6d9f7e4810f, Swift 9cc1947527bacea) 26 | Target: arm64-apple-macosx15.0 27 | Build config: +assertions 28 | ``` 29 | 30 | > Warning: When using other shells, like one integrated into an IDE, be careful to verify that the version of Swift reported matches that of the shell you used to install Swift. 31 | -------------------------------------------------------------------------------- /Sources/EmbeddedSwift/Documentation.docc/SDKSupport/IntegrateWithESP.md-wip: -------------------------------------------------------------------------------- 1 | # ESP IDF 2 | 3 | 🚧 Under construction... 4 | -------------------------------------------------------------------------------- /Sources/EmbeddedSwift/Documentation.docc/UsingEmbeddedSwift/ConditionalCompilation.md: -------------------------------------------------------------------------------- 1 | # Conditionalizing compilation for Embedded Swift 2 | 3 | How to share code between Embedded Swift and full Swift using conditional compilation 4 | 5 | It's often useful to have source code be compilable under both regular Swift and Embedded Swift. The following syntax is available for that (but note that as the rest of Embedded Swift, it's experimental, subject to change and not considered source stable): 6 | 7 | ```swift 8 | func sayHello() { 9 | #if hasFeature(Embedded) 10 | print("I'm Embedded Swift") 11 | #else 12 | print("I'm regular Swift") 13 | #endif 14 | } 15 | ``` 16 | 17 | Additionally, you can also use an attribute (also experimental, and not source stable) to make entire functions, types and other declarations unavailable in Embedded Swift. This can be particularly useful to explicitly mark your own code (and also entire types and conformances) that relies on features unavailable in Embedded Swift, e.g. the Any type or Codable -- it is explicitly allowed to use those in unavailable contexts: 18 | 19 | ```swift 20 | @_unavailableInEmbedded 21 | func useAny(_: Any) { ... } 22 | 23 | @_unavailableInEmbedded 24 | extension MyStruct: Codable { 25 | ... 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /Sources/EmbeddedSwift/Documentation.docc/UsingEmbeddedSwift/Libraries.md: -------------------------------------------------------------------------------- 1 | # Libraries and modules in Embedded Swift 2 | 3 | Understand the library setup and linkage model of Embedded Swift 4 | 5 | Traditional library build and use model of Swift is that library code is compiled into a .swiftmodule, containing the interfaces, and a compiled library with binary code, either a .a static library or a .dylib/.so dynamic library. A client's build then uses the .swiftmodule at compile-time, and the static/dynamic library at link-time. 6 | 7 | The library model in Embedded Swift works slightly differently: All Swift source code of a library is promoted into being inlinable and visible to client builds (this is necessary for generic code, and beneficial for optimizations for non-generic code), and ends up serialized into the .swiftmodule, the interface of the library. Therefore, the compiled code of a library is never needed, and doesn't even need to be produced. For example: 8 | 9 | ```bash 10 | # Build the library, only as a .swiftmodule. Notice that we never build the .o or .a for the library. 11 | $ swiftc -target -enable-experimental-feature Embedded -wmo \ 12 | a.swift b.swift -module-name MyLibrary -emit-module -emit-module-path ./MyLibrary.swiftmodule 13 | 14 | # Build the client, "-I ." add the current directory to the module search path list 15 | $ swiftc -target -enable-experimental-feature Embedded -wmo \ 16 | client.swift -I . -c -o client.o 17 | ``` 18 | 19 | The Embedded Swift standard library is distributed in the toolchain the same way: It's strictly a .swiftmodule without any compiled code present anywhere. All the compiling into machine code is performed as part of the client's build. This has the major benefit that the client's build can provide additional ABI and ISA defining flags, such as the above-mentioned `-mfloat-abi`, `-fshort-enums`, `-mcpu`, `-march` flags, and these flags in the client's build will apply to all the library code (including standard library code) as well. 20 | -------------------------------------------------------------------------------- /Sources/EmbeddedSwift/Empty.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2025 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 | // This file is included so SwiftPM considers the target to be a Swift target. 13 | -------------------------------------------------------------------------------- /Tools/SVDs/README.md: -------------------------------------------------------------------------------- 1 | # SVDs 2 | 3 | The SVDs used in this repo have been copied from the following sources. 4 | 5 | | file | source | 6 | |----------------------|----------------------------------------------------------------------| 7 | | `rp2040.patched.svd` | https://github.com/rp-rs/rp2040-pac/blob/main/svd/rp2040.svd.patched | 8 | | `rp2350.patched.svd` | https://github.com/rp-rs/rp235x-pac/blob/main/svd/RP2350.svd.patched | 9 | -------------------------------------------------------------------------------- /Tools/Toolsets/pico.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0", 3 | "swiftCompiler": { 4 | "extraCLIOptions": [ 5 | "-Xfrontend", "-disable-stack-protector", 6 | "-enable-experimental-feature", "Embedded", 7 | "-Xcc", "-mcpu=cortex-m0plus" 8 | ] 9 | }, 10 | "linker": { 11 | "extraCLIOptions": [ 12 | "-arch", "armv6m", 13 | "-dead_strip", 14 | "-static", 15 | "-e", "_reset", 16 | "-no_zero_fill_sections", 17 | "-segalign", "4", 18 | "-segaddr", "__RESET", "0x20000000", 19 | "-segaddr", "__VECTORS", "0x20000100", 20 | "-seg1addr", "0x20000200", 21 | "-pagezero_size", "0", 22 | "-allow_dead_duplicates" 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Tools/Toolsets/pico2.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0", 3 | "swiftCompiler": { 4 | "extraCLIOptions": [ 5 | "-Xfrontend", "-disable-stack-protector", 6 | "-enable-experimental-feature", "Embedded" 7 | ] 8 | }, 9 | "linker": { 10 | "extraCLIOptions": [ 11 | "-arch", "armv7em", 12 | "-dead_strip", 13 | "-static", 14 | "-e", "_reset", 15 | "-no_zero_fill_sections", 16 | "-segalign", "4", 17 | "-segaddr", "__VECTORS", "0x20000000", 18 | "-segaddr", "__RESET", "0x20000200", 19 | "-seg1addr", "0x20000300", 20 | "-pagezero_size", "0", 21 | "-allow_dead_duplicates" 22 | ] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tools/Toolsets/rpi-5-elf.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0", 3 | "swiftCompiler": { 4 | "extraCLIOptions": [ 5 | "-Xfrontend", "-disable-stack-protector", 6 | "-Xfrontend", "-function-sections", 7 | "-enable-experimental-feature", "Embedded", 8 | "-Xfrontend", "-mergeable-symbols", 9 | "-Xclang-linker", "-fuse-ld=lld", 10 | "-Xclang-linker", "-nostdlib" 11 | ] 12 | }, 13 | "linker": { 14 | "extraCLIOptions": [ 15 | "-T", "Sources/Support/linkerscript.ld", 16 | "--unresolved-symbols=ignore-in-object-files" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tools/Toolsets/stm32f74x-lcd.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0", 3 | "swiftCompiler": { 4 | "extraCLIOptions": [ 5 | "-Xfrontend", "-disable-stack-protector", 6 | "-enable-experimental-feature", "Embedded" 7 | ] 8 | }, 9 | "linker": { 10 | "extraCLIOptions": [ 11 | "-arch", "armv7em", 12 | "-dead_strip", 13 | "-static", 14 | "-e", "_reset", 15 | "-no_zero_fill_sections", 16 | "-segalign", "4", 17 | "-segaddr", "__VECTORS", "0x00200000", 18 | "-seg1addr", "0x00200200", 19 | "-pagezero_size", "0", 20 | "-allow_dead_duplicates" 21 | ] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tools/Toolsets/stm32f74x.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0", 3 | "swiftCompiler": { 4 | "extraCLIOptions": [ 5 | "-Xfrontend", "-disable-stack-protector", 6 | "-enable-experimental-feature", "Embedded" 7 | ] 8 | }, 9 | "linker": { 10 | "extraCLIOptions": [ 11 | "-arch", "armv7em", 12 | "-dead_strip", 13 | "-static", 14 | "-e", "_reset", 15 | "-no_zero_fill_sections", 16 | "-segalign", "4", 17 | "-segaddr", "__VECTORS", "0x20010000", 18 | "-seg1addr", "0x20010200", 19 | "-pagezero_size", "0", 20 | "-allow_dead_duplicates" 21 | ] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /esp32-led-blink-sdk/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.29) 2 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 3 | project(main) 4 | -------------------------------------------------------------------------------- /esp32-led-blink-sdk/README.md: -------------------------------------------------------------------------------- 1 | # esp32-led-blink-sdk 2 | 3 | This example demonstrates how to integrate with the ESP-IDF SDK via CMake and how to use the standard GPIO library to control LED from Swift. This example is specifically made for the RISC-V MCUs from ESP32 (the Xtensa MCUs are not currently supported by Swift). 4 | 5 | ![Led on](assets/images/ledon.jpg) 6 | ![Led off](assets/images/ledoff.jpg) 7 | 8 | ## Requirements 9 | 10 | - Set up the [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/) development environment. Follow the steps in the [ESP32-C6 "Get Started" guide](https://docs.espressif.com/projects/esp-idf/en/v5.2/esp32c6/get-started/index.html). 11 | - Make sure you specifically set up development for the RISC-V ESP32-C6, and not the Xtensa based products. 12 | 13 | - Before trying to use Swift with the ESP-IDF SDK, make sure your environment works and can build the provided C/C++ sample projects, in particular: 14 | - Try building and running the "get-started/blink" example from ESP-IDF written in C. 15 | 16 | ## Building 17 | 18 | - Make sure you have a recent nightly Swift toolchain that has Embedded Swift support. 19 | - If needed, run export.sh to get access to the idf.py script from ESP-IDF. 20 | - Specify the target board type by using `idf.py set-target`. 21 | ``` console 22 | $ cd esp32-led-blink-sdk 23 | $ . /export.sh 24 | $ idf.py set-target esp32c6 25 | $ idf.py build 26 | ``` 27 | 28 | ## Running 29 | 30 | - Connect the Esp32-C6-Bug board (or any other board with integrated LED on GPIO pin 8) over a USB cable to your Mac. Alternatively you can just connect external LED to GPIO pin 8 on any other board. 31 | - Connect RX pin of USB-UART converter to TX0 pin of your board if you need serial ouput. You may also need to connect GND converter pin to the GND pin of the board. 32 | - Use `idf.py` to upload the firmware and to run it: 33 | 34 | ```console 35 | $ idf.py flash 36 | ``` 37 | 38 | - The LED should be blinking now. 39 | 40 | ### Simulating in VS Code 41 | 42 | - Build the project, to generate binaries for simulation 43 | - Install [Wokwi for VS Code](https://docs.wokwi.com/vscode/getting-started/). 44 | - Open the `diagram.json` file. 45 | - Click the Play button to start simulation. 46 | - Click the Pause button to freeze simulation and display states of GPIOs. 47 | 48 | -------------------------------------------------------------------------------- /esp32-led-blink-sdk/assets/images/ledoff.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftlang/swift-embedded-examples/622fd0a41dc883d40c1d101a73f678b1fe31b0d5/esp32-led-blink-sdk/assets/images/ledoff.jpg -------------------------------------------------------------------------------- /esp32-led-blink-sdk/assets/images/ledon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftlang/swift-embedded-examples/622fd0a41dc883d40c1d101a73f678b1fe31b0d5/esp32-led-blink-sdk/assets/images/ledon.jpg -------------------------------------------------------------------------------- /esp32-led-blink-sdk/diagram.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "author": "", 4 | "editor": "wokwi", 5 | "parts": [ 6 | { 7 | "type": "board-esp32-c6-devkitc-1", 8 | "id": "esp", 9 | "top": 0, 10 | "left": 0, 11 | "attrs": { "builder": "esp-idf" } 12 | }, 13 | { 14 | "type": "wokwi-resistor", 15 | "id": "r1", 16 | "top": 119.15, 17 | "left": -76.8, 18 | "attrs": { "value": "1000" } 19 | }, 20 | { 21 | "type": "wokwi-led", 22 | "id": "led1", 23 | "top": 25.2, 24 | "left": -111.4, 25 | "attrs": { "color": "red" } 26 | } 27 | ], 28 | "connections": [ 29 | [ "esp:TX", "$serialMonitor:RX", "", [] ], 30 | [ "esp:RX", "$serialMonitor:TX", "", [] ], 31 | [ "r1:2", "esp:8", "red", [ "v0" ] ], 32 | [ "r1:1", "led1:A", "red", [ "h0" ] ], 33 | [ "led1:C", "esp:GND.1", "black", [ "v0" ] ] 34 | ], 35 | "dependencies": {} 36 | } 37 | -------------------------------------------------------------------------------- /esp32-led-blink-sdk/main/BridgingHeader.h: -------------------------------------------------------------------------------- 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 | #include 13 | 14 | #include "freertos/FreeRTOS.h" 15 | #include "freertos/task.h" 16 | #include "driver/gpio.h" 17 | #include "sdkconfig.h" 18 | -------------------------------------------------------------------------------- /esp32-led-blink-sdk/main/Led.swift: -------------------------------------------------------------------------------- 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 | // A simple "overlay" to provide nicer APIs in Swift 13 | struct Led { 14 | var ledPin: gpio_num_t 15 | init(gpioPin: Int) { 16 | ledPin = gpio_num_t(Int32(gpioPin)) 17 | 18 | guard gpio_reset_pin(ledPin) == ESP_OK else { 19 | fatalError("cannot reset led") 20 | } 21 | 22 | guard gpio_set_direction(ledPin, GPIO_MODE_OUTPUT) == ESP_OK else { 23 | fatalError("cannot reset led") 24 | } 25 | } 26 | func setLed(value: Bool) { 27 | let level: UInt32 = value ? 1 : 0 28 | gpio_set_level(ledPin, level) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /esp32-led-blink-sdk/main/Main.swift: -------------------------------------------------------------------------------- 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 | // The code will blink an LED on GPIO8. To change the pin, modify Led(gpioPin: 8) 13 | @_cdecl("app_main") 14 | func main() { 15 | print("Hello from Swift on ESP32-C6!") 16 | 17 | var ledValue: Bool = false 18 | let blinkDelayMs: UInt32 = 500 19 | let led = Led(gpioPin: 8) 20 | 21 | while true { 22 | led.setLed(value: ledValue) 23 | ledValue.toggle() // Toggle the boolean value 24 | vTaskDelay(blinkDelayMs / (1000 / UInt32(configTICK_RATE_HZ))) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /esp32-led-blink-sdk/main/idf_component.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftlang/swift-embedded-examples/622fd0a41dc883d40c1d101a73f678b1fe31b0d5/esp32-led-blink-sdk/main/idf_component.yml -------------------------------------------------------------------------------- /esp32-led-blink-sdk/wokwi.toml: -------------------------------------------------------------------------------- 1 | # Wokwi Configuration File 2 | # Reference: https://docs.wokwi.com/vscode/project-config 3 | [wokwi] 4 | version = 1 5 | firmware = 'build/flasher_args.json' 6 | elf = 'build/main.elf' 7 | # gdbServerPort=3333 8 | 9 | -------------------------------------------------------------------------------- /esp32-led-strip-sdk/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.29) 2 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 3 | project(main) 4 | -------------------------------------------------------------------------------- /esp32-led-strip-sdk/README.md: -------------------------------------------------------------------------------- 1 | # esp32-led-strip-sdk 2 | 3 | This example demonstrates how to integrate with the ESP-IDF SDK via CMake and how to use the existing LED strip library to control WS8212 lights from Swift. This example is specifically made for the RISC-V MCUs from ESP32 (the Xtensa MCUs are not currently supported by Swift). 4 | 5 | 6 | 7 | ## Requirements 8 | 9 | - Set up the [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/) development environment. Follow the steps in the [ESP32-C6 "Get Started" guide](https://docs.espressif.com/projects/esp-idf/en/v5.2/esp32c6/get-started/index.html). 10 | - Make sure you specifically set up development for the RISC-V ESP32-C6, and not the Xtensa based products. 11 | 12 | - Before trying to use Swift with the ESP-IDF SDK, make sure your environment works and can build the provided C/C++ sample projects, in particular: 13 | - Try building and running the "get-started/blink" example from ESP-IDF written in C. 14 | 15 | ## Building 16 | 17 | - Make sure you have a recent nightly Swift toolchain that has Embedded Swift support. 18 | - If needed, run export.sh to get access to the idf.py script from ESP-IDF. 19 | - Specify and the target board type by using `idf.py set-target`. 20 | ``` console 21 | $ cd esp32-led-strip-sdk 22 | $ . /export.sh 23 | $ idf.py set-target esp32c6 24 | $ idf.py build 25 | ``` 26 | 27 | ## Running 28 | 29 | - Connect the ESP32-C6-DevKitC-1 board over a USB cable to your Mac. 30 | - Wire up an external WS8212 LED strip and use GPIO pin 0 as the data pin. You might need to use a level shifter. 31 | - Use `idf.py` to upload the firmware and to run it: 32 | 33 | ```console 34 | $ idf.py flash 35 | ``` 36 | 37 | - The LED strip should now be animating a sequence of random colors moving in one direction. 38 | 39 | ### Simulating in VS Code 40 | 41 | - Build the project, to generate binaries for simulation 42 | - Install [Wokwi for VS Code](https://docs.wokwi.com/vscode/getting-started/). 43 | - Open the `diagram.json` file. 44 | - Click the Play button to start simulation. 45 | - Click the Pause button to freeze simulation and display states of GPIOs. 46 | 47 | -------------------------------------------------------------------------------- /esp32-led-strip-sdk/diagram.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "author": "", 4 | "editor": "wokwi", 5 | "parts": [ 6 | { 7 | "type": "board-esp32-c6-devkitc-1", 8 | "id": "esp", 9 | "top": -13.91, 10 | "left": 90.52, 11 | "attrs": { "builder": "esp-idf" } 12 | }, 13 | { "type": "wokwi-neopixel", "id": "rgb1", "top": 73.3, "left": -20.2, "attrs": {} }, 14 | { "type": "wokwi-neopixel", "id": "rgb2", "top": 73.3, "left": 18.2, "attrs": {} }, 15 | { "type": "wokwi-neopixel", "id": "rgb3", "top": 73.3, "left": -250.6, "attrs": {} }, 16 | { "type": "wokwi-neopixel", "id": "rgb4", "top": 73.3, "left": -212.2, "attrs": {} }, 17 | { "type": "wokwi-neopixel", "id": "rgb5", "top": 73.3, "left": -58.6, "attrs": {} }, 18 | { "type": "wokwi-neopixel", "id": "rgb6", "top": 73.3, "left": -173.8, "attrs": {} }, 19 | { "type": "wokwi-neopixel", "id": "rgb7", "top": 73.3, "left": -97, "attrs": {} }, 20 | { "type": "wokwi-neopixel", "id": "rgb8", "top": 73.3, "left": -135.4, "attrs": {} } 21 | ], 22 | "connections": [ 23 | [ "esp:TX", "$serialMonitor:RX", "", [] ], 24 | [ "esp:RX", "$serialMonitor:TX", "", [] ], 25 | [ "rgb2:DIN", "esp:0", "green", [ "h28", "v-38.4" ] ], 26 | [ "rgb2:DOUT", "rgb1:DIN", "green", [ "h-9.6", "v-10.5" ] ], 27 | [ "rgb5:DOUT", "rgb7:DIN", "green", [ "h-9.6", "v-10.5" ] ], 28 | [ "rgb8:DOUT", "rgb6:DIN", "green", [ "h-9.6", "v-10.5" ] ], 29 | [ "rgb6:DOUT", "rgb4:DIN", "green", [ "v-0.9", "h-9.6", "v-9.6" ] ], 30 | [ "rgb4:DOUT", "rgb3:DIN", "green", [ "v-0.9", "h-9.6", "v-9.6" ] ], 31 | [ "rgb1:DOUT", "rgb5:DIN", "green", [ "h-9.6", "v-10.5" ] ], 32 | [ "rgb2:VSS", "esp:5V", "red", [ "h18.4", "v75.9" ] ], 33 | [ "rgb2:VDD", "rgb1:VSS", "red", [ "h-9.6", "v10.5" ] ], 34 | [ "rgb1:VDD", "rgb5:VSS", "red", [ "h-9.6", "v10.5" ] ], 35 | [ "rgb5:VDD", "rgb7:VSS", "red", [ "h-9.6", "v10.5" ] ], 36 | [ "rgb7:VDD", "rgb8:VSS", "red", [ "h-9.6", "v10.5" ] ], 37 | [ "rgb8:VDD", "rgb6:VSS", "red", [ "h-9.6", "v10.5" ] ], 38 | [ "rgb6:VDD", "rgb4:VSS", "red", [ "h-9.6", "v10.5" ] ], 39 | [ "rgb4:VDD", "rgb3:VSS", "red", [ "h-9.6", "v10.5" ] ], 40 | [ "rgb3:VDD", "esp:GND.1", "black", [ "h-9.6", "v96" ] ], 41 | [ "rgb8:DIN", "rgb7:DOUT", "green", [ "h8.8", "v9.6", "h9.6" ] ] 42 | ], 43 | "dependencies": {} 44 | } 45 | -------------------------------------------------------------------------------- /esp32-led-strip-sdk/main/BridgingHeader.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | #include 13 | 14 | #include "freertos/FreeRTOS.h" 15 | #include "freertos/task.h" 16 | #include "led_strip.h" 17 | #include "sdkconfig.h" 18 | -------------------------------------------------------------------------------- /esp32-led-strip-sdk/main/LedStrip.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | // A simple "overlay" to provide nicer APIs in Swift 13 | struct LedStrip { 14 | private let handle: led_strip_handle_t 15 | 16 | init(gpioPin: Int, maxLeds: Int) { 17 | var handle = led_strip_handle_t(bitPattern: 0) 18 | var stripConfig = led_strip_config_t( 19 | strip_gpio_num: Int32(gpioPin), 20 | max_leds: UInt32(maxLeds), 21 | led_pixel_format: LED_PIXEL_FORMAT_GRB, 22 | led_model: LED_MODEL_WS2812, 23 | flags: .init(invert_out: 0) 24 | ) 25 | var spiConfig = led_strip_spi_config_t( 26 | clk_src: SPI_CLK_SRC_DEFAULT, 27 | spi_bus: SPI2_HOST, 28 | flags: .init(with_dma: 1) 29 | ) 30 | guard led_strip_new_spi_device(&stripConfig, &spiConfig, &handle) == ESP_OK, 31 | let handle = handle 32 | else { fatalError("cannot configure spi device") } 33 | self.handle = handle 34 | } 35 | 36 | struct Color { 37 | static var white = Color(r: 255, g: 255, b: 255) 38 | static var lightWhite = Color(r: 16, g: 16, b: 16) 39 | static var lightRandom: Color { 40 | Color( 41 | r: .random(in: 0...16), g: .random(in: 0...16), b: .random(in: 0...16)) 42 | } 43 | static var off = Color(r: 0, g: 0, b: 0) 44 | 45 | var r, g, b: UInt8 46 | } 47 | 48 | func setPixel(index: Int, color: Color) { 49 | led_strip_set_pixel( 50 | handle, UInt32(index), UInt32(color.r), UInt32(color.g), UInt32(color.b)) 51 | } 52 | 53 | func refresh() { led_strip_refresh(handle) } 54 | 55 | func clear() { led_strip_clear(handle) } 56 | } 57 | -------------------------------------------------------------------------------- /esp32-led-strip-sdk/main/Main.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | @_cdecl("app_main") 13 | func main() { 14 | print("Hello from Swift on ESP32-C6!") 15 | 16 | let n = 8 17 | let ledStrip = LedStrip(gpioPin: 0, maxLeds: n) 18 | ledStrip.clear() 19 | 20 | var colors: [LedStrip.Color] = .init(repeating: .off, count: n) 21 | while true { 22 | colors.removeLast() 23 | colors.insert(.lightRandom, at: 0) 24 | 25 | for index in 0.. Void) { 16 | self.pin = pin 17 | 18 | gpio_init(pin) 19 | gpio_set_dir(pin, false) // input 20 | gpio_pull_up(pin) // pull up the pin 21 | gpio_set_irq_enabled_with_callback( 22 | pin, UInt32(GPIO_IRQ_EDGE_FALL.rawValue), true, callback) 23 | } 24 | 25 | deinit { 26 | gpio_deinit(self.pin) 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /harmony/Sources/Application/ButtonTimes.swift: -------------------------------------------------------------------------------- 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 | struct ButtonTimes { 13 | typealias Times = ( 14 | UInt32, UInt32, UInt32, UInt32, 15 | UInt32, UInt32, UInt32, UInt32, 16 | UInt32, UInt32, UInt32, UInt32) 17 | static let count = 12 18 | static let rawSize = MemoryLayout.size 19 | static let reboundSize = Self.count * MemoryLayout.size 20 | 21 | private var times: Times = 22 | (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 23 | 24 | subscript(_ index: some FixedWidthInteger) -> UInt32 { 25 | get { 26 | precondition(Self.rawSize == Self.reboundSize) 27 | return withUnsafePointer(to: self.times) { 28 | $0.withMemoryRebound(to: UInt32.self, capacity: Self.count) { 29 | $0[Int(index)] 30 | } 31 | } 32 | } 33 | set { 34 | precondition(Self.rawSize == Self.reboundSize) 35 | return withUnsafeMutablePointer(to: &self.times) { 36 | $0.withMemoryRebound(to: UInt32.self, capacity: Self.count) { 37 | $0[Int(index)] = newValue 38 | } 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /harmony/Sources/Application/Stubs.swift: -------------------------------------------------------------------------------- 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 | // Embedded Swift currently requires posix_memalign, but the C libraries in the 13 | // Pico SDK do not provide it. Let's implement it and forward the calls to 14 | // aligned_alloc(3). 15 | @_cdecl("posix_memalign") 16 | public func posix_memalign( 17 | memptr: UnsafeMutablePointer, 18 | alignment: size_t, 19 | size: size_t 20 | ) -> Int32 { 21 | if let allocation = aligned_alloc(alignment, size) { 22 | memptr.pointee = allocation 23 | return 0 24 | } 25 | return _errno() 26 | } 27 | 28 | // FIXME: document 29 | @_cdecl("swift_isEscapingClosureAtFileLocation") 30 | func swift_isEscapingClosureAtFileLocation( 31 | object: UnsafeRawPointer, 32 | filename: UnsafePointer, 33 | filenameLength: Int32, 34 | line: Int32, 35 | column: Int32, 36 | type: UInt 37 | ) -> Bool { 38 | false 39 | } 40 | -------------------------------------------------------------------------------- /harmony/Sources/Audio/AudioBuffer.swift: -------------------------------------------------------------------------------- 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 | struct AudioBuffer: ~Copyable { 13 | // FIXME: Raw 14 | var storage: UnsafeMutableBufferPointer 15 | var capacity: Int { self.storage.count } 16 | var count: Int 17 | 18 | init(capacity: Int) { 19 | self.storage = .allocate(capacity: capacity) 20 | self.storage.initialize(repeating: 0) 21 | // FIXME: don't assume filled. 22 | self.count = capacity 23 | } 24 | 25 | deinit { 26 | self.storage.deallocate() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /harmony/Sources/Audio/AudioBufferTransport.swift: -------------------------------------------------------------------------------- 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 | struct AudioBufferTransport: ~Copyable { 13 | var emptyBuffers: Ring 14 | var fullBuffers: Ring 15 | var analyzedBuffers: Ring 16 | 17 | init(bufferCount: Int, bufferCapacity: Int) { 18 | // Ring buffer needs one extra slot to distinguish between empty and full. 19 | self.emptyBuffers = Ring(capacity: bufferCount + 1) 20 | self.fullBuffers = Ring(capacity: bufferCount + 1) 21 | self.analyzedBuffers = Ring(capacity: bufferCount + 1) 22 | 23 | for _ in 0.. AudioBuffer? { 36 | self.emptyBuffers.pop() 37 | } 38 | 39 | mutating func pushFullBuffer(_ buffer: consuming AudioBuffer) { 40 | self.fullBuffers.push(buffer) 41 | } 42 | 43 | mutating func popFullBuffer() -> AudioBuffer? { 44 | self.fullBuffers.pop() 45 | } 46 | 47 | mutating func pushAnalyzedBuffer(_ buffer: consuming AnalyzedAudioBuffer) { 48 | self.analyzedBuffers.push(buffer) 49 | } 50 | 51 | mutating func popAnalyzedBuffer() -> AnalyzedAudioBuffer? { 52 | self.analyzedBuffers.pop() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /harmony/Sources/Audio/Resampler.swift: -------------------------------------------------------------------------------- 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 | struct Resampler: ~Copyable { 13 | var channels: Int 14 | var context: btstack_resample_t 15 | 16 | init(channels: Int) { 17 | self.channels = channels 18 | self.context = btstack_resample_t() 19 | btstack_resample_init(&self.context, Int32(channels)) 20 | } 21 | 22 | mutating func set(channels: Int) { 23 | self.channels = channels 24 | btstack_resample_init(&self.context, Int32(channels)) 25 | } 26 | 27 | mutating func set(factor: UInt32) { 28 | btstack_resample_set_factor(&self.context, factor) 29 | } 30 | 31 | /// Resamples the given samples using the previously set resampling factor. 32 | /// 33 | /// Returns a slice of the temporary buffer that contains the resampled audio. 34 | mutating func resample( 35 | samples: UnsafeBufferPointer, 36 | usingTemporaryBuffer buffer: UnsafeMutableBufferPointer 37 | ) -> UnsafeMutableBufferPointer { 38 | precondition(samples.count.isMultiple(of: self.channels)) 39 | 40 | // FIXME: understand why this is not `samples.count / self.channels` 41 | // The documentation just calls this parameter `numFrames` which implies 42 | // the sample count should be divided by the channel count. 43 | let inputFrameCount = samples.count 44 | let resampledFrameCount = btstack_resample_block( 45 | &self.context, 46 | samples.baseAddress, 47 | UInt32(inputFrameCount), 48 | buffer.baseAddress) 49 | let resampledSampleCount = Int(resampledFrameCount) * self.channels 50 | return buffer.extracting(..: ~Copyable { 13 | // FIMXE: Use an inline allocation like `Vector` 14 | var storage: UnsafeMutableBufferPointer 15 | var readerIndex: Int 16 | var writerIndex: Int 17 | 18 | init(capacity: Int) { 19 | self.storage = .allocate(capacity: capacity) 20 | self.readerIndex = 0 21 | self.writerIndex = 0 22 | } 23 | 24 | deinit { 25 | var readerIndex = self.readerIndex 26 | while self.readerIndex != self.writerIndex { 27 | self.storage.deinitializeElement(at: readerIndex) 28 | readerIndex = (readerIndex + 1) % self.storage.count 29 | } 30 | // FIXME: why can't we use a mutating method here? 31 | // while _ = self.pop() { } 32 | self.storage.deallocate() 33 | } 34 | } 35 | 36 | extension Ring where Element: ~Copyable { 37 | mutating func push(_ element: consuming Element) { 38 | let nextWriterIndex = (self.writerIndex + 1) % self.storage.count 39 | guard nextWriterIndex != self.readerIndex else { fatalError("Overflow") } 40 | self.storage.initializeElement(at: self.writerIndex, to: element) 41 | __dsb() // Make sure the element is written before updating the index 42 | self.writerIndex = nextWriterIndex 43 | } 44 | 45 | mutating func pop() -> Element? { 46 | guard self.readerIndex != self.writerIndex else { return nil } 47 | let element = self.storage.moveElement(from: self.readerIndex) 48 | __dsb() // Make sure the element is read before updating the index 49 | self.readerIndex = (self.readerIndex + 1) % self.storage.count 50 | return element 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /harmony/Sources/Audio/SpinLock.swift: -------------------------------------------------------------------------------- 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 | struct SpinLock: ~Copyable { 13 | var _lock: UnsafeMutablePointer 14 | var value: Value 15 | 16 | init(index: Int, initialValue: consuming Value) { 17 | self._lock = spin_lock_init(UInt32(index)) 18 | self.value = initialValue 19 | } 20 | } 21 | 22 | extension SpinLock where Value: ~Copyable { 23 | func lock() -> UInt32 { 24 | spin_lock_blocking(self._lock) 25 | } 26 | 27 | func unlock(irq_mask: UInt32) { 28 | spin_unlock(self._lock, irq_mask) 29 | } 30 | 31 | mutating func withLock( 32 | _ body: (inout Value) throws(Error) -> Result 33 | ) throws(Error) -> Result where Result: ~Copyable { 34 | let irq_mask = self.lock() 35 | defer { self.unlock(irq_mask: irq_mask) } 36 | return try body(&self.value) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /harmony/Sources/Audio/Timer.swift: -------------------------------------------------------------------------------- 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 | // struct Timer: ~Copyable, ~Escapable { 13 | // var context: UnsafePointer 14 | 15 | // init(context: borrowing Context) dependsOn(context) { 16 | // withUnsafePointerToInstance(context) { context in 17 | // self.context = context 18 | // } 19 | // } 20 | // } -------------------------------------------------------------------------------- /harmony/Sources/Bluetooth/HCI.swift: -------------------------------------------------------------------------------- 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 | // Host Controller Interface 13 | 14 | @_cdecl("hci_packet_handler") 15 | func hci_packet_handler( 16 | packet_type: UInt8, 17 | channel: UInt16, 18 | packet: UnsafeMutablePointer?, 19 | size: UInt16 20 | ) { 21 | guard packet_type == HCI_EVENT_PACKET else { return } 22 | guard hci_event_packet_get_type(packet) == HCI_EVENT_PIN_CODE_REQUEST else { 23 | return 24 | } 25 | 26 | var address: bd_addr_t = (0, 0, 0, 0, 0, 0) 27 | log("Pin code request - using '0000'") 28 | hci_event_pin_code_request_get_bd_addr(packet, &address) 29 | gap_pin_code_response(&address, "0000") 30 | } 31 | -------------------------------------------------------------------------------- /harmony/Sources/Bluetooth/SBC.swift: -------------------------------------------------------------------------------- 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 | enum SBCDecoder { 13 | typealias Callback = ( 14 | _ data: UnsafeMutableBufferPointer, 15 | _ num_channels: Int32, 16 | _ sample_rate: Int32 17 | ) -> Void 18 | 19 | static var context = btstack_sbc_decoder_bluedroid_t() 20 | static var instance: UnsafePointer? = nil 21 | static var callback: Callback? = nil 22 | 23 | static func configure(mode: btstack_sbc_mode_t) { 24 | self.instance = btstack_sbc_decoder_bluedroid_init_instance(&context) 25 | 26 | func decode_callback( 27 | _ data: UnsafeMutablePointer?, 28 | _ num_samples: Int32, 29 | _ num_channels: Int32, 30 | _ sample_rate: Int32, 31 | _ context: UnsafeMutableRawPointer? 32 | ) { 33 | let data = UnsafeMutableBufferPointer( 34 | start: data, count: Int(num_samples)) 35 | Self.callback?(data, num_channels, sample_rate) 36 | } 37 | 38 | instance?.pointee.configure(&context, mode, decode_callback, nil) 39 | } 40 | 41 | static func decode_signed_16( 42 | mode: btstack_sbc_mode_t, 43 | packet_status_flag: UInt8, 44 | buffer: UnsafeRawBufferPointer, 45 | callback: Callback 46 | ) { 47 | guard let instance = Self.instance else { 48 | preconditionFailure("Must call configure prior to decode_signed_16") 49 | } 50 | 51 | return withoutActuallyEscaping(callback) { 52 | Self.callback = $0 53 | instance.pointee.decode_signed_16( 54 | &Self.context, 55 | packet_status_flag, 56 | buffer.baseAddress, 57 | UInt16(buffer.count)) 58 | Self.callback = nil 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /harmony/Sources/Bluetooth/SDP.swift: -------------------------------------------------------------------------------- 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 | // Service Discovery Protocol 13 | 14 | struct ServiceDiscoveryProtocol: ~Copyable { 15 | typealias ServiceRecord = UnsafePointer 16 | typealias ServiceRecordHandle = UInt32 17 | 18 | init() { 19 | sdp_init() 20 | } 21 | 22 | deinit { 23 | sdp_deinit() 24 | } 25 | 26 | mutating func registerService(record: ServiceRecord) { 27 | precondition(sdp_register_service(record) == 0) 28 | } 29 | 30 | mutating func registerService(record: UnsafeMutableRawBufferPointer) { 31 | precondition(de_get_len(record.baseAddress) <= record.count) 32 | precondition(sdp_register_service(record.baseAddress) == 0) 33 | } 34 | 35 | mutating func unregisterService(handle: ServiceRecordHandle) { 36 | sdp_unregister_service(handle) 37 | } 38 | 39 | mutating func getServiceRecordHandle(for record: ServiceRecord) -> ServiceRecordHandle { 40 | sdp_get_service_record_handle(record) 41 | } 42 | 43 | mutating func makeServiceRecordHandle() -> ServiceRecordHandle { 44 | sdp_create_service_record_handle() 45 | } 46 | 47 | mutating func getServiceRecord(for handle: ServiceRecordHandle) -> ServiceRecord? { 48 | ServiceRecord(sdp_get_record_for_handle(handle)) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /harmony/Sources/PIOPrograms/I2S.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | ; Transmit a mono or stereo I2S audio stream as stereo 8 | ; This is 16 bits per sample; can be altered by modifying the "set" params, 9 | ; or made programmable by replacing "set x" with "mov x, y" and using Y as a config register. 10 | ; 11 | ; Autopull must be enabled, with threshold set to 32. 12 | ; Since I2S is MSB-first, shift direction should be to left. 13 | ; Hence the format of the FIFO word is: 14 | ; 15 | ; | 31 : 16 | 15 : 0 | 16 | ; | sample ws=0 | sample ws=1 | 17 | ; 18 | ; Data is output at 1 bit per clock. Use clock divider to adjust frequency. 19 | ; Fractional divider will probably be needed to get correct bit clock period, 20 | ; but for common syslck freqs this should still give a constant word select period. 21 | ; 22 | ; One output pin is used for the data output. 23 | ; Two side-set pins are used. Bit 0 is clock, bit 1 is word select. 24 | 25 | ; Send 16 bit words to the PIO for mono, 32 bit words for stereo 26 | 27 | .program audio_i2s 28 | .side_set 2 29 | 30 | ; /--- LRCLK 31 | ; |/-- BCLK 32 | bitloop1: ; || 33 | out pins, 1 side 0b10 34 | jmp x-- bitloop1 side 0b11 35 | out pins, 1 side 0b00 36 | set x, 14 side 0b01 37 | 38 | bitloop0: 39 | out pins, 1 side 0b00 40 | jmp x-- bitloop0 side 0b01 41 | out pins, 1 side 0b10 42 | public entry_point: 43 | set x, 14 side 0b11 44 | 45 | % c-sdk { 46 | 47 | static inline void audio_i2s_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base) { 48 | pio_sm_config sm_config = audio_i2s_program_get_default_config(offset); 49 | 50 | sm_config_set_out_pins(&sm_config, data_pin, 1); 51 | sm_config_set_sideset_pins(&sm_config, clock_pin_base); 52 | sm_config_set_out_shift(&sm_config, false, true, 32); 53 | sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_TX); 54 | 55 | pio_sm_init(pio, sm, offset, &sm_config); 56 | 57 | uint pin_mask = (1u << data_pin) | (3u << clock_pin_base); 58 | pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask); 59 | pio_sm_set_pins(pio, sm, 0); // clear pins 60 | 61 | pio_sm_exec(pio, sm, pio_encode_jmp(offset + audio_i2s_offset_entry_point)); 62 | } 63 | 64 | %} 65 | -------------------------------------------------------------------------------- /harmony/Sources/PIOPrograms/WS2812.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | .pio_version 0 // only requires PIO version 0 7 | 8 | .program ws2812 9 | .side_set 1 10 | 11 | ; The following constants are selected for broad compatibility with WS2812, 12 | ; WS2812B, and SK6812 LEDs. Other constants may support higher bandwidths for 13 | ; specific LEDs, such as (7,10,8) for WS2812B LEDs. 14 | 15 | .define public T1 3 16 | .define public T2 3 17 | .define public T3 4 18 | 19 | .wrap_target 20 | bitloop: 21 | out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls 22 | jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse 23 | do_one: 24 | jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse 25 | do_zero: 26 | nop side 0 [T2 - 1] ; Or drive low, for a short pulse 27 | .wrap 28 | 29 | % c-sdk { 30 | #include "hardware/clocks.h" 31 | 32 | static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) { 33 | 34 | pio_gpio_init(pio, pin); 35 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); 36 | 37 | pio_sm_config c = ws2812_program_get_default_config(offset); 38 | sm_config_set_sideset_pins(&c, pin); 39 | sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24); 40 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 41 | 42 | int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3; 43 | float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit); 44 | sm_config_set_clkdiv(&c, div); 45 | 46 | pio_sm_init(pio, sm, offset, &c); 47 | pio_sm_set_enabled(pio, sm, true); 48 | } 49 | %} 50 | -------------------------------------------------------------------------------- /harmony/assets/hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftlang/swift-embedded-examples/622fd0a41dc883d40c1d101a73f678b1fe31b0d5/harmony/assets/hero.jpg -------------------------------------------------------------------------------- /harmony/include/lwipopts.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org 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 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #ifndef _LWIPOPTS_H 14 | #define _LWIPOPTS_H 15 | 16 | #define NO_SYS 1 17 | #define LWIP_SOCKET 0 18 | #define LWIP_NETCONN 0 19 | 20 | // Watch out: Without this, lwip fails to initialize and crashes inside 21 | // memp_init_pool due to misaligned memory access (the fallback is "1"). 22 | #define MEM_ALIGNMENT 4 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /nrfx-blink-sdk/BridgingHeader.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | #include 13 | 14 | #include 15 | #include 16 | 17 | #define LED0_NODE DT_ALIAS(led0) 18 | static struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(LED0_NODE, gpios); 19 | -------------------------------------------------------------------------------- /nrfx-blink-sdk/Main.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | @main 13 | struct Main { 14 | static func main() { 15 | // Note: & in Swift is not the "address of" operator, but on a global variable declared in C 16 | // it will give the correct address of the global. 17 | gpio_pin_configure_dt( 18 | &led0, GPIO_OUTPUT | GPIO_OUTPUT_INIT_HIGH | GPIO_OUTPUT_INIT_LOGICAL) 19 | while true { 20 | gpio_pin_toggle_dt(&led0) 21 | k_msleep(100) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /nrfx-blink-sdk/README.md: -------------------------------------------------------------------------------- 1 | # nrfx-blink-sdk 2 | 3 | This example demonstrates how to integrate with the Zephyr SDK via CMake and how to build a Swift firmware application on top of the SDK and the libraries from it. The example was tested on a nRF52840-DK board, but it should also work on other Zephyr-supported boards. 4 | 5 | 6 | 7 | ## Requirements 8 | 9 | - Download and install [Zephyr](https://docs.zephyrproject.org/latest/), and make sure you are set up for development with it, e.g. by following the [Zephyr Getting Started Guide](https://docs.zephyrproject.org/latest/develop/getting_started/index.html). In particular, you will need: 10 | - CMake, Ninja, and other build tools. 11 | - The West build system. 12 | - A Python virtualenv for Zephyr. 13 | - Zephyr SDK/toolchain. 14 | - Host flash/debug tools for the board you're using. For example, for the nRF52840-DK board you'll need the [nRF Util](https://www.nordicsemi.com/Products/Development-tools/nRF-Util). 15 | 16 | - Before trying to use Swift with the Zephyr SDK, make sure your environment works and can build the provided C/C++ sample projects, in particular: 17 | - Try building and running the "simple/blink" example from Zephyr written in C. 18 | 19 | ## Building 20 | 21 | - Make sure you have a recent nightly Swift toolchain that has Embedded Swift support. 22 | - Build the program in the Zephyr virtualenv, specify the target board type via the `-DBOARD=...` CMake setting: 23 | 24 | ```console 25 | $ cd nrfx-blink-sdk 26 | $ source ~/zephyrproject/.venv/bin/activate 27 | (.venv) cmake -B build -G Ninja -DBOARD=nrf52840dk/nrf52840 -DUSE_CCACHE=0 . 28 | (.venv) cmake --build build 29 | ``` 30 | 31 | ## Running 32 | 33 | - Connect the nRF52840-DK board over a USB cable to your Mac using the J-Link connector on the board. 34 | - Use the `nrfutil device` command to upload the firmware and to run it: 35 | 36 | ```console 37 | (.venv) nrfutil device program --firmware build/zephyr/zephyr.hex 38 | (.venv) nrfutil device fw-verify --firmware build/zephyr/zephyr.hex 39 | (.venv) nrfutil device reset 40 | ``` 41 | 42 | - The green LED should now be blinking in a pattern. 43 | -------------------------------------------------------------------------------- /nrfx-blink-sdk/Stubs.c: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | #include 13 | #include 14 | 15 | void *aligned_alloc(size_t alignment, size_t size); 16 | 17 | // Embedded Swift currently requires posix_memalign, but the C libraries in the 18 | // Zephyr SDK do not provide it. Let's implement it and forward the calls to 19 | // aligned_alloc(3). 20 | int 21 | posix_memalign(void **memptr, size_t alignment, size_t size) 22 | { 23 | void *p = aligned_alloc(alignment, size); 24 | if (p) { 25 | *memptr = p; 26 | return 0; 27 | } 28 | 29 | return errno; 30 | } 31 | -------------------------------------------------------------------------------- /nrfx-blink-sdk/prj.conf: -------------------------------------------------------------------------------- 1 | CONFIG_GPIO=y 2 | CONFIG_NEWLIB_LIBC=y 3 | -------------------------------------------------------------------------------- /nrfx-blink-sdk/west.yml: -------------------------------------------------------------------------------- 1 | manifest: 2 | remotes: 3 | - name: zephyrproject-rtos 4 | url-base: https://github.com/zephyrproject-rtos 5 | 6 | projects: 7 | - name: zephyr 8 | remote: zephyrproject-rtos 9 | revision: v4.1.0 10 | import: 11 | name-allowlist: 12 | - cmsis # required by the ARM port 13 | - hal_nordic # required by the custom_plank board (Nordic based) 14 | -------------------------------------------------------------------------------- /nuttx-riscv-blink/README.md: -------------------------------------------------------------------------------- 1 | # Swift 6 on NuttX RTOS using CMake 2 | 3 | ## Description 4 | 5 | Run blink rv32-blink_leds (QEMU) example on NuttX RTOS. 6 | 7 | > [!NOTE] 8 | > CMake is adapted to build NuttX and NuttX-apps (Makefiles) with Swift 6. 9 | 10 | ## Requirements 11 | 12 | - [NuttX](https://github.com/apache/nuttx) & [NuttX-apps](https://github.com/apache/nuttx-apps) 13 | - [kconfig-frontends](https://bitbucket.org/nuttx/tools) 14 | - [CMake](https://cmake.org/download/) 15 | - [QEMU](https://www.qemu.org/) 16 | - [Swift 6](https://swift.org/download/) - Swift 6.1 or greater 17 | - [RISC-V GNU Toolchain](https://github.com/riscv-collab/riscv-gnu-toolchain/releases) 18 | 19 | ## How to build 20 | 21 | ```bash 22 | # list all supported boards 23 | cmake -B build -DLIST_ALL_BOARDS=ON | less 24 | # build configuration 25 | cmake -B build -GNinja -DBOARD_CONFIG=rv-virt:leds_swift -DENABLE_NUTTX_TRACE=[ON|OFF] 26 | # build 27 | cmake --build build 28 | # clean 29 | cmake --build build -t distclean 30 | # export NuttX as library 31 | cmake --build build -t nuttx-libs 32 | ``` 33 | 34 | - **Output** 35 | ```bash 36 | qemu-system-riscv32 \ 37 | -semihosting \ 38 | -M virt,aclint=on \ 39 | -cpu rv32 -smp 8 \ 40 | -bios none \ 41 | -kernel build/nuttx.elf -nographic 42 | NuttShell (NSH) NuttX-12.7.0 43 | nsh> leds_swift 44 | leds_main: led_daemon started 45 | 46 | led_daemon (pid# 4): Running 47 | led_daemon: Opening /dev/userleds 48 | led_daemon: Supported LEDs 0x7 49 | led_daemon: LED set 0x1 50 | board_userled: LED 1 set to 1 51 | board_userled: LED 2 set to 0 52 | board_userled: LED 3 set to 0 53 | nsh> led_daemon: LED set 0x0 54 | board_userled: LED 1 set to 0 55 | board_userled: LED 2 set to 0 56 | board_userled: LED 3 set to 0 57 | led_daemon: LED set 0x1 58 | board_userled: LED 1 set to 1 59 | board_userled: LED 2 set to 0 60 | board_userled: LED 3 set to 0 61 | led_daemon: LED set 0x0 62 | # [...] see output in QEMU 63 | ``` 64 | 65 | Quit from QEMU: `Ctrl-a x` 66 | 67 | ## References 68 | 69 | - [Nuttx - Compiling with CMake](https://nuttx.apache.org/docs/latest/quickstart/compiling_cmake.html) 70 | - [NuttX - C++ Example using CMake](https://nuttx.apache.org/docs/latest/guides/cpp_cmake.html) 71 | - [NuttX - leds_rust](https://lupyuen.github.io/articles/rust6) 72 | -------------------------------------------------------------------------------- /nuttx-riscv-blink/defconfig: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated: PLEASE DO NOT EDIT IT. 3 | # 4 | # You can use "make menuconfig" to make any modifications to the installed .config file. 5 | # You can then do "make savedefconfig" to generate a new defconfig file that includes your 6 | # modifications. 7 | # 8 | # CONFIG_DISABLE_OS_API is not set 9 | # CONFIG_NSH_DISABLE_LOSMART is not set 10 | CONFIG_16550_ADDRWIDTH=0 11 | CONFIG_16550_UART0=y 12 | CONFIG_16550_UART0_BASE=0x10000000 13 | CONFIG_16550_UART0_CLOCK=3686400 14 | CONFIG_16550_UART0_IRQ=37 15 | CONFIG_16550_UART0_SERIAL_CONSOLE=y 16 | CONFIG_16550_UART=y 17 | CONFIG_ARCH="risc-v" 18 | CONFIG_ARCH_BOARD="rv-virt" 19 | CONFIG_ARCH_BOARD_QEMU_RV_VIRT=y 20 | CONFIG_ARCH_CHIP="qemu-rv" 21 | # CONFIG_ARCH_CHIP_QEMU_RV64=y 22 | CONFIG_ARCH_CHIP_QEMU_RV=y 23 | CONFIG_ARCH_CHIP_QEMU_RV_ISA_A=y 24 | CONFIG_ARCH_CHIP_QEMU_RV_ISA_C=y 25 | CONFIG_ARCH_CHIP_QEMU_RV_ISA_M=y 26 | CONFIG_ARCH_INTERRUPTSTACK=2048 27 | CONFIG_ARCH_RISCV=y 28 | CONFIG_ARCH_STACKDUMP=y 29 | CONFIG_BCH=y 30 | CONFIG_BOARDCTL_POWEROFF=y 31 | CONFIG_BOARD_LATE_INITIALIZE=y 32 | CONFIG_BOARD_LOOPSPERMSEC=6366 33 | CONFIG_BUILTIN=y 34 | CONFIG_DEBUG_FEATURES=y 35 | CONFIG_DEBUG_FULLOPT=y 36 | CONFIG_DEBUG_SYMBOLS=y 37 | CONFIG_DEVICE_TREE=y 38 | CONFIG_DEV_ZERO=y 39 | CONFIG_ELF=y 40 | # CONFIG_EXAMPLES_HELLO=y 41 | CONFIG_EXAMPLES_LEDS=y 42 | CONFIG_EXAMPLES_LEDS_SWIFT=y 43 | CONFIG_FS_HOSTFS=y 44 | CONFIG_FS_PROCFS=y 45 | CONFIG_IDLETHREAD_STACKSIZE=2048 46 | CONFIG_INIT_ENTRYPOINT="nsh_main" 47 | CONFIG_INIT_STACKSIZE=3072 48 | CONFIG_LIBC_ENVPATH=y 49 | CONFIG_LIBC_EXECFUNCS=y 50 | CONFIG_LIBC_PERROR_STDOUT=y 51 | CONFIG_LIBC_STRERROR=y 52 | CONFIG_LIBM=y 53 | CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6 54 | CONFIG_NSH_ARCHINIT=y 55 | CONFIG_NSH_BUILTIN_APPS=y 56 | CONFIG_NSH_FILEIOSIZE=512 57 | CONFIG_NSH_READLINE=y 58 | CONFIG_PATH_INITIAL="/system/bin" 59 | CONFIG_RAM_SIZE=33554432 60 | CONFIG_RAM_START=0x80000000 61 | CONFIG_READLINE_CMD_HISTORY=y 62 | CONFIG_RISCV_SEMIHOSTING_HOSTFS=y 63 | CONFIG_RR_INTERVAL=200 64 | CONFIG_SCHED_WAITPID=y 65 | CONFIG_SERIAL_UART_ARCH_MMIO=y 66 | CONFIG_STACK_COLORATION=y 67 | CONFIG_START_MONTH=12 68 | CONFIG_START_YEAR=2021 69 | CONFIG_SYMTAB_ORDEREDBYNAME=y 70 | CONFIG_SYSTEM_NSH=y 71 | CONFIG_SYSTEM_NSH_STACKSIZE=3072 72 | CONFIG_TESTING_GETPRIME=y 73 | CONFIG_TESTING_OSTEST=y 74 | CONFIG_USEC_PER_TICK=1000 75 | CONFIG_USERLED=y 76 | CONFIG_USERLED_LOWER=y 77 | -------------------------------------------------------------------------------- /nuttx-riscv-blink/leds_swift/Kconfig: -------------------------------------------------------------------------------- 1 | # 2 | # For a description of the syntax of this configuration file, 3 | # see the file kconfig-language.txt in the NuttX tools repository. 4 | # 5 | 6 | config EXAMPLES_LEDS_SWIFT 7 | tristate "\"LEDs Swift\" example" 8 | default y 9 | depends on USERLED 10 | ---help--- 11 | Enable the \"LEDs Swift\" example 12 | 13 | if EXAMPLES_LEDS_SWIFT 14 | 15 | config EXAMPLES_LEDS_SWIFT_PROGNAME 16 | string "Program name" 17 | default "leds_swift" 18 | ---help--- 19 | This is the name of the program that will be used when the NSH ELF 20 | program is installed. 21 | 22 | config EXAMPLES_LEDS_SWIFT_PRIORITY 23 | int "LEDs Swift task priority" 24 | default 100 25 | 26 | config EXAMPLES_LEDS_SWIFT_STACKSIZE 27 | int "LEDs Swift stack size" 28 | default DEFAULT_TASK_STACKSIZE 29 | 30 | endif 31 | -------------------------------------------------------------------------------- /nuttx-riscv-blink/leds_swift/Make.defs: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # apps/examples/leds_swift/Make.defs 3 | # 4 | # Copyright (c) 2024 Apple Inc. and the Swift project authors. 5 | # Licensed to the Apache Software Foundation (ASF) under one or more 6 | # contributor license agreements. See the NOTICE file distributed with 7 | # this work for additional information regarding copyright ownership. The 8 | # ASF licenses this file to you under the Apache License, Version 2.0 (the 9 | # "License"); you may not use this file except in compliance with the 10 | # License. You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 16 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 17 | # License for the specific language governing permissions and limitations 18 | # under the License. 19 | # 20 | ############################################################################ 21 | 22 | ifneq ($(CONFIG_EXAMPLES_LEDS_SWIFT),) 23 | CONFIGURED_APPS += $(APPDIR)/examples/leds_swift 24 | endif 25 | -------------------------------------------------------------------------------- /nuttx-riscv-blink/leds_swift/Makefile: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # apps/examples/leds_swift/Makefile 3 | # 4 | # Copyright (c) 2024 Apple Inc. and the Swift project authors. 5 | # Licensed to the Apache Software Foundation (ASF) under one or more 6 | # contributor license agreements. See the NOTICE file distributed with 7 | # this work for additional information regarding copyright ownership. The 8 | # ASF licenses this file to you under the Apache License, Version 2.0 (the 9 | # "License"); you may not use this file except in compliance with the 10 | # License. You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 16 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 17 | # License for the specific language governing permissions and limitations 18 | # under the License. 19 | # 20 | ############################################################################ 21 | 22 | include $(APPDIR)/Make.defs 23 | 24 | # Blink in Swift Embedded Example 25 | 26 | MAINSRC = $(wildcard *.swift) $(wildcard *.h) 27 | 28 | # leds_swift built-in application info 29 | 30 | SWIFTFLAGS += -import-bridging-header BridgingHeader.h -I$(TOPDIR)/include 31 | SWIFTFLAGS += -Xfrontend -function-sections -Xfrontend -disable-stack-protector 32 | SWIFTFLAGS += -Xfrontend -enable-single-module-llvm-emission 33 | PROGNAME = $(CONFIG_EXAMPLES_LEDS_SWIFT_PROGNAME) 34 | PRIORITY = $(CONFIG_EXAMPLES_LEDS_SWIFT_PRIORITY) 35 | STACKSIZE = $(CONFIG_EXAMPLES_LEDS_SWIFT_STACKSIZE) 36 | MODULE = $(CONFIG_EXAMPLES_LEDS_SWIFT) 37 | 38 | include $(APPDIR)/Application.mk 39 | -------------------------------------------------------------------------------- /nuttx-riscv-blink/leds_swift/leds_swift.swift: -------------------------------------------------------------------------------- 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 | @_cdecl("leds_swift_main") 13 | public func cMain( 14 | _ argc: Int32, _ argv: UnsafeMutablePointer?> 15 | ) -> Int32 { 16 | let ret = task_create( 17 | "led_daemon", 18 | LEDS_PRIORITY, 19 | LEDS_STACKSIZE, 20 | led_daemon, 21 | nil) 22 | 23 | if ret < 0 { 24 | print("leds_main: ERROR: Failed to start led_daemon") 25 | return ret 26 | } 27 | 28 | print("leds_main: led_daemon started") 29 | return 0 30 | } 31 | -------------------------------------------------------------------------------- /rpi-4b-blink/Makefile: -------------------------------------------------------------------------------- 1 | ##===----------------------------------------------------------------------===## 2 | ## 3 | ## This source file is part of the Swift open source project 4 | ## 5 | ## Copyright (c) 2025 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 | # Paths 13 | REPOROOT := $(shell git rev-parse --show-toplevel) 14 | TOOLSROOT := $(REPOROOT)/Tools 15 | TOOLSET := $(TOOLSROOT)/Toolsets/rpi-5-elf.json 16 | LLVM_OBJCOPY := llvm-objcopy 17 | SWIFT_BUILD := swift build 18 | 19 | # Flags 20 | ARCH := aarch64 21 | TARGET := $(ARCH)-none-none-elf 22 | SWIFT_BUILD_ARGS := \ 23 | --configuration release \ 24 | --triple $(TARGET) \ 25 | --toolset $(TOOLSET) \ 26 | --disable-local-rpath 27 | BUILDROOT := $(shell $(SWIFT_BUILD) $(SWIFT_BUILD_ARGS) --show-bin-path) 28 | 29 | .PHONY: build 30 | build: 31 | @echo "building..." 32 | $(SWIFT_BUILD) \ 33 | $(SWIFT_BUILD_ARGS) \ 34 | --verbose 35 | 36 | @echo "extracting binary..." 37 | $(LLVM_OBJCOPY) \ 38 | -O binary \ 39 | "$(BUILDROOT)/Application" \ 40 | "$(BUILDROOT)/Application.bin" \ 41 | 42 | 43 | .PHONY: clean 44 | clean: 45 | @echo "cleaning..." 46 | @swift package clean 47 | @rm -rf .build 48 | -------------------------------------------------------------------------------- /rpi-4b-blink/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "d0330fea11b0fbdacbddd4f13debd9a4fe72ed5b628ad4959a93972f3572bf1f", 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" : "main", 19 | "revision" : "5232c5129a8c70beafc3d6acfbae2716c1b6822a" 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 | } 34 | -------------------------------------------------------------------------------- /rpi-4b-blink/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "rpi-4b-blink", 7 | products: [ 8 | .executable(name: "Application", targets: ["Application"]) 9 | ], 10 | dependencies: [ 11 | .package(url: "https://github.com/apple/swift-mmio.git", branch: "main") 12 | ], 13 | targets: [ 14 | .executableTarget( 15 | name: "Application", 16 | dependencies: [ 17 | .product(name: "MMIO", package: "swift-mmio"), 18 | "Support", 19 | ]), 20 | .target(name: "Support"), 21 | ]) 22 | -------------------------------------------------------------------------------- /rpi-4b-blink/README.md: -------------------------------------------------------------------------------- 1 | # rpi-4b-blink 2 | 3 | 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 | 10 | ## How to build and run this example: 11 | 12 | - Make sure you have a recent nightly Swift toolchain that has Embedded Swift support. 13 | - Build the program, then copy the kernel image to the SD card. 14 | ``` console 15 | $ cd rpi-4b-blink 16 | $ make 17 | $ cp .build/release/Application.bin /Volumes/bootfs/kernel8.img 18 | ``` 19 | - If your original OS is not 64-bit, make sure to set `arm_64bit=1` in `config.txt`. 20 | - Place the SD card in your Raspberry Pi 4B, and connect it to power. 21 | - After the boot sequence, the green (ACT) led will start blinking in a regular pattern. 22 | -------------------------------------------------------------------------------- /rpi-4b-blink/Sources/Application/Application.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2025 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 | import Support 14 | 15 | @Register(bitWidth: 32) 16 | struct GPSET1 { 17 | @ReadWrite(bits: 10..<11, as: Bool.self) 18 | var set: SET 19 | } 20 | 21 | @Register(bitWidth: 32) 22 | struct GPCLR1 { 23 | @ReadWrite(bits: 10..<11, as: Bool.self) 24 | var clear: CLEAR 25 | } 26 | 27 | @Register(bitWidth: 32) 28 | struct GPFSEL4 { 29 | @ReadWrite(bits: 6..<7, as: Bool.self) 30 | var fsel42b1: FSEL42b1 31 | @ReadWrite(bits: 7..<8, as: Bool.self) 32 | var fsel42b2: FSEL42b2 33 | @ReadWrite(bits: 8..<9, as: Bool.self) 34 | var fsel42b3: FSEL42b3 35 | } 36 | 37 | @RegisterBlock 38 | struct GPIO { 39 | @RegisterBlock(offset: 0x200020) 40 | var gpset1: Register 41 | @RegisterBlock(offset: 0x20002c) 42 | var gpclr1: Register 43 | @RegisterBlock(offset: 0x200010) 44 | var gpfsel4: Register 45 | } 46 | 47 | let gpio = GPIO(unsafeAddress: 0xFE00_0000) 48 | 49 | func setLedOutput() { 50 | gpio.gpfsel4.modify { 51 | // setFunction Select 42 (fsel42) to 001 52 | $0.fsel42b1 = true 53 | $0.fsel42b2 = false 54 | $0.fsel42b3 = false 55 | } 56 | } 57 | 58 | func ledOn() { 59 | gpio.gpset1.modify { 60 | $0.set = true 61 | } 62 | } 63 | 64 | func ledOff() { 65 | gpio.gpclr1.modify { 66 | $0.clear = true 67 | } 68 | } 69 | 70 | func delay() { 71 | for _ in 1..<1_000_000 { nop() } 72 | } 73 | 74 | @main 75 | struct Application { 76 | static func main() { 77 | setLedOutput() 78 | while true { 79 | ledOn() 80 | delay() 81 | ledOff() 82 | delay() 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /rpi-4b-blink/Sources/Support/boot.S: -------------------------------------------------------------------------------- 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 42 | -------------------------------------------------------------------------------- /rpi-4b-blink/Sources/Support/include/Support.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2025 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 | #pragma once 13 | 14 | static inline __attribute((always_inline)) void nop() { 15 | asm volatile("nop"); 16 | } 17 | -------------------------------------------------------------------------------- /rpi-4b-blink/Sources/Support/linkerscript.ld: -------------------------------------------------------------------------------- 1 | SECTIONS 2 | { 3 | /* Kernel load address for AArch64 */ 4 | . = 0x80000; 5 | .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } 6 | .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } 7 | PROVIDE(_data = .); 8 | .data : { *(.data .data.* .gnu.linkonce.d*) } 9 | .bss (NOLOAD) : { 10 | . = ALIGN(16); 11 | __bss_start = .; 12 | *(.bss .bss.*) 13 | *(COMMON) 14 | __bss_end = .; 15 | } 16 | _end = .; 17 | 18 | /DISCARD/ : { 19 | *(.comment) 20 | *(.gnu*) 21 | *(.note*) 22 | *(.eh_frame*) 23 | *(.swift_modhash) 24 | } 25 | } 26 | 27 | __bss_size = (__bss_end - __bss_start) >> 3; 28 | -------------------------------------------------------------------------------- /rpi-4b-blink/assets/hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftlang/swift-embedded-examples/622fd0a41dc883d40c1d101a73f678b1fe31b0d5/rpi-4b-blink/assets/hero.jpg -------------------------------------------------------------------------------- /rpi-5-blink/Makefile: -------------------------------------------------------------------------------- 1 | ##===----------------------------------------------------------------------===## 2 | ## 3 | ## This source file is part of the Swift open source project 4 | ## 5 | ## Copyright (c) 2025 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 | # Paths 13 | REPOROOT := $(shell git rev-parse --show-toplevel) 14 | TOOLSROOT := $(REPOROOT)/Tools 15 | TOOLSET := $(TOOLSROOT)/Toolsets/rpi-5-elf.json 16 | LLVM_OBJCOPY := llvm-objcopy 17 | SWIFT_BUILD := swift build 18 | 19 | # Flags 20 | ARCH := aarch64 21 | TARGET := $(ARCH)-none-none-elf 22 | SWIFT_BUILD_ARGS := \ 23 | --configuration release \ 24 | --triple $(TARGET) \ 25 | --toolset $(TOOLSET) \ 26 | --disable-local-rpath 27 | BUILDROOT := $(shell $(SWIFT_BUILD) $(SWIFT_BUILD_ARGS) --show-bin-path) 28 | 29 | .PHONY: build 30 | build: 31 | @echo "building..." 32 | $(SWIFT_BUILD) \ 33 | $(SWIFT_BUILD_ARGS) \ 34 | --verbose 35 | 36 | @echo "extracting binary..." 37 | $(LLVM_OBJCOPY) \ 38 | -O binary \ 39 | "$(BUILDROOT)/Application" \ 40 | "$(BUILDROOT)/Application.bin" 41 | 42 | 43 | .PHONY: clean 44 | clean: 45 | @echo "cleaning..." 46 | @swift package clean 47 | @rm -rf .build 48 | -------------------------------------------------------------------------------- /rpi-5-blink/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "5bb00fe2bf59115297c70799769d8080cf6ce836d32f4a41d42e7c420afddec6", 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" : "main", 19 | "revision" : "5232c5129a8c70beafc3d6acfbae2716c1b6822a" 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 | } 34 | -------------------------------------------------------------------------------- /rpi-5-blink/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "rpi-5-blink", 7 | products: [ 8 | .executable(name: "Application", targets: ["Application"]) 9 | ], 10 | dependencies: [ 11 | .package(url: "https://github.com/apple/swift-mmio.git", branch: "main") 12 | ], 13 | targets: [ 14 | .executableTarget( 15 | name: "Application", 16 | dependencies: [ 17 | .product(name: "MMIO", package: "swift-mmio"), 18 | "Support", 19 | ]), 20 | .target(name: "Support"), 21 | ]) 22 | -------------------------------------------------------------------------------- /rpi-5-blink/README.md: -------------------------------------------------------------------------------- 1 | # rpi-5-blink 2 | 3 | 4 | 5 | ## Requirements 6 | 7 | - A Raspberry Pi 5 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 `kernel_2712.img` if you need the Linux install later, since we will change these files. 9 | 10 | ## How to build and run this example: 11 | 12 | - Make sure you have a recent nightly Swift toolchain that has Embedded Swift support. 13 | - Build the program, then copy the kernel image to the SD card. 14 | ``` console 15 | $ cd rpi-5-blink 16 | $ make 17 | $ cp .build/release/Application.bin /Volumes/bootfs/kernel8.img # Copy kernel image to SD card 18 | $ rm /Volumes/bootfs/kernel_2712.img # Delete this kernel image so our kernel8.img is used 19 | $ # 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. 20 | ``` 21 | - Place the SD card in your Raspberry Pi 5, and connect it to power. 22 | - After the boot sequence, the green (ACT) led will start blinking in a regular pattern. 23 | -------------------------------------------------------------------------------- /rpi-5-blink/Sources/Application/Application.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2025 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 | import Support 14 | 15 | @Register(bitWidth: 32) 16 | struct GIOIODIR { 17 | @ReadWrite(bits: 9..<10, as: Bool.self) 18 | var direction: DIRECTION 19 | } 20 | 21 | @Register(bitWidth: 32) 22 | struct GIODATA { 23 | @ReadWrite(bits: 9..<10, as: Bool.self) 24 | var value: VALUE 25 | } 26 | 27 | @RegisterBlock 28 | struct GPIO { 29 | @RegisterBlock(offset: 0x00008) 30 | var gioiodir: Register 31 | @RegisterBlock(offset: 0x00004) 32 | var giodata: Register 33 | } 34 | 35 | let gpio = GPIO(unsafeAddress: 0x10_7d51_7c00) 36 | 37 | func setLedOutput() { 38 | gpio.gioiodir.modify { 39 | $0.direction = false // 0 is output, 1 is input 40 | } 41 | } 42 | 43 | func ledOn() { 44 | gpio.giodata.modify { 45 | $0.value = true // pin on 46 | } 47 | } 48 | 49 | func ledOff() { 50 | gpio.giodata.modify { 51 | $0.value = false // pin off 52 | } 53 | } 54 | 55 | func delay() { 56 | for _ in 1..<1_000_000 { nop() } 57 | } 58 | 59 | @main 60 | struct Application { 61 | static func main() { 62 | setLedOutput() 63 | while true { 64 | ledOn() 65 | delay() 66 | ledOff() 67 | delay() 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /rpi-5-blink/Sources/Support/boot.S: -------------------------------------------------------------------------------- 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 42 | -------------------------------------------------------------------------------- /rpi-5-blink/Sources/Support/include/Support.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2025 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 | #pragma once 13 | 14 | static inline __attribute((always_inline)) void nop() { 15 | asm volatile("nop"); 16 | } 17 | -------------------------------------------------------------------------------- /rpi-5-blink/Sources/Support/linkerscript.ld: -------------------------------------------------------------------------------- 1 | SECTIONS 2 | { 3 | /* Kernel load address for AArch64 */ 4 | . = 0x80000; 5 | .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } 6 | .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } 7 | PROVIDE(_data = .); 8 | .data : { *(.data .data.* .gnu.linkonce.d*) } 9 | .bss (NOLOAD) : { 10 | . = ALIGN(16); 11 | __bss_start = .; 12 | *(.bss .bss.*) 13 | *(COMMON) 14 | __bss_end = .; 15 | } 16 | _end = .; 17 | 18 | /DISCARD/ : { 19 | *(.comment) 20 | *(.gnu*) 21 | *(.note*) 22 | *(.eh_frame*) 23 | *(.swift_modhash) 24 | } 25 | } 26 | 27 | __bss_size = (__bss_end - __bss_start) >> 3; 28 | -------------------------------------------------------------------------------- /rpi-5-blink/assets/hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftlang/swift-embedded-examples/622fd0a41dc883d40c1d101a73f678b1fe31b0d5/rpi-5-blink/assets/hero.jpg -------------------------------------------------------------------------------- /rpi-pico-blink-sdk/BridgingHeader.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | #pragma once 13 | 14 | #include "pico/stdlib.h" 15 | -------------------------------------------------------------------------------- /rpi-pico-blink-sdk/Main.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | @main 13 | struct Main { 14 | static func main() { 15 | let led = UInt32(PICO_DEFAULT_LED_PIN) 16 | gpio_init(led) 17 | gpio_set_dir(led, true) 18 | while true { 19 | gpio_put(led, true) 20 | sleep_ms(250) 21 | gpio_put(led, false) 22 | sleep_ms(250) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /rpi-pico-blink/Makefile: -------------------------------------------------------------------------------- 1 | ##===----------------------------------------------------------------------===## 2 | ## 3 | ## This source file is part of the Swift open source project 4 | ## 5 | ## Copyright (c) 2025 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 | # Paths 13 | REPOROOT := $(shell git rev-parse --show-toplevel) 14 | TOOLSROOT := $(REPOROOT)/Tools 15 | TOOLSET := $(TOOLSROOT)/Toolsets/pico.json 16 | MACHO2UF2 := $(TOOLSROOT)/macho2uf2.py 17 | SWIFT_BUILD := swift build 18 | 19 | # Flags 20 | PICO_FAMILY := rp2040 21 | ARCH := armv6m 22 | TARGET := $(ARCH)-apple-none-macho 23 | SWIFT_BUILD_ARGS := \ 24 | --configuration release \ 25 | --triple $(TARGET) \ 26 | --toolset $(TOOLSET) \ 27 | --disable-local-rpath 28 | BUILDROOT := $(shell $(SWIFT_BUILD) $(SWIFT_BUILD_ARGS) --show-bin-path) 29 | 30 | .PHONY: build 31 | build: 32 | @echo "building..." 33 | $(SWIFT_BUILD) \ 34 | $(SWIFT_BUILD_ARGS) \ 35 | -Xlinker -map -Xlinker $(BUILDROOT)/Application.mangled.map \ 36 | --verbose 37 | 38 | @echo "demangling linker map..." 39 | cat $(BUILDROOT)/Application.mangled.map \ 40 | | c++filt | swift demangle > $(BUILDROOT)/Application.map 41 | 42 | @echo "disassembling..." 43 | otool \ 44 | -arch $(ARCH) -v -V -d -t \ 45 | $(BUILDROOT)/Application \ 46 | | c++filt | swift demangle > $(BUILDROOT)/Application.disassembly 47 | 48 | @echo "extracting binary..." 49 | $(MACHO2UF2) \ 50 | --pico-family "$(PICO_FAMILY)" \ 51 | "$(BUILDROOT)/Application" \ 52 | "$(BUILDROOT)/Application.uf2" \ 53 | --base-address 0x20000000 \ 54 | --segments '__TEXT,__DATA,__VECTORS,__RESET' 55 | 56 | 57 | .PHONY: clean 58 | clean: 59 | @echo "cleaning..." 60 | @swift package clean 61 | @rm -rf .build 62 | -------------------------------------------------------------------------------- /rpi-pico-blink/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "RP2040", 7 | products: [ 8 | .executable(name: "Application", targets: ["Application"]) 9 | ], 10 | targets: [ 11 | .executableTarget(name: "Application", dependencies: ["RP2040"]), 12 | .target(name: "Support"), 13 | .target(name: "RP2040", dependencies: ["Support"]), 14 | ], 15 | swiftLanguageModes: [.v5]) 16 | -------------------------------------------------------------------------------- /rpi-pico-blink/README.md: -------------------------------------------------------------------------------- 1 | # rpi-pico-blink 2 | 3 | 4 | 5 | ## Requirements 6 | 7 | - A Raspberry Pi Pico (non-W) board. If you have a Pico W instead, this sample code will require modifications. 8 | 9 | ## How to build and run this example: 10 | 11 | - Connect the Pico board via a USB cable to your Mac, and make sure it's in the USB Mass Storage firmware upload mode (either hold the BOOTSEL button while plugging the board, or make sure your Flash memory doesn't contain any valid firmware). 12 | - Make sure you have a recent nightly Swift toolchain that has Embedded Swift support. 13 | - Build and copy the program in the UF2 format to the Mass Storage device to trigger flashing the program into memory (after which the device will reboot and run the firmware): 14 | 15 | ``` console 16 | $ cd rpi-pico-blink 17 | $ make 18 | $ cp .build/Application.uf2 /Volumes/RP2040 19 | ``` 20 | 21 | - The green LED should now be blinking in a pattern. 22 | -------------------------------------------------------------------------------- /rpi-pico-blink/Sources/RP2040/HAL/RP2040.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | public struct RP2040 { 13 | let hardware = RP2040Hardware() 14 | 15 | public init() {} 16 | } 17 | -------------------------------------------------------------------------------- /rpi-pico-blink/Sources/RP2040/HAL/Time.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 Support 13 | 14 | extension RP2040 { 15 | var now: UInt64 { 16 | var high = hardware.timer.awh 17 | var low: UInt32 18 | repeat { 19 | low = hardware.timer.awl 20 | let nextHigh = hardware.timer.awh 21 | if high == nextHigh { 22 | break 23 | } 24 | high = nextHigh 25 | } while true 26 | return UInt64(high) << 32 | UInt64(low) 27 | } 28 | 29 | public func sleep(forMicroseconds microseconds: UInt64) { 30 | let start = now 31 | let deadline = start + microseconds 32 | 33 | let highDeadline = UInt32(deadline >> 32) 34 | let lowDeadline = UInt32(deadline & UInt64(UInt32.max)) 35 | var high = hardware.timer.awh 36 | 37 | while high < highDeadline { 38 | high = hardware.timer.awh 39 | } 40 | 41 | while high == highDeadline && hardware.timer.awl < lowDeadline { 42 | high = hardware.timer.awh 43 | } 44 | } 45 | 46 | public func sleep(forMilliseconds milliseconds: Int) { 47 | for _ in 0.. 0 { 56 | sleep(forMicroseconds: UInt64(microseconds)) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /rpi-pico-blink/Sources/RP2040/Hardware/PPB.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | // swift-format-ignore-file 13 | 14 | extension RP2040Hardware { 15 | public var ppb: PPB { 16 | PPB(unsafeAddress: 0xe0000000) 17 | } 18 | 19 | public struct PPB { 20 | let unsafeAddress: UInt 21 | 22 | init(unsafeAddress: UInt) { 23 | self.unsafeAddress = unsafeAddress 24 | } 25 | 26 | public var cpuID: CPUID { 27 | CPUID(unsafeAddress: unsafeAddress + 0xed00) 28 | } 29 | 30 | public struct CPUID { 31 | let unsafeAddress: UInt 32 | 33 | init(unsafeAddress: UInt) { 34 | self.unsafeAddress = unsafeAddress 35 | } 36 | } 37 | 38 | public var icsr: ICSR { 39 | ICSR(unsafeAddress: unsafeAddress + 0xed04) 40 | } 41 | 42 | public struct ICSR { 43 | let unsafeAddress: UInt 44 | 45 | init(unsafeAddress: UInt) { 46 | self.unsafeAddress = unsafeAddress 47 | } 48 | } 49 | 50 | public var vtor: VTOR { 51 | VTOR(unsafeAddress: unsafeAddress + 0xed08) 52 | } 53 | 54 | public struct VTOR { 55 | let unsafeAddress: UInt 56 | 57 | init(unsafeAddress: UInt) { 58 | self.unsafeAddress = unsafeAddress 59 | } 60 | 61 | public var tableOffset: UInt32 { 62 | get { 63 | RP2040Hardware.read(UInt32.self, from: unsafeAddress) 64 | } 65 | nonmutating set { 66 | RP2040Hardware.write(newValue & 0xffffff00, to: unsafeAddress) 67 | } 68 | } 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /rpi-pico-blink/Sources/RP2040/Hardware/SIO.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | // swift-format-ignore-file 13 | 14 | extension RP2040Hardware { 15 | public var sio: SIO { 16 | SIO(unsafeAddress: 0xd0000000) 17 | } 18 | 19 | public struct SIO { 20 | let unsafeAddress: UInt 21 | 22 | init(unsafeAddress: UInt) { 23 | self.unsafeAddress = unsafeAddress 24 | } 25 | 26 | public func setOutput(_ bits: UInt32) { 27 | RP2040Hardware.write(bits & 0x3FFFFFFF, to: unsafeAddress, offset: 0x0014) 28 | } 29 | 30 | public func clearOutput(_ bits: UInt32) { 31 | RP2040Hardware.write(bits & 0x3FFFFFFF, to: unsafeAddress, offset: 0x0018) 32 | } 33 | 34 | public func enableOutput(_ bits: UInt32) { 35 | RP2040Hardware.write(bits & 0x3FFFFFFF, to: unsafeAddress, offset: 0x0024) 36 | } 37 | 38 | public func disableOutput(_ bits: UInt32) { 39 | RP2040Hardware.write(bits & 0x3FFFFFFF, to: unsafeAddress, offset: 0x0028) 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /rpi-pico-blink/Sources/RP2040/Hardware/Timer.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | // swift-format-ignore-file 13 | 14 | extension RP2040Hardware { 15 | public var timer: Timer { 16 | Timer(unsafeAddress: 0x40054000) 17 | } 18 | 19 | public struct Timer { 20 | let unsafeAddress: UInt 21 | 22 | init(unsafeAddress: UInt) { 23 | self.unsafeAddress = unsafeAddress 24 | } 25 | 26 | public var awh: UInt32 { 27 | RP2040Hardware.read(UInt32.self, from: unsafeAddress, offset: 0x0024) 28 | } 29 | 30 | public var awl: UInt32 { 31 | RP2040Hardware.read(UInt32.self, from: unsafeAddress, offset: 0x0028) 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /rpi-pico-blink/Sources/RP2040/Hardware/Watchdog.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | // swift-format-ignore-file 13 | 14 | extension RP2040Hardware { 15 | public var watchdog: Watchdog { 16 | Watchdog(unsafeAddress: 0x40058000) 17 | } 18 | 19 | public struct Watchdog { 20 | let unsafeAddress: UInt 21 | 22 | init(unsafeAddress: UInt) { 23 | self.unsafeAddress = unsafeAddress 24 | } 25 | 26 | public var tick: Tick { 27 | Tick(unsafeAddress: unsafeAddress + 0x002c) 28 | } 29 | 30 | public struct Tick { 31 | let unsafeAddress: UInt 32 | 33 | init(unsafeAddress: UInt) { 34 | self.unsafeAddress = unsafeAddress 35 | } 36 | 37 | public struct Projection { 38 | public var count: UInt16 39 | public var running: Bool 40 | public var enabled: Bool 41 | public var cycles: UInt16 42 | 43 | init(decoding value: UInt32) { 44 | count = UInt16((value & 0xFF800) >> 11) 45 | running = value & (1 << 10) != 0 46 | enabled = value & (1 << 9) != 0 47 | cycles = UInt16((value & 0x1FF) >> 0) 48 | } 49 | 50 | var encoded: UInt32 { 51 | UInt32(count & 0x1FF) << 11 | 52 | (running ? 1 << 10 : 0x0) | 53 | (enabled ? 1 << 9 : 0x0) | 54 | UInt32(cycles & 0x1FF) << 0 55 | } 56 | } 57 | 58 | public func modify(_ apply: (inout Projection) -> T) -> T { 59 | let value = RP2040Hardware.read(UInt32.self, from: unsafeAddress) 60 | var projection = Projection(decoding: value) 61 | let result = apply(&projection) 62 | RP2040Hardware.write(projection.encoded, to: unsafeAddress) 63 | return result 64 | } 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /rpi-pico-blink/Sources/Support/Support.c: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | #include 13 | #include 14 | #include 15 | 16 | #include "Support.h" 17 | 18 | extern int main(int argc, char *argv[]); 19 | 20 | void reset(void) { 21 | int exit_code = main(0, NULL); 22 | __builtin_trap(); 23 | } 24 | 25 | void interrupt(void) { 26 | while (1) {} 27 | } 28 | 29 | __attribute((section("__DATA,stack"), aligned(32))) 30 | char stack[0x800]; 31 | 32 | __attribute((used)) __attribute((section("__VECTORS,vectors"))) 33 | void *vector_table[48] = { 34 | (void *)(&stack[sizeof(stack)]), // initial SP 35 | reset, // Reset 36 | 37 | interrupt, // NMI 38 | interrupt, // HardFault 39 | 40 | 0 // NULL for all the other handlers 41 | }; 42 | -------------------------------------------------------------------------------- /rpi-pico-blink/Sources/Support/crt0.S: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | .syntax unified 13 | .thumb 14 | .section __RESET,reset 15 | .thumb_func 16 | .global _entry_point 17 | 18 | _entry_point: 19 | // Set VTOR (offset 0xed08 in Cortex-M0+ registers) to point to the vector table 20 | ldr r0, =_vector_table 21 | ldr r1, =(0xe0000000 + 0x0000ed08) 22 | str r0, [r1] 23 | 24 | // Load the first two entries of the vector table, (1) r1 = initial SP, (2) r2 = reset function 25 | ldmia r0!, {r1, r2} 26 | 27 | // Set stack pointer 28 | msr msp, r1 29 | 30 | // Transfer to reset function 31 | bx r2 32 | -------------------------------------------------------------------------------- /rpi-pico-blink/Sources/Support/include/Support.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | #pragma once 13 | 14 | #include 15 | 16 | #define VOLATILE_LOAD(type) \ 17 | __attribute__((always_inline)) \ 18 | static type volatile_load_##type(const volatile type * _Nonnull pointer) { return *pointer; } 19 | 20 | VOLATILE_LOAD(uint8_t); 21 | VOLATILE_LOAD(uint16_t); 22 | VOLATILE_LOAD(uint32_t); 23 | VOLATILE_LOAD(uint64_t); 24 | 25 | #define VOLATILE_STORE(type) \ 26 | __attribute__((always_inline)) \ 27 | static void volatile_store_##type(volatile type * _Nonnull pointer, type value) { *pointer = value; } 28 | 29 | VOLATILE_STORE(uint8_t); 30 | VOLATILE_STORE(uint16_t); 31 | VOLATILE_STORE(uint32_t); 32 | VOLATILE_STORE(uint64_t); 33 | 34 | static inline void busyWait(uint32_t cycles) { 35 | asm( 36 | "1: subs %0, #3\n" 37 | "bcs 1b\n" 38 | : "+l" (cycles) : : "memory" 39 | ); 40 | } 41 | 42 | static inline void nop(void) { 43 | asm volatile("nop"); 44 | } 45 | -------------------------------------------------------------------------------- /rpi-pico2-neopixel/.sourcekit-lsp/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "swiftPM": { 3 | "configuration": "release" 4 | } 5 | } -------------------------------------------------------------------------------- /rpi-pico2-neopixel/Makefile: -------------------------------------------------------------------------------- 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 | # Determine file paths 13 | REPOROOT := $(shell git rev-parse --show-toplevel) 14 | TOOLSROOT := $(REPOROOT)/Tools 15 | TOOLSET := $(TOOLSROOT)/Toolsets/pico2.json 16 | MACHO2UF2 := $(TOOLSROOT)/macho2uf2.py 17 | SWIFT_BUILD := swift build 18 | 19 | # Setup tools and build flags 20 | ARCH := armv7em 21 | TARGET := $(ARCH)-apple-none-macho 22 | SWIFT_BUILD_ARGS := \ 23 | --configuration release \ 24 | --triple $(TARGET) \ 25 | --toolset $(TOOLSET) \ 26 | --disable-local-rpath 27 | BUILDROOT := $(shell $(SWIFT_BUILD) $(SWIFT_BUILD_ARGS) --show-bin-path) 28 | 29 | .PHONY: build 30 | build: 31 | @echo "building..." 32 | $(SWIFT_BUILD) \ 33 | $(SWIFT_BUILD_ARGS) \ 34 | -Xlinker -map -Xlinker $(BUILDROOT)/Application.mangled.map \ 35 | --verbose 36 | 37 | @echo "demangling linker map..." 38 | cat $(BUILDROOT)/Application.mangled.map \ 39 | | c++filt | swift demangle > $(BUILDROOT)/Application.map 40 | 41 | @echo "disassembling..." 42 | otool \ 43 | -arch $(ARCH) -v -V -d -t \ 44 | $(BUILDROOT)/Application \ 45 | | c++filt | swift demangle > $(BUILDROOT)/Application.disassembly 46 | 47 | @echo "extracting binary..." 48 | $(MACHO2UF2) \ 49 | $(BUILDROOT)/Application \ 50 | $(BUILDROOT)/Application.uf2 \ 51 | --pico-family rp2350 \ 52 | --base-address 0x20000000 \ 53 | --segments '__TEXT,__DATA,__VECTORS,__RESET' 54 | 55 | .PHONY: clean 56 | clean: 57 | @echo "cleaning..." 58 | @swift package clean 59 | @rm -rf .build 60 | -------------------------------------------------------------------------------- /rpi-pico2-neopixel/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "b2e24bf273e42b2971d4fabcbfa10a408d2817cd829c0ce45e1fc8e2e0aac421", 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" : "main", 19 | "revision" : "5232c5129a8c70beafc3d6acfbae2716c1b6822a" 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 | } 34 | -------------------------------------------------------------------------------- /rpi-pico2-neopixel/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "rpi-pico2-neopixel", 7 | products: [ 8 | .executable(name: "Application", targets: ["Application"]) 9 | ], 10 | dependencies: [ 11 | .package(url: "https://github.com/apple/swift-mmio.git", branch: "main") 12 | ], 13 | targets: [ 14 | .executableTarget( 15 | name: "Application", 16 | dependencies: ["RP2350", "Support"]), 17 | .target( 18 | name: "RP2350", 19 | dependencies: [ 20 | .product(name: "MMIO", package: "swift-mmio") 21 | ], 22 | plugins: [ 23 | .plugin(name: "SVD2SwiftPlugin", package: "swift-mmio") 24 | ]), 25 | .target(name: "Support"), 26 | ], 27 | swiftLanguageModes: [.v5]) 28 | -------------------------------------------------------------------------------- /rpi-pico2-neopixel/README.md: -------------------------------------------------------------------------------- 1 | # rpi-pico2-neopixel 2 | 3 | An example project demonstrating how to drive a Neopixel RGB LED from an RP2350. 4 | 5 | ![A SparkFun Pro Micro - RP2350 with its RGB LED glowing Red](assets/images/example.jpg) 6 | 7 | ## Requirements 8 | 9 | - An RP2350 board, such as the "SparkFun Pro Micro - RP2350". 10 | 11 | ## Configuring 12 | 13 | This example uses the hard coded constant `LED_PIN` in `Application.swift` to select the GPIO pin used to drive the attached Neopixel RGB LED. If you are using the "SparkFun Pro Micro - RP2350" no configuration is necessary, if you are using a different board you will need to adjust this constant to the pin used to drive your LED. 14 | 15 | Example diff: 16 | 17 | ```diff 18 | diff --git a/rpi-pico2-neopixel/Sources/Application/Application.swift b/rpi-pico2-neopixel/Sources/Application/Application.swift 19 | index f6867b5..a2291db 100644 20 | --- a/rpi-pico2-neopixel/Sources/Application/Application.swift 21 | +++ b/rpi-pico2-neopixel/Sources/Application/Application.swift 22 | @@ -11,7 +11,7 @@ 23 | 24 | import RP2350 25 | 26 | -let LED_PIN: UInt32 = 25 27 | +let LED_PIN: UInt32 = 18 28 | 29 | /// Configures GPIO pin as a front-end to PIO0. 30 | func configure_output_pin() { 31 | ``` 32 | 33 | ## How to build and run this example: 34 | 35 | - Connect the Pico2 board via a USB cable to your Mac, and make sure it's in the USB Mass Storage firmware upload mode (either hold the BOOTSEL button while plugging the board, or make sure your Flash memory doesn't contain any valid firmware). 36 | - Make sure you have a recent nightly Swift toolchain that has Embedded Swift support. 37 | - Build and copy the program in the UF2 format to the Mass Storage device to trigger flashing the program into memory (after which the device will reboot and run the firmware): 38 | ``` console 39 | $ cd rpi-pico2-neopixel 40 | $ make 41 | $ cp .build/release/Application.uf2 /Volumes/RP2350 42 | ``` 43 | - The RGB LED should be animating through the color wheel. 44 | -------------------------------------------------------------------------------- /rpi-pico2-neopixel/Sources/Application/HSV8Pixel.swift: -------------------------------------------------------------------------------- 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 | struct HSV8Pixel { 13 | var hue: UInt8 14 | var saturation: UInt8 15 | var value: UInt8 16 | } 17 | -------------------------------------------------------------------------------- /rpi-pico2-neopixel/Sources/Application/WS2812.swift: -------------------------------------------------------------------------------- 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 | enum WS2812 { 13 | // Precompiled PIO program using pioasm 14 | static let pioInstructions: (UInt16, UInt16, UInt16, UInt16, UInt16) = ( 15 | 0xe081, // 0: set pindirs, 1 side 0 16 | // .wrap_target 17 | 0x6321, // 1: out x, 1 side 0 [3] 18 | 0x1224, // 2: jmp !x, 4 side 1 [2] 19 | 0x1201, // 3: jmp 1 side 1 [2] 20 | 0xa242 // 4: nop side 0 [2] 21 | // .wrap 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /rpi-pico2-neopixel/Sources/RP2350/Empty.swift: -------------------------------------------------------------------------------- 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 | // This file is intentionally left empty for SwiftPM to recognize the target as 13 | // a Swift source module. 14 | -------------------------------------------------------------------------------- /rpi-pico2-neopixel/Sources/RP2350/rp235x.patched.svd: -------------------------------------------------------------------------------- 1 | ../../../Tools/SVDs/rp235x.patched.svd -------------------------------------------------------------------------------- /rpi-pico2-neopixel/Sources/RP2350/svd2swift.json: -------------------------------------------------------------------------------- 1 | { 2 | "peripherals": [ 3 | "PADS_BANK0", 4 | "PIO0", 5 | "IO_BANK0", 6 | "RESETS" 7 | ], 8 | "access-level": "public" 9 | } 10 | -------------------------------------------------------------------------------- /rpi-pico2-neopixel/Sources/Support/crt0.S: -------------------------------------------------------------------------------- 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 | .syntax unified 13 | .thumb 14 | .section __RESET,reset 15 | .thumb_func 16 | .global _entry_point 17 | 18 | _entry_point: 19 | // Set VTOR (offset 0xed08 in Cortex-M33F registers) to point to the vector table 20 | ldr r0, =_vector_table 21 | ldr r1, =(0xe0000000 + 0x0000ed08) 22 | str r0, [r1] 23 | 24 | // Load the first two entries of the vector table, (1) r1 = initial SP, (2) r2 = reset function 25 | ldmia r0!, {r1, r2} 26 | 27 | // Set stack pointer 28 | msr msp, r1 29 | 30 | // Transfer to reset function 31 | bx r2 32 | -------------------------------------------------------------------------------- /rpi-pico2-neopixel/Sources/Support/include/Support.h: -------------------------------------------------------------------------------- 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 | #pragma once 13 | -------------------------------------------------------------------------------- /rpi-pico2-neopixel/assets/images/example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftlang/swift-embedded-examples/622fd0a41dc883d40c1d101a73f678b1fe31b0d5/rpi-pico2-neopixel/assets/images/example.jpg -------------------------------------------------------------------------------- /rpi-picow-blink-sdk/BridgingHeader.h: -------------------------------------------------------------------------------- 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 | #pragma once 13 | 14 | #include "pico/stdlib.h" 15 | #include "pico/cyw43_arch.h" 16 | -------------------------------------------------------------------------------- /rpi-picow-blink-sdk/Main.swift: -------------------------------------------------------------------------------- 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 | @main 13 | struct Main { 14 | static func main() { 15 | let led = UInt32(CYW43_WL_GPIO_LED_PIN) 16 | if cyw43_arch_init() != 0 { 17 | print("Wi-Fi init failed") 18 | return 19 | } 20 | let dot = { 21 | cyw43_arch_gpio_put(led, true) 22 | sleep_ms(250) 23 | cyw43_arch_gpio_put(led, false) 24 | sleep_ms(250) 25 | } 26 | let dash = { 27 | cyw43_arch_gpio_put(led, true) 28 | sleep_ms(500) 29 | cyw43_arch_gpio_put(led, false) 30 | sleep_ms(250) 31 | } 32 | while true { 33 | dot() 34 | dot() 35 | dot() 36 | 37 | dash() 38 | dash() 39 | dash() 40 | 41 | dot() 42 | dot() 43 | dot() 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /rpi-picow-blink-sdk/README.md: -------------------------------------------------------------------------------- 1 | # rpi-picow-blink-sdk 2 | 3 | This example demonstrates how to integrate with the Pico SDK which is using CMake as its build system -- the simplest way to integrate with it is to also use CMake to build a Swift firmware application on top of the SDK and the libraries from it. 4 | 5 | 6 | 7 | ## Requirements 8 | 9 | - A Raspberry Pi Pico W board. If you have a Pico (non-W) instead, refer to the [rpi-pico-blink-sdk](../rpi-pico-blink-sdk) sample instead. 10 | - Follow the setup steps at https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf, in particular you'll need: 11 | - A checkout of the [pico-sdk](https://github.com/raspberrypi/pico-sdk.git), with git submodules checked out. 12 | - A checkout of the [pico-examples](https://github.com/raspberrypi/pico-examples.git). 13 | - CMake. 14 | - The [Arm Embedded Toolchain](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads). 15 | - Before trying to use Swift with the Pico SDK, make sure your environment works and can build the provided C/C++ sample projects, in particular: 16 | - Try building and running the "blink" example from pico-examples written in C. 17 | 18 | 19 | ## Building 20 | 21 | - Make sure you have a recent nightly Swift toolchain that has Embedded Swift support. 22 | - Build and copy the program in the UF2 format to the Mass Storage device to trigger flashing the program into memory (after which the device will reboot and run the firmware): 23 | ``` console 24 | $ cd rpi-picow-blink-sdk 25 | $ export PICO_BOARD=pico_w 26 | $ export PICO_SDK_PATH='' 27 | $ export PICO_TOOLCHAIN_PATH='' 28 | $ cmake -B build -G Ninja . -DCMAKE_EXPORT_COMPILE_COMMANDS=On 29 | $ cmake --build build 30 | ``` 31 | 32 | ## Running 33 | 34 | - Connect the Pico W board via a USB cable to your Mac, and make sure it's in the USB Mass Storage firmware upload mode (either hold the BOOTSEL button while plugging the board, or make sure your Flash memory doesn't contain any valid firmware). 35 | - Copy the UF2 firmware to the Mass Storage device: 36 | 37 | ```console 38 | $ cp build/swift-blinky.uf2 /Volumes/RP2040 39 | ``` 40 | 41 | - The green LED should now be blinking in a pattern. 42 | -------------------------------------------------------------------------------- /rpi-picow-blink-sdk/include/lwipopts.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org 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 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | // Generally you would define your own explicit list of lwIP options 14 | // (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html) 15 | 16 | #ifndef _LWIPOPTS_H 17 | #define _LWIPOPTS_H 18 | #endif 19 | -------------------------------------------------------------------------------- /stm32-blink/BridgingHeader.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | #pragma once 13 | 14 | static inline __attribute((always_inline)) void nop() { 15 | asm volatile("nop"); 16 | } 17 | -------------------------------------------------------------------------------- /stm32-blink/Main.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | #if STM32F746G_DISCOVERY 13 | 14 | // I1 pin aka "Arduino D13" pin on STM32F746 Discovery Board 15 | // https://www.st.com/resource/en/schematic_pack/mb1191-f746ngh6-c01_schematic.pdf 16 | let ledConfig: (GPIOBank, GPIOPin) = (.i, 1) 17 | 18 | #elseif NUCLEO_F103RB 19 | 20 | // A5 pin aka "Arduino D13" pin on Nucleo-64 boards 21 | // https://www.st.com/resource/en/user_manual/um1724-stm32-nucleo64-boards-mb1136-stmicroelectronics.pdf 22 | let ledConfig: (GPIOBank, GPIOPin) = (.a, 5) 23 | 24 | #else 25 | 26 | #error("Unknown board") 27 | 28 | #endif 29 | 30 | @main 31 | struct Main { 32 | static func main() { 33 | Board.initialize() 34 | 35 | while true { 36 | Board.ledOn() 37 | Board.delay(milliseconds: 100) 38 | Board.ledOff() 39 | Board.delay(milliseconds: 300) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /stm32-blink/Startup.c: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | #include 13 | #include 14 | 15 | extern int main(int argc, char *argv[]); 16 | 17 | void reset(void) { 18 | main(0, NULL); 19 | } 20 | 21 | void interrupt(void) { 22 | while (1) {} 23 | } 24 | 25 | __attribute((used)) 26 | #if defined(__ELF__) 27 | __attribute((section(".vectors"))) 28 | #elif defined(__MACH__) 29 | __attribute((section("__VECTORS,__text"))) 30 | #else 31 | #error Unknown file format 32 | #endif 33 | void *vector_table[114] = { 34 | (void *)0x20001ffc, // initial SP, assume we have 8 KB of SRAM 35 | reset, // Reset 36 | 37 | interrupt, // NMI 38 | interrupt, // HardFault 39 | interrupt, // MemManage 40 | interrupt, // BusFault 41 | interrupt, // UsageFault 42 | 43 | 0 // NULL for all the other handlers 44 | }; 45 | -------------------------------------------------------------------------------- /stm32-blink/build-elf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -vex 4 | 5 | # Determine file paths 6 | REPOROOT=$(realpath -- "$(dirname "${BASH_SOURCE[0]}")")/.. 7 | TOOLSROOT="$REPOROOT/Tools" 8 | SRCROOT="$REPOROOT/stm32-blink" 9 | BUILDROOT="$SRCROOT/.build" 10 | 11 | # Clean the build directory 12 | rm -r "$BUILDROOT" || true 13 | 14 | # Setup tools and build flags 15 | TARGET=armv7em-none-none-eabi 16 | 17 | if [[ ! "$STM_BOARD" ]] ; then 18 | echo "STM_BOARD must be set to STM32F746G_DISCOVERY or NUCLEO_F103RB" 19 | exit 1 20 | fi 21 | 22 | SWIFT_EXEC=${SWIFT_EXEC:-$(which swiftc)} 23 | SWIFT_FLAGS="-target $TARGET -Osize" 24 | SWIFT_FLAGS+=" -import-bridging-header $SRCROOT/BridgingHeader.h -wmo -enable-experimental-feature Embedded" 25 | SWIFT_FLAGS+=" -Xfrontend -function-sections -D${STM_BOARD}" 26 | 27 | CLANG_EXEC=${CLANG_EXEC:-$(which clang)} 28 | CLANG_FLAGS="-target $TARGET -Oz" 29 | 30 | LD_EXEC=${LD_EXEC:-$CLANG_EXEC} 31 | LD_FLAGS="-target $TARGET -fuse-ld=lld -nostdlib -static -Wl,-e,vector_table -Wl,--gc-sections -Wl,-T,$SRCROOT/elf-linkerscript.ld" 32 | 33 | # Create build directory 34 | mkdir -p "$BUILDROOT" 35 | 36 | # Build Swift sources 37 | # shellcheck disable=SC2086 # intentional splitting 38 | "$SWIFT_EXEC" $SWIFT_FLAGS -c $SRCROOT/*.swift -o "$BUILDROOT/blink.o" 39 | 40 | # Build C sources 41 | # shellcheck disable=SC2086 # intentional splitting 42 | "$CLANG_EXEC" $CLANG_FLAGS -c "$SRCROOT/Startup.c" -o "$BUILDROOT/Startup.o" 43 | 44 | # Link objects into executable 45 | # shellcheck disable=SC2086 # intentional splitting 46 | "$LD_EXEC" $LD_FLAGS "$BUILDROOT/blink.o" "$BUILDROOT/Startup.o" -o "$BUILDROOT/blink.elf" 47 | 48 | # Convert to Intel HEX for flashing 49 | "$TOOLSROOT"/elf2hex.py "$BUILDROOT/blink.elf" "$BUILDROOT/blink.hex" 50 | 51 | # Echo final binary path 52 | ls -al "$BUILDROOT/blink.hex" 53 | -------------------------------------------------------------------------------- /stm32-blink/build-macho.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -vex 4 | 5 | # Determine file paths 6 | REPOROOT=$(git rev-parse --show-toplevel) 7 | TOOLSROOT="$REPOROOT/Tools" 8 | SRCROOT="$REPOROOT/stm32-blink" 9 | BUILDROOT="$SRCROOT/.build" 10 | 11 | # Clean the build directory 12 | rm -r "$BUILDROOT" || true 13 | 14 | # Setup tools and build flags 15 | TARGET=armv7em-apple-none-macho 16 | 17 | if [[ ! "$STM_BOARD" ]] ; then 18 | echo "STM_BOARD must be set to STM32F746G_DISCOVERY or NUCLEO_F103RB" 19 | exit 1 20 | fi 21 | 22 | SWIFT_EXEC=${SWIFT_EXEC:-$(xcrun -f swiftc)} 23 | SWIFT_FLAGS="-target $TARGET -Osize" 24 | SWIFT_FLAGS+=" -import-bridging-header $SRCROOT/BridgingHeader.h -wmo -enable-experimental-feature Embedded" 25 | SWIFT_FLAGS+=" -Xcc -ffreestanding" 26 | SWIFT_FLAGS+=" -D${STM_BOARD}" 27 | 28 | CLANG_EXEC=${CLANG_EXEC:-$(xcrun -f clang)} 29 | CLANG_FLAGS="-target $TARGET -Oz" 30 | 31 | LD_EXEC=${LD_EXEC:-$CLANG_EXEC} 32 | LD_FLAGS="-target $TARGET -nostdlib -static -Wl,-e,_reset -dead_strip -Wl,-no_zero_fill_sections -Wl,-segalign,4 -Wl,-segaddr,__VECTORS,0x08000000 -Wl,-seg1addr,0x08000200 -Wl,-pagezero_size,0" 33 | 34 | PYTHON_EXEC=${PYTHON_EXEC:-$(xcrun -f python3)} 35 | MACHO2BIN="$TOOLSROOT/macho2bin.py" 36 | 37 | # Create build directory 38 | mkdir -p "$BUILDROOT" 39 | 40 | # Build Swift sources 41 | # shellcheck disable=SC2086 # intentional splitting 42 | "$SWIFT_EXEC" $SWIFT_FLAGS -c "$SRCROOT/"*.swift -o "$BUILDROOT/blink.o" 43 | 44 | # Build C sources 45 | # shellcheck disable=SC2086 # intentional splitting 46 | "$CLANG_EXEC" $CLANG_FLAGS -c "$SRCROOT/Startup.c" -o "$BUILDROOT/Startup.o" 47 | 48 | # Link objects into executable 49 | # shellcheck disable=SC2086 # intentional splitting 50 | "$LD_EXEC" $LD_FLAGS "$BUILDROOT/blink.o" "$BUILDROOT/Startup.o" -o "$BUILDROOT/blink" 51 | 52 | # Extract sections from executable into flashable binary 53 | "$PYTHON_EXEC" "$MACHO2BIN" "$BUILDROOT/blink" "$BUILDROOT/blink.bin" --base-address 0x08000000 --segments '__TEXT,__DATA,__VECTORS' 54 | 55 | # Echo final binary path 56 | ls -al "$BUILDROOT/blink.bin" 57 | -------------------------------------------------------------------------------- /stm32-blink/elf-linkerscript.ld: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | flash : ORIGIN = 0x08000000, LENGTH = 32K 4 | sram : ORIGIN = 0x20000000, LENGTH = 8K 5 | } 6 | 7 | SECTIONS 8 | { 9 | .text : { *(.vectors*) ; *(.text*) } > flash 10 | .bss : { *(.bss*) } > sram 11 | .data : { *(.data*) } > sram 12 | /DISCARD/ : { *(.swift_modhash*) } 13 | } 14 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Makefile: -------------------------------------------------------------------------------- 1 | ##===----------------------------------------------------------------------===## 2 | ## 3 | ## This source file is part of the Swift open source project 4 | ## 5 | ## Copyright (c) 2023 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 | # Paths 13 | REPOROOT := $(shell git rev-parse --show-toplevel) 14 | TOOLSROOT := $(REPOROOT)/Tools 15 | TOOLSET := $(TOOLSROOT)/Toolsets/stm32f74x-lcd.json 16 | MACHO2BIN := $(TOOLSROOT)/macho2bin.py 17 | SWIFT_BUILD := swift build 18 | 19 | # Flags 20 | ARCH := armv7em 21 | TARGET := $(ARCH)-apple-none-macho 22 | SWIFT_BUILD_ARGS := \ 23 | --configuration release \ 24 | --triple $(TARGET) \ 25 | --toolset $(TOOLSET) \ 26 | --disable-local-rpath 27 | BUILDROOT := $(shell $(SWIFT_BUILD) $(SWIFT_BUILD_ARGS) --show-bin-path) 28 | 29 | .PHONY: build 30 | build: 31 | @echo "building..." 32 | $(SWIFT_BUILD) \ 33 | $(SWIFT_BUILD_ARGS) \ 34 | -Xlinker -map -Xlinker $(BUILDROOT)/Application.mangled.map \ 35 | --verbose 36 | 37 | @echo "demangling linker map..." 38 | cat $(BUILDROOT)/Application.mangled.map \ 39 | | c++filt | swift demangle > $(BUILDROOT)/Application.map 40 | 41 | @echo "disassembling..." 42 | otool \ 43 | -arch $(ARCH) -v -V -d -t \ 44 | $(BUILDROOT)/Application \ 45 | | c++filt | swift demangle > $(BUILDROOT)/Application.disassembly 46 | 47 | @echo "extracting binary..." 48 | $(MACHO2BIN) \ 49 | $(BUILDROOT)/Application \ 50 | $(BUILDROOT)/Application.bin \ 51 | --base-address 0x00200000 \ 52 | --segments '__TEXT,__DATA,__VECTORS' 53 | 54 | .PHONY: clean 55 | clean: 56 | @echo "cleaning..." 57 | @swift package clean 58 | @rm -rf .build 59 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "0259de139c240d30a60426d7d7f2d119ccc92fffcb5377eccefe0c9a21e3ef9c", 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", 17 | "state" : { 18 | "branch" : "main", 19 | "revision" : "5232c5129a8c70beafc3d6acfbae2716c1b6822a" 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 | } 34 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "stm32-lcd-logo", 7 | products: [ 8 | .executable(name: "Application", targets: ["Application"]) 9 | ], 10 | dependencies: [ 11 | .package(url: "https://github.com/apple/swift-mmio", branch: "main") 12 | ], 13 | targets: [ 14 | .executableTarget( 15 | name: "Application", 16 | dependencies: ["STM32F7X6", "Support"], 17 | swiftSettings: [ 18 | .enableExperimentalFeature("InlineArrayTypeSugar"), 19 | .enableExperimentalFeature("SymbolLinkageMarkers"), 20 | ]), 21 | // SVD2Swift \ 22 | // --input Sources/STM32F7X6/stm32f7x6.patched.svd \ 23 | // --output Sources/STM32F7X6 \ 24 | // --access-level public \ 25 | // --indentation-width 2 \ 26 | // --peripherals FLASH GPIOA GPIOB GPIOC GPIOD GPIOE GPIOF GPIOG GPIOH GPIOI GPIOJ GPIOK LTDC RCC 27 | .target( 28 | name: "STM32F7X6", 29 | dependencies: [ 30 | .product(name: "MMIO", package: "swift-mmio") 31 | ], 32 | plugins: [ 33 | // Plugin disabled because SwiftPM is slow. 34 | // .plugin(name: "SVD2SwiftPlugin", package: "swift-mmio") 35 | ]), 36 | .target(name: "Support"), 37 | ], 38 | swiftLanguageModes: [.v5]) 39 | -------------------------------------------------------------------------------- /stm32-lcd-logo/README.md: -------------------------------------------------------------------------------- 1 | # stm32-lcd-logo 2 | 3 | 4 | 5 | ## How to build and run this example: 6 | 7 | - Connect the STM32F746G-DISCO board via the ST-LINK USB port to your Mac. 8 | - Make sure you have a recent nightly Swift toolchain that has Embedded Swift support. 9 | - Install the `stlink` (https://github.com/stlink-org/stlink) command line tools, e.g. via `brew install stlink`. 10 | - Build and upload the program to flash memory of the STM: 11 | ```console 12 | $ cd stm32-lcd-logo 13 | $ make 14 | $ st-flash --reset write .build/lcd-logo.bin 0x08000000 15 | ``` 16 | - The LCD display on the board should now be showing a bouncing animating Swift logo on a fading background, and the user LED on should be blinking. 17 | 18 | Resulting size of the compiled and linked binary follows (3.5 kB of code + 10 kB of pixel data): 19 | ```console 20 | $ size -m .build/lcd-logo 21 | Segment __TEXT: 14376 22 | Section __text: 3604 23 | Section __const: 10000 24 | total 13604 25 | Segment __DATA: 8 26 | Section __nl_symbol_ptr: 4 27 | Section __data: 4 28 | total 8 29 | Segment __VECTORS: 456 30 | Section __text: 456 31 | total 456 32 | Segment __LINKEDIT: 1056 33 | total 15896 34 | ``` 35 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/Application/Geometry/Color.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Color.swift 3 | // stm32-lcd-logo 4 | // 5 | // Created by Rauhul Varma on 3/12/25. 6 | // 7 | 8 | struct Color { 9 | var red: UInt8 10 | var green: UInt8 11 | var blue: UInt8 12 | } 13 | 14 | extension Color { 15 | static func gray(_ value: UInt8) -> Color { 16 | Color(red: value, green: value, blue: value) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/Application/Geometry/Point.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Point.swift 3 | // stm32-lcd-logo 4 | // 5 | // Created by Rauhul Varma on 3/12/25. 6 | // 7 | 8 | struct Point { 9 | var x: Int 10 | var y: Int 11 | 12 | func offset(by: Point) -> Point { 13 | Point(x: x + by.x, y: y + by.y) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/Application/Geometry/Size.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Size.swift 3 | // stm32-lcd-logo 4 | // 5 | // Created by Rauhul Varma on 3/12/25. 6 | // 7 | 8 | struct Size { 9 | var width: Int 10 | var height: Int 11 | } 12 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/STM32F7X6/Device.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// FLASH 6 | public let flash = FLASH(unsafeAddress: 0x40023c00) 7 | 8 | /// General-purpose I/Os 9 | public let gpioa = GPIOA(unsafeAddress: 0x40020000) 10 | 11 | /// General-purpose I/Os 12 | public let gpiob = GPIOB(unsafeAddress: 0x40020400) 13 | 14 | /// General-purpose I/Os 15 | public let gpioc = GPIOC(unsafeAddress: 0x40020800) 16 | 17 | /// General-purpose I/Os 18 | public let gpiod = GPIOD(unsafeAddress: 0x40020c00) 19 | 20 | /// General-purpose I/Os 21 | public let gpioe = GPIOE(unsafeAddress: 0x40021000) 22 | 23 | /// General-purpose I/Os 24 | public let gpiof = GPIOF(unsafeAddress: 0x40021400) 25 | 26 | /// General-purpose I/Os 27 | public let gpiog = GPIOG(unsafeAddress: 0x40021800) 28 | 29 | /// General-purpose I/Os 30 | public let gpioh = GPIOH(unsafeAddress: 0x40021c00) 31 | 32 | /// General-purpose I/Os 33 | public let gpioi = GPIOI(unsafeAddress: 0x40022000) 34 | 35 | /// General-purpose I/Os 36 | public let gpioj = GPIOJ(unsafeAddress: 0x40022400) 37 | 38 | /// General-purpose I/Os 39 | public let gpiok = GPIOK(unsafeAddress: 0x40022800) 40 | 41 | /// LCD-TFT Controller 42 | public let ltdc = LTDC(unsafeAddress: 0x40016800) 43 | 44 | /// Reset and clock control 45 | public let rcc = RCC(unsafeAddress: 0x40023800) 46 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/STM32F7X6/Empty.swift: -------------------------------------------------------------------------------- 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 | // This file is intentionally left empty for SwiftPM to recognize the target as 13 | // a Swift source module. 14 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/STM32F7X6/GPIOB.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOB = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/STM32F7X6/GPIOC.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOC = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/STM32F7X6/GPIOD.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOD = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/STM32F7X6/GPIOE.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOE = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/STM32F7X6/GPIOF.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOF = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/STM32F7X6/GPIOG.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOG = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/STM32F7X6/GPIOH.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOH = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/STM32F7X6/GPIOI.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOI = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/STM32F7X6/GPIOJ.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOJ = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/STM32F7X6/GPIOK.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOK = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/STM32F7X6/stm32f7x6.patched.svd: -------------------------------------------------------------------------------- 1 | ../../../Tools/SVDs/stm32f7x6.patched.svd -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/STM32F7X6/svd2swift.json: -------------------------------------------------------------------------------- 1 | { 2 | "peripherals": [ 3 | "FLASH", 4 | "GPIOA", 5 | "GPIOB", 6 | "GPIOC", 7 | "GPIOD", 8 | "GPIOE", 9 | "GPIOF", 10 | "GPIOG", 11 | "GPIOH", 12 | "GPIOI", 13 | "GPIOJ", 14 | "GPIOK", 15 | "LTDC", 16 | "RCC" 17 | ], 18 | "access-level": "public" 19 | } 20 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/Support/Startup.c: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | #include 13 | #include 14 | 15 | extern int main(int argc, char *argv[]); 16 | 17 | void *memset(void *b, int c, size_t len) { 18 | for (int i = 0; i < len; i++) { 19 | ((char *)b)[i] = c; 20 | } 21 | return b; 22 | } 23 | 24 | void *memcpy(void *restrict dst, const void *restrict src, size_t n) { 25 | for (int i = 0; i < n; i++) { 26 | ((char *)dst)[i] = ((char *)src)[i]; 27 | } 28 | return dst; 29 | } 30 | 31 | void reset(void) { 32 | main(0, NULL); 33 | } 34 | 35 | void interrupt(void) { 36 | while (1) {} 37 | } 38 | 39 | __attribute((used)) __attribute((section("__VECTORS,__text"))) 40 | void *vector_table[114] = { 41 | (void *)0x2000fffc, // initial SP 42 | reset, // Reset 43 | 44 | interrupt, // NMI 45 | interrupt, // HardFault 46 | interrupt, // MemManage 47 | interrupt, // BusFault 48 | interrupt, // UsageFault 49 | 50 | 0 // NULL for all the other handlers 51 | }; 52 | -------------------------------------------------------------------------------- /stm32-lcd-logo/Sources/Support/include/Support.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | #pragma once 13 | 14 | static inline void nop() { 15 | asm volatile("nop"); 16 | } 17 | -------------------------------------------------------------------------------- /stm32-lvgl/.gitignore: -------------------------------------------------------------------------------- 1 | lvgl 2 | llvm-toolchain 3 | -------------------------------------------------------------------------------- /stm32-lvgl/.sourcekit-lsp/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "swiftPM": { 3 | "configuration": "release", 4 | "triple": "armv7em-none-none-eabi", 5 | "toolsets": ["toolset.json"], 6 | "swiftCompilerFlags": [ 7 | "-enable-experimental-feature", "Embedded", 8 | "-enable-experimental-feature", "Extern" 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /stm32-lvgl/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "27891c7454528c694f8ba4111e7214ff70b5364bdb54bce808c6e0d4b188bd81", 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", 17 | "state" : { 18 | "branch" : "main", 19 | "revision" : "5232c5129a8c70beafc3d6acfbae2716c1b6822a" 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 | "identity" : "swiftsdl2", 33 | "kind" : "remoteSourceControl", 34 | "location" : "https://github.com/ctreffs/SwiftSDL2.git", 35 | "state" : { 36 | "revision" : "30a2886bd68e43fc19ba29b63ffe230ac0e4db7a", 37 | "version" : "1.4.1" 38 | } 39 | } 40 | ], 41 | "version" : 3 42 | } 43 | -------------------------------------------------------------------------------- /stm32-lvgl/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.10 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "stm32-lvgl", 7 | platforms: [ 8 | .macOS(.v11) 9 | ], 10 | products: [ 11 | .executable(name: "Application", targets: ["Application"]) 12 | ], 13 | dependencies: [ 14 | .package(url: "https://github.com/apple/swift-mmio", branch: "main"), 15 | .package(url: "https://github.com/ctreffs/SwiftSDL2.git", from: "1.4.0"), 16 | ], 17 | targets: [ 18 | // 19 | // FIRMWARE TARGETS 20 | // 21 | 22 | .executableTarget( 23 | name: "Application", 24 | dependencies: [ 25 | "Registers", 26 | "Support", 27 | "CLVGL", 28 | ]), 29 | 30 | // SVD2Swift \ 31 | // --input Tools/SVDs/stm32f7x6.patched.svd \ 32 | // --output stm32-lvgl/Sources/STM32F7x6 \ 33 | // --peripherals FLASH LTDC RCC PWR FMC SCB DBGMCU USART1 STK NVIC SYSCFG \ 34 | // GPIOA GPIOB GPIOC GPIOD GPIOE GPIOF GPIOG GPIOH GPIOI GPIOJ GPIOK \ 35 | // I2C1 I2C2 I2C3 I2C4 \ 36 | // --access-level public 37 | .target( 38 | name: "Registers", 39 | dependencies: [ 40 | .product(name: "MMIO", package: "swift-mmio") 41 | ]), 42 | 43 | .target(name: "Support"), 44 | 45 | .target(name: "CLVGL"), 46 | 47 | // 48 | // HOST TARGETS 49 | // 50 | 51 | .executableTarget( 52 | name: "HostSDLApp", 53 | dependencies: [ 54 | .product(name: "SDL", package: "SwiftSDL2"), 55 | "CLVGL", 56 | ], 57 | swiftSettings: [.enableExperimentalFeature("Extern")], 58 | linkerSettings: [.unsafeFlags(["-L.build/lvgl-host/lib", "-llvgl", "-llvgl_demos"])]), 59 | ]) 60 | -------------------------------------------------------------------------------- /stm32-lvgl/README.md: -------------------------------------------------------------------------------- 1 | # stm32-lvgl 2 | 3 | 4 | 5 | This sample code is demonstrating a "full" graphical firmware running on an STM32 microcontroller board, concretely the STM32F746G "Discovery" board. It shows using: 6 | 7 | - **ELF file format**, linking with lld, with a custom simple linker script (and thus it builds identically on both macOS and Linux hosts) 8 | - **LLVM Embedded Toolchain for ARM** 9 | - **LVGL** graphical/input/animation library 10 | - The **DRAM, LCD, touch panel, GPIO pins and interrupts** on the STM32F746G 11 | - **No other SDKs or library dependencies** -- all the startup code, including MCU, board and peripheral initialization is done in Swift source code 12 | 13 | Additionally, this sample code: 14 | 15 | - Has **LSP integration** set up via the `.sourcekit-lsp/config.json` file, confirmed to work in multiple code editors (VS Code, Sublime Text, Zed) 16 | - Uses **SwiftPM's toolset.json** to define compiler and linker flags 17 | - Has a host OS (macOS, Linux) **"simulator" using SDL** that can use the same "business logic" code to render the same LVGL UI. 18 | 19 | ## How to build and run this example: 20 | 21 | 1. Connect the STM32F746G-DISCO board via the ST-LINK USB port to your Mac. 22 | 2. Install the right Swift toolchain specified in this repo's `.swift-version` file. This is best done using `swiftly`: 23 | ```console 24 | $ swiftly install # Run in the project's directory 25 | ``` 26 | 3. Install the `stlink` (https://github.com/stlink-org/stlink) command line tools, e.g. via `brew install stlink`. 27 | 4. Run a provided shell script that fetches the LVGL and LLVM dependencies: 28 | ```console 29 | $ ./fetch-dependencies.sh 30 | ``` 31 | 5. Build the firmware by running make: 32 | ```console 33 | $ make 34 | ``` 35 | 6. Flash the built firmware by running make with the following target: 36 | ```console 37 | $ make flash 38 | ``` 39 | 40 | You should now see the UI animating on the board's LCD display, and the touch screen should react to inputs. 41 | 42 | Optionally, you can also run build and run the code in a desktop OS SDL "simulator" by: 43 | ```console 44 | $ make simulator 45 | ``` 46 | 47 | ## Additional information 48 | 49 | - The ELF linking, linker script and packaging scheme is destribed in detail [inside the linker script](Sources/Support/linkerscript.ld). 50 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Application/Debug.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2025 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 Registers 13 | import Support 14 | 15 | extension Main { 16 | static func initDebug() { 17 | // Keep clocks going when we WFI 18 | dbgmcu.cr.modify { $0.raw.dbg_standby = 1 } 19 | } 20 | 21 | static func initUartOutput() { 22 | // A9 is UART1 TX, which is relayed by ST-LINK over USB 23 | 24 | // Clock configuration 25 | rcc.ahb1enr.modify { $0.raw.gpioaen = 1 } // Enable AHB clock to port A 26 | rcc.apb2enr.modify { $0.raw.usart1en = 1 } // Enable APB clock to usart 1 27 | 28 | // Configure A9 as UART1 TX 29 | gpioa.moder.modify { $0.raw.moder9 = 0b10 } // Put Pin A9 into alternate function mode 30 | gpioa.otyper.modify { $0.raw.ot9 = 0b0 } // Put Pin A9 into push pull 31 | gpioa.ospeedr.modify { $0.raw.ospeedr9 = 0b00 } // Put Pin A9 into low speed 32 | gpioa.pupdr.modify { $0.raw.pupdr9 = 0b00 } // Disable pull up/down on Pin A9 33 | gpioa.afrh.modify { $0.raw.afrh9 = 0b0111 } // Set alternate function usart1 on Pin A9 34 | 35 | // Configure UART1, set the baud rate to 115200 (we boot at 16 MHz) 36 | usart1.brr.modify { $0.raw.storage = 16_000_000 / 115_200 } 37 | 38 | usart1.cr1.modify { 39 | $0.raw.ue = 1 // Enable USART 1 40 | $0.raw.te = 1 // Enable TX 41 | } 42 | } 43 | } 44 | 45 | func waitTxBufferEmpty() { 46 | // Spin while tx buffer not empty 47 | while usart1.isr.read().raw.txe == 0 {} 48 | } 49 | 50 | func tx(value: UInt8) { 51 | usart1.tdr.write { $0.raw.tdr_field = UInt32(value) } 52 | } 53 | 54 | @_cdecl("putchar") 55 | public func putchar(_ value: CInt) -> CInt { 56 | waitTxBufferEmpty() 57 | tx(value: UInt8(value)) 58 | waitTxBufferEmpty() 59 | return 0 60 | } 61 | 62 | func log(_ s: String) { 63 | let n = uptimeInMs / 1000 64 | var m = String(uptimeInMs % 1000) 65 | while m.utf8.count < 3 { m = "0" + m } 66 | print("[\(n).\(m)] " + s) 67 | } 68 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Application/Interrupts.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2025 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 Registers 13 | import Support 14 | 15 | func withInterruptsDisabled(body: () -> Void) { 16 | let oldMask = primask_get() 17 | cpsid_i() 18 | body() 19 | primask_set(oldMask) 20 | } 21 | 22 | extension Main { 23 | static func initInterrupts() { 24 | // Disable then clear all RCC interrupts. 25 | rcc.cir.modify { 26 | $1.raw.lsirdyie = 0 27 | $1.raw.lserdyie = 0 28 | $1.raw.hsirdyie = 0 29 | $1.raw.hserdyie = 0 30 | $1.raw.pllrdyie = 0 31 | $1.raw.plli2srdyie = 0 32 | $1.raw.pllsairdyie = 0 33 | } 34 | rcc.cir.modify { 35 | $1.raw.lsirdyie = 1 36 | $1.raw.lserdyie = 1 37 | $1.raw.hsirdyie = 1 38 | $1.raw.hserdyie = 1 39 | $1.raw.pllrdyie = 1 40 | $1.raw.plli2srdyie = 1 41 | $1.raw.pllsairdyie = 1 42 | } 43 | } 44 | } 45 | 46 | var uptimeInMs = 0 47 | 48 | extension Main { 49 | static func initSysTick() { 50 | stk.rvr.write { $0.raw.reload = 200_000 } // Set the reload value, ~1ms 51 | stk.cvr.write { $0.raw.current = 0 } // Clear the current value 52 | 53 | // Enable the SysTick timer with processor clock 54 | stk.csr.modify { 55 | $0.raw.enable = 1 56 | $0.raw.tickint = 1 57 | $0.raw.clksource = 1 58 | } 59 | 60 | // Wait one tick for wrap. This eliminates ambiguity at the 61 | // zero count. This is only possible if SysTick is 62 | // (significantly) slower than the core clock. 63 | while stk.cvr.read().raw.current == 0 { /* busy wait */ } 64 | } 65 | } 66 | 67 | @_cdecl("SystickTimerISR") 68 | func SystickTimerISR() { 69 | uptimeInMs += 1 70 | } 71 | 72 | var lcdInterruptVerticalSyncHandler: (() -> Void)? = nil 73 | 74 | @_cdecl("LtdcIntHandlerISR") 75 | func LtdcIntHandlerISR() { 76 | let sr = ltdc.isr.read() 77 | ltdc.icr.write { $0.storage = sr.storage } 78 | if sr.raw.rrif != 0 { 79 | lcdInterruptVerticalSyncHandler?() 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Application/TouchPanel.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2025 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 | struct TouchData { 13 | var numberOfTouchPoints: Int 14 | var x: Int 15 | var y: Int 16 | } 17 | 18 | enum TouchPanel { 19 | static let touchPanel = I2C.init(interface: .i2c3, deviceAddress: 0x38) 20 | 21 | static func initialize() { 22 | // Init the touch controller 23 | guard touchPanel.write(data: [0xA4, 0x00]) else { 24 | print("Init failed") 25 | return 26 | } 27 | guard touchPanel.write(data: [0xA5, 0x00]) else { 28 | print("Init failed") 29 | return 30 | } 31 | } 32 | 33 | static func readTouchData() -> TouchData { 34 | // Read device registers 0x00 through 0x06: 35 | // 0x00 DEVIDE_MODE Device Mode (bits 4...6) 36 | // 0x01 GEST_ID Gesture ID 37 | // 0x02 TD_STATUS Number of touch points (bits 0...3) 38 | // 0x03 TOUCH1_XH 1st Event Flag (bits 6...7), 1st Touch X Position Hi Bits (bits 0...3) 39 | // 0x04 TOUCH1_XL 1st Touch X Position Lo Bits 40 | // 0x05 TOUCH1_YH 1st Touch ID (bits 4...7), 1st Touch Y Position Hi Bits (bits 0...3) 41 | // 0x06 TOUCH1_YL 1st Touch Y Position Lo Bits 42 | 43 | guard touchPanel.write(data: [0x00]) else { 44 | print("Send failed") 45 | return TouchData(numberOfTouchPoints: 0, x: 0, y: 0) 46 | } 47 | 48 | var buf = [UInt8](repeating: 0, count: 7) 49 | guard touchPanel.read(buffer: &buf, length: 7) else { 50 | print("Read failed") 51 | return TouchData(numberOfTouchPoints: 0, x: 0, y: 0) 52 | } 53 | 54 | let x = Int(buf[3] & 0b1111) << 8 | Int(buf[4]) 55 | let y = Int(buf[5] & 0b1111) << 8 | Int(buf[6]) 56 | 57 | // On STM32F746G discovery board, the x and y are swapped. 58 | return TouchData(numberOfTouchPoints: Int(buf[2] & 0b1111), x: y, y: x) 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/CLVGL/include/CLVGL.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2025 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 | #include "../../../lvgl/lvgl.h" 13 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/CLVGL/include/module.modulemap: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2025 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 | module CLVGL [system] { 13 | header "CLVGL.h" 14 | export * 15 | } 16 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/HostSDLApp/UIAppLogic.swift: -------------------------------------------------------------------------------- 1 | ../Application/UIAppLogic.swift -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Registers/GPIOB.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOB = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Registers/GPIOC.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOC = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Registers/GPIOD.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOD = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Registers/GPIOE.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOE = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Registers/GPIOF.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOF = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Registers/GPIOG.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOG = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Registers/GPIOH.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOH = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Registers/GPIOI.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOI = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Registers/GPIOJ.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOJ = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Registers/GPIOK.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOK = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Registers/I2C2.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// Inter-integrated circuit 6 | public typealias I2C2 = I2C1 7 | 8 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Registers/I2C3.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// Inter-integrated circuit 6 | public typealias I2C3 = I2C1 7 | 8 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Registers/I2C4.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// Inter-integrated circuit 6 | public typealias I2C4 = I2C1 7 | 8 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Registers/STK.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// SysTick timer 6 | @RegisterBlock 7 | public struct STK { 8 | /// SysTick control and status register 9 | @RegisterBlock(offset: 0x0) 10 | public var csr: Register 11 | 12 | /// SysTick reload value register 13 | @RegisterBlock(offset: 0x4) 14 | public var rvr: Register 15 | 16 | /// SysTick current value register 17 | @RegisterBlock(offset: 0x8) 18 | public var cvr: Register 19 | 20 | /// SysTick calibration value register 21 | @RegisterBlock(offset: 0xc) 22 | public var calib: Register 23 | } 24 | 25 | extension STK { 26 | /// SysTick control and status register 27 | @Register(bitWidth: 32) 28 | public struct CSR { 29 | /// Counter enable 30 | @ReadWrite(bits: 0..<1) 31 | public var enable: ENABLE 32 | 33 | /// SysTick exception request enable 34 | @ReadWrite(bits: 1..<2) 35 | public var tickint: TICKINT 36 | 37 | /// Clock source selection 38 | @ReadWrite(bits: 2..<3) 39 | public var clksource: CLKSOURCE 40 | 41 | /// COUNTFLAG 42 | @ReadWrite(bits: 16..<17) 43 | public var countflag: COUNTFLAG 44 | } 45 | 46 | /// SysTick reload value register 47 | @Register(bitWidth: 32) 48 | public struct RVR { 49 | /// RELOAD value 50 | @ReadWrite(bits: 0..<24) 51 | public var reload: RELOAD 52 | } 53 | 54 | /// SysTick current value register 55 | @Register(bitWidth: 32) 56 | public struct CVR { 57 | /// Current counter value 58 | @ReadWrite(bits: 0..<24) 59 | public var current: CURRENT 60 | } 61 | 62 | /// SysTick calibration value register 63 | @Register(bitWidth: 32) 64 | public struct CALIB { 65 | /// Calibration value 66 | @ReadWrite(bits: 0..<24) 67 | public var tenms: TENMS 68 | 69 | /// SKEW flag: Indicates whether the TENMS value is exact 70 | @ReadWrite(bits: 30..<31) 71 | public var skew: SKEW 72 | 73 | /// NOREF flag. Reads as zero 74 | @ReadWrite(bits: 31..<32) 75 | public var noref: NOREF 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Support/Stubs.c: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2025 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 | #include 13 | #include 14 | 15 | // This is NOT real entropy. This implementation is a stub that generates 16 | // predictable, sequential values for testing purposes only. Do not use this 17 | // in production code where actual cryptographic randomness is required. 18 | int getentropy(char *buffer, size_t length) { 19 | for (size_t i = 0; i < length; i++) { 20 | buffer[i] = i % 256; 21 | } 22 | return 0; 23 | } 24 | 25 | void _exit(int status) { 26 | __builtin_trap(); 27 | } 28 | -------------------------------------------------------------------------------- /stm32-lvgl/Sources/Support/include/Support.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2025 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 | #pragma once 13 | 14 | #include 15 | 16 | static inline void nop() { 17 | asm volatile("nop"); 18 | } 19 | 20 | static inline uint32_t primask_get(void) { 21 | uint32_t r; 22 | asm volatile("mrs %0, primask" : "=r" (r)); 23 | return r; 24 | } 25 | 26 | static inline void primask_set(uint32_t m) { 27 | asm volatile("msr primask, %0" : : "r" (m)); 28 | } 29 | 30 | static inline void cpsid_i(void) { 31 | asm volatile("cpsid i"); 32 | } 33 | 34 | static inline void delay(uint32_t i) { 35 | __asm__ volatile( 36 | " .align 2 \n" 37 | "1: subs %0, #1\n" 38 | " bhi 1b\n" 39 | : "=r"(i) 40 | : "0" (i) 41 | : "cc" 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /stm32-lvgl/clang-arm-toolchain.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Generic) 2 | set(CMAKE_SYSTEM_PROCESSOR arm) 3 | set(CMAKE_CROSSCOMPILING TRUE) 4 | 5 | # Specify the compilers 6 | set(CMAKE_C_COMPILER "${TOOLCHAIN_PATH}/bin/clang") 7 | set(CMAKE_CXX_COMPILER "${TOOLCHAIN_PATH}/bin/clang++") 8 | set(CMAKE_ASM_COMPILER "${TOOLCHAIN_PATH}/bin/clang") 9 | 10 | # Target flags 11 | set(CMAKE_C_FLAGS "-target arm-none-eabi -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -ffunction-sections -fdata-sections") 12 | set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++11 -fno-exceptions -fno-rtti") 13 | set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS}") 14 | 15 | # Search paths for libraries 16 | set(CMAKE_FIND_ROOT_PATH "${TOOLCHAIN_PATH}") 17 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 18 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 19 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 20 | 21 | set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) 22 | -------------------------------------------------------------------------------- /stm32-lvgl/fetch-dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -vex 4 | 5 | ### 6 | ### LLVM Embedded Toolchain for ARM 7 | ### 8 | 9 | VER=19.1.5 10 | 11 | if [[ "$(uname -s)" == "Darwin" ]]; then 12 | FILE=LLVM-ET-Arm-${VER}-Darwin-universal 13 | EXT=dmg 14 | SHA=0451e67dc9a9066c17f746c26654962fa3889d4df468db1245d1bae69438eaf5 15 | elif [[ "$(uname -s)" == "Linux" && "$(uname -m)" == "aarch64" ]]; then 16 | FILE=LLVM-ET-Arm-${VER}-Linux-AArch64 17 | EXT=tar.xz 18 | SHA=5e2f6b8c77464371ae2d7445114b4bdc19f56138e8aa864495181b52f57d0b85 19 | elif [[ "$(uname -s)" == "Linux" && "$(uname -m)" == "x86_64" ]]; then 20 | FILE=LLVM-ET-Arm-${VER}-Linux-x86_64 21 | EXT=tar.xz 22 | SHA=34ee877aadc78c5e9f067e603a1bc9745ed93ca7ae5dbfc9b4406508dc153920 23 | else 24 | echo "No LLVM toolchain for this OS/arch ($(uname -s), $(uname -m))" 25 | exit 1 26 | fi 27 | 28 | FOUND=no 29 | SHA_MATCHES=no 30 | if [[ -f /tmp/${FILE}.${EXT} ]]; then 31 | FOUND=yes 32 | if sha256 -c $SHA /tmp/${FILE}.${EXT}; then 33 | SHA_MATCHES=yes 34 | fi 35 | fi 36 | 37 | if [[ "$FOUND" = "no" || "$SHA_MATCHES" = "no" ]]; then 38 | curl https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/releases/download/release-${VER}/${FILE}.${EXT} \ 39 | -L -O --output-dir /tmp 40 | fi 41 | 42 | if [[ "$EXT" = "dmg" ]]; then 43 | hdiutil attach /tmp/${FILE}.${EXT} 44 | ls -al /Volumes/${FILE} 45 | rm -r ./llvm-toolchain || true 46 | cp -a /Volumes/${FILE}/${FILE} ./llvm-toolchain 47 | diskutil eject /Volumes/${FILE} 48 | elif [[ "$EXT" = "tar.xz" ]]; then 49 | rm -r ./llvm-toolchain || true 50 | mkdir -p ./llvm-toolchain 51 | tar -xvf /tmp/${FILE}.${EXT} -C ./llvm-toolchain --strip-components 1 52 | else 53 | exit 1 54 | fi 55 | 56 | 57 | 58 | ### 59 | ### LVGL 60 | ### 61 | 62 | rm -rf ./lvgl || true 63 | git clone https://github.com/lvgl/lvgl.git --branch release/v9.2 64 | -------------------------------------------------------------------------------- /stm32-lvgl/toolset.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0", 3 | "swiftCompiler": { 4 | "extraCLIOptions": [ 5 | "-Xfrontend", "-disable-stack-protector", 6 | "-enable-experimental-feature", "Embedded", 7 | "-enable-experimental-feature", "Extern", 8 | "-Xclang-linker", "-nostdlib", 9 | "-Xfrontend", "-function-sections", 10 | "-Xfrontend", "-mergeable-symbols", 11 | "-Xfrontend", "-enable-single-module-llvm-emission", 12 | "-assert-config", "Debug", 13 | "-Xclang-linker", "-fuse-ld=lld" 14 | ] 15 | }, 16 | "cCompiler": { 17 | "extraCLIOptions": [ 18 | "-nostdlib", 19 | "-ffunction-sections", 20 | "-Illvm-toolchain/lib/clang-runtimes/arm-none-eabi/armv7m_soft_fpv4_sp_d16_exn_rtti/include", 21 | "-mthumb", 22 | "-Werror", 23 | "-Wall", 24 | "-static", 25 | "-fno-stack-protector", 26 | "-fno-common" 27 | ] 28 | }, 29 | "linker": { 30 | "extraCLIOptions": [ 31 | "-nostdlib", 32 | "-t", "-v", 33 | "-T", "Sources/Support/linkerscript.ld", 34 | "-Lllvm-toolchain/lib/clang-runtimes/arm-none-eabi/armv7m_soft_fpv4_sp_d16_exn_rtti/lib", 35 | "-lc", 36 | "-lclang_rt.builtins", 37 | "-L.build/lvgl/lib", 38 | "-llvgl", "-llvgl_demos", 39 | "-static", 40 | "-e", "_start_elf", 41 | "--orphan-handling=error" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /stm32-neopixel/Makefile: -------------------------------------------------------------------------------- 1 | ##===----------------------------------------------------------------------===## 2 | ## 3 | ## This source file is part of the Swift open source project 4 | ## 5 | ## Copyright (c) 2025 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 | # Paths 13 | REPOROOT := $(shell git rev-parse --show-toplevel) 14 | TOOLSROOT := $(REPOROOT)/Tools 15 | TOOLSET := $(TOOLSROOT)/Toolsets/stm32f74x.json 16 | MACHO2BIN := $(TOOLSROOT)/macho2bin.py 17 | SWIFT_BUILD := swift build 18 | 19 | # Flags 20 | ARCH := armv7em 21 | TARGET := $(ARCH)-apple-none-macho 22 | SWIFT_BUILD_ARGS := \ 23 | --configuration release \ 24 | --triple $(TARGET) \ 25 | --toolset $(TOOLSET) \ 26 | --disable-local-rpath 27 | BUILDROOT := $(shell $(SWIFT_BUILD) $(SWIFT_BUILD_ARGS) --show-bin-path) 28 | 29 | .PHONY: build 30 | build: 31 | @echo "building..." 32 | $(SWIFT_BUILD) \ 33 | $(SWIFT_BUILD_ARGS) \ 34 | -Xlinker -map -Xlinker $(BUILDROOT)/Application.mangled.map \ 35 | --verbose 36 | 37 | @echo "demangling linker map..." 38 | cat $(BUILDROOT)/Application.mangled.map \ 39 | | c++filt | swift demangle > $(BUILDROOT)/Application.map 40 | 41 | @echo "disassembling..." 42 | otool \ 43 | -arch $(ARCH) -v -V -d -t \ 44 | $(BUILDROOT)/Application \ 45 | | c++filt | swift demangle > $(BUILDROOT)/Application.disassembly 46 | 47 | @echo "extracting binary..." 48 | $(MACHO2BIN) \ 49 | $(BUILDROOT)/Application \ 50 | $(BUILDROOT)/Application.bin \ 51 | --base-address 0x20010000 \ 52 | --segments '__TEXT,__DATA,__VECTORS' 53 | 54 | .PHONY: clean 55 | clean: 56 | @echo "cleaning..." 57 | @swift package clean 58 | @rm -rf .build 59 | -------------------------------------------------------------------------------- /stm32-neopixel/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "fec1335f7424378925c47a261a19ffe2160476fc1e6fd88c5e270ddff078da9d", 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", 17 | "state" : { 18 | "branch" : "main", 19 | "revision" : "5232c5129a8c70beafc3d6acfbae2716c1b6822a" 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 | } 34 | -------------------------------------------------------------------------------- /stm32-neopixel/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "stm32-neopixel", 7 | products: [ 8 | .executable(name: "Application", targets: ["Application"]) 9 | ], 10 | dependencies: [ 11 | .package(url: "https://github.com/apple/swift-mmio", branch: "main") 12 | ], 13 | targets: [ 14 | .executableTarget( 15 | name: "Application", 16 | dependencies: ["STM32F7X6", "Support"]), 17 | // SVD2Swift \ 18 | // --input Sources/STM32F7X6/stm32f7x6.patched.svd \ 19 | // --output Sources/STM32F7X6 \ 20 | // --access-level public \ 21 | // --indentation-width 2 \ 22 | // --peripherals DMA1 DMA2 GPIOA GPIOB GPIOI RCC SPI1 SPI2 USART1 23 | .target( 24 | name: "STM32F7X6", 25 | dependencies: [ 26 | .product(name: "MMIO", package: "swift-mmio") 27 | ], 28 | plugins: [ 29 | // Plugin disabled because SwiftPM is slow. 30 | // .plugin(name: "SVD2SwiftPlugin", package: "swift-mmio") 31 | ]), 32 | .target(name: "Support"), 33 | ], 34 | swiftLanguageModes: [.v5]) 35 | -------------------------------------------------------------------------------- /stm32-neopixel/README.md: -------------------------------------------------------------------------------- 1 | # stm32-neopixel 2 | 3 | 4 | 5 | This demo is designed to run on an STM32 microcontroller and some additional hardware, detailed below: 6 | 7 | 1. An [STM32F746G Discovery kit](https://www.st.com/en/evaluation-tools/32f746gdiscovery.html) 8 | 2. A 3.3V to 5V level shifter such as [Texas Instrument's TXB0104](https://www.ti.com/lit/ds/symlink/txb0104.pdf) which can be found on a breakout board for easier use, such as: [SparkFun Voltage-Level Translator Breakout](https://www.sparkfun.com/products/11771) 9 | 3. A NeoPixel WS2812 or compatible LED Strip; such as: [SparkFun LED RGB Strip](https://www.sparkfun.com/products/15205) 10 | 4. A breadboard, such as: [SparkFun Translucent Breadboard](https://www.sparkfun.com/products/9567) 11 | 5. A 5V power supply 12 | 13 | Connect the components as shown in the schematic below: 14 | 15 | ![schematic](./schematic.png) 16 | 17 | We recommend including a capacitor across the LED strip power supply. 18 | 19 | ## How to build and run this example: 20 | 21 | - Connect the STM32F746G-DISCO board via the ST-LINK USB port to your Mac. 22 | - Make sure you have a recent nightly Swift toolchain that has Embedded Swift support. 23 | - Install the `stlink` (https://github.com/stlink-org/stlink) command line tools, e.g. via `brew install stlink`. 24 | - Build and upload the program to flash memory of the STM: 25 | ```console 26 | $ cd stm32-neopixel 27 | $ make 28 | $ st-flash --reset write .build/release/Application.bin 0x08000000 29 | ``` 30 | - The LED strip should light up and slowly animate a color gradient. 31 | -------------------------------------------------------------------------------- /stm32-neopixel/Sources/Application/Neopixel/HSV8Pixel.swift: -------------------------------------------------------------------------------- 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 | struct HSV8Pixel { 13 | var hue: UInt8 14 | var saturation: UInt8 15 | var value: UInt8 16 | } 17 | -------------------------------------------------------------------------------- /stm32-neopixel/Sources/Application/Neopixel/SPINeoPixelBit.swift: -------------------------------------------------------------------------------- 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 | enum SPINeoPixelBit: UInt8 { 13 | case reset = 0b0000_0000 14 | case zero = 0b1100_0000 15 | case one = 0b1111_1000 16 | } 17 | -------------------------------------------------------------------------------- /stm32-neopixel/Sources/Application/Neopixel/SPINeoPixelGRB64Pixel.swift: -------------------------------------------------------------------------------- 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 | struct SPINeoPixelGRB64Pixel { 13 | var green: UInt64 14 | var red: UInt64 15 | var blue: UInt64 16 | } 17 | 18 | extension SPINeoPixelGRB64Pixel { 19 | static let reset = Self(green: 0, red: 0, blue: 0) 20 | } 21 | 22 | extension SPINeoPixelGRB64Pixel { 23 | init(_ pixel: RGB8Pixel) { 24 | self.green = .neoPixelChannel(rawChannel: pixel.green) 25 | self.red = .neoPixelChannel(rawChannel: pixel.red) 26 | self.blue = .neoPixelChannel(rawChannel: pixel.blue) 27 | } 28 | } 29 | 30 | extension UInt64 { 31 | static func neoPixelChannel(rawChannel: UInt8) -> Self { 32 | var rawChannel = rawChannel 33 | var neoPixelChannel: Self = 0 34 | for _ in 0..>= 1 37 | neoPixelChannel <<= 8 38 | neoPixelChannel |= Self(bit.rawValue) 39 | } 40 | return neoPixelChannel 41 | } 42 | } 43 | 44 | extension UInt8 { 45 | static func rawChannel(neoPixelChannel: UInt64) -> Self { 46 | var neoPixelChannel = neoPixelChannel 47 | var rawChannel: Self = 0 48 | for _ in 0..>= 8 58 | rawChannel <<= 1 59 | rawChannel |= UInt8(bit) 60 | } 61 | return rawChannel 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /stm32-neopixel/Sources/STM32F7X6/DMA1.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// DMA controller 6 | public typealias DMA1 = DMA2 7 | 8 | -------------------------------------------------------------------------------- /stm32-neopixel/Sources/STM32F7X6/Device.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// DMA controller 6 | public let dma1 = DMA1(unsafeAddress: 0x40026000) 7 | 8 | /// DMA controller 9 | public let dma2 = DMA2(unsafeAddress: 0x40026400) 10 | 11 | /// General-purpose I/Os 12 | public let gpioa = GPIOA(unsafeAddress: 0x40020000) 13 | 14 | /// General-purpose I/Os 15 | public let gpiob = GPIOB(unsafeAddress: 0x40020400) 16 | 17 | /// General-purpose I/Os 18 | public let gpioi = GPIOI(unsafeAddress: 0x40022000) 19 | 20 | /// Reset and clock control 21 | public let rcc = RCC(unsafeAddress: 0x40023800) 22 | 23 | /// Serial peripheral interface 24 | public let spi1 = SPI1(unsafeAddress: 0x40013000) 25 | 26 | /// Serial peripheral interface 27 | public let spi2 = SPI2(unsafeAddress: 0x40003800) 28 | 29 | /// Universal synchronous asynchronous receiver transmitter 30 | public let usart1 = USART1(unsafeAddress: 0x40011000) 31 | -------------------------------------------------------------------------------- /stm32-neopixel/Sources/STM32F7X6/Empty.swift: -------------------------------------------------------------------------------- 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 | // This file is intentionally left empty for SwiftPM to recognize the target as 13 | // a Swift source module. 14 | -------------------------------------------------------------------------------- /stm32-neopixel/Sources/STM32F7X6/GPIOB.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOB = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-neopixel/Sources/STM32F7X6/GPIOI.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOI = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-neopixel/Sources/STM32F7X6/SPI2.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// Serial peripheral interface 6 | public typealias SPI2 = SPI1 7 | 8 | -------------------------------------------------------------------------------- /stm32-neopixel/Sources/STM32F7X6/stm32f7x6.patched.svd: -------------------------------------------------------------------------------- 1 | ../../../Tools/SVDs/stm32f7x6.patched.svd -------------------------------------------------------------------------------- /stm32-neopixel/Sources/STM32F7X6/svd2swift.json: -------------------------------------------------------------------------------- 1 | { 2 | "peripherals": [ 3 | "DMA1", 4 | "DMA2", 5 | "GPIOA", 6 | "GPIOB", 7 | "GPIOI", 8 | "RCC", 9 | "SPI1", 10 | "SPI2", 11 | "USART1" 12 | ], 13 | "access-level": "public" 14 | } 15 | -------------------------------------------------------------------------------- /stm32-neopixel/Sources/Support/include/Support.h: -------------------------------------------------------------------------------- 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 | #pragma once 13 | 14 | void enableFaults(); 15 | void enableFPU(); 16 | -------------------------------------------------------------------------------- /stm32-neopixel/Sources/Support/startup.S: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | .extern _memcpy 13 | .extern _main 14 | 15 | .text 16 | .thumb 17 | .section __TEXT,__text,regular,pure_instructions 18 | .syntax unified 19 | 20 | // reset handler, main entry point 21 | .balign 4 22 | .global _reset 23 | .thumb_func 24 | _reset: 25 | ldr r0, =0x20010000 // dst 26 | ldr r1, =0x08000000 // src 27 | ldr r2, =segment$start$__VECTORS 28 | ldr r3, =segment$end$__DATA 29 | subs r2, r3, r2 // size = segment$end$__DATA - segment$start$__TEXT 30 | 31 | // memcpy (r0: dst = 0x20010000, r1: src = 0x08000000, r2: size = ...) 32 | // Relocate ourselves: copy the entire image (VECTORS, TEXT, DATA segments) 33 | // from flash memory (non-writable) to RAM (writable), so that globals can be 34 | // written to. 35 | bl _memcpy 36 | 37 | // Cannot jump to main normally, because that would call main using a relative 38 | // offset, which would result in a call to the pre-relocation address. 39 | // Loading address of main into a register will give us the post-relocation 40 | // address. 41 | ldr r0, =_main 42 | blx r0 43 | 44 | // Loop forever if main returns. 45 | b . 46 | -------------------------------------------------------------------------------- /stm32-neopixel/schematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftlang/swift-embedded-examples/622fd0a41dc883d40c1d101a73f678b1fe31b0d5/stm32-neopixel/schematic.png -------------------------------------------------------------------------------- /stm32-uart-echo/Makefile: -------------------------------------------------------------------------------- 1 | ##===----------------------------------------------------------------------===## 2 | ## 3 | ## This source file is part of the Swift open source project 4 | ## 5 | ## Copyright (c) 2023 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 | # Paths 13 | REPOROOT := $(shell git rev-parse --show-toplevel) 14 | TOOLSROOT := $(REPOROOT)/Tools 15 | TOOLSET := $(TOOLSROOT)/Toolsets/stm32f74x.json 16 | MACHO2BIN := $(TOOLSROOT)/macho2bin.py 17 | SWIFT_BUILD := swift build 18 | 19 | # Flags 20 | ARCH := armv7em 21 | TARGET := $(ARCH)-apple-none-macho 22 | SWIFT_BUILD_ARGS := \ 23 | --configuration release \ 24 | --triple $(TARGET) \ 25 | --toolset $(TOOLSET) \ 26 | --disable-local-rpath 27 | BUILDROOT := $(shell $(SWIFT_BUILD) $(SWIFT_BUILD_ARGS) --show-bin-path) 28 | 29 | .PHONY: build 30 | build: 31 | @echo "building..." 32 | $(SWIFT_BUILD) \ 33 | $(SWIFT_BUILD_ARGS) \ 34 | -Xlinker -map -Xlinker $(BUILDROOT)/Application.mangled.map \ 35 | --verbose 36 | 37 | @echo "demangling linker map..." 38 | cat $(BUILDROOT)/Application.mangled.map \ 39 | | c++filt | swift demangle > $(BUILDROOT)/Application.map 40 | 41 | @echo "disassembling..." 42 | otool \ 43 | -arch $(ARCH) -v -V -d -t \ 44 | $(BUILDROOT)/Application \ 45 | | c++filt | swift demangle > $(BUILDROOT)/Application.disassembly 46 | 47 | @echo "extracting binary..." 48 | $(MACHO2BIN) \ 49 | $(BUILDROOT)/Application \ 50 | $(BUILDROOT)/Application.bin \ 51 | --base-address 0x20010000 \ 52 | --segments '__TEXT,__DATA,__VECTORS' 53 | 54 | .PHONY: clean 55 | clean: 56 | @echo "cleaning..." 57 | @swift package clean 58 | @rm -rf .build 59 | -------------------------------------------------------------------------------- /stm32-uart-echo/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "b5206e756e7be7138148bd69eca921f1525d2b038657b90a2712a76adba376d0", 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", 17 | "state" : { 18 | "branch" : "main", 19 | "revision" : "5232c5129a8c70beafc3d6acfbae2716c1b6822a" 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 | } 34 | -------------------------------------------------------------------------------- /stm32-uart-echo/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "stm32-uart-echo", 7 | products: [ 8 | .executable(name: "Application", targets: ["Application"]) 9 | ], 10 | dependencies: [ 11 | .package(url: "https://github.com/apple/swift-mmio", branch: "main") 12 | ], 13 | targets: [ 14 | .executableTarget( 15 | name: "Application", 16 | dependencies: ["STM32F7X6", "Support"]), 17 | // SVD2Swift \ 18 | // --input Sources/STM32F7X6/stm32f7x6.patched.svd \ 19 | // --output Sources/STM32F7X6 \ 20 | // --access-level public \ 21 | // --indentation-width 2 \ 22 | // --peripherals GPIOA GPIOB RCC USART1 23 | .target( 24 | name: "STM32F7X6", 25 | dependencies: [ 26 | .product(name: "MMIO", package: "swift-mmio") 27 | ], 28 | plugins: [ 29 | // Plugin disabled because SwiftPM is slow. 30 | // .plugin(name: "SVD2SwiftPlugin", package: "swift-mmio") 31 | ]), 32 | .target(name: "Support"), 33 | ], 34 | swiftLanguageModes: [.v5]) 35 | -------------------------------------------------------------------------------- /stm32-uart-echo/README.md: -------------------------------------------------------------------------------- 1 | # stm32-uart-echo 2 | 3 | This demo is designed to run on an STM32 microcontroller, concretely the STM32F746G Discovery Kit, and implements a simple "echo" service over UART. 4 | 5 | ## How to build and run this example: 6 | 7 | - Connect the STM32F746G-DISCO board via the ST-LINK USB port to your Mac. 8 | - Open a separate terminal window and run a serial UART monitor program. For example, the macOS built-in `screen` program is able to do that (but there are other popular alternatives, like `minicom`). The ST-LINK device shows up as a "usbmodem" device under `/dev`. 9 | ```console 10 | $ screen /dev/cu.usbmodem<...> 115200 11 | ``` 12 | - Make sure you have a recent nightly Swift toolchain that has Embedded Swift support. 13 | - Install the `stlink` (https://github.com/stlink-org/stlink) command line tools, e.g. via `brew install stlink`. 14 | - Build and upload the program to flash memory of the STM: 15 | ```console 16 | $ cd stm32-uart-echo 17 | $ make 18 | $ st-flash --reset write .build/release/Application.bin 0x08000000 19 | ``` 20 | - The other terminal that runs the UART monitoring program should now be showing a "Hello Swift" message and if you type into the terminal, you will see the letter show up (as they are replied back over the UART). 21 | -------------------------------------------------------------------------------- /stm32-uart-echo/Sources/STM32F7X6/Device.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public let gpioa = GPIOA(unsafeAddress: 0x40020000) 7 | 8 | /// General-purpose I/Os 9 | public let gpiob = GPIOB(unsafeAddress: 0x40020400) 10 | 11 | /// Reset and clock control 12 | public let rcc = RCC(unsafeAddress: 0x40023800) 13 | 14 | /// Universal synchronous asynchronous receiver transmitter 15 | public let usart1 = USART1(unsafeAddress: 0x40011000) 16 | -------------------------------------------------------------------------------- /stm32-uart-echo/Sources/STM32F7X6/Empty.swift: -------------------------------------------------------------------------------- 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 | // This file is intentionally left empty for SwiftPM to recognize the target as 13 | // a Swift source module. 14 | -------------------------------------------------------------------------------- /stm32-uart-echo/Sources/STM32F7X6/GPIOB.swift: -------------------------------------------------------------------------------- 1 | // Generated by svd2swift. 2 | 3 | import MMIO 4 | 5 | /// General-purpose I/Os 6 | public typealias GPIOB = GPIOA 7 | 8 | -------------------------------------------------------------------------------- /stm32-uart-echo/Sources/STM32F7X6/stm32f7x6.patched.svd: -------------------------------------------------------------------------------- 1 | ../../../Tools/SVDs/stm32f7x6.patched.svd -------------------------------------------------------------------------------- /stm32-uart-echo/Sources/STM32F7X6/svd2swift.json: -------------------------------------------------------------------------------- 1 | { 2 | "peripherals": [ 3 | "GPIOA", 4 | "GPIOB", 5 | "RCC", 6 | "USART1" 7 | ], 8 | "access-level": "public" 9 | } 10 | -------------------------------------------------------------------------------- /stm32-uart-echo/Sources/Support/Support.c: -------------------------------------------------------------------------------- 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 | #if defined(__arm__) 13 | 14 | #include 15 | #include 16 | 17 | void *memset(void *b, int c, size_t len) { 18 | for (int i = 0; i < len; i++) { 19 | ((char *)b)[i] = c; 20 | } 21 | return b; 22 | } 23 | 24 | void *memcpy(void *restrict dst, const void *restrict src, size_t n) { 25 | for (int i = 0; i < n; i++) { 26 | ((char *)dst)[i] = ((char *)src)[i]; 27 | } 28 | return dst; 29 | } 30 | 31 | extern void reset(void); 32 | 33 | void interrupt(void) { 34 | while (1) {} 35 | } 36 | 37 | __attribute((used)) __attribute((section("__VECTORS,__text"))) 38 | void *vector_table[114] = { 39 | (void *)0x2000fffc, // initial SP 40 | (void *)((uintptr_t)reset - (0x20010000 - 0x08000000)), // Reset 41 | 42 | (void *)((uintptr_t)interrupt - (0x20010000 - 0x08000000)), // NMI 43 | (void *)((uintptr_t)interrupt - (0x20010000 - 0x08000000)), // HardFault 44 | (void *)((uintptr_t)interrupt - (0x20010000 - 0x08000000)), // MemManage 45 | (void *)((uintptr_t)interrupt - (0x20010000 - 0x08000000)), // BusFault 46 | (void *)((uintptr_t)interrupt - (0x20010000 - 0x08000000)), // UsageFault 47 | 48 | 0 // NULL for all the other handlers 49 | }; 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /stm32-uart-echo/Sources/Support/include/Support.h: -------------------------------------------------------------------------------- 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 | #pragma once 13 | -------------------------------------------------------------------------------- /stm32-uart-echo/Sources/Support/startup.S: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift open source project 4 | // 5 | // Copyright (c) 2023 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 | .extern _memcpy 13 | .extern _main 14 | 15 | .text 16 | .thumb 17 | .section __TEXT,__text,regular,pure_instructions 18 | .syntax unified 19 | 20 | // reset handler, main entry point 21 | .balign 4 22 | .global _reset 23 | .thumb_func 24 | _reset: 25 | ldr r0, =0x20010000 // dst 26 | ldr r1, =0x08000000 // src 27 | ldr r2, =segment$start$__VECTORS 28 | ldr r3, =segment$end$__DATA 29 | subs r2, r3, r2 // size = segment$end$__DATA - segment$start$__TEXT 30 | 31 | // memcpy (r0: dst = 0x20010000, r1: src = 0x08000000, r2: size = ...) 32 | // Relocate ourselves: copy the entire image (VECTORS, TEXT, DATA segments) 33 | // from flash memory (non-writable) to RAM (writable), so that globals can be 34 | // written to. 35 | bl _memcpy 36 | 37 | // Cannot jump to main normally, because that would call main using a relative 38 | // offset, which would result in a call to the pre-relocation address. 39 | // Loading address of main into a register will give us the post-relocation 40 | // address. 41 | ldr r0, =_main 42 | blx r0 43 | 44 | // Loop forever if main returns. 45 | b . 46 | --------------------------------------------------------------------------------