Compare commits

...

80 Commits

Author SHA1 Message Date
deadprogram e8615d1007 Prepare for 0.15.0 release
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-09-17 11:48:55 +02:00
sago35 9245337ecf atsamd2x: fix BAUD value 2020-09-17 09:46:15 +02:00
sago35 8170f59440 atsamd5x: fix BAUD value 2020-09-17 09:46:15 +02:00
Lucas Teske 622de53854 nintendoswitch: simplified assembly code 2020-09-17 07:46:29 +02:00
Lucas Teske 6cd9e3c348 arm64: make dynamic loader structs and constants private 2020-09-17 07:46:29 +02:00
Lucas Teske 33e2411b6a nintendoswitch: fix import cycle on dynamic_arm64.go 2020-09-17 07:46:29 +02:00
Lucas Teske fb1fc267ab nintendoswitch: Add dynamic loader for runtime loading PIE sections 2020-09-17 07:46:29 +02:00
Olivier Fauchon 490e377bba bluepill: Enable stm32's USART2 for the board and map it to UART1 tinygo's device 2020-09-17 06:26:57 +02:00
Ayke van Laethem fcedf0beaa stacksize: deal with DW_CFA_advance_loc1
In some cases this operation is emitted. It appears to be emitted when a
switch is lowered to a jump table in the ARM backend.
2020-09-16 05:06:34 +02:00
Ayke van Laethem 4dadb31e18 microbit: reelboard: flash using OpenOCD when needed
When using a SoftDevice, the MSD flash method is not appropriate as it
will erase the entire flash area before writing the new firmware. This
also wipes the SoftDevice. Instead, use OpenOCD to only rewrite the
parts of flash that need to be rewritten and leave the SoftDevice alone.
2020-09-15 17:07:40 +02:00
Ayke van Laethem 5b81b835ba esp32: add SPI support 2020-09-14 12:24:46 +02:00
ardnew a9a6d0ee63 add basic UART handler 2020-09-14 08:48:01 +02:00
Ayke van Laethem 19d5e05e37 esp32: configure the I/O matrix for GPIO pins
Only some pins (notably including GPIO2 aka machine.LED) have GPIO for
the default function 1. Other pins (such as GPIO 15) had a different
function by default. Function 3 means GPIO for all the pins, so always
use that when configuring a pin to use as a GPIO pin.

In the future, the mux configuration will need to be updated for other
functions such as SPI, I2C, etc.
2020-09-13 11:24:33 +02:00
Ayke van Laethem 9e599bac49 nintendoswitch: support outputting .nro files directly
By modifying the linker script a bit and adding the NRO0 header directly
in the assembly, it's possible to craft an ELF file that can be
converted straight to a binary (using objcopy or similar) that is a NRO
file. This avoids custom code for NRO files or an extra build step.

With another change, .nro files are recognized by TinyGo so that this
will create a ready-to-run NRO file:

    tinygo build -o test.nro -target=nintendoswitch examples/serial
2020-09-12 18:37:58 +02:00
Nia Weiss 81e325205f update my name in the contributors list 2020-09-12 16:51:47 +02:00
deadprogram 71cbb1495e docs: add ESP32, ESP8266, and Adafruit Feather STM32F405 to list of supported boards
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-09-11 09:36:19 +02:00
ardnew efc824a103 change default flash method to DFU (using dfu-util over USB) 2020-09-11 09:09:02 +02:00
ardnew cb4f3f12e6 replace flash method with openocd, disable automatic stack sizing 2020-09-11 09:09:02 +02:00
ardnew d1b470f04e remove UART/SPI vars until peripherals implemented 2020-09-11 09:09:02 +02:00
ardnew 7aaa9e45a6 add pin/bus doc comments, I2C pins 2020-09-11 09:09:02 +02:00
ardnew 5c9930df3f move clock settings docs comment to respective const definitions 2020-09-11 09:09:02 +02:00
ardnew cf6b544cd9 remove (or stub) UART/SPI/I2C peripheral code from initial feather-stm32f405 board support 2020-09-11 09:09:02 +02:00
ardnew 097f628955 add feather-stm32f405 smoketest target 2020-09-11 09:09:02 +02:00
ardnew 20a1c730a1 add STM32F405 machine/runtime, and new board/target feather-stm32f405 2020-09-11 09:09:02 +02:00
jreamy 04f65f1189 Adding support for the Arduino Zero (#1365)
* machine/arduino-zero: adding support for Arduino Zero

Co-authored-by: Ayke
Co-authored-by: Jack Reamy
2020-09-10 10:32:12 +02:00
Ayke van Laethem 2ce17a1892 esp8266: add support for this chip
Many thanks to cnlohr for the nosdk8266 project:
    https://github.com/cnlohr/nosdk8266
2020-09-09 19:17:11 +02:00
Lucas Teske 0b9b293651 nintendoswitch: Fix invalid memory read / write in print calls 2020-09-09 18:51:00 +02:00
Ayke van Laethem f20c932bb9 nrf52840: use higher priority for USB-CDC code
This ensures that stdout (println etc) keeps working in interrupts.

Generally you shouldn't print anything in an interrupt. However,
printing things for debugging is very useful and printing panic messages
can be critical when the code doesn't work for some reason.
2020-09-09 17:15:50 +02:00
Ayke van Laethem e7227df80d main: implement tinygo targets to list usable targets
This patch adds the `tinygo targets` command, which lists usable
targets (targets that can be used in the `-target` flag).

The assumption here is that usable targets can either be flashed or
emulated by TinyGo. There is one exception where it doesn't work yet:
the nintendoswitch target. Right now it requires some manual steps to
build a .nro file which can then be run by yuzu, hence why it doesn't
show up in the list.
2020-09-09 13:05:14 +02:00
Ayke van Laethem b99175a436 avr: configure emulator in board files
Instead of specifying the emulator command in atmega328p.json, specify
it in the two boards based on it (arduino and arduino-nano). This makes
the configuration consistent with the machine package, which only
defines the CPUFrequency function in the board files (and not in
machine_atmega328p.json).
2020-09-09 13:05:14 +02:00
Ayke van Laethem de3ffe0af7 main: add cached GOROOT to info subcommand
This is necessary for an upcoming VS Code extension to support TinyGo,
and may be useful for other people wanting to use proper autocompletion
etc in their IDE.
2020-09-07 13:07:17 +02:00
sago35 1e47d9efac docker: fix the problem with the wasm build (#1357)
* docker: fix the problem with the wasm build
2020-09-06 10:22:47 +02:00
Ayke van Laethem 8a2e7bac04 esp32: add libgcc ROM functions to linker script
They are copied from ESP-IDF. They are always available (burned in the
mask ROM) so best to use them.
2020-09-05 10:41:35 +02:00
sago35 ae13db917f ci: set git-fetch-depth to 1 2020-09-05 10:41:04 +02:00
Ayke van Laethem 98dbbbfda6 esp32: export machine.PortMask* for bitbanging implementations
This is useful for some drivers, in particular for the WS2812 driver.
2020-09-05 09:23:31 +02:00
Ayke van Laethem 475135f546 ci: run tinygo test for known-working packages
These packages are known to pass tests with `tinygo test`. It's still a
very short list, but hopefully this list can be expanded to eventually
cover most or all of the standard library.
2020-09-04 14:10:48 +02:00
Ayke van Laethem 88fd2823df all: run test binaries in the correct directory
Test binaries must be run in the source directory of the package to be
tested. This wasn't done, leading to a few "file not found" errors.

This commit implements this. Unfortunately, it does not allow more
packages to be tested as both affected packages (debug/macho and
debug/plan9obj) will still fail with this patch even though the "file
not found" errors are gone.
2020-09-04 12:21:19 +02:00
Ayke van Laethem 57a5b833b2 Makefile: check whether submodules have been downloaded in some common cases
This should help new people making this very common mistake.
2020-09-04 12:07:00 +02:00
Ayke van Laethem c810628a20 loader: rewrite/refactor much of the code to use go list directly
There were a few problems with the go/packages package. While it is more
or less designed for our purpose, it didn't work quite well as it didn't
provide access to indirectly imported packages (most importantly the
runtime package). This led to a workaround that sometimes broke
`tinygo test`.

This PR contains a number of related changes:

  * It uses `go list` directly to retrieve the list of packages/files to
    compile, instead of relying on the go/packages package.
  * It replaces our custom TestMain replace code with the standard code
    for running tests (generated by `go list`).
  * It adds a dummy runtime/pprof package and modifies the testing
    package, to get tests to run again with the code generated by
    `go list`.
2020-09-03 22:10:14 +02:00
Ayke van Laethem 51238fba50 arduino-mega2560: fix flashing on Windows
Without the extra `:i` at the end, avrdude will misinterpret the colon
in Windows paths.
2020-09-03 06:24:18 +02:00
ardnew 7f829fe153 machine/stm32f4: refactor common code and add new build tag stm32f4 (#1332)
* machine/STM32F4: break out STM32F4 machine with new build tag
2020-09-01 11:31:41 +02:00
sago35 946184b8ba flash: call PortReset only on other than openocd 2020-08-31 20:01:22 +02:00
Ayke van Laethem 753162f4e0 esp32: add support for basic GPIO
GPIO is much more advanced on the ESP32, but this is a starting point.
It gets examples/blinky1 to work.
2020-08-31 16:43:31 +02:00
Johan Brandhorst 0e6d2af028 Fix arch release job
Instead of using su, which is blocking, set the
user explicitly for each command.
2020-08-31 14:15:05 +02:00
Ayke van Laethem 0df4a7a35f esp32: support flashing directly from tinygo
Right now this requires setting the -port parameter, but other than that
it totally works (if esptool.py is installed). It works by converting
the ELF file to the custom ESP32 image format and flashing that using
esptool.py.
2020-08-31 13:59:32 +02:00
Ayke van Laethem 9a17698d6a compileopts: add support for custom binary formats
Some chips (like the ESP family) have a particular image format that is
more complex than simply dumping everything in a raw image.
2020-08-31 13:59:32 +02:00
Ayke van Laethem 3ee47a9c1b esp: add support for the Espressif ESP32 chip
This is only very minimal support. More support (such as tinygo flash,
or peripheral access) should be added in later commits, to keep this one
focused.

Importantly, this commit changes the LLVM repo from llvm/llvm-project to
tinygo-org/llvm-project. This provides a little bit of versioning in
case something changes in the Espressif fork. If we want to upgrade to
LLVM 11 it's easy to switch back to llvm/llvm-project until Espressif
has updated their fork.
2020-08-31 09:02:23 +02:00
Ayke van Laethem da7db81087 cortexm: fix stack size calculation with interrupts
Interrupts store 32 bytes on the current stack, which may be a goroutine
stack. After that the interrupt switches to the main stack pointer so
nothing more is pushed to the current stack. However, these 32 bytes
were not included in the stack size calculation.

This commit adds those 32 bytes. The code is rather verbose, but that is
intentional to make sure it is readable. This is tricky code that's hard
to get right, so I'd rather keep it well documented.
2020-08-30 18:23:20 +02:00
Ayke van Laethem 098fb5f39c nrf52840: add build tags for SoftDevice support
The SoftDevice should already be installed on these chips. Adding the
right build tags makes them work with the bluetooth package.

I did not change the HasLowFrequencyCrystal property: all these boards
use the MDBT50Q which appears to include a low-frequency oscillator.
That is, I tested the ItsyBitsy nRF52840 with the property set to true
and advertisement worked just fine.
2020-08-30 16:16:31 +02:00
Ayke van Laethem 47a975a44f nrf: add SoftDevice support for the Circuit Playground Bluefruit
This also fixes a bug: the Bluefruit doesn't have a low frequency
crystal. Somehow non-SoftDevice code still worked. However, the
SoftDevice won't initialize when this flag is set incorrectly.
2020-08-30 16:16:31 +02:00
deadprogram 83252448b0 device/atsamd51x: add all remaining bitfield values for PCHCTRLm Mapping
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-30 09:27:17 +02:00
deadprogram 222977a642 runtime/atsamd51x: use PCHCTRL_GCLK_SERCOMX_SLOW for setting clocks on all SERCOM ports
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-30 09:27:17 +02:00
deadprogram 58565fa46d machine/atsamd51x,runtime/atsamd51x: fixes needed for full support for all PWM pins. Also adds some useful constants to clarify peripheral clock usage
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-30 09:27:17 +02:00
Ayke van Laethem ecaf9461ce loader: be more robust when creating the cached GOROOT
This commit fixes two issues:

  * Do not try to create the cached GOROOT multiple times in parallel.
    This may happen in tests and is a waste of resources (and thus
    speed).
  * Check for an "access denied" error when trying to rename a directory
    over an existing directory. On *nix systems, this results in the
    expected "file exists" error. Unfortunately, Windows gives an access
    denied. This commit fixes the Windows behavior.
2020-08-29 00:02:55 +02:00
sago35 ae01904ab0 flash: add openocd settings to nrf5 2020-08-28 14:54:53 +02:00
Ayke van Laethem 2194d447e9 loader: use ioutil.TempDir to create a temporary directory
... instead of generating one with math/rand. The problem was that
math/rand is deterministic across runs, resulting in a possible race
when trying to create the same directory between two processes.
Additionally, because I used `os.MkdirAll`, no error was reported when
the directory already existed. The solution to this is to use the stdlib
function designed for this: ioutil.TempDir.
2020-08-28 14:48:37 +02:00
Ayke van Laethem a21a039ac7 arm: automatically determine stack sizes
This is a big change that will determine the stack size for many
goroutines automatically. Functions that aren't recursive and don't call
function pointers can in many cases have an automatically determined
worst case stack size. This is useful, as the stack size is usually much
lower than the previous hardcoded default of 1024 bytes: somewhere
around 200-500 bytes is common.

A side effect of this change is that the default stack sizes (including
the stack size for other architectures such as AVR) can now be changed
in the config JSON file, making it tunable per application.
2020-08-27 19:23:22 +02:00
Ayke van Laethem a761f556ff compiler: improve display of goroutine wrappers
This commit stores the original name of functions in metadata so it can
be recovered when printing goroutine names. This removes the .L prefix.
2020-08-27 19:23:22 +02:00
Ron Evans 29d65cb637 machine/itsybitsy-nrf52840: add support for Adafruit Itsybitsy nrf52840 (#1243)
* machine/itsybitsy-nrf52840: add support for Adafruit Itsybitsy nrf52840 Express board
2020-08-25 19:16:42 +02:00
Ayke van Laethem 63005622ae wasm: update wasi-libc dependency
Several updates are necessary for LLVM 11 support, so simply update to
the latest commit.
2020-08-25 18:42:42 +02:00
Ayke van Laethem 510f145a3a interp: show error line in first line of the traceback 2020-08-25 17:34:32 +02:00
sago35 e5e324f93e main: embed git-hash in tinygo-dev executable 2020-08-25 16:23:16 +02:00
Ayke van Laethem 4fa1fc6e72 interp: don't panic in the Store method
Instead return an error, which indicates where it goes wrong. That's
less user unfriendly than panicking.
2020-08-25 16:16:35 +02:00
Ayke van Laethem ccb803e35d interp: replace some panics with error messages
A number of functions now return errors instead of panicking, which
should help greatly when investigating interp errors. It at least shows
the package responsible for it.
2020-08-25 16:16:35 +02:00
sago35 d1ac0138e6 main: use ToSlash() to specify program path 2020-08-25 14:25:49 +02:00
sago35 b132b5bc60 flash: add openocd settings to atsamd21 / atsamd51 2020-08-25 14:18:01 +02:00
deadprogram 743254d5bc main: use simpler file copy instead of file renaming to avoid issues on nrf52840 UF2 bootloaders
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-25 13:51:15 +02:00
Ayke van Laethem 9d625a1ccb nrf: call sd_app_evt_wait when the SoftDevice is enabled
This reduces current consumption from 500-1000µA to very low (<10µA)
current consumption. This change is important for battery powered
devices, especially devices that may be running for long periods of
time.
2020-08-24 22:46:21 +02:00
Ayke van Laethem b75f042c23 runtime: use waitForEvents when appropriate
This is better than using the wfe or wfi instruction directly as it can
be replaced with build tags.
2020-08-24 22:46:21 +02:00
Elliott Sales de Andrade 32c7f3baf9 Remove --no-threads from wasm-ld calls.
The bugs linked from the original addition of this flag are fixed:
https://bugs.llvm.org/show_bug.cgi?id=41508
or 'possibly fixed':
https://bugs.llvm.org/show_bug.cgi?id=37064#c8
since LLVM 8.0.1. TinyGo only support LLVM 9+, and as noted in the
original PR (#377), this can be removed when switching to 9.

Additionally, this flag was dropped from LLVM 11.
2020-08-24 12:04:47 +02:00
deadprogram 1dc85ded47 version: update TinyGo version to 0.15.0-dev
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-24 12:04:47 +02:00
deadprogram 26a0819119 main: release 0.14.1
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-19 08:37:16 +02:00
Ayke van Laethem b59a46eef0 loader: work around Windows symlink limitation
Currently there will be a problem if the TinyGo installation directory
is not the same filesystem as the cache directory (usually the C drive)
and Developer Mode is disabled. Therefore, let's add another fallback
for when both conditions are true, falling back to copying the file
instead of symlinking/hardlinking it.
2020-08-19 08:37:16 +02:00
deadprogram 8a410b993b make,builder: incorporate feedback from code review on Go 1.15 update
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-19 08:37:16 +02:00
deadprogram e412a63a1c make: use buildmode flag to set exe for windows to use standard linker
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-19 08:37:16 +02:00
deadprogram a81face618 builder: simplify Go version check message
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-19 08:37:16 +02:00
deadprogram 0ac216b093 build: use Golang 1.15 for MS Azure builds
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-19 08:37:16 +02:00
deadprogram 9575fe628f internal/bytealg: naive attempt to copy the main Go 1.15 implementatation
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-19 08:37:16 +02:00
deadprogram f4e8ea0d23 builder: allow Go 1.15 to pass config check
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-19 08:37:16 +02:00
deadprogram 2d03f65d67 build: add Go 1.15 to CircleCI build 2020-08-19 08:37:16 +02:00
144 changed files with 4650 additions and 982 deletions
+53 -20
View File
@@ -44,28 +44,40 @@ commands:
command: |
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install ./google-chrome-stable_current_amd64.deb
install-xtensa-toolchain:
parameters:
variant:
type: string
steps:
- run:
name: "Install Xtensa toolchain"
command: |
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
llvm-source-linux:
steps:
- restore_cache:
keys:
- llvm-source-10-v0
- llvm-source-10-v1
- run:
name: "Fetch LLVM source"
command: make llvm-source
- save_cache:
key: llvm-source-10-v0
key: llvm-source-10-v1
paths:
- llvm-project
build-wasi-libc:
steps:
- restore_cache:
keys:
- wasi-libc-sysroot-v2
- wasi-libc-sysroot-v3
- run:
name: "Build wasi-libc"
command: make wasi-libc
- save_cache:
key: wasi-libc-sysroot-v2
key: wasi-libc-sysroot-v3
paths:
- lib/wasi-libc/sysroot
test-linux:
@@ -87,15 +99,16 @@ commands:
- run: go install -tags=llvm<<parameters.llvm>> .
- restore_cache:
keys:
- wasi-libc-sysroot-systemclang-v1
- wasi-libc-sysroot-systemclang-v2
- run: make wasi-libc
- save_cache:
key: wasi-libc-sysroot-systemclang-v1
key: wasi-libc-sysroot-systemclang-v2
paths:
- lib/wasi-libc/sysroot
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./interp ./transform .
- run: make gen-device -j4
- run: make smoketest
- run: make smoketest XTENSA=0
- run: make tinygo-test
- run: make wasmtest
- save_cache:
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
@@ -121,6 +134,8 @@ commands:
gcc-avr \
avr-libc
- install-node
- install-xtensa-toolchain:
variant: "linux-amd64"
- restore_cache:
keys:
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
@@ -128,7 +143,7 @@ commands:
- llvm-source-linux
- restore_cache:
keys:
- llvm-build-10-linux-v0-assert
- llvm-build-10-linux-v1-assert
- run:
name: "Build LLVM"
command: |
@@ -146,7 +161,7 @@ commands:
make ASSERT=1 llvm-build
fi
- save_cache:
key: llvm-build-10-linux-v0-assert
key: llvm-build-10-linux-v1-assert
paths:
llvm-build
- run: make ASSERT=1
@@ -179,6 +194,8 @@ commands:
gcc-avr \
avr-libc
- install-node
- install-xtensa-toolchain:
variant: "linux-amd64"
- restore_cache:
keys:
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
@@ -186,7 +203,7 @@ commands:
- llvm-source-linux
- restore_cache:
keys:
- llvm-build-10-linux-v0
- llvm-build-10-linux-v1
- run:
name: "Build LLVM"
command: |
@@ -204,7 +221,7 @@ commands:
make llvm-build
fi
- save_cache:
key: llvm-build-10-linux-v0
key: llvm-build-10-linux-v1
paths:
llvm-build
- build-wasi-libc
@@ -250,23 +267,25 @@ commands:
sudo tar -C /usr/local -xzf go1.14.darwin-amd64.tar.gz
ln -s /usr/local/go/bin/go /usr/local/bin/go
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
- install-xtensa-toolchain:
variant: "macos"
- restore_cache:
keys:
- go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-macos-v2-{{ checksum "go.mod" }}
- restore_cache:
keys:
- llvm-source-10-macos-v0
- llvm-source-10-macos-v1
- run:
name: "Fetch LLVM source"
command: make llvm-source
- save_cache:
key: llvm-source-10-macos-v0
key: llvm-source-10-macos-v1
paths:
- llvm-project
- restore_cache:
keys:
- llvm-build-10-macos-v0
- llvm-build-10-macos-v1
- run:
name: "Build LLVM"
command: |
@@ -278,17 +297,17 @@ commands:
make llvm-build
fi
- save_cache:
key: llvm-build-10-macos-v0
key: llvm-build-10-macos-v1
paths:
llvm-build
- restore_cache:
keys:
- wasi-libc-sysroot-macos-v1
- wasi-libc-sysroot-macos-v2
- run:
name: "Build wasi-libc"
command: make wasi-libc
- save_cache:
key: wasi-libc-sysroot-macos-v1
key: wasi-libc-sysroot-macos-v2
paths:
- lib/wasi-libc/sysroot
- run:
@@ -322,41 +341,48 @@ commands:
- run:
name: Create TinyGo user
command: useradd -m tinygo
- run:
name: Become TinyGo user
command: su tinygo
- run:
name: Start SSH Agent
user: tinygo
command: eval $(ssh-agent -s)
- run:
name: Add ARCH_RELEASE_SSH_PRIVATE_KEY identity
user: tinygo
command: echo "${ARCH_RELEASE_SSH_PRIVATE_KEY}" | tr -d '\r' | ssh-add -
- run:
name: Create SSH directory
user: tinygo
command: mkdir -p ~/.ssh && chmod 700 ~/.ssh
- run:
name: Add aur.archlinux.org to known hosts
user: tinygo
command: ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts
- run:
name: Clone tinygo-bin repo
user: tinygo
command: git clone ssh://aur@aur.archlinux.org/tinygo-bin.git ~/tinygo-bin
- run:
name: Update package version
user: tinygo
command: sed -i -E "s/(pkgver=)(.*)$/\1${CIRCLE_TAG}/" ~/tinygo-bin/PKGBUILD
- run:
name: Update file checksums
user: tinygo
command: cd ~/tinygo-bin && updpkgsums
- run:
name: Update .SRCINFO
user: tinygo
command: cd ~/tinygo-bin && makepkg --printsrcinfo > .SRCINFO
# Commit the update
- run:
name: Set git commit config
user: tinygo
command: |
git config --global user.email "tinygo-bot@tinygo.org" &&
git config --global user.name "TinyGo Release Bot"
- run:
name: Commit and push changes
user: tinygo
command: |
cd ~/tinygo-bin &&
git commit -a -m "Update tinygo-bin to v${CIRCLE_TAG}" &&
@@ -387,6 +413,12 @@ jobs:
steps:
- test-linux:
llvm: "10"
test-llvm10-go115:
docker:
- image: circleci/golang:1.15-buster
steps:
- test-linux:
llvm: "10"
assert-test-linux:
docker:
- image: circleci/golang:1.14-stretch
@@ -418,6 +450,7 @@ workflows:
- test-llvm10-go112
- test-llvm10-go113
- test-llvm10-go114
- test-llvm10-go115
- build-linux
- build-macos
- assert-test-linux
+1
View File
@@ -3,6 +3,7 @@ docs/_build
src/device/avr/*.go
src/device/avr/*.ld
src/device/avr/*.s
src/device/esp/*.go
src/device/nrf/*.go
src/device/nrf/*.s
src/device/nxp/*.go
+80
View File
@@ -1,3 +1,83 @@
0.15.0
---
* **command-line**
- add cached GOROOT to info subcommand
- embed git-hash in tinygo-dev executable
- implement tinygo targets to list usable targets
- use simpler file copy instead of file renaming to avoid issues on nrf52840 UF2 bootloaders
- use ToSlash() to specify program path
- support flashing esp32/esp8266 directly from tinygo
- when flashing call PortReset only on other than openocd
* **compiler**
- `compileopts`: add support for custom binary formats
- `compiler`: improve display of goroutine wrappers
- `interp`: don't panic in the Store method
- `interp`: replace some panics with error messages
- `interp`: show error line in first line of the traceback
- `loader`: be more robust when creating the cached GOROOT
- `loader`: rewrite/refactor much of the code to use go list directly
- `loader`: use ioutil.TempDir to create a temporary directory
- `stacksize`: deal with DW_CFA_advance_loc1
* **standard library**
- `runtime`: use waitForEvents when appropriate
* **wasm**
- `wasm`: Remove --no-threads from wasm-ld calls.
- `wasm`: update wasi-libc dependency
* **targets**
- `arduino-mega2560`: fix flashing on Windows
- `arm`: automatically determine stack sizes
- `arm64`: make dynamic loader structs and constants private
- `avr`: configure emulator in board files
- `cortexm`: fix stack size calculation with interrupts
- `flash`: add openocd settings to atsamd21 / atsamd51
- `flash`: add openocd settings to nrf5
- `microbit`: reelboard: flash using OpenOCD when needed
- `nintendoswitch`: Add dynamic loader for runtime loading PIE sections
- `nintendoswitch`: fix import cycle on dynamic_arm64.go
- `nintendoswitch`: Fix invalid memory read / write in print calls
- `nintendoswitch`: simplified assembly code
- `nintendoswitch`: support outputting .nro files directly
* **boards**
- `arduino-zero`: Adding support for the Arduino Zero (#1365)
- `atsamd2x`: fix BAUD value
- `atsamd5x`: fix BAUD value
- `bluepill`: Enable stm32's USART2 for the board and map it to UART1 tinygo's device
- `device/atsamd51x`: add all remaining bitfield values for PCHCTRLm Mapping
- `esp32`: add libgcc ROM functions to linker script
- `esp32`: add SPI support
- `esp32`: add support for basic GPIO
- `esp32`: add support for the Espressif ESP32 chip
- `esp32`: configure the I/O matrix for GPIO pins
- `esp32`: export machine.PortMask* for bitbanging implementations
- `esp8266`: add support for this chip
- `machine/atsamd51x,runtime/atsamd51x`: fixes needed for full support for all PWM pins. Also adds some useful constants to clarify peripheral clock usage
- `machine/itsybitsy-nrf52840`: add support for Adafruit Itsybitsy nrf52840 (#1243)
- `machine/stm32f4`: refactor common code and add new build tag stm32f4 (#1332)
- `nrf`: add SoftDevice support for the Circuit Playground Bluefruit
- `nrf`: call sd_app_evt_wait when the SoftDevice is enabled
- `nrf52840`: add build tags for SoftDevice support
- `nrf52840`: use higher priority for USB-CDC code
- `runtime/atsamd51x`: use PCHCTRL_GCLK_SERCOMX_SLOW for setting clocks on all SERCOM ports
- `stm32f405`: add basic UART handler
- `stm32f405`: add STM32F405 machine/runtime, and new board/target feather-stm32f405
* **build**
- `all`: run test binaries in the correct directory
- `build`: Fix arch release job
- `ci`: run `tinygo test` for known-working packages
- `ci`: set git-fetch-depth to 1
- `docker`: fix the problem with the wasm build (#1357)
- `Makefile`: check whether submodules have been downloaded in some common cases
* **docs**
- add ESP32, ESP8266, and Adafruit Feather STM32F405 to list of supported boards
0.14.1
---
* **command-line**
- support for Go 1.15
* **compiler**
- loader: work around Windows symlink limitation
0.14.0
---
* **command-line**
+1 -1
View File
@@ -15,4 +15,4 @@ Ayke van Laethem <aykevanlaethem@gmail.com>
Daniel Esteban <conejo@conejo.me>
Loon, LLC.
Ron Evans <ron@hybridgroup.com>
Jaden Weiss <jaden@jadendw.dev>
Nia Weiss <niaow1234@gmail.com>
+4 -1
View File
@@ -27,7 +27,10 @@ COPY --from=tinygo-base /go/bin/tinygo /go/bin/tinygo
COPY --from=tinygo-base /tinygo/src /tinygo/src
COPY --from=tinygo-base /tinygo/targets /tinygo/targets
RUN apt-get install -y libllvm10 lld-10
RUN cd /tinygo/ && \
apt-get update && \
apt-get install -y make clang-10 libllvm10 lld-10 && \
make wasi-libc
# tinygo-avr stage installs the needed dependencies to compile TinyGo programs for AVR microcontrollers.
FROM tinygo-base AS tinygo-avr
+40 -9
View File
@@ -118,9 +118,10 @@ fmt-check:
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
gen-device: gen-device-avr gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32 gen-device-kendryte gen-device-nxp
gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32 gen-device-kendryte gen-device-nxp
gen-device-avr:
@if [ ! -e lib/avr/README.md ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
$(GO) build -o ./build/gen-device-avr ./tools/gen-device-avr/
./build/gen-device-avr lib/avr/packs/atmega src/device/avr/
./build/gen-device-avr lib/avr/packs/tiny src/device/avr/
@@ -129,6 +130,10 @@ gen-device-avr:
build/gen-device-svd: ./tools/gen-device-svd/*.go
$(GO) build -o $@ ./tools/gen-device-svd/
gen-device-esp: build/gen-device-svd
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Espressif-Community -interrupts=software lib/cmsis-svd/data/Espressif-Community/ src/device/esp/
GO111MODULE=off $(GO) fmt ./src/device/esp
gen-device-nrf: build/gen-device-svd
./build/gen-device-svd -source=https://github.com/NordicSemiconductor/nrfx/tree/master/mdk lib/nrfx/mdk/ src/device/nrf/
GO111MODULE=off $(GO) fmt ./src/device/nrf
@@ -156,13 +161,13 @@ gen-device-stm32: build/gen-device-svd
# Get LLVM sources.
$(LLVM_PROJECTDIR)/README.md:
git clone -b release/10.x --depth=1 https://github.com/llvm/llvm-project $(LLVM_PROJECTDIR)
git clone -b xtensa_release_10.0.1 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
llvm-source: $(LLVM_PROJECTDIR)/README.md
# Configure LLVM.
TINYGO_SOURCE_DIR=$(shell pwd)
$(LLVM_BUILDDIR)/build.ninja: llvm-source
mkdir -p $(LLVM_BUILDDIR); cd $(LLVM_BUILDDIR); cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF $(LLVM_OPTION)
mkdir -p $(LLVM_BUILDDIR); cd $(LLVM_BUILDDIR); cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF $(LLVM_OPTION)
# Build LLVM.
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
@@ -173,19 +178,26 @@ $(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
.PHONY: wasi-libc
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
cd lib/wasi-libc && make -j4 WASM_CC=$(CLANG) WASM_AR=$(LLVM_AR) WASM_NM=$(LLVM_NM)
# Build the Go compiler.
tinygo:
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -o build/tinygo$(EXE) -tags byollvm .
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
test: wasi-libc
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -tags byollvm ./cgo ./compileopts ./interp ./transform .
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./cgo ./compileopts ./interp ./transform .
# Test known-working standard library packages.
# TODO: do this in one command, parallelize, and only show failing tests (no
# implied -v flag).
.PHONY: tinygo-test
tinygo-test:
cd tests/tinygotest && tinygo test
$(TINYGO) test container/list
$(TINYGO) test container/ring
$(TINYGO) test text/scanner
.PHONY: smoketest
smoketest:
@@ -267,7 +279,9 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky2
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=stm32f4disco-1 examples/blinky1
$(TINYGO) build -size short -o test.hex -target=stm32f4disco-1 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-stm32f405 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=circuitplay-bluefruit examples/blinky1
@$(MD5SUM) test.hex
@@ -315,6 +329,17 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=itsybitsy-nrf52840 examples/blinky1
@$(MD5SUM) test.hex
# test pwm
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m4 examples/pwm
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pyportal examples/pwm
@$(MD5SUM) test.hex
ifneq ($(AVR), 0)
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
@$(MD5SUM) test.hex
@@ -330,6 +355,12 @@ ifneq ($(AVR), 0)
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
@$(MD5SUM) test.hex
endif
ifneq ($(XTENSA), 0)
$(TINYGO) build -size short -o test.bin -target=esp32-wroom-32 examples/blinky1
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
@$(MD5SUM) test.bin
endif
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
@$(MD5SUM) test.hex
@@ -342,8 +373,8 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=1 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -o test.elf -target=nintendoswitch examples/serial
@$(MD5SUM) test.elf
$(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
@$(MD5SUM) test.nro
wasmtest:
$(GO) test ./tests/wasm
+6 -1
View File
@@ -43,7 +43,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
The following 39 microcontroller boards/devices are currently supported:
The following 44 microcontroller boards are currently supported:
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
@@ -51,8 +51,10 @@ The following 39 microcontroller boards/devices are currently supported:
* [Adafruit Feather M0](https://www.adafruit.com/product/2772)
* [Adafruit Feather M4](https://www.adafruit.com/product/3857)
* [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)
* [Adafruit Feather STM32F405 Express](https://www.adafruit.com/product/4382)
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
* [Adafruit ItsyBitsy nRF52840](https://www.adafruit.com/product/4481)
* [Adafruit Metro M4 Express Airlift](https://www.adafruit.com/product/4000)
* [Adafruit PyBadge](https://www.adafruit.com/product/4200)
* [Adafruit PyGamer](https://www.adafruit.com/product/4242)
@@ -62,8 +64,11 @@ The following 39 microcontroller boards/devices are currently supported:
* [Arduino Nano](https://store.arduino.cc/arduino-nano)
* [Arduino Nano33 IoT](https://store.arduino.cc/nano-33-iot)
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
* [Arduino Zero](https://store.arduino.cc/usa/arduino-zero)
* [BBC micro:bit](https://microbit.org/)
* [Digispark](http://digistump.com/products/1)
* [ESP32](https://www.espressif.com/en/products/socs/esp32)
* [ESP8266](https://www.espressif.com/en/products/socs/esp8266)
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
* [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/)
* [Nintendo Switch](https://www.nintendo.com/switch/)
+12 -7
View File
@@ -12,22 +12,27 @@ jobs:
steps:
- task: GoTool@0
inputs:
version: '1.14.1'
version: '1.15'
- checkout: self
- task: CacheBeta@0
fetchDepth: 1
- task: Cache@2
displayName: Cache LLVM source
inputs:
key: llvm-source-10-windows-v0
key: llvm-source-10-windows-v1
path: llvm-project
- task: Bash@3
displayName: Download LLVM source
inputs:
targetType: inline
script: make llvm-source
script: |
make llvm-source
# Workaround for bad symlinks:
# https://github.com/microsoft/azure-pipelines-tasks/issues/13418
rm -f llvm-project/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/bad_symlink
- task: CacheBeta@0
displayName: Cache LLVM build
inputs:
key: llvm-build-10-windows-v0
key: llvm-build-10-windows-v1
path: llvm-build
- task: Bash@3
displayName: Build LLVM
@@ -47,7 +52,7 @@ jobs:
- task: CacheBeta@0
displayName: Cache wasi-libc sysroot
inputs:
key: wasi-libc-sysroot-v2
key: wasi-libc-sysroot-v3
path: lib/wasi-libc/sysroot
- task: Bash@3
displayName: Build wasi-libc
@@ -80,4 +85,4 @@ jobs:
script: |
export PATH="$PATH:./llvm-build/bin:/c/Program Files/qemu"
unset GOROOT
make smoketest TINYGO=build/tinygo AVR=0
make smoketest TINYGO=build/tinygo AVR=0 XTENSA=0
+287 -80
View File
@@ -5,6 +5,7 @@ package builder
import (
"debug/elf"
"encoding/binary"
"errors"
"fmt"
"io/ioutil"
@@ -23,22 +24,35 @@ import (
"tinygo.org/x/go-llvm"
)
// BuildResult is the output of a build. This includes the binary itself and
// some other metadata that is obtained while building the binary.
type BuildResult struct {
// A path to the output binary. It will be removed after Build returns, so
// if it should be kept it must be copied or moved away.
Binary string
// The directory of the main package. This is useful for testing as the test
// binary must be run in the directory of the tested package.
MainDir string
}
// Build performs a single package to executable Go build. It takes in a package
// name, an output path, and set of compile options and from that it manages the
// whole compilation process.
//
// The error value may be of type *MultiError. Callers will likely want to check
// for this case and print such errors individually.
func Build(pkgName, outpath string, config *compileopts.Config, action func(string) error) error {
func Build(pkgName, outpath string, config *compileopts.Config, action func(BuildResult) error) error {
// Compile Go code to IR.
machine, err := compiler.NewTargetMachine(config)
if err != nil {
return err
}
mod, extraFiles, extraLDFlags, errs := compiler.Compile(pkgName, machine, config)
buildOutput, errs := compiler.Compile(pkgName, machine, config)
if errs != nil {
return newMultiError(errs)
}
mod := buildOutput.Mod
if config.Options.PrintIR {
fmt.Println("; Generated LLVM IR:")
@@ -115,6 +129,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
}
}
// Make sure stack sizes are loaded from a separate section so they can be
// modified after linking.
var stackSizeLoads []string
if config.AutomaticStackSize() {
stackSizeLoads = transform.CreateStackSizeLoads(mod, config)
}
// Generate output.
outext := filepath.Ext(outpath)
switch outext {
@@ -188,7 +209,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
}
// Compile C files in packages.
for i, file := range extraFiles {
for i, file := range buildOutput.ExtraFiles {
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+filepath.Base(file)+".o")
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...)
if err != nil {
@@ -197,8 +218,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
ldflags = append(ldflags, outpath)
}
if len(extraLDFlags) > 0 {
ldflags = append(ldflags, extraLDFlags...)
if len(buildOutput.ExtraLDFlags) > 0 {
ldflags = append(ldflags, buildOutput.ExtraLDFlags...)
}
// Link the object files together.
@@ -207,6 +228,26 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
return &commandError{"failed to link", executable, err}
}
var calculatedStacks []string
var stackSizes map[string]functionStackSize
if config.Options.PrintStacks || config.AutomaticStackSize() {
// Try to determine stack sizes at compile time.
// Don't do this by default as it usually doesn't work on
// unsupported architectures.
calculatedStacks, stackSizes, err = determineStackSizes(mod, executable)
if err != nil {
return err
}
}
if config.AutomaticStackSize() {
// Modify the .tinygo_stacksizes section that contains a stack size
// for each goroutine.
err = modifyStackSizes(executable, stackSizeLoads, stackSizes)
if err != nil {
return fmt.Errorf("could not modify stack sizes: %w", err)
}
}
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
sizes, err := loadProgramSize(executable)
if err != nil {
@@ -228,28 +269,250 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
// Print goroutine stack sizes, as far as possible.
if config.Options.PrintStacks {
printStacks(mod, executable)
printStacks(calculatedStacks, stackSizes)
}
// Get an Intel .hex file or .bin file from the .elf file.
if outext == ".hex" || outext == ".bin" || outext == ".gba" {
outputBinaryFormat := config.BinaryFormat(outext)
switch outputBinaryFormat {
case "elf":
// do nothing, file is already in ELF format
case "hex", "bin":
// Extract raw binary, either encoding it as a hex file or as a raw
// firmware file.
tmppath = filepath.Join(dir, "main"+outext)
err := objcopy(executable, tmppath)
err := objcopy(executable, tmppath, outputBinaryFormat)
if err != nil {
return err
}
} else if outext == ".uf2" {
case "uf2":
// Get UF2 from the .elf file.
tmppath = filepath.Join(dir, "main"+outext)
err := convertELFFileToUF2File(executable, tmppath, config.Target.UF2FamilyID)
if err != nil {
return err
}
case "esp32", "esp8266":
// Special format for the ESP family of chips (parsed by the ROM
// bootloader).
tmppath = filepath.Join(dir, "main"+outext)
err := makeESPFirmareImage(executable, tmppath, outputBinaryFormat)
if err != nil {
return err
}
default:
return fmt.Errorf("unknown output binary format: %s", outputBinaryFormat)
}
return action(tmppath)
return action(BuildResult{
Binary: tmppath,
MainDir: buildOutput.MainDir,
})
}
}
// functionStackSizes keeps stack size information about a single function
// (usually a goroutine).
type functionStackSize struct {
humanName string
stackSize uint64
stackSizeType stacksize.SizeType
missingStackSize *stacksize.CallNode
}
// determineStackSizes tries to determine the stack sizes of all started
// goroutines and of the reset vector. The LLVM module is necessary to find
// functions that call a function pointer.
func determineStackSizes(mod llvm.Module, executable string) ([]string, map[string]functionStackSize, error) {
var callsIndirectFunction []string
gowrappers := []string{}
gowrapperNames := make(map[string]string)
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
// Determine which functions call a function pointer.
for bb := fn.FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
if inst.IsACallInst().IsNil() {
continue
}
if callee := inst.CalledValue(); callee.IsAFunction().IsNil() && callee.IsAInlineAsm().IsNil() {
callsIndirectFunction = append(callsIndirectFunction, fn.Name())
}
}
}
// Get a list of "go wrappers", small wrapper functions that decode
// parameters when starting a new goroutine.
attr := fn.GetStringAttributeAtIndex(-1, "tinygo-gowrapper")
if !attr.IsNil() {
gowrappers = append(gowrappers, fn.Name())
gowrapperNames[fn.Name()] = attr.GetStringValue()
}
}
sort.Strings(gowrappers)
// Load the ELF binary.
f, err := elf.Open(executable)
if err != nil {
return nil, nil, fmt.Errorf("could not load executable for stack size analysis: %w", err)
}
defer f.Close()
// Determine the frame size of each function (if available) and the callgraph.
functions, err := stacksize.CallGraph(f, callsIndirectFunction)
if err != nil {
return nil, nil, fmt.Errorf("could not parse executable for stack size analysis: %w", err)
}
// Goroutines need to be started and finished and take up some stack space
// that way. This can be measured by measuing the stack size of
// tinygo_startTask.
if numFuncs := len(functions["tinygo_startTask"]); numFuncs != 1 {
return nil, nil, fmt.Errorf("expected exactly one definition of tinygo_startTask, got %d", numFuncs)
}
baseStackSize, baseStackSizeType, baseStackSizeFailedAt := functions["tinygo_startTask"][0].StackSize()
sizes := make(map[string]functionStackSize)
// Add the reset handler function, for convenience. The reset handler runs
// startup code and the scheduler. The listed stack size is not the full
// stack size: interrupts are not counted.
var resetFunction string
switch f.Machine {
case elf.EM_ARM:
// Note: all interrupts happen on this stack so the real size is bigger.
resetFunction = "Reset_Handler"
}
if resetFunction != "" {
funcs := functions[resetFunction]
if len(funcs) != 1 {
return nil, nil, fmt.Errorf("expected exactly one definition of %s in the callgraph, found %d", resetFunction, len(funcs))
}
stackSize, stackSizeType, missingStackSize := funcs[0].StackSize()
sizes[resetFunction] = functionStackSize{
stackSize: stackSize,
stackSizeType: stackSizeType,
missingStackSize: missingStackSize,
humanName: resetFunction,
}
}
// Add all goroutine wrapper functions.
for _, name := range gowrappers {
funcs := functions[name]
if len(funcs) != 1 {
return nil, nil, fmt.Errorf("expected exactly one definition of %s in the callgraph, found %d", name, len(funcs))
}
humanName := gowrapperNames[name]
if humanName == "" {
humanName = name // fallback
}
stackSize, stackSizeType, missingStackSize := funcs[0].StackSize()
if baseStackSizeType != stacksize.Bounded {
// It was not possible to determine the stack size at compile time
// because tinygo_startTask does not have a fixed stack size. This
// can happen when using -opt=1.
stackSizeType = baseStackSizeType
missingStackSize = baseStackSizeFailedAt
} else if stackSize < baseStackSize {
// This goroutine has a very small stack, but still needs to fit all
// registers to start and suspend the goroutine. Otherwise a stack
// overflow will occur even before the goroutine is started.
stackSize = baseStackSize
}
sizes[name] = functionStackSize{
stackSize: stackSize,
stackSizeType: stackSizeType,
missingStackSize: missingStackSize,
humanName: humanName,
}
}
if resetFunction != "" {
return append([]string{resetFunction}, gowrappers...), sizes, nil
}
return gowrappers, sizes, nil
}
// modifyStackSizes modifies the .tinygo_stacksizes section with the updated
// stack size information. Before this modification, all stack sizes in the
// section assume the default stack size (which is relatively big).
func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map[string]functionStackSize) error {
fp, err := os.OpenFile(executable, os.O_RDWR, 0)
if err != nil {
return err
}
defer fp.Close()
elfFile, err := elf.NewFile(fp)
if err != nil {
return err
}
section := elfFile.Section(".tinygo_stacksizes")
if section == nil {
return errors.New("could not find .tinygo_stacksizes section")
}
if section.Size != section.FileSize {
// Sanity check.
return fmt.Errorf("expected .tinygo_stacksizes to have identical size and file size, got %d and %d", section.Size, section.FileSize)
}
// Read all goroutine stack sizes.
data := make([]byte, section.Size)
_, err = fp.ReadAt(data, int64(section.Offset))
if err != nil {
return err
}
if len(stackSizeLoads)*4 != len(data) {
// Note: while AVR should use 2 byte stack sizes, even 64-bit platforms
// should probably stick to 4 byte stack sizes as a larger than 4GB
// stack doesn't make much sense.
return errors.New("expected 4 byte stack sizes")
}
// Modify goroutine stack sizes with a compile-time known worst case stack
// size.
for i, name := range stackSizeLoads {
fn, ok := stackSizes[name]
if !ok {
return fmt.Errorf("could not find symbol %s in ELF file", name)
}
if fn.stackSizeType == stacksize.Bounded {
stackSize := uint32(fn.stackSize)
// Adding 4 for the stack canary. Even though the size may be
// automatically determined, stack overflow checking is still
// important as the stack size cannot be determined for all
// goroutines.
stackSize += 4
// Add stack size used by interrupts.
switch elfFile.Machine {
case elf.EM_ARM:
// On Cortex-M (assumed here), this stack size is 8 words or 32
// bytes. This is only to store the registers that the interrupt
// may modify, the interrupt will switch to the interrupt stack
// (MSP).
// Some background:
// https://interrupt.memfault.com/blog/cortex-m-rtos-context-switching
stackSize += 32
}
// Finally write the stack size to the binary.
binary.LittleEndian.PutUint32(data[i*4:], stackSize)
}
}
// Write back the modified stack sizes.
_, err = fp.WriteAt(data, int64(section.Offset))
if err != nil {
return err
}
return nil
}
// printStacks prints the maximum stack depth for functions that are started as
// goroutines. Stack sizes cannot always be determined statically, in particular
// recursive functions and functions that call interface methods or function
@@ -260,78 +523,22 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
//
// function stack usage (in bytes)
// Reset_Handler 316
// .Lexamples/blinky2.led1 92
// .Lruntime.run$1 300
func printStacks(mod llvm.Module, executable string) {
// Determine which functions call a function pointer.
var callsIndirectFunction []string
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
for bb := fn.FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
if inst.IsACallInst().IsNil() {
continue
}
if callee := inst.CalledValue(); callee.IsAFunction().IsNil() && callee.IsAInlineAsm().IsNil() {
callsIndirectFunction = append(callsIndirectFunction, fn.Name())
}
}
}
}
// Load the ELF binary.
f, err := elf.Open(executable)
if err != nil {
fmt.Fprintln(os.Stderr, "could not load executable for stack size analysis:", err)
return
}
defer f.Close()
// Determine the frame size of each function (if available) and the callgraph.
functions, err := stacksize.CallGraph(f, callsIndirectFunction)
if err != nil {
fmt.Fprintln(os.Stderr, "could not parse executable for stack size analysis:", err)
return
}
// Get a list of "go wrappers", small wrapper functions that decode
// parameters when starting a new goroutine.
var gowrappers []string
for name := range functions {
if strings.HasSuffix(name, "$gowrapper") {
gowrappers = append(gowrappers, name)
}
}
sort.Strings(gowrappers)
switch f.Machine {
case elf.EM_ARM:
// Add the reset handler, which runs startup code and is the
// interrupt/scheduler stack with -scheduler=tasks.
// Note that because interrupts happen on this stack, the stack needed
// by just the Reset_Handler is not enough. Stacks needed by interrupt
// handlers should also be taken into account.
gowrappers = append([]string{"Reset_Handler"}, gowrappers...)
}
// examples/blinky2.led1 92
// runtime.run$1 300
func printStacks(calculatedStacks []string, stackSizes map[string]functionStackSize) {
// Print the sizes of all stacks.
fmt.Printf("%-32s %s\n", "function", "stack usage (in bytes)")
for _, name := range gowrappers {
for _, fn := range functions[name] {
stackSize, stackSizeType, missingStackSize := fn.StackSize()
strippedName := name
if strings.HasSuffix(name, "$gowrapper") {
strippedName = name[:len(name)-len("$gowrapper")]
}
switch stackSizeType {
case stacksize.Bounded:
fmt.Printf("%-32s %d\n", strippedName, stackSize)
case stacksize.Unknown:
fmt.Printf("%-32s unknown, %s does not have stack frame information\n", strippedName, missingStackSize)
case stacksize.Recursive:
fmt.Printf("%-32s recursive, %s may call itself\n", strippedName, missingStackSize)
case stacksize.IndirectCall:
fmt.Printf("%-32s unknown, %s calls a function pointer\n", strippedName, missingStackSize)
}
for _, name := range calculatedStacks {
fn := stackSizes[name]
switch fn.stackSizeType {
case stacksize.Bounded:
fmt.Printf("%-32s %d\n", fn.humanName, fn.stackSize)
case stacksize.Unknown:
fmt.Printf("%-32s unknown, %s does not have stack frame information\n", fn.humanName, fn.missingStackSize)
case stacksize.Recursive:
fmt.Printf("%-32s recursive, %s may call itself\n", fn.humanName, fn.missingStackSize)
case stacksize.IndirectCall:
fmt.Printf("%-32s unknown, %s calls a function pointer\n", fn.humanName, fn.missingStackSize)
}
}
}
+2 -2
View File
@@ -25,8 +25,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
if err != nil {
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
}
if major != 1 || minor < 11 || minor > 14 {
return nil, fmt.Errorf("requires go version 1.11, 1.12, 1.13, or 1.14, got go%d.%d", major, minor)
if major != 1 || minor < 11 || minor > 15 {
return nil, fmt.Errorf("requires go version 1.11 through 1.15, got go%d.%d", major, minor)
}
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
return &compileopts.Config{
+153
View File
@@ -0,0 +1,153 @@
package builder
// This file implements support for writing ESP image files. These image files
// are read by the ROM bootloader so have to be in a particular format.
//
// In the future, it may be necessary to implement support for other image
// formats, such as the ESP8266 image formats (again, used by the ROM bootloader
// to load the firmware).
import (
"bytes"
"crypto/sha256"
"debug/elf"
"encoding/binary"
"fmt"
"io/ioutil"
"sort"
)
type espImageSegment struct {
addr uint32
data []byte
}
// makeESPFirmare converts an input ELF file to an image file for an ESP32 or
// ESP8266 chip. This is a special purpose image format just for the ESP chip
// family, and is parsed by the on-chip mask ROM bootloader.
//
// The following documentation has been used:
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format
// https://github.com/espressif/esp-idf/blob/8fbb63c2a701c22ccf4ce249f43aded73e134a34/components/bootloader_support/include/esp_image_format.h#L58
// https://github.com/espressif/esptool/blob/master/esptool.py
func makeESPFirmareImage(infile, outfile, format string) error {
inf, err := elf.Open(infile)
if err != nil {
return err
}
defer inf.Close()
// Load all segments to be written to the image. These are actually ELF
// sections, not true ELF segments (similar to how esptool does it).
var segments []*espImageSegment
for _, section := range inf.Sections {
if section.Type != elf.SHT_PROGBITS || section.Size == 0 || section.Flags&elf.SHF_ALLOC == 0 {
continue
}
data, err := section.Data()
if err != nil {
return fmt.Errorf("failed to read section data: %w", err)
}
for len(data)%4 != 0 {
// Align segment to 4 bytes.
data = append(data, 0)
}
if uint64(uint32(section.Addr)) != section.Addr {
return fmt.Errorf("section address too big: 0x%x", section.Addr)
}
segments = append(segments, &espImageSegment{
addr: uint32(section.Addr),
data: data,
})
}
// Sort the segments by address. This is what esptool does too.
sort.SliceStable(segments, func(i, j int) bool { return segments[i].addr < segments[j].addr })
// Calculate checksum over the segment data. This is used in the image
// footer.
checksum := uint8(0xef)
for _, segment := range segments {
for _, b := range segment.data {
checksum ^= b
}
}
// Write first to an in-memory buffer, primarily so that we can easily
// calculate a hash over the entire image.
// An added benefit is that we don't need to check for errors all the time.
outf := &bytes.Buffer{}
// Image header.
switch format {
case "esp32":
// Header format:
// https://github.com/espressif/esp-idf/blob/8fbb63c2/components/bootloader_support/include/esp_image_format.h#L58
binary.Write(outf, binary.LittleEndian, struct {
magic uint8
segment_count uint8
spi_mode uint8
spi_speed_size uint8
entry_addr uint32
wp_pin uint8
spi_pin_drv [3]uint8
reserved [11]uint8
hash_appended bool
}{
magic: 0xE9,
segment_count: byte(len(segments)),
spi_mode: 0, // irrelevant, replaced by esptool when flashing
spi_speed_size: 0, // spi_speed, spi_size: replaced by esptool when flashing
entry_addr: uint32(inf.Entry),
wp_pin: 0xEE, // disable WP pin
hash_appended: true, // add a SHA256 hash
})
case "esp8266":
// Header format:
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format
// Basically a truncated version of the ESP32 header.
binary.Write(outf, binary.LittleEndian, struct {
magic uint8
segment_count uint8
spi_mode uint8
spi_speed_size uint8
entry_addr uint32
}{
magic: 0xE9,
segment_count: byte(len(segments)),
spi_mode: 0, // irrelevant, replaced by esptool when flashing
spi_speed_size: 0x20, // spi_speed, spi_size: replaced by esptool when flashing
entry_addr: uint32(inf.Entry),
})
default:
return fmt.Errorf("builder: unknown binary format %#v, expected esp32 or esp8266", format)
}
// Write all segments to the image.
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format#segment
for _, segment := range segments {
binary.Write(outf, binary.LittleEndian, struct {
addr uint32
length uint32
}{
addr: segment.addr,
length: uint32(len(segment.data)),
})
outf.Write(segment.data)
}
// Footer, including checksum.
// The entire image size must be a multiple of 16, so pad the image to one
// byte less than that before writing the checksum.
outf.Write(make([]byte, 15-outf.Len()%16))
outf.WriteByte(checksum)
if format == "esp32" {
// SHA256 hash (to protect against image corruption, not for security).
hash := sha256.Sum256(outf.Bytes())
outf.Write(hash[:])
}
// Write the image to the output file.
return ioutil.WriteFile(outfile, outf.Bytes(), 0666)
}
+19 -14
View File
@@ -4,12 +4,15 @@ import (
"debug/elf"
"io/ioutil"
"os"
"path/filepath"
"sort"
"github.com/marcinbor85/gohex"
)
// maxPadBytes is the maximum allowed bytes to be padded in a rom extraction
// this value is currently defined by Nintendo Switch Page Alignment (4096 bytes)
const maxPadBytes = 4095
// objcopyError is an error returned by functions that act like objcopy.
type objcopyError struct {
Op string
@@ -71,7 +74,12 @@ func extractROM(path string) (uint64, []byte, error) {
var rom []byte
for _, prog := range progs {
if prog.Paddr != progs[0].Paddr+uint64(len(rom)) {
return 0, nil, objcopyError{"ROM segments are non-contiguous: " + path, nil}
diff := prog.Paddr - (progs[0].Paddr + uint64(len(rom)))
if diff > maxPadBytes {
return 0, nil, objcopyError{"ROM segments are non-contiguous: " + path, nil}
}
// Pad the difference
rom = append(rom, make([]byte, diff)...)
}
data, err := ioutil.ReadAll(prog.Open())
if err != nil {
@@ -93,7 +101,7 @@ func extractROM(path string) (uint64, []byte, error) {
// objcopy converts an ELF file to a different (simpler) output file format:
// .bin or .hex. It extracts only the .text section.
func objcopy(infile, outfile string) error {
func objcopy(infile, outfile, binaryFormat string) error {
f, err := os.OpenFile(outfile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return err
@@ -107,23 +115,20 @@ func objcopy(infile, outfile string) error {
}
// Write to the file, in the correct format.
switch filepath.Ext(outfile) {
case ".gba":
// The address is not stored in a .gba file.
_, err := f.Write(data)
return err
case ".bin":
// The address is not stored in a .bin file (therefore you
// should use .hex files in most cases).
_, err := f.Write(data)
return err
case ".hex":
switch binaryFormat {
case "hex":
// Intel hex file, includes the firmware start address.
mem := gohex.NewMemory()
err := mem.AddBinary(uint32(addr), data)
if err != nil {
return objcopyError{"failed to create .hex file", err}
}
return mem.DumpIntelHex(f, 16)
case "bin":
// The start address is not stored in raw firmware files (therefore you
// should use .hex files in most cases).
_, err := f.Write(data)
return err
default:
panic("unreachable")
}
+35
View File
@@ -164,6 +164,16 @@ func (c *Config) PanicStrategy() string {
return c.Options.PanicStrategy
}
// AutomaticStackSize returns whether goroutine stack sizes should be determined
// automatically at compile time, if possible. If it is false, no attempt is
// made.
func (c *Config) AutomaticStackSize() bool {
if c.Target.AutoStackSize != nil && c.Scheduler() == "tasks" {
return *c.Target.AutoStackSize
}
return false
}
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo
// preprocessing.
func (c *Config) CFlags() []string {
@@ -229,6 +239,31 @@ func (c *Config) Debug() bool {
return c.Options.Debug
}
// BinaryFormat returns an appropriate binary format, based on the file
// extension and the configured binary format in the target JSON file.
func (c *Config) BinaryFormat(ext string) string {
switch ext {
case ".bin", ".gba", ".nro":
// The simplest format possible: dump everything in a raw binary file.
if c.Target.BinaryFormat != "" {
return c.Target.BinaryFormat
}
return "bin"
case ".hex":
// Similar to bin, but includes the start address and is thus usually a
// better format.
return "hex"
case ".uf2":
// Special purpose firmware format, mainly used on Adafruit boards.
// More information:
// https://github.com/Microsoft/uf2
return "uf2"
default:
// Use the ELF format for unrecognized file formats.
return "elf"
}
}
// Programmer returns the flash method and OpenOCD interface name given a
// particular configuration. It may either be all configured in the target JSON
// file or be modified using the -programmmer command-line option.
+12
View File
@@ -33,6 +33,8 @@ type TargetSpec struct {
Linker string `json:"linker"`
RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt)
Libc string `json:"libc"`
AutoStackSize *bool `json:"automatic-stack-size"` // Determine stack size automatically at compile time.
DefaultStackSize uint64 `json:"default-stack-size"` // Default stack size if the size couldn't be determined at compile time.
CFlags []string `json:"cflags"`
LDFlags []string `json:"ldflags"`
LinkerScript string `json:"linkerscript"`
@@ -45,6 +47,7 @@ type TargetSpec struct {
FlashVolume string `json:"msd-volume-name"`
FlashFilename string `json:"msd-firmware-name"`
UF2FamilyID string `json:"uf2-family-id"`
BinaryFormat string `json:"binary-format"`
OpenOCDInterface string `json:"openocd-interface"`
OpenOCDTarget string `json:"openocd-target"`
OpenOCDTransport string `json:"openocd-transport"`
@@ -90,6 +93,12 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
if spec2.Libc != "" {
spec.Libc = spec2.Libc
}
if spec2.AutoStackSize != nil {
spec.AutoStackSize = spec2.AutoStackSize
}
if spec2.DefaultStackSize != 0 {
spec.DefaultStackSize = spec2.DefaultStackSize
}
spec.CFlags = append(spec.CFlags, spec2.CFlags...)
spec.LDFlags = append(spec.LDFlags, spec2.LDFlags...)
if spec2.LinkerScript != "" {
@@ -120,6 +129,9 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
if spec2.UF2FamilyID != "" {
spec.UF2FamilyID = spec2.UF2FamilyID
}
if spec2.BinaryFormat != "" {
spec.BinaryFormat = spec2.BinaryFormat
}
if spec2.OpenOCDInterface != "" {
spec.OpenOCDInterface = spec2.OpenOCDInterface
}
+38 -48
View File
@@ -5,18 +5,15 @@ import (
"errors"
"fmt"
"go/ast"
"go/build"
"go/constant"
"go/token"
"go/types"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"github.com/tinygo-org/tinygo/goenv"
"github.com/tinygo-org/tinygo/ir"
"github.com/tinygo-org/tinygo/loader"
"golang.org/x/tools/go/ssa"
@@ -130,6 +127,28 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
return machine, nil
}
// CompilerOutput is returned from the Compile() call. It contains the compile
// output and information necessary to continue to compile and link the program.
type CompilerOutput struct {
// The LLVM module that contains the compiled but not optimized LLVM module
// for all the Go code in the program.
Mod llvm.Module
// ExtraFiles is a list of C source files included in packages that should
// be built and linked together with the main executable to form one
// program. They can be used from CGo, for example.
ExtraFiles []string
// ExtraLDFlags are linker flags obtained during CGo processing. These flags
// must be passed to the linker which links the entire executable.
ExtraLDFlags []string
// MainDir is the absolute directory path to the directory of the main
// package. This is useful for testing: tests must be run in the package
// directory that is being tested.
MainDir string
}
// Compile the given package path or .go file path. Return an error when this
// fails (in any stage). If successful it returns the LLVM module and a list of
// extra C files to be compiled. If not, one or more errors will be returned.
@@ -138,7 +157,7 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
// violation. Eventually, this Compile function should only compile a single
// package and not the whole program, and loading of the program (including CGo
// processing) should be moved outside the compiler package.
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (mod llvm.Module, extrafiles []string, extraldflags []string, errors []error) {
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (output CompilerOutput, errors []error) {
c := &compilerContext{
Config: config,
difiles: make(map[string]llvm.Metadata),
@@ -154,6 +173,7 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
if c.Debug() {
c.dibuilder = llvm.NewDIBuilder(c.mod)
}
output.Mod = c.mod
c.uintptrType = c.ctx.IntType(c.targetData.PointerSize() * 8)
if c.targetData.PointerSize() <= 4 {
@@ -172,55 +192,29 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
dummyFunc.EraseFromParentAsFunction()
wd, err := os.Getwd()
lprogram, err := loader.Load(c.Config, []string{pkgName}, c.ClangHeaders, types.Config{
Sizes: &stdSizes{
IntSize: int64(c.targetData.TypeAllocSize(c.intType)),
PtrSize: int64(c.targetData.PointerSize()),
MaxAlign: int64(c.targetData.PrefTypeAlignment(c.i8ptrType)),
}})
if err != nil {
return c.mod, nil, nil, []error{err}
}
goroot, err := loader.GetCachedGoroot(c.Config)
if err != nil {
return c.mod, nil, nil, []error{err}
}
lprogram := &loader.Program{
Build: &build.Context{
GOARCH: c.GOARCH(),
GOOS: c.GOOS(),
GOROOT: goroot,
GOPATH: goenv.Get("GOPATH"),
CgoEnabled: c.CgoEnabled(),
UseAllFiles: false,
Compiler: "gc", // must be one of the recognized compilers
BuildTags: c.BuildTags(),
},
Tests: c.TestConfig.CompileTestBinary,
TypeChecker: types.Config{
Sizes: &stdSizes{
IntSize: int64(c.targetData.TypeAllocSize(c.intType)),
PtrSize: int64(c.targetData.PointerSize()),
MaxAlign: int64(c.targetData.PrefTypeAlignment(c.i8ptrType)),
},
},
Dir: wd,
TINYGOROOT: goenv.Get("TINYGOROOT"),
CFlags: c.CFlags(),
ClangHeaders: c.ClangHeaders,
}
err = lprogram.Load(pkgName)
if err != nil {
return c.mod, nil, nil, []error{err}
return output, []error{err}
}
err = lprogram.Parse()
if err != nil {
return c.mod, nil, nil, []error{err}
return output, []error{err}
}
output.ExtraLDFlags = lprogram.LDFlags
output.MainDir = lprogram.MainPkg().Dir
c.ir = ir.NewProgram(lprogram)
// Run a simple dead code elimination pass.
err = c.ir.SimpleDCE()
if err != nil {
return c.mod, nil, nil, []error{err}
return output, []error{err}
}
// Initialize debug information.
@@ -359,17 +353,13 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
}
// Gather the list of (C) file paths that should be included in the build.
var extraFiles []string
for _, pkg := range c.ir.LoaderProgram.Sorted() {
for _, file := range pkg.OtherFiles {
switch strings.ToLower(filepath.Ext(file)) {
case ".c":
extraFiles = append(extraFiles, file)
}
for _, filename := range pkg.CFiles {
output.ExtraFiles = append(output.ExtraFiles, filepath.Join(pkg.Dir, filename))
}
}
return c.mod, extraFiles, lprogram.LDFlags, c.diagnostics
return output, c.diagnostics
}
// getLLVMRuntimeType obtains a named type from the runtime package and returns
+19 -3
View File
@@ -19,16 +19,30 @@ import (
// Because a go statement doesn't return anything, return undef.
func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, prefix string, pos token.Pos) llvm.Value {
paramBundle := b.emitPointerPack(params)
var callee llvm.Value
var callee, stackSize llvm.Value
switch b.Scheduler() {
case "none", "tasks":
callee = b.createGoroutineStartWrapper(funcPtr, prefix, pos)
if b.AutomaticStackSize() {
// The stack size is not known until after linking. Call a dummy
// function that will be replaced with a load from a special ELF
// section that contains the stack size (and is modified after
// linking).
stackSize = b.createCall(b.mod.NamedFunction("internal/task.getGoroutineStackSize"), []llvm.Value{callee, llvm.Undef(b.i8ptrType), llvm.Undef(b.i8ptrType)}, "stacksize")
} else {
// The stack size is fixed at compile time. By emitting it here as a
// constant, it can be optimized.
stackSize = llvm.ConstInt(b.uintptrType, b.Target.DefaultStackSize, false)
}
case "coroutines":
callee = b.CreatePtrToInt(funcPtr, b.uintptrType, "")
// There is no goroutine stack size: coroutines are used instead of
// stacks.
stackSize = llvm.Undef(b.uintptrType)
default:
panic("unreachable")
}
b.createCall(b.mod.NamedFunction("internal/task.start"), []llvm.Value{callee, paramBundle, llvm.Undef(b.i8ptrType), llvm.ConstPointerNull(b.i8ptrType)}, "")
b.createCall(b.mod.NamedFunction("internal/task.start"), []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.i8ptrType), llvm.ConstPointerNull(b.i8ptrType)}, "")
return llvm.Undef(funcPtr.Type().ElementType().ReturnType())
}
@@ -67,8 +81,9 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
// Create the wrapper.
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
wrapper.SetLinkage(llvm.PrivateLinkage)
wrapper.SetLinkage(llvm.InternalLinkage)
wrapper.SetUnnamedAddr(true)
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", name))
entry := c.ctx.AddBasicBlock(wrapper, "entry")
builder.SetInsertPointAtEnd(entry)
@@ -125,6 +140,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
wrapper = llvm.AddFunction(c.mod, prefix+".gowrapper", wrapperType)
wrapper.SetLinkage(llvm.InternalLinkage)
wrapper.SetUnnamedAddr(true)
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", ""))
entry := c.ctx.AddBasicBlock(wrapper, "entry")
builder.SetInsertPointAtEnd(entry)
+1 -1
View File
@@ -12,7 +12,7 @@ import (
// Version of TinyGo.
// Update this value before release of new version of software.
const Version = "0.14.0"
const Version = "0.15.0"
// GetGorootVersion returns the major and minor version for a given GOROOT path.
// If the goroot cannot be determined, (0, 0) is returned.
+3 -1
View File
@@ -47,10 +47,12 @@ func (e *Error) Error() string {
// location of the instruction. The location information may not be complete as
// it depends on debug information in the IR.
func (e *evalPackage) errorAt(inst llvm.Value, err error) *Error {
pos := getPosition(inst)
return &Error{
ImportPath: e.packagePath,
Pos: getPosition(inst),
Pos: pos,
Err: err,
Traceback: []ErrorLine{{pos, inst}},
}
}
+37 -9
View File
@@ -95,7 +95,11 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
if !operand.IsConstant() || inst.IsVolatile() || (!operand.Underlying.IsAConstantExpr().IsNil() && operand.Underlying.Opcode() == llvm.BitCast) {
value = fr.builder.CreateLoad(operand.Value(), inst.Name())
} else {
value = operand.Load()
var err error
value, err = operand.Load()
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
}
if value.Type() != inst.Type() {
return nil, nil, fr.errorAt(inst, errors.New("interp: load: type does not match"))
@@ -107,7 +111,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
if inst.IsVolatile() {
fr.builder.CreateStore(value.Value(), ptr.Value())
} else {
ptr.Store(value.Value())
err := ptr.Store(value.Value())
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
}
case !inst.IsAGetElementPtrInst().IsNil():
value := fr.getLocal(inst.Operand(0))
@@ -308,7 +315,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
}
// "key" is a Go string value, which in the TinyGo calling convention is split up
// into separate pointer and length parameters.
m.PutString(keyBuf, keyLen, valPtr)
err := m.PutString(keyBuf, keyLen, valPtr)
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
case callee.Name() == "runtime.hashmapBinarySet":
// set a binary (int etc.) key in the map
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
@@ -329,15 +339,24 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
fr.builder.CreateCall(callee, llvmParams, "")
continue
}
m.PutBinary(keyBuf, valPtr)
err := m.PutBinary(keyBuf, valPtr)
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
case callee.Name() == "runtime.stringConcat":
// adding two strings together
buf1Ptr := fr.getLocal(inst.Operand(0))
buf1Len := fr.getLocal(inst.Operand(1))
buf2Ptr := fr.getLocal(inst.Operand(2))
buf2Len := fr.getLocal(inst.Operand(3))
buf1 := getStringBytes(buf1Ptr, buf1Len.Value())
buf2 := getStringBytes(buf2Ptr, buf2Len.Value())
buf1, err := getStringBytes(buf1Ptr, buf1Len.Value())
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
buf2, err := getStringBytes(buf2Ptr, buf2Len.Value())
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
result := []byte(string(buf1) + string(buf2))
vals := make([]llvm.Value, len(result))
for i := range vals {
@@ -401,9 +420,15 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
return nil, nil, fr.errorAt(inst, errors.New("interp: trying to copy a slice with negative length?"))
}
for i := int64(0); i < length; i++ {
var err error
// *dst = *src
dstArray.Store(srcArray.Load())
val, err := srcArray.Load()
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
err = dstArray.Store(val)
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
// dst++
dstArrayValue, err := dstArray.GetElementPtr([]uint32{1})
if err != nil {
@@ -421,7 +446,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
// convert a string to a []byte
bufPtr := fr.getLocal(inst.Operand(0))
bufLen := fr.getLocal(inst.Operand(1))
result := getStringBytes(bufPtr, bufLen.Value())
result, err := getStringBytes(bufPtr, bufLen.Value())
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
vals := make([]llvm.Value, len(result))
for i := range vals {
vals[i] = llvm.ConstInt(fr.Mod.Context().Int8Type(), uint64(result[i]), false)
+10 -5
View File
@@ -1,6 +1,8 @@
package interp
import (
"errors"
"tinygo.org/x/go-llvm"
)
@@ -18,20 +20,23 @@ func getUses(value llvm.Value) []llvm.Value {
// getStringBytes loads the byte slice of a Go string represented as a
// {ptr, len} pair.
func getStringBytes(strPtr Value, strLen llvm.Value) []byte {
func getStringBytes(strPtr Value, strLen llvm.Value) ([]byte, error) {
if !strLen.IsConstant() {
panic("getStringBytes with a non-constant length")
return nil, errors.New("getStringBytes with a non-constant length")
}
buf := make([]byte, strLen.ZExtValue())
for i := range buf {
gep, err := strPtr.GetElementPtr([]uint32{uint32(i)})
if err != nil {
panic(err) // TODO
return nil, err
}
c, err := gep.Load()
if err != nil {
return nil, err
}
c := gep.Load()
buf[i] = byte(c.ZExtValue())
}
return buf
return buf, nil
}
// getLLVMIndices converts an []uint32 into an []llvm.Value, for use in
+60 -36
View File
@@ -15,8 +15,8 @@ type Value interface {
Value() llvm.Value // returns a LLVM value
Type() llvm.Type // equal to Value().Type()
IsConstant() bool // returns true if this value is a constant value
Load() llvm.Value // dereference a pointer
Store(llvm.Value) // store to a pointer
Load() (llvm.Value, error) // dereference a pointer
Store(llvm.Value) error // store to a pointer
GetElementPtr([]uint32) (Value, error) // returns an interior pointer
String() string // string representation, for debugging
}
@@ -44,29 +44,32 @@ func (v *LocalValue) IsConstant() bool {
}
// Load loads a constant value if this is a constant pointer.
func (v *LocalValue) Load() llvm.Value {
func (v *LocalValue) Load() (llvm.Value, error) {
if !v.Underlying.IsAGlobalVariable().IsNil() {
return v.Underlying.Initializer()
return v.Underlying.Initializer(), nil
}
switch v.Underlying.Opcode() {
case llvm.GetElementPtr:
indices := v.getConstGEPIndices()
if indices[0] != 0 {
panic("invalid GEP")
return llvm.Value{}, errors.New("invalid GEP")
}
global := v.Eval.getValue(v.Underlying.Operand(0))
agg := global.Load()
return llvm.ConstExtractValue(agg, indices[1:])
agg, err := global.Load()
if err != nil {
return llvm.Value{}, err
}
return llvm.ConstExtractValue(agg, indices[1:]), nil
case llvm.BitCast:
panic("interp: load from a bitcast")
return llvm.Value{}, errors.New("interp: load from a bitcast")
default:
panic("interp: load from a constant")
return llvm.Value{}, errors.New("interp: load from a constant")
}
}
// Store stores to the underlying value if the value type is a pointer type,
// otherwise it panics.
func (v *LocalValue) Store(value llvm.Value) {
// otherwise it returns an error.
func (v *LocalValue) Store(value llvm.Value) error {
if !v.Underlying.IsAGlobalVariable().IsNil() {
if !value.IsConstant() {
v.MarkDirty()
@@ -74,26 +77,28 @@ func (v *LocalValue) Store(value llvm.Value) {
} else {
v.Underlying.SetInitializer(value)
}
return
return nil
}
if !value.IsConstant() {
v.MarkDirty()
v.Eval.builder.CreateStore(value, v.Underlying)
return
return nil
}
switch v.Underlying.Opcode() {
case llvm.GetElementPtr:
indices := v.getConstGEPIndices()
if indices[0] != 0 {
panic("invalid GEP")
return errors.New("invalid GEP")
}
global := &LocalValue{v.Eval, v.Underlying.Operand(0)}
agg := global.Load()
agg, err := global.Load()
if err != nil {
return err
}
agg = llvm.ConstInsertValue(agg, value, indices[1:])
global.Store(agg)
return
return global.Store(agg)
default:
panic("interp: store on a constant")
return errors.New("interp: store on a constant")
}
}
@@ -225,7 +230,11 @@ func (v *MapValue) Value() llvm.Value {
keyPtr := llvm.ConstExtractValue(llvmKey, []uint32{0})
keyLen := llvm.ConstExtractValue(llvmKey, []uint32{1})
keyPtrVal := v.Eval.getValue(keyPtr)
keyBuf = getStringBytes(keyPtrVal, keyLen)
var err error
keyBuf, err = getStringBytes(keyPtrVal, keyLen)
if err != nil {
panic(err) // TODO
}
} else if key.Type().TypeKind() == llvm.IntegerTypeKind {
keyBuf = make([]byte, v.Eval.TargetData.TypeAllocSize(key.Type()))
n := key.Value().ZExtValue()
@@ -299,13 +308,15 @@ func (v *MapValue) IsConstant() bool {
}
// Load panics: maps are of reference type so cannot be dereferenced.
func (v *MapValue) Load() llvm.Value {
func (v *MapValue) Load() (llvm.Value, error) {
panic("interp: load from a map")
}
// Store panics: maps are of reference type so cannot be stored to.
func (v *MapValue) Store(value llvm.Value) {
panic("interp: store on a map")
// Store returns an error: maps are of reference type so cannot be stored to.
func (v *MapValue) Store(value llvm.Value) error {
// This must be a bug, but it might be helpful to indicate the location
// anyway.
return errors.New("interp: store on a map")
}
// GetElementPtr panics: maps are of reference type so their (interior)
@@ -316,23 +327,26 @@ func (v *MapValue) GetElementPtr(indices []uint32) (Value, error) {
// PutString does a map assign operation, assuming that the map is of type
// map[string]T.
func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) {
func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) error {
if !v.Underlying.IsNil() {
panic("map already created")
return errors.New("map already created")
}
if valPtr.Underlying.Opcode() == llvm.BitCast {
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
}
value := valPtr.Load()
value, err := valPtr.Load()
if err != nil {
return err
}
if v.ValueType.IsNil() {
v.ValueType = value.Type()
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
panic("interp: map store value type has the wrong size")
return errors.New("interp: map store value type has the wrong size")
}
} else {
if value.Type() != v.ValueType {
panic("interp: map store value type is inconsistent")
return errors.New("interp: map store value type is inconsistent")
}
}
@@ -345,26 +359,31 @@ func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) {
// TODO: avoid duplicate keys
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
v.Values = append(v.Values, &LocalValue{v.Eval, value})
return nil
}
// PutBinary does a map assign operation.
func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) error {
if !v.Underlying.IsNil() {
panic("map already created")
return errors.New("map already created")
}
if valPtr.Underlying.Opcode() == llvm.BitCast {
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
}
value := valPtr.Load()
value, err := valPtr.Load()
if err != nil {
return err
}
if v.ValueType.IsNil() {
v.ValueType = value.Type()
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
panic("interp: map store value type has the wrong size")
return errors.New("interp: map store value type has the wrong size")
}
} else {
if value.Type() != v.ValueType {
panic("interp: map store value type is inconsistent")
return errors.New("interp: map store value type is inconsistent")
}
}
@@ -375,21 +394,26 @@ func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
}
}
key := keyPtr.Load()
key, err := keyPtr.Load()
if err != nil {
return err
}
if v.KeyType.IsNil() {
v.KeyType = key.Type()
if int(v.Eval.TargetData.TypeAllocSize(v.KeyType)) != v.KeySize {
panic("interp: map store key type has the wrong size")
return errors.New("interp: map store key type has the wrong size")
}
} else {
if key.Type() != v.KeyType {
panic("interp: map store key type is inconsistent")
return errors.New("interp: map store key type is inconsistent")
}
}
// TODO: avoid duplicate keys
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
v.Values = append(v.Values, &LocalValue{v.Eval, value})
return nil
}
// Get FNV-1a hash of this string.
+2 -2
View File
@@ -67,7 +67,7 @@ func NewProgram(lprogram *loader.Program) *Program {
program := lprogram.LoadSSA()
program.Build()
mainPkg := program.ImportedPackage(lprogram.MainPkg.PkgPath)
mainPkg := program.ImportedPackage(lprogram.MainPkg().ImportPath)
if mainPkg == nil {
panic("could not find main package")
}
@@ -79,7 +79,7 @@ func NewProgram(lprogram *loader.Program) *Program {
}
for _, pkg := range lprogram.Sorted() {
p.AddPackage(program.ImportedPackage(pkg.PkgPath))
p.AddPackage(program.ImportedPackage(pkg.ImportPath))
}
return p
+13
View File
@@ -1,5 +1,7 @@
package loader
import "go/scanner"
// Errors contains a list of parser errors or a list of typechecker errors for
// the given package.
type Errors struct {
@@ -10,3 +12,14 @@ type Errors struct {
func (e Errors) Error() string {
return "could not compile: " + e.Errs[0].Error()
}
// Error is a regular error but with an added import stack. This is especially
// useful for debugging import cycle errors.
type Error struct {
ImportStack []string
Err scanner.Error
}
func (e Error) Error() string {
return e.Err.Error()
}
+49 -8
View File
@@ -8,19 +8,21 @@ import (
"encoding/hex"
"errors"
"fmt"
"io"
"io/ioutil"
"math/rand"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strconv"
"sync"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv"
)
var gorootCreateMutex sync.Mutex
// GetCachedGoroot creates a new GOROOT by merging both the standard GOROOT and
// the GOROOT from TinyGo using lots of symbolic links.
func GetCachedGoroot(config *compileopts.Config) (string, error) {
@@ -49,16 +51,29 @@ func GetCachedGoroot(config *compileopts.Config) (string, error) {
fmt.Fprintln(hash, tinygoroot)
gorootsHash := hash.Sum(nil)
gorootsHashHex := hex.EncodeToString(gorootsHash[:])
cachedgoroot := filepath.Join(goenv.Get("GOCACHE"), "goroot-"+version+"-"+gorootsHashHex)
cachedgorootName := "goroot-" + version + "-" + gorootsHashHex
cachedgoroot := filepath.Join(goenv.Get("GOCACHE"), cachedgorootName)
if needsSyscallPackage(config.BuildTags()) {
cachedgoroot += "-syscall"
}
// Do not try to create the cached GOROOT in parallel, that's only a waste
// of I/O bandwidth and thus speed. Instead, use a mutex to make sure only
// one goroutine does it at a time.
// This is not a way to ensure atomicity (a different TinyGo invocation
// could be creating the same directory), but instead a way to avoid
// creating it many times in parallel when running tests in parallel.
gorootCreateMutex.Lock()
defer gorootCreateMutex.Unlock()
if _, err := os.Stat(cachedgoroot); err == nil {
return cachedgoroot, nil
}
tmpgoroot := cachedgoroot + ".tmp" + strconv.Itoa(rand.Int())
err = os.MkdirAll(tmpgoroot, 0777)
err = os.MkdirAll(goenv.Get("GOCACHE"), 0777)
if err != nil {
return "", err
}
tmpgoroot, err := ioutil.TempDir(goenv.Get("GOCACHE"), cachedgorootName+".tmp")
if err != nil {
return "", err
}
@@ -85,6 +100,15 @@ func GetCachedGoroot(config *compileopts.Config) (string, error) {
// deleted by the defer above.
return cachedgoroot, nil
}
if runtime.GOOS == "windows" && os.IsPermission(err) {
// On Windows, a rename with a destination directory that already
// exists does not result in an IsExist error, but rather in an
// access denied error. To be sure, check for this case by checking
// whether the target directory exists.
if _, err := os.Stat(cachedgoroot); err == nil {
return cachedgoroot, nil
}
}
return "", err
}
return cachedgoroot, nil
@@ -188,7 +212,7 @@ func pathsToOverride(needsSyscallPackage bool) map[string]bool {
"reflect/": false,
"runtime/": false,
"sync/": true,
"testing/": false,
"testing/": true,
}
if needsSyscallPackage {
paths["syscall/"] = true // include syscall/js
@@ -228,10 +252,27 @@ func symlink(oldname, newname string) error {
return symlinkErr
}
} else {
// Make a hard link.
// Try making a hard link.
err := os.Link(oldname, newname)
if err != nil {
return symlinkErr
// Making a hardlink failed. Try copying the file as a last
// fallback.
inf, err := os.Open(oldname)
if err != nil {
return err
}
defer inf.Close()
outf, err := os.Create(newname)
if err != nil {
return err
}
defer outf.Close()
_, err = io.Copy(outf, inf)
if err != nil {
os.Remove(newname)
return err
}
// File was copied.
}
}
return nil // success
+30
View File
@@ -0,0 +1,30 @@
package loader
import (
"os"
"os/exec"
"strings"
"github.com/tinygo-org/tinygo/compileopts"
)
// List returns a ready-to-run *exec.Cmd for running the `go list` command with
// the configuration used for TinyGo.
func List(config *compileopts.Config, extraArgs, pkgs []string) (*exec.Cmd, error) {
goroot, err := GetCachedGoroot(config)
if err != nil {
return nil, err
}
args := append([]string{"list"}, extraArgs...)
if len(config.BuildTags()) != 0 {
args = append(args, "-tags", strings.Join(config.BuildTags(), " "))
}
args = append(args, pkgs...)
cgoEnabled := "0"
if config.CgoEnabled() {
cgoEnabled = "1"
}
cmd := exec.Command("go", args...)
cmd.Env = append(os.Environ(), "GOROOT="+goroot, "GOOS="+config.GOOS(), "GOARCH="+config.GOARCH(), "CGO_ENABLED="+cgoEnabled)
return cmd, nil
}
+186 -255
View File
@@ -2,165 +2,178 @@ package loader
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/scanner"
"go/token"
"go/types"
"io"
"os"
"os/exec"
"path/filepath"
"sort"
"strconv"
"strings"
"text/template"
"syscall"
"github.com/tinygo-org/tinygo/cgo"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv"
"golang.org/x/tools/go/packages"
)
// Program holds all packages and some metadata about the program as a whole.
type Program struct {
Build *build.Context
Tests bool
Packages map[string]*Package
MainPkg *Package
sorted []*Package
fset *token.FileSet
TypeChecker types.Config
Dir string // current working directory (for error reporting)
TINYGOROOT string // root of the TinyGo installation or root of the source code
CFlags []string
LDFlags []string
ClangHeaders string
config *compileopts.Config
clangHeaders string
typeChecker types.Config
goroot string // synthetic GOROOT
workingDir string
Packages map[string]*Package
sorted []*Package
fset *token.FileSet
// Information obtained during parsing.
LDFlags []string
}
// PackageJSON is a subset of the JSON struct returned from `go list`.
type PackageJSON struct {
Dir string
ImportPath string
ForTest string
// Source files
GoFiles []string
CgoFiles []string
CFiles []string
// Dependency information
Imports []string
ImportMap map[string]string
// Error information
Error *struct {
ImportStack []string
Pos string
Err string
}
}
// Package holds a loaded package, its imports, and its parsed files.
type Package struct {
*Program
*packages.Package
Files []*ast.File
Pkg *types.Package
types.Info
PackageJSON
program *Program
Files []*ast.File
Pkg *types.Package
info types.Info
}
// Load loads the given package with all dependencies (including the runtime
// package). Call .Parse() afterwards to parse all Go files (including CGo
// processing, if necessary).
func (p *Program) Load(importPath string) error {
if p.Packages == nil {
p.Packages = make(map[string]*Package)
func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, typeChecker types.Config) (*Program, error) {
goroot, err := GetCachedGoroot(config)
if err != nil {
return nil, err
}
wd, err := os.Getwd()
if err != nil {
return nil, err
}
p := &Program{
config: config,
clangHeaders: clangHeaders,
typeChecker: typeChecker,
goroot: goroot,
workingDir: wd,
Packages: make(map[string]*Package),
fset: token.NewFileSet(),
}
err := p.loadPackage(importPath)
// List the dependencies of this package, in raw JSON format.
extraArgs := []string{"-json", "-deps"}
if config.TestConfig.CompileTestBinary {
extraArgs = append(extraArgs, "-test")
}
cmd, err := List(config, extraArgs, inputPkgs)
if err != nil {
return err
return nil, err
}
p.MainPkg = p.sorted[len(p.sorted)-1]
if _, ok := p.Packages["runtime"]; !ok {
// The runtime package wasn't loaded. Although `go list -deps` seems to
// return the full dependency list, there is no way to get those
// packages from the go/packages package. Therefore load the runtime
// manually and add it to the list of to-be-compiled packages
// (duplicates are already filtered).
return p.loadPackage("runtime")
buf := &bytes.Buffer{}
cmd.Stdout = buf
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
os.Exit(status.ExitStatus())
}
os.Exit(1)
}
return nil, fmt.Errorf("failed to run `go list`: %s", err)
}
return nil
}
func (p *Program) loadPackage(importPath string) error {
cgoEnabled := "0"
if p.Build.CgoEnabled {
cgoEnabled = "1"
}
pkgs, err := packages.Load(&packages.Config{
Mode: packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps,
Env: append(os.Environ(), "GOROOT="+p.Build.GOROOT, "GOOS="+p.Build.GOOS, "GOARCH="+p.Build.GOARCH, "CGO_ENABLED="+cgoEnabled),
BuildFlags: []string{"-tags", strings.Join(p.Build.BuildTags, " ")},
Tests: p.Tests,
}, importPath)
if err != nil {
return err
}
var pkg *packages.Package
if p.Tests {
// We need the second package. Quoting from the docs:
// > For example, when using the go command, loading "fmt" with Tests=true
// > returns four packages, with IDs "fmt" (the standard package),
// > "fmt [fmt.test]" (the package as compiled for the test),
// > "fmt_test" (the test functions from source files in package fmt_test),
// > and "fmt.test" (the test binary).
pkg = pkgs[1]
} else {
if len(pkgs) != 1 {
return fmt.Errorf("expected exactly one package while importing %s, got %d", importPath, len(pkgs))
// Parse the returned json from `go list`.
decoder := json.NewDecoder(buf)
for {
pkg := &Package{
program: p,
info: types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
Implicits: make(map[ast.Node]types.Object),
Scopes: make(map[ast.Node]*types.Scope),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
},
}
pkg = pkgs[0]
}
var importError *Errors
var addPackages func(pkg *packages.Package)
addPackages = func(pkg *packages.Package) {
if _, ok := p.Packages[pkg.PkgPath]; ok {
return
err := decoder.Decode(&pkg.PackageJSON)
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
pkg2 := p.newPackage(pkg)
p.Packages[pkg.PkgPath] = pkg2
if len(pkg.Errors) != 0 {
if importError != nil {
// There was another error reported already. Do not report
// errors from multiple packages at once.
return
}
importError = &Errors{
Pkg: pkg2,
}
for _, err := range pkg.Errors {
pos := token.Position{}
fields := strings.Split(err.Pos, ":")
if len(fields) >= 2 {
// There is some file/line/column information.
if n, err := strconv.Atoi(fields[len(fields)-2]); err == nil {
// Format: filename.go:line:colum
pos.Filename = strings.Join(fields[:len(fields)-2], ":")
pos.Line = n
pos.Column, _ = strconv.Atoi(fields[len(fields)-1])
} else {
// Format: filename.go:line
pos.Filename = strings.Join(fields[:len(fields)-1], ":")
pos.Line, _ = strconv.Atoi(fields[len(fields)-1])
}
pos.Filename = p.getOriginalPath(pos.Filename)
if pkg.Error != nil {
// There was an error while importing (for example, a circular
// dependency).
pos := token.Position{}
fields := strings.Split(pkg.Error.Pos, ":")
if len(fields) >= 2 {
// There is some file/line/column information.
if n, err := strconv.Atoi(fields[len(fields)-2]); err == nil {
// Format: filename.go:line:colum
pos.Filename = strings.Join(fields[:len(fields)-2], ":")
pos.Line = n
pos.Column, _ = strconv.Atoi(fields[len(fields)-1])
} else {
// Format: filename.go:line
pos.Filename = strings.Join(fields[:len(fields)-1], ":")
pos.Line, _ = strconv.Atoi(fields[len(fields)-1])
}
importError.Errs = append(importError.Errs, scanner.Error{
Pos: pos,
Msg: err.Msg,
})
pos.Filename = p.getOriginalPath(pos.Filename)
}
return
err := scanner.Error{
Pos: pos,
Msg: pkg.Error.Err,
}
if len(pkg.Error.ImportStack) != 0 {
return nil, Error{
ImportStack: pkg.Error.ImportStack,
Err: err,
}
}
return nil, err
}
// Get the list of imports (sorted alphabetically).
names := make([]string, 0, len(pkg.Imports))
for name := range pkg.Imports {
names = append(names, name)
}
sort.Strings(names)
// Add all the imports.
for _, name := range names {
addPackages(pkg.Imports[name])
}
p.sorted = append(p.sorted, pkg2)
p.sorted = append(p.sorted, pkg)
p.Packages[pkg.ImportPath] = pkg
}
addPackages(pkg)
if importError != nil {
return *importError
}
return nil
return p, nil
}
// getOriginalPath looks whether this path is in the generated GOROOT and if so,
@@ -168,23 +181,23 @@ func (p *Program) loadPackage(importPath string) error {
// the input path is returned.
func (p *Program) getOriginalPath(path string) string {
originalPath := path
if strings.HasPrefix(path, p.Build.GOROOT+string(filepath.Separator)) {
if strings.HasPrefix(path, p.goroot+string(filepath.Separator)) {
// If this file is part of the synthetic GOROOT, try to infer the
// original path.
relpath := path[len(filepath.Join(p.Build.GOROOT, "src"))+1:]
relpath := path[len(filepath.Join(p.goroot, "src"))+1:]
realgorootPath := filepath.Join(goenv.Get("GOROOT"), "src", relpath)
if _, err := os.Stat(realgorootPath); err == nil {
originalPath = realgorootPath
}
maybeInTinyGoRoot := false
for prefix := range pathsToOverride(needsSyscallPackage(p.Build.BuildTags)) {
for prefix := range pathsToOverride(needsSyscallPackage(p.config.BuildTags())) {
if !strings.HasPrefix(relpath, prefix) {
continue
}
maybeInTinyGoRoot = true
}
if maybeInTinyGoRoot {
tinygoPath := filepath.Join(p.TINYGOROOT, "src", relpath)
tinygoPath := filepath.Join(goenv.Get("TINYGOROOT"), "src", relpath)
if _, err := os.Stat(tinygoPath); err == nil {
originalPath = tinygoPath
}
@@ -193,28 +206,18 @@ func (p *Program) getOriginalPath(path string) string {
return originalPath
}
// newPackage instantiates a new *Package object with initialized members.
func (p *Program) newPackage(pkg *packages.Package) *Package {
return &Package{
Program: p,
Package: pkg,
Info: types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
Implicits: make(map[ast.Node]types.Object),
Scopes: make(map[ast.Node]*types.Scope),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
},
}
}
// Sorted returns a list of all packages, sorted in a way that no packages come
// before the packages they depend upon.
func (p *Program) Sorted() []*Package {
return p.sorted
}
// MainPkg returns the last package in the Sorted() slice. This is the main
// package of the program.
func (p *Program) MainPkg() *Package {
return p.sorted[len(p.sorted)-1]
}
// Parse parses all packages and typechecks them.
//
// The returned error may be an Errors error, which contains a list of errors.
@@ -222,22 +225,16 @@ func (p *Program) Sorted() []*Package {
// Idempotent.
func (p *Program) Parse() error {
// Parse all packages.
for _, pkg := range p.Sorted() {
// TODO: do this in parallel.
for _, pkg := range p.sorted {
err := pkg.Parse()
if err != nil {
return err
}
}
if p.Tests {
err := p.swapTestMain()
if err != nil {
return err
}
}
// Typecheck all packages.
for _, pkg := range p.Sorted() {
for _, pkg := range p.sorted {
err := pkg.Check()
if err != nil {
return err
@@ -247,82 +244,6 @@ func (p *Program) Parse() error {
return nil
}
func (p *Program) swapTestMain() error {
var tests []string
isTestFunc := func(f *ast.FuncDecl) bool {
// TODO: improve signature check
if strings.HasPrefix(f.Name.Name, "Test") && f.Name.Name != "TestMain" {
return true
}
return false
}
for _, f := range p.MainPkg.Files {
for i, d := range f.Decls {
switch v := d.(type) {
case *ast.FuncDecl:
if isTestFunc(v) {
tests = append(tests, v.Name.Name)
}
if v.Name.Name == "main" {
// Remove main
if len(f.Decls) == 1 {
f.Decls = make([]ast.Decl, 0)
} else {
f.Decls[i] = f.Decls[len(f.Decls)-1]
f.Decls = f.Decls[:len(f.Decls)-1]
}
}
}
}
}
// TODO: Check if they defined a TestMain and call it instead of testing.TestMain
const mainBody = `package main
import (
"testing"
)
func main () {
m := &testing.M{
Tests: []testing.TestToCall{
{{range .TestFunctions}}
{Name: "{{.}}", Func: {{.}}},
{{end}}
},
}
testing.TestMain(m)
}
`
tmpl := template.Must(template.New("testmain").Parse(mainBody))
b := bytes.Buffer{}
tmplData := struct {
TestFunctions []string
}{
TestFunctions: tests,
}
err := tmpl.Execute(&b, tmplData)
if err != nil {
return err
}
path := filepath.Join(p.MainPkg.Dir, "$testmain.go")
if p.fset == nil {
p.fset = token.NewFileSet()
}
newMain, err := parser.ParseFile(p.fset, path, b.Bytes(), parser.AllErrors)
if err != nil {
return err
}
p.MainPkg.Files = append(p.MainPkg.Files, newMain)
return nil
}
// parseFile is a wrapper around parser.ParseFile.
func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
if p.fset == nil {
@@ -342,14 +263,13 @@ func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
// Idempotent.
func (p *Package) Parse() error {
if len(p.Files) != 0 {
return nil
return nil // nothing to do (?)
}
// Load the AST.
// TODO: do this in parallel.
if p.PkgPath == "unsafe" {
// Special case for the unsafe package. Don't even bother loading
// the files.
if p.ImportPath == "unsafe" {
// Special case for the unsafe package, which is defined internally by
// the types package.
p.Pkg = types.Unsafe
return nil
}
@@ -369,11 +289,11 @@ func (p *Package) Parse() error {
// Idempotent.
func (p *Package) Check() error {
if p.Pkg != nil {
return nil
return nil // already typechecked
}
var typeErrors []error
checker := p.TypeChecker
checker := p.program.typeChecker // make a copy, because it will be modified
checker.Error = func(err error) {
typeErrors = append(typeErrors, err)
}
@@ -381,7 +301,7 @@ func (p *Package) Check() error {
// Do typechecking of the package.
checker.Importer = p
typesPkg, err := checker.Check(p.PkgPath, p.fset, p.Files, &p.Info)
typesPkg, err := checker.Check(p.ImportPath, p.program.fset, p.Files, &p.info)
if err != nil {
if err, ok := err.(Errors); ok {
return err
@@ -394,40 +314,46 @@ func (p *Package) Check() error {
// parseFiles parses the loaded list of files and returns this list.
func (p *Package) parseFiles() ([]*ast.File, error) {
// TODO: do this concurrently.
var files []*ast.File
var fileErrs []error
var cgoFiles []*ast.File
for _, file := range p.GoFiles {
f, err := p.parseFile(file, parser.ParseComments)
// Parse all files (incuding CgoFiles).
parseFile := func(file string) {
if !filepath.IsAbs(file) {
file = filepath.Join(p.Dir, file)
}
f, err := p.program.parseFile(file, parser.ParseComments)
if err != nil {
fileErrs = append(fileErrs, err)
continue
}
if err != nil {
fileErrs = append(fileErrs, err)
continue
}
for _, importSpec := range f.Imports {
if importSpec.Path.Value == `"C"` {
cgoFiles = append(cgoFiles, f)
}
return
}
files = append(files, f)
}
if len(cgoFiles) != 0 {
cflags := append(p.CFlags, "-I"+filepath.Dir(p.GoFiles[0]))
if p.ClangHeaders != "" {
cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.ClangHeaders)
for _, file := range p.GoFiles {
parseFile(file)
}
for _, file := range p.CgoFiles {
parseFile(file)
}
// Do CGo processing.
if len(p.CgoFiles) != 0 {
var cflags []string
cflags = append(cflags, p.program.config.CFlags()...)
cflags = append(cflags, "-I"+p.Dir)
if p.program.clangHeaders != "" {
cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.program.clangHeaders)
}
generated, ldflags, errs := cgo.Process(files, p.Program.Dir, p.fset, cflags)
generated, ldflags, errs := cgo.Process(files, p.program.workingDir, p.program.fset, cflags)
if errs != nil {
fileErrs = append(fileErrs, errs...)
}
files = append(files, generated)
p.LDFlags = append(p.LDFlags, ldflags...)
p.program.LDFlags = append(p.program.LDFlags, ldflags...)
}
// Only return an error after CGo processing, so that errors in parsing and
// CGo can be reported together.
if len(fileErrs) != 0 {
return nil, Errors{p, fileErrs}
}
@@ -441,8 +367,13 @@ func (p *Package) Import(to string) (*types.Package, error) {
if to == "unsafe" {
return types.Unsafe, nil
}
if _, ok := p.Imports[to]; ok {
return p.Packages[p.Imports[to].PkgPath].Pkg, nil
if replace, ok := p.ImportMap[to]; ok {
// This import path should be replaced by another import path, according
// to `go list`.
to = replace
}
if imported, ok := p.program.Packages[to]; ok {
return imported.Pkg, nil
} else {
return nil, errors.New("package not imported: " + to)
}
+2 -2
View File
@@ -10,8 +10,8 @@ import (
func (p *Program) LoadSSA() *ssa.Program {
prog := ssa.NewProgram(p.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug)
for _, pkg := range p.Sorted() {
prog.CreatePackage(pkg.Pkg, pkg.Files, &pkg.Info, true)
for _, pkg := range p.sorted {
prog.CreatePackage(pkg.Pkg, pkg.Files, &pkg.info, true)
}
return prog
+111 -84
View File
@@ -8,6 +8,7 @@ import (
"go/scanner"
"go/types"
"io"
"io/ioutil"
"os"
"os/exec"
"os/signal"
@@ -28,6 +29,12 @@ import (
"go.bug.st/serial"
)
var (
// This variable is set at build time using -ldflags parameters.
// See: https://stackoverflow.com/a/11355611
gitSha1 string
)
// commandError is an error type to wrap os/exec.Command errors. This provides
// some more information regarding what went wrong while running a command.
type commandError struct {
@@ -58,33 +65,23 @@ func moveFile(src, dst string) error {
return os.Remove(src)
}
// copyFile copies the given file from src to dst. It copies first to a .tmp
// file which is then moved over a possibly already existing file at the
// destination.
// copyFile copies the given file from src to dst. It can copy over
// a possibly already existing file at the destination.
func copyFile(src, dst string) error {
inf, err := os.Open(src)
if err != nil {
return err
}
defer inf.Close()
outpath := dst + ".tmp"
outf, err := os.Create(outpath)
source, err := os.Open(src)
if err != nil {
return err
}
defer source.Close()
_, err = io.Copy(outf, inf)
if err != nil {
os.Remove(outpath)
return err
}
err = outf.Close()
destination, err := os.Create(dst)
if err != nil {
return err
}
defer destination.Close()
return os.Rename(dst+".tmp", dst)
_, err = io.Copy(destination, source)
return err
}
// Build compiles and links the given package and writes it to outpath.
@@ -94,10 +91,10 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
return err
}
return builder.Build(pkgName, outpath, config, func(tmppath string) error {
if err := os.Rename(tmppath, outpath); err != nil {
return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
if err := os.Rename(result.Binary, outpath); err != nil {
// Moving failed. Do a file copy.
inf, err := os.Open(tmppath)
inf, err := os.Open(result.Binary)
if err != nil {
return err
}
@@ -130,16 +127,11 @@ func Test(pkgName string, options *compileopts.Options) error {
return err
}
// Add test build tag. This is incorrect: `go test` only looks at the
// _test.go file suffix but does not add the test build tag in the process.
// However, it's a simple fix right now.
// For details: https://github.com/golang/go/issues/21360
config.Target.BuildTags = append(config.Target.BuildTags, "test")
return builder.Build(pkgName, ".elf", config, func(tmppath string) error {
cmd := exec.Command(tmppath)
return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error {
cmd := exec.Command(result.Binary)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = result.MainDir
err := cmd.Run()
if err != nil {
// Propagate the exit code
@@ -149,7 +141,7 @@ func Test(pkgName string, options *compileopts.Options) error {
}
os.Exit(1)
}
return &commandError{"failed to run compiled binary", tmppath, err}
return &commandError{"failed to run compiled binary", result.Binary, err}
}
return nil
})
@@ -193,9 +185,9 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
return errors.New("unknown flash method: " + flashMethod)
}
return builder.Build(pkgName, fileExt, config, func(tmppath string) error {
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
// do we need port reset to put MCU into bootloader mode?
if config.Target.PortReset == "true" {
if config.Target.PortReset == "true" && flashMethod != "openocd" {
if port == "" {
var err error
port, err = getDefaultPort()
@@ -206,7 +198,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
err := touchSerialPortAt1200bps(port)
if err != nil {
return &commandError{"failed to reset port", tmppath, err}
return &commandError{"failed to reset port", result.Binary, err}
}
// give the target MCU a chance to restart into bootloader
time.Sleep(3 * time.Second)
@@ -218,7 +210,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
// Create the command.
flashCmd := config.Target.FlashCommand
fileToken := "{" + fileExt[1:] + "}"
flashCmd = strings.Replace(flashCmd, fileToken, tmppath, -1)
flashCmd = strings.Replace(flashCmd, fileToken, result.Binary, -1)
if port == "" && strings.Contains(flashCmd, "{port}") {
var err error
@@ -248,21 +240,21 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
cmd.Dir = goenv.Get("TINYGOROOT")
err := cmd.Run()
if err != nil {
return &commandError{"failed to flash", tmppath, err}
return &commandError{"failed to flash", result.Binary, err}
}
return nil
case "msd":
switch fileExt {
case ".uf2":
err := flashUF2UsingMSD(config.Target.FlashVolume, tmppath)
err := flashUF2UsingMSD(config.Target.FlashVolume, result.Binary)
if err != nil {
return &commandError{"failed to flash", tmppath, err}
return &commandError{"failed to flash", result.Binary, err}
}
return nil
case ".hex":
err := flashHexUsingMSD(config.Target.FlashVolume, tmppath)
err := flashHexUsingMSD(config.Target.FlashVolume, result.Binary)
if err != nil {
return &commandError{"failed to flash", tmppath, err}
return &commandError{"failed to flash", result.Binary, err}
}
return nil
default:
@@ -273,13 +265,13 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
if err != nil {
return err
}
args = append(args, "-c", "program "+tmppath+" reset exit")
args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+" reset exit")
cmd := exec.Command("openocd", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return &commandError{"failed to flash", tmppath, err}
return &commandError{"failed to flash", result.Binary, err}
}
return nil
default:
@@ -304,7 +296,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
return errors.New("gdb not configured in the target specification")
}
return builder.Build(pkgName, "", config, func(tmppath string) error {
return builder.Build(pkgName, "", config, func(result builder.BuildResult) error {
// Find a good way to run GDB.
gdbInterface, openocdInterface := config.Programmer()
switch gdbInterface {
@@ -367,7 +359,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
gdbCommands = append(gdbCommands, "target remote :1234")
// Run in an emulator.
args := append(config.Target.Emulator[1:], tmppath, "-s", "-S")
args := append(config.Target.Emulator[1:], result.Binary, "-s", "-S")
daemon = exec.Command(config.Target.Emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
@@ -375,7 +367,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
gdbCommands = append(gdbCommands, "target remote :2345")
// Run in an emulator.
args := append(config.Target.Emulator[1:], tmppath, "-g")
args := append(config.Target.Emulator[1:], result.Binary, "-g")
daemon = exec.Command(config.Target.Emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
@@ -413,7 +405,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
// Construct and execute a gdb command.
// By default: gdb -ex run <binary>
// Exit GDB with Ctrl-D.
params := []string{tmppath}
params := []string{result.Binary}
for _, cmd := range gdbCommands {
params = append(params, "-ex", cmd)
}
@@ -423,7 +415,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return &commandError{"failed to run gdb with", tmppath, err}
return &commandError{"failed to run gdb with", result.Binary, err}
}
return nil
})
@@ -439,10 +431,10 @@ func Run(pkgName string, options *compileopts.Options) error {
return err
}
return builder.Build(pkgName, ".elf", config, func(tmppath string) error {
return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error {
if len(config.Target.Emulator) == 0 {
// Run directly.
cmd := exec.Command(tmppath)
cmd := exec.Command(result.Binary)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
@@ -451,12 +443,12 @@ func Run(pkgName string, options *compileopts.Options) error {
// Workaround for QEMU which always exits with an error.
return nil
}
return &commandError{"failed to run compiled binary", tmppath, err}
return &commandError{"failed to run compiled binary", result.Binary, err}
}
return nil
} else {
// Run in an emulator.
args := append(config.Target.Emulator[1:], tmppath)
args := append(config.Target.Emulator[1:], result.Binary)
cmd := exec.Command(config.Target.Emulator[0], args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
@@ -466,7 +458,7 @@ func Run(pkgName string, options *compileopts.Options) error {
// Workaround for QEMU which always exits with an error.
return nil
}
return &commandError{"failed to run emulator with", tmppath, err}
return &commandError{"failed to run emulator with", result.Binary, err}
}
return nil
}
@@ -652,36 +644,6 @@ func getDefaultPort() (port string, err error) {
return d[0], nil
}
// runGoList runs the `go list` command but using the configuration used for
// TinyGo.
func runGoList(config *compileopts.Config, flagJSON, flagDeps bool, pkgs []string) error {
goroot, err := loader.GetCachedGoroot(config)
if err != nil {
return err
}
args := []string{"list"}
if flagJSON {
args = append(args, "-json")
}
if flagDeps {
args = append(args, "-deps")
}
if len(config.BuildTags()) != 0 {
args = append(args, "-tags", strings.Join(config.BuildTags(), " "))
}
args = append(args, pkgs...)
cgoEnabled := "0"
if config.CgoEnabled() {
cgoEnabled = "1"
}
cmd := exec.Command("go", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = append(os.Environ(), "GOROOT="+goroot, "GOOS="+config.GOOS(), "GOARCH="+config.GOARCH(), "CGO_ENABLED="+cgoEnabled)
cmd.Run()
return nil
}
func usage() {
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
fmt.Fprintln(os.Stderr, "version:", goenv.Version)
@@ -755,10 +717,16 @@ func printCompilerError(logln func(...interface{}), err error) {
}
}
case loader.Errors:
logln("#", err.Pkg.PkgPath)
logln("#", err.Pkg.ImportPath)
for _, err := range err.Errs {
printCompilerError(logln, err)
}
case loader.Error:
logln(err.Err.Error())
logln("package", err.ImportStack[0])
for _, pkgPath := range err.ImportStack[1:] {
logln("\timports", pkgPath)
}
case *builder.MultiError:
for _, err := range err.Errs {
printCompilerError(logln, err)
@@ -954,6 +922,35 @@ func main() {
}
err := Test(pkgName, options)
handleCompilerError(err)
case "targets":
dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets")
entries, err := ioutil.ReadDir(dir)
if err != nil {
fmt.Fprintln(os.Stderr, "could not list targets:", err)
os.Exit(1)
return
}
for _, entry := range entries {
if !entry.Mode().IsRegular() || !strings.HasSuffix(entry.Name(), ".json") {
// Only inspect JSON files.
continue
}
path := filepath.Join(dir, entry.Name())
spec, err := compileopts.LoadTarget(path)
if err != nil {
fmt.Fprintln(os.Stderr, "could not list target:", err)
os.Exit(1)
return
}
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == nil {
// This doesn't look like a regular target file, but rather like
// a parent target (such as targets/cortex-m.json).
continue
}
name := entry.Name()
name = name[:len(name)-5]
fmt.Println(name)
}
case "info":
if flag.NArg() == 1 {
options.Target = flag.Arg(0)
@@ -973,12 +970,18 @@ func main() {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
cachedGOROOT, err := loader.GetCachedGoroot(config)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Printf("LLVM triple: %s\n", config.Triple())
fmt.Printf("GOOS: %s\n", config.GOOS())
fmt.Printf("GOARCH: %s\n", config.GOARCH())
fmt.Printf("build tags: %s\n", strings.Join(config.BuildTags(), " "))
fmt.Printf("garbage collector: %s\n", config.GC())
fmt.Printf("scheduler: %s\n", config.Scheduler())
fmt.Printf("cached GOROOT: %s\n", cachedGOROOT)
case "list":
config, err := builder.NewConfig(options)
if err != nil {
@@ -986,11 +989,31 @@ func main() {
usage()
os.Exit(1)
}
err = runGoList(config, *flagJSON, *flagDeps, flag.Args())
var extraArgs []string
if *flagJSON {
extraArgs = append(extraArgs, "-json")
}
if *flagDeps {
extraArgs = append(extraArgs, "-deps")
}
cmd, err := loader.List(config, extraArgs, flag.Args())
if err != nil {
fmt.Fprintln(os.Stderr, "failed to run `go list`:", err)
os.Exit(1)
}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
os.Exit(status.ExitStatus())
}
os.Exit(1)
}
fmt.Fprintln(os.Stderr, "failed to run `go list`:", err)
os.Exit(1)
}
case "clean":
// remove cache directory
err := os.RemoveAll(goenv.Get("GOCACHE"))
@@ -1005,7 +1028,11 @@ func main() {
if s, err := goenv.GorootVersionString(goenv.Get("GOROOT")); err == nil {
goversion = s
}
fmt.Printf("tinygo version %s %s/%s (using go version %s and LLVM version %s)\n", goenv.Version, runtime.GOOS, runtime.GOARCH, goversion, llvm.Version)
version := goenv.Version
if strings.HasSuffix(goenv.Version, "-dev") && gitSha1 != "" {
version += "-" + gitSha1
}
fmt.Printf("tinygo version %s %s/%s (using go version %s and LLVM version %s)\n", version, runtime.GOOS, runtime.GOARCH, goversion, llvm.Version)
case "env":
if flag.NArg() == 0 {
// Show all environment variables.
+1 -1
View File
@@ -157,7 +157,7 @@ func runTest(path, target string, t *testing.T) {
PrintIR: false,
DumpSSA: false,
VerifyIR: true,
Debug: false,
Debug: true,
PrintSizes: "",
WasmAbi: "js",
}
+6
View File
@@ -1,9 +1,11 @@
.syntax unified
.cfi_sections .debug_frame
.section .text.HardFault_Handler
.global HardFault_Handler
.type HardFault_Handler, %function
HardFault_Handler:
.cfi_startproc
// Put the old stack pointer in the first argument, for easy debugging. This
// is especially useful on Cortex-M0, which supports far fewer debug
// facilities.
@@ -19,6 +21,7 @@ HardFault_Handler:
// Continue handling this error in Go.
bl handleHardFault
.cfi_endproc
.size HardFault_Handler, .-HardFault_Handler
// This is a convenience function for semihosting support.
@@ -27,5 +30,8 @@ HardFault_Handler:
.global SemihostingCall
.type SemihostingCall, %function
SemihostingCall:
.cfi_startproc
bkpt 0xab
bx lr
.cfi_endproc
.size SemihostingCall, .-SemihostingCall
+52
View File
@@ -0,0 +1,52 @@
// The following definitions were copied from:
// esp-idf/components/xtensa/include/xtensa/corebits.h
#define PS_WOE_MASK 0x00040000
#define PS_OWB_MASK 0x00000F00
#define PS_CALLINC_MASK 0x00030000
#define PS_WOE PS_WOE_MASK
// Only calling it call_start_cpu0 for consistency with ESP-IDF.
.section .text.call_start_cpu0
1:
.long _stack_top
.global call_start_cpu0
call_start_cpu0:
// We need to set the stack pointer to a different value. This is somewhat
// complicated in the Xtensa architecture. The code below is a modified
// version of the following code:
// https://github.com/espressif/esp-idf/blob/c77c4ccf/components/xtensa/include/xt_instr_macros.h#L47
// Disable WOE.
rsr.ps a2
movi a3, ~(PS_WOE_MASK)
and a2, a2, a3
wsr.ps a2
rsync
// Set WINDOWBASE to 1 << WINDOWSTART.
rsr.windowbase a2
ssl a2
movi a2, 1
sll a2, a2
wsr.windowstart a2
rsync
// Load new stack pointer.
l32r sp, 1b
// Re-enable WOE.
rsr.ps a2
movi a3, PS_WOE
or a2, a2, a3
wsr.ps a2
rsync
// Jump to the runtime start function written in Go.
j main
.section .text.tinygo_scanCurrentStack
.global tinygo_scanCurrentStack
tinygo_scanCurrentStack:
// TODO: save callee saved registers on the stack
j tinygo_scanstack
+6
View File
@@ -0,0 +1,6 @@
.section .text.tinygo_scanCurrentStack
.global tinygo_scanCurrentStack
tinygo_scanCurrentStack:
// TODO: save callee saved registers on the stack
j tinygo_scanstack
+71
View File
@@ -0,0 +1,71 @@
// Hand created file. DO NOT DELETE.
// atsamd51x bitfield definitions that are not auto-generated by gen-device-svd.go
// +build sam,atsamd51
// These are the supported pchctrl function numberings on the atsamd51x
// See http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D5xE5x_Family_Data_Sheet_DS60001507F.pdf
// table 14-9
package sam
const (
PCHCTRL_GCLK_OSCCTRL_DFLL48 = 0 // DFLL48 input clock source
PCHCTRL_GCLK_OSCCTRL_FDPLL0 = 1 // Reference clock for FDPLL0
PCHCTRL_GCLK_OSCCTRL_FDPLL1 = 2 // Reference clock for FDPLL1
PCHCTRL_GCLK_OSCCTRL_FDPLL0_32K = 3 // FDPLL0 = 3 // 32KHz clock for internal lock timer
PCHCTRL_GCLK_OSCCTRL_FDPLL1_32K = 3 // FDPLL1 = 3 // 32KHz clock for internal lock timer
PCHCTRL_GCLK_SDHC0_SLOW = 3 // SDHC0 = 3 // Slow
PCHCTRL_GCLK_SDHC1_SLOW = 3 // SDHC1 = 3 // Slow
PCHCTRL_GCLK_SERCOMX_SLOW = 3 // GCLK_SERCOM[0..7]_SLOW = 3
PCHCTRL_GCLK_EIC = 4
PCHCTRL_GCLK_FREQM_MSR = 5 // FREQM Measure
PCHCTRL_GCLK_FREQM_REF = 6 // FREQM Reference
PCHCTRL_GCLK_SERCOM0_CORE = 7 // SERCOM0 Core
PCHCTRL_GCLK_SERCOM1_CORE = 8 // SERCOM1 Core
PCHCTRL_GCLK_TC0 = 9
PCHCTRL_GCLK_TC1 = 9 // TC0, TC1
PCHCTRL_GCLK_USB = 10 // USB
PCHCTRL_GCLK_EVSYS0 = 11
PCHCTRL_GCLK_EVSYS1 = 12
PCHCTRL_GCLK_EVSYS2 = 13
PCHCTRL_GCLK_EVSYS3 = 14
PCHCTRL_GCLK_EVSYS4 = 15
PCHCTRL_GCLK_EVSYS5 = 16
PCHCTRL_GCLK_EVSYS6 = 17
PCHCTRL_GCLK_EVSYS7 = 18
PCHCTRL_GCLK_EVSYS8 = 19
PCHCTRL_GCLK_EVSYS9 = 20
PCHCTRL_GCLK_EVSYS10 = 21
PCHCTRL_GCLK_EVSYS11 = 22
PCHCTRL_GCLK_SERCOM2_CORE = 23 // SERCOM2 Core
PCHCTRL_GCLK_SERCOM3_CORE = 24 // SERCOM3 Core
PCHCTRL_GCLK_TCC0 = 25
PCHCTRL_GCLK_TCC1 = 25 // TCC0, TCC1
PCHCTRL_GCLK_TC2 = 26
PCHCTRL_GCLK_TC3 = 26 // TC2, TC3
PCHCTRL_GCLK_CAN0 = 27 // CAN0
PCHCTRL_GCLK_CAN1 = 28 // CAN1
PCHCTRL_GCLK_TCC2 = 29
PCHCTRL_GCLK_TCC3 = 29 // TCC2, TCC3
PCHCTRL_GCLK_TC4 = 30
PCHCTRL_GCLK_TC5 = 30 // TC4, TC5
PCHCTRL_GCLK_PDEC = 31 // PDEC
PCHCTRL_GCLK_AC = 32 // AC
PCHCTRL_GCLK_CCL = 33 // CCL
PCHCTRL_GCLK_SERCOM4_CORE = 34 // SERCOM4 Core
PCHCTRL_GCLK_SERCOM5_CORE = 35 // SERCOM5 Core
PCHCTRL_GCLK_SERCOM6_CORE = 36 // SERCOM6 Core
PCHCTRL_GCLK_SERCOM7_CORE = 37 // SERCOM7 Core
PCHCTRL_GCLK_TCC4 = 38 // TCC4
PCHCTRL_GCLK_TC6 = 39
PCHCTRL_GCLK_TC7 = 39 // TC6, TC7
PCHCTRL_GCLK_ADC0 = 40 // ADC0
PCHCTRL_GCLK_ADC1 = 41 // ADC1
PCHCTRL_GCLK_DAC = 42 // DAC
PCHCTRL_GCLK_I2S0 = 43
PCHCTRL_GCLK_I2S1 = 44
PCHCTRL_GCLK_SDHC0 = 45 // SDHC0
PCHCTRL_GCLK_SDHC1 = 46 // SDHC1
PCHCTRL_GCLK_CM4_TRACE = 47 // CM4 Trace
)
@@ -1,7 +1,9 @@
// These are the supported alternate function numberings on the stm32f407
// +build stm32,stm32f407
// Hand created file. DO NOT DELETE.
// STM32FXXX (except stm32f1xx) bitfield definitions that are not
// auto-generated by gen-device-svd.go
// +build stm32f4
// Alternate function settings on the stm32f4xx series
// Alternate function settings on the stm32f4 series
package stm32
+1 -1
View File
@@ -8,7 +8,7 @@ import (
// This example assumes that an RGB LED is connected to pins 3, 5 and 6 on an Arduino.
// Change the values below to use different pins.
const (
redPin = machine.D3
redPin = machine.D4
greenPin = machine.D5
bluePin = machine.D6
)
+127
View File
@@ -124,3 +124,130 @@ func IndexString(str, sub string) int {
}
return -1
}
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The following code has been copied from the Go 1.15 release tree.
// PrimeRK is the prime base used in Rabin-Karp algorithm.
const PrimeRK = 16777619
// HashStrBytes returns the hash and the appropriate multiplicative
// factor for use in Rabin-Karp algorithm.
func HashStrBytes(sep []byte) (uint32, uint32) {
hash := uint32(0)
for i := 0; i < len(sep); i++ {
hash = hash*PrimeRK + uint32(sep[i])
}
var pow, sq uint32 = 1, PrimeRK
for i := len(sep); i > 0; i >>= 1 {
if i&1 != 0 {
pow *= sq
}
sq *= sq
}
return hash, pow
}
// HashStr returns the hash and the appropriate multiplicative
// factor for use in Rabin-Karp algorithm.
func HashStr(sep string) (uint32, uint32) {
hash := uint32(0)
for i := 0; i < len(sep); i++ {
hash = hash*PrimeRK + uint32(sep[i])
}
var pow, sq uint32 = 1, PrimeRK
for i := len(sep); i > 0; i >>= 1 {
if i&1 != 0 {
pow *= sq
}
sq *= sq
}
return hash, pow
}
// HashStrRevBytes returns the hash of the reverse of sep and the
// appropriate multiplicative factor for use in Rabin-Karp algorithm.
func HashStrRevBytes(sep []byte) (uint32, uint32) {
hash := uint32(0)
for i := len(sep) - 1; i >= 0; i-- {
hash = hash*PrimeRK + uint32(sep[i])
}
var pow, sq uint32 = 1, PrimeRK
for i := len(sep); i > 0; i >>= 1 {
if i&1 != 0 {
pow *= sq
}
sq *= sq
}
return hash, pow
}
// HashStrRev returns the hash of the reverse of sep and the
// appropriate multiplicative factor for use in Rabin-Karp algorithm.
func HashStrRev(sep string) (uint32, uint32) {
hash := uint32(0)
for i := len(sep) - 1; i >= 0; i-- {
hash = hash*PrimeRK + uint32(sep[i])
}
var pow, sq uint32 = 1, PrimeRK
for i := len(sep); i > 0; i >>= 1 {
if i&1 != 0 {
pow *= sq
}
sq *= sq
}
return hash, pow
}
// IndexRabinKarpBytes uses the Rabin-Karp search algorithm to return the index of the
// first occurence of substr in s, or -1 if not present.
func IndexRabinKarpBytes(s, sep []byte) int {
// Rabin-Karp search
hashsep, pow := HashStrBytes(sep)
n := len(sep)
var h uint32
for i := 0; i < n; i++ {
h = h*PrimeRK + uint32(s[i])
}
if h == hashsep && Equal(s[:n], sep) {
return 0
}
for i := n; i < len(s); {
h *= PrimeRK
h += uint32(s[i])
h -= pow * uint32(s[i-n])
i++
if h == hashsep && Equal(s[i-n:i], sep) {
return i - n
}
}
return -1
}
// IndexRabinKarp uses the Rabin-Karp search algorithm to return the index of the
// first occurence of substr in s, or -1 if not present.
func IndexRabinKarp(s, substr string) int {
// Rabin-Karp search
hashss, pow := HashStr(substr)
n := len(substr)
var h uint32
for i := 0; i < n; i++ {
h = h*PrimeRK + uint32(s[i])
}
if h == hashss && s[:n] == substr {
return 0
}
for i := n; i < len(s); {
h *= PrimeRK
h += uint32(s[i])
h -= pow * uint32(s[i-n])
i++
if h == hashss && s[i-n:i] == substr {
return i - n
}
}
return -1
}
+5
View File
@@ -18,3 +18,8 @@ type Task struct {
// state is the underlying running state of the task.
state state
}
// getGoroutineStackSize is a compiler intrinsic that returns the stack size for
// the given function and falls back to the default stack size. It is replaced
// with a load from a special section just before codegen.
func getGoroutineStackSize(fn uintptr) uintptr
+1 -1
View File
@@ -67,7 +67,7 @@ func createTask() *Task {
// start invokes a function in a new goroutine. Calls to this are inserted by the compiler.
// The created goroutine starts running immediately.
// This is implemented inside the compiler.
func start(fn uintptr, args unsafe.Pointer)
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr)
// Current returns the current active task.
// This is implemented inside the compiler.
+1 -1
View File
@@ -17,7 +17,7 @@ func Current() *Task {
}
//go:noinline
func start(fn uintptr, args unsafe.Pointer) {
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
// The compiler will error if this is reachable.
runtimePanic("scheduler is disabled")
}
+3 -3
View File
@@ -54,7 +54,7 @@ func (t *Task) Resume() {
}
// initialize the state and prepare to call the specified function with the specified argument bundle.
func (s *state) initialize(fn uintptr, args unsafe.Pointer) {
func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
// Create a stack.
stack := make([]uintptr, stackSize/unsafe.Sizeof(uintptr(0)))
@@ -67,9 +67,9 @@ func runqueuePushBack(*Task)
// start creates and starts a new goroutine with the given function and arguments.
// The new goroutine is scheduled to run later.
func start(fn uintptr, args unsafe.Pointer) {
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
t := &Task{}
t.state.initialize(fn, args)
t.state.initialize(fn, args, stackSize)
runqueuePushBack(t)
}
-2
View File
@@ -4,8 +4,6 @@ package task
import "unsafe"
const stackSize = 256
// calleeSavedRegs is the list of registers that must be saved and restored when
// switching between tasks. Also see scheduler_avr.S that relies on the
// exact layout of this struct.
-2
View File
@@ -4,8 +4,6 @@ package task
import "unsafe"
const stackSize = 1024
// calleeSavedRegs is the list of registers that must be saved and restored when
// switching between tasks. Also see scheduler_cortexm.S that relies on the
// exact layout of this struct.
+101
View File
@@ -0,0 +1,101 @@
// +build sam,atsamd21,arduino_zero
package machine
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0x07738135
// GPIO Pins - Digital Low
const (
D0 = PA11 // RX
D1 = PA10 // TX
D2 = PA14
D3 = PA09 // PWM available
D4 = PA08 // PWM available
D5 = PA15 // PWM available
D6 = PA20 // PWM available
D7 = PA21
)
// GPIO Pins - Digital High
const (
D8 = PA06 // PWM available
D9 = PA07 // PWM available
D10 = PA18 // PWM available
D11 = PA16 // PWM available
D12 = PA19 // PWM available
D13 = PA17 // PWM available
)
// LEDs on the Arduino Zero
const (
LED = LED1
LED1 Pin = D13
LED2 Pin = PA27 // TX LED
LED3 Pin = PB03 // RX LED
)
// ADC pins
const (
AREF Pin = PA03
ADC0 Pin = PA02
ADC1 Pin = PB08
ADC2 Pin = PB09
ADC3 Pin = PA04
ADC4 Pin = PA05
ADC5 Pin = PB02
)
// SPI pins - EDBG connected
const (
SPI0_SDO_PIN Pin = PA16 // MOSI: SERCOM1/PAD[0]
SPI0_SDI_PIN Pin = PA19 // MISO: SERCOM1/PAD[2]
SPI0_SCK_PIN Pin = PA17 // SCK: SERCOM1/PAD[3]
)
// SPI pins (Legacy ICSP)
const (
SPI1_SDO_PIN Pin = PB10 // MOSI: SERCOM4/PAD[2] - Pin 4
SPI1_SDI_PIN Pin = PA12 // MISO: SERCOM4/PAD[0] - Pin 1
SPI1_SCK_PIN Pin = PB11 // SCK: SERCOM4/PAD[3] - Pin 3
)
// I2C pins - EDBG connected
const (
SDA_PIN Pin = PA22 // SDA: SERCOM3/PAD[0] - Pin 20
SCL_PIN Pin = PA23 // SCL: SERCOM3/PAD[1] - Pin 21
)
// I2S pins - might not be exposed
const (
I2S_SCK_PIN Pin = PA10
I2S_SD_PIN Pin = PA07
I2S_WS_PIN Pin = PA11
)
// UART0 pins - EDBG connected
const (
UART_RX_PIN Pin = D0
UART_TX_PIN Pin = D1
)
// 'native' USB port pins
const (
USBCDC_DM_PIN Pin = PA24
USBCDC_DP_PIN Pin = PA25
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Arduino Zero"
usb_STRING_MANUFACTURER = "Arduino LLC"
usb_VID uint16 = 0x2341
usb_PID uint16 = 0x804d
)
// 32.768 KHz Crystal
const (
XIN32 Pin = PA00
XOUT32 Pin = PA01
)
+5 -1
View File
@@ -65,11 +65,15 @@ var (
Buffer: NewRingBuffer(),
Bus: stm32.USART1,
}
UART1 = &UART0
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART2,
}
)
func init() {
UART0.Interrupt = interrupt.New(stm32.IRQ_USART1, UART0.handleInterrupt)
UART1.Interrupt = interrupt.New(stm32.IRQ_USART2, UART1.handleInterrupt)
}
// SPI pins
+1 -1
View File
@@ -2,7 +2,7 @@
package machine
const HasLowFrequencyCrystal = true
const HasLowFrequencyCrystal = false
// GPIO Pins
const (
+6
View File
@@ -0,0 +1,6 @@
// +build esp32_wroom_32
package machine
// Blue LED on the ESP32-WROOM-32 module.
const LED = Pin(2)
+213
View File
@@ -0,0 +1,213 @@
// +build feather_stm32f405
package machine
import (
"device/stm32"
"runtime/interrupt"
)
const (
NUM_DIGITAL_IO_PINS = 39
NUM_ANALOG_IO_PINS = 7
)
// Digital pins
const (
// Arduino pin = MCU port pin // primary functions (alternate functions)
D0 = PB11 // USART3 RX, PWM TIM2_CH4 (I2C2 SDA)
D1 = PB10 // USART3 TX, PWM TIM2_CH3 (I2C2 SCL, I2S2 BCK)
D2 = PB3 // GPIO, SPI3 FLASH SCK
D3 = PB4 // GPIO, SPI3 FLASH MISO
D4 = PB5 // GPIO, SPI3 FLASH MOSI
D5 = PC7 // GPIO, PWM TIM3_CH2 (USART6 RX, I2S3 MCK)
D6 = PC6 // GPIO, PWM TIM3_CH1 (USART6 TX, I2S2 MCK)
D7 = PA15 // GPIO, SPI3 FLASH CS
D8 = PC0 // GPIO, Neopixel
D9 = PB8 // GPIO, PWM TIM4_CH3 (CAN1 RX, I2C1 SCL)
D10 = PB9 // GPIO, PWM TIM4_CH4 (CAN1 TX, I2C1 SDA, I2S2 WSL)
D11 = PC3 // GPIO (I2S2 SD, SPI2 MOSI)
D12 = PC2 // GPIO (I2S2ext SD, SPI2 MISO)
D13 = PC1 // GPIO, Builtin LED
D14 = PB7 // I2C1 SDA, PWM TIM4_CH2 (USART1 RX)
D15 = PB6 // I2C1 SCL, PWM TIM4_CH1 (USART1 TX, CAN2 TX)
D16 = PA4 // A0 (DAC OUT1)
D17 = PA5 // A1 (DAC OUT2, SPI1 SCK)
D18 = PA6 // A2, PWM TIM3_CH1 (SPI1 MISO)
D19 = PA7 // A3, PWM TIM3_CH2 (SPI1 MOSI)
D20 = PC4 // A4
D21 = PC5 // A5
D22 = PA3 // A6
D23 = PB13 // SPI2 SCK, PWM TIM1_CH1N (I2S2 BCK, CAN2 TX)
D24 = PB14 // SPI2 MISO, PWM TIM1_CH2N (I2S2ext SD)
D25 = PB15 // SPI2 MOSI, PWM TIM1_CH3N (I2S2 SD)
D26 = PC8 // SDIO
D27 = PC9 // SDIO
D28 = PC10 // SDIO
D29 = PC11 // SDIO
D30 = PC12 // SDIO
D31 = PD2 // SDIO
D32 = PB12 // SD Detect
D33 = PC14 // OSC32
D34 = PC15 // OSC32
D35 = PA11 // USB D+
D36 = PA12 // USB D-
D37 = PA13 // SWDIO
D38 = PA14 // SWCLK
)
// Analog pins
const (
A0 = D16 // ADC12 IN4
A1 = D17 // ADC12 IN5
A2 = D18 // ADC12 IN6
A3 = D19 // ADC12 IN7
A4 = D20 // ADC12 IN14
A5 = D21 // ADC12 IN15
A6 = D22 // VBAT
)
func init() {
initLED()
initUART()
initSPI()
initI2C()
}
// -- LEDs ---------------------------------------------------------------------
const (
NUM_BOARD_LED = 1
NUM_BOARD_NEOPIXEL = 1
LED_RED = D13
LED_NEOPIXEL = D8
LED_BUILTIN = LED_RED
LED = LED_BUILTIN
)
func initLED() {}
// -- UART ---------------------------------------------------------------------
const (
// #===========#==========#==============#============#=======#=======#
// | Interface | Hardware | Bus(Freq) | RX/TX Pins | AltFn | Alias |
// #===========#==========#==============#============#=======#=======#
// | UART1 | USART3 | APB1(42 MHz) | D0/D1 | 7 | ~ |
// | UART2 | USART6 | APB2(84 MHz) | D5/D6 | 8 | ~ |
// | UART3 | USART1 | APB2(84 MHz) | D14/D15 | 7 | ~ |
// | --------- | -------- | ------------ | ---------- | ----- | ----- |
// | UART0 | USART3 | APB1(42 MHz) | D0/D1 | 7 | UART1 |
// #===========#==========#==============#============#=======#=======#
NUM_UART_INTERFACES = 3
UART1_RX_PIN = D0 // UART1 = hardware: USART3
UART1_TX_PIN = D1 //
UART2_RX_PIN = D5 // UART2 = hardware: USART6
UART2_TX_PIN = D6 //
UART3_RX_PIN = D14 // UART3 = hardware: USART1
UART3_TX_PIN = D15 //
UART0_RX_PIN = UART1_RX_PIN // UART0 = alias: UART1
UART0_TX_PIN = UART1_TX_PIN //
UART_RX_PIN = UART0_RX_PIN // default/primary UART pins
UART_TX_PIN = UART0_TX_PIN //
)
var (
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART3,
AltFuncSelector: stm32.AF7_USART1_2_3,
}
UART2 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART6,
AltFuncSelector: stm32.AF8_USART4_5_6,
}
UART3 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART1,
AltFuncSelector: stm32.AF7_USART1_2_3,
}
UART0 = UART1
)
func initUART() {
UART1.Interrupt = interrupt.New(stm32.IRQ_USART3, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(stm32.IRQ_USART6, UART2.handleInterrupt)
UART3.Interrupt = interrupt.New(stm32.IRQ_USART1, UART3.handleInterrupt)
}
// -- SPI ----------------------------------------------------------------------
const (
// #===========#==========#==============#==================#=======#=======#
// | Interface | Hardware | Bus(Freq) | SCK/SDI/SDO Pins | AltFn | Alias |
// #===========#==========#==============#==================#=======#=======#
// | SPI1 | SPI2 | APB1(42 MHz) | D23/D24/D25 | 5 | ~ |
// | SPI2 | SPI3 | APB1(42 MHz) | D2/D3/D4 | 6 | ~ |
// | SPI3 | SPI1 | APB2(84 MHz) | D17/D18/D19 | 5 | ~ |
// | --------- | -------- | ------------ | ---------------- | ----- | ----- |
// | SPI0 | SPI2 | APB1(42 MHz) | D23/D24/D25 | 5 | SPI1 |
// #===========#==========#==============#==================#=======#=======#
NUM_SPI_INTERFACES = 3
SPI1_SCK_PIN = D23 //
SPI1_SDI_PIN = D24 // SPI1 = hardware: SPI2
SPI1_SDO_PIN = D25 //
SPI2_SCK_PIN = D2 //
SPI2_SDI_PIN = D3 // SPI2 = hardware: SPI3
SPI2_SDO_PIN = D4 //
SPI3_SCK_PIN = D17 //
SPI3_SDI_PIN = D18 // SPI3 = hardware: SPI1
SPI3_SDO_PIN = D19 //
SPI0_SCK_PIN = SPI1_SCK_PIN //
SPI0_SDI_PIN = SPI1_SDI_PIN // SPI0 = alias: SPI1
SPI0_SDO_PIN = SPI1_SDO_PIN //
SPI_SCK_PIN = SPI0_SCK_PIN //
SPI_SDI_PIN = SPI0_SDI_PIN // default/primary SPI pins
SPI_SDO_PIN = SPI0_SDO_PIN //
)
func initSPI() {}
// -- I2C ----------------------------------------------------------------------
const (
// #===========#==========#==============#==============#=======#=======#
// | Interface | Hardware | Bus(Freq) | SDA/SCL Pins | AltFn | Alias |
// #===========#==========#==============#==============#=======#=======#
// | I2C1 | I2C1 | | D14/D15 | | ~ |
// | I2C2 | I2C2 | | D0/D1 | | ~ |
// | I2C3 | I2C1 | | D9/D10 | | ~ |
// | --------- | -------- | ------------ | ------------ | ----- | ----- |
// | I2C0 | I2C1 | | D14/D15 | | I2C1 |
// #===========#==========#==============#==============#=======#=======#
NUM_I2C_INTERFACES = 3
I2C1_SDA_PIN = D14 // I2C1 = hardware: I2C1
I2C1_SCL_PIN = D15 //
I2C2_SDA_PIN = D0 // I2C2 = hardware: I2C2
I2C2_SCL_PIN = D1 //
I2C3_SDA_PIN = D9 // I2C3 = hardware: I2C1
I2C3_SCL_PIN = D10 // (interface duplicated on second pair of pins)
I2C0_SDA_PIN = I2C1_SDA_PIN // I2C0 = alias: I2C1
I2C0_SCL_PIN = I2C1_SCL_PIN //
I2C_SDA_PIN = I2C0_SDA_PIN // default/primary I2C pins
I2C_SCL_PIN = I2C0_SCL_PIN //
)
func initI2C() {}
+100
View File
@@ -0,0 +1,100 @@
// +build itsybitsy_nrf52840
package machine
const HasLowFrequencyCrystal = true
// GPIO Pins
const (
D0 = P0_25 // UART TX
D1 = P0_24 // UART RX
D2 = P1_02
D3 = P0_06 // LED1
D4 = P0_29 // Button
D5 = P0_27
D6 = P1_09 // DotStar Clock
D7 = P1_08
D8 = P0_08 // DotStar Data
D9 = P0_07
D10 = P0_05
D11 = P0_26
D12 = P0_11
D13 = P0_12
D14 = P0_04 // A0
D15 = P0_30 // A1
D16 = P0_28 // A2
D17 = P0_31 // A3
D18 = P0_02 // A4
D19 = P0_03 // A5
D20 = P0_05 // A6
D21 = P0_16 // I2C SDA
D22 = P0_14 // I2C SCL
D23 = P0_20 // SPI SDI
D24 = P0_15 // SPI SDO
D25 = P0_13 // SPI SCK
D26 = P0_19 // QSPI SCK
D27 = P0_23 // QSPI CS
D28 = P0_21 // QSPI Data 0
D29 = P0_22 // QSPI Data 1
D30 = P1_00 // QSPI Data 2
D31 = P0_17 // QSPI Data 3
)
// Analog Pins
const (
A0 = D14
A1 = D15
A2 = D16
A3 = D17
A4 = D18
A5 = D19
A6 = D20
)
const (
LED = D3
LED1 = LED
BUTTON = D4
QSPI_SCK = D26
QSPI_CS = D27
QSPI_DATA0 = D28
QSPI_DATA1 = D29
QSPI_DATA2 = D30
QSPI_DATA3 = D31
)
// UART0 pins (logical UART1)
const (
UART_RX_PIN = D0
UART_TX_PIN = D1
)
// UART0 is the USB device
var (
UART0 = USB
)
// I2C pins
const (
SDA_PIN = D21 // I2C0 external
SCL_PIN = D22 // I2C0 external
)
// SPI pins
const (
SPI0_SCK_PIN = D25
SPI0_SDO_PIN = D24
SPI0_SDI_PIN = D23
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Adafruit ItsyBitsy nRF52840 Express"
usb_STRING_MANUFACTURER = "Adafruit"
)
var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x8051
)
+21
View File
@@ -0,0 +1,21 @@
// +build nodemcu
// Pinout for the NodeMCU dev kit.
package machine
// GPIO pins on the NodeMCU board.
const (
D0 Pin = 16
D1 Pin = 5
D2 Pin = 4
D3 Pin = 0
D4 Pin = 2
D5 Pin = 14
D6 Pin = 12
D7 Pin = 13
D8 Pin = 15
)
// Onboard blue LED (on the AI-Thinker module).
const LED = D4
+1 -12
View File
@@ -1,16 +1,5 @@
// +build bluepill nucleof103rb stm32f4disco
// +build bluepill nucleof103rb stm32f4
package machine
// Peripheral abstraction layer for the stm32.
const (
portA Pin = iota * 16
portB
portC
portD
portE
portF
portG
portH
)
-90
View File
@@ -7,96 +7,6 @@ import (
"runtime/interrupt"
)
const (
PA0 = portA + 0
PA1 = portA + 1
PA2 = portA + 2
PA3 = portA + 3
PA4 = portA + 4
PA5 = portA + 5
PA6 = portA + 6
PA7 = portA + 7
PA8 = portA + 8
PA9 = portA + 9
PA10 = portA + 10
PA11 = portA + 11
PA12 = portA + 12
PA13 = portA + 13
PA14 = portA + 14
PA15 = portA + 15
PB0 = portB + 0
PB1 = portB + 1
PB2 = portB + 2
PB3 = portB + 3
PB4 = portB + 4
PB5 = portB + 5
PB6 = portB + 6
PB7 = portB + 7
PB8 = portB + 8
PB9 = portB + 9
PB10 = portB + 10
PB11 = portB + 11
PB12 = portB + 12
PB13 = portB + 13
PB14 = portB + 14
PB15 = portB + 15
PC0 = portC + 0
PC1 = portC + 1
PC2 = portC + 2
PC3 = portC + 3
PC4 = portC + 4
PC5 = portC + 5
PC6 = portC + 6
PC7 = portC + 7
PC8 = portC + 8
PC9 = portC + 9
PC10 = portC + 10
PC11 = portC + 11
PC12 = portC + 12
PC13 = portC + 13
PC14 = portC + 14
PC15 = portC + 15
PD0 = portD + 0
PD1 = portD + 1
PD2 = portD + 2
PD3 = portD + 3
PD4 = portD + 4
PD5 = portD + 5
PD6 = portD + 6
PD7 = portD + 7
PD8 = portD + 8
PD9 = portD + 9
PD10 = portD + 10
PD11 = portD + 11
PD12 = portD + 12
PD13 = portD + 13
PD14 = portD + 14
PD15 = portD + 15
PE0 = portE + 0
PE1 = portE + 1
PE2 = portE + 2
PE3 = portE + 3
PE4 = portE + 4
PE5 = portE + 5
PE6 = portE + 6
PE7 = portE + 7
PE8 = portE + 8
PE9 = portE + 9
PE10 = portE + 10
PE11 = portE + 11
PE12 = portE + 12
PE13 = portE + 13
PE14 = portE + 14
PE15 = portE + 15
PH0 = portH + 0
PH1 = portH + 1
)
const (
LED = LED_BUILTIN
LED1 = LED_GREEN
+1 -1
View File
@@ -1,4 +1,4 @@
// +build avr nrf sam stm32,!stm32f4disco fe310 k210
// +build avr nrf sam stm32,!stm32f4 fe310 k210
package machine
+4 -1
View File
@@ -1188,7 +1188,10 @@ func (spi SPI) Configure(config SPIConfig) error {
}
// Set synch speed for SPI
baudRate := (CPUFrequency() / (2 * config.Frequency)) - 1
baudRate := CPUFrequency() / (2 * config.Frequency)
if baudRate > 0 {
baudRate--
}
spi.Bus.BAUD.Set(uint8(baudRate))
// Enable SPI port.
+56 -17
View File
@@ -1417,6 +1417,9 @@ func (spi SPI) Configure(config SPIConfig) error {
// Set synch speed for SPI
baudRate := SERCOM_FREQ_REF / (2 * config.Frequency)
if baudRate > 0 {
baudRate--
}
spi.Bus.BAUD.Set(uint8(baudRate))
// Enable SPI port.
@@ -1580,14 +1583,14 @@ func (pwm PWM) setPinCfg(val uint8) {
// setChannel sets the value for the correct channel for PWM on this pin.
func (pwm PWM) setChannel(timer *sam.TCC_Type, val uint32) {
switch pwm.Pin {
case PA16:
timer.CC[0].Set(val)
case PA17:
timer.CC[1].Set(val)
case PA14:
timer.CC[0].Set(val)
case PA15:
timer.CC[1].Set(val)
case PA16:
timer.CC[0].Set(val)
case PA17:
timer.CC[1].Set(val)
case PA18:
timer.CC[2].Set(val)
case PA19:
@@ -1596,10 +1599,22 @@ func (pwm PWM) setChannel(timer *sam.TCC_Type, val uint32) {
timer.CC[0].Set(val)
case PA21:
timer.CC[1].Set(val)
case PA23:
timer.CC[3].Set(val)
case PA22:
timer.CC[2].Set(val)
case PA23:
timer.CC[3].Set(val)
case PB12:
timer.CC[0].Set(val)
case PB13:
timer.CC[1].Set(val)
case PB14:
timer.CC[0].Set(val)
case PB15:
timer.CC[1].Set(val)
case PB16:
timer.CC[4].Set(val)
case PB17:
timer.CC[5].Set(val)
case PB31:
timer.CC[1].Set(val)
default:
@@ -1610,14 +1625,14 @@ func (pwm PWM) setChannel(timer *sam.TCC_Type, val uint32) {
// setChannelBuffer sets the value for the correct channel buffer for PWM on this pin
func (pwm PWM) setChannelBuffer(timer *sam.TCC_Type, val uint32) {
switch pwm.Pin {
case PA16:
timer.CCBUF[0].Set(val)
case PA17:
timer.CCBUF[1].Set(val)
case PA14:
timer.CCBUF[0].Set(val)
case PA15:
timer.CCBUF[1].Set(val)
case PA16:
timer.CCBUF[0].Set(val)
case PA17:
timer.CCBUF[1].Set(val)
case PA18:
timer.CCBUF[2].Set(val)
case PA19:
@@ -1626,10 +1641,22 @@ func (pwm PWM) setChannelBuffer(timer *sam.TCC_Type, val uint32) {
timer.CCBUF[0].Set(val)
case PA21:
timer.CCBUF[1].Set(val)
case PA23:
timer.CCBUF[3].Set(val)
case PA22:
timer.CCBUF[2].Set(val)
case PA23:
timer.CCBUF[3].Set(val)
case PB12:
timer.CCBUF[0].Set(val)
case PB13:
timer.CCBUF[1].Set(val)
case PB14:
timer.CCBUF[0].Set(val)
case PB15:
timer.CCBUF[1].Set(val)
case PB16:
timer.CCBUF[4].Set(val)
case PB17:
timer.CCBUF[5].Set(val)
case PB31:
timer.CCBUF[1].Set(val)
default:
@@ -1640,14 +1667,14 @@ func (pwm PWM) setChannelBuffer(timer *sam.TCC_Type, val uint32) {
// getMux returns the pin mode mux to be used for PWM on this pin.
func (pwm PWM) getMux() PinMode {
switch pwm.Pin {
case PA16:
return PinPWMF
case PA17:
return PinPWMF
case PA14:
return PinPWMF
case PA15:
return PinPWMF
case PA16:
return PinPWMF
case PA17:
return PinPWMF
case PA18:
return PinPWMF
case PA19:
@@ -1656,9 +1683,21 @@ func (pwm PWM) getMux() PinMode {
return PinPWMG
case PA21:
return PinPWMG
case PA22:
return PinPWMG
case PA23:
return PinPWMG
case PA22:
case PB12:
return PinPWMF
case PB13:
return PinPWMF
case PB14:
return PinPWMF
case PB15:
return PinPWMF
case PB16:
return PinPWMG
case PB17:
return PinPWMG
case PB31:
return PinPWMF
+8 -8
View File
@@ -18,23 +18,23 @@ func InitPWM() {
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
//use clock generator 0
sam.GCLK.PCHCTRL[25].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
sam.GCLK.PCHCTRL[29].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
}
// getTimer returns the timer to be used for PWM on this pin
func (pwm PWM) getTimer() *sam.TCC_Type {
switch pwm.Pin {
case PA16:
return sam.TCC1
case PA17:
return sam.TCC1
case PA14:
return sam.TCC2
case PA15:
return sam.TCC2
case PA16:
return sam.TCC1
case PA17:
return sam.TCC1
case PA18:
return sam.TCC1
case PA19:
@@ -43,10 +43,10 @@ func (pwm PWM) getTimer() *sam.TCC_Type {
return sam.TCC0
case PA21:
return sam.TCC0
case PA23:
return sam.TCC0
case PA22:
return sam.TCC0
case PA23:
return sam.TCC0
default:
return nil // not supported on this pin
}
+22 -10
View File
@@ -3,7 +3,7 @@
// Peripheral abstraction layer for the atsamd51.
//
// Datasheet:
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
// http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D5xE5x_Family_Data_Sheet_DS60001507F.pdf
//
package machine
@@ -15,29 +15,29 @@ const HSRAM_SIZE = 0x00030000
func InitPWM() {
// turn on timer clocks used for PWM
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
//use clock generator 0
sam.GCLK.PCHCTRL[25].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
sam.GCLK.PCHCTRL[29].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
sam.GCLK.PCHCTRL[38].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
}
// getTimer returns the timer to be used for PWM on this pin
func (pwm PWM) getTimer() *sam.TCC_Type {
switch pwm.Pin {
case PA16:
return sam.TCC1
case PA17:
return sam.TCC1
case PA14:
return sam.TCC2
case PA15:
return sam.TCC2
case PA16:
return sam.TCC1
case PA17:
return sam.TCC1
case PA18:
return sam.TCC1
case PA19:
@@ -46,9 +46,21 @@ func (pwm PWM) getTimer() *sam.TCC_Type {
return sam.TCC0
case PA21:
return sam.TCC0
case PA22:
return sam.TCC0
case PA23:
return sam.TCC0
case PA22:
case PB12:
return sam.TCC3
case PB13:
return sam.TCC3
case PB14:
return sam.TCC4
case PB15:
return sam.TCC4
case PB16:
return sam.TCC0
case PB17:
return sam.TCC0
case PB31:
return sam.TCC4
+21 -9
View File
@@ -15,29 +15,29 @@ const HSRAM_SIZE = 0x00040000
func InitPWM() {
// turn on timer clocks used for PWM
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
//use clock generator 0
sam.GCLK.PCHCTRL[25].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
sam.GCLK.PCHCTRL[29].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
sam.GCLK.PCHCTRL[38].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
}
// getTimer returns the timer to be used for PWM on this pin
func (pwm PWM) getTimer() *sam.TCC_Type {
switch pwm.Pin {
case PA16:
return sam.TCC1
case PA17:
return sam.TCC1
case PA14:
return sam.TCC2
case PA15:
return sam.TCC2
case PA16:
return sam.TCC1
case PA17:
return sam.TCC1
case PA18:
return sam.TCC1
case PA19:
@@ -46,9 +46,21 @@ func (pwm PWM) getTimer() *sam.TCC_Type {
return sam.TCC0
case PA21:
return sam.TCC0
case PA22:
return sam.TCC0
case PA23:
return sam.TCC0
case PA22:
case PB12:
return sam.TCC3
case PB13:
return sam.TCC3
case PB14:
return sam.TCC4
case PB15:
return sam.TCC4
case PB16:
return sam.TCC0
case PB17:
return sam.TCC0
case PB31:
return sam.TCC4
+21 -9
View File
@@ -15,29 +15,29 @@ const HSRAM_SIZE = 0x00030000
func InitPWM() {
// turn on timer clocks used for PWM
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
//use clock generator 0
sam.GCLK.PCHCTRL[25].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
sam.GCLK.PCHCTRL[29].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
sam.GCLK.PCHCTRL[38].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
}
// getTimer returns the timer to be used for PWM on this pin
func (pwm PWM) getTimer() *sam.TCC_Type {
switch pwm.Pin {
case PA16:
return sam.TCC1
case PA17:
return sam.TCC1
case PA14:
return sam.TCC2
case PA15:
return sam.TCC2
case PA16:
return sam.TCC1
case PA17:
return sam.TCC1
case PA18:
return sam.TCC1
case PA19:
@@ -46,9 +46,21 @@ func (pwm PWM) getTimer() *sam.TCC_Type {
return sam.TCC0
case PA21:
return sam.TCC0
case PA22:
return sam.TCC0
case PA23:
return sam.TCC0
case PA22:
case PB12:
return sam.TCC3
case PB13:
return sam.TCC3
case PB14:
return sam.TCC4
case PB15:
return sam.TCC4
case PB16:
return sam.TCC0
case PB17:
return sam.TCC0
case PB31:
return sam.TCC4
+503
View File
@@ -0,0 +1,503 @@
// +build esp32
package machine
import (
"device/esp"
"errors"
"runtime/volatile"
"unsafe"
)
const peripheralClock = 80000000 // 80MHz
// CPUFrequency returns the current CPU frequency of the chip.
// Currently it is a fixed frequency but it may allow changing in the future.
func CPUFrequency() uint32 {
return 160e6 // 160MHz
}
var (
ErrInvalidSPIBus = errors.New("machine: invalid SPI bus")
)
type PinMode uint8
const (
PinOutput PinMode = iota
PinInput
PinInputPullup
PinInputPulldown
)
// Configure this pin with the given configuration.
func (p Pin) Configure(config PinConfig) {
// Output function 256 is a special value reserved for use as a regular GPIO
// pin. Peripherals (SPI etc) can set a custom output function by calling
// lowercase configure() instead with a signal name.
p.configure(config, 256)
}
// configure is the same as Configure, but allows for setting a specific input
// or output signal.
// Signals are always routed through the GPIO matrix for simplicity. Output
// signals are configured in FUNCx_OUT_SEL_CFG which selects a particular signal
// to output on a given pin. Input signals are configured in FUNCy_IN_SEL_CFG,
// which sets the pin to use for a particular input signal.
func (p Pin) configure(config PinConfig, signal uint32) {
if p == NoPin {
// This simplifies pin configuration in peripherals such as SPI.
return
}
var muxConfig uint32 // The mux configuration.
// Configure this pin as a GPIO pin.
const function = 3 // function 3 is GPIO for every pin
muxConfig |= (function - 1) << esp.IO_MUX_GPIO0_MCU_SEL_Pos
// Make this pin an input pin (always).
muxConfig |= esp.IO_MUX_GPIO0_FUN_IE
// Set drive strength: 0 is lowest, 3 is highest.
muxConfig |= 2 << esp.IO_MUX_GPIO0_FUN_DRV_Pos
// Select pull mode.
if config.Mode == PinInputPullup {
muxConfig |= esp.IO_MUX_GPIO0_FUN_WPU
} else if config.Mode == PinInputPulldown {
muxConfig |= esp.IO_MUX_GPIO0_FUN_WPD
}
// Configure the pad with the given IO mux configuration.
p.mux().Set(muxConfig)
switch config.Mode {
case PinOutput:
// Set the 'output enable' bit.
if p < 32 {
esp.GPIO.ENABLE_W1TS.Set(1 << p)
} else {
esp.GPIO.ENABLE1_W1TS.Set(1 << (p - 32))
}
// Set the signal to read the output value from. It can be a peripheral
// output signal, or the special value 256 which indicates regular GPIO
// usage.
p.outFunc().Set(signal)
case PinInput, PinInputPullup, PinInputPulldown:
// Clear the 'output enable' bit.
if p < 32 {
esp.GPIO.ENABLE_W1TC.Set(1 << p)
} else {
esp.GPIO.ENABLE1_W1TC.Set(1 << (p - 32))
}
if signal != 256 {
// Signal is a peripheral function (not a simple GPIO). Connect this
// signal to the pin.
// Note that outFunc and inFunc work in the opposite direction.
// outFunc configures a pin to use a given output signal, while
// inFunc specifies a pin to use to read the signal from.
inFunc(signal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(p)<<esp.GPIO_FUNC_IN_SEL_CFG_IN_SEL_Pos)
}
}
}
// outFunc returns the FUNCx_OUT_SEL_CFG register used for configuring the
// output function selection.
func (p Pin) outFunc() *volatile.Register32 {
return (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.GPIO.FUNC0_OUT_SEL_CFG)) + uintptr(p)*4)))
}
// inFunc returns the FUNCy_IN_SEL_CFG register used for configuring the input
// function selection.
func inFunc(signal uint32) *volatile.Register32 {
return (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.GPIO.FUNC0_IN_SEL_CFG)) + uintptr(signal)*4)))
}
// Set the pin to high or low.
// Warning: only use this on an output pin!
func (p Pin) Set(value bool) {
if value {
reg, mask := p.portMaskSet()
reg.Set(mask)
} else {
reg, mask := p.portMaskClear()
reg.Set(mask)
}
}
// Return the register and mask to enable a given GPIO pin. This can be used to
// implement bit-banged drivers.
//
// Warning: only use this on an output pin!
func (p Pin) PortMaskSet() (*uint32, uint32) {
reg, mask := p.portMaskSet()
return &reg.Reg, mask
}
// Return the register and mask to disable a given GPIO pin. This can be used to
// implement bit-banged drivers.
//
// Warning: only use this on an output pin!
func (p Pin) PortMaskClear() (*uint32, uint32) {
reg, mask := p.portMaskClear()
return &reg.Reg, mask
}
func (p Pin) portMaskSet() (*volatile.Register32, uint32) {
if p < 32 {
return &esp.GPIO.OUT_W1TS, 1 << p
} else {
return &esp.GPIO.OUT1_W1TS, 1 << (p - 32)
}
}
func (p Pin) portMaskClear() (*volatile.Register32, uint32) {
if p < 32 {
return &esp.GPIO.OUT_W1TC, 1 << p
} else {
return &esp.GPIO.OUT1_W1TC, 1 << (p - 32)
}
}
// Get returns the current value of a GPIO pin when the pin is configured as an
// input.
func (p Pin) Get() bool {
if p < 32 {
return esp.GPIO.IN.Get()&(1<<p) != 0
} else {
return esp.GPIO.IN1.Get()&(1<<(p-32)) != 0
}
}
// mux returns the I/O mux configuration register corresponding to the given
// GPIO pin.
func (p Pin) mux() *volatile.Register32 {
// I have no idea whether there is any pattern in the GPIO <-> pad mapping.
// I couldn't find it.
switch p {
case 36:
return &esp.IO_MUX.GPIO36
case 37:
return &esp.IO_MUX.GPIO37
case 38:
return &esp.IO_MUX.GPIO38
case 39:
return &esp.IO_MUX.GPIO39
case 34:
return &esp.IO_MUX.GPIO34
case 35:
return &esp.IO_MUX.GPIO35
case 32:
return &esp.IO_MUX.GPIO32
case 33:
return &esp.IO_MUX.GPIO33
case 25:
return &esp.IO_MUX.GPIO25
case 26:
return &esp.IO_MUX.GPIO26
case 27:
return &esp.IO_MUX.GPIO27
case 14:
return &esp.IO_MUX.MTMS
case 12:
return &esp.IO_MUX.MTDI
case 13:
return &esp.IO_MUX.MTCK
case 15:
return &esp.IO_MUX.MTDO
case 2:
return &esp.IO_MUX.GPIO2
case 0:
return &esp.IO_MUX.GPIO0
case 4:
return &esp.IO_MUX.GPIO4
case 16:
return &esp.IO_MUX.GPIO16
case 17:
return &esp.IO_MUX.GPIO17
case 9:
return &esp.IO_MUX.SD_DATA2
case 10:
return &esp.IO_MUX.SD_DATA3
case 11:
return &esp.IO_MUX.SD_CMD
case 6:
return &esp.IO_MUX.SD_CLK
case 7:
return &esp.IO_MUX.SD_DATA0
case 8:
return &esp.IO_MUX.SD_DATA1
case 5:
return &esp.IO_MUX.GPIO5
case 18:
return &esp.IO_MUX.GPIO18
case 19:
return &esp.IO_MUX.GPIO19
case 20:
return &esp.IO_MUX.GPIO20
case 21:
return &esp.IO_MUX.GPIO21
case 22:
return &esp.IO_MUX.GPIO22
case 3:
return &esp.IO_MUX.U0RXD
case 1:
return &esp.IO_MUX.U0TXD
case 23:
return &esp.IO_MUX.GPIO23
case 24:
return &esp.IO_MUX.GPIO24
default:
return nil
}
}
var (
UART0 = UART{Bus: esp.UART0, Buffer: NewRingBuffer()}
UART1 = UART{Bus: esp.UART1, Buffer: NewRingBuffer()}
UART2 = UART{Bus: esp.UART2, Buffer: NewRingBuffer()}
)
type UART struct {
Bus *esp.UART_Type
Buffer *RingBuffer
}
func (uart UART) Configure(config UARTConfig) {
if config.BaudRate == 0 {
config.BaudRate = 115200
}
uart.Bus.CLKDIV.Set(peripheralClock / config.BaudRate)
}
func (uart UART) WriteByte(b byte) error {
for (uart.Bus.STATUS.Get()>>16)&0xff >= 128 {
// Read UART_TXFIFO_CNT from the status register, which indicates how
// many bytes there are in the transmit buffer. Wait until there are
// less than 128 bytes in this buffer (the default buffer size).
}
uart.Bus.TX_FIFO.Set(b)
return nil
}
// Serial Peripheral Interface on the ESP32.
type SPI struct {
Bus *esp.SPI_Type
}
var (
// SPI0 and SPI1 are reserved for use by the caching system etc.
SPI2 = SPI{esp.SPI2}
SPI3 = SPI{esp.SPI3}
)
// SPIConfig configures a SPI peripheral on the ESP32. Make sure to set at least
// SCK, SDO and SDI (possibly to NoPin if not in use). The default for LSBFirst
// (false) and Mode (0) are good for most applications. The frequency defaults
// to 1MHz if not set but can be configured up to 40MHz. Possible values are
// 40MHz and integer divisions from 40MHz such as 20MHz, 13.3MHz, 10MHz, 8MHz,
// etc.
type SPIConfig struct {
Frequency uint32
SCK Pin
SDO Pin
SDI Pin
LSBFirst bool
Mode uint8
}
// Configure and make the SPI peripheral ready to use.
func (spi SPI) Configure(config SPIConfig) error {
if config.Frequency == 0 {
config.Frequency = 1e6 // default to 1MHz
}
// Configure the SPI clock. This assumes a peripheral clock of 80MHz.
var clockReg uint32
if config.Frequency >= 40e6 {
// Don't use a prescaler, but directly connect to the APB clock. This
// results in a SPI clock frequency of 40MHz.
clockReg |= esp.SPI_CLOCK_CLK_EQU_SYSCLK
} else {
// Use a prescaler for frequencies below 40MHz. They will get rounded
// down to the next possible frequency (20MHz, 13.3MHz, 10MHz, 8MHz,
// 6.7MHz, 5.7MHz, 5MHz, etc).
// This code is much simpler than how ESP-IDF configures the frequency,
// but should be just as accurate. The only exception is for frequencies
// below 4883Hz, which will need special support.
if config.Frequency < 4883 {
// The current lower limit is 4883Hz.
// The hardware supports lower frequencies by setting the h and n
// variables, but that's not yet implemented.
config.Frequency = 4883
}
// The prescaler value is 40e6 / config.Frequency, but rounded up so
// that the actual frequency is never higher than the frequency
// requested in config.Frequency.
var (
pre uint32 = (40e6 + config.Frequency - 1) / config.Frequency
n uint32 = 2 // this value seems to equal the number of ticks per SPI clock tick
h uint32 = 1 // must be half of n according to the formula in the reference manual
l uint32 = n // must equal n according to the reference manual
)
clockReg |= (pre - 1) << esp.SPI_CLOCK_CLKDIV_PRE_Pos
clockReg |= (n - 1) << esp.SPI_CLOCK_CLKCNT_N_Pos
clockReg |= (h - 1) << esp.SPI_CLOCK_CLKCNT_H_Pos
clockReg |= (l - 1) << esp.SPI_CLOCK_CLKCNT_L_Pos
}
spi.Bus.CLOCK.Set(clockReg)
// SPI_CTRL_REG controls bit order.
var ctrlReg uint32
if config.LSBFirst {
ctrlReg |= esp.SPI_CTRL_WR_BIT_ORDER
ctrlReg |= esp.SPI_CTRL_RD_BIT_ORDER
}
spi.Bus.CTRL.Set(ctrlReg)
// SPI_CTRL2_REG, SPI_USER_REG and SPI_PIN_REG control SPI clock polarity
// (mode), among others.
var ctrl2Reg, userReg, pinReg uint32
// For mode configuration, see table 29 in the reference manual (page 128).
var delayMode uint32
switch config.Mode {
case 0:
delayMode = 2
case 1:
delayMode = 1
userReg |= esp.SPI_USER_CK_OUT_EDGE
case 2:
delayMode = 1
userReg |= esp.SPI_USER_CK_OUT_EDGE
pinReg |= esp.SPI_PIN_CK_IDLE_EDGE
case 3:
delayMode = 2
pinReg |= esp.SPI_PIN_CK_IDLE_EDGE
}
// Extra configuration necessary for correct data input at high frequencies.
// This is only necessary when MISO goes through the GPIO matrix (which it
// currently does).
if config.Frequency >= 40e6 {
// Delay mode must be set to 0 and SPI_USR_DUMMY_CYCLELEN should be set
// to 0 (the default).
userReg |= esp.SPI_USER_USR_DUMMY
} else if config.Frequency >= 20e6 {
// Nothing to do here, delay mode should be set to 0 according to the
// datasheet.
} else {
// Follow the delay mode as given in table 29 on page 128 of the
// reference manual.
// Note that this is only specified for SPI frequency of 10MHz and
// below (≤Fapb/8), so 13.3MHz appears to be left unspecified.
ctrl2Reg |= delayMode << esp.SPI_CTRL2_MOSI_DELAY_MODE_Pos
}
// Enable full-duplex communication.
userReg |= esp.SPI_USER_DOUTDIN
userReg |= esp.SPI_USER_USR_MOSI
// Write values to registers.
spi.Bus.CTRL2.Set(ctrl2Reg)
spi.Bus.USER.Set(userReg)
spi.Bus.PIN.Set(pinReg)
// Configure pins.
// TODO: use direct output if possible, if the configured pins match the
// possible direct configurations (e.g. for SPI2, when SCK is pin 14 etc).
if spi.Bus == esp.SPI2 {
config.SCK.configure(PinConfig{Mode: PinOutput}, 8) // HSPICLK
config.SDI.configure(PinConfig{Mode: PinInput}, 9) // HSPIQ
config.SDO.configure(PinConfig{Mode: PinOutput}, 10) // HSPID
} else if spi.Bus == esp.SPI3 {
config.SCK.configure(PinConfig{Mode: PinOutput}, 63) // VSPICLK
config.SDI.configure(PinConfig{Mode: PinInput}, 64) // VSPIQ
config.SDO.configure(PinConfig{Mode: PinOutput}, 65) // VSPID
} else {
// Don't know how to configure this bus.
return ErrInvalidSPIBus
}
return nil
}
// Transfer writes/reads a single byte using the SPI interface. If you need to
// transfer larger amounts of data, Tx will be faster.
func (spi SPI) Transfer(w byte) (byte, error) {
spi.Bus.MISO_DLEN.Set(7 << esp.SPI_MISO_DLEN_USR_MISO_DBITLEN_Pos)
spi.Bus.MOSI_DLEN.Set(7 << esp.SPI_MOSI_DLEN_USR_MOSI_DBITLEN_Pos)
spi.Bus.W0.Set(uint32(w))
// Send/receive byte.
spi.Bus.CMD.Set(esp.SPI_CMD_USR)
for spi.Bus.CMD.Get() != 0 {
}
// The received byte is stored in W0.
return byte(spi.Bus.W0.Get()), nil
}
// Tx handles read/write operation for SPI interface. Since SPI is a syncronous write/read
// interface, there must always be the same number of bytes written as bytes read.
// This is accomplished by sending zero bits if r is bigger than w or discarding
// the incoming data if w is bigger than r.
//
func (spi SPI) Tx(w, r []byte) error {
toTransfer := len(w)
if len(r) > toTransfer {
toTransfer = len(r)
}
for toTransfer != 0 {
// Do only 64 bytes at a time.
chunkSize := toTransfer
if chunkSize > 64 {
chunkSize = 64
}
// Fill tx buffer.
transferWords := (*[16]volatile.Register32)(unsafe.Pointer(uintptr(unsafe.Pointer(&spi.Bus.W0))))
var outBuf [16]uint32
txSize := 64
if txSize > len(w) {
txSize = len(w)
}
for i := 0; i < txSize; i++ {
outBuf[i/4] = outBuf[i/4] | uint32(w[i])<<((i%4)*8)
}
for i, word := range outBuf {
transferWords[i].Set(word)
}
// Do the transfer.
spi.Bus.MISO_DLEN.Set((uint32(chunkSize)*8 - 1) << esp.SPI_MISO_DLEN_USR_MISO_DBITLEN_Pos)
spi.Bus.MOSI_DLEN.Set((uint32(chunkSize)*8 - 1) << esp.SPI_MOSI_DLEN_USR_MOSI_DBITLEN_Pos)
spi.Bus.CMD.Set(esp.SPI_CMD_USR)
for spi.Bus.CMD.Get() != 0 {
}
// Read rx buffer.
rxSize := 64
if rxSize > len(r) {
rxSize = len(r)
}
for i := 0; i < rxSize; i++ {
r[i] = byte(transferWords[i/4].Get() >> ((i % 4) * 8))
}
// Cut off some part of the output buffer so the next iteration we will
// only send the remaining bytes.
if len(w) < chunkSize {
w = nil
} else {
w = w[chunkSize:]
}
if len(r) < chunkSize {
r = nil
} else {
r = r[chunkSize:]
}
toTransfer -= chunkSize
}
return nil
}
+159
View File
@@ -0,0 +1,159 @@
// +build esp8266
package machine
import (
"device/esp"
"runtime/volatile"
)
func CPUFrequency() uint32 {
return 80000000 // 80MHz
}
type PinMode uint8
const (
PinOutput PinMode = iota
PinInput
)
// Pins that are fixed by the chip.
const (
UART_TX_PIN Pin = 1
UART_RX_PIN Pin = 3
)
// Pin functions are not trivial. The below array maps a pin number (GPIO
// number) to the pad as used in the IO mux.
// Tables with the mapping:
// https://www.esp8266.com/wiki/doku.php?id=esp8266_gpio_pin_allocations#pin_functions
// https://www.espressif.com/sites/default/files/documentation/ESP8266_Pin_List_0.xls
var pinPadMapping = [...]uint8{
12: 0,
13: 1,
14: 2,
15: 3,
3: 4,
1: 5,
6: 6,
7: 7,
8: 8,
9: 9,
10: 10,
11: 11,
0: 12,
2: 13,
4: 14,
5: 15,
}
// getPad returns the pad number and the register to configure this pad.
func (p Pin) getPad() (uint8, *volatile.Register32) {
pad := pinPadMapping[p]
var reg *volatile.Register32
switch pad {
case 0:
reg = &esp.IO_MUX.IO_MUX_MTDI
case 1:
reg = &esp.IO_MUX.IO_MUX_MTCK
case 2:
reg = &esp.IO_MUX.IO_MUX_MTMS
case 3:
reg = &esp.IO_MUX.IO_MUX_MTDO
case 4:
reg = &esp.IO_MUX.IO_MUX_U0RXD
case 5:
reg = &esp.IO_MUX.IO_MUX_U0TXD
case 6:
reg = &esp.IO_MUX.IO_MUX_SD_CLK
case 7:
reg = &esp.IO_MUX.IO_MUX_SD_DATA0
case 8:
reg = &esp.IO_MUX.IO_MUX_SD_DATA1
case 9:
reg = &esp.IO_MUX.IO_MUX_SD_DATA2
case 10:
reg = &esp.IO_MUX.IO_MUX_SD_DATA3
case 11:
reg = &esp.IO_MUX.IO_MUX_SD_CMD
case 12:
reg = &esp.IO_MUX.IO_MUX_GPIO0
case 13:
reg = &esp.IO_MUX.IO_MUX_GPIO2
case 14:
reg = &esp.IO_MUX.IO_MUX_GPIO4
case 15:
reg = &esp.IO_MUX.IO_MUX_GPIO5
}
return pad, reg
}
// Configure sets the given pin as output or input pin.
func (p Pin) Configure(config PinConfig) {
switch config.Mode {
case PinInput, PinOutput:
pad, reg := p.getPad()
if pad >= 12 { // pin 0, 2, 4, 5
reg.Set(0 << 4) // function 0 at bit position 4
} else {
reg.Set(3 << 4) // function 3 at bit position 4
}
if config.Mode == PinOutput {
esp.GPIO.GPIO_ENABLE_W1TS.Set(1 << p)
} else {
esp.GPIO.GPIO_ENABLE_W1TC.Set(1 << p)
}
}
}
// Set sets the output value of this pin to high (true) or low (false).
func (p Pin) Set(value bool) {
if value {
esp.GPIO.GPIO_OUT_W1TS.Set(1 << p)
} else {
esp.GPIO.GPIO_OUT_W1TC.Set(1 << p)
}
}
// Return the register and mask to enable a given GPIO pin. This can be used to
// implement bit-banged drivers.
//
// Warning: only use this on an output pin!
func (p Pin) PortMaskSet() (*uint32, uint32) {
return &esp.GPIO.GPIO_OUT_W1TS.Reg, 1 << p
}
// Return the register and mask to disable a given GPIO pin. This can be used to
// implement bit-banged drivers.
//
// Warning: only use this on an output pin!
func (p Pin) PortMaskClear() (*uint32, uint32) {
return &esp.GPIO.GPIO_OUT_W1TC.Reg, 1 << p
}
// UART0 is a hardware UART that supports both TX and RX.
var UART0 = UART{Buffer: NewRingBuffer()}
type UART struct {
Buffer *RingBuffer
}
// Configure the UART baud rate. TX and RX pins are fixed by the hardware so
// cannot be modified and will be ignored.
func (uart UART) Configure(config UARTConfig) {
if config.BaudRate == 0 {
config.BaudRate = 115200
}
esp.UART0.UART_CLKDIV.Set(CPUFrequency() / config.BaudRate)
}
// WriteByte writes a single byte to the output buffer. Note that the hardware
// includes a buffer of 128 bytes which will be used first.
func (uart UART) WriteByte(c byte) error {
for (esp.UART0.UART_STATUS.Get()>>16)&0xff >= 128 {
// Wait until the TX buffer has room.
}
esp.UART0.UART_FIFO.Set(uint32(c))
return nil
}
+13
View File
@@ -4,6 +4,19 @@ package machine
// Peripheral abstraction layer for the stm32.
const (
portA Pin = iota * 16
portB
portC
portD
portE
portF
portG
portH
portI
portJ
)
type PinMode uint8
// Peripheral operations sequence:
+229
View File
@@ -0,0 +1,229 @@
// +build stm32f4
package machine
// Peripheral abstraction layer for the stm32f4
import (
"device/stm32"
"unsafe"
)
const (
PA0 = portA + 0
PA1 = portA + 1
PA2 = portA + 2
PA3 = portA + 3
PA4 = portA + 4
PA5 = portA + 5
PA6 = portA + 6
PA7 = portA + 7
PA8 = portA + 8
PA9 = portA + 9
PA10 = portA + 10
PA11 = portA + 11
PA12 = portA + 12
PA13 = portA + 13
PA14 = portA + 14
PA15 = portA + 15
PB0 = portB + 0
PB1 = portB + 1
PB2 = portB + 2
PB3 = portB + 3
PB4 = portB + 4
PB5 = portB + 5
PB6 = portB + 6
PB7 = portB + 7
PB8 = portB + 8
PB9 = portB + 9
PB10 = portB + 10
PB11 = portB + 11
PB12 = portB + 12
PB13 = portB + 13
PB14 = portB + 14
PB15 = portB + 15
PC0 = portC + 0
PC1 = portC + 1
PC2 = portC + 2
PC3 = portC + 3
PC4 = portC + 4
PC5 = portC + 5
PC6 = portC + 6
PC7 = portC + 7
PC8 = portC + 8
PC9 = portC + 9
PC10 = portC + 10
PC11 = portC + 11
PC12 = portC + 12
PC13 = portC + 13
PC14 = portC + 14
PC15 = portC + 15
PD0 = portD + 0
PD1 = portD + 1
PD2 = portD + 2
PD3 = portD + 3
PD4 = portD + 4
PD5 = portD + 5
PD6 = portD + 6
PD7 = portD + 7
PD8 = portD + 8
PD9 = portD + 9
PD10 = portD + 10
PD11 = portD + 11
PD12 = portD + 12
PD13 = portD + 13
PD14 = portD + 14
PD15 = portD + 15
PE0 = portE + 0
PE1 = portE + 1
PE2 = portE + 2
PE3 = portE + 3
PE4 = portE + 4
PE5 = portE + 5
PE6 = portE + 6
PE7 = portE + 7
PE8 = portE + 8
PE9 = portE + 9
PE10 = portE + 10
PE11 = portE + 11
PE12 = portE + 12
PE13 = portE + 13
PE14 = portE + 14
PE15 = portE + 15
PH0 = portH + 0
PH1 = portH + 1
)
func (p Pin) getPort() *stm32.GPIO_Type {
switch p / 16 {
case 0:
return stm32.GPIOA
case 1:
return stm32.GPIOB
case 2:
return stm32.GPIOC
case 3:
return stm32.GPIOD
case 4:
return stm32.GPIOE
case 5:
return stm32.GPIOF
case 6:
return stm32.GPIOG
case 7:
return stm32.GPIOH
case 8:
return stm32.GPIOI
default:
panic("machine: unknown port")
}
}
// enableClock enables the clock for this desired GPIO port.
func (p Pin) enableClock() {
switch p / 16 {
case 0:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOAEN)
case 1:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOBEN)
case 2:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOCEN)
case 3:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIODEN)
case 4:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOEEN)
case 5:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOFEN)
case 6:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOGEN)
case 7:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOHEN)
case 8:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOIEN)
default:
panic("machine: unknown port")
}
}
// Enable peripheral clock
func enableAltFuncClock(bus unsafe.Pointer) {
switch bus {
case unsafe.Pointer(stm32.DAC): // DAC interface clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_DACEN)
case unsafe.Pointer(stm32.PWR): // Power interface clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN)
case unsafe.Pointer(stm32.CAN2): // CAN 2 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_CAN2EN)
case unsafe.Pointer(stm32.CAN1): // CAN 1 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_CAN1EN)
case unsafe.Pointer(stm32.I2C3): // I2C3 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C3EN)
case unsafe.Pointer(stm32.I2C2): // I2C2 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C2EN)
case unsafe.Pointer(stm32.I2C1): // I2C1 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN)
case unsafe.Pointer(stm32.UART5): // UART5 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_UART5EN)
case unsafe.Pointer(stm32.UART4): // UART4 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_UART4EN)
case unsafe.Pointer(stm32.USART3): // USART3 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART3EN)
case unsafe.Pointer(stm32.USART2): // USART2 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN)
case unsafe.Pointer(stm32.SPI3): // SPI3 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_SPI3EN)
case unsafe.Pointer(stm32.SPI2): // SPI2 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_SPI2EN)
case unsafe.Pointer(stm32.WWDG): // Window watchdog clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_WWDGEN)
case unsafe.Pointer(stm32.TIM14): // TIM14 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM14EN)
case unsafe.Pointer(stm32.TIM13): // TIM13 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM13EN)
case unsafe.Pointer(stm32.TIM12): // TIM12 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM12EN)
case unsafe.Pointer(stm32.TIM7): // TIM7 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM7EN)
case unsafe.Pointer(stm32.TIM6): // TIM6 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM6EN)
case unsafe.Pointer(stm32.TIM5): // TIM5 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM5EN)
case unsafe.Pointer(stm32.TIM4): // TIM4 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM4EN)
case unsafe.Pointer(stm32.TIM3): // TIM3 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
case unsafe.Pointer(stm32.TIM2): // TIM2 clock enable
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM2EN)
case unsafe.Pointer(stm32.TIM11): // TIM11 clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM11EN)
case unsafe.Pointer(stm32.TIM10): // TIM10 clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM10EN)
case unsafe.Pointer(stm32.TIM9): // TIM9 clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM9EN)
case unsafe.Pointer(stm32.SYSCFG): // System configuration controller clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SYSCFGEN)
case unsafe.Pointer(stm32.SPI1): // SPI1 clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
case unsafe.Pointer(stm32.SDIO): // SDIO clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SDIOEN)
case unsafe.Pointer(stm32.ADC3): // ADC3 clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADC3EN)
case unsafe.Pointer(stm32.ADC2): // ADC2 clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADC2EN)
case unsafe.Pointer(stm32.ADC1): // ADC1 clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADC1EN)
case unsafe.Pointer(stm32.USART6): // USART6 clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART6EN)
case unsafe.Pointer(stm32.USART1): // USART1 clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
case unsafe.Pointer(stm32.TIM8): // TIM8 clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM8EN)
case unsafe.Pointer(stm32.TIM1): // TIM1 clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM1EN)
}
}
+57
View File
@@ -0,0 +1,57 @@
// +build stm32f405
package machine
// Peripheral abstraction layer for the stm32f405
import (
"device/stm32"
"runtime/interrupt"
)
func CPUFrequency() uint32 {
return 168000000
}
// -- UART ---------------------------------------------------------------------
type UART struct {
Buffer *RingBuffer
Bus *stm32.USART_Type
Interrupt interrupt.Interrupt
AltFuncSelector stm32.AltFunc
}
func (uart UART) configurePins(config UARTConfig) {
// enable the alternate functions on the TX and RX pins
config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.AltFuncSelector)
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.AltFuncSelector)
}
func (uart UART) getBaudRateDivisor(baudRate uint32) uint32 {
var clock uint32
switch uart.Bus {
case stm32.USART1, stm32.USART6:
clock = CPUFrequency() / 2 // APB2 Frequency
case stm32.USART2, stm32.USART3, stm32.UART4, stm32.UART5:
clock = CPUFrequency() / 4 // APB1 Frequency
}
return clock / baudRate
}
// -- SPI ----------------------------------------------------------------------
type SPI struct {
Bus *stm32.SPI_Type
AltFuncSelector stm32.AltFunc
}
func (spi SPI) configurePins(config SPIConfig) {}
func (spi SPI) getBaudRate(config SPIConfig) uint32 { return 0 }
// -- I2C ----------------------------------------------------------------------
type I2C struct {
Bus *stm32.I2C_Type
AltFuncSelector stm32.AltFunc
}
+2 -68
View File
@@ -1,84 +1,18 @@
// +build stm32,stm32f407
// +build stm32f407
package machine
// Peripheral abstraction layer for the stm32f4(07)
// Peripheral abstraction layer for the stm32f407
import (
"device/stm32"
"runtime/interrupt"
"unsafe"
)
func CPUFrequency() uint32 {
return 168000000
}
func (p Pin) getPort() *stm32.GPIO_Type {
switch p / 16 {
case 0:
return stm32.GPIOA
case 1:
return stm32.GPIOB
case 2:
return stm32.GPIOC
case 3:
return stm32.GPIOD
case 4:
return stm32.GPIOE
case 5:
return stm32.GPIOF
case 6:
return stm32.GPIOG
case 7:
return stm32.GPIOH
case 8:
return stm32.GPIOI
default:
panic("machine: unknown port")
}
}
// enableClock enables the clock for this desired GPIO port.
func (p Pin) enableClock() {
switch p / 16 {
case 0:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOAEN)
case 1:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOBEN)
case 2:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOCEN)
case 3:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIODEN)
case 4:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOEEN)
case 5:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOFEN)
case 6:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOGEN)
case 7:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOHEN)
case 8:
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOIEN)
default:
panic("machine: unknown port")
}
}
// Enable peripheral clock
func enableAltFuncClock(bus unsafe.Pointer) {
switch bus {
case unsafe.Pointer(stm32.USART1):
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
case unsafe.Pointer(stm32.USART2):
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN)
case unsafe.Pointer(stm32.I2C1):
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN)
case unsafe.Pointer(stm32.SPI1):
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
}
}
//---------- UART related types and code
// UART representation
+1 -1
View File
@@ -1,4 +1,4 @@
// +build avr nrf sam sifive stm32 k210 nxp
// +build avr esp nrf sam sifive stm32 k210 nxp
package machine
+5 -2
View File
@@ -87,9 +87,12 @@ func exitCriticalSection() {
// Configure the USB CDC interface. The config is here for compatibility with the UART interface.
func (usbcdc *USBCDC) Configure(config UARTConfig) {
// enable IRQ
// Enable IRQ. Make sure this is higher than the SWI2 interrupt handler so
// that it is possible to print to the console from a BLE interrupt. You
// shouldn't generally do that but it is useful for debugging and panic
// logging.
usbcdc.interrupt = interrupt.New(nrf.IRQ_USBD, USB.handleInterrupt)
usbcdc.interrupt.SetPriority(0xD0)
usbcdc.interrupt.SetPriority(0x40) // interrupt priority 2 (lower number means more important)
usbcdc.interrupt.Enable()
// enable USB
-4
View File
@@ -102,7 +102,3 @@ func procPin() {
func procUnpin() {
arm.EnableInterrupts(procPinnedMask)
}
func waitForEvents() {
arm.Asm("wfe")
}
+15
View File
@@ -0,0 +1,15 @@
// +build xtensa
package runtime
const GOARCH = "arm" // xtensa pretends to be arm
// The bitness of the CPU (e.g. 8, 32, 64).
const TargetBits = 32
// Align on a word boundary.
func align(ptr uintptr) uintptr {
return (ptr + 3) &^ 3
}
func getCurrentStackPointer() uintptr
+71
View File
@@ -0,0 +1,71 @@
package runtime
import (
"unsafe"
)
const debugLoader = false
const (
rAARCH64_RELATIVE = 1027
dtNULL = 0 /* Terminating entry. */
dtRELA = 7 /* Address of ElfNN_Rela relocations. */
dtRELASZ = 8 /* Total size of ElfNN_Rela relocations. */
)
/* ELF64 relocations that need an addend field. */
type rela64 struct {
Off uint64 /* Location to be relocated. */
Info uint64 /* Relocation type and symbol index. */
Addend int64 /* Addend. */
}
// ELF64 Dynamic structure. The ".dynamic" section contains an array of them.
type dyn64 struct {
Tag int64 /* Entry type. */
Val uint64 /* Integer/address value */
}
//export __dynamic_loader
func dynamicLoader(base uintptr, dyn *dyn64) {
var rela *rela64
relasz := uint64(0)
if debugLoader {
println("ASLR Base: ", base)
}
for dyn.Tag != dtNULL {
switch dyn.Tag {
case dtRELA:
rela = (*rela64)(unsafe.Pointer(base + uintptr(dyn.Val)))
case dtRELASZ:
relasz = uint64(dyn.Val) / uint64(unsafe.Sizeof(rela64{}))
}
ptr := uintptr(unsafe.Pointer(dyn))
ptr += unsafe.Sizeof(dyn64{})
dyn = (*dyn64)(unsafe.Pointer(ptr))
}
if rela == nil {
runtimePanic("bad reloc")
}
if debugLoader {
println("Sections to load: ", relasz)
}
for relasz > 0 && rela != nil {
switch rela.Info {
case rAARCH64_RELATIVE:
ptr := (*uint64)(unsafe.Pointer(base + uintptr(rela.Off)))
*ptr = uint64(base + uintptr(rela.Addend))
}
rptr := uintptr(unsafe.Pointer(rela))
rptr += unsafe.Sizeof(rela64{})
rela = (*rela64)(unsafe.Pointer(rptr))
relasz--
}
}
+31
View File
@@ -0,0 +1,31 @@
// +build xtensa
package interrupt
import "device"
// State represents the previous global interrupt state.
type State uintptr
// Disable disables all interrupts and returns the previous interrupt state. It
// can be used in a critical section like this:
//
// state := interrupt.Disable()
// // critical section
// interrupt.Restore(state)
//
// Critical sections can be nested. Make sure to call Restore in the same order
// as you called Disable (this happens naturally with the pattern above).
func Disable() (state State) {
return State(device.AsmFull("rsil {}, 15", nil))
}
// Restore restores interrupts to what they were before. Give the previous state
// returned by Disable as a parameter. If interrupts were disabled before
// calling Disable, this will not re-enable interrupts, allowing for nested
// cricital sections.
func Restore(state State) {
device.AsmFull("wsr {state}, PS", map[string]interface{}{
"state": state,
})
}
+29
View File
@@ -0,0 +1,29 @@
package pprof
// TinyGo does not implement pprof. However, a dummy shell is needed for the
// testing package (and testing/internal/pprof).
import (
"errors"
"io"
)
var ErrUnimplemented = errors.New("runtime/pprof: unimplemented")
type Profile struct {
}
func StartCPUProfile(w io.Writer) error {
return nil
}
func StopCPUProfile() {
}
func Lookup(name string) *Profile {
return nil
}
func (p *Profile) WriteTo(w io.Writer, debug int) error {
return ErrUnimplemented
}
+5 -1
View File
@@ -308,7 +308,7 @@ func timerSleep(ticks uint32) bool {
sam.RTC_MODE0.INTENSET.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
wait:
arm.Asm("wfe")
waitForEvents()
if timerWakeup.Get() != 0 {
return true
}
@@ -345,3 +345,7 @@ func initADCClock() {
sam.GCLK_CLKCTRL_CLKEN)
waitForSync()
}
func waitForEvents() {
arm.Asm("wfe")
}
+8 -4
View File
@@ -292,7 +292,7 @@ func timerSleep(ticks uint32) bool {
sam.RTC_MODE0.INTENSET.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
wait:
arm.Asm("wfe")
waitForEvents()
if timerWakeup.Get() != 0 {
return true
}
@@ -317,7 +317,7 @@ func initUSBClock() {
// Put Generic Clock Generator 1 as source for USB
//GCLK->PCHCTRL[USB_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
sam.GCLK.PCHCTRL[10].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_USB].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
}
@@ -327,8 +327,12 @@ func initADCClock() {
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_ADC1_)
// Put Generic Clock Generator 1 as source for ADC0 and ADC1.
sam.GCLK.PCHCTRL[40].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_ADC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
sam.GCLK.PCHCTRL[41].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_ADC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
}
func waitForEvents() {
arm.Asm("wfe")
}
+7 -7
View File
@@ -9,35 +9,35 @@ import (
func initSERCOMClocks() {
// Turn on clock to SERCOM0 for UART0
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
sam.GCLK.PCHCTRL[7].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM0_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// sets the "slow" clock shared by all SERCOM
sam.GCLK.PCHCTRL[3].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOMX_SLOW].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM1
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
sam.GCLK.PCHCTRL[8].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM1_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM2
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
sam.GCLK.PCHCTRL[23].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM2_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM3
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
sam.GCLK.PCHCTRL[24].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM3_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM4
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
sam.GCLK.PCHCTRL[34].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM4_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM5
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
sam.GCLK.PCHCTRL[35].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM5_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
}
+7 -7
View File
@@ -9,35 +9,35 @@ import (
func initSERCOMClocks() {
// Turn on clock to SERCOM0 for UART0
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
sam.GCLK.PCHCTRL[7].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM0_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// sets the "slow" clock shared by all SERCOM
sam.GCLK.PCHCTRL[3].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOMX_SLOW].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM1
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
sam.GCLK.PCHCTRL[8].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM1_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM2
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
sam.GCLK.PCHCTRL[23].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM2_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM3
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
sam.GCLK.PCHCTRL[24].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM3_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM4
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
sam.GCLK.PCHCTRL[34].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM4_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM5
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
sam.GCLK.PCHCTRL[35].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM5_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
}
+7 -7
View File
@@ -9,35 +9,35 @@ import (
func initSERCOMClocks() {
// Turn on clock to SERCOM0 for UART0
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
sam.GCLK.PCHCTRL[7].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM0_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// sets the "slow" clock shared by all SERCOM
sam.GCLK.PCHCTRL[3].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOMX_SLOW].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM1
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
sam.GCLK.PCHCTRL[8].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM1_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM2
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
sam.GCLK.PCHCTRL[23].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM2_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM3
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
sam.GCLK.PCHCTRL[24].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM3_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM4
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
sam.GCLK.PCHCTRL[34].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM4_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM5
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
sam.GCLK.PCHCTRL[35].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM5_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
}
+9 -9
View File
@@ -9,45 +9,45 @@ import (
func initSERCOMClocks() {
// Turn on clock to SERCOM0 for UART0
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
sam.GCLK.PCHCTRL[7].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM0_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// sets the "slow" clock shared by all SERCOM
sam.GCLK.PCHCTRL[3].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOMX_SLOW].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM1
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
sam.GCLK.PCHCTRL[8].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM1_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM2
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
sam.GCLK.PCHCTRL[23].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM2_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM3
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
sam.GCLK.PCHCTRL[24].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM3_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM4
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
sam.GCLK.PCHCTRL[34].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM4_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM5
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
sam.GCLK.PCHCTRL[35].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM5_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM6
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM6_)
sam.GCLK.PCHCTRL[36].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM6_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM7
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM7_)
sam.GCLK.PCHCTRL[37].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM7_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
}
+4
View File
@@ -50,3 +50,7 @@ var stdoutWrite = (*volatile.Register8)(unsafe.Pointer(uintptr(0x4000c000)))
func putchar(c byte) {
stdoutWrite.Set(uint8(c))
}
func waitForEvents() {
arm.Asm("wfe")
}
+126
View File
@@ -0,0 +1,126 @@
// +build esp32
package runtime
import (
"device"
"device/esp"
"machine"
"unsafe"
)
type timeUnit int64
var currentTime timeUnit
func putchar(c byte) {
machine.UART0.WriteByte(c)
}
func postinit() {}
// This is the function called on startup right after the stack pointer has been
// set.
//export main
func main() {
// Disable both watchdog timers that are enabled by default on startup.
// Note that these watchdogs can be protected, but the ROM bootloader
// doesn't seem to protect them.
esp.RTCCNTL.WDTCONFIG0.Set(0)
esp.TIMG0.WDTCONFIG0.Set(0)
// Switch SoC clock source to PLL (instead of the default which is XTAL).
// This switches the CPU (and APB) clock from 40MHz to 80MHz.
// Options:
// RTCCNTL_CLK_CONF_SOC_CLK_SEL: PLL (default XTAL)
// RTCCNTL_CLK_CONF_CK8M_DIV_SEL: 2 (default)
// RTCCNTL_CLK_CONF_DIG_CLK8M_D256_EN: Enable (default)
// RTCCNTL_CLK_CONF_CK8M_DIV: DIV256 (default)
// The only real change made here is modifying RTCCNTL_CLK_CONF_SOC_CLK_SEL,
// but setting a fixed value produces smaller code.
esp.RTCCNTL.CLK_CONF.Set((esp.RTCCNTL_CLK_CONF_SOC_CLK_SEL_PLL << esp.RTCCNTL_CLK_CONF_SOC_CLK_SEL_Pos) |
(2 << esp.RTCCNTL_CLK_CONF_CK8M_DIV_SEL_Pos) |
(esp.RTCCNTL_CLK_CONF_DIG_CLK8M_D256_EN_Enable << esp.RTCCNTL_CLK_CONF_DIG_CLK8M_D256_EN_Pos) |
(esp.RTCCNTL_CLK_CONF_CK8M_DIV_DIV256 << esp.RTCCNTL_CLK_CONF_CK8M_DIV_Pos))
// Switch CPU from 80MHz to 160MHz. This doesn't affect the APB clock,
// which is still running at 80MHz.
esp.DPORT.CPU_PER_CONF.Set(esp.DPORT_CPU_PER_CONF_CPUPERIOD_SEL_SEL_160)
// Clear .bss section. .data has already been loaded by the ROM bootloader.
// Do this after increasing the CPU clock to possibly make startup slightly
// faster.
preinit()
// Initialize UART.
machine.UART0.Configure(machine.UARTConfig{})
// Configure timer 0 in timer group 0, for timekeeping.
// EN: Enable the timer.
// INCREASE: Count up every tick (as opposed to counting down).
// DIVIDER: 16-bit prescaler, set to 2 for dividing the APB clock by two
// (40MHz).
esp.TIMG0.T0CONFIG.Set(esp.TIMG_T0CONFIG_T0_EN | esp.TIMG_T0CONFIG_T0_INCREASE | 2<<esp.TIMG_T0CONFIG_T0_DIVIDER_Pos)
// Set the timer counter value to 0.
esp.TIMG0.T0LOADLO.Set(0)
esp.TIMG0.T0LOADHI.Set(0)
esp.TIMG0.T0LOAD.Set(0) // value doesn't matter.
run()
// Fallback: if main ever returns, hang the CPU.
abort()
}
//go:extern _sbss
var _sbss [0]byte
//go:extern _ebss
var _ebss [0]byte
func preinit() {
// Initialize .bss: zero-initialized global variables.
// The .data section has already been loaded by the ROM bootloader.
ptr := unsafe.Pointer(&_sbss)
for ptr != unsafe.Pointer(&_ebss) {
*(*uint32)(ptr) = 0
ptr = unsafe.Pointer(uintptr(ptr) + 4)
}
}
func ticks() timeUnit {
// First, update the LO and HI register pair by writing any value to the
// register. This allows reading the pair atomically.
esp.TIMG0.T0UPDATE.Set(0)
// Then read the two 32-bit parts of the timer.
return timeUnit(uint64(esp.TIMG0.T0LO.Get()) | uint64(esp.TIMG0.T0HI.Get())<<32)
}
const asyncScheduler = false
func nanosecondsToTicks(ns int64) timeUnit {
// Calculate the number of ticks from the number of nanoseconds. At a 80MHz
// APB clock, that's 25 nanoseconds per tick with a timer prescaler of 2:
// 25 = 1e9 / (80MHz / 2)
return timeUnit(ns / 25)
}
func ticksToNanoseconds(ticks timeUnit) int64 {
// See nanosecondsToTicks.
return int64(ticks) * 25
}
// sleepTicks busy-waits until the given number of ticks have passed.
func sleepTicks(d timeUnit) {
sleepUntil := ticks() + d
for ticks() < sleepUntil {
// TODO: suspend the CPU to not burn power here unnecessarily.
}
}
func abort() {
for {
device.Asm("waiti 0")
}
}
+115
View File
@@ -0,0 +1,115 @@
// +build esp8266
package runtime
import (
"device"
"device/esp"
"machine"
"unsafe"
)
type timeUnit int64
var currentTime timeUnit = 0
func putchar(c byte) {
machine.UART0.WriteByte(c)
}
// Write to the internal control bus (using I2C?).
// Signature found here:
// https://github.com/espressif/ESP8266_RTOS_SDK/blob/14171de0/components/esp8266/include/esp8266/rom_functions.h#L54
//export rom_i2c_writeReg
func rom_i2c_writeReg(block, host_id, reg_add, data uint8)
func postinit() {}
//export main
func main() {
// Clear .bss section. .data has already been loaded by the ROM bootloader.
preinit()
// Initialize PLL.
// I'm not quite sure what this magic incantation means, but it does set the
// esp8266 to the right clock speed. Without this, it is running too slow.
rom_i2c_writeReg(103, 4, 1, 136)
rom_i2c_writeReg(103, 4, 2, 145)
// Initialize UART.
machine.UART0.Configure(machine.UARTConfig{})
// Initialize timer. Bits:
// ENABLE: timer enable
// ROLLOVER: automatically reload when hitting 0
// PRESCALE: divide by 256
esp.TIMER.FRC1_CTRL.Set(
esp.TIMER_FRC1_CTRL_TIMER_ENABLE | esp.TIMER_FRC1_CTRL_ROLLOVER | esp.TIMER_FRC1_CTRL_PRESCALE_DIVIDER_DEVIDED_BY_256<<esp.TIMER_FRC1_CTRL_PRESCALE_DIVIDER_Pos)
esp.TIMER.FRC1_LOAD.Set(0x3fffff) // set all 22 bits to 1
esp.TIMER.FRC1_COUNT.Set(0x3fffff) // set all 22 bits to 1
run()
// Fallback: if main ever returns, hang the CPU.
abort()
}
//go:extern _sbss
var _sbss [0]byte
//go:extern _ebss
var _ebss [0]byte
func preinit() {
// Initialize .bss: zero-initialized global variables.
ptr := unsafe.Pointer(&_sbss)
for ptr != unsafe.Pointer(&_ebss) {
*(*uint32)(ptr) = 0
ptr = unsafe.Pointer(uintptr(ptr) + 4)
}
}
func ticks() timeUnit {
// Get the counter value of the timer. It is 22 bits and starts with all
// ones (0x3fffff). To make it easier to work with, let it count upwards.
count := 0x3fffff - esp.TIMER.FRC1_COUNT.Get()
// Replace the lowest 22 bits of the current time with the counter.
newTime := (currentTime &^ 0x3fffff) | timeUnit(count)
// If there was an overflow, the new time will be lower than the current
// time, so will need to add (1<<22).
if newTime < currentTime {
newTime += 0x400000
}
// Update the timestamp for the next call to ticks().
currentTime = newTime
return currentTime
}
const asyncScheduler = false
const tickNanos = 3200 // time.Second / (80MHz / 256)
func ticksToNanoseconds(ticks timeUnit) int64 {
return int64(ticks) * tickNanos
}
func nanosecondsToTicks(ns int64) timeUnit {
return timeUnit(ns / tickNanos)
}
// sleepTicks busy-waits until the given number of ticks have passed.
func sleepTicks(d timeUnit) {
sleepUntil := ticks() + d
for ticks() < sleepUntil {
}
}
func abort() {
for {
device.Asm("waiti 0")
}
}
+19 -10
View File
@@ -2,6 +2,8 @@
package runtime
import "unsafe"
type timeUnit int64
const asyncScheduler = false
@@ -40,21 +42,18 @@ func ticks() timeUnit {
return timeUnit(ticksToNanoseconds(timeUnit(getArmSystemTick())))
}
var stdoutBuffer = make([]byte, 0, 120)
var stdoutBuffer = make([]byte, 120)
var position = 0
func putchar(c byte) {
if c == '\n' || len(stdoutBuffer)+1 >= 120 {
NxOutputString(string(stdoutBuffer))
stdoutBuffer = stdoutBuffer[:0]
if c == '\n' || position > len(stdoutBuffer) {
nxOutputString(&stdoutBuffer[0], uint64(position))
position = 0
return
}
stdoutBuffer = append(stdoutBuffer, c)
}
func usleep(usec uint) int {
sleepThread(uint64(usec) * 1000)
return 0
stdoutBuffer[position] = c
position++
}
func abort() {
@@ -63,6 +62,16 @@ func abort() {
}
}
//export write
func write(fd int32, buf *byte, count int) int {
// TODO: Proper handling write
for i := 0; i < count; i++ {
putchar(*buf)
buf = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(buf)) + 1))
}
return count
}
//export sleepThread
func sleepThread(nanos uint64)
+1 -7
View File
@@ -4,17 +4,11 @@
package runtime
import "unsafe"
const heapSize = 0x2000000 * 16 // Default by libnx
//go:extern _stack_top
var stackTopSymbol [0]byte
var (
heapStart = uintptr(0)
heapEnd = uintptr(0)
stackTop = uintptr(unsafe.Pointer(&stackTopSymbol))
)
//export setHeapSize
@@ -24,7 +18,7 @@ func preinit() {
setHeapSize(&heapStart, heapSize)
if heapStart == 0 {
panic("failed to allocate heap")
runtimePanic("failed to allocate heap")
}
heapEnd = heapStart + heapSize
+1 -2
View File
@@ -3,7 +3,6 @@
package runtime
import (
"device/arm"
"device/nrf"
"machine"
"runtime/interrupt"
@@ -114,6 +113,6 @@ func rtc_sleep(ticks uint32) {
}
nrf.RTC1.CC[0].Set((nrf.RTC1.COUNTER.Get() + ticks) & 0x00ffffff)
for rtc_wakeup.Get() == 0 {
arm.Asm("wfi")
waitForEvents()
}
}
+9
View File
@@ -0,0 +1,9 @@
// +build nrf,!softdevice
package runtime
import "device/arm"
func waitForEvents() {
arm.Asm("wfe")
}
+40
View File
@@ -0,0 +1,40 @@
// +build nrf,softdevice
package runtime
import (
"device/arm"
"device/nrf"
)
//export sd_app_evt_wait
func sd_app_evt_wait()
func waitForEvents() {
// Call into the SoftDevice to sleep. This is necessary here because a
// normal wfe will not put the chip in low power mode (it still consumes
// 500µA-1mA). It is really needed to call sd_app_evt_wait for low power
// consumption.
// First check whether the SoftDevice is enabled. Unfortunately,
// sd_app_evt_wait cannot be called when the SoftDevice is not enabled.
var enabled uint8
arm.SVCall1(0x12, &enabled) // sd_softdevice_is_enabled
if enabled != 0 {
// Now pick the appropriate SVCall number. Hopefully they won't change
// in the future with a different SoftDevice version.
if nrf.DEVICE == "nrf51" {
// sd_app_evt_wait: SOC_SVC_BASE_NOT_AVAILABLE + 29
arm.SVCall0(0x2B + 29)
} else if nrf.DEVICE == "nrf52" || nrf.DEVICE == "nrf52840" {
// sd_app_evt_wait: SOC_SVC_BASE_NOT_AVAILABLE + 21
arm.SVCall0(0x2C + 21)
} else {
sd_app_evt_wait()
}
} else {
// SoftDevice is disabled so we can sleep normally.
arm.Asm("wfe")
}
}
+4
View File
@@ -265,3 +265,7 @@ func abort() {
machine.PollUART(&machine.UART2)
}
}
func waitForEvents() {
arm.Asm("wfe")
}
+6
View File
@@ -2,6 +2,8 @@
package runtime
import "device/arm"
type timeUnit int64
func postinit() {}
@@ -12,3 +14,7 @@ func main() {
run()
abort()
}
func waitForEvents() {
arm.Asm("wfe")
}
+248
View File
@@ -0,0 +1,248 @@
// +build stm32f405
package runtime
import (
"device/arm"
"device/stm32"
"machine"
"runtime/interrupt"
"runtime/volatile"
)
func init() {
initOSC() // configure oscillators
initCLK() // configure CPU, AHB, and APB bus clocks
initTIM() // configure timers
initCOM() // configure serial comm interfaces
}
const (
// +----------------------+
// | Clock Settings |
// +-------------+--------+
// | HSE | 12mhz |
// | SYSCLK | 168mhz |
// | HCLK | 168mhz |
// | APB1(PCLK1) | 42mhz |
// | APB2(PCLK2) | 84mhz |
// +-------------+--------+
HCLK_FREQ_HZ = 168000000
PCLK1_FREQ_HZ = HCLK_FREQ_HZ / 4
PCLK2_FREQ_HZ = HCLK_FREQ_HZ / 2
)
const (
PWR_SCALE1 = 1 << stm32.PWR_CSR_VOSRDY_Pos // max value of HCLK = 168 MHz
PWR_SCALE2 = 0 // max value of HCLK = 144 MHz
PLL_SRC_HSE = 1 << stm32.RCC_PLLCFGR_PLLSRC_Pos // use HSE for PLL and PLLI2S
PLL_SRC_HSI = 0 // use HSI for PLL and PLLI2S
PLL_DIV_M = 6 << stm32.RCC_PLLCFGR_PLLM0_Pos
PLL_MLT_N = 168 << stm32.RCC_PLLCFGR_PLLN0_Pos
PLL_DIV_P = ((2 >> 1) - 1) << stm32.RCC_PLLCFGR_PLLP0_Pos
PLL_DIV_Q = 7 << stm32.RCC_PLLCFGR_PLLQ0_Pos
SYSCLK_SRC_PLL = 2 << stm32.RCC_CFGR_SW0_Pos
SYSCLK_STAT_PLL = 2 << stm32.RCC_CFGR_SWS0_Pos
RCC_DIV_PCLK1 = 5 << stm32.RCC_CFGR_PPRE1_Pos // HCLK / 4
RCC_DIV_PCLK2 = 4 << stm32.RCC_CFGR_PPRE2_Pos // HCLK / 2
RCC_DIV_HCLK = 0 << stm32.RCC_CFGR_HPRE_Pos // SYSCLK / 1
CLK_CCM_RAM = 1 << 20
)
const (
// +-----------------------------------+
// | Voltage range = 2.7V - 3.6V |
// +----------------+------------------+
// | Wait states | System Bus |
// | (WS, LATENCY) | HCLK (MHz) |
// +----------------+------------------+
// | 0 WS, 1 cycle | 0 < HCLK ≤ 30 |
// | 1 WS, 2 cycles | 30 < HCLK ≤ 60 |
// | 2 WS, 3 cycles | 60 < HCLK ≤ 90 |
// | 3 WS, 4 cycles | 90 < HCLK ≤ 120 |
// | 4 WS, 5 cycles | 120 < HCLK ≤ 150 |
// | 5 WS, 6 cycles | 150 < HCLK ≤ 168 |
// +----------------+------------------+
FLASH_LATENCY = 5 << stm32.FLASH_ACR_LATENCY_Pos // 5 WS (6 CPU cycles)
// instruction cache, data cache, and prefetch
FLASH_OPTIONS = stm32.FLASH_ACR_ICEN | stm32.FLASH_ACR_DCEN | stm32.FLASH_ACR_PRFTEN
)
func initOSC() {
// enable voltage regulator
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN)
stm32.PWR.CR.SetBits(PWR_SCALE1)
// enable HSE
stm32.RCC.CR.Set(stm32.RCC_CR_HSEON)
for !stm32.RCC.CR.HasBits(stm32.RCC_CR_HSERDY) {
}
// Since the main-PLL configuration parameters cannot be changed once PLL is
// enabled, it is recommended to configure PLL before enabling it (selection
// of the HSI or HSE oscillator as PLL clock source, and configuration of
// division factors M, N, P, and Q).
// disable PLL and wait for it to reset
stm32.RCC.CR.ClearBits(stm32.RCC_CR_PLLON)
for stm32.RCC.CR.HasBits(stm32.RCC_CR_PLLRDY) {
}
// set HSE as PLL source and configure clock divisors
stm32.RCC.PLLCFGR.Set(PLL_SRC_HSE | PLL_DIV_M | PLL_MLT_N | PLL_DIV_P | PLL_DIV_Q)
// enable PLL and wait for it to sync
stm32.RCC.CR.SetBits(stm32.RCC_CR_PLLON)
for !stm32.RCC.CR.HasBits(stm32.RCC_CR_PLLRDY) {
}
}
func initCLK() {
// After reset, the CPU clock frequency is 16 MHz and 0 wait state (WS) is
// configured in the FLASH_ACR register.
//
// It is highly recommended to use the following software sequences to tune
// the number of wait states needed to access the Flash memory with the CPU
// frequency.
//
// 1. Program the new number of wait states to the LATENCY bits in the
// FLASH_ACR register
// 2. Check that the new number of wait states is taken into account to access
// the Flash memory by reading the FLASH_ACR register
// 3. Modify the CPU clock source by writing the SW bits in the RCC_CFGR
// register
// 4. If needed, modify the CPU clock prescaler by writing the HPRE bits in
// RCC_CFGR
// 5. Check that the new CPU clock source or/and the new CPU clock prescaler
// value is/are taken into account by reading the clock source status (SWS
// bits) or/and the AHB prescaler value (HPRE bits), respectively, in the
// RCC_CFGR register.
// configure instruction/data caching, prefetch, and flash access wait states
stm32.FLASH.ACR.Set(FLASH_OPTIONS | FLASH_LATENCY)
for !stm32.FLASH.ACR.HasBits(FLASH_LATENCY) { // verify new wait states
}
// After a system reset, the HSI oscillator is selected as the system clock.
// When a clock source is used directly or through PLL as the system clock, it
// is not possible to stop it.
//
// A switch from one clock source to another occurs only if the target clock
// source is ready (clock stable after startup delay or PLL locked). If a
// clock source that is not yet ready is selected, the switch occurs when the
// clock source is ready. Status bits in the RCC clock control register
// (RCC_CR) indicate which clock(s) is (are) ready and which clock is
// currently used as the system clock.
// set CPU clock source to PLL
stm32.RCC.CFGR.SetBits(SYSCLK_SRC_PLL)
// update PCKL1/2 and HCLK divisors
stm32.RCC.CFGR.SetBits(RCC_DIV_PCLK1 | RCC_DIV_PCLK2 | RCC_DIV_HCLK)
// verify system clock source is ready
for !stm32.RCC.CFGR.HasBits(SYSCLK_STAT_PLL) {
}
// enable the CCM RAM clock
stm32.RCC.AHB1ENR.SetBits(CLK_CCM_RAM)
}
func initTIM() {
// enable sleep counter (TIM3)
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
tim3 := interrupt.New(stm32.IRQ_TIM3, handleTIM3)
tim3.SetPriority(0xC3)
tim3.Enable()
// enable tick counter (TIM7)
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM7EN)
stm32.TIM7.PSC.Set((PCLK1_FREQ_HZ*2)/10000 - 1) // 84mhz to 10khz(0.1ms)
stm32.TIM7.ARR.Set(10 - 1) // interrupt per 1ms
stm32.TIM7.DIER.SetBits(stm32.TIM_DIER_UIE) // enable interrupt
stm32.TIM7.CR1.SetBits(stm32.TIM_CR1_CEN) // enable timer
tim7 := interrupt.New(stm32.IRQ_TIM7, handleTIM7)
tim7.SetPriority(0xC1)
tim7.Enable()
}
func initCOM() {
if machine.NUM_UART_INTERFACES > 0 {
machine.UART0.Configure(machine.UARTConfig{})
}
}
var (
// tick in milliseconds
tickCount timeUnit
timerWakeup volatile.Register8
)
const asyncScheduler = false
func ticksToNanoseconds(ticks timeUnit) int64 {
return int64(ticks) * 1000
}
func nanosecondsToTicks(ns int64) timeUnit {
return timeUnit(ns / 1000)
}
// sleepTicks should sleep for specific number of microseconds.
func sleepTicks(d timeUnit) {
timerSleep(uint32(d))
}
// number of ticks (microseconds) since start.
func ticks() timeUnit {
return tickCount * 1000 // milliseconds to microseconds
}
// ticks are in microseconds
func timerSleep(ticks uint32) {
timerWakeup.Set(0)
stm32.TIM3.PSC.Set((PCLK1_FREQ_HZ*2)/10000 - 1) // 8399
arr := (ticks / 100) - 1 // microseconds to 0.1 ms
if arr == 0 {
arr = 1 // avoid blocking
}
stm32.TIM3.ARR.Set(arr)
stm32.TIM3.DIER.SetBits(stm32.TIM_DIER_UIE) // enable interrupt
stm32.TIM3.CR1.SetBits(stm32.TIM_CR1_CEN) // enable the timer
// wait for timer
for timerWakeup.Get() == 0 {
arm.Asm("wfi")
}
}
func handleTIM3(interrupt.Interrupt) {
if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) {
stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN) // disable the timer
stm32.TIM3.SR.ClearBits(stm32.TIM_SR_UIF) // clear the update flag
timerWakeup.Set(1) // flag timer ISR
}
}
func handleTIM7(interrupt.Interrupt) {
if stm32.TIM7.SR.HasBits(stm32.TIM_SR_UIF) {
stm32.TIM7.SR.ClearBits(stm32.TIM_SR_UIF) // clear the update flag
tickCount++
}
}
func putchar(c byte) {
machine.UART0.WriteByte(c)
}
+4 -7
View File
@@ -41,11 +41,7 @@ tinygo_getSystemStackPointer:
.cfi_endproc
.size tinygo_getSystemStackPointer, .-tinygo_getSystemStackPointer
// switchToScheduler and switchToTask are also in the same section, to make sure
// relative branches work.
.section .text.tinygo_swapTask
.section .text.tinygo_switchToScheduler
.global tinygo_switchToScheduler
.type tinygo_switchToScheduler, %function
tinygo_switchToScheduler:
@@ -62,6 +58,7 @@ tinygo_switchToScheduler:
.cfi_endproc
.size tinygo_switchToScheduler, .-tinygo_switchToScheduler
.section .text.tinygo_switchToTask
.global tinygo_switchToTask
.type tinygo_switchToTask, %function
tinygo_switchToTask:
@@ -72,11 +69,11 @@ tinygo_switchToTask:
// and then we can invoke swapTask.
msr PSP, r0
// Continue executing in the swapTask function, which swaps the stack
// pointer.
b.n tinygo_swapTask
.cfi_endproc
.size tinygo_switchToTask, .-tinygo_switchToTask
.section .text.tinygo_swapTask
.global tinygo_swapTask
.type tinygo_swapTask, %function
tinygo_swapTask:
+5
View File
@@ -15,3 +15,8 @@ type B struct {
common
N int
}
type InternalBenchmark struct {
Name string
F func(b *B)
}
+18 -7
View File
@@ -154,18 +154,16 @@ func (c *common) Skipped() bool {
return c.skipped
}
// TestToCall is a reference to a test that should be called during a test suite run.
type TestToCall struct {
// Name of the test to call.
// InternalTest is a reference to a test that should be called during a test suite run.
type InternalTest struct {
Name string
// Function reference to the test.
Func func(*T)
F func(*T)
}
// M is a test suite.
type M struct {
// tests is a list of the test names to execute
Tests []TestToCall
Tests []InternalTest
}
// Run the test suite.
@@ -180,7 +178,7 @@ func (m *M) Run() int {
}
fmt.Printf("=== RUN %s\n", test.Name)
test.Func(t)
test.F(t)
if t.failed {
fmt.Printf("--- FAIL: %s\n", test.Name)
@@ -204,3 +202,16 @@ func (m *M) Run() int {
func TestMain(m *M) {
os.Exit(m.Run())
}
func MainStart(deps interface{}, tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M {
return &M{
Tests: tests,
}
}
type InternalExample struct {
Name string
F func()
Output string
Unordered bool
}
+10
View File
@@ -207,6 +207,16 @@ func (fi *frameInfo) exec(bytecode []byte) ([]frameInfoLine, error) {
switch lowBits {
case 0: // DW_CFA_nop
// no operation
case 0x02: // DW_CFA_advance_loc1
// Very similar to DW_CFA_advance_loc but allows for a slightly
// larger range.
offset, err := r.ReadByte()
if err != nil {
return nil, err
}
fi.loc += uint64(offset) * fi.cie.codeAlignmentFactor
entries = append(entries, fi.newLine())
// TODO: DW_CFA_advance_loc2 etc
case 0x07: // DW_CFA_undefined
// Marks a single register as undefined. This is used to stop
// unwinding in tinygo_startTask using:
+24 -5
View File
@@ -14,19 +14,38 @@ import (
// set to true to print information useful for debugging
const debugPrint = false
type sizeType uint8
// SizeType indicates whether a stack or frame size could be determined and if
// not, why.
type SizeType uint8
// Results after trying to determine the stack size of a function in the call
// graph. The goal is to find a maximum (bounded) stack size, but sometimes this
// is not possible for some reasons such as recursion or indirect calls.
const (
Undefined sizeType = iota // not yet calculated
Undefined SizeType = iota // not yet calculated
Unknown // child has unknown stack size
Bounded // stack size is fixed at compile time (no recursion etc)
Recursive
IndirectCall
)
func (s SizeType) String() string {
switch s {
case Undefined:
return "undefined"
case Unknown:
return "unknown"
case Bounded:
return "bounded"
case Recursive:
return "recursive"
case IndirectCall:
return "indirect call"
default:
return "<?>"
}
}
// CallNode is a node in the call graph (that is, a function). Because this is
// determined after linking, there may be multiple names for a single function
// (due to aliases). It is also possible multiple functions have the same name
@@ -37,9 +56,9 @@ type CallNode struct {
Size uint64 // symbol size, in bytes
Children []*CallNode // functions this function calls
FrameSize uint64 // frame size, if FrameSizeType is Bounded
FrameSizeType sizeType // can be Undefined or Bounded
FrameSizeType SizeType // can be Undefined or Bounded
stackSize uint64
stackSizeType sizeType
stackSizeType SizeType
missingFrameInfo *CallNode // the child function that is the cause for not being able to determine the stack size
}
@@ -236,7 +255,7 @@ func findSymbol(symbolList []*CallNode, address uint64) *CallNode {
// returns the maximum stack size, whether this size can be known at compile
// time and the call node responsible for failing to determine the maximum stack
// usage. The stack size is only valid if sizeType is Bounded.
func (node *CallNode) StackSize() (uint64, sizeType, *CallNode) {
func (node *CallNode) StackSize() (uint64, SizeType, *CallNode) {
if node.stackSizeType == Undefined {
node.determineStackSize(make(map[*CallNode]struct{}))
}
+1 -1
View File
@@ -4,5 +4,5 @@
"ldflags": [
"-Wl,--defsym=_bootloader_size=8192"
],
"flash-command":"avrdude -c wiring -b 115200 -p atmega2560 -P {port} -U flash:w:{hex} -v -D"
"flash-command":"avrdude -c wiring -b 115200 -p atmega2560 -P {port} -U flash:w:{hex}:i -v -D"
}
+2 -1
View File
@@ -5,5 +5,6 @@
"-Wl,--defsym=_bootloader_size=512",
"-Wl,--defsym=_stack_size=512"
],
"flash-command": "avrdude -c arduino -p atmega328p -b 57600 -P {port} -U flash:w:{hex}:i"
"flash-command": "avrdude -c arduino -p atmega328p -b 57600 -P {port} -U flash:w:{hex}:i",
"emulator": ["simavr", "-m", "atmega328p", "-f", "16000000"]
}
+6
View File
@@ -0,0 +1,6 @@
{
"inherits": ["atsamd21g18a"],
"build-tags": ["sam", "atsamd21g18a", "arduino_zero"],
"flash-command": "bossac -i -e -w -v -R -U --port={port} --offset=0x2000 {bin}",
"flash-1200-bps-reset": "true"
}
+2 -1
View File
@@ -5,5 +5,6 @@
"-Wl,--defsym=_bootloader_size=512",
"-Wl,--defsym=_stack_size=512"
],
"flash-command": "avrdude -c arduino -p atmega328p -P {port} -U flash:w:{hex}:i"
"flash-command": "avrdude -c arduino -p atmega328p -P {port} -U flash:w:{hex}:i",
"emulator": ["simavr", "-m", "atmega328p", "-f", "16000000"]
}
+5
View File
@@ -16,6 +16,11 @@ SECTIONS
. = ALIGN(4);
} >FLASH_TEXT
.tinygo_stacksizes :
{
*(.tinygo_stacksizes)
} > FLASH_TEXT
/* Put the stack at the bottom of RAM, so that the application will
* crash on stack overflow instead of silently corrupting memory.
* See: http://blog.japaric.io/stack-overflow-protection/ */

Some files were not shown because too many files have changed in this diff Show More