Skip to content

Commit 31f821a

Browse files
committed
[embedded] Add an initial docs/EmbeddedSwift/UserManual.md
1 parent c266644 commit 31f821a

File tree

1 file changed

+138
-0
lines changed

1 file changed

+138
-0
lines changed

docs/EmbeddedSwift/UserManual.md

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Embedded Swift -- User Manual
2+
3+
**⚠️ Embedded Swift is experimental. This document might be out of date with latest development.**
4+
5+
**‼️ Use the latest downloadable 'Trunk Development' snapshot from swift.org to use Embedded Swift. Public releases of Swift do not yet support Embedded Swift.**
6+
7+
For an introduction and motivation into Embedded Swift, please see "[A Vision for Embedded Swift](https://github.com/apple/swift-evolution/blob/main/visions/embedded-swift.md)", a Swift Evolution document highlighting the main goals and approaches.
8+
9+
The following document explains how to use Embedded Swift's support in the Swift compiler and toolchain.
10+
11+
## What Embedded Swift is, and what it isn't
12+
13+
- Embedded Swift **is** a way to produce small and freestanding binaries (with no, or trivial dependencies).
14+
- Embedded Swift **is not** a complete one-click solution to program all embedded boards and MCUs.
15+
- Embedded Swift **is** a compilation model that's analogous to a traditional C compiler in the sense that the compiler produces an object file (.o) that can be simply linked with your existing code, and it's not going to require you to port any libraries or runtimes.
16+
- Embedded Swift **is not** a HAL, it's not an SDK for development, it's not a set of libraries to program peripherals using high-level APIs. It's instead a compilation mode that's suitable for creating these components.
17+
18+
## Using Embedded Swift
19+
20+
A typical setup and build + run cycle for an embedded development board involves:
21+
22+
- (1) Getting an SDK with the C compilers, headers and libraries for the target
23+
- (2) Building the C source code, and Swift source code
24+
- (3) Linking all the libraries, C object files, and Swift object files.
25+
- (4) Post-processing the linked firmware into a flashable format (UD2, BIN, or bespoke formats)
26+
- (5) Uploading the flashable binary to the board over a USB cable using some vendor-provided JTAG/SWD tool or by copying it to a fake USB Mass Storage volume presented by the board.
27+
- (6) Restarting the board, observing physical effects of the firmware (LEDs light up) or UART output over USB, or presence on network, etc.
28+
29+
Most of these steps are out of scope for this document, instead refer to the vendor provided documentation and get familiar with the details of firmware development for your board without Swift in the mix first. Even if you want to build a completely pure Swift firmware, you are still very likely going to need the the vendor provided tooling for linking, post-processing, uploading, etc.
30+
31+
## Building code using Embedded Swift
32+
33+
A basic way to build a set of Swift source files in Embedded Swift mode, is to simply give the compiler (1) a target triple, (2) the `-enable-experimental-feature Embedded` flag, (3) the set of source files that form the input module:
34+
35+
```bash
36+
$ swiftc -target <target triple> -enable-experimental-feature Embedded \
37+
input1.swift input2.swift ... -c -o output.o
38+
```
39+
40+
The target triple also decides whether whether the output object file will be an ELF file, or a Mach-O. For example:
41+
42+
```bash
43+
# To build an ARMv7 Mach-O object file:
44+
$ swiftc -target armv7-apple-none-macho -enable-experimental-feature Embedded \
45+
input1.swift input2.swift ... -c -o output.o
46+
47+
# To build an ARMv7 ELF object file:
48+
$ swiftc -target armv7-unknown-none-eabi -enable-experimental-feature Embedded \
49+
input1.swift input2.swift ... -c -o output.o
50+
```
51+
52+
Additionally, you probably want to specify additional Clang and/or LLVM flags to get the compiler to produce code for the exact ISA and ABI you need for your target. For example, a Raspberry Pi Pico / Pico W expects the `-mfloat-abi=soft` Clang option, and if you want to match ABI with libraries built with the GNU toolchain, you might also need `-fshort-enums`. To pass those to Swift, use the `-Xcc` prefix:
53+
54+
```bash
55+
# To build an ELF object file for ARMv6-M with soft float ABI (floating-point arguments passed in integer registers) and "short enums":
56+
$ swiftc -target armv6m-unknown-none-eabi -enable-experimental-feature Embedded \
57+
-Xcc -mfloat-abi=soft -Xcc -fshort-enums \
58+
input1.swift input2.swift ... -c -o output.o
59+
```
60+
61+
This might not be obvious: `-Xcc` flags are typically only used to alter behavior of the Clang importer, but passing flags to Clang this way also works to specify LLVM target options like selecting a specific CPU architecture (`-march`, `-mcpu`, `-mmcu`), FPU unit availability (`-mfpu`), which registers are used to pass floating-point values (`-mfloat-abi`), and others.
62+
63+
## Examples
64+
65+
### Building a pure Swift firmware for an embedded target
66+
67+
TODO
68+
69+
### Integrating with embedded SDKs and build systems
70+
71+
TODO
72+
73+
### Building a macOS Embedded Swift program:
74+
75+
It's also possible to build in Embedded Swift mode for regular non-embedded operating systems, like macOS. This is very useful for testing purposes, or if you just want to observe and experiment with Embedded Swift. A simple source code like this:
76+
77+
```swift
78+
print("Hello, embedded world!")
79+
```
80+
81+
...can be compiled using the `-enable-experimental-feature Embedded` flag (the implicit `-target` matches the host OS):
82+
83+
```bash
84+
$ xcrun swiftc hello.swift -enable-experimental-feature Embedded -wmo
85+
$ ./hello
86+
Hello, embedded world!
87+
```
88+
89+
Note that the resulting executable is still a *dynamically-linked executable*, so it's not fully standalone in the embedded sense. Namely is still uses `putchar` from Libsystem. But the singular object file that was used to build this executable was produced by the compiler in the same fashion that a real embedded build would. If we ask the compiler and linker to minimize the size of the outputs and to remove any unused code, we can observe that the binary has no other dependencies other than `putchar` and that the machine code section is very small (172 bytes in the `__text` section):
90+
91+
```bash
92+
$ xcrun swiftc hello.swift -enable-experimental-feature Embedded -wmo -Osize -Xlinker -dead_strip
93+
$ nm -um ./hello
94+
(undefined) external _putchar (from libSystem)
95+
$ size -m ./hello
96+
Segment __TEXT: 16384
97+
Section __text: 172
98+
...
99+
```
100+
101+
## Embedded Swift is a subset of Swift
102+
103+
Embedded Swift is a subset of the Swift language, and some features are not available in Embedded Swift, however features are available, including: Generics, protocols, enums with associated values, tuples, optionals, classes (instances are allocated on the heap and refcounted just like in regular Swift), inheritance, runtime polymorphism, arrays (heap-allocated copy-on-write just like in regular Swift) and many more.
104+
105+
Features that are not available:
106+
107+
- **Not available**: Runtime reflection (`Mirror` APIs).
108+
- **Not available**: Values of protocol types ("existentials"), e.g. `let a: Hashable = ...`, are not allowed. `Any` and `AnyObject` are also not allowed.
109+
- **Not available**: Metatypes, e.g. `let t = SomeClass.Type` or `type(of: value)` are not allowed.
110+
- **Not available yet (under development)**: The print() function for types other than StaticString and integers.
111+
- **Not available yet (under development)**: String. (StaticString **is** available).
112+
- **Not available yet (under development)**: Set.
113+
- **Not available yet (under development)**: Dictionary.
114+
115+
## Libraries and modules in Embedded Swift
116+
117+
TODO
118+
119+
## Allocating and non-allocating Embedded Swift mode
120+
121+
TODO
122+
123+
## Runtime support for allocating Embedded Swift
124+
125+
TODO
126+
127+
## External dependencies
128+
129+
Embedded Swift minimizes external dependencies, but they still exist. In Embedded Swift compilation mode, the compiler only triggers external dependencies based on actual usage of the program under compilation. The following table lists which situations cause which external dependencies:
130+
131+
| | external dependencies: |
132+
|---------------------------------------------------------------------|-------------------------------------------------------------------------|
133+
| *(basic dependencies, added by the compiler in various situations)* | `void *memset(void *, int, size_t);` |
134+
| instantiating a class, or using UnsafePointer.allocate() | `int posix_memalign(void **, size_t, size_t);`<br/>`void free(void *);` |
135+
| using print() | `int putchar(int);` |
136+
| using Hashable, Set, Dictionary, or random-number generating APIs | `void arc4random_buf(void *, size_t);` |
137+
138+
The user and/or the platform is expected to provide these well-known APIs.

0 commit comments

Comments
 (0)