Compare commits

..

1 Commits

Author SHA1 Message Date
Nia 18c97e9b55 Revert "compiler: saturate float-to-int conversions"
This reverts commit a867b56e5f.
2021-01-16 14:27:35 -05:00
189 changed files with 3309 additions and 5694 deletions
+28 -23
View File
@@ -80,12 +80,12 @@ commands:
steps:
- restore_cache:
keys:
- wasi-libc-sysroot-v4
- wasi-libc-sysroot-v3
- run:
name: "Build wasi-libc"
command: make wasi-libc
- save_cache:
key: wasi-libc-sysroot-v4
key: wasi-libc-sysroot-v3
paths:
- lib/wasi-libc/sysroot
test-linux:
@@ -108,10 +108,10 @@ commands:
- run: go install -tags=llvm<<parameters.llvm>> .
- restore_cache:
keys:
- wasi-libc-sysroot-systemclang-v3
- wasi-libc-sysroot-systemclang-v2
- run: make wasi-libc
- save_cache:
key: wasi-libc-sysroot-systemclang-v3
key: wasi-libc-sysroot-systemclang-v2
paths:
- lib/wasi-libc/sysroot
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./compiler ./interp ./transform .
@@ -132,7 +132,6 @@ commands:
- run:
name: "Install apt dependencies"
command: |
sudo apt-get update
sudo apt-get install \
gcc-arm-linux-gnueabihf \
libc6-dev-armel-cross \
@@ -154,7 +153,7 @@ commands:
- llvm-source-linux
- restore_cache:
keys:
- llvm-build-11-linux-v2-assert
- llvm-build-11-linux-v1-assert
- run:
name: "Build LLVM"
command: |
@@ -169,7 +168,7 @@ commands:
make ASSERT=1 llvm-build
fi
- save_cache:
key: llvm-build-11-linux-v2-assert
key: llvm-build-11-linux-v1-assert
paths:
llvm-build
- run: make ASSERT=1
@@ -191,7 +190,6 @@ commands:
- run:
name: "Install apt dependencies"
command: |
sudo apt-get update
sudo apt-get install \
gcc-arm-linux-gnueabihf \
libc6-dev-armel-cross \
@@ -213,7 +211,7 @@ commands:
- llvm-source-linux
- restore_cache:
keys:
- llvm-build-11-linux-v2-noassert
- llvm-build-11-linux-v1-noassert
- run:
name: "Build LLVM"
command: |
@@ -228,7 +226,7 @@ commands:
make llvm-build
fi
- save_cache:
key: llvm-build-11-linux-v2-noassert
key: llvm-build-11-linux-v1-noassert
paths:
llvm-build
- build-wasi-libc
@@ -270,8 +268,8 @@ commands:
- run:
name: "Install dependencies"
command: |
curl https://dl.google.com/go/go1.16.darwin-amd64.tar.gz -o go1.16.darwin-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.16.darwin-amd64.tar.gz
curl https://dl.google.com/go/go1.15.5.darwin-amd64.tar.gz -o go1.15.5.darwin-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.15.5.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:
@@ -292,7 +290,7 @@ commands:
- llvm-project
- restore_cache:
keys:
- llvm-build-11-macos-v2
- llvm-build-11-macos-v1
- run:
name: "Build LLVM"
command: |
@@ -304,17 +302,17 @@ commands:
make llvm-build
fi
- save_cache:
key: llvm-build-11-macos-v2
key: llvm-build-11-macos-v1
paths:
llvm-build
- restore_cache:
keys:
- wasi-libc-sysroot-macos-v3
- wasi-libc-sysroot-macos-v2
- run:
name: "Build wasi-libc"
command: make wasi-libc
- save_cache:
key: wasi-libc-sysroot-macos-v3
key: wasi-libc-sysroot-macos-v2
paths:
- lib/wasi-libc/sysroot
- run:
@@ -342,6 +340,18 @@ commands:
- /go/pkg/mod
jobs:
test-llvm9-go111:
docker:
- image: circleci/golang:1.11-buster
steps:
- test-linux:
llvm: "9"
test-llvm10-go112:
docker:
- image: circleci/golang:1.12-buster
steps:
- test-linux:
llvm: "10"
test-llvm10-go113:
docker:
- image: circleci/golang:1.13-buster
@@ -360,12 +370,6 @@ jobs:
steps:
- test-linux:
llvm: "11"
test-llvm11-go116:
docker:
- image: circleci/golang:1.16-buster
steps:
- test-linux:
llvm: "11"
assert-test-linux:
docker:
- image: circleci/golang:1.14-stretch
@@ -387,10 +391,11 @@ jobs:
workflows:
test-all:
jobs:
- test-llvm9-go111
- test-llvm10-go112
- test-llvm10-go113
- test-llvm10-go114
- test-llvm11-go115
- test-llvm11-go116
- build-linux
- build-macos
- assert-test-linux
-7
View File
@@ -19,10 +19,3 @@ src/device/kendryte/*.s
vendor
llvm-build
llvm-project
# Ignore files generated by smoketest
test.gba
test.hex
test.nro
test.wasm
wasm.wasm
+6 -1
View File
@@ -23,7 +23,7 @@ different guide:
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
build tools to be built. Go is of course necessary to build TinyGo itself.
* Go (1.13+)
* Go (1.11+)
* Standard build tools (gcc/clang)
* git
* CMake
@@ -43,6 +43,11 @@ You can also store LLVM outside of the TinyGo root directory by setting the
`LLVM_BUILDDIR`, `CLANG_SRC` and `LLD_SRC` make variables, but that is not
covered by this guide.
TinyGo uses Go modules, so if you clone TinyGo inside your GOPATH (and are using
Go below 1.13), make sure that Go modules are enabled:
export GO111MODULE=on
## Build LLVM, Clang, LLD
Before starting the build, you may want to set the following environment
-90
View File
@@ -1,93 +1,3 @@
0.17.0
---
* **command line**
- switch to LLVM 11 for static builds
- support gdb debugging with AVR
- add support for additional openocd commands
- add `-x` flag to print commands
- use LLVM 11 by default when linking LLVM dynamically
- update go-llvm to use LLVM 11 on macOS
- bump go.bug.st/serial to version 1.1.2
- do not build LLVM with libxml to work around a bugo on macOS
- add support for Go 1.16
- support gdb daemonization on Windows
- remove support for LLVM 9, to fix CI
- kill OpenOCD if it does not exit with a regular quit signal
- support `-ocd-output` on Windows
* **compiler**
- `builder`: parallelize most of the build
- `builder`: remove unused cacheKey parameter
- `builder`: add -mcpu flag while building libraries
- `builder`: wait for running jobs to finish
- `cgo`: add support for variadic functions
- `compiler`: fix undefined behavior in wordpack
- `compiler`: fix incorrect "exported function" panic
- `compiler`: fix non-int integer constants (fixing a crash)
- `compiler`: refactor and add tests
- `compiler`: emit a nil check when slicing an array pointer
- `compiler`: saturate float-to-int conversions
- `compiler`: test float to int conversions and fix upper-bound calculation
- `compiler`: support all kinds of deferred builtins
- `compiler`: remove ir package
- `compiler`: remove unnecessary main.main call workaround
- `compiler`: move the setting of attributes to getFunction
- `compiler`: create runtime types lazily when needed
- `compiler`: move settings to a separate Config struct
- `compiler`: work around an ARM backend bug in LLVM
- `interp`: rewrite entire package
- `interp`: fix alignment of untyped globals
- `loader`: use name "main" for the main package
- `loader`: support imports from vendor directories
- `stacksize`: add support for DW_CFA_offset_extended
- `transform`: show better error message in coroutines lowering
* **standard library**
- `machine`: accept configuration struct for ADC parameters
- `machine`: make I2C.Configure signature consistent
- `reflect`: implement PtrTo
- `runtime`: refactor to simplify stack switching
- `runtime`: put metadata at the top end of the heap
* **targets**
- `atsam`: add a length check to findPinPadMapping
- `atsam`: improve USBCDC
- `atsam`: avoid infinite loop when USBCDC is disconnected
- `avr`: add SPI support for Atmega based chips
- `avr`: use Clang for compiling C and assembly files
- `esp32`: implement task based scheduler
- `esp32`: enable the FPU
- `esp8266`: implement task based scheduler
- `esp`: add compiler-rt library
- `esp`: add picolibc
- `nrf`: refactor code a bit to reduce duplication
- `nrf`: use SPIM peripheral instead of the legacy SPI peripheral
- `nrf`: update nrfx submodule to latest commit
- `nrf52840`: ensure that USB CDC interface is only initialized once
- `nrf52840`: improve USBCDC
- `stm32`: use stm32-rs SVDs which are of much higher quality
- `stm32`: harmonization of UART logic
- `stm32`: replace I2C addressable interface with simpler type
- `stm32`: fix i2c and add stm32f407 i2c
- `stm32`: revert change that adds support for channels in interrupts
- `wasm`: implement a growable heap
- `wasm`: fix typo in wasm_exec.js, syscall/js.valueLoadString()
- `wasm`: Namespaced Wasm Imports so they don't conflict across modules, or reserved LLVM IR
- `wasi`: support env variables based on libc
- `wasi`: specify wasi-libc in a different way, to improve error message
* **boards**
- `matrixportal-m4`: add support for board Adafruit Matrix Portal M4
- `mkr1000`: add this board
- `nucleo-f722ze`: add this board
- `clue`: correct volume name and add alias for release version of Adafruit Clue board
- `p1am-100`: add support for the P1AM-100 (similar to Arduino MKR)
- `microbit-v2`: add initial support based on work done by @alankrantas thank you!
- `lgt92`: support for STM32L0 MCUs and Dragino LGT92 device
- `nicenano`: nice!nano board support
- `circuitplay-bluefruit`: correct internal I2C pin mapping
- `clue`: correct for lack of low frequency crystal
- `digispark`: split off attiny85 target
- `nucleo-l552ze`: implementation with CLOCK, LED, and UART
- `nrf52840-mdk-usb-dongle`: add this board
0.16.0
---
+6 -6
View File
@@ -1,10 +1,10 @@
# TinyGo base stage installs the most recent Go 1.15.x, LLVM 11 and the TinyGo compiler itself.
# TinyGo base stage installs the most recent Go 1.15.x, LLVM 10 and the TinyGo compiler itself.
FROM golang:1.15 AS tinygo-base
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-11 main" >> /etc/apt/sources.list && \
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-10 main" >> /etc/apt/sources.list && \
apt-get update && \
apt-get install -y llvm-11-dev libclang-11-dev lld-11 git
apt-get install -y llvm-10-dev libclang-10-dev lld-10 git
COPY . /tinygo
@@ -29,7 +29,7 @@ COPY --from=tinygo-base /tinygo/targets /tinygo/targets
RUN cd /tinygo/ && \
apt-get update && \
apt-get install -y make clang-11 libllvm11 lld-11 && \
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.
@@ -61,7 +61,7 @@ COPY --from=tinygo-base /tinygo/lib /tinygo/lib
RUN cd /tinygo/ && \
apt-get update && \
apt-get install -y apt-utils make clang-11 && \
apt-get install -y apt-utils make clang-10 && \
make gen-device-nrf && make gen-device-stm32
# tinygo-all stage installs the needed dependencies to compile TinyGo programs for all platforms.
@@ -73,7 +73,7 @@ COPY --from=tinygo-base /tinygo/lib /tinygo/lib
RUN cd /tinygo/ && \
apt-get update && \
apt-get install -y apt-utils make clang-11 binutils-avr gcc-avr avr-libc && \
apt-get install -y apt-utils make clang-10 binutils-avr gcc-avr avr-libc && \
make gen-device
CMD ["tinygo"]
+2 -2
View File
@@ -1,7 +1,7 @@
Copyright (c) 2018-2021 TinyGo Authors. All rights reserved.
Copyright (c) 2018-2020 TinyGo Authors. All rights reserved.
TinyGo includes portions of the Go standard library.
Copyright (c) 2009-2021 The Go Authors. All rights reserved.
Copyright (c) 2009-2020 The Go Authors. All rights reserved.
TinyGo includes portions of LLVM, which is under the Apache License v2.0 with
LLVM Exceptions. See https://llvm.org/LICENSE.txt for license information.
+10 -28
View File
@@ -54,53 +54,39 @@ ifeq ($(OS),Windows_NT)
CGO_LDFLAGS += -static -static-libgcc -static-libstdc++
CGO_LDFLAGS_EXTRA += -lversion
LIBCLANG_NAME = libclang
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/liblibclang.a
else ifeq ($(shell uname -s),Darwin)
MD5SUM = md5
LIBCLANG_NAME = clang
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
else ifeq ($(shell uname -s),FreeBSD)
MD5SUM = md5
LIBCLANG_NAME = clang
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
START_GROUP = -Wl,--start-group
END_GROUP = -Wl,--end-group
else
LIBCLANG_NAME = clang
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
START_GROUP = -Wl,--start-group
END_GROUP = -Wl,--end-group
endif
# Libraries that should be linked in for the statically linked Clang.
CLANG_LIB_NAMES = clangAnalysis clangARCMigrate clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangStaticAnalyzerCheckers clangStaticAnalyzerCore clangStaticAnalyzerFrontend clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
CLANG_LIBS = $(START_GROUP) -lclangAnalysis -lclangARCMigrate -lclangAST -lclangASTMatchers -lclangBasic -lclangCodeGen -lclangCrossTU -lclangDriver -lclangDynamicASTMatchers -lclangEdit -lclangFormat -lclangFrontend -lclangFrontendTool -lclangHandleCXX -lclangHandleLLVM -lclangIndex -lclangLex -lclangParse -lclangRewrite -lclangRewriteFrontend -lclangSema -lclangSerialization -lclangStaticAnalyzerCheckers -lclangStaticAnalyzerCore -lclangStaticAnalyzerFrontend -lclangTooling -lclangToolingASTDiff -lclangToolingCore -lclangToolingInclusions $(END_GROUP) -lstdc++
# Libraries that should be linked in for the statically linked LLD.
LLD_LIB_NAMES = lldCOFF lldCommon lldCore lldDriver lldELF lldMachO lldMinGW lldReaderWriter lldWasm lldYAML
LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP)
LLD_LIBS = $(START_GROUP) -llldCOFF -llldCommon -llldCore -llldDriver -llldELF -llldMachO -llldMinGW -llldReaderWriter -llldWasm -llldYAML $(END_GROUP)
# Other libraries that are needed to link TinyGo.
EXTRA_LIB_NAMES = LLVMInterpreter
# These build targets appear to be the only ones necessary to build all TinyGo
# dependencies. Only building a subset significantly speeds up rebuilding LLVM.
# The Makefile rules convert a name like lldELF to lib/liblldELF.a to match the
# library path (for ninja).
# This list also includes a few tools that are necessary as part of the full
# TinyGo build.
NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(addsuffix .a,$(LIBCLANG_NAME) $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)))
# For static linking.
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
CGO_CPPFLAGS+=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
CGO_CXXFLAGS=-std=c++14
CGO_LDFLAGS+=$(abspath $(LLVM_BUILDDIR))/lib/lib$(LIBCLANG_NAME).a -L$(abspath $(LLVM_BUILDDIR)/lib) $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
CGO_LDFLAGS+=$(LIBCLANG_PATH) -L$(abspath $(LLVM_BUILDDIR)/lib) $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
endif
clean:
@rm -rf build
FMT_PATHS = ./*.go builder cgo compiler interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/internal/reflectlite transform
FMT_PATHS = ./*.go builder cgo compiler interp ir loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/internal/reflectlite transform
fmt:
@gofmt -l -w $(FMT_PATHS)
fmt-check:
@@ -156,11 +142,11 @@ 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;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_LIBXML2=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
cd $(LLVM_BUILDDIR); ninja $(NINJA_BUILD_TARGETS)
cd $(LLVM_BUILDDIR); ninja
# Build wasi-libc sysroot
@@ -341,8 +327,6 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nucleo-l552ze examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=p1am-100 examples/blinky1
@$(MD5SUM) test.hex
# test pwm
@@ -391,8 +375,6 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
@$(MD5SUM) test.nro
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
@$(MD5SUM) test.hex
wasmtest:
$(GO) test ./tests/wasm
+3 -14
View File
@@ -43,11 +43,11 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
The following 55 microcontroller boards 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)
* [Adafruit CLUE](https://www.adafruit.com/product/4500)
* [Adafruit CLUE Alpha](https://www.adafruit.com/product/4500)
* [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)
@@ -55,29 +55,22 @@ The following 55 microcontroller boards are currently supported:
* [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 Matrix Portal M4](https://www.adafruit.com/product/4745)
* [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)
* [Adafruit PyPortal](https://www.adafruit.com/product/4116)
* [Adafruit QT Py](https://www.adafruit.com/product/4600)
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
* [Arduino Mega 2560](https://store.arduino.cc/arduino-mega-2560-rev3)
* [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000-wifi)
* [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/)
* [BBC micro:bit v2](https://microbit.org/new-microbit/)
* [Digispark](http://digistump.com/products/1)
* [Dragino LoRaWAN GPS Tracker LGT-92](http://www.dragino.com/products/lora-lorawan-end-node/item/142-lgt-92.html)
* [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/)
* [Makerdiary nRF52840-MDK USB Dongle](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/)
* [nice!nano](https://docs.nicekeyboards.com/#/nice!nano/)
* [Nintendo Switch](https://www.nintendo.com/switch/)
* [Nordic Semiconductor PCA10031](https://www.nordicsemi.com/eng/Products/nRF51-Dongle)
* [Nordic Semiconductor PCA10040](https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF52-DK)
@@ -88,15 +81,11 @@ The following 55 microcontroller boards are currently supported:
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
* [PineTime DevKit](https://www.pine64.org/pinetime/)
* [PJRC Teensy 3.6](https://www.pjrc.com/store/teensy36.html)
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
* [ProductivityOpen P1AM-100](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html)
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
* [SiFIve HiFive1](https://www.sifive.com/boards/hifive1)
* [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
* [ST Micro "Nucleo" F722ZE](https://www.st.com/en/evaluation-tools/nucleo-f722ze.html)
* [ST Micro "Nucleo" L552ZE](https://www.st.com/en/evaluation-tools/nucleo-l552ze-q.html)
* [ST Micro "Nucleo F103RB"](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
* [ST Micro STM32F103XX "Bluepill"](https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill)
* [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html)
* [X9 Pro smartwatch](https://github.com/curtpw/nRF5x-device-reverse-engineering/tree/master/X9-nrf52832-activity-tracker/)
+8 -4
View File
@@ -12,7 +12,7 @@ jobs:
steps:
- task: GoTool@0
inputs:
version: '1.16'
version: '1.15'
- checkout: self
fetchDepth: 1
- task: Cache@2
@@ -24,11 +24,15 @@ jobs:
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-11-windows-v4
key: llvm-build-11-windows-v3
path: llvm-build
- task: Bash@3
displayName: Build LLVM
@@ -52,7 +56,7 @@ jobs:
- task: CacheBeta@0
displayName: Cache wasi-libc sysroot
inputs:
key: wasi-libc-sysroot-v4
key: wasi-libc-sysroot-v3
path: lib/wasi-libc/sysroot
- task: Bash@3
displayName: Build wasi-libc
+205 -508
View File
@@ -4,18 +4,14 @@
package builder
import (
"crypto/sha512"
"debug/elf"
"encoding/binary"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"go/types"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"
@@ -42,26 +38,6 @@ type BuildResult struct {
MainDir string
}
// packageAction is the struct that is serialized to JSON and hashed, to work as
// a cache key of compiled packages. It should contain all the information that
// goes into a compiled package to avoid using stale data.
//
// Right now it's still important to include a hash of every import, because a
// dependency might have a public constant that this package uses and thus this
// package will need to be recompiled if that constant changes. In the future,
// the type data should be serialized to disk which can then be used as cache
// key, avoiding the need for recompiling all dependencies when only the
// implementation of an imported package changes.
type packageAction struct {
ImportPath string
CompilerVersion int // compiler.Version
LLVMVersion string
Config *compiler.Config
CFlags []string
FileHashes map[string]string // hash of every file that's part of the package
Imports map[string]string // map from imported package to action ID hash
}
// 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.
@@ -69,34 +45,10 @@ type packageAction struct {
// 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(BuildResult) error) error {
// Create a temporary directory for intermediary files.
dir, err := ioutil.TempDir("", "tinygo")
if err != nil {
return err
}
defer os.RemoveAll(dir)
compilerConfig := &compiler.Config{
Triple: config.Triple(),
CPU: config.CPU(),
Features: config.Features(),
GOOS: config.GOOS(),
GOARCH: config.GOARCH(),
CodeModel: config.CodeModel(),
RelocationModel: config.RelocationModel(),
Scheduler: config.Scheduler(),
FuncImplementation: config.FuncImplementation(),
AutomaticStackSize: config.AutomaticStackSize(),
DefaultStackSize: config.Target.DefaultStackSize,
NeedsStackObjects: config.NeedsStackObjects(),
Debug: config.Debug(),
}
// Load the target machine, which is the LLVM object that contains all
// details of a target (alignment restrictions, pointer size, default
// address spaces, etc).
machine, err := compiler.NewTargetMachine(compilerConfig)
machine, err := compiler.NewTargetMachine(config)
if err != nil {
return err
}
@@ -113,465 +65,21 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return err
}
// The slice of jobs that orchestrates most of the build.
// This is somewhat like an in-memory Makefile with each job being a
// Makefile target.
var jobs []*compileJob
// Create the *ssa.Program. This does not yet build the entire SSA of the
// program so it's pretty fast and doesn't need to be parallelized.
program := lprogram.LoadSSA()
// Add jobs to compile each package.
// Packages that have a cache hit will not be compiled again.
var packageJobs []*compileJob
packageBitcodePaths := make(map[string]string)
packageActionIDs := make(map[string]string)
for _, pkg := range lprogram.Sorted() {
pkg := pkg // necessary to avoid a race condition
// Create a cache key: a hash from the action ID below that contains all
// the parameters for the build.
actionID := packageAction{
ImportPath: pkg.ImportPath,
CompilerVersion: compiler.Version,
LLVMVersion: llvm.Version,
Config: compilerConfig,
CFlags: pkg.CFlags,
FileHashes: make(map[string]string, len(pkg.FileHashes)),
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
}
for filePath, hash := range pkg.FileHashes {
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
}
for _, imported := range pkg.Pkg.Imports() {
hash, ok := packageActionIDs[imported.Path()]
if !ok {
return fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
}
actionID.Imports[imported.Path()] = hash
}
buf, err := json.Marshal(actionID)
if err != nil {
panic(err) // shouldn't happen
}
hash := sha512.Sum512_224(buf)
packageActionIDs[pkg.ImportPath] = hex.EncodeToString(hash[:])
// Determine the path of the bitcode file (which is a serialized version
// of a LLVM module).
cacheDir := goenv.Get("GOCACHE")
if cacheDir == "off" {
// Use temporary build directory instead, effectively disabling the
// build cache.
cacheDir = dir
}
bitcodePath := filepath.Join(cacheDir, "pkg-"+hex.EncodeToString(hash[:])+".bc")
packageBitcodePaths[pkg.ImportPath] = bitcodePath
// Check whether this package has been compiled before, and if so don't
// compile it again.
if _, err := os.Stat(bitcodePath); err == nil {
// Already cached, don't recreate this package.
continue
}
// The package has not yet been compiled, so create a job to do so.
job := &compileJob{
description: "compile package " + pkg.ImportPath,
run: func() error {
// Compile AST to IR. The compiler.CompilePackage function will
// build the SSA as needed.
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
if errs != nil {
return newMultiError(errs)
}
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return errors.New("verification error after compiling package " + pkg.ImportPath)
}
// Serialize the LLVM module as a bitcode file.
// Write to a temporary path that is renamed to the destination
// file to avoid race conditions with other TinyGo invocatiosn
// that might also be compiling this package at the same time.
f, err := ioutil.TempFile(filepath.Dir(bitcodePath), filepath.Base(bitcodePath))
if err != nil {
return err
}
if runtime.GOOS == "windows" {
// Work around a problem on Windows.
// For some reason, WriteBitcodeToFile causes TinyGo to
// exit with the following message:
// LLVM ERROR: IO failure on output stream: Bad file descriptor
buf := llvm.WriteBitcodeToMemoryBuffer(mod)
defer buf.Dispose()
_, err = f.Write(buf.Bytes())
} else {
// Otherwise, write bitcode directly to the file (probably
// faster).
err = llvm.WriteBitcodeToFile(mod, f)
}
if err != nil {
// WriteBitcodeToFile doesn't produce a useful error on its
// own, so create a somewhat useful error message here.
return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, bitcodePath)
}
err = f.Close()
if err != nil {
return err
}
return os.Rename(f.Name(), bitcodePath)
},
}
jobs = append(jobs, job)
packageJobs = append(packageJobs, job)
// Compile AST to IR.
mod, errs := compiler.CompileProgram(pkgName, lprogram, machine, config)
if errs != nil {
return newMultiError(errs)
}
// Add job that links and optimizes all packages together.
var mod llvm.Module
var stackSizeLoads []string
programJob := &compileJob{
description: "link+optimize packages (LTO)",
dependencies: packageJobs,
run: func() error {
// Load and link all the bitcode files. This does not yet optimize
// anything, it only links the bitcode files together.
ctx := llvm.NewContext()
mod = ctx.NewModule("")
for _, pkg := range lprogram.Sorted() {
pkgMod, err := ctx.ParseBitcodeFile(packageBitcodePaths[pkg.ImportPath])
if err != nil {
return fmt.Errorf("failed to load bitcode file: %w", err)
}
err = llvm.LinkModules(mod, pkgMod)
if err != nil {
return fmt.Errorf("failed to link module: %w", err)
}
}
// Create runtime.initAll function that calls the runtime
// initializer of each package.
llvmInitFn := mod.NamedFunction("runtime.initAll")
llvmInitFn.SetLinkage(llvm.InternalLinkage)
llvmInitFn.SetUnnamedAddr(true)
llvmInitFn.Param(0).SetName("context")
llvmInitFn.Param(1).SetName("parentHandle")
block := mod.Context().AddBasicBlock(llvmInitFn, "entry")
irbuilder := mod.Context().NewBuilder()
defer irbuilder.Dispose()
irbuilder.SetInsertPointAtEnd(block)
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
for _, pkg := range lprogram.Sorted() {
pkgInit := mod.NamedFunction(pkg.Pkg.Path() + ".init")
if pkgInit.IsNil() {
panic("init not found for " + pkg.Pkg.Path())
}
irbuilder.CreateCall(pkgInit, []llvm.Value{llvm.Undef(i8ptrType), llvm.Undef(i8ptrType)}, "")
}
irbuilder.CreateRetVoid()
// After linking, functions should (as far as possible) be set to
// private linkage or internal linkage. The compiler package marks
// non-exported functions by setting the visibility to hidden or
// (for thunks) to linkonce_odr linkage. Change the linkage here to
// internal to benefit much more from interprocedural optimizations.
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
if fn.Visibility() == llvm.HiddenVisibility {
fn.SetVisibility(llvm.DefaultVisibility)
fn.SetLinkage(llvm.InternalLinkage)
} else if fn.Linkage() == llvm.LinkOnceODRLinkage {
fn.SetLinkage(llvm.InternalLinkage)
}
}
// Do the same for globals.
for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
if global.Visibility() == llvm.HiddenVisibility {
global.SetVisibility(llvm.DefaultVisibility)
global.SetLinkage(llvm.InternalLinkage)
} else if global.Linkage() == llvm.LinkOnceODRLinkage {
global.SetLinkage(llvm.InternalLinkage)
}
}
if config.Options.PrintIR {
fmt.Println("; Generated LLVM IR:")
fmt.Println(mod.String())
}
// Run all optimization passes, which are much more effective now
// that the optimizer can see the whole program at once.
err := optimizeProgram(mod, config)
if err != nil {
return err
}
// Make sure stack sizes are loaded from a separate section so they can be
// modified after linking.
if config.AutomaticStackSize() {
stackSizeLoads = transform.CreateStackSizeLoads(mod, config)
}
return nil
},
if config.Options.PrintIR {
fmt.Println("; Generated LLVM IR:")
fmt.Println(mod.String())
}
jobs = append(jobs, programJob)
// Check whether we only need to create an object file.
// If so, we don't need to link anything and will be finished quickly.
outext := filepath.Ext(outpath)
if outext == ".o" || outext == ".bc" || outext == ".ll" {
// Run jobs to produce the LLVM module.
err := runJobs(jobs)
if err != nil {
return err
}
// Generate output.
switch outext {
case ".o":
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
if err != nil {
return err
}
return ioutil.WriteFile(outpath, llvmBuf.Bytes(), 0666)
case ".bc":
data := llvm.WriteBitcodeToMemoryBuffer(mod).Bytes()
return ioutil.WriteFile(outpath, data, 0666)
case ".ll":
data := []byte(mod.String())
return ioutil.WriteFile(outpath, data, 0666)
default:
panic("unreachable")
}
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return errors.New("verification error after IR construction")
}
// Act as a compiler driver, as we need to produce a complete executable.
// First add all jobs necessary to build this object file, then afterwards
// run all jobs in parallel as far as possible.
// Add job to write the output object file.
objfile := filepath.Join(dir, "main.o")
outputObjectFileJob := &compileJob{
description: "generate output file",
dependencies: []*compileJob{programJob},
run: func() error {
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
if err != nil {
return err
}
return ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666)
},
}
jobs = append(jobs, outputObjectFileJob)
// Prepare link command.
linkerDependencies := []*compileJob{outputObjectFileJob}
executable := filepath.Join(dir, "main")
tmppath := executable // final file
ldflags := append(config.LDFlags(), "-o", executable, objfile)
// Add compiler-rt dependency if needed. Usually this is a simple load from
// a cache.
if config.Target.RTLib == "compiler-rt" {
path, job, err := CompilerRT.load(config.Triple(), config.CPU(), dir)
if err != nil {
return err
}
if job != nil {
// The library was not loaded from cache so needs to be compiled
// (and then stored in the cache).
jobs = append(jobs, job.dependencies...)
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
}
ldflags = append(ldflags, path)
}
// Add libc dependency if needed.
root := goenv.Get("TINYGOROOT")
switch config.Target.Libc {
case "picolibc":
path, job, err := Picolibc.load(config.Triple(), config.CPU(), dir)
if err != nil {
return err
}
if job != nil {
// The library needs to be compiled (cache miss).
jobs = append(jobs, job.dependencies...)
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
}
ldflags = append(ldflags, path)
case "wasi-libc":
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
if _, err := os.Stat(path); os.IsNotExist(err) {
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
}
ldflags = append(ldflags, path)
case "":
// no library specified, so nothing to do
default:
return fmt.Errorf("unknown libc: %s", config.Target.Libc)
}
// Add jobs to compile extra files. These files are in C or assembly and
// contain things like the interrupt vector table and low level operations
// such as stack switching.
for i, path := range config.ExtraFiles() {
abspath := filepath.Join(root, path)
outpath := filepath.Join(dir, "extra-"+strconv.Itoa(i)+"-"+filepath.Base(path)+".o")
job := &compileJob{
description: "compile extra file " + path,
run: func() error {
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, abspath)...)
if err != nil {
return &commandError{"failed to build", path, err}
}
return nil
},
}
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
ldflags = append(ldflags, outpath)
}
// Add jobs to compile C files in all packages. This is part of CGo.
// TODO: do this as part of building the package to be able to link the
// bitcode files together.
for i, pkg := range lprogram.Sorted() {
for j, filename := range pkg.CFiles {
file := filepath.Join(pkg.Dir, filename)
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"."+strconv.Itoa(j)+"-"+filepath.Base(file)+".o")
job := &compileJob{
description: "compile CGo file " + file,
run: func() error {
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...)
if err != nil {
return &commandError{"failed to build", file, err}
}
return nil
},
}
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
ldflags = append(ldflags, outpath)
}
}
// Linker flags from CGo lines:
// #cgo LDFLAGS: foo
if len(lprogram.LDFlags) > 0 {
ldflags = append(ldflags, lprogram.LDFlags...)
}
// Create a linker job, which links all object files together and does some
// extra stuff that can only be done after linking.
jobs = append(jobs, &compileJob{
description: "link",
dependencies: linkerDependencies,
run: func() error {
err = link(config.Target.Linker, ldflags...)
if err != nil {
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 {
return err
}
if config.Options.PrintSizes == "short" {
fmt.Printf(" code data bss | flash ram\n")
fmt.Printf("%7d %7d %7d | %7d %7d\n", sizes.Code, sizes.Data, sizes.BSS, sizes.Code+sizes.Data, sizes.Data+sizes.BSS)
} else {
fmt.Printf(" code rodata data bss | flash ram | package\n")
for _, name := range sizes.sortedPackageNames() {
pkgSize := sizes.Packages[name]
fmt.Printf("%7d %7d %7d %7d | %7d %7d | %s\n", pkgSize.Code, pkgSize.ROData, pkgSize.Data, pkgSize.BSS, pkgSize.Flash(), pkgSize.RAM(), name)
}
fmt.Printf("%7d %7d %7d %7d | %7d %7d | (sum)\n", sizes.Sum.Code, sizes.Sum.ROData, sizes.Sum.Data, sizes.Sum.BSS, sizes.Sum.Flash(), sizes.Sum.RAM())
fmt.Printf("%7d - %7d %7d | %7d %7d | (all)\n", sizes.Code, sizes.Data, sizes.BSS, sizes.Code+sizes.Data, sizes.Data+sizes.BSS)
}
}
// Print goroutine stack sizes, as far as possible.
if config.Options.PrintStacks {
printStacks(calculatedStacks, stackSizes)
}
return nil
},
})
// Run all jobs to compile and link the program.
// Do this now (instead of after elf-to-hex and similar conversions) as it
// is simpler and cannot be parallelized.
err = runJobs(jobs)
if err != nil {
return err
}
// Get an Intel .hex file or .bin file from the .elf file.
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, outputBinaryFormat)
if err != nil {
return err
}
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(BuildResult{
Binary: tmppath,
MainDir: lprogram.MainPkg().Dir,
})
}
// optimizeProgram runs a series of optimizations and transformations that are
// needed to convert a program to its final form. Some transformations are not
// optional and must be run as the compiler expects them to run.
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
err := interp.Run(mod, config.DumpSSA())
err = interp.Run(mod, config.DumpSSA())
if err != nil {
return err
}
@@ -597,10 +105,17 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
// Optimization levels here are roughly the same as Clang, but probably not
// exactly.
var errs []error
errs = nil
switch config.Options.Opt {
case "none", "0":
errs = transform.Optimize(mod, config, 0, 0, 0) // -O0
/*
Currently, turning optimizations off causes compile failures.
We rely on the optimizer removing some dead symbols.
Avoid providing an option that does not work right now.
In the future once everything has been fixed we can re-enable this.
case "none", "0":
errs = transform.Optimize(mod, config, 0, 0, 0) // -O0
*/
case "1":
errs = transform.Optimize(mod, config, 1, 0, 0) // -O1
case "2":
@@ -610,7 +125,7 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
case "z":
errs = transform.Optimize(mod, config, 2, 2, 5) // -Oz, default
default:
return errors.New("unknown optimization level: -opt=" + config.Options.Opt)
errs = []error{errors.New("unknown optimization level: -opt=" + config.Options.Opt)}
}
if len(errs) > 0 {
return newMultiError(errs)
@@ -630,7 +145,189 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
transform.DisableTailCalls(mod)
}
return nil
// 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 {
case ".o":
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
if err != nil {
return err
}
return ioutil.WriteFile(outpath, llvmBuf.Bytes(), 0666)
case ".bc":
data := llvm.WriteBitcodeToMemoryBuffer(mod).Bytes()
return ioutil.WriteFile(outpath, data, 0666)
case ".ll":
data := []byte(mod.String())
return ioutil.WriteFile(outpath, data, 0666)
default:
// Act as a compiler driver.
// Create a temporary directory for intermediary files.
dir, err := ioutil.TempDir("", "tinygo")
if err != nil {
return err
}
defer os.RemoveAll(dir)
// Write the object file.
objfile := filepath.Join(dir, "main.o")
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
if err != nil {
return err
}
err = ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666)
if err != nil {
return err
}
// Prepare link command.
executable := filepath.Join(dir, "main")
tmppath := executable // final file
ldflags := append(config.LDFlags(), "-o", executable, objfile)
// Load builtins library from the cache, possibly compiling it on the
// fly.
if config.Target.RTLib == "compiler-rt" {
librt, err := CompilerRT.Load(config.Triple())
if err != nil {
return err
}
ldflags = append(ldflags, librt)
}
// Add libc.
if config.Target.Libc == "picolibc" {
libc, err := Picolibc.Load(config.Triple())
if err != nil {
return err
}
ldflags = append(ldflags, libc)
}
// Compile extra files.
root := goenv.Get("TINYGOROOT")
for i, path := range config.ExtraFiles() {
abspath := filepath.Join(root, path)
outpath := filepath.Join(dir, "extra-"+strconv.Itoa(i)+"-"+filepath.Base(path)+".o")
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, abspath)...)
if err != nil {
return &commandError{"failed to build", path, err}
}
ldflags = append(ldflags, outpath)
}
// Compile C files in packages.
// Gather the list of (C) file paths that should be included in the build.
for i, pkg := range lprogram.Sorted() {
for j, filename := range pkg.CFiles {
file := filepath.Join(pkg.Dir, filename)
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"."+strconv.Itoa(j)+"-"+filepath.Base(file)+".o")
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...)
if err != nil {
return &commandError{"failed to build", file, err}
}
ldflags = append(ldflags, outpath)
}
}
if len(lprogram.LDFlags) > 0 {
ldflags = append(ldflags, lprogram.LDFlags...)
}
// Link the object files together.
err = link(config.Target.Linker, ldflags...)
if err != nil {
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 {
return err
}
if config.Options.PrintSizes == "short" {
fmt.Printf(" code data bss | flash ram\n")
fmt.Printf("%7d %7d %7d | %7d %7d\n", sizes.Code, sizes.Data, sizes.BSS, sizes.Code+sizes.Data, sizes.Data+sizes.BSS)
} else {
fmt.Printf(" code rodata data bss | flash ram | package\n")
for _, name := range sizes.sortedPackageNames() {
pkgSize := sizes.Packages[name]
fmt.Printf("%7d %7d %7d %7d | %7d %7d | %s\n", pkgSize.Code, pkgSize.ROData, pkgSize.Data, pkgSize.BSS, pkgSize.Flash(), pkgSize.RAM(), name)
}
fmt.Printf("%7d %7d %7d %7d | %7d %7d | (sum)\n", sizes.Sum.Code, sizes.Sum.ROData, sizes.Sum.Data, sizes.Sum.BSS, sizes.Sum.Flash(), sizes.Sum.RAM())
fmt.Printf("%7d - %7d %7d | %7d %7d | (all)\n", sizes.Code, sizes.Data, sizes.BSS, sizes.Code+sizes.Data, sizes.Data+sizes.BSS)
}
}
// Print goroutine stack sizes, as far as possible.
if config.Options.PrintStacks {
printStacks(calculatedStacks, stackSizes)
}
// Get an Intel .hex file or .bin file from the .elf file.
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, outputBinaryFormat)
if err != nil {
return err
}
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(BuildResult{
Binary: tmppath,
MainDir: lprogram.MainPkg().Dir,
})
}
}
// functionStackSizes keeps stack size information about a single function
+19 -6
View File
@@ -30,7 +30,10 @@ func cacheTimestamp(paths []string) (time.Time, error) {
// Try to load a given file from the cache. Return "", nil if no cached file can
// be found (or the file is stale), return the absolute path if there is a cache
// and return an error on I/O errors.
func cacheLoad(name string, sourceFiles []string) (string, error) {
//
// TODO: the configKey is currently ignored. It is supposed to be used as extra
// data for the cache key, like the compiler version and arguments.
func cacheLoad(name, configKey string, sourceFiles []string) (string, error) {
cachepath := filepath.Join(goenv.Get("GOCACHE"), name)
cacheStat, err := os.Stat(cachepath)
if os.IsNotExist(err) {
@@ -55,7 +58,9 @@ func cacheLoad(name string, sourceFiles []string) (string, error) {
// Store the file located at tmppath in the cache with the given name. The
// tmppath may or may not be gone afterwards.
func cacheStore(tmppath, name string, sourceFiles []string) (string, error) {
//
// Note: the configKey is ignored, see cacheLoad.
func cacheStore(tmppath, name, configKey string, sourceFiles []string) (string, error) {
// get the last modified time
if len(sourceFiles) == 0 {
panic("cache: no source files")
@@ -69,16 +74,24 @@ func cacheStore(tmppath, name string, sourceFiles []string) (string, error) {
return "", err
}
cachepath := filepath.Join(dir, name)
err = copyFile(tmppath, cachepath)
err = moveFile(tmppath, cachepath)
if err != nil {
return "", err
}
return cachepath, nil
}
// 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 {
// moveFile renames the file from src to dst. If renaming doesn't work (for
// example, the rename crosses a filesystem boundary), the file is copied and
// the old file is removed.
func moveFile(src, dst string) error {
err := os.Rename(src, dst)
if err == nil {
// Success!
return nil
}
// Failed to move, probably a different filesystem.
// Do a copy + remove.
inf, err := os.Open(src)
if err != nil {
return err
+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 < 13 || minor > 16 {
return nil, fmt.Errorf("requires go version 1.13 through 1.16, 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{
-160
View File
@@ -1,160 +0,0 @@
package builder
// This file implements a job runner for the compiler, which runs jobs in
// parallel while taking care of dependencies.
import (
"fmt"
"runtime"
"time"
)
// Set to true to enable logging in the job runner. This may help to debug
// concurrency or performance issues.
const jobRunnerDebug = false
type jobState uint8
const (
jobStateQueued jobState = iota // not yet running
jobStateRunning // running
jobStateFinished // finished running
)
// compileJob is a single compiler job, comparable to a single Makefile target.
// It is used to orchestrate various compiler tasks that can be run in parallel
// but that have dependencies and thus have limitations in how they can be run.
type compileJob struct {
description string // description, only used for logging
dependencies []*compileJob
run func() error
state jobState
err error // error if finished
duration time.Duration // how long it took to run this job (only set after finishing)
}
// readyToRun returns whether this job is ready to run: it is itself not yet
// started and all dependencies are finished.
func (job *compileJob) readyToRun() bool {
if job.state != jobStateQueued {
// Already running or finished, so shouldn't be run again.
return false
}
// Check dependencies.
for _, dep := range job.dependencies {
if dep.state != jobStateFinished {
// A dependency is not finished, so this job has to wait until it
// is.
return false
}
}
// All conditions are satisfied.
return true
}
// runJobs runs all the jobs indicated in the jobs slice and returns the error
// of the first job that fails to run.
// It runs all jobs in the order of the slice, as long as all dependencies have
// already run. Therefore, if some jobs are preferred to run before others, they
// should be ordered as such in this slice.
func runJobs(jobs []*compileJob) error {
// Create channels to communicate with the workers.
doneChan := make(chan *compileJob)
workerChan := make(chan *compileJob)
defer close(workerChan)
// Start a number of workers.
for i := 0; i < runtime.NumCPU(); i++ {
if jobRunnerDebug {
fmt.Println("## starting worker", i)
}
go jobWorker(workerChan, doneChan)
}
// Send each job in the jobs slice to a worker, taking care of job
// dependencies.
numRunningJobs := 0
var totalTime time.Duration
start := time.Now()
for {
// If there are free workers, try starting a new job (if one is
// available). If it succeeds, try again to fill the entire worker pool.
if numRunningJobs < runtime.NumCPU() {
jobToRun := nextJob(jobs)
if jobToRun != nil {
// Start job.
if jobRunnerDebug {
fmt.Println("## start: ", jobToRun.description)
}
jobToRun.state = jobStateRunning
workerChan <- jobToRun
numRunningJobs++
continue
}
}
// When there are no jobs running, all jobs in the jobs slice must have
// been finished. Therefore, the work is done.
if numRunningJobs == 0 {
break
}
// Wait until a job is finished.
job := <-doneChan
job.state = jobStateFinished
numRunningJobs--
totalTime += job.duration
if jobRunnerDebug {
fmt.Println("## finished:", job.description, "(time "+job.duration.String()+")")
}
if job.err != nil {
// Wait for running jobs to finish.
for numRunningJobs != 0 {
<-doneChan
numRunningJobs--
}
// Return error of first failing job.
return job.err
}
}
// Some statistics, if debugging.
if jobRunnerDebug {
// Total duration of running all jobs.
duration := time.Since(start)
fmt.Println("## total: ", duration)
// The individual time of each job combined. On a multicore system, this
// should be lower than the total above.
fmt.Println("## job sum: ", totalTime)
}
return nil
}
// nextJob returns the first ready-to-run job.
// This is an implementation detail of runJobs.
func nextJob(jobs []*compileJob) *compileJob {
for _, job := range jobs {
if job.readyToRun() {
return job
}
}
return nil
}
// jobWorker is the goroutine that runs received jobs.
// This is an implementation detail of runJobs.
func jobWorker(workerChan, doneChan chan *compileJob) {
for job := range workerChan {
start := time.Now()
err := job.run()
if err != nil {
job.err = err
}
job.duration = time.Since(start)
doneChan <- job
}
}
+29 -74
View File
@@ -1,6 +1,7 @@
package builder
import (
"io/ioutil"
"os"
"path/filepath"
"strings"
@@ -39,64 +40,34 @@ func (l *Library) sourcePaths(target string) []string {
}
// Load the library archive, possibly generating and caching it if needed.
// The resulting file is stored in the provided tmpdir, which is expected to be
// removed after the Load call.
func (l *Library) Load(target, tmpdir string) (path string, err error) {
path, job, err := l.load(target, "", tmpdir)
if err != nil {
return "", err
}
if job != nil {
jobs := append([]*compileJob{job}, job.dependencies...)
err = runJobs(jobs)
}
return path, err
}
// load returns a path to the library file for the given target, loading it from
// cache if possible. It will return a non-zero compiler job if the library
// wasn't cached, this job (and its dependencies) must be run before the library
// path is valid.
// The provided tmpdir will be used to store intermediary files and possibly the
// output archive file, it is expected to be removed after use.
func (l *Library) load(target, cpu, tmpdir string) (path string, job *compileJob, err error) {
func (l *Library) Load(target string) (path string, err error) {
// Try to load a precompiled library.
precompiledPath := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", target, l.name+".a")
if _, err := os.Stat(precompiledPath); err == nil {
// Found a precompiled library for this OS/architecture. Return the path
// directly.
return precompiledPath, nil, nil
return precompiledPath, nil
}
var outfile string
if cpu != "" {
outfile = l.name + "-" + target + "-" + cpu + ".a"
} else {
outfile = l.name + "-" + target + ".a"
}
outfile := l.name + "-" + target + ".a"
// Try to fetch this library from the cache.
if path, err := cacheLoad(outfile, l.sourcePaths(target)); path != "" || err != nil {
if path, err := cacheLoad(outfile, commands["clang"][0], l.sourcePaths(target)); path != "" || err != nil {
// Cache hit.
return path, nil, err
return path, err
}
// Cache miss, build it now.
remapDir := filepath.Join(os.TempDir(), "tinygo-"+l.name)
dir := filepath.Join(tmpdir, "build-lib-"+l.name)
err = os.Mkdir(dir, 0777)
dirPrefix := "tinygo-" + l.name
remapDir := filepath.Join(os.TempDir(), dirPrefix)
dir, err := ioutil.TempDir(os.TempDir(), dirPrefix)
if err != nil {
return "", nil, err
return "", err
}
defer os.RemoveAll(dir)
// Precalculate the flags to the compiler invocation.
// Note: -fdebug-prefix-map is necessary to make the output archive
// reproducible. Otherwise the temporary directory is stored in the archive
// itself, which varies each run.
args := append(l.cflags(), "-c", "-Oz", "-g", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
if cpu != "" {
args = append(args, "-mcpu="+cpu)
}
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft")
}
@@ -107,44 +78,28 @@ func (l *Library) load(target, cpu, tmpdir string) (path string, job *compileJob
args = append(args, "-march=rv64gc", "-mabi=lp64")
}
// Create job to put all the object files in a single archive. This archive
// file is the (static) library file.
// Compile all sources.
var objs []string
arpath := filepath.Join(dir, l.name+".a")
job = &compileJob{
description: "ar " + l.name + ".a",
run: func() error {
// Create an archive of all object files.
err := makeArchive(arpath, objs)
if err != nil {
return err
}
// Store this archive in the cache.
_, err = cacheStore(arpath, outfile, l.sourcePaths(target))
return err
},
}
// Create jobs to compile all sources. These jobs are depended upon by the
// archive job above, so must be run first.
for _, srcpath := range l.sourcePaths(target) {
srcpath := srcpath // avoid concurrency issues by redefining inside the loop
objpath := filepath.Join(dir, filepath.Base(srcpath)+".o")
objs = append(objs, objpath)
job.dependencies = append(job.dependencies, &compileJob{
description: "compile " + srcpath,
run: func() error {
var compileArgs []string
compileArgs = append(compileArgs, args...)
compileArgs = append(compileArgs, "-o", objpath, srcpath)
err := runCCompiler("clang", compileArgs...)
if err != nil {
return &commandError{"failed to build", srcpath, err}
}
return nil
},
})
// Note: -fdebug-prefix-map is necessary to make the output archive
// reproducible. Otherwise the temporary directory is stored in the
// archive itself, which varies each run.
err := runCCompiler("clang", append(args, "-o", objpath, srcpath)...)
if err != nil {
return "", &commandError{"failed to build", srcpath, err}
}
}
return arpath, job, nil
// Put all the object files in a single archive. This archive file will be
// used to statically link this library.
arpath := filepath.Join(dir, l.name+".a")
err = makeArchive(arpath, objs)
if err != nil {
return "", err
}
// Store this archive in the cache.
return cacheStore(arpath, outfile, commands["clang"][0], l.sourcePaths(target))
}
+8 -22
View File
@@ -42,7 +42,6 @@ type cgoPackage struct {
enums map[string]enumInfo
anonStructNum int
ldflags []string
visitedFiles map[string][]byte
}
// constantInfo stores some information about a CGo constant found by libclang
@@ -55,10 +54,9 @@ type constantInfo struct {
// functionInfo stores some information about a CGo function found by libclang
// and declared in the AST.
type functionInfo struct {
args []paramInfo
results *ast.FieldList
pos token.Pos
variadic bool
args []paramInfo
results *ast.FieldList
pos token.Pos
}
// paramInfo is a parameter of a CGo function (see functionInfo).
@@ -157,10 +155,9 @@ typedef unsigned long long _Cgo_ulonglong;
// Process extracts `import "C"` statements from the AST, parses the comment
// with libclang, and modifies the AST to use this information. It returns a
// newly created *ast.File that should be added to the list of to-be-parsed
// files, the LDFLAGS for this package, and a map of file hashes of the accessed
// C header files. If there is one or more error, it returns these in the
// []error slice but still modifies the AST.
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, map[string][]byte, []error) {
// files. If there is one or more error, it returns these in the []error slice
// but still modifies the AST.
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, []error) {
p := &cgoPackage{
dir: dir,
fset: fset,
@@ -172,7 +169,6 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
typedefs: map[string]*typedefInfo{},
elaboratedTypes: map[string]*elaboratedTypeInfo{},
enums: map[string]enumInfo{},
visitedFiles: map[string][]byte{},
}
// Disable _FORTIFY_SOURCE as it causes problems on macOS.
@@ -188,7 +184,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
// Find the absolute path for this package.
packagePath, err := filepath.Abs(fset.File(files[0].Pos()).Name())
if err != nil {
return nil, nil, nil, []error{
return nil, nil, []error{
scanner.Error{
Pos: fset.Position(files[0].Pos()),
Msg: "cgo: cannot find absolute path: " + err.Error(), // TODO: wrap this error
@@ -430,7 +426,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
// Print the newly generated in-memory AST, for debugging.
//ast.Print(fset, p.generated)
return p.generated, p.ldflags, p.visitedFiles, p.errors
return p.generated, p.ldflags, p.errors
}
// makePathsAbsolute converts some common path compiler flags (-I, -L) from
@@ -488,16 +484,6 @@ func (p *cgoPackage) addFuncDecls() {
Results: fn.results,
},
}
if fn.variadic {
decl.Doc = &ast.CommentGroup{
List: []*ast.Comment{
&ast.Comment{
Slash: fn.pos,
Text: "//go:variadic",
},
},
}
}
obj.Decl = decl
for i, arg := range fn.args {
args[i] = &ast.Field{
+4 -19
View File
@@ -5,7 +5,6 @@ import (
"flag"
"fmt"
"go/ast"
"go/build"
"go/format"
"go/parser"
"go/token"
@@ -24,7 +23,7 @@ var flagUpdate = flag.Bool("update", false, "Update images based on test output.
// normalizeResult normalizes Go source code that comes out of tests across
// platforms and Go versions.
func normalizeResult(result string) string {
actual := strings.ReplaceAll(result, "\r\n", "\n")
actual := strings.Replace(result, "\r\n", "\n", -1)
// Make sure all functions are wrapped, even those that would otherwise be
// single-line functions. This is necessary because Go 1.14 changed the way
@@ -42,20 +41,6 @@ func TestCGo(t *testing.T) {
for _, name := range []string{"basic", "errors", "types", "flags", "const"} {
name := name // avoid a race condition
t.Run(name, func(t *testing.T) {
// Skip tests that require specific Go version.
if name == "errors" {
ok := false
for _, version := range build.Default.ReleaseTags {
if version == "go1.16" {
ok = true
break
}
}
if !ok {
t.Skip("Results for errors test are only valid for Go 1.16+")
}
}
// Read the AST in memory.
path := filepath.Join("testdata", name+".go")
fset := token.NewFileSet()
@@ -65,7 +50,7 @@ func TestCGo(t *testing.T) {
}
// Process the AST with CGo.
cgoAST, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
cgoAST, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
// Check the AST for type errors.
var typecheckErrors []error
@@ -113,7 +98,7 @@ func TestCGo(t *testing.T) {
if err != nil {
t.Fatalf("could not read expected output: %v", err)
}
expected := strings.ReplaceAll(string(expectedBytes), "\r\n", "\n")
expected := strings.Replace(string(expectedBytes), "\r\n", "\n", -1)
// Check whether the output is as expected.
if expected != actual {
@@ -154,7 +139,7 @@ func formatDiagnostic(err error) string {
msg := err.Error()
if runtime.GOOS == "windows" {
// Fix Windows path slashes.
msg = strings.ReplaceAll(msg, "testdata\\", "testdata/")
msg = strings.Replace(msg, "testdata\\", "testdata/", -1)
}
return "// " + msg + "\n"
}
+4 -37
View File
@@ -4,7 +4,6 @@ package cgo
// modification. It does not touch the AST itself.
import (
"crypto/sha512"
"fmt"
"go/ast"
"go/scanner"
@@ -57,7 +56,6 @@ unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c);
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
int tinygo_clang_enum_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
void tinygo_clang_inclusion_visitor(CXFile included_file, CXSourceLocation *inclusion_stack, unsigned include_len, CXClientData client_data);
*/
import "C"
@@ -116,7 +114,6 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
}
defer C.clang_disposeTranslationUnit(unit)
// Report parser and type errors.
if numDiagnostics := int(C.clang_getNumDiagnostics(unit)); numDiagnostics != 0 {
addDiagnostic := func(diagnostic C.CXDiagnostic) {
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
@@ -137,36 +134,10 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
}
}
// Extract information required by CGo.
ref := storedRefs.Put(p)
defer storedRefs.Remove(ref)
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
// Determine files read during CGo processing, for caching.
inclusionCallback := func(includedFile C.CXFile) {
// Get full file path.
path := getString(C.clang_getFileName(includedFile))
// Get contents of file (that should be in-memory).
size := C.size_t(0)
rawData := C.clang_getFileContents(unit, includedFile, &size)
if rawData == nil {
// Sanity check. This should (hopefully) never trigger.
panic("libclang: file contents was not loaded")
}
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
// Hash the contents if it isn't hashed yet.
if _, ok := p.visitedFiles[path]; !ok {
// already stored
sum := sha512.Sum512_224(data)
p.visitedFiles[path] = sum[:]
}
}
inclusionCallbackRef := storedRefs.Put(inclusionCallback)
defer storedRefs.Remove(inclusionCallbackRef)
C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef))
}
//export tinygo_clang_globals_visitor
@@ -181,10 +152,12 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
return C.CXChildVisit_Continue
}
cursorType := C.tinygo_clang_getCursorType(c)
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
return C.CXChildVisit_Continue // not supported
}
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
fn := &functionInfo{
pos: pos,
variadic: C.clang_isFunctionTypeVariadic(cursorType) != 0,
pos: pos,
}
p.functions[name] = fn
for i := 0; i < numArgs; i++ {
@@ -801,9 +774,3 @@ func tinygo_clang_enum_visitor(c, parent C.GoCXCursor, client_data C.CXClientDat
}
return C.CXChildVisit_Continue
}
//export tinygo_clang_inclusion_visitor
func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) {
callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile))
callback(includedFile)
}
+7 -7
View File
@@ -1,14 +1,14 @@
// +build !byollvm
// +build !llvm10
// +build !llvm9,!llvm11
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-11/include
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@11/include
#cgo freebsd CFLAGS: -I/usr/local/llvm11/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-11/lib -lclang
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@11/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm11/lib -lclang
#cgo linux CFLAGS: -I/usr/lib/llvm-10/include
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@10/include
#cgo freebsd CFLAGS: -I/usr/local/llvm10/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-10/lib -lclang
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@10/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm10/lib -lclang
*/
import "C"
-14
View File
@@ -1,14 +0,0 @@
// +build !byollvm
// +build llvm10
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-10/include
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@10/include
#cgo freebsd CFLAGS: -I/usr/local/llvm10/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-10/lib -lclang
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@10/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm10/lib -lclang
*/
import "C"
+14
View File
@@ -0,0 +1,14 @@
// +build !byollvm
// +build llvm11
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-11/include
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@11/include
#cgo freebsd CFLAGS: -I/usr/local/llvm11/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-11/lib -lclang
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@11/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm11/lib -lclang
*/
import "C"
+14
View File
@@ -0,0 +1,14 @@
// +build !byollvm
// +build llvm9
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-9/include
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@9/include
#cgo freebsd CFLAGS: -I/usr/local/llvm9/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-9/lib -lclang
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@9/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm9/lib -lclang
*/
import "C"
+2 -2
View File
@@ -4,10 +4,10 @@
// testdata/errors.go:13:23: unexpected token )
// Type checking errors after CGo processing:
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as uint8 value in variable declaration (overflows)
// testdata/errors.go:102: 2 << 10 (untyped int constant 2048) overflows uint8
// testdata/errors.go:105: unknown field z in struct literal
// testdata/errors.go:108: undeclared name: C.SOME_CONST_1
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
// testdata/errors.go:110: C.SOME_CONST_3 (untyped int constant 1234) overflows byte
package main
-10
View File
@@ -104,10 +104,6 @@ typedef struct {
unsigned char e : 3;
// Note that C++ allows bitfields bigger than the underlying type.
} bitfield_t;
// Function signatures.
void variadic0();
void variadic2(int x, int y, ...);
*/
import "C"
@@ -167,9 +163,3 @@ func accessUnion() {
var _ *C.int = union2d.unionfield_i()
var _ *[2]float64 = union2d.unionfield_d()
}
// Test function signatures.
func accessFunctions() {
C.variadic0()
C.variadic2(3, 5)
}
-5
View File
@@ -4,11 +4,6 @@ import "unsafe"
var _ unsafe.Pointer
func C.variadic0() //go:variadic
func C.variadic2(x C.int, y C.int) //go:variadic
var C.variadic0$funcaddr unsafe.Pointer
var C.variadic2$funcaddr unsafe.Pointer
const C.option2A = 20
const C.optionA = 0
const C.optionB = 1
+31 -18
View File
@@ -21,6 +21,29 @@ type Config struct {
TestConfig TestConfig
}
// FuncValueImplementation is an enum for the particular implementations of Go
// func values.
type FuncValueImplementation int
// These constants describe the various possible implementations of Go func
// values.
const (
FuncValueNone FuncValueImplementation = iota
// A func value is implemented as a pair of pointers:
// {context, function pointer}
// where the context may be a pointer to a heap-allocated struct containing
// the free variables, or it may be undef if the function being pointed to
// doesn't need a context. The function pointer is a regular function
// pointer.
FuncValueDoubleword
// As funcValueDoubleword, but with the function pointer replaced by a
// unique ID per function signature. Function values are called by using a
// switch statement and choosing which function to call.
FuncValueSwitch
)
// Triple returns the LLVM target triple, like armv6m-none-eabi.
func (c *Config) Triple() string {
return c.Target.Triple
@@ -120,24 +143,14 @@ func (c *Config) Scheduler() string {
// FuncImplementation picks an appropriate func value implementation for the
// target.
func (c *Config) FuncImplementation() string {
func (c *Config) FuncImplementation() FuncValueImplementation {
// Always pick the switch implementation, as it allows the use of blocking
// inside a function that is used as a func value.
switch c.Scheduler() {
case "tasks":
// A func value is implemented as a pair of pointers:
// {context, function pointer}
// where the context may be a pointer to a heap-allocated struct
// containing the free variables, or it may be undef if the function
// being pointed to doesn't need a context. The function pointer is a
// regular function pointer.
return "doubleword"
case "none", "coroutines":
// As "doubleword", but with the function pointer replaced by a unique
// ID per function signature. Function values are called by using a
// switch statement and choosing which function to call.
// Pick the switch implementation with the coroutines scheduler, as it
// allows the use of blocking inside a function that is used as a func
// value.
return "switch"
return FuncValueSwitch
case "tasks":
return FuncValueDoubleword
default:
panic("unknown scheduler type")
}
@@ -165,7 +178,7 @@ func (c *Config) AutomaticStackSize() bool {
func (c *Config) CFlags() []string {
cflags := append([]string{}, c.Options.CFlags...)
for _, flag := range c.Target.CFlags {
cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT")))
cflags = append(cflags, strings.Replace(flag, "{root}", goenv.Get("TINYGOROOT"), -1))
}
if c.Target.Libc == "picolibc" {
root := goenv.Get("TINYGOROOT")
@@ -186,7 +199,7 @@ func (c *Config) LDFlags() []string {
// Merge and adjust LDFlags.
ldflags := append([]string{}, c.Options.LDFlags...)
for _, flag := range c.Target.LDFlags {
ldflags = append(ldflags, strings.ReplaceAll(flag, "{root}", root))
ldflags = append(ldflags, strings.Replace(flag, "{root}", root, -1))
}
ldflags = append(ldflags, "-L", root)
if c.Target.LinkerScript != "" {
+6 -6
View File
@@ -16,7 +16,7 @@ import (
// slice. This is required by the Go language spec: an index out of bounds must
// cause a panic.
func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value, indexType types.Type) {
if b.info.nobounds {
if b.fn.IsNoBounds() {
// The //go:nobounds pragma was added to the function to avoid bounds
// checking.
return
@@ -48,7 +48,7 @@ func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value, indexType
// biggest possible slice capacity, 'low' means len and 'high' means cap. The
// logic is the same in both cases.
func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lowType, highType, maxType *types.Basic) {
if b.info.nobounds {
if b.fn.IsNoBounds() {
// The //go:nobounds pragma was added to the function to avoid bounds
// checking.
return
@@ -104,7 +104,7 @@ func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lo
// createChanBoundsCheck creates a bounds check before creating a new channel to
// check that the value is not too big for runtime.chanMake.
func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value, bufSizeType *types.Basic, pos token.Pos) {
if b.info.nobounds {
if b.fn.IsNoBounds() {
// The //go:nobounds pragma was added to the function to avoid bounds
// checking.
return
@@ -189,7 +189,7 @@ func (b *builder) createNilCheck(inst ssa.Value, ptr llvm.Value, blockPrefix str
// createNegativeShiftCheck creates an assertion that panics if the given shift value is negative.
// This function assumes that the shift value is signed.
func (b *builder) createNegativeShiftCheck(shift llvm.Value) {
if b.info.nobounds {
if b.fn.IsNoBounds() {
// Function disabled bounds checking - skip shift check.
return
}
@@ -212,8 +212,8 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
}
}
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
nextBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".next")
faultBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, blockPrefix+".throw")
nextBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, blockPrefix+".next")
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
// Now branch to the out-of-bounds or the regular block.
-27
View File
@@ -1,8 +1,6 @@
package compiler
import (
"strings"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
@@ -37,31 +35,6 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
ptr := b.getValue(call.Args[0])
old := b.getValue(call.Args[1])
newVal := b.getValue(call.Args[2])
if strings.HasSuffix(name, "64") {
arch := strings.Split(b.Triple, "-")[0]
if strings.HasPrefix(arch, "arm") && strings.HasSuffix(arch, "m") {
// Work around a bug in LLVM, at least LLVM 11:
// https://reviews.llvm.org/D95891
// Check for armv6m, armv7, armv7em, and perhaps others.
// See also: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
compareAndSwap := b.mod.NamedFunction("__sync_val_compare_and_swap_8")
if compareAndSwap.IsNil() {
// Declare the function if it isn't already declared.
i64Type := b.ctx.Int64Type()
fnType := llvm.FunctionType(i64Type, []llvm.Type{llvm.PointerType(i64Type, 0), i64Type, i64Type}, false)
compareAndSwap = llvm.AddFunction(b.mod, "__sync_val_compare_and_swap_8", fnType)
}
actualOldValue := b.CreateCall(compareAndSwap, []llvm.Value{ptr, old, newVal}, "")
// The __sync_val_compare_and_swap_8 function returns the old
// value. However, we shouldn't return the old value, we should
// return whether the compare/exchange was successful. This is
// easily done by comparing the returned (actual) old value with
// the expected old value passed to
// __sync_val_compare_and_swap_8.
swapped := b.CreateICmp(llvm.IntEQ, old, actualOldValue, "")
return swapped, true
}
}
tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true)
swapped := b.CreateExtractValue(tuple, 1, "")
return swapped, true
+5 -6
View File
@@ -4,7 +4,6 @@ import (
"go/types"
"strconv"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
@@ -35,14 +34,14 @@ const (
// createCall creates a new call to runtime.<fnName> with the given arguments.
func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
llvmFn := b.getFunction(fn)
if llvmFn.IsNil() {
panic("trying to call non-existent function: " + fn.RelString(nil))
fullName := "runtime." + fnName
fn := b.mod.NamedFunction(fullName)
if fn.IsNil() {
panic("trying to call non-existent function: " + fullName)
}
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
args = append(args, llvm.ConstPointerNull(b.i8ptrType)) // coroutine handle
return b.createCall(llvmFn, args, name)
return b.createCall(fn, args, name)
}
// createCall creates a call to the given function with the arguments possibly
+2 -1
View File
@@ -79,7 +79,8 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
}
// createChanClose closes the given channel.
func (b *builder) createChanClose(ch llvm.Value) {
func (b *builder) createChanClose(param ssa.Value) {
ch := b.getValue(param)
b.createRuntimeCall("chanClose", []llvm.Value{ch}, "")
}
+357 -320
View File
File diff suppressed because it is too large Load Diff
+2 -14
View File
@@ -28,17 +28,7 @@ func TestCompiler(t *testing.T) {
Options: &compileopts.Options{},
Target: target,
}
compilerConfig := &Config{
Triple: config.Triple(),
GOOS: config.GOOS(),
GOARCH: config.GOARCH(),
CodeModel: config.CodeModel(),
RelocationModel: config.RelocationModel(),
Scheduler: config.Scheduler(),
FuncImplementation: config.FuncImplementation(),
AutomaticStackSize: config.AutomaticStackSize(),
}
machine, err := NewTargetMachine(compilerConfig)
machine, err := NewTargetMachine(config)
if err != nil {
t.Fatal("failed to create target machine:", err)
}
@@ -47,7 +37,6 @@ func TestCompiler(t *testing.T) {
"basic.go",
"pointer.go",
"slice.go",
"float.go",
}
for _, testCase := range tests {
@@ -65,9 +54,8 @@ func TestCompiler(t *testing.T) {
}
// Compile AST to IR.
program := lprogram.LoadSSA()
pkg := lprogram.MainPkg()
mod, errs := CompilePackage(testCase, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false)
mod, errs := CompilePackage(testCase, pkg, machine, config)
if errs != nil {
for _, err := range errs {
t.Log("error:", err)
+40 -40
View File
@@ -14,9 +14,9 @@ package compiler
// frames.
import (
"go/types"
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"github.com/tinygo-org/tinygo/ir"
"go/types"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
@@ -26,9 +26,9 @@ import (
// calls.
func (b *builder) deferInitFunc() {
// Some setup.
b.deferFuncs = make(map[*ssa.Function]int)
b.deferFuncs = make(map[*ir.Function]int)
b.deferInvokeFuncs = make(map[string]int)
b.deferClosureFuncs = make(map[*ssa.Function]int)
b.deferClosureFuncs = make(map[*ir.Function]int)
b.deferExprFuncs = make(map[ssa.Value]int)
b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin)
@@ -107,11 +107,13 @@ func (b *builder) createDefer(instr *ssa.Defer) {
} else if callee, ok := instr.Call.Value.(*ssa.Function); ok {
// Regular function call.
if _, ok := b.deferFuncs[callee]; !ok {
b.deferFuncs[callee] = len(b.allDeferFuncs)
b.allDeferFuncs = append(b.allDeferFuncs, callee)
fn := b.ir.GetFunction(callee)
if _, ok := b.deferFuncs[fn]; !ok {
b.deferFuncs[fn] = len(b.allDeferFuncs)
b.allDeferFuncs = append(b.allDeferFuncs, fn)
}
callback := llvm.ConstInt(b.uintptrType, uint64(b.deferFuncs[callee]), false)
callback := llvm.ConstInt(b.uintptrType, uint64(b.deferFuncs[fn]), false)
// Collect all values to be put in the struct (starting with
// runtime._defer fields).
@@ -133,7 +135,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
context := b.CreateExtractValue(closure, 0, "")
// Get the callback number.
fn := makeClosure.Fn.(*ssa.Function)
fn := b.ir.GetFunction(makeClosure.Fn.(*ssa.Function))
if _, ok := b.deferClosureFuncs[fn]; !ok {
b.deferClosureFuncs[fn] = len(b.allDeferFuncs)
b.allDeferFuncs = append(b.allDeferFuncs, makeClosure)
@@ -153,19 +155,19 @@ func (b *builder) createDefer(instr *ssa.Defer) {
valueTypes = append(valueTypes, context.Type())
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
var argTypes []types.Type
var argValues []llvm.Value
for _, arg := range instr.Call.Args {
argTypes = append(argTypes, arg.Type())
argValues = append(argValues, b.getValue(arg))
var funcName string
switch builtin.Name() {
case "close":
funcName = "chanClose"
default:
b.addError(instr.Pos(), "todo: Implement defer for "+builtin.Name())
return
}
if _, ok := b.deferBuiltinFuncs[instr.Call.Value]; !ok {
b.deferBuiltinFuncs[instr.Call.Value] = deferBuiltin{
callName: builtin.Name(),
pos: builtin.Pos(),
argTypes: argTypes,
callback: len(b.allDeferFuncs),
funcName,
len(b.allDeferFuncs),
}
b.allDeferFuncs = append(b.allDeferFuncs, instr.Call.Value)
}
@@ -174,9 +176,10 @@ func (b *builder) createDefer(instr *ssa.Defer) {
// Collect all values to be put in the struct (starting with
// runtime._defer fields).
values = []llvm.Value{callback, next}
for _, param := range argValues {
values = append(values, param)
valueTypes = append(valueTypes, param.Type())
for _, param := range instr.Call.Args {
llvmParam := b.getValue(param)
values = append(values, llvmParam)
valueTypes = append(valueTypes, llvmParam.Type())
}
} else {
@@ -220,7 +223,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "defer.alloc.call")
alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc")
}
if b.NeedsStackObjects {
if b.NeedsStackObjects() {
b.trackPointer(alloca)
}
b.CreateStore(deferFrame, alloca)
@@ -248,10 +251,10 @@ func (b *builder) createRunDefers() {
// }
// Create loop.
loophead := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loophead")
loop := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loop")
unreachable := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.default")
end := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.end")
loophead := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.loophead")
loop := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.loop")
unreachable := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.default")
end := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.end")
b.CreateBr(loophead)
// Create loop head:
@@ -283,7 +286,7 @@ func (b *builder) createRunDefers() {
// Create switch case, for example:
// case 0:
// // run first deferred call
block := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.callback")
block := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.callback")
sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), block)
b.SetInsertPointAtEnd(block)
switch callback := callback.(type) {
@@ -347,12 +350,12 @@ func (b *builder) createRunDefers() {
b.createCall(fnPtr, forwardParams, "")
case *ssa.Function:
case *ir.Function:
// Direct call.
// Get the real defer struct type and cast to it.
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
for _, param := range getParams(callback.Signature) {
for _, param := range callback.Params {
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
}
deferFrameType := b.ctx.StructType(valueTypes, false)
@@ -361,7 +364,7 @@ func (b *builder) createRunDefers() {
// Extract the params from the struct.
forwardParams := []llvm.Value{}
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := range getParams(callback.Signature) {
for i := range callback.Params {
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
forwardParam := b.CreateLoad(gep, "param")
forwardParams = append(forwardParams, forwardParam)
@@ -369,7 +372,7 @@ func (b *builder) createRunDefers() {
// Plain TinyGo functions add some extra parameters to implement async functionality and function recievers.
// These parameters should not be supplied when calling into an external C/ASM function.
if !b.getFunctionInfo(callback).exported {
if !callback.IsExported() {
// Add the context parameter. We know it is ignored by the receiving
// function, but we have to pass one anyway.
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
@@ -379,11 +382,11 @@ func (b *builder) createRunDefers() {
}
// Call real function.
b.createCall(b.getFunction(callback), forwardParams, "")
b.createCall(callback.LLVMFn, forwardParams, "")
case *ssa.MakeClosure:
// Get the real defer struct type and cast to it.
fn := callback.Fn.(*ssa.Function)
fn := b.ir.GetFunction(callback.Fn.(*ssa.Function))
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
params := fn.Signature.Params()
for i := 0; i < params.Len(); i++ {
@@ -406,7 +409,7 @@ func (b *builder) createRunDefers() {
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
// Call deferred function.
b.createCall(b.getFunction(fn), forwardParams, "")
b.createCall(fn.LLVMFn, forwardParams, "")
case *ssa.Builtin:
db := b.deferBuiltinFuncs[callback]
@@ -423,18 +426,15 @@ func (b *builder) createRunDefers() {
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
// Extract the params from the struct.
var argValues []llvm.Value
var forwardParams []llvm.Value
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := 0; i < params.Len(); i++ {
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
forwardParam := b.CreateLoad(gep, "param")
argValues = append(argValues, forwardParam)
forwardParams = append(forwardParams, forwardParam)
}
_, err := b.createBuiltin(db.argTypes, argValues, db.callName, db.pos)
if err != nil {
b.diagnostics = append(b.diagnostics, err)
}
b.createRuntimeCall(db.funcName, forwardParams, "")
default:
panic("unknown deferred function type")
}
+1 -1
View File
@@ -14,7 +14,7 @@ import (
// makeError makes it easy to create an error from a token.Pos with a message.
func (c *compilerContext) makeError(pos token.Pos, msg string) types.Error {
return types.Error{
Fset: c.program.Fset,
Fset: c.ir.Program.Fset,
Pos: pos,
Msg: msg,
}
+13 -12
View File
@@ -6,6 +6,7 @@ package compiler
import (
"go/types"
"github.com/tinygo-org/tinygo/compileopts"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
@@ -20,11 +21,11 @@ func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signat
// context.
func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
var funcValueScalar llvm.Value
switch c.FuncImplementation {
case "doubleword":
switch c.FuncImplementation() {
case compileopts.FuncValueDoubleword:
// Closure is: {context, function pointer}
funcValueScalar = funcPtr
case "switch":
case compileopts.FuncValueSwitch:
sigGlobal := c.getTypeCode(sig)
funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
@@ -37,7 +38,7 @@ func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context
funcValueWithSignatureGlobal = llvm.AddGlobal(c.mod, funcValueWithSignatureType, funcValueWithSignatureGlobalName)
funcValueWithSignatureGlobal.SetInitializer(funcValueWithSignature)
funcValueWithSignatureGlobal.SetGlobalConstant(true)
funcValueWithSignatureGlobal.SetLinkage(llvm.LinkOnceODRLinkage)
funcValueWithSignatureGlobal.SetLinkage(llvm.InternalLinkage)
}
funcValueScalar = llvm.ConstPtrToInt(funcValueWithSignatureGlobal, c.uintptrType)
default:
@@ -66,10 +67,10 @@ func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value {
// value. This may be an expensive operation.
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
context = b.CreateExtractValue(funcValue, 0, "")
switch b.FuncImplementation {
case "doubleword":
switch b.FuncImplementation() {
case compileopts.FuncValueDoubleword:
funcPtr = b.CreateExtractValue(funcValue, 1, "")
case "switch":
case compileopts.FuncValueSwitch:
llvmSig := b.getRawFuncType(sig)
sigGlobal := b.getTypeCode(sig)
funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
@@ -82,11 +83,11 @@ func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (f
// getFuncType returns the type of a func value given a signature.
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
switch c.FuncImplementation {
case "doubleword":
switch c.FuncImplementation() {
case compileopts.FuncValueDoubleword:
rawPtr := c.getRawFuncType(typ)
return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false)
case "switch":
case compileopts.FuncValueSwitch:
return c.getLLVMRuntimeType("funcValue")
default:
panic("unimplemented func value variant")
@@ -148,7 +149,7 @@ func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) {
if len(expr.Bindings) == 0 {
panic("unexpected: MakeClosure without bound variables")
}
f := expr.Fn.(*ssa.Function)
f := b.ir.GetFunction(expr.Fn.(*ssa.Function))
// Collect all bound variables.
boundVars := make([]llvm.Value, len(expr.Bindings))
@@ -163,5 +164,5 @@ func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) {
context := b.emitPointerPack(boundVars)
// Create the closure.
return b.createFuncValue(b.getFunction(f), context, f.Signature), nil
return b.createFuncValue(f.LLVMFn, context, f.Signature), nil
}
+11 -14
View File
@@ -7,7 +7,6 @@ import (
"go/token"
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
@@ -21,20 +20,19 @@ import (
func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, prefix string, pos token.Pos) llvm.Value {
paramBundle := b.emitPointerPack(params)
var callee, stackSize llvm.Value
switch b.Scheduler {
switch b.Scheduler() {
case "none", "tasks":
callee = b.createGoroutineStartWrapper(funcPtr, prefix, pos)
if b.AutomaticStackSize {
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).
stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function))
stackSize = b.createCall(stackSizeFn, []llvm.Value{callee, llvm.Undef(b.i8ptrType), llvm.Undef(b.i8ptrType)}, "stacksize")
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.DefaultStackSize, false)
stackSize = llvm.ConstInt(b.uintptrType, b.Target.DefaultStackSize, false)
}
case "coroutines":
callee = b.CreatePtrToInt(funcPtr, b.uintptrType, "")
@@ -44,8 +42,7 @@ func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, p
default:
panic("unreachable")
}
start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
b.createCall(start, []llvm.Value{callee, paramBundle, stackSize, 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())
}
@@ -84,14 +81,14 @@ 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.LinkOnceODRLinkage)
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)
if c.Debug {
pos := c.program.Fset.Position(pos)
if c.Debug() {
pos := c.ir.Program.Fset.Position(pos)
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
File: c.getDIFile(pos.Filename),
Parameters: nil, // do not show parameters in debugger
@@ -141,14 +138,14 @@ 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, prefix+".gowrapper", wrapperType)
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
wrapper.SetLinkage(llvm.InternalLinkage)
wrapper.SetUnnamedAddr(true)
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", ""))
entry := c.ctx.AddBasicBlock(wrapper, "entry")
builder.SetInsertPointAtEnd(entry)
if c.Debug {
pos := c.program.Fset.Position(pos)
if c.Debug() {
pos := c.ir.Program.Fset.Position(pos)
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
File: c.getDIFile(pos.Filename),
Parameters: nil, // do not show parameters in debugger
+26 -73
View File
@@ -11,6 +11,7 @@ import (
"strconv"
"strings"
"github.com/tinygo-org/tinygo/ir"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
@@ -31,7 +32,7 @@ func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.
itfConcreteTypeGlobal = llvm.AddGlobal(b.mod, typeInInterface, "typeInInterface:"+itfTypeCodeGlobal.Name())
itfConcreteTypeGlobal.SetInitializer(llvm.ConstNamedStruct(typeInInterface, []llvm.Value{itfTypeCodeGlobal, itfMethodSetGlobal}))
itfConcreteTypeGlobal.SetGlobalConstant(true)
itfConcreteTypeGlobal.SetLinkage(llvm.LinkOnceODRLinkage)
itfConcreteTypeGlobal.SetLinkage(llvm.PrivateLinkage)
}
itfTypeCode := b.CreatePtrToInt(itfConcreteTypeGlobal, b.uintptrType, "")
itf := llvm.Undef(b.getLLVMRuntimeType("_interface"))
@@ -80,7 +81,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
}
global.SetInitializer(globalValue)
global.SetLinkage(llvm.LinkOnceODRLinkage)
global.SetLinkage(llvm.PrivateLinkage)
}
global.SetGlobalConstant(true)
}
@@ -235,7 +236,7 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
}
ms := c.program.MethodSets.MethodSet(typ)
ms := c.ir.Program.MethodSets.MethodSet(typ)
if ms.Len() == 0 {
// no methods, so can leave that one out
return llvm.ConstPointerNull(llvm.PointerType(c.getLLVMRuntimeType("interfaceMethodInfo"), 0))
@@ -246,16 +247,15 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
for i := 0; i < ms.Len(); i++ {
method := ms.At(i)
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
fn := c.program.MethodValue(method)
llvmFn := c.getFunction(fn)
if llvmFn.IsNil() {
f := c.ir.GetFunction(c.ir.Program.MethodValue(method))
if f.LLVMFn.IsNil() {
// compiler error, so panic
panic("cannot find function: " + c.getFunctionInfo(fn).linkName)
panic("cannot find function: " + f.LinkName())
}
wrapper := c.getInterfaceInvokeWrapper(fn, llvmFn)
fn := c.getInterfaceInvokeWrapper(f)
methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
signatureGlobal,
llvm.ConstPtrToInt(wrapper, c.uintptrType),
llvm.ConstPtrToInt(fn, c.uintptrType),
})
methods[i] = methodInfo
}
@@ -264,7 +264,7 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
global = llvm.AddGlobal(c.mod, arrayType, typ.String()+"$methodset")
global.SetInitializer(value)
global.SetGlobalConstant(true)
global.SetLinkage(llvm.LinkOnceODRLinkage)
global.SetLinkage(llvm.PrivateLinkage)
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
}
@@ -295,7 +295,7 @@ func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
global = llvm.AddGlobal(c.mod, value.Type(), name+"$interface")
global.SetInitializer(value)
global.SetGlobalConstant(true)
global.SetLinkage(llvm.LinkOnceODRLinkage)
global.SetLinkage(llvm.PrivateLinkage)
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
}
@@ -303,7 +303,7 @@ func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
// external *i8 indicating the indicating the signature of this method. It is
// used during the interface lowering pass.
func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value {
signature := methodSignature(method)
signature := ir.MethodSignature(method)
signatureGlobal := c.mod.NamedGlobal("func " + signature)
if signatureGlobal.IsNil() {
signatureGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), "func "+signature)
@@ -357,8 +357,8 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
// value.
prevBlock := b.GetInsertBlock()
okBlock := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.ok")
nextBlock := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.next")
okBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, "typeassert.ok")
nextBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, "typeassert.next")
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
b.CreateCondBr(commaOk, okBlock, nextBlock)
@@ -436,8 +436,8 @@ func (b *builder) getInvokeCall(instr *ssa.CallCommon) (llvm.Value, []llvm.Value
// value, dereferences or unpacks it if necessary, and calls the real method.
// If the method to wrap has a pointer receiver, no wrapping is necessary and
// the function is returned directly.
func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llvm.Value) llvm.Value {
wrapperName := llvmFn.Name() + "$invoke"
func (c *compilerContext) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
wrapperName := f.LinkName() + "$invoke"
wrapper := c.mod.NamedFunction(wrapperName)
if !wrapper.IsNil() {
// Wrapper already created. Return it directly.
@@ -445,7 +445,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
}
// Get the expanded receiver type.
receiverType := c.getLLVMType(fn.Signature.Recv().Type())
receiverType := c.getLLVMType(f.Params[0].Type())
var expandedReceiverType []llvm.Type
for _, info := range expandFormalParamType(receiverType, "", nil) {
expandedReceiverType = append(expandedReceiverType, info.llvmType)
@@ -457,17 +457,17 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
// Casting a function signature to a different signature and calling it
// with a receiver pointer bitcasted to *i8 (as done in calls on an
// interface) is hopefully a safe (defined) operation.
return llvmFn
return f.LLVMFn
}
// create wrapper function
fnType := llvmFn.Type().ElementType()
fnType := f.LLVMFn.Type().ElementType()
paramTypes := append([]llvm.Type{c.i8ptrType}, fnType.ParamTypes()[len(expandedReceiverType):]...)
wrapFnType := llvm.FunctionType(fnType.ReturnType(), paramTypes, false)
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
wrapper.LastParam().SetName("parentHandle")
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
wrapper.SetLinkage(llvm.InternalLinkage)
wrapper.SetUnnamedAddr(true)
// Create a new builder just to create this wrapper.
@@ -478,9 +478,9 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
defer b.Builder.Dispose()
// add debug info if needed
if c.Debug {
pos := c.program.Fset.Position(fn.Pos())
difunc := c.attachDebugInfoRaw(fn, wrapper, "$invoke", pos.Filename, pos.Line)
if c.Debug() {
pos := c.ir.Program.Fset.Position(f.Pos())
difunc := c.attachDebugInfoRaw(f, wrapper, "$invoke", pos.Filename, pos.Line)
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
}
@@ -490,60 +490,13 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
receiverValue := b.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0]
params := append(b.expandFormalParam(receiverValue), wrapper.Params()[1:]...)
if llvmFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind {
b.CreateCall(llvmFn, params, "")
if f.LLVMFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind {
b.CreateCall(f.LLVMFn, params, "")
b.CreateRetVoid()
} else {
ret := b.CreateCall(llvmFn, params, "ret")
ret := b.CreateCall(f.LLVMFn, params, "ret")
b.CreateRet(ret)
}
return wrapper
}
// methodSignature creates a readable version of a method signature (including
// the function name, excluding the receiver name). This string is used
// internally to match interfaces and to call the correct method on an
// interface. Examples:
//
// String() string
// Read([]byte) (int, error)
func methodSignature(method *types.Func) string {
return method.Name() + signature(method.Type().(*types.Signature))
}
// Make a readable version of a function (pointer) signature.
// Examples:
//
// () string
// (string, int) (int, error)
func signature(sig *types.Signature) string {
s := ""
if sig.Params().Len() == 0 {
s += "()"
} else {
s += "("
for i := 0; i < sig.Params().Len(); i++ {
if i > 0 {
s += ", "
}
s += sig.Params().At(i).Type().String()
}
s += ")"
}
if sig.Results().Len() == 0 {
// keep as-is
} else if sig.Results().Len() == 1 {
s += " " + sig.Results().At(0).Type().String()
} else {
s += " ("
for i := 0; i < sig.Results().Len(); i++ {
if i > 0 {
s += ", "
}
s += sig.Results().At(i).Type().String()
}
s += ")"
}
return s
}
+5 -5
View File
@@ -39,14 +39,14 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
// Create a new global of type runtime/interrupt.handle. Globals of this
// type are lowered in the interrupt lowering pass.
globalType := b.program.ImportedPackage("runtime/interrupt").Type("handle").Type()
globalType := b.ir.Program.ImportedPackage("runtime/interrupt").Type("handle").Type()
globalLLVMType := b.getLLVMType(globalType)
globalName := "runtime/interrupt.$interrupt" + strconv.FormatInt(id.Int64(), 10)
if global := b.mod.NamedGlobal(globalName); !global.IsNil() {
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt redeclared in this program")
}
global := llvm.AddGlobal(b.mod, globalLLVMType, globalName)
global.SetVisibility(llvm.HiddenVisibility)
global.SetLinkage(llvm.PrivateLinkage)
global.SetGlobalConstant(true)
global.SetUnnamedAddr(true)
initializer := llvm.ConstNull(globalLLVMType)
@@ -55,8 +55,8 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
global.SetInitializer(initializer)
// Add debug info to the interrupt global.
if b.Debug {
pos := b.program.Fset.Position(instr.Pos())
if b.Debug() {
pos := b.ir.Program.Fset.Position(instr.Pos())
diglobal := b.dibuilder.CreateGlobalVariableExpression(b.getDIFile(pos.Filename), llvm.DIGlobalVariableExpression{
Name: "interrupt" + strconv.FormatInt(id.Int64(), 10),
LinkageName: globalName,
@@ -79,7 +79,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
// PLIC where each interrupt must be enabled using the interrupt number, and
// thus keeps the Interrupt object alive.
// This call is removed during interrupt lowering.
if strings.HasPrefix(b.Triple, "avr") {
if strings.HasPrefix(b.Triple(), "avr") {
useFn := b.mod.NamedFunction("runtime/interrupt.use")
if useFn.IsNil() {
useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
+1 -1
View File
@@ -44,7 +44,7 @@ func (b *builder) emitLifetimeEnd(ptr, size llvm.Value) {
// bitcasts, or else allocates a value on the heap if it cannot be packed in the
// pointer value directly. It returns the pointer with the packed data.
func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
return llvmutil.EmitPointerPack(b.Builder, b.mod, b.NeedsStackObjects, values)
return llvmutil.EmitPointerPack(b.Builder, b.mod, b.Config, values)
}
// emitPointerUnpack extracts a list of values packed using emitPointerPack.
+3 -2
View File
@@ -5,6 +5,7 @@ package llvmutil
// itself if possible and legal.
import (
"github.com/tinygo-org/tinygo/compileopts"
"tinygo.org/x/go-llvm"
)
@@ -12,7 +13,7 @@ import (
// bitcasts, or else allocates a value on the heap if it cannot be packed in the
// pointer value directly. It returns the pointer with the packed data.
// If the values are all constants, they are be stored in a constant global and deduplicated.
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, needsStackObjects bool, values []llvm.Value) llvm.Value {
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, config *compileopts.Config, values []llvm.Value) llvm.Value {
ctx := mod.Context()
targetData := llvm.NewTargetData(mod.DataLayout())
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
@@ -100,7 +101,7 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, needsStackObjects bo
llvm.Undef(i8ptrType), // unused context parameter
llvm.ConstPointerNull(i8ptrType), // coroutine handle
}, "")
if needsStackObjects {
if config.NeedsStackObjects() {
trackPointer := mod.NamedFunction("runtime.trackPointer")
builder.CreateCall(trackPointer, []llvm.Value{
packedHeapAlloc,
+27 -330
View File
@@ -15,298 +15,6 @@ import (
"tinygo.org/x/go-llvm"
)
// functionInfo contains some information about a function or method. In
// particular, it contains information obtained from pragmas.
//
// The linkName value contains a valid link name, even if //go:linkname is not
// present.
type functionInfo struct {
module string // go:wasm-module
importName string // go:linkname, go:export - The name the developer assigns
linkName string // go:linkname, go:export - The name that we map for the particular module -> importName
exported bool // go:export, CGo
nobounds bool // go:nobounds
variadic bool // go:variadic (CGo only)
inline inlineType // go:inline
}
type inlineType int
// How much to inline.
const (
// Default behavior. The compiler decides for itself whether any given
// function will be inlined. Whether any function is inlined depends on the
// optimization level.
inlineDefault inlineType = iota
// Inline hint, just like the C inline keyword (signalled using
// //go:inline). The compiler will be more likely to inline this function,
// but it is not a guarantee.
inlineHint
// Don't inline, just like the GCC noinline attribute. Signalled using
// //go:noinline.
inlineNone
)
// getFunction returns the LLVM function for the given *ssa.Function, creating
// it if needed. It can later be filled with compilerContext.createFunction().
func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
info := c.getFunctionInfo(fn)
llvmFn := c.mod.NamedFunction(info.linkName)
if !llvmFn.IsNil() {
return llvmFn
}
var retType llvm.Type
if fn.Signature.Results() == nil {
retType = c.ctx.VoidType()
} else if fn.Signature.Results().Len() == 1 {
retType = c.getLLVMType(fn.Signature.Results().At(0).Type())
} else {
results := make([]llvm.Type, 0, fn.Signature.Results().Len())
for i := 0; i < fn.Signature.Results().Len(); i++ {
results = append(results, c.getLLVMType(fn.Signature.Results().At(i).Type()))
}
retType = c.ctx.StructType(results, false)
}
var paramInfos []paramInfo
for _, param := range getParams(fn.Signature) {
paramType := c.getLLVMType(param.Type())
paramFragmentInfos := expandFormalParamType(paramType, param.Name(), param.Type())
paramInfos = append(paramInfos, paramFragmentInfos...)
}
// Add an extra parameter as the function context. This context is used in
// closures and bound methods, but should be optimized away when not used.
if !info.exported {
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "context", flags: 0})
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "parentHandle", flags: 0})
}
var paramTypes []llvm.Type
for _, info := range paramInfos {
paramTypes = append(paramTypes, info.llvmType)
}
fnType := llvm.FunctionType(retType, paramTypes, info.variadic)
llvmFn = llvm.AddFunction(c.mod, info.linkName, fnType)
if strings.HasPrefix(c.Triple, "wasm") {
// C functions without prototypes like this:
// void foo();
// are actually variadic functions. However, it appears that it has been
// decided in WebAssembly that such prototype-less functions are not
// allowed in WebAssembly.
// In C, this can only happen when there are zero parameters, hence this
// check here. For more information:
// https://reviews.llvm.org/D48443
// https://github.com/WebAssembly/tool-conventions/issues/16
if info.variadic && len(fn.Params) == 0 {
attr := c.ctx.CreateStringAttribute("no-prototype", "")
llvmFn.AddFunctionAttr(attr)
}
}
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
for i, info := range paramInfos {
if info.flags&paramIsDeferenceableOrNull == 0 {
continue
}
if info.llvmType.TypeKind() == llvm.PointerTypeKind {
el := info.llvmType.ElementType()
size := c.targetData.TypeAllocSize(el)
if size == 0 {
// dereferenceable_or_null(0) appears to be illegal in LLVM.
continue
}
dereferenceableOrNull := c.ctx.CreateEnumAttribute(dereferenceableOrNullKind, size)
llvmFn.AddAttributeAtIndex(i+1, dereferenceableOrNull)
}
}
// Set a number of function or parameter attributes, depending on the
// function. These functions are runtime functions that are known to have
// certain attributes that might not be inferred by the compiler.
switch info.linkName {
case "abort":
// On *nix systems, the "abort" functuion in libc is used to handle fatal panics.
// Mark it as noreturn so LLVM can optimize away code.
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noreturn"), 0))
case "runtime.alloc":
// Tell the optimizer that runtime.alloc is an allocator, meaning that it
// returns values that are never null and never alias to an existing value.
for _, attrName := range []string{"noalias", "nonnull"} {
llvmFn.AddAttributeAtIndex(0, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(attrName), 0))
}
case "runtime.trackPointer":
// This function is necessary for tracking pointers on the stack in a
// portable way (see gc_stack_portable.go). Indicate to the optimizer
// that the only thing we'll do is read the pointer.
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
}
// External/exported functions may not retain pointer values.
// https://golang.org/cmd/cgo/#hdr-Passing_pointers
if info.exported {
// Set the wasm-import-module attribute if the function's module is set.
if info.module != "" {
// We need to add the wasm-import-module and the wasm-import-name
wasmImportModuleAttr := c.ctx.CreateStringAttribute("wasm-import-module", info.module)
llvmFn.AddFunctionAttr(wasmImportModuleAttr)
// Add the Wasm Import Name, if we are a named wasm import
if info.importName != "" {
wasmImportNameAttr := c.ctx.CreateStringAttribute("wasm-import-name", info.importName)
llvmFn.AddFunctionAttr(wasmImportNameAttr)
}
}
nocaptureKind := llvm.AttributeKindID("nocapture")
nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0)
for i, typ := range paramTypes {
if typ.TypeKind() == llvm.PointerTypeKind {
llvmFn.AddAttributeAtIndex(i+1, nocapture)
}
}
}
// Synthetic functions are functions that do not appear in the source code,
// they are artificially constructed. Usually they are wrapper functions
// that are not referenced anywhere except in a SSA call instruction so
// should be created right away.
// The exception is the package initializer, which does appear in the
// *ssa.Package members and so shouldn't be created here.
if fn.Synthetic != "" && fn.Synthetic != "package initializer" {
irbuilder := c.ctx.NewBuilder()
b := newBuilder(c, irbuilder, fn)
b.createFunction()
irbuilder.Dispose()
llvmFn.SetLinkage(llvm.LinkOnceODRLinkage)
llvmFn.SetUnnamedAddr(true)
}
return llvmFn
}
// getFunctionInfo returns information about a function that is not directly
// present in *ssa.Function, such as the link name and whether it should be
// exported.
func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
info := functionInfo{}
if strings.HasPrefix(f.Name(), "C.") {
// Created by CGo: such a name cannot be created by regular C code.
info.linkName = f.Name()[2:]
info.exported = true
} else {
// Pick the default linkName.
info.linkName = f.RelString(nil)
}
// Check for //go: pragmas, which may change the link name (among others).
info.parsePragmas(f)
return info
}
// parsePragmas is used by getFunctionInfo to parse function pragmas such as
// //export or //go:noinline.
func (info *functionInfo) parsePragmas(f *ssa.Function) {
if f.Syntax() == nil {
return
}
if decl, ok := f.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil {
// Our importName for a wasm module (if we are compiling to wasm), or llvm link name
var importName string
for _, comment := range decl.Doc.List {
text := comment.Text
if strings.HasPrefix(text, "//export ") {
// Rewrite '//export' to '//go:export' for compatibility with
// gc.
text = "//go:" + text[2:]
}
if !strings.HasPrefix(text, "//go:") {
continue
}
parts := strings.Fields(text)
switch parts[0] {
case "//go:export":
if len(parts) != 2 {
continue
}
importName = parts[1]
info.exported = true
case "//go:wasm-module":
// Alternative comment for setting the import module.
if len(parts) != 2 {
continue
}
info.module = parts[1]
case "//go:inline":
info.inline = inlineHint
case "//go:noinline":
info.inline = inlineNone
case "//go:linkname":
if len(parts) != 3 || parts[1] != f.Name() {
continue
}
// Only enable go:linkname when the package imports "unsafe".
// This is a slightly looser requirement than what gc uses: gc
// requires the file to import "unsafe", not the package as a
// whole.
if hasUnsafeImport(f.Pkg.Pkg) {
info.linkName = parts[2]
}
case "//go:nobounds":
// Skip bounds checking in this function. Useful for some
// runtime functions.
// This is somewhat dangerous and thus only imported in packages
// that import unsafe.
if hasUnsafeImport(f.Pkg.Pkg) {
info.nobounds = true
}
case "//go:variadic":
// The //go:variadic pragma is emitted by the CGo preprocessing
// pass for C variadic functions. This includes both explicit
// (with ...) and implicit (no parameters in signature)
// functions.
if strings.HasPrefix(f.Name(), "C.") {
// This prefix cannot naturally be created, it must have
// been created as a result of CGo preprocessing.
info.variadic = true
}
}
}
// Set the importName for our exported function if we have one
if importName != "" {
if info.module == "" {
info.linkName = importName
} else {
// WebAssembly import
info.importName = importName
}
}
}
}
// getParams returns the function parameters, including the receiver at the
// start. This is an alternative to the Params member of *ssa.Function, which is
// not yet populated when the package has not yet been built.
func getParams(sig *types.Signature) []*types.Var {
params := []*types.Var{}
if sig.Recv() != nil {
params = append(params, sig.Recv())
}
for i := 0; i < sig.Params().Len(); i++ {
params = append(params, sig.Params().At(i))
}
return params
}
// globalInfo contains some information about a specific global. By default,
// linkName is equal to .RelString(nil) on a global and extern is false, but for
// some symbols this is different (due to //go:extern for example).
@@ -318,22 +26,25 @@ type globalInfo struct {
// loadASTComments loads comments on globals from the AST, for use later in the
// program. In particular, they are required for //go:extern pragmas on globals.
func (c *compilerContext) loadASTComments(pkg *loader.Package) {
for _, file := range pkg.Files {
for _, decl := range file.Decls {
switch decl := decl.(type) {
case *ast.GenDecl:
switch decl.Tok {
case token.VAR:
if len(decl.Specs) != 1 {
continue
}
for _, spec := range decl.Specs {
switch spec := spec.(type) {
case *ast.ValueSpec: // decl.Tok == token.VAR
for _, name := range spec.Names {
id := pkg.Pkg.Path() + "." + name.Name
c.astComments[id] = decl.Doc
func (c *compilerContext) loadASTComments(lprogram *loader.Program) {
c.astComments = map[string]*ast.CommentGroup{}
for _, pkgInfo := range lprogram.Sorted() {
for _, file := range pkgInfo.Files {
for _, decl := range file.Decls {
switch decl := decl.(type) {
case *ast.GenDecl:
switch decl.Tok {
case token.VAR:
if len(decl.Specs) != 1 {
continue
}
for _, spec := range decl.Specs {
switch spec := spec.(type) {
case *ast.ValueSpec: // decl.Tok == token.VAR
for _, name := range spec.Names {
id := pkgInfo.Pkg.Path() + "." + name.Name
c.astComments[id] = decl.Doc
}
}
}
}
@@ -352,6 +63,10 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
typ := g.Type().(*types.Pointer).Elem()
llvmType := c.getLLVMType(typ)
llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName)
if !info.extern {
llvmGlobal.SetInitializer(llvm.ConstNull(llvmType))
llvmGlobal.SetLinkage(llvm.InternalLinkage)
}
// Set alignment from the //go:align comment.
var alignInBits uint32
@@ -367,9 +82,11 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
}
}
if c.Debug && !info.extern {
if c.Debug() && !info.extern {
// Add debug info.
pos := c.program.Fset.Position(g.Pos())
// TODO: this should be done for every global in the program, not just
// the ones that are referenced from some code.
pos := c.ir.Program.Fset.Position(g.Pos())
diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{
Name: g.RelString(nil),
LinkageName: info.linkName,
@@ -428,23 +145,3 @@ func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) {
}
}
}
// Get all methods of a type.
func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
ms := prog.MethodSets.MethodSet(typ)
methods := make([]*types.Selection, ms.Len())
for i := 0; i < ms.Len(); i++ {
methods[i] = ms.At(i)
}
return methods
}
// Return true if this package imports "unsafe", false otherwise.
func hasUnsafeImport(pkg *types.Package) bool {
for _, imp := range pkg.Imports() {
if imp == types.Unsafe {
return true
}
}
return false
}
+8 -8
View File
@@ -16,8 +16,8 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
num := b.getValue(call.Args[0])
var syscallResult llvm.Value
switch {
case b.GOARCH == "amd64":
if b.GOOS == "darwin" {
case b.GOARCH() == "amd64":
if b.GOOS() == "darwin" {
// Darwin adds this magic number to system call numbers:
//
// > Syscall classes for 64-bit system call entry.
@@ -58,7 +58,7 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel)
syscallResult = b.CreateCall(target, args, "")
case b.GOARCH == "386" && b.GOOS == "linux":
case b.GOARCH() == "386" && b.GOOS() == "linux":
// Sources:
// syscall(2) man page
// https://stackoverflow.com/a/2538212
@@ -84,7 +84,7 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel)
syscallResult = b.CreateCall(target, args, "")
case b.GOARCH == "arm" && b.GOOS == "linux":
case b.GOARCH() == "arm" && b.GOOS() == "linux":
// Implement the EABI system call convention for Linux.
// Source: syscall(2) man page.
args := []llvm.Value{}
@@ -116,7 +116,7 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
syscallResult = b.CreateCall(target, args, "")
case b.GOARCH == "arm64" && b.GOOS == "linux":
case b.GOARCH() == "arm64" && b.GOOS() == "linux":
// Source: syscall(2) man page.
args := []llvm.Value{}
argTypes := []llvm.Type{}
@@ -149,9 +149,9 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
syscallResult = b.CreateCall(target, args, "")
default:
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS()+"/"+b.GOARCH())
}
switch b.GOOS {
switch b.GOOS() {
case "linux", "freebsd":
// Return values: r0, r1 uintptr, err Errno
// Pseudocode:
@@ -187,6 +187,6 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
retval = b.CreateInsertValue(retval, errResult, 2, "")
return retval, nil
default:
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS()+"/"+b.GOARCH())
}
}
+14 -16
View File
@@ -3,72 +3,70 @@ source_filename = "basic.go"
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
target triple = "i686--linux"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret void
}
define hidden i32 @main.addInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
define internal i32 @main.addInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = add i32 %x, %y
ret i32 %0
}
define hidden i1 @main.equalInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
define internal i1 @main.equalInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = icmp eq i32 %x, %y
ret i1 %0
}
define hidden i1 @main.floatEQ(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
define internal i1 @main.floatEQ(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fcmp oeq float %x, %y
ret i1 %0
}
define hidden i1 @main.floatNE(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
define internal i1 @main.floatNE(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fcmp une float %x, %y
ret i1 %0
}
define hidden i1 @main.floatLower(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
define internal i1 @main.floatLower(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fcmp olt float %x, %y
ret i1 %0
}
define hidden i1 @main.floatLowerEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
define internal i1 @main.floatLowerEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fcmp ole float %x, %y
ret i1 %0
}
define hidden i1 @main.floatGreater(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
define internal i1 @main.floatGreater(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fcmp ogt float %x, %y
ret i1 %0
}
define hidden i1 @main.floatGreaterEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
define internal i1 @main.floatGreaterEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fcmp oge float %x, %y
ret i1 %0
}
define hidden float @main.complexReal(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
define internal float @main.complexReal(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret float %x.r
}
define hidden float @main.complexImag(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
define internal float @main.complexImag(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret float %x.i
}
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
define internal { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fadd float %x.r, %y.r
%1 = fadd float %x.i, %y.i
@@ -77,7 +75,7 @@ entry:
ret { float, float } %3
}
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
define internal { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fsub float %x.r, %y.r
%1 = fsub float %x.i, %y.i
@@ -86,7 +84,7 @@ entry:
ret { float, float } %3
}
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
define internal { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fmul float %x.r, %y.r
%1 = fmul float %x.i, %y.i
-39
View File
@@ -1,39 +0,0 @@
package main
// Test converting floats to ints.
func f32tou32(v float32) uint32 {
return uint32(v)
}
func maxu32f() float32 {
return float32(^uint32(0))
}
func maxu32tof32() uint32 {
f := float32(^uint32(0))
return uint32(f)
}
func inftoi32() (uint32, uint32, int32, int32) {
inf := 1.0
inf /= 0.0
return uint32(inf), uint32(-inf), int32(inf), int32(-inf)
}
func u32tof32tou32(v uint32) uint32 {
return uint32(float32(v))
}
func f32tou32tof32(v float32) float32 {
return float32(uint32(v))
}
func f32tou8(v float32) uint8 {
return uint8(v)
}
func f32toi8(v float32) int8 {
return int8(v)
}
-82
View File
@@ -1,82 +0,0 @@
; ModuleID = 'float.go'
source_filename = "float.go"
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
target triple = "i686--linux"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret void
}
define hidden i32 @main.f32tou32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%positive = fcmp oge float %v, 0.000000e+00
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
%inbounds = and i1 %positive, %withinmax
%saturated = sext i1 %positive to i32
%normal = fptoui float %v to i32
%0 = select i1 %inbounds, i32 %normal, i32 %saturated
ret i32 %0
}
define hidden float @main.maxu32f(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret float 0x41F0000000000000
}
define hidden i32 @main.maxu32tof32(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret i32 -1
}
define hidden { i32, i32, i32, i32 } @main.inftoi32(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
}
define hidden i32 @main.u32tof32tou32(i32 %v, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = uitofp i32 %v to float
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
%normal = fptoui float %0 to i32
%1 = select i1 %withinmax, i32 %normal, i32 -1
ret i32 %1
}
define hidden float @main.f32tou32tof32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%positive = fcmp oge float %v, 0.000000e+00
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
%inbounds = and i1 %positive, %withinmax
%saturated = sext i1 %positive to i32
%normal = fptoui float %v to i32
%0 = select i1 %inbounds, i32 %normal, i32 %saturated
%1 = uitofp i32 %0 to float
ret float %1
}
define hidden i8 @main.f32tou8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%positive = fcmp oge float %v, 0.000000e+00
%withinmax = fcmp ole float %v, 2.550000e+02
%inbounds = and i1 %positive, %withinmax
%saturated = sext i1 %positive to i8
%normal = fptoui float %v to i8
%0 = select i1 %inbounds, i8 %normal, i8 %saturated
ret i8 %0
}
define hidden i8 @main.f32toi8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%abovemin = fcmp oge float %v, -1.280000e+02
%belowmax = fcmp ole float %v, 1.270000e+02
%inbounds = and i1 %abovemin, %belowmax
%saturated = select i1 %abovemin, i8 127, i8 -128
%isnan = fcmp uno float %v, 0.000000e+00
%remapped = select i1 %isnan, i8 0, i8 %saturated
%normal = fptosi float %v to i8
%0 = select i1 %inbounds, i8 %normal, i8 %remapped
ret i8 %0
}
+8 -10
View File
@@ -3,48 +3,46 @@ source_filename = "pointer.go"
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
target triple = "i686--linux"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret void
}
define hidden [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context, i8* %parentHandle) unnamed_addr {
define internal [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret [0 x i32] zeroinitializer
}
define hidden i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context, i8* %parentHandle) unnamed_addr {
define internal i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = bitcast i8* %x to i32*
ret i32* %0
}
define hidden i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context, i8* %parentHandle) unnamed_addr {
define internal i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = bitcast i32* %x to i8*
ret i8* %0
}
define hidden i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context, i8* %parentHandle) unnamed_addr {
define internal i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret i8* %x
}
define hidden i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context, i8* %parentHandle) unnamed_addr {
define internal i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = getelementptr inbounds i8, i8* %ptr, i32 10
ret i8* %0
}
define hidden i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
define internal i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
ret i8* %0
}
define hidden i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
define internal i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = getelementptr i32, i32* %ptr, i32 %offset
ret i32* %0
+3 -5
View File
@@ -3,19 +3,17 @@ source_filename = "slice.go"
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
target triple = "i686--linux"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret void
}
define hidden i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
define internal i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret i32 %ints.len
}
define hidden i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
define internal i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret i32 %ints.cap
}
+5 -7
View File
@@ -1,16 +1,14 @@
module github.com/tinygo-org/tinygo
go 1.13
go 1.11
require (
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
github.com/chromedp/cdproto v0.0.0-20210113043257-dabd2f2e7693
github.com/chromedp/chromedp v0.6.4
github.com/chromedp/cdproto v0.0.0-20200709115526-d1f6fc58448b
github.com/chromedp/chromedp v0.5.4-0.20200303084119-2bb39134ab9e
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
github.com/mattn/go-colorable v0.1.8
go.bug.st/serial v1.1.2
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
go.bug.st/serial v1.0.0
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4
tinygo.org/x/go-llvm v0.0.0-20201104183921-570e7a6841d9
)
+19 -28
View File
@@ -1,39 +1,33 @@
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
github.com/chromedp/cdproto v0.0.0-20210113043257-dabd2f2e7693 h1:11eq/RkpaotwdF6b1TRMcdgQUPNmyFEJOB7zLvh0O/Y=
github.com/chromedp/cdproto v0.0.0-20210113043257-dabd2f2e7693/go.mod h1:55pim6Ht4LJKdVLlyFJV/g++HsEA1hQxPbB5JyNdZC0=
github.com/chromedp/chromedp v0.6.4 h1:Gx7ZkRyrSVmbbDDja/ieNgNGJIvElroPOyeqYQGVDSY=
github.com/chromedp/chromedp v0.6.4/go.mod h1:vodUdJf5dF/b8n0UBJv6NeM/QK28RjP3j+eM7fq4+84=
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g=
github.com/chromedp/cdproto v0.0.0-20200709115526-d1f6fc58448b h1:LF+GRwyzxrO3MUzPvejv+yBup0lNG+/QdIRrkxOPseA=
github.com/chromedp/cdproto v0.0.0-20200709115526-d1f6fc58448b/go.mod h1:E6LPWRdIJc11h/di5p0rwvRmUYbhGpBEH7ZbPfzDIOE=
github.com/chromedp/chromedp v0.5.4-0.20200303084119-2bb39134ab9e h1:Hv0JVyHhbIXb9NiYQe4NsrfgrSofAp0q2FnhhJOXgi8=
github.com/chromedp/chromedp v0.5.4-0.20200303084119-2bb39134ab9e/go.mod h1:vmQMRHFZrY3T+Jv51T0n87OK/i6bK+5P9a+Fg5jPwgQ=
github.com/creack/goselect v0.1.1 h1:tiSSgKE1eJtxs1h/VgGQWuXUP0YS4CDIFMp6vaI1ls0=
github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.4 h1:5eXU1CZhpQdq5kXbKb+sECH5Ia5KiO6CYzIzdlVx6Bs=
github.com/gobwas/ws v1.0.4/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.3 h1:ZOigqf7iBxkA4jdQ3am7ATzdlOFp9YzA6NmuvEEZc9g=
github.com/gobwas/ws v1.0.3/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08 h1:V0an7KRw92wmJysvFvtqtKMAPmvS5O0jtB0nYo6t+gs=
github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08/go.mod h1:dFWs1zEqDjFtnBXsd1vPOZaLsESovai349994nHx3e0=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8=
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892 h1:6J+qramlHVLmiBOgRiBOnQkno8uprqG6YFFQTt6uYIw=
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892/go.mod h1:Pb6XcsXyropB9LNHhnqaknG/vEwYztLkQzVCHv8sQ3M=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
go.bug.st/serial v1.0.0 h1:ogEPzrllCsnG00EqKRjeYvPRsO7NJW6DqykzkdD6E/k=
go.bug.st/serial v1.0.0/go.mod h1:rpXPISGjuNjPTRTcMlxi9lN6LoIPxd1ixVjBd8aSk/Q=
go.bug.st/serial v1.1.2 h1:6xDpbta8KJ+VLRTeM8ghhxXRMLE/Lr8h9iDKwydarAY=
go.bug.st/serial v1.1.2/go.mod h1:VmYBeyJWp5BnJ0tw2NUJHZdJTGl2ecBGABHlzRK1knY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
@@ -45,11 +39,8 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2 h1:0sfSpGSa544Fwnbot3Oxq/U6SXqjty6Jy/3wRhVS7ig=
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -57,5 +48,5 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbO
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs=
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
tinygo.org/x/go-llvm v0.0.0-20201104183921-570e7a6841d9 h1:l2kTQOhqEoeDTK3ckUnwReOQwMPUmURMIdjJbeAuDT4=
tinygo.org/x/go-llvm v0.0.0-20201104183921-570e7a6841d9/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
+2 -21
View File
@@ -142,11 +142,10 @@ func getHomeDir() string {
// getGoroot returns an appropriate GOROOT from various sources. If it can't be
// found, it returns an empty string.
func getGoroot() string {
// An explicitly set GOROOT always has preference.
goroot := os.Getenv("GOROOT")
if goroot != "" {
// Convert to the standard GOROOT being referenced, if it's a TinyGo cache.
return getStandardGoroot(goroot)
// An explicitly set GOROOT always has preference.
return goroot
}
// Check for the location of the 'go' binary and base GOROOT on that.
@@ -196,21 +195,3 @@ func isGoroot(goroot string) bool {
_, err := os.Stat(filepath.Join(goroot, "src", "runtime", "internal", "sys", "zversion.go"))
return err == nil
}
// getStandardGoroot returns the physical path to a real, standard Go GOROOT
// implied by the given path.
// If the given path appears to be a TinyGo cached GOROOT, it returns the path
// referenced by symlinks contained in the cache. Otherwise, it returns the
// given path as-is.
func getStandardGoroot(path string) string {
// Check if the "bin" subdirectory of our given GOROOT is a symlink, and then
// return the _parent_ directory of its destination.
if dest, err := os.Readlink(filepath.Join(path, "bin")); nil == err {
// Clean the destination to remove any trailing slashes, so that
// filepath.Dir will always return the parent.
// (because both "/foo" and "/foo/" are valid symlink destinations,
// but filepath.Dir would return "/" and "/foo", respectively)
return filepath.Dir(filepath.Clean(dest))
}
return path
}
+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.18.0-dev"
const Version = "0.17.0-dev"
// GetGorootVersion returns the major and minor version for a given GOROOT path.
// If the goroot cannot be determined, (0, 0) is returned.
+4 -2
View File
@@ -11,17 +11,19 @@ import (
"tinygo.org/x/go-llvm"
)
var errLiteralToPointer = errors.New("interp: trying to convert literal value to pointer")
// These errors are expected during normal execution and can be recovered from
// by running the affected function at runtime instead of compile time.
var (
errIntegerAsPointer = errors.New("interp: trying to use an integer as a pointer (memory-mapped I/O?)")
errExpectedPointer = errors.New("interp: trying to use an integer as a pointer (memory-mapped I/O?)")
errUnsupportedInst = errors.New("interp: unsupported instruction")
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
errMapAlreadyCreated = errors.New("interp: map already created")
)
func isRecoverableError(err error) bool {
return err == errIntegerAsPointer || err == errUnsupportedInst || err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated
return err == errExpectedPointer || err == errUnsupportedInst || err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated
}
// ErrorLine is one line in a traceback. The position may be missing.
+1 -1
View File
@@ -102,7 +102,7 @@ func Run(mod llvm.Module, debug bool) error {
if callErr != nil {
if isRecoverableError(callErr.Err) {
if r.debug {
fmt.Fprintln(os.Stderr, "not interpreting", r.pkgName, "because of error:", callErr.Error())
fmt.Fprintln(os.Stderr, "not interpretring", r.pkgName, "because of error:", callErr.Err)
}
mem.revert()
continue
+1 -16
View File
@@ -550,22 +550,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
}
ptr, err := operands[0].asPointer(r)
if err != nil {
if err != errIntegerAsPointer {
return nil, mem, r.errorAt(inst, err)
}
// GEP on fixed pointer value (for example, memory-mapped I/O).
ptrValue := operands[0].Uint() + offset
switch operands[0].len(r) {
case 8:
locals[inst.localIndex] = literalValue{uint64(ptrValue)}
case 4:
locals[inst.localIndex] = literalValue{uint32(ptrValue)}
case 2:
locals[inst.localIndex] = literalValue{uint16(ptrValue)}
default:
panic("pointer operand is not of a known pointer size")
}
continue
return nil, mem, r.errorAt(inst, err)
}
ptr = ptr.addOffset(uint32(offset))
locals[inst.localIndex] = ptr
+2 -2
View File
@@ -348,7 +348,7 @@ func (v literalValue) clone() value {
}
func (v literalValue) asPointer(r *runner) (pointerValue, error) {
return pointerValue{}, errIntegerAsPointer
return pointerValue{}, errLiteralToPointer
}
func (v literalValue) asRawValue(r *runner) rawValue {
@@ -949,7 +949,7 @@ func (v rawValue) clone() value {
func (v rawValue) asPointer(r *runner) (pointerValue, error) {
if v.buf[0] <= 255 {
// Probably a null pointer or memory-mapped I/O.
return pointerValue{}, errIntegerAsPointer
return pointerValue{}, errExpectedPointer
}
return pointerValue{v.buf[0]}, nil
}
+271
View File
@@ -0,0 +1,271 @@
package ir
import (
"go/ast"
"go/types"
"sort"
"strings"
"github.com/tinygo-org/tinygo/loader"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
// This file provides a wrapper around go/ssa values and adds extra
// functionality to them.
// View on all functions, types, and globals in a program, with analysis
// results.
type Program struct {
Program *ssa.Program
LoaderProgram *loader.Program
mainPkg *ssa.Package
Functions []*Function
functionMap map[*ssa.Function]*Function
}
// Function or method.
type Function struct {
*ssa.Function
LLVMFn llvm.Value
module string // go:wasm-module
linkName string // go:linkname, go:export
exported bool // go:export
nobounds bool // go:nobounds
flag bool // used by dead code elimination
inline InlineType // go:inline
}
// Interface type that is at some point used in a type assert (to check whether
// it implements another interface).
type Interface struct {
Num int
Type *types.Interface
}
type InlineType int
// How much to inline.
const (
// Default behavior. The compiler decides for itself whether any given
// function will be inlined. Whether any function is inlined depends on the
// optimization level.
InlineDefault InlineType = iota
// Inline hint, just like the C inline keyword (signalled using
// //go:inline). The compiler will be more likely to inline this function,
// but it is not a guarantee.
InlineHint
// Don't inline, just like the GCC noinline attribute. Signalled using
// //go:noinline.
InlineNone
)
// Create and initialize a new *Program from a *ssa.Program.
func NewProgram(lprogram *loader.Program) *Program {
program := lprogram.LoadSSA()
program.Build()
mainPkg := program.Package(lprogram.MainPkg().Pkg)
if mainPkg == nil {
panic("could not find main package")
}
p := &Program{
Program: program,
LoaderProgram: lprogram,
mainPkg: mainPkg,
functionMap: make(map[*ssa.Function]*Function),
}
for _, pkg := range lprogram.Sorted() {
p.AddPackage(program.Package(pkg.Pkg))
}
return p
}
// Add a package to this Program. All packages need to be added first before any
// analysis is done for correct results.
func (p *Program) AddPackage(pkg *ssa.Package) {
memberNames := make([]string, 0)
for name := range pkg.Members {
memberNames = append(memberNames, name)
}
sort.Strings(memberNames)
for _, name := range memberNames {
member := pkg.Members[name]
switch member := member.(type) {
case *ssa.Function:
p.addFunction(member)
case *ssa.Type:
methods := getAllMethods(pkg.Prog, member.Type())
if !types.IsInterface(member.Type()) {
// named type
for _, method := range methods {
p.addFunction(pkg.Prog.MethodValue(method))
}
}
case *ssa.Global:
// Ignore. Globals are not handled here.
case *ssa.NamedConst:
// Ignore: these are already resolved.
default:
panic("unknown member type: " + member.String())
}
}
}
func (p *Program) addFunction(ssaFn *ssa.Function) {
if _, ok := p.functionMap[ssaFn]; ok {
return
}
f := &Function{Function: ssaFn}
f.parsePragmas()
p.Functions = append(p.Functions, f)
p.functionMap[ssaFn] = f
for _, anon := range ssaFn.AnonFuncs {
p.addFunction(anon)
}
}
// Return true if this package imports "unsafe", false otherwise.
func hasUnsafeImport(pkg *types.Package) bool {
for _, imp := range pkg.Imports() {
if imp == types.Unsafe {
return true
}
}
return false
}
func (p *Program) GetFunction(ssaFn *ssa.Function) *Function {
return p.functionMap[ssaFn]
}
func (p *Program) MainPkg() *ssa.Package {
return p.mainPkg
}
// Parse compiler directives in the preceding comments.
func (f *Function) parsePragmas() {
if f.Syntax() == nil {
return
}
if decl, ok := f.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil {
for _, comment := range decl.Doc.List {
text := comment.Text
if strings.HasPrefix(text, "//export ") {
// Rewrite '//export' to '//go:export' for compatibility with
// gc.
text = "//go:" + text[2:]
}
if !strings.HasPrefix(text, "//go:") {
continue
}
parts := strings.Fields(text)
switch parts[0] {
case "//go:export":
if len(parts) != 2 {
continue
}
f.linkName = parts[1]
f.exported = true
case "//go:wasm-module":
// Alternative comment for setting the import module.
if len(parts) != 2 {
continue
}
f.module = parts[1]
case "//go:inline":
f.inline = InlineHint
case "//go:noinline":
f.inline = InlineNone
case "//go:linkname":
if len(parts) != 3 || parts[1] != f.Name() {
continue
}
// Only enable go:linkname when the package imports "unsafe".
// This is a slightly looser requirement than what gc uses: gc
// requires the file to import "unsafe", not the package as a
// whole.
if hasUnsafeImport(f.Pkg.Pkg) {
f.linkName = parts[2]
}
case "//go:nobounds":
// Skip bounds checking in this function. Useful for some
// runtime functions.
// This is somewhat dangerous and thus only imported in packages
// that import unsafe.
if hasUnsafeImport(f.Pkg.Pkg) {
f.nobounds = true
}
}
}
}
}
func (f *Function) IsNoBounds() bool {
return f.nobounds
}
// Return true iff this function is externally visible.
func (f *Function) IsExported() bool {
return f.exported || f.CName() != ""
}
// Return the inline directive of this function.
func (f *Function) Inline() InlineType {
return f.inline
}
// Return the module name if not the default.
func (f *Function) Module() string {
return f.module
}
// Return the link name for this function.
func (f *Function) LinkName() string {
if f.linkName != "" {
return f.linkName
}
if f.Signature.Recv() != nil {
// Method on a defined type (which may be a pointer).
return f.RelString(nil)
} else {
// Bare function.
if name := f.CName(); name != "" {
// Name CGo functions directly.
return name
} else {
return f.RelString(nil)
}
}
}
// Return the name of the C function if this is a CGo wrapper. Otherwise, return
// a zero-length string.
func (f *Function) CName() string {
name := f.Name()
if strings.HasPrefix(name, "_Cfunc_") {
// emitted by `go tool cgo`
return name[len("_Cfunc_"):]
}
if strings.HasPrefix(name, "C.") {
// created by ../loader/cgo.go
return name[2:]
}
return ""
}
// Get all methods of a type.
func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
ms := prog.MethodSets.MethodSet(typ)
methods := make([]*types.Selection, ms.Len())
for i := 0; i < ms.Len(); i++ {
methods[i] = ms.At(i)
}
return methods
}
+149
View File
@@ -0,0 +1,149 @@
package ir
import (
"errors"
"go/types"
"golang.org/x/tools/go/ssa"
)
// This file implements several optimization passes (analysis + transform) to
// optimize code in SSA form before it is compiled to LLVM IR. It is based on
// the IR defined in ir.go.
// Make a readable version of a method signature (including the function name,
// excluding the receiver name). This string is used internally to match
// interfaces and to call the correct method on an interface. Examples:
//
// String() string
// Read([]byte) (int, error)
func MethodSignature(method *types.Func) string {
return method.Name() + signature(method.Type().(*types.Signature))
}
// Make a readable version of a function (pointer) signature.
// Examples:
//
// () string
// (string, int) (int, error)
func signature(sig *types.Signature) string {
s := ""
if sig.Params().Len() == 0 {
s += "()"
} else {
s += "("
for i := 0; i < sig.Params().Len(); i++ {
if i > 0 {
s += ", "
}
s += sig.Params().At(i).Type().String()
}
s += ")"
}
if sig.Results().Len() == 0 {
// keep as-is
} else if sig.Results().Len() == 1 {
s += " " + sig.Results().At(0).Type().String()
} else {
s += " ("
for i := 0; i < sig.Results().Len(); i++ {
if i > 0 {
s += ", "
}
s += sig.Results().At(i).Type().String()
}
s += ")"
}
return s
}
// Simple pass that removes dead code. This pass makes later analysis passes
// more useful.
func (p *Program) SimpleDCE() error {
// Unmark all functions.
for _, f := range p.Functions {
f.flag = false
}
// Initial set of live functions. Include main.main, *.init and runtime.*
// functions.
main, ok := p.mainPkg.Members["main"].(*ssa.Function)
if !ok {
if p.mainPkg.Members["main"] == nil {
return errors.New("function main is undeclared in the main package")
} else {
return errors.New("cannot declare main - must be func")
}
}
runtimePkg := p.Program.ImportedPackage("runtime")
mathPkg := p.Program.ImportedPackage("math")
taskPkg := p.Program.ImportedPackage("internal/task")
p.GetFunction(main).flag = true
worklist := []*ssa.Function{main}
for _, f := range p.Functions {
if f.exported || f.Synthetic == "package initializer" || f.Pkg == runtimePkg || f.Pkg == taskPkg || (f.Pkg == mathPkg && f.Pkg != nil) {
if f.flag {
continue
}
f.flag = true
worklist = append(worklist, f.Function)
}
}
// Mark all called functions recursively.
for len(worklist) != 0 {
f := worklist[len(worklist)-1]
worklist = worklist[:len(worklist)-1]
for _, block := range f.Blocks {
for _, instr := range block.Instrs {
if instr, ok := instr.(*ssa.MakeInterface); ok {
for _, sel := range getAllMethods(p.Program, instr.X.Type()) {
fn := p.Program.MethodValue(sel)
callee := p.GetFunction(fn)
if callee == nil {
// TODO: why is this necessary?
p.addFunction(fn)
callee = p.GetFunction(fn)
}
if !callee.flag {
callee.flag = true
worklist = append(worklist, callee.Function)
}
}
}
for _, operand := range instr.Operands(nil) {
if operand == nil || *operand == nil {
continue
}
switch operand := (*operand).(type) {
case *ssa.Function:
f := p.GetFunction(operand)
if f == nil {
// FIXME HACK: this function should have been
// discovered already. It is not for bound methods.
p.addFunction(operand)
f = p.GetFunction(operand)
}
if !f.flag {
f.flag = true
worklist = append(worklist, operand)
}
}
}
}
}
}
// Remove unmarked functions.
livefunctions := []*Function{}
for _, f := range p.Functions {
if f.flag {
livefunctions = append(livefunctions, f)
} else {
delete(p.functionMap, f.Function)
}
}
p.Functions = livefunctions
return nil
}
+21 -28
View File
@@ -2,7 +2,6 @@ package loader
import (
"bytes"
"crypto/sha512"
"encoding/json"
"errors"
"fmt"
@@ -12,12 +11,12 @@ import (
"go/token"
"go/types"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"syscall"
"github.com/tinygo-org/tinygo/cgo"
"github.com/tinygo-org/tinygo/compileopts"
@@ -53,8 +52,7 @@ type PackageJSON struct {
CFiles []string
// Dependency information
Imports []string
ImportMap map[string]string
Imports []string
// Error information
Error *struct {
@@ -68,12 +66,10 @@ type PackageJSON struct {
type Package struct {
PackageJSON
program *Program
Files []*ast.File
FileHashes map[string][]byte
CFlags []string // CFlags used during CGo preprocessing (only set if CGo is used)
Pkg *types.Package
info types.Info
program *Program
Files []*ast.File
Pkg *types.Package
info types.Info
}
// Load loads the given package with all dependencies (including the runtime
@@ -113,7 +109,10 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
err = cmd.Run()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
os.Exit(exitErr.ExitCode())
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)
}
@@ -122,8 +121,7 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
decoder := json.NewDecoder(buf)
for {
pkg := &Package{
program: p,
FileHashes: make(map[string][]byte),
program: p,
info: types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
@@ -282,15 +280,17 @@ func (p *Program) Parse() error {
}
// parseFile is a wrapper around parser.ParseFile.
func (p *Package) parseFile(path string, mode parser.Mode) (*ast.File, error) {
originalPath := p.program.getOriginalPath(path)
data, err := ioutil.ReadFile(path)
func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
if p.fset == nil {
p.fset = token.NewFileSet()
}
rd, err := os.Open(path)
if err != nil {
return nil, err
}
sum := sha512.Sum512_224(data)
p.FileHashes[originalPath] = sum[:]
return parser.ParseFile(p.program.fset, originalPath, data, mode)
defer rd.Close()
return parser.ParseFile(p.fset, p.getOriginalPath(path), rd, mode)
}
// Parse parses and typechecks this package.
@@ -366,7 +366,7 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
if !filepath.IsAbs(file) {
file = filepath.Join(p.Dir, file)
}
f, err := p.parseFile(file, parser.ParseComments)
f, err := p.program.parseFile(file, parser.ParseComments)
if err != nil {
fileErrs = append(fileErrs, err)
return
@@ -388,11 +388,7 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
if p.program.clangHeaders != "" {
cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.program.clangHeaders)
}
p.CFlags = cflags
generated, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.program.fset, cflags)
for path, hash := range accessedFiles {
p.FileHashes[path] = hash
}
generated, ldflags, errs := cgo.Process(files, p.program.workingDir, p.program.fset, cflags)
if errs != nil {
fileErrs = append(fileErrs, errs...)
}
@@ -415,9 +411,6 @@ func (p *Package) Import(to string) (*types.Package, error) {
if to == "unsafe" {
return types.Unsafe, nil
}
if newTo, ok := p.ImportMap[to]; ok && !strings.HasSuffix(newTo, ".test]") {
to = newTo
}
if imported, ok := p.program.Packages[to]; ok {
return imported.Pkg, nil
} else {
+16 -36
View File
@@ -15,16 +15,14 @@ import (
"path/filepath"
"runtime"
"strings"
"sync/atomic"
"syscall"
"time"
"github.com/mattn/go-colorable"
"github.com/tinygo-org/tinygo/builder"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv"
"github.com/tinygo-org/tinygo/interp"
"github.com/tinygo-org/tinygo/loader"
"github.com/tinygo-org/tinygo/transform"
"tinygo.org/x/go-llvm"
"go.bug.st/serial"
@@ -165,7 +163,10 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
if err != nil {
// Propagate the exit code
if err, ok := err.(*exec.ExitError); ok {
os.Exit(err.ExitCode())
if status, ok := err.Sys().(syscall.WaitStatus); ok {
os.Exit(status.ExitStatus())
}
os.Exit(1)
}
return &commandError{"failed to run compiled binary", result.Binary, err}
}
@@ -262,7 +263,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
// Create the command.
flashCmd := config.Target.FlashCommand
fileToken := "{" + fileExt[1:] + "}"
flashCmd = strings.ReplaceAll(flashCmd, fileToken, result.Binary)
flashCmd = strings.Replace(flashCmd, fileToken, result.Binary, -1)
if port == "" && strings.Contains(flashCmd, "{port}") {
var err error
@@ -272,7 +273,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
}
}
flashCmd = strings.ReplaceAll(flashCmd, "{port}", port)
flashCmd = strings.Replace(flashCmd, "{port}", port, -1)
// Execute the command.
var cmd *exec.Cmd
@@ -391,7 +392,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
if ocdOutput {
// Make it clear which output is from the daemon.
w := &ColorWriter{
Out: colorable.NewColorableStderr(),
Out: os.Stderr,
Prefix: "openocd: ",
Color: TermColorYellow,
}
@@ -406,7 +407,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
if ocdOutput {
// Make it clear which output is from the daemon.
w := &ColorWriter{
Out: colorable.NewColorableStderr(),
Out: os.Stderr,
Prefix: "jlink: ",
Color: TermColorYellow,
}
@@ -463,15 +464,8 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
}
defer func() {
daemon.Process.Signal(os.Interrupt)
var stopped uint32
go func() {
time.Sleep(time.Millisecond * 100)
if atomic.LoadUint32(&stopped) == 0 {
daemon.Process.Kill()
}
}()
// Maybe we should send a .Kill() after x seconds?
daemon.Wait()
atomic.StoreUint32(&stopped, 1)
}()
}
@@ -773,15 +767,6 @@ func printCompilerError(logln func(...interface{}), err error) {
logln()
}
}
case transform.CoroutinesError:
logln(err.Pos.String() + ": " + err.Msg)
logln("\ntraceback:")
for _, line := range err.Traceback {
logln(line.Name)
if line.Position.IsValid() {
logln("\t" + line.Position.String())
}
}
case loader.Errors:
logln("#", err.Pkg.ImportPath)
for _, err := range err.Errs {
@@ -947,17 +932,9 @@ func main() {
fmt.Fprintf(os.Stderr, "Unknown library: %s\n", name)
os.Exit(1)
}
tmpdir, err := ioutil.TempDir("", "tinygo*")
if err != nil {
handleCompilerError(err)
}
defer os.RemoveAll(tmpdir)
path, err := lib.Load(*target, tmpdir)
path, err := lib.Load(*target)
handleCompilerError(err)
err = copyFile(path, outpath)
if err != nil {
handleCompilerError(err)
}
copyFile(path, outpath)
case "flash", "gdb":
pkgName := filepath.ToSlash(flag.Arg(0))
if command == "flash" {
@@ -1076,7 +1053,10 @@ func main() {
err = cmd.Run()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
os.Exit(exitErr.ExitCode())
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)
+2 -14
View File
@@ -58,9 +58,6 @@ func TestCompiler(t *testing.T) {
if runtime.GOOS != "windows" {
t.Run("Host", func(t *testing.T) {
runPlatTests("", matches, t)
if runtime.GOOS == "darwin" {
runTest("testdata/libc/env.go", "", t, []string{"ENV1=VALUE1", "ENV2=VALUE2"}...)
}
})
}
@@ -107,7 +104,6 @@ func TestCompiler(t *testing.T) {
t.Run("WASI", func(t *testing.T) {
runPlatTests("wasi", matches, t)
runTest("testdata/libc/env.go", "wasi", t, []string{"ENV1=VALUE1", "ENV2=VALUE2"}...)
})
}
}
@@ -117,6 +113,7 @@ func runPlatTests(target string, matches []string, t *testing.T) {
for _, path := range matches {
path := path // redefine to avoid race condition
t.Run(filepath.Base(path), func(t *testing.T) {
t.Parallel()
runTest(path, target, t)
@@ -136,7 +133,7 @@ func runBuild(src, out string, opts *compileopts.Options) error {
return Build(src, out, opts)
}
func runTest(path, target string, t *testing.T, environmentVars ...string) {
func runTest(path, target string, t *testing.T) {
// Get the expected output for this test.
txtpath := path[:len(path)-3] + ".txt"
if path[len(path)-1] == os.PathSeparator {
@@ -185,7 +182,6 @@ func runTest(path, target string, t *testing.T, environmentVars ...string) {
ranTooLong := false
if target == "" {
cmd = exec.Command(binary)
cmd.Env = append(cmd.Env, environmentVars...)
} else {
spec, err := compileopts.LoadTarget(target)
if err != nil {
@@ -197,14 +193,6 @@ func runTest(path, target string, t *testing.T, environmentVars ...string) {
args := append(spec.Emulator[1:], binary)
cmd = exec.Command(spec.Emulator[0], args...)
}
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
for _, v := range environmentVars {
cmd.Args = append(cmd.Args, "--env", v)
}
} else {
cmd.Env = append(cmd.Env, environmentVars...)
}
}
stdout := &bytes.Buffer{}
cmd.Stdout = stdout
+1 -1
View File
@@ -15,7 +15,7 @@ func main() {
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
sensor := machine.ADC{machine.ADC2}
sensor.Configure(machine.ADCConfig{})
sensor.Configure()
for {
val := sensor.Get()
+13
View File
@@ -77,9 +77,22 @@ func Current() *Task
// This is implemented inside the compiler.
func Pause()
type taskHolder interface {
setState(*rawState) *rawState
returnTo(*rawState)
returnCurrent()
setReturnPtr(unsafe.Pointer)
getReturnPtr() unsafe.Pointer
}
// If there are no direct references to the task methods, they will not be discovered by the compiler, and this will trigger a compiler error.
// Instantiating this interface forces discovery of these methods.
var _ = taskHolder((*Task)(nil))
func fake() {
// Hack to ensure intrinsics are discovered.
Current()
go func() {}()
Pause()
}
-12
View File
@@ -1,12 +0,0 @@
package machine
// Hardware abstraction layer for the analog-to-digital conversion (ADC)
// peripheral.
// ADCConfig holds ADC configuration parameters. If left unspecified, the zero
// value of each parameter will use the peripheral's default settings.
type ADCConfig struct {
Reference uint32 // analog reference voltage (AREF) in millivolts
Resolution uint32 // number of bits for a single conversion (e.g., 8, 10, 12)
Samples uint32 // number of samples for a single conversion (e.g., 4, 8, 16, 32)
}
+2 -2
View File
@@ -85,6 +85,6 @@ const (
// I2C pins
const (
I2C0_SDA_PIN = PB7
I2C0_SCL_PIN = PB6
SDA_PIN = PB7
SCL_PIN = PB6
)
+2 -2
View File
@@ -66,8 +66,8 @@ const (
SDA_PIN = P0_05 // I2C0 external
SCL_PIN = P0_04 // I2C0 external
SDA1_PIN = P1_10 // I2C1 internal
SCL1_PIN = P1_12 // I2C1 internal
SDA1_PIN = P0_00 // I2C1 internal
SCL1_PIN = P0_01 // I2C1 internal
)
// SPI pins (internal flash)
+1 -1
View File
@@ -2,7 +2,7 @@
package machine
const HasLowFrequencyCrystal = false
const HasLowFrequencyCrystal = true
// GPIO Pins
const (
-243
View File
@@ -1,243 +0,0 @@
// +build grandcentral_m4
package machine
// Digital pins
const (
// = Pin Alt. Function SERCOM PWM Timer Interrupt
// ------ -------------------- -------- ----------- -----------
D0 = PB25 // UART1 RX 0[1] EXTI9
D1 = PB24 // UART1 TX 0[0] EXTI8
D2 = PC18 // TCC0[2] EXTI2
D3 = PC19 // TCC0[3] EXTI3
D4 = PC20 // TCC0[4] EXTI4
D5 = PC21 // TCC0[5] EXTI5
D6 = PD20 // TCC1[0] EXTI10
D7 = PD21 // TCC1[1] EXTI11
D8 = PB18 // TCC1[0] EXTI2
D9 = PB02 // TC6[0] EXTI3
D10 = PB22 // TC7[0] EXTI6
D11 = PB23 // EXTI7
D12 = PB00 // TC7[0] EXTI0
D13 = PB01 // On-board LED TC7[1] EXTI1
D14 = PB16 // UART4 TX, I2S0 SCK 5[0] TC6[0] EXTI0
D15 = PB17 // UART4 RX, I2S0 MCK 5[1] EXTI1
D16 = PC22 // UART3 TX 1[0] EXTI6
D17 = PC23 // UART3 RX 1[1] EXTI6
D18 = PB12 // UART2 TX 4[0] TCC3[0] EXTI12
D19 = PB13 // UART2 RX 4[1] TCC3[1] EXTI13
D20 = PB20 // I2C0 SDA 3[0] EXTI4
D21 = PB21 // I2C0 SCL 3[1] EXTI5
D22 = PD12 // EXTI7
D23 = PA15 // TCC2[1] EXTI15
D24 = PC17 // I2C1 SCL 6[1] TCC0[1] EXTI1
D25 = PC16 // I2C1 SDA 6[0] TCC0[0] EXTI0
D26 = PA12 // PCC DEN1 TC2[0] EXTI12
D27 = PA13 // PCC DEN2 TC2[1] EXTI13
D28 = PA14 // PCC CLK TCC2[0] EXTI14
D29 = PB19 // PCC XCLK EXTI3
D30 = PA23 // PCC D7 TC4[1] EXTI7
D31 = PA22 // PCC D6, I2S0 SDI TC4[0] EXTI6
D32 = PA21 // PCC D5, I2S0 SDO EXTI5
D33 = PA20 // PCC D4, I2S0 FS EXTI4
D34 = PA19 // PCC D3 TC3[1] EXTI3
D35 = PA18 // PCC D2 TC3[0] EXTI2
D36 = PA17 // PCC D1 EXTI1
D37 = PA16 // PCC D0 EXTI0
D38 = PB15 // PCC D9 TCC4[1] EXTI15
D39 = PB14 // PCC D8 TCC4[0] EXTI14
D40 = PC13 // PCC D11 EXTI13
D41 = PC12 // PCC D10 EXTI12
D42 = PC15 // PCC D13 EXTI15
D43 = PC14 // PCC D12 EXTI14
D44 = PC11 // EXTI11
D45 = PC10 // EXTI10
D46 = PC06 // EXTI6
D47 = PC07 // EXTI5
D48 = PC04 // EXTI4
D49 = PC05 // EXTI5
D50 = PD11 // SPI0 SDI 7[3] EXTI11
D51 = PD08 // SPI0 SDO 7[0] EXTI8
D52 = PD09 // SPI0 SCK 7[1] EXTI9
D53 = PD10 // SPI0 CS EXTI10
D54 = PB05 // ADC1 (A8) EXTI5
D55 = PB06 // ADC1 (A9) EXTI6
D56 = PB07 // ADC1 (A10) EXTI7
D57 = PB08 // ADC1 (A11) EXTI8
D58 = PB09 // ADC1 (A12) EXTI9
D59 = PA04 // ADC0 (A13) TC0[0] EXTI4
D60 = PA06 // ADC0 (A14) TC1[0] EXTI6
D61 = PA07 // ADC0 (A15) TC1[1] EXTI7
D62 = PB20 // I2C0 SDA 3[0] TCC1[2] EXTI4
D63 = PB21 // I2C0 SCL 3[1] TCC1[3] EXTI5
D64 = PD11 // SPI0 SDI 7[3] EXTI6
D65 = PD08 // SPI0 SDO 7[0] EXTI3
D66 = PD09 // SPI0 SCK 7[1] EXTI4
D67 = PA02 // ADC0 (A0), DAC0 EXTI2
D68 = PA05 // ADC0 (A1), DAC1 EXTI5
D69 = PB03 // ADC0 (A2) TC6[1] EXTI3
D70 = PC00 // ADC1 (A3) EXTI0
D71 = PC01 // ADC1 (A4) EXTI1
D72 = PC02 // ADC1 (A5) EXTI2
D73 = PC03 // ADC1 (A6) EXTI3
D74 = PB04 // ADC1 (A7) EXTI4
D75 = PC31 // UART RX LED
D76 = PC30 // UART TX LED
D77 = PA27 // USB HOST EN
D78 = PA24 // USB DM EXTI8
D79 = PA25 // USB DP EXTI9
D80 = PB29 // SD/SPI1 SDI 2[3]
D81 = PB27 // SD/SPI1 SCK 2[1]
D82 = PB26 // SD/SPI1 SDO 2[0]
D83 = PB28 // SD/SPI1 CS
D84 = PA03 // AREF EXTI3
D85 = PA02 // DAC0 EXTI2
D86 = PA05 // DAC1 EXTI5
D87 = PB01 // On-board LED (D13) TC7[1] EXTI1
D88 = PC24 // On-board NeoPixel
D89 = PB10 // QSPI SCK EXTI10
D90 = PB11 // QSPI CS EXTI11
D91 = PA08 // QSPI ID0 EXTI(NMI)
D92 = PA09 // QSPI ID1 EXTI9
D93 = PA10 // QSPI ID2 EXTI10
D94 = PA11 // QSPI ID3 EXTI11
D95 = PB31 // SD Detect EXTI15
D96 = PB30 // SWO EXTI14
)
// Analog pins
const (
A0 = D67 // (PA02) ADC0 ch. 0,
A1 = D68 // (PA05) ADC0 ch. 5,
A2 = D69 // (PB03) ADC0 ch. 15
A3 = D70 // (PC00) ADC1 ch. 10
A4 = D71 // (PC01) ADC1 ch. 11
A5 = D72 // (PC02) ADC1 ch. 4
A6 = D73 // (PC03) ADC1 ch. 5
A7 = D74 // (PB04) ADC1 ch. 6
A8 = D54 // (PB05) ADC1 ch. 7
A9 = D55 // (PB06) ADC1 ch. 8
A10 = D56 // (PB07) ADC1 ch. 9
A11 = D57 // (PB08) ADC1 ch. 0
A12 = D58 // (PB09) ADC1 ch. 1
A13 = D59 // (PA04) ADC0 ch. 4
A14 = D60 // (PA06) ADC0 ch. 6
A15 = D61 // (PA07) ADC0 ch. 7
AREF = D84 // (PA03)
)
// LED pins
const (
LED_PIN = D13 // (PB01), also on D87
UART_RX_LED_PIN = D75 // (PC31)
UART_TX_LED_PIN = D76 // (PC30)
NEOPIXEL_PIN = D88 // (PC24)
// aliases used by examples and drivers
LED = LED_PIN
LED_RX = UART_RX_LED_PIN
LED_TX = UART_TX_LED_PIN
NEOPIXEL = NEOPIXEL_PIN
)
// UART pins
const (
UART1_RX_PIN = D0 // (PB25)
UART1_TX_PIN = D1 // (PB24)
UART2_RX_PIN = D19 // (PB13)
UART2_TX_PIN = D18 // (PB12)
UART3_RX_PIN = D17 // (PC23)
UART3_TX_PIN = D16 // (PC22)
UART4_RX_PIN = D15 // (PB17)
UART4_TX_PIN = D14 // (PB16)
UART_RX_PIN = UART1_RX_PIN // default pins
UART_TX_PIN = UART1_TX_PIN //
)
// SPI pins
const (
SPI0_SCK_PIN = D66 // (PD09), also on D52
SPI0_SDO_PIN = D65 // (PD08), also on D51
SPI0_SDI_PIN = D64 // (PD11), also on D50
SPI0_CS_PIN = D53 // (PD10)
SPI1_SCK_PIN = D81 // (PB27)
SPI1_SDO_PIN = D82 // (PB26)
SPI1_SDI_PIN = D80 // (PB29)
SPI_SCK_PIN = SPI0_SCK_PIN // default pins
SPI_SDO_PIN = SPI0_SDO_PIN //
SPI_SDI_PIN = SPI0_SDI_PIN //
SPI_CS_PIN = SPI0_CS_PIN //
)
// I2C pins
const (
I2C0_SDA_PIN = D62 // (PB20), also on D20
I2C0_SCL_PIN = D63 // (PB21), also on D21
I2C1_SDA_PIN = D25 // (PC16)
I2C1_SCL_PIN = D24 // (PC17)
I2C_SDA_PIN = I2C0_SDA_PIN // default pins
I2C_SCL_PIN = I2C0_SCL_PIN //
SDA_PIN = I2C_SDA_PIN // unconventional pin names
SCL_PIN = I2C_SCL_PIN // (required by machine_atsamd51.go)
)
// I2S pins
const (
I2S0_SCK_PIN = D14 // (PB16)
I2S0_MCK_PIN = D15 // (PB17)
I2S0_FS_PIN = D33 // (PA20)
I2S0_SDO_PIN = D32 // (PA21)
I2S0_SDI_PIN = D31 // (PA22)
I2S_SCK_PIN = I2S0_SCK_PIN // default pins
I2S_WS_PIN = I2S0_FS_PIN //
I2S_SD_PIN = I2S0_SDO_PIN //
)
// SD card pins
const (
SD0_SCK_PIN = D81 // (PB27)
SD0_SDO_PIN = D82 // (PB26)
SD0_SDI_PIN = D80 // (PB29)
SD0_CS_PIN = D83 // (PB28)
SD0_DET_PIN = D95 // (PB31)
SDCARD_SCK_PIN = SD0_SCK_PIN // default pins
SDCARD_SDO_PIN = SD0_SDO_PIN //
SDCARD_SDI_PIN = SD0_SDI_PIN //
SDCARD_CS_PIN = SD0_CS_PIN //
SDCARD_DET_PIN = SD0_DET_PIN //
)
// Other peripheral constants
const (
RESET_MAGIC_VALUE = 0xF01669EF // Used to reset into bootloader
)
// USB CDC pins
const (
USBCDC_HOSTEN_PIN = D77 // (PA27) host enable
USBCDC_DM_PIN = D78 // (PA24) D-
USBCDC_DP_PIN = D79 // (PA25) D+
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Adafruit Grand Central M4"
usb_STRING_MANUFACTURER = "Adafruit"
)
var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x8031
)
@@ -1,63 +0,0 @@
// +build grandcentral_m4
package machine
import (
"device/sam"
"runtime/interrupt"
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM4_2, UART2.handleInterrupt)
UART3.Interrupt = interrupt.New(sam.IRQ_SERCOM1_2, UART3.handleInterrupt)
UART4.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, UART4.handleInterrupt)
}
// UART on the Grand Central M4
var (
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM0_USART_INT,
SERCOM: 0,
}
UART2 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM4_USART_INT,
SERCOM: 4,
}
UART3 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM1_USART_INT,
SERCOM: 1,
}
UART4 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM5_USART_INT,
SERCOM: 5,
}
)
// I2C on the Grand Central M4
var (
I2C0 = I2C{
Bus: sam.SERCOM3_I2CM,
SERCOM: 3,
}
I2C1 = I2C{
Bus: sam.SERCOM6_I2CM,
SERCOM: 6,
}
)
// SPI on the Grand Central M4
var (
SPI0 = SPI{
Bus: sam.SERCOM7_SPIM,
SERCOM: 7,
}
SPI1 = SPI{ // SD card
Bus: sam.SERCOM2_SPIM,
SERCOM: 2,
}
)
+10 -10
View File
@@ -90,16 +90,16 @@ const (
// LED matrix pins
const (
LED_COL_1 Pin = P0_28
LED_COL_2 Pin = P0_11
LED_COL_3 Pin = P0_31
LED_COL_4 Pin = P1_05
LED_COL_5 Pin = P0_30
LED_ROW_1 Pin = P0_21
LED_ROW_2 Pin = P0_22
LED_ROW_3 Pin = P0_15
LED_ROW_4 Pin = P0_24
LED_ROW_5 Pin = P0_19
LED_COL_1 Pin = P4
LED_COL_2 Pin = P7
LED_COL_3 Pin = P3
LED_COL_4 Pin = P6
LED_COL_5 Pin = P10
LED_ROW_1 Pin = P21
LED_ROW_2 Pin = P22
LED_ROW_3 Pin = P23
LED_ROW_4 Pin = P24
LED_ROW_5 Pin = P25
)
// USB CDC identifiers
-84
View File
@@ -1,84 +0,0 @@
// +build nicenano
package machine
const HasLowFrequencyCrystal = true
// GPIO Pins
const (
D006 = P0_06
D008 = P0_08
D017 = P0_17
D020 = P0_20
D022 = P0_22
D024 = P0_24
D100 = P1_00
D011 = P0_11
D104 = P1_04
D106 = P1_06
D004 = P0_04 // AIN2; P0.04 (AIN2) is used to read the voltage of the battery via ADC. It cant be used for any other function.
D013 = P0_13 // VCC 3.3V; P0.13 on VCC shuts off the power to VCC when you set it to high; This saves on battery immensely for LEDs of all kinds that eat power even when off
D115 = P1_15
D113 = P1_13
D031 = P0_31 // AIN7
D029 = P0_29 // AIN5
D002 = P0_02 // AIN0
D111 = P1_11
D010 = P0_10 // NFC2
D009 = P0_09 // NFC1
D026 = P0_26
D012 = P0_12
D101 = P1_01
D102 = P1_02
D107 = P1_07
)
// Analog Pins
const (
AIN2 = P0_04 // Battery
AIN7 = P0_31
AIN5 = P0_29
AIN0 = P0_02
)
const (
LED = P0_15
)
// UART0 pins (logical UART1)
const (
UART_RX_PIN = P0_06
UART_TX_PIN = P0_08
)
// UART0 is the USB device
var (
UART0 = USB
)
// I2C pins
const (
SDA_PIN = P0_17 // I2C0 external
SCL_PIN = P0_20 // I2C0 external
)
// SPI pins
const (
SPI0_SCK_PIN = P0_22 // SCK
SPI0_SDO_PIN = P0_24 // SDO
SPI0_SDI_PIN = P1_00 // SDI
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "nice!nano"
usb_STRING_MANUFACTURER = "Nice Keyboards"
)
var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x0029
)
@@ -1,53 +0,0 @@
// +build nrf52840_mdk_usb_dongle
package machine
const HasLowFrequencyCrystal = true
// LEDs on the nrf52840-mdk-usb-dongle
const (
LED Pin = LED_GREEN
LED_GREEN Pin = 22
LED_RED Pin = 23
LED_BLUE Pin = 24
)
// RESET/USR button, depending on value of PSELRESET UICR register
const (
BUTTON Pin = 18
)
// UART pins
const (
UART_TX_PIN Pin = NoPin
UART_RX_PIN Pin = NoPin
)
// UART0 is the USB device
var (
UART0 = USB
)
// I2C pins (unused)
const (
SDA_PIN = NoPin
SCL_PIN = NoPin
)
// SPI pins (unused)
const (
SPI0_SCK_PIN = NoPin
SPI0_SDO_PIN = NoPin
SPI0_SDI_PIN = NoPin
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Makerdiary nRF52840 MDK USB Dongle"
usb_STRING_MANUFACTURER = "Makerdiary"
)
var (
usb_VID uint16 = 0x1915
usb_PID uint16 = 0xCAFE
)
+1 -1
View File
@@ -38,7 +38,7 @@ const (
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Makerdiary nRF52840 MDK"
usb_STRING_PRODUCT = "Makerdiary nRF52840 MDK USB Dongle"
usb_STRING_MANUFACTURER = "Makerdiary"
)
+2 -2
View File
@@ -120,6 +120,6 @@ const (
// I2C pins
const (
I2C0_SCL_PIN = PB6
I2C0_SDA_PIN = PB7
SCL_PIN = PB6
SDA_PIN = PB7
)
-45
View File
@@ -1,45 +0,0 @@
// +build nucleol552ze
package machine
import (
"device/stm32"
"runtime/interrupt"
)
const (
LED = LED_BUILTIN
LED_BUILTIN = LED_GREEN
LED_GREEN = PC7
LED_BLUE = PB7
LED_RED = PA9
)
const (
BUTTON = BUTTON_USER
BUTTON_USER = PC13
)
// UART pins
const (
// PG7 and PG8 are connected to the ST-Link Virtual Com Port (VCP)
UART_TX_PIN = PG7
UART_RX_PIN = PG8
UART_ALT_FN = 8 // GPIO_AF8_LPUART1
)
var (
// LPUART1 is the hardware serial port connected to the onboard ST-LINK
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
// Both UART0 and UART1 refer to LPUART1.
UART0 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.LPUART1,
AltFuncSelector: UART_ALT_FN,
}
UART1 = &UART0
)
func init() {
UART0.Interrupt = interrupt.New(stm32.IRQ_LPUART1, UART0.handleInterrupt)
}
+5
View File
@@ -0,0 +1,5 @@
// +build bluepill nucleof103rb stm32f4
package machine
// Peripheral abstraction layer for the stm32.
-12
View File
@@ -66,15 +66,3 @@ var (
}
SPI1 = &SPI0
)
const (
I2C0_SCL_PIN = PB6
I2C0_SDA_PIN = PB9
)
var (
I2C0 = I2C{
Bus: stm32.I2C1,
AltFuncSelector: AF4_I2C1_2_3,
}
)
+14 -22
View File
@@ -136,11 +136,9 @@ const (
)
var (
UART0 = &UART1 // alias UART0 to UART1
UART1 = UART{
Bus: nxp.LPUART6,
Buffer: NewRingBuffer(),
txBuffer: NewRingBuffer(),
Buffer: NewRingBuffer(),
Bus: nxp.LPUART6,
muxRX: muxSelect{ // D0 (PA3 [AD_B0_03])
mux: nxp.IOMUXC_LPUART6_RX_SELECT_INPUT_DAISY_GPIO_AD_B0_03_ALT2,
sel: &nxp.IOMUXC.LPUART6_RX_SELECT_INPUT,
@@ -151,9 +149,8 @@ var (
},
}
UART2 = UART{
Bus: nxp.LPUART4,
Buffer: NewRingBuffer(),
txBuffer: NewRingBuffer(),
Buffer: NewRingBuffer(),
Bus: nxp.LPUART4,
muxRX: muxSelect{ // D7 (PB17 [B1_01])
mux: nxp.IOMUXC_LPUART4_RX_SELECT_INPUT_DAISY_GPIO_B1_01_ALT2,
sel: &nxp.IOMUXC.LPUART4_RX_SELECT_INPUT,
@@ -164,9 +161,8 @@ var (
},
}
UART3 = UART{
Bus: nxp.LPUART2,
Buffer: NewRingBuffer(),
txBuffer: NewRingBuffer(),
Buffer: NewRingBuffer(),
Bus: nxp.LPUART2,
muxRX: muxSelect{ // D15 (PA19 [AD_B1_03])
mux: nxp.IOMUXC_LPUART2_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_03_ALT2,
sel: &nxp.IOMUXC.LPUART2_RX_SELECT_INPUT,
@@ -177,9 +173,8 @@ var (
},
}
UART4 = UART{
Bus: nxp.LPUART3,
Buffer: NewRingBuffer(),
txBuffer: NewRingBuffer(),
Buffer: NewRingBuffer(),
Bus: nxp.LPUART3,
muxRX: muxSelect{ // D16 (PA23 [AD_B1_07])
mux: nxp.IOMUXC_LPUART3_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_07_ALT2,
sel: &nxp.IOMUXC.LPUART3_RX_SELECT_INPUT,
@@ -190,9 +185,8 @@ var (
},
}
UART5 = UART{
Bus: nxp.LPUART8,
Buffer: NewRingBuffer(),
txBuffer: NewRingBuffer(),
Buffer: NewRingBuffer(),
Bus: nxp.LPUART8,
muxRX: muxSelect{ // D21 (PA27 [AD_B1_11])
mux: nxp.IOMUXC_LPUART8_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_11_ALT2,
sel: &nxp.IOMUXC.LPUART8_RX_SELECT_INPUT,
@@ -203,17 +197,15 @@ var (
},
}
UART6 = UART{
Bus: nxp.LPUART1,
Buffer: NewRingBuffer(),
txBuffer: NewRingBuffer(),
Buffer: NewRingBuffer(),
Bus: nxp.LPUART1,
// LPUART1 not connected via IOMUXC
// RX: D24 (PA12 [AD_B0_12])
// TX: D25 (PA13 [AD_B0_13])
}
UART7 = UART{
Bus: nxp.LPUART7,
Buffer: NewRingBuffer(),
txBuffer: NewRingBuffer(),
Buffer: NewRingBuffer(),
Bus: nxp.LPUART7,
muxRX: muxSelect{ // D28 (PC18 [EMC_32])
mux: nxp.IOMUXC_LPUART7_RX_SELECT_INPUT_DAISY_GPIO_EMC_32_ALT2,
sel: &nxp.IOMUXC.LPUART7_RX_SELECT_INPUT,
+1 -1
View File
@@ -1,4 +1,4 @@
// +build avr nrf sam stm32,!stm32f7x2,!stm32l5x2,!stm32l0 fe310 k210
// +build avr nrf sam stm32,!stm32f407,!stm32f7x2,!stm32l0 fe310 k210
package machine
+1 -116
View File
@@ -5,8 +5,6 @@ package machine
import (
"device/avr"
"runtime/interrupt"
"runtime/volatile"
"unsafe"
)
// I2CConfig is used to store config info for I2C.
@@ -15,7 +13,7 @@ type I2CConfig struct {
}
// Configure is intended to setup the I2C interface.
func (i2c I2C) Configure(config I2CConfig) error {
func (i2c I2C) Configure(config I2CConfig) {
// Default I2C bus speed is 100 kHz.
if config.Frequency == 0 {
config.Frequency = TWI_FREQ_100KHZ
@@ -35,8 +33,6 @@ func (i2c I2C) Configure(config I2CConfig) error {
// Enable twi module.
avr.TWCR.Set(avr.TWCR_TWEN)
return nil
}
// Tx does a single I2C transaction at the specified address.
@@ -114,12 +110,6 @@ func (i2c I2C) readByte() byte {
return byte(avr.TWDR.Get())
}
// UART
var (
// UART0 is the hardware serial port on the AVR.
UART0 = UART{Buffer: NewRingBuffer()}
)
// UART on the AVR.
type UART struct {
Buffer *RingBuffer
@@ -165,108 +155,3 @@ func (uart UART) WriteByte(c byte) error {
avr.UDR0.Set(c) // send char
return nil
}
// SPIConfig is used to store config info for SPI.
type SPIConfig struct {
Frequency uint32
LSBFirst bool
Mode uint8
}
// SPI is for the Serial Peripheral Interface
// Data is taken from http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf page 169 and following
type SPI struct {
// The registers for the SPIx port set by the chip
spcr *volatile.Register8
spdr *volatile.Register8
spsr *volatile.Register8
// The io pins for the SPIx port set by the chip
sck Pin
sdi Pin
sdo Pin
cs Pin
}
// Configure is intended to setup the SPI interface.
func (s SPI) Configure(config SPIConfig) error {
// This is only here to help catch a bug with the configuration
// where a machine missed a value.
if s.spcr == (*volatile.Register8)(unsafe.Pointer(uintptr(0))) ||
s.spsr == (*volatile.Register8)(unsafe.Pointer(uintptr(0))) ||
s.spdr == (*volatile.Register8)(unsafe.Pointer(uintptr(0))) ||
s.sck == 0 || s.sdi == 0 || s.sdo == 0 || s.cs == 0 {
return errSPIInvalidMachineConfig
}
// Make the defaults meaningful
if config.Frequency == 0 {
config.Frequency = 4000000
}
// Default all port configuration bits to 0 for simplicity
s.spcr.Set(0)
s.spsr.Set(0)
// Setup pins output configuration
s.sck.Configure(PinConfig{Mode: PinOutput})
s.sdi.Configure(PinConfig{Mode: PinInput})
s.sdo.Configure(PinConfig{Mode: PinOutput})
// Prevent CS glitches if the pin is enabled Low (0, default)
s.cs.High()
// If the CS pin is not configured as output the SPI port operates in
// slave mode.
s.cs.Configure(PinConfig{Mode: PinOutput})
frequencyDivider := CPUFrequency() / config.Frequency
switch {
case frequencyDivider >= 128:
s.spcr.SetBits(avr.SPCR_SPR0 | avr.SPCR_SPR1)
case frequencyDivider >= 64:
s.spcr.SetBits(avr.SPCR_SPR1)
case frequencyDivider >= 32:
s.spcr.SetBits(avr.SPCR_SPR1)
s.spsr.SetBits(avr.SPSR_SPI2X)
case frequencyDivider >= 16:
s.spcr.SetBits(avr.SPCR_SPR0)
case frequencyDivider >= 8:
s.spcr.SetBits(avr.SPCR_SPR0)
s.spsr.SetBits(avr.SPSR_SPI2X)
case frequencyDivider >= 4:
// The clock is already set to all 0's.
default: // defaults to fastest which is /2
s.spsr.SetBits(avr.SPSR_SPI2X)
}
switch config.Mode {
case Mode1:
s.spcr.SetBits(avr.SPCR_CPHA)
case Mode2:
s.spcr.SetBits(avr.SPCR_CPOL)
case Mode3:
s.spcr.SetBits(avr.SPCR_CPHA | avr.SPCR_CPOL)
default: // default is mode 0
}
if config.LSBFirst {
s.spcr.SetBits(avr.SPCR_DORD)
}
// enable SPI, set controller, set clock rate
s.spcr.SetBits(avr.SPCR_SPE | avr.SPCR_MSTR)
return nil
}
// Transfer writes the byte into the register and returns the read content
func (s SPI) Transfer(b byte) (byte, error) {
s.spdr.Set(uint8(b))
for !s.spsr.HasBits(avr.SPSR_SPIF) {
}
return byte(s.spdr.Get()), nil
}
-10
View File
@@ -69,13 +69,3 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
return avr.PORTD, 1 << uint8(p-portD)
}
}
// SPI configuration
var SPI0 = SPI{
spcr: avr.SPCR,
spsr: avr.SPSR,
spdr: avr.SPDR,
sck: PB7,
sdo: PB5,
sdi: PB6,
cs: PB4}
-10
View File
@@ -126,13 +126,3 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
return avr.PORTA, 255
}
}
// SPI configuration
var SPI0 = SPI{
spcr: avr.SPCR,
spdr: avr.SPDR,
spsr: avr.SPSR,
sck: PB1,
sdo: PB2,
sdi: PB3,
cs: PB0}
-10
View File
@@ -88,13 +88,3 @@ func (pwm PWM) Set(value uint16) {
panic("Invalid PWM pin")
}
}
// SPI configuration
var SPI0 = SPI{
spcr: avr.SPCR,
spdr: avr.SPDR,
spsr: avr.SPSR,
sck: PB5,
sdo: PB3,
sdi: PB4,
cs: PB2}
-109
View File
@@ -1,109 +0,0 @@
// +build avr,atmega328pb
package machine
import (
"device/avr"
"runtime/volatile"
)
const irq_USART0_RX = avr.IRQ_USART0_RX
// getPortMask returns the PORTx register and mask for the pin.
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
switch {
case p >= PB0 && p <= PB7: // port B
return avr.PORTB, 1 << uint8(p-portB)
case p >= PC0 && p <= PC7: // port C
return avr.PORTC, 1 << uint8(p-portC)
default: // port D
return avr.PORTD, 1 << uint8(p-portD)
}
}
// InitPWM initializes the registers needed for PWM.
func InitPWM() {
// use waveform generation
avr.TCCR0A.SetBits(avr.TCCR0A_WGM00)
// set timer 0 prescale factor to 64
avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00)
// set timer 1 prescale factor to 64
avr.TCCR1B.SetBits(avr.TCCR1B_CS11)
// put timer 1 in 8-bit phase correct pwm mode
avr.TCCR1A.SetBits(avr.TCCR1A_WGM10)
// set timer 2 prescale factor to 64
avr.TCCR2B.SetBits(avr.TCCR2B_CS22)
// configure timer 2 for phase correct pwm (8-bit)
avr.TCCR2A.SetBits(avr.TCCR2A_WGM20)
}
// Configure configures a PWM pin for output.
func (pwm PWM) Configure() error {
switch pwm.Pin / 8 {
case 0: // port B
avr.DDRB.SetBits(1 << uint8(pwm.Pin))
case 2: // port D
avr.DDRD.SetBits(1 << uint8(pwm.Pin-16))
}
return nil
}
// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
// 8-bit value ranging from 0 to 255.
func (pwm PWM) Set(value uint16) {
value8 := uint8(value >> 8)
switch pwm.Pin {
case PD3:
// connect pwm to pin on timer 2, channel B
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
avr.OCR2B.Set(value8) // set pwm duty
case PD5:
// connect pwm to pin on timer 0, channel B
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
avr.OCR0B.Set(value8) // set pwm duty
case PD6:
// connect pwm to pin on timer 0, channel A
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
avr.OCR0A.Set(value8) // set pwm duty
case PB1:
// connect pwm to pin on timer 1, channel A
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
// this is a 16-bit value, but we only currently allow the low order bits to be set
avr.OCR1AL.Set(value8) // set pwm duty
case PB2:
// connect pwm to pin on timer 1, channel B
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
// this is a 16-bit value, but we only currently allow the low order bits to be set
avr.OCR1BL.Set(value8) // set pwm duty
case PB3:
// connect pwm to pin on timer 2, channel A
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
avr.OCR2A.Set(value8) // set pwm duty
default:
panic("Invalid PWM pin")
}
}
// SPI configuration
var SPI0 = SPI{
spcr: avr.SPCR0,
spdr: avr.SPDR0,
spsr: avr.SPSR0,
sck: PB5,
sdo: PB3,
sdi: PB4,
cs: PB2}
var SPI1 = SPI{
spcr: avr.SPCR1,
spdr: avr.SPDR1,
spsr: avr.SPSR1,
sck: PC1,
sdo: PE3,
sdi: PC0,
cs: PE2}
+27 -305
View File
@@ -8,12 +8,9 @@
package machine
import (
"device"
"device/arm"
"device/sam"
"errors"
"runtime/interrupt"
"runtime/volatile"
"unsafe"
)
@@ -310,30 +307,13 @@ func InitADC() {
// set calibration
sam.ADC.CALIB.Set((bias << 8) | linearity)
}
// Configure configures a ADC pin to be able to be used to read data.
func (a ADC) Configure(config ADCConfig) {
// Wait for synchronization
waitADCSync()
var resolution uint32
switch config.Resolution {
case 8:
resolution = sam.ADC_CTRLB_RESSEL_8BIT
case 10:
resolution = sam.ADC_CTRLB_RESSEL_10BIT
case 12:
resolution = sam.ADC_CTRLB_RESSEL_12BIT
case 16:
resolution = sam.ADC_CTRLB_RESSEL_16BIT
default:
resolution = sam.ADC_CTRLB_RESSEL_12BIT
}
// Divide Clock by 32 with 12 bits resolution as default
sam.ADC.CTRLB.Set((sam.ADC_CTRLB_PRESCALER_DIV32 << sam.ADC_CTRLB_PRESCALER_Pos) |
uint16(resolution<<sam.ADC_CTRLB_RESSEL_Pos))
(sam.ADC_CTRLB_RESSEL_12BIT << sam.ADC_CTRLB_RESSEL_Pos))
// Sampling Time Length
sam.ADC.SAMPCTRL.Set(5)
@@ -345,44 +325,18 @@ func (a ADC) Configure(config ADCConfig) {
sam.ADC.INPUTCTRL.Set(sam.ADC_INPUTCTRL_MUXNEG_GND << sam.ADC_INPUTCTRL_MUXNEG_Pos)
// Averaging (see datasheet table in AVGCTRL register description)
var samples uint32
switch config.Samples {
case 1:
samples = sam.ADC_AVGCTRL_SAMPLENUM_1
case 2:
samples = sam.ADC_AVGCTRL_SAMPLENUM_2
case 4:
samples = sam.ADC_AVGCTRL_SAMPLENUM_4
case 8:
samples = sam.ADC_AVGCTRL_SAMPLENUM_8
case 16:
samples = sam.ADC_AVGCTRL_SAMPLENUM_16
case 32:
samples = sam.ADC_AVGCTRL_SAMPLENUM_32
case 64:
samples = sam.ADC_AVGCTRL_SAMPLENUM_64
case 128:
samples = sam.ADC_AVGCTRL_SAMPLENUM_128
case 256:
samples = sam.ADC_AVGCTRL_SAMPLENUM_256
case 512:
samples = sam.ADC_AVGCTRL_SAMPLENUM_512
case 1024:
samples = sam.ADC_AVGCTRL_SAMPLENUM_1024
default:
samples = sam.ADC_AVGCTRL_SAMPLENUM_1
}
sam.ADC.AVGCTRL.Set(uint8(samples<<sam.ADC_AVGCTRL_SAMPLENUM_Pos) |
sam.ADC.AVGCTRL.Set((sam.ADC_AVGCTRL_SAMPLENUM_1 << sam.ADC_AVGCTRL_SAMPLENUM_Pos) |
(0x0 << sam.ADC_AVGCTRL_ADJRES_Pos))
// TODO: use config.Reference to set AREF level
// Analog Reference is AREF pin (3.3v)
sam.ADC.INPUTCTRL.SetBits(sam.ADC_INPUTCTRL_GAIN_DIV2 << sam.ADC_INPUTCTRL_GAIN_Pos)
// 1/2 VDDANA = 0.5 * 3V3 = 1.65V
sam.ADC.REFCTRL.SetBits(sam.ADC_REFCTRL_REFSEL_INTVCC1 << sam.ADC_REFCTRL_REFSEL_Pos)
}
// Configure configures a ADCPin to be able to be used to read data.
func (a ADC) Configure() {
a.Pin.Configure(PinConfig{Mode: PinAnalog})
return
}
@@ -1267,160 +1221,6 @@ func (spi SPI) Transfer(w byte) (byte, error) {
return byte(spi.Bus.DATA.Get()), nil
}
var (
ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size")
)
// 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.
// The Tx method knows about this, and offers a few different ways of calling it.
//
// This form sends the bytes in tx buffer, putting the resulting bytes read into the rx buffer.
// Note that the tx and rx buffers must be the same size:
//
// spi.Tx(tx, rx)
//
// This form sends the tx buffer, ignoring the result. Useful for sending "commands" that return zeros
// until all the bytes in the command packet have been received:
//
// spi.Tx(tx, nil)
//
// This form sends zeros, putting the result into the rx buffer. Good for reading a "result packet":
//
// spi.Tx(nil, rx)
//
func (spi SPI) Tx(w, r []byte) error {
if spi.Bus.BAUD.Get() == 0x00 {
// When the SPI Freq is 24MHz, special processing is performed to improve the speed.
switch {
case w == nil:
// read only, so write zero and read a result.
spi.rx(r)
case r == nil:
// write only
spi.tx24mhz(w)
default:
// write/read
if len(w) != len(r) {
return ErrTxInvalidSliceSize
}
spi.txrx24mhz(w, r)
}
} else {
switch {
case w == nil:
// read only, so write zero and read a result.
spi.rx(r)
case r == nil:
// write only
spi.tx(w)
default:
// write/read
if len(w) != len(r) {
return ErrTxInvalidSliceSize
}
spi.txrx(w, r)
}
}
return nil
}
func (spi SPI) tx(tx []byte) {
for i := 0; i < len(tx); i++ {
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
}
spi.Bus.DATA.Set(uint32(tx[i]))
}
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_TXC) {
}
// read to clear RXC register
for spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
spi.Bus.DATA.Get()
}
}
func (spi SPI) rx(rx []byte) {
spi.Bus.DATA.Set(0)
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
}
for i := 1; i < len(rx); i++ {
spi.Bus.DATA.Set(0)
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
}
rx[i-1] = byte(spi.Bus.DATA.Get())
}
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
}
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
}
func (spi SPI) txrx(tx, rx []byte) {
spi.Bus.DATA.Set(uint32(tx[0]))
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
}
for i := 1; i < len(rx); i++ {
spi.Bus.DATA.Set(uint32(tx[i]))
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
}
rx[i-1] = byte(spi.Bus.DATA.Get())
}
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
}
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
}
// tx24mhz is a special tx/rx function for CPU Clock 48 Mhz and SPI Freq 24 Mhz
func (spi SPI) tx24mhz(tx []byte) {
spi.Bus.DATA.Set(uint32(tx[0]))
device.Asm("nop")
device.Asm("nop")
device.Asm("nop")
device.Asm("nop")
device.Asm("nop")
device.Asm("nop")
for i := 1; i < len(tx); i++ {
spi.Bus.DATA.Set(uint32(tx[i]))
device.Asm("nop")
device.Asm("nop")
spi.Bus.DATA.Get()
}
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
}
spi.Bus.DATA.Get()
}
// txrx24mhz is a special tx/rx function for CPU Clock 48 Mhz and SPI Freq 24 Mhz
func (spi SPI) txrx24mhz(tx, rx []byte) {
spi.Bus.DATA.Set(uint32(tx[0]))
device.Asm("nop")
device.Asm("nop")
device.Asm("nop")
device.Asm("nop")
device.Asm("nop")
device.Asm("nop")
for i := 1; i < len(rx); i++ {
spi.Bus.DATA.Set(uint32(tx[i]))
device.Asm("nop")
device.Asm("nop")
rx[i-1] = byte(spi.Bus.DATA.Get())
}
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
}
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
}
// PWM
const period = 0xFFFF
@@ -1621,104 +1421,36 @@ func (pwm PWM) setChannel(timer *sam.TCC_Type, val uint32) {
// USBCDC is the USB CDC aka serial over USB interface on the SAMD21.
type USBCDC struct {
Buffer *RingBuffer
TxIdx volatile.Register8
waitTxc bool
waitTxcRetryCount uint8
sent bool
}
const (
usbcdcTxSizeMask uint8 = 0x3F
usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask
usbcdcTxBank1st uint8 = 0x00
usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1
usbcdcTxMaxRetriesAllowed uint8 = 5
)
// Flush flushes buffered data.
func (usbcdc *USBCDC) Flush() error {
if usbLineInfo.lineState > 0 {
idx := usbcdc.TxIdx.Get()
sz := idx & usbcdcTxSizeMask
bk := idx & usbcdcTxBankMask
if 0 < sz {
if usbcdc.waitTxc {
// waiting for the next flush(), because the transmission is not complete
return nil
}
usbcdc.waitTxc = true
usbcdc.waitTxcRetryCount = 0
// set the data
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][bk]))))
if bk == usbcdcTxBank1st {
usbcdc.TxIdx.Set(usbcdcTxBank2nd)
} else {
usbcdc.TxIdx.Set(usbcdcTxBank1st)
}
// clean multi packet size of bytes already sent
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
// set count of bytes to be sent
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((uint32(sz) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
// clear transfer complete flag
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
// send data by setting bank ready
setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
UART0.sent = true
}
}
return nil
Buffer *RingBuffer
}
// WriteByte writes a byte of data to the USB CDC interface.
func (usbcdc *USBCDC) WriteByte(c byte) error {
func (usbcdc USBCDC) WriteByte(c byte) error {
// Supposedly to handle problem with Windows USB serial ports?
if usbLineInfo.lineState > 0 {
ok := false
for {
mask := interrupt.Disable()
// set the data
udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][0] = c
idx := UART0.TxIdx.Get()
if (idx & usbcdcTxSizeMask) < usbcdcTxSizeMask {
udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][idx] = c
UART0.TxIdx.Set(idx + 1)
ok = true
}
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN]))))
interrupt.Restore(mask)
// clean multi packet size of bytes already sent
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
if ok {
break
} else if usbcdcTxMaxRetriesAllowed < UART0.waitTxcRetryCount {
mask := interrupt.Disable()
UART0.waitTxc = false
UART0.waitTxcRetryCount = 0
usbcdc.TxIdx.Set(0)
usbLineInfo.lineState = 0
interrupt.Restore(mask)
break
} else {
mask := interrupt.Disable()
if UART0.sent {
if UART0.waitTxc {
if (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) != 0 {
setEPSTATUSCLR(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
UART0.waitTxc = false
UART0.Flush()
}
} else {
UART0.Flush()
}
}
interrupt.Restore(mask)
// set count of bytes to be sent
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((1 & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
// clear transfer complete flag
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
// send data by setting bank ready
setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
// wait for transfer to complete
timeout := 3000
for (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) == 0 {
timeout--
if timeout == 0 {
return errUSBCDCWriteByteTimeout
}
}
}
@@ -1925,19 +1657,9 @@ func handleUSB(intr interrupt.Interrupt) {
case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM:
setEPSTATUSCLR(i, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
setEPINTFLAG(i, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
if i == usb_CDC_ENDPOINT_IN {
UART0.waitTxc = false
}
}
}
if i == usb_CDC_ENDPOINT_IN && UART0.waitTxc {
UART0.waitTxcRetryCount++
}
}
UART0.Flush()
}
func initEndpoint(ep, config uint32) {
+78 -266
View File
@@ -10,9 +10,7 @@ package machine
import (
"device/arm"
"device/sam"
"errors"
"runtime/interrupt"
"runtime/volatile"
"unsafe"
)
@@ -722,83 +720,65 @@ func InitADC() {
// calibrate ADC1
sam.ADC1.CALIB.Set(uint16((biascomp | biasr2r | biasref) >> 16))
sam.ADC0.CTRLA.SetBits(sam.ADC_CTRLA_PRESCALER_DIV32 << sam.ADC_CTRLA_PRESCALER_Pos)
// adcs[i]->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_10BIT_Val;
sam.ADC0.CTRLB.SetBits(sam.ADC_CTRLB_RESSEL_12BIT << sam.ADC_CTRLB_RESSEL_Pos)
// wait for sync
for sam.ADC0.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_CTRLB) {
}
// sampling Time Length
sam.ADC0.SAMPCTRL.Set(5)
// wait for sync
for sam.ADC0.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_SAMPCTRL) {
}
// No Negative input (Internal Ground)
sam.ADC0.INPUTCTRL.Set(sam.ADC_INPUTCTRL_MUXNEG_GND << sam.ADC_INPUTCTRL_MUXNEG_Pos)
// wait for sync
for sam.ADC0.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_INPUTCTRL) {
}
// Averaging (see datasheet table in AVGCTRL register description)
// 1 sample only (no oversampling nor averaging), adjusting result by 0
sam.ADC0.AVGCTRL.Set(sam.ADC_AVGCTRL_SAMPLENUM_1 | (0 << sam.ADC_AVGCTRL_ADJRES_Pos))
// wait for sync
for sam.ADC0.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_AVGCTRL) {
}
// same for ADC1, as for ADC0
sam.ADC1.CTRLA.SetBits(sam.ADC_CTRLA_PRESCALER_DIV32 << sam.ADC_CTRLA_PRESCALER_Pos)
sam.ADC1.CTRLB.SetBits(sam.ADC_CTRLB_RESSEL_12BIT << sam.ADC_CTRLB_RESSEL_Pos)
for sam.ADC1.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_CTRLB) {
}
sam.ADC1.SAMPCTRL.Set(5)
for sam.ADC1.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_SAMPCTRL) {
}
sam.ADC1.INPUTCTRL.Set(sam.ADC_INPUTCTRL_MUXNEG_GND << sam.ADC_INPUTCTRL_MUXNEG_Pos)
for sam.ADC1.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_INPUTCTRL) {
}
sam.ADC1.AVGCTRL.Set(sam.ADC_AVGCTRL_SAMPLENUM_1 | (0 << sam.ADC_AVGCTRL_ADJRES_Pos))
for sam.ADC1.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_AVGCTRL) {
}
// wait for sync
for sam.ADC0.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_REFCTRL) {
}
for sam.ADC1.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_REFCTRL) {
}
// default is 3V3 reference voltage
sam.ADC0.REFCTRL.SetBits(sam.ADC_REFCTRL_REFSEL_INTVCC1)
sam.ADC1.REFCTRL.SetBits(sam.ADC_REFCTRL_REFSEL_INTVCC1)
}
// Configure configures a ADCPin to be able to be used to read data.
func (a ADC) Configure(config ADCConfig) {
for _, adc := range []*sam.ADC_Type{sam.ADC0, sam.ADC1} {
for adc.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_CTRLB) {
} // wait for sync
adc.CTRLA.SetBits(sam.ADC_CTRLA_PRESCALER_DIV32 << sam.ADC_CTRLA_PRESCALER_Pos)
var resolution uint32
switch config.Resolution {
case 8:
resolution = sam.ADC_CTRLB_RESSEL_8BIT
case 10:
resolution = sam.ADC_CTRLB_RESSEL_10BIT
case 12:
resolution = sam.ADC_CTRLB_RESSEL_12BIT
case 16:
resolution = sam.ADC_CTRLB_RESSEL_16BIT
default:
resolution = sam.ADC_CTRLB_RESSEL_12BIT
}
adc.CTRLB.SetBits(uint16(resolution << sam.ADC_CTRLB_RESSEL_Pos))
adc.SAMPCTRL.Set(5) // sampling Time Length
for adc.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_SAMPCTRL) {
} // wait for sync
// No Negative input (Internal Ground)
adc.INPUTCTRL.Set(sam.ADC_INPUTCTRL_MUXNEG_GND << sam.ADC_INPUTCTRL_MUXNEG_Pos)
for adc.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_INPUTCTRL) {
} // wait for sync
// Averaging (see datasheet table in AVGCTRL register description)
var samples uint32
switch config.Samples {
case 1:
samples = sam.ADC_AVGCTRL_SAMPLENUM_1
case 2:
samples = sam.ADC_AVGCTRL_SAMPLENUM_2
case 4:
samples = sam.ADC_AVGCTRL_SAMPLENUM_4
case 8:
samples = sam.ADC_AVGCTRL_SAMPLENUM_8
case 16:
samples = sam.ADC_AVGCTRL_SAMPLENUM_16
case 32:
samples = sam.ADC_AVGCTRL_SAMPLENUM_32
case 64:
samples = sam.ADC_AVGCTRL_SAMPLENUM_64
case 128:
samples = sam.ADC_AVGCTRL_SAMPLENUM_128
case 256:
samples = sam.ADC_AVGCTRL_SAMPLENUM_256
case 512:
samples = sam.ADC_AVGCTRL_SAMPLENUM_512
case 1024:
samples = sam.ADC_AVGCTRL_SAMPLENUM_1024
default: // 1 sample only (no oversampling nor averaging), adjusting result by 0
samples = sam.ADC_AVGCTRL_SAMPLENUM_1
}
adc.AVGCTRL.Set(uint8(samples<<sam.ADC_AVGCTRL_SAMPLENUM_Pos) |
(0 << sam.ADC_AVGCTRL_ADJRES_Pos))
for adc.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_AVGCTRL) {
} // wait for sync
for adc.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_REFCTRL) {
} // wait for sync
// TODO: use config.Reference to set AREF level
// default is 3V3 reference voltage
adc.REFCTRL.SetBits(sam.ADC_REFCTRL_REFSEL_INTVCC1)
}
func (a ADC) Configure() {
a.Pin.Configure(PinConfig{Mode: PinAnalog})
}
@@ -1470,96 +1450,6 @@ func (spi SPI) Transfer(w byte) (byte, error) {
return byte(spi.Bus.DATA.Get()), nil
}
var (
ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size")
)
// 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.
// The Tx method knows about this, and offers a few different ways of calling it.
//
// This form sends the bytes in tx buffer, putting the resulting bytes read into the rx buffer.
// Note that the tx and rx buffers must be the same size:
//
// spi.Tx(tx, rx)
//
// This form sends the tx buffer, ignoring the result. Useful for sending "commands" that return zeros
// until all the bytes in the command packet have been received:
//
// spi.Tx(tx, nil)
//
// This form sends zeros, putting the result into the rx buffer. Good for reading a "result packet":
//
// spi.Tx(nil, rx)
//
func (spi SPI) Tx(w, r []byte) error {
switch {
case w == nil:
// read only, so write zero and read a result.
spi.rx(r)
case r == nil:
// write only
spi.tx(w)
default:
// write/read
if len(w) != len(r) {
return ErrTxInvalidSliceSize
}
spi.txrx(w, r)
}
return nil
}
func (spi SPI) tx(tx []byte) {
for i := 0; i < len(tx); i++ {
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
spi.Bus.DATA.Set(uint32(tx[i]))
}
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_TXC) {
}
// read to clear RXC register
for spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
spi.Bus.DATA.Get()
}
}
func (spi SPI) rx(rx []byte) {
spi.Bus.DATA.Set(0)
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
for i := 1; i < len(rx); i++ {
spi.Bus.DATA.Set(0)
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
}
rx[i-1] = byte(spi.Bus.DATA.Get())
}
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
}
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
}
func (spi SPI) txrx(tx, rx []byte) {
spi.Bus.DATA.Set(uint32(tx[0]))
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
for i := 1; i < len(rx); i++ {
spi.Bus.DATA.Set(uint32(tx[i]))
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
}
rx[i-1] = byte(spi.Bus.DATA.Get())
}
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
}
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
}
// The QSPI peripheral on ATSAMD51 is only available on the following pins
const (
QSPI_SCK = PB10
@@ -1825,104 +1715,36 @@ func (pwm PWM) getMux() PinMode {
// USBCDC is the USB CDC aka serial over USB interface on the SAMD21.
type USBCDC struct {
Buffer *RingBuffer
TxIdx volatile.Register8
waitTxc bool
waitTxcRetryCount uint8
sent bool
}
const (
usbcdcTxSizeMask uint8 = 0x3F
usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask
usbcdcTxBank1st uint8 = 0x00
usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1
usbcdcTxMaxRetriesAllowed uint8 = 5
)
// Flush flushes buffered data.
func (usbcdc *USBCDC) Flush() error {
if usbLineInfo.lineState > 0 {
idx := usbcdc.TxIdx.Get()
sz := idx & usbcdcTxSizeMask
bk := idx & usbcdcTxBankMask
if 0 < sz {
if usbcdc.waitTxc {
// waiting for the next flush(), because the transmission is not complete
return nil
}
usbcdc.waitTxc = true
usbcdc.waitTxcRetryCount = 0
// set the data
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][bk]))))
if bk == usbcdcTxBank1st {
usbcdc.TxIdx.Set(usbcdcTxBank2nd)
} else {
usbcdc.TxIdx.Set(usbcdcTxBank1st)
}
// clean multi packet size of bytes already sent
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
// set count of bytes to be sent
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((uint32(sz) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
// clear transfer complete flag
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
// send data by setting bank ready
setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
UART0.sent = true
}
}
return nil
Buffer *RingBuffer
}
// WriteByte writes a byte of data to the USB CDC interface.
func (usbcdc *USBCDC) WriteByte(c byte) error {
func (usbcdc USBCDC) WriteByte(c byte) error {
// Supposedly to handle problem with Windows USB serial ports?
if usbLineInfo.lineState > 0 {
ok := false
for {
mask := interrupt.Disable()
// set the data
udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][0] = c
idx := UART0.TxIdx.Get()
if (idx & usbcdcTxSizeMask) < usbcdcTxSizeMask {
udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][idx] = c
UART0.TxIdx.Set(idx + 1)
ok = true
}
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN]))))
interrupt.Restore(mask)
// clean multi packet size of bytes already sent
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
if ok {
break
} else if usbcdcTxMaxRetriesAllowed < UART0.waitTxcRetryCount {
mask := interrupt.Disable()
UART0.waitTxc = false
UART0.waitTxcRetryCount = 0
usbcdc.TxIdx.Set(0)
usbLineInfo.lineState = 0
interrupt.Restore(mask)
break
} else {
mask := interrupt.Disable()
if UART0.sent {
if UART0.waitTxc {
if (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) != 0 {
setEPSTATUSCLR(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
UART0.waitTxc = false
UART0.Flush()
}
} else {
UART0.Flush()
}
}
interrupt.Restore(mask)
// set count of bytes to be sent
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((1 & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
// clear transfer complete flag
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
// send data by setting bank ready
setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
// wait for transfer to complete
timeout := 3000
for (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
timeout--
if timeout == 0 {
return errUSBCDCWriteByteTimeout
}
}
}
@@ -2131,19 +1953,9 @@ func handleUSBIRQ(interrupt.Interrupt) {
case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM:
setEPSTATUSCLR(i, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
setEPINTFLAG(i, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
if i == usb_CDC_ENDPOINT_IN {
UART0.waitTxc = false
}
}
}
if i == usb_CDC_ENDPOINT_IN && UART0.waitTxc {
UART0.waitTxcRetryCount++
}
}
UART0.Flush()
}
func initEndpoint(ep, config uint32) {
-70
View File
@@ -1,70 +0,0 @@
// +build sam,atsamd51,atsamd51p20
// Peripheral abstraction layer for the atsamd51.
//
// Datasheet:
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
//
package machine
import "device/sam"
const HSRAM_SIZE = 0x00040000
// InitPWM initializes the PWM interface.
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_TCC3_)
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
//use clock generator 0
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[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
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 PC18:
return sam.TCC0
case PC19:
return sam.TCC0
case PC20:
return sam.TCC0
case PC21:
return sam.TCC0
case PD20:
return sam.TCC1
case PD21:
return sam.TCC1
case PB18:
return sam.TCC1
case PB12:
return sam.TCC3
case PB13:
return sam.TCC3
case PA15:
return sam.TCC2
case PC17:
return sam.TCC0
case PC16:
return sam.TCC0
case PA14:
return sam.TCC2
case PB15:
return sam.TCC4
case PB14:
return sam.TCC4
case PB20:
return sam.TCC1
case PB21:
return sam.TCC1
default:
return nil // not supported on this pin
}
}
+17
View File
@@ -2,6 +2,23 @@
package machine
// UART on the AVR is a dummy implementation. UART has not been implemented for ATtiny
// devices.
type UART struct {
Buffer *RingBuffer
}
// Configure is a dummy implementation. UART has not been implemented for ATtiny
// devices.
func (uart UART) Configure(config UARTConfig) {
}
// WriteByte is a dummy implementation. UART has not been implemented for ATtiny
// devices.
func (uart UART) WriteByte(c byte) error {
return nil
}
// Tx is a dummy implementation. I2C has not been implemented for ATtiny
// devices.
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
+7 -1
View File
@@ -118,7 +118,7 @@ func InitADC() {
}
// Configure configures a ADCPin to be able to be used to read data.
func (a ADC) Configure(ADCConfig) {
func (a ADC) Configure() {
return // no pin specific setup on AVR machine.
}
@@ -148,3 +148,9 @@ type I2C struct {
// I2C0 is the only I2C interface on most AVRs.
var I2C0 = I2C{}
// UART
var (
// UART0 is the hardware serial port on the AVR.
UART0 = UART{Buffer: NewRingBuffer()}
)
+2 -4
View File
@@ -174,13 +174,11 @@ func (spi SPI) Transfer(w byte) (byte, error) {
spi.Bus.TXDATA.Set(uint32(w))
// wait until receive has data
data := spi.Bus.RXDATA.Get()
for data&sifive.QSPI_RXDATA_EMPTY > 0 {
data = spi.Bus.RXDATA.Get()
for spi.Bus.RXDATA.HasBits(sifive.QSPI_RXDATA_EMPTY) {
}
// return data
return byte(data), nil
return byte(spi.Bus.RXDATA.Get() & sifive.QSPI_RXDATA_DATA_Msk), nil
}
// I2C on the FE310-G002.
+2 -3
View File
@@ -73,7 +73,7 @@ func InitADC() {
}
// Configure configures an ADC pin to be able to be used to read data.
func (adc ADC) Configure(ADCConfig) {
func (adc ADC) Configure() {
}
// Get reads the current analog value from this ADC peripheral.
@@ -115,9 +115,8 @@ type I2CConfig struct {
}
// Configure is intended to setup the I2C interface.
func (i2c I2C) Configure(config I2CConfig) error {
func (i2c I2C) Configure(config I2CConfig) {
i2cConfigure(i2c.Bus, config.SCL, config.SDA)
return nil
}
// Tx does a single I2C transaction at the specified address.
+4 -4
View File
@@ -15,10 +15,6 @@ type UART struct {
Buffer *RingBuffer
Interrupt interrupt.Interrupt
// txBuffer should be allocated globally (such as when UART is created) to
// prevent it being reclaimed or cleaned up prematurely.
txBuffer *RingBuffer
// these hold the input selector ("daisy chain") values that select which pins
// are connected to the LPUART device, and should be defined where the UART
// instance is declared. see the godoc comments on type muxSelect for more
@@ -35,6 +31,7 @@ type UART struct {
configured bool
msbFirst bool
transmitting volatile.Register32
txBuffer *RingBuffer
}
func (uart *UART) isTransmitting() bool { return uart.transmitting.Get() != 0 }
@@ -200,6 +197,9 @@ func (uart *UART) Sync() error {
// WriteByte writes a single byte of data to the UART interface.
func (uart *UART) WriteByte(c byte) error {
if nil == uart.txBuffer {
uart.txBuffer = NewRingBuffer()
}
uart.startTransmitting()
for !uart.txBuffer.Put(c) {
}
+1 -3
View File
@@ -220,7 +220,7 @@ type I2CConfig struct {
}
// Configure is intended to setup the I2C interface.
func (i2c I2C) Configure(config I2CConfig) error {
func (i2c I2C) Configure(config I2CConfig) {
// Default I2C bus speed is 100 kHz.
if config.Frequency == 0 {
config.Frequency = TWI_FREQ_100KHZ
@@ -254,8 +254,6 @@ func (i2c I2C) Configure(config I2CConfig) error {
i2c.Bus.ENABLE.Set(nrf.TWI_ENABLE_ENABLE_Enabled)
i2c.setPins(config.SCL, config.SDA)
return nil
}
// Tx does a single I2C transaction at the specified address.
+1 -1
View File
@@ -17,7 +17,7 @@ func InitADC() {
}
// Configure configures an ADC pin to be able to read analog data.
func (a ADC) Configure(ADCConfig) {
func (a ADC) Configure() {
return // no pin specific setup on nrf52 machine.
}
+62
View File
@@ -0,0 +1,62 @@
// +build stm32f7
package machine
// Peripheral abstraction layer for UARTs on the stm32 family.
import (
"device/stm32"
"runtime/interrupt"
"unsafe"
)
// Configure the UART.
func (uart UART) Configure(config UARTConfig) {
// Default baud rate to 115200.
if config.BaudRate == 0 {
config.BaudRate = 115200
}
// Set the GPIO pins to defaults if they're not set
if config.TX == 0 && config.RX == 0 {
config.TX = UART_TX_PIN
config.RX = UART_RX_PIN
}
// Enable USART clock
enableAltFuncClock(unsafe.Pointer(uart.Bus))
uart.configurePins(config)
// Set baud rate
uart.SetBaudRate(config.BaudRate)
// Enable USART port, tx, rx and rx interrupts
uart.Bus.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
// Enable RX IRQ
uart.Interrupt.SetPriority(0xc0)
uart.Interrupt.Enable()
}
// handleInterrupt should be called from the appropriate interrupt handler for
// this UART instance.
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
uart.Receive(byte((uart.Bus.RDR.Get() & 0xFF)))
}
// SetBaudRate sets the communication speed for the UART. Defer to chip-specific
// routines for calculation
func (uart UART) SetBaudRate(br uint32) {
divider := uart.getBaudRateDivisor(br)
uart.Bus.BRR.Set(divider)
}
// WriteByte writes a byte of data to the UART.
func (uart UART) WriteByte(c byte) error {
uart.Bus.TDR.Set(uint32(c))
for !uart.Bus.ISR.HasBits(stm32.USART_ISR_TXE) {
}
return nil
}
+71 -19
View File
@@ -1,4 +1,4 @@
// +build stm32,!stm32f7x2,!stm32l5x2,!stm32l0
// +build stm32,!stm32f103,!stm32f407,!stm32f7x2,!stm32l0
package machine
@@ -91,6 +91,42 @@ const (
frameNoOption = 0xFFFF0000
)
// addressable represents a type that can provide fully-formatted I2C peripheral
// addresses for both read operations and write operations.
type addressable interface {
toRead() uint32
toWrite() uint32
bitSize() uint8
}
// address7Bit and address10Bit stores the unshifted original I2C peripheral address
// in an unsigned integral data type and implements the addressable interface
// to reformat addresses as required for read/write operations.
// TODO:
// add 10-bit address support
type (
address7Bit uint8
//address10Bit uint16
)
func (sa address7Bit) toRead() uint32 {
return uint32(((uint8(sa) << 1) | 1) & 0xFF)
}
func (sa address7Bit) toWrite() uint32 {
return uint32((uint8(sa) << 1) & 0xFF)
}
func (sa address7Bit) bitSize() uint8 { return 7 } // 7-bit addresses
//func (sa address10Bit) toRead() uint32 {}
//func (sa address10Bit) toWrite() uint32 {}
//func (sa address10Bit) bitSize() uint8 { return 10 } // 10-bit addresses
func readAddress7Bit(addr uint8) uint32 { return address7Bit(addr).toRead() }
func writeAddress7Bit(addr uint8) uint32 { return address7Bit(addr).toWrite() }
//func readAddress10Bit(addr uint16) uint32 { return address10Bit(addr).toRead() }
//func writeAddress10Bit(addr uint16) uint32 { return address10Bit(addr).toWrite() }
// I2C fast mode (Fm) duty cycle
const (
DutyCycle2 = 0
@@ -106,7 +142,7 @@ type I2CConfig struct {
}
// Configure is intended to setup the STM32 I2C interface.
func (i2c I2C) Configure(config I2CConfig) error {
func (i2c I2C) Configure(config I2CConfig) {
// The following is the required sequence in controller mode.
// 1. Program the peripheral input clock in I2C_CR2 Register in order to
@@ -151,32 +187,31 @@ func (i2c I2C) Configure(config I2CConfig) error {
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ENGC | stm32.I2C_CR1_NOSTRETCH)
// enable I2C interface
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_PE)
return nil
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_PE)
}
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
if err := i2c.controllerTransmit(addr, w); nil != err {
a := address7Bit(addr)
if err := i2c.controllerTransmit(a, w); nil != err {
return err
}
if len(r) > 0 {
if err := i2c.controllerReceive(addr, r); nil != err {
return err
}
if err := i2c.controllerReceive(a, r); nil != err {
return err
}
return nil
}
func (i2c I2C) controllerTransmit(addr uint16, w []byte) error {
func (i2c I2C) controllerTransmit(addr addressable, w []byte) error {
if !i2c.waitForFlag(flagBUSY, false) {
return errI2CBusReadyTimeout
}
// ensure peripheral is enabled
if !i2c.Bus.CR1.HasBits(stm32.I2C_CR1_PE) {
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_PE)
}
// disable POS
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_POS)
@@ -223,7 +258,7 @@ func (i2c I2C) controllerTransmit(addr uint16, w []byte) error {
return nil
}
func (i2c I2C) controllerRequestWrite(addr uint16, option transferOption) error {
func (i2c I2C) controllerRequestWrite(addr addressable, option transferOption) error {
if frameFirstAndLast == option || frameFirst == option || frameNoOption == option {
// generate start condition
@@ -239,7 +274,13 @@ func (i2c I2C) controllerRequestWrite(addr uint16, option transferOption) error
}
// send peripheral address
i2c.Bus.DR.Set(uint32(addr) << 1)
switch addr.bitSize() {
case 7: // 7-bit peripheral address
i2c.Bus.DR.Set(addr.toWrite())
case 10: // 10-bit peripheral address
// TODO
}
// wait for address ACK from peripheral
if !i2c.waitForFlagOrError(flagADDR, true) {
@@ -249,12 +290,17 @@ func (i2c I2C) controllerRequestWrite(addr uint16, option transferOption) error
return nil
}
func (i2c I2C) controllerReceive(addr uint16, r []byte) error {
func (i2c I2C) controllerReceive(addr addressable, r []byte) error {
if !i2c.waitForFlag(flagBUSY, false) {
return errI2CBusReadyTimeout
}
// ensure peripheral is enabled
if !i2c.Bus.CR1.HasBits(stm32.I2C_CR1_PE) {
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_PE)
}
// disable POS
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_POS)
@@ -399,7 +445,7 @@ func (i2c I2C) controllerReceive(addr uint16, r []byte) error {
return nil
}
func (i2c I2C) controllerRequestRead(addr uint16, option transferOption) error {
func (i2c I2C) controllerRequestRead(addr addressable, option transferOption) error {
// enable ACK
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
@@ -418,7 +464,13 @@ func (i2c I2C) controllerRequestRead(addr uint16, option transferOption) error {
}
// send peripheral address
i2c.Bus.DR.Set(uint32(addr)<<1 | 1)
switch addr.bitSize() {
case 7: // 7-bit peripheral address
i2c.Bus.DR.Set(addr.toRead())
case 10: // 10-bit peripheral address
// TODO
}
// wait for address ACK from peripheral
if !i2c.waitForFlagOrError(flagADDR, true) {
+1 -1
View File
@@ -1,4 +1,4 @@
// +build stm32,!stm32f7x2,!stm32l5x2
// +build stm32,!stm32f7x2
package machine
+7 -27
View File
@@ -1,4 +1,4 @@
// +build stm32
// +build stm32,!stm32f7,!stm32l0
package machine
@@ -7,26 +7,11 @@ package machine
import (
"device/stm32"
"runtime/interrupt"
"runtime/volatile"
"unsafe"
)
// UART representation
type UART struct {
Buffer *RingBuffer
Bus *stm32.USART_Type
Interrupt interrupt.Interrupt
AltFuncSelector uint8
// Registers specific to the chip
rxReg *volatile.Register32
txReg *volatile.Register32
statusReg *volatile.Register32
txEmptyFlag uint32
}
// Configure the UART.
func (uart *UART) Configure(config UARTConfig) {
func (uart UART) Configure(config UARTConfig) {
// Default baud rate to 115200.
if config.BaudRate == 0 {
config.BaudRate = 115200
@@ -38,11 +23,6 @@ func (uart *UART) Configure(config UARTConfig) {
config.RX = UART_RX_PIN
}
// STM32 families have different, but compatible, registers for
// basic UART functions. For each family populate the registers
// into `uart`.
uart.setRegisters()
// Enable USART clock
enableAltFuncClock(unsafe.Pointer(uart.Bus))
@@ -62,21 +42,21 @@ func (uart *UART) Configure(config UARTConfig) {
// handleInterrupt should be called from the appropriate interrupt handler for
// this UART instance.
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
uart.Receive(byte((uart.rxReg.Get() & 0xFF)))
uart.Receive(byte((uart.Bus.DR.Get() & 0xFF)))
}
// SetBaudRate sets the communication speed for the UART. Defer to chip-specific
// routines for calculation
func (uart *UART) SetBaudRate(br uint32) {
func (uart UART) SetBaudRate(br uint32) {
divider := uart.getBaudRateDivisor(br)
uart.Bus.BRR.Set(divider)
}
// WriteByte writes a byte of data to the UART.
func (uart *UART) WriteByte(c byte) error {
uart.txReg.Set(uint32(c))
func (uart UART) WriteByte(c byte) error {
uart.Bus.DR.Set(uint32(c))
for !uart.statusReg.HasBits(uart.txEmptyFlag) {
for !uart.Bus.SR.HasBits(stm32.USART_SR_TXE) {
}
return nil
}
+433 -53
View File
@@ -6,6 +6,7 @@ package machine
import (
"device/stm32"
"runtime/interrupt"
"unsafe"
)
@@ -102,10 +103,17 @@ func enableAltFuncClock(bus unsafe.Pointer) {
}
}
//---------- UART related code
//---------- UART related types and code
// UART representation
type UART struct {
Buffer *RingBuffer
Bus *stm32.USART_Type
Interrupt interrupt.Interrupt
}
// Configure the TX and RX pins
func (uart *UART) configurePins(config UARTConfig) {
func (uart UART) configurePins(config UARTConfig) {
// pins
switch config.TX {
@@ -125,7 +133,7 @@ func (uart *UART) configurePins(config UARTConfig) {
}
// Determine the divisor for USARTs to get the given baudrate
func (uart *UART) getBaudRateDivisor(br uint32) uint32 {
func (uart UART) getBaudRateDivisor(br uint32) uint32 {
// Note: PCLK2 (from APB2) used for USART1 and PCLK1 for USART2, 3, 4, 5
var divider uint32
@@ -139,14 +147,6 @@ func (uart *UART) getBaudRateDivisor(br uint32) uint32 {
return divider
}
// Register names vary by ST processor, these are for STM F103xx
func (uart *UART) setRegisters() {
uart.rxReg = &uart.Bus.DR
uart.txReg = &uart.Bus.DR
uart.statusReg = &uart.Bus.SR
uart.txEmptyFlag = stm32.USART_SR_TXE
}
//---------- SPI related types and code
type SPI struct {
@@ -197,6 +197,10 @@ func (spi SPI) configurePins(config SPIConfig) {
//---------- I2C related types and code
type I2C struct {
Bus *stm32.I2C_Type
}
// There are 2 I2C interfaces on the STM32F103xx.
// Since the first interface is named I2C1, both I2C0 and I2C1 refer to I2C1.
// TODO: implement I2C2.
@@ -205,73 +209,449 @@ var (
I2C0 = I2C1
)
type I2C struct {
Bus *stm32.I2C_Type
// I2CConfig is used to store config info for I2C.
type I2CConfig struct {
Frequency uint32
SCL Pin
SDA Pin
}
func (i2c I2C) configurePins(config I2CConfig) {
if config.SDA == PB9 {
// Configure is intended to setup the I2C interface.
func (i2c I2C) Configure(config I2CConfig) {
// Default I2C bus speed is 100 kHz.
if config.Frequency == 0 {
config.Frequency = TWI_FREQ_100KHZ
}
// enable clock for I2C
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN)
// I2C1 pins
switch config.SDA {
case PB9:
config.SCL = PB8
// use alternate I2C1 pins PB8/PB9 via AFIO mapping
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_AFIOEN)
stm32.AFIO.MAPR.SetBits(stm32.AFIO_MAPR_I2C1_REMAP)
default:
// use default I2C1 pins PB6/PB7
config.SDA = SDA_PIN
config.SCL = SCL_PIN
}
config.SDA.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltOpenDrain})
config.SCL.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltOpenDrain})
}
func (i2c I2C) getFreqRange(config I2CConfig) uint32 {
// Disable the selected I2C peripheral to configure
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_PE)
// pclk1 clock speed is main frequency divided by PCLK1 prescaler (div 2)
pclk1 := CPUFrequency() / 2
// set freqency range to PCLK1 clock speed in MHz
// aka setting the value 36 means to use 36 MHz clock
return pclk1 / 1000000
pclk1Mhz := pclk1 / 1000000
i2c.Bus.CR2.SetBits(pclk1Mhz)
switch config.Frequency {
case TWI_FREQ_100KHZ:
// Normal mode speed calculation
ccr := pclk1 / (config.Frequency * 2)
i2c.Bus.CCR.Set(ccr)
// duty cycle 2
i2c.Bus.CCR.ClearBits(stm32.I2C_CCR_DUTY)
// frequency standard mode
i2c.Bus.CCR.ClearBits(stm32.I2C_CCR_F_S)
// Set Maximum Rise Time for standard mode
i2c.Bus.TRISE.Set(pclk1Mhz)
case TWI_FREQ_400KHZ:
// Fast mode speed calculation
ccr := pclk1 / (config.Frequency * 3)
i2c.Bus.CCR.Set(ccr)
// duty cycle 2
i2c.Bus.CCR.ClearBits(stm32.I2C_CCR_DUTY)
// frequency fast mode
i2c.Bus.CCR.SetBits(stm32.I2C_CCR_F_S)
// Set Maximum Rise Time for fast mode
i2c.Bus.TRISE.Set(((pclk1Mhz * 300) / 1000))
}
// re-enable the selected I2C peripheral
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_PE)
}
func (i2c I2C) getRiseTime(config I2CConfig) uint32 {
// These bits must be programmed with the maximum SCL rise time given in the
// I2C bus specification, incremented by 1.
// For instance: in Sm mode, the maximum allowed SCL rise time is 1000 ns.
// If, in the I2C_CR2 register, the value of FREQ[5:0] bits is equal to 0x08
// and PCLK1 = 125 ns, therefore the TRISE[5:0] bits must be programmed with
// 09h (1000 ns / 125 ns = 8 + 1)
freqRange := i2c.getFreqRange(config)
if config.Frequency > 100000 {
// fast mode (Fm) adjustment
freqRange *= 300
freqRange /= 1000
// Tx does a single I2C transaction at the specified address.
// It clocks out the given address, writes the bytes in w, reads back len(r)
// bytes and stores them in r, and generates a stop condition on the bus.
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
var err error
if len(w) != 0 {
// start transmission for writing
err = i2c.signalStart()
if err != nil {
return err
}
// send address
err = i2c.sendAddress(uint8(addr), true)
if err != nil {
return err
}
for _, b := range w {
err = i2c.WriteByte(b)
if err != nil {
return err
}
}
// sending stop here for write
err = i2c.signalStop()
if err != nil {
return err
}
}
return (freqRange + 1) << stm32.I2C_TRISE_TRISE_Pos
if len(r) != 0 {
// re-start transmission for reading
err = i2c.signalStart()
if err != nil {
return err
}
// 1 byte
switch len(r) {
case 1:
// send address
err = i2c.sendAddress(uint8(addr), false)
if err != nil {
return err
}
// Disable ACK of received data
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK)
// clear timeout here
timeout := i2cTimeout
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY) {
timeout--
if timeout == 0 {
return errI2CWriteTimeout
}
}
// Generate stop condition
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
timeout = i2cTimeout
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_RxNE) {
timeout--
if timeout == 0 {
return errI2CReadTimeout
}
}
// Read and return data byte from I2C data register
r[0] = byte(i2c.Bus.DR.Get())
// wait for stop
return i2c.waitForStop()
case 2:
// enable pos
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_POS)
// Enable ACK of received data
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
// send address
err = i2c.sendAddress(uint8(addr), false)
if err != nil {
return err
}
// clear address here
timeout := i2cTimeout
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY) {
timeout--
if timeout == 0 {
return errI2CWriteTimeout
}
}
// Disable ACK of received data
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK)
// wait for btf. we need a longer timeout here than normal.
timeout = 1000
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
timeout--
if timeout == 0 {
return errI2CReadTimeout
}
}
// Generate stop condition
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
// read the 2 bytes by reading twice.
r[0] = byte(i2c.Bus.DR.Get())
r[1] = byte(i2c.Bus.DR.Get())
// wait for stop
err = i2c.waitForStop()
//disable pos
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_POS)
return err
case 3:
// Enable ACK of received data
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
// send address
err = i2c.sendAddress(uint8(addr), false)
if err != nil {
return err
}
// clear address here
timeout := i2cTimeout
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY) {
timeout--
if timeout == 0 {
return errI2CWriteTimeout
}
}
// Enable ACK of received data
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
// wait for btf. we need a longer timeout here than normal.
timeout = 1000
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
timeout--
if timeout == 0 {
return errI2CReadTimeout
}
}
// Disable ACK of received data
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK)
// read the first byte
r[0] = byte(i2c.Bus.DR.Get())
timeout = 1000
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
timeout--
if timeout == 0 {
return errI2CReadTimeout
}
}
// Generate stop condition
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
// read the last 2 bytes by reading twice.
r[1] = byte(i2c.Bus.DR.Get())
r[2] = byte(i2c.Bus.DR.Get())
// wait for stop
return i2c.waitForStop()
default:
// more than 3 bytes of data to read
// send address
err = i2c.sendAddress(uint8(addr), false)
if err != nil {
return err
}
// clear address here
timeout := i2cTimeout
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY) {
timeout--
if timeout == 0 {
return errI2CWriteTimeout
}
}
for i := 0; i < len(r)-3; i++ {
// Enable ACK of received data
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
// wait for btf. we need a longer timeout here than normal.
timeout = 1000
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
timeout--
if timeout == 0 {
return errI2CReadTimeout
}
}
// read the next byte
r[i] = byte(i2c.Bus.DR.Get())
}
// wait for btf. we need a longer timeout here than normal.
timeout = 1000
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
timeout--
if timeout == 0 {
return errI2CReadTimeout
}
}
// Disable ACK of received data
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK)
// get third from last byte
r[len(r)-3] = byte(i2c.Bus.DR.Get())
// Generate stop condition
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
// get second from last byte
r[len(r)-2] = byte(i2c.Bus.DR.Get())
timeout = i2cTimeout
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_RxNE) {
timeout--
if timeout == 0 {
return errI2CReadTimeout
}
}
// get last byte
r[len(r)-1] = byte(i2c.Bus.DR.Get())
// wait for stop
return i2c.waitForStop()
}
}
return nil
}
func (i2c I2C) getSpeed(config I2CConfig) uint32 {
ccr := func(pclk uint32, freq uint32, coeff uint32) uint32 {
return (((pclk - 1) / (freq * coeff)) + 1) & stm32.I2C_CCR_CCR_Msk
}
sm := func(pclk uint32, freq uint32) uint32 { // standard mode (Sm)
if s := ccr(pclk, freq, 2); s < 4 {
return 4
} else {
return s
const i2cTimeout = 1000
// signalStart sends a start signal.
func (i2c I2C) signalStart() error {
// Wait until I2C is not busy
timeout := i2cTimeout
for i2c.Bus.SR2.HasBits(stm32.I2C_SR2_BUSY) {
timeout--
if timeout == 0 {
return errI2CSignalStartTimeout
}
}
fm := func(pclk uint32, freq uint32, duty uint8) uint32 { // fast mode (Fm)
if duty == DutyCycle2 {
return ccr(pclk, freq, 3)
} else {
return ccr(pclk, freq, 25) | stm32.I2C_CCR_DUTY
// clear stop
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_STOP)
// Generate start condition
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_START)
// Wait for I2C EV5 aka SB flag.
timeout = i2cTimeout
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_SB) {
timeout--
if timeout == 0 {
return errI2CSignalStartTimeout
}
}
clock := CPUFrequency() / 2
if config.Frequency <= 100000 {
return sm(clock, config.Frequency)
return nil
}
// signalStop sends a stop signal and waits for it to succeed.
func (i2c I2C) signalStop() error {
// Generate stop condition
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
// wait for stop
return i2c.waitForStop()
}
// waitForStop waits after a stop signal.
func (i2c I2C) waitForStop() error {
// Wait until I2C is stopped
timeout := i2cTimeout
for i2c.Bus.SR1.HasBits(stm32.I2C_SR1_STOPF) {
timeout--
if timeout == 0 {
return errI2CSignalStopTimeout
}
}
return nil
}
// Send address of device we want to talk to
func (i2c I2C) sendAddress(address uint8, write bool) error {
data := (address << 1)
if !write {
data |= 1 // set read flag
}
i2c.Bus.DR.Set(uint32(data))
// Wait for I2C EV6 event.
// Destination device acknowledges address
timeout := i2cTimeout
if write {
// EV6 which is ADDR flag.
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_ADDR) {
timeout--
if timeout == 0 {
return errI2CWriteTimeout
}
}
timeout = i2cTimeout
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY | stm32.I2C_SR2_TRA) {
timeout--
if timeout == 0 {
return errI2CWriteTimeout
}
}
} else {
s := fm(clock, config.Frequency, config.DutyCycle)
if (s & stm32.I2C_CCR_CCR_Msk) == 0 {
return 1
} else {
return s | stm32.I2C_CCR_F_S
// I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED which is ADDR flag.
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_ADDR) {
timeout--
if timeout == 0 {
return errI2CWriteTimeout
}
}
}
return nil
}
// WriteByte writes a single byte to the I2C bus.
func (i2c I2C) WriteByte(data byte) error {
// Send data byte
i2c.Bus.DR.Set(uint32(data))
// Wait for I2C EV8_2 when data has been physically shifted out and
// output on the bus.
// I2C_EVENT_MASTER_BYTE_TRANSMITTED is TXE flag.
timeout := i2cTimeout
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_TxE) {
timeout--
if timeout == 0 {
return errI2CWriteTimeout
}
}
return nil
}
+10 -10
View File
@@ -7,6 +7,7 @@ package machine
import (
"device/stm32"
"math/bits"
"runtime/interrupt"
)
func CPUFrequency() uint32 {
@@ -35,13 +36,20 @@ const (
// -- UART ---------------------------------------------------------------------
func (uart *UART) configurePins(config UARTConfig) {
type UART struct {
Buffer *RingBuffer
Bus *stm32.USART_Type
Interrupt interrupt.Interrupt
AltFuncSelector uint8
}
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 {
func (uart UART) getBaudRateDivisor(baudRate uint32) uint32 {
var clock uint32
switch uart.Bus {
case stm32.USART1, stm32.USART6:
@@ -52,14 +60,6 @@ func (uart *UART) getBaudRateDivisor(baudRate uint32) uint32 {
return clock / baudRate
}
// Register names vary by ST processor, these are for STM F405
func (uart *UART) setRegisters() {
uart.rxReg = &uart.Bus.DR
uart.txReg = &uart.Bus.DR
uart.statusReg = &uart.Bus.SR
uart.txEmptyFlag = stm32.USART_SR_TXE
}
// -- SPI ----------------------------------------------------------------------
type SPI struct {
+12 -89
View File
@@ -6,6 +6,7 @@ package machine
import (
"device/stm32"
"runtime/interrupt"
)
func CPUFrequency() uint32 {
@@ -32,10 +33,18 @@ const (
AF15_EVENTOUT = 15
)
//---------- UART related code
//---------- UART related types and code
// UART representation
type UART struct {
Buffer *RingBuffer
Bus *stm32.USART_Type
Interrupt interrupt.Interrupt
AltFuncSelector uint8
}
// Configure the UART.
func (uart *UART) configurePins(config UARTConfig) {
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)
@@ -43,7 +52,7 @@ func (uart *UART) configurePins(config UARTConfig) {
// UART baudrate calc based on the bus and clockspeed
// NOTE: keep this in sync with the runtime/runtime_stm32f407.go clock init code
func (uart *UART) getBaudRateDivisor(baudRate uint32) uint32 {
func (uart UART) getBaudRateDivisor(baudRate uint32) uint32 {
var clock uint32
switch uart.Bus {
case stm32.USART1, stm32.USART6:
@@ -54,14 +63,6 @@ func (uart *UART) getBaudRateDivisor(baudRate uint32) uint32 {
return clock / baudRate
}
// Register names vary by ST processor, these are for STM F407
func (uart *UART) setRegisters() {
uart.rxReg = &uart.Bus.DR
uart.txReg = &uart.Bus.DR
uart.statusReg = &uart.Bus.SR
uart.txEmptyFlag = stm32.USART_SR_TXE
}
//---------- SPI related types and code
// SPI on the STM32Fxxx using MODER / alternate function pins
@@ -120,81 +121,3 @@ func (spi SPI) configurePins(config SPIConfig) {
config.SDO.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDO}, spi.AltFuncSelector)
config.SDI.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDI}, spi.AltFuncSelector)
}
// -- I2C ----------------------------------------------------------------------
type I2C struct {
Bus *stm32.I2C_Type
AltFuncSelector uint8
}
func (i2c I2C) configurePins(config I2CConfig) {
config.SCL.ConfigureAltFunc(PinConfig{Mode: PinModeI2CSCL}, i2c.AltFuncSelector)
config.SDA.ConfigureAltFunc(PinConfig{Mode: PinModeI2CSDA}, i2c.AltFuncSelector)
}
func (i2c I2C) getFreqRange(config I2CConfig) uint32 {
// all I2C interfaces are on APB1 (42 MHz)
clock := CPUFrequency() / 4
// convert to MHz
clock /= 1000000
// must be between 2 MHz (or 4 MHz for fast mode (Fm)) and 50 MHz, inclusive
var min, max uint32 = 2, 50
if config.Frequency > 100000 {
min = 4 // fast mode (Fm)
}
if clock < min {
clock = min
} else if clock > max {
clock = max
}
return clock << stm32.I2C_CR2_FREQ_Pos
}
func (i2c I2C) getRiseTime(config I2CConfig) uint32 {
// These bits must be programmed with the maximum SCL rise time given in the
// I2C bus specification, incremented by 1.
// For instance: in Sm mode, the maximum allowed SCL rise time is 1000 ns.
// If, in the I2C_CR2 register, the value of FREQ[5:0] bits is equal to 0x08
// and PCLK1 = 125 ns, therefore the TRISE[5:0] bits must be programmed with
// 09h (1000 ns / 125 ns = 8 + 1)
freqRange := i2c.getFreqRange(config)
if config.Frequency > 100000 {
// fast mode (Fm) adjustment
freqRange *= 300
freqRange /= 1000
}
return (freqRange + 1) << stm32.I2C_TRISE_TRISE_Pos
}
func (i2c I2C) getSpeed(config I2CConfig) uint32 {
ccr := func(pclk uint32, freq uint32, coeff uint32) uint32 {
return (((pclk - 1) / (freq * coeff)) + 1) & stm32.I2C_CCR_CCR_Msk
}
sm := func(pclk uint32, freq uint32) uint32 { // standard mode (Sm)
if s := ccr(pclk, freq, 2); s < 4 {
return 4
} else {
return s
}
}
fm := func(pclk uint32, freq uint32, duty uint8) uint32 { // fast mode (Fm)
if duty == DutyCycle2 {
return ccr(pclk, freq, 3)
} else {
return ccr(pclk, freq, 25) | stm32.I2C_CCR_DUTY
}
}
// all I2C interfaces are on APB1 (42 MHz)
clock := CPUFrequency() / 4
if config.Frequency <= 100000 {
return sm(clock, config.Frequency)
} else {
s := fm(clock, config.Frequency, config.DutyCycle)
if (s & stm32.I2C_CCR_CCR_Msk) == 0 {
return 1
} else {
return s | stm32.I2C_CCR_F_S
}
}
}

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