|
| 1 | +# TinyGo |
| 2 | + |
| 3 | +## What is this? |
| 4 | + |
| 5 | +A runtime for the [Go programming language](https://golang.org/) that is very |
| 6 | +small in size and doesn't rely as much on an OS. |
| 7 | + |
| 8 | +**Note**: This project is unrelated to the now-dead [Tiny |
| 9 | +runtime](https://code.google.com/archive/p/tinygo/) of the Go language, see |
| 10 | +below. |
| 11 | + |
| 12 | +## Why was it created? |
| 13 | + |
| 14 | +I wanted to be able to run Go on a microcontroller. While this goal hasn't been |
| 15 | +reached yet, the resulting binary can be _very_ small. |
| 16 | + |
| 17 | +Inside `src/hello/hello.go`: |
| 18 | + |
| 19 | +```go |
| 20 | +// Simple program with the bare minimum of Go. |
| 21 | +package main |
| 22 | + |
| 23 | +func main() { |
| 24 | + println("hello, world!") |
| 25 | +} |
| 26 | +``` |
| 27 | + |
| 28 | +Running on an ARM machine (Raspberry Pi 3): |
| 29 | + |
| 30 | +``` |
| 31 | +$ make PKG=hello LTO=1 |
| 32 | +[lots of build output] |
| 33 | +
|
| 34 | +$ strip build/hello |
| 35 | +
|
| 36 | +$ ./build/hello |
| 37 | +hello, world! |
| 38 | +
|
| 39 | +$ /bin/ls -lh build/hello |
| 40 | +-rwxrwxr-x 1 ayke ayke 5,5K mrt 9 23:43 build/hello |
| 41 | +
|
| 42 | +$ size build/hello |
| 43 | + text data bss dec hex filename |
| 44 | + 1961 316 20 2297 8f9 build/hello |
| 45 | +``` |
| 46 | + |
| 47 | +As you can see, this produces a file that's just 5.5kB in size and contains |
| 48 | +about 2kB of machine code. The same source compiled by the official Go compiler |
| 49 | +(1.7.4) produces an executable that is about 564kB in size after stripping, most |
| 50 | +of which is also `.text`. |
| 51 | + |
| 52 | +Of course, this is a bit cheating. The small 'hello' program does not include |
| 53 | +most of the runtime, that's all stripped away by the linker. A somewhat more |
| 54 | +realistic example is the |
| 55 | +[channel](https://github.com/aykevl/tinygo/blob/master/src/channel/channel.go) |
| 56 | +example, which produces an executable of 18kB. That runtime contains the |
| 57 | +scheduler, memory allocator (no GC yet) and channel send/receive primitives. |
| 58 | + |
| 59 | +## Limitations |
| 60 | + |
| 61 | +There are many. |
| 62 | + |
| 63 | + * No support for anything except ARM. |
| 64 | + * Works with gccgo 6.3 (Debian stretch), no other compiler versions have been |
| 65 | + tested. |
| 66 | + * Source files must be placed in a subdirectory of src/. |
| 67 | + * No `recover()` |
| 68 | + * No function names or line numbers in the `panic()` output. I would like to |
| 69 | + fix this at some time, but it will increase the binary size if it must work |
| 70 | + after stripping. It currently outputs the function addresses, which you can |
| 71 | + look up with `nm` (e.g. `nm -S ./build/example | egrep ' [tT] main\.'`). |
| 72 | + * No garbage collector, yet. Allocated memory is never freed. |
| 73 | + * Many types might not be implemented, but support will probably be pretty |
| 74 | + easy to add by including the correct files from `gofrontend/libgo/runtime`. |
| 75 | + * The scheduler is pretty dumb right now. Passing messages between two |
| 76 | + goroutines is fast (on the Raspberry Pi 3 slightly faster than the standard |
| 77 | + Go compiler!) but any extra goroutine that is started will slow down any |
| 78 | + blocking operation. |
| 79 | + * No support for running multiple goroutines on different threads. I don't |
| 80 | + really have an intention to implement this as it will complicate the |
| 81 | + scheduler and I don't want a complicated scheduler. |
| 82 | + * Most of the standard library doesn't build yet, in particular the `fmt` |
| 83 | + package (which depends on a whole slew of other packages). Some leaf |
| 84 | + packages will work when added to the Makefile manually. |
| 85 | + * No automatic dependency installation, except for a few defined in the |
| 86 | + Makefile. |
| 87 | + * ... probably many more. |
| 88 | + |
| 89 | +## Current status |
| 90 | + |
| 91 | +I mainly developed this as a proof of concept so I'm not sure how I'll continue, |
| 92 | +but I'm happy that it works (to some degree). I'm interested in microcontrollers |
| 93 | +and really like the Go language so may actually use it in the future, in which |
| 94 | +case I surely will improve it. |
| 95 | + |
| 96 | +Also, it depends on gccgo and gccgo seems to be moving to a runtime written in |
| 97 | +Go, so I might need to adjust to that in the future. |
| 98 | + |
| 99 | +## Building |
| 100 | + |
| 101 | +Dependencies: |
| 102 | + |
| 103 | + * An ARM machine, like a Raspberry Pi. Making it work on anything else |
| 104 | + (without backtrace support) should not be very difficult. |
| 105 | + * GCCGO, tested version 6.3. |
| 106 | + * An initialized gofrontend, run `git submodule update --init`. |
| 107 | + |
| 108 | +Then just execute `make PKG=<dirname>` where dirname is the name of the |
| 109 | +directory (under `src/`), for example `make PKG=myproject`. You have to put your |
| 110 | +source files under `src/`. The executable is then put in the `build/` directory. |
| 111 | +You may want to enable link-time optimization with the `LTO=1` flag. |
| 112 | + |
| 113 | +## License |
| 114 | + |
| 115 | +The license is the same BSD 3-clause license as the one used for Go. See the |
| 116 | +LICENSE file for details. |
| 117 | + |
| 118 | +## Bare metal support |
| 119 | + |
| 120 | +Requirements for porting this to bare-metal processors (e.g. ARM Cortex-M): |
| 121 | + |
| 122 | + * Replace all calls to `printf()` with something that logs to a serial |
| 123 | + console. |
| 124 | + * Implement a memory allocator, for example by copying [the one from |
| 125 | + MicroPython](https://github.com/micropython/micropython/blob/master/py/gc.c). |
| 126 | + * Implement context switching, probably using setjmp/longjmp (take a look at |
| 127 | + how MicroPython does it). |
| 128 | + |
| 129 | +## About the 'other' TinyGo |
| 130 | + |
| 131 | +There is another (now dead) project called 'tinygo' [over |
| 132 | +here](https://code.google.com/archive/p/tinygo/) and |
| 133 | +[here](https://github.com/jackmanlabs/tinygo) that tried to run a very old |
| 134 | +variant of the runtime bare-metal. I only discovered it the moment I wanted to |
| 135 | +push this project to GitHub. |
0 commit comments