diff --git a/.github/workflows/build-stm.yml b/.github/workflows/build-stm.yml new file mode 100644 index 00000000..4de13860 --- /dev/null +++ b/.github/workflows/build-stm.yml @@ -0,0 +1,44 @@ +name: Build STM32 Examples + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + schedule: + # Build on Mondays at 9am PST every week + - cron: '0 17 * * 1' + +jobs: + build-stm32: + runs-on: ubuntu-24.04 + + strategy: + fail-fast: false + matrix: + example: [stm32-blink] + swift: [swift-DEVELOPMENT-SNAPSHOT-2024-12-04-a] + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.11 + + - name: Install ${{ matrix.swift }} + run: | + wget -q https://download.swift.org/development/ubuntu2404/${{ matrix.swift }}/${{ matrix.swift }}-ubuntu24.04.tar.gz + tar xzf ${{ matrix.swift }}-ubuntu24.04.tar.gz + export PATH="`pwd`/${{ matrix.swift }}-ubuntu24.04/usr/bin/:$PATH" + echo "PATH=$PATH" >> $GITHUB_ENV + swiftc --version + + - name: Build ${{ matrix.example }} + working-directory: ${{ matrix.example }} + run: | + pip3 install -r ../Tools/requirements.txt + export STM_BOARD=STM32F746G_DISCOVERY + ./build-elf.sh diff --git a/Tools/elf2hex.py b/Tools/elf2hex.py new file mode 100755 index 00000000..db0449fa --- /dev/null +++ b/Tools/elf2hex.py @@ -0,0 +1,98 @@ +#!/usr/bin/env -S python3 -u -tt + +# This source file is part of the Swift open source project +# +# Copyright (c) 2023 Apple Inc. and the Swift project authors. +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information + +# +# elf2hex -- Converts a statically-linked ELF executable into an "Intel HEX" +# file format suitable for flashing onto some embedded devices. +# +# Usage: +# $ elf2hex.py [--symbol-map ] +# +# Example: +# $ elf2hex.py ./blink ./blink.hex --symbol-map ./blink.symbols +# + +import argparse +import json +import pathlib + +import elftools.elf.elffile + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('input') + parser.add_argument('output') + parser.add_argument('--symbol-map') + args = parser.parse_args() + + inf = open(args.input, "rb") + outf = open(args.output, "wb") + + def emitrecord(record): + checksum = 0 + pos = 0 + while pos < len(record): + checksum = (checksum + int(record[pos:pos + 2], 16)) % 256 + pos += 2 + checksum = (256 - checksum) % 256 + outf.write((":" + record + f"{checksum:02X}" + "\n").encode()) + + def emit(vmaddr, data): + pos = 0 + while pos < len(data): + chunklen = min(16, len(data) - pos) + chunk = data[pos:pos + chunklen] + chunkhex = chunk.hex().upper() + + assert vmaddr < 0x100000000, f"vmaddr: {vmaddr:x}" + vmaddr_high = (vmaddr >> 16) & 0xffff + recordtype = "04" # Extended Linear Address + emitrecord(f"{2:02X}{0:04X}{recordtype}{vmaddr_high:04X}") + + vmaddr_low = vmaddr & 0xffff + recordtype = "00" # Data + emitrecord(f"{chunklen:02X}{vmaddr_low:04X}{recordtype}{chunkhex}") + + pos += chunklen + vmaddr += chunklen + + elffile = elftools.elf.elffile.ELFFile(inf) + for segment in elffile.iter_segments(): + if segment.header.p_type != "PT_LOAD": + continue + vmaddr = segment.header.p_paddr + data = segment.data() + emit(segment.header.p_paddr, data) + + chunklen = 0 + vmaddr = 0 + recordtype = "01" # EOF + emitrecord(f"{chunklen:02X}{vmaddr:04X}{recordtype}") + + symbol_map = {} + symtab_section = elffile.get_section_by_name(".symtab") + for s in symtab_section.iter_symbols(): + if s.entry.st_info.type not in ["STT_FUNC", "STT_NOTYPE"]: + continue + if s.entry.st_shndx == "SHN_ABS": + continue + if s.name == "": + continue + symbol_map[s.name] = s.entry.st_value + + if args.symbol_map is not None: + pathlib.Path(args.symbol_map).write_text(json.dumps(symbol_map)) + + inf.close() + outf.close() + + +if __name__ == '__main__': + main() diff --git a/Tools/requirements.txt b/Tools/requirements.txt index 08708ebc..c07854d6 100644 --- a/Tools/requirements.txt +++ b/Tools/requirements.txt @@ -1 +1,2 @@ macholib==1.16.3 +pyelftools==0.31 diff --git a/stm32-blink/Board.swift b/stm32-blink/Board.swift new file mode 100644 index 00000000..130932e2 --- /dev/null +++ b/stm32-blink/Board.swift @@ -0,0 +1,115 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2023 Apple Inc. and the Swift project authors. +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +#if STM32F746G_DISCOVERY + +typealias Board = STM32F746Board +enum STM32F746Board { + static func initialize() { + // (1) AHB1ENR[lecConfig.0] = 1 ... enable clock + setRegisterBit( + baseAddress: RCC.BaseAddress, offset: RCC.Offsets.AHB1ENR, + bit: RCC.AHB1ENRBit(for: ledConfig.0), + value: 1) + // (2) MODER[1] = 1 ... set mode to output + setRegisterTwoBitField( + baseAddress: GPIO.GPIOi_BaseAddress, offset: GPIO.Offsets.MODER, + bitsStartingAt: 2 * ledConfig.1, value: 1) + // (3) OTYPER[1] = 0 ... output type is push-pull + setRegisterBit( + baseAddress: GPIO.GPIOi_BaseAddress, offset: GPIO.Offsets.OTYPER, + bit: ledConfig.1, + value: 0) + // (4) OSPEEDR[1] = 2 ... speed is high + setRegisterTwoBitField( + baseAddress: GPIO.GPIOi_BaseAddress, offset: GPIO.Offsets.OSPEEDR, + bitsStartingAt: 2 * ledConfig.1, value: 2) + // (5) PUPDR[1] = 2 ... set pull to down + setRegisterTwoBitField( + baseAddress: GPIO.GPIOi_BaseAddress, offset: GPIO.Offsets.PUPDR, + bitsStartingAt: 2 * ledConfig.1, value: 2) + + ledOff() + } + + static func ledOn() { + // ODR[1] = 1 + setRegisterBit( + baseAddress: GPIO.GPIOi_BaseAddress, offset: GPIO.Offsets.ODR, bit: 1, + value: 1) + } + + static func ledOff() { + // ODR[1] = 0 + setRegisterBit( + baseAddress: GPIO.GPIOi_BaseAddress, offset: GPIO.Offsets.ODR, bit: 1, + value: 0) + } + + static func delay(milliseconds: Int) { + for _ in 0..<10_000 * milliseconds { + nop() + } + } +} + +#elseif NUCLEO_F103RB + +typealias Board = STM32F1Board +enum STM32F1Board { + static func initialize() { + // (1) APB2ENR[ledConfig.0] = 1 ... enable clock + setRegisterBit( + baseAddress: RCC.BaseAddress, offset: RCC.Offsets.APB2ENR, + bit: RCC.APB2ENRBit(for: ledConfig.0), + value: 1) + // (2) CRL.MODE[ledConfig.1] = 0b11 ... set mode to output, high speed + setRegisterTwoBitField( + baseAddress: GPIO.GPIOBaseAddress(for: ledConfig.0), + offset: GPIO.Offsets.CRL, + bitsStartingAt: 4 * ledConfig.1, value: 3) + // (3) CRL.CNF[ledConfig.1] = 0b00 ... general purpose, push-pull + setRegisterTwoBitField( + baseAddress: GPIO.GPIOBaseAddress(for: ledConfig.0), + offset: GPIO.Offsets.CRL, + bitsStartingAt: 4 * ledConfig.1 + 2, value: 0) + + ledOff() + } + + static func ledOn() { + // ODR[ledConfig.1] = 1 + setRegisterBit( + baseAddress: GPIO.GPIOBaseAddress(for: ledConfig.0), + offset: GPIO.Offsets.ODR, bit: ledConfig.1, + value: 1) + } + + static func ledOff() { + // ODR[ledConfig.1] = 0 + setRegisterBit( + baseAddress: GPIO.GPIOBaseAddress(for: ledConfig.0), + offset: GPIO.Offsets.ODR, bit: ledConfig.1, + value: 0) + } + + static func delay(milliseconds: Int) { + for _ in 0..<10_000 * milliseconds { + nop() + } + } +} + +#else + +#error("Unknown board") + +#endif diff --git a/stm32-blink/BridgingHeader.h b/stm32-blink/BridgingHeader.h index 5a160e8b..6b807332 100644 --- a/stm32-blink/BridgingHeader.h +++ b/stm32-blink/BridgingHeader.h @@ -11,16 +11,6 @@ #pragma once -#include - -static inline __attribute((always_inline)) uint32_t volatile_load_uint32_t(const volatile uint32_t * _Nonnull source) { - return *((const volatile uint32_t * _Nonnull) source); -} - -static inline __attribute((always_inline)) void volatile_store_uint32_t(volatile uint32_t * _Nonnull destination, uint32_t value) { - *((volatile uint32_t * _Nonnull) destination) = value; -} - static inline __attribute((always_inline)) void nop() { asm volatile("nop"); } diff --git a/stm32-blink/Main.swift b/stm32-blink/Main.swift index c21650e3..43d23858 100644 --- a/stm32-blink/Main.swift +++ b/stm32-blink/Main.swift @@ -9,59 +9,26 @@ // //===----------------------------------------------------------------------===// -enum STM32F746Board { - static func initialize() { - // Configure pin I1 as an LED +#if STM32F746G_DISCOVERY - // (1) AHB1ENR[i] = 1 ... enable clock - setRegisterBit( - baseAddress: RCC.BaseAddress, offset: RCC.Offsets.AHB1ENR, bit: 8, - value: 1) - // (2) MODER[1] = 1 ... set mode to output - setRegisterTwoBitField( - baseAddress: GPIO.GPIOi_BaseAddress, offset: GPIO.Offsets.MODER, - bitsStartingAt: 2, value: 1) - // (3) OTYPER[1] = 0 ... output type is push-pull - setRegisterBit( - baseAddress: GPIO.GPIOi_BaseAddress, offset: GPIO.Offsets.OTYPER, bit: 1, - value: 0) - // (4) OSPEEDR[1] = 2 ... speed is high - setRegisterTwoBitField( - baseAddress: GPIO.GPIOi_BaseAddress, offset: GPIO.Offsets.OSPEEDR, - bitsStartingAt: 2, value: 2) - // (5) PUPDR[1] = 2 ... set pull to down - setRegisterTwoBitField( - baseAddress: GPIO.GPIOi_BaseAddress, offset: GPIO.Offsets.PUPDR, - bitsStartingAt: 2, value: 2) +// I1 pin aka "Arduino D13" pin on STM32F746 Discovery Board +// https://www.st.com/resource/en/schematic_pack/mb1191-f746ngh6-c01_schematic.pdf +let ledConfig: (GPIOBank, GPIOPin) = (.i, 1) - ledOff() - } +#elseif NUCLEO_F103RB - static func ledOn() { - // ODR[1] = 1 - setRegisterBit( - baseAddress: GPIO.GPIOi_BaseAddress, offset: GPIO.Offsets.ODR, bit: 1, - value: 1) - } +// A5 pin aka "Arduino D13" pin on Nucleo-64 boards +// https://www.st.com/resource/en/user_manual/um1724-stm32-nucleo64-boards-mb1136-stmicroelectronics.pdf +let ledConfig: (GPIOBank, GPIOPin) = (.a, 5) - static func ledOff() { - // ODR[1] = 0 - setRegisterBit( - baseAddress: GPIO.GPIOi_BaseAddress, offset: GPIO.Offsets.ODR, bit: 1, - value: 0) - } +#else - static func delay(milliseconds: Int) { - for _ in 0..<10_000 * milliseconds { - nop() - } - } -} +#error("Unknown board") + +#endif @main struct Main { - typealias Board = STM32F746Board - static func main() { Board.initialize() diff --git a/stm32-blink/README.md b/stm32-blink/README.md index 9289f7d5..5ae799f3 100644 --- a/stm32-blink/README.md +++ b/stm32-blink/README.md @@ -4,19 +4,41 @@ This example shows a simple baremetal firmware for an STM32 board that blinks an -## How to build and run this example: +## Requirements - Connect the STM32F746G-DISCO board via the ST-LINK USB port to your Mac. -- Make sure you have a recent nightly Swift toolchain that has Embedded Swift support. +- Download and install a [recent nightly Swift toolchain](https://swift.org/download). Use the "Development Snapshot" from "main". - Install the [`stlink`](https://github.com/stlink-org/stlink) command line tools, e.g. via `brew install stlink`. + +## Building and running the firmware as Mach-O on macOS + - Build and upload the program to flash memory of the microcontroller: ```console $ cd stm32-blink -$ TOOLCHAINS='' ./build.sh +$ export TOOLCHAINS=$(plutil -extract CFBundleIdentifier raw /Library/Developer/Toolchains/swift-latest.xctoolchain/Info.plist) +$ export STM_BOARD=STM32F746G_DISCOVERY # or NUCLEO_F103RB +$ ./build-macho.sh $ st-flash --reset write .build/blink.bin 0x08000000 ``` - The green LED next to the RESET button should now be blinking in a pattern. +## Building and running the firmware as ELF (on either macOS or Linux) + +- Build and upload the program to flash memory of the microcontroller: +```console +$ cd stm32-blink + +# If on macOS, select the right latest nightly toolchain (on Linux this is not needed): +$ export TOOLCHAINS=$(plutil -extract CFBundleIdentifier raw /Library/Developer/Toolchains/swift-latest.xctoolchain/Info.plist) + +$ export STM_BOARD=STM32F746G_DISCOVERY # or NUCLEO_F103RB +$ ./build-elf.sh +$ st-flash --format ihex --reset write .build/blink.hex +``` +- The green LED next to the RESET button should now be blinking in a pattern. + +## Binary size + The resulting size of the compiled and linked binary is very small (which shouldn't be surprising given that this toy example only blinks an LED), and demonstrates how the Embedded Swift compilation mode doesn't include unnecessary code or data in the resulting program: ```console diff --git a/stm32-blink/Registers.swift b/stm32-blink/Registers.swift index ef22eec9..ccbf13fb 100644 --- a/stm32-blink/Registers.swift +++ b/stm32-blink/Registers.swift @@ -11,45 +11,51 @@ // swift-format-ignore-file -extension UnsafeMutablePointer where Pointee == UInt32 { - func volatileLoad() -> Pointee { - return volatile_load_uint32_t(self) - } +import _Volatile - func volatileStore(_ value: Pointee) { - volatile_store_uint32_t(self, value) - } +#if STM32F746G_DISCOVERY + +// Register definitions for STM32F746NG MCU +// https://www.st.com/resource/en/reference_manual/rm0385-stm32f75xxx-and-stm32f74xxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf + +enum GPIOBank: Int { + case a, b, c, d, e, f, g, h, i, j, k } +typealias GPIOPin = Int enum RCC { static let BaseAddress = UnsafeMutablePointer(bitPattern: 0x40023800 as UInt)! enum Offsets { - static let CR = 0x0 - static let PLLCFGR = 0x4 - static let CFGR = 0x8 - static let CIR = 0xc - static let AHB1RSTR = 0x10 - static let AHB2RSTR = 0x14 - static let AHB3RSTR = 0x18 - static let APB1RSTR = 0x20 - static let APB2RSTR = 0x24 static let AHB1ENR = 0x30 - static let AHB2ENR = 0x34 - static let AHB3ENR = 0x38 - static let APB1ENR = 0x40 - static let APB2ENR = 0x44 - static let AHB1LPENR = 0x50 - static let AHB2LPENR = 0x54 - static let AHB3LPENR = 0x58 - static let APB1LPENR = 0x60 - static let APB2LPENR = 0x64 - static let BDCR = 0x70 - static let CSR = 0x74 - static let SSCGR = 0x80 - static let PLLI2SCFGR = 0x84 - static let PLLSAICFGR = 0x88 - static let DKCFGR1 = 0x8c - static let DKCFGR2 = 0x90 + } + enum Bits { + static let AHB1ENR_GPIOAEN = 0 + static let AHB1ENR_GPIOBEN = 1 + static let AHB1ENR_GPIOCEN = 2 + static let AHB1ENR_GPIODEN = 3 + static let AHB1ENR_GPIOEEN = 4 + static let AHB1ENR_GPIOFEN = 5 + static let AHB1ENR_GPIOGEN = 6 + static let AHB1ENR_GPIOHEN = 7 + static let AHB1ENR_GPIOIEN = 8 + static let AHB1ENR_GPIOJEN = 9 + static let AHB1ENR_GPIOKEN = 10 + } + + static func AHB1ENRBit(for bank: GPIOBank) -> Int { + return switch bank { + case .a: Bits.AHB1ENR_GPIOAEN + case .b: Bits.AHB1ENR_GPIOBEN + case .c: Bits.AHB1ENR_GPIOCEN + case .d: Bits.AHB1ENR_GPIODEN + case .e: Bits.AHB1ENR_GPIOEEN + case .f: Bits.AHB1ENR_GPIOFEN + case .g: Bits.AHB1ENR_GPIOGEN + case .h: Bits.AHB1ENR_GPIOHEN + case .i: Bits.AHB1ENR_GPIOIEN + case .j: Bits.AHB1ENR_GPIOJEN + case .k: Bits.AHB1ENR_GPIOKEN + } } } @@ -73,30 +79,100 @@ enum GPIO { static let PUPDR = 0xc static let IDR = 0x10 static let ODR = 0x14 - static let BSRR = 0x18 - static let LCKR = 0x1c - static let AFRL = 0x20 - static let AFRH = 0x24 - static let BRR = 0x28 } } +#elseif NUCLEO_F103RB + +// Register definitions for STM32F103RB MCU +// https://www.st.com/resource/en/reference_manual/rm0008-stm32f101xx-stm32f102xx-stm32f103xx-stm32f105xx-and-stm32f107xx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf + +enum GPIOBank: Int { + case a, b, c, d, e, f, g +} +typealias GPIOPin = Int + +enum RCC { + static let BaseAddress = UnsafeMutablePointer(bitPattern: 0x40021000 as UInt)! + enum Offsets { + static let APB2ENR = 0x18 + } + enum Bits { + static let APB2ENR_IOPAEN = 2 + static let APB2ENR_IOPBEN = 3 + static let APB2ENR_IOPCEN = 4 + static let APB2ENR_IOPDEN = 5 + static let APB2ENR_IOPEEN = 6 + static let APB2ENR_IOPFEN = 7 + static let APB2ENR_IOPGEN = 8 + } + + static func APB2ENRBit(for bank: GPIOBank) -> Int { + return switch bank { + case .a: Bits.APB2ENR_IOPAEN + case .b: Bits.APB2ENR_IOPBEN + case .c: Bits.APB2ENR_IOPCEN + case .d: Bits.APB2ENR_IOPDEN + case .e: Bits.APB2ENR_IOPEEN + case .f: Bits.APB2ENR_IOPFEN + case .g: Bits.APB2ENR_IOPGEN + } + } +} + +enum GPIO { + static let GPIOa_BaseAddress = UnsafeMutablePointer(bitPattern: 0x40010800 as UInt)! + static let GPIOb_BaseAddress = UnsafeMutablePointer(bitPattern: 0x40010c00 as UInt)! + static let GPIOc_BaseAddress = UnsafeMutablePointer(bitPattern: 0x40011000 as UInt)! + static let GPIOd_BaseAddress = UnsafeMutablePointer(bitPattern: 0x40011400 as UInt)! + static let GPIOe_BaseAddress = UnsafeMutablePointer(bitPattern: 0x40011800 as UInt)! + static let GPIOf_BaseAddress = UnsafeMutablePointer(bitPattern: 0x40011c00 as UInt)! + static let GPIOg_BaseAddress = UnsafeMutablePointer(bitPattern: 0x40012000 as UInt)! + + static func GPIOBaseAddress(for bank: GPIOBank) -> UnsafeMutablePointer { + return switch bank { + case .a: GPIOa_BaseAddress + case .b: GPIOb_BaseAddress + case .c: GPIOc_BaseAddress + case .d: GPIOd_BaseAddress + case .e: GPIOe_BaseAddress + case .f: GPIOf_BaseAddress + case .g: GPIOg_BaseAddress + } + } + + enum Offsets { + static let CRL = 0x0 + static let CRH = 0x4 + static let IDR = 0x8 + static let ODR = 0xc + } +} + +#else + +#error("Unknown board") + +#endif + func setRegisterBit(baseAddress: UnsafeMutablePointer, offset: Int, bit: Int, value: Int) { precondition(offset % 4 == 0) precondition(bit >= 0 && bit < 32) precondition(value >= 0 && value < 2) let p = baseAddress.advanced(by: offset / 4) - let previousValue: UInt32 = p.volatileLoad() + let m = VolatileMappedRegister(unsafeBitPattern: UInt(bitPattern: p)) + let previousValue: UInt32 = m.load() let newValue: UInt32 = previousValue & ~(1 << UInt32(bit)) | (UInt32(value) << UInt32(bit)) - p.volatileStore(newValue) + m.store(newValue) } func setRegisterTwoBitField(baseAddress: UnsafeMutablePointer, offset: Int, bitsStartingAt: Int, value: Int) { precondition(offset % 4 == 0) - precondition(bitsStartingAt >= 0 && bitsStartingAt < 16) + precondition(bitsStartingAt >= 0 && bitsStartingAt < 31) precondition(value >= 0 && value < 4) let p = baseAddress.advanced(by: offset / 4) - let previousValue: UInt32 = p.volatileLoad() + let m = VolatileMappedRegister(unsafeBitPattern: UInt(bitPattern: p)) + let previousValue: UInt32 = m.load() let newValue: UInt32 = previousValue & ~(0b11 << UInt32(bitsStartingAt)) | (UInt32(value) << UInt32(bitsStartingAt)) - p.volatileStore(newValue) + m.store(newValue) } diff --git a/stm32-blink/Startup.c b/stm32-blink/Startup.c index 16ea6367..9237afb1 100644 --- a/stm32-blink/Startup.c +++ b/stm32-blink/Startup.c @@ -22,9 +22,16 @@ void interrupt(void) { while (1) {} } -__attribute((used)) __attribute((section("__VECTORS,__text"))) +__attribute((used)) +#if defined(__ELF__) +__attribute((section(".vectors"))) +#elif defined(__MACH__) +__attribute((section("__VECTORS,__text"))) +#else +#error Unknown file format +#endif void *vector_table[114] = { - (void *)0x2000fffc, // initial SP + (void *)0x20001ffc, // initial SP, assume we have 8 KB of SRAM reset, // Reset interrupt, // NMI diff --git a/stm32-blink/build-elf.sh b/stm32-blink/build-elf.sh new file mode 100755 index 00000000..afd9413e --- /dev/null +++ b/stm32-blink/build-elf.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +set -vex + +# Determine file paths +REPOROOT=$(realpath -- "$(dirname "${BASH_SOURCE[0]}")")/.. +TOOLSROOT="$REPOROOT/Tools" +SRCROOT="$REPOROOT/stm32-blink" +BUILDROOT="$SRCROOT/.build" + +# Clean the build directory +rm -r "$BUILDROOT" || true + +# Setup tools and build flags +TARGET=armv7em-none-none-eabi + +if [[ ! "$STM_BOARD" ]] ; then + echo "STM_BOARD must be set to STM32F746G_DISCOVERY or NUCLEO_F103RB" + exit 1 +fi + +SWIFT_EXEC=${SWIFT_EXEC:-$(which swiftc)} +SWIFT_FLAGS="-target $TARGET -Osize" +SWIFT_FLAGS+=" -import-bridging-header $SRCROOT/BridgingHeader.h -wmo -enable-experimental-feature Embedded" +SWIFT_FLAGS+=" -Xfrontend -function-sections -D${STM_BOARD}" + +CLANG_EXEC=${CLANG_EXEC:-$(which clang)} +CLANG_FLAGS="-target $TARGET -Oz" + +LD_EXEC=${LD_EXEC:-$CLANG_EXEC} +LD_FLAGS="-target $TARGET -fuse-ld=lld -nostdlib -static -Wl,-e,vector_table -Wl,--gc-sections -Wl,-T,$SRCROOT/elf-linkerscript.ld" + +# Create build directory +mkdir -p "$BUILDROOT" + +# Build Swift sources +# shellcheck disable=SC2086 # intentional splitting +"$SWIFT_EXEC" $SWIFT_FLAGS -c $SRCROOT/*.swift -o "$BUILDROOT/blink.o" + +# Build C sources +# shellcheck disable=SC2086 # intentional splitting +"$CLANG_EXEC" $CLANG_FLAGS -c "$SRCROOT/Startup.c" -o "$BUILDROOT/Startup.o" + +# Link objects into executable +# shellcheck disable=SC2086 # intentional splitting +"$LD_EXEC" $LD_FLAGS "$BUILDROOT/blink.o" "$BUILDROOT/Startup.o" -o "$BUILDROOT/blink.elf" + +# Convert to Intel HEX for flashing +"$TOOLSROOT"/elf2hex.py "$BUILDROOT/blink.elf" "$BUILDROOT/blink.hex" + +# Echo final binary path +ls -al "$BUILDROOT/blink.hex" diff --git a/stm32-blink/build-macho.sh b/stm32-blink/build-macho.sh new file mode 100755 index 00000000..b45c44cc --- /dev/null +++ b/stm32-blink/build-macho.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +set -vex + +# Determine file paths +REPOROOT=$(git rev-parse --show-toplevel) +TOOLSROOT="$REPOROOT/Tools" +SRCROOT="$REPOROOT/stm32-blink" +BUILDROOT="$SRCROOT/.build" + +# Clean the build directory +rm -r "$BUILDROOT" || true + +# Setup tools and build flags +TARGET=armv7em-apple-none-macho + +if [[ ! "$STM_BOARD" ]] ; then + echo "STM_BOARD must be set to STM32F746G_DISCOVERY or NUCLEO_F103RB" + exit 1 +fi + +SWIFT_EXEC=${SWIFT_EXEC:-$(xcrun -f swiftc)} +SWIFT_FLAGS="-target $TARGET -Osize" +SWIFT_FLAGS+=" -import-bridging-header $SRCROOT/BridgingHeader.h -wmo -enable-experimental-feature Embedded" +SWIFT_FLAGS+=" -Xcc -D__APPLE__ -Xcc -D__MACH__ -Xcc -ffreestanding" +SWIFT_FLAGS+=" -D${STM_BOARD}" + +CLANG_EXEC=${CLANG_EXEC:-$(xcrun -f clang)} +CLANG_FLAGS="-target $TARGET -Oz" + +LD_EXEC=${LD_EXEC:-$CLANG_EXEC} +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" + +PYTHON_EXEC=${PYTHON_EXEC:-$(xcrun -f python3)} +MACHO2BIN="$TOOLSROOT/macho2bin.py" + +# Create build directory +mkdir -p "$BUILDROOT" + +# Build Swift sources +# shellcheck disable=SC2086 # intentional splitting +"$SWIFT_EXEC" $SWIFT_FLAGS -c "$SRCROOT/"*.swift -o "$BUILDROOT/blink.o" + +# Build C sources +# shellcheck disable=SC2086 # intentional splitting +"$CLANG_EXEC" $CLANG_FLAGS -c "$SRCROOT/Startup.c" -o "$BUILDROOT/Startup.o" + +# Link objects into executable +# shellcheck disable=SC2086 # intentional splitting +"$LD_EXEC" $LD_FLAGS "$BUILDROOT/blink.o" "$BUILDROOT/Startup.o" -o "$BUILDROOT/blink" + +# Extract sections from executable into flashable binary +"$PYTHON_EXEC" "$MACHO2BIN" "$BUILDROOT/blink" "$BUILDROOT/blink.bin" --base-address 0x08000000 --segments '__TEXT,__DATA,__VECTORS' + +# Echo final binary path +ls -al "$BUILDROOT/blink.bin" diff --git a/stm32-blink/build.sh b/stm32-blink/build.sh deleted file mode 100755 index adc54c8c..00000000 --- a/stm32-blink/build.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/sh - -set -vex - -# Determine file paths -REPOROOT=$(git rev-parse --show-toplevel) -TOOLSROOT="$REPOROOT/Tools" -SRCROOT="$REPOROOT/stm32-blink" -BUILDROOT="$SRCROOT/.build" - -# Setup tools and build flags -TARGET=armv7-apple-none-macho - -SWIFT_EXEC=${SWIFT_EXEC:-$(xcrun -f swiftc)} -SWIFT_FLAGS="-target $TARGET -Osize -import-bridging-header $SRCROOT/BridgingHeader.h -wmo -enable-experimental-feature Embedded -Xcc -D__APPLE__ -Xcc -D__MACH__ -Xcc -ffreestanding" - -CLANG_EXEC=${CLANG_EXEC:-$(xcrun -f clang)} -CLANG_FLAGS="-target $TARGET -Oz" - -LD_EXEC=${LD_EXEC:-$CLANG_EXEC} -LD_FLAGS="-target $TARGET -nostdlib -static -Wl,-e,_reset -dead_strip -Wl,-no_zero_fill_sections -Wl,-segalign,4 -Wl,-segaddr,__VECTORS,0x00200000 -Wl,-seg1addr,0x00200200 -Wl,-pagezero_size,0" - -PYTHON_EXEC=${PYTHON_EXEC:-$(xcrun -f python3)} -MACHO2BIN="$TOOLSROOT/macho2bin.py" - -# Create build directory -mkdir -p "$BUILDROOT" - -# Build Swift sources -"$SWIFT_EXEC" "$SWIFT_FLAGS" -c "$SRCROOT/*.swift" -o "$BUILDROOT/blink.o" - -# Build C sources -"$CLANG_EXEC" "$CLANG_FLAGS" -c "$SRCROOT/Startup.c" -o "$BUILDROOT/Startup.o" - -# Link objects into executable -"$LD_EXEC" "$LD_FLAGS" "$BUILDROOT/blink.o" "$BUILDROOT/Startup.o" -o "$BUILDROOT/blink" - -# Extract sections from executable into flashable binary -"$PYTHON_EXEC" "$MACHO2BIN" "$BUILDROOT/blink" "$BUILDROOT/blink.bin" --base-address 0x00200000 --segments '__TEXT,__DATA,__VECTORS' - -# Echo final binary path -ls -al "$BUILDROOT/blink.bin" diff --git a/stm32-blink/elf-linkerscript.ld b/stm32-blink/elf-linkerscript.ld new file mode 100644 index 00000000..98d5f00a --- /dev/null +++ b/stm32-blink/elf-linkerscript.ld @@ -0,0 +1,13 @@ +MEMORY +{ + flash : ORIGIN = 0x08000000, LENGTH = 32K + sram : ORIGIN = 0x20000000, LENGTH = 8K +} + +SECTIONS +{ + .text : { *(.vectors*) ; *(.text*) } > flash + .bss : { *(.bss*) } > sram + .data : { *(.data*) } > sram + /DISCARD/ : { *(.swift_modhash*) } +}