From d36b009603f3d867467c47172a9641fe1ecc0428 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Mon, 13 Jan 2025 22:19:01 -0800 Subject: [PATCH 1/3] Make stm32-blink sample building as both Mach-O and ELF --- .github/workflows/build-stm.yml | 43 +++++++ Tools/elf2hex.py | 92 ++++++++++++++ Tools/requirements.txt | 1 + stm32-blink/Main.swift | 112 ++++++++++++++++- stm32-blink/README.md | 25 +++- stm32-blink/Registers.swift | 145 ++++++++++++++++++----- stm32-blink/Startup.c | 11 +- stm32-blink/build-elf.sh | 42 +++++++ stm32-blink/{build.sh => build-macho.sh} | 11 +- stm32-blink/elf-linkerscript.ld | 13 ++ 10 files changed, 451 insertions(+), 44 deletions(-) create mode 100644 .github/workflows/build-stm.yml create mode 100755 Tools/elf2hex.py create mode 100755 stm32-blink/build-elf.sh rename stm32-blink/{build.sh => build-macho.sh} (80%) create mode 100644 stm32-blink/elf-linkerscript.ld diff --git a/.github/workflows/build-stm.yml b/.github/workflows/build-stm.yml new file mode 100644 index 0000000..60a49f4 --- /dev/null +++ b/.github/workflows/build-stm.yml @@ -0,0 +1,43 @@ +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 + ./build-elf.sh diff --git a/Tools/elf2hex.py b/Tools/elf2hex.py new file mode 100755 index 0000000..54a0b26 --- /dev/null +++ b/Tools/elf2hex.py @@ -0,0 +1,92 @@ +#!/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 os +import pathlib +import json +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 08708eb..c07854d 100644 --- a/Tools/requirements.txt +++ b/Tools/requirements.txt @@ -1 +1,2 @@ macholib==1.16.3 +pyelftools==0.31 diff --git a/stm32-blink/Main.swift b/stm32-blink/Main.swift index c21650e..ad04c60 100644 --- a/stm32-blink/Main.swift +++ b/stm32-blink/Main.swift @@ -9,10 +9,22 @@ // //===----------------------------------------------------------------------===// +enum GPIOBank: Int { + case a, b, c, d, e, f, g, h, i, j, k +} +typealias GPIOPin = Int + +// I1 pin on STM32F746 Discovery Board +//let ledConfig: (GPIOBank, GPIOPin) = (.i, 1) + +// A5 aka "Arduino D13" pin on Nucleo-64 boards +let ledConfig: (GPIOBank, GPIOPin) = (.a, 5) + +#if STM32F74_F75 + +typealias Board = STM32F746Board enum STM32F746Board { static func initialize() { - // Configure pin I1 as an LED - // (1) AHB1ENR[i] = 1 ... enable clock setRegisterBit( baseAddress: RCC.BaseAddress, offset: RCC.Offsets.AHB1ENR, bit: 8, @@ -58,10 +70,102 @@ enum STM32F746Board { } } +#elseif STM32F1 + +typealias Board = STM32F1Board +enum STM32F1Board { + static func initialize() { + // (1) IOPENR[ledConfig.0] = 1 ... enable clock + setRegisterBit( + baseAddress: RCC.BaseAddress, offset: RCC.Offsets.APB2ENR, bit: RCC.APB2ENRBit(for: ledConfig.0), + value: 1) + // (2) MODE[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) CNF[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[1] = 1 + setRegisterBit( + baseAddress: GPIO.GPIOBaseAddress(for: ledConfig.0), offset: GPIO.Offsets.ODR, bit: ledConfig.1, + value: 1) + } + + static func ledOff() { + // ODR[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() + } + } +} + +#elseif STM32C0 + +typealias Board = STM32C0Board +enum STM32C0Board { + static func initialize() { + // (1) IOPENR[ledConfig.0] = 1 ... enable clock + setRegisterBit( + baseAddress: RCC.BaseAddress, offset: RCC.Offsets.IOPENR, bit: ledConfig.0.rawValue, + value: 1) + // (2) MODER[1] = 1 ... set mode to output + setRegisterTwoBitField( + baseAddress: GPIO.GPIOBaseAddress(for: ledConfig.0), offset: GPIO.Offsets.MODER, + bitsStartingAt: 2 * ledConfig.1, value: 1) + // (3) OTYPER[1] = 0 ... output type is push-pull + setRegisterBit( + baseAddress: GPIO.GPIOBaseAddress(for: ledConfig.0), offset: GPIO.Offsets.OTYPER, bit: ledConfig.1, + value: 0) + // (4) OSPEEDR[1] = 2 ... speed is high + setRegisterTwoBitField( + baseAddress: GPIO.GPIOBaseAddress(for: ledConfig.0), offset: GPIO.Offsets.OSPEEDR, + bitsStartingAt: 2 * ledConfig.1, value: 2) + // (5) PUPDR[1] = 2 ... set pull to down + setRegisterTwoBitField( + baseAddress: GPIO.GPIOBaseAddress(for: ledConfig.0), offset: GPIO.Offsets.PUPDR, + bitsStartingAt: 2 * ledConfig.1, value: 2) + + ledOff() + } + + static func ledOn() { + // ODR[1] = 1 + setRegisterBit( + baseAddress: GPIO.GPIOBaseAddress(for: ledConfig.0), offset: GPIO.Offsets.ODR, bit: ledConfig.1, + value: 1) + } + + static func ledOff() { + // ODR[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() + } + } +} + +#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 9289f7d..df2a0d9 100644 --- a/stm32-blink/README.md +++ b/stm32-blink/README.md @@ -4,19 +4,38 @@ 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) +$ ./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 +$ export TOOLCHAINS=$(plutil -extract CFBundleIdentifier raw /Library/Developer/Toolchains/swift-latest.xctoolchain/Info.plist) +$ ./build-elf.sh +$ st-util +(then in a separate terminal) +$ st-flash --reset write .build/blink.elf 0x08000000 +``` +- 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 ef22eec..5a5bbce 100644 --- a/stm32-blink/Registers.swift +++ b/stm32-blink/Registers.swift @@ -21,35 +21,12 @@ extension UnsafeMutablePointer where Pointee == UInt32 { } } +#if STM32F74_F75 + 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 } } @@ -73,14 +50,120 @@ 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 STM32F1 + +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 + default: fatalError("invalid bank") + } + } +} + +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 + default: fatalError("invalid bank") + } + } + + enum Offsets { + static let CRL = 0x0 + static let CRH = 0x4 + static let IDR = 0x8 + static let ODR = 0xc + } +} + +#elseif STM32C0 + +enum RCC { + static let BaseAddress = UnsafeMutablePointer(bitPattern: 0x40021000 as UInt)! + enum Offsets { + static let IOPENR = 0x34 + } + enum Bits { + static let IOPENR_GPIOAEN = 0 + static let IOPENR_GPIOBEN = 1 + static let IOPENR_GPIOCEN = 2 + static let IOPENR_GPIODEN = 3 + static let IOPENR_GPIOFEN = 5 + } +} + +enum GPIO { + static let GPIOa_BaseAddress = UnsafeMutablePointer(bitPattern: 0x50000000 as UInt)! + static let GPIOb_BaseAddress = UnsafeMutablePointer(bitPattern: 0x50000400 as UInt)! + static let GPIOc_BaseAddress = UnsafeMutablePointer(bitPattern: 0x50000800 as UInt)! + static let GPIOd_BaseAddress = UnsafeMutablePointer(bitPattern: 0x50000c00 as UInt)! + static let GPIOf_BaseAddress = UnsafeMutablePointer(bitPattern: 0x50001400 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 .f: GPIOf_BaseAddress + default: fatalError("invalid bank") + } + } + + enum Offsets { + static let MODER = 0x0 + static let OTYPER = 0x4 + static let OSPEEDR = 0x8 + static let PUPDR = 0xc + static let IDR = 0x10 + static let ODR = 0x14 + } +} + +#else + +#error("Unknown MCU") + +#endif + func setRegisterBit(baseAddress: UnsafeMutablePointer, offset: Int, bit: Int, value: Int) { precondition(offset % 4 == 0) precondition(bit >= 0 && bit < 32) @@ -93,7 +176,7 @@ func setRegisterBit(baseAddress: UnsafeMutablePointer, offset: Int, bit: 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() diff --git a/stm32-blink/Startup.c b/stm32-blink/Startup.c index 16ea636..88f8eb1 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 *)0x20002ffc, // initial SP, assume we have 12 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 0000000..009efcb --- /dev/null +++ b/stm32-blink/build-elf.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +set -vex + +# Determine file paths +REPOROOT=$(realpath -e -- $(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 + +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+=" -Xcc -D__APPLE__ -Xcc -D__MACH__ -Xcc -ffreestanding" +SWIFT_FLAGS+=" -Xfrontend -function-sections -DSTM32F1" + +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 +"$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.elf" + +# Convert to Intel HEX for flashing +$TOOLSROOT/elf2hex.py "$BUILDROOT/blink.elf" "$BUILDROOT/blink.hex" diff --git a/stm32-blink/build.sh b/stm32-blink/build-macho.sh similarity index 80% rename from stm32-blink/build.sh rename to stm32-blink/build-macho.sh index adc54c8..f3d8743 100755 --- a/stm32-blink/build.sh +++ b/stm32-blink/build-macho.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -vex @@ -8,6 +8,9 @@ TOOLSROOT="$REPOROOT/Tools" SRCROOT="$REPOROOT/stm32-blink" BUILDROOT="$SRCROOT/.build" +# Clean the build directory +rm -r $BUILDROOT || true + # Setup tools and build flags TARGET=armv7-apple-none-macho @@ -27,13 +30,13 @@ MACHO2BIN="$TOOLSROOT/macho2bin.py" mkdir -p "$BUILDROOT" # Build Swift sources -"$SWIFT_EXEC" "$SWIFT_FLAGS" -c "$SRCROOT/*.swift" -o "$BUILDROOT/blink.o" +"$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" +"$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" +"$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' diff --git a/stm32-blink/elf-linkerscript.ld b/stm32-blink/elf-linkerscript.ld new file mode 100644 index 0000000..d72351a --- /dev/null +++ b/stm32-blink/elf-linkerscript.ld @@ -0,0 +1,13 @@ +MEMORY +{ + flash : ORIGIN = 0x08000000, LENGTH = 32K + sram : ORIGIN = 0x20000000, LENGTH = 12K +} + +SECTIONS +{ + .text : { *(.vectors*) ; *(.text*) } > flash + .bss : { *(.bss*) } > sram + .data : { *(.data*) } > sram + /DISCARD/ : { *(.swift_modhash*) } +} From 4360aa50c3411ef4daf68e7553129d4314f33d82 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Thu, 16 Jan 2025 10:52:04 -0800 Subject: [PATCH 2/3] Use STM_BOARD env var to select which board to target on stm32-blink example --- .github/workflows/build-stm.yml | 1 + stm32-blink/Board.swift | 115 +++++++++++++++++++++++ stm32-blink/BridgingHeader.h | 10 -- stm32-blink/Main.swift | 157 ++------------------------------ stm32-blink/README.md | 9 +- stm32-blink/Registers.swift | 115 +++++++++++------------ stm32-blink/Startup.c | 2 +- stm32-blink/build-elf.sh | 20 +++- stm32-blink/build-macho.sh | 23 +++-- stm32-blink/elf-linkerscript.ld | 2 +- 10 files changed, 220 insertions(+), 234 deletions(-) create mode 100644 stm32-blink/Board.swift diff --git a/.github/workflows/build-stm.yml b/.github/workflows/build-stm.yml index 60a49f4..4de1386 100644 --- a/.github/workflows/build-stm.yml +++ b/.github/workflows/build-stm.yml @@ -40,4 +40,5 @@ jobs: working-directory: ${{ matrix.example }} run: | pip3 install -r ../Tools/requirements.txt + export STM_BOARD=STM32F746G_DISCOVERY ./build-elf.sh diff --git a/stm32-blink/Board.swift b/stm32-blink/Board.swift new file mode 100644 index 0000000..130932e --- /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 5a160e8..6b80733 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 ad04c60..43d2385 100644 --- a/stm32-blink/Main.swift +++ b/stm32-blink/Main.swift @@ -9,158 +9,21 @@ // //===----------------------------------------------------------------------===// -enum GPIOBank: Int { - case a, b, c, d, e, f, g, h, i, j, k -} -typealias GPIOPin = Int - -// I1 pin on STM32F746 Discovery Board -//let ledConfig: (GPIOBank, GPIOPin) = (.i, 1) - -// A5 aka "Arduino D13" pin on Nucleo-64 boards -let ledConfig: (GPIOBank, GPIOPin) = (.a, 5) - -#if STM32F74_F75 - -typealias Board = STM32F746Board -enum STM32F746Board { - static func initialize() { - // (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) - - 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 STM32F1 - -typealias Board = STM32F1Board -enum STM32F1Board { - static func initialize() { - // (1) IOPENR[ledConfig.0] = 1 ... enable clock - setRegisterBit( - baseAddress: RCC.BaseAddress, offset: RCC.Offsets.APB2ENR, bit: RCC.APB2ENRBit(for: ledConfig.0), - value: 1) - // (2) MODE[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) CNF[1] = 0b00 ... general purpose, push-pull - setRegisterTwoBitField( - baseAddress: GPIO.GPIOBaseAddress(for: ledConfig.0), offset: GPIO.Offsets.CRL, - bitsStartingAt: 4 * ledConfig.1 + 2, value: 0) +#if STM32F746G_DISCOVERY - ledOff() - } - - static func ledOn() { - // ODR[1] = 1 - setRegisterBit( - baseAddress: GPIO.GPIOBaseAddress(for: ledConfig.0), offset: GPIO.Offsets.ODR, bit: ledConfig.1, - value: 1) - } - - static func ledOff() { - // ODR[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() - } - } -} +// 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) -#elseif STM32C0 +#elseif NUCLEO_F103RB -typealias Board = STM32C0Board -enum STM32C0Board { - static func initialize() { - // (1) IOPENR[ledConfig.0] = 1 ... enable clock - setRegisterBit( - baseAddress: RCC.BaseAddress, offset: RCC.Offsets.IOPENR, bit: ledConfig.0.rawValue, - value: 1) - // (2) MODER[1] = 1 ... set mode to output - setRegisterTwoBitField( - baseAddress: GPIO.GPIOBaseAddress(for: ledConfig.0), offset: GPIO.Offsets.MODER, - bitsStartingAt: 2 * ledConfig.1, value: 1) - // (3) OTYPER[1] = 0 ... output type is push-pull - setRegisterBit( - baseAddress: GPIO.GPIOBaseAddress(for: ledConfig.0), offset: GPIO.Offsets.OTYPER, bit: ledConfig.1, - value: 0) - // (4) OSPEEDR[1] = 2 ... speed is high - setRegisterTwoBitField( - baseAddress: GPIO.GPIOBaseAddress(for: ledConfig.0), offset: GPIO.Offsets.OSPEEDR, - bitsStartingAt: 2 * ledConfig.1, value: 2) - // (5) PUPDR[1] = 2 ... set pull to down - setRegisterTwoBitField( - baseAddress: GPIO.GPIOBaseAddress(for: ledConfig.0), offset: GPIO.Offsets.PUPDR, - bitsStartingAt: 2 * ledConfig.1, value: 2) +// 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) - ledOff() - } +#else - static func ledOn() { - // ODR[1] = 1 - setRegisterBit( - baseAddress: GPIO.GPIOBaseAddress(for: ledConfig.0), offset: GPIO.Offsets.ODR, bit: ledConfig.1, - value: 1) - } - - static func ledOff() { - // ODR[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() - } - } -} +#error("Unknown board") #endif diff --git a/stm32-blink/README.md b/stm32-blink/README.md index df2a0d9..5ae799f 100644 --- a/stm32-blink/README.md +++ b/stm32-blink/README.md @@ -16,6 +16,7 @@ This example shows a simple baremetal firmware for an STM32 board that blinks an ```console $ cd stm32-blink $ 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 ``` @@ -26,11 +27,13 @@ $ st-flash --reset write .build/blink.bin 0x08000000 - 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-util -(then in a separate terminal) -$ st-flash --reset write .build/blink.elf 0x08000000 +$ st-flash --format ihex --reset write .build/blink.hex ``` - The green LED next to the RESET button should now be blinking in a pattern. diff --git a/stm32-blink/Registers.swift b/stm32-blink/Registers.swift index 5a5bbce..ccbf13f 100644 --- a/stm32-blink/Registers.swift +++ b/stm32-blink/Registers.swift @@ -11,23 +11,52 @@ // 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 -#if STM32F74_F75 +// 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 AHB1ENR = 0x30 } + 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 + } + } } enum GPIO { @@ -53,7 +82,15 @@ enum GPIO { } } -#elseif STM32F1 +#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)! @@ -79,7 +116,6 @@ enum RCC { case .e: Bits.APB2ENR_IOPEEN case .f: Bits.APB2ENR_IOPFEN case .g: Bits.APB2ENR_IOPGEN - default: fatalError("invalid bank") } } } @@ -102,7 +138,6 @@ enum GPIO { case .e: GPIOe_BaseAddress case .f: GPIOf_BaseAddress case .g: GPIOg_BaseAddress - default: fatalError("invalid bank") } } @@ -114,53 +149,9 @@ enum GPIO { } } -#elseif STM32C0 - -enum RCC { - static let BaseAddress = UnsafeMutablePointer(bitPattern: 0x40021000 as UInt)! - enum Offsets { - static let IOPENR = 0x34 - } - enum Bits { - static let IOPENR_GPIOAEN = 0 - static let IOPENR_GPIOBEN = 1 - static let IOPENR_GPIOCEN = 2 - static let IOPENR_GPIODEN = 3 - static let IOPENR_GPIOFEN = 5 - } -} - -enum GPIO { - static let GPIOa_BaseAddress = UnsafeMutablePointer(bitPattern: 0x50000000 as UInt)! - static let GPIOb_BaseAddress = UnsafeMutablePointer(bitPattern: 0x50000400 as UInt)! - static let GPIOc_BaseAddress = UnsafeMutablePointer(bitPattern: 0x50000800 as UInt)! - static let GPIOd_BaseAddress = UnsafeMutablePointer(bitPattern: 0x50000c00 as UInt)! - static let GPIOf_BaseAddress = UnsafeMutablePointer(bitPattern: 0x50001400 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 .f: GPIOf_BaseAddress - default: fatalError("invalid bank") - } - } - - enum Offsets { - static let MODER = 0x0 - static let OTYPER = 0x4 - static let OSPEEDR = 0x8 - static let PUPDR = 0xc - static let IDR = 0x10 - static let ODR = 0x14 - } -} - #else -#error("Unknown MCU") +#error("Unknown board") #endif @@ -169,9 +160,10 @@ func setRegisterBit(baseAddress: UnsafeMutablePointer, offset: Int, bit: 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) { @@ -179,7 +171,8 @@ func setRegisterTwoBitField(baseAddress: UnsafeMutablePointer, offset: I 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 88f8eb1..9237afb 100644 --- a/stm32-blink/Startup.c +++ b/stm32-blink/Startup.c @@ -31,7 +31,7 @@ __attribute((section("__VECTORS,__text"))) #error Unknown file format #endif void *vector_table[114] = { - (void *)0x20002ffc, // initial SP, assume we have 12 KB of SRAM + (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 index 009efcb..afd9413 100755 --- a/stm32-blink/build-elf.sh +++ b/stm32-blink/build-elf.sh @@ -3,22 +3,26 @@ set -vex # Determine file paths -REPOROOT=$(realpath -e -- $(dirname ${BASH_SOURCE[0]}))/.. +REPOROOT=$(realpath -- "$(dirname "${BASH_SOURCE[0]}")")/.. TOOLSROOT="$REPOROOT/Tools" SRCROOT="$REPOROOT/stm32-blink" BUILDROOT="$SRCROOT/.build" # Clean the build directory -rm -r $BUILDROOT || true +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+=" -Xcc -D__APPLE__ -Xcc -D__MACH__ -Xcc -ffreestanding" -SWIFT_FLAGS+=" -Xfrontend -function-sections -DSTM32F1" +SWIFT_FLAGS+=" -Xfrontend -function-sections -D${STM_BOARD}" CLANG_EXEC=${CLANG_EXEC:-$(which clang)} CLANG_FLAGS="-target $TARGET -Oz" @@ -30,13 +34,19 @@ LD_FLAGS="-target $TARGET -fuse-ld=lld -nostdlib -static -Wl,-e,vector_table -Wl 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" +"$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 index f3d8743..b45c44c 100755 --- a/stm32-blink/build-macho.sh +++ b/stm32-blink/build-macho.sh @@ -9,19 +9,27 @@ SRCROOT="$REPOROOT/stm32-blink" BUILDROOT="$SRCROOT/.build" # Clean the build directory -rm -r $BUILDROOT || true +rm -r "$BUILDROOT" || true # Setup tools and build flags -TARGET=armv7-apple-none-macho +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 -import-bridging-header $SRCROOT/BridgingHeader.h -wmo -enable-experimental-feature Embedded -Xcc -D__APPLE__ -Xcc -D__MACH__ -Xcc -ffreestanding" +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,0x00200000 -Wl,-seg1addr,0x00200200 -Wl,-pagezero_size,0" +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" @@ -30,16 +38,19 @@ MACHO2BIN="$TOOLSROOT/macho2bin.py" mkdir -p "$BUILDROOT" # Build Swift sources -"$SWIFT_EXEC" $SWIFT_FLAGS -c $SRCROOT/*.swift -o "$BUILDROOT/blink.o" +# 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 0x00200000 --segments '__TEXT,__DATA,__VECTORS' +"$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/elf-linkerscript.ld b/stm32-blink/elf-linkerscript.ld index d72351a..98d5f00 100644 --- a/stm32-blink/elf-linkerscript.ld +++ b/stm32-blink/elf-linkerscript.ld @@ -1,7 +1,7 @@ MEMORY { flash : ORIGIN = 0x08000000, LENGTH = 32K - sram : ORIGIN = 0x20000000, LENGTH = 12K + sram : ORIGIN = 0x20000000, LENGTH = 8K } SECTIONS From a99078ff56ace9ef5e7d4e0d95fb6e4accf10025 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Thu, 16 Jan 2025 10:58:15 -0800 Subject: [PATCH 3/3] Fix python lint issues in Tools/elf2hex.py --- Tools/elf2hex.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/Tools/elf2hex.py b/Tools/elf2hex.py index 54a0b26..db0449f 100755 --- a/Tools/elf2hex.py +++ b/Tools/elf2hex.py @@ -8,8 +8,8 @@ # 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. +# 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 ] @@ -19,11 +19,12 @@ # import argparse -import os -import pathlib import json +import pathlib + import elftools.elf.elffile + def main(): parser = argparse.ArgumentParser() parser.add_argument('input') @@ -38,7 +39,7 @@ def emitrecord(record): checksum = 0 pos = 0 while pos < len(record): - checksum = (checksum + int(record[pos:pos+2], 16)) % 256 + checksum = (checksum + int(record[pos:pos + 2], 16)) % 256 pos += 2 checksum = (256 - checksum) % 256 outf.write((":" + record + f"{checksum:02X}" + "\n").encode()) @@ -47,16 +48,16 @@ def emit(vmaddr, data): pos = 0 while pos < len(data): chunklen = min(16, len(data) - pos) - chunk = data[pos:pos+chunklen] + 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 + recordtype = "04" # Extended Linear Address emitrecord(f"{2:02X}{0:04X}{recordtype}{vmaddr_high:04X}") vmaddr_low = vmaddr & 0xffff - recordtype = "00" # Data + recordtype = "00" # Data emitrecord(f"{chunklen:02X}{vmaddr_low:04X}{recordtype}{chunkhex}") pos += chunklen @@ -64,22 +65,26 @@ def emit(vmaddr, data): elffile = elftools.elf.elffile.ELFFile(inf) for segment in elffile.iter_segments(): - if segment.header.p_type != "PT_LOAD": continue + 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 + 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 + 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: @@ -88,5 +93,6 @@ def emit(vmaddr, data): inf.close() outf.close() + if __name__ == '__main__': main()