mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-20 04:19:04 +00:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22b5b69cf0 | |||
| 2081380b5c | |||
| 3d0c6dd02d | |||
| 869e917dc6 | |||
| 41bcad9c19 | |||
| 73ab44c178 | |||
| b70b6076e3 | |||
| 7b41d92198 | |||
| 2f4f3bd1ba | |||
| 7cb44fb373 | |||
| 73ad825b67 | |||
| 539e1324c8 | |||
| 641dcd7c16 | |||
| 523d1f28cf | |||
| 782c57cc11 | |||
| c1d697f868 | |||
| 335fb71d2f | |||
| 5866a47e77 | |||
| 823c9c25cf | |||
| d15e32fb89 | |||
| b534dd67e0 | |||
| 6c02b4956c | |||
| 1681ed02d3 | |||
| 7e68980c39 | |||
| cf640290a3 | |||
| 78fec3719f | |||
| af4d0fe191 |
+164
-85
@@ -6,26 +6,6 @@ commands:
|
|||||||
- run:
|
- run:
|
||||||
name: "Pull submodules"
|
name: "Pull submodules"
|
||||||
command: git submodule update --init
|
command: git submodule update --init
|
||||||
apt-dependencies:
|
|
||||||
parameters:
|
|
||||||
llvm:
|
|
||||||
type: string
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: "Install apt dependencies"
|
|
||||||
command: |
|
|
||||||
echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<parameters.llvm>> main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
|
||||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install --no-install-recommends \
|
|
||||||
llvm-<<parameters.llvm>>-dev \
|
|
||||||
clang-<<parameters.llvm>> \
|
|
||||||
libclang-<<parameters.llvm>>-dev \
|
|
||||||
lld-<<parameters.llvm>> \
|
|
||||||
qemu-system-arm \
|
|
||||||
qemu-user \
|
|
||||||
gcc-avr \
|
|
||||||
avr-libc
|
|
||||||
install-node:
|
install-node:
|
||||||
steps:
|
steps:
|
||||||
- run:
|
- run:
|
||||||
@@ -49,6 +29,13 @@ commands:
|
|||||||
command: |
|
command: |
|
||||||
curl https://wasmtime.dev/install.sh -sSf | bash
|
curl https://wasmtime.dev/install.sh -sSf | bash
|
||||||
sudo ln -s ~/.wasmtime/bin/wasmtime /usr/local/bin/wasmtime
|
sudo ln -s ~/.wasmtime/bin/wasmtime /usr/local/bin/wasmtime
|
||||||
|
install-cmake:
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: "Install CMake"
|
||||||
|
command: |
|
||||||
|
wget https://github.com/Kitware/CMake/releases/download/v3.21.4/cmake-3.21.4-linux-x86_64.tar.gz
|
||||||
|
sudo tar --strip-components=1 -C /usr/local -xf cmake-3.21.4-linux-x86_64.tar.gz
|
||||||
install-xtensa-toolchain:
|
install-xtensa-toolchain:
|
||||||
parameters:
|
parameters:
|
||||||
variant:
|
variant:
|
||||||
@@ -76,6 +63,39 @@ commands:
|
|||||||
- llvm-project/clang/include
|
- llvm-project/clang/include
|
||||||
- llvm-project/lld/include
|
- llvm-project/lld/include
|
||||||
- llvm-project/llvm/include
|
- llvm-project/llvm/include
|
||||||
|
hack-ninja-jobs:
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: "Hack Ninja to use less jobs"
|
||||||
|
command: |
|
||||||
|
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
|
||||||
|
chmod +x /go/bin/ninja
|
||||||
|
build-binaryen-linux:
|
||||||
|
steps:
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- binaryen-linux-v1
|
||||||
|
- run:
|
||||||
|
name: "Build Binaryen"
|
||||||
|
command: |
|
||||||
|
make binaryen
|
||||||
|
- save_cache:
|
||||||
|
key: binaryen-linux-v1
|
||||||
|
paths:
|
||||||
|
- build/wasm-opt
|
||||||
|
build-binaryen-linux-stretch:
|
||||||
|
steps:
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- binaryen-linux-stretch-v1
|
||||||
|
- run:
|
||||||
|
name: "Build Binaryen"
|
||||||
|
command: |
|
||||||
|
CC=$PWD/llvm-build/bin/clang make binaryen
|
||||||
|
- save_cache:
|
||||||
|
key: binaryen-linux-stretch-v1
|
||||||
|
paths:
|
||||||
|
- build/wasm-opt
|
||||||
build-wasi-libc:
|
build-wasi-libc:
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
@@ -95,11 +115,23 @@ commands:
|
|||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- submodules
|
- submodules
|
||||||
- apt-dependencies:
|
- run:
|
||||||
llvm: "<<parameters.llvm>>"
|
name: "Install apt dependencies"
|
||||||
- install-node
|
command: |
|
||||||
- install-chrome
|
echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<parameters.llvm>> main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
||||||
- install-wasmtime
|
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install --no-install-recommends \
|
||||||
|
llvm-<<parameters.llvm>>-dev \
|
||||||
|
clang-<<parameters.llvm>> \
|
||||||
|
libclang-<<parameters.llvm>>-dev \
|
||||||
|
lld-<<parameters.llvm>> \
|
||||||
|
gcc-avr \
|
||||||
|
avr-libc \
|
||||||
|
cmake \
|
||||||
|
ninja-build
|
||||||
|
- hack-ninja-jobs
|
||||||
|
- build-binaryen-linux
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||||
@@ -114,14 +146,8 @@ commands:
|
|||||||
key: wasi-libc-sysroot-systemclang-v3
|
key: wasi-libc-sysroot-systemclang-v3
|
||||||
paths:
|
paths:
|
||||||
- lib/wasi-libc/sysroot
|
- lib/wasi-libc/sysroot
|
||||||
- run:
|
|
||||||
name: "Test TinyGo"
|
|
||||||
command: go test -v -timeout=20m -tags=llvm<<parameters.llvm>> ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
|
||||||
no_output_timeout: 20m
|
|
||||||
- run: make gen-device -j4
|
- run: make gen-device -j4
|
||||||
- run: make smoketest XTENSA=0
|
- run: make smoketest XTENSA=0
|
||||||
- run: make tinygo-test
|
|
||||||
- run: make wasmtest
|
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||||
paths:
|
paths:
|
||||||
@@ -141,9 +167,14 @@ commands:
|
|||||||
qemu-system-arm \
|
qemu-system-arm \
|
||||||
qemu-user \
|
qemu-user \
|
||||||
gcc-avr \
|
gcc-avr \
|
||||||
avr-libc
|
avr-libc \
|
||||||
|
ninja-build \
|
||||||
|
python3
|
||||||
- install-node
|
- install-node
|
||||||
|
- install-chrome
|
||||||
- install-wasmtime
|
- install-wasmtime
|
||||||
|
- install-cmake
|
||||||
|
- hack-ninja-jobs
|
||||||
- install-xtensa-toolchain:
|
- install-xtensa-toolchain:
|
||||||
variant: "linux-amd64"
|
variant: "linux-amd64"
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
@@ -159,14 +190,9 @@ commands:
|
|||||||
command: |
|
command: |
|
||||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
if [ ! -f llvm-build/lib/liblldELF.a ]
|
||||||
then
|
then
|
||||||
# fetch LLVM source
|
# fetch LLVM source (may only have headers right now)
|
||||||
rm -rf llvm-project
|
rm -rf llvm-project
|
||||||
make llvm-source
|
make llvm-source
|
||||||
# install dependencies
|
|
||||||
sudo apt-get install cmake ninja-build
|
|
||||||
# hack ninja to use less jobs
|
|
||||||
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
|
|
||||||
chmod +x /go/bin/ninja
|
|
||||||
# build!
|
# build!
|
||||||
make ASSERT=1 llvm-build
|
make ASSERT=1 llvm-build
|
||||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
@@ -175,7 +201,12 @@ commands:
|
|||||||
key: llvm-build-11-linux-v4-assert
|
key: llvm-build-11-linux-v4-assert
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
- run: make ASSERT=1
|
- build-binaryen-linux-stretch
|
||||||
|
- run:
|
||||||
|
name: "Build TinyGo"
|
||||||
|
command: |
|
||||||
|
make ASSERT=1
|
||||||
|
echo 'export PATH=$(pwd)/build:$PATH' >> $BASH_ENV
|
||||||
- build-wasi-libc
|
- build-wasi-libc
|
||||||
- run:
|
- run:
|
||||||
name: "Test TinyGo"
|
name: "Test TinyGo"
|
||||||
@@ -192,59 +223,67 @@ commands:
|
|||||||
- ~/.cache/go-build
|
- ~/.cache/go-build
|
||||||
- /go/pkg/mod
|
- /go/pkg/mod
|
||||||
- run: make gen-device -j4
|
- run: make gen-device -j4
|
||||||
- run: make smoketest TINYGO=build/tinygo
|
- run: make smoketest
|
||||||
|
- run: make tinygo-test
|
||||||
|
- run: make wasmtest
|
||||||
build-linux:
|
build-linux:
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
|
||||||
- submodules
|
|
||||||
- run:
|
- run:
|
||||||
name: "Install apt dependencies"
|
name: "Install apk dependencies"
|
||||||
command: |
|
command: |
|
||||||
sudo apt-get update
|
apk add git openssh make g++ gcompat
|
||||||
sudo apt-get install --no-install-recommends \
|
- checkout
|
||||||
libgnutls30 libssl1.0.2 \
|
- run:
|
||||||
qemu-system-arm \
|
name: "Install Go"
|
||||||
qemu-user \
|
command: |
|
||||||
gcc-avr \
|
wget https://dl.google.com/go/go1.17.3.linux-amd64.tar.gz
|
||||||
avr-libc
|
tar -C /usr/local -xzf go1.17.3.linux-amd64.tar.gz
|
||||||
- install-node
|
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
||||||
- install-wasmtime
|
#- submodules
|
||||||
- install-xtensa-toolchain:
|
|
||||||
variant: "linux-amd64"
|
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
name: "Restore Go cache"
|
||||||
keys:
|
keys:
|
||||||
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
- go-cache-alpine-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||||
- go-cache-v2-{{ checksum "go.mod" }}
|
- go-cache-alpine-v2-{{ checksum "go.mod" }}
|
||||||
- llvm-source-linux
|
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
name: "Restore llvm-source cache"
|
||||||
keys:
|
keys:
|
||||||
- llvm-build-11-linux-v4-noassert
|
- llvm-source-11-alpine-v2-x0
|
||||||
|
- run:
|
||||||
|
name: "Fetch LLVM source"
|
||||||
|
command: make llvm-source
|
||||||
|
- save_cache:
|
||||||
|
name: "Save llvm-source cache"
|
||||||
|
key: llvm-source-11-alpine-v2-x0
|
||||||
|
paths:
|
||||||
|
- llvm-project
|
||||||
|
#- llvm-project/clang/lib/Headers
|
||||||
|
#- llvm-project/clang/include
|
||||||
|
#- llvm-project/lld/include
|
||||||
|
#- llvm-project/llvm/include
|
||||||
|
- restore_cache:
|
||||||
|
name: "Restore llvm-build cache"
|
||||||
|
keys:
|
||||||
|
- llvm-build-11-alpine-v1-noassert-x0
|
||||||
- run:
|
- run:
|
||||||
name: "Build LLVM"
|
name: "Build LLVM"
|
||||||
command: |
|
command: |
|
||||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
if [ ! -f llvm-build/lib/liblldELF.a ]
|
||||||
then
|
then
|
||||||
# fetch LLVM source
|
# fetch LLVM source (may only have headers right now)
|
||||||
rm -rf llvm-project
|
#rm -rf llvm-project
|
||||||
make llvm-source
|
#make llvm-source
|
||||||
# install dependencies
|
# install dependencies
|
||||||
sudo apt-get install cmake ninja-build
|
apk add cmake samurai python3
|
||||||
# hack ninja to use less jobs
|
|
||||||
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
|
|
||||||
chmod +x /go/bin/ninja
|
|
||||||
# build!
|
# build!
|
||||||
make llvm-build
|
SAMUFLAGS=-j3 make llvm-build
|
||||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
#find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
fi
|
fi
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-build-11-linux-v4-noassert
|
name: "Save llvm-build cache"
|
||||||
|
key: llvm-build-11-alpine-v1-noassert-x0
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
- build-wasi-libc
|
|
||||||
- run:
|
|
||||||
name: "Test TinyGo"
|
|
||||||
command: make test
|
|
||||||
no_output_timeout: 20m
|
|
||||||
- run:
|
- run:
|
||||||
name: "Install fpm"
|
name: "Install fpm"
|
||||||
command: |
|
command: |
|
||||||
@@ -256,21 +295,41 @@ commands:
|
|||||||
make release deb -j3
|
make release deb -j3
|
||||||
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
|
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
|
||||||
cp -p build/release.deb /tmp/tinygo_amd64.deb
|
cp -p build/release.deb /tmp/tinygo_amd64.deb
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: /tmp
|
||||||
|
paths:
|
||||||
|
- tinygo.linux-amd64.tar.gz
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: /tmp/tinygo.linux-amd64.tar.gz
|
path: /tmp/tinygo.linux-amd64.tar.gz
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: /tmp/tinygo_amd64.deb
|
path: /tmp/tinygo_amd64.deb
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
name: "Save Go cache"
|
||||||
|
key: go-cache-alpine-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||||
paths:
|
paths:
|
||||||
- ~/.cache/go-build
|
- ~/.cache/go-build
|
||||||
- /go/pkg/mod
|
- /go/pkg/mod
|
||||||
|
test-linux-build:
|
||||||
|
# Now run the smoke tests for the generated binary.
|
||||||
|
steps:
|
||||||
|
- attach_workspace:
|
||||||
|
at: /tmp/workspace
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: "Install apt dependencies"
|
||||||
|
command: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install --no-install-recommends \
|
||||||
|
gcc-avr \
|
||||||
|
avr-libc
|
||||||
|
- install-xtensa-toolchain:
|
||||||
|
variant: "linux-amd64"
|
||||||
- run:
|
- run:
|
||||||
name: "Extract release tarball"
|
name: "Extract release tarball"
|
||||||
command: |
|
command: |
|
||||||
mkdir -p ~/lib
|
mkdir -p ~/lib
|
||||||
tar -C ~/lib -xf /tmp/tinygo.linux-amd64.tar.gz
|
tar -C ~/lib -xf /tmp/workspace/tinygo.linux-amd64.tar.gz
|
||||||
ln -s ~/lib/tinygo/bin/tinygo /go/bin/tinygo
|
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
|
||||||
tinygo version
|
tinygo version
|
||||||
- run: make smoketest
|
- run: make smoketest
|
||||||
build-macos:
|
build-macos:
|
||||||
@@ -283,7 +342,7 @@ commands:
|
|||||||
curl https://dl.google.com/go/go1.17.darwin-amd64.tar.gz -o go1.17.darwin-amd64.tar.gz
|
curl https://dl.google.com/go/go1.17.darwin-amd64.tar.gz -o go1.17.darwin-amd64.tar.gz
|
||||||
sudo tar -C /usr/local -xzf go1.17.darwin-amd64.tar.gz
|
sudo tar -C /usr/local -xzf go1.17.darwin-amd64.tar.gz
|
||||||
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
||||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
|
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu cmake ninja
|
||||||
- install-xtensa-toolchain:
|
- install-xtensa-toolchain:
|
||||||
variant: "macos"
|
variant: "macos"
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
@@ -311,11 +370,9 @@ commands:
|
|||||||
command: |
|
command: |
|
||||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
if [ ! -f llvm-build/lib/liblldELF.a ]
|
||||||
then
|
then
|
||||||
# fetch LLVM source
|
# fetch LLVM source (may only have headers right now)
|
||||||
rm -rf llvm-project
|
rm -rf llvm-project
|
||||||
make llvm-source
|
make llvm-source
|
||||||
# install dependencies
|
|
||||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake ninja
|
|
||||||
# build!
|
# build!
|
||||||
make llvm-build
|
make llvm-build
|
||||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
@@ -324,6 +381,20 @@ commands:
|
|||||||
key: llvm-build-11-macos-v5
|
key: llvm-build-11-macos-v5
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- binaryen-macos-v1
|
||||||
|
- run:
|
||||||
|
name: "Build Binaryen"
|
||||||
|
command: |
|
||||||
|
if [ ! -f build/wasm-opt ]
|
||||||
|
then
|
||||||
|
make binaryen
|
||||||
|
fi
|
||||||
|
- save_cache:
|
||||||
|
key: binaryen-macos-v1
|
||||||
|
paths:
|
||||||
|
- build/wasm-opt
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- wasi-libc-sysroot-macos-v4
|
- wasi-libc-sysroot-macos-v4
|
||||||
@@ -379,9 +450,14 @@ jobs:
|
|||||||
- assert-test-linux
|
- assert-test-linux
|
||||||
build-linux:
|
build-linux:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.17-stretch
|
- image: alpine:3.14
|
||||||
steps:
|
steps:
|
||||||
- build-linux
|
- build-linux
|
||||||
|
test-linux-build:
|
||||||
|
docker:
|
||||||
|
- image: cimg/go:1.17
|
||||||
|
steps:
|
||||||
|
- test-linux-build
|
||||||
build-macos:
|
build-macos:
|
||||||
macos:
|
macos:
|
||||||
xcode: "11.1.0" # macOS 10.14
|
xcode: "11.1.0" # macOS 10.14
|
||||||
@@ -393,8 +469,11 @@ jobs:
|
|||||||
workflows:
|
workflows:
|
||||||
test-all:
|
test-all:
|
||||||
jobs:
|
jobs:
|
||||||
- test-llvm11-go115
|
#- test-llvm11-go115
|
||||||
- test-llvm11-go116
|
#- test-llvm11-go116
|
||||||
- build-linux
|
- build-linux
|
||||||
- build-macos
|
- test-linux-build:
|
||||||
- assert-test-linux
|
requires:
|
||||||
|
- build-linux
|
||||||
|
#- build-macos
|
||||||
|
#- assert-test-linux
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
name: Windows
|
name: Windows
|
||||||
|
|
||||||
on: push
|
on:
|
||||||
|
#pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
- release
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-windows:
|
build-windows:
|
||||||
runs-on: windows-2016
|
runs-on: windows-2019
|
||||||
steps:
|
steps:
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
@@ -15,6 +20,10 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
choco install qemu --version=2020.06.12
|
choco install qemu --version=2020.06.12
|
||||||
echo "C:\Program Files\QEMU" >> $GITHUB_PATH
|
echo "C:\Program Files\QEMU" >> $GITHUB_PATH
|
||||||
|
- name: Install Ninja
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
choco install ninja
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
@@ -45,8 +54,6 @@ jobs:
|
|||||||
# fetch LLVM source
|
# fetch LLVM source
|
||||||
rm -rf llvm-project
|
rm -rf llvm-project
|
||||||
make llvm-source
|
make llvm-source
|
||||||
# install dependencies
|
|
||||||
choco install ninja
|
|
||||||
# build!
|
# build!
|
||||||
make llvm-build
|
make llvm-build
|
||||||
# Remove unnecessary object files (to reduce cache size).
|
# Remove unnecessary object files (to reduce cache size).
|
||||||
@@ -60,6 +67,15 @@ jobs:
|
|||||||
- name: Build wasi-libc
|
- name: Build wasi-libc
|
||||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||||
run: make wasi-libc
|
run: make wasi-libc
|
||||||
|
- name: Cache Binaryen
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-binaryen
|
||||||
|
with:
|
||||||
|
key: binaryen-v1
|
||||||
|
path: build/binaryen
|
||||||
|
- name: Build Binaryen
|
||||||
|
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||||
|
run: make binaryen
|
||||||
- name: Test TinyGo
|
- name: Test TinyGo
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make test
|
run: make test
|
||||||
@@ -83,3 +99,5 @@ jobs:
|
|||||||
- name: Smoke tests
|
- name: Smoke tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make smoketest TINYGO=build/tinygo AVR=0 XTENSA=0
|
run: make smoketest TINYGO=build/tinygo AVR=0 XTENSA=0
|
||||||
|
- name: Test stdlib packages
|
||||||
|
run: make tinygo-test
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
build
|
|
||||||
docs/_build
|
docs/_build
|
||||||
src/device/avr/*.go
|
src/device/avr/*.go
|
||||||
src/device/avr/*.ld
|
src/device/avr/*.ld
|
||||||
|
|||||||
@@ -26,3 +26,9 @@
|
|||||||
[submodule "lib/musl"]
|
[submodule "lib/musl"]
|
||||||
path = lib/musl
|
path = lib/musl
|
||||||
url = git://git.musl-libc.org/musl
|
url = git://git.musl-libc.org/musl
|
||||||
|
[submodule "lib/binaryen"]
|
||||||
|
path = lib/binaryen
|
||||||
|
url = https://github.com/WebAssembly/binaryen.git
|
||||||
|
[submodule "lib/mingw-w64"]
|
||||||
|
path = lib/mingw-w64
|
||||||
|
url = https://github.com/mingw-w64/mingw-w64.git
|
||||||
|
|||||||
+3
-2
@@ -29,8 +29,9 @@ COPY --from=tinygo-base /tinygo/targets /tinygo/targets
|
|||||||
|
|
||||||
RUN cd /tinygo/ && \
|
RUN cd /tinygo/ && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y make clang-11 libllvm11 lld-11 && \
|
apt-get install -y make clang-11 libllvm11 lld-11 cmake ninja-build && \
|
||||||
make wasi-libc
|
mkdir build && \
|
||||||
|
make wasi-libc binaryen
|
||||||
|
|
||||||
# tinygo-avr stage installs the needed dependencies to compile TinyGo programs for AVR microcontrollers.
|
# tinygo-avr stage installs the needed dependencies to compile TinyGo programs for AVR microcontrollers.
|
||||||
FROM tinygo-base AS tinygo-avr
|
FROM tinygo-base AS tinygo-avr
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ endif
|
|||||||
|
|
||||||
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
|
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
|
||||||
|
|
||||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf executionengine frontendopenmp instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target
|
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsmanifest
|
||||||
|
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
EXE = .exe
|
EXE = .exe
|
||||||
@@ -54,6 +54,8 @@ ifeq ($(OS),Windows_NT)
|
|||||||
CGO_LDFLAGS += -static -static-libgcc -static-libstdc++
|
CGO_LDFLAGS += -static -static-libgcc -static-libstdc++
|
||||||
CGO_LDFLAGS_EXTRA += -lversion
|
CGO_LDFLAGS_EXTRA += -lversion
|
||||||
|
|
||||||
|
BINARYEN_OPTION += -DCMAKE_EXE_LINKER_FLAGS='-static-libgcc -static-libstdc++'
|
||||||
|
|
||||||
LIBCLANG_NAME = libclang
|
LIBCLANG_NAME = libclang
|
||||||
|
|
||||||
else ifeq ($(shell uname -s),Darwin)
|
else ifeq ($(shell uname -s),Darwin)
|
||||||
@@ -163,12 +165,18 @@ llvm-source: $(LLVM_PROJECTDIR)/llvm
|
|||||||
# Configure LLVM.
|
# Configure LLVM.
|
||||||
TINYGO_SOURCE_DIR=$(shell pwd)
|
TINYGO_SOURCE_DIR=$(shell pwd)
|
||||||
$(LLVM_BUILDDIR)/build.ninja: llvm-source
|
$(LLVM_BUILDDIR)/build.ninja: llvm-source
|
||||||
mkdir -p $(LLVM_BUILDDIR); cd $(LLVM_BUILDDIR); cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
|
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
|
||||||
|
|
||||||
# Build LLVM.
|
# Build LLVM.
|
||||||
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
$(LLVM_BUILDDIR): #$(LLVM_BUILDDIR)/build.ninja
|
||||||
cd $(LLVM_BUILDDIR); ninja $(NINJA_BUILD_TARGETS)
|
cd $(LLVM_BUILDDIR) && ninja llvm-nm #$(NINJA_BUILD_TARGETS)
|
||||||
|
|
||||||
|
# Build Binaryen
|
||||||
|
.PHONY: binaryen
|
||||||
|
binaryen: build/wasm-opt
|
||||||
|
build/wasm-opt:
|
||||||
|
cd lib/binaryen && cmake -G Ninja . -DBUILD_STATIC_LIB=ON $(BINARYEN_OPTION) && ninja
|
||||||
|
cp lib/binaryen/bin/wasm-opt build/wasm-opt
|
||||||
|
|
||||||
# Build wasi-libc sysroot
|
# Build wasi-libc sysroot
|
||||||
.PHONY: wasi-libc
|
.PHONY: wasi-libc
|
||||||
@@ -208,13 +216,16 @@ TEST_PACKAGES = \
|
|||||||
hash/crc64 \
|
hash/crc64 \
|
||||||
html \
|
html \
|
||||||
index/suffixarray \
|
index/suffixarray \
|
||||||
|
internal/itoa \
|
||||||
math \
|
math \
|
||||||
math/cmplx \
|
math/cmplx \
|
||||||
|
net/mail \
|
||||||
|
reflect \
|
||||||
testing \
|
testing \
|
||||||
testing/iotest \
|
testing/iotest \
|
||||||
text/scanner \
|
text/scanner \
|
||||||
text/scanner \
|
|
||||||
unicode \
|
unicode \
|
||||||
|
unicode/utf16 \
|
||||||
unicode/utf8 \
|
unicode/utf8 \
|
||||||
|
|
||||||
# Test known-working standard library packages.
|
# Test known-working standard library packages.
|
||||||
@@ -466,7 +477,10 @@ endif
|
|||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
||||||
|
GOOS=windows GOARCH=amd64 $(TINYGO) build -o test.exe ./testdata/cgo
|
||||||
ifneq ($(OS),Windows_NT)
|
ifneq ($(OS),Windows_NT)
|
||||||
|
# TODO: this does not yet work on Windows. Somehow, unused functions are
|
||||||
|
# not garbage collected.
|
||||||
$(TINYGO) build -o test.elf -gc=leaking -scheduler=none examples/serial
|
$(TINYGO) build -o test.elf -gc=leaking -scheduler=none examples/serial
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -474,11 +488,13 @@ endif
|
|||||||
wasmtest:
|
wasmtest:
|
||||||
$(GO) test ./tests/wasm
|
$(GO) test ./tests/wasm
|
||||||
|
|
||||||
build/release: tinygo gen-device wasi-libc
|
build/release: tinygo gen-device wasi-libc binaryen
|
||||||
@mkdir -p build/release/tinygo/bin
|
@mkdir -p build/release/tinygo/bin
|
||||||
@mkdir -p build/release/tinygo/lib/clang/include
|
@mkdir -p build/release/tinygo/lib/clang/include
|
||||||
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
||||||
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
|
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
|
||||||
|
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||||
|
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
|
||||||
@mkdir -p build/release/tinygo/lib/musl/arch
|
@mkdir -p build/release/tinygo/lib/musl/arch
|
||||||
@mkdir -p build/release/tinygo/lib/musl/crt
|
@mkdir -p build/release/tinygo/lib/musl/crt
|
||||||
@mkdir -p build/release/tinygo/lib/musl/src
|
@mkdir -p build/release/tinygo/lib/musl/src
|
||||||
@@ -491,6 +507,7 @@ build/release: tinygo gen-device wasi-libc
|
|||||||
@mkdir -p build/release/tinygo/pkg/armv7em-unknown-unknown-eabi
|
@mkdir -p build/release/tinygo/pkg/armv7em-unknown-unknown-eabi
|
||||||
@echo copying source files
|
@echo copying source files
|
||||||
@cp -p build/tinygo$(EXE) build/release/tinygo/bin
|
@cp -p build/tinygo$(EXE) build/release/tinygo/bin
|
||||||
|
@cp -p build/wasm-opt$(EXE) build/release/tinygo/bin
|
||||||
@cp -p $(abspath $(CLANG_SRC))/lib/Headers/*.h build/release/tinygo/lib/clang/include
|
@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/CMSIS/Include build/release/tinygo/lib/CMSIS/CMSIS
|
||||||
@cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS
|
@cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS
|
||||||
@@ -518,6 +535,11 @@ build/release: tinygo gen-device wasi-libc
|
|||||||
@cp -rp lib/musl/src/thread build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/thread build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/time build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/time build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/unistd build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/unistd build/release/tinygo/lib/musl/src
|
||||||
|
@cp -rp lib/mingw-w64/mingw-w64-crt/def-include build/release/tinygo/lib/mingw-w64/mingw-w64-crt
|
||||||
|
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/api-ms-win-crt-* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||||
|
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/kernel32.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||||
|
@cp -rp lib/mingw-w64/mingw-w64-headers/crt/ build/release/tinygo/lib/mingw-w64/mingw-w64-headers
|
||||||
|
@cp -rp lib/mingw-w64/mingw-w64-headers/defaults/include build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
|
||||||
@cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx
|
@cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx
|
||||||
@cp -rp lib/picolibc/newlib/libc/ctype build/release/tinygo/lib/picolibc/newlib/libc
|
@cp -rp lib/picolibc/newlib/libc/ctype build/release/tinygo/lib/picolibc/newlib/libc
|
||||||
@cp -rp lib/picolibc/newlib/libc/include build/release/tinygo/lib/picolibc/newlib/libc
|
@cp -rp lib/picolibc/newlib/libc/include build/release/tinygo/lib/picolibc/newlib/libc
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
+30
-6
@@ -3,6 +3,7 @@ package builder
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
|
"debug/pe"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -39,10 +40,7 @@ func makeArchive(arfile *os.File, objs []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read the symbols and add them to the symbol table.
|
// Read the symbols and add them to the symbol table.
|
||||||
dbg, err := elf.NewFile(objfile)
|
if dbg, err := elf.NewFile(objfile); err == nil {
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to open file %s: %w", objpath, err)
|
|
||||||
}
|
|
||||||
symbols, err := dbg.Symbols()
|
symbols, err := dbg.Symbols()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -63,6 +61,22 @@ func makeArchive(arfile *os.File, objs []string) error {
|
|||||||
fileIndex int
|
fileIndex int
|
||||||
}{symbol.Name, i})
|
}{symbol.Name, i})
|
||||||
}
|
}
|
||||||
|
} else if dbg, err := pe.NewFile(objfile); err == nil {
|
||||||
|
for _, symbol := range dbg.Symbols {
|
||||||
|
if symbol.StorageClass != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if symbol.SectionNumber == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
symbolTable = append(symbolTable, struct {
|
||||||
|
name string
|
||||||
|
fileIndex int
|
||||||
|
}{symbol.Name, i})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("failed to open file %s as ELF or PE/COFF: %w", objpath, err)
|
||||||
|
}
|
||||||
|
|
||||||
// Close file, to avoid issues with too many open files (especially on
|
// Close file, to avoid issues with too many open files (especially on
|
||||||
// MacOS X).
|
// MacOS X).
|
||||||
@@ -120,11 +134,13 @@ func makeArchive(arfile *os.File, objs []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add all object files to the archive.
|
// Add all object files to the archive.
|
||||||
|
var copyBuf bytes.Buffer
|
||||||
for i, objpath := range objs {
|
for i, objpath := range objs {
|
||||||
objfile, err := os.Open(objpath)
|
objfile, err := os.Open(objpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer objfile.Close()
|
||||||
|
|
||||||
// Store the start index, for when we'll update the symbol table with
|
// Store the start index, for when we'll update the symbol table with
|
||||||
// the correct file start indices.
|
// the correct file start indices.
|
||||||
@@ -155,13 +171,21 @@ func makeArchive(arfile *os.File, objs []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copy the file contents into the archive.
|
// Copy the file contents into the archive.
|
||||||
n, err := io.Copy(arwriter, objfile)
|
// First load all contents into a buffer, then write it all in one go to
|
||||||
|
// the archive file. This is a bit complicated, but is necessary because
|
||||||
|
// io.Copy can't deal with files that are of an odd size.
|
||||||
|
copyBuf.Reset()
|
||||||
|
n, err := io.Copy(©Buf, objfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("could not copy object file into ar file: %w", err)
|
||||||
}
|
}
|
||||||
if n != st.Size() {
|
if n != st.Size() {
|
||||||
return errors.New("file modified during ar creation: " + arfile.Name())
|
return errors.New("file modified during ar creation: " + arfile.Name())
|
||||||
}
|
}
|
||||||
|
_, err = arwriter.Write(copyBuf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not copy object file into ar file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// File is not needed anymore.
|
// File is not needed anymore.
|
||||||
objfile.Close()
|
objfile.Close()
|
||||||
|
|||||||
+42
-1
@@ -16,9 +16,11 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/cgo"
|
"github.com/tinygo-org/tinygo/cgo"
|
||||||
@@ -113,6 +115,12 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
||||||
}
|
}
|
||||||
libcDependencies = append(libcDependencies, dummyCompileJob(path))
|
libcDependencies = append(libcDependencies, dummyCompileJob(path))
|
||||||
|
case "mingw-w64":
|
||||||
|
_, err := MinGW.load(config, dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
libcDependencies = append(libcDependencies, makeMinGWExtraLibs(dir)...)
|
||||||
case "":
|
case "":
|
||||||
// no library specified, so nothing to do
|
// no library specified, so nothing to do
|
||||||
default:
|
default:
|
||||||
@@ -136,7 +144,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
DefaultStackSize: config.Target.DefaultStackSize,
|
DefaultStackSize: config.Target.DefaultStackSize,
|
||||||
NeedsStackObjects: config.NeedsStackObjects(),
|
NeedsStackObjects: config.NeedsStackObjects(),
|
||||||
Debug: true,
|
Debug: true,
|
||||||
LLVMFeatures: config.LLVMFeatures(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the target machine, which is the LLVM object that contains all
|
// Load the target machine, which is the LLVM object that contains all
|
||||||
@@ -517,6 +524,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// Prepare link command.
|
// Prepare link command.
|
||||||
linkerDependencies := []*compileJob{outputObjectFileJob}
|
linkerDependencies := []*compileJob{outputObjectFileJob}
|
||||||
executable := filepath.Join(dir, "main")
|
executable := filepath.Join(dir, "main")
|
||||||
|
if config.GOOS() == "windows" {
|
||||||
|
executable += ".exe"
|
||||||
|
}
|
||||||
tmppath := executable // final file
|
tmppath := executable // final file
|
||||||
ldflags := append(config.LDFlags(), "-o", executable)
|
ldflags := append(config.LDFlags(), "-o", executable)
|
||||||
|
|
||||||
@@ -659,6 +669,37 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run wasm-opt if necessary.
|
||||||
|
if config.Scheduler() == "asyncify" {
|
||||||
|
var optLevel, shrinkLevel int
|
||||||
|
switch config.Options.Opt {
|
||||||
|
case "none", "0":
|
||||||
|
case "1":
|
||||||
|
optLevel = 1
|
||||||
|
case "2":
|
||||||
|
optLevel = 2
|
||||||
|
case "s":
|
||||||
|
optLevel = 2
|
||||||
|
shrinkLevel = 1
|
||||||
|
case "z":
|
||||||
|
optLevel = 2
|
||||||
|
shrinkLevel = 2
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown opt level: %q", config.Options.Opt)
|
||||||
|
}
|
||||||
|
cmd := exec.Command(goenv.Get("WASMOPT"), "--asyncify", "-g",
|
||||||
|
"--optimize-level", strconv.Itoa(optLevel),
|
||||||
|
"--shrink-level", strconv.Itoa(shrinkLevel),
|
||||||
|
executable, "--output", executable)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("wasm-opt failed: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Print code size if requested.
|
// Print code size if requested.
|
||||||
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
|
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
|
||||||
packagePathMap := make(map[string]string, len(lprogram.Packages))
|
packagePathMap := make(map[string]string, len(lprogram.Packages))
|
||||||
|
|||||||
+22
-5
@@ -12,8 +12,8 @@ import (
|
|||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Test whether the Clang generated "target-cpu" attribute matches the CPU
|
// Test whether the Clang generated "target-cpu" and "target-features"
|
||||||
// property in TinyGo target files.
|
// attributes match the CPU and Features property in TinyGo target files.
|
||||||
func TestClangAttributes(t *testing.T) {
|
func TestClangAttributes(t *testing.T) {
|
||||||
var targetNames = []string{
|
var targetNames = []string{
|
||||||
// Please keep this list sorted!
|
// Please keep this list sorted!
|
||||||
@@ -52,10 +52,13 @@ func TestClangAttributes(t *testing.T) {
|
|||||||
for _, options := range []*compileopts.Options{
|
for _, options := range []*compileopts.Options{
|
||||||
{GOOS: "linux", GOARCH: "386"},
|
{GOOS: "linux", GOARCH: "386"},
|
||||||
{GOOS: "linux", GOARCH: "amd64"},
|
{GOOS: "linux", GOARCH: "amd64"},
|
||||||
{GOOS: "linux", GOARCH: "arm"},
|
{GOOS: "linux", GOARCH: "arm", GOARM: "5"},
|
||||||
|
{GOOS: "linux", GOARCH: "arm", GOARM: "6"},
|
||||||
|
{GOOS: "linux", GOARCH: "arm", GOARM: "7"},
|
||||||
{GOOS: "linux", GOARCH: "arm64"},
|
{GOOS: "linux", GOARCH: "arm64"},
|
||||||
{GOOS: "darwin", GOARCH: "amd64"},
|
{GOOS: "darwin", GOARCH: "amd64"},
|
||||||
{GOOS: "darwin", GOARCH: "arm64"},
|
{GOOS: "darwin", GOARCH: "arm64"},
|
||||||
|
{GOOS: "windows", GOARCH: "amd64"},
|
||||||
} {
|
} {
|
||||||
t.Run("GOOS="+options.GOOS+",GOARCH="+options.GOARCH, func(t *testing.T) {
|
t.Run("GOOS="+options.GOOS+",GOARCH="+options.GOARCH, func(t *testing.T) {
|
||||||
testClangAttributes(t, options)
|
testClangAttributes(t, options)
|
||||||
@@ -112,16 +115,30 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
|||||||
t.Errorf("target has LLVM triple %#v but Clang makes it LLVM triple %#v", config.Triple(), mod.Target())
|
t.Errorf("target has LLVM triple %#v but Clang makes it LLVM triple %#v", config.Triple(), mod.Target())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the "target-cpu" string attribute of the add function.
|
// Check the "target-cpu" and "target-features" string attribute of the add
|
||||||
|
// function.
|
||||||
add := mod.NamedFunction("add")
|
add := mod.NamedFunction("add")
|
||||||
var cpu string
|
var cpu, features string
|
||||||
cpuAttr := add.GetStringAttributeAtIndex(-1, "target-cpu")
|
cpuAttr := add.GetStringAttributeAtIndex(-1, "target-cpu")
|
||||||
|
featuresAttr := add.GetStringAttributeAtIndex(-1, "target-features")
|
||||||
if !cpuAttr.IsNil() {
|
if !cpuAttr.IsNil() {
|
||||||
cpu = cpuAttr.GetStringValue()
|
cpu = cpuAttr.GetStringValue()
|
||||||
}
|
}
|
||||||
|
if !featuresAttr.IsNil() {
|
||||||
|
features = featuresAttr.GetStringValue()
|
||||||
|
}
|
||||||
if cpu != config.CPU() {
|
if cpu != config.CPU() {
|
||||||
t.Errorf("target has CPU %#v but Clang makes it CPU %#v", config.CPU(), cpu)
|
t.Errorf("target has CPU %#v but Clang makes it CPU %#v", config.CPU(), cpu)
|
||||||
}
|
}
|
||||||
|
if features != config.Features() {
|
||||||
|
if llvm.Version != "11.0.0" {
|
||||||
|
// This needs to be removed once we switch to LLVM 12.
|
||||||
|
// LLVM 11.0.0 uses a different "target-features" string than LLVM
|
||||||
|
// 11.1.0 for Thumb targets. The Xtensa fork is still based on LLVM
|
||||||
|
// 11.0.0, so we need to skip this check on that version.
|
||||||
|
t.Errorf("target has LLVM features %#v but Clang makes it %#v", config.Features(), features)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This TestMain is necessary because TinyGo may also be invoked to run certain
|
// This TestMain is necessary because TinyGo may also be invoked to run certain
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ bool tinygo_link_elf(int argc, char **argv) {
|
|||||||
return lld::elf::link(args, false, llvm::outs(), llvm::errs());
|
return lld::elf::link(args, false, llvm::outs(), llvm::errs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool tinygo_link_mingw(int argc, char **argv) {
|
||||||
|
std::vector<const char*> args(argv, argv + argc);
|
||||||
|
return lld::mingw::link(args, false, llvm::outs(), llvm::errs());
|
||||||
|
}
|
||||||
|
|
||||||
bool tinygo_link_wasm(int argc, char **argv) {
|
bool tinygo_link_wasm(int argc, char **argv) {
|
||||||
std::vector<const char*> args(argv, argv + argc);
|
std::vector<const char*> args(argv, argv + argc);
|
||||||
return lld::wasm::link(args, false, llvm::outs(), llvm::errs());
|
return lld::wasm::link(args, false, llvm::outs(), llvm::errs());
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var MinGW = Library{
|
||||||
|
name: "mingw-w64",
|
||||||
|
makeHeaders: func(target, includeDir string) error {
|
||||||
|
// copy _mingw.h
|
||||||
|
srcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib", "mingw-w64")
|
||||||
|
outf, err := os.Create(includeDir + "/_mingw.h")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer outf.Close()
|
||||||
|
inf, err := os.Open(srcDir + "/mingw-w64-headers/crt/_mingw.h.in")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = io.Copy(outf, inf)
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
cflags: func(target, headerPath string) []string {
|
||||||
|
// No flags necessary because there are no files to compile.
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
librarySources: func(target string) []string {
|
||||||
|
// We only use the UCRT DLL file. No source files necessary.
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeMinGWExtraLibs returns a slice of jobs to import the correct .dll
|
||||||
|
// libraries. This is done by converting input .def files to .lib files which
|
||||||
|
// can then be linked as usual.
|
||||||
|
//
|
||||||
|
// TODO: cache the result. At the moment, it costs a few hundred milliseconds to
|
||||||
|
// compile these files.
|
||||||
|
func makeMinGWExtraLibs(tmpdir string) []*compileJob {
|
||||||
|
var jobs []*compileJob
|
||||||
|
root := goenv.Get("TINYGOROOT")
|
||||||
|
// Normally all the api-ms-win-crt-*.def files are all compiled to a single
|
||||||
|
// .lib file. But to simplify things, we're going to leave them as separate
|
||||||
|
// files.
|
||||||
|
for _, name := range []string{
|
||||||
|
"kernel32.def.in",
|
||||||
|
"api-ms-win-crt-conio-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-convert-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-environment-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-filesystem-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-heap-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-locale-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-math-l1-1-0.def.in",
|
||||||
|
"api-ms-win-crt-multibyte-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-private-l1-1-0.def.in",
|
||||||
|
"api-ms-win-crt-process-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-runtime-l1-1-0.def.in",
|
||||||
|
"api-ms-win-crt-stdio-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-string-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-time-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-utility-l1-1-0.def",
|
||||||
|
} {
|
||||||
|
outpath := filepath.Join(tmpdir, filepath.Base(name)+".lib")
|
||||||
|
inpath := filepath.Join(root, "lib/mingw-w64/mingw-w64-crt/lib-common/"+name)
|
||||||
|
job := &compileJob{
|
||||||
|
description: "create lib file " + inpath,
|
||||||
|
result: outpath,
|
||||||
|
run: func(job *compileJob) error {
|
||||||
|
defpath := inpath
|
||||||
|
if strings.HasSuffix(inpath, ".in") {
|
||||||
|
// .in files need to be preprocessed by a preprocessor (-E)
|
||||||
|
// first.
|
||||||
|
defpath = outpath + ".def"
|
||||||
|
err := runCCompiler("-E", "-x", "c", "-Wp,-w", "-P", "-DDEF_X64", "-o", defpath, inpath, "-I"+goenv.Get("TINYGOROOT")+"/lib/mingw-w64/mingw-w64-crt/def-include/")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return link("ld.lld", "-m", "i386pep", "-o", outpath, defpath)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
jobs = append(jobs, job)
|
||||||
|
}
|
||||||
|
return jobs
|
||||||
|
}
|
||||||
@@ -288,6 +288,13 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
|||||||
if symType != elf.STT_FUNC && symType != elf.STT_OBJECT && symType != elf.STT_NOTYPE {
|
if symType != elf.STT_FUNC && symType != elf.STT_OBJECT && symType != elf.STT_NOTYPE {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if symbol.Section >= elf.SHN_LORESERVE {
|
||||||
|
// Not a regular section, so skip it.
|
||||||
|
// One example is elf.SHN_ABS, which is used for symbols
|
||||||
|
// declared with an absolute value such as the memset function
|
||||||
|
// on the ESP32 which is defined in the mask ROM.
|
||||||
|
continue
|
||||||
|
}
|
||||||
section := file.Sections[symbol.Section]
|
section := file.Sections[symbol.Section]
|
||||||
if section.Flags&elf.SHF_ALLOC == 0 {
|
if section.Flags&elf.SHF_ALLOC == 0 {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
bool tinygo_clang_driver(int argc, char **argv);
|
bool tinygo_clang_driver(int argc, char **argv);
|
||||||
bool tinygo_link_elf(int argc, char **argv);
|
bool tinygo_link_elf(int argc, char **argv);
|
||||||
|
bool tinygo_link_mingw(int argc, char **argv);
|
||||||
bool tinygo_link_wasm(int argc, char **argv);
|
bool tinygo_link_wasm(int argc, char **argv);
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
@@ -24,6 +25,10 @@ const hasBuiltinTools = true
|
|||||||
// This version actually runs the tools because TinyGo was compiled while
|
// This version actually runs the tools because TinyGo was compiled while
|
||||||
// linking statically with LLVM (with the byollvm build tag).
|
// linking statically with LLVM (with the byollvm build tag).
|
||||||
func RunTool(tool string, args ...string) error {
|
func RunTool(tool string, args ...string) error {
|
||||||
|
linker := "elf"
|
||||||
|
if tool == "ld.lld" && len(args) >= 2 && args[0] == "-m" && args[1] == "i386pep" {
|
||||||
|
linker = "mingw"
|
||||||
|
}
|
||||||
args = append([]string{"tinygo:" + tool}, args...)
|
args = append([]string{"tinygo:" + tool}, args...)
|
||||||
|
|
||||||
var cflag *C.char
|
var cflag *C.char
|
||||||
@@ -41,7 +46,14 @@ func RunTool(tool string, args ...string) error {
|
|||||||
case "clang":
|
case "clang":
|
||||||
ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf))
|
ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf))
|
||||||
case "ld.lld":
|
case "ld.lld":
|
||||||
|
switch linker {
|
||||||
|
case "elf":
|
||||||
ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf))
|
ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf))
|
||||||
|
case "mingw":
|
||||||
|
ok = C.tinygo_link_mingw(C.int(len(args)), (**C.char)(buf))
|
||||||
|
default:
|
||||||
|
return errors.New("unknown linker: " + linker)
|
||||||
|
}
|
||||||
case "wasm-ld":
|
case "wasm-ld":
|
||||||
ok = C.tinygo_link_wasm(C.int(len(args)), (**C.char)(buf))
|
ok = C.tinygo_link_wasm(C.int(len(args)), (**C.char)(buf))
|
||||||
default:
|
default:
|
||||||
|
|||||||
+25
-8
@@ -34,11 +34,17 @@ func (c *Config) CPU() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Features returns a list of features this CPU supports. For example, for a
|
// Features returns a list of features this CPU supports. For example, for a
|
||||||
// RISC-V processor, that could be ["+a", "+c", "+m"]. For many targets, an
|
// RISC-V processor, that could be "+a,+c,+m". For many targets, an empty list
|
||||||
// empty list will be returned.
|
// will be returned.
|
||||||
func (c *Config) Features() []string {
|
func (c *Config) Features() string {
|
||||||
|
if c.Target.Features == "" {
|
||||||
|
return c.Options.LLVMFeatures
|
||||||
|
}
|
||||||
|
if c.Options.LLVMFeatures == "" {
|
||||||
return c.Target.Features
|
return c.Target.Features
|
||||||
}
|
}
|
||||||
|
return c.Target.Features + "," + c.Options.LLVMFeatures
|
||||||
|
}
|
||||||
|
|
||||||
// GOOS returns the GOOS of the target. This might not always be the actual OS:
|
// GOOS returns the GOOS of the target. This might not always be the actual OS:
|
||||||
// for example, bare-metal targets will usually pretend to be linux to get the
|
// for example, bare-metal targets will usually pretend to be linux to get the
|
||||||
@@ -54,6 +60,12 @@ func (c *Config) GOARCH() string {
|
|||||||
return c.Target.GOARCH
|
return c.Target.GOARCH
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GOARM will return the GOARM environment variable given to the compiler when
|
||||||
|
// building a program.
|
||||||
|
func (c *Config) GOARM() string {
|
||||||
|
return c.Options.GOARM
|
||||||
|
}
|
||||||
|
|
||||||
// BuildTags returns the complete list of build tags used during this build.
|
// BuildTags returns the complete list of build tags used during this build.
|
||||||
func (c *Config) BuildTags() []string {
|
func (c *Config) BuildTags() []string {
|
||||||
tags := append(c.Target.BuildTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
|
tags := append(c.Target.BuildTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
|
||||||
@@ -151,7 +163,7 @@ func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) {
|
|||||||
// target.
|
// target.
|
||||||
func (c *Config) FuncImplementation() string {
|
func (c *Config) FuncImplementation() string {
|
||||||
switch c.Scheduler() {
|
switch c.Scheduler() {
|
||||||
case "tasks":
|
case "tasks", "asyncify":
|
||||||
// A func value is implemented as a pair of pointers:
|
// A func value is implemented as a pair of pointers:
|
||||||
// {context, function pointer}
|
// {context, function pointer}
|
||||||
// where the context may be a pointer to a heap-allocated struct
|
// where the context may be a pointer to a heap-allocated struct
|
||||||
@@ -260,6 +272,15 @@ func (c *Config) CFlags() []string {
|
|||||||
case "wasi-libc":
|
case "wasi-libc":
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
cflags = append(cflags, "--sysroot="+root+"/lib/wasi-libc/sysroot")
|
cflags = append(cflags, "--sysroot="+root+"/lib/wasi-libc/sysroot")
|
||||||
|
case "mingw-w64":
|
||||||
|
root := goenv.Get("TINYGOROOT")
|
||||||
|
path, _ := c.LibcPath("mingw-w64")
|
||||||
|
cflags = append(cflags,
|
||||||
|
"--sysroot="+path,
|
||||||
|
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"),
|
||||||
|
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"),
|
||||||
|
"-D_UCRT",
|
||||||
|
)
|
||||||
case "":
|
case "":
|
||||||
// No libc specified, nothing to add.
|
// No libc specified, nothing to add.
|
||||||
default:
|
default:
|
||||||
@@ -441,10 +462,6 @@ func (c *Config) WasmAbi() string {
|
|||||||
return c.Target.WasmAbi
|
return c.Target.WasmAbi
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) LLVMFeatures() string {
|
|
||||||
return c.Options.LLVMFeatures
|
|
||||||
}
|
|
||||||
|
|
||||||
type TestConfig struct {
|
type TestConfig struct {
|
||||||
CompileTestBinary bool
|
CompileTestBinary bool
|
||||||
// TODO: Filter the test functions to run, include verbose flag, etc
|
// TODO: Filter the test functions to run, include verbose flag, etc
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
validGCOptions = []string{"none", "leaking", "extalloc", "conservative"}
|
validGCOptions = []string{"none", "leaking", "extalloc", "conservative"}
|
||||||
validSchedulerOptions = []string{"none", "tasks", "coroutines"}
|
validSchedulerOptions = []string{"none", "tasks", "coroutines", "asyncify"}
|
||||||
validSerialOptions = []string{"none", "uart", "usb"}
|
validSerialOptions = []string{"none", "uart", "usb"}
|
||||||
validPrintSizeOptions = []string{"none", "short", "full"}
|
validPrintSizeOptions = []string{"none", "short", "full"}
|
||||||
validPanicStrategyOptions = []string{"print", "trap"}
|
validPanicStrategyOptions = []string{"print", "trap"}
|
||||||
@@ -21,6 +21,7 @@ var (
|
|||||||
type Options struct {
|
type Options struct {
|
||||||
GOOS string // environment variable
|
GOOS string // environment variable
|
||||||
GOARCH string // environment variable
|
GOARCH string // environment variable
|
||||||
|
GOARM string // environment variable (only used with GOARCH=arm)
|
||||||
Target string
|
Target string
|
||||||
Opt string
|
Opt string
|
||||||
GC string
|
GC string
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
func TestVerifyOptions(t *testing.T) {
|
func TestVerifyOptions(t *testing.T) {
|
||||||
|
|
||||||
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, extalloc, conservative`)
|
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, extalloc, conservative`)
|
||||||
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, coroutines`)
|
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, coroutines, asyncify`)
|
||||||
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full`)
|
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full`)
|
||||||
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
|
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
|
||||||
|
|
||||||
|
|||||||
+65
-17
@@ -5,6 +5,7 @@ package compileopts
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -25,7 +26,7 @@ type TargetSpec struct {
|
|||||||
Inherits []string `json:"inherits"`
|
Inherits []string `json:"inherits"`
|
||||||
Triple string `json:"llvm-target"`
|
Triple string `json:"llvm-target"`
|
||||||
CPU string `json:"cpu"`
|
CPU string `json:"cpu"`
|
||||||
Features []string `json:"features"`
|
Features string `json:"features"`
|
||||||
GOOS string `json:"goos"`
|
GOOS string `json:"goos"`
|
||||||
GOARCH string `json:"goarch"`
|
GOARCH string `json:"goarch"`
|
||||||
BuildTags []string `json:"build-tags"`
|
BuildTags []string `json:"build-tags"`
|
||||||
@@ -94,7 +95,7 @@ func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
|
|||||||
dst.Set(src)
|
dst.Set(src)
|
||||||
case "append", "":
|
case "append", "":
|
||||||
// or append the field of child to spec
|
// or append the field of child to spec
|
||||||
dst.Set(reflect.AppendSlice(src, dst))
|
dst.Set(reflect.AppendSlice(dst, src))
|
||||||
default:
|
default:
|
||||||
panic("override mode must be 'copy' or 'append' (default). I don't know how to '" + tag + "'.")
|
panic("override mode must be 'copy' or 'append' (default). I don't know how to '" + tag + "'.")
|
||||||
}
|
}
|
||||||
@@ -165,13 +166,26 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
|||||||
if options.Target == "" {
|
if options.Target == "" {
|
||||||
// Configure based on GOOS/GOARCH environment variables (falling back to
|
// Configure based on GOOS/GOARCH environment variables (falling back to
|
||||||
// runtime.GOOS/runtime.GOARCH), and generate a LLVM target based on it.
|
// runtime.GOOS/runtime.GOARCH), and generate a LLVM target based on it.
|
||||||
llvmarch := map[string]string{
|
var llvmarch string
|
||||||
"386": "i386",
|
switch options.GOARCH {
|
||||||
"amd64": "x86_64",
|
case "386":
|
||||||
"arm64": "aarch64",
|
llvmarch = "i386"
|
||||||
"arm": "armv7",
|
case "amd64":
|
||||||
}[options.GOARCH]
|
llvmarch = "x86_64"
|
||||||
if llvmarch == "" {
|
case "arm64":
|
||||||
|
llvmarch = "aarch64"
|
||||||
|
case "arm":
|
||||||
|
switch options.GOARM {
|
||||||
|
case "5":
|
||||||
|
llvmarch = "armv5"
|
||||||
|
case "6":
|
||||||
|
llvmarch = "armv6"
|
||||||
|
case "7":
|
||||||
|
llvmarch = "armv7"
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid GOARM=%s, must be 5, 6, or 7", options.GOARM)
|
||||||
|
}
|
||||||
|
default:
|
||||||
llvmarch = options.GOARCH
|
llvmarch = options.GOARCH
|
||||||
}
|
}
|
||||||
llvmos := options.GOOS
|
llvmos := options.GOOS
|
||||||
@@ -192,6 +206,9 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
|||||||
if options.GOARCH == "arm" {
|
if options.GOARCH == "arm" {
|
||||||
target += "-gnueabihf"
|
target += "-gnueabihf"
|
||||||
}
|
}
|
||||||
|
if options.GOOS == "windows" {
|
||||||
|
target += "-gnu"
|
||||||
|
}
|
||||||
return defaultTarget(options.GOOS, options.GOARCH, target)
|
return defaultTarget(options.GOOS, options.GOARCH, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,16 +225,15 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if spec.Scheduler == "asyncify" {
|
||||||
|
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_asyncify_wasm.S")
|
||||||
|
}
|
||||||
|
|
||||||
return spec, nil
|
return spec, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WindowsBuildNotSupportedErr is being thrown, when goos is windows and no target has been specified.
|
|
||||||
var WindowsBuildNotSupportedErr = errors.New("Building Windows binaries is currently not supported. Try specifying a different target")
|
|
||||||
|
|
||||||
func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||||
if goos == "windows" {
|
|
||||||
return nil, WindowsBuildNotSupportedErr
|
|
||||||
}
|
|
||||||
// No target spec available. Use the default one, useful on most systems
|
// No target spec available. Use the default one, useful on most systems
|
||||||
// with a regular OS.
|
// with a regular OS.
|
||||||
spec := TargetSpec{
|
spec := TargetSpec{
|
||||||
@@ -234,12 +250,23 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
switch goarch {
|
switch goarch {
|
||||||
case "386":
|
case "386":
|
||||||
spec.CPU = "pentium4"
|
spec.CPU = "pentium4"
|
||||||
|
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||||
case "amd64":
|
case "amd64":
|
||||||
spec.CPU = "x86-64"
|
spec.CPU = "x86-64"
|
||||||
|
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||||
case "arm":
|
case "arm":
|
||||||
spec.CPU = "generic"
|
spec.CPU = "generic"
|
||||||
|
switch strings.Split(triple, "-")[0] {
|
||||||
|
case "armv5":
|
||||||
|
spec.Features = "+armv5t,+strict-align,-thumb-mode"
|
||||||
|
case "armv6":
|
||||||
|
spec.Features = "+armv6,+dsp,+fp64,+strict-align,+vfp2,+vfp2sp,-thumb-mode"
|
||||||
|
case "armv7":
|
||||||
|
spec.Features = "+armv7-a,+dsp,+fp64,+vfp2,+vfp2sp,+vfp3d16,+vfp3d16sp,-thumb-mode"
|
||||||
|
}
|
||||||
case "arm64":
|
case "arm64":
|
||||||
spec.CPU = "generic"
|
spec.CPU = "generic"
|
||||||
|
spec.Features = "+neon"
|
||||||
}
|
}
|
||||||
if goos == "darwin" {
|
if goos == "darwin" {
|
||||||
spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
|
spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
|
||||||
@@ -249,12 +276,28 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
spec.RTLib = "compiler-rt"
|
spec.RTLib = "compiler-rt"
|
||||||
spec.Libc = "musl"
|
spec.Libc = "musl"
|
||||||
spec.LDFlags = append(spec.LDFlags, "--gc-sections")
|
spec.LDFlags = append(spec.LDFlags, "--gc-sections")
|
||||||
|
} else if goos == "windows" {
|
||||||
|
spec.Linker = "ld.lld"
|
||||||
|
spec.Libc = "mingw-w64"
|
||||||
|
spec.LDFlags = append(spec.LDFlags,
|
||||||
|
"-m", "i386pep",
|
||||||
|
"-Bdynamic",
|
||||||
|
"--image-base", "0x400000",
|
||||||
|
"--gc-sections",
|
||||||
|
"--no-insert-timestamp",
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
||||||
}
|
}
|
||||||
if goarch != "wasm" {
|
if goarch != "wasm" {
|
||||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+".S")
|
suffix := ""
|
||||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+".S")
|
if goos == "windows" {
|
||||||
|
// Windows uses a different calling convention from other operating
|
||||||
|
// systems so we need separate assembly files.
|
||||||
|
suffix = "_windows"
|
||||||
|
}
|
||||||
|
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+suffix+".S")
|
||||||
|
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+suffix+".S")
|
||||||
}
|
}
|
||||||
if goarch != runtime.GOARCH {
|
if goarch != runtime.GOARCH {
|
||||||
// Some educated guesses as to how to invoke helper programs.
|
// Some educated guesses as to how to invoke helper programs.
|
||||||
@@ -266,6 +309,11 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
spec.Emulator = []string{"qemu-aarch64"}
|
spec.Emulator = []string{"qemu-aarch64"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if goos != runtime.GOOS {
|
||||||
|
if goos == "windows" {
|
||||||
|
spec.Emulator = []string{"wine"}
|
||||||
|
}
|
||||||
|
}
|
||||||
return &spec, nil
|
return &spec, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func TestOverrideProperties(t *testing.T) {
|
|||||||
base := &TargetSpec{
|
base := &TargetSpec{
|
||||||
GOOS: "baseGoos",
|
GOOS: "baseGoos",
|
||||||
CPU: "baseCpu",
|
CPU: "baseCpu",
|
||||||
Features: []string{"bf1", "bf2"},
|
CFlags: []string{"-base-foo", "-base-bar"},
|
||||||
BuildTags: []string{"bt1", "bt2"},
|
BuildTags: []string{"bt1", "bt2"},
|
||||||
Emulator: []string{"be1", "be2"},
|
Emulator: []string{"be1", "be2"},
|
||||||
DefaultStackSize: 42,
|
DefaultStackSize: 42,
|
||||||
@@ -37,7 +37,7 @@ func TestOverrideProperties(t *testing.T) {
|
|||||||
child := &TargetSpec{
|
child := &TargetSpec{
|
||||||
GOOS: "",
|
GOOS: "",
|
||||||
CPU: "chlidCpu",
|
CPU: "chlidCpu",
|
||||||
Features: []string{"cf1", "cf2"},
|
CFlags: []string{"-child-foo", "-child-bar"},
|
||||||
Emulator: []string{"ce1", "ce2"},
|
Emulator: []string{"ce1", "ce2"},
|
||||||
AutoStackSize: &childAutoStackSize,
|
AutoStackSize: &childAutoStackSize,
|
||||||
DefaultStackSize: 64,
|
DefaultStackSize: 64,
|
||||||
@@ -51,8 +51,8 @@ func TestOverrideProperties(t *testing.T) {
|
|||||||
if base.CPU != "chlidCpu" {
|
if base.CPU != "chlidCpu" {
|
||||||
t.Errorf("Overriding failed : got %v", base.CPU)
|
t.Errorf("Overriding failed : got %v", base.CPU)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(base.Features, []string{"cf1", "cf2", "bf1", "bf2"}) {
|
if !reflect.DeepEqual(base.CFlags, []string{"-base-foo", "-base-bar", "-child-foo", "-child-bar"}) {
|
||||||
t.Errorf("Overriding failed : got %v", base.Features)
|
t.Errorf("Overriding failed : got %v", base.CFlags)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(base.BuildTags, []string{"bt1", "bt2"}) {
|
if !reflect.DeepEqual(base.BuildTags, []string{"bt1", "bt2"}) {
|
||||||
t.Errorf("Overriding failed : got %v", base.BuildTags)
|
t.Errorf("Overriding failed : got %v", base.BuildTags)
|
||||||
|
|||||||
+26
-49
@@ -15,22 +15,16 @@ import (
|
|||||||
// createLookupBoundsCheck emits a bounds check before doing a lookup into a
|
// createLookupBoundsCheck emits a bounds check before doing a lookup into a
|
||||||
// slice. This is required by the Go language spec: an index out of bounds must
|
// slice. This is required by the Go language spec: an index out of bounds must
|
||||||
// cause a panic.
|
// cause a panic.
|
||||||
func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value, indexType types.Type) {
|
// The caller should make sure that index is at least as big as arrayLen.
|
||||||
|
func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value) {
|
||||||
if b.info.nobounds {
|
if b.info.nobounds {
|
||||||
// The //go:nobounds pragma was added to the function to avoid bounds
|
// The //go:nobounds pragma was added to the function to avoid bounds
|
||||||
// checking.
|
// checking.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if index.Type().IntTypeWidth() < arrayLen.Type().IntTypeWidth() {
|
// Extend arrayLen if it's too small.
|
||||||
// Sometimes, the index can be e.g. an uint8 or int8, and we have to
|
if index.Type().IntTypeWidth() > arrayLen.Type().IntTypeWidth() {
|
||||||
// correctly extend that type.
|
|
||||||
if indexType.Underlying().(*types.Basic).Info()&types.IsUnsigned == 0 {
|
|
||||||
index = b.CreateZExt(index, arrayLen.Type(), "")
|
|
||||||
} else {
|
|
||||||
index = b.CreateSExt(index, arrayLen.Type(), "")
|
|
||||||
}
|
|
||||||
} else if index.Type().IntTypeWidth() > arrayLen.Type().IntTypeWidth() {
|
|
||||||
// The index is bigger than the array length type, so extend it.
|
// The index is bigger than the array length type, so extend it.
|
||||||
arrayLen = b.CreateZExt(arrayLen, index.Type(), "")
|
arrayLen = b.CreateZExt(arrayLen, index.Type(), "")
|
||||||
}
|
}
|
||||||
@@ -70,27 +64,9 @@ func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extend low and high to be the same size as capacity.
|
// Extend low and high to be the same size as capacity.
|
||||||
if low.Type().IntTypeWidth() < capacityType.IntTypeWidth() {
|
low = b.extendInteger(low, lowType, capacityType)
|
||||||
if lowType.Info()&types.IsUnsigned != 0 {
|
high = b.extendInteger(high, highType, capacityType)
|
||||||
low = b.CreateZExt(low, capacityType, "")
|
max = b.extendInteger(max, maxType, capacityType)
|
||||||
} else {
|
|
||||||
low = b.CreateSExt(low, capacityType, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if high.Type().IntTypeWidth() < capacityType.IntTypeWidth() {
|
|
||||||
if highType.Info()&types.IsUnsigned != 0 {
|
|
||||||
high = b.CreateZExt(high, capacityType, "")
|
|
||||||
} else {
|
|
||||||
high = b.CreateSExt(high, capacityType, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if max.Type().IntTypeWidth() < capacityType.IntTypeWidth() {
|
|
||||||
if maxType.Info()&types.IsUnsigned != 0 {
|
|
||||||
max = b.CreateZExt(max, capacityType, "")
|
|
||||||
} else {
|
|
||||||
max = b.CreateSExt(max, capacityType, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now do the bounds check: low > high || high > capacity
|
// Now do the bounds check: low > high || high > capacity
|
||||||
outOfBounds1 := b.CreateICmp(llvm.IntUGT, low, high, "slice.lowhigh")
|
outOfBounds1 := b.CreateICmp(llvm.IntUGT, low, high, "slice.lowhigh")
|
||||||
@@ -125,13 +101,7 @@ func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Bas
|
|||||||
// using an unsiged greater than.
|
// using an unsiged greater than.
|
||||||
|
|
||||||
// Make sure the len value is at least as big as a uintptr.
|
// Make sure the len value is at least as big as a uintptr.
|
||||||
if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
len = b.extendInteger(len, lenType, b.uintptrType)
|
||||||
if lenType.Info()&types.IsUnsigned != 0 {
|
|
||||||
len = b.CreateZExt(len, b.uintptrType, "")
|
|
||||||
} else {
|
|
||||||
len = b.CreateSExt(len, b.uintptrType, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the maximum slice size, and therefore the maximum value of the
|
// Determine the maximum slice size, and therefore the maximum value of the
|
||||||
// len parameter.
|
// len parameter.
|
||||||
@@ -159,17 +129,8 @@ func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether the bufSize parameter must be cast to a wider integer for
|
// Make sure bufSize is at least as big as maxBufSize (an uintptr).
|
||||||
// comparison.
|
bufSize = b.extendInteger(bufSize, bufSizeType, b.uintptrType)
|
||||||
if bufSize.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
|
||||||
if bufSizeType.Info()&types.IsUnsigned != 0 {
|
|
||||||
// Unsigned, so zero-extend to uint type.
|
|
||||||
bufSize = b.CreateZExt(bufSize, b.intType, "")
|
|
||||||
} else {
|
|
||||||
// Signed, so sign-extend to int type.
|
|
||||||
bufSize = b.CreateSExt(bufSize, b.intType, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate (^uintptr(0)) >> 1, which is the max value that fits in an
|
// Calculate (^uintptr(0)) >> 1, which is the max value that fits in an
|
||||||
// uintptr if uintptrs were signed.
|
// uintptr if uintptrs were signed.
|
||||||
@@ -294,3 +255,19 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
|
|||||||
// Ok: assert didn't trigger so continue normally.
|
// Ok: assert didn't trigger so continue normally.
|
||||||
b.SetInsertPointAtEnd(nextBlock)
|
b.SetInsertPointAtEnd(nextBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extendInteger extends the value to at least targetType using a zero or sign
|
||||||
|
// extend. The resulting value is not truncated: it may still be bigger than
|
||||||
|
// targetType.
|
||||||
|
func (b *builder) extendInteger(value llvm.Value, valueType types.Type, targetType llvm.Type) llvm.Value {
|
||||||
|
if value.Type().IntTypeWidth() < targetType.IntTypeWidth() {
|
||||||
|
if valueType.Underlying().(*types.Basic).Info()&types.IsUnsigned != 0 {
|
||||||
|
// Unsigned, so zero-extend to the target type.
|
||||||
|
value = b.CreateZExt(value, targetType, "")
|
||||||
|
} else {
|
||||||
|
// Signed, so sign-extend to the target type.
|
||||||
|
value = b.CreateSExt(value, targetType, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|||||||
+25
-34
@@ -23,7 +23,7 @@ import (
|
|||||||
// Version of the compiler pacakge. Must be incremented each time the compiler
|
// Version of the compiler pacakge. Must be incremented each time the compiler
|
||||||
// package changes in a way that affects the generated LLVM module.
|
// package changes in a way that affects the generated LLVM module.
|
||||||
// This version is independent of the TinyGo version number.
|
// This version is independent of the TinyGo version number.
|
||||||
const Version = 24 // last change: add layout param to runtime.alloc calls
|
const Version = 25 // last change: add "target-cpu" and "target-features" attributes
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
llvm.InitializeAllTargets()
|
llvm.InitializeAllTargets()
|
||||||
@@ -43,7 +43,7 @@ type Config struct {
|
|||||||
// Target and output information.
|
// Target and output information.
|
||||||
Triple string
|
Triple string
|
||||||
CPU string
|
CPU string
|
||||||
Features []string
|
Features string
|
||||||
GOOS string
|
GOOS string
|
||||||
GOARCH string
|
GOARCH string
|
||||||
CodeModel string
|
CodeModel string
|
||||||
@@ -57,7 +57,6 @@ type Config struct {
|
|||||||
DefaultStackSize uint64
|
DefaultStackSize uint64
|
||||||
NeedsStackObjects bool
|
NeedsStackObjects bool
|
||||||
Debug bool // Whether to emit debug information in the LLVM module.
|
Debug bool // Whether to emit debug information in the LLVM module.
|
||||||
LLVMFeatures string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// compilerContext contains function-independent data that should still be
|
// compilerContext contains function-independent data that should still be
|
||||||
@@ -189,12 +188,6 @@ func NewTargetMachine(config *Config) (llvm.TargetMachine, error) {
|
|||||||
return llvm.TargetMachine{}, err
|
return llvm.TargetMachine{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
feat := config.Features
|
|
||||||
if len(config.LLVMFeatures) > 0 {
|
|
||||||
feat = append(feat, config.LLVMFeatures)
|
|
||||||
}
|
|
||||||
features := strings.Join(feat, ",")
|
|
||||||
|
|
||||||
var codeModel llvm.CodeModel
|
var codeModel llvm.CodeModel
|
||||||
var relocationModel llvm.RelocMode
|
var relocationModel llvm.RelocMode
|
||||||
|
|
||||||
@@ -222,7 +215,7 @@ func NewTargetMachine(config *Config) (llvm.TargetMachine, error) {
|
|||||||
relocationModel = llvm.RelocDynamicNoPic
|
relocationModel = llvm.RelocDynamicNoPic
|
||||||
}
|
}
|
||||||
|
|
||||||
machine := target.CreateTargetMachine(config.Triple, config.CPU, features, llvm.CodeGenLevelDefault, relocationModel, codeModel)
|
machine := target.CreateTargetMachine(config.Triple, config.CPU, config.Features, llvm.CodeGenLevelDefault, relocationModel, codeModel)
|
||||||
return machine, nil
|
return machine, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1629,10 +1622,14 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
array := b.getValue(expr.X)
|
array := b.getValue(expr.X)
|
||||||
index := b.getValue(expr.Index)
|
index := b.getValue(expr.Index)
|
||||||
|
|
||||||
|
// Extend index to at least uintptr size, because getelementptr assumes
|
||||||
|
// index is a signed integer.
|
||||||
|
index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
|
||||||
|
|
||||||
// Check bounds.
|
// Check bounds.
|
||||||
arrayLen := expr.X.Type().Underlying().(*types.Array).Len()
|
arrayLen := expr.X.Type().Underlying().(*types.Array).Len()
|
||||||
arrayLenLLVM := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false)
|
arrayLenLLVM := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false)
|
||||||
b.createLookupBoundsCheck(arrayLenLLVM, index, expr.Index.Type())
|
b.createLookupBoundsCheck(arrayLenLLVM, index)
|
||||||
|
|
||||||
// Can't load directly from array (as index is non-constant), so have to
|
// Can't load directly from array (as index is non-constant), so have to
|
||||||
// do it using an alloca+gep+load.
|
// do it using an alloca+gep+load.
|
||||||
@@ -1673,8 +1670,12 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
return llvm.Value{}, b.makeError(expr.Pos(), "todo: indexaddr: "+ptrTyp.String())
|
return llvm.Value{}, b.makeError(expr.Pos(), "todo: indexaddr: "+ptrTyp.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure index is at least the size of uintptr becuase getelementptr
|
||||||
|
// assumes index is a signed integer.
|
||||||
|
index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
|
||||||
|
|
||||||
// Bounds check.
|
// Bounds check.
|
||||||
b.createLookupBoundsCheck(buflen, index, expr.Index.Type())
|
b.createLookupBoundsCheck(buflen, index)
|
||||||
|
|
||||||
switch expr.X.Type().Underlying().(type) {
|
switch expr.X.Type().Underlying().(type) {
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
@@ -1698,9 +1699,17 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
panic("lookup on non-string?")
|
panic("lookup on non-string?")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sometimes, the index can be e.g. an uint8 or int8, and we have to
|
||||||
|
// correctly extend that type for two reasons:
|
||||||
|
// 1. The lookup bounds check expects an index of at least uintptr
|
||||||
|
// size.
|
||||||
|
// 2. getelementptr has signed operands, and therefore s[uint8(x)]
|
||||||
|
// can be lowered as s[int8(x)]. That would be a bug.
|
||||||
|
index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
|
||||||
|
|
||||||
// Bounds check.
|
// Bounds check.
|
||||||
length := b.CreateExtractValue(value, 1, "len")
|
length := b.CreateExtractValue(value, 1, "len")
|
||||||
b.createLookupBoundsCheck(length, index, expr.Index.Type())
|
b.createLookupBoundsCheck(length, index)
|
||||||
|
|
||||||
// Lookup byte
|
// Lookup byte
|
||||||
buf := b.CreateExtractValue(value, 0, "")
|
buf := b.CreateExtractValue(value, 0, "")
|
||||||
@@ -1826,13 +1835,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
if expr.Low != nil {
|
if expr.Low != nil {
|
||||||
lowType = expr.Low.Type().Underlying().(*types.Basic)
|
lowType = expr.Low.Type().Underlying().(*types.Basic)
|
||||||
low = b.getValue(expr.Low)
|
low = b.getValue(expr.Low)
|
||||||
if low.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
low = b.extendInteger(low, lowType, b.uintptrType)
|
||||||
if lowType.Info()&types.IsUnsigned != 0 {
|
|
||||||
low = b.CreateZExt(low, b.uintptrType, "")
|
|
||||||
} else {
|
|
||||||
low = b.CreateSExt(low, b.uintptrType, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
lowType = types.Typ[types.Uintptr]
|
lowType = types.Typ[types.Uintptr]
|
||||||
low = llvm.ConstInt(b.uintptrType, 0, false)
|
low = llvm.ConstInt(b.uintptrType, 0, false)
|
||||||
@@ -1841,13 +1844,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
if expr.High != nil {
|
if expr.High != nil {
|
||||||
highType = expr.High.Type().Underlying().(*types.Basic)
|
highType = expr.High.Type().Underlying().(*types.Basic)
|
||||||
high = b.getValue(expr.High)
|
high = b.getValue(expr.High)
|
||||||
if high.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
high = b.extendInteger(high, highType, b.uintptrType)
|
||||||
if highType.Info()&types.IsUnsigned != 0 {
|
|
||||||
high = b.CreateZExt(high, b.uintptrType, "")
|
|
||||||
} else {
|
|
||||||
high = b.CreateSExt(high, b.uintptrType, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
highType = types.Typ[types.Uintptr]
|
highType = types.Typ[types.Uintptr]
|
||||||
}
|
}
|
||||||
@@ -1855,13 +1852,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
if expr.Max != nil {
|
if expr.Max != nil {
|
||||||
maxType = expr.Max.Type().Underlying().(*types.Basic)
|
maxType = expr.Max.Type().Underlying().(*types.Basic)
|
||||||
max = b.getValue(expr.Max)
|
max = b.getValue(expr.Max)
|
||||||
if max.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
max = b.extendInteger(max, maxType, b.uintptrType)
|
||||||
if maxType.Info()&types.IsUnsigned != 0 {
|
|
||||||
max = b.CreateZExt(max, b.uintptrType, "")
|
|
||||||
} else {
|
|
||||||
max = b.CreateSExt(max, b.uintptrType, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
maxType = types.Typ[types.Uintptr]
|
maxType = types.Typ[types.Uintptr]
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-16
@@ -20,6 +20,7 @@ var flagUpdate = flag.Bool("update", false, "update tests based on test output")
|
|||||||
type testCase struct {
|
type testCase struct {
|
||||||
file string
|
file string
|
||||||
target string
|
target string
|
||||||
|
scheduler string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Basic tests for the compiler. Build some Go files and compare the output with
|
// Basic tests for the compiler. Build some Go files and compare the output with
|
||||||
@@ -41,20 +42,21 @@ func TestCompiler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tests := []testCase{
|
tests := []testCase{
|
||||||
{"basic.go", ""},
|
{"basic.go", "", ""},
|
||||||
{"pointer.go", ""},
|
{"pointer.go", "", ""},
|
||||||
{"slice.go", ""},
|
{"slice.go", "", ""},
|
||||||
{"string.go", ""},
|
{"string.go", "", ""},
|
||||||
{"float.go", ""},
|
{"float.go", "", ""},
|
||||||
{"interface.go", ""},
|
{"interface.go", "", ""},
|
||||||
{"func.go", ""},
|
{"func.go", "", "coroutines"},
|
||||||
{"pragma.go", ""},
|
{"pragma.go", "", ""},
|
||||||
{"goroutine.go", "wasm"},
|
{"goroutine.go", "wasm", "asyncify"},
|
||||||
{"goroutine.go", "cortex-m-qemu"},
|
{"goroutine.go", "wasm", "coroutines"},
|
||||||
{"channel.go", ""},
|
{"goroutine.go", "cortex-m-qemu", "tasks"},
|
||||||
{"intrinsics.go", "cortex-m-qemu"},
|
{"channel.go", "", ""},
|
||||||
{"intrinsics.go", "wasm"},
|
{"intrinsics.go", "cortex-m-qemu", ""},
|
||||||
{"gc.go", ""},
|
{"intrinsics.go", "wasm", ""},
|
||||||
|
{"gc.go", "", ""},
|
||||||
}
|
}
|
||||||
|
|
||||||
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||||
@@ -62,7 +64,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
t.Fatal("could not read Go version:", err)
|
t.Fatal("could not read Go version:", err)
|
||||||
}
|
}
|
||||||
if minor >= 17 {
|
if minor >= 17 {
|
||||||
tests = append(tests, testCase{"go1.17.go", ""})
|
tests = append(tests, testCase{"go1.17.go", "", ""})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
@@ -70,7 +72,10 @@ func TestCompiler(t *testing.T) {
|
|||||||
targetString := "wasm"
|
targetString := "wasm"
|
||||||
if tc.target != "" {
|
if tc.target != "" {
|
||||||
targetString = tc.target
|
targetString = tc.target
|
||||||
name = tc.file + "-" + tc.target
|
name += "-" + tc.target
|
||||||
|
}
|
||||||
|
if tc.scheduler != "" {
|
||||||
|
name += "-" + tc.scheduler
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
@@ -81,6 +86,9 @@ func TestCompiler(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("failed to load target:", err)
|
t.Fatal("failed to load target:", err)
|
||||||
}
|
}
|
||||||
|
if tc.scheduler != "" {
|
||||||
|
options.Scheduler = tc.scheduler
|
||||||
|
}
|
||||||
config := &compileopts.Config{
|
config := &compileopts.Config{
|
||||||
Options: options,
|
Options: options,
|
||||||
Target: target,
|
Target: target,
|
||||||
@@ -94,6 +102,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
Scheduler: config.Scheduler(),
|
Scheduler: config.Scheduler(),
|
||||||
FuncImplementation: config.FuncImplementation(),
|
FuncImplementation: config.FuncImplementation(),
|
||||||
AutomaticStackSize: config.AutomaticStackSize(),
|
AutomaticStackSize: config.AutomaticStackSize(),
|
||||||
|
DefaultStackSize: config.Target.DefaultStackSize,
|
||||||
}
|
}
|
||||||
machine, err := NewTargetMachine(compilerConfig)
|
machine, err := NewTargetMachine(compilerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -142,6 +151,9 @@ func TestCompiler(t *testing.T) {
|
|||||||
if tc.target != "" {
|
if tc.target != "" {
|
||||||
outFilePrefix += "-" + tc.target
|
outFilePrefix += "-" + tc.target
|
||||||
}
|
}
|
||||||
|
if tc.scheduler != "" {
|
||||||
|
outFilePrefix += "-" + tc.scheduler
|
||||||
|
}
|
||||||
outPath := "./testdata/" + outFilePrefix + ".ll"
|
outPath := "./testdata/" + outFilePrefix + ".ll"
|
||||||
|
|
||||||
// Update test if needed. Do not check the result.
|
// Update test if needed. Do not check the result.
|
||||||
|
|||||||
+25
-3
@@ -100,7 +100,7 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
switch b.Scheduler {
|
switch b.Scheduler {
|
||||||
case "none", "coroutines":
|
case "none", "coroutines":
|
||||||
// There are no additional parameters needed for the goroutine start operation.
|
// There are no additional parameters needed for the goroutine start operation.
|
||||||
case "tasks":
|
case "tasks", "asyncify":
|
||||||
// Add the function pointer as a parameter to start the goroutine.
|
// Add the function pointer as a parameter to start the goroutine.
|
||||||
params = append(params, funcPtr)
|
params = append(params, funcPtr)
|
||||||
default:
|
default:
|
||||||
@@ -112,7 +112,7 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
paramBundle := b.emitPointerPack(params)
|
paramBundle := b.emitPointerPack(params)
|
||||||
var callee, stackSize llvm.Value
|
var callee, stackSize llvm.Value
|
||||||
switch b.Scheduler {
|
switch b.Scheduler {
|
||||||
case "none", "tasks":
|
case "none", "tasks", "asyncify":
|
||||||
callee = b.createGoroutineStartWrapper(funcPtr, prefix, hasContext, instr.Pos())
|
callee = b.createGoroutineStartWrapper(funcPtr, prefix, hasContext, instr.Pos())
|
||||||
if b.AutomaticStackSize {
|
if b.AutomaticStackSize {
|
||||||
// The stack size is not known until after linking. Call a dummy
|
// The stack size is not known until after linking. Call a dummy
|
||||||
@@ -124,7 +124,7 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
} else {
|
} else {
|
||||||
// The stack size is fixed at compile time. By emitting it here as a
|
// The stack size is fixed at compile time. By emitting it here as a
|
||||||
// constant, it can be optimized.
|
// constant, it can be optimized.
|
||||||
if b.Scheduler == "tasks" && b.DefaultStackSize == 0 {
|
if (b.Scheduler == "tasks" || b.Scheduler == "asyncify") && b.DefaultStackSize == 0 {
|
||||||
b.addError(instr.Pos(), "default stack size for goroutines is not set")
|
b.addError(instr.Pos(), "default stack size for goroutines is not set")
|
||||||
}
|
}
|
||||||
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
|
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
|
||||||
@@ -170,6 +170,11 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
|||||||
builder := c.ctx.NewBuilder()
|
builder := c.ctx.NewBuilder()
|
||||||
defer builder.Dispose()
|
defer builder.Dispose()
|
||||||
|
|
||||||
|
var deadlock llvm.Value
|
||||||
|
if c.Scheduler == "asyncify" {
|
||||||
|
deadlock = c.getFunction(c.program.ImportedPackage("runtime").Members["deadlock"].(*ssa.Function))
|
||||||
|
}
|
||||||
|
|
||||||
if !fn.IsAFunction().IsNil() {
|
if !fn.IsAFunction().IsNil() {
|
||||||
// See whether this wrapper has already been created. If so, return it.
|
// See whether this wrapper has already been created. If so, return it.
|
||||||
name := fn.Name()
|
name := fn.Name()
|
||||||
@@ -225,6 +230,12 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
|||||||
// Create the call.
|
// Create the call.
|
||||||
builder.CreateCall(fn, params, "")
|
builder.CreateCall(fn, params, "")
|
||||||
|
|
||||||
|
if c.Scheduler == "asyncify" {
|
||||||
|
builder.CreateCall(deadlock, []llvm.Value{
|
||||||
|
llvm.Undef(c.i8ptrType), llvm.Undef(c.i8ptrType),
|
||||||
|
}, "")
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// For a function pointer like this:
|
// For a function pointer like this:
|
||||||
//
|
//
|
||||||
@@ -292,11 +303,22 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
|||||||
|
|
||||||
// Create the call.
|
// Create the call.
|
||||||
builder.CreateCall(fnPtr, params, "")
|
builder.CreateCall(fnPtr, params, "")
|
||||||
|
|
||||||
|
if c.Scheduler == "asyncify" {
|
||||||
|
builder.CreateCall(deadlock, []llvm.Value{
|
||||||
|
llvm.Undef(c.i8ptrType), llvm.Undef(c.i8ptrType),
|
||||||
|
}, "")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Scheduler == "asyncify" {
|
||||||
|
// The goroutine was terminated via deadlock.
|
||||||
|
builder.CreateUnreachable()
|
||||||
|
} else {
|
||||||
// Finish the function. Every basic block must end in a terminator, and
|
// Finish the function. Every basic block must end in a terminator, and
|
||||||
// because goroutines never return a value we can simply return void.
|
// because goroutines never return a value we can simply return void.
|
||||||
builder.CreateRetVoid()
|
builder.CreateRetVoid()
|
||||||
|
}
|
||||||
|
|
||||||
// Return a ptrtoint of the wrapper, not the function itself.
|
// Return a ptrtoint of the wrapper, not the function itself.
|
||||||
return builder.CreatePtrToInt(wrapper, c.uintptrType, "")
|
return builder.CreatePtrToInt(wrapper, c.uintptrType, "")
|
||||||
|
|||||||
@@ -346,6 +346,12 @@ func (c *compilerContext) addStandardDeclaredAttributes(llvmFn llvm.Value) {
|
|||||||
attr := c.ctx.CreateEnumAttribute(kind, 0)
|
attr := c.ctx.CreateEnumAttribute(kind, 0)
|
||||||
llvmFn.AddFunctionAttr(attr)
|
llvmFn.AddFunctionAttr(attr)
|
||||||
}
|
}
|
||||||
|
if c.CPU != "" {
|
||||||
|
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("target-cpu", c.CPU))
|
||||||
|
}
|
||||||
|
if c.Features != "" {
|
||||||
|
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("target-features", c.Features))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// addStandardDefinedAttributes adds the set of attributes that are added to
|
// addStandardDefinedAttributes adds the set of attributes that are added to
|
||||||
|
|||||||
+57
-2
@@ -156,12 +156,12 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
// createSyscall emits instructions for the syscall.Syscall* family of
|
// createSyscall emits instructions for the syscall.Syscall* family of
|
||||||
// functions, depending on the target OS/arch.
|
// functions, depending on the target OS/arch.
|
||||||
func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
|
switch b.GOOS {
|
||||||
|
case "linux", "freebsd":
|
||||||
syscallResult, err := b.createRawSyscall(call)
|
syscallResult, err := b.createRawSyscall(call)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return syscallResult, err
|
return syscallResult, err
|
||||||
}
|
}
|
||||||
switch b.GOOS {
|
|
||||||
case "linux", "freebsd":
|
|
||||||
// Return values: r0, r1 uintptr, err Errno
|
// Return values: r0, r1 uintptr, err Errno
|
||||||
// Pseudocode:
|
// Pseudocode:
|
||||||
// var err uintptr
|
// var err uintptr
|
||||||
@@ -180,6 +180,10 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
||||||
return retval, nil
|
return retval, nil
|
||||||
case "darwin":
|
case "darwin":
|
||||||
|
syscallResult, err := b.createRawSyscall(call)
|
||||||
|
if err != nil {
|
||||||
|
return syscallResult, err
|
||||||
|
}
|
||||||
// Return values: r0, r1 uintptr, err Errno
|
// Return values: r0, r1 uintptr, err Errno
|
||||||
// Pseudocode:
|
// Pseudocode:
|
||||||
// var err uintptr
|
// var err uintptr
|
||||||
@@ -195,6 +199,57 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
retval = b.CreateInsertValue(retval, zero, 1, "")
|
retval = b.CreateInsertValue(retval, zero, 1, "")
|
||||||
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
||||||
return retval, nil
|
return retval, nil
|
||||||
|
case "windows":
|
||||||
|
// On Windows, syscall.Syscall* is basically just a function pointer
|
||||||
|
// call. This is complicated in gc because of stack switching and the
|
||||||
|
// different ABI, but easy in TinyGo: just call the function pointer.
|
||||||
|
// The signature looks like this:
|
||||||
|
// func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||||
|
|
||||||
|
// Prepare input values.
|
||||||
|
var paramTypes []llvm.Type
|
||||||
|
var params []llvm.Value
|
||||||
|
for _, val := range call.Args[2:] {
|
||||||
|
param := b.getValue(val)
|
||||||
|
params = append(params, param)
|
||||||
|
paramTypes = append(paramTypes, param.Type())
|
||||||
|
}
|
||||||
|
llvmType := llvm.FunctionType(b.uintptrType, paramTypes, false)
|
||||||
|
fn := b.getValue(call.Args[0])
|
||||||
|
fnPtr := b.CreateIntToPtr(fn, llvm.PointerType(llvmType, 0), "")
|
||||||
|
|
||||||
|
// Prepare some functions that will be called later.
|
||||||
|
setLastError := b.mod.NamedFunction("SetLastError")
|
||||||
|
if setLastError.IsNil() {
|
||||||
|
llvmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.ctx.Int32Type()}, false)
|
||||||
|
setLastError = llvm.AddFunction(b.mod, "SetLastError", llvmType)
|
||||||
|
}
|
||||||
|
getLastError := b.mod.NamedFunction("GetLastError")
|
||||||
|
if getLastError.IsNil() {
|
||||||
|
llvmType := llvm.FunctionType(b.ctx.Int32Type(), nil, false)
|
||||||
|
getLastError = llvm.AddFunction(b.mod, "GetLastError", llvmType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now do the actual call. Pseudocode:
|
||||||
|
// SetLastError(0)
|
||||||
|
// r1 = trap(a1, a2, a3, ...)
|
||||||
|
// err = uintptr(GetLastError())
|
||||||
|
// return r1, 0, err
|
||||||
|
// Note that SetLastError/GetLastError could be replaced with direct
|
||||||
|
// access to the thread control block, which is probably smaller and
|
||||||
|
// faster. The Go runtime does this in assembly.
|
||||||
|
b.CreateCall(setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
|
||||||
|
syscallResult := b.CreateCall(fnPtr, params, "")
|
||||||
|
errResult := b.CreateCall(getLastError, nil, "err")
|
||||||
|
if b.uintptrType != b.ctx.Int32Type() {
|
||||||
|
errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return r1, 0, err
|
||||||
|
retval := llvm.ConstNull(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false))
|
||||||
|
retval = b.CreateInsertValue(retval, syscallResult, 0, "")
|
||||||
|
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
||||||
|
return retval, nil
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+2
-1
@@ -6,7 +6,8 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
||||||
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
||||||
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
|
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
|
||||||
%"internal/task.state" = type { i8* }
|
%"internal/task.state" = type { i32, i8*, %"internal/task.stackState", i1 }
|
||||||
|
%"internal/task.stackState" = type { i32, i32 }
|
||||||
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||||
|
|||||||
Vendored
+3
-4
@@ -5,7 +5,6 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
|
|
||||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
|
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
|
||||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||||
%runtime.funcValue = type { i8*, i32 }
|
|
||||||
%runtime._interface = type { i32, i8* }
|
%runtime._interface = type { i32, i8* }
|
||||||
|
|
||||||
@main.scalar1 = hidden global i8* null, align 4
|
@main.scalar1 = hidden global i8* null, align 4
|
||||||
@@ -72,11 +71,11 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime.funcValue* @main.newFuncValue(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
define hidden { i8*, void ()* }* @main.newFuncValue(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%new = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 197 to i8*), i8* undef, i8* null) #0
|
%new = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 197 to i8*), i8* undef, i8* null) #0
|
||||||
%0 = bitcast i8* %new to %runtime.funcValue*
|
%0 = bitcast i8* %new to { i8*, void ()* }*
|
||||||
ret %runtime.funcValue* %0
|
ret { i8*, void ()* }* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
|
|||||||
Vendored
+210
@@ -0,0 +1,210 @@
|
|||||||
|
; ModuleID = 'goroutine.go'
|
||||||
|
source_filename = "goroutine.go"
|
||||||
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
|
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
||||||
|
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
||||||
|
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
|
||||||
|
%"internal/task.state" = type { i32, i8*, %"internal/task.stackState", i1 }
|
||||||
|
%"internal/task.stackState" = type { i32, i32 }
|
||||||
|
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
||||||
|
|
||||||
|
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
||||||
|
|
||||||
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.regularFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 8192, i8* undef, i8* null) #0
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @main.regularFunction(i32, i8*, i8*)
|
||||||
|
|
||||||
|
declare void @runtime.deadlock(i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%unpack.int = ptrtoint i8* %0 to i32
|
||||||
|
call void @main.regularFunction(i32 %unpack.int, i8* undef, i8* undef) #0
|
||||||
|
call void @runtime.deadlock(i8* undef, i8* undef) #0
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @"internal/task.start"(i32, i8*, i32, i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.inlineFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 8192, i8* undef, i8* null) #0
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
%unpack.int = ptrtoint i8* %0 to i32
|
||||||
|
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef, i8* undef)
|
||||||
|
call void @runtime.deadlock(i8* undef, i8* undef) #0
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.closureFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
|
%0 = bitcast i8* %n to i32*
|
||||||
|
store i32 3, i32* %0, align 4
|
||||||
|
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef, i8* null) #0
|
||||||
|
%2 = bitcast i8* %1 to i32*
|
||||||
|
store i32 5, i32* %2, align 4
|
||||||
|
%3 = getelementptr inbounds i8, i8* %1, i32 4
|
||||||
|
%4 = bitcast i8* %3 to i8**
|
||||||
|
store i8* %n, i8** %4, align 4
|
||||||
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 8192, i8* undef, i8* null) #0
|
||||||
|
%5 = load i32, i32* %0, align 4
|
||||||
|
call void @runtime.printint32(i32 %5, i8* undef, i8* null) #0
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%unpack.ptr = bitcast i8* %context to i32*
|
||||||
|
store i32 7, i32* %unpack.ptr, align 4
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #3 {
|
||||||
|
entry:
|
||||||
|
%1 = bitcast i8* %0 to i32*
|
||||||
|
%2 = load i32, i32* %1, align 4
|
||||||
|
%3 = getelementptr inbounds i8, i8* %0, i32 4
|
||||||
|
%4 = bitcast i8* %3 to i8**
|
||||||
|
%5 = load i8*, i8** %4, align 4
|
||||||
|
call void @"main.closureFunctionGoroutine$1"(i32 %2, i8* %5, i8* undef)
|
||||||
|
call void @runtime.deadlock(i8* undef, i8* undef) #0
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.printint32(i32, i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef, i8* null) #0
|
||||||
|
%1 = bitcast i8* %0 to i32*
|
||||||
|
store i32 5, i32* %1, align 4
|
||||||
|
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
||||||
|
%3 = bitcast i8* %2 to i8**
|
||||||
|
store i8* %fn.context, i8** %3, align 4
|
||||||
|
%4 = getelementptr inbounds i8, i8* %0, i32 8
|
||||||
|
%5 = bitcast i8* %4 to void ()**
|
||||||
|
store void ()* %fn.funcptr, void ()** %5, align 4
|
||||||
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 8192, i8* undef, i8* null) #0
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #4 {
|
||||||
|
entry:
|
||||||
|
%1 = bitcast i8* %0 to i32*
|
||||||
|
%2 = load i32, i32* %1, align 4
|
||||||
|
%3 = getelementptr inbounds i8, i8* %0, i32 4
|
||||||
|
%4 = bitcast i8* %3 to i8**
|
||||||
|
%5 = load i8*, i8** %4, align 4
|
||||||
|
%6 = getelementptr inbounds i8, i8* %0, i32 8
|
||||||
|
%7 = bitcast i8* %6 to void (i32, i8*, i8*)**
|
||||||
|
%8 = load void (i32, i8*, i8*)*, void (i32, i8*, i8*)** %7, align 4
|
||||||
|
call void %8(i32 %2, i8* %5, i8* undef) #0
|
||||||
|
call void @runtime.deadlock(i8* undef, i8* undef) #0
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.recoverBuiltinGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef, i8* null) #0
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef, i8* null) #0
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef, i8* null) #0
|
||||||
|
%1 = bitcast i8* %0 to i8**
|
||||||
|
store i8* %itf.value, i8** %1, align 4
|
||||||
|
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
||||||
|
%.repack = bitcast i8* %2 to i8**
|
||||||
|
store i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"main$string", i32 0, i32 0), i8** %.repack, align 4
|
||||||
|
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
|
||||||
|
%3 = bitcast i8* %.repack1 to i32*
|
||||||
|
store i32 4, i32* %3, align 4
|
||||||
|
%4 = getelementptr inbounds i8, i8* %0, i32 12
|
||||||
|
%5 = bitcast i8* %4 to i32*
|
||||||
|
store i32 %itf.typecode, i32* %5, align 4
|
||||||
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* nonnull %0, i32 8192, i8* undef, i8* null) #0
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*, i8*) #5
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #6 {
|
||||||
|
entry:
|
||||||
|
%1 = bitcast i8* %0 to i8**
|
||||||
|
%2 = load i8*, i8** %1, align 4
|
||||||
|
%3 = getelementptr inbounds i8, i8* %0, i32 4
|
||||||
|
%4 = bitcast i8* %3 to i8**
|
||||||
|
%5 = load i8*, i8** %4, align 4
|
||||||
|
%6 = getelementptr inbounds i8, i8* %0, i32 8
|
||||||
|
%7 = bitcast i8* %6 to i32*
|
||||||
|
%8 = load i32, i32* %7, align 4
|
||||||
|
%9 = getelementptr inbounds i8, i8* %0, i32 12
|
||||||
|
%10 = bitcast i8* %9 to i32*
|
||||||
|
%11 = load i32, i32* %10, align 4
|
||||||
|
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8* %2, i8* %5, i32 %8, i32 %11, i8* undef, i8* undef) #0
|
||||||
|
call void @runtime.deadlock(i8* undef, i8* undef) #0
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes #0 = { nounwind }
|
||||||
|
attributes #1 = { nounwind "tinygo-gowrapper"="main.regularFunction" }
|
||||||
|
attributes #2 = { nounwind "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||||
|
attributes #3 = { nounwind "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||||
|
attributes #4 = { nounwind "tinygo-gowrapper" }
|
||||||
|
attributes #5 = { "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||||
|
attributes #6 = { nounwind "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||||
Vendored
+5
@@ -27,3 +27,8 @@ func stringCompareUnequal(s1, s2 string) bool {
|
|||||||
func stringCompareLarger(s1, s2 string) bool {
|
func stringCompareLarger(s1, s2 string) bool {
|
||||||
return s1 > s2
|
return s1 > s2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func stringLookup(s string, x uint8) byte {
|
||||||
|
// Test that x is correctly extended to an uint before comparison.
|
||||||
|
return s[x]
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+17
@@ -78,4 +78,21 @@ entry:
|
|||||||
|
|
||||||
declare i1 @runtime.stringLess(i8*, i32, i8*, i32, i8*, i8*)
|
declare i1 @runtime.stringLess(i8*, i32, i8*, i32, i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i8 @main.stringLookup(i8* %s.data, i32 %s.len, i8 %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = zext i8 %x to i32
|
||||||
|
%.not = icmp ult i32 %0, %s.len
|
||||||
|
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||||
|
|
||||||
|
lookup.throw: ; preds = %entry
|
||||||
|
call void @runtime.lookupPanic(i8* undef, i8* null) #0
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
lookup.next: ; preds = %entry
|
||||||
|
%1 = getelementptr inbounds i8, i8* %s.data, i32 %0
|
||||||
|
%2 = load i8, i8* %1, align 1
|
||||||
|
ret i8 %2
|
||||||
|
}
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
attributes #0 = { nounwind }
|
||||||
|
|||||||
+110
@@ -3,12 +3,15 @@
|
|||||||
package goenv
|
package goenv
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Keys is a slice of all available environment variable keys.
|
// Keys is a slice of all available environment variable keys.
|
||||||
@@ -22,6 +25,12 @@ var Keys = []string{
|
|||||||
"TINYGOROOT",
|
"TINYGOROOT",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if Get("GOARCH") == "arm" {
|
||||||
|
Keys = append(Keys, "GOARM")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TINYGOROOT is the path to the final location for checking tinygo files. If
|
// 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
|
// unset (by a -X ldflag), then sourceDir() will fallback to the original build
|
||||||
// directory.
|
// directory.
|
||||||
@@ -41,6 +50,20 @@ func Get(name string) string {
|
|||||||
return dir
|
return dir
|
||||||
}
|
}
|
||||||
return runtime.GOARCH
|
return runtime.GOARCH
|
||||||
|
case "GOARM":
|
||||||
|
if goarm := os.Getenv("GOARM"); goarm != "" {
|
||||||
|
return goarm
|
||||||
|
}
|
||||||
|
if goos := Get("GOOS"); goos == "windows" || goos == "android" {
|
||||||
|
// Assume Windows and Android are running on modern CPU cores.
|
||||||
|
// This matches upstream Go.
|
||||||
|
return "7"
|
||||||
|
}
|
||||||
|
// Default to ARMv6 on other devices.
|
||||||
|
// The difference between ARMv5 and ARMv6 is big, much bigger than the
|
||||||
|
// difference between ARMv6 and ARMv7. ARMv6 binaries are much smaller,
|
||||||
|
// especially when floating point instructions are involved.
|
||||||
|
return "6"
|
||||||
case "GOROOT":
|
case "GOROOT":
|
||||||
return getGoroot()
|
return getGoroot()
|
||||||
case "GOPATH":
|
case "GOPATH":
|
||||||
@@ -67,11 +90,98 @@ func Get(name string) string {
|
|||||||
return "1"
|
return "1"
|
||||||
case "TINYGOROOT":
|
case "TINYGOROOT":
|
||||||
return sourceDir()
|
return sourceDir()
|
||||||
|
case "WASMOPT":
|
||||||
|
if path := os.Getenv("WASMOPT"); path != "" {
|
||||||
|
err := wasmOptCheckVersion(path)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "cannot use %q as wasm-opt (from WASMOPT environment variable): %s", path, err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
return findWasmOpt()
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find wasm-opt, or exit with an error.
|
||||||
|
func findWasmOpt() string {
|
||||||
|
tinygoroot := sourceDir()
|
||||||
|
searchPaths := []string{
|
||||||
|
tinygoroot + "/bin/wasm-opt",
|
||||||
|
tinygoroot + "/build/wasm-opt",
|
||||||
|
}
|
||||||
|
|
||||||
|
var paths []string
|
||||||
|
for _, path := range searchPaths {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
path += ".exe"
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
if err != nil && os.IsNotExist(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
paths = append(paths, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if path, err := exec.LookPath("wasm-opt"); err == nil {
|
||||||
|
paths = append(paths, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(paths) == 0 {
|
||||||
|
fmt.Fprintln(os.Stderr, "error: could not find wasm-opt, set the WASMOPT environment variable to override")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
errs := make([]error, len(paths))
|
||||||
|
for i, path := range paths {
|
||||||
|
err := wasmOptCheckVersion(path)
|
||||||
|
if err == nil {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
errs[i] = err
|
||||||
|
}
|
||||||
|
fmt.Fprintln(os.Stderr, "no usable wasm-opt found, update or run \"make binaryen\"")
|
||||||
|
for i, path := range paths {
|
||||||
|
fmt.Fprintf(os.Stderr, "\t%s: %s\n", path, errs[i].Error())
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
// wasmOptCheckVersion checks if a copy of wasm-opt is usable.
|
||||||
|
func wasmOptCheckVersion(path string) error {
|
||||||
|
cmd := exec.Command(path, "--version")
|
||||||
|
var buf bytes.Buffer
|
||||||
|
cmd.Stdout = &buf
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
str := buf.String()
|
||||||
|
if strings.Contains(str, "(") {
|
||||||
|
// The git tag may be placed in parentheses after the main version string.
|
||||||
|
str = strings.Split(str, "(")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
str = strings.TrimSpace(str)
|
||||||
|
var ver uint
|
||||||
|
_, err = fmt.Sscanf(str, "wasm-opt version %d", &ver)
|
||||||
|
if err != nil || ver < 102 {
|
||||||
|
return errors.New("incompatible wasm-opt (need 102 or newer)")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Return the TINYGOROOT, or exit with an error.
|
// Return the TINYGOROOT, or exit with an error.
|
||||||
func sourceDir() string {
|
func sourceDir() string {
|
||||||
// Use $TINYGOROOT as root, if available.
|
// Use $TINYGOROOT as root, if available.
|
||||||
|
|||||||
@@ -145,6 +145,9 @@ func Run(mod llvm.Module, debug bool) error {
|
|||||||
if obj.buffer == nil {
|
if obj.buffer == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if obj.constant {
|
||||||
|
continue // constant buffers can't have been modified
|
||||||
|
}
|
||||||
initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
|
initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
|
||||||
if err == errInvalidPtrToIntSize {
|
if err == errInvalidPtrToIntSize {
|
||||||
// This can happen when a previous interp run did not have the
|
// This can happen when a previous interp run did not have the
|
||||||
@@ -248,6 +251,9 @@ func RunFunc(fn llvm.Value, debug bool) error {
|
|||||||
if obj.buffer == nil {
|
if obj.buffer == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if obj.constant {
|
||||||
|
continue // constant, so can't have been modified
|
||||||
|
}
|
||||||
initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
|
initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -941,6 +941,7 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
|
|||||||
agg := operands[0]
|
agg := operands[0]
|
||||||
for i := 0; i < len(indices)-1; i++ {
|
for i := 0; i < len(indices)-1; i++ {
|
||||||
agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg")
|
agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg")
|
||||||
|
mem.instructions = append(mem.instructions, agg)
|
||||||
}
|
}
|
||||||
result = r.builder.CreateExtractValue(agg, int(indices[len(indices)-1]), inst.name)
|
result = r.builder.CreateExtractValue(agg, int(indices[len(indices)-1]), inst.name)
|
||||||
case llvm.InsertValue:
|
case llvm.InsertValue:
|
||||||
@@ -953,11 +954,15 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
|
|||||||
for i := 0; i < len(indices)-1; i++ {
|
for i := 0; i < len(indices)-1; i++ {
|
||||||
agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg"+strconv.Itoa(i))
|
agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg"+strconv.Itoa(i))
|
||||||
aggregates = append(aggregates, agg)
|
aggregates = append(aggregates, agg)
|
||||||
|
mem.instructions = append(mem.instructions, agg)
|
||||||
}
|
}
|
||||||
result = operands[1]
|
result = operands[1]
|
||||||
for i := len(indices) - 1; i >= 0; i-- {
|
for i := len(indices) - 1; i >= 0; i-- {
|
||||||
agg := aggregates[i]
|
agg := aggregates[i]
|
||||||
result = r.builder.CreateInsertValue(agg, result, int(indices[i]), inst.name+".insertvalue"+strconv.Itoa(i))
|
result = r.builder.CreateInsertValue(agg, result, int(indices[i]), inst.name+".insertvalue"+strconv.Itoa(i))
|
||||||
|
if i != 0 { // don't add last result to mem.instructions as it will be done at the end already
|
||||||
|
mem.instructions = append(mem.instructions, result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case llvm.Add:
|
case llvm.Add:
|
||||||
|
|||||||
+6
-1
@@ -41,6 +41,7 @@ type object struct {
|
|||||||
globalName string // name, if not yet created (not guaranteed to be the final name)
|
globalName string // name, if not yet created (not guaranteed to be the final name)
|
||||||
buffer value // buffer with value as given by interp, nil if external
|
buffer value // buffer with value as given by interp, nil if external
|
||||||
size uint32 // must match buffer.len(), if available
|
size uint32 // must match buffer.len(), if available
|
||||||
|
constant bool // true if this is a constant global
|
||||||
marked uint8 // 0 means unmarked, 1 means external read, 2 means external write
|
marked uint8 // 0 means unmarked, 1 means external read, 2 means external write
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +224,7 @@ func (mv *memoryView) hasExternalLoadOrStore(v pointerValue) bool {
|
|||||||
// possible for the interpreter to read from the object.
|
// possible for the interpreter to read from the object.
|
||||||
func (mv *memoryView) hasExternalStore(v pointerValue) bool {
|
func (mv *memoryView) hasExternalStore(v pointerValue) bool {
|
||||||
obj := mv.get(v.index())
|
obj := mv.get(v.index())
|
||||||
return obj.marked >= 2
|
return obj.marked >= 2 && !obj.constant
|
||||||
}
|
}
|
||||||
|
|
||||||
// get returns an object that can only be read from, as it may return an object
|
// get returns an object that can only be read from, as it may return an object
|
||||||
@@ -263,6 +264,9 @@ func (mv *memoryView) put(index uint32, obj object) {
|
|||||||
if checks && mv.get(index).buffer.len(mv.r) != obj.buffer.len(mv.r) {
|
if checks && mv.get(index).buffer.len(mv.r) != obj.buffer.len(mv.r) {
|
||||||
panic("put() with a differently-sized object")
|
panic("put() with a differently-sized object")
|
||||||
}
|
}
|
||||||
|
if checks && obj.constant {
|
||||||
|
panic("interp: store to a constant")
|
||||||
|
}
|
||||||
mv.objects[index] = obj
|
mv.objects[index] = obj
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1138,6 +1142,7 @@ func (r *runner) getValue(llvmValue llvm.Value) value {
|
|||||||
obj.size = uint32(r.targetData.TypeAllocSize(llvmValue.Type().ElementType()))
|
obj.size = uint32(r.targetData.TypeAllocSize(llvmValue.Type().ElementType()))
|
||||||
if initializer := llvmValue.Initializer(); !initializer.IsNil() {
|
if initializer := llvmValue.Initializer(); !initializer.IsNil() {
|
||||||
obj.buffer = r.getValue(initializer)
|
obj.buffer = r.getValue(initializer)
|
||||||
|
obj.constant = llvmValue.IsGlobalConstant()
|
||||||
}
|
}
|
||||||
} else if !llvmValue.IsAFunction().IsNil() {
|
} else if !llvmValue.IsAFunction().IsNil() {
|
||||||
// OK
|
// OK
|
||||||
|
|||||||
Vendored
+8
@@ -6,6 +6,7 @@ target triple = "x86_64--linux"
|
|||||||
@main.nonConst2 = global i64 0
|
@main.nonConst2 = global i64 0
|
||||||
@main.someArray = global [8 x {i16, i32}] zeroinitializer
|
@main.someArray = global [8 x {i16, i32}] zeroinitializer
|
||||||
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
|
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
|
||||||
|
@main.exportedConst = constant i64 42
|
||||||
@main.exposedValue1 = global i16 0
|
@main.exposedValue1 = global i16 0
|
||||||
@main.exposedValue2 = global i16 0
|
@main.exposedValue2 = global i16 0
|
||||||
@main.insertedValue = global {i8, i32, {float, {i64, i16}}} zeroinitializer
|
@main.insertedValue = global {i8, i32, {float, {i64, i16}}} zeroinitializer
|
||||||
@@ -62,6 +63,11 @@ entry:
|
|||||||
call void @modifyExternal(i32* bitcast ([1 x i16*]* @main.exportedValue to i32*))
|
call void @modifyExternal(i32* bitcast ([1 x i16*]* @main.exportedValue to i32*))
|
||||||
store i16 5, i16* @main.exposedValue1
|
store i16 5, i16* @main.exposedValue1
|
||||||
|
|
||||||
|
; Test that marking a constant as external still allows loading from it.
|
||||||
|
call void @readExternal(i32* bitcast (i64* @main.exportedConst to i32*))
|
||||||
|
%constLoad = load i64, i64 * @main.exportedConst
|
||||||
|
call void @runtime.printint64(i64 %constLoad)
|
||||||
|
|
||||||
; Test that this even propagates through functions.
|
; Test that this even propagates through functions.
|
||||||
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
|
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
|
||||||
store i16 7, i16* @main.exposedValue2
|
store i16 7, i16* @main.exposedValue2
|
||||||
@@ -96,6 +102,8 @@ declare i64 @someValue()
|
|||||||
|
|
||||||
declare void @modifyExternal(i32*)
|
declare void @modifyExternal(i32*)
|
||||||
|
|
||||||
|
declare void @readExternal(i32*)
|
||||||
|
|
||||||
; This function will modify an external value. By passing this function as a
|
; This function will modify an external value. By passing this function as a
|
||||||
; function pointer to an external function, @main.exposedValue2 should be
|
; function pointer to an external function, @main.exposedValue2 should be
|
||||||
; marked as external.
|
; marked as external.
|
||||||
|
|||||||
Vendored
+5
@@ -5,6 +5,7 @@ target triple = "x86_64--linux"
|
|||||||
@main.nonConst2 = local_unnamed_addr global i64 0
|
@main.nonConst2 = local_unnamed_addr global i64 0
|
||||||
@main.someArray = global [8 x { i16, i32 }] zeroinitializer
|
@main.someArray = global [8 x { i16, i32 }] zeroinitializer
|
||||||
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
|
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
|
||||||
|
@main.exportedConst = constant i64 42
|
||||||
@main.exposedValue1 = global i16 0
|
@main.exposedValue1 = global i16 0
|
||||||
@main.exposedValue2 = local_unnamed_addr global i16 0
|
@main.exposedValue2 = local_unnamed_addr global i16 0
|
||||||
@main.insertedValue = local_unnamed_addr global { i8, i32, { float, { i64, i16 } } } zeroinitializer
|
@main.insertedValue = local_unnamed_addr global { i8, i32, { float, { i64, i16 } } } zeroinitializer
|
||||||
@@ -24,6 +25,8 @@ entry:
|
|||||||
call void @modifyExternal(i32* bitcast (i8* getelementptr inbounds (i8, i8* bitcast ([8 x { i16, i32 }]* @main.someArray to i8*), i32 28) to i32*))
|
call void @modifyExternal(i32* bitcast (i8* getelementptr inbounds (i8, i8* bitcast ([8 x { i16, i32 }]* @main.someArray to i8*), i32 28) to i32*))
|
||||||
call void @modifyExternal(i32* bitcast ([1 x i16*]* @main.exportedValue to i32*))
|
call void @modifyExternal(i32* bitcast ([1 x i16*]* @main.exportedValue to i32*))
|
||||||
store i16 5, i16* @main.exposedValue1, align 2
|
store i16 5, i16* @main.exposedValue1, align 2
|
||||||
|
call void @readExternal(i32* bitcast (i64* @main.exportedConst to i32*))
|
||||||
|
call void @runtime.printint64(i64 42)
|
||||||
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
|
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
|
||||||
store i16 7, i16* @main.exposedValue2, align 2
|
store i16 7, i16* @main.exposedValue2, align 2
|
||||||
call void @modifyExternal(i32* bitcast (void ()* @hasInlineAsm to i32*))
|
call void @modifyExternal(i32* bitcast (void ()* @hasInlineAsm to i32*))
|
||||||
@@ -54,6 +57,8 @@ declare i64 @someValue() local_unnamed_addr
|
|||||||
|
|
||||||
declare void @modifyExternal(i32*) local_unnamed_addr
|
declare void @modifyExternal(i32*) local_unnamed_addr
|
||||||
|
|
||||||
|
declare void @readExternal(i32*) local_unnamed_addr
|
||||||
|
|
||||||
define void @willModifyGlobal() {
|
define void @willModifyGlobal() {
|
||||||
entry:
|
entry:
|
||||||
store i16 8, i16* @main.exposedValue2, align 2
|
store i16 8, i16* @main.exposedValue2, align 2
|
||||||
|
|||||||
Vendored
+11
@@ -5,9 +5,12 @@ declare void @externalCall(i64)
|
|||||||
|
|
||||||
@foo.knownAtRuntime = global i64 0
|
@foo.knownAtRuntime = global i64 0
|
||||||
@bar.knownAtRuntime = global i64 0
|
@bar.knownAtRuntime = global i64 0
|
||||||
|
@baz.someGlobal = external global [3 x {i64, i32}]
|
||||||
|
@baz.someInt = global i32 0
|
||||||
|
|
||||||
define void @runtime.initAll() unnamed_addr {
|
define void @runtime.initAll() unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
|
call void @baz.init(i8* undef, i8* undef)
|
||||||
call void @foo.init(i8* undef, i8* undef)
|
call void @foo.init(i8* undef, i8* undef)
|
||||||
call void @bar.init(i8* undef, i8* undef)
|
call void @bar.init(i8* undef, i8* undef)
|
||||||
call void @main.init(i8* undef, i8* undef)
|
call void @main.init(i8* undef, i8* undef)
|
||||||
@@ -25,6 +28,14 @@ define internal void @bar.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define internal void @baz.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
; Test extractvalue/insertvalue with more than one index.
|
||||||
|
%val = load [3 x {i64, i32}], [3 x {i64, i32}]* @baz.someGlobal
|
||||||
|
%part = extractvalue [3 x {i64, i32}] %val, 0, 1
|
||||||
|
%val2 = insertvalue [3 x {i64, i32}] %val, i32 5, 2, 1
|
||||||
|
unreachable ; trigger revert
|
||||||
|
}
|
||||||
|
|
||||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
call void @externalCall(i64 3)
|
call void @externalCall(i64 3)
|
||||||
|
|||||||
Vendored
+7
@@ -3,11 +3,14 @@ target triple = "x86_64--linux"
|
|||||||
|
|
||||||
@foo.knownAtRuntime = local_unnamed_addr global i64 0
|
@foo.knownAtRuntime = local_unnamed_addr global i64 0
|
||||||
@bar.knownAtRuntime = local_unnamed_addr global i64 0
|
@bar.knownAtRuntime = local_unnamed_addr global i64 0
|
||||||
|
@baz.someGlobal = external local_unnamed_addr global [3 x { i64, i32 }]
|
||||||
|
@baz.someInt = local_unnamed_addr global i32 0
|
||||||
|
|
||||||
declare void @externalCall(i64) local_unnamed_addr
|
declare void @externalCall(i64) local_unnamed_addr
|
||||||
|
|
||||||
define void @runtime.initAll() unnamed_addr {
|
define void @runtime.initAll() unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
|
call fastcc void @baz.init(i8* undef, i8* undef)
|
||||||
call fastcc void @foo.init(i8* undef, i8* undef)
|
call fastcc void @foo.init(i8* undef, i8* undef)
|
||||||
%val = load i64, i64* @foo.knownAtRuntime, align 8
|
%val = load i64, i64* @foo.knownAtRuntime, align 8
|
||||||
store i64 %val, i64* @bar.knownAtRuntime, align 8
|
store i64 %val, i64* @bar.knownAtRuntime, align 8
|
||||||
@@ -19,3 +22,7 @@ define internal fastcc void @foo.init(i8* %context, i8* %parentHandle) unnamed_a
|
|||||||
store i64 5, i64* @foo.knownAtRuntime, align 8
|
store i64 5, i64* @foo.knownAtRuntime, align 8
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define internal fastcc void @baz.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
|
|||||||
Submodule
+1
Submodule lib/binaryen added at 96f7acf09a
Submodule
+1
Submodule lib/mingw-w64 added at acc9b9d9eb
@@ -228,6 +228,7 @@ func pathsToOverride(needsSyscallPackage bool) map[string]bool {
|
|||||||
"internal/bytealg/": false,
|
"internal/bytealg/": false,
|
||||||
"internal/reflectlite/": false,
|
"internal/reflectlite/": false,
|
||||||
"internal/task/": false,
|
"internal/task/": false,
|
||||||
|
"internal/itoa/": false, // TODO: Remove when we drop support for go 1.16
|
||||||
"machine/": false,
|
"machine/": false,
|
||||||
"net/": true,
|
"net/": true,
|
||||||
"os/": true,
|
"os/": true,
|
||||||
|
|||||||
@@ -244,8 +244,18 @@ func runPackageTest(config *compileopts.Config, result builder.BuildResult, test
|
|||||||
cmd.Dir = result.MainDir
|
cmd.Dir = result.MainDir
|
||||||
} else {
|
} else {
|
||||||
// Run in an emulator.
|
// Run in an emulator.
|
||||||
// TODO: pass the -test.v flag if needed.
|
|
||||||
args := append(config.Target.Emulator[1:], result.Binary)
|
args := append(config.Target.Emulator[1:], result.Binary)
|
||||||
|
if config.Target.Emulator[0] == "wasmtime" {
|
||||||
|
// allow reading from current directory: --dir=.
|
||||||
|
// mark end of wasmtime arguments and start of program ones: --
|
||||||
|
args = append(args, "--dir=.", "--")
|
||||||
|
if testVerbose {
|
||||||
|
args = append(args, "-test.v")
|
||||||
|
}
|
||||||
|
if testShort {
|
||||||
|
args = append(args, "-test.short")
|
||||||
|
}
|
||||||
|
}
|
||||||
cmd = executeCommand(config.Options, config.Target.Emulator[0], args...)
|
cmd = executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||||
}
|
}
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
@@ -1182,6 +1192,7 @@ func main() {
|
|||||||
options := &compileopts.Options{
|
options := &compileopts.Options{
|
||||||
GOOS: goenv.Get("GOOS"),
|
GOOS: goenv.Get("GOOS"),
|
||||||
GOARCH: goenv.Get("GOARCH"),
|
GOARCH: goenv.Get("GOARCH"),
|
||||||
|
GOARM: goenv.Get("GOARM"),
|
||||||
Target: *target,
|
Target: *target,
|
||||||
Opt: *opt,
|
Opt: *opt,
|
||||||
GC: *gc,
|
GC: *gc,
|
||||||
@@ -1391,6 +1402,7 @@ func main() {
|
|||||||
fmt.Printf("LLVM triple: %s\n", config.Triple())
|
fmt.Printf("LLVM triple: %s\n", config.Triple())
|
||||||
fmt.Printf("GOOS: %s\n", config.GOOS())
|
fmt.Printf("GOOS: %s\n", config.GOOS())
|
||||||
fmt.Printf("GOARCH: %s\n", config.GOARCH())
|
fmt.Printf("GOARCH: %s\n", config.GOARCH())
|
||||||
|
fmt.Printf("GOARM: %s\n", config.GOARM())
|
||||||
fmt.Printf("build tags: %s\n", strings.Join(config.BuildTags(), " "))
|
fmt.Printf("build tags: %s\n", strings.Join(config.BuildTags(), " "))
|
||||||
fmt.Printf("garbage collector: %s\n", config.GC())
|
fmt.Printf("garbage collector: %s\n", config.GC())
|
||||||
fmt.Printf("scheduler: %s\n", config.Scheduler())
|
fmt.Printf("scheduler: %s\n", config.Scheduler())
|
||||||
|
|||||||
+38
-15
@@ -71,11 +71,9 @@ func TestCompiler(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
t.Run("Host", func(t *testing.T) {
|
t.Run("Host", func(t *testing.T) {
|
||||||
runPlatTests(optionsFromTarget(""), tests, t)
|
runPlatTests(optionsFromTarget(""), tests, t)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
return
|
return
|
||||||
@@ -95,13 +93,13 @@ func TestCompiler(t *testing.T) {
|
|||||||
|
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
t.Run("X86Linux", func(t *testing.T) {
|
t.Run("X86Linux", func(t *testing.T) {
|
||||||
runPlatTests(optionsFromOSARCH("linux", "386"), tests, t)
|
runPlatTests(optionsFromOSARCH("linux/386"), tests, t)
|
||||||
})
|
})
|
||||||
t.Run("ARMLinux", func(t *testing.T) {
|
t.Run("ARMLinux", func(t *testing.T) {
|
||||||
runPlatTests(optionsFromOSARCH("linux", "arm"), tests, t)
|
runPlatTests(optionsFromOSARCH("linux/arm/6"), tests, t)
|
||||||
})
|
})
|
||||||
t.Run("ARM64Linux", func(t *testing.T) {
|
t.Run("ARM64Linux", func(t *testing.T) {
|
||||||
runPlatTests(optionsFromOSARCH("linux", "arm64"), tests, t)
|
runPlatTests(optionsFromOSARCH("linux/arm64"), tests, t)
|
||||||
})
|
})
|
||||||
t.Run("WebAssembly", func(t *testing.T) {
|
t.Run("WebAssembly", func(t *testing.T) {
|
||||||
runPlatTests(optionsFromTarget("wasm"), tests, t)
|
runPlatTests(optionsFromTarget("wasm"), tests, t)
|
||||||
@@ -113,10 +111,6 @@ func TestCompiler(t *testing.T) {
|
|||||||
|
|
||||||
// Test a few build options.
|
// Test a few build options.
|
||||||
t.Run("build-options", func(t *testing.T) {
|
t.Run("build-options", func(t *testing.T) {
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
// These tests assume a host that is supported by TinyGo.
|
|
||||||
t.Skip("can't test build options on Windows")
|
|
||||||
}
|
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Test with few optimizations enabled (no inlining, etc).
|
// Test with few optimizations enabled (no inlining, etc).
|
||||||
@@ -125,6 +119,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
runTestWithConfig("stdlib.go", t, compileopts.Options{
|
runTestWithConfig("stdlib.go", t, compileopts.Options{
|
||||||
GOOS: goenv.Get("GOOS"),
|
GOOS: goenv.Get("GOOS"),
|
||||||
GOARCH: goenv.Get("GOARCH"),
|
GOARCH: goenv.Get("GOARCH"),
|
||||||
|
GOARM: goenv.Get("GOARM"),
|
||||||
Opt: "1",
|
Opt: "1",
|
||||||
}, nil, nil)
|
}, nil, nil)
|
||||||
})
|
})
|
||||||
@@ -136,6 +131,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
runTestWithConfig("print.go", t, compileopts.Options{
|
runTestWithConfig("print.go", t, compileopts.Options{
|
||||||
GOOS: goenv.Get("GOOS"),
|
GOOS: goenv.Get("GOOS"),
|
||||||
GOARCH: goenv.Get("GOARCH"),
|
GOARCH: goenv.Get("GOARCH"),
|
||||||
|
GOARM: goenv.Get("GOARM"),
|
||||||
Opt: "0",
|
Opt: "0",
|
||||||
}, nil, nil)
|
}, nil, nil)
|
||||||
})
|
})
|
||||||
@@ -145,6 +141,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
runTestWithConfig("ldflags.go", t, compileopts.Options{
|
runTestWithConfig("ldflags.go", t, compileopts.Options{
|
||||||
GOOS: goenv.Get("GOOS"),
|
GOOS: goenv.Get("GOOS"),
|
||||||
GOARCH: goenv.Get("GOARCH"),
|
GOARCH: goenv.Get("GOARCH"),
|
||||||
|
GOARM: goenv.Get("GOARM"),
|
||||||
GlobalValues: map[string]map[string]string{
|
GlobalValues: map[string]map[string]string{
|
||||||
"main": {
|
"main": {
|
||||||
"someGlobal": "foobar",
|
"someGlobal": "foobar",
|
||||||
@@ -169,6 +166,14 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
runTest("env.go", options, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
|
runTest("env.go", options, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
|
||||||
})
|
})
|
||||||
|
if options.Target == "wasi" {
|
||||||
|
t.Run("alias.go-scheduler-none", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
options := compileopts.Options(options)
|
||||||
|
options.Scheduler = "none"
|
||||||
|
runTest("alias.go", options, t, nil, nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
if options.Target == "" || options.Target == "wasi" {
|
if options.Target == "" || options.Target == "wasi" {
|
||||||
t.Run("filesystem.go", func(t *testing.T) {
|
t.Run("filesystem.go", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
@@ -200,15 +205,24 @@ func optionsFromTarget(target string) compileopts.Options {
|
|||||||
// GOOS/GOARCH are only used if target == ""
|
// GOOS/GOARCH are only used if target == ""
|
||||||
GOOS: goenv.Get("GOOS"),
|
GOOS: goenv.Get("GOOS"),
|
||||||
GOARCH: goenv.Get("GOARCH"),
|
GOARCH: goenv.Get("GOARCH"),
|
||||||
|
GOARM: goenv.Get("GOARM"),
|
||||||
Target: target,
|
Target: target,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func optionsFromOSARCH(goos, goarch string) compileopts.Options {
|
// optionsFromOSARCH returns a set of options based on the "osarch" string. This
|
||||||
return compileopts.Options{
|
// string is in the form of "os/arch/subarch", with the subarch only sometimes
|
||||||
GOOS: goos,
|
// being necessary. Examples are "darwin/amd64" or "linux/arm/7".
|
||||||
GOARCH: goarch,
|
func optionsFromOSARCH(osarch string) compileopts.Options {
|
||||||
|
parts := strings.Split(osarch, "/")
|
||||||
|
options := compileopts.Options{
|
||||||
|
GOOS: parts[0],
|
||||||
|
GOARCH: parts[1],
|
||||||
}
|
}
|
||||||
|
if options.GOARCH == "arm" {
|
||||||
|
options.GOARM = parts[2]
|
||||||
|
}
|
||||||
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTest(name string, options compileopts.Options, t *testing.T, cmdArgs, environmentVars []string) {
|
func runTest(name string, options compileopts.Options, t *testing.T, cmdArgs, environmentVars []string) {
|
||||||
@@ -278,6 +292,9 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
|||||||
|
|
||||||
// Build the test binary.
|
// Build the test binary.
|
||||||
binary := filepath.Join(tmpdir, "test")
|
binary := filepath.Join(tmpdir, "test")
|
||||||
|
if spec.GOOS == "windows" {
|
||||||
|
binary += ".exe"
|
||||||
|
}
|
||||||
err = runBuild("./"+path, binary, &options)
|
err = runBuild("./"+path, binary, &options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printCompilerError(t.Log, err)
|
printCompilerError(t.Log, err)
|
||||||
@@ -319,7 +336,13 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
|||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
// Terminate the process if it runs too long.
|
// Terminate the process if it runs too long.
|
||||||
timer := time.NewTimer(10 * time.Second)
|
maxDuration := 10 * time.Second
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
// For some reason, tests on Windows can take around
|
||||||
|
// 30s to complete. TODO: investigate why and fix this.
|
||||||
|
maxDuration = 40 * time.Second
|
||||||
|
}
|
||||||
|
timer := time.NewTimer(maxDuration)
|
||||||
select {
|
select {
|
||||||
case <-runComplete:
|
case <-runComplete:
|
||||||
timer.Stop()
|
timer.Stop()
|
||||||
@@ -349,7 +372,7 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
|||||||
t.Log("failed to run:", err)
|
t.Log("failed to run:", err)
|
||||||
fail = true
|
fail = true
|
||||||
} else if !bytes.Equal(expected, actual) {
|
} else if !bytes.Equal(expected, actual) {
|
||||||
t.Log("output did not match")
|
t.Logf("output did not match (expected %d bytes, got %d bytes):", len(expected), len(actual))
|
||||||
fail = true
|
fail = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package rand
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Reader = &reader{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type reader struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
var errRandom = errors.New("failed to obtain random data from rand_s")
|
||||||
|
|
||||||
|
func (r *reader) Read(b []byte) (n int, err error) {
|
||||||
|
if len(b) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var randomByte uint32
|
||||||
|
for i := range b {
|
||||||
|
// Call rand_s every four bytes because it's a C int (always 32-bit in
|
||||||
|
// Windows).
|
||||||
|
if i%4 == 0 {
|
||||||
|
errCode := libc_rand_s(&randomByte)
|
||||||
|
if errCode != 0 {
|
||||||
|
// According to the documentation, it can return an error.
|
||||||
|
return n, errRandom
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
randomByte >>= 8
|
||||||
|
}
|
||||||
|
b[i] = byte(randomByte)
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cryptographically secure random number generator.
|
||||||
|
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/rand-s?view=msvc-170
|
||||||
|
// errno_t rand_s(unsigned int* randomValue);
|
||||||
|
//export rand_s
|
||||||
|
func libc_rand_s(randomValue *uint32) int32
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
// Hardfault aliases for definitions that have inconsistent naming (which are
|
// Hardfault aliases for definitions that have inconsistent naming (which are
|
||||||
// auto-generated by gen-device-svd.go) among devices in package nxp.
|
// auto-generated by gen-device-svd.go) among devices in package nxp.
|
||||||
|
|
||||||
|
//go:build nxp && !mimxrt1062
|
||||||
// +build nxp,!mimxrt1062
|
// +build nxp,!mimxrt1062
|
||||||
|
|
||||||
package nxp
|
package nxp
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
// Type definitions, fields, and constants associated with various clocks and
|
// Type definitions, fields, and constants associated with various clocks and
|
||||||
// peripherals of the NXP MIMXRT1062.
|
// peripherals of the NXP MIMXRT1062.
|
||||||
|
|
||||||
|
//go:build nxp && mimxrt1062
|
||||||
// +build nxp,mimxrt1062
|
// +build nxp,mimxrt1062
|
||||||
|
|
||||||
package nxp
|
package nxp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/arm"
|
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@@ -378,6 +378,30 @@ func (clk Clock) setCcm(value uint32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setSysPfd(value ...uint32) {
|
||||||
|
for i, val := range value {
|
||||||
|
pfd528 := CCM_ANALOG.PFD_528.Get() &
|
||||||
|
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||||
|
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
|
||||||
|
// disable the clock output first
|
||||||
|
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||||
|
// set the new value and enable output
|
||||||
|
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUsb1Pfd(value ...uint32) {
|
||||||
|
for i, val := range value {
|
||||||
|
pfd480 := CCM_ANALOG.PFD_480.Get() &
|
||||||
|
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||||
|
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
|
||||||
|
// disable the clock output first
|
||||||
|
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||||
|
// set the new value and enable output
|
||||||
|
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// PLL configuration for ARM
|
// PLL configuration for ARM
|
||||||
type ClockConfigArmPll struct {
|
type ClockConfigArmPll struct {
|
||||||
LoopDivider uint32 // PLL loop divider. Valid range for divider value: 54-108. Fout=Fin*LoopDivider/2.
|
LoopDivider uint32 // PLL loop divider. Valid range for divider value: 54-108. Fout=Fin*LoopDivider/2.
|
||||||
@@ -448,178 +472,59 @@ func (cfg ClockConfigSysPll) Configure(pfd ...uint32) {
|
|||||||
setSysPfd(pfd...)
|
setSysPfd(pfd...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setSysPfd(value ...uint32) {
|
|
||||||
for i, val := range value {
|
|
||||||
pfd528 := CCM_ANALOG.PFD_528.Get() &
|
|
||||||
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
|
||||||
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
|
|
||||||
// disable the clock output first
|
|
||||||
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
|
||||||
// set the new value and enable output
|
|
||||||
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PHY configuration for USB HS
|
|
||||||
type ClockConfigUsbPhy struct {
|
|
||||||
Instance uint8 // USB PHY number (1 or 2)
|
|
||||||
XtalFreq uint32 // External reference clock frequency (Hz)
|
|
||||||
DCal uint32 // Decode to trim nominal 17.78mA current source
|
|
||||||
TxCal45DP uint32 // Decode to trim nominal 45-Ohm series Rp on USB D+
|
|
||||||
TxCal45DM uint32 // Decode to trim nominal 45-Ohm series Rp on USB D-
|
|
||||||
PllConfig ClockConfigUsbPll
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure initializes the USB HS (480 Mbit/s) PHY and PLL clocks, including
|
|
||||||
// the USB +3V regulator (PMU), for use as either USB host or device.
|
|
||||||
func (cfg ClockConfigUsbPhy) Configure() {
|
|
||||||
|
|
||||||
var (
|
|
||||||
usb *USB_Type
|
|
||||||
phy *USBPHY_Type
|
|
||||||
chrgDetectReg *volatile.Register32
|
|
||||||
chrgDetectMsk uint32
|
|
||||||
)
|
|
||||||
|
|
||||||
// Select appropriate peripherals based on receiver Instance
|
|
||||||
switch cfg.Instance {
|
|
||||||
case 1:
|
|
||||||
usb = USB1 // Select USB1 HS PHY/PLL
|
|
||||||
phy = USBPHY1 //
|
|
||||||
chrgDetectReg = &USB_ANALOG.USB1_CHRG_DETECT_SET
|
|
||||||
chrgDetectMsk = USB_ANALOG_USB1_CHRG_DETECT_SET_CHK_CHRG_B |
|
|
||||||
USB_ANALOG_USB1_CHRG_DETECT_SET_EN_B
|
|
||||||
case 2:
|
|
||||||
usb = USB2 // Select USB2 HS PHY/PLL
|
|
||||||
phy = USBPHY2 //
|
|
||||||
chrgDetectReg = &USB_ANALOG.USB2_CHRG_DETECT_SET
|
|
||||||
chrgDetectMsk = USB_ANALOG_USB2_CHRG_DETECT_SET_CHK_CHRG_B |
|
|
||||||
USB_ANALOG_USB2_CHRG_DETECT_SET_EN_B
|
|
||||||
default:
|
|
||||||
panic("nxp: invalid USB PHY")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure and enable USB PLL clocks
|
|
||||||
cfg.PllConfig.Configure()
|
|
||||||
|
|
||||||
// Release PHY from reset
|
|
||||||
phy.CTRL.ClearBits(USBPHY_CTRL_SFTRST)
|
|
||||||
phy.CTRL.ClearBits(USBPHY_CTRL_CLKGATE)
|
|
||||||
|
|
||||||
// Enable power to USB PHY
|
|
||||||
phy.PWD.Set(0)
|
|
||||||
phy.CTRL.SetBits(USBPHY_CTRL_ENAUTOCLR_PHY_PWD | USBPHY_CTRL_ENAUTOCLR_CLKGATE |
|
|
||||||
// enable support for low-speed device connection, direct and indirect (hub)
|
|
||||||
USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3)
|
|
||||||
|
|
||||||
// Enable USB HS clocks gate
|
|
||||||
ClockIpUsbOh3.Enable(true)
|
|
||||||
// Reset USB peripheral
|
|
||||||
usb.USBCMD.SetBits(USB_USBCMD_RST)
|
|
||||||
|
|
||||||
// Add a delay after RST to ensure there is a USB D+ pullup sequence
|
|
||||||
nopDelay(400000)
|
|
||||||
|
|
||||||
// Enable USB LDO
|
|
||||||
PMU.REG_3P0.Set((PMU.REG_3P0.Get() & ^uint32(PMU_REG_3P0_OUTPUT_TRG_Msk)) |
|
|
||||||
(0x17 << PMU_REG_3P0_OUTPUT_TRG_Pos) | PMU_REG_3P0_ENABLE_LINREG)
|
|
||||||
|
|
||||||
// check whether we are connected to USB charger
|
|
||||||
chrgDetectReg.Set(chrgDetectMsk)
|
|
||||||
|
|
||||||
// Decode to trim nominal 17.78mA source for HS TX on USB D+/D-
|
|
||||||
phy.TX.Set((phy.TX.Get() &
|
|
||||||
^uint32(USBPHY_TX_D_CAL_Msk|USBPHY_TX_TXCAL45DN_Msk|USBPHY_TX_TXCAL45DP_Msk)) |
|
|
||||||
((cfg.DCal << USBPHY_TX_D_CAL_Pos) & USBPHY_TX_D_CAL_Msk) |
|
|
||||||
((cfg.TxCal45DM << USBPHY_TX_TXCAL45DN_Pos) & USBPHY_TX_TXCAL45DN_Msk) |
|
|
||||||
((cfg.TxCal45DP << USBPHY_TX_TXCAL45DP_Pos) & USBPHY_TX_TXCAL45DP_Msk))
|
|
||||||
}
|
|
||||||
|
|
||||||
// PLL configuration for USB
|
// PLL configuration for USB
|
||||||
type ClockConfigUsbPll struct {
|
type ClockConfigUsbPll struct {
|
||||||
Instance uint8 // USB PLL number (1 or 2)
|
Instance uint8 // USB PLL number (1 or 2)
|
||||||
LoopDivider uint8 // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
|
LoopDivider uint8 // PLL loop divider: 0 - Fout=Fref*20, 1 - Fout=Fref*22
|
||||||
Src uint8 // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
|
Src uint8 // Pll clock source, reference _clock_pll_clk_src
|
||||||
Pfd []uint32 // Phase fractional divisors (len=4, or nil for boot default)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg ClockConfigUsbPll) Configure() {
|
func (cfg ClockConfigUsbPll) Configure(pfd ...uint32) {
|
||||||
// select USB peripheral registers based on receiver's Instance
|
|
||||||
switch cfg.Instance {
|
switch cfg.Instance {
|
||||||
case 1: // USB1 PLL
|
case 1:
|
||||||
if CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_ENABLE) {
|
|
||||||
// PLL already configured, enable USB clocks
|
|
||||||
CCM_ANALOG.PLL_USB1.SetBits(CCM_ANALOG_PLL_USB1_EN_USB_CLKS)
|
|
||||||
} else {
|
|
||||||
// bypass PLL first
|
// bypass PLL first
|
||||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) &
|
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
|
||||||
CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
|
|
||||||
CCM_ANALOG.PLL_USB1.Set(
|
CCM_ANALOG.PLL_USB1.Set(
|
||||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
|
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
|
||||||
CCM_ANALOG_PLL_USB1_BYPASS | src)
|
CCM_ANALOG_PLL_USB1_BYPASS_Msk | src)
|
||||||
// reconfigure PLL
|
|
||||||
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) &
|
|
||||||
CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk
|
|
||||||
CCM_ANALOG.PLL_USB1.Set(
|
|
||||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
|
|
||||||
CCM_ANALOG_PLL_USB1_ENABLE | CCM_ANALOG_PLL_USB1_POWER |
|
|
||||||
CCM_ANALOG_PLL_USB1_EN_USB_CLKS | sel)
|
|
||||||
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK) {
|
|
||||||
}
|
|
||||||
// disable bypass
|
|
||||||
CCM_ANALOG.PLL_USB1.ClearBits(CCM_ANALOG_PLL_USB1_BYPASS)
|
|
||||||
|
|
||||||
// update PFDs (if provided)
|
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)
|
||||||
if nil != cfg.Pfd {
|
CCM_ANALOG.PLL_USB1_SET.Set(
|
||||||
setUsb1Pfd(cfg.Pfd...)
|
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
|
||||||
|
CCM_ANALOG_PLL_USB1_ENABLE_Msk | CCM_ANALOG_PLL_USB1_POWER_Msk |
|
||||||
|
CCM_ANALOG_PLL_USB1_EN_USB_CLKS_Msk | sel)
|
||||||
|
|
||||||
|
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK_Msk) {
|
||||||
}
|
}
|
||||||
}
|
|
||||||
case 2: // USB2 PLL
|
// disable bypass
|
||||||
if CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_ENABLE) {
|
CCM_ANALOG.PLL_USB1_CLR.Set(CCM_ANALOG_PLL_USB1_BYPASS_Msk)
|
||||||
// PLL already configured, enable USB clocks
|
|
||||||
CCM_ANALOG.PLL_USB2.SetBits(CCM_ANALOG_PLL_USB2_EN_USB_CLKS)
|
// update PFDs after update
|
||||||
} else {
|
setUsb1Pfd(pfd...)
|
||||||
|
|
||||||
|
case 2:
|
||||||
// bypass PLL first
|
// bypass PLL first
|
||||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) &
|
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
|
||||||
CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
|
|
||||||
CCM_ANALOG.PLL_USB2.Set(
|
CCM_ANALOG.PLL_USB2.Set(
|
||||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
|
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
|
||||||
CCM_ANALOG_PLL_USB2_BYPASS | src)
|
CCM_ANALOG_PLL_USB2_BYPASS_Msk | src)
|
||||||
// reconfigure PLL
|
|
||||||
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) &
|
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)
|
||||||
CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk
|
|
||||||
CCM_ANALOG.PLL_USB2.Set(
|
CCM_ANALOG.PLL_USB2.Set(
|
||||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
|
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
|
||||||
CCM_ANALOG_PLL_USB2_ENABLE | CCM_ANALOG_PLL_USB2_POWER |
|
CCM_ANALOG_PLL_USB2_ENABLE_Msk | CCM_ANALOG_PLL_USB2_POWER_Msk |
|
||||||
CCM_ANALOG_PLL_USB2_EN_USB_CLKS | sel)
|
CCM_ANALOG_PLL_USB2_EN_USB_CLKS_Msk | sel)
|
||||||
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK) {
|
|
||||||
|
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK_Msk) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable bypass
|
// disable bypass
|
||||||
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS)
|
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS_Msk)
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("nxp: invalid USB PLL")
|
panic("nxp: invalid USB PLL")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setUsb1Pfd(value ...uint32) {
|
|
||||||
for i, val := range value {
|
|
||||||
pfd480 := CCM_ANALOG.PFD_480.Get() &
|
|
||||||
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
|
||||||
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
|
|
||||||
// disable the clock output first
|
|
||||||
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
|
||||||
// set the new value and enable output
|
|
||||||
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We cannot use the sleep timer from this context (import cycle), but we need
|
|
||||||
// an approximate method to spin CPU cycles for short periods of time.
|
|
||||||
// go:inline
|
|
||||||
func nopDelay(cycles uint32) {
|
|
||||||
for i := uint32(0); i < cycles; i++ {
|
|
||||||
arm.Asm(`nop`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Hardfault aliases for definitions that have inconsistent naming (which are
|
// Hardfault aliases for definitions that have inconsistent naming (which are
|
||||||
// auto-generated by gen-device-svd.go) among devices in package nxp.
|
// auto-generated by gen-device-svd.go) among devices in package nxp.
|
||||||
|
|
||||||
|
//go:build nxp && mimxrt1062
|
||||||
// +build nxp,mimxrt1062
|
// +build nxp,mimxrt1062
|
||||||
|
|
||||||
package nxp
|
package nxp
|
||||||
|
|||||||
@@ -278,83 +278,3 @@ func enableDcache(enable bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlushDcache flushes data from cache to memory
|
|
||||||
//
|
|
||||||
// Normally FlushDcache is used when metadata written to memory will be used by
|
|
||||||
// a DMA or a bus-controller peripheral. Any data in the cache is written to
|
|
||||||
// memory. A copy remains in the cache, so this is typically used with special
|
|
||||||
// fields you will want to quickly access in the future. For data transmission,
|
|
||||||
// use FlushDeleteDcache.
|
|
||||||
//go:inline
|
|
||||||
func FlushDcache(addr, size uintptr) {
|
|
||||||
location := addr & 0xFFFFFFE0
|
|
||||||
endAddr := addr + size
|
|
||||||
arm.AsmFull(`
|
|
||||||
dsb 0xF
|
|
||||||
`, nil)
|
|
||||||
for {
|
|
||||||
SystemControl.DCCMVAC.Set(uint32(location))
|
|
||||||
location += 32
|
|
||||||
if location >= endAddr {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
arm.AsmFull(`
|
|
||||||
dsb 0xF
|
|
||||||
isb 0xF
|
|
||||||
`, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteDcache deletes data from the cache, without touching memory.
|
|
||||||
//
|
|
||||||
// Normally DeleteDcache is used before receiving data via DMA or from
|
|
||||||
// bus-controller peripherals which write to memory. You want to delete anything
|
|
||||||
// the cache may have stored, so your next read is certain to access the
|
|
||||||
// physical memory.
|
|
||||||
//go:inline
|
|
||||||
func DeleteDcache(addr, size uintptr) {
|
|
||||||
location := addr & 0xFFFFFFE0
|
|
||||||
endAddr := addr + size
|
|
||||||
arm.AsmFull(`
|
|
||||||
dsb 0xF
|
|
||||||
`, nil)
|
|
||||||
for {
|
|
||||||
SystemControl.DCIMVAC.Set(uint32(location))
|
|
||||||
location += 32
|
|
||||||
if location >= endAddr {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
arm.AsmFull(`
|
|
||||||
dsb 0xF
|
|
||||||
isb 0xF
|
|
||||||
`, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlushDeleteDcache flushes data from cache to memory, and delete it from the
|
|
||||||
// cache
|
|
||||||
//
|
|
||||||
// Normally FlushDeleteDcache is used when transmitting data via DMA or
|
|
||||||
// bus-controller peripherals which read from memory. You want any cached data
|
|
||||||
// written to memory, and then removed from the cache, because you no longer
|
|
||||||
// need to access the data after transmission.
|
|
||||||
//go:inline
|
|
||||||
func FlushDeleteDcache(addr, size uintptr) {
|
|
||||||
location := addr & 0xFFFFFFE0
|
|
||||||
endAddr := addr + size
|
|
||||||
arm.AsmFull(`
|
|
||||||
dsb 0xF
|
|
||||||
`, nil)
|
|
||||||
for {
|
|
||||||
SystemControl.DCCIMVAC.Set(uint32(location))
|
|
||||||
location += 32
|
|
||||||
if location >= endAddr {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
arm.AsmFull(`
|
|
||||||
dsb 0xF
|
|
||||||
isb 0xF
|
|
||||||
`, nil)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Hand created file. DO NOT DELETE.
|
// Hand created file. DO NOT DELETE.
|
||||||
// atsamd51x bitfield definitions that are not auto-generated by gen-device-svd.go
|
// atsamd51x bitfield definitions that are not auto-generated by gen-device-svd.go
|
||||||
|
|
||||||
|
//go:build sam && atsamd51
|
||||||
// +build sam,atsamd51
|
// +build sam,atsamd51
|
||||||
|
|
||||||
// These are the supported pchctrl function numberings on the atsamd51x
|
// These are the supported pchctrl function numberings on the atsamd51x
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Hand created file. DO NOT DELETE.
|
// Hand created file. DO NOT DELETE.
|
||||||
// atsamd51x bitfield definitions that are not auto-generated by gen-device-svd.go
|
// atsamd51x bitfield definitions that are not auto-generated by gen-device-svd.go
|
||||||
|
|
||||||
|
//go:build sam && atsame5x
|
||||||
// +build sam,atsame5x
|
// +build sam,atsame5x
|
||||||
|
|
||||||
// These are the supported pchctrl function numberings on the atsamd51x
|
// These are the supported pchctrl function numberings on the atsamd51x
|
||||||
|
|||||||
@@ -1,251 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
"machine/usb"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var keyboard = machine.HID0.Keyboard()
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
println("USB HID keyboard demo")
|
|
||||||
|
|
||||||
for {
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
|
|
||||||
// Open a new text editor
|
|
||||||
keyboard.Down(usb.KeyModifierAlt)
|
|
||||||
keyboard.Press(usb.KeySpace)
|
|
||||||
keyboard.Up(usb.KeyModifierAlt)
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
keyboard.Write([]byte("kate"))
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
keyboard.Press(usb.KeyEnter)
|
|
||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
|
|
||||||
// Use the io.Writer interface
|
|
||||||
keyboard.Write([]byte("TinyGo USB Keyboard Control Test\n"))
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
|
|
||||||
// Or manually specify keycodes and Unicode codepoints
|
|
||||||
testKeys([]Key{
|
|
||||||
// Print alphabet out-of-order
|
|
||||||
{Press: usb.KeyX},
|
|
||||||
{Press: usb.KeyY},
|
|
||||||
{Press: usb.KeyZ},
|
|
||||||
{Press: usb.KeyG},
|
|
||||||
{Press: usb.KeyH},
|
|
||||||
{Press: usb.KeyI},
|
|
||||||
{Press: usb.KeyJ},
|
|
||||||
{Press: usb.KeyK},
|
|
||||||
{Press: usb.KeyL},
|
|
||||||
{Press: usb.KeyM},
|
|
||||||
{Press: usb.KeyN},
|
|
||||||
{Press: usb.KeyO},
|
|
||||||
{Press: usb.KeyP},
|
|
||||||
{Press: usb.KeyQ},
|
|
||||||
{Press: usb.KeyR},
|
|
||||||
{Press: usb.KeyS},
|
|
||||||
{Press: usb.KeyT},
|
|
||||||
{Press: usb.KeyA},
|
|
||||||
{Press: usb.KeyB},
|
|
||||||
{Press: usb.KeyC},
|
|
||||||
{Press: usb.KeyD},
|
|
||||||
{Press: usb.KeyE},
|
|
||||||
{Press: usb.KeyF},
|
|
||||||
{Press: usb.KeyU},
|
|
||||||
{Press: usb.KeyV},
|
|
||||||
{Press: usb.KeyW},
|
|
||||||
// Pause 1 second
|
|
||||||
{Time: time.Second},
|
|
||||||
// Move cursor left x3
|
|
||||||
{Press: usb.KeyLeft},
|
|
||||||
{Press: usb.KeyLeft},
|
|
||||||
{Press: usb.KeyLeft},
|
|
||||||
// Pause 1 second
|
|
||||||
{Time: time.Second},
|
|
||||||
// Highlight 6 symbols to the left
|
|
||||||
{Down: usb.KeyModifierShift},
|
|
||||||
{Press: usb.KeyLeft},
|
|
||||||
{Press: usb.KeyLeft},
|
|
||||||
{Press: usb.KeyLeft},
|
|
||||||
{Press: usb.KeyLeft},
|
|
||||||
{Press: usb.KeyLeft},
|
|
||||||
{Press: usb.KeyLeft},
|
|
||||||
{Up: usb.KeyModifierShift},
|
|
||||||
// Pause 1 second
|
|
||||||
{Time: time.Second},
|
|
||||||
// Use Ctrl-X to cut
|
|
||||||
{Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl},
|
|
||||||
// Pause 1 second
|
|
||||||
{Time: time.Second},
|
|
||||||
// Move to beginning of line
|
|
||||||
{Press: usb.KeyHome},
|
|
||||||
// Pause 1 second
|
|
||||||
{Time: time.Second},
|
|
||||||
// Use Ctrl-V to paste
|
|
||||||
{Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl},
|
|
||||||
// Pause 1 second
|
|
||||||
{Time: time.Second},
|
|
||||||
// Highlight 3 symbols to the right
|
|
||||||
{Down: usb.KeyModifierShift},
|
|
||||||
{Press: usb.KeyRight},
|
|
||||||
{Press: usb.KeyRight},
|
|
||||||
{Press: usb.KeyRight},
|
|
||||||
{Up: usb.KeyModifierShift},
|
|
||||||
// Use Ctrl-X to cut
|
|
||||||
{Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl},
|
|
||||||
// Pause 1 second
|
|
||||||
{Time: time.Second},
|
|
||||||
// Move to end of line
|
|
||||||
{Press: usb.KeyEnd},
|
|
||||||
// Pause 1 second
|
|
||||||
{Time: time.Second},
|
|
||||||
// Use Ctrl-V to paste
|
|
||||||
{Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl},
|
|
||||||
// Pause 1 second
|
|
||||||
{Time: time.Second},
|
|
||||||
// Newline
|
|
||||||
{Press: usb.KeyEnter},
|
|
||||||
{Press: usb.KeyEnter},
|
|
||||||
}, 150*time.Millisecond)
|
|
||||||
|
|
||||||
// Highlight all text and delete
|
|
||||||
keyboard.Down(usb.KeyModifierCtrl)
|
|
||||||
keyboard.Press(usb.KeyA)
|
|
||||||
keyboard.Up(usb.KeyModifierCtrl)
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
keyboard.Press(usb.KeyDelete)
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
// Close window
|
|
||||||
keyboard.Down(usb.KeyModifierCtrl)
|
|
||||||
keyboard.Press(usb.KeyQ)
|
|
||||||
keyboard.Up(usb.KeyModifierCtrl)
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
// Confirm discard file
|
|
||||||
keyboard.Down(usb.KeyModifierAlt)
|
|
||||||
keyboard.Press(usb.KeyD)
|
|
||||||
keyboard.Up(usb.KeyModifierAlt)
|
|
||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
|
|
||||||
// Open a new terminal
|
|
||||||
keyboard.Down(usb.KeyModifierAlt)
|
|
||||||
keyboard.Press(usb.KeySpace)
|
|
||||||
keyboard.Up(usb.KeyModifierAlt)
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
keyboard.Write([]byte("konsole"))
|
|
||||||
keyboard.Press(usb.KeyEnter)
|
|
||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
|
|
||||||
// Open serial connection
|
|
||||||
keyboard.Write([]byte("screen /dev/ttyACM0 115200"))
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
keyboard.Press(usb.KeyEnter)
|
|
||||||
time.Sleep(4 * time.Second)
|
|
||||||
|
|
||||||
// Write to UART
|
|
||||||
keyboard.Write([]byte("hello!"))
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
keyboard.Press(usb.KeyEnter)
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
keyboard.Write([]byte("NO U"))
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
keyboard.Press(usb.KeyEnter)
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
|
|
||||||
// Close serial connection
|
|
||||||
keyboard.Down(usb.KeyModifierCtrl)
|
|
||||||
keyboard.Press(usb.KeyX)
|
|
||||||
keyboard.Up(usb.KeyModifierCtrl)
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
keyboard.Press(usb.KeyBackslash)
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
keyboard.Press(usb.KeyY)
|
|
||||||
keyboard.Press(usb.KeyEnter)
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
|
|
||||||
// Close terminal
|
|
||||||
keyboard.Down(usb.KeyModifierCtrl)
|
|
||||||
keyboard.Press(usb.KeyD)
|
|
||||||
keyboard.Up(usb.KeyModifierCtrl)
|
|
||||||
|
|
||||||
time.Sleep(25 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Key struct {
|
|
||||||
Press usb.Keycode
|
|
||||||
Down usb.Keycode
|
|
||||||
Up usb.Keycode
|
|
||||||
Time time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func testKeys(key []Key, delay time.Duration) {
|
|
||||||
for _, k := range key {
|
|
||||||
if 0 != k.Down {
|
|
||||||
keyboard.Down(k.Down)
|
|
||||||
}
|
|
||||||
if 0 != k.Press {
|
|
||||||
keyboard.Press(k.Press)
|
|
||||||
}
|
|
||||||
if 0 != k.Up {
|
|
||||||
keyboard.Up(k.Up)
|
|
||||||
}
|
|
||||||
if 0 != k.Time {
|
|
||||||
time.Sleep(k.Time)
|
|
||||||
} else {
|
|
||||||
time.Sleep(delay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testInternationalLayout() {
|
|
||||||
// International keyboard layouts also supported
|
|
||||||
keyboard.Write([]byte("TinyGo USB Keyboard Layout Test\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Lowercase: abcdefghijklmnopqrstuvwxyz\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Uppercase: ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Numbers: 0123456789\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Symbols1: !\"#$%&'()*+,-./\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Symbols2: :;<=>?[\\]^_`{|}~\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Symbols3: ¡¢£¤¥¦§¨©ª«¬®¯°±\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Symbols4: ²³´µ¶·¸¹º»¼½¾¿×÷\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Grave: ÀÈÌÒÙàèìòù\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Acute: ÁÉÍÓÚÝáéíóúý\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Circumflex: ÂÊÎÔÛâêîôû\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Tilde: ÃÑÕãñõ\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Diaeresis: ÄËÏÖÜäëïöüÿ\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Cedilla: Çç\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Ring Above: Åå\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("AE: Ææ\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Thorn: Þþ\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Sharp S: ß\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("O-Stroke: Øø\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Eth: Ðð\n"))
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
keyboard.Write([]byte("Euro: €\n"))
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
// This is a echo console running on the device UART.
|
|
||||||
// Connect using default baudrate for this hardware, 8-N-1 with your terminal program.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
uart := machine.UART0
|
|
||||||
uart.Write([]byte("Echo console enabled. Type something then press enter:\r\n"))
|
|
||||||
|
|
||||||
input := make([]byte, 4096)
|
|
||||||
i := 0
|
|
||||||
for {
|
|
||||||
if uart.Buffered() > 0 {
|
|
||||||
data, _ := uart.ReadByte()
|
|
||||||
|
|
||||||
switch data {
|
|
||||||
case 13:
|
|
||||||
// return key
|
|
||||||
uart.Write([]byte("\r\n"))
|
|
||||||
uart.Write([]byte("You typed: "))
|
|
||||||
uart.Write(input[:i])
|
|
||||||
uart.Write([]byte("\r\n"))
|
|
||||||
i = 0
|
|
||||||
default:
|
|
||||||
// just echo the character
|
|
||||||
uart.WriteByte(data)
|
|
||||||
input[i] = data
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
internal/itoa is new to go as of 1.17.
|
||||||
|
This directory should be removed when tinygo drops support for go 1.16.
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Simple conversions to avoid depending on strconv.
|
||||||
|
|
||||||
|
package itoa
|
||||||
|
|
||||||
|
// Itoa converts val to a decimal string.
|
||||||
|
func Itoa(val int) string {
|
||||||
|
if val < 0 {
|
||||||
|
return "-" + Uitoa(uint(-val))
|
||||||
|
}
|
||||||
|
return Uitoa(uint(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uitoa converts val to a decimal string.
|
||||||
|
func Uitoa(val uint) string {
|
||||||
|
if val == 0 { // avoid string allocation
|
||||||
|
return "0"
|
||||||
|
}
|
||||||
|
var buf [20]byte // big enough for 64bit value base 10
|
||||||
|
i := len(buf) - 1
|
||||||
|
for val >= 10 {
|
||||||
|
q := val / 10
|
||||||
|
buf[i] = byte('0' + val - q*10)
|
||||||
|
i--
|
||||||
|
val = q
|
||||||
|
}
|
||||||
|
// val < 10
|
||||||
|
buf[i] = byte('0' + val)
|
||||||
|
return string(buf[i:])
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package itoa_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"internal/itoa"
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
minInt64 int64 = math.MinInt64
|
||||||
|
maxInt64 int64 = math.MaxInt64
|
||||||
|
maxUint64 uint64 = math.MaxUint64
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestItoa(t *testing.T) {
|
||||||
|
tests := []int{int(minInt64), math.MinInt32, -999, -100, -1, 0, 1, 100, 999, math.MaxInt32, int(maxInt64)}
|
||||||
|
for _, tt := range tests {
|
||||||
|
got := itoa.Itoa(tt)
|
||||||
|
want := fmt.Sprint(tt)
|
||||||
|
if want != got {
|
||||||
|
t.Fatalf("Itoa(%d) = %s, want %s", tt, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUitoa(t *testing.T) {
|
||||||
|
tests := []uint{0, 1, 100, 999, math.MaxUint32, uint(maxUint64)}
|
||||||
|
for _, tt := range tests {
|
||||||
|
got := itoa.Uitoa(tt)
|
||||||
|
want := fmt.Sprint(tt)
|
||||||
|
if want != got {
|
||||||
|
t.Fatalf("Uitoa(%d) = %s, want %s", tt, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
//go:build scheduler.asyncify
|
||||||
|
// +build scheduler.asyncify
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Stack canary, to detect a stack overflow. The number is a random number
|
||||||
|
// generated by random.org. The bit fiddling dance is necessary because
|
||||||
|
// otherwise Go wouldn't allow the cast to a smaller integer size.
|
||||||
|
const stackCanary = uintptr(uint64(0x670c1333b83bf575) & uint64(^uintptr(0)))
|
||||||
|
|
||||||
|
//go:linkname runtimePanic runtime.runtimePanic
|
||||||
|
func runtimePanic(str string)
|
||||||
|
|
||||||
|
// state is a structure which holds a reference to the state of the task.
|
||||||
|
// When the task is suspended, the stack pointers are saved here.
|
||||||
|
type state struct {
|
||||||
|
// entry is the entry function of the task.
|
||||||
|
// This is needed every time the function is invoked so that asyncify knows what to rewind.
|
||||||
|
entry uintptr
|
||||||
|
|
||||||
|
// args are a pointer to a struct holding the arguments of the function.
|
||||||
|
args unsafe.Pointer
|
||||||
|
|
||||||
|
// stackState is the state of the stack while unwound.
|
||||||
|
stackState
|
||||||
|
|
||||||
|
launched bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// stackState is the saved state of a stack while unwound.
|
||||||
|
// The stack is arranged with asyncify at the bottom, C stack at the top, and a gap of available stack space between the two.
|
||||||
|
type stackState struct {
|
||||||
|
// asyncify is the stack pointer of the asyncify stack.
|
||||||
|
// This starts from the bottom and grows upwards.
|
||||||
|
asyncifysp uintptr
|
||||||
|
|
||||||
|
// asyncify is stack pointer of the C stack.
|
||||||
|
// This starts from the top and grows downwards.
|
||||||
|
csp uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// start creates and starts a new goroutine with the given function and arguments.
|
||||||
|
// The new goroutine is immediately started.
|
||||||
|
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||||
|
t := &Task{}
|
||||||
|
t.state.initialize(fn, args, stackSize)
|
||||||
|
runqueuePushBack(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export tinygo_launch
|
||||||
|
func (*state) launch()
|
||||||
|
|
||||||
|
//go:linkname align runtime.align
|
||||||
|
func align(p uintptr) uintptr
|
||||||
|
|
||||||
|
// initialize the state and prepare to call the specified function with the specified argument bundle.
|
||||||
|
func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||||
|
// Save the entry call.
|
||||||
|
s.entry = fn
|
||||||
|
s.args = args
|
||||||
|
|
||||||
|
// Create a stack.
|
||||||
|
stack := make([]uintptr, stackSize/unsafe.Sizeof(uintptr(0)))
|
||||||
|
|
||||||
|
// Calculate stack base addresses.
|
||||||
|
s.asyncifysp = uintptr(unsafe.Pointer(&stack[0]))
|
||||||
|
s.csp = uintptr(unsafe.Pointer(&stack[0])) + uintptr(len(stack))*unsafe.Sizeof(uintptr(0))
|
||||||
|
stack[0] = stackCanary
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname runqueuePushBack runtime.runqueuePushBack
|
||||||
|
func runqueuePushBack(*Task)
|
||||||
|
|
||||||
|
// currentTask is the current running task, or nil if currently in the scheduler.
|
||||||
|
var currentTask *Task
|
||||||
|
|
||||||
|
// Current returns the current active task.
|
||||||
|
func Current() *Task {
|
||||||
|
return currentTask
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause suspends the current task and returns to the scheduler.
|
||||||
|
// This function may only be called when running on a goroutine stack, not when running on the system stack.
|
||||||
|
func Pause() {
|
||||||
|
// This is mildly unsafe but this is also the only place we can do this.
|
||||||
|
if *(*uintptr)(unsafe.Pointer(currentTask.state.asyncifysp)) != stackCanary {
|
||||||
|
runtimePanic("stack overflow")
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTask.state.unwind()
|
||||||
|
|
||||||
|
*(*uintptr)(unsafe.Pointer(currentTask.state.asyncifysp)) = stackCanary
|
||||||
|
}
|
||||||
|
|
||||||
|
//export tinygo_unwind
|
||||||
|
func (*stackState) unwind()
|
||||||
|
|
||||||
|
// Resume the task until it pauses or completes.
|
||||||
|
// This may only be called from the scheduler.
|
||||||
|
func (t *Task) Resume() {
|
||||||
|
// The current task must be saved and restored because this can nest on WASM with JS.
|
||||||
|
prevTask := currentTask
|
||||||
|
currentTask = t
|
||||||
|
if !t.state.launched {
|
||||||
|
t.state.launch()
|
||||||
|
t.state.launched = true
|
||||||
|
} else {
|
||||||
|
t.state.rewind()
|
||||||
|
}
|
||||||
|
currentTask = prevTask
|
||||||
|
if t.state.asyncifysp > t.state.csp {
|
||||||
|
runtimePanic("stack overflow")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//export tinygo_rewind
|
||||||
|
func (*state) rewind()
|
||||||
|
|
||||||
|
// OnSystemStack returns whether the caller is running on the system stack.
|
||||||
|
func OnSystemStack() bool {
|
||||||
|
// If there is not an active goroutine, then this must be running on the system stack.
|
||||||
|
return Current() == nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
.globaltype __stack_pointer, i32
|
||||||
|
|
||||||
|
.global tinygo_unwind
|
||||||
|
.hidden tinygo_unwind
|
||||||
|
.type tinygo_unwind,@function
|
||||||
|
tinygo_unwind: // func (state *stackState) unwind()
|
||||||
|
.functype tinygo_unwind (i32) -> ()
|
||||||
|
// Check if we are rewinding.
|
||||||
|
i32.const 0
|
||||||
|
i32.load8_u tinygo_rewinding
|
||||||
|
if // if tinygo_rewinding {
|
||||||
|
// Stop rewinding.
|
||||||
|
call stop_rewind
|
||||||
|
i32.const 0
|
||||||
|
i32.const 0
|
||||||
|
i32.store8 tinygo_rewinding // tinygo_rewinding = false;
|
||||||
|
else
|
||||||
|
// Save the C stack pointer (destination structure pointer is in local 0).
|
||||||
|
local.get 0
|
||||||
|
global.get __stack_pointer
|
||||||
|
i32.store 4 // state.csp = getCurrentStackPointer()
|
||||||
|
// Ask asyncify to unwind.
|
||||||
|
// When resuming, asyncify will return this function with tinygo_rewinding set to true.
|
||||||
|
local.get 0
|
||||||
|
call start_unwind // asyncify.start_unwind(state)
|
||||||
|
end_if
|
||||||
|
return
|
||||||
|
end_function
|
||||||
|
|
||||||
|
.global tinygo_launch
|
||||||
|
.hidden tinygo_launch
|
||||||
|
.type tinygo_launch,@function
|
||||||
|
tinygo_launch: // func (state *state) launch()
|
||||||
|
.functype tinygo_launch (i32) -> ()
|
||||||
|
// Switch to the goroutine's C stack.
|
||||||
|
global.get __stack_pointer // prev := getCurrentStackPointer()
|
||||||
|
local.get 0
|
||||||
|
i32.load 12
|
||||||
|
global.set __stack_pointer // setStackPointer(state.csp)
|
||||||
|
// Get the argument pack and entry pointer.
|
||||||
|
local.get 0
|
||||||
|
i32.load 4 // args := state.args
|
||||||
|
local.get 0
|
||||||
|
i32.load 0 // fn := state.entry
|
||||||
|
// Launch the entry function.
|
||||||
|
call_indirect (i32) -> () // fn(args)
|
||||||
|
// Stop unwinding.
|
||||||
|
call stop_unwind
|
||||||
|
// Restore the C stack.
|
||||||
|
global.set __stack_pointer // setStackPointer(prev)
|
||||||
|
return
|
||||||
|
end_function
|
||||||
|
|
||||||
|
.global tinygo_rewind
|
||||||
|
.hidden tinygo_rewind
|
||||||
|
.type tinygo_rewind,@function
|
||||||
|
tinygo_rewind: // func (state *state) rewind()
|
||||||
|
.functype tinygo_rewind (i32) -> ()
|
||||||
|
// Switch to the goroutine's C stack.
|
||||||
|
global.get __stack_pointer // prev := getCurrentStackPointer()
|
||||||
|
local.get 0
|
||||||
|
i32.load 12
|
||||||
|
global.set __stack_pointer // setStackPointer(state.csp)
|
||||||
|
// Get the argument pack and entry pointer.
|
||||||
|
local.get 0
|
||||||
|
i32.load 4 // args := state.args
|
||||||
|
local.get 0
|
||||||
|
i32.load 0 // fn := state.entry
|
||||||
|
// Prepare to rewind.
|
||||||
|
i32.const 0
|
||||||
|
i32.const 1
|
||||||
|
i32.store8 tinygo_rewinding // tinygo_rewinding = true;
|
||||||
|
local.get 0
|
||||||
|
i32.const 8
|
||||||
|
i32.add
|
||||||
|
call start_rewind // asyncify.start_rewind(&state.stackState)
|
||||||
|
// Launch the entry function.
|
||||||
|
// This will actually rewind the call stack.
|
||||||
|
call_indirect (i32) -> () // fn(args)
|
||||||
|
// Stop unwinding.
|
||||||
|
call stop_unwind
|
||||||
|
// Restore the C stack.
|
||||||
|
global.set __stack_pointer // setStackPointer(prev)
|
||||||
|
return
|
||||||
|
end_function
|
||||||
|
|
||||||
|
.functype start_unwind (i32) -> ()
|
||||||
|
.import_module start_unwind, asyncify
|
||||||
|
.functype stop_unwind () -> ()
|
||||||
|
.import_module stop_unwind, asyncify
|
||||||
|
.functype start_rewind (i32) -> ()
|
||||||
|
.import_module start_rewind, asyncify
|
||||||
|
.functype stop_rewind () -> ()
|
||||||
|
.import_module stop_rewind, asyncify
|
||||||
|
|
||||||
|
.hidden tinygo_rewinding # @tinygo_rewinding
|
||||||
|
.type tinygo_rewinding,@object
|
||||||
|
.section .bss.tinygo_rewinding,"",@
|
||||||
|
.globl tinygo_rewinding
|
||||||
|
tinygo_rewinding:
|
||||||
|
.int8 0 # 0x0
|
||||||
|
.size tinygo_rewinding, 1
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build scheduler.tasks,amd64
|
// +build scheduler.tasks,amd64,!windows
|
||||||
|
|
||||||
package task
|
package task
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
// Windows on amd64 has a slightly different ABI than other (*nix) systems.
|
||||||
|
// Therefore, assembly functions need to be tweaked slightly.
|
||||||
|
|
||||||
|
.section .text.tinygo_startTask,"ax"
|
||||||
|
.global tinygo_startTask
|
||||||
|
tinygo_startTask:
|
||||||
|
// Small assembly stub for starting a goroutine. This is already run on the
|
||||||
|
// new stack, with the callee-saved registers already loaded.
|
||||||
|
// Most importantly, r12 contain the pc of the to-be-started function and
|
||||||
|
// r13 contain the only argument it is given. Multiple arguments are packed
|
||||||
|
// into one by storing them in a new allocation.
|
||||||
|
|
||||||
|
// Set the first argument of the goroutine start wrapper, which contains all
|
||||||
|
// the arguments.
|
||||||
|
movq %r13, %rcx
|
||||||
|
|
||||||
|
// Branch to the "goroutine start" function.
|
||||||
|
callq *%r12
|
||||||
|
|
||||||
|
// After return, exit this goroutine. This is a tail call.
|
||||||
|
jmp tinygo_pause
|
||||||
|
|
||||||
|
.global tinygo_swapTask
|
||||||
|
.section .text.tinygo_swapTask,"ax"
|
||||||
|
tinygo_swapTask:
|
||||||
|
// This function gets the following parameters:
|
||||||
|
// %rcx = newStack uintptr
|
||||||
|
// %rdx = oldStack *uintptr
|
||||||
|
|
||||||
|
// Save all callee-saved registers:
|
||||||
|
pushq %r15
|
||||||
|
pushq %r14
|
||||||
|
pushq %r13
|
||||||
|
pushq %r12
|
||||||
|
pushq %rsi
|
||||||
|
pushq %rdi
|
||||||
|
pushq %rbp
|
||||||
|
pushq %rbx
|
||||||
|
|
||||||
|
// Save the current stack pointer in oldStack.
|
||||||
|
movq %rsp, (%rdx)
|
||||||
|
|
||||||
|
// Switch to the new stack pointer.
|
||||||
|
movq %rcx, %rsp
|
||||||
|
|
||||||
|
// Load saved register from the new stack.
|
||||||
|
popq %rbx
|
||||||
|
popq %rbp
|
||||||
|
popq %rdi
|
||||||
|
popq %rsi
|
||||||
|
popq %r12
|
||||||
|
popq %r13
|
||||||
|
popq %r14
|
||||||
|
popq %r15
|
||||||
|
|
||||||
|
// Return into the new task, as if tinygo_swapTask was a regular call.
|
||||||
|
ret
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
// +build scheduler.tasks,amd64,windows
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
// This is almost the same as task_stack_amd64.go, but with the extra rdi and
|
||||||
|
// rsi registers saved: Windows has a slightly different calling convention.
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
var systemStack uintptr
|
||||||
|
|
||||||
|
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||||
|
// switching between tasks. Also see task_stack_amd64_windows.S that relies on
|
||||||
|
// the exact layout of this struct.
|
||||||
|
type calleeSavedRegs struct {
|
||||||
|
rbx uintptr
|
||||||
|
rbp uintptr
|
||||||
|
rdi uintptr
|
||||||
|
rsi uintptr
|
||||||
|
r12 uintptr
|
||||||
|
r13 uintptr
|
||||||
|
r14 uintptr
|
||||||
|
r15 uintptr
|
||||||
|
|
||||||
|
pc uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// archInit runs architecture-specific setup for the goroutine startup.
|
||||||
|
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||||
|
// Store the initial sp for the startTask function (implemented in assembly).
|
||||||
|
s.sp = uintptr(unsafe.Pointer(r))
|
||||||
|
|
||||||
|
// Initialize the registers.
|
||||||
|
// These will be popped off of the stack on the first resume of the goroutine.
|
||||||
|
|
||||||
|
// Start the function at tinygo_startTask (defined in
|
||||||
|
// src/internal/task/task_stack_amd64_windows.S). This assembly code calls a
|
||||||
|
// function (passed in r12) with a single argument (passed in r13). After
|
||||||
|
// the function returns, it calls Pause().
|
||||||
|
r.pc = uintptr(unsafe.Pointer(&startTask))
|
||||||
|
|
||||||
|
// Pass the function to call in r12.
|
||||||
|
// This function is a compiler-generated wrapper which loads arguments out
|
||||||
|
// of a struct pointer. See createGoroutineStartWrapper (defined in
|
||||||
|
// compiler/goroutine.go) for more information.
|
||||||
|
r.r12 = fn
|
||||||
|
|
||||||
|
// Pass the pointer to the arguments struct in r13.
|
||||||
|
r.r13 = uintptr(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *state) resume() {
|
||||||
|
swapTask(s.sp, &systemStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *state) pause() {
|
||||||
|
newStack := systemStack
|
||||||
|
systemStack = 0
|
||||||
|
swapTask(newStack, &s.sp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemStack returns the system stack pointer when called from a task stack.
|
||||||
|
// When called from the system stack, it returns 0.
|
||||||
|
func SystemStack() uintptr {
|
||||||
|
return systemStack
|
||||||
|
}
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
// +build sam,atsamd51,matrixportal_m4
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"device/sam"
|
|
||||||
"runtime/interrupt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// UART on the MatrixPortal M4
|
|
||||||
var (
|
|
||||||
Serial = UART1
|
|
||||||
UART1 = &_UART1
|
|
||||||
_UART1 = UART{
|
|
||||||
Buffer: NewRingBuffer(),
|
|
||||||
Bus: sam.SERCOM1_USART_INT,
|
|
||||||
SERCOM: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
UART2 = &_UART2
|
|
||||||
_UART2 = UART{
|
|
||||||
Buffer: NewRingBuffer(),
|
|
||||||
Bus: sam.SERCOM4_USART_INT,
|
|
||||||
SERCOM: 4,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM1_1, _UART1.handleInterrupt)
|
|
||||||
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM4_1, _UART2.handleInterrupt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// I2C on the MatrixPortal M4
|
|
||||||
var (
|
|
||||||
I2C0 = &I2C{
|
|
||||||
Bus: sam.SERCOM5_I2CM,
|
|
||||||
SERCOM: 5,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// SPI on the MatrixPortal M4
|
|
||||||
var (
|
|
||||||
SPI0 = SPI{
|
|
||||||
Bus: sam.SERCOM3_SPIM,
|
|
||||||
SERCOM: 3, // BUG: SDO on SERCOM1!
|
|
||||||
}
|
|
||||||
NINA_SPI = SPI0
|
|
||||||
|
|
||||||
SPI1 = SPI{
|
|
||||||
Bus: sam.SERCOM0_SPIM,
|
|
||||||
SERCOM: 0,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
|
//go:build nano_33_ble
|
||||||
// +build nano_33_ble
|
// +build nano_33_ble
|
||||||
|
|
||||||
// This contains the pin mappings for the Arduino Nano 33 BLE [Sense] boards.
|
// This contains the pin mappings for the Arduino Nano 33 BLE [Sense] boards.
|
||||||
|
// - https://store.arduino.cc/arduino-nano-33-ble
|
||||||
|
// - https://store.arduino.cc/arduino-nano-33-ble-sense
|
||||||
//
|
//
|
||||||
// Flashing the board requires special version of bossac.
|
// ----------------------------------------------------------------------------
|
||||||
|
// Flashing
|
||||||
//
|
//
|
||||||
|
// Special version of bossac is required.
|
||||||
// This executable can be obtained two ways:
|
// This executable can be obtained two ways:
|
||||||
// 1) In Arduino IDE, install support for the board ("Arduino Mbed OS Nano Boards")
|
// 1) In Arduino IDE, install support for the board ("Arduino Mbed OS Nano Boards")
|
||||||
// Search for "tools/bossac/1.9.1-arduino2/bossac" in Arduino IDEs directory
|
// Search for "tools/bossac/1.9.1-arduino2/bossac" in Arduino IDEs directory
|
||||||
@@ -16,9 +21,14 @@
|
|||||||
// It is possible to replace original bossac with this new one (this only adds support for nrf chip).
|
// It is possible to replace original bossac with this new one (this only adds support for nrf chip).
|
||||||
// In that case make "bossac_arduino2" symlink on it, for the board target to be able to find it.
|
// In that case make "bossac_arduino2" symlink on it, for the board target to be able to find it.
|
||||||
//
|
//
|
||||||
// For more information, see:
|
// ----------------------------------------------------------------------------
|
||||||
// - https://store.arduino.cc/arduino-nano-33-ble
|
// Bluetooth
|
||||||
// - https://store.arduino.cc/arduino-nano-33-ble-sense
|
//
|
||||||
|
// SoftDevice (s140v7) must be flashed first to enable use of bluetooth on this board.
|
||||||
|
// See https://github.com/tinygo-org/bluetooth
|
||||||
|
//
|
||||||
|
// SoftDevice overwrites original bootloader and flashing method described above is not avalable anymore.
|
||||||
|
// Instead, please use debug probe and flash your code with "nano-33-ble-s140v7" target.
|
||||||
//
|
//
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
@@ -62,6 +72,7 @@ const (
|
|||||||
LED_RED = P0_24
|
LED_RED = P0_24
|
||||||
LED_GREEN = P0_16
|
LED_GREEN = P0_16
|
||||||
LED_BLUE = P0_06
|
LED_BLUE = P0_06
|
||||||
|
LED_PWR = P1_09
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART0 pins
|
// UART0 pins
|
||||||
@@ -72,8 +83,19 @@ const (
|
|||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SDA_PIN = P0_31
|
// Defaults to internal
|
||||||
SCL_PIN = P0_02
|
SDA_PIN = SDA1_PIN
|
||||||
|
SCL_PIN = SCL1_PIN
|
||||||
|
|
||||||
|
// I2C0 (external) pins
|
||||||
|
SDA0_PIN = P0_31
|
||||||
|
SCL0_PIN = P0_02
|
||||||
|
|
||||||
|
// I2C1 (internal) pins
|
||||||
|
SDA1_PIN = P0_14
|
||||||
|
SCL1_PIN = P0_15
|
||||||
|
|
||||||
|
I2C_PULLUP = P1_00 // Set high for I2C to work
|
||||||
)
|
)
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
@@ -83,6 +105,19 @@ const (
|
|||||||
SPI0_SDI_PIN = P1_08
|
SPI0_SDI_PIN = P1_08
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Peripherals
|
||||||
|
const (
|
||||||
|
APDS_INT = P0_19 // Proximity (APDS9960) interrupt pin
|
||||||
|
|
||||||
|
LSM_PWR = P0_22 // IMU (LSM9DS1) power
|
||||||
|
LSP_PWR = P0_22 // Pressure (LSP22) power
|
||||||
|
HTS_PWR = P0_22 // Humidity (HTS221) power
|
||||||
|
|
||||||
|
MIC_PWR = P0_17 // Microphone (MP34DT06JTR) power
|
||||||
|
MIC_CLK = P0_26
|
||||||
|
MIC_DIN = P0_25
|
||||||
|
)
|
||||||
|
|
||||||
// USB CDC identifiers
|
// USB CDC identifiers
|
||||||
const (
|
const (
|
||||||
usb_STRING_PRODUCT = "Nano 33 BLE"
|
usb_STRING_PRODUCT = "Nano 33 BLE"
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/nxp"
|
"device/nxp"
|
||||||
"machine/usb"
|
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -56,21 +55,21 @@ const (
|
|||||||
|
|
||||||
// Analog pins
|
// Analog pins
|
||||||
const (
|
const (
|
||||||
// = Pin // Dig [Pad] {ADC1/ADC2}
|
// = Pin // Dig | [Pad] {ADC1/ADC2}
|
||||||
A0 = PA18 // D14 [AD_B1_02] { 7 / 7 }
|
A0 = PA18 // D14 | [AD_B1_02] { 7 / 7 }
|
||||||
A1 = PA19 // D15 [AD_B1_03] { 8 / 8 }
|
A1 = PA19 // D15 | [AD_B1_03] { 8 / 8 }
|
||||||
A2 = PA23 // D16 [AD_B1_07] { 12 / 12 }
|
A2 = PA23 // D16 | [AD_B1_07] { 12 / 12 }
|
||||||
A3 = PA22 // D17 [AD_B1_06] { 11 / 11 }
|
A3 = PA22 // D17 | [AD_B1_06] { 11 / 11 }
|
||||||
A4 = PA17 // D18 [AD_B1_01] { 6 / 6 }
|
A4 = PA17 // D18 | [AD_B1_01] { 6 / 6 }
|
||||||
A5 = PA16 // D19 [AD_B1_00] { 5 / 5 }
|
A5 = PA16 // D19 | [AD_B1_00] { 5 / 5 }
|
||||||
A6 = PA26 // D20 [AD_B1_10] { 15 / 15 }
|
A6 = PA26 // D20 | [AD_B1_10] { 15 / 15 }
|
||||||
A7 = PA27 // D21 [AD_B1_11] { 0 / 0 }
|
A7 = PA27 // D21 | [AD_B1_11] { 0 / 0 }
|
||||||
A8 = PA24 // D22 [AD_B1_08] { 13 / 13 }
|
A8 = PA24 // D22 | [AD_B1_08] { 13 / 13 }
|
||||||
A9 = PA25 // D23 [AD_B1_09] { 14 / 14 }
|
A9 = PA25 // D23 | [AD_B1_09] { 14 / 14 }
|
||||||
A10 = PA12 // D24 [AD_B0_12] { 1 / - }
|
A10 = PA12 // D24 | [AD_B0_12] { 1 / - }
|
||||||
A11 = PA13 // D25 [AD_B0_13] { 2 / - }
|
A11 = PA13 // D25 | [AD_B0_13] { 2 / - }
|
||||||
A12 = PA30 // D26 [AD_B1_14] { - / 3 }
|
A12 = PA30 // D26 | [AD_B1_14] { - / 3 }
|
||||||
A13 = PA31 // D27 [AD_B1_15] { - / 4 }
|
A13 = PA31 // D27 | [AD_B1_15] { - / 4 }
|
||||||
)
|
)
|
||||||
|
|
||||||
// Default peripheral pins
|
// Default peripheral pins
|
||||||
@@ -105,15 +104,6 @@ func init() {
|
|||||||
_UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, _UART7.handleInterrupt)
|
_UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, _UART7.handleInterrupt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// #=====================================================#
|
|
||||||
// | USB |
|
|
||||||
// #=====================================================#
|
|
||||||
var (
|
|
||||||
// UART0 = usb.UART{Port: 0}
|
|
||||||
HID0 = usb.HID{Port: 0}
|
|
||||||
UART0 = &UART1
|
|
||||||
)
|
|
||||||
|
|
||||||
// #=====================================================#
|
// #=====================================================#
|
||||||
// | UART |
|
// | UART |
|
||||||
// #===========#===========#=============#===============#
|
// #===========#===========#=============#===============#
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
// Peripheral abstraction layer for the MIMXRT1062
|
// Peripheral abstraction layer for the MIMXRT1062
|
||||||
|
|
||||||
//go:inline
|
|
||||||
func CPUFrequency() uint32 {
|
func CPUFrequency() uint32 {
|
||||||
return 600000000
|
return 600000000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build rp2040
|
||||||
// +build rp2040
|
// +build rp2040
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
@@ -114,3 +115,11 @@ func init() {
|
|||||||
UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.handleInterrupt)
|
UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.handleInterrupt)
|
||||||
UART1.Interrupt = interrupt.New(rp.IRQ_UART1_IRQ, _UART1.handleInterrupt)
|
UART1.Interrupt = interrupt.New(rp.IRQ_UART1_IRQ, _UART1.handleInterrupt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CurrentCore returns the core number the call was made from.
|
||||||
|
func CurrentCore() int {
|
||||||
|
return int(rp.SIO.CPUID.Get())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumCores returns number of cores available on the device.
|
||||||
|
func NumCores() int { return 2 }
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
//go:build rp2040
|
||||||
// +build rp2040
|
// +build rp2040
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/rp"
|
"device/rp"
|
||||||
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@@ -208,3 +210,108 @@ func (p Pin) Set(value bool) {
|
|||||||
func (p Pin) Get() bool {
|
func (p Pin) Get() bool {
|
||||||
return p.get()
|
return p.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PinChange represents one or more trigger events that can happen on a given GPIO pin
|
||||||
|
// on the RP2040. ORed PinChanges are valid input to most IRQ functions.
|
||||||
|
type PinChange uint8
|
||||||
|
|
||||||
|
// Pin change interrupt constants for SetInterrupt.
|
||||||
|
const (
|
||||||
|
// PinLevelLow triggers whenever pin is at a low (around 0V) logic level.
|
||||||
|
PinLevelLow PinChange = 1 << iota
|
||||||
|
// PinLevelLow triggers whenever pin is at a high (around 3V) logic level.
|
||||||
|
PinLevelHigh
|
||||||
|
// Edge falling
|
||||||
|
PinFalling
|
||||||
|
// Edge rising
|
||||||
|
PinRising
|
||||||
|
)
|
||||||
|
|
||||||
|
// Callbacks to be called for pins configured with SetInterrupt.
|
||||||
|
var (
|
||||||
|
pinCallbacks [2]func(Pin)
|
||||||
|
setInt [2]bool
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetInterrupt sets an interrupt to be executed when a particular pin changes
|
||||||
|
// state. The pin should already be configured as an input, including a pull up
|
||||||
|
// or down if no external pull is provided.
|
||||||
|
//
|
||||||
|
// This call will replace a previously set callback on this pin. You can pass a
|
||||||
|
// nil func to unset the pin change interrupt. If you do so, the change
|
||||||
|
// parameter is ignored and can be set to any value (such as 0).
|
||||||
|
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
|
||||||
|
if p > 31 || p < 0 {
|
||||||
|
return ErrInvalidInputPin
|
||||||
|
}
|
||||||
|
core := CurrentCore()
|
||||||
|
if callback == nil {
|
||||||
|
// disable current interrupt
|
||||||
|
p.setInterrupt(change, false)
|
||||||
|
pinCallbacks[core] = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if pinCallbacks[core] != nil {
|
||||||
|
// Callback already configured. Should disable callback by passing a nil callback first.
|
||||||
|
return ErrNoPinChangeChannel
|
||||||
|
}
|
||||||
|
p.setInterrupt(change, true)
|
||||||
|
pinCallbacks[core] = callback
|
||||||
|
|
||||||
|
if setInt[core] {
|
||||||
|
// interrupt has already been set. Exit.
|
||||||
|
println("core set")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
interrupt.New(rp.IRQ_IO_IRQ_BANK0, gpioHandleInterrupt).Enable()
|
||||||
|
irqSet(rp.IRQ_IO_IRQ_BANK0, true)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// gpioHandleInterrupt finds the corresponding pin for the interrupt.
|
||||||
|
// C SDK equivalent of gpio_irq_handler
|
||||||
|
func gpioHandleInterrupt(intr interrupt.Interrupt) {
|
||||||
|
// panic("END") // if program is not ended here rp2040 will call interrupt again when finished, a vicious spin cycle.
|
||||||
|
core := CurrentCore()
|
||||||
|
callback := pinCallbacks[core]
|
||||||
|
if callback != nil {
|
||||||
|
// TODO fix gpio acquisition (see below)
|
||||||
|
// For now all callbacks get pin 255 (nonexistent).
|
||||||
|
callback(0xff)
|
||||||
|
}
|
||||||
|
var gpio Pin
|
||||||
|
for gpio = 0; gpio < _NUMBANK0_GPIOS; gpio++ {
|
||||||
|
// Acknowledge all GPIO interrupts for now
|
||||||
|
// since we are yet unable to acquire interrupt status
|
||||||
|
gpio.acknowledgeInterrupt(0xff) // TODO fix status get. For now we acknowledge all pending interrupts.
|
||||||
|
// Commented code below from C SDK not working.
|
||||||
|
// statreg := base.intS[gpio>>3]
|
||||||
|
// change := getIntChange(gpio, statreg.Get())
|
||||||
|
// if change != 0 {
|
||||||
|
// gpio.acknowledgeInterrupt(change)
|
||||||
|
// if callback != nil {
|
||||||
|
// callback(gpio)
|
||||||
|
// return
|
||||||
|
// } else {
|
||||||
|
// panic("unset callback in handler")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// events returns the bit representation of the pin change for the rp2040.
|
||||||
|
func (change PinChange) events() uint32 {
|
||||||
|
return uint32(change)
|
||||||
|
}
|
||||||
|
|
||||||
|
// intBit is the bit storage form of a PinChange for a given Pin
|
||||||
|
// in the IO_BANK0 interrupt registers (page 269 RP2040 Datasheet).
|
||||||
|
func (p Pin) ioIntBit(change PinChange) uint32 {
|
||||||
|
return change.events() << (4 * (p % 8))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Acquire interrupt data from a INT status register.
|
||||||
|
func getIntChange(p Pin, status uint32) PinChange {
|
||||||
|
return PinChange(status>>(4*(p%8))) & 0xf
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
//go:build rp2040
|
||||||
|
// +build rp2040
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/rp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// machine_rp2040_sync.go contains interrupt and
|
||||||
|
// lock primitives similar to those found in Pico SDK's
|
||||||
|
// irq.c
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Number of spin locks available
|
||||||
|
_NUMSPINLOCKS = 32
|
||||||
|
// Number of interrupt handlers available
|
||||||
|
_NUMIRQ = 32
|
||||||
|
_PICO_SPINLOCK_ID_IRQ = 9
|
||||||
|
_NUMBANK0_GPIOS = 30
|
||||||
|
)
|
||||||
|
|
||||||
|
// Clears interrupt flag on a pin
|
||||||
|
func (p Pin) acknowledgeInterrupt(change PinChange) {
|
||||||
|
ioBank0.intR[p>>3].Set(p.ioIntBit(change))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic interrupt setting via ioBANK0 for GPIO interrupts.
|
||||||
|
func (p Pin) setInterrupt(change PinChange, enabled bool) {
|
||||||
|
// Separate mask/force/status per-core, so check which core called, and
|
||||||
|
// set the relevant IRQ controls.
|
||||||
|
switch CurrentCore() {
|
||||||
|
case 0:
|
||||||
|
p.ctrlSetInterrupt(change, enabled, &ioBank0.proc0IRQctrl)
|
||||||
|
case 1:
|
||||||
|
p.ctrlSetInterrupt(change, enabled, &ioBank0.proc1IRQctrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ctrlSetInterrupt acknowledges any pending interrupt and enables or disables
|
||||||
|
// the interrupt for a given IRQ control bank (IOBANK, DormantIRQ, QSPI).
|
||||||
|
//
|
||||||
|
// pico-sdk calls this the _gpio_set_irq_enabled, not to be confused with
|
||||||
|
// gpio_set_irq_enabled (no leading underscore).
|
||||||
|
func (p Pin) ctrlSetInterrupt(change PinChange, enabled bool, base *irqCtrl) {
|
||||||
|
p.acknowledgeInterrupt(change)
|
||||||
|
enReg := &base.intE[p>>3]
|
||||||
|
if enabled {
|
||||||
|
enReg.SetBits(p.ioIntBit(change))
|
||||||
|
} else {
|
||||||
|
enReg.ClearBits(p.ioIntBit(change))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable or disable a specific interrupt on the executing core.
|
||||||
|
// num is the interrupt number which must be in [0,31].
|
||||||
|
func irqSet(num uint32, enabled bool) {
|
||||||
|
if num >= _NUMIRQ {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
irqSetMask(1<<num, enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
func irqSetMask(mask uint32, enabled bool) {
|
||||||
|
if enabled {
|
||||||
|
// Clear pending before enable
|
||||||
|
// (if IRQ is actually asserted, it will immediately re-pend)
|
||||||
|
rp.PPB.NVIC_ICPR.Set(mask)
|
||||||
|
rp.PPB.NVIC_ISER.Set(mask)
|
||||||
|
} else {
|
||||||
|
rp.PPB.NVIC_ICER.Set(mask)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,935 +0,0 @@
|
|||||||
package usb
|
|
||||||
|
|
||||||
// Implementation of 32-bit target-agnostic USB device controller driver (dcd).
|
|
||||||
|
|
||||||
import "unsafe"
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if unsafe.Sizeof(uintptr(0)) > 4 {
|
|
||||||
panic("USB device controller is only supported on 32-bit systems")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dcdCount defines the number of USB cores to configure for device mode. It is
|
|
||||||
// computed as the sum of all declared device configuration descriptors.
|
|
||||||
const dcdCount = descCDCACMCount + descHIDCount
|
|
||||||
|
|
||||||
// dcdInstance provides statically-allocated instances of each USB device
|
|
||||||
// controller configured on this platform.
|
|
||||||
var dcdInstance [dcdCount]dcd
|
|
||||||
|
|
||||||
// dhwInstance provides statically-allocated instances of each USB hardware
|
|
||||||
// abstraction for ports configured as device on this platform.
|
|
||||||
var dhwInstance [dcdCount]dhw
|
|
||||||
|
|
||||||
// dcd implements a generic USB device controller driver (dcd) for 32-bit ARM
|
|
||||||
// targets.
|
|
||||||
type dcd struct {
|
|
||||||
*dhw // USB hardware abstraction layer
|
|
||||||
|
|
||||||
core *core // Parent USB core this instance is attached to
|
|
||||||
port int // USB port index
|
|
||||||
cc class // USB device class
|
|
||||||
id int // USB device controller index
|
|
||||||
}
|
|
||||||
|
|
||||||
// initDCD initializes and assigns a free device controller instance to the
|
|
||||||
// given USB port. Returns the initialized device controller or nil if no free
|
|
||||||
// device controller instances remain.
|
|
||||||
func initDCD(port int, class class) (*dcd, status) {
|
|
||||||
if 0 == dcdCount {
|
|
||||||
return nil, statusInvalid // Must have defined device controllers
|
|
||||||
}
|
|
||||||
switch class.id {
|
|
||||||
case classDeviceCDCACM:
|
|
||||||
if 0 == class.config || class.config > descCDCACMCount {
|
|
||||||
return nil, statusInvalid // Must have defined descriptors
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
// Return the first instance whose assigned core is currently nil.
|
|
||||||
for i := range dcdInstance {
|
|
||||||
if nil == dcdInstance[i].core {
|
|
||||||
// Initialize device controller.
|
|
||||||
dcdInstance[i].dhw = allocDHW(port, i, &dcdInstance[i])
|
|
||||||
dcdInstance[i].core = &coreInstance[port]
|
|
||||||
dcdInstance[i].port = port
|
|
||||||
dcdInstance[i].cc = class
|
|
||||||
dcdInstance[i].id = i
|
|
||||||
return &dcdInstance[i], statusOK
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, statusBusy // No free device controller instances available.
|
|
||||||
}
|
|
||||||
|
|
||||||
// class returns the receiver's current device class configuration.
|
|
||||||
func (d *dcd) class() class { return d.cc }
|
|
||||||
|
|
||||||
// dcdSetupSize defines the size (bytes) of a USB standard setup packet.
|
|
||||||
const dcdSetupSize = 8 // bytes
|
|
||||||
|
|
||||||
// dcdSetup contains the USB standard setup packet used to configure a device.
|
|
||||||
type dcdSetup struct {
|
|
||||||
bmRequestType uint8
|
|
||||||
bRequest uint8
|
|
||||||
wValue uint16
|
|
||||||
wIndex uint16
|
|
||||||
wLength uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
// pack returns the receiver setup packet encoded as uint64.
|
|
||||||
func (s dcdSetup) pack() uint64 {
|
|
||||||
return ((uint64(s.bmRequestType) & 0xFF) << 0) |
|
|
||||||
((uint64(s.bRequest) & 0xFF) << 8) |
|
|
||||||
((uint64(s.wValue) & 0xFFFF) << 16) |
|
|
||||||
((uint64(s.wIndex) & 0xFFFF) << 32) |
|
|
||||||
((uint64(s.wLength) & 0xFFFF) << 48)
|
|
||||||
}
|
|
||||||
|
|
||||||
// dcdEvent is used to describe virtual interrupts on the USB bus to a device
|
|
||||||
// controller.
|
|
||||||
//
|
|
||||||
// Since the device controller software is intended for use with multiple TinyGo
|
|
||||||
// targets, all of which may not have exactly the same USB bus interrupts, a
|
|
||||||
// "virtual interrupt" is defined that is common to all targets. The target's
|
|
||||||
// hardware implementation (type dhw) is responsible for translating real system
|
|
||||||
// interrupts it receives into the appropriate virtual interrupt code, defined
|
|
||||||
// below, and notifying the device controller via method (*dcd).event(dcdEvent).
|
|
||||||
type dcdEvent struct {
|
|
||||||
id uint8
|
|
||||||
setup dcdSetup
|
|
||||||
mask uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enumerated constants for all possible USB device controller interrupt codes.
|
|
||||||
const (
|
|
||||||
dcdEventInvalid uint8 = iota // Invalid interrupt
|
|
||||||
dcdEventStatusReset // USB reset received
|
|
||||||
dcdEventStatusRun // USB controller entered run state
|
|
||||||
dcdEventStatusSuspend // USB suspend received
|
|
||||||
dcdEventStatusError // USB error condition detected on bus
|
|
||||||
dcdEventControlSetup // USB setup received
|
|
||||||
dcdEventPeripheralReady // USB PHY powered and ready to _go_
|
|
||||||
dcdEventTransactComplete // USB transaction complete
|
|
||||||
dcdEventTimer // USB (system) timer
|
|
||||||
)
|
|
||||||
|
|
||||||
func (d *dcd) event(ev dcdEvent) {
|
|
||||||
|
|
||||||
switch ev.id {
|
|
||||||
case dcdEventInvalid:
|
|
||||||
case dcdEventStatusReset:
|
|
||||||
d.endpointMask = 0
|
|
||||||
|
|
||||||
case dcdEventPeripheralReady:
|
|
||||||
// Configure and enable control endpoint 0
|
|
||||||
d.endpointEnable(0, true, 0)
|
|
||||||
|
|
||||||
case dcdEventStatusRun:
|
|
||||||
case dcdEventStatusSuspend:
|
|
||||||
case dcdEventStatusError:
|
|
||||||
case dcdEventControlSetup:
|
|
||||||
// On control endpoint 0 setup events, the ev.setup field will be defined
|
|
||||||
switch d.controlSetup(ev.setup) {
|
|
||||||
case dcdStageSetup:
|
|
||||||
case dcdStageData:
|
|
||||||
case dcdStageStatus:
|
|
||||||
case dcdStageStall:
|
|
||||||
d.controlStall()
|
|
||||||
}
|
|
||||||
|
|
||||||
case dcdEventTransactComplete:
|
|
||||||
case dcdEventTimer:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dcdStage represents the USB transaction stage of a control request.
|
|
||||||
type dcdStage uint8
|
|
||||||
|
|
||||||
// Enumerated constants for all possible USB transaction stages.
|
|
||||||
const (
|
|
||||||
dcdStageSetup dcdStage = iota // Indicates no stage transition required
|
|
||||||
dcdStageData // IN/OUT data transfer
|
|
||||||
dcdStageStatus // Setup request complete
|
|
||||||
dcdStageStall // Unhandled or invalid request
|
|
||||||
)
|
|
||||||
|
|
||||||
// controlSetup handles setup messages on control endpoint 0.
|
|
||||||
func (d *dcd) controlSetup(sup dcdSetup) dcdStage {
|
|
||||||
|
|
||||||
// Reset endpoint 0 notify mask
|
|
||||||
d.controlMask = 0
|
|
||||||
|
|
||||||
// First, switch on the type of request (standard, class, or vendor)
|
|
||||||
switch sup.bmRequestType & descRequestTypeTypeMsk {
|
|
||||||
|
|
||||||
// === STANDARD REQUEST ===
|
|
||||||
case descRequestTypeTypeStandard:
|
|
||||||
|
|
||||||
// Switch on the recepient and direction of the request
|
|
||||||
switch sup.bmRequestType &
|
|
||||||
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
|
|
||||||
|
|
||||||
// --- DEVICE Rx (OUT) ---
|
|
||||||
case descRequestTypeRecipientDevice | descRequestTypeDirOut:
|
|
||||||
|
|
||||||
// Identify which request was received
|
|
||||||
switch sup.bRequest {
|
|
||||||
|
|
||||||
// SET ADDRESS (0x05):
|
|
||||||
case descRequestStandardSetAddress:
|
|
||||||
d.setDeviceAddress(sup.wValue)
|
|
||||||
d.controlReceive(uintptr(0), 0, false)
|
|
||||||
return dcdStageSetup
|
|
||||||
|
|
||||||
// SET CONFIGURATION (0x09):
|
|
||||||
case descRequestStandardSetConfiguration:
|
|
||||||
d.cc.config = int(sup.wValue)
|
|
||||||
if 0 == d.cc.config || d.cc.config > dcdCount {
|
|
||||||
// Use default if invalid index received
|
|
||||||
d.cc.config = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Respond based on our device class configuration
|
|
||||||
switch d.cc.id {
|
|
||||||
|
|
||||||
// CDC-ACM (single)
|
|
||||||
case classDeviceCDCACM:
|
|
||||||
d.uartConfigure()
|
|
||||||
d.controlReceive(uintptr(0), 0, false)
|
|
||||||
|
|
||||||
// HID
|
|
||||||
case classDeviceHID:
|
|
||||||
d.serialConfigure()
|
|
||||||
d.keyboardConfigure()
|
|
||||||
d.mouseConfigure()
|
|
||||||
d.joystickConfigure()
|
|
||||||
d.controlReceive(uintptr(0), 0, false)
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled device class
|
|
||||||
}
|
|
||||||
return dcdStageSetup
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled request
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- DEVICE Tx (IN) ---
|
|
||||||
case descRequestTypeRecipientDevice | descRequestTypeDirIn:
|
|
||||||
|
|
||||||
// Identify which request was received
|
|
||||||
switch sup.bRequest {
|
|
||||||
|
|
||||||
// GET STATUS (0x00):
|
|
||||||
case descRequestStandardGetStatus:
|
|
||||||
d.controlReply[0] = 0
|
|
||||||
d.controlReply[1] = 0
|
|
||||||
d.controlTransmit(
|
|
||||||
uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false)
|
|
||||||
return dcdStageSetup
|
|
||||||
|
|
||||||
// GET DESCRIPTOR (0x06):
|
|
||||||
case descRequestStandardGetDescriptor:
|
|
||||||
|
|
||||||
// Respond based on our device class configuration
|
|
||||||
switch d.cc.id {
|
|
||||||
|
|
||||||
// CDC-ACM (single)
|
|
||||||
case classDeviceCDCACM:
|
|
||||||
d.controlDescriptorCDCACM(sup)
|
|
||||||
return dcdStageSetup
|
|
||||||
|
|
||||||
// HID
|
|
||||||
case classDeviceHID:
|
|
||||||
d.controlDescriptorHID(sup)
|
|
||||||
return dcdStageSetup
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled device class
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET CONFIGURATION (0x08):
|
|
||||||
case descRequestStandardGetConfiguration:
|
|
||||||
d.controlReply[0] = uint8(d.cc.config)
|
|
||||||
d.controlTransmit(
|
|
||||||
uintptr(unsafe.Pointer(&d.controlReply[0])), 1, false)
|
|
||||||
return dcdStageSetup
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled request
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- INTERFACE Tx (IN) ---
|
|
||||||
case descRequestTypeRecipientInterface | descRequestTypeDirIn:
|
|
||||||
|
|
||||||
// Identify which request was received
|
|
||||||
switch sup.bRequest {
|
|
||||||
|
|
||||||
// GET DESCRIPTOR (0x06):
|
|
||||||
case descRequestStandardGetDescriptor:
|
|
||||||
|
|
||||||
// Respond based on our device class configuration
|
|
||||||
switch d.cc.id {
|
|
||||||
|
|
||||||
// CDC-ACM (single)
|
|
||||||
case classDeviceCDCACM:
|
|
||||||
d.controlDescriptorCDCACM(sup)
|
|
||||||
return dcdStageSetup
|
|
||||||
|
|
||||||
// HID
|
|
||||||
case classDeviceHID:
|
|
||||||
d.controlDescriptorHID(sup)
|
|
||||||
return dcdStageSetup
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled device class
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET DESCRIPTOR (0x06):
|
|
||||||
case descHIDRequestGetReport:
|
|
||||||
|
|
||||||
// Respond based on our device class configuration
|
|
||||||
switch d.cc.id {
|
|
||||||
|
|
||||||
// HID
|
|
||||||
case classDeviceHID:
|
|
||||||
d.controlDescriptorHID(sup)
|
|
||||||
return dcdStageSetup
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled device class
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled request
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- ENDPOINT Rx (OUT) ---
|
|
||||||
case descRequestTypeRecipientEndpoint | descRequestTypeDirOut:
|
|
||||||
|
|
||||||
// Identify which request was received
|
|
||||||
switch sup.bRequest {
|
|
||||||
|
|
||||||
// CLEAR FEATURE (0x01):
|
|
||||||
case descRequestStandardClearFeature:
|
|
||||||
d.endpointClearFeature(uint8(sup.wIndex))
|
|
||||||
d.controlReceive(uintptr(0), 0, false)
|
|
||||||
return dcdStageSetup
|
|
||||||
|
|
||||||
// SET FEATURE (0x03):
|
|
||||||
case descRequestStandardSetFeature:
|
|
||||||
d.endpointSetFeature(uint8(sup.wIndex))
|
|
||||||
d.controlReceive(uintptr(0), 0, false)
|
|
||||||
return dcdStageSetup
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled request
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- ENDPOINT Tx (IN) ---
|
|
||||||
case descRequestTypeRecipientEndpoint | descRequestTypeDirIn:
|
|
||||||
|
|
||||||
// Identify which request was received
|
|
||||||
switch sup.bRequest {
|
|
||||||
|
|
||||||
// GET STATUS (0x00):
|
|
||||||
case descRequestStandardGetStatus:
|
|
||||||
status := d.endpointStatus(uint8(sup.wIndex))
|
|
||||||
d.controlReply[0] = uint8(status)
|
|
||||||
d.controlReply[1] = uint8(status >> 8)
|
|
||||||
d.controlTransmit(
|
|
||||||
uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false)
|
|
||||||
return dcdStageSetup
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled request
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled request recepient or direction
|
|
||||||
}
|
|
||||||
|
|
||||||
// === CLASS REQUEST ===
|
|
||||||
case descRequestTypeTypeClass:
|
|
||||||
|
|
||||||
// Switch on the recepient and direction of the request
|
|
||||||
switch sup.bmRequestType &
|
|
||||||
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
|
|
||||||
|
|
||||||
// --- INTERFACE Rx (OUT) ---
|
|
||||||
case descRequestTypeRecipientInterface | descRequestTypeDirOut:
|
|
||||||
|
|
||||||
// Identify which request was received
|
|
||||||
switch sup.bRequest {
|
|
||||||
|
|
||||||
// CDC | SET LINE CODING (0x20):
|
|
||||||
case descCDCRequestSetLineCoding:
|
|
||||||
|
|
||||||
// Respond based on our device class configuration
|
|
||||||
switch d.cc.id {
|
|
||||||
|
|
||||||
// CDC-ACM (single)
|
|
||||||
case classDeviceCDCACM:
|
|
||||||
// line coding must contain exactly 7 bytes
|
|
||||||
if descCDCACMCodingSize == sup.wLength {
|
|
||||||
d.setup = sup
|
|
||||||
d.controlReceive(
|
|
||||||
uintptr(unsafe.Pointer(&descCDCACM[d.cc.config-1].cx[0])),
|
|
||||||
descCDCACMCodingSize, true)
|
|
||||||
// CDC Line Coding packet receipt handling occurs in method
|
|
||||||
// controlComplete().
|
|
||||||
return dcdStageSetup
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled device class
|
|
||||||
}
|
|
||||||
|
|
||||||
// CDC | SET CONTROL LINE STATE (0x22):
|
|
||||||
case descCDCRequestSetControlLineState:
|
|
||||||
|
|
||||||
// Respond based on our device class configuration
|
|
||||||
switch d.cc.id {
|
|
||||||
|
|
||||||
// CDC-ACM (single)
|
|
||||||
case classDeviceCDCACM:
|
|
||||||
|
|
||||||
// Determine interface destination of the request
|
|
||||||
switch sup.wIndex {
|
|
||||||
|
|
||||||
// Control/status interface:
|
|
||||||
case descCDCACMInterfaceCtrl:
|
|
||||||
// DTR is bit 0 (mask 0x01), RTS is bit 1 (mask 0x02)
|
|
||||||
d.uartSetLineState(0 != sup.wValue&0x01, 0 != sup.wValue&0x02)
|
|
||||||
d.controlReceive(uintptr(0), 0, false)
|
|
||||||
return dcdStageSetup
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled device interface
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled device class
|
|
||||||
}
|
|
||||||
|
|
||||||
// CDC | SEND BREAK (0x23):
|
|
||||||
case descCDCRequestSendBreak:
|
|
||||||
|
|
||||||
// Respond based on our device class configuration
|
|
||||||
switch d.cc.id {
|
|
||||||
|
|
||||||
// CDC-ACM (single)
|
|
||||||
case classDeviceCDCACM:
|
|
||||||
d.controlReceive(uintptr(0), 0, false)
|
|
||||||
return dcdStageSetup
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled device class
|
|
||||||
}
|
|
||||||
|
|
||||||
// HID | SET REPORT (0x09)
|
|
||||||
case descHIDRequestSetReport:
|
|
||||||
|
|
||||||
// Respond based on our device class configuration
|
|
||||||
switch d.cc.id {
|
|
||||||
|
|
||||||
// HID
|
|
||||||
case classDeviceHID:
|
|
||||||
if sup.wLength <= descHIDCxCount {
|
|
||||||
d.setup = sup
|
|
||||||
descHID[d.cc.config-1].cx[0] = 0xE9
|
|
||||||
d.controlReceive(
|
|
||||||
uintptr(unsafe.Pointer(&descHID[d.cc.config-1].cx[0])),
|
|
||||||
uint32(sup.wLength), true)
|
|
||||||
return dcdStageSetup
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled device class
|
|
||||||
}
|
|
||||||
|
|
||||||
// HID | SET IDLE (0x0A)
|
|
||||||
case descHIDRequestSetIdle:
|
|
||||||
|
|
||||||
// Respond based on our device class configuration
|
|
||||||
switch d.cc.id {
|
|
||||||
|
|
||||||
// HID
|
|
||||||
case classDeviceHID:
|
|
||||||
idleRate := sup.wValue >> 8
|
|
||||||
// TBD: do we need to handle this request? wIndex contains the target
|
|
||||||
// interface of the request.
|
|
||||||
_ = idleRate
|
|
||||||
d.controlReceive(uintptr(0), 0, false)
|
|
||||||
return dcdStageSetup
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled device class
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled request
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- INTERFACE Tx (IN) ---
|
|
||||||
case descRequestTypeRecipientInterface | descRequestTypeDirIn:
|
|
||||||
|
|
||||||
// Identify which request was received
|
|
||||||
switch sup.bRequest {
|
|
||||||
|
|
||||||
// HID | GET REPORT (0x01)
|
|
||||||
case descHIDRequestGetReport:
|
|
||||||
|
|
||||||
// Respond based on our device class configuration
|
|
||||||
switch d.cc.id {
|
|
||||||
|
|
||||||
// HID
|
|
||||||
case classDeviceHID:
|
|
||||||
reportType := uint8(sup.wValue >> 8)
|
|
||||||
reportID := uint8(sup.wValue)
|
|
||||||
// TBD: do we need to handle this request? wIndex contains the target
|
|
||||||
// interface of the request.
|
|
||||||
_, _ = reportType, reportID
|
|
||||||
d.controlReply[0] = 0
|
|
||||||
d.controlReply[1] = 0
|
|
||||||
d.controlTransmit(
|
|
||||||
uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false)
|
|
||||||
return dcdStageSetup
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled device class
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled request
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled request recepient or direction
|
|
||||||
}
|
|
||||||
|
|
||||||
case descRequestTypeTypeVendor:
|
|
||||||
default:
|
|
||||||
// Unhandled request type
|
|
||||||
}
|
|
||||||
|
|
||||||
// All successful requests return early. If we reach this point, the request
|
|
||||||
// was invalid or unhandled. Stall the endpoint.
|
|
||||||
return dcdStageStall
|
|
||||||
}
|
|
||||||
|
|
||||||
// controlComplete handles the setup completion of control endpoint 0.
|
|
||||||
func (d *dcd) controlComplete(status uint32) {
|
|
||||||
|
|
||||||
// Reset endpoint 0 notify mask
|
|
||||||
d.controlMask = 0
|
|
||||||
|
|
||||||
// First, switch on the type of request (standard, class, or vendor)
|
|
||||||
switch d.setup.bmRequestType & descRequestTypeTypeMsk {
|
|
||||||
|
|
||||||
// === CLASS REQUEST ===
|
|
||||||
case descRequestTypeTypeClass:
|
|
||||||
|
|
||||||
// Switch on the recepient and direction of the request
|
|
||||||
switch d.setup.bmRequestType &
|
|
||||||
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
|
|
||||||
|
|
||||||
// --- INTERFACE Rx (OUT) ---
|
|
||||||
case descRequestTypeRecipientInterface | descRequestTypeDirOut:
|
|
||||||
|
|
||||||
// Identify which request was received
|
|
||||||
switch d.setup.bRequest {
|
|
||||||
|
|
||||||
// CDC | SET LINE CODING (0x20):
|
|
||||||
case descCDCRequestSetLineCoding:
|
|
||||||
|
|
||||||
// Respond based on our device class configuration
|
|
||||||
switch d.cc.id {
|
|
||||||
|
|
||||||
// CDC-ACM (single)
|
|
||||||
case classDeviceCDCACM:
|
|
||||||
acm := &descCDCACM[d.cc.config-1]
|
|
||||||
|
|
||||||
// Determine interface destination of the request
|
|
||||||
switch d.setup.wIndex {
|
|
||||||
|
|
||||||
// CDC-ACM Control Interface:
|
|
||||||
case descCDCACMInterfaceCtrl:
|
|
||||||
// Notify PHY to handle triggers like special baud rates, which
|
|
||||||
// signal to reboot into bootloader or begin receiving OTA updates
|
|
||||||
d.uartSetLineCoding(descCDCACMLineCoding{
|
|
||||||
baud: packU32(acm.cx[:]),
|
|
||||||
stopBits: acm.cx[4],
|
|
||||||
parity: acm.cx[5],
|
|
||||||
numBits: acm.cx[6],
|
|
||||||
})
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled device interface
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled device class
|
|
||||||
}
|
|
||||||
|
|
||||||
// HID | SET REPORT (0x09)
|
|
||||||
case descHIDRequestSetReport:
|
|
||||||
|
|
||||||
// Respond based on our device class configuration
|
|
||||||
switch d.cc.id {
|
|
||||||
|
|
||||||
// HID
|
|
||||||
case classDeviceHID:
|
|
||||||
hid := &descHID[d.cc.config-1]
|
|
||||||
|
|
||||||
// Determine interface destination of the request
|
|
||||||
switch d.setup.wIndex {
|
|
||||||
|
|
||||||
// HID Keyboard Interface
|
|
||||||
case descHIDInterfaceKeyboard:
|
|
||||||
|
|
||||||
// Determine the type of descriptor being requested
|
|
||||||
switch d.setup.wValue >> 8 {
|
|
||||||
|
|
||||||
// Configuration descriptor
|
|
||||||
case descTypeConfigure:
|
|
||||||
if 1 == d.setup.wLength {
|
|
||||||
hid.keyboard.led = hid.cx[0]
|
|
||||||
d.controlTransmit(uintptr(0), 0, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled descriptor type
|
|
||||||
}
|
|
||||||
|
|
||||||
// HID Serial Interface
|
|
||||||
case descHIDInterfaceSerial:
|
|
||||||
|
|
||||||
// Determine the type of descriptor being requested
|
|
||||||
switch d.setup.wValue >> 8 {
|
|
||||||
|
|
||||||
// String descriptor
|
|
||||||
case descTypeString:
|
|
||||||
if d.setup.wLength >= 4 && 0x68C245A9 == packU32(hid.cx[0:4]) {
|
|
||||||
d.enableSOF(true, descHIDInterfaceCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled descriptor type
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled device interface
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled device class
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled request
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled recepient or direction
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled request type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dcd) controlDescriptorCDCACM(sup dcdSetup) {
|
|
||||||
|
|
||||||
acm := &descCDCACM[d.cc.config-1]
|
|
||||||
dxn := uint8(0)
|
|
||||||
|
|
||||||
// Determine the type of descriptor being requested
|
|
||||||
switch sup.wValue >> 8 {
|
|
||||||
|
|
||||||
// Device descriptor
|
|
||||||
case descTypeDevice:
|
|
||||||
dxn = descLengthDevice
|
|
||||||
_ = copy(acm.dx[:], acm.device[:dxn])
|
|
||||||
|
|
||||||
// Configuration descriptor
|
|
||||||
case descTypeConfigure:
|
|
||||||
dxn = uint8(descCDCACMConfigSize)
|
|
||||||
_ = copy(acm.dx[:], acm.config[:dxn])
|
|
||||||
|
|
||||||
// String descriptor
|
|
||||||
case descTypeString:
|
|
||||||
if 0 == len(acm.locale) {
|
|
||||||
break // No string descriptors defined!
|
|
||||||
}
|
|
||||||
var sd []uint8
|
|
||||||
if 0 == uint8(sup.wValue) {
|
|
||||||
|
|
||||||
// setup.wIndex contains an arbitrary index referring to a collection of
|
|
||||||
// strings in some given language. This case (setup.wValue = [0x03]00)
|
|
||||||
// is a string request from the host to determine what that language is.
|
|
||||||
//
|
|
||||||
// In subsequent string requests, the host will populate setup.wIndex
|
|
||||||
// with the language code we return here in this string descriptor.
|
|
||||||
//
|
|
||||||
// This way all strings returned to the host are in the same language,
|
|
||||||
// whatever language that may be.
|
|
||||||
code := int(sup.wIndex)
|
|
||||||
if code >= len(acm.locale) {
|
|
||||||
code = 0
|
|
||||||
}
|
|
||||||
sd = acm.locale[code].descriptor[sup.wValue&0xFF][:]
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// setup.wIndex now contains a language code, which we specified in a
|
|
||||||
// previous request (above: setup.wValue = [0x03]00). We need to locate
|
|
||||||
// the set of strings whose language matches the language code given in
|
|
||||||
// this new setup.wIndex.
|
|
||||||
for code := range acm.locale {
|
|
||||||
if sup.wIndex == acm.locale[code].language {
|
|
||||||
// Found language, check if string descriptor at given index exists
|
|
||||||
if int(sup.wValue&0xFF) < len(acm.locale[code].descriptor) {
|
|
||||||
|
|
||||||
// Found language with a string defined at the requested index.
|
|
||||||
//
|
|
||||||
// TODO: Add API methods to device controller that allows the user
|
|
||||||
// to provide these strings at/before driver initialization.
|
|
||||||
//
|
|
||||||
// For now, we just always use the descCommon* strings.
|
|
||||||
var s string
|
|
||||||
switch uint8(sup.wValue) {
|
|
||||||
case 1:
|
|
||||||
s = descCommonManufacturer
|
|
||||||
case 2:
|
|
||||||
s = descCommonProduct + " CDC-ACM"
|
|
||||||
case 3:
|
|
||||||
s = descCommonSerialNumber
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct a string descriptor dynamically to be transmitted on
|
|
||||||
// the serial bus.
|
|
||||||
sd = acm.locale[code].descriptor[int(sup.wValue&0xFF)][:]
|
|
||||||
// String descriptor format is 2-byte header + 2-bytes per rune
|
|
||||||
sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length
|
|
||||||
sd[1] = descTypeString // header[1] = descriptor type
|
|
||||||
// Copy UTF-8 string into string descriptor as UTF-16
|
|
||||||
for n, c := range s {
|
|
||||||
if 2+2*n >= len(sd) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
sd[2+2*n] = uint8(c)
|
|
||||||
sd[3+2*n] = 0
|
|
||||||
}
|
|
||||||
break // end search for matching language code
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Copy string descriptor into descriptor transmit buffer
|
|
||||||
if nil != sd && len(sd) >= 0 {
|
|
||||||
dxn = sd[0]
|
|
||||||
_ = copy(acm.dx[:], sd[:dxn])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Device qualification descriptor
|
|
||||||
case descTypeQualification:
|
|
||||||
dxn = descLengthQualification
|
|
||||||
_ = copy(acm.dx[:], acm.qualif[:dxn])
|
|
||||||
|
|
||||||
// Alternate configuration descriptor
|
|
||||||
case descTypeOtherSpeedConfiguration:
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled descriptor type
|
|
||||||
}
|
|
||||||
|
|
||||||
if dxn > 0 {
|
|
||||||
if dxn > uint8(sup.wLength) {
|
|
||||||
dxn = uint8(sup.wLength)
|
|
||||||
}
|
|
||||||
flushCache(
|
|
||||||
uintptr(unsafe.Pointer(&acm.dx[0])), uintptr(dxn))
|
|
||||||
d.controlTransmit(
|
|
||||||
uintptr(unsafe.Pointer(&acm.dx[0])), uint32(dxn), false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dcd) controlDescriptorHID(sup dcdSetup) {
|
|
||||||
|
|
||||||
hid := &descHID[d.cc.config-1]
|
|
||||||
dxn := uint8(0)
|
|
||||||
pos := uint8(0)
|
|
||||||
|
|
||||||
// Determine the type of descriptor being requested
|
|
||||||
switch sup.wValue >> 8 {
|
|
||||||
|
|
||||||
// Device descriptor
|
|
||||||
case descTypeDevice:
|
|
||||||
dxn = descLengthDevice
|
|
||||||
_ = copy(hid.dx[:], hid.device[:dxn])
|
|
||||||
|
|
||||||
// Configuration descriptor
|
|
||||||
case descTypeConfigure:
|
|
||||||
dxn = uint8(descHIDConfigSize)
|
|
||||||
_ = copy(hid.dx[:], hid.config[:dxn])
|
|
||||||
|
|
||||||
// String descriptor
|
|
||||||
case descTypeString:
|
|
||||||
if 0 == len(hid.locale) {
|
|
||||||
break // No string descriptors defined!
|
|
||||||
}
|
|
||||||
var sd []uint8
|
|
||||||
if 0 == uint8(sup.wValue) {
|
|
||||||
|
|
||||||
// setup.wIndex contains an arbitrary index referring to a collection of
|
|
||||||
// strings in some given language. This case (setup.wValue = [0x03]00)
|
|
||||||
// is a string request from the host to determine what that language is.
|
|
||||||
//
|
|
||||||
// In subsequent string requests, the host will populate setup.wIndex
|
|
||||||
// with the language code we return here in this string descriptor.
|
|
||||||
//
|
|
||||||
// This way all strings returned to the host are in the same language,
|
|
||||||
// whatever language that may be.
|
|
||||||
code := int(sup.wIndex)
|
|
||||||
if code >= len(hid.locale) {
|
|
||||||
code = 0
|
|
||||||
}
|
|
||||||
sd = hid.locale[code].descriptor[sup.wValue&0xFF][:]
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// setup.wIndex now contains a language code, which we specified in a
|
|
||||||
// previous request (above: setup.wValue = [0x03]00). We need to locate
|
|
||||||
// the set of strings whose language matches the language code given in
|
|
||||||
// this new setup.wIndex.
|
|
||||||
for code := range hid.locale {
|
|
||||||
if sup.wIndex == hid.locale[code].language {
|
|
||||||
// Found language, check if string descriptor at given index exists
|
|
||||||
if int(sup.wValue&0xFF) < len(hid.locale[code].descriptor) {
|
|
||||||
|
|
||||||
// Found language with a string defined at the requested index.
|
|
||||||
//
|
|
||||||
// TODO: Add API methods to device controller that allows the user
|
|
||||||
// to provide these strings at/before driver initialization.
|
|
||||||
//
|
|
||||||
// For now, we just always use the descCommon* strings.
|
|
||||||
var s string
|
|
||||||
switch uint8(sup.wValue) {
|
|
||||||
case 1:
|
|
||||||
s = descCommonManufacturer
|
|
||||||
case 2:
|
|
||||||
s = descCommonProduct + " HID"
|
|
||||||
case 3:
|
|
||||||
s = descCommonSerialNumber
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct a string descriptor dynamically to be transmitted on
|
|
||||||
// the serial bus.
|
|
||||||
sd = hid.locale[code].descriptor[int(sup.wValue&0xFF)][:]
|
|
||||||
// String descriptor format is 2-byte header + 2-bytes per rune
|
|
||||||
sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length
|
|
||||||
sd[1] = descTypeString // header[1] = descriptor type
|
|
||||||
// Copy UTF-8 string into string descriptor as UTF-16
|
|
||||||
for n, c := range s {
|
|
||||||
if 2+2*n >= len(sd) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
sd[2+2*n] = uint8(c)
|
|
||||||
sd[3+2*n] = 0
|
|
||||||
}
|
|
||||||
break // end search for matching language code
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Copy string descriptor into descriptor transmit buffer
|
|
||||||
if nil != sd && len(sd) >= 0 {
|
|
||||||
dxn = sd[0]
|
|
||||||
_ = copy(hid.dx[:], sd[:dxn])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Device qualification descriptor
|
|
||||||
case descTypeQualification:
|
|
||||||
dxn = descLengthQualification
|
|
||||||
_ = copy(hid.dx[:], hid.qualif[:dxn])
|
|
||||||
|
|
||||||
// Alternate configuration descriptor
|
|
||||||
case descTypeOtherSpeedConfiguration:
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
// HID descriptor
|
|
||||||
case descTypeHID:
|
|
||||||
|
|
||||||
// Determine interface destination of the request
|
|
||||||
switch sup.wIndex {
|
|
||||||
case descHIDInterfaceKeyboard:
|
|
||||||
pos = descHIDConfigKeyboardPos
|
|
||||||
|
|
||||||
case descHIDInterfaceMouse:
|
|
||||||
pos = descHIDConfigMousePos
|
|
||||||
|
|
||||||
case descHIDInterfaceSerial:
|
|
||||||
pos = descHIDConfigSerialPos
|
|
||||||
|
|
||||||
case descHIDInterfaceJoystick:
|
|
||||||
pos = descHIDConfigJoystickPos
|
|
||||||
|
|
||||||
case descHIDInterfaceMediaKey:
|
|
||||||
pos = descHIDConfigMediaKeyPos
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled HID interface
|
|
||||||
}
|
|
||||||
|
|
||||||
if 0 != pos {
|
|
||||||
dxn = descLengthInterface
|
|
||||||
_ = copy(hid.dx[:], hid.config[pos:pos+dxn])
|
|
||||||
}
|
|
||||||
|
|
||||||
// HID report descriptor
|
|
||||||
case descTypeHIDReport:
|
|
||||||
|
|
||||||
// Determine interface destination of the request
|
|
||||||
switch sup.wIndex {
|
|
||||||
case descHIDInterfaceKeyboard:
|
|
||||||
dxn = uint8(len(descHIDReportKeyboard))
|
|
||||||
_ = copy(hid.dx[:], descHIDReportKeyboard[:])
|
|
||||||
|
|
||||||
case descHIDInterfaceMouse:
|
|
||||||
dxn = uint8(len(descHIDReportMouse))
|
|
||||||
_ = copy(hid.dx[:], descHIDReportMouse[:])
|
|
||||||
|
|
||||||
case descHIDInterfaceSerial:
|
|
||||||
dxn = uint8(len(descHIDReportSerial))
|
|
||||||
_ = copy(hid.dx[:], descHIDReportSerial[:])
|
|
||||||
|
|
||||||
case descHIDInterfaceJoystick:
|
|
||||||
dxn = uint8(len(descHIDReportJoystick))
|
|
||||||
_ = copy(hid.dx[:], descHIDReportJoystick[:])
|
|
||||||
|
|
||||||
case descHIDInterfaceMediaKey:
|
|
||||||
dxn = uint8(len(descHIDReportMediaKey))
|
|
||||||
_ = copy(hid.dx[:], descHIDReportMediaKey[:])
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled HID interface
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unhandled descriptor type
|
|
||||||
}
|
|
||||||
|
|
||||||
if dxn > 0 {
|
|
||||||
if dxn > uint8(sup.wLength) {
|
|
||||||
dxn = uint8(sup.wLength)
|
|
||||||
}
|
|
||||||
flushCache(
|
|
||||||
uintptr(unsafe.Pointer(&hid.dx[0])), uintptr(dxn))
|
|
||||||
d.controlTransmit(
|
|
||||||
uintptr(unsafe.Pointer(&hid.dx[0])), uint32(dxn), false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,604 +0,0 @@
|
|||||||
// +build mimxrt1062
|
|
||||||
|
|
||||||
package usb
|
|
||||||
|
|
||||||
// descCPUFrequencyHz defines the target CPU frequency (Hz).
|
|
||||||
const descCPUFrequencyHz = 600000000
|
|
||||||
|
|
||||||
// descCDCACMCount defines the number of USB cores that may be configured as
|
|
||||||
// CDC-ACM (single) devices.
|
|
||||||
const descCDCACMCount = 0
|
|
||||||
|
|
||||||
// descHIDCount defines the number of USB cores that may be configured as a
|
|
||||||
// composite (keyboard + mouse + joystick) human interface device (HID).
|
|
||||||
const descHIDCount = 1
|
|
||||||
|
|
||||||
// General USB device identification constants.
|
|
||||||
const (
|
|
||||||
descCommonVendorID = 0x16C0
|
|
||||||
descCommonProductID = 0x0483
|
|
||||||
descCommonReleaseID = 0x0101 // BCD (1.1)
|
|
||||||
|
|
||||||
descCommonLanguage = descLanguageEnglish
|
|
||||||
descCommonManufacturer = "TinyGo"
|
|
||||||
descCommonProduct = "USB"
|
|
||||||
descCommonSerialNumber = "00000"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Constants for USB CDC-ACM device classes.
|
|
||||||
const (
|
|
||||||
descCDCACMMaxPower = 50 // 100 mA
|
|
||||||
|
|
||||||
// CDC-ACM Control Buffers
|
|
||||||
|
|
||||||
descCDCACMQHCount = 2 * (descCDCACMEndpointCount + 1)
|
|
||||||
descCDCACMCxCount = 8
|
|
||||||
|
|
||||||
// CDC-ACM Data Buffers
|
|
||||||
|
|
||||||
descCDCACMRDCount = 2 * descCDCACMEndpointCount
|
|
||||||
descCDCACMRxSize = descCDCACMDataRxPacketSize
|
|
||||||
descCDCACMRxCount = descCDCACMRxSize * descCDCACMRDCount
|
|
||||||
|
|
||||||
descCDCACMTDCount = descCDCACMEndpointCount
|
|
||||||
descCDCACMTxSize = 4 * descCDCACMDataTxPacketSize
|
|
||||||
descCDCACMTxCount = descCDCACMTxSize * descCDCACMTDCount
|
|
||||||
|
|
||||||
descCDCACMTxTimeoutMs = 120 // millisec
|
|
||||||
descCDCACMTxSyncUs = 75 // microsec
|
|
||||||
|
|
||||||
// Default CDC-ACM Endpoint Configurations (High-Speed)
|
|
||||||
|
|
||||||
descCDCACMStatusInterval = descCDCACMStatusHSInterval // Status
|
|
||||||
descCDCACMStatusPacketSize = descCDCACMStatusHSPacketSize //
|
|
||||||
|
|
||||||
descCDCACMDataRxPacketSize = descCDCACMDataRxHSPacketSize // Data Rx
|
|
||||||
|
|
||||||
descCDCACMDataTxPacketSize = descCDCACMDataTxHSPacketSize // Data Tx
|
|
||||||
|
|
||||||
// CDC-ACM Endpoint Configurations for Full-Speed Device
|
|
||||||
|
|
||||||
descCDCACMStatusFSInterval = 5 // Status
|
|
||||||
descCDCACMStatusFSPacketSize = 16 // (full-speed)
|
|
||||||
|
|
||||||
descCDCACMDataRxFSPacketSize = 64 // Data Rx (full-speed)
|
|
||||||
|
|
||||||
descCDCACMDataTxFSPacketSize = 64 // Data Tx (full-speed)
|
|
||||||
|
|
||||||
// CDC-ACM Endpoint Configurations for High-Speed Device
|
|
||||||
|
|
||||||
descCDCACMStatusHSInterval = 5 // Status
|
|
||||||
descCDCACMStatusHSPacketSize = 16 // (high-speed)
|
|
||||||
|
|
||||||
descCDCACMDataRxHSPacketSize = 512 // Data Rx (high-speed)
|
|
||||||
|
|
||||||
descCDCACMDataTxHSPacketSize = 512 // Data Tx (high-speed)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Constants for USB HID (keyboard, mouse, joystick) device classes.
|
|
||||||
const (
|
|
||||||
descHIDMaxPower = 50 // 100 mA
|
|
||||||
|
|
||||||
// HID Control Buffers
|
|
||||||
|
|
||||||
descHIDQHCount = 2 * (descHIDEndpointCount + 1)
|
|
||||||
descHIDCxCount = 8
|
|
||||||
|
|
||||||
// HID Serial Buffers
|
|
||||||
|
|
||||||
descHIDSerialRDCount = 8
|
|
||||||
descHIDSerialRxSize = descHIDSerialRxPacketSize
|
|
||||||
descHIDSerialRxCount = descHIDSerialRxSize * descHIDSerialRDCount
|
|
||||||
|
|
||||||
descHIDSerialTDCount = 12
|
|
||||||
descHIDSerialTxSize = descHIDSerialTxPacketSize
|
|
||||||
descHIDSerialTxCount = descHIDSerialTxSize * descHIDSerialTDCount
|
|
||||||
|
|
||||||
descHIDSerialTxTimeoutMs = 50 // millisec
|
|
||||||
descHIDSerialTxSyncUs = 75 // microsec
|
|
||||||
|
|
||||||
// HID Keyboard Buffers
|
|
||||||
|
|
||||||
descHIDKeyboardTDCount = 12
|
|
||||||
descHIDKeyboardTxSize = 4 * descHIDKeyboardTxPacketSize
|
|
||||||
descHIDKeyboardTxCount = descHIDKeyboardTxSize * descHIDKeyboardTDCount
|
|
||||||
|
|
||||||
descHIDKeyboardTxTimeoutMs = 50 // millisec
|
|
||||||
|
|
||||||
// HID Mouse Buffers
|
|
||||||
|
|
||||||
descHIDMouseTDCount = 4
|
|
||||||
descHIDMouseTxSize = 4 * descHIDMouseTxPacketSize
|
|
||||||
descHIDMouseTxCount = descHIDMouseTxSize * descHIDMouseTDCount
|
|
||||||
|
|
||||||
descHIDMouseTxTimeoutMs = 30 // millisec
|
|
||||||
|
|
||||||
// HID Joystick Buffers
|
|
||||||
|
|
||||||
descHIDJoystickTDCount = 4
|
|
||||||
descHIDJoystickTxSize = 4 * descHIDJoystickTxPacketSize
|
|
||||||
descHIDJoystickTxCount = descHIDJoystickTxSize * descHIDJoystickTDCount
|
|
||||||
|
|
||||||
descHIDJoystickTxTimeoutMs = 30 // millisec
|
|
||||||
|
|
||||||
// Default HID Endpoint Configurations (High-Speed)
|
|
||||||
|
|
||||||
descHIDSerialRxInterval = descHIDSerialRxHSInterval // Serial Rx
|
|
||||||
descHIDSerialRxPacketSize = descHIDSerialRxHSPacketSize //
|
|
||||||
|
|
||||||
descHIDSerialTxInterval = descHIDSerialTxHSInterval // Serial Tx
|
|
||||||
descHIDSerialTxPacketSize = descHIDSerialTxHSPacketSize //
|
|
||||||
|
|
||||||
descHIDKeyboardTxInterval = descHIDKeyboardTxHSInterval // Keyboard
|
|
||||||
descHIDKeyboardTxPacketSize = descHIDKeyboardTxHSPacketSize //
|
|
||||||
|
|
||||||
descHIDMediaKeyTxInterval = descHIDMediaKeyTxHSInterval // Keyboard Media Keys
|
|
||||||
descHIDMediaKeyTxPacketSize = descHIDMediaKeyTxHSPacketSize //
|
|
||||||
|
|
||||||
descHIDMouseTxInterval = descHIDMouseTxHSInterval // Mouse
|
|
||||||
descHIDMouseTxPacketSize = descHIDMouseTxHSPacketSize //
|
|
||||||
|
|
||||||
descHIDJoystickTxInterval = descHIDJoystickTxHSInterval // Joystick
|
|
||||||
descHIDJoystickTxPacketSize = descHIDJoystickTxHSPacketSize //
|
|
||||||
|
|
||||||
// HID Endpoint Configurations for Full-Speed Device
|
|
||||||
|
|
||||||
descHIDSerialRxFSInterval = 2 // Serial Rx
|
|
||||||
descHIDSerialRxFSPacketSize = 8 // (full-speed)
|
|
||||||
|
|
||||||
descHIDSerialTxFSInterval = 1 // Serial Tx
|
|
||||||
descHIDSerialTxFSPacketSize = 16 // (full-speed)
|
|
||||||
|
|
||||||
descHIDKeyboardTxFSInterval = 4 // Keyboard
|
|
||||||
descHIDKeyboardTxFSPacketSize = 8 // (full-speed)
|
|
||||||
|
|
||||||
descHIDMediaKeyTxFSInterval = 4 // Keyboard Media Keys
|
|
||||||
descHIDMediaKeyTxFSPacketSize = 8 // (full-speed)
|
|
||||||
|
|
||||||
descHIDMouseTxFSInterval = 4 // Mouse
|
|
||||||
descHIDMouseTxFSPacketSize = 8 // (full-speed)
|
|
||||||
|
|
||||||
descHIDJoystickTxFSInterval = 4 // Joystick
|
|
||||||
descHIDJoystickTxFSPacketSize = 12 // (full-speed)
|
|
||||||
|
|
||||||
// HID Endpoint Configurations for High-Speed Device
|
|
||||||
|
|
||||||
descHIDSerialRxHSInterval = 2 // Serial
|
|
||||||
descHIDSerialRxHSPacketSize = 32 // (high-speed)
|
|
||||||
|
|
||||||
descHIDSerialTxHSInterval = 1 // Serial Tx
|
|
||||||
descHIDSerialTxHSPacketSize = 64 // (high-speed)
|
|
||||||
|
|
||||||
descHIDKeyboardTxHSInterval = 1 // Keyboard
|
|
||||||
descHIDKeyboardTxHSPacketSize = 8 // (high-speed)
|
|
||||||
|
|
||||||
descHIDMediaKeyTxHSInterval = 4 // Keyboard Media Keys
|
|
||||||
descHIDMediaKeyTxHSPacketSize = 8 // (high-speed)
|
|
||||||
|
|
||||||
descHIDMouseTxHSInterval = 1 // Mouse
|
|
||||||
descHIDMouseTxHSPacketSize = 8 // (high-speed)
|
|
||||||
|
|
||||||
descHIDJoystickTxHSInterval = 2 // Joystick
|
|
||||||
descHIDJoystickTxHSPacketSize = 12 // (high-speed)
|
|
||||||
)
|
|
||||||
|
|
||||||
// descCDCACM0QH is an array of endpoint queue heads, which is where all
|
|
||||||
// transfers for a given endpoint are managed, for the default CDC-ACM (single)
|
|
||||||
// device class configuration (index 1).
|
|
||||||
//
|
|
||||||
// From the iMXRT1062 Reference Manual:
|
|
||||||
//
|
|
||||||
// Software must ensure that no interface data structure reachable
|
|
||||||
// by the Device Controller spans a 4K-page boundary.
|
|
||||||
//
|
|
||||||
// The [queue head] is a 48-byte data structure, but must be aligned on
|
|
||||||
// 64-byte boundaries.
|
|
||||||
//
|
|
||||||
// Endpoint queue heads are arranged in an array in a continuous area of
|
|
||||||
// memory pointed to by the USB.ENDPOINTLISTADDR pointer. The even-numbered
|
|
||||||
// device queue heads in the list support receive endpoints (OUT/SETUP) and
|
|
||||||
// the odd-numbered queue heads in the list are used for transmit endpoints
|
|
||||||
// (IN/INTERRUPT). The device controller will index into this array based upon
|
|
||||||
// the endpoint number received from the USB bus. All information necessary to
|
|
||||||
// respond to transactions for all primed transfers is contained in this list
|
|
||||||
// so the Device Controller can readily respond to incoming requests without
|
|
||||||
// having to traverse a linked list.
|
|
||||||
//go:align 4096
|
|
||||||
var descCDCACM0QH [descCDCACMQHCount]dhwEndpoint
|
|
||||||
|
|
||||||
// descCDCACM0CD is the transfer descriptor for messages transmitted or received
|
|
||||||
// on the status/control endpoint 0 for the default CDC-ACM (single) device
|
|
||||||
// class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descCDCACM0CD dhwTransfer
|
|
||||||
|
|
||||||
// descCDCACM0Cx is the buffer for control/status data received on endpoint 0 of
|
|
||||||
// the default CDC-ACM (single) device class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descCDCACM0Cx [descCDCACMCxCount]uint8
|
|
||||||
|
|
||||||
// descCDCACM0AD is the transfer descriptor for ackowledgement (ACK) messages
|
|
||||||
// transmitted or received on the status/control endpoint 0 for the default
|
|
||||||
// CDC-ACM (single) device class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descCDCACM0AD dhwTransfer
|
|
||||||
|
|
||||||
// descCDCACM0Dx is the transmit (Tx) buffer of descriptor data on endpoint 0
|
|
||||||
// for the default CDC-ACM (single) device class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descCDCACM0Dx [descCDCACMConfigSize]uint8
|
|
||||||
|
|
||||||
// descCDCACM0RD is an array of transfer descriptors for Rx (OUT) transfers,
|
|
||||||
// which describe to the device controller the location and quantity of data
|
|
||||||
// being received for a given transfer, for the default CDC-ACM (single) device
|
|
||||||
// class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descCDCACM0RD [descCDCACMRDCount]dhwTransfer
|
|
||||||
|
|
||||||
// descCDCACM0Rx is the receive (Rx) transfer buffer for the default CDC-ACM
|
|
||||||
// (single) device class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descCDCACM0Rx [descCDCACMRxCount]uint8
|
|
||||||
|
|
||||||
// descCDCACM0TD is an array of transfer descriptors for Tx (IN) transfers,
|
|
||||||
// which describe to the device controller the location and quantity of data
|
|
||||||
// being transmitted for a given transfer, for the default CDC-ACM (single)
|
|
||||||
// device class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descCDCACM0TD [descCDCACMTDCount]dhwTransfer
|
|
||||||
|
|
||||||
// descCDCACM0Tx is the transmit (Tx) transfer buffer for the default CDC-ACM
|
|
||||||
// (single) device class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descCDCACM0Tx [descCDCACMTxCount]uint8
|
|
||||||
|
|
||||||
var descCDCACM0RDNum [descCDCACMRDCount]uint16
|
|
||||||
var descCDCACM0RDIdx [descCDCACMRDCount]uint16
|
|
||||||
var descCDCACM0RDQue [(descCDCACMRDCount + 1)]uint16
|
|
||||||
|
|
||||||
// descCDCACMClassData holds the buffers and control states for all CDC-ACM
|
|
||||||
// (single) device class configurations, ordered by index (offset by -1), for
|
|
||||||
// iMXRT1062 targets only.
|
|
||||||
//
|
|
||||||
// Instances of this type (elements of descCDCACMData) are embedded in elements
|
|
||||||
// of the common/target-agnostic CDC-ACM class configurations (descCDCACM).
|
|
||||||
// Methods defined on this type implement target-specific functionality, and
|
|
||||||
// some of these methods are required by the common device controller driver.
|
|
||||||
// Thus, this type functions as a hardware abstraction layer (HAL).
|
|
||||||
type descCDCACMClassData struct {
|
|
||||||
|
|
||||||
// CDC-ACM Control Buffers
|
|
||||||
|
|
||||||
qh *[descCDCACMQHCount]dhwEndpoint // endpoint queue heads
|
|
||||||
|
|
||||||
cd *dhwTransfer // control endpoint 0 Rx/Tx transfer descriptor
|
|
||||||
cx *[descCDCACMCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer
|
|
||||||
ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor
|
|
||||||
dx *[descCDCACMConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer
|
|
||||||
|
|
||||||
// CDC-ACM Data Buffers
|
|
||||||
|
|
||||||
rd *[descCDCACMRDCount]dhwTransfer // bulk data endpoint Rx (OUT) transfer descriptors
|
|
||||||
rx *[descCDCACMRxCount]uint8 // bulk data endpoint Rx (OUT) transfer buffer
|
|
||||||
td *[descCDCACMTDCount]dhwTransfer // bulk data endpoint Tx (IN) transfer descriptors
|
|
||||||
tx *[descCDCACMTxCount]uint8 // bulk data endpoint Tx (IN) transfer buffer
|
|
||||||
|
|
||||||
rxCount *[descCDCACMRDCount]uint16
|
|
||||||
rxIndex *[descCDCACMRDCount]uint16
|
|
||||||
rxQueue *[(descCDCACMRDCount + 1)]uint16
|
|
||||||
|
|
||||||
sxSize uint16
|
|
||||||
rxSize uint16
|
|
||||||
txSize uint16
|
|
||||||
|
|
||||||
txHead uint8
|
|
||||||
txFree uint16
|
|
||||||
txPrev bool
|
|
||||||
|
|
||||||
rxHead uint8
|
|
||||||
rxTail uint8
|
|
||||||
rxFree uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
// descCDCACMData holds statically-allocated instances for each of the target-
|
|
||||||
// specific (iMXRT1062) CDC-ACM (single) device class configurations' control
|
|
||||||
// and data structures, ordered by configuration index (offset by -1). Each
|
|
||||||
// element is embedded in a corresponding element of descCDCACM.
|
|
||||||
//go:align 64
|
|
||||||
var descCDCACMData = [dcdCount]descCDCACMClassData{
|
|
||||||
|
|
||||||
{ // -- CDC-ACM (single) Class Configuration Index 1 --
|
|
||||||
|
|
||||||
// CDC-ACM Control Buffers
|
|
||||||
|
|
||||||
qh: &descCDCACM0QH,
|
|
||||||
|
|
||||||
cd: &descCDCACM0CD,
|
|
||||||
cx: &descCDCACM0Cx,
|
|
||||||
ad: &descCDCACM0AD,
|
|
||||||
dx: &descCDCACM0Dx,
|
|
||||||
|
|
||||||
// CDC-ACM Data Buffers
|
|
||||||
|
|
||||||
rd: &descCDCACM0RD,
|
|
||||||
rx: &descCDCACM0Rx,
|
|
||||||
td: &descCDCACM0TD,
|
|
||||||
tx: &descCDCACM0Tx,
|
|
||||||
|
|
||||||
rxCount: &descCDCACM0RDNum,
|
|
||||||
rxIndex: &descCDCACM0RDIdx,
|
|
||||||
rxQueue: &descCDCACM0RDQue,
|
|
||||||
|
|
||||||
sxSize: descCDCACMStatusPacketSize,
|
|
||||||
rxSize: descCDCACMDataRxPacketSize,
|
|
||||||
txSize: descCDCACMDataTxPacketSize,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// descHID0QH is an array of endpoint queue heads, which is where all transfers
|
|
||||||
// for a given endpoint are managed, for the default HID device class
|
|
||||||
// configuration (index 1).
|
|
||||||
//
|
|
||||||
// From the iMXRT1062 Reference Manual:
|
|
||||||
//
|
|
||||||
// Software must ensure that no interface data structure reachable
|
|
||||||
// by the Device Controller spans a 4K-page boundary.
|
|
||||||
//
|
|
||||||
// The [queue head] is a 48-byte data structure, but must be aligned on
|
|
||||||
// 64-byte boundaries.
|
|
||||||
//
|
|
||||||
// Endpoint queue heads are arranged in an array in a continuous area of
|
|
||||||
// memory pointed to by the USB.ENDPOINTLISTADDR pointer. The even-numbered
|
|
||||||
// device queue heads in the list support receive endpoints (OUT/SETUP) and
|
|
||||||
// the odd-numbered queue heads in the list are used for transmit endpoints
|
|
||||||
// (IN/INTERRUPT). The device controller will index into this array based upon
|
|
||||||
// the endpoint number received from the USB bus. All information necessary to
|
|
||||||
// respond to transactions for all primed transfers is contained in this list
|
|
||||||
// so the Device Controller can readily respond to incoming requests without
|
|
||||||
// having to traverse a linked list.
|
|
||||||
//go:align 4096
|
|
||||||
var descHID0QH [descHIDQHCount]dhwEndpoint
|
|
||||||
|
|
||||||
// descHID0CD is the transfer descriptor for messages transmitted or received on
|
|
||||||
// the status/control endpoint 0 for the default HID device class configuration
|
|
||||||
// (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descHID0CD dhwTransfer
|
|
||||||
|
|
||||||
// descHID0Cx is the buffer for control/status data received on endpoint 0 of
|
|
||||||
// the default HID device class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descHID0Cx [descHIDCxCount]uint8
|
|
||||||
|
|
||||||
// descHID0AD is the transfer descriptor for ackowledgement (ACK) messages
|
|
||||||
// transmitted or received on the status/control endpoint 0 for the default HID
|
|
||||||
// device class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descHID0AD dhwTransfer
|
|
||||||
|
|
||||||
// descHID0Dx is the transmit (Tx) buffer of descriptor data on endpoint 0 for
|
|
||||||
// the default HID device class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descHID0Dx [descHIDConfigSize]uint8
|
|
||||||
|
|
||||||
// descHID0SerialRD is an array of transfer descriptors for serial Rx (OUT)
|
|
||||||
// transfers, which describe to the device controller the location and quantity
|
|
||||||
// of data being received for a given transfer, for the default HID device class
|
|
||||||
// configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descHID0SerialRD [descHIDSerialRDCount]dhwTransfer
|
|
||||||
|
|
||||||
// descHID0SerialRx is the serial receive (Rx) transfer buffer for the default
|
|
||||||
// HID device class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descHID0SerialRx [descHIDSerialRxCount]uint8
|
|
||||||
|
|
||||||
// descHID0SerialTD is an array of transfer descriptors for serial Tx (IN)
|
|
||||||
// transfers, which describe to the device controller the location and quantity
|
|
||||||
// of data being transmitted for a given transfer, for the default HID device
|
|
||||||
// class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descHID0SerialTD [descHIDSerialTDCount]dhwTransfer
|
|
||||||
|
|
||||||
// descHID0SerialTx is the serial transmit (Tx) transfer buffer for the default
|
|
||||||
// HID device class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descHID0SerialTx [descHIDSerialTxCount]uint8
|
|
||||||
|
|
||||||
var descHID0SerialRDIdx [descHIDSerialRDCount]uint16
|
|
||||||
var descHID0SerialRDQue [(descHIDSerialRDCount + 1)]uint16
|
|
||||||
|
|
||||||
// descHID0KeyboardTD is an array of transfer descriptors for keyboard Tx (IN)
|
|
||||||
// transfers, which describe to the device controller the location and quantity
|
|
||||||
// of data being transmitted for a given transfer, for the default HID device
|
|
||||||
// class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descHID0KeyboardTD [descHIDKeyboardTDCount]dhwTransfer
|
|
||||||
|
|
||||||
// descHID0KeyboardTx is the keyboard transmit (Tx) transfer buffer for the
|
|
||||||
// default HID device class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descHID0KeyboardTx [descHIDKeyboardTxCount]uint8
|
|
||||||
|
|
||||||
// descHID0KeyboardTp is the keyboard HID report transmit (Tx) transfer buffer
|
|
||||||
// for the default HID device class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descHID0KeyboardTp [descHIDKeyboardTxPacketSize]uint8
|
|
||||||
|
|
||||||
//go:align 32
|
|
||||||
var descHID0KeyboardTxKey [hidKeyboardKeyCount]uint8
|
|
||||||
|
|
||||||
//go:align 32
|
|
||||||
var descHID0KeyboardTxCon [hidKeyboardConCount]uint16
|
|
||||||
|
|
||||||
//go:align 32
|
|
||||||
var descHID0KeyboardTxSys [hidKeyboardSysCount]uint8
|
|
||||||
|
|
||||||
// descHID0MouseTD is an array of transfer descriptors for mouse Tx (IN)
|
|
||||||
// transfers, which describe to the device controller the location and quantity
|
|
||||||
// of data being transmitted for a given transfer, for the default HID device
|
|
||||||
// class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descHID0MouseTD [descHIDMouseTDCount]dhwTransfer
|
|
||||||
|
|
||||||
// descHID0MouseTx is the mouse transmit (Tx) transfer buffer for the default
|
|
||||||
// HID device class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descHID0MouseTx [descHIDMouseTxCount]uint8
|
|
||||||
|
|
||||||
// descHID0JoystickTD is an array of transfer descriptors for joystick Tx (IN)
|
|
||||||
// transfers, which describe to the device controller the location and quantity
|
|
||||||
// of data being transmitted for a given transfer, for the default HID device
|
|
||||||
// class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descHID0JoystickTD [descHIDJoystickTDCount]dhwTransfer
|
|
||||||
|
|
||||||
// descHID0JoystickTx is the joystick transmit (Tx) transfer buffer for the
|
|
||||||
// default HID device class configuration (index 1).
|
|
||||||
//go:align 32
|
|
||||||
var descHID0JoystickTx [descHIDJoystickTxCount]uint8
|
|
||||||
|
|
||||||
// descHID0Keyboard is the Keyboard instance with which the user may interact
|
|
||||||
// when using the default HID device class configuration (index 1).
|
|
||||||
//go:align 64
|
|
||||||
var descHID0Keyboard = Keyboard{
|
|
||||||
key: &descHID0KeyboardTxKey,
|
|
||||||
con: &descHID0KeyboardTxCon,
|
|
||||||
sys: &descHID0KeyboardTxSys,
|
|
||||||
}
|
|
||||||
|
|
||||||
// descHIDClassData holds the buffers and control states for all of the HID
|
|
||||||
// device class configurations, ordered by index (offset by -1), for iMXRT1062
|
|
||||||
// targets only.
|
|
||||||
//
|
|
||||||
// Instances of this type (elements of descHIDData) are embedded in elements
|
|
||||||
// of the common/target-agnostic HID class configurations (descHID).
|
|
||||||
// Methods defined on this type implement target-specific functionality, and
|
|
||||||
// some of these methods are required by the common device controller driver.
|
|
||||||
// Thus, this type functions as a hardware abstraction layer (HAL).
|
|
||||||
type descHIDClassData struct {
|
|
||||||
|
|
||||||
// HID Control Buffers
|
|
||||||
|
|
||||||
qh *[descHIDQHCount]dhwEndpoint // endpoint queue heads
|
|
||||||
|
|
||||||
cd *dhwTransfer // control endpoint 0 Rx/Tx transfer descriptor
|
|
||||||
cx *[descHIDCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer
|
|
||||||
ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor
|
|
||||||
dx *[descHIDConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer
|
|
||||||
|
|
||||||
// HID Serial Buffers
|
|
||||||
|
|
||||||
rdSerial *[descHIDSerialRDCount]dhwTransfer // interrupt endpoint serial Rx (OUT) transfer descriptors
|
|
||||||
rxSerial *[descHIDSerialRxCount]uint8 // interrupt endpoint serial Rx (OUT) transfer buffer
|
|
||||||
tdSerial *[descHIDSerialTDCount]dhwTransfer // interrupt endpoint serial Tx (IN) transfer descriptors
|
|
||||||
txSerial *[descHIDSerialTxCount]uint8 // interrupt endpoint serial Tx (IN) transfer buffer
|
|
||||||
|
|
||||||
rxSerialIndex *[descHIDSerialRDCount]uint16
|
|
||||||
rxSerialQueue *[(descHIDSerialRDCount + 1)]uint16
|
|
||||||
|
|
||||||
rxSerialSize uint16
|
|
||||||
txSerialSize uint16
|
|
||||||
|
|
||||||
txSerialHead uint8
|
|
||||||
txSerialFree uint16
|
|
||||||
txSerialPrev bool
|
|
||||||
|
|
||||||
rxSerialHead uint8
|
|
||||||
rxSerialTail uint8
|
|
||||||
rxSerialFree uint16
|
|
||||||
|
|
||||||
// HID Keyboard Buffers
|
|
||||||
|
|
||||||
tdKeyboard *[descHIDKeyboardTDCount]dhwTransfer // interrupt endpoint keyboard Tx (IN) transfer descriptors
|
|
||||||
txKeyboard *[descHIDKeyboardTxCount]uint8 // interrupt endpoint keyboard Tx (IN) transfer buffer
|
|
||||||
tpKeyboard *[descHIDKeyboardTxPacketSize]uint8 // interrupt endpoint keyboard Tx (IN) HID report bbuffer
|
|
||||||
|
|
||||||
txKeyboardSize uint16
|
|
||||||
|
|
||||||
txKeyboardHead uint8
|
|
||||||
txKeyboardPrev bool
|
|
||||||
|
|
||||||
// HID Mouse Buffers
|
|
||||||
|
|
||||||
tdMouse *[descHIDMouseTDCount]dhwTransfer // interrupt endpoint mouse Tx (IN) transfer descriptors
|
|
||||||
txMouse *[descHIDMouseTxCount]uint8 // interrupt endpoint mouse Tx (IN) transfer buffer
|
|
||||||
|
|
||||||
txMouseSize uint16
|
|
||||||
|
|
||||||
txMouseHead uint8
|
|
||||||
txMousePrev bool
|
|
||||||
|
|
||||||
// HID Joystick Buffers
|
|
||||||
|
|
||||||
tdJoystick *[descHIDJoystickTDCount]dhwTransfer // interrupt endpoint joystick Tx (IN) transfer descriptors
|
|
||||||
txJoystick *[descHIDJoystickTxCount]uint8 // interrupt endpoint joystick Tx (IN) transfer buffer
|
|
||||||
|
|
||||||
txJoystickSize uint16
|
|
||||||
|
|
||||||
txJoystickHead uint8
|
|
||||||
txJoystickPrev bool
|
|
||||||
|
|
||||||
// HID Device Instances
|
|
||||||
|
|
||||||
keyboard *Keyboard
|
|
||||||
}
|
|
||||||
|
|
||||||
// descHIDData holds statically-allocated instances for each of the target-
|
|
||||||
// specific (iMXRT1062) HID device class configurations' control and data
|
|
||||||
// structures, ordered by configuration index (offset by -1). Each element is
|
|
||||||
// embedded in a corresponding element of descHID.
|
|
||||||
//go:align 64
|
|
||||||
var descHIDData = [dcdCount]descHIDClassData{
|
|
||||||
|
|
||||||
{ // -- HID Class Configuration Index 1 --
|
|
||||||
|
|
||||||
// HID Control Buffers
|
|
||||||
|
|
||||||
qh: &descHID0QH,
|
|
||||||
|
|
||||||
cd: &descHID0CD,
|
|
||||||
cx: &descHID0Cx,
|
|
||||||
ad: &descHID0AD,
|
|
||||||
dx: &descHID0Dx,
|
|
||||||
|
|
||||||
// HID Serial Buffers
|
|
||||||
|
|
||||||
rdSerial: &descHID0SerialRD,
|
|
||||||
rxSerial: &descHID0SerialRx,
|
|
||||||
tdSerial: &descHID0SerialTD,
|
|
||||||
txSerial: &descHID0SerialTx,
|
|
||||||
|
|
||||||
rxSerialIndex: &descHID0SerialRDIdx,
|
|
||||||
rxSerialQueue: &descHID0SerialRDQue,
|
|
||||||
|
|
||||||
rxSerialSize: descHIDSerialRxPacketSize,
|
|
||||||
txSerialSize: descHIDSerialTxPacketSize,
|
|
||||||
|
|
||||||
// HID Keyboard Buffers
|
|
||||||
|
|
||||||
tdKeyboard: &descHID0KeyboardTD,
|
|
||||||
txKeyboard: &descHID0KeyboardTx,
|
|
||||||
tpKeyboard: &descHID0KeyboardTp,
|
|
||||||
|
|
||||||
txKeyboardSize: descHIDKeyboardTxPacketSize,
|
|
||||||
|
|
||||||
// HID Mouse Buffers
|
|
||||||
|
|
||||||
tdMouse: &descHID0MouseTD,
|
|
||||||
txMouse: &descHID0MouseTx,
|
|
||||||
|
|
||||||
txMouseSize: descHIDMouseTxPacketSize,
|
|
||||||
|
|
||||||
// HID Joystick Buffers
|
|
||||||
|
|
||||||
tdJoystick: &descHID0JoystickTD,
|
|
||||||
txJoystick: &descHID0JoystickTx,
|
|
||||||
|
|
||||||
txJoystickSize: descHIDJoystickTxPacketSize,
|
|
||||||
|
|
||||||
// HID Device Instances
|
|
||||||
|
|
||||||
keyboard: &descHID0Keyboard,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,53 +0,0 @@
|
|||||||
package usb
|
|
||||||
|
|
||||||
// Implementation of 32-bit target-agnostic USB host controller driver (hcd).
|
|
||||||
|
|
||||||
// hcdCount defines the number of USB cores to configure for host mode. It is
|
|
||||||
// computed as the sum of all declared host configuration descriptors.
|
|
||||||
const hcdCount = 0 // + ...
|
|
||||||
|
|
||||||
// hcdInstance provides statically-allocated instances of each USB host
|
|
||||||
// controller configured on this platform.
|
|
||||||
var hcdInstance [hcdCount]hcd
|
|
||||||
|
|
||||||
// hhwInstance provides statically-allocated instances of each USB hardware
|
|
||||||
// abstraction for ports configured as host on this platform.
|
|
||||||
var hhwInstance [hcdCount]hhw
|
|
||||||
|
|
||||||
// hcd implements USB host controller driver (hcd) interface.
|
|
||||||
type hcd struct {
|
|
||||||
*hhw // USB hardware abstraction layer
|
|
||||||
|
|
||||||
core *core // Parent USB core this instance is attached to
|
|
||||||
port int // USB port index
|
|
||||||
cc class // USB host class
|
|
||||||
id int // USB host controller index
|
|
||||||
}
|
|
||||||
|
|
||||||
// initHCD initializes and assigns a free host controller instance to the given
|
|
||||||
// USB port. Returns the initialized host controller or nil if no free host
|
|
||||||
// controller instances remain.
|
|
||||||
func initHCD(port int, class class) (*hcd, status) {
|
|
||||||
if 0 == hcdCount {
|
|
||||||
return nil, statusInvalid // Must have defined host controllers
|
|
||||||
}
|
|
||||||
switch class.id {
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
// Return the first instance whose assigned core is currently nil.
|
|
||||||
for i := range hcdInstance {
|
|
||||||
if nil == hcdInstance[i].core {
|
|
||||||
// Initialize host controller.
|
|
||||||
hcdInstance[i].hhw = allocHHW(port, i, &hcdInstance[i])
|
|
||||||
hcdInstance[i].core = &coreInstance[port]
|
|
||||||
hcdInstance[i].port = port
|
|
||||||
hcdInstance[i].cc = class
|
|
||||||
hcdInstance[i].id = i
|
|
||||||
return &hcdInstance[i], statusOK
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, statusBusy // No free host controller instances available.
|
|
||||||
}
|
|
||||||
|
|
||||||
// class returns the receiver's current host class configuration.
|
|
||||||
func (h *hcd) class() class { return h.cc }
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
// +build mimxrt1062
|
|
||||||
|
|
||||||
package usb
|
|
||||||
|
|
||||||
// Implementation of USB host controller driver (hcd) for NXP iMXRT1062.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"device/nxp"
|
|
||||||
"runtime/interrupt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// hcdInterruptPriority defines the priority for all USB host interrupts.
|
|
||||||
const hcdInterruptPriority = 3
|
|
||||||
|
|
||||||
// hcd implements USB host controller driver (hcd) interface.
|
|
||||||
type hhw struct {
|
|
||||||
*hcd // USB host controller driver
|
|
||||||
|
|
||||||
bus *nxp.USB_Type // USB core register
|
|
||||||
phy *nxp.USBPHY_Type // USB PHY register
|
|
||||||
irq interrupt.Interrupt // USB IRQ, only a single interrupt on iMXRT1062
|
|
||||||
}
|
|
||||||
|
|
||||||
// allocHHW returns a reference to the USB hardware abstraction for the given
|
|
||||||
// host controller driver. Should be called only one time and during host
|
|
||||||
// controller initialization.
|
|
||||||
func allocHHW(port, instance int, hc *hcd) *hhw {
|
|
||||||
switch port {
|
|
||||||
case 0:
|
|
||||||
hhwInstance[instance].hcd = hc
|
|
||||||
hhwInstance[instance].bus = nxp.USB1
|
|
||||||
hhwInstance[instance].phy = nxp.USBPHY1
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
hhwInstance[instance].hcd = hc
|
|
||||||
hhwInstance[instance].bus = nxp.USB2
|
|
||||||
hhwInstance[instance].phy = nxp.USBPHY2
|
|
||||||
}
|
|
||||||
|
|
||||||
return &hhwInstance[instance]
|
|
||||||
}
|
|
||||||
|
|
||||||
// init configures the USB port for host mode operation by initializing all
|
|
||||||
// endpoint and transfer descriptor data structures, initializing core registers
|
|
||||||
// and interrupts, resetting the USB PHY, and enabling power on the bust.
|
|
||||||
func (h *hhw) init() status {
|
|
||||||
|
|
||||||
return statusOK
|
|
||||||
}
|
|
||||||
|
|
||||||
// enable causes the USB core to enter (or exit) the normal run state and
|
|
||||||
// enables/disables all interrupts on the receiver's USB port.
|
|
||||||
func (h *hhw) enable(enable bool) {
|
|
||||||
if enable {
|
|
||||||
h.irq.Enable() // Enable USB interrupts
|
|
||||||
} else {
|
|
||||||
h.irq.Disable() // Disable USB interrupts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package usb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrHIDInvalidPort = errors.New("invalid USB port")
|
|
||||||
ErrHIDInvalidCore = errors.New("invalid USB core")
|
|
||||||
ErrHIDReportTransfer = errors.New("failed to transfer HID report")
|
|
||||||
)
|
|
||||||
|
|
||||||
type HID struct {
|
|
||||||
Port int
|
|
||||||
core *core
|
|
||||||
}
|
|
||||||
|
|
||||||
type HIDConfig struct {
|
|
||||||
// Port is the MCU's native USB core number. If in doubt, leave it
|
|
||||||
// uninitialized for default (0).
|
|
||||||
Port int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hid *HID) Configure(config HIDConfig) error {
|
|
||||||
|
|
||||||
if config.Port >= CoreCount || config.Port >= dcdCount {
|
|
||||||
return ErrHIDInvalidPort
|
|
||||||
}
|
|
||||||
hid.Port = config.Port
|
|
||||||
|
|
||||||
// verify we have a free USB port and take ownership of it
|
|
||||||
var st status
|
|
||||||
hid.core, st = initCore(hid.Port, class{id: classDeviceHID, config: 1})
|
|
||||||
if !st.ok() {
|
|
||||||
return ErrHIDInvalidPort
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hid *HID) Keyboard() *Keyboard {
|
|
||||||
return hid.core.dc.keyboard()
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,74 +0,0 @@
|
|||||||
package usb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrUARTInvalidPort = errors.New("invalid USB port")
|
|
||||||
ErrUARTInvalidCore = errors.New("invalid USB core")
|
|
||||||
ErrUARTEmptyBuffer = errors.New("USB receive buffer empty")
|
|
||||||
ErrUARTWriteFailed = errors.New("USB write failure")
|
|
||||||
)
|
|
||||||
|
|
||||||
// UART represents a virtual serial (UART) device emulation using the USB
|
|
||||||
// CDC-ACM device class driver.
|
|
||||||
type UART struct {
|
|
||||||
Port int
|
|
||||||
core *core
|
|
||||||
}
|
|
||||||
|
|
||||||
type UARTConfig struct {
|
|
||||||
// Port is the MCU's native USB core number. If in doubt, leave it
|
|
||||||
// uninitialized for default (0).
|
|
||||||
Port int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (uart *UART) Configure(config UARTConfig) error {
|
|
||||||
|
|
||||||
if config.Port >= CoreCount || config.Port >= dcdCount {
|
|
||||||
return ErrUARTInvalidPort
|
|
||||||
}
|
|
||||||
uart.Port = config.Port
|
|
||||||
|
|
||||||
// verify we have a free USB port and take ownership of it
|
|
||||||
var st status
|
|
||||||
uart.core, st = initCore(uart.Port, class{id: classDeviceCDCACM, config: 1})
|
|
||||||
if !st.ok() {
|
|
||||||
return ErrUARTInvalidPort
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Buffered returns the number of bytes currently stored in the RX buffer.
|
|
||||||
func (uart UART) Buffered() int {
|
|
||||||
return uart.core.dc.uartAvailable()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadByte reads a single byte from the RX buffer.
|
|
||||||
// If there is no data in the buffer, returns an error.
|
|
||||||
func (uart UART) ReadByte() (byte, error) {
|
|
||||||
n, ok := uart.core.dc.uartReadByte()
|
|
||||||
if !ok {
|
|
||||||
return 0, ErrUARTEmptyBuffer
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read from the RX buffer.
|
|
||||||
func (uart UART) Read(data []byte) (n int, err error) {
|
|
||||||
return uart.core.dc.uartRead(data), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteByte writes a single byte of data to the UART interface.
|
|
||||||
func (uart UART) WriteByte(c byte) error {
|
|
||||||
if !uart.core.dc.uartWriteByte(c) {
|
|
||||||
return ErrUARTWriteFailed
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write data to the UART.
|
|
||||||
func (uart UART) Write(data []byte) (n int, err error) {
|
|
||||||
return uart.core.dc.uartWrite(data), nil
|
|
||||||
}
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
package usb
|
|
||||||
|
|
||||||
// Hardware abstraction for USB ports configured as either host or device.
|
|
||||||
|
|
||||||
// CoreCount defines the total number of USB cores to configure in device or
|
|
||||||
// host mode.
|
|
||||||
const CoreCount = dcdCount + hcdCount
|
|
||||||
|
|
||||||
// coreInstance provides statically-allocated instances of each USB core
|
|
||||||
// configured on this platform.
|
|
||||||
var coreInstance [CoreCount]core
|
|
||||||
|
|
||||||
// core represents the core of a USB port configured as either host or device.
|
|
||||||
type core struct {
|
|
||||||
port int
|
|
||||||
mode int
|
|
||||||
dc *dcd
|
|
||||||
hc *hcd
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constant definitions for USB core operating modes.
|
|
||||||
const (
|
|
||||||
modeIdle = 0 // USB port has not been configured
|
|
||||||
modeDevice = 1
|
|
||||||
modeHost = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
// initCore initializes a free USB core with given operating mode on the USB
|
|
||||||
// port at given index, if available. Returns a reference to the initialized
|
|
||||||
// core or nil if the core is unavailable.
|
|
||||||
func initCore(port int, class class) (*core, status) {
|
|
||||||
|
|
||||||
iv := disableInterrupts()
|
|
||||||
defer enableInterrupts(iv)
|
|
||||||
|
|
||||||
if port < 0 || port >= CoreCount || 0 == class.config {
|
|
||||||
return nil, statusInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
if modeIdle != coreInstance[port].mode {
|
|
||||||
// Check if requested port is already configured as requested class. If so,
|
|
||||||
// just return a reference to the existing core instead of an error.
|
|
||||||
//
|
|
||||||
// This will allow, for instance, TinyGo examples that try to reconfigure
|
|
||||||
// the USB (CDC-ACM) UART port (which is already configured by the runtime)
|
|
||||||
// to continue without error.
|
|
||||||
if coreInstance[port].mode == class.mode() {
|
|
||||||
switch class.mode() {
|
|
||||||
case modeDevice:
|
|
||||||
if coreInstance[port].dc.class().equals(class) {
|
|
||||||
return &coreInstance[port], statusOK
|
|
||||||
}
|
|
||||||
case modeHost:
|
|
||||||
if coreInstance[port].hc.class().equals(class) {
|
|
||||||
return &coreInstance[port], statusOK
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, statusBusy
|
|
||||||
}
|
|
||||||
|
|
||||||
switch class.mode() {
|
|
||||||
case modeDevice:
|
|
||||||
// Allocate a free device controller and install interrupts
|
|
||||||
dc, st := initDCD(port, class)
|
|
||||||
if !st.ok() {
|
|
||||||
return nil, st
|
|
||||||
}
|
|
||||||
// Initialize buffers and device descriptors
|
|
||||||
if st = dc.init(); !st.ok() {
|
|
||||||
return nil, st
|
|
||||||
}
|
|
||||||
coreInstance[port].port = port
|
|
||||||
coreInstance[port].mode = modeDevice
|
|
||||||
coreInstance[port].dc = dc
|
|
||||||
dc.enable(true) // Enable interrupts and enter runtime
|
|
||||||
|
|
||||||
case modeHost:
|
|
||||||
// Allocate a free host controller and install interrupts
|
|
||||||
hc, st := initHCD(port, class)
|
|
||||||
if !st.ok() {
|
|
||||||
return nil, st
|
|
||||||
}
|
|
||||||
// Initialize buffers and device descriptors
|
|
||||||
if st = hc.init(); !st.ok() {
|
|
||||||
return nil, st
|
|
||||||
}
|
|
||||||
coreInstance[port].port = port
|
|
||||||
coreInstance[port].mode = modeHost
|
|
||||||
coreInstance[port].hc = hc
|
|
||||||
hc.enable(true) // Enable interrupts and enter runtime
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil, statusInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
return &coreInstance[port], statusOK
|
|
||||||
}
|
|
||||||
|
|
||||||
// class represents the type of a host/device and its class configuration index.
|
|
||||||
// The first valid configuration index is 1. Index 0 is reserved and invalid.
|
|
||||||
type class struct {
|
|
||||||
id int
|
|
||||||
config int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enumerated constants for all host/device class configurations.
|
|
||||||
const (
|
|
||||||
classDeviceCDCACM = 0
|
|
||||||
classDeviceHID = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
// mode returns the USB core operating mode of the receiver class c.
|
|
||||||
//go:inline
|
|
||||||
func (c class) mode() int {
|
|
||||||
switch c.id {
|
|
||||||
case classDeviceCDCACM, classDeviceHID:
|
|
||||||
return modeDevice
|
|
||||||
default:
|
|
||||||
return modeIdle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// equals returns true if and only if all fields of the given class are equal to
|
|
||||||
// those of the receiver c.
|
|
||||||
//go:inline
|
|
||||||
func (c class) equals(class class) bool {
|
|
||||||
return c.id == class.id && c.config == class.config
|
|
||||||
}
|
|
||||||
|
|
||||||
// status represents the return code of a subroutine.
|
|
||||||
type status uint8
|
|
||||||
|
|
||||||
// Constant definitions for all status codes used within the package.
|
|
||||||
const (
|
|
||||||
statusOK status = iota // Success
|
|
||||||
statusBusy // Busy
|
|
||||||
statusInvalid // Invalid argument
|
|
||||||
)
|
|
||||||
|
|
||||||
// ok returns true if and only if the receiver st equals statusOK.
|
|
||||||
//go:inline
|
|
||||||
func (s status) ok() bool { return statusOK == s }
|
|
||||||
@@ -1,271 +0,0 @@
|
|||||||
package usb
|
|
||||||
|
|
||||||
//go:linkname ticks runtime.ticks
|
|
||||||
func ticks() int64
|
|
||||||
|
|
||||||
// leU64 returns a slice containing 8 bytes from the given uint64 u.
|
|
||||||
//
|
|
||||||
// The returned bytes have little-endian ordering; that is, the first element
|
|
||||||
// at index 0 is the least-significant byte in u and index 7 is the most-
|
|
||||||
// significant byte.
|
|
||||||
//go:inline
|
|
||||||
func leU64(u uint64) []uint8 {
|
|
||||||
if u == 0 {
|
|
||||||
// skip all processing for the common case (u = 0)
|
|
||||||
return []uint8{0, 0, 0, 0, 0, 0, 0, 0}
|
|
||||||
}
|
|
||||||
return []uint8{
|
|
||||||
uint8(u), uint8(u >> 8), uint8(u >> 16), uint8(u >> 24),
|
|
||||||
uint8(u >> 32), uint8(u >> 40), uint8(u >> 48), uint8(u >> 56),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// leU32 returns a slice containing 4 bytes from the given uint32 u.
|
|
||||||
//
|
|
||||||
// The returned bytes have little-endian ordering; that is, the first element
|
|
||||||
// at index 0 is the least-significant byte in u and index 3 is the most-
|
|
||||||
// significant byte.
|
|
||||||
//go:inline
|
|
||||||
func leU32(u uint32) []uint8 {
|
|
||||||
if u == 0 {
|
|
||||||
// skip all processing for the common case (u = 0)
|
|
||||||
return []uint8{0, 0, 0, 0}
|
|
||||||
}
|
|
||||||
return []uint8{
|
|
||||||
uint8(u), uint8(u >> 8), uint8(u >> 16), uint8(u >> 24),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// leU16 returns a slice containing 2 bytes from the given uint16 u.
|
|
||||||
//
|
|
||||||
// The returned bytes have little-endian ordering; that is, the first element
|
|
||||||
// at index 0 is the least-significant byte in u and index 1 is the most-
|
|
||||||
// significant byte.
|
|
||||||
//go:inline
|
|
||||||
func leU16(u uint16) []uint8 {
|
|
||||||
if u == 0 {
|
|
||||||
// skip all processing for the common case (u = 0)
|
|
||||||
return []uint8{0, 0}
|
|
||||||
}
|
|
||||||
return []uint8{
|
|
||||||
uint8(u), uint8(u >> 8),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// beU64 returns a slice containing 8 bytes from the given uint64 u.
|
|
||||||
//
|
|
||||||
// The returned bytes have big-endian ordering; that is, the first element at
|
|
||||||
// index 0 is the most-significant byte in u and index 7 is the least-
|
|
||||||
// significant byte.
|
|
||||||
//go:inline
|
|
||||||
func beU64(u uint64) []uint8 {
|
|
||||||
if u == 0 {
|
|
||||||
// skip all processing for the common case (u = 0)
|
|
||||||
return []uint8{0, 0, 0, 0, 0, 0, 0, 0}
|
|
||||||
}
|
|
||||||
return []uint8{
|
|
||||||
uint8(u >> 56), uint8(u >> 48), uint8(u >> 40), uint8(u >> 32),
|
|
||||||
uint8(u >> 24), uint8(u >> 16), uint8(u >> 8), uint8(u),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// beU32 returns a slice containing 4 bytes from the given uint32 u.
|
|
||||||
//
|
|
||||||
// The returned bytes have big-endian ordering; that is, the first element at
|
|
||||||
// index 0 is the most-significant byte in u and index 3 is the least-
|
|
||||||
// significant byte.
|
|
||||||
//go:inline
|
|
||||||
func beU32(u uint32) []uint8 {
|
|
||||||
if u == 0 {
|
|
||||||
// skip all processing for the common case (u = 0)
|
|
||||||
return []uint8{0, 0, 0, 0}
|
|
||||||
}
|
|
||||||
return []uint8{
|
|
||||||
uint8(u >> 24), uint8(u >> 16), uint8(u >> 8), uint8(u),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// beU16 returns a slice containing 2 bytes from the given uint16 u.
|
|
||||||
//
|
|
||||||
// The returned bytes have big-endian ordering; that is, the first element at
|
|
||||||
// index 0 is the most-significant byte in u and index 1 is the least-
|
|
||||||
// significant byte.
|
|
||||||
//go:inline
|
|
||||||
func beU16(u uint16) []uint8 {
|
|
||||||
if u == 0 {
|
|
||||||
// skip all processing for the common case (u = 0)
|
|
||||||
return []uint8{0, 0}
|
|
||||||
}
|
|
||||||
return []uint8{
|
|
||||||
uint8(u >> 8), uint8(u),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// revU64 returns the given uint64 u with bytes in the reverse order.
|
|
||||||
//go:inline
|
|
||||||
func revU64(u uint64) uint64 {
|
|
||||||
if u == 0 {
|
|
||||||
// skip all processing for the common case (u = 0)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return ((u & 0x00000000000000FF) << 56) |
|
|
||||||
((u & 0x000000000000FF00) << 40) |
|
|
||||||
((u & 0x0000000000FF0000) << 24) |
|
|
||||||
((u & 0x00000000FF000000) << 8) |
|
|
||||||
((u & 0x000000FF00000000) >> 8) |
|
|
||||||
((u & 0x0000FF0000000000) >> 24) |
|
|
||||||
((u & 0x00FF000000000000) >> 40) |
|
|
||||||
((u & 0xFF00000000000000) >> 56)
|
|
||||||
}
|
|
||||||
|
|
||||||
// revU32 returns the given uint32 u with bytes in the reverse order.
|
|
||||||
//go:inline
|
|
||||||
func revU32(u uint32) uint32 {
|
|
||||||
if u == 0 {
|
|
||||||
// skip all processing for the common case (u = 0)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return ((u & 0x000000FF) << 24) | ((u & 0x0000FF00) << 8) |
|
|
||||||
((u & 0x00FF0000) >> 8) | ((u & 0xFF000000) >> 24)
|
|
||||||
}
|
|
||||||
|
|
||||||
// revU16 returns the given uint16 u with bytes in the reverse order.
|
|
||||||
//go:inline
|
|
||||||
func revU16(u uint16) uint16 {
|
|
||||||
if u == 0 {
|
|
||||||
// skip all processing for the common case (u = 0)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return ((u & 0x00FF) << 8) | ((u & 0xFF00) >> 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
// packU64 returns a uint64 constructed by concatenating the bytes in slice b.
|
|
||||||
//
|
|
||||||
// The least-significant byte in the returned value is the first element at
|
|
||||||
// index 0 in b and the most significant byte is index 7, if given. If fewer
|
|
||||||
// than 8 elements are given in b, the corresponding bytes in the returned value
|
|
||||||
// are all 0.
|
|
||||||
//go:inline
|
|
||||||
func packU64(b []uint8) (u uint64) {
|
|
||||||
for i := 0; i < 8 && i < len(b); i++ {
|
|
||||||
u |= uint64(b[i]) << (i * 8)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// packU32 returns a uint32 constructed by concatenating the bytes in slice b.
|
|
||||||
//
|
|
||||||
// The least-significant byte in the returned value is the first element at
|
|
||||||
// index 0 in b and the most significant byte is index 3, if given. If fewer
|
|
||||||
// than 4 elements are given in b, the corresponding bytes in the returned value
|
|
||||||
// are all 0.
|
|
||||||
//go:inline
|
|
||||||
func packU32(b []uint8) (u uint32) {
|
|
||||||
for i := 0; i < 4 && i < len(b); i++ {
|
|
||||||
u |= uint32(b[i]) << (i * 8)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// packU16 returns a uint16 constructed by concatenating the bytes in slice b.
|
|
||||||
//
|
|
||||||
// The least-significant byte in the returned value is the first element at
|
|
||||||
// index 0 in b and the most significant byte is index 1, if given. If fewer
|
|
||||||
// than 2 elements are given in b, the corresponding bytes in the returned value
|
|
||||||
// are all 0.
|
|
||||||
//go:inline
|
|
||||||
func packU16(b []uint8) (u uint16) {
|
|
||||||
for i := 0; i < 2 && i < len(b); i++ {
|
|
||||||
u |= uint16(b[i]) << (i * 8)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// msU8 returns the most-significant byte of u.
|
|
||||||
//go:inline
|
|
||||||
func msU8(u uint16) uint8 { return uint8(u >> 8) }
|
|
||||||
|
|
||||||
// lsU8 returns the least-significant byte of u.
|
|
||||||
//go:inline
|
|
||||||
func lsU8(u uint16) uint8 { return uint8(u) }
|
|
||||||
|
|
||||||
// cycles converts the given number of microseconds to CPU cycles for a CPU with
|
|
||||||
// given frequency.
|
|
||||||
//go:inline
|
|
||||||
func cycles(microsec, cpuFreqHz uint32) uint32 {
|
|
||||||
return uint32((uint64(microsec) * uint64(cpuFreqHz)) / 1000000)
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:inline
|
|
||||||
func unpackEndpoint(address uint8) (number, direction uint8) {
|
|
||||||
return (address & descEndptAddrNumberMsk) >> descEndptAddrNumberPos,
|
|
||||||
(address & descEndptAddrDirectionMsk) >> descEndptAddrDirectionPos
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:inline
|
|
||||||
func rxEndpoint(number uint8) uint8 {
|
|
||||||
return (number & descEndptAddrNumberMsk) | descEndptAddrDirectionOut
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:inline
|
|
||||||
func txEndpoint(number uint8) uint8 {
|
|
||||||
return (number & descEndptAddrNumberMsk) | descEndptAddrDirectionIn
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:inline
|
|
||||||
func endpointIndex(address uint8) uint8 {
|
|
||||||
return ((address & descEndptAddrNumberMsk) << 1) |
|
|
||||||
((address & descEndptAddrDirectionMsk) >> descEndptAddrDirectionPos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The following buffLo and buffHi are helper methods for slice definitions from
|
|
||||||
// potentially zero-length arrays (depending on compile-time constants).
|
|
||||||
//
|
|
||||||
// For example, if we have an array containing a 5-element buffer for three
|
|
||||||
// instances of some device class (15 total elements), partitioned as follows,
|
|
||||||
// then we compute the indices for instance 2 as usual:
|
|
||||||
//
|
|
||||||
// Index: 01234 56789 ABCDE
|
|
||||||
// Array: [ 1 | 2 | 3 ]
|
|
||||||
//
|
|
||||||
// Lo: (n-1) * size => (2-1) * 5 => 5
|
|
||||||
// Hi: (n) * size => (2) * 5 => 10 (0xA)
|
|
||||||
//
|
|
||||||
// However, if we have specified (via const definition) that 0 instances of some
|
|
||||||
// device class be allocated, then the associated device class buffer arrays
|
|
||||||
// will all be zero-length arrays, and the arithmetic to compute the slice
|
|
||||||
// indices used above will result in out-of-bounds indices:
|
|
||||||
//
|
|
||||||
// Index:
|
|
||||||
// Array: []
|
|
||||||
//
|
|
||||||
// Lo: (n-1) * size => (2-1) * 5 => 5 [Error!]
|
|
||||||
// Hi: (n) * size => (2) * 5 => 10 (0xA) [Error!]
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// I couldn't figure out a straight-forward way to resolve these slice indices
|
|
||||||
// using only arithmetic, so I've resorted to simple conditionals. If the number
|
|
||||||
// of instances for some given class is zero (count=0), defined via compile-time
|
|
||||||
// constant, then just use the empty slice range [0:0].
|
|
||||||
|
|
||||||
// buffLo returns the starting array slice index for the n'th region of size
|
|
||||||
// elements from an array containing count regions of size elements.
|
|
||||||
// Regions are specified using a 1-based index (n > 0). Returns 0 if any given
|
|
||||||
// argument equals 0.
|
|
||||||
func buffLo(n, count, size uint16) uint16 {
|
|
||||||
if 0 == n || 0 == count || 0 == size {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return (n - 1) * size
|
|
||||||
}
|
|
||||||
|
|
||||||
// buffHi returns the ending array slice index for the n'th region of size
|
|
||||||
// elements from an array containing count regions of size elements.
|
|
||||||
// Regions are specified using a 1-based index (n > 0). Returns 0 if any given
|
|
||||||
// argument equals 0.
|
|
||||||
func buffHi(n, count, size uint16) uint16 {
|
|
||||||
if 0 == n || 0 == count || 0 == size {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return n * size
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
// +build arm
|
|
||||||
|
|
||||||
package usb
|
|
||||||
|
|
||||||
import "device/arm"
|
|
||||||
|
|
||||||
// udelay waits for the given number of microseconds before returning.
|
|
||||||
// We cannot use the sleep timer from this context (import cycle), but we need
|
|
||||||
// an approximate method to spin CPU cycles for short periods of time.
|
|
||||||
//go:inline
|
|
||||||
func udelay(microsec uint32) {
|
|
||||||
n := cycles(microsec, descCPUFrequencyHz)
|
|
||||||
for i := uint32(0); i < n; i++ {
|
|
||||||
arm.Asm(`nop`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func disableInterrupts() uintptr {
|
|
||||||
return arm.DisableInterrupts()
|
|
||||||
}
|
|
||||||
|
|
||||||
func enableInterrupts(mask uintptr) {
|
|
||||||
arm.EnableInterrupts(mask)
|
|
||||||
}
|
|
||||||
+5
-2
@@ -14,7 +14,10 @@
|
|||||||
|
|
||||||
package net
|
package net
|
||||||
|
|
||||||
import "internal/bytealg"
|
import (
|
||||||
|
"internal/bytealg"
|
||||||
|
"internal/itoa"
|
||||||
|
)
|
||||||
|
|
||||||
// IP address lengths (bytes).
|
// IP address lengths (bytes).
|
||||||
const (
|
const (
|
||||||
@@ -533,7 +536,7 @@ func (n *IPNet) String() string {
|
|||||||
if l == -1 {
|
if l == -1 {
|
||||||
return nn.String() + "/" + m.String()
|
return nn.String() + "/" + m.String()
|
||||||
}
|
}
|
||||||
return nn.String() + "/" + uitoa(uint(l))
|
return nn.String() + "/" + itoa.Uitoa(uint(l))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse IPv4 address (d.d.d.d).
|
// Parse IPv4 address (d.d.d.d).
|
||||||
|
|||||||
@@ -64,24 +64,6 @@ func xtoi2(s string, e byte) (byte, bool) {
|
|||||||
return byte(n), ok && ei == 2
|
return byte(n), ok && ei == 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert unsigned integer to decimal string.
|
|
||||||
func uitoa(val uint) string {
|
|
||||||
if val == 0 { // avoid string allocation
|
|
||||||
return "0"
|
|
||||||
}
|
|
||||||
var buf [20]byte // big enough for 64bit value base 10
|
|
||||||
i := len(buf) - 1
|
|
||||||
for val >= 10 {
|
|
||||||
q := val / 10
|
|
||||||
buf[i] = byte('0' + val - q*10)
|
|
||||||
i--
|
|
||||||
val = q
|
|
||||||
}
|
|
||||||
// val < 10
|
|
||||||
buf[i] = byte('0' + val)
|
|
||||||
return string(buf[i:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert i to a hexadecimal string. Leading zeros are not printed.
|
// Convert i to a hexadecimal string. Leading zeros are not printed.
|
||||||
func appendHex(dst []byte, i uint32) []byte {
|
func appendHex(dst []byte, i uint32) []byte {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
// +build !baremetal,!js
|
||||||
|
|
||||||
|
package os
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Mount the host filesystem at the root directory. This is what most
|
||||||
|
// programs will be expecting.
|
||||||
|
Mount("/", unixFilesystem{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
|
||||||
|
// standard output, and standard error file descriptors.
|
||||||
|
var (
|
||||||
|
Stdin = &File{unixFileHandle(syscall.Stdin), "/dev/stdin"}
|
||||||
|
Stdout = &File{unixFileHandle(syscall.Stdout), "/dev/stdout"}
|
||||||
|
Stderr = &File{unixFileHandle(syscall.Stderr), "/dev/stderr"}
|
||||||
|
)
|
||||||
|
|
||||||
|
// isOS indicates whether we're running on a real operating system with
|
||||||
|
// filesystem support.
|
||||||
|
const isOS = true
|
||||||
|
|
||||||
|
// unixFilesystem is an empty handle for a Unix/Linux filesystem. All operations
|
||||||
|
// are relative to the current working directory.
|
||||||
|
type unixFilesystem struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs unixFilesystem) Mkdir(path string, perm FileMode) error {
|
||||||
|
return handleSyscallError(syscall.Mkdir(path, uint32(perm)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs unixFilesystem) Remove(path string) error {
|
||||||
|
return handleSyscallError(syscall.Unlink(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs unixFilesystem) OpenFile(path string, flag int, perm FileMode) (FileHandle, error) {
|
||||||
|
// Map os package flags to syscall flags.
|
||||||
|
syscallFlag := 0
|
||||||
|
if flag&O_RDONLY != 0 {
|
||||||
|
syscallFlag |= syscall.O_RDONLY
|
||||||
|
}
|
||||||
|
if flag&O_WRONLY != 0 {
|
||||||
|
syscallFlag |= syscall.O_WRONLY
|
||||||
|
}
|
||||||
|
if flag&O_RDWR != 0 {
|
||||||
|
syscallFlag |= syscall.O_RDWR
|
||||||
|
}
|
||||||
|
if flag&O_APPEND != 0 {
|
||||||
|
syscallFlag |= syscall.O_APPEND
|
||||||
|
}
|
||||||
|
if flag&O_CREATE != 0 {
|
||||||
|
syscallFlag |= syscall.O_CREAT
|
||||||
|
}
|
||||||
|
if flag&O_EXCL != 0 {
|
||||||
|
syscallFlag |= syscall.O_EXCL
|
||||||
|
}
|
||||||
|
if flag&O_SYNC != 0 {
|
||||||
|
syscallFlag |= syscall.O_SYNC
|
||||||
|
}
|
||||||
|
if flag&O_TRUNC != 0 {
|
||||||
|
syscallFlag |= syscall.O_TRUNC
|
||||||
|
}
|
||||||
|
fp, err := syscall.Open(path, syscallFlag, uint32(perm))
|
||||||
|
return unixFileHandle(fp), handleSyscallError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unixFileHandle is a Unix file pointer with associated methods that implement
|
||||||
|
// the FileHandle interface.
|
||||||
|
type unixFileHandle uintptr
|
||||||
|
|
||||||
|
// Read reads up to len(b) bytes from the File. It returns the number of bytes
|
||||||
|
// read and any error encountered. At end of file, Read returns 0, io.EOF.
|
||||||
|
func (f unixFileHandle) Read(b []byte) (n int, err error) {
|
||||||
|
n, err = syscall.Read(syscallFd(f), b)
|
||||||
|
err = handleSyscallError(err)
|
||||||
|
if n == 0 && err == nil {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes len(b) bytes to the File. It returns the number of bytes written
|
||||||
|
// and an error, if any. Write returns a non-nil error when n != len(b).
|
||||||
|
func (f unixFileHandle) Write(b []byte) (n int, err error) {
|
||||||
|
n, err = syscall.Write(syscallFd(f), b)
|
||||||
|
err = handleSyscallError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the File, rendering it unusable for I/O.
|
||||||
|
func (f unixFileHandle) Close() error {
|
||||||
|
return handleSyscallError(syscall.Close(syscallFd(f)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleSyscallError converts syscall errors into regular os package errors.
|
||||||
|
// The err parameter must be either nil or of type syscall.Errno.
|
||||||
|
func handleSyscallError(err error) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch err.(syscall.Errno) {
|
||||||
|
case syscall.EEXIST:
|
||||||
|
return ErrExist
|
||||||
|
case syscall.ENOENT:
|
||||||
|
return ErrNotExist
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-110
@@ -2,113 +2,4 @@
|
|||||||
|
|
||||||
package os
|
package os
|
||||||
|
|
||||||
import (
|
type syscallFd = int
|
||||||
"io"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Mount the host filesystem at the root directory. This is what most
|
|
||||||
// programs will be expecting.
|
|
||||||
Mount("/", unixFilesystem{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
|
|
||||||
// standard output, and standard error file descriptors.
|
|
||||||
var (
|
|
||||||
Stdin = &File{unixFileHandle(0), "/dev/stdin"}
|
|
||||||
Stdout = &File{unixFileHandle(1), "/dev/stdout"}
|
|
||||||
Stderr = &File{unixFileHandle(2), "/dev/stderr"}
|
|
||||||
)
|
|
||||||
|
|
||||||
// isOS indicates whether we're running on a real operating system with
|
|
||||||
// filesystem support.
|
|
||||||
const isOS = true
|
|
||||||
|
|
||||||
// unixFilesystem is an empty handle for a Unix/Linux filesystem. All operations
|
|
||||||
// are relative to the current working directory.
|
|
||||||
type unixFilesystem struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs unixFilesystem) Mkdir(path string, perm FileMode) error {
|
|
||||||
return handleSyscallError(syscall.Mkdir(path, uint32(perm)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs unixFilesystem) Remove(path string) error {
|
|
||||||
return handleSyscallError(syscall.Unlink(path))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs unixFilesystem) OpenFile(path string, flag int, perm FileMode) (FileHandle, error) {
|
|
||||||
// Map os package flags to syscall flags.
|
|
||||||
syscallFlag := 0
|
|
||||||
if flag&O_RDONLY != 0 {
|
|
||||||
syscallFlag |= syscall.O_RDONLY
|
|
||||||
}
|
|
||||||
if flag&O_WRONLY != 0 {
|
|
||||||
syscallFlag |= syscall.O_WRONLY
|
|
||||||
}
|
|
||||||
if flag&O_RDWR != 0 {
|
|
||||||
syscallFlag |= syscall.O_RDWR
|
|
||||||
}
|
|
||||||
if flag&O_APPEND != 0 {
|
|
||||||
syscallFlag |= syscall.O_APPEND
|
|
||||||
}
|
|
||||||
if flag&O_CREATE != 0 {
|
|
||||||
syscallFlag |= syscall.O_CREAT
|
|
||||||
}
|
|
||||||
if flag&O_EXCL != 0 {
|
|
||||||
syscallFlag |= syscall.O_EXCL
|
|
||||||
}
|
|
||||||
if flag&O_SYNC != 0 {
|
|
||||||
syscallFlag |= syscall.O_SYNC
|
|
||||||
}
|
|
||||||
if flag&O_TRUNC != 0 {
|
|
||||||
syscallFlag |= syscall.O_TRUNC
|
|
||||||
}
|
|
||||||
fp, err := syscall.Open(path, syscallFlag, uint32(perm))
|
|
||||||
return unixFileHandle(fp), handleSyscallError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// unixFileHandle is a Unix file pointer with associated methods that implement
|
|
||||||
// the FileHandle interface.
|
|
||||||
type unixFileHandle uintptr
|
|
||||||
|
|
||||||
// Read reads up to len(b) bytes from the File. It returns the number of bytes
|
|
||||||
// read and any error encountered. At end of file, Read returns 0, io.EOF.
|
|
||||||
func (f unixFileHandle) Read(b []byte) (n int, err error) {
|
|
||||||
n, err = syscall.Read(int(f), b)
|
|
||||||
err = handleSyscallError(err)
|
|
||||||
if n == 0 && err == nil {
|
|
||||||
err = io.EOF
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes len(b) bytes to the File. It returns the number of bytes written
|
|
||||||
// and an error, if any. Write returns a non-nil error when n != len(b).
|
|
||||||
func (f unixFileHandle) Write(b []byte) (n int, err error) {
|
|
||||||
n, err = syscall.Write(int(f), b)
|
|
||||||
err = handleSyscallError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the File, rendering it unusable for I/O.
|
|
||||||
func (f unixFileHandle) Close() error {
|
|
||||||
return handleSyscallError(syscall.Close(int(f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleSyscallError converts syscall errors into regular os package errors.
|
|
||||||
// The err parameter must be either nil or of type syscall.Errno.
|
|
||||||
func handleSyscallError(err error) error {
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
switch err.(syscall.Errno) {
|
|
||||||
case syscall.EEXIST:
|
|
||||||
return ErrExist
|
|
||||||
case syscall.ENOENT:
|
|
||||||
return ErrNotExist
|
|
||||||
default:
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package os
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
type syscallFd = syscall.Handle
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package reflect_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
. "reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Basic struct {
|
||||||
|
x int
|
||||||
|
y float32
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotBasic Basic
|
||||||
|
|
||||||
|
type DeepEqualTest struct {
|
||||||
|
a, b interface{}
|
||||||
|
eq bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple functions for DeepEqual tests.
|
||||||
|
var (
|
||||||
|
fn1 func() // nil.
|
||||||
|
fn2 func() // nil.
|
||||||
|
fn3 = func() { fn1() } // Not nil.
|
||||||
|
)
|
||||||
|
|
||||||
|
type self struct{}
|
||||||
|
|
||||||
|
type Loopy interface{}
|
||||||
|
|
||||||
|
var loopy1, loopy2 Loopy
|
||||||
|
var cycleMap1, cycleMap2, cycleMap3 map[string]interface{}
|
||||||
|
|
||||||
|
type structWithSelfPtr struct {
|
||||||
|
p *structWithSelfPtr
|
||||||
|
s string
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
loopy1 = &loopy2
|
||||||
|
loopy2 = &loopy1
|
||||||
|
|
||||||
|
cycleMap1 = map[string]interface{}{}
|
||||||
|
cycleMap1["cycle"] = cycleMap1
|
||||||
|
cycleMap2 = map[string]interface{}{}
|
||||||
|
cycleMap2["cycle"] = cycleMap2
|
||||||
|
cycleMap3 = map[string]interface{}{}
|
||||||
|
cycleMap3["different"] = cycleMap3
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: all tests involving maps have been commented out because they aren't
|
||||||
|
// supported yet.
|
||||||
|
var deepEqualTests = []DeepEqualTest{
|
||||||
|
// Equalities
|
||||||
|
{nil, nil, true},
|
||||||
|
{1, 1, true},
|
||||||
|
{int32(1), int32(1), true},
|
||||||
|
{0.5, 0.5, true},
|
||||||
|
{float32(0.5), float32(0.5), true},
|
||||||
|
{"hello", "hello", true},
|
||||||
|
{make([]int, 10), make([]int, 10), true},
|
||||||
|
{&[3]int{1, 2, 3}, &[3]int{1, 2, 3}, true},
|
||||||
|
{Basic{1, 0.5}, Basic{1, 0.5}, true},
|
||||||
|
{error(nil), error(nil), true},
|
||||||
|
//{map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true},
|
||||||
|
{fn1, fn2, true},
|
||||||
|
{[]byte{1, 2, 3}, []byte{1, 2, 3}, true},
|
||||||
|
{[]MyByte{1, 2, 3}, []MyByte{1, 2, 3}, true},
|
||||||
|
{MyBytes{1, 2, 3}, MyBytes{1, 2, 3}, true},
|
||||||
|
|
||||||
|
// Inequalities
|
||||||
|
{1, 2, false},
|
||||||
|
{int32(1), int32(2), false},
|
||||||
|
{0.5, 0.6, false},
|
||||||
|
{float32(0.5), float32(0.6), false},
|
||||||
|
{"hello", "hey", false},
|
||||||
|
{make([]int, 10), make([]int, 11), false},
|
||||||
|
{&[3]int{1, 2, 3}, &[3]int{1, 2, 4}, false},
|
||||||
|
{Basic{1, 0.5}, Basic{1, 0.6}, false},
|
||||||
|
{Basic{1, 0}, Basic{2, 0}, false},
|
||||||
|
//{map[int]string{1: "one", 3: "two"}, map[int]string{2: "two", 1: "one"}, false},
|
||||||
|
//{map[int]string{1: "one", 2: "txo"}, map[int]string{2: "two", 1: "one"}, false},
|
||||||
|
//{map[int]string{1: "one"}, map[int]string{2: "two", 1: "one"}, false},
|
||||||
|
//{map[int]string{2: "two", 1: "one"}, map[int]string{1: "one"}, false},
|
||||||
|
{nil, 1, false},
|
||||||
|
{1, nil, false},
|
||||||
|
{fn1, fn3, false},
|
||||||
|
{fn3, fn3, false},
|
||||||
|
{[][]int{{1}}, [][]int{{2}}, false},
|
||||||
|
{&structWithSelfPtr{p: &structWithSelfPtr{s: "a"}}, &structWithSelfPtr{p: &structWithSelfPtr{s: "b"}}, false},
|
||||||
|
|
||||||
|
// Fun with floating point.
|
||||||
|
{math.NaN(), math.NaN(), false},
|
||||||
|
{&[1]float64{math.NaN()}, &[1]float64{math.NaN()}, false},
|
||||||
|
{&[1]float64{math.NaN()}, self{}, true},
|
||||||
|
{[]float64{math.NaN()}, []float64{math.NaN()}, false},
|
||||||
|
{[]float64{math.NaN()}, self{}, true},
|
||||||
|
//{map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false},
|
||||||
|
//{map[float64]float64{math.NaN(): 1}, self{}, true},
|
||||||
|
|
||||||
|
// Nil vs empty: not the same.
|
||||||
|
{[]int{}, []int(nil), false},
|
||||||
|
{[]int{}, []int{}, true},
|
||||||
|
{[]int(nil), []int(nil), true},
|
||||||
|
//{map[int]int{}, map[int]int(nil), false},
|
||||||
|
//{map[int]int{}, map[int]int{}, true},
|
||||||
|
//{map[int]int(nil), map[int]int(nil), true},
|
||||||
|
|
||||||
|
// Mismatched types
|
||||||
|
{1, 1.0, false},
|
||||||
|
{int32(1), int64(1), false},
|
||||||
|
{0.5, "hello", false},
|
||||||
|
{[]int{1, 2, 3}, [3]int{1, 2, 3}, false},
|
||||||
|
{&[3]interface{}{1, 2, 4}, &[3]interface{}{1, 2, "s"}, false},
|
||||||
|
{Basic{1, 0.5}, NotBasic{1, 0.5}, false},
|
||||||
|
{map[uint]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, false},
|
||||||
|
{[]byte{1, 2, 3}, []MyByte{1, 2, 3}, false},
|
||||||
|
{[]MyByte{1, 2, 3}, MyBytes{1, 2, 3}, false},
|
||||||
|
{[]byte{1, 2, 3}, MyBytes{1, 2, 3}, false},
|
||||||
|
|
||||||
|
// Possible loops.
|
||||||
|
{&loopy1, &loopy1, true},
|
||||||
|
{&loopy1, &loopy2, true},
|
||||||
|
//{&cycleMap1, &cycleMap2, true},
|
||||||
|
{&cycleMap1, &cycleMap3, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeepEqual(t *testing.T) {
|
||||||
|
for _, test := range deepEqualTests {
|
||||||
|
if test.b == (self{}) {
|
||||||
|
test.b = test.a
|
||||||
|
}
|
||||||
|
if r := DeepEqual(test.a, test.b); r != test.eq {
|
||||||
|
t.Errorf("DeepEqual(%#v, %#v) = %v, want %v", test.a, test.b, r, test.eq)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Recursive struct {
|
||||||
|
x int
|
||||||
|
r *Recursive
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeepEqualRecursiveStruct(t *testing.T) {
|
||||||
|
a, b := new(Recursive), new(Recursive)
|
||||||
|
*a = Recursive{12, a}
|
||||||
|
*b = Recursive{12, b}
|
||||||
|
if !DeepEqual(a, b) {
|
||||||
|
t.Error("DeepEqual(recursive same) = false, want true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type _Complex struct {
|
||||||
|
a int
|
||||||
|
b [3]*_Complex
|
||||||
|
c *string
|
||||||
|
d map[float64]float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeepEqualComplexStruct(t *testing.T) {
|
||||||
|
m := make(map[float64]float64)
|
||||||
|
stra, strb := "hello", "hello"
|
||||||
|
a, b := new(_Complex), new(_Complex)
|
||||||
|
*a = _Complex{5, [3]*_Complex{a, b, a}, &stra, m}
|
||||||
|
*b = _Complex{5, [3]*_Complex{b, a, a}, &strb, m}
|
||||||
|
if !DeepEqual(a, b) {
|
||||||
|
t.Error("DeepEqual(complex same) = false, want true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeepEqualComplexStructInequality(t *testing.T) {
|
||||||
|
m := make(map[float64]float64)
|
||||||
|
stra, strb := "hello", "helloo" // Difference is here
|
||||||
|
a, b := new(_Complex), new(_Complex)
|
||||||
|
*a = _Complex{5, [3]*_Complex{a, b, a}, &stra, m}
|
||||||
|
*b = _Complex{5, [3]*_Complex{b, a, a}, &strb, m}
|
||||||
|
if DeepEqual(a, b) {
|
||||||
|
t.Error("DeepEqual(complex different) = true, want false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyBytes []byte
|
||||||
|
type MyByte byte
|
||||||
+189
-2
@@ -1,9 +1,196 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Deep equality test via reflection
|
||||||
|
|
||||||
package reflect
|
package reflect
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
// During deepValueEqual, must keep track of checks that are
|
||||||
|
// in progress. The comparison algorithm assumes that all
|
||||||
|
// checks in progress are true when it reencounters them.
|
||||||
|
// Visited comparisons are stored in a map indexed by visit.
|
||||||
|
type visit struct {
|
||||||
|
a1 unsafe.Pointer
|
||||||
|
a2 unsafe.Pointer
|
||||||
|
typ rawType
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests for deep equality using reflected types. The map argument tracks
|
||||||
|
// comparisons that have already been seen, which allows short circuiting on
|
||||||
|
// recursive types.
|
||||||
|
func deepValueEqual(v1, v2 Value, visited map[visit]struct{}) bool {
|
||||||
|
if !v1.IsValid() || !v2.IsValid() {
|
||||||
|
return v1.IsValid() == v2.IsValid()
|
||||||
|
}
|
||||||
|
if v1.typecode != v2.typecode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want to avoid putting more in the visited map than we need to.
|
||||||
|
// For any possible reference cycle that might be encountered,
|
||||||
|
// hard(v1, v2) needs to return true for at least one of the types in the cycle,
|
||||||
|
// and it's safe and valid to get Value's internal pointer.
|
||||||
|
hard := func(v1, v2 Value) bool {
|
||||||
|
switch v1.Kind() {
|
||||||
|
case Map, Slice, Ptr, Interface:
|
||||||
|
// Nil pointers cannot be cyclic. Avoid putting them in the visited map.
|
||||||
|
return !v1.IsNil() && !v2.IsNil()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if hard(v1, v2) {
|
||||||
|
addr1 := v1.pointer()
|
||||||
|
addr2 := v2.pointer()
|
||||||
|
if uintptr(addr1) > uintptr(addr2) {
|
||||||
|
// Canonicalize order to reduce number of entries in visited.
|
||||||
|
// Assumes non-moving garbage collector.
|
||||||
|
addr1, addr2 = addr2, addr1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short circuit if references are already seen.
|
||||||
|
v := visit{addr1, addr2, v1.typecode}
|
||||||
|
if _, ok := visited[v]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remember for later.
|
||||||
|
visited[v] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v1.Kind() {
|
||||||
|
case Array:
|
||||||
|
for i := 0; i < v1.Len(); i++ {
|
||||||
|
if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case Slice:
|
||||||
|
if v1.IsNil() != v2.IsNil() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if v1.Len() != v2.Len() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if v1.Pointer() == v2.Pointer() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for i := 0; i < v1.Len(); i++ {
|
||||||
|
if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case Interface:
|
||||||
|
if v1.IsNil() || v2.IsNil() {
|
||||||
|
return v1.IsNil() == v2.IsNil()
|
||||||
|
}
|
||||||
|
return deepValueEqual(v1.Elem(), v2.Elem(), visited)
|
||||||
|
case Ptr:
|
||||||
|
if v1.Pointer() == v2.Pointer() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return deepValueEqual(v1.Elem(), v2.Elem(), visited)
|
||||||
|
case Struct:
|
||||||
|
for i, n := 0, v1.NumField(); i < n; i++ {
|
||||||
|
if !deepValueEqual(v1.Field(i), v2.Field(i), visited) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case Map:
|
||||||
|
if v1.IsNil() != v2.IsNil() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if v1.Len() != v2.Len() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if v1.Pointer() == v2.Pointer() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, k := range v1.MapKeys() {
|
||||||
|
val1 := v1.MapIndex(k)
|
||||||
|
val2 := v2.MapIndex(k)
|
||||||
|
if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(val1, val2, visited) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case Func:
|
||||||
|
if v1.IsNil() && v2.IsNil() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Can't do better than this:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
// Normal equality suffices
|
||||||
|
return valueInterfaceUnsafe(v1) == valueInterfaceUnsafe(v2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepEqual reports whether x and y are ``deeply equal,'' defined as follows.
|
||||||
|
// Two values of identical type are deeply equal if one of the following cases applies.
|
||||||
|
// Values of distinct types are never deeply equal.
|
||||||
|
//
|
||||||
|
// Array values are deeply equal when their corresponding elements are deeply equal.
|
||||||
|
//
|
||||||
|
// Struct values are deeply equal if their corresponding fields,
|
||||||
|
// both exported and unexported, are deeply equal.
|
||||||
|
//
|
||||||
|
// Func values are deeply equal if both are nil; otherwise they are not deeply equal.
|
||||||
|
//
|
||||||
|
// Interface values are deeply equal if they hold deeply equal concrete values.
|
||||||
|
//
|
||||||
|
// Map values are deeply equal when all of the following are true:
|
||||||
|
// they are both nil or both non-nil, they have the same length,
|
||||||
|
// and either they are the same map object or their corresponding keys
|
||||||
|
// (matched using Go equality) map to deeply equal values.
|
||||||
|
//
|
||||||
|
// Pointer values are deeply equal if they are equal using Go's == operator
|
||||||
|
// or if they point to deeply equal values.
|
||||||
|
//
|
||||||
|
// Slice values are deeply equal when all of the following are true:
|
||||||
|
// they are both nil or both non-nil, they have the same length,
|
||||||
|
// and either they point to the same initial entry of the same underlying array
|
||||||
|
// (that is, &x[0] == &y[0]) or their corresponding elements (up to length) are deeply equal.
|
||||||
|
// Note that a non-nil empty slice and a nil slice (for example, []byte{} and []byte(nil))
|
||||||
|
// are not deeply equal.
|
||||||
|
//
|
||||||
|
// Other values - numbers, bools, strings, and channels - are deeply equal
|
||||||
|
// if they are equal using Go's == operator.
|
||||||
|
//
|
||||||
|
// In general DeepEqual is a recursive relaxation of Go's == operator.
|
||||||
|
// However, this idea is impossible to implement without some inconsistency.
|
||||||
|
// Specifically, it is possible for a value to be unequal to itself,
|
||||||
|
// either because it is of func type (uncomparable in general)
|
||||||
|
// or because it is a floating-point NaN value (not equal to itself in floating-point comparison),
|
||||||
|
// or because it is an array, struct, or interface containing
|
||||||
|
// such a value.
|
||||||
|
// On the other hand, pointer values are always equal to themselves,
|
||||||
|
// even if they point at or contain such problematic values,
|
||||||
|
// because they compare equal using Go's == operator, and that
|
||||||
|
// is a sufficient condition to be deeply equal, regardless of content.
|
||||||
|
// DeepEqual has been defined so that the same short-cut applies
|
||||||
|
// to slices and maps: if x and y are the same slice or the same map,
|
||||||
|
// they are deeply equal regardless of content.
|
||||||
|
//
|
||||||
|
// As DeepEqual traverses the data values it may find a cycle. The
|
||||||
|
// second and subsequent times that DeepEqual compares two pointer
|
||||||
|
// values that have been compared before, it treats the values as
|
||||||
|
// equal rather than examining the values to which they point.
|
||||||
|
// This ensures that DeepEqual terminates.
|
||||||
func DeepEqual(x, y interface{}) bool {
|
func DeepEqual(x, y interface{}) bool {
|
||||||
if x == nil || y == nil {
|
if x == nil || y == nil {
|
||||||
return x == y
|
return x == y
|
||||||
}
|
}
|
||||||
|
v1 := ValueOf(x)
|
||||||
panic("unimplemented: reflect.DeepEqual()")
|
v2 := ValueOf(y)
|
||||||
|
if v1.typecode != v2.typecode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return deepValueEqual(v1, v2, make(map[visit]struct{}))
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-7
@@ -67,6 +67,14 @@ func (v Value) Interface() interface{} {
|
|||||||
// valueInterfaceUnsafe is used by the runtime to hash map keys. It should not
|
// valueInterfaceUnsafe is used by the runtime to hash map keys. It should not
|
||||||
// be subject to the isExported check.
|
// be subject to the isExported check.
|
||||||
func valueInterfaceUnsafe(v Value) interface{} {
|
func valueInterfaceUnsafe(v Value) interface{} {
|
||||||
|
if v.typecode.Kind() == Interface {
|
||||||
|
// The value itself is an interface. This can happen when getting the
|
||||||
|
// value of a struct field of interface type, like this:
|
||||||
|
// type T struct {
|
||||||
|
// X interface{}
|
||||||
|
// }
|
||||||
|
return *(*interface{})(v.value)
|
||||||
|
}
|
||||||
if v.isIndirect() && v.typecode.Size() <= unsafe.Sizeof(uintptr(0)) {
|
if v.isIndirect() && v.typecode.Size() <= unsafe.Sizeof(uintptr(0)) {
|
||||||
// Value was indirect but must be put back directly in the interface
|
// Value was indirect but must be put back directly in the interface
|
||||||
// value.
|
// value.
|
||||||
@@ -132,10 +140,7 @@ func (v Value) IsNil() bool {
|
|||||||
func (v Value) Pointer() uintptr {
|
func (v Value) Pointer() uintptr {
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
case Chan, Map, Ptr, UnsafePointer:
|
case Chan, Map, Ptr, UnsafePointer:
|
||||||
if v.isIndirect() {
|
return uintptr(v.pointer())
|
||||||
return *(*uintptr)(v.value)
|
|
||||||
}
|
|
||||||
return uintptr(v.value)
|
|
||||||
case Slice:
|
case Slice:
|
||||||
slice := (*sliceHeader)(v.value)
|
slice := (*sliceHeader)(v.value)
|
||||||
return uintptr(slice.data)
|
return uintptr(slice.data)
|
||||||
@@ -146,6 +151,15 @@ func (v Value) Pointer() uintptr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pointer returns the underlying pointer represented by v.
|
||||||
|
// v.Kind() must be Ptr, Map, Chan, or UnsafePointer
|
||||||
|
func (v Value) pointer() unsafe.Pointer {
|
||||||
|
if v.isIndirect() {
|
||||||
|
return *(*unsafe.Pointer)(v.value)
|
||||||
|
}
|
||||||
|
return v.value
|
||||||
|
}
|
||||||
|
|
||||||
func (v Value) IsValid() bool {
|
func (v Value) IsValid() bool {
|
||||||
return v.typecode != 0
|
return v.typecode != 0
|
||||||
}
|
}
|
||||||
@@ -392,7 +406,14 @@ func (v Value) Elem() Value {
|
|||||||
value: ptr,
|
value: ptr,
|
||||||
flags: v.flags | valueFlagIndirect,
|
flags: v.flags | valueFlagIndirect,
|
||||||
}
|
}
|
||||||
default: // not implemented: Interface
|
case Interface:
|
||||||
|
typecode, value := decomposeInterface(*(*interface{})(v.value))
|
||||||
|
return Value{
|
||||||
|
typecode: typecode,
|
||||||
|
value: value,
|
||||||
|
flags: v.flags &^ valueFlagIndirect,
|
||||||
|
}
|
||||||
|
default:
|
||||||
panic(&ValueError{"Elem"})
|
panic(&ValueError{"Elem"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -513,11 +534,17 @@ func (v Value) Index(i int) Value {
|
|||||||
if size > unsafe.Sizeof(uintptr(0)) {
|
if size > unsafe.Sizeof(uintptr(0)) {
|
||||||
// The element fits in a pointer, but the array does not.
|
// The element fits in a pointer, but the array does not.
|
||||||
// Load the value from the pointer.
|
// Load the value from the pointer.
|
||||||
addr := uintptr(v.value) + elemSize*uintptr(i) // pointer to new value
|
addr := unsafe.Pointer(uintptr(v.value) + elemSize*uintptr(i)) // pointer to new value
|
||||||
|
value := addr
|
||||||
|
if !v.isIndirect() {
|
||||||
|
// Use a pointer to the value (don't load the value) if the
|
||||||
|
// 'indirect' flag is set.
|
||||||
|
value = unsafe.Pointer(loadValue(addr, elemSize))
|
||||||
|
}
|
||||||
return Value{
|
return Value{
|
||||||
typecode: v.typecode.elem(),
|
typecode: v.typecode.elem(),
|
||||||
flags: v.flags,
|
flags: v.flags,
|
||||||
value: unsafe.Pointer(loadValue(unsafe.Pointer(addr), elemSize)),
|
value: value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
.section .text.tinygo_scanCurrentStack,"ax"
|
||||||
|
.global tinygo_scanCurrentStack
|
||||||
|
tinygo_scanCurrentStack:
|
||||||
|
// Save callee-saved registers.
|
||||||
|
pushq %rbx
|
||||||
|
pushq %rbp
|
||||||
|
pushq %rdi
|
||||||
|
pushq %rsi
|
||||||
|
pushq %r12
|
||||||
|
pushq %r13
|
||||||
|
pushq %r14
|
||||||
|
pushq %r15
|
||||||
|
|
||||||
|
// Scan the stack.
|
||||||
|
subq $8, %rsp // adjust the stack before the call to maintain 16-byte alignment
|
||||||
|
movq %rsp, %rdi
|
||||||
|
callq tinygo_scanstack
|
||||||
|
|
||||||
|
// Restore the stack pointer. Registers do not need to be restored as they
|
||||||
|
// were only pushed to be discoverable by the GC.
|
||||||
|
addq $72, %rsp
|
||||||
|
retq
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build gc.conservative
|
||||||
// +build gc.conservative
|
// +build gc.conservative
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
const GOOS = "windows"
|
||||||
@@ -273,7 +273,9 @@ func printspace() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func printnl() {
|
func printnl() {
|
||||||
|
if baremetal {
|
||||||
putchar('\r')
|
putchar('\r')
|
||||||
|
}
|
||||||
putchar('\n')
|
putchar('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func handleHardFault(sp *interruptStack) {
|
|||||||
if fault.Mem().WhileUnstackingException() {
|
if fault.Mem().WhileUnstackingException() {
|
||||||
print(" while unstacking exception")
|
print(" while unstacking exception")
|
||||||
}
|
}
|
||||||
if fault.Mem().WhileStackingException() {
|
if fault.Mem().WileStackingException() {
|
||||||
print(" while stacking exception")
|
print(" while stacking exception")
|
||||||
}
|
}
|
||||||
if fault.Mem().DuringFPLazyStatePres() {
|
if fault.Mem().DuringFPLazyStatePres() {
|
||||||
@@ -161,13 +161,13 @@ func (fs MemFaultStatus) WhileUnstackingException() bool {
|
|||||||
return fs&arm.SCB_CFSR_MUNSTKERR != 0
|
return fs&arm.SCB_CFSR_MUNSTKERR != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// WhileStackingException: stacking for an exception entry has caused one or more
|
// WileStackingException: stacking for an exception entry has caused one or more
|
||||||
// access violations
|
// access violations
|
||||||
//
|
//
|
||||||
// "When this bit is 1, the SP is still adjusted but the values in the context
|
// "When this bit is 1, the SP is still adjusted but the values in the context
|
||||||
// area on the stack might be incorrect. The processor has not written a fault
|
// area on the stack might be incorrect. The processor has not written a fault
|
||||||
// address to the MMAR."
|
// address to the MMAR."
|
||||||
func (fs MemFaultStatus) WhileStackingException() bool {
|
func (fs MemFaultStatus) WileStackingException() bool {
|
||||||
return fs&arm.SCB_CFSR_MSTKERR != 0
|
return fs&arm.SCB_CFSR_MSTKERR != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"device/arm"
|
"device/arm"
|
||||||
"device/nxp"
|
"device/nxp"
|
||||||
"machine"
|
"machine"
|
||||||
"machine/usb"
|
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@@ -37,7 +36,7 @@ func main() {
|
|||||||
// initialize cache and MPU
|
// initialize cache and MPU
|
||||||
initCache()
|
initCache()
|
||||||
|
|
||||||
// enable SysTick, GPIO, USB, and other peripherals
|
// enable SysTick, GPIO, and peripherals
|
||||||
initPeripherals()
|
initPeripherals()
|
||||||
|
|
||||||
// reenable interrupts
|
// reenable interrupts
|
||||||
@@ -109,8 +108,7 @@ func initPeripherals() {
|
|||||||
initPins() // configure GPIO
|
initPins() // configure GPIO
|
||||||
|
|
||||||
enablePeripheralClocks() // activate peripheral clock gates
|
enablePeripheralClocks() // activate peripheral clock gates
|
||||||
initUSB() // configure USB CDC-ACM (UART0)
|
initUART() // configure UART (initialized first for debugging)
|
||||||
initUART() // configure hardware UART (UART1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initPins() {
|
func initPins() {
|
||||||
@@ -125,14 +123,8 @@ func initUART() {
|
|||||||
machine.UART1.Configure(machine.UARTConfig{})
|
machine.UART1.Configure(machine.UARTConfig{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func initUSB() {
|
|
||||||
machine.HID0.Configure(usb.HIDConfig{})
|
|
||||||
// machine.UART0.Configure(usb.UARTConfig{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func putchar(c byte) {
|
func putchar(c byte) {
|
||||||
// machine.UART0.WriteByte(c) // print to USB UART
|
machine.UART1.WriteByte(c)
|
||||||
machine.UART1.WriteByte(c) // print to hardware UART
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func exit(code int) {
|
func exit(code int) {
|
||||||
|
|||||||
@@ -35,31 +35,15 @@ var (
|
|||||||
Denominator: 1, // 30-bit DENOM of fractional loop divider
|
Denominator: 1, // 30-bit DENOM of fractional loop divider
|
||||||
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
|
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
|
||||||
}
|
}
|
||||||
Usb1PhyConfig = nxp.ClockConfigUsbPhy{
|
Usb1PllConfig = nxp.ClockConfigUsbPll{
|
||||||
Instance: 1, // USB PHY number (1 or 2)
|
Instance: 1, // USB PLL instance
|
||||||
XtalFreq: OSC_FREQ, // External reference clock frequency (Hz)
|
LoopDivider: 0, // PLL loop divider, Fout=Fin*20
|
||||||
DCal: 0xC, // Decode to trim nominal 17.78mA current source
|
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
|
||||||
TxCal45DP: 0x6, // Decode to trim nominal 45-Ohm series Rp on USB D+
|
|
||||||
TxCal45DM: 0x6, // Decode to trim nominal 45-Ohm series Rp on USB D-
|
|
||||||
PllConfig: nxp.ClockConfigUsbPll{
|
|
||||||
Instance: 1, // USB PLL number (1 or 2)
|
|
||||||
LoopDivider: 0, // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
|
|
||||||
Src: 0, // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
|
|
||||||
Pfd: nil, // Phase fractional divisors (len=4, or nil for boot default)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
Usb2PhyConfig = nxp.ClockConfigUsbPhy{
|
Usb2PllConfig = nxp.ClockConfigUsbPll{
|
||||||
Instance: 2, // USB PHY number (1 or 2)
|
Instance: 2, // USB PLL instance
|
||||||
XtalFreq: OSC_FREQ, // External reference clock frequency (Hz)
|
LoopDivider: 0, // PLL loop divider, Fout=Fin*20
|
||||||
DCal: 0xC, // Decode to trim the nominal 17.78mA current source
|
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
|
||||||
TxCal45DP: 0x6, // Decode to trim the nominal 45-Ohm series Rp on USB D+
|
|
||||||
TxCal45DM: 0x6, // Decode to trim the nominal 45-Ohm series Rp on USB D-
|
|
||||||
PllConfig: nxp.ClockConfigUsbPll{
|
|
||||||
Instance: 2, // USB PLL number (1 or 2)
|
|
||||||
LoopDivider: 0, // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
|
|
||||||
Src: 0, // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
|
|
||||||
Pfd: nil, // Phase fractional divisors (len=4, or nil for boot default)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -101,7 +85,7 @@ func initClocks() {
|
|||||||
|
|
||||||
// set VDD_SOC to 1.275V, necessary to config AHB to 600 MHz
|
// set VDD_SOC to 1.275V, necessary to config AHB to 600 MHz
|
||||||
nxp.DCDC.REG3.Set((nxp.DCDC.REG3.Get() & ^uint32(nxp.DCDC_REG3_TRG_Msk)) |
|
nxp.DCDC.REG3.Set((nxp.DCDC.REG3.Get() & ^uint32(nxp.DCDC_REG3_TRG_Msk)) |
|
||||||
((0x13 << nxp.DCDC_REG3_TRG_Pos) & nxp.DCDC_REG3_TRG_Msk))
|
((13 << nxp.DCDC_REG3_TRG_Pos) & nxp.DCDC_REG3_TRG_Msk))
|
||||||
|
|
||||||
// wait until DCDC_STS_DC_OK bit is asserted
|
// wait until DCDC_STS_DC_OK bit is asserted
|
||||||
for !nxp.DCDC.REG0.HasBits(nxp.DCDC_REG0_STS_DC_OK_Msk) {
|
for !nxp.DCDC.REG0.HasBits(nxp.DCDC_REG0_STS_DC_OK_Msk) {
|
||||||
@@ -120,8 +104,6 @@ func initClocks() {
|
|||||||
nxp.DivIpArm.Div(1) // divide ARM_PODF (DIV2)
|
nxp.DivIpArm.Div(1) // divide ARM_PODF (DIV2)
|
||||||
nxp.DivIpPeriphClk2.Div(0) // divide PERIPH_CLK2_PODF (DIV1)
|
nxp.DivIpPeriphClk2.Div(0) // divide PERIPH_CLK2_PODF (DIV1)
|
||||||
|
|
||||||
nxp.ClockIpUsbOh3.Enable(false) // disable USB
|
|
||||||
|
|
||||||
nxp.ClockIpGpt1.Enable(false) // disable GPT/PIT
|
nxp.ClockIpGpt1.Enable(false) // disable GPT/PIT
|
||||||
nxp.ClockIpGpt1S.Enable(false) //
|
nxp.ClockIpGpt1S.Enable(false) //
|
||||||
nxp.ClockIpGpt2.Enable(false) //
|
nxp.ClockIpGpt2.Enable(false) //
|
||||||
@@ -130,11 +112,6 @@ func initClocks() {
|
|||||||
|
|
||||||
nxp.DivIpPerclk.Div(0) // divide PERCLK_PODF (DIV1)
|
nxp.DivIpPerclk.Div(0) // divide PERCLK_PODF (DIV1)
|
||||||
|
|
||||||
nxp.ClockIpGpio1.Enable(false) // disable GPIO
|
|
||||||
nxp.ClockIpGpio2.Enable(false) //
|
|
||||||
nxp.ClockIpGpio3.Enable(false) //
|
|
||||||
nxp.ClockIpGpio4.Enable(false) //
|
|
||||||
|
|
||||||
nxp.ClockIpUsdhc1.Enable(false) // disable USDHC1
|
nxp.ClockIpUsdhc1.Enable(false) // disable USDHC1
|
||||||
nxp.DivIpUsdhc1.Div(1) // divide USDHC1_PODF (DIV2)
|
nxp.DivIpUsdhc1.Div(1) // divide USDHC1_PODF (DIV2)
|
||||||
nxp.MuxIpUsdhc1.Mux(1) // USDHC1 select PLL2_PFD0
|
nxp.MuxIpUsdhc1.Mux(1) // USDHC1 select PLL2_PFD0
|
||||||
@@ -143,9 +120,9 @@ func initClocks() {
|
|||||||
nxp.MuxIpUsdhc2.Mux(1) // USDHC2 select PLL2_PFD0
|
nxp.MuxIpUsdhc2.Mux(1) // USDHC2 select PLL2_PFD0
|
||||||
|
|
||||||
nxp.ClockIpSemc.Enable(false) // disable SEMC
|
nxp.ClockIpSemc.Enable(false) // disable SEMC
|
||||||
nxp.DivIpSemc.Div(7) // divide SEMC_PODF (DIV8)
|
nxp.DivIpSemc.Div(1) // divide SEMC_PODF (DIV2)
|
||||||
nxp.MuxIpSemcAlt.Mux(0) // SEMC_ALT select PLL2_PFD2
|
nxp.MuxIpSemcAlt.Mux(0) // SEMC_ALT select PLL2_PFD2
|
||||||
nxp.MuxIpSemc.Mux(0) // SEMC select PERIPH_CLK
|
nxp.MuxIpSemc.Mux(1) // SEMC select SEMC_ALT
|
||||||
|
|
||||||
if false {
|
if false {
|
||||||
// TODO: external flash is on this bus, configured via DCD block
|
// TODO: external flash is on this bus, configured via DCD block
|
||||||
@@ -214,7 +191,7 @@ func initClocks() {
|
|||||||
nxp.ClockIpLcdPixel.Enable(false) // disable LCDIF
|
nxp.ClockIpLcdPixel.Enable(false) // disable LCDIF
|
||||||
nxp.DivIpLcdifPre.Div(1) // divide LCDIF_PRED (DIV2)
|
nxp.DivIpLcdifPre.Div(1) // divide LCDIF_PRED (DIV2)
|
||||||
nxp.DivIpLcdif.Div(3) // divide LCDIF_CLK_PODF (DIV4)
|
nxp.DivIpLcdif.Div(3) // divide LCDIF_CLK_PODF (DIV4)
|
||||||
nxp.MuxIpLcdifPre.Mux(4) // LCDIF_PRE select PLL2_PFD1
|
nxp.MuxIpLcdifPre.Mux(5) // LCDIF_PRE select PLL3_PFD1
|
||||||
|
|
||||||
nxp.ClockIpSpdif.Enable(false) // disable SPDIF
|
nxp.ClockIpSpdif.Enable(false) // disable SPDIF
|
||||||
nxp.DivIpSpdif0Pre.Div(1) // divide SPDIF0_CLK_PRED (DIV2)
|
nxp.DivIpSpdif0Pre.Div(1) // divide SPDIF0_CLK_PRED (DIV2)
|
||||||
@@ -232,43 +209,43 @@ func initClocks() {
|
|||||||
|
|
||||||
nxp.MuxIpPll3Sw.Mux(0) // PLL3_SW select PLL3_MAIN
|
nxp.MuxIpPll3Sw.Mux(0) // PLL3_SW select PLL3_MAIN
|
||||||
|
|
||||||
// Disable Audio/Video/Ethernet PLLs
|
|
||||||
nxp.CCM_ANALOG.PLL_AUDIO.Set(nxp.CCM_ANALOG_PLL_AUDIO_POWERDOWN_Msk)
|
|
||||||
nxp.CCM_ANALOG.PLL_VIDEO.Set(nxp.CCM_ANALOG_PLL_VIDEO_POWERDOWN_Msk)
|
|
||||||
nxp.CCM_ANALOG.PLL_ENET.Set(nxp.CCM_ANALOG_PLL_ENET_POWERDOWN_Msk)
|
|
||||||
|
|
||||||
ArmPllConfig.Configure() // init ARM PLL
|
ArmPllConfig.Configure() // init ARM PLL
|
||||||
// SYS PLL (PLL2) @ 528 MHz
|
// SYS PLL (PLL2) @ 528 MHz
|
||||||
// PFD0 = 396 MHz -> USDHC1/USDHC2(DIV2)=198 MHz
|
// PFD0 = 396 MHz -> USDHC1/USDHC2(DIV2)=198 MHz
|
||||||
// PFD1 = 594 MHz -> (currently unused)
|
// PFD1 = 594 MHz -> (currently unused)
|
||||||
// PFD2 = 327.72 MHz -> FlexSPI/FlexSPI2=327.72 MHz
|
// PFD2 = 327.72 MHz -> SEMC(DIV2)=163.86 MHz, FlexSPI/FlexSPI2=327.72 MHz
|
||||||
// PFD3 = 594 MHz -> (currently unused)
|
// PFD3 = 454.73 MHz -> (currently unused)
|
||||||
SysPllConfig.Configure(24, 16, 29, 16) // init SYS PLL and PFDs
|
SysPllConfig.Configure(24, 16, 29, 16) // init SYS PLL and PFDs
|
||||||
|
|
||||||
Usb1PhyConfig.Configure() // init USB1 HS PHY/PLL
|
// USB1 PLL (PLL3) @ 480 MHz
|
||||||
Usb2PhyConfig.Configure() // init USB2 HS PHY/PLL
|
// PFD0 -> (currently unused)
|
||||||
|
// PFD1 -> (currently unused)
|
||||||
|
// PFD2 -> (currently unused)
|
||||||
|
// PFD3 -> (currently unused)
|
||||||
|
Usb1PllConfig.Configure() // init USB1 PLL and PFDs
|
||||||
|
Usb2PllConfig.Configure() // init USB2 PLL
|
||||||
|
|
||||||
nxp.MuxIpPrePeriph.Mux(3) // PRE_PERIPH select ARM_PLL
|
nxp.MuxIpPrePeriph.Mux(3) // PRE_PERIPH select ARM_PLL
|
||||||
nxp.MuxIpPeriph.Mux(0) // PERIPH select PRE_PERIPH
|
nxp.MuxIpPeriph.Mux(0) // PERIPH select PRE_PERIPH
|
||||||
nxp.MuxIpPeriphClk2.Mux(1) // PERIPH_CLK2 select OSC
|
nxp.MuxIpPeriphClk2.Mux(1) // PERIPH_CLK2 select OSC
|
||||||
nxp.MuxIpPerclk.Mux(1) // PERCLK select OSC
|
nxp.MuxIpPerclk.Mux(1) // PERCLK select OSC
|
||||||
|
|
||||||
// set LVDS1 clock source (ARM_PLL)
|
// set LVDS1 clock source
|
||||||
nxp.CCM_ANALOG.MISC1.Set((nxp.CCM_ANALOG.MISC1.Get() & ^uint32(nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Msk)) |
|
nxp.CCM_ANALOG.MISC1.Set((nxp.CCM_ANALOG.MISC1.Get() & ^uint32(nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Msk)) |
|
||||||
((0 << nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Pos) & nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Msk))
|
((0 << nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Pos) & nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Msk))
|
||||||
|
|
||||||
// set CLOCK_OUT1 divider (DIV1)
|
// set CLOCK_OUT1 divider
|
||||||
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO1_DIV_Msk)) |
|
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO1_DIV_Msk)) |
|
||||||
((0 << nxp.CCM_CCOSR_CLKO1_DIV_Pos) & nxp.CCM_CCOSR_CLKO1_DIV_Msk))
|
((0 << nxp.CCM_CCOSR_CLKO1_DIV_Pos) & nxp.CCM_CCOSR_CLKO1_DIV_Msk))
|
||||||
// set CLOCK_OUT1 source (PLL2/DIV2)
|
// set CLOCK_OUT1 source
|
||||||
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO1_SEL_Msk)) |
|
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO1_SEL_Msk)) |
|
||||||
((1 << nxp.CCM_CCOSR_CLKO1_SEL_Pos) & nxp.CCM_CCOSR_CLKO1_SEL_Msk))
|
((1 << nxp.CCM_CCOSR_CLKO1_SEL_Pos) & nxp.CCM_CCOSR_CLKO1_SEL_Msk))
|
||||||
// set CLOCK_OUT2 divider (DIV1)
|
// set CLOCK_OUT2 divider
|
||||||
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO2_DIV_Msk)) |
|
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO2_DIV_Msk)) |
|
||||||
((0 << nxp.CCM_CCOSR_CLKO2_DIV_Pos) & nxp.CCM_CCOSR_CLKO2_DIV_Msk))
|
((0 << nxp.CCM_CCOSR_CLKO2_DIV_Pos) & nxp.CCM_CCOSR_CLKO2_DIV_Msk))
|
||||||
// set CLOCK_OUT2 source (SPDIF0)
|
// set CLOCK_OUT2 source
|
||||||
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO2_SEL_Msk)) |
|
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO2_SEL_Msk)) |
|
||||||
((29 << nxp.CCM_CCOSR_CLKO2_SEL_Pos) & nxp.CCM_CCOSR_CLKO2_SEL_Msk))
|
((18 << nxp.CCM_CCOSR_CLKO2_SEL_Pos) & nxp.CCM_CCOSR_CLKO2_SEL_Msk))
|
||||||
|
|
||||||
nxp.CCM.CCOSR.ClearBits(nxp.CCM_CCOSR_CLK_OUT_SEL_Msk) // set CLK_OUT1 drives CLK_OUT
|
nxp.CCM.CCOSR.ClearBits(nxp.CCM_CCOSR_CLK_OUT_SEL_Msk) // set CLK_OUT1 drives CLK_OUT
|
||||||
nxp.CCM.CCOSR.SetBits(nxp.CCM_CCOSR_CLKO1_EN_Msk) // enable CLK_OUT1
|
nxp.CCM.CCOSR.SetBits(nxp.CCM_CCOSR_CLKO1_EN_Msk) // enable CLK_OUT1
|
||||||
@@ -276,10 +253,15 @@ func initClocks() {
|
|||||||
|
|
||||||
nxp.ClockIpIomuxcGpr.Enable(false) // disable IOMUXC_GPR
|
nxp.ClockIpIomuxcGpr.Enable(false) // disable IOMUXC_GPR
|
||||||
nxp.ClockIpIomuxc.Enable(false) // disable IOMUXC
|
nxp.ClockIpIomuxc.Enable(false) // disable IOMUXC
|
||||||
// set GPT1 High frequency reference clock source (PERCLK)
|
// set GPT1 High frequency reference clock source
|
||||||
nxp.IOMUXC_GPR.GPR5.ClearBits(nxp.IOMUXC_GPR_GPR5_VREF_1M_CLK_GPT1_Msk)
|
nxp.IOMUXC_GPR.GPR5.ClearBits(nxp.IOMUXC_GPR_GPR5_VREF_1M_CLK_GPT1_Msk)
|
||||||
// set GPT2 High frequency reference clock source (PERCLK)
|
// set GPT2 High frequency reference clock source
|
||||||
nxp.IOMUXC_GPR.GPR5.ClearBits(nxp.IOMUXC_GPR_GPR5_VREF_1M_CLK_GPT2_Msk)
|
nxp.IOMUXC_GPR.GPR5.ClearBits(nxp.IOMUXC_GPR_GPR5_VREF_1M_CLK_GPT2_Msk)
|
||||||
|
|
||||||
|
nxp.ClockIpGpio1.Enable(false) // disable GPIO
|
||||||
|
nxp.ClockIpGpio2.Enable(false) //
|
||||||
|
nxp.ClockIpGpio3.Enable(false) //
|
||||||
|
nxp.ClockIpGpio4.Enable(false) //
|
||||||
}
|
}
|
||||||
|
|
||||||
func enableTimerClocks() {
|
func enableTimerClocks() {
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ func timerSleep(cycles uint32) bool {
|
|||||||
nxp.PIT.TIMER[pitSleepTimer].TCTRL.Set(nxp.PIT_TIMER_TCTRL_TIE) // enable interrupts
|
nxp.PIT.TIMER[pitSleepTimer].TCTRL.Set(nxp.PIT_TIMER_TCTRL_TIE) // enable interrupts
|
||||||
nxp.PIT.TIMER[pitSleepTimer].TCTRL.SetBits(nxp.PIT_TIMER_TCTRL_TEN) // start timer
|
nxp.PIT.TIMER[pitSleepTimer].TCTRL.SetBits(nxp.PIT_TIMER_TCTRL_TEN) // start timer
|
||||||
for {
|
for {
|
||||||
waitForEvents()
|
//arm.Asm("wfi") // TODO: causes hardfault! why?
|
||||||
if pitActive.Get() == 0 {
|
if pitActive.Get() == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build wasm && !wasi
|
||||||
// +build wasm,!wasi
|
// +build wasm,!wasi
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
@@ -6,13 +7,19 @@ import "unsafe"
|
|||||||
|
|
||||||
type timeUnit float64 // time in milliseconds, just like Date.now() in JavaScript
|
type timeUnit float64 // time in milliseconds, just like Date.now() in JavaScript
|
||||||
|
|
||||||
|
// wasmNested is used to detect scheduler nesting (WASM calls into JS calls back into WASM).
|
||||||
|
// When this happens, we need to use a reduced version of the scheduler.
|
||||||
|
var wasmNested bool
|
||||||
|
|
||||||
//export _start
|
//export _start
|
||||||
func _start() {
|
func _start() {
|
||||||
// These need to be initialized early so that the heap can be initialized.
|
// These need to be initialized early so that the heap can be initialized.
|
||||||
heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
|
heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
|
||||||
heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize)
|
heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize)
|
||||||
|
|
||||||
|
wasmNested = true
|
||||||
run()
|
run()
|
||||||
|
wasmNested = false
|
||||||
}
|
}
|
||||||
|
|
||||||
var handleEvent func()
|
var handleEvent func()
|
||||||
@@ -27,12 +34,27 @@ func resume() {
|
|||||||
go func() {
|
go func() {
|
||||||
handleEvent()
|
handleEvent()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if wasmNested {
|
||||||
|
minSched()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wasmNested = true
|
||||||
scheduler()
|
scheduler()
|
||||||
|
wasmNested = false
|
||||||
}
|
}
|
||||||
|
|
||||||
//export go_scheduler
|
//export go_scheduler
|
||||||
func go_scheduler() {
|
func go_scheduler() {
|
||||||
|
if wasmNested {
|
||||||
|
minSched()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wasmNested = true
|
||||||
scheduler()
|
scheduler()
|
||||||
|
wasmNested = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func ticksToNanoseconds(ticks timeUnit) int64 {
|
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user