aboutsummaryrefslogtreecommitdiffhomepage
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/index.rst1
-rw-r--r--docs/microcontrollers.rst132
2 files changed, 133 insertions, 0 deletions
diff --git a/docs/index.rst b/docs/index.rst
index f49ea214a..4ce8a2adc 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -12,4 +12,5 @@ Contents:
:maxdepth: 2
installation
+ microcontrollers
internals
diff --git a/docs/microcontrollers.rst b/docs/microcontrollers.rst
new file mode 100644
index 000000000..7509190d8
--- /dev/null
+++ b/docs/microcontrollers.rst
@@ -0,0 +1,132 @@
+.. microcontrollers:
+
+.. highlight:: go
+
+
+Go on microcontrollers
+======================
+
+TinyGo was designed to run on microcontrollers, but the Go language wasn't.
+This means there are a few challenges to writing Go code for microcontrollers.
+
+Microcontrollers have very little RAM and execute code directly from flash.
+Also, constant globals are generally put in flash whenever possible. The Go
+language itself heavily relies on garbage collection so care must be taken to
+avoid dynamic memory allocation.
+
+
+Heap allocation
+---------------
+
+Many operations in Go rely on heap allocation. Some of these heap allocations
+are optimized away, but not all of them. Also, TinyGo does not yet contain a
+garbage collector so heap allocation must be avoided whenever possible outside
+of initialization code.
+
+These operations currently do heap allocations:
+
+ * Taking the pointer of a local variable. This will result in a heap
+ allocation, unless the compiler can see the resulting pointer never
+ escapes. This causes a heap allocation::
+
+ var global *int
+
+ func foo() {
+ i := 3
+ global = &i
+ }
+
+ This does not cause a heap allocation::
+
+ func foo() {
+ i := 3
+ bar(&i)
+ }
+
+ func bar(i *int) {
+ println(*i)
+ }
+
+ * Converting between ``string`` and ``[]byte``. In general, this causes a
+ heap allocation because one is constant while the other is not: for
+ example, a ``[]byte`` is not allowed to write to the underlying buffer of a
+ ``string``. However, there is an optimization that avoids a heap allocation
+ when converting a string to a ``[]byte`` when the compiler can see the
+ slice is never written to. For example, this ``WriteString`` function does
+ not cause a heap allocation::
+
+ func WriteString(s string) {
+ Write([]byte(s))
+ }
+
+ func Write(buf []byte) {
+ for _, c := range buf {
+ WriteByte(c)
+ }
+ }
+
+ * Converting a ``byte`` or ``rune`` into a ``string``. This operation is
+ actually a conversion from a Unicode code point into a single-character
+ string so is similar to the previous point.
+
+ * Concatenating strings, unless one of them is zero length.
+
+ * Creating an interface with a value larger than a pointer. Interfaces in Go
+ are not a zero-cost abstraction and should be used carefully on
+ microcontrollers.
+
+ * Closures where the collection of shared variables between the closure and
+ the main function is larger than a pointer.
+
+ * Creating and modifying maps. Maps have *very* little support at the moment
+ and should not yet be used. They exist mostly for compatibility with some
+ standard library packages.
+
+ * Starting goroutines. There is limited support for goroutines and currently
+ they are not at all efficient. Also, there is no support for channels yet
+ so their usefulness is limited.
+
+
+The ``volatile`` keyword
+------------------------
+
+Go does not have the ``volatile`` keyword like C/C++. This keyword is
+unnecessary in most desktop use cases but is required for memory mapped I/O on
+microcontrollers and interrupt handlers. As a workaround, any variable of a
+type annotated with the ``//go:volatile`` pragma will be marked volatile. For
+example::
+
+ //go:volatile
+ type volatileBool bool
+
+ var isrFlag volatileBool
+
+This is a workaround for a limitation in the Go language and should at some
+point be replaced with something else.
+
+
+Inline assembly
+---------------
+
+The device-specific packages like ``device/avr`` and ``device/arm`` provide
+``Asm`` functions which you can use to write inline assembly::
+
+ arm.Asm("wfi")
+
+There is no support yet for inline assembly that takes (register) parameters or
+returns a value.
+
+
+Harvard architectures (AVR)
+---------------------------
+
+The AVR architecture is a modified Harvard architecture, which means that flash
+and RAM live in different address spaces. In practice, this means that any
+given pointer may either point to RAM or flash, but this is not visible from
+the pointer itself.
+
+To get TinyGo to work on the Arduino, which uses the AVR architecutre, all
+global variables (which include string constants!) are marked non-constant and
+thus are stored in RAM and all pointer dereferences assume that pointers point
+to RAM. At some point this should be optimized so that obviously constant data
+is kept in read-only memory but this optimization has not yet been implemented.