mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-31 09:07:46 +00:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a771e3f89 | |||
| 5ad251b2bd | |||
| 5324fb7a40 | |||
| 7369a0e7f2 | |||
| 8906192690 | |||
| 20a55e7944 | |||
| a2c7112b1f | |||
| 3eec878a11 | |||
| e6cab6c327 | |||
| 2463153a8c | |||
| 2a71aa90bc | |||
| 52bac4d75b | |||
| be9e4f439c | |||
| ba49148644 | |||
| 02facb8568 | |||
| 4164c39a4a | |||
| 832f301a74 | |||
| 2168b1b516 | |||
| 92e07ec8af | |||
| be40c383c8 | |||
| b730590059 | |||
| fb1a476033 | |||
| 4397152108 | |||
| d266e44bc5 | |||
| 8fe126b1f6 | |||
| ab50f823dd | |||
| 3fa926c512 | |||
| 01e58691a1 | |||
| 81c0f9af4e | |||
| b5ecd9cab0 | |||
| 9aace79298 | |||
| c16e07469b | |||
| 582457b81e | |||
| 4ea1559d46 | |||
| da7f7eef00 | |||
| 17ef7a5c32 | |||
| 6c9e55bd06 | |||
| 368abeeb9a | |||
| 076699add8 | |||
| d843ebfe40 | |||
| d17f500c8b | |||
| 65beddafe8 | |||
| cea0d9f864 | |||
| 1913e3e1cd | |||
| 9d47897368 | |||
| a7d00f1b6d | |||
| 7732c6f449 | |||
| cf2a7b5089 | |||
| d40fb5bc46 |
+81
-4
@@ -65,7 +65,7 @@ commands:
|
||||
- go-cache-v2-{{ checksum "go.mod" }}
|
||||
- llvm-source-linux
|
||||
- run: go install .
|
||||
- run: go test -v ./transform .
|
||||
- run: go test -v ./interp ./transform .
|
||||
- run: make gen-device -j4
|
||||
- run: make smoketest RISCV=0
|
||||
- save_cache:
|
||||
@@ -75,6 +75,71 @@ commands:
|
||||
- ~/.cache/tinygo
|
||||
- /go/pkg/mod
|
||||
- run: make fmt-check
|
||||
assert-test-linux:
|
||||
steps:
|
||||
- checkout
|
||||
- submodules
|
||||
- run:
|
||||
name: "Install apt dependencies"
|
||||
command: |
|
||||
sudo apt-get install \
|
||||
python3 \
|
||||
gcc-arm-linux-gnueabihf \
|
||||
binutils-arm-none-eabi \
|
||||
libc6-dev-armel-cross \
|
||||
gcc-aarch64-linux-gnu \
|
||||
libc6-dev-arm64-cross \
|
||||
qemu-system-arm \
|
||||
qemu-user \
|
||||
gcc-avr \
|
||||
avr-libc
|
||||
- install-node
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-v2-{{ checksum "go.mod" }}
|
||||
- llvm-source-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-8-linux-v7-assert
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
||||
then
|
||||
# install dependencies
|
||||
sudo apt-get install cmake clang ninja-build
|
||||
# make build faster
|
||||
export CC=clang
|
||||
export CXX=clang++
|
||||
# hack ninja to use less jobs
|
||||
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
|
||||
chmod +x /go/bin/ninja
|
||||
# build!
|
||||
make ASSERT=1 llvm-build
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-8-linux-v7-assert
|
||||
paths:
|
||||
llvm-build
|
||||
- run:
|
||||
name: "Create LLVM symlinks"
|
||||
command: |
|
||||
ln -s $PWD/llvm-build/bin/clang-8 /go/bin/clang-8
|
||||
ln -s $PWD/llvm-build/bin/ld.lld /go/bin/ld.lld-8
|
||||
ln -s $PWD/llvm-build/bin/wasm-ld /go/bin/wasm-ld-8
|
||||
- run: make ASSERT=1
|
||||
- run:
|
||||
name: "Test TinyGo"
|
||||
command: make ASSERT=1 test
|
||||
- save_cache:
|
||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
- ~/.cache/go-build
|
||||
- ~/.cache/tinygo
|
||||
- /go/pkg/mod
|
||||
- run: make gen-device -j4
|
||||
- run: make smoketest TINYGO=build/tinygo RISCV=0
|
||||
build-linux:
|
||||
steps:
|
||||
- checkout
|
||||
@@ -164,8 +229,8 @@ commands:
|
||||
- run:
|
||||
name: "Install dependencies"
|
||||
command: |
|
||||
curl https://dl.google.com/go/go1.12.5.darwin-amd64.tar.gz -o go1.12.5.darwin-amd64.tar.gz
|
||||
sudo tar -C /usr/local -xzf go1.12.5.darwin-amd64.tar.gz
|
||||
curl https://dl.google.com/go/go1.13.darwin-amd64.tar.gz -o go1.13.darwin-amd64.tar.gz
|
||||
sudo tar -C /usr/local -xzf go1.13.darwin-amd64.tar.gz
|
||||
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
|
||||
- restore_cache:
|
||||
@@ -245,9 +310,19 @@ jobs:
|
||||
- image: circleci/golang:1.12-stretch
|
||||
steps:
|
||||
- test-linux
|
||||
test-llvm8-go113:
|
||||
docker:
|
||||
- image: circleci/golang:1.13-stretch
|
||||
steps:
|
||||
- test-linux
|
||||
assert-test-linux:
|
||||
docker:
|
||||
- image: circleci/golang:1.13-stretch
|
||||
steps:
|
||||
- assert-test-linux
|
||||
build-linux:
|
||||
docker:
|
||||
- image: circleci/golang:1.12-stretch
|
||||
- image: circleci/golang:1.13-stretch
|
||||
steps:
|
||||
- build-linux
|
||||
build-macos:
|
||||
@@ -264,5 +339,7 @@ workflows:
|
||||
jobs:
|
||||
- test-llvm8-go111
|
||||
- test-llvm8-go112
|
||||
- test-llvm8-go113
|
||||
- build-linux
|
||||
- build-macos
|
||||
- assert-test-linux
|
||||
|
||||
+1
-1
@@ -12,5 +12,5 @@ src/device/sifive/*.s
|
||||
src/device/stm32/*.go
|
||||
src/device/stm32/*.s
|
||||
vendor
|
||||
llvm
|
||||
llvm-build
|
||||
llvm-project
|
||||
|
||||
@@ -1,3 +1,36 @@
|
||||
0.9.0
|
||||
---
|
||||
* **command line**
|
||||
- implement 1200-baud UART bootloader reset when flashing boards that support
|
||||
it
|
||||
- flash using mass-storage device for boards that support it
|
||||
- implement `tinygo env`
|
||||
- add support for Windows (but not yet producing Windows binaries)
|
||||
- add Go version to `tinygo env`
|
||||
- update SVD files for up-to-date peripheral interfaces
|
||||
* **compiler**
|
||||
- add `//go:align` pragma
|
||||
- fix bug related to type aliases
|
||||
- add support for buffered channels
|
||||
- remove incorrect reflect optimization
|
||||
- implement copying slices in init interpretation
|
||||
- add support for constant indices with a named type
|
||||
- add support for recursive types like linked lists
|
||||
- fix miscompile of function nil panics
|
||||
- fix bug related to goroutines
|
||||
* **standard library**
|
||||
- `machine`: do not check for nil slices in `SPI.Tx`
|
||||
- `reflectlite`: add support for Go 1.13
|
||||
- `runtime`: implement `internal/bytealg.CountString`
|
||||
- `sync`: properly handle nil `New` func in `sync.Pool`
|
||||
* **targets**
|
||||
- `arduino`: fix .bss section initialization
|
||||
- `fe310`: implement `Pin.Get`
|
||||
- `gameboy-advance`: support directly outputting .gba files
|
||||
- `samd`: reduce code size by avoiding reflection
|
||||
- `samd21`: do not hardcode pin numbers for peripherals
|
||||
- `stm32f103`: avoid issue with `time.Sleep` less than 200µs
|
||||
|
||||
0.8.0
|
||||
---
|
||||
* **command line**
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
# TinyGo base stage installs Go 1.12, LLVM 8 and the TinyGo compiler itself.
|
||||
FROM golang:1.12 AS tinygo-base
|
||||
# TinyGo base stage installs Go 1.13, LLVM 8 and the TinyGo compiler itself.
|
||||
FROM golang:1.13 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-8 main" >> /etc/apt/sources.list && \
|
||||
|
||||
@@ -1,40 +1,93 @@
|
||||
|
||||
# aliases
|
||||
all: tinygo
|
||||
tinygo: build/tinygo
|
||||
|
||||
# Default build and source directories, as created by `make llvm-build`.
|
||||
LLVM_BUILDDIR ?= llvm-build
|
||||
CLANG_SRC ?= llvm-project/clang
|
||||
LLD_SRC ?= llvm-project/lld
|
||||
|
||||
.PHONY: all tinygo build/tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-avr
|
||||
# Go binary and GOROOT to select
|
||||
GO ?= go
|
||||
export GOROOT = $(shell $(GO) env GOROOT)
|
||||
|
||||
# md5sum binary
|
||||
MD5SUM = md5sum
|
||||
|
||||
# Python binary
|
||||
PYTHON ?= python
|
||||
|
||||
# tinygo binary for tests
|
||||
TINYGO ?= tinygo
|
||||
|
||||
# Use CCACHE for LLVM if possible
|
||||
ifneq (, $(shell which ccache))
|
||||
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=ON'
|
||||
endif
|
||||
|
||||
# Allow enabling LLVM assertions
|
||||
ifeq (1, $(ASSERT))
|
||||
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=ON'
|
||||
else
|
||||
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF'
|
||||
endif
|
||||
|
||||
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-avr
|
||||
|
||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines debuginfodwarf executionengine instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
ifeq ($(OS),Windows_NT)
|
||||
EXE = .exe
|
||||
START_GROUP = -Wl,--start-group
|
||||
END_GROUP = -Wl,--end-group
|
||||
|
||||
# LLVM compiled using MinGW on Windows appears to have problems with threads.
|
||||
# Without this flag, linking results in errors like these:
|
||||
# libLLVMSupport.a(Threading.cpp.obj):Threading.cpp:(.text+0x55): undefined reference to `std::thread::hardware_concurrency()'
|
||||
LLVM_OPTION += -DLLVM_ENABLE_THREADS=OFF
|
||||
|
||||
CGO_LDFLAGS += -static -static-libgcc -static-libstdc++
|
||||
CGO_LDFLAGS_EXTRA += -lversion
|
||||
|
||||
# Build libclang manually because the CMake-based build system on Windows
|
||||
# doesn't allow building libclang as a static library.
|
||||
LIBCLANG_PATH = $(abspath build/libclang-custom.a)
|
||||
LIBCLANG_FILES = $(abspath $(wildcard $(LLVM_BUILDDIR)/tools/clang/tools/libclang/CMakeFiles/libclang.dir/*.cpp.obj))
|
||||
|
||||
# Add the libclang dependency to the tinygo binary target.
|
||||
tinygo: $(LIBCLANG_PATH)
|
||||
test: $(LIBCLANG_PATH)
|
||||
# Build libclang.
|
||||
$(LIBCLANG_PATH): $(LIBCLANG_FILES)
|
||||
@mkdir -p build
|
||||
ar rcs $(LIBCLANG_PATH) $^
|
||||
|
||||
else ifeq ($(shell uname -s),Darwin)
|
||||
MD5SUM = md5
|
||||
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
||||
else
|
||||
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
||||
START_GROUP = -Wl,--start-group
|
||||
END_GROUP = -Wl,--end-group
|
||||
endif
|
||||
|
||||
CLANG_LIBS = $(START_GROUP) $(abspath $(LLVM_BUILDDIR))/lib/libclang.a -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++
|
||||
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++
|
||||
|
||||
LLD_LIBS = $(START_GROUP) -llldCOFF -llldCommon -llldCore -llldDriver -llldELF -llldMachO -llldMinGW -llldReaderWriter -llldWasm -llldYAML $(END_GROUP)
|
||||
|
||||
|
||||
# For static linking.
|
||||
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config)","")
|
||||
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
||||
CGO_CPPFLAGS=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||
CGO_CXXFLAGS=-std=c++11
|
||||
CGO_LDFLAGS=-L$(LLVM_BUILDDIR)/lib $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS))
|
||||
CGO_LDFLAGS+=$(LIBCLANG_PATH) -std=c++11 -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 cgo compiler interp ir loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall
|
||||
FMT_PATHS = ./*.go 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:
|
||||
@@ -44,25 +97,25 @@ fmt-check:
|
||||
gen-device: gen-device-avr gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32
|
||||
|
||||
gen-device-avr:
|
||||
./tools/gen-device-avr.py lib/avr/packs/atmega src/device/avr/
|
||||
./tools/gen-device-avr.py lib/avr/packs/tiny src/device/avr/
|
||||
go fmt ./src/device/avr
|
||||
$(PYTHON) ./tools/gen-device-avr.py lib/avr/packs/atmega src/device/avr/
|
||||
$(PYTHON) ./tools/gen-device-avr.py lib/avr/packs/tiny src/device/avr/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/avr
|
||||
|
||||
gen-device-nrf:
|
||||
./tools/gen-device-svd.py lib/nrfx/mdk/ src/device/nrf/ --source=https://github.com/NordicSemiconductor/nrfx/tree/master/mdk
|
||||
go fmt ./src/device/nrf
|
||||
$(PYTHON) ./tools/gen-device-svd.py lib/nrfx/mdk/ src/device/nrf/ --source=https://github.com/NordicSemiconductor/nrfx/tree/master/mdk
|
||||
GO111MODULE=off $(GO) fmt ./src/device/nrf
|
||||
|
||||
gen-device-sam:
|
||||
./tools/gen-device-svd.py lib/cmsis-svd/data/Atmel/ src/device/sam/ --source=https://github.com/posborne/cmsis-svd/tree/master/data/Atmel
|
||||
go fmt ./src/device/sam
|
||||
$(PYTHON) ./tools/gen-device-svd.py lib/cmsis-svd/data/Atmel/ src/device/sam/ --source=https://github.com/posborne/cmsis-svd/tree/master/data/Atmel
|
||||
GO111MODULE=off $(GO) fmt ./src/device/sam
|
||||
|
||||
gen-device-sifive:
|
||||
./tools/gen-device-svd.py lib/cmsis-svd/data/SiFive-Community/ src/device/sifive/ --source=https://github.com/AdaCore/svd2ada/tree/master/CMSIS-SVD/SiFive-Community
|
||||
go fmt ./src/device/sifive
|
||||
$(PYTHON) ./tools/gen-device-svd.py lib/cmsis-svd/data/SiFive-Community/ src/device/sifive/ --source=https://github.com/AdaCore/svd2ada/tree/master/CMSIS-SVD/SiFive-Community
|
||||
GO111MODULE=off $(GO) fmt ./src/device/sifive
|
||||
|
||||
gen-device-stm32:
|
||||
./tools/gen-device-svd.py lib/cmsis-svd/data/STMicro/ src/device/stm32/ --source=https://github.com/posborne/cmsis-svd/tree/master/data/STMicro
|
||||
go fmt ./src/device/stm32
|
||||
$(PYTHON) ./tools/gen-device-svd.py lib/cmsis-svd/data/STMicro/ src/device/stm32/ --source=https://github.com/posborne/cmsis-svd/tree/master/data/STMicro
|
||||
GO111MODULE=off $(GO) fmt ./src/device/stm32
|
||||
|
||||
|
||||
# Get LLVM sources.
|
||||
@@ -73,7 +126,7 @@ llvm-source: llvm-project/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-project/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;RISCV" -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=OFF -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF
|
||||
mkdir -p $(LLVM_BUILDDIR); cd $(LLVM_BUILDDIR); cmake -G Ninja $(TINYGO_SOURCE_DIR)/llvm-project/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;RISCV" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF $(LLVM_OPTION)
|
||||
|
||||
# Build LLVM.
|
||||
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
||||
@@ -81,12 +134,12 @@ $(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
||||
|
||||
|
||||
# Build the Go compiler.
|
||||
build/tinygo:
|
||||
tinygo:
|
||||
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go build -o build/tinygo -tags byollvm .
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -o build/tinygo$(EXE) -tags byollvm .
|
||||
|
||||
test:
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test -v -tags byollvm ./transform .
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -tags byollvm ./interp ./transform .
|
||||
|
||||
tinygo-test:
|
||||
cd tests/tinygotest && tinygo test
|
||||
@@ -94,50 +147,84 @@ tinygo-test:
|
||||
.PHONY: smoketest
|
||||
smoketest:
|
||||
# test all examples
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/adc
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/blinkm
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/blinky2
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/button
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/button2
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/echo
|
||||
tinygo build -size short -o test.elf -target=circuitplay-express examples/i2s
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/mcp3008
|
||||
tinygo build -size short -o test.elf -target=microbit examples/microbit-blink
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/pwm
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/serial
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/test
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/adc
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinkm
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky2
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/button
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/button2
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/mcp3008
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit examples/microbit-blink
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/test
|
||||
@$(MD5SUM) test.hex
|
||||
# test all targets/boards
|
||||
tinygo build -o test.wasm -tags=pca10040 examples/blinky2
|
||||
tinygo build -size short -o test.elf -target=microbit examples/echo
|
||||
tinygo build -size short -o test.elf -target=nrf52840-mdk examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=pca10031 examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=bluepill examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=reelboard examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=reelboard examples/blinky2
|
||||
tinygo build -size short -o test.elf -target=pca10056 examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=pca10056 examples/blinky2
|
||||
tinygo build -size short -o test.elf -target=itsybitsy-m0 examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=feather-m0 examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=trinket-m0 examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=circuitplay-express examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky2
|
||||
tinygo build -size short -o test.elf -target=circuitplay-express examples/i2s
|
||||
tinygo build -size short -o test.elf -target=gameboy-advance examples/gba-display
|
||||
tinygo build -size short -o test.elf -target=itsybitsy-m4 examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=nucleo-f103rb examples/blinky1
|
||||
$(TINYGO) build -o test.wasm -tags=pca10040 examples/blinky2
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nrf52840-mdk examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10031 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=reelboard examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=reelboard examples/blinky2
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10056 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10056 examples/blinky2
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m0 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=trinket-m0 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky2
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.gba -target=gameboy-advance examples/gba-display
|
||||
@$(MD5SUM) test.gba
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m4 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-f103rb examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(AVR), 0)
|
||||
tinygo build -size short -o test.elf -target=arduino examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=digispark examples/blinky1
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
endif
|
||||
ifneq ($(RISCV), 0)
|
||||
tinygo build -size short -o test.elf -target=hifive1b examples/blinky1
|
||||
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
endif
|
||||
tinygo build -o wasm.wasm -target=wasm examples/wasm/export
|
||||
tinygo build -o wasm.wasm -target=wasm examples/wasm/main
|
||||
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/export
|
||||
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/main
|
||||
|
||||
release: build/tinygo gen-device
|
||||
release: tinygo gen-device
|
||||
@mkdir -p build/release/tinygo/bin
|
||||
@mkdir -p build/release/tinygo/lib/clang/include
|
||||
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
||||
@@ -147,7 +234,7 @@ release: build/tinygo gen-device
|
||||
@mkdir -p build/release/tinygo/pkg/armv7m-none-eabi
|
||||
@mkdir -p build/release/tinygo/pkg/armv7em-none-eabi
|
||||
@echo copying source files
|
||||
@cp -p build/tinygo build/release/tinygo/bin
|
||||
@cp -p build/tinygo$(EXE) build/release/tinygo/bin
|
||||
@cp -p $(abspath $(CLANG_SRC))/lib/Headers/*.h build/release/tinygo/lib/clang/include
|
||||
@cp -rp lib/CMSIS/CMSIS/Include build/release/tinygo/lib/CMSIS/CMSIS
|
||||
@cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# TinyGo - Go compiler for small places
|
||||
|
||||
[](https://circleci.com/gh/tinygo-org/tinygo/tree/dev)
|
||||
[](https://circleci.com/gh/tinygo-org/tinygo/tree/dev) [](https://dev.azure.com/tinygo/tinygo/_build/latest?definitionId=1&branchName=dev)
|
||||
|
||||
TinyGo is a Go compiler intended for use in small places such as microcontrollers, WebAssembly (Wasm), and command-line tools.
|
||||
|
||||
@@ -43,7 +43,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
||||
|
||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||
|
||||
The following 18 microcontroller boards are currently supported:
|
||||
The following 19 microcontroller boards are currently supported:
|
||||
|
||||
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
||||
* [Adafruit Feather M0](https://www.adafruit.com/product/2772)
|
||||
@@ -53,16 +53,17 @@ The following 18 microcontroller boards are currently supported:
|
||||
* [Arduino Nano33 IoT](https://store.arduino.cc/nano-33-iot)
|
||||
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
|
||||
* [BBC micro:bit](https://microbit.org/)
|
||||
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
|
||||
* [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
|
||||
* [ST Micro "Nucleo F103RB"](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
||||
* [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html)
|
||||
* [Digispark](http://digistump.com/products/1)
|
||||
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
|
||||
* [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/)
|
||||
* [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)
|
||||
* [Nordic Semiconductor PCA10056](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK)
|
||||
* [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/)
|
||||
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
|
||||
* [SiFIve HiFive1](https://www.sifive.com/boards/hifive1)
|
||||
* [ST Micro "Nucleo F103RB"](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
||||
* [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
|
||||
* [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html)
|
||||
|
||||
For more information, see [this list of boards](https://tinygo.org/microcontrollers/). Pull requests for additional support are welcome!
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
jobs:
|
||||
- job: Build
|
||||
timeoutInMinutes: 180
|
||||
pool:
|
||||
vmImage: 'VS2017-Win2016'
|
||||
steps:
|
||||
- checkout: self
|
||||
- task: CacheBeta@0
|
||||
displayName: Cache LLVM source
|
||||
inputs:
|
||||
key: llvm-source-8-windows-v0
|
||||
path: llvm-project
|
||||
- task: Bash@3
|
||||
displayName: Download LLVM source
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: make llvm-source
|
||||
- task: CacheBeta@0
|
||||
displayName: Cache LLVM build
|
||||
inputs:
|
||||
key: llvm-build-8-windows-v1
|
||||
path: llvm-build
|
||||
- task: Bash@3
|
||||
displayName: Build LLVM
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: |
|
||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
||||
then
|
||||
choco install ninja
|
||||
make llvm-build
|
||||
fi
|
||||
- task: Bash@3
|
||||
displayName: Install QEMU
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: choco install qemu
|
||||
- task: Bash@3
|
||||
displayName: Test TinyGo
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: |
|
||||
export PATH="$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
||||
make test
|
||||
- task: Bash@3
|
||||
displayName: Build TinyGo release tarball
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: |
|
||||
export PATH="$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
||||
make release -j4
|
||||
- publish: $(System.DefaultWorkingDirectory)/build/release.tar.gz
|
||||
displayName: Publish tarball as artifact
|
||||
artifact: tinygo.windows-amd64.tar.gz
|
||||
- task: Bash@3
|
||||
displayName: Smoke tests
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: |
|
||||
export PATH="$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
||||
make smoketest TINYGO=build/tinygo AVR=0 RISCV=0
|
||||
+6
-14
@@ -5,16 +5,9 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Get the cache directory, usually ~/.cache/tinygo
|
||||
func cacheDir() string {
|
||||
dir, err := os.UserCacheDir()
|
||||
if err != nil {
|
||||
panic("could not find cache dir: " + err.Error())
|
||||
}
|
||||
return filepath.Join(dir, "tinygo")
|
||||
}
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
// Return the newest timestamp of all the file paths passed in. Used to check
|
||||
// for stale caches.
|
||||
@@ -41,8 +34,7 @@ func cacheTimestamp(paths []string) (time.Time, 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) {
|
||||
dir := cacheDir()
|
||||
cachepath := filepath.Join(dir, name)
|
||||
cachepath := filepath.Join(goenv.Get("GOCACHE"), name)
|
||||
cacheStat, err := os.Stat(cachepath)
|
||||
if os.IsNotExist(err) {
|
||||
return "", nil // does not exist
|
||||
@@ -76,7 +68,7 @@ func cacheStore(tmppath, name, configKey string, sourceFiles []string) (string,
|
||||
|
||||
// TODO: check the config key
|
||||
|
||||
dir := cacheDir()
|
||||
dir := goenv.Get("GOCACHE")
|
||||
err := os.MkdirAll(dir, 0777)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -117,10 +109,10 @@ func moveFile(src, dst string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Rename(dst+".tmp", dst)
|
||||
err = outf.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return outf.Close()
|
||||
return os.Rename(dst+".tmp", dst)
|
||||
}
|
||||
|
||||
+3
-2
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/blakesmith/ar"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
// These are the GENERIC_SOURCES according to CMakeList.txt.
|
||||
@@ -169,13 +170,13 @@ func builtinFiles(target string) []string {
|
||||
|
||||
// builtinsDir returns the directory where the sources for compiler-rt are kept.
|
||||
func builtinsDir() string {
|
||||
return filepath.Join(sourceDir(), "lib", "compiler-rt", "lib", "builtins")
|
||||
return filepath.Join(goenv.Get("TINYGOROOT"), "lib", "compiler-rt", "lib", "builtins")
|
||||
}
|
||||
|
||||
// Get the builtins archive, possibly generating it as needed.
|
||||
func loadBuiltins(target string) (path string, err error) {
|
||||
// Try to load a precompiled compiler-rt library.
|
||||
precompiledPath := filepath.Join(sourceDir(), "pkg", target, "compiler-rt.a")
|
||||
precompiledPath := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", target, "compiler-rt.a")
|
||||
if _, err := os.Stat(precompiledPath); err == nil {
|
||||
// Found a precompiled compiler-rt for this OS/architecture. Return the
|
||||
// path directly.
|
||||
|
||||
+9
-1
@@ -24,6 +24,14 @@ func init() {
|
||||
commands["ld.lld"] = append(commands["ld.lld"], "/usr/local/opt/llvm/bin/ld.lld")
|
||||
commands["wasm-ld"] = append(commands["wasm-ld"], "/usr/local/opt/llvm/bin/wasm-ld")
|
||||
}
|
||||
// Add the path for when LLVM was installed with the installer from
|
||||
// llvm.org, which by default doesn't add LLVM to the $PATH environment
|
||||
// variable.
|
||||
if runtime.GOOS == "windows" {
|
||||
commands["clang"] = append(commands["clang"], "clang", "C:\\Program Files\\LLVM\\bin\\clang.exe")
|
||||
commands["ld.lld"] = append(commands["ld.lld"], "lld", "C:\\Program Files\\LLVM\\bin\\lld.exe")
|
||||
commands["wasm-ld"] = append(commands["wasm-ld"], "C:\\Program Files\\LLVM\\bin\\wasm-ld.exe")
|
||||
}
|
||||
}
|
||||
|
||||
func execCommand(cmdNames []string, args ...string) error {
|
||||
@@ -33,7 +41,7 @@ func execCommand(cmdNames []string, args ...string) error {
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
if err, ok := err.(*exec.Error); ok && err.Err == exec.ErrNotFound {
|
||||
if err, ok := err.(*exec.Error); ok && (err.Err == exec.ErrNotFound || err.Err.Error() == "file does not exist") {
|
||||
// this command was not found, try the next
|
||||
continue
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ func (c *Compiler) emitLookupBoundsCheck(frame *Frame, arrayLen, index llvm.Valu
|
||||
if index.Type().IntTypeWidth() < arrayLen.Type().IntTypeWidth() {
|
||||
// Sometimes, the index can be e.g. an uint8 or int8, and we have to
|
||||
// correctly extend that type.
|
||||
if indexType.(*types.Basic).Info()&types.IsUnsigned == 0 {
|
||||
if indexType.Underlying().(*types.Basic).Info()&types.IsUnsigned == 0 {
|
||||
index = c.builder.CreateZExt(index, arrayLen.Type(), "")
|
||||
} else {
|
||||
index = c.builder.CreateSExt(index, arrayLen.Type(), "")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
@@ -20,6 +21,9 @@ func (c *Compiler) createRuntimeCall(fnName string, args []llvm.Value, name stri
|
||||
panic("trying to call runtime." + fnName)
|
||||
}
|
||||
fn := c.ir.GetFunction(member.(*ssa.Function))
|
||||
if fn.LLVMFn.IsNil() {
|
||||
panic(fmt.Errorf("function %s does not appear in LLVM IR", fnName))
|
||||
}
|
||||
if !fn.IsExported() {
|
||||
args = append(args, llvm.Undef(c.i8ptrType)) // unused context parameter
|
||||
args = append(args, llvm.ConstPointerNull(c.i8ptrType)) // coroutine handle
|
||||
|
||||
+6
-25
@@ -4,32 +4,17 @@ package compiler
|
||||
// or pseudo-operations that are lowered during goroutine lowering.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// emitMakeChan returns a new channel value for the given channel type.
|
||||
func (c *Compiler) emitMakeChan(expr *ssa.MakeChan) (llvm.Value, error) {
|
||||
chanType := c.getLLVMType(expr.Type())
|
||||
size := c.targetData.TypeAllocSize(chanType.ElementType())
|
||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||
ptr := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "chan.alloc")
|
||||
ptr = c.builder.CreateBitCast(ptr, chanType, "chan")
|
||||
// Set the elementSize field
|
||||
elementSizePtr := c.builder.CreateGEP(ptr, []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
}, "")
|
||||
func (c *Compiler) emitMakeChan(frame *Frame, expr *ssa.MakeChan) llvm.Value {
|
||||
elementSize := c.targetData.TypeAllocSize(c.getLLVMType(expr.Type().(*types.Chan).Elem()))
|
||||
if elementSize > 0xffff {
|
||||
return ptr, c.makeError(expr.Pos(), fmt.Sprintf("element size is %d bytes, which is bigger than the maximum of %d bytes", elementSize, 0xffff))
|
||||
}
|
||||
elementSizeValue := llvm.ConstInt(c.ctx.Int16Type(), elementSize, false)
|
||||
c.builder.CreateStore(elementSizeValue, elementSizePtr)
|
||||
return ptr, nil
|
||||
elementSizeValue := llvm.ConstInt(c.uintptrType, elementSize, false)
|
||||
bufSize := c.getValue(frame, expr.Size)
|
||||
return c.createRuntimeCall("chanMake", []llvm.Value{elementSizeValue, bufSize}, "")
|
||||
}
|
||||
|
||||
// emitChanSend emits a pseudo chan send operation. It is lowered to the actual
|
||||
@@ -44,8 +29,7 @@ func (c *Compiler) emitChanSend(frame *Frame, instr *ssa.Send) {
|
||||
c.builder.CreateStore(chanValue, valueAlloca)
|
||||
|
||||
// Do the send.
|
||||
coroutine := c.createRuntimeCall("getCoroutine", nil, "")
|
||||
c.createRuntimeCall("chanSend", []llvm.Value{coroutine, ch, valueAllocaCast}, "")
|
||||
c.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast}, "")
|
||||
|
||||
// End the lifetime of the alloca.
|
||||
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
||||
@@ -63,14 +47,11 @@ func (c *Compiler) emitChanRecv(frame *Frame, unop *ssa.UnOp) llvm.Value {
|
||||
valueAlloca, valueAllocaCast, valueAllocaSize := c.createTemporaryAlloca(valueType, "chan.value")
|
||||
|
||||
// Do the receive.
|
||||
coroutine := c.createRuntimeCall("getCoroutine", nil, "")
|
||||
c.createRuntimeCall("chanRecv", []llvm.Value{coroutine, ch, valueAllocaCast}, "")
|
||||
commaOk := c.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast}, "")
|
||||
received := c.builder.CreateLoad(valueAlloca, "chan.received")
|
||||
c.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||
|
||||
if unop.CommaOk {
|
||||
commaOk := c.createRuntimeCall("getTaskStateData", []llvm.Value{coroutine}, "chan.commaOk.wide")
|
||||
commaOk = c.builder.CreateTrunc(commaOk, c.ctx.Int1Type(), "chan.commaOk")
|
||||
tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{valueType, c.ctx.Int1Type()}, false))
|
||||
tuple = c.builder.CreateInsertValue(tuple, received, 0, "")
|
||||
tuple = c.builder.CreateInsertValue(tuple, commaOk, 1, "")
|
||||
|
||||
+57
-18
@@ -1,6 +1,7 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"debug/dwarf"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
@@ -36,13 +37,24 @@ const tinygoPath = "github.com/tinygo-org/tinygo"
|
||||
var functionsUsedInTransforms = []string{
|
||||
"runtime.alloc",
|
||||
"runtime.free",
|
||||
"runtime.sleepTask",
|
||||
"runtime.sleepCurrentTask",
|
||||
"runtime.scheduler",
|
||||
"runtime.nilPanic",
|
||||
}
|
||||
|
||||
var taskFunctionsUsedInTransforms = []string{
|
||||
"runtime.startGoroutine",
|
||||
}
|
||||
|
||||
var coroFunctionsUsedInTransforms = []string{
|
||||
"runtime.avrSleep",
|
||||
"runtime.getFakeCoroutine",
|
||||
"runtime.setTaskStatePtr",
|
||||
"runtime.getTaskStatePtr",
|
||||
"runtime.activateTask",
|
||||
"runtime.scheduler",
|
||||
"runtime.startGoroutine",
|
||||
"runtime.noret",
|
||||
"runtime.getParentHandle",
|
||||
"runtime.getCoroutine",
|
||||
"runtime.llvmCoroRefHolder",
|
||||
}
|
||||
|
||||
// Configure the compiler.
|
||||
@@ -81,6 +93,7 @@ type Compiler struct {
|
||||
dibuilder *llvm.DIBuilder
|
||||
cu llvm.Metadata
|
||||
difiles map[string]llvm.Metadata
|
||||
ditypes map[types.Type]llvm.Metadata
|
||||
machine llvm.TargetMachine
|
||||
targetData llvm.TargetData
|
||||
intType llvm.Type
|
||||
@@ -126,6 +139,7 @@ func NewCompiler(pkgName string, config Config) (*Compiler, error) {
|
||||
c := &Compiler{
|
||||
Config: config,
|
||||
difiles: make(map[string]llvm.Metadata),
|
||||
ditypes: make(map[types.Type]llvm.Metadata),
|
||||
}
|
||||
|
||||
target, err := llvm.GetTargetFromTriple(config.Triple)
|
||||
@@ -177,11 +191,6 @@ func (c *Compiler) Module() llvm.Module {
|
||||
return c.mod
|
||||
}
|
||||
|
||||
// Return the LLVM target data object. Only valid after a successful compile.
|
||||
func (c *Compiler) TargetData() llvm.TargetData {
|
||||
return c.targetData
|
||||
}
|
||||
|
||||
// selectGC picks an appropriate GC strategy if none was provided.
|
||||
func (c *Compiler) selectGC() string {
|
||||
if c.GC != "" {
|
||||
@@ -201,6 +210,20 @@ func (c *Compiler) selectScheduler() string {
|
||||
return "coroutines"
|
||||
}
|
||||
|
||||
// getFunctionsUsedInTransforms gets a list of all special functions that should be preserved during transforms and optimization.
|
||||
func (c *Compiler) getFunctionsUsedInTransforms() []string {
|
||||
fnused := functionsUsedInTransforms
|
||||
switch c.selectScheduler() {
|
||||
case "coroutines":
|
||||
fnused = append(append([]string{}, fnused...), coroFunctionsUsedInTransforms...)
|
||||
case "tasks":
|
||||
fnused = append(append([]string{}, fnused...), taskFunctionsUsedInTransforms...)
|
||||
default:
|
||||
panic(fmt.Errorf("invalid scheduler %q", c.selectScheduler()))
|
||||
}
|
||||
return fnused
|
||||
}
|
||||
|
||||
// Compile the given package path or .go file path. Return an error when this
|
||||
// fails (in any stage).
|
||||
func (c *Compiler) Compile(mainPath string) []error {
|
||||
@@ -248,7 +271,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||
path = path[len(tinygoPath+"/src/"):]
|
||||
}
|
||||
switch path {
|
||||
case "machine", "os", "reflect", "runtime", "runtime/volatile", "sync", "testing":
|
||||
case "machine", "os", "reflect", "runtime", "runtime/volatile", "sync", "testing", "internal/reflectlite":
|
||||
return path
|
||||
default:
|
||||
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
||||
@@ -366,10 +389,10 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||
realMain.SetLinkage(llvm.ExternalLinkage) // keep alive until goroutine lowering
|
||||
|
||||
// Make sure these functions are kept in tact during TinyGo transformation passes.
|
||||
for _, name := range functionsUsedInTransforms {
|
||||
for _, name := range c.getFunctionsUsedInTransforms() {
|
||||
fn := c.mod.NamedFunction(name)
|
||||
if fn.IsNil() {
|
||||
continue
|
||||
panic(fmt.Errorf("missing core function %q", name))
|
||||
}
|
||||
fn.SetLinkage(llvm.ExternalLinkage)
|
||||
}
|
||||
@@ -577,6 +600,17 @@ func isPointer(typ types.Type) bool {
|
||||
|
||||
// Get the DWARF type for this Go type.
|
||||
func (c *Compiler) getDIType(typ types.Type) llvm.Metadata {
|
||||
if md, ok := c.ditypes[typ]; ok {
|
||||
return md
|
||||
}
|
||||
md := c.createDIType(typ)
|
||||
c.ditypes[typ] = md
|
||||
return md
|
||||
}
|
||||
|
||||
// createDIType creates a new DWARF type. Don't call this function directly,
|
||||
// call getDIType instead.
|
||||
func (c *Compiler) createDIType(typ types.Type) llvm.Metadata {
|
||||
llvmType := c.getLLVMType(typ)
|
||||
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
|
||||
switch typ := typ.(type) {
|
||||
@@ -713,14 +747,17 @@ func (c *Compiler) getDIType(typ types.Type) llvm.Metadata {
|
||||
},
|
||||
})
|
||||
case *types.Struct:
|
||||
// Placeholder metadata node, to be replaced afterwards.
|
||||
temporaryMDNode := c.dibuilder.CreateReplaceableCompositeType(llvm.Metadata{}, llvm.DIReplaceableCompositeType{
|
||||
Tag: dwarf.TagStructType,
|
||||
SizeInBits: sizeInBytes * 8,
|
||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||
})
|
||||
c.ditypes[typ] = temporaryMDNode
|
||||
elements := make([]llvm.Metadata, typ.NumFields())
|
||||
for i := range elements {
|
||||
field := typ.Field(i)
|
||||
fieldType := field.Type()
|
||||
if _, ok := fieldType.Underlying().(*types.Pointer); ok {
|
||||
// XXX hack to avoid recursive types
|
||||
fieldType = types.Typ[types.UnsafePointer]
|
||||
}
|
||||
llvmField := c.getLLVMType(fieldType)
|
||||
elements[i] = c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||
Name: field.Name(),
|
||||
@@ -730,11 +767,13 @@ func (c *Compiler) getDIType(typ types.Type) llvm.Metadata {
|
||||
Type: c.getDIType(fieldType),
|
||||
})
|
||||
}
|
||||
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
|
||||
md := c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
|
||||
SizeInBits: sizeInBytes * 8,
|
||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||
Elements: elements,
|
||||
})
|
||||
temporaryMDNode.ReplaceAllUsesWith(md)
|
||||
return md
|
||||
default:
|
||||
panic("unknown type while generating DWARF debug type: " + typ.String())
|
||||
}
|
||||
@@ -1618,7 +1657,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
panic("unknown lookup type: " + expr.String())
|
||||
}
|
||||
case *ssa.MakeChan:
|
||||
return c.emitMakeChan(expr)
|
||||
return c.emitMakeChan(frame, expr), nil
|
||||
case *ssa.MakeClosure:
|
||||
return c.parseMakeClosure(frame, expr)
|
||||
case *ssa.MakeInterface:
|
||||
|
||||
+502
-240
@@ -105,16 +105,20 @@ package compiler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// setting this to true will cause the compiler to spew tons of information about coroutine transformations
|
||||
// this can be useful when debugging coroutine lowering or looking for potential missed optimizations
|
||||
const coroDebug = false
|
||||
|
||||
type asyncFunc struct {
|
||||
taskHandle llvm.Value
|
||||
cleanupBlock llvm.BasicBlock
|
||||
suspendBlock llvm.BasicBlock
|
||||
unreachableBlock llvm.BasicBlock
|
||||
taskHandle llvm.Value
|
||||
cleanupBlock llvm.BasicBlock
|
||||
suspendBlock llvm.BasicBlock
|
||||
}
|
||||
|
||||
// LowerGoroutines performs some IR transformations necessary to support
|
||||
@@ -142,7 +146,7 @@ func (c *Compiler) lowerTasks() error {
|
||||
mainCall := uses[0]
|
||||
|
||||
realMain := c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path() + ".main")
|
||||
if len(getUses(c.mod.NamedFunction("runtime.startGoroutine"))) != 0 {
|
||||
if len(getUses(c.mod.NamedFunction("runtime.startGoroutine"))) != 0 || len(getUses(c.mod.NamedFunction("runtime.yield"))) != 0 {
|
||||
// Program needs a scheduler. Start main.main as a goroutine and start
|
||||
// the scheduler.
|
||||
realMainWrapper := c.createGoroutineStartWrapper(realMain)
|
||||
@@ -150,10 +154,6 @@ func (c *Compiler) lowerTasks() error {
|
||||
zero := llvm.ConstInt(c.uintptrType, 0, false)
|
||||
c.createRuntimeCall("startGoroutine", []llvm.Value{realMainWrapper, zero}, "")
|
||||
c.createRuntimeCall("scheduler", nil, "")
|
||||
sleep := c.mod.NamedFunction("time.Sleep")
|
||||
if !sleep.IsNil() {
|
||||
sleep.ReplaceAllUsesWith(c.mod.NamedFunction("runtime.sleepCurrentTask"))
|
||||
}
|
||||
} else {
|
||||
// Program doesn't need a scheduler. Call main.main directly.
|
||||
c.builder.SetInsertPointBefore(mainCall)
|
||||
@@ -162,9 +162,6 @@ func (c *Compiler) lowerTasks() error {
|
||||
llvm.Undef(c.i8ptrType), // unused coroutine handle
|
||||
}
|
||||
c.createCall(realMain, params, "")
|
||||
// runtime.Goexit isn't needed so let it be optimized away by
|
||||
// globalopt.
|
||||
c.mod.NamedFunction("runtime.Goexit").SetLinkage(llvm.InternalLinkage)
|
||||
}
|
||||
mainCall.EraseFromParentAsInstruction()
|
||||
|
||||
@@ -195,7 +192,13 @@ func (c *Compiler) lowerCoroutines() error {
|
||||
// optionally followed by a call to runtime.scheduler().
|
||||
c.builder.SetInsertPointBefore(mainCall)
|
||||
realMain := c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path() + ".main")
|
||||
c.builder.CreateCall(realMain, []llvm.Value{llvm.Undef(c.i8ptrType), llvm.ConstPointerNull(c.i8ptrType)}, "")
|
||||
var ph llvm.Value
|
||||
if needsScheduler {
|
||||
ph = c.createRuntimeCall("getFakeCoroutine", []llvm.Value{}, "")
|
||||
} else {
|
||||
ph = llvm.Undef(c.i8ptrType)
|
||||
}
|
||||
c.builder.CreateCall(realMain, []llvm.Value{llvm.Undef(c.i8ptrType), ph}, "")
|
||||
if needsScheduler {
|
||||
c.createRuntimeCall("scheduler", nil, "")
|
||||
}
|
||||
@@ -218,6 +221,12 @@ func (c *Compiler) lowerCoroutines() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func coroDebugPrintln(s ...interface{}) {
|
||||
if coroDebug {
|
||||
fmt.Println(s...)
|
||||
}
|
||||
}
|
||||
|
||||
// markAsyncFunctions does the bulk of the work of lowering goroutines. It
|
||||
// determines whether a scheduler is needed, and if it is, it transforms
|
||||
// blocking operations into goroutines and blocking calls into await calls.
|
||||
@@ -233,26 +242,14 @@ func (c *Compiler) lowerCoroutines() error {
|
||||
func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
var worklist []llvm.Value
|
||||
|
||||
sleep := c.mod.NamedFunction("time.Sleep")
|
||||
if !sleep.IsNil() {
|
||||
worklist = append(worklist, sleep)
|
||||
}
|
||||
deadlock := c.mod.NamedFunction("runtime.deadlock")
|
||||
if !deadlock.IsNil() {
|
||||
worklist = append(worklist, deadlock)
|
||||
}
|
||||
chanSend := c.mod.NamedFunction("runtime.chanSend")
|
||||
if !chanSend.IsNil() {
|
||||
worklist = append(worklist, chanSend)
|
||||
}
|
||||
chanRecv := c.mod.NamedFunction("runtime.chanRecv")
|
||||
if !chanRecv.IsNil() {
|
||||
worklist = append(worklist, chanRecv)
|
||||
yield := c.mod.NamedFunction("runtime.yield")
|
||||
if !yield.IsNil() {
|
||||
worklist = append(worklist, yield)
|
||||
}
|
||||
|
||||
if len(worklist) == 0 {
|
||||
// There are no blocking operations, so no need to transform anything.
|
||||
return false, c.lowerMakeGoroutineCalls()
|
||||
return false, c.lowerMakeGoroutineCalls(false)
|
||||
}
|
||||
|
||||
// Find all async functions.
|
||||
@@ -269,6 +266,9 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
if _, ok := asyncFuncs[f]; ok {
|
||||
continue // already processed
|
||||
}
|
||||
if f.Name() == "resume" {
|
||||
continue
|
||||
}
|
||||
// Add to set of async functions.
|
||||
asyncFuncs[f] = &asyncFunc{}
|
||||
asyncList = append(asyncList, f)
|
||||
@@ -312,11 +312,23 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
|
||||
// Check whether a scheduler is needed.
|
||||
makeGoroutine := c.mod.NamedFunction("runtime.makeGoroutine")
|
||||
if c.GOOS == "js" && strings.HasPrefix(c.Triple, "wasm") {
|
||||
// JavaScript always needs a scheduler, as in general no blocking
|
||||
// operations are possible. Blocking operations block the browser UI,
|
||||
// which is very bad.
|
||||
needsScheduler = true
|
||||
if strings.HasPrefix(c.Triple, "avr") {
|
||||
needsScheduler = false
|
||||
getCoroutine := c.mod.NamedFunction("runtime.getCoroutine")
|
||||
for _, inst := range getUses(getCoroutine) {
|
||||
inst.ReplaceAllUsesWith(llvm.Undef(inst.Type()))
|
||||
inst.EraseFromParentAsInstruction()
|
||||
}
|
||||
yield := c.mod.NamedFunction("runtime.yield")
|
||||
for _, inst := range getUses(yield) {
|
||||
inst.EraseFromParentAsInstruction()
|
||||
}
|
||||
sleep := c.mod.NamedFunction("time.Sleep")
|
||||
for _, inst := range getUses(sleep) {
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
c.createRuntimeCall("avrSleep", []llvm.Value{inst.Operand(0)}, "")
|
||||
inst.EraseFromParentAsInstruction()
|
||||
}
|
||||
} else {
|
||||
// Only use a scheduler when an async goroutine is started. When the
|
||||
// goroutine is not async (does not do any blocking operation), no
|
||||
@@ -328,18 +340,347 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
panic("expected const ptrtoint operand of runtime.makeGoroutine")
|
||||
}
|
||||
goroutine := ptrtoint.Operand(0)
|
||||
if goroutine.Name() == "runtime.fakeCoroutine" {
|
||||
continue
|
||||
}
|
||||
if _, ok := asyncFuncs[goroutine]; ok {
|
||||
needsScheduler = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if _, ok := asyncFuncs[c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path()+".main")]; ok {
|
||||
needsScheduler = true
|
||||
}
|
||||
}
|
||||
|
||||
if !needsScheduler {
|
||||
// on wasm, we may still have calls to deadlock
|
||||
// replace these with an abort
|
||||
abort := c.mod.NamedFunction("runtime.abort")
|
||||
if deadlock := c.mod.NamedFunction("runtime.deadlock"); !deadlock.IsNil() {
|
||||
deadlock.ReplaceAllUsesWith(abort)
|
||||
}
|
||||
|
||||
// No scheduler is needed. Do not transform all functions here.
|
||||
// However, make sure that all go calls (which are all non-async) are
|
||||
// transformed into regular calls.
|
||||
return false, c.lowerMakeGoroutineCalls()
|
||||
return false, c.lowerMakeGoroutineCalls(false)
|
||||
}
|
||||
|
||||
if noret := c.mod.NamedFunction("runtime.noret"); noret.IsNil() {
|
||||
panic("missing noret")
|
||||
}
|
||||
|
||||
// replace indefinitely blocking yields
|
||||
getCoroutine := c.mod.NamedFunction("runtime.getCoroutine")
|
||||
coroDebugPrintln("replace indefinitely blocking yields")
|
||||
nonReturning := map[llvm.Value]bool{}
|
||||
for _, f := range asyncList {
|
||||
if f == yield {
|
||||
continue
|
||||
}
|
||||
coroDebugPrintln("scanning", f.Name())
|
||||
|
||||
var callsAsyncNotYield bool
|
||||
var callsYield bool
|
||||
var getsCoroutine bool
|
||||
for bb := f.EntryBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
if !inst.IsACallInst().IsNil() {
|
||||
callee := inst.CalledValue()
|
||||
if callee == yield {
|
||||
callsYield = true
|
||||
} else if callee == getCoroutine {
|
||||
getsCoroutine = true
|
||||
} else if _, ok := asyncFuncs[callee]; ok {
|
||||
callsAsyncNotYield = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
coroDebugPrintln("result", f.Name(), callsYield, getsCoroutine, callsAsyncNotYield)
|
||||
|
||||
if callsYield && !getsCoroutine && !callsAsyncNotYield {
|
||||
coroDebugPrintln("optimizing", f.Name())
|
||||
// calls yield without registering for a wakeup
|
||||
// this actually could otherwise wake up, but only in the case of really messed up undefined behavior
|
||||
// so everything after a yield is unreachable, so we can just inject a fake return
|
||||
delQueue := []llvm.Value{}
|
||||
for bb := f.EntryBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||
var broken bool
|
||||
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
if !broken && !inst.IsACallInst().IsNil() && inst.CalledValue() == yield {
|
||||
coroDebugPrintln("broke", f.Name(), bb.AsValue().Name())
|
||||
broken = true
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
c.createRuntimeCall("noret", []llvm.Value{}, "")
|
||||
if f.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind {
|
||||
c.builder.CreateRetVoid()
|
||||
} else {
|
||||
c.builder.CreateRet(llvm.Undef(f.Type().ElementType().ReturnType()))
|
||||
}
|
||||
}
|
||||
if broken {
|
||||
if inst.Type().TypeKind() != llvm.VoidTypeKind {
|
||||
inst.ReplaceAllUsesWith(llvm.Undef(inst.Type()))
|
||||
}
|
||||
delQueue = append(delQueue, inst)
|
||||
}
|
||||
}
|
||||
if !broken {
|
||||
coroDebugPrintln("did not break", f.Name(), bb.AsValue().Name())
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range delQueue {
|
||||
v.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
nonReturning[f] = true
|
||||
}
|
||||
}
|
||||
|
||||
// convert direct calls into an async call followed by a yield operation
|
||||
coroDebugPrintln("convert direct calls into an async call followed by a yield operation")
|
||||
for _, f := range asyncList {
|
||||
if f == yield {
|
||||
continue
|
||||
}
|
||||
coroDebugPrintln("scanning", f.Name())
|
||||
|
||||
// Rewrite async calls
|
||||
for bb := f.EntryBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
if !inst.IsACallInst().IsNil() {
|
||||
callee := inst.CalledValue()
|
||||
if _, ok := asyncFuncs[callee]; !ok || callee == yield {
|
||||
continue
|
||||
}
|
||||
|
||||
uses := getUses(inst)
|
||||
next := llvm.NextInstruction(inst)
|
||||
switch {
|
||||
case nonReturning[callee]:
|
||||
// callee blocks forever
|
||||
coroDebugPrintln("optimizing indefinitely blocking call", f.Name(), callee.Name())
|
||||
|
||||
// never calls getCoroutine - coroutine handle is irrelevant
|
||||
inst.SetOperand(inst.OperandsCount()-2, llvm.Undef(c.i8ptrType))
|
||||
|
||||
// insert return
|
||||
c.builder.SetInsertPointBefore(next)
|
||||
c.createRuntimeCall("noret", []llvm.Value{}, "")
|
||||
var retInst llvm.Value
|
||||
if f.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind {
|
||||
retInst = c.builder.CreateRetVoid()
|
||||
} else {
|
||||
retInst = c.builder.CreateRet(llvm.Undef(f.Type().ElementType().ReturnType()))
|
||||
}
|
||||
|
||||
// delete everything after return
|
||||
for next := llvm.NextInstruction(retInst); !next.IsNil(); next = llvm.NextInstruction(retInst) {
|
||||
if next.Type().TypeKind() != llvm.VoidTypeKind {
|
||||
next.ReplaceAllUsesWith(llvm.Undef(next.Type()))
|
||||
}
|
||||
next.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
continue
|
||||
case next.IsAReturnInst().IsNil():
|
||||
// not a return instruction
|
||||
coroDebugPrintln("not a return instruction", f.Name(), callee.Name())
|
||||
case callee.Type().ElementType().ReturnType() != f.Type().ElementType().ReturnType():
|
||||
// return types do not match
|
||||
coroDebugPrintln("return types do not match", f.Name(), callee.Name())
|
||||
case callee.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind:
|
||||
fallthrough
|
||||
case next.Operand(0) == inst:
|
||||
// async tail call optimization - just pass parent handle
|
||||
coroDebugPrintln("doing async tail call opt", f.Name())
|
||||
|
||||
// insert before call
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
|
||||
// get parent handle
|
||||
parentHandle := c.createRuntimeCall("getParentHandle", []llvm.Value{}, "")
|
||||
|
||||
// pass parent handle directly into function
|
||||
inst.SetOperand(inst.OperandsCount()-2, parentHandle)
|
||||
|
||||
if callee.Type().ElementType().ReturnType().TypeKind() != llvm.VoidTypeKind {
|
||||
// delete return value
|
||||
uses[0].SetOperand(0, llvm.Undef(callee.Type().ElementType().ReturnType()))
|
||||
}
|
||||
|
||||
c.builder.SetInsertPointBefore(next)
|
||||
c.createRuntimeCall("yield", []llvm.Value{}, "")
|
||||
c.createRuntimeCall("noret", []llvm.Value{}, "")
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
coroDebugPrintln("inserting regular call", f.Name(), callee.Name())
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
|
||||
// insert call to getCoroutine, this will be lowered later
|
||||
coro := c.createRuntimeCall("getCoroutine", []llvm.Value{}, "")
|
||||
|
||||
// provide coroutine handle to function
|
||||
inst.SetOperand(inst.OperandsCount()-2, coro)
|
||||
|
||||
// Allocate space for the return value.
|
||||
var retvalAlloca llvm.Value
|
||||
if callee.Type().ElementType().ReturnType().TypeKind() != llvm.VoidTypeKind {
|
||||
// allocate return value buffer
|
||||
retvalAlloca = c.createInstructionAlloca(callee.Type().ElementType().ReturnType(), inst, "coro.retvalAlloca")
|
||||
|
||||
// call before function
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
|
||||
// cast buffer pointer to *i8
|
||||
data := c.builder.CreateBitCast(retvalAlloca, c.i8ptrType, "")
|
||||
|
||||
// set state pointer to return value buffer so it can be written back
|
||||
c.createRuntimeCall("setTaskStatePtr", []llvm.Value{coro, data}, "")
|
||||
}
|
||||
|
||||
// insert yield after starting function
|
||||
c.builder.SetInsertPointBefore(llvm.NextInstruction(inst))
|
||||
yieldCall := c.createRuntimeCall("yield", []llvm.Value{}, "")
|
||||
|
||||
if !retvalAlloca.IsNil() && !inst.FirstUse().IsNil() {
|
||||
// Load the return value from the alloca.
|
||||
// The callee has written the return value to it.
|
||||
c.builder.SetInsertPointBefore(llvm.NextInstruction(yieldCall))
|
||||
retval := c.builder.CreateLoad(retvalAlloca, "coro.retval")
|
||||
inst.ReplaceAllUsesWith(retval)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ditch unnecessary tail yields
|
||||
coroDebugPrintln("ditch unnecessary tail yields")
|
||||
noret := c.mod.NamedFunction("runtime.noret")
|
||||
for _, f := range asyncList {
|
||||
if f == yield {
|
||||
continue
|
||||
}
|
||||
coroDebugPrintln("scanning", f.Name())
|
||||
|
||||
// we can only ditch a yield if we can ditch all yields
|
||||
var yields []llvm.Value
|
||||
var canDitch bool
|
||||
scanYields:
|
||||
for bb := f.EntryBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
if inst.IsACallInst().IsNil() || inst.CalledValue() != yield {
|
||||
continue
|
||||
}
|
||||
|
||||
yields = append(yields, inst)
|
||||
|
||||
// we can only ditch the yield if the next instruction is a void return *or* noret
|
||||
next := llvm.NextInstruction(inst)
|
||||
ditchable := false
|
||||
switch {
|
||||
case !next.IsACallInst().IsNil() && next.CalledValue() == noret:
|
||||
coroDebugPrintln("ditching yield with noret", f.Name())
|
||||
ditchable = true
|
||||
case !next.IsAReturnInst().IsNil() && f.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind:
|
||||
coroDebugPrintln("ditching yield with void return", f.Name())
|
||||
ditchable = true
|
||||
case !next.IsAReturnInst().IsNil():
|
||||
coroDebugPrintln("not ditching because return is not void", f.Name(), f.Type().ElementType().ReturnType().String())
|
||||
default:
|
||||
coroDebugPrintln("not ditching", f.Name())
|
||||
}
|
||||
if !ditchable {
|
||||
// unditchable yield
|
||||
canDitch = false
|
||||
break scanYields
|
||||
}
|
||||
|
||||
// ditchable yield
|
||||
canDitch = true
|
||||
}
|
||||
}
|
||||
|
||||
if canDitch {
|
||||
coroDebugPrintln("ditching all in", f.Name())
|
||||
for _, inst := range yields {
|
||||
if !llvm.NextInstruction(inst).IsAReturnInst().IsNil() {
|
||||
// insert noret
|
||||
coroDebugPrintln("insering noret", f.Name())
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
c.createRuntimeCall("noret", []llvm.Value{}, "")
|
||||
}
|
||||
|
||||
// delete original yield
|
||||
inst.EraseFromParentAsInstruction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generate return reactivations
|
||||
coroDebugPrintln("generate return reactivations")
|
||||
for _, f := range asyncList {
|
||||
if f == yield {
|
||||
continue
|
||||
}
|
||||
coroDebugPrintln("scanning", f.Name())
|
||||
|
||||
var retPtr llvm.Value
|
||||
for bb := f.EntryBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||
block:
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
switch {
|
||||
case !inst.IsACallInst().IsNil() && inst.CalledValue() == noret:
|
||||
// does not return normally - skip this basic block
|
||||
coroDebugPrintln("noret found - skipping", f.Name(), bb.AsValue().Name())
|
||||
break block
|
||||
case !inst.IsAReturnInst().IsNil():
|
||||
// return instruction - rewrite to reactivation
|
||||
coroDebugPrintln("adding return reactivation", f.Name(), bb.AsValue().Name())
|
||||
if f.Type().ElementType().ReturnType().TypeKind() != llvm.VoidTypeKind {
|
||||
// returns something
|
||||
if retPtr.IsNil() {
|
||||
coroDebugPrintln("adding return pointer get", f.Name())
|
||||
|
||||
// get return pointer in entry block
|
||||
c.builder.SetInsertPointBefore(f.EntryBasicBlock().FirstInstruction())
|
||||
parentHandle := c.createRuntimeCall("getParentHandle", []llvm.Value{}, "")
|
||||
ptr := c.createRuntimeCall("getTaskStatePtr", []llvm.Value{parentHandle}, "")
|
||||
retPtr = c.builder.CreateBitCast(ptr, llvm.PointerType(f.Type().ElementType().ReturnType(), 0), "retPtr")
|
||||
}
|
||||
|
||||
coroDebugPrintln("adding return store", f.Name(), bb.AsValue().Name())
|
||||
|
||||
// store result into return pointer
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
c.builder.CreateStore(inst.Operand(0), retPtr)
|
||||
|
||||
// delete return value
|
||||
inst.SetOperand(0, llvm.Undef(f.Type().ElementType().ReturnType()))
|
||||
}
|
||||
|
||||
// insert reactivation call
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
parentHandle := c.createRuntimeCall("getParentHandle", []llvm.Value{}, "")
|
||||
c.createRuntimeCall("activateTask", []llvm.Value{parentHandle}, "")
|
||||
|
||||
// mark as noret
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
c.createRuntimeCall("noret", []llvm.Value{}, "")
|
||||
break block
|
||||
|
||||
// DO NOT ERASE THE RETURN!!!!!!!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a few LLVM intrinsics for coroutine support.
|
||||
@@ -362,45 +703,66 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
coroFreeType := llvm.FunctionType(c.i8ptrType, []llvm.Type{c.ctx.TokenType(), c.i8ptrType}, false)
|
||||
coroFreeFunc := llvm.AddFunction(c.mod, "llvm.coro.free", coroFreeType)
|
||||
|
||||
// Transform all async functions into coroutines.
|
||||
// split blocks and add LLVM coroutine intrinsics
|
||||
coroDebugPrintln("split blocks and add LLVM coroutine intrinsics")
|
||||
for _, f := range asyncList {
|
||||
if f == sleep || f == deadlock || f == chanSend || f == chanRecv {
|
||||
if f == yield {
|
||||
continue
|
||||
}
|
||||
|
||||
frame := asyncFuncs[f]
|
||||
frame.cleanupBlock = c.ctx.AddBasicBlock(f, "task.cleanup")
|
||||
frame.suspendBlock = c.ctx.AddBasicBlock(f, "task.suspend")
|
||||
frame.unreachableBlock = c.ctx.AddBasicBlock(f, "task.unreachable")
|
||||
|
||||
// Scan for async calls and return instructions that need to have
|
||||
// suspend points inserted.
|
||||
var asyncCalls []llvm.Value
|
||||
var returns []llvm.Value
|
||||
// find calls to yield
|
||||
var yieldCalls []llvm.Value
|
||||
for bb := f.EntryBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
if !inst.IsACallInst().IsNil() {
|
||||
callee := inst.CalledValue()
|
||||
if _, ok := asyncFuncs[callee]; !ok || callee == sleep || callee == deadlock || callee == chanSend || callee == chanRecv {
|
||||
continue
|
||||
}
|
||||
asyncCalls = append(asyncCalls, inst)
|
||||
} else if !inst.IsAReturnInst().IsNil() {
|
||||
returns = append(returns, inst)
|
||||
if !inst.IsACallInst().IsNil() && inst.CalledValue() == yield {
|
||||
yieldCalls = append(yieldCalls, inst)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Coroutine setup.
|
||||
if len(yieldCalls) == 0 {
|
||||
// no yields - we do not have to LLVM-ify this
|
||||
coroDebugPrintln("skipping", f.Name())
|
||||
deleteQueue := []llvm.Value{}
|
||||
for bb := f.EntryBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
if !inst.IsACallInst().IsNil() && inst.CalledValue() == getCoroutine {
|
||||
// no seperate local task - replace getCoroutine with getParentHandle
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
inst.ReplaceAllUsesWith(c.createRuntimeCall("getParentHandle", []llvm.Value{}, ""))
|
||||
deleteQueue = append(deleteQueue, inst)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, v := range deleteQueue {
|
||||
v.EraseFromParentAsInstruction()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
coroDebugPrintln("converting", f.Name())
|
||||
|
||||
// get frame data to mess with
|
||||
frame := asyncFuncs[f]
|
||||
|
||||
// add basic blocks to put cleanup and suspend code
|
||||
frame.cleanupBlock = c.ctx.AddBasicBlock(f, "task.cleanup")
|
||||
frame.suspendBlock = c.ctx.AddBasicBlock(f, "task.suspend")
|
||||
|
||||
// at start of function
|
||||
c.builder.SetInsertPointBefore(f.EntryBasicBlock().FirstInstruction())
|
||||
taskState := c.builder.CreateAlloca(c.getLLVMRuntimeType("taskState"), "task.state")
|
||||
stateI8 := c.builder.CreateBitCast(taskState, c.i8ptrType, "task.state.i8")
|
||||
|
||||
// get LLVM-assigned coroutine ID
|
||||
id := c.builder.CreateCall(coroIdFunc, []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
stateI8,
|
||||
llvm.ConstNull(c.i8ptrType),
|
||||
llvm.ConstNull(c.i8ptrType),
|
||||
}, "task.token")
|
||||
|
||||
// allocate buffer for task struct
|
||||
size := c.builder.CreateCall(coroSizeFunc, nil, "task.size")
|
||||
if c.targetData.TypeAllocSize(size.Type()) > c.targetData.TypeAllocSize(c.uintptrType) {
|
||||
size = c.builder.CreateTrunc(size, c.uintptrType, "task.size.uintptr")
|
||||
@@ -411,108 +773,10 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
if c.needsStackObjects() {
|
||||
c.trackPointer(data)
|
||||
}
|
||||
|
||||
// invoke llvm.coro.begin intrinsic and save task pointer
|
||||
frame.taskHandle = c.builder.CreateCall(coroBeginFunc, []llvm.Value{id, data}, "task.handle")
|
||||
|
||||
// Modify async calls so this function suspends right after the child
|
||||
// returns, because the child is probably not finished yet. Wait until
|
||||
// the child reactivates the parent.
|
||||
for _, inst := range asyncCalls {
|
||||
inst.SetOperand(inst.OperandsCount()-2, frame.taskHandle)
|
||||
|
||||
// Split this basic block.
|
||||
await := c.splitBasicBlock(inst, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.await")
|
||||
|
||||
// Allocate space for the return value.
|
||||
var retvalAlloca llvm.Value
|
||||
if inst.Type().TypeKind() != llvm.VoidTypeKind {
|
||||
c.builder.SetInsertPointBefore(inst.InstructionParent().Parent().EntryBasicBlock().FirstInstruction())
|
||||
retvalAlloca = c.builder.CreateAlloca(inst.Type(), "coro.retvalAlloca")
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
data := c.builder.CreateBitCast(retvalAlloca, c.i8ptrType, "")
|
||||
c.createRuntimeCall("setTaskStatePtr", []llvm.Value{frame.taskHandle, data}, "")
|
||||
}
|
||||
|
||||
// Suspend.
|
||||
c.builder.SetInsertPointAtEnd(inst.InstructionParent())
|
||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||
llvm.ConstNull(c.ctx.TokenType()),
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||
}, "")
|
||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), await)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
|
||||
|
||||
if inst.Type().TypeKind() != llvm.VoidTypeKind {
|
||||
// Load the return value from the alloca. The callee has
|
||||
// written the return value to it.
|
||||
c.builder.SetInsertPointBefore(await.FirstInstruction())
|
||||
retval := c.builder.CreateLoad(retvalAlloca, "coro.retval")
|
||||
inst.ReplaceAllUsesWith(retval)
|
||||
}
|
||||
}
|
||||
|
||||
// Replace return instructions with suspend points that should
|
||||
// reactivate the parent coroutine.
|
||||
for _, inst := range returns {
|
||||
// These properties were added by the functionattrs pass. Remove
|
||||
// them, because now we start using the parameter.
|
||||
// https://llvm.org/docs/Passes.html#functionattrs-deduce-function-attributes
|
||||
for _, kind := range []string{"nocapture", "readnone"} {
|
||||
kindID := llvm.AttributeKindID(kind)
|
||||
f.RemoveEnumAttributeAtIndex(f.ParamsCount(), kindID)
|
||||
}
|
||||
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
|
||||
var parentHandle llvm.Value
|
||||
if f.Linkage() == llvm.ExternalLinkage {
|
||||
// Exported function.
|
||||
// Note that getTaskStatePtr will panic if it is called with
|
||||
// a nil pointer, so blocking exported functions that try to
|
||||
// return anything will not work.
|
||||
parentHandle = llvm.ConstPointerNull(c.i8ptrType)
|
||||
} else {
|
||||
parentHandle = f.LastParam()
|
||||
if parentHandle.IsNil() || parentHandle.Name() != "parentHandle" {
|
||||
// sanity check
|
||||
panic("trying to make exported function async: " + f.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// Store return values.
|
||||
switch inst.OperandsCount() {
|
||||
case 0:
|
||||
// Nothing to return.
|
||||
case 1:
|
||||
// Return this value by writing to the pointer stored in the
|
||||
// parent handle. The parent coroutine has made an alloca that
|
||||
// we can write to to store our return value.
|
||||
returnValuePtr := c.createRuntimeCall("getTaskStatePtr", []llvm.Value{parentHandle}, "coro.parentData")
|
||||
alloca := c.builder.CreateBitCast(returnValuePtr, llvm.PointerType(inst.Operand(0).Type(), 0), "coro.parentAlloca")
|
||||
c.builder.CreateStore(inst.Operand(0), alloca)
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Reactivate the parent coroutine. This adds it back to the run
|
||||
// queue, so it is started again by the scheduler when possible
|
||||
// (possibly right after the following suspend).
|
||||
c.createRuntimeCall("activateTask", []llvm.Value{parentHandle}, "")
|
||||
|
||||
// Suspend this coroutine.
|
||||
// It would look like this is unnecessary, but if this
|
||||
// suspend point is left out, it leads to undefined
|
||||
// behavior somehow (with the unreachable instruction).
|
||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||
llvm.ConstNull(c.ctx.TokenType()),
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||
}, "ret")
|
||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), frame.unreachableBlock)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
|
||||
inst.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
// Coroutine cleanup. Free resources associated with this coroutine.
|
||||
c.builder.SetInsertPointAtEnd(frame.cleanupBlock)
|
||||
mem := c.builder.CreateCall(coroFreeFunc, []llvm.Value{id, frame.taskHandle}, "task.data.free")
|
||||
@@ -529,106 +793,96 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
c.builder.CreateRet(llvm.Undef(returnType))
|
||||
}
|
||||
|
||||
// Coroutine exit. All final suspends (return instructions) will branch
|
||||
// here.
|
||||
c.builder.SetInsertPointAtEnd(frame.unreachableBlock)
|
||||
c.builder.CreateUnreachable()
|
||||
for _, inst := range yieldCalls {
|
||||
// Replace call to yield with a suspension of the coroutine.
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||
llvm.ConstNull(c.ctx.TokenType()),
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||
}, "")
|
||||
wakeup := c.splitBasicBlock(inst, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup")
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), wakeup)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
|
||||
inst.EraseFromParentAsInstruction()
|
||||
}
|
||||
ditchQueue := []llvm.Value{}
|
||||
for bb := f.EntryBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
if !inst.IsACallInst().IsNil() && inst.CalledValue() == getCoroutine {
|
||||
// replace getCoroutine calls with the task handle
|
||||
inst.ReplaceAllUsesWith(frame.taskHandle)
|
||||
ditchQueue = append(ditchQueue, inst)
|
||||
}
|
||||
if !inst.IsACallInst().IsNil() && inst.CalledValue() == noret {
|
||||
// replace tail yield with jump to cleanup, otherwise we end up with undefined behavior
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
c.builder.CreateBr(frame.cleanupBlock)
|
||||
ditchQueue = append(ditchQueue, inst, llvm.NextInstruction(inst))
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, v := range ditchQueue {
|
||||
v.EraseFromParentAsInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
// Replace calls to runtime.getCoroutineCall with the coroutine of this
|
||||
// frame.
|
||||
for _, getCoroutineCall := range getUses(c.mod.NamedFunction("runtime.getCoroutine")) {
|
||||
frame := asyncFuncs[getCoroutineCall.InstructionParent().Parent()]
|
||||
getCoroutineCall.ReplaceAllUsesWith(frame.taskHandle)
|
||||
getCoroutineCall.EraseFromParentAsInstruction()
|
||||
// check for leftover calls to getCoroutine
|
||||
if uses := getUses(getCoroutine); len(uses) > 0 {
|
||||
useNames := make([]string, 0, len(uses))
|
||||
for _, u := range uses {
|
||||
if u.InstructionParent().Parent().Name() == "runtime.llvmCoroRefHolder" {
|
||||
continue
|
||||
}
|
||||
useNames = append(useNames, u.InstructionParent().Parent().Name())
|
||||
}
|
||||
if len(useNames) > 0 {
|
||||
panic("bad use of getCoroutine: " + strings.Join(useNames, ","))
|
||||
}
|
||||
}
|
||||
|
||||
// Transform calls to time.Sleep() into coroutine suspend points.
|
||||
for _, sleepCall := range getUses(sleep) {
|
||||
// sleepCall must be a call instruction.
|
||||
frame := asyncFuncs[sleepCall.InstructionParent().Parent()]
|
||||
duration := sleepCall.Operand(0)
|
||||
|
||||
// Set task state to TASK_STATE_SLEEP and set the duration.
|
||||
c.builder.SetInsertPointBefore(sleepCall)
|
||||
c.createRuntimeCall("sleepTask", []llvm.Value{frame.taskHandle, duration}, "")
|
||||
|
||||
// Yield to scheduler.
|
||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||
llvm.ConstNull(c.ctx.TokenType()),
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||
}, "")
|
||||
wakeup := c.splitBasicBlock(sleepCall, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup")
|
||||
c.builder.SetInsertPointBefore(sleepCall)
|
||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), wakeup)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
|
||||
sleepCall.EraseFromParentAsInstruction()
|
||||
// rewrite calls to getParentHandle
|
||||
for _, inst := range getUses(c.mod.NamedFunction("runtime.getParentHandle")) {
|
||||
f := inst.InstructionParent().Parent()
|
||||
var parentHandle llvm.Value
|
||||
parentHandle = f.LastParam()
|
||||
if parentHandle.IsNil() || parentHandle.Name() != "parentHandle" {
|
||||
// sanity check
|
||||
panic("trying to make exported function async: " + f.Name())
|
||||
}
|
||||
inst.ReplaceAllUsesWith(parentHandle)
|
||||
inst.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
// Transform calls to runtime.deadlock into coroutine suspends (without
|
||||
// resume).
|
||||
for _, deadlockCall := range getUses(deadlock) {
|
||||
// deadlockCall must be a call instruction.
|
||||
frame := asyncFuncs[deadlockCall.InstructionParent().Parent()]
|
||||
|
||||
// Exit coroutine.
|
||||
c.builder.SetInsertPointBefore(deadlockCall)
|
||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||
llvm.ConstNull(c.ctx.TokenType()),
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||
}, "")
|
||||
c.splitBasicBlock(deadlockCall, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup.dead")
|
||||
c.builder.SetInsertPointBefore(deadlockCall)
|
||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), frame.unreachableBlock)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
|
||||
deadlockCall.EraseFromParentAsInstruction()
|
||||
// ditch invalid function attributes
|
||||
bads := []llvm.Value{c.mod.NamedFunction("runtime.setTaskStatePtr")}
|
||||
for _, f := range append(bads, asyncList...) {
|
||||
// These properties were added by the functionattrs pass. Remove
|
||||
// them, because now we start using the parameter.
|
||||
// https://llvm.org/docs/Passes.html#functionattrs-deduce-function-attributes
|
||||
for _, kind := range []string{"nocapture", "readnone"} {
|
||||
kindID := llvm.AttributeKindID(kind)
|
||||
n := f.ParamsCount()
|
||||
for i := 0; i <= n; i++ {
|
||||
f.RemoveEnumAttributeAtIndex(i, kindID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transform calls to runtime.chanSend into channel send operations.
|
||||
for _, sendOp := range getUses(chanSend) {
|
||||
// sendOp must be a call instruction.
|
||||
frame := asyncFuncs[sendOp.InstructionParent().Parent()]
|
||||
|
||||
// Yield to scheduler.
|
||||
c.builder.SetInsertPointBefore(llvm.NextInstruction(sendOp))
|
||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||
llvm.ConstNull(c.ctx.TokenType()),
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||
}, "")
|
||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||
wakeup := c.splitBasicBlock(sw, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.sent")
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), wakeup)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
|
||||
// eliminate noret
|
||||
for _, inst := range getUses(noret) {
|
||||
inst.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
// Transform calls to runtime.chanRecv into channel receive operations.
|
||||
for _, recvOp := range getUses(chanRecv) {
|
||||
// recvOp must be a call instruction.
|
||||
frame := asyncFuncs[recvOp.InstructionParent().Parent()]
|
||||
|
||||
// Yield to scheduler.
|
||||
c.builder.SetInsertPointBefore(llvm.NextInstruction(recvOp))
|
||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||
llvm.ConstNull(c.ctx.TokenType()),
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||
}, "")
|
||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||
wakeup := c.splitBasicBlock(sw, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.received")
|
||||
c.builder.SetInsertPointAtEnd(recvOp.InstructionParent())
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), wakeup)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
|
||||
}
|
||||
|
||||
return true, c.lowerMakeGoroutineCalls()
|
||||
return true, c.lowerMakeGoroutineCalls(true)
|
||||
}
|
||||
|
||||
// Lower runtime.makeGoroutine calls to regular call instructions. This is done
|
||||
// after the regular goroutine transformations. The started goroutines are
|
||||
// either non-blocking (in which case they can be called directly) or blocking,
|
||||
// in which case they will ask the scheduler themselves to be rescheduled.
|
||||
func (c *Compiler) lowerMakeGoroutineCalls() error {
|
||||
func (c *Compiler) lowerMakeGoroutineCalls(sched bool) error {
|
||||
// The following Go code:
|
||||
// go startedGoroutine()
|
||||
//
|
||||
@@ -661,13 +915,21 @@ func (c *Compiler) lowerMakeGoroutineCalls() error {
|
||||
for i := 0; i < realCall.OperandsCount()-1; i++ {
|
||||
params = append(params, realCall.Operand(i))
|
||||
}
|
||||
params[len(params)-1] = llvm.ConstPointerNull(c.i8ptrType) // parent coroutine handle (must be nil)
|
||||
c.builder.SetInsertPointBefore(realCall)
|
||||
if (!sched) || goroutine.InstructionParent().Parent() == c.mod.NamedFunction("runtime.getFakeCoroutine") {
|
||||
params[len(params)-1] = llvm.Undef(c.i8ptrType)
|
||||
} else {
|
||||
params[len(params)-1] = c.createRuntimeCall("getFakeCoroutine", []llvm.Value{}, "") // parent coroutine handle (must not be nil)
|
||||
}
|
||||
c.builder.CreateCall(origFunc, params, "")
|
||||
realCall.EraseFromParentAsInstruction()
|
||||
inttoptrOut.EraseFromParentAsInstruction()
|
||||
goroutine.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
if !sched && len(getUses(c.mod.NamedFunction("runtime.getFakeCoroutine"))) > 0 {
|
||||
panic("getFakeCoroutine used without scheduler")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -54,6 +54,22 @@ func (c *Compiler) createTemporaryAlloca(t llvm.Type, name string) (alloca, bitc
|
||||
return
|
||||
}
|
||||
|
||||
// createInstructionAlloca creates an alloca in the entry block, and places lifetime control intrinsics around the instruction
|
||||
func (c *Compiler) createInstructionAlloca(t llvm.Type, inst llvm.Value, name string) llvm.Value {
|
||||
alloca := c.createEntryBlockAlloca(t, name)
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
bitcast := c.builder.CreateBitCast(alloca, c.i8ptrType, name+".bitcast")
|
||||
size := llvm.ConstInt(c.ctx.Int64Type(), c.targetData.TypeAllocSize(t), false)
|
||||
c.builder.CreateCall(c.getLifetimeStartFunc(), []llvm.Value{size, bitcast}, "")
|
||||
if next := llvm.NextInstruction(inst); !next.IsNil() {
|
||||
c.builder.SetInsertPointBefore(next)
|
||||
} else {
|
||||
c.builder.SetInsertPointAtEnd(inst.InstructionParent())
|
||||
}
|
||||
c.builder.CreateCall(c.getLifetimeEndFunc(), []llvm.Value{size, bitcast}, "")
|
||||
return alloca
|
||||
}
|
||||
|
||||
// emitLifetimeEnd signals the end of an (alloca) lifetime by calling the
|
||||
// llvm.lifetime.end intrinsic. It is commonly used together with
|
||||
// createTemporaryAlloca.
|
||||
|
||||
+3
-103
@@ -50,7 +50,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
||||
|
||||
// Run Go-specific optimization passes.
|
||||
transform.OptimizeMaps(c.mod)
|
||||
c.OptimizeStringToBytes()
|
||||
transform.OptimizeStringToBytes(c.mod)
|
||||
transform.OptimizeAllocs(c.mod)
|
||||
c.LowerInterfaces()
|
||||
c.LowerFuncValues()
|
||||
@@ -62,7 +62,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
||||
|
||||
// Run TinyGo-specific interprocedural optimizations.
|
||||
transform.OptimizeAllocs(c.mod)
|
||||
c.OptimizeStringToBytes()
|
||||
transform.OptimizeStringToBytes(c.mod)
|
||||
|
||||
// Lower runtime.isnil calls to regular nil comparisons.
|
||||
isnil := c.mod.NamedFunction("runtime.isnil")
|
||||
@@ -108,7 +108,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
||||
}
|
||||
|
||||
// After TinyGo-specific transforms have finished, undo exporting these functions.
|
||||
for _, name := range functionsUsedInTransforms {
|
||||
for _, name := range c.getFunctionsUsedInTransforms() {
|
||||
fn := c.mod.NamedFunction(name)
|
||||
if fn.IsNil() {
|
||||
continue
|
||||
@@ -158,103 +158,3 @@ func (c *Compiler) replacePanicsWithTrap() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transform runtime.stringToBytes(...) calls into const []byte slices whenever
|
||||
// possible. This optimizes the following pattern:
|
||||
// w.Write([]byte("foo"))
|
||||
// where Write does not store to the slice.
|
||||
func (c *Compiler) OptimizeStringToBytes() {
|
||||
stringToBytes := c.mod.NamedFunction("runtime.stringToBytes")
|
||||
if stringToBytes.IsNil() {
|
||||
// nothing to optimize
|
||||
return
|
||||
}
|
||||
|
||||
for _, call := range getUses(stringToBytes) {
|
||||
strptr := call.Operand(0)
|
||||
strlen := call.Operand(1)
|
||||
|
||||
// strptr is always constant because strings are always constant.
|
||||
|
||||
convertedAllUses := true
|
||||
for _, use := range getUses(call) {
|
||||
nilValue := llvm.Value{}
|
||||
if use.IsAExtractValueInst() == nilValue {
|
||||
convertedAllUses = false
|
||||
continue
|
||||
}
|
||||
switch use.Type().TypeKind() {
|
||||
case llvm.IntegerTypeKind:
|
||||
// A length (len or cap). Propagate the length value.
|
||||
use.ReplaceAllUsesWith(strlen)
|
||||
use.EraseFromParentAsInstruction()
|
||||
case llvm.PointerTypeKind:
|
||||
// The string pointer itself.
|
||||
if !c.isReadOnly(use) {
|
||||
convertedAllUses = false
|
||||
continue
|
||||
}
|
||||
use.ReplaceAllUsesWith(strptr)
|
||||
use.EraseFromParentAsInstruction()
|
||||
default:
|
||||
// should not happen
|
||||
panic("unknown return type of runtime.stringToBytes: " + use.Type().String())
|
||||
}
|
||||
}
|
||||
if convertedAllUses {
|
||||
// Call to runtime.stringToBytes can be eliminated: both the input
|
||||
// and the output is constant.
|
||||
call.EraseFromParentAsInstruction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the given value (which is of pointer type) is never stored to.
|
||||
func (c *Compiler) isReadOnly(value llvm.Value) bool {
|
||||
uses := getUses(value)
|
||||
for _, use := range uses {
|
||||
nilValue := llvm.Value{}
|
||||
if use.IsAGetElementPtrInst() != nilValue {
|
||||
if !c.isReadOnly(use) {
|
||||
return false
|
||||
}
|
||||
} else if use.IsACallInst() != nilValue {
|
||||
if !c.hasFlag(use, value, "readonly") {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
// Unknown instruction, might not be readonly.
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Check whether all uses of this param as parameter to the call have the given
|
||||
// flag. In most cases, there will only be one use but a function could take the
|
||||
// same parameter twice, in which case both must have the flag.
|
||||
// A flag can be any enum flag, like "readonly".
|
||||
func (c *Compiler) hasFlag(call, param llvm.Value, kind string) bool {
|
||||
fn := call.CalledValue()
|
||||
nilValue := llvm.Value{}
|
||||
if fn.IsAFunction() == nilValue {
|
||||
// This is not a function but something else, like a function pointer.
|
||||
return false
|
||||
}
|
||||
kindID := llvm.AttributeKindID(kind)
|
||||
for i := 0; i < fn.ParamsCount(); i++ {
|
||||
if call.Operand(i) != param {
|
||||
// This is not the parameter we're checking.
|
||||
continue
|
||||
}
|
||||
index := i + 1 // param attributes start at 1
|
||||
attr := fn.GetEnumAttributeAtIndex(index, kindID)
|
||||
nilAttribute := llvm.Attribute{}
|
||||
if attr == nilAttribute {
|
||||
// At least one parameter doesn't have the flag (there may be
|
||||
// multiple).
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
+2
-9
@@ -126,15 +126,8 @@ type typeCodeAssignmentState struct {
|
||||
// that is ever stored in an interface. It tries to use the smallest possible
|
||||
// numbers to make the code that works with interfaces as small as possible.
|
||||
func (c *Compiler) assignTypeCodes(typeSlice typeInfoSlice) {
|
||||
fn := c.mod.NamedFunction("reflect.ValueOf")
|
||||
if fn.IsNil() {
|
||||
// reflect.ValueOf is never used, so we can use the most efficient
|
||||
// encoding possible.
|
||||
for i, t := range typeSlice {
|
||||
t.num = uint64(i + 1)
|
||||
}
|
||||
return
|
||||
}
|
||||
// if reflect were not used, we could skip generating the sidetable
|
||||
// this does not help in practice, and is difficult to do correctly
|
||||
|
||||
// Assign typecodes the way the reflect package expects.
|
||||
state := typeCodeAssignmentState{
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
@@ -20,6 +21,7 @@ import (
|
||||
type globalInfo struct {
|
||||
linkName string // go:extern
|
||||
extern bool // go:extern
|
||||
align int // go:align
|
||||
}
|
||||
|
||||
// loadASTComments loads comments on globals from the AST, for use later in the
|
||||
@@ -64,6 +66,9 @@ func (c *Compiler) getGlobal(g *ssa.Global) llvm.Value {
|
||||
llvmGlobal.SetInitializer(llvm.ConstNull(llvmType))
|
||||
llvmGlobal.SetLinkage(llvm.InternalLinkage)
|
||||
}
|
||||
if info.align > c.targetData.ABITypeAlignment(llvmType) {
|
||||
llvmGlobal.SetAlignment(info.align)
|
||||
}
|
||||
}
|
||||
return llvmGlobal
|
||||
}
|
||||
@@ -102,6 +107,11 @@ func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) {
|
||||
if len(parts) == 2 {
|
||||
info.linkName = parts[1]
|
||||
}
|
||||
case "//go:align":
|
||||
align, err := strconv.Atoi(parts[1])
|
||||
if err == nil {
|
||||
info.align = align
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ go 1.11
|
||||
|
||||
require (
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
||||
github.com/creack/goselect v0.1.0 // indirect
|
||||
github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46
|
||||
go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 // indirect
|
||||
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef
|
||||
tinygo.org/x/go-llvm v0.0.0-20190818154551-95bc4ffe1add
|
||||
)
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
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/creack/goselect v0.1.0 h1:4QiXIhcpSQF50XGaBsFzesjwX/1qOY5bOveQPmN9CXY=
|
||||
github.com/creack/goselect v0.1.0/go.mod h1:gHrIcH/9UZDn2qgeTUeW5K9eZsVYCH6/60J/FHysWyE=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46 h1:wXG2bA8fO7Vv7lLk2PihFMTqmbT173Tje39oKzQ50Mo=
|
||||
github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46/go.mod h1:Pb6XcsXyropB9LNHhnqaknG/vEwYztLkQzVCHv8sQ3M=
|
||||
go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45 h1:mACY1anK6HNCZtm/DK2Rf2ZPHggVqeB0+7rY9Gl6wyI=
|
||||
go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45/go.mod h1:dRSl/CVCTf56CkXgJMDOdSwNfo2g1orOGE/gBGdvjZw=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef h1:ymc9FeDom3RIEA3coKokSllBB1hRcMT0tZ1W3Jf9Ids=
|
||||
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
// Package goenv returns environment variables that are used in various parts of
|
||||
// the compiler. You can query it manually with the `tinygo env` subcommand.
|
||||
package goenv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// Keys is a slice of all available environment variable keys.
|
||||
var Keys = []string{
|
||||
"GOOS",
|
||||
"GOARCH",
|
||||
"GOROOT",
|
||||
"GOPATH",
|
||||
"GOCACHE",
|
||||
"TINYGOROOT",
|
||||
}
|
||||
|
||||
// TINYGOROOT is the path to the final location for checking tinygo files. If
|
||||
// unset (by a -X ldflag), then sourceDir() will fallback to the original build
|
||||
// directory.
|
||||
var TINYGOROOT string
|
||||
|
||||
// Get returns a single environment variable, possibly calculating it on-demand.
|
||||
// The empty string is returned for unknown environment variables.
|
||||
func Get(name string) string {
|
||||
switch name {
|
||||
case "GOOS":
|
||||
if dir := os.Getenv("GOOS"); dir != "" {
|
||||
return dir
|
||||
}
|
||||
return runtime.GOOS
|
||||
case "GOARCH":
|
||||
if dir := os.Getenv("GOARCH"); dir != "" {
|
||||
return dir
|
||||
}
|
||||
return runtime.GOARCH
|
||||
case "GOROOT":
|
||||
return getGoroot()
|
||||
case "GOPATH":
|
||||
if dir := os.Getenv("GOPATH"); dir != "" {
|
||||
return dir
|
||||
}
|
||||
|
||||
// fallback
|
||||
home := getHomeDir()
|
||||
return filepath.Join(home, "go")
|
||||
case "GOCACHE":
|
||||
// Get the cache directory, usually ~/.cache/tinygo
|
||||
dir, err := os.UserCacheDir()
|
||||
if err != nil {
|
||||
panic("could not find cache dir: " + err.Error())
|
||||
}
|
||||
return filepath.Join(dir, "tinygo")
|
||||
case "TINYGOROOT":
|
||||
return sourceDir()
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// Return the TINYGOROOT, or exit with an error.
|
||||
func sourceDir() string {
|
||||
// Use $TINYGOROOT as root, if available.
|
||||
root := os.Getenv("TINYGOROOT")
|
||||
if root != "" {
|
||||
if !isSourceDir(root) {
|
||||
fmt.Fprintln(os.Stderr, "error: $TINYGOROOT was not set to the correct root")
|
||||
os.Exit(1)
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
if TINYGOROOT != "" {
|
||||
if !isSourceDir(TINYGOROOT) {
|
||||
fmt.Fprintln(os.Stderr, "error: TINYGOROOT was not set to the correct root")
|
||||
os.Exit(1)
|
||||
}
|
||||
return TINYGOROOT
|
||||
}
|
||||
|
||||
// Find root from executable path.
|
||||
path, err := os.Executable()
|
||||
if err != nil {
|
||||
// Very unlikely. Bail out if it happens.
|
||||
panic("could not get executable path: " + err.Error())
|
||||
}
|
||||
root = filepath.Dir(filepath.Dir(path))
|
||||
if isSourceDir(root) {
|
||||
return root
|
||||
}
|
||||
|
||||
// Fallback: use the original directory from where it was built
|
||||
// https://stackoverflow.com/a/32163888/559350
|
||||
_, path, _, _ = runtime.Caller(0)
|
||||
root = filepath.Dir(filepath.Dir(path))
|
||||
if isSourceDir(root) {
|
||||
return root
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "error: could not autodetect root directory, set the TINYGOROOT environment variable to override")
|
||||
os.Exit(1)
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// isSourceDir returns true if the directory looks like a TinyGo source directory.
|
||||
func isSourceDir(root string) bool {
|
||||
_, err := os.Stat(filepath.Join(root, "src/runtime/internal/sys/zversion.go"))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_, err = os.Stat(filepath.Join(root, "src/device/arm/arm.go"))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func getHomeDir() string {
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
panic("cannot get current user: " + err.Error())
|
||||
}
|
||||
if u.HomeDir == "" {
|
||||
// This is very unlikely, so panic here.
|
||||
// Not the nicest solution, however.
|
||||
panic("could not find home directory")
|
||||
}
|
||||
return u.HomeDir
|
||||
}
|
||||
|
||||
// getGoroot returns an appropriate GOROOT from various sources. If it can't be
|
||||
// found, it returns an empty string.
|
||||
func getGoroot() string {
|
||||
goroot := os.Getenv("GOROOT")
|
||||
if goroot != "" {
|
||||
// An explicitly set GOROOT always has preference.
|
||||
return goroot
|
||||
}
|
||||
|
||||
// Check for the location of the 'go' binary and base GOROOT on that.
|
||||
binpath, err := exec.LookPath("go")
|
||||
if err == nil {
|
||||
binpath, err = filepath.EvalSymlinks(binpath)
|
||||
if err == nil {
|
||||
goroot := filepath.Dir(filepath.Dir(binpath))
|
||||
if isGoroot(goroot) {
|
||||
return goroot
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check what GOROOT was at compile time.
|
||||
if isGoroot(runtime.GOROOT()) {
|
||||
return runtime.GOROOT()
|
||||
}
|
||||
|
||||
// Check for some standard locations, as a last resort.
|
||||
var candidates []string
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
candidates = []string{
|
||||
"/usr/local/go", // manually installed
|
||||
"/usr/lib/go", // from the distribution
|
||||
}
|
||||
case "darwin":
|
||||
candidates = []string{
|
||||
"/usr/local/go", // manually installed
|
||||
"/usr/local/opt/go/libexec", // from Homebrew
|
||||
}
|
||||
}
|
||||
|
||||
for _, candidate := range candidates {
|
||||
if isGoroot(candidate) {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
|
||||
// Can't find GOROOT...
|
||||
return ""
|
||||
}
|
||||
|
||||
// isGoroot checks whether the given path looks like a GOROOT.
|
||||
func isGoroot(goroot string) bool {
|
||||
_, err := os.Stat(filepath.Join(goroot, "src", "runtime", "internal", "sys", "zversion.go"))
|
||||
return err == nil
|
||||
}
|
||||
@@ -316,6 +316,49 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||
ret = llvm.ConstInsertValue(ret, retPtr, []uint32{0})
|
||||
ret = llvm.ConstInsertValue(ret, retLen, []uint32{1})
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, ret}
|
||||
case callee.Name() == "runtime.sliceCopy":
|
||||
elementSize := fr.getLocal(inst.Operand(4)).(*LocalValue).Value().ZExtValue()
|
||||
dstArray := fr.getLocal(inst.Operand(0)).(*LocalValue).stripPointerCasts()
|
||||
srcArray := fr.getLocal(inst.Operand(1)).(*LocalValue).stripPointerCasts()
|
||||
dstLen := fr.getLocal(inst.Operand(2)).(*LocalValue)
|
||||
srcLen := fr.getLocal(inst.Operand(3)).(*LocalValue)
|
||||
if elementSize != 1 && dstArray.Type().ElementType().TypeKind() == llvm.ArrayTypeKind && srcArray.Type().ElementType().TypeKind() == llvm.ArrayTypeKind {
|
||||
// Slice data pointers are created by adding a global array
|
||||
// and getting the address of the first element using a GEP.
|
||||
// However, before the compiler can pass it to
|
||||
// runtime.sliceCopy, it has to perform a bitcast to a *i8,
|
||||
// to make it a unsafe.Pointer. Now, when the IR builder
|
||||
// sees a bitcast of a GEP with zero indices, it will make
|
||||
// a bitcast of the original array instead of the GEP,
|
||||
// which breaks our assumptions.
|
||||
// Re-add this GEP, in the hope that it it is then of the correct type...
|
||||
dstArray = dstArray.GetElementPtr([]uint32{0, 0}).(*LocalValue)
|
||||
srcArray = srcArray.GetElementPtr([]uint32{0, 0}).(*LocalValue)
|
||||
}
|
||||
if fr.Eval.TargetData.TypeAllocSize(dstArray.Type().ElementType()) != elementSize {
|
||||
return nil, nil, errors.New("interp: slice dst element size does not match pointer type")
|
||||
}
|
||||
if fr.Eval.TargetData.TypeAllocSize(srcArray.Type().ElementType()) != elementSize {
|
||||
return nil, nil, errors.New("interp: slice src element size does not match pointer type")
|
||||
}
|
||||
if dstArray.Type() != srcArray.Type() {
|
||||
return nil, nil, errors.New("interp: slice element types don't match")
|
||||
}
|
||||
length := dstLen.Value().SExtValue()
|
||||
if srcLength := srcLen.Value().SExtValue(); srcLength < length {
|
||||
length = srcLength
|
||||
}
|
||||
if length < 0 {
|
||||
return nil, nil, errors.New("interp: trying to copy a slice with negative length?")
|
||||
}
|
||||
for i := int64(0); i < length; i++ {
|
||||
// *dst = *src
|
||||
dstArray.Store(srcArray.Load())
|
||||
// dst++
|
||||
dstArray = dstArray.GetElementPtr([]uint32{1}).(*LocalValue)
|
||||
// src++
|
||||
srcArray = srcArray.GetElementPtr([]uint32{1}).(*LocalValue)
|
||||
}
|
||||
case callee.Name() == "runtime.stringToBytes":
|
||||
// convert a string to a []byte
|
||||
bufPtr := fr.getLocal(inst.Operand(0))
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ type Eval struct {
|
||||
|
||||
// Run evaluates the function with the given name and then eliminates all
|
||||
// callers.
|
||||
func Run(mod llvm.Module, targetData llvm.TargetData, debug bool) error {
|
||||
func Run(mod llvm.Module, debug bool) error {
|
||||
if debug {
|
||||
println("\ncompile-time evaluation:")
|
||||
}
|
||||
@@ -32,7 +32,7 @@ func Run(mod llvm.Module, targetData llvm.TargetData, debug bool) error {
|
||||
name := "runtime.initAll"
|
||||
e := &Eval{
|
||||
Mod: mod,
|
||||
TargetData: targetData,
|
||||
TargetData: llvm.NewTargetData(mod.DataLayout()),
|
||||
Debug: debug,
|
||||
dirtyGlobals: map[llvm.Value]struct{}{},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
func TestInterp(t *testing.T) {
|
||||
for _, name := range []string{
|
||||
"basic",
|
||||
"slice-copy",
|
||||
} {
|
||||
name := name // make tc local to this closure
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest(t, "testdata/"+name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func runTest(t *testing.T, pathPrefix string) {
|
||||
// Read the input IR.
|
||||
ctx := llvm.NewContext()
|
||||
buf, err := llvm.NewMemoryBufferFromFile(pathPrefix + ".ll")
|
||||
os.Stat(pathPrefix + ".ll") // make sure this file is tracked by `go test` caching
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file %s: %v", pathPrefix+".ll", err)
|
||||
}
|
||||
mod, err := ctx.ParseIR(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("could not load module:\n%v", err)
|
||||
}
|
||||
|
||||
// Perform the transform.
|
||||
err = Run(mod, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Run some cleanup passes to get easy-to-read outputs.
|
||||
pm := llvm.NewPassManager()
|
||||
defer pm.Dispose()
|
||||
pm.AddGlobalOptimizerPass()
|
||||
pm.AddDeadStoreEliminationPass()
|
||||
pm.Run(mod)
|
||||
|
||||
// Read the expected output IR.
|
||||
out, err := ioutil.ReadFile(pathPrefix + ".out.ll")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read output file %s: %v", pathPrefix+".out.ll", err)
|
||||
}
|
||||
|
||||
// See whether the transform output matches with the expected output IR.
|
||||
expected := string(out)
|
||||
actual := mod.String()
|
||||
if !fuzzyEqualIR(expected, actual) {
|
||||
t.Logf("output does not match expected output:\n%s", actual)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
// fuzzyEqualIR returns true if the two LLVM IR strings passed in are roughly
|
||||
// equal. That means, only relevant lines are compared (excluding comments
|
||||
// etc.).
|
||||
func fuzzyEqualIR(s1, s2 string) bool {
|
||||
lines1 := filterIrrelevantIRLines(strings.Split(s1, "\n"))
|
||||
lines2 := filterIrrelevantIRLines(strings.Split(s2, "\n"))
|
||||
if len(lines1) != len(lines2) {
|
||||
return false
|
||||
}
|
||||
for i, line := range lines1 {
|
||||
if line != lines2[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// filterIrrelevantIRLines removes lines from the input slice of strings that
|
||||
// are not relevant in comparing IR. For example, empty lines and comments are
|
||||
// stripped out.
|
||||
func filterIrrelevantIRLines(lines []string) []string {
|
||||
var out []string
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line) // drop '\r' on Windows
|
||||
if line == "" || line[0] == ';' {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, "source_filename = ") {
|
||||
continue
|
||||
}
|
||||
out = append(out, line)
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -35,6 +35,8 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
|
||||
return &sideEffectResult{severity: sideEffectLimited}
|
||||
case "runtime.interfaceImplements":
|
||||
return &sideEffectResult{severity: sideEffectNone}
|
||||
case "runtime.sliceCopy":
|
||||
return &sideEffectResult{severity: sideEffectNone}
|
||||
case "runtime.trackPointer":
|
||||
return &sideEffectResult{severity: sideEffectNone}
|
||||
case "llvm.dbg.value":
|
||||
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
@main.v1 = internal global i64 0
|
||||
|
||||
declare void @runtime.printint64(i64) unnamed_addr
|
||||
|
||||
declare void @runtime.printnl() unnamed_addr
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call void @runtime.init()
|
||||
call void @main.init()
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() unnamed_addr {
|
||||
entry:
|
||||
%0 = load i64, i64* @main.v1
|
||||
call void @runtime.printint64(i64 %0)
|
||||
call void @runtime.printnl()
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @runtime.init() unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @main.init() unnamed_addr {
|
||||
entry:
|
||||
store i64 3, i64* @main.v1
|
||||
call void @"main.init#1"()
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @"main.init#1"() unnamed_addr {
|
||||
entry:
|
||||
call void @runtime.printint64(i64 5)
|
||||
call void @runtime.printnl()
|
||||
ret void
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
declare void @runtime.printint64(i64) unnamed_addr
|
||||
|
||||
declare void @runtime.printnl() unnamed_addr
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call void @runtime.printint64(i64 5)
|
||||
call void @runtime.printnl()
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() unnamed_addr {
|
||||
entry:
|
||||
call void @runtime.printint64(i64 3)
|
||||
call void @runtime.printnl()
|
||||
ret void
|
||||
}
|
||||
Vendored
+86
@@ -0,0 +1,86 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
@main.uint8SliceSrc.buf = internal global [2 x i8] c"\03d"
|
||||
@main.uint8SliceSrc = internal unnamed_addr global { i8*, i64, i64 } { i8* getelementptr inbounds ([2 x i8], [2 x i8]* @main.uint8SliceSrc.buf, i32 0, i32 0), i64 2, i64 2 }
|
||||
@main.uint8SliceDst = internal unnamed_addr global { i8*, i64, i64 } zeroinitializer
|
||||
@main.int16SliceSrc.buf = internal global [3 x i16] [i16 5, i16 123, i16 1024]
|
||||
@main.int16SliceSrc = internal unnamed_addr global { i16*, i64, i64 } { i16* getelementptr inbounds ([3 x i16], [3 x i16]* @main.int16SliceSrc.buf, i32 0, i32 0), i64 3, i64 3 }
|
||||
@main.int16SliceDst = internal unnamed_addr global { i16*, i64, i64 } zeroinitializer
|
||||
|
||||
declare i64 @runtime.sliceCopy(i8* %dst, i8* %src, i64 %dstLen, i64 %srcLen, i64 %elemSize) unnamed_addr
|
||||
|
||||
declare i8* @runtime.alloc(i64) unnamed_addr
|
||||
|
||||
declare void @runtime.printuint8(i8)
|
||||
|
||||
declare void @runtime.printint16(i16)
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call void @main.init()
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() unnamed_addr {
|
||||
entry:
|
||||
; print(uintSliceSrc[0])
|
||||
%uint8SliceSrc.buf = load i8*, i8** getelementptr inbounds ({ i8*, i64, i64 }, { i8*, i64, i64 }* @main.uint8SliceSrc, i64 0, i32 0)
|
||||
%uint8SliceSrc.val = load i8, i8* %uint8SliceSrc.buf
|
||||
call void @runtime.printuint8(i8 %uint8SliceSrc.val)
|
||||
|
||||
; print(uintSliceDst[0])
|
||||
%uint8SliceDst.buf = load i8*, i8** getelementptr inbounds ({ i8*, i64, i64 }, { i8*, i64, i64 }* @main.uint8SliceDst, i64 0, i32 0)
|
||||
%uint8SliceDst.val = load i8, i8* %uint8SliceDst.buf
|
||||
call void @runtime.printuint8(i8 %uint8SliceDst.val)
|
||||
|
||||
; print(int16SliceSrc[0])
|
||||
%int16SliceSrc.buf = load i16*, i16** getelementptr inbounds ({ i16*, i64, i64 }, { i16*, i64, i64 }* @main.int16SliceSrc, i64 0, i32 0)
|
||||
%int16SliceSrc.val = load i16, i16* %int16SliceSrc.buf
|
||||
call void @runtime.printint16(i16 %int16SliceSrc.val)
|
||||
|
||||
; print(int16SliceDst[0])
|
||||
%int16SliceDst.buf = load i16*, i16** getelementptr inbounds ({ i16*, i64, i64 }, { i16*, i64, i64 }* @main.int16SliceDst, i64 0, i32 0)
|
||||
%int16SliceDst.val = load i16, i16* %int16SliceDst.buf
|
||||
call void @runtime.printint16(i16 %int16SliceDst.val)
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @main.init() unnamed_addr {
|
||||
entry:
|
||||
; equivalent of:
|
||||
; uint8SliceDst = make([]uint8, len(uint8SliceSrc))
|
||||
%uint8SliceSrc = load { i8*, i64, i64 }, { i8*, i64, i64 }* @main.uint8SliceSrc
|
||||
%uint8SliceSrc.len = extractvalue { i8*, i64, i64 } %uint8SliceSrc, 1
|
||||
%uint8SliceDst.buf = call i8* @runtime.alloc(i64 %uint8SliceSrc.len)
|
||||
%0 = insertvalue { i8*, i64, i64 } undef, i8* %uint8SliceDst.buf, 0
|
||||
%1 = insertvalue { i8*, i64, i64 } %0, i64 %uint8SliceSrc.len, 1
|
||||
%2 = insertvalue { i8*, i64, i64 } %1, i64 %uint8SliceSrc.len, 2
|
||||
store { i8*, i64, i64 } %2, { i8*, i64, i64 }* @main.uint8SliceDst
|
||||
|
||||
; equivalent of:
|
||||
; copy(uint8SliceDst, uint8SliceSrc)
|
||||
%uint8SliceSrc.buf = extractvalue { i8*, i64, i64 } %uint8SliceSrc, 0
|
||||
%copy.n = call i64 @runtime.sliceCopy(i8* %uint8SliceDst.buf, i8* %uint8SliceSrc.buf, i64 %uint8SliceSrc.len, i64 %uint8SliceSrc.len, i64 1)
|
||||
|
||||
; equivalent of:
|
||||
; int16SliceDst = make([]int16, len(int16SliceSrc))
|
||||
%int16SliceSrc = load { i16*, i64, i64 }, { i16*, i64, i64 }* @main.int16SliceSrc
|
||||
%int16SliceSrc.len = extractvalue { i16*, i64, i64 } %int16SliceSrc, 1
|
||||
%int16SliceSrc.len.bytes = mul i64 %int16SliceSrc.len, 2
|
||||
%int16SliceDst.buf.raw = call i8* @runtime.alloc(i64 %int16SliceSrc.len.bytes)
|
||||
%int16SliceDst.buf = bitcast i8* %int16SliceDst.buf.raw to i16*
|
||||
%3 = insertvalue { i16*, i64, i64 } undef, i16* %int16SliceDst.buf, 0
|
||||
%4 = insertvalue { i16*, i64, i64 } %3, i64 %int16SliceSrc.len, 1
|
||||
%5 = insertvalue { i16*, i64, i64 } %4, i64 %int16SliceSrc.len, 2
|
||||
store { i16*, i64, i64 } %5, { i16*, i64, i64 }* @main.int16SliceDst
|
||||
|
||||
; equivalent of:
|
||||
; copy(int16SliceDst, int16SliceSrc)
|
||||
%int16SliceSrc.buf = extractvalue { i16*, i64, i64 } %int16SliceSrc, 0
|
||||
%int16SliceSrc.buf.i8ptr = bitcast i16* %int16SliceSrc.buf to i8*
|
||||
%int16SliceDst.buf.i8ptr = bitcast i16* %int16SliceDst.buf to i8*
|
||||
%copy.n2 = call i64 @runtime.sliceCopy(i8* %int16SliceDst.buf.i8ptr, i8* %int16SliceSrc.buf.i8ptr, i64 %int16SliceSrc.len, i64 %int16SliceSrc.len, i64 2)
|
||||
|
||||
ret void
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
declare void @runtime.printuint8(i8) local_unnamed_addr
|
||||
|
||||
declare void @runtime.printint16(i16) local_unnamed_addr
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() unnamed_addr {
|
||||
entry:
|
||||
call void @runtime.printuint8(i8 3)
|
||||
call void @runtime.printuint8(i8 3)
|
||||
call void @runtime.printint16(i16 5)
|
||||
call void @runtime.printint16(i16 5)
|
||||
ret void
|
||||
}
|
||||
+27
-7
@@ -98,13 +98,33 @@ func (v *LocalValue) GetElementPtr(indices []uint32) Value {
|
||||
gep := llvm.ConstGEP(v.Underlying, getLLVMIndices(int32Type, indices))
|
||||
return &LocalValue{v.Eval, gep}
|
||||
}
|
||||
switch v.Underlying.Opcode() {
|
||||
case llvm.GetElementPtr, llvm.IntToPtr:
|
||||
int32Type := v.Underlying.Type().Context().Int32Type()
|
||||
llvmIndices := getLLVMIndices(int32Type, indices)
|
||||
return &LocalValue{v.Eval, llvm.ConstGEP(v.Underlying, llvmIndices)}
|
||||
default:
|
||||
panic("interp: GEP on a constant")
|
||||
if !v.Underlying.IsAConstantExpr().IsNil() {
|
||||
switch v.Underlying.Opcode() {
|
||||
case llvm.GetElementPtr, llvm.IntToPtr, llvm.BitCast:
|
||||
int32Type := v.Underlying.Type().Context().Int32Type()
|
||||
llvmIndices := getLLVMIndices(int32Type, indices)
|
||||
return &LocalValue{v.Eval, llvm.ConstGEP(v.Underlying, llvmIndices)}
|
||||
}
|
||||
}
|
||||
panic("interp: unknown GEP")
|
||||
}
|
||||
|
||||
// stripPointerCasts removes all const bitcasts from pointer values, if there
|
||||
// are any.
|
||||
func (v *LocalValue) stripPointerCasts() *LocalValue {
|
||||
value := v.Underlying
|
||||
for {
|
||||
if !value.IsAConstantExpr().IsNil() {
|
||||
switch value.Opcode() {
|
||||
case llvm.BitCast:
|
||||
value = value.Operand(0)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return &LocalValue{
|
||||
Eval: v.Eval,
|
||||
Underlying: value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -178,6 +178,9 @@ func (p *Program) AddPackage(pkg *ssa.Package) {
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
+1
-1
Submodule lib/cmsis-svd updated: 5910f3d115...7d9b416701
+3
-1
@@ -9,6 +9,8 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -63,7 +65,7 @@ func Link(linker string, flags ...string) error {
|
||||
cmd := exec.Command(linker, flags...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = sourceDir()
|
||||
cmd.Dir = goenv.Get("TINYGOROOT")
|
||||
return cmd.Run()
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -8,6 +8,8 @@ package main
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
// Link invokes a linker with the given name and arguments.
|
||||
@@ -20,6 +22,6 @@ func Link(linker string, flags ...string) error {
|
||||
cmd := exec.Command(linker, flags...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = sourceDir()
|
||||
cmd.Dir = goenv.Get("TINYGOROOT")
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
+3
-3
@@ -317,9 +317,9 @@ func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
||||
defer rd.Close()
|
||||
relpath := path
|
||||
if filepath.IsAbs(path) {
|
||||
relpath, err = filepath.Rel(p.Dir, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
rp, err := filepath.Rel(p.Dir, path)
|
||||
if err == nil {
|
||||
relpath = rp
|
||||
}
|
||||
}
|
||||
return parser.ParseFile(p.fset, relpath, rd, mode)
|
||||
|
||||
@@ -15,10 +15,14 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compiler"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"github.com/tinygo-org/tinygo/interp"
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
|
||||
serial "go.bug.st/serial.v1"
|
||||
)
|
||||
|
||||
// commandError is an error type to wrap os/exec.Command errors. This provides
|
||||
@@ -67,7 +71,7 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
||||
config.gc = spec.GC
|
||||
}
|
||||
|
||||
root := sourceDir()
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
|
||||
// Merge and adjust CFlags.
|
||||
cflags := append([]string{}, config.cFlags...)
|
||||
@@ -81,7 +85,7 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
||||
ldflags = append(ldflags, strings.Replace(flag, "{root}", root, -1))
|
||||
}
|
||||
|
||||
goroot := getGoroot()
|
||||
goroot := goenv.Get("GOROOT")
|
||||
if goroot == "" {
|
||||
return errors.New("cannot locate $GOROOT, please set it manually")
|
||||
}
|
||||
@@ -90,8 +94,8 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||
}
|
||||
if major != 1 || (minor != 11 && minor != 12) {
|
||||
return fmt.Errorf("requires go version 1.11 or 1.12, got go%d.%d", major, minor)
|
||||
if major != 1 || (minor != 11 && minor != 12 && minor != 13) {
|
||||
return fmt.Errorf("requires go version 1.11, 1.12, or 1.13, got go%d.%d", major, minor)
|
||||
}
|
||||
for i := 1; i <= minor; i++ {
|
||||
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
||||
@@ -120,7 +124,7 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
||||
VerifyIR: config.verifyIR,
|
||||
TINYGOROOT: root,
|
||||
GOROOT: goroot,
|
||||
GOPATH: getGopath(),
|
||||
GOPATH: goenv.Get("GOPATH"),
|
||||
BuildTags: tags,
|
||||
TestConfig: config.testConfig,
|
||||
}
|
||||
@@ -145,7 +149,7 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
||||
return errors.New("verification error after IR construction")
|
||||
}
|
||||
|
||||
err = interp.Run(c.Module(), c.TargetData(), config.dumpSSA)
|
||||
err = interp.Run(c.Module(), config.dumpSSA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -312,7 +316,7 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
||||
}
|
||||
|
||||
// Get an Intel .hex file or .bin file from the .elf file.
|
||||
if outext == ".hex" || outext == ".bin" {
|
||||
if outext == ".hex" || outext == ".bin" || outext == ".gba" {
|
||||
tmppath = filepath.Join(dir, "main"+outext)
|
||||
err := Objcopy(executable, tmppath)
|
||||
if err != nil {
|
||||
@@ -400,40 +404,97 @@ func Flash(pkgName, target, port string, config *BuildConfig) error {
|
||||
// determine the type of file to compile
|
||||
var fileExt string
|
||||
|
||||
switch {
|
||||
case strings.Contains(spec.Flasher, "{hex}"):
|
||||
switch spec.FlashMethod {
|
||||
case "command", "":
|
||||
switch {
|
||||
case strings.Contains(spec.FlashCommand, "{hex}"):
|
||||
fileExt = ".hex"
|
||||
case strings.Contains(spec.FlashCommand, "{elf}"):
|
||||
fileExt = ".elf"
|
||||
case strings.Contains(spec.FlashCommand, "{bin}"):
|
||||
fileExt = ".bin"
|
||||
case strings.Contains(spec.FlashCommand, "{uf2}"):
|
||||
fileExt = ".uf2"
|
||||
default:
|
||||
return errors.New("invalid target file - did you forget the {hex} token in the 'flash-command' section?")
|
||||
}
|
||||
case "msd":
|
||||
if spec.FlashFilename == "" {
|
||||
return errors.New("invalid target file: flash-method was set to \"msd\" but no msd-firmware-name was set")
|
||||
}
|
||||
fileExt = filepath.Ext(spec.FlashFilename)
|
||||
case "openocd":
|
||||
fileExt = ".hex"
|
||||
case strings.Contains(spec.Flasher, "{elf}"):
|
||||
fileExt = ".elf"
|
||||
case strings.Contains(spec.Flasher, "{bin}"):
|
||||
fileExt = ".bin"
|
||||
case strings.Contains(spec.Flasher, "{uf2}"):
|
||||
fileExt = ".uf2"
|
||||
case "native":
|
||||
return errors.New("unknown flash method \"native\" - did you miss a -target flag?")
|
||||
default:
|
||||
return errors.New("invalid target file - did you forget the {hex} token in the 'flash' section?")
|
||||
return errors.New("unknown flash method: " + spec.FlashMethod)
|
||||
}
|
||||
|
||||
return Compile(pkgName, fileExt, spec, config, func(tmppath string) error {
|
||||
if spec.Flasher == "" {
|
||||
return errors.New("no flash command specified - did you miss a -target flag?")
|
||||
// do we need port reset to put MCU into bootloader mode?
|
||||
if spec.PortReset == "true" {
|
||||
err := touchSerialPortAt1200bps(port)
|
||||
if err != nil {
|
||||
return &commandError{"failed to reset port", tmppath, err}
|
||||
}
|
||||
// give the target MCU a chance to restart into bootloader
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
|
||||
// Create the command.
|
||||
flashCmd := spec.Flasher
|
||||
fileToken := "{" + fileExt[1:] + "}"
|
||||
flashCmd = strings.Replace(flashCmd, fileToken, tmppath, -1)
|
||||
flashCmd = strings.Replace(flashCmd, "{port}", port, -1)
|
||||
// this flashing method copies the binary data to a Mass Storage Device (msd)
|
||||
switch spec.FlashMethod {
|
||||
case "", "command":
|
||||
// Create the command.
|
||||
flashCmd := spec.FlashCommand
|
||||
fileToken := "{" + fileExt[1:] + "}"
|
||||
flashCmd = strings.Replace(flashCmd, fileToken, tmppath, -1)
|
||||
flashCmd = strings.Replace(flashCmd, "{port}", port, -1)
|
||||
|
||||
// Execute the command.
|
||||
cmd := exec.Command("/bin/sh", "-c", flashCmd)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = sourceDir()
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return &commandError{"failed to flash", tmppath, err}
|
||||
// Execute the command.
|
||||
cmd := exec.Command("/bin/sh", "-c", flashCmd)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = goenv.Get("TINYGOROOT")
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return &commandError{"failed to flash", tmppath, err}
|
||||
}
|
||||
return nil
|
||||
case "msd":
|
||||
switch fileExt {
|
||||
case ".uf2":
|
||||
err := flashUF2UsingMSD(spec.FlashVolume, tmppath)
|
||||
if err != nil {
|
||||
return &commandError{"failed to flash", tmppath, err}
|
||||
}
|
||||
return nil
|
||||
case ".hex":
|
||||
err := flashHexUsingMSD(spec.FlashVolume, tmppath)
|
||||
if err != nil {
|
||||
return &commandError{"failed to flash", tmppath, err}
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return errors.New("mass storage device flashing currently only supports uf2 and hex")
|
||||
}
|
||||
case "openocd":
|
||||
args, err := spec.OpenOCDConfiguration()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args = append(args, "-c", "program "+tmppath+" reset exit")
|
||||
cmd := exec.Command("openocd", args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return &commandError{"failed to flash", tmppath, err}
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unknown flash method: %s", spec.FlashMethod)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -452,9 +513,33 @@ func FlashGDB(pkgName, target, port string, ocdOutput bool, config *BuildConfig)
|
||||
}
|
||||
|
||||
return Compile(pkgName, "", spec, config, func(tmppath string) error {
|
||||
if len(spec.OCDDaemon) != 0 {
|
||||
// Find a good way to run GDB.
|
||||
gdbInterface := spec.FlashMethod
|
||||
switch gdbInterface {
|
||||
case "msd", "command", "":
|
||||
if gdbInterface == "" {
|
||||
gdbInterface = "command"
|
||||
}
|
||||
if spec.OpenOCDInterface != "" && spec.OpenOCDTarget != "" {
|
||||
gdbInterface = "openocd"
|
||||
}
|
||||
}
|
||||
|
||||
// Run the GDB server, if necessary.
|
||||
var gdbCommands []string
|
||||
switch gdbInterface {
|
||||
case "native":
|
||||
// Run GDB directly.
|
||||
gdbCommands = append(gdbCommands, "run")
|
||||
case "openocd":
|
||||
gdbCommands = append(gdbCommands, "target remote :3333", "monitor halt", "load", "monitor reset", "c")
|
||||
|
||||
// We need a separate debugging daemon for on-chip debugging.
|
||||
daemon := exec.Command(spec.OCDDaemon[0], spec.OCDDaemon[1:]...)
|
||||
args, err := spec.OpenOCDConfiguration()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
daemon := exec.Command("openocd", args...)
|
||||
if ocdOutput {
|
||||
// Make it clear which output is from the daemon.
|
||||
w := &ColorWriter{
|
||||
@@ -467,11 +552,7 @@ func FlashGDB(pkgName, target, port string, ocdOutput bool, config *BuildConfig)
|
||||
}
|
||||
// Make sure the daemon doesn't receive Ctrl-C that is intended for
|
||||
// GDB (to break the currently executing program).
|
||||
// https://stackoverflow.com/a/35435038/559350
|
||||
daemon.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
Pgid: 0,
|
||||
}
|
||||
setCommandAsDaemon(daemon)
|
||||
// Start now, and kill it on exit.
|
||||
daemon.Start()
|
||||
defer func() {
|
||||
@@ -479,6 +560,10 @@ func FlashGDB(pkgName, target, port string, ocdOutput bool, config *BuildConfig)
|
||||
// Maybe we should send a .Kill() after x seconds?
|
||||
daemon.Wait()
|
||||
}()
|
||||
case "msd":
|
||||
return errors.New("gdb is not supported for drag-and-drop programmable devices")
|
||||
default:
|
||||
return fmt.Errorf("gdb is not supported with interface %#v", gdbInterface)
|
||||
}
|
||||
|
||||
// Ignore Ctrl-C, it must be passed on to GDB.
|
||||
@@ -493,7 +578,7 @@ func FlashGDB(pkgName, target, port string, ocdOutput bool, config *BuildConfig)
|
||||
// By default: gdb -ex run <binary>
|
||||
// Exit GDB with Ctrl-D.
|
||||
params := []string{tmppath}
|
||||
for _, cmd := range spec.GDBCmds {
|
||||
for _, cmd := range gdbCommands {
|
||||
params = append(params, "-ex", cmd)
|
||||
}
|
||||
cmd := exec.Command(spec.GDB, params...)
|
||||
@@ -549,6 +634,54 @@ func Run(pkgName, target string, config *BuildConfig) error {
|
||||
})
|
||||
}
|
||||
|
||||
func touchSerialPortAt1200bps(port string) error {
|
||||
// Open port
|
||||
p, err := serial.Open(port, &serial.Mode{BaudRate: 1200})
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening port: %s", err)
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
p.SetDTR(false)
|
||||
return nil
|
||||
}
|
||||
|
||||
func flashUF2UsingMSD(volume, tmppath string) error {
|
||||
// find standard UF2 info path
|
||||
infoPath := "/media/*/" + volume + "/INFO_UF2.TXT"
|
||||
if runtime.GOOS == "darwin" {
|
||||
infoPath = "/Volumes/" + volume + "/INFO_UF2.TXT"
|
||||
}
|
||||
|
||||
d, err := filepath.Glob(infoPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d == nil {
|
||||
return errors.New("unable to locate UF2 device: " + volume)
|
||||
}
|
||||
|
||||
return moveFile(tmppath, filepath.Dir(d[0])+"/flash.uf2")
|
||||
}
|
||||
|
||||
func flashHexUsingMSD(volume, tmppath string) error {
|
||||
// find expected volume path
|
||||
destPath := "/media/*/" + volume
|
||||
if runtime.GOOS == "darwin" {
|
||||
destPath = "/Volumes/" + volume
|
||||
}
|
||||
|
||||
d, err := filepath.Glob(destPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d == nil {
|
||||
return errors.New("unable to locate device: " + volume)
|
||||
}
|
||||
|
||||
return moveFile(tmppath, d[0]+"/flash.hex")
|
||||
}
|
||||
|
||||
// parseSize converts a human-readable size (with k/m/g suffix) into a plain
|
||||
// number.
|
||||
func parseSize(s string) (int64, error) {
|
||||
@@ -583,7 +716,8 @@ func usage() {
|
||||
fmt.Fprintln(os.Stderr, " test: test packages")
|
||||
fmt.Fprintln(os.Stderr, " flash: compile and flash to the device")
|
||||
fmt.Fprintln(os.Stderr, " gdb: run/flash and immediately enter GDB")
|
||||
fmt.Fprintln(os.Stderr, " clean: empty cache directory ("+cacheDir()+")")
|
||||
fmt.Fprintln(os.Stderr, " env: list environment variables used during build")
|
||||
fmt.Fprintln(os.Stderr, " clean: empty cache directory ("+goenv.Get("GOCACHE")+")")
|
||||
fmt.Fprintln(os.Stderr, " help: print this help text")
|
||||
fmt.Fprintln(os.Stderr, "\nflags:")
|
||||
flag.PrintDefaults()
|
||||
@@ -754,8 +888,7 @@ func main() {
|
||||
handleCompilerError(err)
|
||||
case "clean":
|
||||
// remove cache directory
|
||||
dir := cacheDir()
|
||||
err := os.RemoveAll(dir)
|
||||
err := os.RemoveAll(goenv.Get("GOCACHE"))
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "cannot clean cache:", err)
|
||||
os.Exit(1)
|
||||
@@ -763,7 +896,23 @@ func main() {
|
||||
case "help":
|
||||
usage()
|
||||
case "version":
|
||||
fmt.Printf("tinygo version %s %s/%s\n", version, runtime.GOOS, runtime.GOARCH)
|
||||
goversion := "<unknown>"
|
||||
if s, err := getGorootVersionString(goenv.Get("GOROOT")); err == nil {
|
||||
goversion = s
|
||||
}
|
||||
fmt.Printf("tinygo version %s %s/%s (using go version %s)\n", version, runtime.GOOS, runtime.GOARCH, goversion)
|
||||
case "env":
|
||||
if flag.NArg() == 0 {
|
||||
// Show all environment variables.
|
||||
for _, key := range goenv.Keys {
|
||||
fmt.Printf("%s=%#v\n", key, goenv.Get(key))
|
||||
}
|
||||
} else {
|
||||
// Show only one (or a few) environment variables.
|
||||
for i := 0; i < flag.NArg(); i++ {
|
||||
fmt.Println(goenv.Get(flag.Arg(i)))
|
||||
}
|
||||
}
|
||||
default:
|
||||
fmt.Fprintln(os.Stderr, "Unknown command:", command)
|
||||
usage()
|
||||
|
||||
+8
-5
@@ -45,11 +45,13 @@ func TestCompiler(t *testing.T) {
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
t.Log("running tests on host...")
|
||||
for _, path := range matches {
|
||||
t.Run(path, func(t *testing.T) {
|
||||
runTest(path, tmpdir, "", t)
|
||||
})
|
||||
if runtime.GOOS != "windows" {
|
||||
t.Log("running tests on host...")
|
||||
for _, path := range matches {
|
||||
t.Run(path, func(t *testing.T) {
|
||||
runTest(path, tmpdir, "", t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if testing.Short() {
|
||||
@@ -162,6 +164,7 @@ func runTest(path, tmpdir string, target string, t *testing.T) {
|
||||
|
||||
// putchar() prints CRLF, convert it to LF.
|
||||
actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1)
|
||||
expected = bytes.Replace(expected, []byte{'\r', '\n'}, []byte{'\n'}, -1) // for Windows
|
||||
|
||||
// Check whether the command ran successfully.
|
||||
fail := false
|
||||
|
||||
@@ -108,6 +108,10 @@ func Objcopy(infile, outfile string) error {
|
||||
|
||||
// Write to the file, in the correct format.
|
||||
switch filepath.Ext(outfile) {
|
||||
case ".gba":
|
||||
// The address is not stored in a .gba file.
|
||||
_, err := f.Write(data)
|
||||
return err
|
||||
case ".bin":
|
||||
// The address is not stored in a .bin file (therefore you
|
||||
// should use .hex files in most cases).
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package reflectlite
|
||||
|
||||
import "reflect"
|
||||
|
||||
func Swapper(slice interface{}) func(i, j int) {
|
||||
return reflect.Swapper(slice)
|
||||
}
|
||||
|
||||
type Kind = reflect.Kind
|
||||
type Type = reflect.Type
|
||||
type Value = reflect.Value
|
||||
|
||||
const (
|
||||
Invalid Kind = reflect.Invalid
|
||||
Bool Kind = reflect.Bool
|
||||
Int Kind = reflect.Int
|
||||
Int8 Kind = reflect.Int8
|
||||
Int16 Kind = reflect.Int16
|
||||
Int32 Kind = reflect.Int32
|
||||
Int64 Kind = reflect.Int64
|
||||
Uint Kind = reflect.Uint
|
||||
Uint8 Kind = reflect.Uint8
|
||||
Uint16 Kind = reflect.Uint16
|
||||
Uint32 Kind = reflect.Uint32
|
||||
Uint64 Kind = reflect.Uint64
|
||||
Uintptr Kind = reflect.Uintptr
|
||||
Float32 Kind = reflect.Float32
|
||||
Float64 Kind = reflect.Float64
|
||||
Complex64 Kind = reflect.Complex64
|
||||
Complex128 Kind = reflect.Complex128
|
||||
Array Kind = reflect.Array
|
||||
Chan Kind = reflect.Chan
|
||||
Func Kind = reflect.Func
|
||||
Interface Kind = reflect.Interface
|
||||
Map Kind = reflect.Map
|
||||
Ptr Kind = reflect.Ptr
|
||||
Slice Kind = reflect.Slice
|
||||
String Kind = reflect.String
|
||||
Struct Kind = reflect.Struct
|
||||
UnsafePointer Kind = reflect.UnsafePointer
|
||||
)
|
||||
|
||||
func ValueOf(i interface{}) reflect.Value {
|
||||
return reflect.ValueOf(i)
|
||||
}
|
||||
|
||||
func TypeOf(i interface{}) reflect.Type {
|
||||
return reflect.TypeOf(i)
|
||||
}
|
||||
|
||||
type ValueError = reflect.ValueError
|
||||
@@ -68,10 +68,10 @@ const (
|
||||
|
||||
// UART1 on the Arduino Nano 33 connects to the onboard NINA-W102 WiFi chip.
|
||||
var (
|
||||
UART1 = UART{Bus: sam.SERCOM5_USART,
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Mode: PinSERCOMAlt,
|
||||
IRQVal: sam.IRQ_SERCOM5,
|
||||
Bus: sam.SERCOM5_USART,
|
||||
SERCOM: 5,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -88,10 +88,10 @@ func handleUART1() {
|
||||
|
||||
// UART2 on the Arduino Nano 33 connects to the normal TX/RX pins.
|
||||
var (
|
||||
UART2 = UART{Bus: sam.SERCOM3_USART,
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Mode: PinSERCOMAlt,
|
||||
IRQVal: sam.IRQ_SERCOM3,
|
||||
Bus: sam.SERCOM3_USART,
|
||||
SERCOM: 3,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -110,10 +110,10 @@ const (
|
||||
|
||||
// I2C on the Arduino Nano 33.
|
||||
var (
|
||||
I2C0 = I2C{Bus: sam.SERCOM4_I2CM,
|
||||
SDA: SDA_PIN,
|
||||
SCL: SCL_PIN,
|
||||
PinMode: PinSERCOMAlt}
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM4_I2CM,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
@@ -125,13 +125,10 @@ const (
|
||||
|
||||
// SPI on the Arduino Nano 33.
|
||||
var (
|
||||
SPI0 = SPI{Bus: sam.SERCOM0_SPI,
|
||||
SCK: SPI0_SCK_PIN,
|
||||
MOSI: SPI0_MOSI_PIN,
|
||||
MISO: SPI0_MISO_PIN,
|
||||
DOpad: spiTXPad2SCK3,
|
||||
DIpad: sercomRXPad0,
|
||||
PinMode: PinSERCOM}
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM0_SPI,
|
||||
SERCOM: 0,
|
||||
}
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
|
||||
@@ -70,10 +70,10 @@ const (
|
||||
|
||||
// UART1 on the Circuit Playground Express.
|
||||
var (
|
||||
UART1 = UART{Bus: sam.SERCOM1_USART,
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Mode: PinSERCOM,
|
||||
IRQVal: sam.IRQ_SERCOM1,
|
||||
Bus: sam.SERCOM4_USART,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -94,15 +94,15 @@ const (
|
||||
// I2C on the Circuit Playground Express.
|
||||
var (
|
||||
// external device
|
||||
I2C0 = I2C{Bus: sam.SERCOM5_I2CM,
|
||||
SDA: SDA_PIN,
|
||||
SCL: SCL_PIN,
|
||||
PinMode: PinSERCOM}
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM5_I2CM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
// internal device
|
||||
I2C1 = I2C{Bus: sam.SERCOM1_I2CM,
|
||||
SDA: SDA1_PIN,
|
||||
SCL: SCL1_PIN,
|
||||
PinMode: PinSERCOMAlt}
|
||||
I2C1 = I2C{
|
||||
Bus: sam.SERCOM1_I2CM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins (internal flash)
|
||||
@@ -114,13 +114,10 @@ const (
|
||||
|
||||
// SPI on the Circuit Playground Express.
|
||||
var (
|
||||
SPI0 = SPI{Bus: sam.SERCOM3_SPI,
|
||||
SCK: SPI0_SCK_PIN,
|
||||
MOSI: SPI0_MOSI_PIN,
|
||||
MISO: SPI0_MISO_PIN,
|
||||
DOpad: spiTXPad2SCK3,
|
||||
DIpad: sercomRXPad0,
|
||||
PinMode: PinSERCOMAlt}
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM3_SPI,
|
||||
SERCOM: 3,
|
||||
}
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
|
||||
@@ -53,10 +53,10 @@ const (
|
||||
|
||||
// UART1 on the Feather M0.
|
||||
var (
|
||||
UART1 = UART{Bus: sam.SERCOM1_USART,
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Mode: PinSERCOM,
|
||||
IRQVal: sam.IRQ_SERCOM1,
|
||||
Bus: sam.SERCOM1_USART,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -73,10 +73,10 @@ const (
|
||||
|
||||
// I2C on the Feather M0.
|
||||
var (
|
||||
I2C0 = I2C{Bus: sam.SERCOM3_I2CM,
|
||||
SDA: SDA_PIN,
|
||||
SCL: SCL_PIN,
|
||||
PinMode: PinSERCOM}
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM3_I2CM,
|
||||
SERCOM: 3,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
@@ -88,13 +88,10 @@ const (
|
||||
|
||||
// SPI on the Feather M0.
|
||||
var (
|
||||
SPI0 = SPI{Bus: sam.SERCOM4_SPI,
|
||||
SCK: SPI0_SCK_PIN,
|
||||
MOSI: SPI0_MOSI_PIN,
|
||||
MISO: SPI0_MISO_PIN,
|
||||
DOpad: spiTXPad2SCK3,
|
||||
DIpad: sercomRXPad0,
|
||||
PinMode: PinSERCOMAlt}
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM4_SPI,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
|
||||
@@ -2,6 +2,29 @@
|
||||
|
||||
package machine
|
||||
|
||||
const (
|
||||
D0 = P16
|
||||
D1 = P17
|
||||
D2 = P18
|
||||
D3 = P19 // Green LED/PWM
|
||||
D4 = P20 // PWM
|
||||
D5 = P21 // Blue LED/PWM
|
||||
D6 = P22 // Red LED/PWM
|
||||
D7 = P16
|
||||
D8 = NoPin // PWM?
|
||||
D9 = P01
|
||||
D10 = P02
|
||||
D11 = P03
|
||||
D12 = P04
|
||||
D13 = P05
|
||||
D14 = NoPin // not connected
|
||||
D15 = P09 // does not seem to work?
|
||||
D16 = P10 // PWM
|
||||
D17 = P11 // PWM
|
||||
D18 = P12 // SDA/PWM
|
||||
D19 = P13 // SDL/PWM
|
||||
)
|
||||
|
||||
const (
|
||||
LED = LED1
|
||||
LED1 = LED_RED
|
||||
|
||||
@@ -55,10 +55,10 @@ const (
|
||||
|
||||
// UART1 on the ItsyBitsy M0.
|
||||
var (
|
||||
UART1 = UART{Bus: sam.SERCOM1_USART,
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Mode: PinSERCOM,
|
||||
IRQVal: sam.IRQ_SERCOM1,
|
||||
Bus: sam.SERCOM1_USART,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -75,10 +75,10 @@ const (
|
||||
|
||||
// I2C on the ItsyBitsy M0.
|
||||
var (
|
||||
I2C0 = I2C{Bus: sam.SERCOM3_I2CM,
|
||||
SDA: SDA_PIN,
|
||||
SCL: SCL_PIN,
|
||||
PinMode: PinSERCOM}
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM3_I2CM,
|
||||
SERCOM: 3,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
@@ -90,13 +90,10 @@ const (
|
||||
|
||||
// SPI on the ItsyBitsy M0.
|
||||
var (
|
||||
SPI0 = SPI{Bus: sam.SERCOM4_SPI,
|
||||
SCK: SPI0_SCK_PIN,
|
||||
MOSI: SPI0_MOSI_PIN,
|
||||
MISO: SPI0_MISO_PIN,
|
||||
DOpad: spiTXPad2SCK3,
|
||||
DIpad: sercomRXPad0,
|
||||
PinMode: PinSERCOMAlt}
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM4_SPI,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
// "Internal" SPI pins; SPI flash is attached to these on ItsyBitsy M0
|
||||
@@ -109,13 +106,10 @@ const (
|
||||
|
||||
// "Internal" SPI on Sercom 5
|
||||
var (
|
||||
SPI1 = SPI{Bus: sam.SERCOM5_SPI,
|
||||
SCK: SPI1_SCK_PIN,
|
||||
MOSI: SPI1_MOSI_PIN,
|
||||
MISO: SPI1_MISO_PIN,
|
||||
DOpad: spiTXPad2SCK3,
|
||||
DIpad: sercomRXPad1,
|
||||
PinMode: PinSERCOMAlt}
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM5_SPI,
|
||||
SERCOM: 5,
|
||||
}
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
|
||||
@@ -74,7 +74,7 @@ const (
|
||||
|
||||
// SPI on the ItsyBitsy M4.
|
||||
var (
|
||||
SPI0 = SPI{Bus: sam.SERCOM1_SPI,
|
||||
SPI0 = SPI{Bus: sam.SERCOM1_SPIM,
|
||||
SCK: SPI0_SCK_PIN,
|
||||
MOSI: SPI0_MOSI_PIN,
|
||||
MISO: SPI0_MISO_PIN,
|
||||
|
||||
@@ -44,10 +44,10 @@ const (
|
||||
|
||||
// UART1 on the Trinket M0.
|
||||
var (
|
||||
UART1 = UART{Bus: sam.SERCOM1_USART,
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Mode: PinSERCOM,
|
||||
IRQVal: sam.IRQ_SERCOM1,
|
||||
Bus: sam.SERCOM0_USART,
|
||||
SERCOM: 0,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -65,13 +65,10 @@ const (
|
||||
|
||||
// SPI on the Trinket M0.
|
||||
var (
|
||||
SPI0 = SPI{Bus: sam.SERCOM0_SPI,
|
||||
SCK: SPI0_SCK_PIN,
|
||||
MOSI: SPI0_MOSI_PIN,
|
||||
MISO: SPI0_MISO_PIN,
|
||||
DOpad: spiTXPad2SCK3,
|
||||
DIpad: sercomRXPad0,
|
||||
PinMode: PinSERCOMAlt}
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM0_SPI,
|
||||
SERCOM: 0,
|
||||
}
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
@@ -82,10 +79,10 @@ const (
|
||||
|
||||
// I2C on the Trinket M0.
|
||||
var (
|
||||
I2C0 = I2C{Bus: sam.SERCOM2_I2CM,
|
||||
SDA: SDA_PIN,
|
||||
SCL: SCL_PIN,
|
||||
PinMode: PinSERCOMAlt}
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
package machine
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrInvalidInputPin = errors.New("machine: invalid input pin")
|
||||
ErrInvalidOutputPin = errors.New("machine: invalid output pin")
|
||||
ErrInvalidClockPin = errors.New("machine: invalid clock pin")
|
||||
ErrInvalidDataPin = errors.New("machine: invalid data pin")
|
||||
)
|
||||
|
||||
type PinConfig struct {
|
||||
Mode PinMode
|
||||
}
|
||||
|
||||
+250
-96
@@ -8,10 +8,8 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -105,6 +103,117 @@ const (
|
||||
PB31 Pin = 63
|
||||
)
|
||||
|
||||
const (
|
||||
pinPadMapSERCOM0Pad0 byte = (0x10 << 1) | 0x00
|
||||
pinPadMapSERCOM1Pad0 byte = (0x20 << 1) | 0x00
|
||||
pinPadMapSERCOM2Pad0 byte = (0x30 << 1) | 0x00
|
||||
pinPadMapSERCOM3Pad0 byte = (0x40 << 1) | 0x00
|
||||
pinPadMapSERCOM4Pad0 byte = (0x50 << 1) | 0x00
|
||||
pinPadMapSERCOM5Pad0 byte = (0x60 << 1) | 0x00
|
||||
pinPadMapSERCOM0Pad2 byte = (0x10 << 1) | 0x10
|
||||
pinPadMapSERCOM1Pad2 byte = (0x20 << 1) | 0x10
|
||||
pinPadMapSERCOM2Pad2 byte = (0x30 << 1) | 0x10
|
||||
pinPadMapSERCOM3Pad2 byte = (0x40 << 1) | 0x10
|
||||
pinPadMapSERCOM4Pad2 byte = (0x50 << 1) | 0x10
|
||||
pinPadMapSERCOM5Pad2 byte = (0x60 << 1) | 0x10
|
||||
|
||||
pinPadMapSERCOM0AltPad0 byte = (0x01 << 1) | 0x00
|
||||
pinPadMapSERCOM1AltPad0 byte = (0x02 << 1) | 0x00
|
||||
pinPadMapSERCOM2AltPad0 byte = (0x03 << 1) | 0x00
|
||||
pinPadMapSERCOM3AltPad0 byte = (0x04 << 1) | 0x00
|
||||
pinPadMapSERCOM4AltPad0 byte = (0x05 << 1) | 0x00
|
||||
pinPadMapSERCOM5AltPad0 byte = (0x06 << 1) | 0x00
|
||||
pinPadMapSERCOM0AltPad2 byte = (0x01 << 1) | 0x01
|
||||
pinPadMapSERCOM1AltPad2 byte = (0x02 << 1) | 0x01
|
||||
pinPadMapSERCOM2AltPad2 byte = (0x03 << 1) | 0x01
|
||||
pinPadMapSERCOM3AltPad2 byte = (0x04 << 1) | 0x01
|
||||
pinPadMapSERCOM4AltPad2 byte = (0x05 << 1) | 0x01
|
||||
pinPadMapSERCOM5AltPad2 byte = (0x06 << 1) | 0x01
|
||||
)
|
||||
|
||||
// pinPadMapping lists which pins have which SERCOMs attached to them.
|
||||
// The encoding is rather dense, with each byte encoding two pins and both
|
||||
// SERCOM and SERCOM-ALT.
|
||||
//
|
||||
// Observations:
|
||||
// * There are six SERCOMs. Those SERCOM numbers can be encoded in 3 bits.
|
||||
// * Even pad numbers are always on even pins, and odd pad numbers are always on
|
||||
// odd pins.
|
||||
// * Pin pads come in pairs. If PA00 has pad 0, then PA01 has pad 1.
|
||||
// With this information, we can encode SERCOM pin/pad numbers much more
|
||||
// efficiently. First of all, due to pads coming in pairs, we can ignore half
|
||||
// the pins: the information for an odd pin can be calculated easily from the
|
||||
// preceding even pin. And second, if odd pads are always on odd pins and even
|
||||
// pads on even pins, we can drop a single bit from the pad number.
|
||||
//
|
||||
// Each byte below is split in two nibbles. The 4 high bits are for SERCOM and
|
||||
// the 4 low bits are for SERCOM-ALT. Of each nibble, the 3 high bits encode the
|
||||
// SERCOM + 1 while the low bit encodes whether this is PAD0 or PAD2 (0 means
|
||||
// PAD0, 1 means PAD2). It encodes SERCOM + 1 instead of just the SERCOM number,
|
||||
// to make it easy to check whether a nibble is set at all.
|
||||
var pinPadMapping = [32]byte{
|
||||
// page 21
|
||||
PA00 / 2: 0 | pinPadMapSERCOM1AltPad0,
|
||||
PB08 / 2: 0 | pinPadMapSERCOM4AltPad0,
|
||||
PA04 / 2: 0 | pinPadMapSERCOM0AltPad0,
|
||||
PA06 / 2: 0 | pinPadMapSERCOM0AltPad2,
|
||||
PA08 / 2: pinPadMapSERCOM0Pad0 | pinPadMapSERCOM2AltPad0,
|
||||
PA10 / 2: pinPadMapSERCOM0Pad2 | pinPadMapSERCOM2AltPad2,
|
||||
|
||||
// page 22
|
||||
PB10 / 2: 0 | pinPadMapSERCOM4AltPad2,
|
||||
PB12 / 2: pinPadMapSERCOM4Pad0 | 0,
|
||||
PB14 / 2: pinPadMapSERCOM4Pad2 | 0,
|
||||
PA12 / 2: pinPadMapSERCOM2Pad0 | pinPadMapSERCOM4AltPad0,
|
||||
PA14 / 2: pinPadMapSERCOM2Pad2 | pinPadMapSERCOM4AltPad2,
|
||||
PA16 / 2: pinPadMapSERCOM1Pad0 | pinPadMapSERCOM3AltPad0,
|
||||
PA18 / 2: pinPadMapSERCOM1Pad2 | pinPadMapSERCOM3AltPad2,
|
||||
PB16 / 2: pinPadMapSERCOM5Pad0 | 0,
|
||||
PA20 / 2: pinPadMapSERCOM5Pad2 | pinPadMapSERCOM3AltPad2,
|
||||
PA22 / 2: pinPadMapSERCOM3Pad0 | pinPadMapSERCOM5AltPad0,
|
||||
PA24 / 2: pinPadMapSERCOM3Pad2 | pinPadMapSERCOM5AltPad2,
|
||||
|
||||
// page 23
|
||||
PB22 / 2: 0 | pinPadMapSERCOM5AltPad2,
|
||||
PA30 / 2: 0 | pinPadMapSERCOM1AltPad2,
|
||||
PB30 / 2: 0 | pinPadMapSERCOM5AltPad0,
|
||||
PB00 / 2: 0 | pinPadMapSERCOM5AltPad2,
|
||||
PB02 / 2: 0 | pinPadMapSERCOM5AltPad0,
|
||||
}
|
||||
|
||||
// findPinPadMapping looks up the pad number and the pinmode for a given pin,
|
||||
// given a SERCOM number. The result can either be SERCOM, SERCOM-ALT, or "not
|
||||
// found" (indicated by returning ok=false). The pad number is returned to
|
||||
// calculate the DOPO/DIPO bitfields of the various serial peripherals.
|
||||
func findPinPadMapping(sercom uint8, pin Pin) (pinMode PinMode, pad uint32, ok bool) {
|
||||
nibbles := pinPadMapping[pin/2]
|
||||
upper := nibbles >> 4
|
||||
lower := nibbles & 0xf
|
||||
|
||||
if upper != 0 {
|
||||
// SERCOM
|
||||
if (upper>>1)-1 == sercom {
|
||||
pinMode = PinSERCOM
|
||||
pad |= uint32((upper & 1) << 1)
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
if lower != 0 {
|
||||
// SERCOM-ALT
|
||||
if (lower>>1)-1 == sercom {
|
||||
pinMode = PinSERCOMAlt
|
||||
pad |= uint32((lower & 1) << 1)
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
|
||||
if ok {
|
||||
// The lower bit of the pad is the same as the lower bit of the pin number.
|
||||
pad |= uint32(pin & 1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// InitADC initializes the ADC.
|
||||
func InitADC() {
|
||||
// ADC Bias Calibration
|
||||
@@ -255,8 +364,7 @@ func waitADCSync() {
|
||||
type UART struct {
|
||||
Buffer *RingBuffer
|
||||
Bus *sam.SERCOM_USART_Type
|
||||
Mode PinMode
|
||||
IRQVal uint32
|
||||
SERCOM uint8
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -265,71 +373,54 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
sampleRate16X = 16
|
||||
lsbFirst = 1
|
||||
sercomRXPad0 = 0
|
||||
sercomRXPad1 = 1
|
||||
sercomRXPad2 = 2
|
||||
sercomRXPad3 = 3
|
||||
sercomTXPad0 = 0 // Only for UART
|
||||
sercomTXPad2 = 1 // Only for UART
|
||||
sercomTXPad023 = 2 // Only for UART with TX on PAD0, RTS on PAD2 and CTS on PAD3
|
||||
|
||||
spiTXPad0SCK1 = 0
|
||||
spiTXPad2SCK3 = 1
|
||||
spiTXPad3SCK1 = 2
|
||||
spiTXPad0SCK3 = 3
|
||||
sampleRate16X = 16
|
||||
lsbFirst = 1
|
||||
)
|
||||
|
||||
// Configure the UART.
|
||||
func (uart UART) Configure(config UARTConfig) {
|
||||
func (uart UART) Configure(config UARTConfig) error {
|
||||
// Default baud rate to 115200.
|
||||
if config.BaudRate == 0 {
|
||||
config.BaudRate = 115200
|
||||
}
|
||||
|
||||
// determine pins
|
||||
if config.TX == 0 {
|
||||
// Use default pins if pins are not set.
|
||||
if config.TX == 0 && config.RX == 0 {
|
||||
// use default pins
|
||||
config.TX = UART_TX_PIN
|
||||
config.RX = UART_RX_PIN
|
||||
}
|
||||
|
||||
// determine pads
|
||||
var txpad, rxpad int
|
||||
switch config.TX {
|
||||
case PA10:
|
||||
txpad = sercomTXPad2
|
||||
case PA18:
|
||||
txpad = sercomTXPad2
|
||||
case PA16:
|
||||
txpad = sercomTXPad0
|
||||
case PA22:
|
||||
txpad = sercomTXPad0
|
||||
// Determine transmit pinout.
|
||||
txPinMode, txPad, ok := findPinPadMapping(uart.SERCOM, config.TX)
|
||||
if !ok {
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
var txPinOut uint32
|
||||
// See table 25-9 of the datasheet (page 459) for how pads are mapped to
|
||||
// pinout values.
|
||||
switch txPad {
|
||||
case 0:
|
||||
txPinOut = 0
|
||||
case 2:
|
||||
txPinOut = 1
|
||||
default:
|
||||
panic("Invalid TX pin for UART")
|
||||
// TODO: flow control (RTS/CTS)
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
|
||||
switch config.RX {
|
||||
case PA11:
|
||||
rxpad = sercomRXPad3
|
||||
case PA18:
|
||||
rxpad = sercomRXPad2
|
||||
case PA16:
|
||||
rxpad = sercomRXPad0
|
||||
case PA19:
|
||||
rxpad = sercomRXPad3
|
||||
case PA17:
|
||||
rxpad = sercomRXPad1
|
||||
case PA23:
|
||||
rxpad = sercomRXPad1
|
||||
default:
|
||||
panic("Invalid RX pin for UART")
|
||||
// Determine receive pinout.
|
||||
rxPinMode, rxPad, ok := findPinPadMapping(uart.SERCOM, config.RX)
|
||||
if !ok {
|
||||
return ErrInvalidInputPin
|
||||
}
|
||||
// As you can see in table 25-8 on page 459 of the datasheet, input pins
|
||||
// are mapped directly.
|
||||
rxPinOut := rxPad
|
||||
|
||||
// configure pins
|
||||
config.TX.Configure(PinConfig{Mode: uart.Mode})
|
||||
config.RX.Configure(PinConfig{Mode: uart.Mode})
|
||||
config.TX.Configure(PinConfig{Mode: txPinMode})
|
||||
config.RX.Configure(PinConfig{Mode: rxPinMode})
|
||||
|
||||
// reset SERCOM0
|
||||
uart.Bus.CTRLA.SetBits(sam.SERCOM_USART_CTRLA_SWRST)
|
||||
@@ -363,8 +454,8 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
// set UART pads. This is not same as pins...
|
||||
// SERCOM_USART_CTRLA_TXPO(txPad) |
|
||||
// SERCOM_USART_CTRLA_RXPO(rxPad);
|
||||
uart.Bus.CTRLA.SetBits(uint32((txpad << sam.SERCOM_USART_CTRLA_TXPO_Pos) |
|
||||
(rxpad << sam.SERCOM_USART_CTRLA_RXPO_Pos)))
|
||||
uart.Bus.CTRLA.SetBits((txPinOut << sam.SERCOM_USART_CTRLA_TXPO_Pos) |
|
||||
(rxPinOut << sam.SERCOM_USART_CTRLA_RXPO_Pos))
|
||||
|
||||
// Enable Transceiver and Receiver
|
||||
//sercom->USART.CTRLB.reg |= SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_RXEN ;
|
||||
@@ -380,7 +471,12 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
uart.Bus.INTENSET.Set(sam.SERCOM_USART_INTENSET_RXC)
|
||||
|
||||
// Enable RX IRQ.
|
||||
arm.EnableIRQ(uart.IRQVal)
|
||||
// IRQ lines are in the same order as SERCOM instance numbers on SAMD21
|
||||
// chips, so the IRQ number can be trivially determined from the SERCOM
|
||||
// number.
|
||||
arm.EnableIRQ(sam.IRQ_SERCOM0 + uint32(uart.SERCOM))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the UART.
|
||||
@@ -415,10 +511,8 @@ func defaultUART1Handler() {
|
||||
|
||||
// I2C on the SAMD21.
|
||||
type I2C struct {
|
||||
Bus *sam.SERCOM_I2CM_Type
|
||||
SCL Pin
|
||||
SDA Pin
|
||||
PinMode PinMode
|
||||
Bus *sam.SERCOM_I2CM_Type
|
||||
SERCOM uint8
|
||||
}
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
@@ -448,11 +542,30 @@ const (
|
||||
const i2cTimeout = 1000
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c I2C) Configure(config I2CConfig) {
|
||||
func (i2c I2C) Configure(config I2CConfig) error {
|
||||
// Default I2C bus speed is 100 kHz.
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = TWI_FREQ_100KHZ
|
||||
}
|
||||
if config.SDA == 0 && config.SCL == 0 {
|
||||
config.SDA = SDA_PIN
|
||||
config.SCL = SCL_PIN
|
||||
}
|
||||
|
||||
sclPinMode, sclPad, ok := findPinPadMapping(i2c.SERCOM, config.SCL)
|
||||
if !ok || sclPad != 1 {
|
||||
// SCL must be on pad 1, according to section 27.5 of the datasheet.
|
||||
// Note: this is not an exhaustive test for I2C support on the pin: not
|
||||
// all pins support I2C.
|
||||
return ErrInvalidClockPin
|
||||
}
|
||||
sdaPinMode, sdaPad, ok := findPinPadMapping(i2c.SERCOM, config.SDA)
|
||||
if !ok || sdaPad != 0 {
|
||||
// SDA must be on pad 0, according to section 27.5 of the datasheet.
|
||||
// Note: this is not an exhaustive test for I2C support on the pin: not
|
||||
// all pins support I2C.
|
||||
return ErrInvalidDataPin
|
||||
}
|
||||
|
||||
// reset SERCOM
|
||||
i2c.Bus.CTRLA.SetBits(sam.SERCOM_I2CM_CTRLA_SWRST)
|
||||
@@ -478,8 +591,10 @@ func (i2c I2C) Configure(config I2CConfig) {
|
||||
}
|
||||
|
||||
// enable pins
|
||||
i2c.SDA.Configure(PinConfig{Mode: i2c.PinMode})
|
||||
i2c.SCL.Configure(PinConfig{Mode: i2c.PinMode})
|
||||
config.SDA.Configure(PinConfig{Mode: sdaPinMode})
|
||||
config.SCL.Configure(PinConfig{Mode: sclPinMode})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the I2C.
|
||||
@@ -876,13 +991,8 @@ func waitForSync() {
|
||||
|
||||
// SPI
|
||||
type SPI struct {
|
||||
Bus *sam.SERCOM_SPI_Type
|
||||
SCK Pin
|
||||
MOSI Pin
|
||||
MISO Pin
|
||||
DOpad int
|
||||
DIpad int
|
||||
PinMode PinMode
|
||||
Bus *sam.SERCOM_SPI_Type
|
||||
SERCOM uint8
|
||||
}
|
||||
|
||||
// SPIConfig is used to store config info for SPI.
|
||||
@@ -896,30 +1006,69 @@ type SPIConfig struct {
|
||||
}
|
||||
|
||||
// Configure is intended to setup the SPI interface.
|
||||
func (spi SPI) Configure(config SPIConfig) {
|
||||
config.SCK = spi.SCK
|
||||
config.MOSI = spi.MOSI
|
||||
config.MISO = spi.MISO
|
||||
|
||||
doPad := spi.DOpad
|
||||
diPad := spi.DIpad
|
||||
|
||||
pinMode := spi.PinMode
|
||||
func (spi SPI) Configure(config SPIConfig) error {
|
||||
// Use default pins if not set.
|
||||
if config.SCK == 0 && config.MOSI == 0 && config.MISO == 0 {
|
||||
config.SCK = SPI0_SCK_PIN
|
||||
config.MOSI = SPI0_MOSI_PIN
|
||||
config.MISO = SPI0_MISO_PIN
|
||||
}
|
||||
|
||||
// set default frequency
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = 4000000
|
||||
}
|
||||
|
||||
// Determine the input pinout (for MISO).
|
||||
misoPinMode, misoPad, ok := findPinPadMapping(spi.SERCOM, config.MISO)
|
||||
if !ok {
|
||||
return ErrInvalidInputPin
|
||||
}
|
||||
dataInPinout := misoPad // mapped directly
|
||||
|
||||
// Determine the output pinout (for MOSI/SCK).
|
||||
// See table 26-7 on page 494 of the datasheet.
|
||||
var dataOutPinout uint32
|
||||
sckPinMode, sckPad, ok := findPinPadMapping(spi.SERCOM, config.SCK)
|
||||
if !ok {
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
mosiPinMode, mosiPad, ok := findPinPadMapping(spi.SERCOM, config.MOSI)
|
||||
if !ok {
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
switch sckPad {
|
||||
case 1:
|
||||
switch mosiPad {
|
||||
case 0:
|
||||
dataOutPinout = 0x0
|
||||
case 3:
|
||||
dataOutPinout = 0x2
|
||||
default:
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
case 3:
|
||||
switch mosiPad {
|
||||
case 2:
|
||||
dataOutPinout = 0x1
|
||||
case 0:
|
||||
dataOutPinout = 0x3
|
||||
default:
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
default:
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
|
||||
// Disable SPI port.
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPI_CTRLA_ENABLE)
|
||||
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPI_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
// enable pins
|
||||
config.SCK.Configure(PinConfig{Mode: pinMode})
|
||||
config.MOSI.Configure(PinConfig{Mode: pinMode})
|
||||
config.MISO.Configure(PinConfig{Mode: pinMode})
|
||||
config.SCK.Configure(PinConfig{Mode: sckPinMode})
|
||||
config.MOSI.Configure(PinConfig{Mode: mosiPinMode})
|
||||
config.MISO.Configure(PinConfig{Mode: misoPinMode})
|
||||
|
||||
// reset SERCOM
|
||||
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPI_CTRLA_SWRST)
|
||||
@@ -928,16 +1077,16 @@ func (spi SPI) Configure(config SPIConfig) {
|
||||
}
|
||||
|
||||
// set bit transfer order
|
||||
dataOrder := 0
|
||||
dataOrder := uint32(0)
|
||||
if config.LSBFirst {
|
||||
dataOrder = 1
|
||||
}
|
||||
|
||||
// Set SPI master
|
||||
spi.Bus.CTRLA.Set(uint32((sam.SERCOM_SPI_CTRLA_MODE_SPI_MASTER << sam.SERCOM_SPI_CTRLA_MODE_Pos) |
|
||||
(doPad << sam.SERCOM_SPI_CTRLA_DOPO_Pos) |
|
||||
(diPad << sam.SERCOM_SPI_CTRLA_DIPO_Pos) |
|
||||
(dataOrder << sam.SERCOM_SPI_CTRLA_DORD_Pos)))
|
||||
spi.Bus.CTRLA.Set((sam.SERCOM_SPI_CTRLA_MODE_SPI_MASTER << sam.SERCOM_SPI_CTRLA_MODE_Pos) |
|
||||
(dataOutPinout << sam.SERCOM_SPI_CTRLA_DOPO_Pos) |
|
||||
(dataInPinout << sam.SERCOM_SPI_CTRLA_DIPO_Pos) |
|
||||
(dataOrder << sam.SERCOM_SPI_CTRLA_DORD_Pos))
|
||||
|
||||
spi.Bus.CTRLB.SetBits((0 << sam.SERCOM_SPI_CTRLB_CHSIZE_Pos) | // 8bit char size
|
||||
sam.SERCOM_SPI_CTRLB_RXEN) // receive enable
|
||||
@@ -971,6 +1120,8 @@ func (spi SPI) Configure(config SPIConfig) {
|
||||
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPI_CTRLA_ENABLE)
|
||||
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPI_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transfer writes/reads a single byte using the SPI interface.
|
||||
@@ -1607,24 +1758,27 @@ func handleStandardSetup(setup usbSetup) bool {
|
||||
func cdcSetup(setup usbSetup) bool {
|
||||
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_GET_LINE_CODING {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, 7))
|
||||
binary.Write(buf, binary.LittleEndian, usbLineInfo.dwDTERate)
|
||||
binary.Write(buf, binary.LittleEndian, usbLineInfo.bCharFormat)
|
||||
binary.Write(buf, binary.LittleEndian, usbLineInfo.bParityType)
|
||||
binary.Write(buf, binary.LittleEndian, usbLineInfo.bDataBits)
|
||||
b := make([]byte, 7)
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
b[3] = byte(usbLineInfo.dwDTERate >> 24)
|
||||
b[4] = byte(usbLineInfo.bCharFormat)
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
sendUSBPacket(0, buf.Bytes())
|
||||
sendUSBPacket(0, b)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING {
|
||||
buf := bytes.NewBuffer(receiveUSBControlPacket())
|
||||
binary.Read(buf, binary.LittleEndian, &(usbLineInfo.dwDTERate))
|
||||
binary.Read(buf, binary.LittleEndian, &(usbLineInfo.bCharFormat))
|
||||
binary.Read(buf, binary.LittleEndian, &(usbLineInfo.bParityType))
|
||||
binary.Read(buf, binary.LittleEndian, &(usbLineInfo.bDataBits))
|
||||
b := receiveUSBControlPacket()
|
||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
usbLineInfo.bCharFormat = b[4]
|
||||
usbLineInfo.bParityType = b[5]
|
||||
usbLineInfo.bDataBits = b[6]
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
@@ -1676,7 +1830,7 @@ func receiveUSBControlPacket() []byte {
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
// Wait until OUT transfer is ready.
|
||||
timeout := 3000
|
||||
timeout := 300000
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
|
||||
+137
-307
@@ -8,10 +8,8 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -318,7 +316,7 @@ func (a ADC) getADCChannel() uint8 {
|
||||
// UART on the SAMD51.
|
||||
type UART struct {
|
||||
Buffer *RingBuffer
|
||||
Bus *sam.SERCOM_USART_Type
|
||||
Bus *sam.SERCOM_USART_INT_Type
|
||||
Mode PinMode
|
||||
}
|
||||
|
||||
@@ -327,7 +325,7 @@ var (
|
||||
UART0 = USBCDC{Buffer: NewRingBuffer()}
|
||||
|
||||
// The first hardware serial port on the SAMD51. Uses the SERCOM3 interface.
|
||||
UART1 = UART{Bus: sam.SERCOM3_USART,
|
||||
UART1 = UART{Bus: sam.SERCOM3_USART_INT,
|
||||
Buffer: NewRingBuffer(),
|
||||
Mode: PinSERCOMAlt,
|
||||
}
|
||||
@@ -397,17 +395,17 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
config.RX.Configure(PinConfig{Mode: uart.Mode})
|
||||
|
||||
// reset SERCOM0
|
||||
uart.Bus.CTRLA.SetBits(sam.SERCOM_USART_CTRLA_SWRST)
|
||||
for uart.Bus.CTRLA.HasBits(sam.SERCOM_USART_CTRLA_SWRST) ||
|
||||
uart.Bus.SYNCBUSY.HasBits(sam.SERCOM_USART_SYNCBUSY_SWRST) {
|
||||
uart.Bus.CTRLA.SetBits(sam.SERCOM_USART_INT_CTRLA_SWRST)
|
||||
for uart.Bus.CTRLA.HasBits(sam.SERCOM_USART_INT_CTRLA_SWRST) ||
|
||||
uart.Bus.SYNCBUSY.HasBits(sam.SERCOM_USART_INT_SYNCBUSY_SWRST) {
|
||||
}
|
||||
|
||||
// set UART mode/sample rate
|
||||
// SERCOM_USART_CTRLA_MODE(mode) |
|
||||
// SERCOM_USART_CTRLA_SAMPR(sampleRate);
|
||||
// sam.SERCOM_USART_CTRLA_MODE_USART_INT_CLK = 1?
|
||||
uart.Bus.CTRLA.Set((1 << sam.SERCOM_USART_CTRLA_MODE_Pos) |
|
||||
(1 << sam.SERCOM_USART_CTRLA_SAMPR_Pos)) // sample rate of 16x
|
||||
uart.Bus.CTRLA.Set((1 << sam.SERCOM_USART_INT_CTRLA_MODE_Pos) |
|
||||
(1 << sam.SERCOM_USART_INT_CTRLA_SAMPR_Pos)) // sample rate of 16x
|
||||
|
||||
// Set baud rate
|
||||
uart.SetBaudRate(config.BaudRate)
|
||||
@@ -415,35 +413,35 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
// setup UART frame
|
||||
// SERCOM_USART_CTRLA_FORM( (parityMode == SERCOM_NO_PARITY ? 0 : 1) ) |
|
||||
// dataOrder << SERCOM_USART_CTRLA_DORD_Pos;
|
||||
uart.Bus.CTRLA.SetBits((0 << sam.SERCOM_USART_CTRLA_FORM_Pos) | // no parity
|
||||
(lsbFirst << sam.SERCOM_USART_CTRLA_DORD_Pos)) // data order
|
||||
uart.Bus.CTRLA.SetBits((0 << sam.SERCOM_USART_INT_CTRLA_FORM_Pos) | // no parity
|
||||
(lsbFirst << sam.SERCOM_USART_INT_CTRLA_DORD_Pos)) // data order
|
||||
|
||||
// set UART stop bits/parity
|
||||
// SERCOM_USART_CTRLB_CHSIZE(charSize) |
|
||||
// nbStopBits << SERCOM_USART_CTRLB_SBMODE_Pos |
|
||||
// (parityMode == SERCOM_NO_PARITY ? 0 : parityMode) << SERCOM_USART_CTRLB_PMODE_Pos; //If no parity use default value
|
||||
uart.Bus.CTRLB.SetBits((0 << sam.SERCOM_USART_CTRLB_CHSIZE_Pos) | // 8 bits is 0
|
||||
(0 << sam.SERCOM_USART_CTRLB_SBMODE_Pos) | // 1 stop bit is zero
|
||||
(0 << sam.SERCOM_USART_CTRLB_PMODE_Pos)) // no parity
|
||||
uart.Bus.CTRLB.SetBits((0 << sam.SERCOM_USART_INT_CTRLB_CHSIZE_Pos) | // 8 bits is 0
|
||||
(0 << sam.SERCOM_USART_INT_CTRLB_SBMODE_Pos) | // 1 stop bit is zero
|
||||
(0 << sam.SERCOM_USART_INT_CTRLB_PMODE_Pos)) // no parity
|
||||
|
||||
// set UART pads. This is not same as pins...
|
||||
// SERCOM_USART_CTRLA_TXPO(txPad) |
|
||||
// SERCOM_USART_CTRLA_RXPO(rxPad);
|
||||
uart.Bus.CTRLA.SetBits(uint32((txpad << sam.SERCOM_USART_CTRLA_TXPO_Pos) |
|
||||
(rxpad << sam.SERCOM_USART_CTRLA_RXPO_Pos)))
|
||||
uart.Bus.CTRLA.SetBits(uint32((txpad << sam.SERCOM_USART_INT_CTRLA_TXPO_Pos) |
|
||||
(rxpad << sam.SERCOM_USART_INT_CTRLA_RXPO_Pos)))
|
||||
|
||||
// Enable Transceiver and Receiver
|
||||
//sercom->USART.CTRLB.reg |= SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_RXEN ;
|
||||
uart.Bus.CTRLB.SetBits(sam.SERCOM_USART_CTRLB_TXEN | sam.SERCOM_USART_CTRLB_RXEN)
|
||||
uart.Bus.CTRLB.SetBits(sam.SERCOM_USART_INT_CTRLB_TXEN | sam.SERCOM_USART_INT_CTRLB_RXEN)
|
||||
|
||||
// Enable USART1 port.
|
||||
// sercom->USART.CTRLA.bit.ENABLE = 0x1u;
|
||||
uart.Bus.CTRLA.SetBits(sam.SERCOM_USART_CTRLA_ENABLE)
|
||||
for uart.Bus.SYNCBUSY.HasBits(sam.SERCOM_USART_SYNCBUSY_ENABLE) {
|
||||
uart.Bus.CTRLA.SetBits(sam.SERCOM_USART_INT_CTRLA_ENABLE)
|
||||
for uart.Bus.SYNCBUSY.HasBits(sam.SERCOM_USART_INT_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
// setup interrupt on receive
|
||||
uart.Bus.INTENSET.Set(sam.SERCOM_USART_INTENSET_RXC)
|
||||
uart.Bus.INTENSET.Set(sam.SERCOM_USART_INT_INTENSET_RXC)
|
||||
|
||||
// Enable RX IRQ. Currently assumes SERCOM3.
|
||||
arm.EnableIRQ(sam.IRQ_SERCOM3_0)
|
||||
@@ -462,14 +460,14 @@ func (uart UART) SetBaudRate(br uint32) {
|
||||
|
||||
// sercom->USART.BAUD.FRAC.FP = (baudTimes8 % 8);
|
||||
// sercom->USART.BAUD.FRAC.BAUD = (baudTimes8 / 8);
|
||||
uart.Bus.BAUD.Set(uint16(((baud % 8) << sam.SERCOM_USART_BAUD_FRAC_MODE_FP_Pos) |
|
||||
((baud / 8) << sam.SERCOM_USART_BAUD_FRAC_MODE_BAUD_Pos)))
|
||||
uart.Bus.BAUD.Set(uint16(((baud % 8) << sam.SERCOM_USART_INT_BAUD_FRAC_MODE_FP_Pos) |
|
||||
((baud / 8) << sam.SERCOM_USART_INT_BAUD_FRAC_MODE_BAUD_Pos)))
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the UART.
|
||||
func (uart UART) WriteByte(c byte) error {
|
||||
// wait until ready to receive
|
||||
for !uart.Bus.INTFLAG.HasBits(sam.SERCOM_USART_INTFLAG_DRE) {
|
||||
for !uart.Bus.INTFLAG.HasBits(sam.SERCOM_USART_INT_INTFLAG_DRE) {
|
||||
}
|
||||
uart.Bus.DATA.Set(uint32(c))
|
||||
return nil
|
||||
@@ -498,7 +496,7 @@ func handleSERCOM3_OTHER() {
|
||||
func handleUART1() {
|
||||
// should reset IRQ
|
||||
UART1.Receive(byte((UART1.Bus.DATA.Get() & 0xFF)))
|
||||
UART1.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
||||
UART1.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC)
|
||||
}
|
||||
|
||||
// I2C on the SAMD51.
|
||||
@@ -664,7 +662,7 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
// WriteByte writes a single byte to the I2C bus.
|
||||
func (i2c I2C) WriteByte(data byte) error {
|
||||
// Send data byte
|
||||
i2c.Bus.DATA.Set(uint32(data))
|
||||
i2c.Bus.DATA.Set(data)
|
||||
|
||||
// wait until transmission successful
|
||||
timeout := i2cTimeout
|
||||
@@ -739,7 +737,7 @@ func (i2c I2C) readByte() byte {
|
||||
|
||||
// SPI
|
||||
type SPI struct {
|
||||
Bus *sam.SERCOM_SPI_Type
|
||||
Bus *sam.SERCOM_SPIM_Type
|
||||
SCK Pin
|
||||
MOSI Pin
|
||||
MISO Pin
|
||||
@@ -772,8 +770,8 @@ func (spi SPI) Configure(config SPIConfig) {
|
||||
}
|
||||
|
||||
// Disable SPI port.
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPI_CTRLA_ENABLE)
|
||||
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPI_SYNCBUSY_ENABLE) {
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPIM_CTRLA_ENABLE)
|
||||
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
// enable pins
|
||||
@@ -782,9 +780,9 @@ func (spi SPI) Configure(config SPIConfig) {
|
||||
config.MISO.Configure(PinConfig{Mode: PinSERCOMAlt})
|
||||
|
||||
// reset SERCOM
|
||||
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPI_CTRLA_SWRST)
|
||||
for spi.Bus.CTRLA.HasBits(sam.SERCOM_SPI_CTRLA_SWRST) ||
|
||||
spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPI_SYNCBUSY_SWRST) {
|
||||
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPIM_CTRLA_SWRST)
|
||||
for spi.Bus.CTRLA.HasBits(sam.SERCOM_SPIM_CTRLA_SWRST) ||
|
||||
spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_SWRST) {
|
||||
}
|
||||
|
||||
// set bit transfer order
|
||||
@@ -794,34 +792,34 @@ func (spi SPI) Configure(config SPIConfig) {
|
||||
}
|
||||
|
||||
// Set SPI master
|
||||
// SERCOM_SPI_CTRLA_MODE_SPI_MASTER = 3
|
||||
spi.Bus.CTRLA.Set(uint32((3 << sam.SERCOM_SPI_CTRLA_MODE_Pos) |
|
||||
(doPad << sam.SERCOM_SPI_CTRLA_DOPO_Pos) |
|
||||
(diPad << sam.SERCOM_SPI_CTRLA_DIPO_Pos) |
|
||||
(dataOrder << sam.SERCOM_SPI_CTRLA_DORD_Pos)))
|
||||
// SERCOM_SPIM_CTRLA_MODE_SPI_MASTER = 3
|
||||
spi.Bus.CTRLA.Set(uint32((3 << sam.SERCOM_SPIM_CTRLA_MODE_Pos) |
|
||||
(doPad << sam.SERCOM_SPIM_CTRLA_DOPO_Pos) |
|
||||
(diPad << sam.SERCOM_SPIM_CTRLA_DIPO_Pos) |
|
||||
(dataOrder << sam.SERCOM_SPIM_CTRLA_DORD_Pos)))
|
||||
|
||||
spi.Bus.CTRLB.SetBits((0 << sam.SERCOM_SPI_CTRLB_CHSIZE_Pos) | // 8bit char size
|
||||
sam.SERCOM_SPI_CTRLB_RXEN) // receive enable
|
||||
spi.Bus.CTRLB.SetBits((0 << sam.SERCOM_SPIM_CTRLB_CHSIZE_Pos) | // 8bit char size
|
||||
sam.SERCOM_SPIM_CTRLB_RXEN) // receive enable
|
||||
|
||||
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPI_SYNCBUSY_CTRLB) {
|
||||
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_CTRLB) {
|
||||
}
|
||||
|
||||
// set mode
|
||||
switch config.Mode {
|
||||
case 0:
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPI_CTRLA_CPHA)
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPI_CTRLA_CPOL)
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPIM_CTRLA_CPHA)
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPIM_CTRLA_CPOL)
|
||||
case 1:
|
||||
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPI_CTRLA_CPHA)
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPI_CTRLA_CPOL)
|
||||
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPIM_CTRLA_CPHA)
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPIM_CTRLA_CPOL)
|
||||
case 2:
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPI_CTRLA_CPHA)
|
||||
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPI_CTRLA_CPOL)
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPIM_CTRLA_CPHA)
|
||||
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPIM_CTRLA_CPOL)
|
||||
case 3:
|
||||
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPI_CTRLA_CPHA | sam.SERCOM_SPI_CTRLA_CPOL)
|
||||
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPIM_CTRLA_CPHA | sam.SERCOM_SPIM_CTRLA_CPOL)
|
||||
default: // to mode 0
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPI_CTRLA_CPHA)
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPI_CTRLA_CPOL)
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPIM_CTRLA_CPHA)
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPIM_CTRLA_CPOL)
|
||||
}
|
||||
|
||||
// Set synch speed for SPI
|
||||
@@ -829,8 +827,8 @@ func (spi SPI) Configure(config SPIConfig) {
|
||||
spi.Bus.BAUD.Set(uint8(baudRate))
|
||||
|
||||
// Enable SPI port.
|
||||
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPI_CTRLA_ENABLE)
|
||||
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPI_SYNCBUSY_ENABLE) {
|
||||
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPIM_CTRLA_ENABLE)
|
||||
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -840,7 +838,7 @@ func (spi SPI) Transfer(w byte) (byte, error) {
|
||||
spi.Bus.DATA.Set(uint32(w))
|
||||
|
||||
// wait for receive
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
|
||||
}
|
||||
|
||||
// return data
|
||||
@@ -857,33 +855,33 @@ func InitPWM() {
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||
|
||||
//use clock generator 0
|
||||
sam.GCLK.PCHCTRL25.Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK.PCHCTRL[25].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.GCLK.PCHCTRL29.Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK.PCHCTRL[29].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() {
|
||||
// Set pin as output
|
||||
sam.PORT.DIRSET0.Set(1 << uint8(pwm.Pin))
|
||||
sam.PORT.GROUP[0].DIRSET.Set(1 << uint8(pwm.Pin))
|
||||
// Set pin to low
|
||||
sam.PORT.OUTCLR0.Set(1 << uint8(pwm.Pin))
|
||||
sam.PORT.GROUP[0].OUTCLR.Set(1 << uint8(pwm.Pin))
|
||||
|
||||
// Enable the port multiplexer for pin
|
||||
pwm.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
|
||||
pwm.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN)
|
||||
|
||||
// Connect timer/mux to pin.
|
||||
pwmConfig := pwm.getMux()
|
||||
|
||||
if pwm.Pin&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := pwm.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_PMUX0_PMUXO_Pos))
|
||||
val := pwm.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
|
||||
pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_GROUP_PMUX_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := pwm.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_PMUX0_PMUXE_Pos))
|
||||
val := pwm.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
|
||||
pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_GROUP_PMUX_PMUXE_Pos))
|
||||
}
|
||||
|
||||
// figure out which TCCX timer for this pin
|
||||
@@ -1009,25 +1007,25 @@ func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||
func (pwm PWM) setChannel(val uint32) {
|
||||
switch pwm.Pin {
|
||||
case PA16:
|
||||
pwm.getTimer().CC0.Set(val)
|
||||
pwm.getTimer().CC[0].Set(val)
|
||||
case PA17:
|
||||
pwm.getTimer().CC1.Set(val)
|
||||
pwm.getTimer().CC[1].Set(val)
|
||||
case PA14:
|
||||
pwm.getTimer().CC0.Set(val)
|
||||
pwm.getTimer().CC[0].Set(val)
|
||||
case PA15:
|
||||
pwm.getTimer().CC1.Set(val)
|
||||
pwm.getTimer().CC[1].Set(val)
|
||||
case PA18:
|
||||
pwm.getTimer().CC2.Set(val)
|
||||
pwm.getTimer().CC[2].Set(val)
|
||||
case PA19:
|
||||
pwm.getTimer().CC3.Set(val)
|
||||
pwm.getTimer().CC[3].Set(val)
|
||||
case PA20:
|
||||
pwm.getTimer().CC0.Set(val)
|
||||
pwm.getTimer().CC[0].Set(val)
|
||||
case PA21:
|
||||
pwm.getTimer().CC1.Set(val)
|
||||
pwm.getTimer().CC[1].Set(val)
|
||||
case PA23:
|
||||
pwm.getTimer().CC3.Set(val)
|
||||
pwm.getTimer().CC[3].Set(val)
|
||||
case PA22:
|
||||
pwm.getTimer().CC2.Set(val)
|
||||
pwm.getTimer().CC[2].Set(val)
|
||||
default:
|
||||
return // not supported on this pin
|
||||
}
|
||||
@@ -1037,25 +1035,25 @@ func (pwm PWM) setChannel(val uint32) {
|
||||
func (pwm PWM) setChannelBuffer(val uint32) {
|
||||
switch pwm.Pin {
|
||||
case PA16:
|
||||
pwm.getTimer().CCBUF0.Set(val)
|
||||
pwm.getTimer().CCBUF[0].Set(val)
|
||||
case PA17:
|
||||
pwm.getTimer().CCBUF1.Set(val)
|
||||
pwm.getTimer().CCBUF[1].Set(val)
|
||||
case PA14:
|
||||
pwm.getTimer().CCBUF0.Set(val)
|
||||
pwm.getTimer().CCBUF[0].Set(val)
|
||||
case PA15:
|
||||
pwm.getTimer().CCBUF1.Set(val)
|
||||
pwm.getTimer().CCBUF[1].Set(val)
|
||||
case PA18:
|
||||
pwm.getTimer().CCBUF2.Set(val)
|
||||
pwm.getTimer().CCBUF[2].Set(val)
|
||||
case PA19:
|
||||
pwm.getTimer().CCBUF3.Set(val)
|
||||
pwm.getTimer().CCBUF[3].Set(val)
|
||||
case PA20:
|
||||
pwm.getTimer().CCBUF0.Set(val)
|
||||
pwm.getTimer().CCBUF[0].Set(val)
|
||||
case PA21:
|
||||
pwm.getTimer().CCBUF1.Set(val)
|
||||
pwm.getTimer().CCBUF[1].Set(val)
|
||||
case PA23:
|
||||
pwm.getTimer().CCBUF3.Set(val)
|
||||
pwm.getTimer().CCBUF[3].Set(val)
|
||||
case PA22:
|
||||
pwm.getTimer().CCBUF2.Set(val)
|
||||
pwm.getTimer().CCBUF[2].Set(val)
|
||||
default:
|
||||
return // not supported on this pin
|
||||
}
|
||||
@@ -1110,14 +1108,14 @@ func (usbcdc USBCDC) WriteByte(c byte) error {
|
||||
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)
|
||||
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_EPSTATUSSET_BK1RDY)
|
||||
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_EPINTFLAG_TRCPT1) == 0 {
|
||||
for (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errors.New("USBCDC write byte timeout")
|
||||
@@ -1282,7 +1280,7 @@ func handleUSBIRQ() {
|
||||
initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL)
|
||||
|
||||
// Enable Setup-Received interrupt
|
||||
setEPINTENSET(0, sam.USB_DEVICE_EPINTENSET_RXSTP)
|
||||
setEPINTENSET(0, sam.USB_DEVICE_ENDPOINT_EPINTENSET_RXSTP)
|
||||
|
||||
usbConfiguration = 0
|
||||
|
||||
@@ -1296,15 +1294,15 @@ func handleUSBIRQ() {
|
||||
}
|
||||
|
||||
// Endpoint 0 Setup interrupt
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_EPINTFLAG_RXSTP > 0 {
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP > 0 {
|
||||
// ack setup received
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_RXSTP)
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP)
|
||||
|
||||
// parse setup
|
||||
setup := newUSBSetup(udd_ep_out_cache_buffer[0][:])
|
||||
|
||||
// Clear the Bank 0 ready flag on Control OUT
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
ok := false
|
||||
@@ -1320,18 +1318,18 @@ func handleUSBIRQ() {
|
||||
|
||||
if ok {
|
||||
// set Bank1 ready
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
} else {
|
||||
// Stall endpoint
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_EPINTFLAG_STALL1)
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
||||
}
|
||||
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_EPINTFLAG_STALL1 > 0 {
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1 > 0 {
|
||||
// ack the stall
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_STALL1)
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
||||
|
||||
// clear stall request
|
||||
setEPINTENCLR(0, sam.USB_DEVICE_EPINTENCLR_STALL1)
|
||||
setEPINTENCLR(0, sam.USB_DEVICE_ENDPOINT_EPINTENCLR_STALL1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1340,15 +1338,15 @@ func handleUSBIRQ() {
|
||||
for i = 1; i < uint32(len(endPoints)); i++ {
|
||||
// Check if endpoint has a pending interrupt
|
||||
epFlags := getEPINTFLAG(i)
|
||||
if (epFlags&sam.USB_DEVICE_EPINTFLAG_TRCPT0) > 0 ||
|
||||
(epFlags&sam.USB_DEVICE_EPINTFLAG_TRCPT1) > 0 {
|
||||
if (epFlags&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) > 0 ||
|
||||
(epFlags&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) > 0 {
|
||||
switch i {
|
||||
case usb_CDC_ENDPOINT_OUT:
|
||||
handleEndpoint(i)
|
||||
setEPINTFLAG(i, epFlags)
|
||||
case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM:
|
||||
setEPSTATUSCLR(i, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
|
||||
setEPINTFLAG(i, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
setEPSTATUSCLR(i, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
|
||||
setEPINTFLAG(i, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1364,7 +1362,7 @@ func initEndpoint(ep, config uint32) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointOut:
|
||||
// set packet size
|
||||
@@ -1374,16 +1372,16 @@ func initEndpoint(ep, config uint32) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// receive interrupts when current transfer complete
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT0)
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// ready for next transfer
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut:
|
||||
// TODO: not really anything, seems like...
|
||||
@@ -1396,10 +1394,10 @@ func initEndpoint(ep, config uint32) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// NAK on endpoint IN, the bank is not yet filled in.
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
|
||||
|
||||
case usb_ENDPOINT_TYPE_CONTROL:
|
||||
// Control OUT
|
||||
@@ -1410,7 +1408,7 @@ func initEndpoint(ep, config uint32) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// Control IN
|
||||
// set packet size
|
||||
@@ -1420,7 +1418,7 @@ func initEndpoint(ep, config uint32) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// Prepare OUT endpoint for receive
|
||||
// set multi packet size for expected number of receive bytes on control OUT
|
||||
@@ -1430,7 +1428,7 @@ func initEndpoint(ep, config uint32) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// NAK on endpoint OUT to show we are ready to receive control data
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_EPSTATUSSET_BK0RDY)
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK0RDY)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1473,14 +1471,14 @@ func handleStandardSetup(setup usbSetup) bool {
|
||||
uint32(1<<31)) // autozlp
|
||||
|
||||
// ack the transfer is complete from the request
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
|
||||
// set bank ready for data
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
|
||||
// wait for transfer to complete
|
||||
timeout := 3000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) == 0 {
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return true
|
||||
@@ -1514,10 +1512,10 @@ func handleStandardSetup(setup usbSetup) bool {
|
||||
usbConfiguration = setup.wValueL
|
||||
|
||||
// Enable interrupt for CDC control messages from host (OUT packet)
|
||||
setEPINTENSET(usb_CDC_ENDPOINT_ACM, sam.USB_DEVICE_EPINTENSET_TRCPT1)
|
||||
setEPINTENSET(usb_CDC_ENDPOINT_ACM, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1)
|
||||
|
||||
// Enable interrupt for CDC data messages from host
|
||||
setEPINTENSET(usb_CDC_ENDPOINT_OUT, sam.USB_DEVICE_EPINTENSET_TRCPT0)
|
||||
setEPINTENSET(usb_CDC_ENDPOINT_OUT, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
|
||||
|
||||
sendZlp(0)
|
||||
return true
|
||||
@@ -1544,24 +1542,27 @@ func handleStandardSetup(setup usbSetup) bool {
|
||||
func cdcSetup(setup usbSetup) bool {
|
||||
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_GET_LINE_CODING {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, 7))
|
||||
binary.Write(buf, binary.LittleEndian, usbLineInfo.dwDTERate)
|
||||
binary.Write(buf, binary.LittleEndian, usbLineInfo.bCharFormat)
|
||||
binary.Write(buf, binary.LittleEndian, usbLineInfo.bParityType)
|
||||
binary.Write(buf, binary.LittleEndian, usbLineInfo.bDataBits)
|
||||
b := make([]byte, 7)
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
b[3] = byte(usbLineInfo.dwDTERate >> 24)
|
||||
b[4] = byte(usbLineInfo.bCharFormat)
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
sendUSBPacket(0, buf.Bytes())
|
||||
sendUSBPacket(0, b)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING {
|
||||
buf := bytes.NewBuffer(receiveUSBControlPacket())
|
||||
binary.Read(buf, binary.LittleEndian, &(usbLineInfo.dwDTERate))
|
||||
binary.Read(buf, binary.LittleEndian, &(usbLineInfo.bCharFormat))
|
||||
binary.Read(buf, binary.LittleEndian, &(usbLineInfo.bParityType))
|
||||
binary.Read(buf, binary.LittleEndian, &(usbLineInfo.bDataBits))
|
||||
b := receiveUSBControlPacket()
|
||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
usbLineInfo.bCharFormat = b[4]
|
||||
usbLineInfo.bParityType = b[5]
|
||||
usbLineInfo.bDataBits = b[6]
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
@@ -1610,11 +1611,11 @@ func receiveUSBControlPacket() []byte {
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
// Wait until OUT transfer is ready.
|
||||
timeout := 3000
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout := 300000
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_ENDPOINT_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return []byte{}
|
||||
@@ -1623,7 +1624,7 @@ func receiveUSBControlPacket() []byte {
|
||||
|
||||
// Wait until OUT transfer is completed.
|
||||
timeout = 300000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT0) == 0 {
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return []byte{}
|
||||
@@ -1751,7 +1752,7 @@ func sendConfiguration(setup usbSetup) {
|
||||
sz := uint16(configDescriptorSize + cdcSize)
|
||||
config := NewConfigDescriptor(sz, 2)
|
||||
|
||||
buf := make([]byte, 0, sz)
|
||||
buf := make([]byte, 0)
|
||||
buf = append(buf, config.Bytes()...)
|
||||
buf = append(buf, cdc.Bytes()...)
|
||||
|
||||
@@ -1776,7 +1777,7 @@ func handleEndpoint(ep uint32) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
}
|
||||
|
||||
func sendZlp(ep uint32) {
|
||||
@@ -1807,210 +1808,39 @@ func epPacketSize(size uint16) uint32 {
|
||||
}
|
||||
|
||||
func getEPCFG(ep uint32) uint8 {
|
||||
switch ep {
|
||||
case 0:
|
||||
return sam.USB_DEVICE.EPCFG0.Get()
|
||||
case 1:
|
||||
return sam.USB_DEVICE.EPCFG1.Get()
|
||||
case 2:
|
||||
return sam.USB_DEVICE.EPCFG2.Get()
|
||||
case 3:
|
||||
return sam.USB_DEVICE.EPCFG3.Get()
|
||||
case 4:
|
||||
return sam.USB_DEVICE.EPCFG4.Get()
|
||||
case 5:
|
||||
return sam.USB_DEVICE.EPCFG5.Get()
|
||||
case 6:
|
||||
return sam.USB_DEVICE.EPCFG6.Get()
|
||||
case 7:
|
||||
return sam.USB_DEVICE.EPCFG7.Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Get()
|
||||
}
|
||||
|
||||
func setEPCFG(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPCFG0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPCFG1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPCFG2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPCFG3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPCFG4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPCFG5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPCFG6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPCFG7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Set(val)
|
||||
}
|
||||
|
||||
func setEPSTATUSCLR(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPSTATUSCLR0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPSTATUSCLR1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPSTATUSCLR2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPSTATUSCLR3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPSTATUSCLR4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPSTATUSCLR5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPSTATUSCLR6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPSTATUSCLR7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSCLR.Set(val)
|
||||
}
|
||||
|
||||
func setEPSTATUSSET(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPSTATUSSET0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPSTATUSSET1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPSTATUSSET2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPSTATUSSET3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPSTATUSSET4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPSTATUSSET5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPSTATUSSET6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPSTATUSSET7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSSET.Set(val)
|
||||
}
|
||||
|
||||
func getEPSTATUS(ep uint32) uint8 {
|
||||
switch ep {
|
||||
case 0:
|
||||
return sam.USB_DEVICE.EPSTATUS0.Get()
|
||||
case 1:
|
||||
return sam.USB_DEVICE.EPSTATUS1.Get()
|
||||
case 2:
|
||||
return sam.USB_DEVICE.EPSTATUS2.Get()
|
||||
case 3:
|
||||
return sam.USB_DEVICE.EPSTATUS3.Get()
|
||||
case 4:
|
||||
return sam.USB_DEVICE.EPSTATUS4.Get()
|
||||
case 5:
|
||||
return sam.USB_DEVICE.EPSTATUS5.Get()
|
||||
case 6:
|
||||
return sam.USB_DEVICE.EPSTATUS6.Get()
|
||||
case 7:
|
||||
return sam.USB_DEVICE.EPSTATUS7.Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUS.Get()
|
||||
}
|
||||
|
||||
func getEPINTFLAG(ep uint32) uint8 {
|
||||
switch ep {
|
||||
case 0:
|
||||
return sam.USB_DEVICE.EPINTFLAG0.Get()
|
||||
case 1:
|
||||
return sam.USB_DEVICE.EPINTFLAG1.Get()
|
||||
case 2:
|
||||
return sam.USB_DEVICE.EPINTFLAG2.Get()
|
||||
case 3:
|
||||
return sam.USB_DEVICE.EPINTFLAG3.Get()
|
||||
case 4:
|
||||
return sam.USB_DEVICE.EPINTFLAG4.Get()
|
||||
case 5:
|
||||
return sam.USB_DEVICE.EPINTFLAG5.Get()
|
||||
case 6:
|
||||
return sam.USB_DEVICE.EPINTFLAG6.Get()
|
||||
case 7:
|
||||
return sam.USB_DEVICE.EPINTFLAG7.Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Get()
|
||||
}
|
||||
|
||||
func setEPINTFLAG(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPINTFLAG0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPINTFLAG1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPINTFLAG2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPINTFLAG3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPINTFLAG4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPINTFLAG5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPINTFLAG6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPINTFLAG7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Set(val)
|
||||
}
|
||||
|
||||
func setEPINTENCLR(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPINTENCLR0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPINTENCLR1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPINTENCLR2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPINTENCLR3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPINTENCLR4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPINTENCLR5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPINTENCLR6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPINTENCLR7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENCLR.Set(val)
|
||||
}
|
||||
|
||||
func setEPINTENSET(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPINTENSET0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPINTENSET1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPINTENSET2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPINTENSET3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPINTENSET4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPINTENSET5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPINTENSET6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPINTENSET7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENSET.Set(val)
|
||||
}
|
||||
|
||||
// ResetProcessor should perform a system reset in preperation
|
||||
|
||||
@@ -15,9 +15,9 @@ import (
|
||||
// implement bit-banged drivers.
|
||||
func (p Pin) PortMaskSet() (*uint32, uint32) {
|
||||
if p < 32 {
|
||||
return &sam.PORT.OUTSET0.Reg, 1 << uint8(p)
|
||||
return &sam.PORT.GROUP[0].OUTSET.Reg, 1 << uint8(p)
|
||||
} else {
|
||||
return &sam.PORT.OUTSET1.Reg, 1 << uint8(p-32)
|
||||
return &sam.PORT.GROUP[1].OUTSET.Reg, 1 << uint8(p-32)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ func (p Pin) PortMaskSet() (*uint32, uint32) {
|
||||
// implement bit-banged drivers.
|
||||
func (p Pin) PortMaskClear() (*uint32, uint32) {
|
||||
if p < 32 {
|
||||
return &sam.PORT.OUTCLR0.Reg, 1 << uint8(p)
|
||||
return &sam.PORT.GROUP[0].OUTCLR.Reg, 1 << uint8(p)
|
||||
} else {
|
||||
return &sam.PORT.OUTCLR1.Reg, 1 << uint8(p-32)
|
||||
return &sam.PORT.GROUP[1].OUTCLR.Reg, 1 << uint8(p-32)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,15 +36,15 @@ func (p Pin) PortMaskClear() (*uint32, uint32) {
|
||||
func (p Pin) Set(high bool) {
|
||||
if p < 32 {
|
||||
if high {
|
||||
sam.PORT.OUTSET0.Set(1 << uint8(p))
|
||||
sam.PORT.GROUP[0].OUTSET.Set(1 << uint8(p))
|
||||
} else {
|
||||
sam.PORT.OUTCLR0.Set(1 << uint8(p))
|
||||
sam.PORT.GROUP[0].OUTCLR.Set(1 << uint8(p))
|
||||
}
|
||||
} else {
|
||||
if high {
|
||||
sam.PORT.OUTSET1.Set(1 << uint8(p-32))
|
||||
sam.PORT.GROUP[1].OUTSET.Set(1 << uint8(p-32))
|
||||
} else {
|
||||
sam.PORT.OUTCLR1.Set(1 << uint8(p-32))
|
||||
sam.PORT.GROUP[1].OUTCLR.Set(1 << uint8(p-32))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,9 +52,9 @@ func (p Pin) Set(high bool) {
|
||||
// Get returns the current value of a GPIO pin.
|
||||
func (p Pin) Get() bool {
|
||||
if p < 32 {
|
||||
return (sam.PORT.IN0.Get()>>uint8(p))&1 > 0
|
||||
return (sam.PORT.GROUP[0].IN.Get()>>uint8(p))&1 > 0
|
||||
} else {
|
||||
return (sam.PORT.IN1.Get()>>(uint8(p)-32))&1 > 0
|
||||
return (sam.PORT.GROUP[1].IN.Get()>>(uint8(p)-32))&1 > 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,166 +63,106 @@ func (p Pin) Configure(config PinConfig) {
|
||||
switch config.Mode {
|
||||
case PinOutput:
|
||||
if p < 32 {
|
||||
sam.PORT.DIRSET0.Set(1 << uint8(p))
|
||||
sam.PORT.GROUP[0].DIRSET.Set(1 << uint8(p))
|
||||
// output is also set to input enable so pin can read back its own value
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||
p.setPinCfg(sam.PORT_GROUP_PINCFG_INEN)
|
||||
} else {
|
||||
sam.PORT.DIRSET1.Set(1 << uint8(p-32))
|
||||
sam.PORT.GROUP[1].DIRSET.Set(1 << uint8(p-32))
|
||||
// output is also set to input enable so pin can read back its own value
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||
p.setPinCfg(sam.PORT_GROUP_PINCFG_INEN)
|
||||
}
|
||||
|
||||
case PinInput:
|
||||
if p < 32 {
|
||||
sam.PORT.DIRCLR0.Set(1 << uint8(p))
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||
sam.PORT.GROUP[0].DIRCLR.Set(1 << uint8(p))
|
||||
p.setPinCfg(sam.PORT_GROUP_PINCFG_INEN)
|
||||
} else {
|
||||
sam.PORT.DIRCLR1.Set(1<<uint8(p) - 32)
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||
sam.PORT.GROUP[1].DIRCLR.Set(1<<uint8(p) - 32)
|
||||
p.setPinCfg(sam.PORT_GROUP_PINCFG_INEN)
|
||||
}
|
||||
|
||||
case PinInputPulldown:
|
||||
if p < 32 {
|
||||
sam.PORT.DIRCLR0.Set(1 << uint8(p))
|
||||
sam.PORT.OUTCLR0.Set(1 << uint8(p))
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)
|
||||
sam.PORT.GROUP[0].DIRCLR.Set(1 << uint8(p))
|
||||
sam.PORT.GROUP[0].OUTCLR.Set(1 << uint8(p))
|
||||
p.setPinCfg(sam.PORT_GROUP_PINCFG_INEN | sam.PORT_GROUP_PINCFG_PULLEN)
|
||||
} else {
|
||||
sam.PORT.DIRCLR1.Set(1<<uint8(p) - 32)
|
||||
sam.PORT.OUTCLR1.Set(1<<uint8(p) - 32)
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)
|
||||
sam.PORT.GROUP[1].DIRCLR.Set(1<<uint8(p) - 32)
|
||||
sam.PORT.GROUP[1].OUTCLR.Set(1<<uint8(p) - 32)
|
||||
p.setPinCfg(sam.PORT_GROUP_PINCFG_INEN | sam.PORT_GROUP_PINCFG_PULLEN)
|
||||
}
|
||||
|
||||
case PinInputPullup:
|
||||
if p < 32 {
|
||||
sam.PORT.DIRCLR0.Set(1 << uint8(p))
|
||||
sam.PORT.OUTSET0.Set(1 << uint8(p))
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)
|
||||
sam.PORT.GROUP[0].DIRCLR.Set(1 << uint8(p))
|
||||
sam.PORT.GROUP[0].OUTSET.Set(1 << uint8(p))
|
||||
p.setPinCfg(sam.PORT_GROUP_PINCFG_INEN | sam.PORT_GROUP_PINCFG_PULLEN)
|
||||
} else {
|
||||
sam.PORT.DIRCLR1.Set(1<<uint8(p) - 32)
|
||||
sam.PORT.OUTSET1.Set(1<<uint8(p) - 32)
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)
|
||||
sam.PORT.GROUP[1].DIRCLR.Set(1<<uint8(p) - 32)
|
||||
sam.PORT.GROUP[1].OUTSET.Set(1<<uint8(p) - 32)
|
||||
p.setPinCfg(sam.PORT_GROUP_PINCFG_INEN | sam.PORT_GROUP_PINCFG_PULLEN)
|
||||
}
|
||||
|
||||
case PinSERCOM:
|
||||
if p&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
p.setPMux(val | (uint8(PinSERCOM) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
|
||||
p.setPMux(val | (uint8(PinSERCOM) << sam.PORT_GROUP_PMUX_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
p.setPMux(val | (uint8(PinSERCOM) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
|
||||
p.setPMux(val | (uint8(PinSERCOM) << sam.PORT_GROUP_PMUX_PMUXE_Pos))
|
||||
}
|
||||
// enable port config
|
||||
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR | sam.PORT_PINCFG0_INEN)
|
||||
p.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN | sam.PORT_GROUP_PINCFG_DRVSTR | sam.PORT_GROUP_PINCFG_INEN)
|
||||
|
||||
case PinSERCOMAlt:
|
||||
if p&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
p.setPMux(val | (uint8(PinSERCOMAlt) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
|
||||
p.setPMux(val | (uint8(PinSERCOMAlt) << sam.PORT_GROUP_PMUX_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
p.setPMux(val | (uint8(PinSERCOMAlt) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
|
||||
p.setPMux(val | (uint8(PinSERCOMAlt) << sam.PORT_GROUP_PMUX_PMUXE_Pos))
|
||||
}
|
||||
// enable port config
|
||||
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR)
|
||||
p.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN | sam.PORT_GROUP_PINCFG_DRVSTR)
|
||||
|
||||
case PinCom:
|
||||
if p&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
p.setPMux(val | (uint8(PinCom) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
|
||||
p.setPMux(val | (uint8(PinCom) << sam.PORT_GROUP_PMUX_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
p.setPMux(val | (uint8(PinCom) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
|
||||
p.setPMux(val | (uint8(PinCom) << sam.PORT_GROUP_PMUX_PMUXE_Pos))
|
||||
}
|
||||
// enable port config
|
||||
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
|
||||
p.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN)
|
||||
case PinAnalog:
|
||||
if p&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
p.setPMux(val | (uint8(PinAnalog) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
|
||||
p.setPMux(val | (uint8(PinAnalog) << sam.PORT_GROUP_PMUX_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
p.setPMux(val | (uint8(PinAnalog) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
|
||||
p.setPMux(val | (uint8(PinAnalog) << sam.PORT_GROUP_PMUX_PMUXE_Pos))
|
||||
}
|
||||
// enable port config
|
||||
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR)
|
||||
p.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN | sam.PORT_GROUP_PINCFG_DRVSTR)
|
||||
}
|
||||
}
|
||||
|
||||
// getPMux returns the value for the correct PMUX register for this pin.
|
||||
func (p Pin) getPMux() uint8 {
|
||||
switch uint8(p) >> 1 {
|
||||
case 0:
|
||||
return sam.PORT.PMUX0_0.Get()
|
||||
case 1:
|
||||
return sam.PORT.PMUX0_1.Get()
|
||||
case 2:
|
||||
return sam.PORT.PMUX0_2.Get()
|
||||
case 3:
|
||||
return sam.PORT.PMUX0_3.Get()
|
||||
case 4:
|
||||
return sam.PORT.PMUX0_4.Get()
|
||||
case 5:
|
||||
return sam.PORT.PMUX0_5.Get()
|
||||
case 6:
|
||||
return sam.PORT.PMUX0_6.Get()
|
||||
case 7:
|
||||
return sam.PORT.PMUX0_7.Get()
|
||||
case 8:
|
||||
return sam.PORT.PMUX0_8.Get()
|
||||
case 9:
|
||||
return sam.PORT.PMUX0_9.Get()
|
||||
case 10:
|
||||
return sam.PORT.PMUX0_10.Get()
|
||||
case 11:
|
||||
return sam.PORT.PMUX0_11.Get()
|
||||
case 12:
|
||||
return sam.PORT.PMUX0_12.Get()
|
||||
case 13:
|
||||
return sam.PORT.PMUX0_13.Get()
|
||||
case 14:
|
||||
return sam.PORT.PMUX0_14.Get()
|
||||
case 15:
|
||||
return sam.PORT.PMUX0_15.Get()
|
||||
case 16:
|
||||
return uint8(sam.PORT.PMUX1_0.Get()>>0) & 0xff
|
||||
case 17:
|
||||
return uint8(sam.PORT.PMUX1_0.Get()>>8) & 0xff
|
||||
case 18:
|
||||
return uint8(sam.PORT.PMUX1_0.Get()>>16) & 0xff
|
||||
case 19:
|
||||
return uint8(sam.PORT.PMUX1_0.Get()>>24) & 0xff
|
||||
case 20:
|
||||
return uint8(sam.PORT.PMUX1_4.Get()>>0) & 0xff
|
||||
case 21:
|
||||
return uint8(sam.PORT.PMUX1_4.Get()>>8) & 0xff
|
||||
case 22:
|
||||
return uint8(sam.PORT.PMUX1_4.Get()>>16) & 0xff
|
||||
case 23:
|
||||
return uint8(sam.PORT.PMUX1_4.Get()>>24) & 0xff
|
||||
case 24:
|
||||
return uint8(sam.PORT.PMUX1_8.Get()>>0) & 0xff
|
||||
case 25:
|
||||
return uint8(sam.PORT.PMUX1_8.Get()>>8) & 0xff
|
||||
case 26:
|
||||
return uint8(sam.PORT.PMUX1_8.Get()>>16) & 0xff
|
||||
case 27:
|
||||
return uint8(sam.PORT.PMUX1_8.Get()>>24) & 0xff
|
||||
case 28:
|
||||
return uint8(sam.PORT.PMUX1_12.Get()>>0) & 0xff
|
||||
case 29:
|
||||
return uint8(sam.PORT.PMUX1_12.Get()>>8) & 0xff
|
||||
case 30:
|
||||
return uint8(sam.PORT.PMUX1_12.Get()>>16) & 0xff
|
||||
case 31:
|
||||
return uint8(sam.PORT.PMUX1_12.Get()>>24) & 0xff
|
||||
switch {
|
||||
case p < 32:
|
||||
return sam.PORT.GROUP[0].PMUX[uint8(p)>>1].Get()
|
||||
case p >= 32 && p < 64:
|
||||
return sam.PORT.GROUP[1].PMUX[uint8(p-32)>>1].Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
@@ -230,205 +170,21 @@ func (p Pin) getPMux() uint8 {
|
||||
|
||||
// setPMux sets the value for the correct PMUX register for this pin.
|
||||
func (p Pin) setPMux(val uint8) {
|
||||
switch uint8(p) >> 1 {
|
||||
case 0:
|
||||
sam.PORT.PMUX0_0.Set(val)
|
||||
case 1:
|
||||
sam.PORT.PMUX0_1.Set(val)
|
||||
case 2:
|
||||
sam.PORT.PMUX0_2.Set(val)
|
||||
case 3:
|
||||
sam.PORT.PMUX0_3.Set(val)
|
||||
case 4:
|
||||
sam.PORT.PMUX0_4.Set(val)
|
||||
case 5:
|
||||
sam.PORT.PMUX0_5.Set(val)
|
||||
case 6:
|
||||
sam.PORT.PMUX0_6.Set(val)
|
||||
case 7:
|
||||
sam.PORT.PMUX0_7.Set(val)
|
||||
case 8:
|
||||
sam.PORT.PMUX0_8.Set(val)
|
||||
case 9:
|
||||
sam.PORT.PMUX0_9.Set(val)
|
||||
case 10:
|
||||
sam.PORT.PMUX0_10.Set(val)
|
||||
case 11:
|
||||
sam.PORT.PMUX0_11.Set(val)
|
||||
case 12:
|
||||
sam.PORT.PMUX0_12.Set(val)
|
||||
case 13:
|
||||
sam.PORT.PMUX0_13.Set(val)
|
||||
case 14:
|
||||
sam.PORT.PMUX0_14.Set(val)
|
||||
case 15:
|
||||
sam.PORT.PMUX0_15.Set(val)
|
||||
case 16:
|
||||
sam.PORT.PMUX1_0.Set(sam.PORT.PMUX1_0.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 17:
|
||||
sam.PORT.PMUX1_0.Set(sam.PORT.PMUX1_0.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 18:
|
||||
sam.PORT.PMUX1_0.Set(sam.PORT.PMUX1_0.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 19:
|
||||
sam.PORT.PMUX1_0.Set(sam.PORT.PMUX1_0.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 20:
|
||||
sam.PORT.PMUX1_4.Set(sam.PORT.PMUX1_4.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 21:
|
||||
sam.PORT.PMUX1_4.Set(sam.PORT.PMUX1_4.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 22:
|
||||
sam.PORT.PMUX1_4.Set(sam.PORT.PMUX1_4.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 23:
|
||||
sam.PORT.PMUX1_4.Set(sam.PORT.PMUX1_4.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 24:
|
||||
sam.PORT.PMUX1_8.Set(sam.PORT.PMUX1_8.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 25:
|
||||
sam.PORT.PMUX1_8.Set(sam.PORT.PMUX1_8.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 26:
|
||||
sam.PORT.PMUX1_8.Set(sam.PORT.PMUX1_8.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 27:
|
||||
sam.PORT.PMUX1_8.Set(sam.PORT.PMUX1_8.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 28:
|
||||
sam.PORT.PMUX1_12.Set(sam.PORT.PMUX1_12.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 29:
|
||||
sam.PORT.PMUX1_12.Set(sam.PORT.PMUX1_12.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 30:
|
||||
sam.PORT.PMUX1_12.Set(sam.PORT.PMUX1_12.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 31:
|
||||
sam.PORT.PMUX1_12.Set(sam.PORT.PMUX1_12.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
switch {
|
||||
case p < 32:
|
||||
sam.PORT.GROUP[0].PMUX[uint8(p)>>1].Set(val)
|
||||
case p >= 32 && p < 64:
|
||||
sam.PORT.GROUP[1].PMUX[uint8(p-32)>>1].Set(val)
|
||||
}
|
||||
}
|
||||
|
||||
// getPinCfg returns the value for the correct PINCFG register for this pin.
|
||||
func (p Pin) getPinCfg() uint8 {
|
||||
switch p {
|
||||
case 0:
|
||||
return sam.PORT.PINCFG0_0.Get()
|
||||
case 1:
|
||||
return sam.PORT.PINCFG0_1.Get()
|
||||
case 2:
|
||||
return sam.PORT.PINCFG0_2.Get()
|
||||
case 3:
|
||||
return sam.PORT.PINCFG0_3.Get()
|
||||
case 4:
|
||||
return sam.PORT.PINCFG0_4.Get()
|
||||
case 5:
|
||||
return sam.PORT.PINCFG0_5.Get()
|
||||
case 6:
|
||||
return sam.PORT.PINCFG0_6.Get()
|
||||
case 7:
|
||||
return sam.PORT.PINCFG0_7.Get()
|
||||
case 8:
|
||||
return sam.PORT.PINCFG0_8.Get()
|
||||
case 9:
|
||||
return sam.PORT.PINCFG0_9.Get()
|
||||
case 10:
|
||||
return sam.PORT.PINCFG0_10.Get()
|
||||
case 11:
|
||||
return sam.PORT.PINCFG0_11.Get()
|
||||
case 12:
|
||||
return sam.PORT.PINCFG0_12.Get()
|
||||
case 13:
|
||||
return sam.PORT.PINCFG0_13.Get()
|
||||
case 14:
|
||||
return sam.PORT.PINCFG0_14.Get()
|
||||
case 15:
|
||||
return sam.PORT.PINCFG0_15.Get()
|
||||
case 16:
|
||||
return sam.PORT.PINCFG0_16.Get()
|
||||
case 17:
|
||||
return sam.PORT.PINCFG0_17.Get()
|
||||
case 18:
|
||||
return sam.PORT.PINCFG0_18.Get()
|
||||
case 19:
|
||||
return sam.PORT.PINCFG0_19.Get()
|
||||
case 20:
|
||||
return sam.PORT.PINCFG0_20.Get()
|
||||
case 21:
|
||||
return sam.PORT.PINCFG0_21.Get()
|
||||
case 22:
|
||||
return sam.PORT.PINCFG0_22.Get()
|
||||
case 23:
|
||||
return sam.PORT.PINCFG0_23.Get()
|
||||
case 24:
|
||||
return sam.PORT.PINCFG0_24.Get()
|
||||
case 25:
|
||||
return sam.PORT.PINCFG0_25.Get()
|
||||
case 26:
|
||||
return sam.PORT.PINCFG0_26.Get()
|
||||
case 27:
|
||||
return sam.PORT.PINCFG0_27.Get()
|
||||
case 28:
|
||||
return sam.PORT.PINCFG0_28.Get()
|
||||
case 29:
|
||||
return sam.PORT.PINCFG0_29.Get()
|
||||
case 30:
|
||||
return sam.PORT.PINCFG0_30.Get()
|
||||
case 31:
|
||||
return sam.PORT.PINCFG0_31.Get()
|
||||
case 32: // PB00
|
||||
return uint8(sam.PORT.PINCFG1_0.Get()>>0) & 0xff
|
||||
case 33: // PB01
|
||||
return uint8(sam.PORT.PINCFG1_0.Get()>>8) & 0xff
|
||||
case 34: // PB02
|
||||
return uint8(sam.PORT.PINCFG1_0.Get()>>16) & 0xff
|
||||
case 35: // PB03
|
||||
return uint8(sam.PORT.PINCFG1_0.Get()>>24) & 0xff
|
||||
case 37: // PB04
|
||||
return uint8(sam.PORT.PINCFG1_4.Get()>>0) & 0xff
|
||||
case 38: // PB05
|
||||
return uint8(sam.PORT.PINCFG1_4.Get()>>8) & 0xff
|
||||
case 39: // PB06
|
||||
return uint8(sam.PORT.PINCFG1_4.Get()>>16) & 0xff
|
||||
case 40: // PB07
|
||||
return uint8(sam.PORT.PINCFG1_4.Get()>>24) & 0xff
|
||||
case 41: // PB08
|
||||
return uint8(sam.PORT.PINCFG1_8.Get()>>0) & 0xff
|
||||
case 42: // PB09
|
||||
return uint8(sam.PORT.PINCFG1_8.Get()>>8) & 0xff
|
||||
case 43: // PB10
|
||||
return uint8(sam.PORT.PINCFG1_8.Get()>>16) & 0xff
|
||||
case 44: // PB11
|
||||
return uint8(sam.PORT.PINCFG1_8.Get()>>24) & 0xff
|
||||
case 45: // PB12
|
||||
return uint8(sam.PORT.PINCFG1_12.Get()>>0) & 0xff
|
||||
case 46: // PB13
|
||||
return uint8(sam.PORT.PINCFG1_12.Get()>>8) & 0xff
|
||||
case 47: // PB14
|
||||
return uint8(sam.PORT.PINCFG1_12.Get()>>16) & 0xff
|
||||
case 48: // PB15
|
||||
return uint8(sam.PORT.PINCFG1_12.Get()>>24) & 0xff
|
||||
case 49: // PB16
|
||||
return uint8(sam.PORT.PINCFG1_16.Get()>>0) & 0xff
|
||||
case 50: // PB17
|
||||
return uint8(sam.PORT.PINCFG1_16.Get()>>8) & 0xff
|
||||
case 51: // PB18
|
||||
return uint8(sam.PORT.PINCFG1_16.Get()>>16) & 0xff
|
||||
case 52: // PB19
|
||||
return uint8(sam.PORT.PINCFG1_16.Get()>>24) & 0xff
|
||||
case 53: // PB20
|
||||
return uint8(sam.PORT.PINCFG1_20.Get()>>0) & 0xff
|
||||
case 54: // PB21
|
||||
return uint8(sam.PORT.PINCFG1_20.Get()>>8) & 0xff
|
||||
case 55: // PB22
|
||||
return uint8(sam.PORT.PINCFG1_20.Get()>>16) & 0xff
|
||||
case 56: // PB23
|
||||
return uint8(sam.PORT.PINCFG1_20.Get()>>24) & 0xff
|
||||
case 57: // PB24
|
||||
return uint8(sam.PORT.PINCFG1_24.Get()>>0) & 0xff
|
||||
case 58: // PB25
|
||||
return uint8(sam.PORT.PINCFG1_24.Get()>>8) & 0xff
|
||||
case 59: // PB26
|
||||
return uint8(sam.PORT.PINCFG1_24.Get()>>16) & 0xff
|
||||
case 60: // PB27
|
||||
return uint8(sam.PORT.PINCFG1_24.Get()>>24) & 0xff
|
||||
case 61: // PB28
|
||||
return uint8(sam.PORT.PINCFG1_28.Get()>>0) & 0xff
|
||||
case 62: // PB29
|
||||
return uint8(sam.PORT.PINCFG1_28.Get()>>8) & 0xff
|
||||
case 63: // PB30
|
||||
return uint8(sam.PORT.PINCFG1_28.Get()>>16) & 0xff
|
||||
case 64: // PB31
|
||||
return uint8(sam.PORT.PINCFG1_28.Get()>>24) & 0xff
|
||||
switch {
|
||||
case p < 32:
|
||||
return sam.PORT.GROUP[0].PINCFG[p].Get()
|
||||
case p >= 32 && p <= 64:
|
||||
return sam.PORT.GROUP[1].PINCFG[p-32].Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
@@ -436,134 +192,10 @@ func (p Pin) getPinCfg() uint8 {
|
||||
|
||||
// setPinCfg sets the value for the correct PINCFG register for this pin.
|
||||
func (p Pin) setPinCfg(val uint8) {
|
||||
switch p {
|
||||
case 0:
|
||||
sam.PORT.PINCFG0_0.Set(val)
|
||||
case 1:
|
||||
sam.PORT.PINCFG0_1.Set(val)
|
||||
case 2:
|
||||
sam.PORT.PINCFG0_2.Set(val)
|
||||
case 3:
|
||||
sam.PORT.PINCFG0_3.Set(val)
|
||||
case 4:
|
||||
sam.PORT.PINCFG0_4.Set(val)
|
||||
case 5:
|
||||
sam.PORT.PINCFG0_5.Set(val)
|
||||
case 6:
|
||||
sam.PORT.PINCFG0_6.Set(val)
|
||||
case 7:
|
||||
sam.PORT.PINCFG0_7.Set(val)
|
||||
case 8:
|
||||
sam.PORT.PINCFG0_8.Set(val)
|
||||
case 9:
|
||||
sam.PORT.PINCFG0_9.Set(val)
|
||||
case 10:
|
||||
sam.PORT.PINCFG0_10.Set(val)
|
||||
case 11:
|
||||
sam.PORT.PINCFG0_11.Set(val)
|
||||
case 12:
|
||||
sam.PORT.PINCFG0_12.Set(val)
|
||||
case 13:
|
||||
sam.PORT.PINCFG0_13.Set(val)
|
||||
case 14:
|
||||
sam.PORT.PINCFG0_14.Set(val)
|
||||
case 15:
|
||||
sam.PORT.PINCFG0_15.Set(val)
|
||||
case 16:
|
||||
sam.PORT.PINCFG0_16.Set(val)
|
||||
case 17:
|
||||
sam.PORT.PINCFG0_17.Set(val)
|
||||
case 18:
|
||||
sam.PORT.PINCFG0_18.Set(val)
|
||||
case 19:
|
||||
sam.PORT.PINCFG0_19.Set(val)
|
||||
case 20:
|
||||
sam.PORT.PINCFG0_20.Set(val)
|
||||
case 21:
|
||||
sam.PORT.PINCFG0_21.Set(val)
|
||||
case 22:
|
||||
sam.PORT.PINCFG0_22.Set(val)
|
||||
case 23:
|
||||
sam.PORT.PINCFG0_23.Set(val)
|
||||
case 24:
|
||||
sam.PORT.PINCFG0_24.Set(val)
|
||||
case 25:
|
||||
sam.PORT.PINCFG0_25.Set(val)
|
||||
case 26:
|
||||
sam.PORT.PINCFG0_26.Set(val)
|
||||
case 27:
|
||||
sam.PORT.PINCFG0_27.Set(val)
|
||||
case 28:
|
||||
sam.PORT.PINCFG0_28.Set(val)
|
||||
case 29:
|
||||
sam.PORT.PINCFG0_29.Set(val)
|
||||
case 30:
|
||||
sam.PORT.PINCFG0_30.Set(val)
|
||||
case 31:
|
||||
sam.PORT.PINCFG0_31.Set(val)
|
||||
case 32: // PB00
|
||||
sam.PORT.PINCFG1_0.Set(sam.PORT.PINCFG1_0.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 33: // PB01
|
||||
sam.PORT.PINCFG1_0.Set(sam.PORT.PINCFG1_0.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 34: // PB02
|
||||
sam.PORT.PINCFG1_0.Set(sam.PORT.PINCFG1_0.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 35: // PB03
|
||||
sam.PORT.PINCFG1_0.Set(sam.PORT.PINCFG1_0.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 36: // PB04
|
||||
sam.PORT.PINCFG1_4.Set(sam.PORT.PINCFG1_4.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 37: // PB05
|
||||
sam.PORT.PINCFG1_4.Set(sam.PORT.PINCFG1_4.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 38: // PB06
|
||||
sam.PORT.PINCFG1_4.Set(sam.PORT.PINCFG1_4.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 39: // PB07
|
||||
sam.PORT.PINCFG1_4.Set(sam.PORT.PINCFG1_4.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 40: // PB08
|
||||
sam.PORT.PINCFG1_8.Set(sam.PORT.PINCFG1_8.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 41: // PB09
|
||||
sam.PORT.PINCFG1_8.Set(sam.PORT.PINCFG1_8.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 42: // PB10
|
||||
sam.PORT.PINCFG1_8.Set(sam.PORT.PINCFG1_8.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 43: // PB11
|
||||
sam.PORT.PINCFG1_8.Set(sam.PORT.PINCFG1_8.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 44: // PB12
|
||||
sam.PORT.PINCFG1_12.Set(sam.PORT.PINCFG1_12.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 45: // PB13
|
||||
sam.PORT.PINCFG1_12.Set(sam.PORT.PINCFG1_12.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 46: // PB14
|
||||
sam.PORT.PINCFG1_12.Set(sam.PORT.PINCFG1_12.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 47: // PB15
|
||||
sam.PORT.PINCFG1_12.Set(sam.PORT.PINCFG1_12.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 48: // PB16
|
||||
sam.PORT.PINCFG1_16.Set(sam.PORT.PINCFG1_16.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 49: // PB17
|
||||
sam.PORT.PINCFG1_16.Set(sam.PORT.PINCFG1_16.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 50: // PB18
|
||||
sam.PORT.PINCFG1_16.Set(sam.PORT.PINCFG1_16.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 51: // PB19
|
||||
sam.PORT.PINCFG1_16.Set(sam.PORT.PINCFG1_16.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 52: // PB20
|
||||
sam.PORT.PINCFG1_20.Set(sam.PORT.PINCFG1_20.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 53: // PB21
|
||||
sam.PORT.PINCFG1_20.Set(sam.PORT.PINCFG1_20.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 54: // PB22
|
||||
sam.PORT.PINCFG1_20.Set(sam.PORT.PINCFG1_20.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 55: // PB23
|
||||
sam.PORT.PINCFG1_20.Set(sam.PORT.PINCFG1_20.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 56: // PB24
|
||||
sam.PORT.PINCFG1_24.Set(sam.PORT.PINCFG1_24.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 57: // PB25
|
||||
sam.PORT.PINCFG1_24.Set(sam.PORT.PINCFG1_24.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 58: // PB26
|
||||
sam.PORT.PINCFG1_24.Set(sam.PORT.PINCFG1_24.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 59: // PB27
|
||||
sam.PORT.PINCFG1_24.Set(sam.PORT.PINCFG1_24.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 60: // PB28
|
||||
sam.PORT.PINCFG1_28.Set(sam.PORT.PINCFG1_28.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 61: // PB29
|
||||
sam.PORT.PINCFG1_28.Set(sam.PORT.PINCFG1_28.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 62: // PB30
|
||||
sam.PORT.PINCFG1_28.Set(sam.PORT.PINCFG1_28.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 63: // PB31
|
||||
sam.PORT.PINCFG1_28.Set(sam.PORT.PINCFG1_28.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
switch {
|
||||
case p < 32:
|
||||
sam.PORT.GROUP[0].PINCFG[p].Set(val)
|
||||
case p >= 32 && p <= 64:
|
||||
sam.PORT.GROUP[1].PINCFG[p-32].Set(val)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,13 @@ func (p Pin) Set(high bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the current value of a GPIO pin.
|
||||
func (p Pin) Get() bool {
|
||||
val := sifive.GPIO0.VALUE.Get() & (1 << uint8(p))
|
||||
println(sifive.GPIO0.VALUE.Get())
|
||||
return (val > 0)
|
||||
}
|
||||
|
||||
type UART struct {
|
||||
Bus *sifive.UART_Type
|
||||
Buffer *RingBuffer
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrTxSlicesRequired = errors.New("SPI Tx requires a write or read slice, or both")
|
||||
ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size")
|
||||
)
|
||||
|
||||
@@ -348,10 +347,6 @@ func (spi SPI) Transfer(w byte) (byte, error) {
|
||||
// spi.Tx(nil, rx)
|
||||
//
|
||||
func (spi SPI) Tx(w, r []byte) error {
|
||||
if w == nil && r == nil {
|
||||
return ErrTxSlicesRequired
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
switch {
|
||||
|
||||
@@ -5,7 +5,6 @@ package machine
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrTxSlicesRequired = errors.New("SPI Tx requires a write or read slice, or both")
|
||||
ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size")
|
||||
)
|
||||
|
||||
@@ -28,10 +27,6 @@ var (
|
||||
// spi.Tx(nil, rx)
|
||||
//
|
||||
func (spi SPI) Tx(w, r []byte) error {
|
||||
if w == nil && r == nil {
|
||||
return ErrTxSlicesRequired
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
switch {
|
||||
|
||||
+88
-84
@@ -4,7 +4,6 @@ package machine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"runtime/volatile"
|
||||
)
|
||||
@@ -41,22 +40,26 @@ func NewDeviceDescriptor(class, subClass, proto, packetSize0 uint8, vid, pid, ve
|
||||
|
||||
// Bytes returns DeviceDescriptor data
|
||||
func (d DeviceDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, deviceDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.bcdUSB)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDeviceClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDeviceSubClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDeviceProtocol)
|
||||
binary.Write(buf, binary.LittleEndian, d.bMaxPacketSize0)
|
||||
binary.Write(buf, binary.LittleEndian, d.idVendor)
|
||||
binary.Write(buf, binary.LittleEndian, d.idProduct)
|
||||
binary.Write(buf, binary.LittleEndian, d.bcdDevice)
|
||||
binary.Write(buf, binary.LittleEndian, d.iManufacturer)
|
||||
binary.Write(buf, binary.LittleEndian, d.iProduct)
|
||||
binary.Write(buf, binary.LittleEndian, d.iSerialNumber)
|
||||
binary.Write(buf, binary.LittleEndian, d.bNumConfigurations)
|
||||
return buf.Bytes()
|
||||
b := make([]byte, deviceDescriptorSize)
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bcdUSB)
|
||||
b[3] = byte(d.bcdUSB >> 8)
|
||||
b[4] = byte(d.bDeviceClass)
|
||||
b[5] = byte(d.bDeviceSubClass)
|
||||
b[6] = byte(d.bDeviceProtocol)
|
||||
b[7] = byte(d.bMaxPacketSize0)
|
||||
b[8] = byte(d.idVendor)
|
||||
b[9] = byte(d.idVendor >> 8)
|
||||
b[10] = byte(d.idProduct)
|
||||
b[11] = byte(d.idProduct >> 8)
|
||||
b[12] = byte(d.bcdDevice)
|
||||
b[13] = byte(d.bcdDevice >> 8)
|
||||
b[14] = byte(d.iManufacturer)
|
||||
b[15] = byte(d.iProduct)
|
||||
b[16] = byte(d.iSerialNumber)
|
||||
b[17] = byte(d.bNumConfigurations)
|
||||
return b
|
||||
}
|
||||
|
||||
const configDescriptorSize = 9
|
||||
@@ -85,16 +88,17 @@ func NewConfigDescriptor(totalLength uint16, interfaces uint8) ConfigDescriptor
|
||||
|
||||
// Bytes returns ConfigDescriptor data.
|
||||
func (d ConfigDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, configDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.wTotalLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bNumInterfaces)
|
||||
binary.Write(buf, binary.LittleEndian, d.bConfigurationValue)
|
||||
binary.Write(buf, binary.LittleEndian, d.iConfiguration)
|
||||
binary.Write(buf, binary.LittleEndian, d.bmAttributes)
|
||||
binary.Write(buf, binary.LittleEndian, d.bMaxPower)
|
||||
return buf.Bytes()
|
||||
b := make([]byte, configDescriptorSize)
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.wTotalLength)
|
||||
b[3] = byte(d.wTotalLength >> 8)
|
||||
b[4] = byte(d.bNumInterfaces)
|
||||
b[5] = byte(d.bConfigurationValue)
|
||||
b[6] = byte(d.iConfiguration)
|
||||
b[7] = byte(d.bmAttributes)
|
||||
b[8] = byte(d.bMaxPower)
|
||||
return b
|
||||
}
|
||||
|
||||
const interfaceDescriptorSize = 9
|
||||
@@ -124,17 +128,17 @@ func NewInterfaceDescriptor(n, numEndpoints, class, subClass, protocol uint8) In
|
||||
|
||||
// Bytes returns InterfaceDescriptor data.
|
||||
func (d InterfaceDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, interfaceDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterfaceNumber)
|
||||
binary.Write(buf, binary.LittleEndian, d.bAlternateSetting)
|
||||
binary.Write(buf, binary.LittleEndian, d.bNumEndpoints)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterfaceClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterfaceSubClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterfaceProtocol)
|
||||
binary.Write(buf, binary.LittleEndian, d.iInterface)
|
||||
return buf.Bytes()
|
||||
b := make([]byte, interfaceDescriptorSize)
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bInterfaceNumber)
|
||||
b[3] = byte(d.bAlternateSetting)
|
||||
b[4] = byte(d.bNumEndpoints)
|
||||
b[5] = byte(d.bInterfaceClass)
|
||||
b[6] = byte(d.bInterfaceSubClass)
|
||||
b[7] = byte(d.bInterfaceProtocol)
|
||||
b[8] = byte(d.iInterface)
|
||||
return b
|
||||
}
|
||||
|
||||
const endpointDescriptorSize = 7
|
||||
@@ -160,14 +164,15 @@ func NewEndpointDescriptor(addr, attr uint8, packetSize uint16, interval uint8)
|
||||
|
||||
// Bytes returns EndpointDescriptor data.
|
||||
func (d EndpointDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, endpointDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.bEndpointAddress)
|
||||
binary.Write(buf, binary.LittleEndian, d.bmAttributes)
|
||||
binary.Write(buf, binary.LittleEndian, d.wMaxPacketSize)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterval)
|
||||
return buf.Bytes()
|
||||
b := make([]byte, endpointDescriptorSize)
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bEndpointAddress)
|
||||
b[3] = byte(d.bmAttributes)
|
||||
b[4] = byte(d.wMaxPacketSize)
|
||||
b[5] = byte(d.wMaxPacketSize >> 8)
|
||||
b[6] = byte(d.bInterval)
|
||||
return b
|
||||
}
|
||||
|
||||
const iadDescriptorSize = 8
|
||||
@@ -197,16 +202,16 @@ func NewIADDescriptor(firstInterface, count, class, subClass, protocol uint8) IA
|
||||
|
||||
// Bytes returns IADDescriptor data.
|
||||
func (d IADDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, iadDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.bFirstInterface)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterfaceCount)
|
||||
binary.Write(buf, binary.LittleEndian, d.bFunctionClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bFunctionSubClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bFunctionProtocol)
|
||||
binary.Write(buf, binary.LittleEndian, d.iFunction)
|
||||
return buf.Bytes()
|
||||
b := make([]byte, iadDescriptorSize)
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bFirstInterface)
|
||||
b[3] = byte(d.bInterfaceCount)
|
||||
b[4] = byte(d.bFunctionClass)
|
||||
b[5] = byte(d.bFunctionSubClass)
|
||||
b[6] = byte(d.bFunctionProtocol)
|
||||
b[7] = byte(d.iFunction)
|
||||
return b
|
||||
}
|
||||
|
||||
const cdcCSInterfaceDescriptorSize = 5
|
||||
@@ -227,13 +232,13 @@ func NewCDCCSInterfaceDescriptor(subtype, d0, d1 uint8) CDCCSInterfaceDescriptor
|
||||
|
||||
// Bytes returns CDCCSInterfaceDescriptor data.
|
||||
func (d CDCCSInterfaceDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, cdcCSInterfaceDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.len)
|
||||
binary.Write(buf, binary.LittleEndian, d.dtype)
|
||||
binary.Write(buf, binary.LittleEndian, d.subtype)
|
||||
binary.Write(buf, binary.LittleEndian, d.d0)
|
||||
binary.Write(buf, binary.LittleEndian, d.d1)
|
||||
return buf.Bytes()
|
||||
b := make([]byte, cdcCSInterfaceDescriptorSize)
|
||||
b[0] = byte(d.len)
|
||||
b[1] = byte(d.dtype)
|
||||
b[2] = byte(d.subtype)
|
||||
b[3] = byte(d.d0)
|
||||
b[4] = byte(d.d1)
|
||||
return b
|
||||
}
|
||||
|
||||
const cmFunctionalDescriptorSize = 5
|
||||
@@ -254,13 +259,13 @@ func NewCMFunctionalDescriptor(subtype, d0, d1 uint8) CMFunctionalDescriptor {
|
||||
|
||||
// Bytes returns the CMFunctionalDescriptor data.
|
||||
func (d CMFunctionalDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, cmFunctionalDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bFunctionLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorSubtype)
|
||||
binary.Write(buf, binary.LittleEndian, d.bmCapabilities)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDataInterface)
|
||||
return buf.Bytes()
|
||||
b := make([]byte, cmFunctionalDescriptorSize)
|
||||
b[0] = byte(d.bFunctionLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bDescriptorSubtype)
|
||||
b[3] = byte(d.bmCapabilities)
|
||||
b[4] = byte(d.bDescriptorSubtype)
|
||||
return b
|
||||
}
|
||||
|
||||
const acmFunctionalDescriptorSize = 4
|
||||
@@ -280,12 +285,12 @@ func NewACMFunctionalDescriptor(subtype, d0 uint8) ACMFunctionalDescriptor {
|
||||
|
||||
// Bytes returns the ACMFunctionalDescriptor data.
|
||||
func (d ACMFunctionalDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, acmFunctionalDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.len)
|
||||
binary.Write(buf, binary.LittleEndian, d.dtype)
|
||||
binary.Write(buf, binary.LittleEndian, d.subtype)
|
||||
binary.Write(buf, binary.LittleEndian, d.bmCapabilities)
|
||||
return buf.Bytes()
|
||||
b := make([]byte, acmFunctionalDescriptorSize)
|
||||
b[0] = byte(d.len)
|
||||
b[1] = byte(d.dtype)
|
||||
b[2] = byte(d.subtype)
|
||||
b[3] = byte(d.bmCapabilities)
|
||||
return b
|
||||
}
|
||||
|
||||
// CDCDescriptor is the Communication Device Class (CDC) descriptor.
|
||||
@@ -343,7 +348,7 @@ const cdcSize = iadDescriptorSize +
|
||||
|
||||
// Bytes returns CDCDescriptor data.
|
||||
func (d CDCDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, cdcSize))
|
||||
buf := bytes.NewBuffer(make([]byte, 0))
|
||||
buf.Write(d.iad.Bytes())
|
||||
buf.Write(d.cif.Bytes())
|
||||
buf.Write(d.header.Bytes())
|
||||
@@ -523,14 +528,13 @@ type usbSetup struct {
|
||||
}
|
||||
|
||||
func newUSBSetup(data []byte) usbSetup {
|
||||
buf := bytes.NewBuffer(data)
|
||||
u := usbSetup{}
|
||||
binary.Read(buf, binary.LittleEndian, &(u.bmRequestType))
|
||||
binary.Read(buf, binary.LittleEndian, &(u.bRequest))
|
||||
binary.Read(buf, binary.LittleEndian, &(u.wValueL))
|
||||
binary.Read(buf, binary.LittleEndian, &(u.wValueH))
|
||||
binary.Read(buf, binary.LittleEndian, &(u.wIndex))
|
||||
binary.Read(buf, binary.LittleEndian, &(u.wLength))
|
||||
u.bmRequestType = uint8(data[0])
|
||||
u.bRequest = uint8(data[1])
|
||||
u.wValueL = uint8(data[2])
|
||||
u.wValueH = uint8(data[3])
|
||||
u.wIndex = uint16(data[4]) | uint16(data[5]<<8)
|
||||
u.wLength = uint16(data[6]) | uint16(data[7]<<8)
|
||||
return u
|
||||
}
|
||||
|
||||
|
||||
+9
-2
@@ -318,7 +318,7 @@ func (t Type) Size() uintptr {
|
||||
case Slice:
|
||||
return unsafe.Sizeof(SliceHeader{})
|
||||
case Interface:
|
||||
return unsafe.Sizeof(interfaceHeader{})
|
||||
return unsafe.Sizeof(interface{}(nil))
|
||||
case Array:
|
||||
return t.Elem().Size() * uintptr(t.Len())
|
||||
case Struct:
|
||||
@@ -364,7 +364,7 @@ func (t Type) Align() int {
|
||||
case Slice:
|
||||
return int(unsafe.Alignof(SliceHeader{}))
|
||||
case Interface:
|
||||
return int(unsafe.Alignof(interfaceHeader{}))
|
||||
return int(unsafe.Alignof(interface{}(nil)))
|
||||
case Struct:
|
||||
numField := t.NumField()
|
||||
alignment := 1
|
||||
@@ -398,6 +398,13 @@ func (t Type) AssignableTo(u Type) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (t Type) Implements(u Type) bool {
|
||||
if t.Kind() != Interface {
|
||||
panic("reflect: non-interface type passed to Type.Implements")
|
||||
}
|
||||
return u.AssignableTo(t)
|
||||
}
|
||||
|
||||
// Comparable returns whether values of this type can be compared to each other.
|
||||
func (t Type) Comparable() bool {
|
||||
switch t.Kind() {
|
||||
|
||||
+13
-17
@@ -35,20 +35,22 @@ func Indirect(v Value) Value {
|
||||
return v.Elem()
|
||||
}
|
||||
|
||||
//go:linkname composeInterface runtime.composeInterface
|
||||
func composeInterface(Type, unsafe.Pointer) interface{}
|
||||
|
||||
//go:linkname decomposeInterface runtime.decomposeInterface
|
||||
func decomposeInterface(i interface{}) (Type, unsafe.Pointer)
|
||||
|
||||
func ValueOf(i interface{}) Value {
|
||||
v := (*interfaceHeader)(unsafe.Pointer(&i))
|
||||
typecode, value := decomposeInterface(i)
|
||||
return Value{
|
||||
typecode: v.typecode,
|
||||
value: v.value,
|
||||
typecode: typecode,
|
||||
value: value,
|
||||
flags: valueFlagExported,
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) Interface() interface{} {
|
||||
i := interfaceHeader{
|
||||
typecode: v.typecode,
|
||||
value: v.value,
|
||||
}
|
||||
if v.isIndirect() && v.Type().Size() <= unsafe.Sizeof(uintptr(0)) {
|
||||
// Value was indirect but must be put back directly in the interface
|
||||
// value.
|
||||
@@ -56,9 +58,9 @@ func (v Value) Interface() interface{} {
|
||||
for j := v.Type().Size(); j != 0; j-- {
|
||||
value = (value << 8) | uintptr(*(*uint8)(unsafe.Pointer(uintptr(v.value) + j - 1)))
|
||||
}
|
||||
i.value = unsafe.Pointer(value)
|
||||
v.value = unsafe.Pointer(value)
|
||||
}
|
||||
return *(*interface{})(unsafe.Pointer(&i))
|
||||
return composeInterface(v.typecode, v.value)
|
||||
}
|
||||
|
||||
func (v Value) Type() Type {
|
||||
@@ -94,8 +96,8 @@ func (v Value) IsNil() bool {
|
||||
if v.value == nil {
|
||||
return true
|
||||
}
|
||||
itf := (*interfaceHeader)(v.value)
|
||||
return itf.value == nil
|
||||
_, val := decomposeInterface(*(*interface{})(v.value))
|
||||
return val == nil
|
||||
default:
|
||||
panic(&ValueError{"IsNil"})
|
||||
}
|
||||
@@ -657,12 +659,6 @@ type funcHeader struct {
|
||||
Code unsafe.Pointer
|
||||
}
|
||||
|
||||
// This is the same thing as an interface{}.
|
||||
type interfaceHeader struct {
|
||||
typecode Type
|
||||
value unsafe.Pointer
|
||||
}
|
||||
|
||||
type SliceHeader struct {
|
||||
Data uintptr
|
||||
Len uintptr
|
||||
|
||||
+295
-126
@@ -27,21 +27,245 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func chanDebug(ch *channel) {
|
||||
if schedulerDebug {
|
||||
if ch.bufSize > 0 {
|
||||
println("--- channel update:", ch, ch.state.String(), ch.bufSize, ch.bufUsed)
|
||||
} else {
|
||||
println("--- channel update:", ch, ch.state.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type channel struct {
|
||||
elementSize uint16 // the size of one value in this channel
|
||||
elementSize uintptr // the size of one value in this channel
|
||||
bufSize uintptr // size of buffer (in elements)
|
||||
state chanState
|
||||
blocked *task
|
||||
bufHead uintptr // head index of buffer (next push index)
|
||||
bufTail uintptr // tail index of buffer (next pop index)
|
||||
bufUsed uintptr // number of elements currently in buffer
|
||||
buf unsafe.Pointer // pointer to first element of buffer
|
||||
}
|
||||
|
||||
// chanMake creates a new channel with the given element size and buffer length in number of elements.
|
||||
// This is a compiler intrinsic.
|
||||
func chanMake(elementSize uintptr, bufSize uintptr) *channel {
|
||||
return &channel{
|
||||
elementSize: elementSize,
|
||||
bufSize: bufSize,
|
||||
buf: alloc(elementSize * bufSize),
|
||||
}
|
||||
}
|
||||
|
||||
// push value to end of channel if space is available
|
||||
// returns whether there was space for the value in the buffer
|
||||
func (ch *channel) push(value unsafe.Pointer) bool {
|
||||
// immediately return false if the channel is not buffered
|
||||
if ch.bufSize == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// ensure space is available
|
||||
if ch.bufUsed == ch.bufSize {
|
||||
return false
|
||||
}
|
||||
|
||||
// copy value to buffer
|
||||
memcpy(
|
||||
unsafe.Pointer( // pointer to the base of the buffer + offset = pointer to destination element
|
||||
uintptr(ch.buf)+
|
||||
uintptr( // element size * equivalent slice index = offset
|
||||
ch.elementSize* // element size (bytes)
|
||||
ch.bufHead, // index of first available buffer entry
|
||||
),
|
||||
),
|
||||
value,
|
||||
ch.elementSize,
|
||||
)
|
||||
|
||||
// update buffer state
|
||||
ch.bufUsed++
|
||||
ch.bufHead++
|
||||
if ch.bufHead == ch.bufSize {
|
||||
ch.bufHead = 0
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// pop value from channel buffer if one is available
|
||||
// returns whether a value was popped or not
|
||||
// result is stored into value pointer
|
||||
func (ch *channel) pop(value unsafe.Pointer) bool {
|
||||
// channel is empty
|
||||
if ch.bufUsed == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// compute address of source
|
||||
addr := unsafe.Pointer(uintptr(ch.buf) + (ch.elementSize * ch.bufTail))
|
||||
|
||||
// copy value from buffer
|
||||
memcpy(
|
||||
value,
|
||||
addr,
|
||||
ch.elementSize,
|
||||
)
|
||||
|
||||
// zero buffer element to allow garbage collection of value
|
||||
memzero(
|
||||
addr,
|
||||
ch.elementSize,
|
||||
)
|
||||
|
||||
// update buffer state
|
||||
ch.bufUsed--
|
||||
|
||||
// move tail up
|
||||
ch.bufTail++
|
||||
if ch.bufTail == ch.bufSize {
|
||||
ch.bufTail = 0
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// try to send a value to a channel, without actually blocking
|
||||
// returns whether the value was sent
|
||||
// will panic if channel is closed
|
||||
func (ch *channel) trySend(value unsafe.Pointer) bool {
|
||||
if ch == nil {
|
||||
// send to nil channel blocks forever
|
||||
// this is non-blocking, so just say no
|
||||
return false
|
||||
}
|
||||
|
||||
switch ch.state {
|
||||
case chanStateEmpty, chanStateBuf:
|
||||
// try to dump the value directly into the buffer
|
||||
if ch.push(value) {
|
||||
ch.state = chanStateBuf
|
||||
return true
|
||||
}
|
||||
return false
|
||||
case chanStateRecv:
|
||||
// unblock reciever
|
||||
receiver := unblockChain(&ch.blocked, nil)
|
||||
|
||||
// copy value to reciever
|
||||
receiverState := receiver.state()
|
||||
memcpy(receiverState.ptr, value, ch.elementSize)
|
||||
receiverState.data = 1 // commaOk = true
|
||||
|
||||
// change state to empty if there are no more receivers
|
||||
if ch.blocked == nil {
|
||||
ch.state = chanStateEmpty
|
||||
}
|
||||
|
||||
return true
|
||||
case chanStateSend:
|
||||
// something else is already waiting to send
|
||||
return false
|
||||
case chanStateClosed:
|
||||
runtimePanic("send on closed channel")
|
||||
default:
|
||||
runtimePanic("invalid channel state")
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// try to recieve a value from a channel, without really blocking
|
||||
// returns whether a value was recieved
|
||||
// second return is the comma-ok value
|
||||
func (ch *channel) tryRecv(value unsafe.Pointer) (bool, bool) {
|
||||
if ch == nil {
|
||||
// recieve from nil channel blocks forever
|
||||
// this is non-blocking, so just say no
|
||||
return false, false
|
||||
}
|
||||
|
||||
switch ch.state {
|
||||
case chanStateBuf, chanStateSend:
|
||||
// try to pop the value directly from the buffer
|
||||
if ch.pop(value) {
|
||||
// unblock next sender if applicable
|
||||
if sender := unblockChain(&ch.blocked, nil); sender != nil {
|
||||
// push sender's value into buffer
|
||||
ch.push(sender.state().ptr)
|
||||
|
||||
if ch.blocked == nil {
|
||||
// last sender unblocked - update state
|
||||
ch.state = chanStateBuf
|
||||
}
|
||||
}
|
||||
|
||||
if ch.bufUsed == 0 {
|
||||
// channel empty - update state
|
||||
ch.state = chanStateEmpty
|
||||
}
|
||||
|
||||
return true, true
|
||||
} else if sender := unblockChain(&ch.blocked, nil); sender != nil {
|
||||
// unblock next sender if applicable
|
||||
// copy sender's value
|
||||
memcpy(value, sender.state().ptr, ch.elementSize)
|
||||
|
||||
if ch.blocked == nil {
|
||||
// last sender unblocked - update state
|
||||
ch.state = chanStateEmpty
|
||||
}
|
||||
|
||||
return true, true
|
||||
}
|
||||
return false, false
|
||||
case chanStateRecv, chanStateEmpty:
|
||||
// something else is already waiting to recieve
|
||||
return false, false
|
||||
case chanStateClosed:
|
||||
if ch.pop(value) {
|
||||
return true, true
|
||||
}
|
||||
|
||||
// channel closed - nothing to recieve
|
||||
memzero(value, ch.elementSize)
|
||||
return true, false
|
||||
default:
|
||||
runtimePanic("invalid channel state")
|
||||
}
|
||||
|
||||
runtimePanic("unreachable")
|
||||
return false, false
|
||||
}
|
||||
|
||||
type chanState uint8
|
||||
|
||||
const (
|
||||
chanStateEmpty chanState = iota
|
||||
chanStateRecv
|
||||
chanStateSend
|
||||
chanStateClosed
|
||||
chanStateEmpty chanState = iota // nothing in channel, no senders/recievers
|
||||
chanStateRecv // nothing in channel, recievers waiting
|
||||
chanStateSend // senders waiting, buffer full if present
|
||||
chanStateBuf // buffer not empty, no senders waiting
|
||||
chanStateClosed // channel closed
|
||||
)
|
||||
|
||||
func (s chanState) String() string {
|
||||
switch s {
|
||||
case chanStateEmpty:
|
||||
return "empty"
|
||||
case chanStateRecv:
|
||||
return "recv"
|
||||
case chanStateSend:
|
||||
return "send"
|
||||
case chanStateBuf:
|
||||
return "buffered"
|
||||
case chanStateClosed:
|
||||
return "closed"
|
||||
default:
|
||||
return "invalid"
|
||||
}
|
||||
}
|
||||
|
||||
// chanSelectState is a single channel operation (send/recv) in a select
|
||||
// statement. The value pointer is either nil (for receives) or points to the
|
||||
// value to send (for sends).
|
||||
@@ -50,89 +274,59 @@ type chanSelectState struct {
|
||||
value unsafe.Pointer
|
||||
}
|
||||
|
||||
// chanSend sends a single value over the channel. If this operation can
|
||||
// complete immediately (there is a goroutine waiting for a value), it sends the
|
||||
// value and re-activates both goroutines. If not, it sets itself as waiting on
|
||||
// a value.
|
||||
func chanSend(sender *task, ch *channel, value unsafe.Pointer) {
|
||||
if ch == nil {
|
||||
// A nil channel blocks forever. Do not scheduler this goroutine again.
|
||||
chanYield()
|
||||
// chanSend sends a single value over the channel.
|
||||
// This operation will block unless a value is immediately available.
|
||||
// May panic if the channel is closed.
|
||||
func chanSend(ch *channel, value unsafe.Pointer) {
|
||||
if ch.trySend(value) {
|
||||
// value immediately sent
|
||||
chanDebug(ch)
|
||||
return
|
||||
}
|
||||
switch ch.state {
|
||||
case chanStateEmpty:
|
||||
scheduleLogChan(" send: chan is empty ", ch, sender)
|
||||
sender.state().ptr = value
|
||||
ch.state = chanStateSend
|
||||
ch.blocked = sender
|
||||
chanYield()
|
||||
case chanStateRecv:
|
||||
scheduleLogChan(" send: chan in recv mode", ch, sender)
|
||||
receiver := ch.blocked
|
||||
receiverState := receiver.state()
|
||||
memcpy(receiverState.ptr, value, uintptr(ch.elementSize))
|
||||
receiverState.data = 1 // commaOk = true
|
||||
ch.blocked = receiverState.next
|
||||
receiverState.next = nil
|
||||
activateTask(receiver)
|
||||
reactivateParent(sender)
|
||||
if ch.blocked == nil {
|
||||
ch.state = chanStateEmpty
|
||||
}
|
||||
case chanStateClosed:
|
||||
runtimePanic("send on closed channel")
|
||||
case chanStateSend:
|
||||
scheduleLogChan(" send: chan in send mode", ch, sender)
|
||||
sender.state().ptr = value
|
||||
sender.state().next = ch.blocked
|
||||
ch.blocked = sender
|
||||
chanYield()
|
||||
|
||||
if ch == nil {
|
||||
// A nil channel blocks forever. Do not schedule this goroutine again.
|
||||
deadlock()
|
||||
}
|
||||
|
||||
// wait for reciever
|
||||
sender := getCoroutine()
|
||||
ch.state = chanStateSend
|
||||
senderState := sender.state()
|
||||
senderState.ptr = value
|
||||
ch.blocked, senderState.next = sender, ch.blocked
|
||||
chanDebug(ch)
|
||||
yield()
|
||||
senderState.ptr = nil
|
||||
}
|
||||
|
||||
// chanRecv receives a single value over a channel. If there is an available
|
||||
// sender, it receives the value immediately and re-activates both coroutines.
|
||||
// If not, it sets itself as available for receiving. If the channel is closed,
|
||||
// it immediately activates itself with a zero value as the result.
|
||||
func chanRecv(receiver *task, ch *channel, value unsafe.Pointer) {
|
||||
// chanRecv receives a single value over a channel.
|
||||
// It blocks if there is no available value to recieve.
|
||||
// The recieved value is copied into the value pointer.
|
||||
// Returns the comma-ok value.
|
||||
func chanRecv(ch *channel, value unsafe.Pointer) bool {
|
||||
if rx, ok := ch.tryRecv(value); rx {
|
||||
// value immediately available
|
||||
chanDebug(ch)
|
||||
return ok
|
||||
}
|
||||
|
||||
if ch == nil {
|
||||
// A nil channel blocks forever. Do not scheduler this goroutine again.
|
||||
chanYield()
|
||||
return
|
||||
}
|
||||
switch ch.state {
|
||||
case chanStateSend:
|
||||
scheduleLogChan(" recv: chan in send mode", ch, receiver)
|
||||
sender := ch.blocked
|
||||
senderState := sender.state()
|
||||
memcpy(value, senderState.ptr, uintptr(ch.elementSize))
|
||||
receiver.state().data = 1 // commaOk = true
|
||||
ch.blocked = senderState.next
|
||||
senderState.next = nil
|
||||
reactivateParent(receiver)
|
||||
activateTask(sender)
|
||||
if ch.blocked == nil {
|
||||
ch.state = chanStateEmpty
|
||||
}
|
||||
case chanStateEmpty:
|
||||
scheduleLogChan(" recv: chan is empty ", ch, receiver)
|
||||
receiver.state().ptr = value
|
||||
ch.state = chanStateRecv
|
||||
ch.blocked = receiver
|
||||
chanYield()
|
||||
case chanStateClosed:
|
||||
scheduleLogChan(" recv: chan is closed ", ch, receiver)
|
||||
memzero(value, uintptr(ch.elementSize))
|
||||
receiver.state().data = 0 // commaOk = false
|
||||
reactivateParent(receiver)
|
||||
case chanStateRecv:
|
||||
scheduleLogChan(" recv: chan in recv mode", ch, receiver)
|
||||
receiver.state().ptr = value
|
||||
receiver.state().next = ch.blocked
|
||||
ch.blocked = receiver
|
||||
chanYield()
|
||||
// A nil channel blocks forever. Do not schedule this goroutine again.
|
||||
deadlock()
|
||||
}
|
||||
|
||||
// wait for a value
|
||||
receiver := getCoroutine()
|
||||
ch.state = chanStateRecv
|
||||
receiverState := receiver.state()
|
||||
receiverState.ptr, receiverState.data = value, 0
|
||||
ch.blocked, receiverState.next = receiver, ch.blocked
|
||||
chanDebug(ch)
|
||||
yield()
|
||||
ok := receiverState.data == 1
|
||||
receiverState.ptr, receiverState.data = nil, 0
|
||||
return ok
|
||||
}
|
||||
|
||||
// chanClose closes the given channel. If this channel has a receiver or is
|
||||
@@ -153,17 +347,22 @@ func chanClose(ch *channel) {
|
||||
// before the close.
|
||||
runtimePanic("close channel during send")
|
||||
case chanStateRecv:
|
||||
// The receiver must be re-activated with a zero value.
|
||||
receiverState := ch.blocked.state()
|
||||
memzero(receiverState.ptr, uintptr(ch.elementSize))
|
||||
receiverState.data = 0 // commaOk = false
|
||||
activateTask(ch.blocked)
|
||||
ch.state = chanStateClosed
|
||||
ch.blocked = nil
|
||||
case chanStateEmpty:
|
||||
// unblock all receivers with the zero value
|
||||
for rx := unblockChain(&ch.blocked, nil); rx != nil; rx = unblockChain(&ch.blocked, nil) {
|
||||
// get receiver state
|
||||
state := rx.state()
|
||||
|
||||
// store the zero value
|
||||
memzero(state.ptr, ch.elementSize)
|
||||
|
||||
// set the comma-ok value to false (channel closed)
|
||||
state.data = 0
|
||||
}
|
||||
case chanStateEmpty, chanStateBuf:
|
||||
// Easy case. No available sender or receiver.
|
||||
ch.state = chanStateClosed
|
||||
}
|
||||
ch.state = chanStateClosed
|
||||
chanDebug(ch)
|
||||
}
|
||||
|
||||
// chanSelect is the runtime implementation of the select statement. This is
|
||||
@@ -175,47 +374,17 @@ func chanClose(ch *channel) {
|
||||
func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, blocking bool) (uintptr, bool) {
|
||||
// See whether we can receive from one of the channels.
|
||||
for i, state := range states {
|
||||
if state.ch == nil {
|
||||
// A nil channel blocks forever, so don't consider it here.
|
||||
continue
|
||||
}
|
||||
if state.value == nil {
|
||||
// A receive operation.
|
||||
switch state.ch.state {
|
||||
case chanStateSend:
|
||||
// We can receive immediately.
|
||||
sender := state.ch.blocked
|
||||
senderState := sender.state()
|
||||
memcpy(recvbuf, senderState.ptr, uintptr(state.ch.elementSize))
|
||||
state.ch.blocked = senderState.next
|
||||
senderState.next = nil
|
||||
activateTask(sender)
|
||||
if state.ch.blocked == nil {
|
||||
state.ch.state = chanStateEmpty
|
||||
}
|
||||
return uintptr(i), true // commaOk = true
|
||||
case chanStateClosed:
|
||||
// Receive the zero value.
|
||||
memzero(recvbuf, uintptr(state.ch.elementSize))
|
||||
return uintptr(i), false // commaOk = false
|
||||
if rx, ok := state.ch.tryRecv(recvbuf); rx {
|
||||
chanDebug(state.ch)
|
||||
return uintptr(i), ok
|
||||
}
|
||||
} else {
|
||||
// A send operation: state.value is not nil.
|
||||
switch state.ch.state {
|
||||
case chanStateRecv:
|
||||
receiver := state.ch.blocked
|
||||
receiverState := receiver.state()
|
||||
memcpy(receiverState.ptr, state.value, uintptr(state.ch.elementSize))
|
||||
receiverState.data = 1 // commaOk = true
|
||||
state.ch.blocked = receiverState.next
|
||||
receiverState.next = nil
|
||||
activateTask(receiver)
|
||||
if state.ch.blocked == nil {
|
||||
state.ch.state = chanStateEmpty
|
||||
}
|
||||
return uintptr(i), false
|
||||
case chanStateClosed:
|
||||
runtimePanic("send on closed channel")
|
||||
if state.ch.trySend(state.value) {
|
||||
chanDebug(state.ch)
|
||||
return uintptr(i), true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,16 @@ type _interface struct {
|
||||
value unsafe.Pointer
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func composeInterface(typecode uintptr, value unsafe.Pointer) _interface {
|
||||
return _interface{typecode, value}
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func decomposeInterface(i _interface) (uintptr, unsafe.Pointer) {
|
||||
return i.typecode, i.value
|
||||
}
|
||||
|
||||
// Return true iff both interfaces are equal.
|
||||
func interfaceEqual(x, y _interface) bool {
|
||||
if x.typecode != y.typecode {
|
||||
|
||||
@@ -44,15 +44,15 @@ func initClocks() {
|
||||
|
||||
// Set OSCULP32K as source of Generic Clock Generator 3
|
||||
// GCLK->GENCTRL[GENERIC_CLOCK_GENERATOR_XOSC32K].reg = GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_OSCULP32K) | GCLK_GENCTRL_GENEN; //generic clock gen 3
|
||||
sam.GCLK.GENCTRL3.Set((sam.GCLK_GENCTRL_SRC_OSCULP32K << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
sam.GCLK.GENCTRL[3].Set((sam.GCLK_GENCTRL_SRC_OSCULP32K << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
sam.GCLK_GENCTRL_GENEN)
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL3) {
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK3) {
|
||||
}
|
||||
|
||||
// Set OSCULP32K as source of Generic Clock Generator 0
|
||||
sam.GCLK.GENCTRL0.Set((sam.GCLK_GENCTRL_SRC_OSCULP32K << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
sam.GCLK.GENCTRL[0].Set((sam.GCLK_GENCTRL_SRC_OSCULP32K << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
sam.GCLK_GENCTRL_GENEN)
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL0) {
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK0) {
|
||||
}
|
||||
|
||||
// Enable DFLL48M clock
|
||||
@@ -82,56 +82,56 @@ func initClocks() {
|
||||
}
|
||||
|
||||
// set GCLK7 to use DFLL48M as clock source
|
||||
sam.GCLK.GENCTRL7.Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
sam.GCLK.GENCTRL[7].Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
(24 << sam.GCLK_GENCTRL_DIVSEL_Pos) |
|
||||
sam.GCLK_GENCTRL_GENEN)
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL7) {
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK7) {
|
||||
}
|
||||
|
||||
// Set up the PLLs
|
||||
|
||||
// Set PLL0 at 120MHz
|
||||
sam.GCLK.PCHCTRL1.Set(sam.GCLK_PCHCTRL_CHEN |
|
||||
sam.GCLK.PCHCTRL[1].Set(sam.GCLK_PCHCTRL_CHEN |
|
||||
(sam.GCLK_PCHCTRL_GEN_GCLK7 << sam.GCLK_PCHCTRL_GEN_Pos))
|
||||
|
||||
sam.OSCCTRL.DPLLRATIO0.Set((0x0 << sam.OSCCTRL_DPLLRATIO_LDRFRAC_Pos) |
|
||||
(59 << sam.OSCCTRL_DPLLRATIO_LDR_Pos))
|
||||
for sam.OSCCTRL.DPLLSYNCBUSY0.HasBits(sam.OSCCTRL_DPLLSYNCBUSY_DPLLRATIO) {
|
||||
sam.OSCCTRL.DPLL[0].DPLLRATIO.Set((0x0 << sam.OSCCTRL_DPLL_DPLLRATIO_LDRFRAC_Pos) |
|
||||
(59 << sam.OSCCTRL_DPLL_DPLLRATIO_LDRFRAC_Pos))
|
||||
for sam.OSCCTRL.DPLL[0].DPLLSYNCBUSY.HasBits(sam.OSCCTRL_DPLL_DPLLSYNCBUSY_DPLLRATIO) {
|
||||
}
|
||||
|
||||
// MUST USE LBYPASS DUE TO BUG IN REV A OF SAMD51, via Adafruit lib.
|
||||
sam.OSCCTRL.DPLLCTRLB0.Set((sam.OSCCTRL_DPLLCTRLB_REFCLK_GCLK << sam.OSCCTRL_DPLLCTRLB_REFCLK_Pos) |
|
||||
sam.OSCCTRL_DPLLCTRLB_LBYPASS)
|
||||
sam.OSCCTRL.DPLL[0].DPLLCTRLB.Set((sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_GCLK << sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_Pos) |
|
||||
sam.OSCCTRL_DPLL_DPLLCTRLB_LBYPASS)
|
||||
|
||||
sam.OSCCTRL.DPLLCTRLA0.Set(sam.OSCCTRL_DPLLCTRLA_ENABLE)
|
||||
for !sam.OSCCTRL.DPLLSTATUS0.HasBits(sam.OSCCTRL_DPLLSTATUS_CLKRDY) ||
|
||||
!sam.OSCCTRL.DPLLSTATUS0.HasBits(sam.OSCCTRL_DPLLSTATUS_LOCK) {
|
||||
sam.OSCCTRL.DPLL[0].DPLLCTRLA.Set(sam.OSCCTRL_DPLL_DPLLCTRLA_ENABLE)
|
||||
for !sam.OSCCTRL.DPLL[0].DPLLSTATUS.HasBits(sam.OSCCTRL_DPLL_DPLLSTATUS_CLKRDY) ||
|
||||
!sam.OSCCTRL.DPLL[0].DPLLSTATUS.HasBits(sam.OSCCTRL_DPLL_DPLLSTATUS_LOCK) {
|
||||
}
|
||||
|
||||
// // Set PLL1 to 100MHz
|
||||
sam.GCLK.PCHCTRL2.Set(sam.GCLK_PCHCTRL_CHEN |
|
||||
sam.GCLK.PCHCTRL[2].Set(sam.GCLK_PCHCTRL_CHEN |
|
||||
(sam.GCLK_PCHCTRL_GEN_GCLK7 << sam.GCLK_PCHCTRL_GEN_Pos))
|
||||
|
||||
sam.OSCCTRL.DPLLRATIO1.Set((0x0 << sam.OSCCTRL_DPLLRATIO_LDRFRAC_Pos) |
|
||||
(49 << sam.OSCCTRL_DPLLRATIO_LDR_Pos)) // this means 100 Mhz?
|
||||
for sam.OSCCTRL.DPLLSYNCBUSY1.HasBits(sam.OSCCTRL_DPLLSYNCBUSY_DPLLRATIO) {
|
||||
sam.OSCCTRL.DPLL[1].DPLLRATIO.Set((0x0 << sam.OSCCTRL_DPLL_DPLLRATIO_LDRFRAC_Pos) |
|
||||
(49 << sam.OSCCTRL_DPLL_DPLLRATIO_LDR_Pos)) // this means 100 Mhz?
|
||||
for sam.OSCCTRL.DPLL[1].DPLLSYNCBUSY.HasBits(sam.OSCCTRL_DPLL_DPLLSYNCBUSY_DPLLRATIO) {
|
||||
}
|
||||
|
||||
// // MUST USE LBYPASS DUE TO BUG IN REV A OF SAMD51
|
||||
sam.OSCCTRL.DPLLCTRLB1.Set((sam.OSCCTRL_DPLLCTRLB_REFCLK_GCLK << sam.OSCCTRL_DPLLCTRLB_REFCLK_Pos) |
|
||||
sam.OSCCTRL_DPLLCTRLB_LBYPASS)
|
||||
sam.OSCCTRL.DPLL[1].DPLLCTRLB.Set((sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_GCLK << sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_Pos) |
|
||||
sam.OSCCTRL_DPLL_DPLLCTRLB_LBYPASS)
|
||||
|
||||
sam.OSCCTRL.DPLLCTRLA1.Set(sam.OSCCTRL_DPLLCTRLA_ENABLE)
|
||||
sam.OSCCTRL.DPLL[1].DPLLCTRLA.Set(sam.OSCCTRL_DPLL_DPLLCTRLA_ENABLE)
|
||||
// for !sam.OSCCTRL.DPLLSTATUS1.HasBits(sam.OSCCTRL_DPLLSTATUS_CLKRDY) ||
|
||||
// !sam.OSCCTRL.DPLLSTATUS1.HasBits(sam.OSCCTRL_DPLLSTATUS_LOCK) {
|
||||
// }
|
||||
|
||||
// Set up the peripheral clocks
|
||||
// Set 48MHZ CLOCK FOR USB
|
||||
sam.GCLK.GENCTRL1.Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
sam.GCLK.GENCTRL[1].Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
sam.GCLK_GENCTRL_IDC |
|
||||
sam.GCLK_GENCTRL_GENEN)
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL1) {
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK1) {
|
||||
}
|
||||
|
||||
// // Set 100MHZ CLOCK FOR OTHER PERIPHERALS
|
||||
@@ -142,18 +142,18 @@ func initClocks() {
|
||||
// }
|
||||
|
||||
// // Set 12MHZ CLOCK FOR DAC
|
||||
sam.GCLK.GENCTRL4.Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
sam.GCLK.GENCTRL[4].Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
sam.GCLK_GENCTRL_IDC |
|
||||
(4 << sam.GCLK_GENCTRL_DIVSEL_Pos) |
|
||||
sam.GCLK_GENCTRL_GENEN)
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL4) {
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK4) {
|
||||
}
|
||||
|
||||
// // Set up main clock
|
||||
sam.GCLK.GENCTRL0.Set((sam.GCLK_GENCTRL_SRC_DPLL0 << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
sam.GCLK.GENCTRL[0].Set((sam.GCLK_GENCTRL_SRC_DPLL0 << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
sam.GCLK_GENCTRL_IDC |
|
||||
sam.GCLK_GENCTRL_GENEN)
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL0) {
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK0) {
|
||||
}
|
||||
|
||||
sam.MCLK.CPUDIV.Set(sam.MCLK_CPUDIV_DIV_DIV1)
|
||||
@@ -258,7 +258,7 @@ func timerSleep(ticks uint32) {
|
||||
// set compare value
|
||||
cnt := sam.RTC_MODE0.COUNT.Get()
|
||||
|
||||
sam.RTC_MODE0.COMP0.Set(uint32(cnt) + (ticks * 10 / 305)) // each counter tick == 30.5us
|
||||
sam.RTC_MODE0.COMP[0].Set(uint32(cnt) + (ticks * 10 / 305)) // each counter tick == 30.5us
|
||||
|
||||
// enable IRQ for CMP0 compare
|
||||
sam.RTC_MODE0.INTENSET.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
||||
@@ -285,7 +285,7 @@ func initUSBClock() {
|
||||
|
||||
// Put Generic Clock Generator 1 as source for USB
|
||||
//GCLK->PCHCTRL[USB_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
|
||||
sam.GCLK.PCHCTRL10.Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK.PCHCTRL[10].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
|
||||
@@ -295,8 +295,8 @@ func initADCClock() {
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_ADC1_)
|
||||
|
||||
// Put Generic Clock Generator 1 as source for ADC0 and ADC1.
|
||||
sam.GCLK.PCHCTRL40.Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK.PCHCTRL[40].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.GCLK.PCHCTRL41.Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK.PCHCTRL[41].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
|
||||
@@ -9,35 +9,35 @@ import (
|
||||
func initSERCOMClocks() {
|
||||
// Turn on clock to SERCOM0 for UART0
|
||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
|
||||
sam.GCLK.PCHCTRL7.Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK.PCHCTRL[7].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// sets the "slow" clock shared by all SERCOM
|
||||
sam.GCLK.PCHCTRL3.Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK.PCHCTRL[3].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM1
|
||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
|
||||
sam.GCLK.PCHCTRL8.Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK.PCHCTRL[8].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM2
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
|
||||
sam.GCLK.PCHCTRL23.Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK.PCHCTRL[23].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM3
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
|
||||
sam.GCLK.PCHCTRL24.Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK.PCHCTRL[24].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM4
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
|
||||
sam.GCLK.PCHCTRL34.Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK.PCHCTRL[34].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM5
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
|
||||
sam.GCLK.PCHCTRL35.Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK.PCHCTRL[35].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
|
||||
@@ -31,10 +31,10 @@ const (
|
||||
)
|
||||
|
||||
//go:extern _sbss
|
||||
var _sbss unsafe.Pointer
|
||||
var _sbss [0]byte
|
||||
|
||||
//go:extern _ebss
|
||||
var _ebss unsafe.Pointer
|
||||
var _ebss [0]byte
|
||||
|
||||
//go:export main
|
||||
func main() {
|
||||
@@ -49,8 +49,8 @@ func preinit() {
|
||||
// Initialize .bss: zero-initialized global variables.
|
||||
ptr := unsafe.Pointer(&_sbss)
|
||||
for ptr != unsafe.Pointer(&_ebss) {
|
||||
*(*uint32)(ptr) = 0
|
||||
ptr = unsafe.Pointer(uintptr(ptr) + 4)
|
||||
*(*uint8)(ptr) = 0
|
||||
ptr = unsafe.Pointer(uintptr(ptr) + 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,17 +47,17 @@ func init() {
|
||||
|
||||
func pric_init() {
|
||||
// Make sure the HFROSC is on
|
||||
sifive.PRIC.HFROSCCFG.SetBits(sifive.PRIC_HFROSCCFG_ENABLE)
|
||||
sifive.PRCI.HFROSCCFG.SetBits(sifive.PRCI_HFROSCCFG_ENABLE)
|
||||
|
||||
// Run off 16 MHz Crystal for accuracy.
|
||||
sifive.PRIC.PLLCFG.SetBits(sifive.PRIC_PLLCFG_REFSEL | sifive.PRIC_PLLCFG_BYPASS)
|
||||
sifive.PRIC.PLLCFG.SetBits(sifive.PRIC_PLLCFG_SEL)
|
||||
sifive.PRCI.PLLCFG.SetBits(sifive.PRCI_PLLCFG_REFSEL | sifive.PRCI_PLLCFG_BYPASS)
|
||||
sifive.PRCI.PLLCFG.SetBits(sifive.PRCI_PLLCFG_SEL)
|
||||
|
||||
// Turn off HFROSC to save power
|
||||
sifive.PRIC.HFROSCCFG.ClearBits(sifive.PRIC_HFROSCCFG_ENABLE)
|
||||
sifive.PRCI.HFROSCCFG.ClearBits(sifive.PRCI_HFROSCCFG_ENABLE)
|
||||
|
||||
// Enable the RTC.
|
||||
sifive.RTC.CONFIG.Set(sifive.RTC_CONFIG_ENALWAYS)
|
||||
sifive.RTC.RTCCFG.Set(sifive.RTC_RTCCFG_ENALWAYS)
|
||||
}
|
||||
|
||||
func preinit() {
|
||||
@@ -85,10 +85,10 @@ func putchar(c byte) {
|
||||
func ticks() timeUnit {
|
||||
// Combining the low bits and the high bits yields a time span of over 270
|
||||
// years without counter rollover.
|
||||
highBits := sifive.RTC.HI.Get()
|
||||
highBits := sifive.RTC.RTCHI.Get()
|
||||
for {
|
||||
lowBits := sifive.RTC.LO.Get()
|
||||
newHighBits := sifive.RTC.HI.Get()
|
||||
lowBits := sifive.RTC.RTCLO.Get()
|
||||
newHighBits := sifive.RTC.RTCHI.Get()
|
||||
if newHighBits == highBits {
|
||||
// High bits stayed the same.
|
||||
return timeUnit(lowBits) | (timeUnit(highBits) << 32)
|
||||
|
||||
@@ -160,12 +160,18 @@ func timerSleep(ticks uint32) {
|
||||
|
||||
// TODO: support smaller or larger scales (autoscaling) based
|
||||
// on the length of sleep time requested.
|
||||
// The current scaling only supports a range of 100 usec to 6553 msec.
|
||||
// The current scaling only supports a range of 200 usec to 6553 msec.
|
||||
|
||||
// prescale counter down from 72mhz to 10khz aka 0.1 ms frequency.
|
||||
stm32.TIM3.PSC.Set(machine.CPU_FREQUENCY/10000 - 1) // 7199
|
||||
|
||||
// set duty aka duration
|
||||
// Set duty aka duration.
|
||||
// STM32 dividers use n-1, i.e. n counts from 0 to n-1.
|
||||
// As a result, with these prescaler settings,
|
||||
// the minimum allowed duration is 200 microseconds.
|
||||
if ticks < 200 {
|
||||
ticks = 200
|
||||
}
|
||||
stm32.TIM3.ARR.Set(ticks/100 - 1) // convert from microseconds to 0.1 ms
|
||||
|
||||
// Enable the hardware interrupt.
|
||||
|
||||
@@ -59,16 +59,78 @@ func scheduleLogChan(msg string, ch *channel, t *task) {
|
||||
}
|
||||
}
|
||||
|
||||
// Set the task to sleep for a given time.
|
||||
// deadlock is called when a goroutine cannot proceed any more, but is in theory
|
||||
// not exited (so deferred calls won't run). This can happen for example in code
|
||||
// like this, that blocks forever:
|
||||
//
|
||||
// This is a compiler intrinsic.
|
||||
func sleepTask(caller *task, duration int64) {
|
||||
if schedulerDebug {
|
||||
println(" set sleep:", caller, uint(duration/tickMicros))
|
||||
// select{}
|
||||
//go:noinline
|
||||
func deadlock() {
|
||||
// call yield without requesting a wakeup
|
||||
yield()
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Goexit terminates the currently running goroutine. No other goroutines are affected.
|
||||
//
|
||||
// Unlike the main Go implementation, no deffered calls will be run.
|
||||
//go:inline
|
||||
func Goexit() {
|
||||
// its really just a deadlock
|
||||
deadlock()
|
||||
}
|
||||
|
||||
// unblock unblocks a task and returns the next value
|
||||
func unblock(t *task) *task {
|
||||
state := t.state()
|
||||
next := state.next
|
||||
state.next = nil
|
||||
activateTask(t)
|
||||
return next
|
||||
}
|
||||
|
||||
// unblockChain unblocks the next task on the stack/queue, returning it
|
||||
// also updates the chain, putting the next element into the chain pointer
|
||||
// if the chain is used as a queue, tail is used as a pointer to the final insertion point
|
||||
// if the chain is used as a stack, tail should be nil
|
||||
func unblockChain(chain **task, tail ***task) *task {
|
||||
t := *chain
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
state := caller.state()
|
||||
state.data = uint(duration / tickMicros) // TODO: longer durations
|
||||
addSleepTask(caller)
|
||||
*chain = unblock(t)
|
||||
if tail != nil && *chain == nil {
|
||||
*tail = chain
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// dropChain drops a task from the given stack or queue
|
||||
// if the chain is used as a queue, tail is used as a pointer to the field containing a pointer to the next insertion point
|
||||
// if the chain is used as a stack, tail should be nil
|
||||
func dropChain(t *task, chain **task, tail ***task) {
|
||||
for c := chain; *c != nil; c = &((*c).state().next) {
|
||||
if *c == t {
|
||||
next := (*c).state().next
|
||||
if next == nil && tail != nil {
|
||||
*tail = c
|
||||
}
|
||||
*c = next
|
||||
return
|
||||
}
|
||||
}
|
||||
panic("runtime: task not in chain")
|
||||
}
|
||||
|
||||
// Pause the current task for a given time.
|
||||
//go:linkname sleep time.Sleep
|
||||
func sleep(duration int64) {
|
||||
addSleepTask(getCoroutine(), duration)
|
||||
yield()
|
||||
}
|
||||
|
||||
func avrSleep(duration int64) {
|
||||
sleepTicks(timeUnit(duration / tickMicros))
|
||||
}
|
||||
|
||||
// Add a non-queued task to the run queue.
|
||||
@@ -85,6 +147,7 @@ func activateTask(t *task) {
|
||||
|
||||
// getTaskStateData is a helper function to get the current .data field of the
|
||||
// goroutine state.
|
||||
//go:inline
|
||||
func getTaskStateData(t *task) uint {
|
||||
return t.state().data
|
||||
}
|
||||
@@ -93,6 +156,7 @@ func getTaskStateData(t *task) uint {
|
||||
// done.
|
||||
func runqueuePushBack(t *task) {
|
||||
if schedulerDebug {
|
||||
scheduleLogTask(" pushing back:", t)
|
||||
if t.state().next != nil {
|
||||
panic("runtime: runqueuePushBack: expected next task to be nil")
|
||||
}
|
||||
@@ -124,12 +188,14 @@ func runqueuePopFront() *task {
|
||||
}
|
||||
|
||||
// Add this task to the sleep queue, assuming its state is set to sleeping.
|
||||
func addSleepTask(t *task) {
|
||||
func addSleepTask(t *task, duration int64) {
|
||||
if schedulerDebug {
|
||||
println(" set sleep:", t, uint(duration/tickMicros))
|
||||
if t.state().next != nil {
|
||||
panic("runtime: addSleepTask: expected next task to be nil")
|
||||
}
|
||||
}
|
||||
t.state().data = uint(duration / tickMicros) // TODO: longer durations
|
||||
now := ticks()
|
||||
if sleepQueue == nil {
|
||||
scheduleLog(" -> sleep new queue")
|
||||
@@ -209,3 +275,8 @@ func scheduler() {
|
||||
t.resume()
|
||||
}
|
||||
}
|
||||
|
||||
func Gosched() {
|
||||
runqueuePushBack(getCoroutine())
|
||||
yield()
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ func makeGoroutine(uintptr) uintptr
|
||||
// removed in the goroutine lowering pass.
|
||||
func getCoroutine() *task
|
||||
|
||||
// getTaskStatePtr is a helper function to set the current .ptr field of a
|
||||
// setTaskStatePtr is a helper function to set the current .ptr field of a
|
||||
// coroutine promise.
|
||||
func setTaskStatePtr(t *task, value unsafe.Pointer) {
|
||||
t.state().ptr = value
|
||||
@@ -65,37 +65,40 @@ func getTaskStatePtr(t *task) unsafe.Pointer {
|
||||
return t.state().ptr
|
||||
}
|
||||
|
||||
//go:linkname sleep time.Sleep
|
||||
func sleep(d int64) {
|
||||
sleepTicks(timeUnit(d / tickMicros))
|
||||
}
|
||||
|
||||
// deadlock is called when a goroutine cannot proceed any more, but is in theory
|
||||
// not exited (so deferred calls won't run). This can happen for example in code
|
||||
// like this, that blocks forever:
|
||||
//
|
||||
// select{}
|
||||
//
|
||||
// The coroutine version is implemented directly in the compiler but it needs
|
||||
// this definition to work.
|
||||
func deadlock()
|
||||
|
||||
// reactivateParent reactivates the parent goroutine. It is necessary in case of
|
||||
// the coroutine-based scheduler.
|
||||
func reactivateParent(t *task) {
|
||||
activateTask(t)
|
||||
}
|
||||
|
||||
// chanYield exits the current goroutine. Used in the channel implementation, to
|
||||
// suspend the current goroutine until it is reactivated by a channel operation
|
||||
// of a different goroutine. It is a no-op in the coroutine implementation.
|
||||
func chanYield() {
|
||||
// Nothing to do here, simply returning from the channel operation also exits
|
||||
// the goroutine temporarily.
|
||||
}
|
||||
// yield suspends execution of the current goroutine
|
||||
// any wakeups must be configured before calling yield
|
||||
func yield()
|
||||
|
||||
// getSystemStackPointer returns the current stack pointer of the system stack.
|
||||
// This is always the current stack pointer.
|
||||
func getSystemStackPointer() uintptr {
|
||||
return getCurrentStackPointer()
|
||||
}
|
||||
|
||||
func fakeCoroutine(dst **task) {
|
||||
*dst = getCoroutine()
|
||||
for {
|
||||
yield()
|
||||
}
|
||||
}
|
||||
|
||||
func getFakeCoroutine() *task {
|
||||
// this isnt defined behavior, but this is what our implementation does
|
||||
// this is really a horrible hack
|
||||
var t *task
|
||||
go fakeCoroutine(&t)
|
||||
|
||||
// the first line of fakeCoroutine will have completed by now
|
||||
return t
|
||||
}
|
||||
|
||||
// noret is a placeholder that can be used to indicate that an async function is not going to directly return here
|
||||
func noret()
|
||||
|
||||
func getParentHandle() *task
|
||||
|
||||
func llvmCoroRefHolder() {
|
||||
noret()
|
||||
getParentHandle()
|
||||
getCoroutine()
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ tinygo_startTask:
|
||||
blx r4
|
||||
|
||||
// After return, exit this goroutine. This is a tail call.
|
||||
bl runtime.Goexit
|
||||
bl runtime.yield
|
||||
|
||||
.section .text.tinygo_swapTask
|
||||
.global tinygo_swapTask
|
||||
|
||||
@@ -31,6 +31,7 @@ type task struct {
|
||||
// getCoroutine returns the currently executing goroutine. It is used as an
|
||||
// intrinsic when compiling channel operations, but is not necessary with the
|
||||
// task-based scheduler.
|
||||
//go:inline
|
||||
func getCoroutine() *task {
|
||||
return currentTask
|
||||
}
|
||||
@@ -67,15 +68,6 @@ func swapTask(oldTask, newTask *task) {
|
||||
//go:linkname swapTaskLower tinygo_swapTask
|
||||
func swapTaskLower(oldTask, newTask *task)
|
||||
|
||||
// Goexit terminates the currently running goroutine. No other goroutines are affected.
|
||||
//
|
||||
// Unlike the main Go implementation, no deffered calls will be run.
|
||||
//export runtime.Goexit
|
||||
func Goexit() {
|
||||
// Swap without rescheduling first, effectively exiting the goroutine.
|
||||
swapTask(currentTask, &schedulerState)
|
||||
}
|
||||
|
||||
// startTask is a small wrapper function that sets up the first (and only)
|
||||
// argument to the new goroutine and makes sure it is exited when the goroutine
|
||||
// finishes.
|
||||
@@ -96,40 +88,13 @@ func startGoroutine(fn, args uintptr) {
|
||||
runqueuePushBack(t)
|
||||
}
|
||||
|
||||
//go:linkname sleep time.Sleep
|
||||
func sleep(d int64) {
|
||||
sleepTicks(timeUnit(d / tickMicros))
|
||||
}
|
||||
|
||||
// sleepCurrentTask suspends the current goroutine. This is a compiler
|
||||
// intrinsic. It replaces calls to time.Sleep when a scheduler is in use.
|
||||
func sleepCurrentTask(d int64) {
|
||||
sleepTask(currentTask, d)
|
||||
// yield suspends execution of the current goroutine
|
||||
// any wakeups must be configured before calling yield
|
||||
//export runtime.yield
|
||||
func yield() {
|
||||
swapTask(currentTask, &schedulerState)
|
||||
}
|
||||
|
||||
// deadlock is called when a goroutine cannot proceed any more, but is in theory
|
||||
// not exited (so deferred calls won't run). This can happen for example in code
|
||||
// like this, that blocks forever:
|
||||
//
|
||||
// select{}
|
||||
func deadlock() {
|
||||
Goexit()
|
||||
}
|
||||
|
||||
// reactivateParent reactivates the parent goroutine. It is a no-op for the task
|
||||
// based scheduler.
|
||||
func reactivateParent(t *task) {
|
||||
// Nothing to do here, tasks don't stop automatically.
|
||||
}
|
||||
|
||||
// chanYield exits the current goroutine. Used in the channel implementation, to
|
||||
// suspend the current goroutine until it is reactivated by a channel operation
|
||||
// of a different goroutine.
|
||||
func chanYield() {
|
||||
Goexit()
|
||||
}
|
||||
|
||||
// getSystemStackPointer returns the current stack pointer of the system stack.
|
||||
// This is not necessarily the same as the current stack pointer.
|
||||
func getSystemStackPointer() uintptr {
|
||||
|
||||
@@ -216,3 +216,16 @@ func indexByteString(s string, c byte) int {
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// countString copies the implementation from
|
||||
// https://github.com/golang/go/blob/67f181bfd84dfd5942fe9a29d8a20c9ce5eb2fea/src/internal/bytealg/count_generic.go#L1
|
||||
//go:linkname countString internal/bytealg.CountString
|
||||
func countString(s string, c byte) int {
|
||||
n := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == c {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ type Pool struct {
|
||||
|
||||
// Get returns the value of calling Pool.New().
|
||||
func (p *Pool) Get() interface{} {
|
||||
if p.New == nil {
|
||||
return nil
|
||||
}
|
||||
return p.New()
|
||||
}
|
||||
|
||||
|
||||
@@ -8,17 +8,13 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TINYGOROOT is the path to the final location for checking tinygo files. If
|
||||
// unset (by a -X ldflag), then sourceDir() will fallback to the original build
|
||||
// directory.
|
||||
var TINYGOROOT string
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
// Target specification for a given target. Used for bare metal targets.
|
||||
//
|
||||
@@ -26,26 +22,32 @@ var TINYGOROOT string
|
||||
// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/spec/struct.TargetOptions.html
|
||||
// https://github.com/shepmaster/rust-arduino-blink-led-no-core-with-cargo/blob/master/blink/arduino.json
|
||||
type TargetSpec struct {
|
||||
Inherits []string `json:"inherits"`
|
||||
Triple string `json:"llvm-target"`
|
||||
CPU string `json:"cpu"`
|
||||
Features []string `json:"features"`
|
||||
GOOS string `json:"goos"`
|
||||
GOARCH string `json:"goarch"`
|
||||
BuildTags []string `json:"build-tags"`
|
||||
GC string `json:"gc"`
|
||||
Scheduler string `json:"scheduler"`
|
||||
Compiler string `json:"compiler"`
|
||||
Linker string `json:"linker"`
|
||||
RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt)
|
||||
CFlags []string `json:"cflags"`
|
||||
LDFlags []string `json:"ldflags"`
|
||||
ExtraFiles []string `json:"extra-files"`
|
||||
Emulator []string `json:"emulator"`
|
||||
Flasher string `json:"flash"`
|
||||
OCDDaemon []string `json:"ocd-daemon"`
|
||||
GDB string `json:"gdb"`
|
||||
GDBCmds []string `json:"gdb-initial-cmds"`
|
||||
Inherits []string `json:"inherits"`
|
||||
Triple string `json:"llvm-target"`
|
||||
CPU string `json:"cpu"`
|
||||
Features []string `json:"features"`
|
||||
GOOS string `json:"goos"`
|
||||
GOARCH string `json:"goarch"`
|
||||
BuildTags []string `json:"build-tags"`
|
||||
GC string `json:"gc"`
|
||||
Scheduler string `json:"scheduler"`
|
||||
Compiler string `json:"compiler"`
|
||||
Linker string `json:"linker"`
|
||||
RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt)
|
||||
CFlags []string `json:"cflags"`
|
||||
LDFlags []string `json:"ldflags"`
|
||||
ExtraFiles []string `json:"extra-files"`
|
||||
Emulator []string `json:"emulator"`
|
||||
FlashCommand string `json:"flash-command"`
|
||||
OCDDaemon []string `json:"ocd-daemon"`
|
||||
GDB string `json:"gdb"`
|
||||
PortReset string `json:"flash-1200-bps-reset"`
|
||||
FlashMethod string `json:"flash-method"`
|
||||
FlashVolume string `json:"msd-volume-name"`
|
||||
FlashFilename string `json:"msd-firmware-name"`
|
||||
OpenOCDInterface string `json:"openocd-interface"`
|
||||
OpenOCDTarget string `json:"openocd-target"`
|
||||
OpenOCDTransport string `json:"openocd-transport"`
|
||||
}
|
||||
|
||||
// copyProperties copies all properties that are set in spec2 into itself.
|
||||
@@ -88,8 +90,8 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
|
||||
if len(spec2.Emulator) != 0 {
|
||||
spec.Emulator = spec2.Emulator
|
||||
}
|
||||
if spec2.Flasher != "" {
|
||||
spec.Flasher = spec2.Flasher
|
||||
if spec2.FlashCommand != "" {
|
||||
spec.FlashCommand = spec2.FlashCommand
|
||||
}
|
||||
if len(spec2.OCDDaemon) != 0 {
|
||||
spec.OCDDaemon = spec2.OCDDaemon
|
||||
@@ -97,8 +99,26 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
|
||||
if spec2.GDB != "" {
|
||||
spec.GDB = spec2.GDB
|
||||
}
|
||||
if len(spec2.GDBCmds) != 0 {
|
||||
spec.GDBCmds = spec2.GDBCmds
|
||||
if spec2.PortReset != "" {
|
||||
spec.PortReset = spec2.PortReset
|
||||
}
|
||||
if spec2.FlashMethod != "" {
|
||||
spec.FlashMethod = spec2.FlashMethod
|
||||
}
|
||||
if spec2.FlashVolume != "" {
|
||||
spec.FlashVolume = spec2.FlashVolume
|
||||
}
|
||||
if spec2.FlashFilename != "" {
|
||||
spec.FlashFilename = spec2.FlashFilename
|
||||
}
|
||||
if spec2.OpenOCDInterface != "" {
|
||||
spec.OpenOCDInterface = spec2.OpenOCDInterface
|
||||
}
|
||||
if spec2.OpenOCDTarget != "" {
|
||||
spec.OpenOCDTarget = spec2.OpenOCDTarget
|
||||
}
|
||||
if spec2.OpenOCDTransport != "" {
|
||||
spec.OpenOCDTransport = spec2.OpenOCDTransport
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +143,7 @@ func (spec *TargetSpec) loadFromGivenStr(str string) error {
|
||||
if strings.HasSuffix(str, ".json") {
|
||||
path, _ = filepath.Abs(str)
|
||||
} else {
|
||||
path = filepath.Join(sourceDir(), "targets", strings.ToLower(str)+".json")
|
||||
path = filepath.Join(goenv.Get("TINYGOROOT"), "targets", strings.ToLower(str)+".json")
|
||||
}
|
||||
fp, err := os.Open(path)
|
||||
if err != nil {
|
||||
@@ -162,14 +182,8 @@ func LoadTarget(target string) (*TargetSpec, error) {
|
||||
if target == "" {
|
||||
// Configure based on GOOS/GOARCH environment variables (falling back to
|
||||
// runtime.GOOS/runtime.GOARCH), and generate a LLVM target based on it.
|
||||
goos := os.Getenv("GOOS")
|
||||
if goos == "" {
|
||||
goos = runtime.GOOS
|
||||
}
|
||||
goarch := os.Getenv("GOARCH")
|
||||
if goarch == "" {
|
||||
goarch = runtime.GOARCH
|
||||
}
|
||||
goos := goenv.Get("GOOS")
|
||||
goarch := goenv.Get("GOARCH")
|
||||
llvmos := goos
|
||||
llvmarch := map[string]string{
|
||||
"386": "i386",
|
||||
@@ -229,14 +243,15 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
// No target spec available. Use the default one, useful on most systems
|
||||
// with a regular OS.
|
||||
spec := TargetSpec{
|
||||
Triple: triple,
|
||||
GOOS: goos,
|
||||
GOARCH: goarch,
|
||||
BuildTags: []string{goos, goarch},
|
||||
Compiler: "clang",
|
||||
Linker: "cc",
|
||||
GDB: "gdb",
|
||||
GDBCmds: []string{"run"},
|
||||
Triple: triple,
|
||||
GOOS: goos,
|
||||
GOARCH: goarch,
|
||||
BuildTags: []string{goos, goarch},
|
||||
Compiler: "clang",
|
||||
Linker: "cc",
|
||||
GDB: "gdb",
|
||||
PortReset: "false",
|
||||
FlashMethod: "native",
|
||||
}
|
||||
if goos == "darwin" {
|
||||
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
|
||||
@@ -263,163 +278,38 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
return &spec, nil
|
||||
}
|
||||
|
||||
// Return the TINYGOROOT, or exit with an error.
|
||||
func sourceDir() string {
|
||||
// Use $TINYGOROOT as root, if available.
|
||||
root := os.Getenv("TINYGOROOT")
|
||||
if root != "" {
|
||||
if !isSourceDir(root) {
|
||||
fmt.Fprintln(os.Stderr, "error: $TINYGOROOT was not set to the correct root")
|
||||
os.Exit(1)
|
||||
}
|
||||
return root
|
||||
// OpenOCDConfiguration returns a list of command line arguments to OpenOCD.
|
||||
// This list of command-line arguments is based on the various OpenOCD-related
|
||||
// flags in the target specification.
|
||||
func (spec *TargetSpec) OpenOCDConfiguration() (args []string, err error) {
|
||||
if spec.OpenOCDInterface == "" {
|
||||
return nil, errors.New("OpenOCD programmer not set")
|
||||
}
|
||||
|
||||
if TINYGOROOT != "" {
|
||||
if !isSourceDir(TINYGOROOT) {
|
||||
fmt.Fprintln(os.Stderr, "error: TINYGOROOT was not set to the correct root")
|
||||
os.Exit(1)
|
||||
}
|
||||
return TINYGOROOT
|
||||
if !regexp.MustCompile("^[\\p{L}0-9_-]+$").MatchString(spec.OpenOCDInterface) {
|
||||
return nil, fmt.Errorf("OpenOCD programmer has an invalid name: %#v", spec.OpenOCDInterface)
|
||||
}
|
||||
|
||||
// Find root from executable path.
|
||||
path, err := os.Executable()
|
||||
if err != nil {
|
||||
// Very unlikely. Bail out if it happens.
|
||||
panic("could not get executable path: " + err.Error())
|
||||
if spec.OpenOCDTarget == "" {
|
||||
return nil, errors.New("OpenOCD chip not set")
|
||||
}
|
||||
root = filepath.Dir(filepath.Dir(path))
|
||||
if isSourceDir(root) {
|
||||
return root
|
||||
if !regexp.MustCompile("^[\\p{L}0-9_-]+$").MatchString(spec.OpenOCDTarget) {
|
||||
return nil, fmt.Errorf("OpenOCD target has an invalid name: %#v", spec.OpenOCDTarget)
|
||||
}
|
||||
|
||||
// Fallback: use the original directory from where it was built
|
||||
// https://stackoverflow.com/a/32163888/559350
|
||||
_, path, _, _ = runtime.Caller(0)
|
||||
root = filepath.Dir(path)
|
||||
if isSourceDir(root) {
|
||||
return root
|
||||
if spec.OpenOCDTransport != "" && spec.OpenOCDTransport != "swd" {
|
||||
return nil, fmt.Errorf("unknown OpenOCD transport: %#v", spec.OpenOCDTransport)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "error: could not autodetect root directory, set the TINYGOROOT environment variable to override")
|
||||
os.Exit(1)
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// isSourceDir returns true if the directory looks like a TinyGo source directory.
|
||||
func isSourceDir(root string) bool {
|
||||
_, err := os.Stat(filepath.Join(root, "src/runtime/internal/sys/zversion.go"))
|
||||
if err != nil {
|
||||
return false
|
||||
args = []string{"-f", "interface/" + spec.OpenOCDInterface + ".cfg"}
|
||||
if spec.OpenOCDTransport != "" {
|
||||
args = append(args, "-c", "transport select "+spec.OpenOCDTransport)
|
||||
}
|
||||
_, err = os.Stat(filepath.Join(root, "src/device/arm/arm.go"))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func getGopath() string {
|
||||
gopath := os.Getenv("GOPATH")
|
||||
if gopath != "" {
|
||||
return gopath
|
||||
}
|
||||
|
||||
// fallback
|
||||
home := getHomeDir()
|
||||
return filepath.Join(home, "go")
|
||||
}
|
||||
|
||||
func getHomeDir() string {
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
panic("cannot get current user: " + err.Error())
|
||||
}
|
||||
if u.HomeDir == "" {
|
||||
// This is very unlikely, so panic here.
|
||||
// Not the nicest solution, however.
|
||||
panic("could not find home directory")
|
||||
}
|
||||
return u.HomeDir
|
||||
}
|
||||
|
||||
// getGoroot returns an appropriate GOROOT from various sources. If it can't be
|
||||
// found, it returns an empty string.
|
||||
func getGoroot() string {
|
||||
goroot := os.Getenv("GOROOT")
|
||||
if goroot != "" {
|
||||
// An explicitly set GOROOT always has preference.
|
||||
return goroot
|
||||
}
|
||||
|
||||
// Check for the location of the 'go' binary and base GOROOT on that.
|
||||
binpath, err := exec.LookPath("go")
|
||||
if err == nil {
|
||||
binpath, err = filepath.EvalSymlinks(binpath)
|
||||
if err == nil {
|
||||
goroot := filepath.Dir(filepath.Dir(binpath))
|
||||
if isGoroot(goroot) {
|
||||
return goroot
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check what GOROOT was at compile time.
|
||||
if isGoroot(runtime.GOROOT()) {
|
||||
return runtime.GOROOT()
|
||||
}
|
||||
|
||||
// Check for some standard locations, as a last resort.
|
||||
var candidates []string
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
candidates = []string{
|
||||
"/usr/local/go", // manually installed
|
||||
"/usr/lib/go", // from the distribution
|
||||
}
|
||||
case "darwin":
|
||||
candidates = []string{
|
||||
"/usr/local/go", // manually installed
|
||||
"/usr/local/opt/go/libexec", // from Homebrew
|
||||
}
|
||||
}
|
||||
|
||||
for _, candidate := range candidates {
|
||||
if isGoroot(candidate) {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
|
||||
// Can't find GOROOT...
|
||||
return ""
|
||||
}
|
||||
|
||||
// isGoroot checks whether the given path looks like a GOROOT.
|
||||
func isGoroot(goroot string) bool {
|
||||
_, err := os.Stat(filepath.Join(goroot, "src", "runtime", "internal", "sys", "zversion.go"))
|
||||
return err == nil
|
||||
args = append(args, "-f", "target/"+spec.OpenOCDTarget+".cfg")
|
||||
return args, nil
|
||||
}
|
||||
|
||||
// getGorootVersion returns the major and minor version for a given GOROOT path.
|
||||
// If the goroot cannot be determined, (0, 0) is returned.
|
||||
func getGorootVersion(goroot string) (major, minor int, err error) {
|
||||
var s string
|
||||
var n int
|
||||
var trailing string
|
||||
|
||||
if data, err := ioutil.ReadFile(filepath.Join(
|
||||
goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
|
||||
|
||||
r := regexp.MustCompile("const TheVersion = `(.*)`")
|
||||
matches := r.FindSubmatch(data)
|
||||
if len(matches) != 2 {
|
||||
return 0, 0, errors.New("Invalid go version output:\n" + string(data))
|
||||
}
|
||||
|
||||
s = string(matches[1])
|
||||
|
||||
} else if data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
|
||||
s = string(data)
|
||||
|
||||
} else {
|
||||
s, err := getGorootVersionString(goroot)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
@@ -433,7 +323,8 @@ func getGorootVersion(goroot string) (major, minor int, err error) {
|
||||
}
|
||||
|
||||
// Ignore the errors, we don't really handle errors here anyway.
|
||||
n, err = fmt.Sscanf(s, "go%d.%d%s", &major, &minor, &trailing)
|
||||
var trailing string
|
||||
n, err := fmt.Sscanf(s, "go%d.%d%s", &major, &minor, &trailing)
|
||||
if n == 2 && err == io.EOF {
|
||||
// Means there were no trailing characters (i.e., not an alpha/beta)
|
||||
err = nil
|
||||
@@ -444,6 +335,29 @@ func getGorootVersion(goroot string) (major, minor int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// getGorootVersionString returns the version string as reported by the Go
|
||||
// toolchain for the given GOROOT path. It is usually of the form `go1.x.y` but
|
||||
// can have some variations (for beta releases, for example).
|
||||
func getGorootVersionString(goroot string) (string, error) {
|
||||
if data, err := ioutil.ReadFile(filepath.Join(
|
||||
goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
|
||||
|
||||
r := regexp.MustCompile("const TheVersion = `(.*)`")
|
||||
matches := r.FindSubmatch(data)
|
||||
if len(matches) != 2 {
|
||||
return "", errors.New("Invalid go version output:\n" + string(data))
|
||||
}
|
||||
|
||||
return string(matches[1]), nil
|
||||
|
||||
} else if data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
|
||||
return string(data), nil
|
||||
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// getClangHeaderPath returns the path to the built-in Clang headers. It tries
|
||||
// multiple locations, which should make it find the directory when installed in
|
||||
// various ways.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"inherits": ["atsamd21g18a"],
|
||||
"build-tags": ["sam", "atsamd21g18a", "arduino_nano33"],
|
||||
"flash": "bossac -d -i -e -w -v -R --port={port} --offset=0x2000 {bin}"
|
||||
"flash-command": "bossac -d -i -e -w -v -R --port={port} --offset=0x2000 {bin}",
|
||||
"flash-1200-bps-reset": "true"
|
||||
}
|
||||
|
||||
@@ -15,5 +15,5 @@
|
||||
"targets/avr.S",
|
||||
"src/device/avr/atmega328p.s"
|
||||
],
|
||||
"flash": "avrdude -c arduino -p atmega328p -P {port} -U flash:w:{hex}"
|
||||
"flash-command": "avrdude -c arduino -p atmega328p -P {port} -U flash:w:{hex}"
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"extra-files": [
|
||||
"src/device/stm32/stm32f103xx.s"
|
||||
],
|
||||
"flash": "openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg -c 'program {hex} reset exit'",
|
||||
"ocd-daemon": ["openocd", "-f", "interface/stlink-v2.cfg", "-f", "target/stm32f1x.cfg"],
|
||||
"gdb-initial-cmds": ["target remote :3333", "monitor halt", "load", "monitor reset", "c"]
|
||||
"flash-method": "openocd",
|
||||
"openocd-interface": "stlink-v2",
|
||||
"openocd-target": "stm32f1x"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"inherits": ["atsamd21g18a"],
|
||||
"build-tags": ["sam", "atsamd21g18a", "circuitplay_express"],
|
||||
"flash": "uf2conv.py {bin}"
|
||||
"flash-1200-bps-reset": "true",
|
||||
"flash-method": "msd",
|
||||
"msd-volume-name": "CPLAYBOOT",
|
||||
"msd-firmware-name": "firmware.uf2"
|
||||
}
|
||||
|
||||
@@ -15,5 +15,5 @@
|
||||
"targets/avr.S",
|
||||
"src/device/avr/attiny85.s"
|
||||
],
|
||||
"flash": "micronucleus --run {hex}"
|
||||
"flash-command": "micronucleus --run {hex}"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"inherits": ["atsamd21g18a"],
|
||||
"build-tags": ["sam", "atsamd21g18a", "feather_m0"],
|
||||
"flash": "bossac -d -i -e -w -v -R --port={port} --offset=0x2000 {hex}"
|
||||
"flash-1200-bps-reset": "true",
|
||||
"flash-method": "msd",
|
||||
"msd-volume-name": "FEATHERBOOT",
|
||||
"msd-firmware-name": "firmware.uf2"
|
||||
}
|
||||
|
||||
@@ -3,5 +3,8 @@
|
||||
"build-tags": ["hifive1b"],
|
||||
"ldflags": [
|
||||
"-T", "targets/hifive1b.ld"
|
||||
]
|
||||
],
|
||||
"flash-method": "msd",
|
||||
"msd-volume-name": "HiFive",
|
||||
"msd-firmware-name": "firmware.hex"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"inherits": ["atsamd21g18a"],
|
||||
"build-tags": ["sam", "atsamd21g18a", "itsybitsy_m0"],
|
||||
"flash": "bossac -d -i -e -w -v -R --port={port} --offset=0x2000 {hex}"
|
||||
"flash-1200-bps-reset": "true",
|
||||
"flash-method": "msd",
|
||||
"msd-volume-name": "ITSYBOOT",
|
||||
"msd-firmware-name": "firmware.uf2"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"inherits": ["atsamd51g19a"],
|
||||
"build-tags": ["sam", "atsamd51g19a", "itsybitsy_m4"],
|
||||
"flash": "bossac -d -i -e -w -v -R --offset=0x4000 {bin}"
|
||||
"flash-1200-bps-reset": "true",
|
||||
"flash-method": "msd",
|
||||
"msd-volume-name": "ITSYM4BOOT",
|
||||
"msd-firmware-name": "firmware.uf2"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"inherits": ["nrf51"],
|
||||
"build-tags": ["microbit"],
|
||||
"flash": "openocd -f interface/cmsis-dap.cfg -f target/nrf51.cfg -c 'program {hex} reset exit'",
|
||||
"ocd-daemon": ["openocd", "-f", "interface/cmsis-dap.cfg", "-f", "target/nrf51.cfg"],
|
||||
"gdb-initial-cmds": ["target remote :3333", "monitor halt", "load", "monitor reset", "c"]
|
||||
"flash-method": "msd",
|
||||
"openocd-interface": "cmsis-dap",
|
||||
"msd-volume-name": "MICROBIT",
|
||||
"msd-firmware-name": "firmware.hex"
|
||||
}
|
||||
|
||||
+2
-1
@@ -14,5 +14,6 @@
|
||||
"extra-files": [
|
||||
"lib/nrfx/mdk/system_nrf51.c",
|
||||
"src/device/nrf/nrf51.s"
|
||||
]
|
||||
],
|
||||
"openocd-target": "nrf51"
|
||||
}
|
||||
|
||||
+2
-1
@@ -15,5 +15,6 @@
|
||||
"extra-files": [
|
||||
"lib/nrfx/mdk/system_nrf52.c",
|
||||
"src/device/nrf/nrf52.s"
|
||||
]
|
||||
],
|
||||
"openocd-target": "nrf51"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"inherits": ["nrf52840"],
|
||||
"build-tags": ["nrf52840_mdk"],
|
||||
"flash": "openocd -f interface/cmsis-dap.cfg -f target/nrf51.cfg -c 'program {hex} reset exit'",
|
||||
"ocd-daemon": ["openocd", "-f", "interface/cmsis-dap.cfg", "-f", "target/nrf51.cfg"],
|
||||
"gdb-initial-cmds": ["target remote :3333", "monitor halt", "load", "monitor reset", "c"]
|
||||
"flash-method": "openocd",
|
||||
"openocd-interface": "cmsis-dap"
|
||||
}
|
||||
|
||||
@@ -15,5 +15,6 @@
|
||||
"extra-files": [
|
||||
"lib/nrfx/mdk/system_nrf52840.c",
|
||||
"src/device/nrf/nrf52840.s"
|
||||
]
|
||||
],
|
||||
"openocd-target": "nrf51"
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"extra-files": [
|
||||
"src/device/stm32/stm32f103xx.s"
|
||||
],
|
||||
"flash": "openocd -f interface/stlink-v2-1.cfg -f target/stm32f1x.cfg -c 'program {hex} reset exit'",
|
||||
"ocd-daemon": ["openocd", "-f", "interface/stlink-v2-1.cfg", "-f", "target/stm32f1x.cfg"],
|
||||
"gdb-initial-cmds": ["target remote :3333", "monitor halt", "load", "monitor reset", "c"]
|
||||
"flash-method": "openocd",
|
||||
"openocd-interface": "stlink-v2-1",
|
||||
"openocd-target": "stm32f1x"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"inherits": ["nrf51"],
|
||||
"build-tags": ["pca10031"],
|
||||
"flash": "nrfjprog -f nrf51 --sectorerase --program {hex} --reset",
|
||||
"ocd-daemon": ["openocd", "-f", "interface/cmsis-dap.cfg", "-f", "target/nrf51.cfg"],
|
||||
"gdb-initial-cmds": ["target remote :3333", "monitor halt", "load", "monitor reset", "c"]
|
||||
"flash-command": "nrfjprog -f nrf51 --sectorerase --program {hex} --reset",
|
||||
"openocd-interface": "cmsis-dap"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"inherits": ["nrf52"],
|
||||
"build-tags": ["pca10040"],
|
||||
"flash": "nrfjprog -f nrf52 --sectorerase --program {hex} --reset",
|
||||
"ocd-daemon": ["openocd", "-f", "interface/jlink.cfg", "-c", "transport select swd", "-f", "target/nrf51.cfg"],
|
||||
"gdb-initial-cmds": ["target remote :3333", "monitor halt", "load", "monitor reset", "c"]
|
||||
"flash-method": "openocd",
|
||||
"flash-command": "nrfjprog -f nrf52 --sectorerase --program {hex} --reset",
|
||||
"openocd-interface": "jlink",
|
||||
"openocd-transport": "swd"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"inherits": ["nrf52840"],
|
||||
"build-tags": ["pca10056"],
|
||||
"flash": "nrfjprog -f nrf52 --sectorerase --program {hex} --reset",
|
||||
"ocd-daemon": ["openocd", "-f", "interface/cmsis-dap.cfg", "-f", "target/nrf51.cfg"],
|
||||
"gdb-initial-cmds": ["target remote :3333", "monitor halt", "load", "monitor reset", "c"]
|
||||
"flash-method": "command",
|
||||
"flash-command": "nrfjprog -f nrf52 --sectorerase --program {hex} --reset",
|
||||
"msd-volume-name": "JLINK",
|
||||
"msd-firmware-name": "firmware.hex"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"inherits": ["nrf52840"],
|
||||
"build-tags": ["reelboard"],
|
||||
"flash": "openocd -f interface/cmsis-dap.cfg -f target/nrf51.cfg -c 'program {hex} reset exit'",
|
||||
"ocd-daemon": ["openocd", "-f", "interface/cmsis-dap.cfg", "-f", "target/nrf51.cfg"],
|
||||
"gdb-initial-cmds": ["target remote :3333", "monitor halt", "load", "monitor reset", "c"]
|
||||
"flash-method": "msd",
|
||||
"msd-volume-name": "reel-board",
|
||||
"msd-firmware-name": "firmware.hex",
|
||||
"openocd-interface": "cmsis-dap"
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"extra-files": [
|
||||
"src/device/stm32/stm32f407.s"
|
||||
],
|
||||
"flash": "openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg -c 'program {hex} reset exit'",
|
||||
"ocd-daemon": ["openocd", "-f", "interface/stlink.cfg", "-f", "target/stm32f4x.cfg"],
|
||||
"gdb-initial-cmds": ["target remote :3333", "monitor halt", "load", "monitor reset", "c"]
|
||||
"flash-method": "openocd",
|
||||
"openocd-interface": "stlink-v2",
|
||||
"openocd-target": "stm32f4x"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"inherits": ["atsamd21e18a"],
|
||||
"build-tags": ["sam", "atsamd21e18a", "trinket_m0"],
|
||||
"flash": "bossac -d -i -e -w -v -R --port={port} --offset=0x2000 {hex}"
|
||||
"flash-1200-bps-reset": "true",
|
||||
"flash-method": "msd",
|
||||
"msd-volume-name": "TRINKETBOOT",
|
||||
"msd-firmware-name": "firmware.uf2"
|
||||
}
|
||||
|
||||
Vendored
+38
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
type x struct{}
|
||||
|
||||
func (x x) name() string {
|
||||
return "x"
|
||||
}
|
||||
|
||||
type y = x
|
||||
|
||||
type a struct {
|
||||
n int
|
||||
}
|
||||
|
||||
func (a a) fruit() string {
|
||||
return "apple"
|
||||
}
|
||||
|
||||
type b = a
|
||||
|
||||
type fruit interface {
|
||||
fruit() string
|
||||
}
|
||||
|
||||
type f = fruit
|
||||
|
||||
func main() {
|
||||
// test a basic alias
|
||||
println(y{}.name())
|
||||
|
||||
// test using a type alias value as an interface
|
||||
var v f = b{}
|
||||
println(v.fruit())
|
||||
|
||||
// test comparing an alias interface with the referred-to type
|
||||
println(a{} == b{})
|
||||
println(a{2} == b{3})
|
||||
}
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
x
|
||||
apple
|
||||
true
|
||||
false
|
||||
Vendored
+104
-8
@@ -1,65 +1,110 @@
|
||||
package main
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// waitGroup is a small type reimplementing some of the behavior of sync.WaitGroup
|
||||
type waitGroup uint
|
||||
|
||||
func (wg *waitGroup) wait() {
|
||||
n := 0
|
||||
for *wg != 0 {
|
||||
// pause and wait to be rescheduled
|
||||
runtime.Gosched()
|
||||
|
||||
if n > 100 {
|
||||
// if something is using the sleep queue, this may be necessary
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
|
||||
n++
|
||||
}
|
||||
}
|
||||
|
||||
func (wg *waitGroup) add(n uint) {
|
||||
*wg += waitGroup(n)
|
||||
}
|
||||
|
||||
func (wg *waitGroup) done() {
|
||||
if *wg == 0 {
|
||||
panic("wait group underflow")
|
||||
}
|
||||
*wg--
|
||||
}
|
||||
|
||||
var wg waitGroup
|
||||
|
||||
func main() {
|
||||
ch := make(chan int)
|
||||
println("len, cap of channel:", len(ch), cap(ch), ch == nil)
|
||||
|
||||
wg.add(1)
|
||||
go sender(ch)
|
||||
|
||||
n, ok := <-ch
|
||||
println("recv from open channel:", n, ok)
|
||||
|
||||
for n := range ch {
|
||||
if n == 6 {
|
||||
time.Sleep(time.Microsecond)
|
||||
}
|
||||
println("received num:", n)
|
||||
}
|
||||
|
||||
wg.wait()
|
||||
n, ok = <-ch
|
||||
println("recv from closed channel:", n, ok)
|
||||
|
||||
// Test bigger values
|
||||
ch2 := make(chan complex128)
|
||||
wg.add(1)
|
||||
go sendComplex(ch2)
|
||||
println("complex128:", <-ch2)
|
||||
wg.wait()
|
||||
|
||||
// Test multi-sender.
|
||||
ch = make(chan int)
|
||||
wg.add(3)
|
||||
go fastsender(ch, 10)
|
||||
go fastsender(ch, 23)
|
||||
go fastsender(ch, 40)
|
||||
slowreceiver(ch)
|
||||
wg.wait()
|
||||
|
||||
// Test multi-receiver.
|
||||
ch = make(chan int)
|
||||
wg.add(3)
|
||||
go fastreceiver(ch)
|
||||
go fastreceiver(ch)
|
||||
go fastreceiver(ch)
|
||||
slowsender(ch)
|
||||
wg.wait()
|
||||
|
||||
// Test iterator style channel.
|
||||
ch = make(chan int)
|
||||
wg.add(1)
|
||||
go iterator(ch, 100)
|
||||
sum := 0
|
||||
for i := range ch {
|
||||
sum += i
|
||||
}
|
||||
wg.wait()
|
||||
println("sum(100):", sum)
|
||||
|
||||
// Test simple selects.
|
||||
go selectDeadlock()
|
||||
go selectDeadlock() // cannot use waitGroup here - never terminates
|
||||
wg.add(1)
|
||||
go selectNoOp()
|
||||
wg.wait()
|
||||
|
||||
// Test select with a single send operation (transformed into chan send).
|
||||
ch = make(chan int)
|
||||
wg.add(1)
|
||||
go fastreceiver(ch)
|
||||
select {
|
||||
case ch <- 5:
|
||||
}
|
||||
close(ch)
|
||||
time.Sleep(time.Millisecond)
|
||||
wg.wait()
|
||||
println("did send one")
|
||||
|
||||
// Test select with a single recv operation (transformed into chan recv).
|
||||
@@ -70,9 +115,12 @@ func main() {
|
||||
|
||||
// Test select recv with channel that has one entry.
|
||||
ch = make(chan int)
|
||||
wg.add(1)
|
||||
go func(ch chan int) {
|
||||
ch <- 55
|
||||
wg.done()
|
||||
}(ch)
|
||||
// not defined behavior, but we cant really fix this until select has been fixed
|
||||
time.Sleep(time.Millisecond)
|
||||
select {
|
||||
case make(chan int) <- 3:
|
||||
@@ -82,6 +130,7 @@ func main() {
|
||||
case n := <-make(chan int):
|
||||
println("unreachable:", n)
|
||||
}
|
||||
wg.wait()
|
||||
|
||||
// Test select recv with closed channel.
|
||||
close(ch)
|
||||
@@ -96,6 +145,7 @@ func main() {
|
||||
|
||||
// Test select send.
|
||||
ch = make(chan int)
|
||||
wg.add(1)
|
||||
go fastreceiver(ch)
|
||||
time.Sleep(time.Millisecond)
|
||||
select {
|
||||
@@ -105,9 +155,49 @@ func main() {
|
||||
println("unreachable:", n)
|
||||
}
|
||||
close(ch)
|
||||
wg.wait()
|
||||
|
||||
// Allow goroutines to exit.
|
||||
time.Sleep(time.Microsecond)
|
||||
// test non-concurrent buffered channels
|
||||
ch = make(chan int, 2)
|
||||
ch <- 1
|
||||
ch <- 2
|
||||
println("non-concurrent channel recieve:", <-ch)
|
||||
println("non-concurrent channel recieve:", <-ch)
|
||||
|
||||
// test closing channels with buffered data
|
||||
ch <- 3
|
||||
ch <- 4
|
||||
close(ch)
|
||||
println("closed buffered channel recieve:", <-ch)
|
||||
println("closed buffered channel recieve:", <-ch)
|
||||
println("closed buffered channel recieve:", <-ch)
|
||||
|
||||
// test using buffered channels as regular channels with special properties
|
||||
wg.add(6)
|
||||
ch = make(chan int, 2)
|
||||
go send(ch)
|
||||
go send(ch)
|
||||
go send(ch)
|
||||
go send(ch)
|
||||
go receive(ch)
|
||||
go receive(ch)
|
||||
wg.wait()
|
||||
close(ch)
|
||||
var count int
|
||||
for range ch {
|
||||
count++
|
||||
}
|
||||
println("hybrid buffered channel recieve:", count)
|
||||
}
|
||||
|
||||
func send(ch chan<- int) {
|
||||
ch <- 1
|
||||
wg.done()
|
||||
}
|
||||
|
||||
func receive(ch <-chan int) {
|
||||
<-ch
|
||||
wg.done()
|
||||
}
|
||||
|
||||
func sender(ch chan int) {
|
||||
@@ -119,15 +209,18 @@ func sender(ch chan int) {
|
||||
ch <- i
|
||||
}
|
||||
close(ch)
|
||||
wg.done()
|
||||
}
|
||||
|
||||
func sendComplex(ch chan complex128) {
|
||||
ch <- 7 + 10.5i
|
||||
wg.done()
|
||||
}
|
||||
|
||||
func fastsender(ch chan int, n int) {
|
||||
ch <- n
|
||||
ch <- n + 1
|
||||
wg.done()
|
||||
}
|
||||
|
||||
func slowreceiver(ch chan int) {
|
||||
@@ -153,6 +246,7 @@ func fastreceiver(ch chan int) {
|
||||
sum += n
|
||||
}
|
||||
println("sum:", sum)
|
||||
wg.done()
|
||||
}
|
||||
|
||||
func iterator(ch chan int, top int) {
|
||||
@@ -160,6 +254,7 @@ func iterator(ch chan int, top int) {
|
||||
ch <- i
|
||||
}
|
||||
close(ch)
|
||||
wg.done()
|
||||
}
|
||||
|
||||
func selectDeadlock() {
|
||||
@@ -174,4 +269,5 @@ func selectNoOp() {
|
||||
default:
|
||||
}
|
||||
println("after no-op")
|
||||
wg.done()
|
||||
}
|
||||
|
||||
Vendored
+6
@@ -25,3 +25,9 @@ select n from chan: 55
|
||||
select n from closed chan: 0
|
||||
select send
|
||||
sum: 235
|
||||
non-concurrent channel recieve: 1
|
||||
non-concurrent channel recieve: 2
|
||||
closed buffered channel recieve: 3
|
||||
closed buffered channel recieve: 4
|
||||
closed buffered channel recieve: 0
|
||||
hybrid buffered channel recieve: 2
|
||||
|
||||
Vendored
+18
@@ -13,6 +13,11 @@ func main() {
|
||||
println("v5:", len(v5), v5 == nil)
|
||||
println("v6:", v6)
|
||||
println("v7:", cap(v7), string(v7))
|
||||
|
||||
println(uint8SliceSrc[0])
|
||||
println(uint8SliceDst[0])
|
||||
println(intSliceSrc[0])
|
||||
println(intSliceDst[0])
|
||||
}
|
||||
|
||||
type (
|
||||
@@ -30,4 +35,17 @@ var (
|
||||
v5 = map[string]int{}
|
||||
v6 = float64(v1) < 2.6
|
||||
v7 = []byte("foo")
|
||||
|
||||
uint8SliceSrc = []uint8{3, 100}
|
||||
uint8SliceDst []uint8
|
||||
intSliceSrc = []int16{5, 123, 1024}
|
||||
intSliceDst []int16
|
||||
)
|
||||
|
||||
func init() {
|
||||
uint8SliceDst = make([]uint8, len(uint8SliceSrc))
|
||||
copy(uint8SliceDst, uint8SliceSrc)
|
||||
|
||||
intSliceDst = make([]int16, len(intSliceSrc))
|
||||
copy(intSliceDst, intSliceSrc)
|
||||
}
|
||||
|
||||
Vendored
+4
@@ -7,3 +7,7 @@ v4: 0 true
|
||||
v5: 0 false
|
||||
v6: false
|
||||
v7: 3 foo
|
||||
3
|
||||
3
|
||||
5
|
||||
5
|
||||
|
||||
Vendored
+7
@@ -2,6 +2,13 @@ package main
|
||||
|
||||
type MySlice [32]byte
|
||||
|
||||
type myUint8 uint8
|
||||
|
||||
// Indexing into slice with named type (regression test).
|
||||
var array = [4]int{
|
||||
myUint8(2): 3,
|
||||
}
|
||||
|
||||
func main() {
|
||||
l := 5
|
||||
foo := []int{1, 2, 4, 5}
|
||||
|
||||
Vendored
+12
@@ -60,6 +60,13 @@ type s8 struct {
|
||||
b byte // 1 element
|
||||
}
|
||||
|
||||
// linked list (recursive type)
|
||||
type s9 struct {
|
||||
n int
|
||||
next *s9
|
||||
s []*s9
|
||||
}
|
||||
|
||||
func test0(s s0) {
|
||||
println("test0")
|
||||
}
|
||||
@@ -106,6 +113,10 @@ func test8(s s8) {
|
||||
println("test8", len(s.a), cap(s.a), s.a[0], s.a[1], s.b)
|
||||
}
|
||||
|
||||
func test9(s s9) {
|
||||
println("test9", s.next.next)
|
||||
}
|
||||
|
||||
func main() {
|
||||
test0(s0{})
|
||||
test1(s1{1})
|
||||
@@ -119,4 +130,5 @@ func main() {
|
||||
test6(s6{"foo", 5})
|
||||
test7(s7{a: nil, b: 8})
|
||||
test8(s8{[]byte{12, 13, 14}[:2], 6})
|
||||
test9(s9{next: &s9{}})
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user