mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-27 07:49:05 +00:00
Compare commits
131 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 27a9e5d764 | |||
| 297d3cbcc2 | |||
| 2651b1d8bd | |||
| 319b49e81c | |||
| 7e493642eb | |||
| 674b3b966f | |||
| d9d29d3c68 | |||
| e94ab78926 | |||
| 8fa58fdc6d | |||
| 4b81985d2b | |||
| 3b892cbe41 | |||
| 5f6489d3cf | |||
| 2f50d24fec | |||
| 6e29c17a8b | |||
| 67e68e484f | |||
| 4b8b4243ec | |||
| d2a1835ffc | |||
| 34b931b6f3 | |||
| 07964818d9 | |||
| 079eace344 | |||
| 69e8257e7b | |||
| 8103b3c3c8 | |||
| a041a7866d | |||
| 6583ec1448 | |||
| 87a4676137 | |||
| fd20f63ee3 | |||
| 9dd249a431 | |||
| 777d3f3ea5 | |||
| ea3b5dc689 | |||
| 3dd502a928 | |||
| 48242ba8d6 | |||
| 7ea9eff406 | |||
| 80d94115dc | |||
| 9de76fb42e | |||
| 52c61de19f | |||
| f308d7d28c | |||
| 7729a36782 | |||
| 5404c81ffd | |||
| b52310fed2 | |||
| 046070074d | |||
| c23a5b65ef | |||
| b8e433821a | |||
| dd1a836903 | |||
| 4ed0936c0e | |||
| 7a5d4c9537 | |||
| af6bbaf532 | |||
| 4c7449efe5 | |||
| 995e815b63 | |||
| 109b5298c4 | |||
| a94e03eff2 | |||
| eb3d6261b4 | |||
| 15724848f9 | |||
| 8f40b107d9 | |||
| fa673f0827 | |||
| e3fe6d8f37 | |||
| 5c23f6fb6c | |||
| 5afb63df60 | |||
| 1d2c39c3b9 | |||
| 270a2f51fd | |||
| e87cd23e87 | |||
| db389ba443 | |||
| d1dfa1155c | |||
| cf0b7edc78 | |||
| a32cda7a4c | |||
| b251ce7b33 | |||
| 1abe1203a3 | |||
| 8b360ec911 | |||
| 93654544b3 | |||
| 050d516264 | |||
| 9a8328fcb7 | |||
| 6812a4dc04 | |||
| 8568d4f625 | |||
| bd56636d58 | |||
| 4fe3a379a5 | |||
| fce42fc7fa | |||
| 5d177d1328 | |||
| 9eb4a6268a | |||
| 11a402de95 | |||
| cdd9198888 | |||
| 6055e6eb23 | |||
| 9239c61edb | |||
| 008b373df4 | |||
| c387a09f54 | |||
| 5bb3ca2618 | |||
| a4ea85b303 | |||
| 27d38a9663 | |||
| ee497a296b | |||
| 6b31ee1e93 | |||
| cad6a57077 | |||
| 52233790cc | |||
| 9160f3f16d | |||
| 69d7ed5cda | |||
| 111661658b | |||
| 3000b2d15e | |||
| c89f026532 | |||
| e09b1d06f5 | |||
| 0590ccb260 | |||
| fe0acb1670 | |||
| d58c7d2521 | |||
| 85f5411d60 | |||
| c5de68622e | |||
| 8fb93fbac4 | |||
| ef3b3c0d6a | |||
| 2dc46a851b | |||
| f3afc7bbf3 | |||
| 4858b27120 | |||
| 7f507a7026 | |||
| 83227e68df | |||
| 09a3c6a16b | |||
| f613cb41a3 | |||
| e060e588ab | |||
| 8dfb317d28 | |||
| 9cedd78d9c | |||
| 20a27746b2 | |||
| c78df85015 | |||
| c0d257d682 | |||
| 7af24c7864 | |||
| 3dc1e403ea | |||
| 1deee3ec5a | |||
| 0f9f316db2 | |||
| 0dd521e1d1 | |||
| 1dcdd5f2d2 | |||
| dcd8ee63a8 | |||
| b8bf0ac3f1 | |||
| 73571dd423 | |||
| 8872229a0b | |||
| 7e05efa274 | |||
| b02cea0321 | |||
| 8823a934a3 | |||
| 6ac02cc34c | |||
| e259598b68 |
+10
-14
@@ -22,15 +22,16 @@ commands:
|
|||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-source-13-v1
|
- llvm-source-14-v2
|
||||||
- run:
|
- run:
|
||||||
name: "Fetch LLVM source"
|
name: "Fetch LLVM source"
|
||||||
command: make llvm-source
|
command: make llvm-source
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-source-13-v1
|
key: llvm-source-14-v2
|
||||||
paths:
|
paths:
|
||||||
- llvm-project/clang/lib/Headers
|
- llvm-project/clang/lib/Headers
|
||||||
- llvm-project/clang/include
|
- llvm-project/clang/include
|
||||||
|
- llvm-project/compiler-rt
|
||||||
- llvm-project/lld/include
|
- llvm-project/lld/include
|
||||||
- llvm-project/llvm/include
|
- llvm-project/llvm/include
|
||||||
hack-ninja-jobs:
|
hack-ninja-jobs:
|
||||||
@@ -85,10 +86,10 @@ commands:
|
|||||||
- run: go install -tags=llvm<<parameters.llvm>> .
|
- run: go install -tags=llvm<<parameters.llvm>> .
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- wasi-libc-sysroot-systemclang-v4
|
- wasi-libc-sysroot-systemclang-v5
|
||||||
- run: make wasi-libc
|
- run: make wasi-libc
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: wasi-libc-sysroot-systemclang-v4
|
key: wasi-libc-sysroot-systemclang-v5
|
||||||
paths:
|
paths:
|
||||||
- lib/wasi-libc/sysroot
|
- lib/wasi-libc/sysroot
|
||||||
- run: make gen-device -j4
|
- run: make gen-device -j4
|
||||||
@@ -101,21 +102,16 @@ commands:
|
|||||||
- run: make fmt-check
|
- run: make fmt-check
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-llvm11-go115:
|
test-llvm13-go116:
|
||||||
docker:
|
|
||||||
- image: circleci/golang:1.15-buster
|
|
||||||
steps:
|
|
||||||
- test-linux:
|
|
||||||
llvm: "11"
|
|
||||||
test-llvm12-go116:
|
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.16-buster
|
- image: circleci/golang:1.16-buster
|
||||||
steps:
|
steps:
|
||||||
- test-linux:
|
- test-linux:
|
||||||
llvm: "12"
|
llvm: "13"
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
test-all:
|
test-all:
|
||||||
jobs:
|
jobs:
|
||||||
- test-llvm11-go115
|
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
||||||
- test-llvm12-go116
|
# least the smoke tests still pass.
|
||||||
|
- test-llvm13-go116
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build-macos:
|
build-macos:
|
||||||
name: build-macos
|
name: build-macos
|
||||||
runs-on: macos-10.15
|
runs-on: macos-11
|
||||||
steps:
|
steps:
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: '1.17'
|
go-version: '1.18.1'
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -35,10 +35,11 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-13-macos-v1
|
key: llvm-source-14-macos-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
|
llvm-project/compiler-rt
|
||||||
llvm-project/lld/include
|
llvm-project/lld/include
|
||||||
llvm-project/llvm/include
|
llvm-project/llvm/include
|
||||||
- name: Download LLVM source
|
- name: Download LLVM source
|
||||||
@@ -48,7 +49,7 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-13-macos-v3
|
key: llvm-build-14-macos-v1
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -66,7 +67,7 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-wasi-libc
|
id: cache-wasi-libc
|
||||||
with:
|
with:
|
||||||
key: wasi-libc-sysroot-v2
|
key: wasi-libc-sysroot-v3
|
||||||
path: lib/wasi-libc/sysroot
|
path: lib/wasi-libc/sysroot
|
||||||
- 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'
|
||||||
|
|||||||
+218
-12
@@ -26,7 +26,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: '1.17'
|
go-version: '1.18.1'
|
||||||
- name: Cache Go
|
- name: Cache Go
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
@@ -38,10 +38,11 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-13-linux-v1
|
key: llvm-source-14-linux-v2
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
|
llvm-project/compiler-rt
|
||||||
llvm-project/lld/include
|
llvm-project/lld/include
|
||||||
llvm-project/llvm/include
|
llvm-project/llvm/include
|
||||||
- name: Download LLVM source
|
- name: Download LLVM source
|
||||||
@@ -51,7 +52,7 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-13-linux-v2
|
key: llvm-build-14-linux-v1
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -76,14 +77,13 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-wasi-libc
|
id: cache-wasi-libc
|
||||||
with:
|
with:
|
||||||
key: wasi-libc-sysroot-linux-asserts-v3
|
key: wasi-libc-sysroot-linux-asserts-v4
|
||||||
path: lib/wasi-libc/sysroot
|
path: lib/wasi-libc/sysroot
|
||||||
- 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: Install fpm
|
- name: Install fpm
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install ruby ruby-dev
|
|
||||||
sudo gem install --no-document fpm
|
sudo gem install --no-document fpm
|
||||||
- name: Build TinyGo release
|
- name: Build TinyGo release
|
||||||
run: |
|
run: |
|
||||||
@@ -93,7 +93,7 @@ jobs:
|
|||||||
- name: Publish release artifact
|
- name: Publish release artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: release-double-zipped
|
name: linux-amd64-double-zipped
|
||||||
path: |
|
path: |
|
||||||
/tmp/tinygo.linux-amd64.tar.gz
|
/tmp/tinygo.linux-amd64.tar.gz
|
||||||
/tmp/tinygo_amd64.deb
|
/tmp/tinygo_amd64.deb
|
||||||
@@ -107,7 +107,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: '1.17'
|
go-version: '1.18.1'
|
||||||
- name: Install wasmtime
|
- name: Install wasmtime
|
||||||
run: |
|
run: |
|
||||||
curl https://wasmtime.dev/install.sh -sSf | bash
|
curl https://wasmtime.dev/install.sh -sSf | bash
|
||||||
@@ -115,7 +115,7 @@ jobs:
|
|||||||
- name: Download release artifact
|
- name: Download release artifact
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: release-double-zipped
|
name: linux-amd64-double-zipped
|
||||||
- name: Extract release tarball
|
- name: Extract release tarball
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ~/lib
|
mkdir -p ~/lib
|
||||||
@@ -159,7 +159,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: '1.17'
|
go-version: '1.18.1'
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
@@ -179,10 +179,11 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-13-linux-asserts-v1
|
key: llvm-source-14-linux-asserts-v2
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
|
llvm-project/compiler-rt
|
||||||
llvm-project/lld/include
|
llvm-project/lld/include
|
||||||
llvm-project/llvm/include
|
llvm-project/llvm/include
|
||||||
- name: Download LLVM source
|
- name: Download LLVM source
|
||||||
@@ -192,7 +193,7 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-13-linux-asserts-v2
|
key: llvm-build-14-linux-asserts-v1
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -217,7 +218,7 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-wasi-libc
|
id: cache-wasi-libc
|
||||||
with:
|
with:
|
||||||
key: wasi-libc-sysroot-linux-asserts-v3
|
key: wasi-libc-sysroot-linux-asserts-v4
|
||||||
path: lib/wasi-libc/sysroot
|
path: lib/wasi-libc/sysroot
|
||||||
- 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'
|
||||||
@@ -240,3 +241,208 @@ jobs:
|
|||||||
- run: make smoketest
|
- run: make smoketest
|
||||||
- run: make wasmtest
|
- run: make wasmtest
|
||||||
- run: make tinygo-baremetal
|
- run: make tinygo-baremetal
|
||||||
|
build-linux-arm:
|
||||||
|
# Build ARM Linux binaries, ready for release.
|
||||||
|
# This intentionally uses an older Linux image, so that we compile against
|
||||||
|
# an older glibc version and therefore are compatible with a wide range of
|
||||||
|
# Linux distributions.
|
||||||
|
# It is set to "needs: build-linux" because it modifies the release created
|
||||||
|
# in that process to avoid doing lots of duplicate work and to avoid
|
||||||
|
# complications around precompiled libraries such as compiler-rt shipped as
|
||||||
|
# part of the release tarball.
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
needs: build-linux
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Install apt dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get install --no-install-recommends \
|
||||||
|
qemu-user \
|
||||||
|
g++-arm-linux-gnueabihf \
|
||||||
|
libc6-dev-armhf-cross
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: '1.18.1'
|
||||||
|
- name: Cache Go
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
key: go-cache-linux-arm-v2-${{ hashFiles('go.mod') }}
|
||||||
|
path: |
|
||||||
|
~/.cache/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
- name: Cache LLVM source
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-llvm-source
|
||||||
|
with:
|
||||||
|
key: llvm-source-14-linux-v2
|
||||||
|
path: |
|
||||||
|
llvm-project/clang/lib/Headers
|
||||||
|
llvm-project/clang/include
|
||||||
|
llvm-project/compiler-rt
|
||||||
|
llvm-project/lld/include
|
||||||
|
llvm-project/llvm/include
|
||||||
|
- name: Download LLVM source
|
||||||
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
|
run: make llvm-source
|
||||||
|
- name: Cache LLVM build
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-llvm-build
|
||||||
|
with:
|
||||||
|
key: llvm-build-14-linux-arm-v1
|
||||||
|
path: llvm-build
|
||||||
|
- name: Build LLVM
|
||||||
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
# fetch LLVM source
|
||||||
|
rm -rf llvm-project
|
||||||
|
make llvm-source
|
||||||
|
# Install build dependencies.
|
||||||
|
sudo apt-get install --no-install-recommends ninja-build
|
||||||
|
# build!
|
||||||
|
make llvm-build CROSS=arm-linux-gnueabihf
|
||||||
|
# Remove unnecessary object files (to reduce cache size).
|
||||||
|
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
|
- name: Cache Binaryen
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-binaryen
|
||||||
|
with:
|
||||||
|
key: binaryen-linux-arm-v1
|
||||||
|
path: build/wasm-opt
|
||||||
|
- name: Build Binaryen
|
||||||
|
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
sudo apt-get install --no-install-recommends ninja-build
|
||||||
|
git submodule update --init lib/binaryen
|
||||||
|
make CROSS=arm-linux-gnueabihf binaryen
|
||||||
|
- name: Install fpm
|
||||||
|
run: |
|
||||||
|
sudo gem install --no-document fpm
|
||||||
|
- name: Build TinyGo binary
|
||||||
|
run: |
|
||||||
|
make CROSS=arm-linux-gnueabihf
|
||||||
|
- name: Download amd64 release
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: linux-amd64-double-zipped
|
||||||
|
- name: Extract amd64 release
|
||||||
|
run: |
|
||||||
|
mkdir -p build/release
|
||||||
|
tar -xf tinygo.linux-amd64.tar.gz -C build/release tinygo
|
||||||
|
- name: Modify release
|
||||||
|
run: |
|
||||||
|
cp -p build/tinygo build/release/tinygo/bin
|
||||||
|
cp -p build/wasm-opt build/release/tinygo/bin
|
||||||
|
- name: Create arm release
|
||||||
|
run: |
|
||||||
|
make release deb RELEASEONLY=1 DEB_ARCH=armhf
|
||||||
|
cp -p build/release.tar.gz /tmp/tinygo.linux-arm.tar.gz
|
||||||
|
cp -p build/release.deb /tmp/tinygo_armhf.deb
|
||||||
|
- name: Publish release artifact
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: linux-arm-double-zipped
|
||||||
|
path: |
|
||||||
|
/tmp/tinygo.linux-arm.tar.gz
|
||||||
|
/tmp/tinygo_armhf.deb
|
||||||
|
build-linux-arm64:
|
||||||
|
# Build ARM64 Linux binaries, ready for release.
|
||||||
|
# It is set to "needs: build-linux" because it modifies the release created
|
||||||
|
# in that process to avoid doing lots of duplicate work and to avoid
|
||||||
|
# complications around precompiled libraries such as compiler-rt shipped as
|
||||||
|
# part of the release tarball.
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
needs: build-linux
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Install apt dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get install --no-install-recommends \
|
||||||
|
qemu-user \
|
||||||
|
g++-aarch64-linux-gnu \
|
||||||
|
libc6-dev-arm64-cross \
|
||||||
|
ninja-build
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: '1.18.1'
|
||||||
|
- name: Cache Go
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
key: go-cache-linux-arm64-v2-${{ hashFiles('go.mod') }}
|
||||||
|
path: |
|
||||||
|
~/.cache/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
- name: Cache LLVM source
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-llvm-source
|
||||||
|
with:
|
||||||
|
key: llvm-source-14-linux-v1
|
||||||
|
path: |
|
||||||
|
llvm-project/clang/lib/Headers
|
||||||
|
llvm-project/clang/include
|
||||||
|
llvm-project/compiler-rt
|
||||||
|
llvm-project/lld/include
|
||||||
|
llvm-project/llvm/include
|
||||||
|
- name: Download LLVM source
|
||||||
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
|
run: make llvm-source
|
||||||
|
- name: Cache LLVM build
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-llvm-build
|
||||||
|
with:
|
||||||
|
key: llvm-build-14-linux-arm64-v1
|
||||||
|
path: llvm-build
|
||||||
|
- name: Build LLVM
|
||||||
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
# fetch LLVM source
|
||||||
|
rm -rf llvm-project
|
||||||
|
make llvm-source
|
||||||
|
# build!
|
||||||
|
make llvm-build CROSS=aarch64-linux-gnu
|
||||||
|
# Remove unnecessary object files (to reduce cache size).
|
||||||
|
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
|
- name: Cache Binaryen
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-binaryen
|
||||||
|
with:
|
||||||
|
key: binaryen-linux-arm64-v1
|
||||||
|
path: build/wasm-opt
|
||||||
|
- name: Build Binaryen
|
||||||
|
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
git submodule update --init lib/binaryen
|
||||||
|
make CROSS=aarch64-linux-gnu binaryen
|
||||||
|
- name: Install fpm
|
||||||
|
run: |
|
||||||
|
sudo gem install --no-document fpm
|
||||||
|
- name: Build TinyGo binary
|
||||||
|
run: |
|
||||||
|
make CROSS=aarch64-linux-gnu
|
||||||
|
- name: Download amd64 release
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: linux-amd64-double-zipped
|
||||||
|
- name: Extract amd64 release
|
||||||
|
run: |
|
||||||
|
mkdir -p build/release
|
||||||
|
tar -xf tinygo.linux-amd64.tar.gz -C build/release tinygo
|
||||||
|
- name: Modify release
|
||||||
|
run: |
|
||||||
|
cp -p build/tinygo build/release/tinygo/bin
|
||||||
|
cp -p build/wasm-opt build/release/tinygo/bin
|
||||||
|
- name: Create arm64 release
|
||||||
|
run: |
|
||||||
|
make release deb RELEASEONLY=1 DEB_ARCH=arm64
|
||||||
|
cp -p build/release.tar.gz /tmp/tinygo.linux-arm64.tar.gz
|
||||||
|
cp -p build/release.deb /tmp/tinygo_arm64.deb
|
||||||
|
- name: Publish release artifact
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: linux-arm64-double-zipped
|
||||||
|
path: |
|
||||||
|
/tmp/tinygo.linux-arm64.tar.gz
|
||||||
|
/tmp/tinygo_arm64.deb
|
||||||
|
|||||||
@@ -9,13 +9,15 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-windows:
|
build-windows:
|
||||||
runs-on: windows-2019
|
runs-on: windows-2022
|
||||||
steps:
|
steps:
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: '1.17'
|
go-version: '1.18.1'
|
||||||
- uses: brechtm/setup-scoop@v2
|
- uses: brechtm/setup-scoop@v2
|
||||||
|
with:
|
||||||
|
scoop_update: 'false'
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -35,10 +37,11 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-13-windows-v1
|
key: llvm-source-14-windows-v2
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
|
llvm-project/compiler-rt
|
||||||
llvm-project/lld/include
|
llvm-project/lld/include
|
||||||
llvm-project/llvm/include
|
llvm-project/llvm/include
|
||||||
- name: Download LLVM source
|
- name: Download LLVM source
|
||||||
@@ -48,7 +51,7 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-13-windows-v2
|
key: llvm-build-14-windows-v1
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -65,11 +68,14 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-wasi-libc
|
id: cache-wasi-libc
|
||||||
with:
|
with:
|
||||||
key: wasi-libc-sysroot-v2
|
key: wasi-libc-sysroot-v3
|
||||||
path: lib/wasi-libc/sysroot
|
path: lib/wasi-libc/sysroot
|
||||||
- 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: Install wasmtime
|
||||||
|
run: |
|
||||||
|
scoop install wasmtime
|
||||||
- name: Test TinyGo
|
- name: Test TinyGo
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make test GOTESTFLAGS="-v -short"
|
run: make test GOTESTFLAGS="-v -short"
|
||||||
@@ -95,3 +101,5 @@ jobs:
|
|||||||
run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0 XTENSA=0
|
run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0 XTENSA=0
|
||||||
- name: Test stdlib packages
|
- name: Test stdlib packages
|
||||||
run: make tinygo-test
|
run: make tinygo-test
|
||||||
|
- name: Test stdlib packages on wasi
|
||||||
|
run: make tinygo-test-wasi-fast
|
||||||
|
|||||||
@@ -10,10 +10,6 @@
|
|||||||
[submodule "lib/cmsis-svd"]
|
[submodule "lib/cmsis-svd"]
|
||||||
path = lib/cmsis-svd
|
path = lib/cmsis-svd
|
||||||
url = https://github.com/tinygo-org/cmsis-svd
|
url = https://github.com/tinygo-org/cmsis-svd
|
||||||
[submodule "lib/compiler-rt"]
|
|
||||||
path = lib/compiler-rt
|
|
||||||
url = https://github.com/llvm-mirror/compiler-rt.git
|
|
||||||
branch = release_80
|
|
||||||
[submodule "lib/wasi-libc"]
|
[submodule "lib/wasi-libc"]
|
||||||
path = lib/wasi-libc
|
path = lib/wasi-libc
|
||||||
url = https://github.com/CraneStation/wasi-libc
|
url = https://github.com/CraneStation/wasi-libc
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ tarball. If you want to help with development of TinyGo itself, you should follo
|
|||||||
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
|
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
|
||||||
build tools to be built. Go is of course necessary to build TinyGo itself.
|
build tools to be built. Go is of course necessary to build TinyGo itself.
|
||||||
|
|
||||||
* Go (1.15+)
|
* Go (1.16+)
|
||||||
* Standard build tools (gcc/clang)
|
* Standard build tools (gcc/clang)
|
||||||
* git
|
* git
|
||||||
* CMake
|
* CMake
|
||||||
|
|||||||
@@ -1,3 +1,101 @@
|
|||||||
|
0.23.0
|
||||||
|
---
|
||||||
|
|
||||||
|
* **command line**
|
||||||
|
- add `-work` flag
|
||||||
|
- add Go 1.18 support
|
||||||
|
- add LLVM 14 support
|
||||||
|
- `run`: add support for command-line parameters
|
||||||
|
- `build`: calculate default output path if `-o` is not specified
|
||||||
|
- `build`: add JSON output
|
||||||
|
- `test`: support multiple test binaries with `-c`
|
||||||
|
- `test`: support flags like `-v` on all targets (including emulated firmware)
|
||||||
|
* **compiler**
|
||||||
|
- add support for ThinLTO
|
||||||
|
- use compiler-rt from LLVM
|
||||||
|
- `builder`: prefer GNU build ID over Go build ID for caching
|
||||||
|
- `builder`: add support for cross compiling to Darwin
|
||||||
|
- `builder`: support machine outlining pass in stacksize calculation
|
||||||
|
- `builder`: disable asynchronous unwind tables
|
||||||
|
- `compileopts`: fix emulator configuration on non-amd64 Linux architectures
|
||||||
|
- `compiler`: move allocations > 256 bytes to the heap
|
||||||
|
- `compiler`: fix incorrect `unsafe.Alignof` on some 32-bit architectures
|
||||||
|
- `compiler`: accept alias for slice `cap` builtin
|
||||||
|
- `compiler`: allow slices of empty structs
|
||||||
|
- `compiler`: fix difference in aliases in interface methods
|
||||||
|
- `compiler`: make `RawSyscall` an alias for `Syscall`
|
||||||
|
- `compiler`: remove support for memory references in `AsmFull`
|
||||||
|
- `loader`: only add Clang header path for CGo
|
||||||
|
- `transform`: fix poison value in heap-to-stack transform
|
||||||
|
* **standard library**
|
||||||
|
- `internal/fuzz`: add this package as a shim
|
||||||
|
- `os`: implement readdir for darwin and linux
|
||||||
|
- `os`: add `DirFS`, which is used by many programs to access readdir.
|
||||||
|
- `os`: isWine: be compatible with older versions of wine, too
|
||||||
|
- `os`: implement `RemoveAll`
|
||||||
|
- `os`: Use a `uintptr` for `NewFile`
|
||||||
|
- `os`: add stubs for `exec.ExitError` and `ProcessState.ExitCode`
|
||||||
|
- `os`: export correct values for `DevNull` for each OS
|
||||||
|
- `os`: improve support for `Signal` by fixing various bugs
|
||||||
|
- `os`: implement `File.Fd` method
|
||||||
|
- `os`: implement `UserHomeDir`
|
||||||
|
- `os`: add `exec.ProcessState` stub
|
||||||
|
- `os`: implement `Pipe` for darwin
|
||||||
|
- `os`: define stub `ErrDeadlineExceeded`
|
||||||
|
- `reflect`: add stubs for more missing methods
|
||||||
|
- `reflect`: rename `reflect.Ptr` to `reflect.Pointer`
|
||||||
|
- `reflect`: add `Value.FieldByIndexErr` stub
|
||||||
|
- `runtime`: fix various small GC bugs
|
||||||
|
- `runtime`: use memzero for leaking collector instead of manually zeroing objects
|
||||||
|
- `runtime`: implement `memhash`
|
||||||
|
- `runtime`: implement `fastrand`
|
||||||
|
- `runtime`: add stub for `debug.ReadBuildInfo`
|
||||||
|
- `runtime`: add stub for `NumCPU`
|
||||||
|
- `runtime`: don't inline `runtime.alloc` with `-gc=leaking`
|
||||||
|
- `runtime`: add `Version`
|
||||||
|
- `runtime`: add stubs for `NumCgoCall` and `NumGoroutine`
|
||||||
|
- `runtime`: stub {Lock,Unlock}OSThread on Windows
|
||||||
|
- `runtime`: be able to deal with a very small heap
|
||||||
|
- `syscall`: make `Environ` return a copy of the environment
|
||||||
|
- `syscall`: implement getpagesize and munmap
|
||||||
|
- `syscall`: `wasi`: define `MAP_SHARED` and `PROT_READ`
|
||||||
|
- `syscall`: stub mmap(), munmap(), MAP_SHARED, PROT_READ, SIGBUS, etc. on nonhosted targets
|
||||||
|
- `syscall`: darwin: more complete list of signals
|
||||||
|
- `syscall`: `wasi`: more complete list of signals
|
||||||
|
- `syscall`: stub `WaitStatus`
|
||||||
|
- `syscall/js`: allow copyBytesTo(Go|JS) to use `Uint8ClampedArray`
|
||||||
|
- `testing`: implement `TempDir`
|
||||||
|
- `testing`: nudge type TB closer to upstream; should be a no-op change.
|
||||||
|
- `testing`: on baremetal platforms, use simpler test matcher
|
||||||
|
* **targets**
|
||||||
|
- `atsamd`: fix usbcdc initialization when `-serial=uart`
|
||||||
|
- `atsamd51`: allow higher frequency when using SPI
|
||||||
|
- `esp`: support CGo
|
||||||
|
- `esp32c3`: add support for input pin
|
||||||
|
- `esp32c3`: add support for GPIO interrupts
|
||||||
|
- `esp32c3`: add support to receive UART data
|
||||||
|
- `rp2040`: fix PWM bug at high frequency
|
||||||
|
- `rp2040`: fix some minor I2C bugs
|
||||||
|
- `rp2040`: fix incorrect inline assembly
|
||||||
|
- `rp2040`: fix spurious i2c STOP during write+read transaction
|
||||||
|
- `rp2040`: improve ADC support
|
||||||
|
- `wasi`: remove `--export-dynamic` linker flag
|
||||||
|
- `wasm`: remove heap allocator from wasi-libc
|
||||||
|
* **boards**
|
||||||
|
- `circuitplay-bluefruit`: move pin mappings so board can be compiled for WASM use in Playground
|
||||||
|
- `esp32-c3-12f`: add the ESP32-C3-12f Kit
|
||||||
|
- `m5stamp-c3`: add pin setting of UART
|
||||||
|
- `macropad-rp2040`: add the Adafruit MacroPad RP2040 board
|
||||||
|
- `nano-33-ble`: typo in LPS22HB peripheral definition and documentation (#2579)
|
||||||
|
- `teensy41`: add the Teensy 4.1 board
|
||||||
|
- `teensy40`: add ADC support
|
||||||
|
- `teensy40`: add SPI support
|
||||||
|
- `thingplus-rp2040`: add the SparkFun Thing Plus RP2040 board
|
||||||
|
- `wioterminal`: add DefaultUART
|
||||||
|
- `wioterminal`: verify written data when flashing through OpenOCD
|
||||||
|
- `xiao-ble`: add XIAO BLE nRF52840 support
|
||||||
|
|
||||||
|
|
||||||
0.22.0
|
0.22.0
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
# tinygo-llvm stage obtains the llvm source for TinyGo
|
# tinygo-llvm stage obtains the llvm source for TinyGo
|
||||||
FROM golang:1.17 AS tinygo-llvm
|
FROM golang:1.18 AS tinygo-llvm
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y apt-utils make cmake clang-11 binutils-avr gcc-avr avr-libc ninja-build
|
apt-get install -y apt-utils make cmake clang-11 binutils-avr gcc-avr avr-libc ninja-build
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
|
|||||||
|
|
||||||
# Try to autodetect LLVM build tools.
|
# Try to autodetect LLVM build tools.
|
||||||
# Versions are listed here in descending priority order.
|
# Versions are listed here in descending priority order.
|
||||||
LLVM_VERSIONS = 13 12 11
|
LLVM_VERSIONS = 14 13 12 11
|
||||||
errifempty = $(if $(1),$(1),$(error $(2)))
|
errifempty = $(if $(1),$(1),$(error $(2)))
|
||||||
detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2)))
|
detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2)))
|
||||||
toolSearchPathsVersion = $(1)-$(2)
|
toolSearchPathsVersion = $(1)-$(2)
|
||||||
@@ -50,6 +50,38 @@ else
|
|||||||
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF'
|
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# Cross compiling support.
|
||||||
|
ifneq ($(CROSS),)
|
||||||
|
CC = $(CROSS)-gcc
|
||||||
|
CXX = $(CROSS)-g++
|
||||||
|
LLVM_OPTION += \
|
||||||
|
-DCMAKE_C_COMPILER=$(CC) \
|
||||||
|
-DCMAKE_CXX_COMPILER=$(CXX) \
|
||||||
|
-DLLVM_DEFAULT_TARGET_TRIPLE=$(CROSS) \
|
||||||
|
-DCROSS_TOOLCHAIN_FLAGS_NATIVE="-UCMAKE_C_COMPILER;-UCMAKE_CXX_COMPILER"
|
||||||
|
ifeq ($(CROSS), arm-linux-gnueabihf)
|
||||||
|
# Assume we're building on a Debian-like distro, with QEMU installed.
|
||||||
|
LLVM_CONFIG_PREFIX = qemu-arm -L /usr/arm-linux-gnueabihf/
|
||||||
|
# The CMAKE_SYSTEM_NAME flag triggers cross compilation mode.
|
||||||
|
LLVM_OPTION += \
|
||||||
|
-DCMAKE_SYSTEM_NAME=Linux \
|
||||||
|
-DLLVM_TARGET_ARCH=ARM
|
||||||
|
GOENVFLAGS = GOARCH=arm CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
|
||||||
|
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
|
||||||
|
else ifeq ($(CROSS), aarch64-linux-gnu)
|
||||||
|
# Assume we're building on a Debian-like distro, with QEMU installed.
|
||||||
|
LLVM_CONFIG_PREFIX = qemu-aarch64 -L /usr/aarch64-linux-gnu/
|
||||||
|
# The CMAKE_SYSTEM_NAME flag triggers cross compilation mode.
|
||||||
|
LLVM_OPTION += \
|
||||||
|
-DCMAKE_SYSTEM_NAME=Linux \
|
||||||
|
-DLLVM_TARGET_ARCH=AArch64
|
||||||
|
GOENVFLAGS = GOARCH=arm64 CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
|
||||||
|
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
|
||||||
|
else
|
||||||
|
$(error Unknown cross compilation target: $(CROSS))
|
||||||
|
endif
|
||||||
|
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 debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsmanifest
|
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
|
||||||
@@ -91,11 +123,11 @@ CLANG_LIB_NAMES = clangAnalysis clangAST clangASTMatchers clangBasic clangCodeGe
|
|||||||
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
|
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
|
||||||
|
|
||||||
# Libraries that should be linked in for the statically linked LLD.
|
# Libraries that should be linked in for the statically linked LLD.
|
||||||
LLD_LIB_NAMES = lldCOFF lldCommon lldCore lldDriver lldELF lldMachO2 lldMinGW lldReaderWriter lldWasm lldYAML
|
LLD_LIB_NAMES = lldCOFF lldCommon lldELF lldMachO lldMinGW lldWasm
|
||||||
LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP)
|
LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP)
|
||||||
|
|
||||||
# Other libraries that are needed to link TinyGo.
|
# Other libraries that are needed to link TinyGo.
|
||||||
EXTRA_LIB_NAMES = LLVMInterpreter
|
EXTRA_LIB_NAMES = LLVMInterpreter LLVMMCA LLVMX86TargetMCA
|
||||||
|
|
||||||
# All libraries to be built and linked with the tinygo binary (lib/lib*.a).
|
# All libraries to be built and linked with the tinygo binary (lib/lib*.a).
|
||||||
LIB_NAMES = clang $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)
|
LIB_NAMES = clang $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)
|
||||||
@@ -110,9 +142,9 @@ NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(ad
|
|||||||
|
|
||||||
# For static linking.
|
# For static linking.
|
||||||
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
||||||
CGO_CPPFLAGS+=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
CGO_CPPFLAGS+=$(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||||
CGO_CXXFLAGS=-std=c++14
|
CGO_CXXFLAGS=-std=c++14
|
||||||
CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@@ -175,12 +207,12 @@ gen-device-rp: build/gen-device-svd
|
|||||||
|
|
||||||
# Get LLVM sources.
|
# Get LLVM sources.
|
||||||
$(LLVM_PROJECTDIR)/llvm:
|
$(LLVM_PROJECTDIR)/llvm:
|
||||||
git clone -b xtensa_release_13.0.0 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
git clone -b xtensa_release_14.0.0-patched --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
||||||
llvm-source: $(LLVM_PROJECTDIR)/llvm
|
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:
|
||||||
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.
|
||||||
@@ -208,21 +240,18 @@ lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
|||||||
# Build the Go compiler.
|
# Build the Go compiler.
|
||||||
tinygo:
|
tinygo:
|
||||||
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
|
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
|
||||||
|
|
||||||
test: wasi-libc
|
test: wasi-libc
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||||
|
|
||||||
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
|
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
|
||||||
TEST_PACKAGES_SLOW = \
|
TEST_PACKAGES_SLOW = \
|
||||||
compress/bzip2 \
|
compress/bzip2 \
|
||||||
compress/flate \
|
|
||||||
crypto/dsa \
|
crypto/dsa \
|
||||||
index/suffixarray \
|
index/suffixarray \
|
||||||
|
|
||||||
# Standard library packages that pass tests quickly on darwin, linux, wasi, and windows
|
# Standard library packages that pass tests quickly on darwin, linux, wasi, and windows
|
||||||
TEST_PACKAGES_FAST = \
|
TEST_PACKAGES_FAST = \
|
||||||
compress/lzw \
|
|
||||||
compress/zlib \
|
compress/zlib \
|
||||||
container/heap \
|
container/heap \
|
||||||
container/list \
|
container/list \
|
||||||
@@ -236,6 +265,7 @@ TEST_PACKAGES_FAST = \
|
|||||||
crypto/sha256 \
|
crypto/sha256 \
|
||||||
crypto/sha512 \
|
crypto/sha512 \
|
||||||
debug/macho \
|
debug/macho \
|
||||||
|
embed/internal/embedtest \
|
||||||
encoding \
|
encoding \
|
||||||
encoding/ascii85 \
|
encoding/ascii85 \
|
||||||
encoding/base32 \
|
encoding/base32 \
|
||||||
@@ -269,17 +299,25 @@ TEST_PACKAGES_FAST = \
|
|||||||
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows
|
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows
|
||||||
# io/fs requires os.ReadDir, which is not yet supported on windows or wasi
|
# io/fs requires os.ReadDir, which is not yet supported on windows or wasi
|
||||||
# testing/fstest requires os.ReadDir, which is not yet supported on windows or wasi
|
# testing/fstest requires os.ReadDir, which is not yet supported on windows or wasi
|
||||||
|
# compress/flate fails windows go 1.18, https://github.com/tinygo-org/tinygo/issues/2762
|
||||||
|
# compress/lzw fails windows go 1.18 wasi, https://github.com/tinygo-org/tinygo/issues/2762
|
||||||
|
|
||||||
# Additional standard library packages that pass tests on individual platforms
|
# Additional standard library packages that pass tests on individual platforms
|
||||||
TEST_PACKAGES_LINUX := \
|
TEST_PACKAGES_LINUX := \
|
||||||
archive/zip \
|
archive/zip \
|
||||||
|
compress/flate \
|
||||||
|
compress/lzw \
|
||||||
debug/dwarf \
|
debug/dwarf \
|
||||||
debug/plan9obj \
|
debug/plan9obj \
|
||||||
io/fs \
|
io/fs \
|
||||||
|
io/ioutil \
|
||||||
testing/fstest
|
testing/fstest
|
||||||
|
|
||||||
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
|
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
|
||||||
|
|
||||||
|
TEST_PACKAGES_WINDOWS := \
|
||||||
|
compress/lzw
|
||||||
|
|
||||||
# Report platforms on which each standard library package is known to pass tests
|
# Report platforms on which each standard library package is known to pass tests
|
||||||
jointmp := $(shell echo /tmp/join.$$$$)
|
jointmp := $(shell echo /tmp/join.$$$$)
|
||||||
report-stdlib-tests-pass:
|
report-stdlib-tests-pass:
|
||||||
@@ -298,7 +336,7 @@ ifeq ($(shell uname),Linux)
|
|||||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
|
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
|
||||||
endif
|
endif
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST)
|
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Test known-working standard library packages.
|
# Test known-working standard library packages.
|
||||||
@@ -358,8 +396,6 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo
|
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo2
|
|
||||||
@$(MD5SUM) test.hex
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
|
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/mcp3008
|
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/mcp3008
|
||||||
@@ -390,6 +426,8 @@ ifneq ($(WASM), 0)
|
|||||||
@$(MD5SUM) test.wasm
|
@$(MD5SUM) test.wasm
|
||||||
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_express examples/blinky1
|
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_express examples/blinky1
|
||||||
@$(MD5SUM) test.wasm
|
@$(MD5SUM) test.wasm
|
||||||
|
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_bluefruit examples/blinky1
|
||||||
|
@$(MD5SUM) test.wasm
|
||||||
endif
|
endif
|
||||||
# test all targets/boards
|
# test all targets/boards
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
|
||||||
@@ -606,7 +644,8 @@ endif
|
|||||||
@$(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 -size short -o test.exe ./testdata/cgo
|
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
||||||
GOOS=darwin GOARCH=amd64 $(TINYGO) build -o test ./testdata/cgo
|
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo
|
||||||
|
GOOS=darwin GOARCH=arm64 $(TINYGO) build -size short -o test ./testdata/cgo
|
||||||
ifneq ($(OS),Windows_NT)
|
ifneq ($(OS),Windows_NT)
|
||||||
# TODO: this does not yet work on Windows. Somehow, unused functions are
|
# TODO: this does not yet work on Windows. Somehow, unused functions are
|
||||||
# not garbage collected.
|
# not garbage collected.
|
||||||
@@ -621,7 +660,6 @@ build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_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/macos-minimal-sdk
|
@mkdir -p build/release/tinygo/lib/macos-minimal-sdk
|
||||||
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
@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/mingw-w64/mingw-w64-headers/defaults
|
||||||
@@ -643,9 +681,6 @@ endif
|
|||||||
@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
|
||||||
@cp -rp lib/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt/lib
|
|
||||||
@cp -rp lib/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt
|
|
||||||
@cp -rp lib/compiler-rt/README.txt build/release/tinygo/lib/compiler-rt
|
|
||||||
@cp -rp lib/macos-minimal-sdk/* build/release/tinygo/lib/macos-minimal-sdk
|
@cp -rp lib/macos-minimal-sdk/* build/release/tinygo/lib/macos-minimal-sdk
|
||||||
@cp -rp lib/musl/arch/aarch64 build/release/tinygo/lib/musl/arch
|
@cp -rp lib/musl/arch/aarch64 build/release/tinygo/lib/musl/arch
|
||||||
@cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch
|
@cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch
|
||||||
@@ -682,21 +717,29 @@ endif
|
|||||||
@cp -rp lib/picolibc/newlib/libm/common build/release/tinygo/lib/picolibc/newlib/libm
|
@cp -rp lib/picolibc/newlib/libm/common build/release/tinygo/lib/picolibc/newlib/libm
|
||||||
@cp -rp lib/picolibc-stdio.c build/release/tinygo/lib
|
@cp -rp lib/picolibc-stdio.c build/release/tinygo/lib
|
||||||
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
|
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
|
||||||
|
@cp -rp llvm-project/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
|
||||||
|
@cp -rp llvm-project/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt-builtins
|
||||||
@cp -rp src build/release/tinygo/src
|
@cp -rp src build/release/tinygo/src
|
||||||
@cp -rp targets build/release/tinygo/targets
|
@cp -rp targets build/release/tinygo/targets
|
||||||
./build/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/compiler-rt compiler-rt
|
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/compiler-rt compiler-rt
|
||||||
./build/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/compiler-rt compiler-rt
|
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/compiler-rt compiler-rt
|
||||||
./build/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/compiler-rt compiler-rt
|
./build/release/tinygo/bin/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/compiler-rt compiler-rt
|
||||||
./build/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/picolibc picolibc
|
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/picolibc picolibc
|
||||||
./build/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/picolibc picolibc
|
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/picolibc picolibc
|
||||||
./build/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/picolibc picolibc
|
./build/release/tinygo/bin/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/picolibc picolibc
|
||||||
|
|
||||||
release: build/release
|
release:
|
||||||
tar -czf build/release.tar.gz -C build/release tinygo
|
tar -czf build/release.tar.gz -C build/release tinygo
|
||||||
|
|
||||||
deb: build/release
|
DEB_ARCH ?= native
|
||||||
|
deb:
|
||||||
@mkdir -p build/release-deb/usr/local/bin
|
@mkdir -p build/release-deb/usr/local/bin
|
||||||
@mkdir -p build/release-deb/usr/local/lib
|
@mkdir -p build/release-deb/usr/local/lib
|
||||||
cp -ar build/release/tinygo build/release-deb/usr/local/lib/tinygo
|
cp -ar build/release/tinygo build/release-deb/usr/local/lib/tinygo
|
||||||
ln -sf ../lib/tinygo/bin/tinygo build/release-deb/usr/local/bin/tinygo
|
ln -sf ../lib/tinygo/bin/tinygo build/release-deb/usr/local/bin/tinygo
|
||||||
fpm -f -s dir -t deb -n tinygo -v $(shell grep "const Version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb
|
fpm -f -s dir -t deb -n tinygo -a $(DEB_ARCH) -v $(shell grep "const Version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb
|
||||||
|
|
||||||
|
ifneq ($(RELEASEONLY), 1)
|
||||||
|
release: build/release
|
||||||
|
deb: build/release
|
||||||
|
endif
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
|||||||
|
|
||||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||||
|
|
||||||
The following 82 microcontroller boards are currently supported:
|
The following 85 microcontroller boards are currently supported:
|
||||||
|
|
||||||
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
|
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
|
||||||
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
||||||
@@ -85,6 +85,7 @@ The following 82 microcontroller boards are currently supported:
|
|||||||
* [Dragino LoRaWAN GPS Tracker LGT-92](http://www.dragino.com/products/lora-lorawan-end-node/item/142-lgt-92.html)
|
* [Dragino LoRaWAN GPS Tracker LGT-92](http://www.dragino.com/products/lora-lorawan-end-node/item/142-lgt-92.html)
|
||||||
* [ESP32 - Core board](https://www.espressif.com/en/products/socs/esp32)
|
* [ESP32 - Core board](https://www.espressif.com/en/products/socs/esp32)
|
||||||
* [ESP32 - mini32](https://www.espressif.com/en/products/socs/esp32)
|
* [ESP32 - mini32](https://www.espressif.com/en/products/socs/esp32)
|
||||||
|
* [ESP32-C3-12f](https://www.espressif.com/en/products/socs/esp32-c3)
|
||||||
* [ESP8266 - d1mini](https://www.espressif.com/en/products/socs/esp8266)
|
* [ESP8266 - d1mini](https://www.espressif.com/en/products/socs/esp8266)
|
||||||
* [ESP8266 - NodeMCU](https://www.espressif.com/en/products/socs/esp8266)
|
* [ESP8266 - NodeMCU](https://www.espressif.com/en/products/socs/esp8266)
|
||||||
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
|
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
|
||||||
@@ -112,10 +113,12 @@ The following 82 microcontroller boards are currently supported:
|
|||||||
* [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/)
|
* [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/)
|
||||||
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
|
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
|
||||||
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
|
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
|
||||||
|
* [Seeed XIAO BLE](https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html)
|
||||||
* [Seeed LoRa-E5 Development Kit](https://www.seeedstudio.com/LoRa-E5-Dev-Kit-p-4868.html)
|
* [Seeed LoRa-E5 Development Kit](https://www.seeedstudio.com/LoRa-E5-Dev-Kit-p-4868.html)
|
||||||
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
|
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
|
||||||
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
||||||
* [SiFIve HiFive1 Rev B](https://www.sifive.com/boards/hifive1)
|
* [SiFIve HiFive1 Rev B](https://www.sifive.com/boards/hifive1)
|
||||||
|
* [Sparkfun Thing Plus RP2040](https://www.sparkfun.com/products/17745)
|
||||||
* [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
* [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
||||||
* [ST Micro "Nucleo" F722ZE](https://www.st.com/en/evaluation-tools/nucleo-f722ze.html)
|
* [ST Micro "Nucleo" F722ZE](https://www.st.com/en/evaluation-tools/nucleo-f722ze.html)
|
||||||
* [ST Micro "Nucleo" L031K6](https://www.st.com/ja/evaluation-tools/nucleo-l031k6.html)
|
* [ST Micro "Nucleo" L031K6](https://www.st.com/ja/evaluation-tools/nucleo-l031k6.html)
|
||||||
|
|||||||
+250
-57
@@ -4,6 +4,7 @@
|
|||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
@@ -37,8 +38,14 @@ import (
|
|||||||
// BuildResult is the output of a build. This includes the binary itself and
|
// BuildResult is the output of a build. This includes the binary itself and
|
||||||
// some other metadata that is obtained while building the binary.
|
// some other metadata that is obtained while building the binary.
|
||||||
type BuildResult struct {
|
type BuildResult struct {
|
||||||
|
// The executable directly from the linker, usually including debug
|
||||||
|
// information. Used for GDB for example.
|
||||||
|
Executable string
|
||||||
|
|
||||||
// A path to the output binary. It will be removed after Build returns, so
|
// A path to the output binary. It will be removed after Build returns, so
|
||||||
// if it should be kept it must be copied or moved away.
|
// if it should be kept it must be copied or moved away.
|
||||||
|
// It is often the same as Executable, but differs if the output format is
|
||||||
|
// .hex for example (instead of the usual ELF).
|
||||||
Binary string
|
Binary string
|
||||||
|
|
||||||
// The directory of the main package. This is useful for testing as the test
|
// The directory of the main package. This is useful for testing as the test
|
||||||
@@ -74,6 +81,7 @@ type packageAction struct {
|
|||||||
Config *compiler.Config
|
Config *compiler.Config
|
||||||
CFlags []string
|
CFlags []string
|
||||||
FileHashes map[string]string // hash of every file that's part of the package
|
FileHashes map[string]string // hash of every file that's part of the package
|
||||||
|
EmbeddedFiles map[string]string // hash of all the //go:embed files in the package
|
||||||
Imports map[string]string // map from imported package to action ID hash
|
Imports map[string]string // map from imported package to action ID hash
|
||||||
OptLevel int // LLVM optimization level (0-3)
|
OptLevel int // LLVM optimization level (0-3)
|
||||||
SizeLevel int // LLVM optimization for size level (0-2)
|
SizeLevel int // LLVM optimization for size level (0-2)
|
||||||
@@ -182,6 +190,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer machine.Dispose()
|
||||||
|
|
||||||
// Load entire program AST into memory.
|
// Load entire program AST into memory.
|
||||||
lprogram, err := loader.Load(config, []string{pkgName}, config.ClangHeaders, types.Config{
|
lprogram, err := loader.Load(config, []string{pkgName}, config.ClangHeaders, types.Config{
|
||||||
@@ -202,8 +211,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// Add jobs to compile each package.
|
// Add jobs to compile each package.
|
||||||
// Packages that have a cache hit will not be compiled again.
|
// Packages that have a cache hit will not be compiled again.
|
||||||
var packageJobs []*compileJob
|
var packageJobs []*compileJob
|
||||||
packageBitcodePaths := make(map[string]string)
|
packageActionIDJobs := make(map[string]*compileJob)
|
||||||
packageActionIDs := make(map[string]string)
|
|
||||||
|
|
||||||
if config.Options.GlobalValues["runtime"]["buildVersion"] == "" {
|
if config.Options.GlobalValues["runtime"]["buildVersion"] == "" {
|
||||||
version := goenv.Version
|
version := goenv.Version
|
||||||
@@ -219,6 +227,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
config.Options.GlobalValues["runtime"]["buildVersion"] = version
|
config.Options.GlobalValues["runtime"]["buildVersion"] = version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var embedFileObjects []*compileJob
|
||||||
for _, pkg := range lprogram.Sorted() {
|
for _, pkg := range lprogram.Sorted() {
|
||||||
pkg := pkg // necessary to avoid a race condition
|
pkg := pkg // necessary to avoid a race condition
|
||||||
|
|
||||||
@@ -228,52 +237,114 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
}
|
}
|
||||||
sort.Strings(undefinedGlobals)
|
sort.Strings(undefinedGlobals)
|
||||||
|
|
||||||
// Create a cache key: a hash from the action ID below that contains all
|
// Make compile jobs to load files to be embedded in the output binary.
|
||||||
// the parameters for the build.
|
var actionIDDependencies []*compileJob
|
||||||
actionID := packageAction{
|
allFiles := map[string][]*loader.EmbedFile{}
|
||||||
ImportPath: pkg.ImportPath,
|
for _, files := range pkg.EmbedGlobals {
|
||||||
CompilerBuildID: string(compilerBuildID),
|
for _, file := range files {
|
||||||
TinyGoVersion: goenv.Version,
|
allFiles[file.Name] = append(allFiles[file.Name], file)
|
||||||
LLVMVersion: llvm.Version,
|
}
|
||||||
Config: compilerConfig,
|
|
||||||
CFlags: pkg.CFlags,
|
|
||||||
FileHashes: make(map[string]string, len(pkg.FileHashes)),
|
|
||||||
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
|
||||||
OptLevel: optLevel,
|
|
||||||
SizeLevel: sizeLevel,
|
|
||||||
UndefinedGlobals: undefinedGlobals,
|
|
||||||
}
|
}
|
||||||
for filePath, hash := range pkg.FileHashes {
|
for name, files := range allFiles {
|
||||||
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
|
name := name
|
||||||
|
files := files
|
||||||
|
job := &compileJob{
|
||||||
|
description: "make object file for " + name,
|
||||||
|
run: func(job *compileJob) error {
|
||||||
|
// Read the file contents in memory.
|
||||||
|
path := filepath.Join(pkg.Dir, name)
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash the file.
|
||||||
|
sum := sha256.Sum256(data)
|
||||||
|
hexSum := hex.EncodeToString(sum[:16])
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
file.Size = uint64(len(data))
|
||||||
|
file.Hash = hexSum
|
||||||
|
if file.NeedsData {
|
||||||
|
file.Data = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
job.result, err = createEmbedObjectFile(string(data), hexSum, name, pkg.OriginalDir(), dir, compilerConfig)
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
actionIDDependencies = append(actionIDDependencies, job)
|
||||||
|
embedFileObjects = append(embedFileObjects, job)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Action ID jobs need to know the action ID of all the jobs the package
|
||||||
|
// imports.
|
||||||
|
var importedPackages []*compileJob
|
||||||
for _, imported := range pkg.Pkg.Imports() {
|
for _, imported := range pkg.Pkg.Imports() {
|
||||||
hash, ok := packageActionIDs[imported.Path()]
|
job, ok := packageActionIDJobs[imported.Path()]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
|
return fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
|
||||||
}
|
}
|
||||||
actionID.Imports[imported.Path()] = hash
|
importedPackages = append(importedPackages, job)
|
||||||
|
actionIDDependencies = append(actionIDDependencies, job)
|
||||||
}
|
}
|
||||||
buf, err := json.Marshal(actionID)
|
|
||||||
if err != nil {
|
// Create a job that will calculate the action ID for a package compile
|
||||||
panic(err) // shouldn't happen
|
// job. The action ID is the cache key that is used for caching this
|
||||||
|
// package.
|
||||||
|
packageActionIDJob := &compileJob{
|
||||||
|
description: "calculate cache key for package " + pkg.ImportPath,
|
||||||
|
dependencies: actionIDDependencies,
|
||||||
|
run: func(job *compileJob) error {
|
||||||
|
// Create a cache key: a hash from the action ID below that contains all
|
||||||
|
// the parameters for the build.
|
||||||
|
actionID := packageAction{
|
||||||
|
ImportPath: pkg.ImportPath,
|
||||||
|
CompilerBuildID: string(compilerBuildID),
|
||||||
|
TinyGoVersion: goenv.Version,
|
||||||
|
LLVMVersion: llvm.Version,
|
||||||
|
Config: compilerConfig,
|
||||||
|
CFlags: pkg.CFlags,
|
||||||
|
FileHashes: make(map[string]string, len(pkg.FileHashes)),
|
||||||
|
EmbeddedFiles: make(map[string]string, len(allFiles)),
|
||||||
|
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
||||||
|
OptLevel: optLevel,
|
||||||
|
SizeLevel: sizeLevel,
|
||||||
|
UndefinedGlobals: undefinedGlobals,
|
||||||
|
}
|
||||||
|
for filePath, hash := range pkg.FileHashes {
|
||||||
|
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
|
||||||
|
}
|
||||||
|
for name, files := range allFiles {
|
||||||
|
actionID.EmbeddedFiles[name] = files[0].Hash
|
||||||
|
}
|
||||||
|
for i, imported := range pkg.Pkg.Imports() {
|
||||||
|
actionID.Imports[imported.Path()] = importedPackages[i].result
|
||||||
|
}
|
||||||
|
buf, err := json.Marshal(actionID)
|
||||||
|
if err != nil {
|
||||||
|
return err // shouldn't happen
|
||||||
|
}
|
||||||
|
hash := sha512.Sum512_224(buf)
|
||||||
|
job.result = hex.EncodeToString(hash[:])
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
hash := sha512.Sum512_224(buf)
|
packageActionIDJobs[pkg.ImportPath] = packageActionIDJob
|
||||||
packageActionIDs[pkg.ImportPath] = hex.EncodeToString(hash[:])
|
|
||||||
|
|
||||||
// Determine the path of the bitcode file (which is a serialized version
|
// Now create the job to actually build the package. It will exit early
|
||||||
// of a LLVM module).
|
// if the package is already compiled.
|
||||||
bitcodePath := filepath.Join(cacheDir, "pkg-"+hex.EncodeToString(hash[:])+".bc")
|
|
||||||
packageBitcodePaths[pkg.ImportPath] = bitcodePath
|
|
||||||
|
|
||||||
// The package has not yet been compiled, so create a job to do so.
|
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
description: "compile package " + pkg.ImportPath,
|
description: "compile package " + pkg.ImportPath,
|
||||||
run: func(*compileJob) error {
|
dependencies: []*compileJob{packageActionIDJob},
|
||||||
|
run: func(job *compileJob) error {
|
||||||
|
job.result = filepath.Join(cacheDir, "pkg-"+packageActionIDJob.result+".bc")
|
||||||
// Acquire a lock (if supported).
|
// Acquire a lock (if supported).
|
||||||
unlock := lock(bitcodePath + ".lock")
|
unlock := lock(job.result + ".lock")
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
if _, err := os.Stat(bitcodePath); err == nil {
|
if _, err := os.Stat(job.result); err == nil {
|
||||||
// Already cached, don't recreate this package.
|
// Already cached, don't recreate this package.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -281,6 +352,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// Compile AST to IR. The compiler.CompilePackage function will
|
// Compile AST to IR. The compiler.CompilePackage function will
|
||||||
// build the SSA as needed.
|
// build the SSA as needed.
|
||||||
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
|
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
|
||||||
|
defer mod.Context().Dispose()
|
||||||
|
defer mod.Dispose()
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
return newMultiError(errs)
|
return newMultiError(errs)
|
||||||
}
|
}
|
||||||
@@ -392,7 +465,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// Write to a temporary path that is renamed to the destination
|
// Write to a temporary path that is renamed to the destination
|
||||||
// file to avoid race conditions with other TinyGo invocatiosn
|
// file to avoid race conditions with other TinyGo invocatiosn
|
||||||
// that might also be compiling this package at the same time.
|
// that might also be compiling this package at the same time.
|
||||||
f, err := ioutil.TempFile(filepath.Dir(bitcodePath), filepath.Base(bitcodePath))
|
f, err := ioutil.TempFile(filepath.Dir(job.result), filepath.Base(job.result))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -412,13 +485,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
// WriteBitcodeToFile doesn't produce a useful error on its
|
// WriteBitcodeToFile doesn't produce a useful error on its
|
||||||
// own, so create a somewhat useful error message here.
|
// own, so create a somewhat useful error message here.
|
||||||
return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, bitcodePath)
|
return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, job.result)
|
||||||
}
|
}
|
||||||
err = f.Close()
|
err = f.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return os.Rename(f.Name(), bitcodePath)
|
return os.Rename(f.Name(), job.result)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
packageJobs = append(packageJobs, job)
|
packageJobs = append(packageJobs, job)
|
||||||
@@ -426,6 +499,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
|
|
||||||
// Add job that links and optimizes all packages together.
|
// Add job that links and optimizes all packages together.
|
||||||
var mod llvm.Module
|
var mod llvm.Module
|
||||||
|
defer func() {
|
||||||
|
if !mod.IsNil() {
|
||||||
|
ctx := mod.Context()
|
||||||
|
mod.Dispose()
|
||||||
|
ctx.Dispose()
|
||||||
|
}
|
||||||
|
}()
|
||||||
var stackSizeLoads []string
|
var stackSizeLoads []string
|
||||||
programJob := &compileJob{
|
programJob := &compileJob{
|
||||||
description: "link+optimize packages (LTO)",
|
description: "link+optimize packages (LTO)",
|
||||||
@@ -435,8 +515,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// anything, it only links the bitcode files together.
|
// anything, it only links the bitcode files together.
|
||||||
ctx := llvm.NewContext()
|
ctx := llvm.NewContext()
|
||||||
mod = ctx.NewModule("main")
|
mod = ctx.NewModule("main")
|
||||||
for _, pkg := range lprogram.Sorted() {
|
for _, pkgJob := range packageJobs {
|
||||||
pkgMod, err := ctx.ParseBitcodeFile(packageBitcodePaths[pkg.ImportPath])
|
pkgMod, err := ctx.ParseBitcodeFile(pkgJob.result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to load bitcode file: %w", err)
|
return fmt.Errorf("failed to load bitcode file: %w", err)
|
||||||
}
|
}
|
||||||
@@ -528,6 +608,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer llvmBuf.Dispose()
|
||||||
return ioutil.WriteFile(outpath, llvmBuf.Bytes(), 0666)
|
return ioutil.WriteFile(outpath, llvmBuf.Bytes(), 0666)
|
||||||
case ".bc":
|
case ".bc":
|
||||||
var buf llvm.MemoryBuffer
|
var buf llvm.MemoryBuffer
|
||||||
@@ -636,6 +717,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// Add libc dependencies, if they exist.
|
// Add libc dependencies, if they exist.
|
||||||
linkerDependencies = append(linkerDependencies, libcDependencies...)
|
linkerDependencies = append(linkerDependencies, libcDependencies...)
|
||||||
|
|
||||||
|
// Add embedded files.
|
||||||
|
linkerDependencies = append(linkerDependencies, embedFileObjects...)
|
||||||
|
|
||||||
// Strip debug information with -no-debug.
|
// Strip debug information with -no-debug.
|
||||||
if !config.Debug() {
|
if !config.Debug() {
|
||||||
for _, tag := range config.BuildTags() {
|
for _, tag := range config.BuildTags() {
|
||||||
@@ -688,11 +772,24 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
config.Options.PrintCommands(config.Target.Linker, ldflags...)
|
config.Options.PrintCommands(config.Target.Linker, ldflags...)
|
||||||
}
|
}
|
||||||
if config.UseThinLTO() {
|
if config.UseThinLTO() {
|
||||||
ldflags = append(ldflags,
|
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
|
||||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
if config.GOOS() == "windows" {
|
||||||
"-plugin-opt=mcpu="+config.CPU(),
|
// Options for the MinGW wrapper for the lld COFF linker.
|
||||||
"-plugin-opt=O"+strconv.Itoa(optLevel),
|
ldflags = append(ldflags,
|
||||||
"-plugin-opt=thinlto")
|
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel),
|
||||||
|
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
|
||||||
|
} else if config.GOOS() == "darwin" {
|
||||||
|
// Options for the ld64-compatible lld linker.
|
||||||
|
ldflags = append(ldflags,
|
||||||
|
"--lto-O"+strconv.Itoa(optLevel),
|
||||||
|
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
|
||||||
|
} else {
|
||||||
|
// Options for the ELF linker.
|
||||||
|
ldflags = append(ldflags,
|
||||||
|
"--lto-O"+strconv.Itoa(optLevel),
|
||||||
|
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
||||||
|
)
|
||||||
|
}
|
||||||
if config.CodeModel() != "default" {
|
if config.CodeModel() != "default" {
|
||||||
ldflags = append(ldflags,
|
ldflags = append(ldflags,
|
||||||
"-mllvm", "-code-model="+config.CodeModel())
|
"-mllvm", "-code-model="+config.CodeModel())
|
||||||
@@ -835,7 +932,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "esp32", "esp32c3", "esp8266":
|
case "esp32", "esp32-img", "esp32c3", "esp8266":
|
||||||
// Special format for the ESP family of chips (parsed by the ROM
|
// Special format for the ESP family of chips (parsed by the ROM
|
||||||
// bootloader).
|
// bootloader).
|
||||||
tmppath = filepath.Join(dir, "main"+outext)
|
tmppath = filepath.Join(dir, "main"+outext)
|
||||||
@@ -867,6 +964,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
}
|
}
|
||||||
|
|
||||||
return action(BuildResult{
|
return action(BuildResult{
|
||||||
|
Executable: executable,
|
||||||
Binary: tmppath,
|
Binary: tmppath,
|
||||||
MainDir: lprogram.MainPkg().Dir,
|
MainDir: lprogram.MainPkg().Dir,
|
||||||
ModuleRoot: moduleroot,
|
ModuleRoot: moduleroot,
|
||||||
@@ -874,6 +972,112 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createEmbedObjectFile creates a new object file with the given contents, for
|
||||||
|
// the embed package.
|
||||||
|
func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, compilerConfig *compiler.Config) (string, error) {
|
||||||
|
// TODO: this works for small files, but can be a problem for larger files.
|
||||||
|
// For larger files, it seems more appropriate to generate the object file
|
||||||
|
// manually without going through LLVM.
|
||||||
|
// On the other hand, generating DWARF like we do here can be difficult
|
||||||
|
// without assistance from LLVM.
|
||||||
|
|
||||||
|
// Create new LLVM module just for this file.
|
||||||
|
ctx := llvm.NewContext()
|
||||||
|
defer ctx.Dispose()
|
||||||
|
mod := ctx.NewModule("data")
|
||||||
|
defer mod.Dispose()
|
||||||
|
|
||||||
|
// Create data global.
|
||||||
|
value := ctx.ConstString(data, false)
|
||||||
|
globalName := "embed/file_" + hexSum
|
||||||
|
global := llvm.AddGlobal(mod, value.Type(), globalName)
|
||||||
|
global.SetInitializer(value)
|
||||||
|
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
|
global.SetGlobalConstant(true)
|
||||||
|
global.SetUnnamedAddr(true)
|
||||||
|
global.SetAlignment(1)
|
||||||
|
if compilerConfig.GOOS != "darwin" {
|
||||||
|
// MachO doesn't support COMDATs, while COFF requires it (to avoid
|
||||||
|
// "duplicate symbol" errors). ELF works either way.
|
||||||
|
// Therefore, only use a COMDAT on non-MachO systems (aka non-MacOS).
|
||||||
|
global.SetComdat(mod.Comdat(globalName))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add DWARF debug information to this global, so that it is
|
||||||
|
// correctly counted when compiling with the -size= flag.
|
||||||
|
dibuilder := llvm.NewDIBuilder(mod)
|
||||||
|
dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
||||||
|
Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?)
|
||||||
|
File: sourceFile,
|
||||||
|
Dir: sourceDir,
|
||||||
|
Producer: "TinyGo",
|
||||||
|
Optimized: false,
|
||||||
|
})
|
||||||
|
ditype := dibuilder.CreateArrayType(llvm.DIArrayType{
|
||||||
|
SizeInBits: uint64(len(data)) * 8,
|
||||||
|
AlignInBits: 8,
|
||||||
|
ElementType: dibuilder.CreateBasicType(llvm.DIBasicType{
|
||||||
|
Name: "byte",
|
||||||
|
SizeInBits: 8,
|
||||||
|
Encoding: llvm.DW_ATE_unsigned_char,
|
||||||
|
}),
|
||||||
|
Subscripts: []llvm.DISubrange{
|
||||||
|
{
|
||||||
|
Lo: 0,
|
||||||
|
Count: int64(len(data)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
difile := dibuilder.CreateFile(sourceFile, sourceDir)
|
||||||
|
diglobalexpr := dibuilder.CreateGlobalVariableExpression(difile, llvm.DIGlobalVariableExpression{
|
||||||
|
Name: globalName,
|
||||||
|
File: difile,
|
||||||
|
Line: 1,
|
||||||
|
Type: ditype,
|
||||||
|
Expr: dibuilder.CreateExpression(nil),
|
||||||
|
AlignInBits: 8,
|
||||||
|
})
|
||||||
|
global.AddMetadata(0, diglobalexpr)
|
||||||
|
mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||||
|
ctx.MDNode([]llvm.Metadata{
|
||||||
|
llvm.ConstInt(ctx.Int32Type(), 2, false).ConstantAsMetadata(), // Warning on mismatch
|
||||||
|
ctx.MDString("Debug Info Version"),
|
||||||
|
llvm.ConstInt(ctx.Int32Type(), 3, false).ConstantAsMetadata(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||||
|
ctx.MDNode([]llvm.Metadata{
|
||||||
|
llvm.ConstInt(ctx.Int32Type(), 7, false).ConstantAsMetadata(), // Max on mismatch
|
||||||
|
ctx.MDString("Dwarf Version"),
|
||||||
|
llvm.ConstInt(ctx.Int32Type(), 4, false).ConstantAsMetadata(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
dibuilder.Finalize()
|
||||||
|
dibuilder.Destroy()
|
||||||
|
|
||||||
|
// Write this LLVM module out as an object file.
|
||||||
|
machine, err := compiler.NewTargetMachine(compilerConfig)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer machine.Dispose()
|
||||||
|
outfile, err := os.CreateTemp(tmpdir, "embed-"+hexSum+"-*.o")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer outfile.Close()
|
||||||
|
buf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer buf.Dispose()
|
||||||
|
_, err = outfile.Write(buf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return outfile.Name(), outfile.Close()
|
||||||
|
}
|
||||||
|
|
||||||
// optimizeProgram runs a series of optimizations and transformations that are
|
// optimizeProgram runs a series of optimizations and transformations that are
|
||||||
// needed to convert a program to its final form. Some transformations are not
|
// needed to convert a program to its final form. Some transformations are not
|
||||||
// optional and must be run as the compiler expects them to run.
|
// optional and must be run as the compiler expects them to run.
|
||||||
@@ -928,17 +1132,6 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
|||||||
return errors.New("verification failure after LLVM optimization passes")
|
return errors.New("verification failure after LLVM optimization passes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// LLVM 11 by default tries to emit tail calls (even with the target feature
|
|
||||||
// disabled) unless it is explicitly disabled with a function attribute.
|
|
||||||
// This is a problem, as it tries to emit them and prints an error when it
|
|
||||||
// can't with this feature disabled.
|
|
||||||
// Because as of september 2020 tail calls are not yet widely supported,
|
|
||||||
// they need to be disabled until they are widely supported (at which point
|
|
||||||
// the +tail-call target feautre can be set).
|
|
||||||
if strings.HasPrefix(config.Triple(), "wasm") {
|
|
||||||
transform.DisableTailCalls(mod)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func TestClangAttributes(t *testing.T) {
|
|||||||
"cortex-m0",
|
"cortex-m0",
|
||||||
"cortex-m0plus",
|
"cortex-m0plus",
|
||||||
"cortex-m3",
|
"cortex-m3",
|
||||||
//"cortex-m33", // TODO: broken in LLVM 11, fixed in https://reviews.llvm.org/D90305
|
"cortex-m33",
|
||||||
"cortex-m4",
|
"cortex-m4",
|
||||||
"cortex-m7",
|
"cortex-m7",
|
||||||
"esp32c3",
|
"esp32c3",
|
||||||
|
|||||||
+22
-3
@@ -1,7 +1,11 @@
|
|||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// These are the GENERIC_SOURCES according to CMakeList.txt.
|
// These are the GENERIC_SOURCES according to CMakeList.txt.
|
||||||
@@ -36,7 +40,6 @@ var genericBuiltins = []string{
|
|||||||
"divdf3.c",
|
"divdf3.c",
|
||||||
"divdi3.c",
|
"divdi3.c",
|
||||||
"divmoddi4.c",
|
"divmoddi4.c",
|
||||||
"divmodsi4.c",
|
|
||||||
"divsc3.c",
|
"divsc3.c",
|
||||||
"divsf3.c",
|
"divsf3.c",
|
||||||
"divsi3.c",
|
"divsi3.c",
|
||||||
@@ -72,6 +75,7 @@ var genericBuiltins = []string{
|
|||||||
"floatunsisf.c",
|
"floatunsisf.c",
|
||||||
"floatuntidf.c",
|
"floatuntidf.c",
|
||||||
"floatuntisf.c",
|
"floatuntisf.c",
|
||||||
|
"fp_mode.c",
|
||||||
//"int_util.c",
|
//"int_util.c",
|
||||||
"lshrdi3.c",
|
"lshrdi3.c",
|
||||||
"lshrti3.c",
|
"lshrti3.c",
|
||||||
@@ -122,7 +126,6 @@ var genericBuiltins = []string{
|
|||||||
"ucmpti2.c",
|
"ucmpti2.c",
|
||||||
"udivdi3.c",
|
"udivdi3.c",
|
||||||
"udivmoddi4.c",
|
"udivmoddi4.c",
|
||||||
"udivmodsi4.c",
|
|
||||||
"udivmodti4.c",
|
"udivmodti4.c",
|
||||||
"udivsi3.c",
|
"udivsi3.c",
|
||||||
"udivti3.c",
|
"udivti3.c",
|
||||||
@@ -149,6 +152,14 @@ var aeabiBuiltins = []string{
|
|||||||
"arm/aeabi_memset.S",
|
"arm/aeabi_memset.S",
|
||||||
"arm/aeabi_uidivmod.S",
|
"arm/aeabi_uidivmod.S",
|
||||||
"arm/aeabi_uldivmod.S",
|
"arm/aeabi_uldivmod.S",
|
||||||
|
|
||||||
|
// These two are not technically EABI builtins but are used by them and only
|
||||||
|
// seem to be used on ARM. LLVM seems to use __divsi3 and __modsi3 on most
|
||||||
|
// other architectures.
|
||||||
|
// Most importantly, they have a different calling convention on AVR so
|
||||||
|
// should not be used on AVR.
|
||||||
|
"divmodsi4.c",
|
||||||
|
"udivmodsi4.c",
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompilerRT is a library with symbols required by programs compiled with LLVM.
|
// CompilerRT is a library with symbols required by programs compiled with LLVM.
|
||||||
@@ -161,7 +172,15 @@ var CompilerRT = Library{
|
|||||||
cflags: func(target, headerPath string) []string {
|
cflags: func(target, headerPath string) []string {
|
||||||
return []string{"-Werror", "-Wall", "-std=c11", "-nostdlibinc"}
|
return []string{"-Werror", "-Wall", "-std=c11", "-nostdlibinc"}
|
||||||
},
|
},
|
||||||
sourceDir: "lib/compiler-rt/lib/builtins",
|
sourceDir: func() string {
|
||||||
|
llvmDir := filepath.Join(goenv.Get("TINYGOROOT"), "llvm-project/compiler-rt/lib/builtins")
|
||||||
|
if _, err := os.Stat(llvmDir); err == nil {
|
||||||
|
// Release build.
|
||||||
|
return llvmDir
|
||||||
|
}
|
||||||
|
// Development build.
|
||||||
|
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/compiler-rt-builtins")
|
||||||
|
},
|
||||||
librarySources: func(target string) []string {
|
librarySources: func(target string) []string {
|
||||||
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
|
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
|
||||||
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
||||||
|
|||||||
+3
-4
@@ -38,6 +38,7 @@
|
|||||||
#include "llvm/MC/MCStreamer.h"
|
#include "llvm/MC/MCStreamer.h"
|
||||||
#include "llvm/MC/MCSubtargetInfo.h"
|
#include "llvm/MC/MCSubtargetInfo.h"
|
||||||
#include "llvm/MC/MCTargetOptions.h"
|
#include "llvm/MC/MCTargetOptions.h"
|
||||||
|
#include "llvm/MC/TargetRegistry.h"
|
||||||
#include "llvm/Option/Arg.h"
|
#include "llvm/Option/Arg.h"
|
||||||
#include "llvm/Option/ArgList.h"
|
#include "llvm/Option/ArgList.h"
|
||||||
#include "llvm/Option/OptTable.h"
|
#include "llvm/Option/OptTable.h"
|
||||||
@@ -51,7 +52,6 @@
|
|||||||
#include "llvm/Support/Process.h"
|
#include "llvm/Support/Process.h"
|
||||||
#include "llvm/Support/Signals.h"
|
#include "llvm/Support/Signals.h"
|
||||||
#include "llvm/Support/SourceMgr.h"
|
#include "llvm/Support/SourceMgr.h"
|
||||||
#include "llvm/Support/TargetRegistry.h"
|
|
||||||
#include "llvm/Support/TargetSelect.h"
|
#include "llvm/Support/TargetSelect.h"
|
||||||
#include "llvm/Support/Timer.h"
|
#include "llvm/Support/Timer.h"
|
||||||
#include "llvm/Support/raw_ostream.h"
|
#include "llvm/Support/raw_ostream.h"
|
||||||
@@ -120,7 +120,6 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
|||||||
llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
|
llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
|
||||||
.Case("none", llvm::DebugCompressionType::None)
|
.Case("none", llvm::DebugCompressionType::None)
|
||||||
.Case("zlib", llvm::DebugCompressionType::Z)
|
.Case("zlib", llvm::DebugCompressionType::Z)
|
||||||
.Case("zlib-gnu", llvm::DebugCompressionType::GNU)
|
|
||||||
.Default(llvm::DebugCompressionType::None);
|
.Default(llvm::DebugCompressionType::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,7 +381,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI,
|
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI,
|
||||||
Opts.RelaxAll, Opts.IncrementalLinkerCompatible,
|
Opts.RelaxAll, Opts.IncrementalLinkerCompatible,
|
||||||
/*DWARFMustBeAtTheEnd*/ true));
|
/*DWARFMustBeAtTheEnd*/ true));
|
||||||
Str.get()->InitSections(Opts.NoExecStack);
|
Str.get()->initSections(Opts.NoExecStack, *STI);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When -fembed-bitcode is passed to clang_as, a 1-byte marker
|
// When -fembed-bitcode is passed to clang_as, a 1-byte marker
|
||||||
@@ -442,7 +441,7 @@ bool ExecuteAssembler(AssemblerInvocation &Opts,
|
|||||||
return Failed;
|
return Failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void LLVMErrorHandler(void *UserData, const std::string &Message,
|
static void LLVMErrorHandler(void *UserData, const char *Message,
|
||||||
bool GenCrashDiag) {
|
bool GenCrashDiag) {
|
||||||
DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData);
|
DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData);
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -74,7 +74,7 @@ func LookupCommand(name string) (string, error) {
|
|||||||
}
|
}
|
||||||
return cmdName, nil
|
return cmdName, nil
|
||||||
}
|
}
|
||||||
return "", errors.New("%#v: none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
|
return "", errors.New("none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
|
||||||
}
|
}
|
||||||
|
|
||||||
func execCommand(name string, args ...string) error {
|
func execCommand(name string, args ...string) error {
|
||||||
|
|||||||
+2
-2
@@ -33,8 +33,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||||
}
|
}
|
||||||
if major != 1 || minor < 15 || minor > 17 {
|
if major != 1 || minor < 16 || minor > 18 {
|
||||||
return nil, fmt.Errorf("requires go version 1.15 through 1.17, got go%d.%d", major, minor)
|
return nil, fmt.Errorf("requires go version 1.16 through 1.18, got go%d.%d", major, minor)
|
||||||
}
|
}
|
||||||
|
|
||||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func makeDarwinLibSystemJob(config *compileopts.Config, tmpdir string) *compileJ
|
|||||||
// Link object file to dynamic library.
|
// Link object file to dynamic library.
|
||||||
platformVersion := strings.TrimPrefix(strings.Split(config.Triple(), "-")[2], "macosx")
|
platformVersion := strings.TrimPrefix(strings.Split(config.Triple(), "-")[2], "macosx")
|
||||||
flags = []string{
|
flags = []string{
|
||||||
"-flavor", "darwinnew",
|
"-flavor", "darwin",
|
||||||
"-demangle",
|
"-demangle",
|
||||||
"-dynamic",
|
"-dynamic",
|
||||||
"-dylib",
|
"-dylib",
|
||||||
|
|||||||
+30
-3
@@ -15,6 +15,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type espImageSegment struct {
|
type espImageSegment struct {
|
||||||
@@ -78,15 +79,31 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
|||||||
// An added benefit is that we don't need to check for errors all the time.
|
// An added benefit is that we don't need to check for errors all the time.
|
||||||
outf := &bytes.Buffer{}
|
outf := &bytes.Buffer{}
|
||||||
|
|
||||||
|
// Separate esp32 and esp32-img. The -img suffix indicates we should make an
|
||||||
|
// image, not just a binary to be flashed at 0x1000 for example.
|
||||||
|
chip := format
|
||||||
|
makeImage := false
|
||||||
|
if strings.HasSuffix(format, "-img") {
|
||||||
|
makeImage = true
|
||||||
|
chip = format[:len(format)-len("-img")]
|
||||||
|
}
|
||||||
|
|
||||||
|
if makeImage {
|
||||||
|
// The bootloader starts at 0x1000, or 4096.
|
||||||
|
// TinyGo doesn't use a separate bootloader and runs the entire
|
||||||
|
// application in the bootloader location.
|
||||||
|
outf.Write(make([]byte, 4096))
|
||||||
|
}
|
||||||
|
|
||||||
// Chip IDs. Source:
|
// Chip IDs. Source:
|
||||||
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L22
|
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L22
|
||||||
chip_id := map[string]uint16{
|
chip_id := map[string]uint16{
|
||||||
"esp32": 0x0000,
|
"esp32": 0x0000,
|
||||||
"esp32c3": 0x0005,
|
"esp32c3": 0x0005,
|
||||||
}[format]
|
}[chip]
|
||||||
|
|
||||||
// Image header.
|
// Image header.
|
||||||
switch format {
|
switch chip {
|
||||||
case "esp32", "esp32c3":
|
case "esp32", "esp32c3":
|
||||||
// Header format:
|
// Header format:
|
||||||
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
|
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
|
||||||
@@ -155,12 +172,22 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
|||||||
outf.Write(make([]byte, 15-outf.Len()%16))
|
outf.Write(make([]byte, 15-outf.Len()%16))
|
||||||
outf.WriteByte(checksum)
|
outf.WriteByte(checksum)
|
||||||
|
|
||||||
if format != "esp8266" {
|
if chip != "esp8266" {
|
||||||
// SHA256 hash (to protect against image corruption, not for security).
|
// SHA256 hash (to protect against image corruption, not for security).
|
||||||
hash := sha256.Sum256(outf.Bytes())
|
hash := sha256.Sum256(outf.Bytes())
|
||||||
outf.Write(hash[:])
|
outf.Write(hash[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QEMU (or more precisely, qemu-system-xtensa from Espressif) expects the
|
||||||
|
// image to be a certain size.
|
||||||
|
if makeImage {
|
||||||
|
// Use a default image size of 4MB.
|
||||||
|
grow := 4096*1024 - outf.Len()
|
||||||
|
if grow > 0 {
|
||||||
|
outf.Write(make([]byte, grow))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Write the image to the output file.
|
// Write the image to the output file.
|
||||||
return ioutil.WriteFile(outfile, outf.Bytes(), 0666)
|
return ioutil.WriteFile(outfile, outf.Bytes(), 0666)
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-5
@@ -24,8 +24,8 @@ type Library struct {
|
|||||||
// cflags returns the C flags specific to this library
|
// cflags returns the C flags specific to this library
|
||||||
cflags func(target, headerPath string) []string
|
cflags func(target, headerPath string) []string
|
||||||
|
|
||||||
// The source directory, relative to TINYGOROOT.
|
// The source directory.
|
||||||
sourceDir string
|
sourceDir func() string
|
||||||
|
|
||||||
// The source files, relative to sourceDir.
|
// The source files, relative to sourceDir.
|
||||||
librarySources func(target string) []string
|
librarySources func(target string) []string
|
||||||
@@ -148,12 +148,21 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
// However, ARM has not done this.
|
// However, ARM has not done this.
|
||||||
if strings.HasPrefix(target, "i386") || strings.HasPrefix(target, "x86_64") {
|
if strings.HasPrefix(target, "i386") || strings.HasPrefix(target, "x86_64") {
|
||||||
args = append(args, "-march="+cpu)
|
args = append(args, "-march="+cpu)
|
||||||
|
} else if strings.HasPrefix(target, "avr") {
|
||||||
|
args = append(args, "-mmcu="+cpu)
|
||||||
} else {
|
} else {
|
||||||
args = append(args, "-mcpu="+cpu)
|
args = append(args, "-mcpu="+cpu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
||||||
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables")
|
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(target, "avr") {
|
||||||
|
// AVR defaults to C float and double both being 32-bit. This deviates
|
||||||
|
// from what most code (and certainly compiler-rt) expects. So we need
|
||||||
|
// to force the compiler to use 64-bit floating point numbers for
|
||||||
|
// double.
|
||||||
|
args = append(args, "-mdouble=64")
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(target, "riscv32-") {
|
if strings.HasPrefix(target, "riscv32-") {
|
||||||
args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
|
args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
|
||||||
@@ -161,6 +170,12 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
if strings.HasPrefix(target, "riscv64-") {
|
if strings.HasPrefix(target, "riscv64-") {
|
||||||
args = append(args, "-march=rv64gc", "-mabi=lp64")
|
args = append(args, "-march=rv64gc", "-mabi=lp64")
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(target, "xtensa") {
|
||||||
|
// Hack to work around an issue in the Xtensa port:
|
||||||
|
// https://github.com/espressif/llvm-project/issues/52
|
||||||
|
// Hopefully this will be fixed soon (LLVM 14).
|
||||||
|
args = append(args, "-D__ELF__")
|
||||||
|
}
|
||||||
|
|
||||||
var once sync.Once
|
var once sync.Once
|
||||||
|
|
||||||
@@ -195,6 +210,8 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sourceDir := l.sourceDir()
|
||||||
|
|
||||||
// Create jobs to compile all sources. These jobs are depended upon by the
|
// Create jobs to compile all sources. These jobs are depended upon by the
|
||||||
// archive job above, so must be run first.
|
// archive job above, so must be run first.
|
||||||
for _, path := range l.librarySources(target) {
|
for _, path := range l.librarySources(target) {
|
||||||
@@ -203,7 +220,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
for strings.HasPrefix(cleanpath, "../") {
|
for strings.HasPrefix(cleanpath, "../") {
|
||||||
cleanpath = cleanpath[3:]
|
cleanpath = cleanpath[3:]
|
||||||
}
|
}
|
||||||
srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, path)
|
srcpath := filepath.Join(sourceDir, path)
|
||||||
objpath := filepath.Join(dir, cleanpath+".o")
|
objpath := filepath.Join(dir, cleanpath+".o")
|
||||||
os.MkdirAll(filepath.Dir(objpath), 0o777)
|
os.MkdirAll(filepath.Dir(objpath), 0o777)
|
||||||
objs = append(objs, objpath)
|
objs = append(objs, objpath)
|
||||||
@@ -227,7 +244,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
// (It could be done in parallel with creating the ar file, but it probably
|
// (It could be done in parallel with creating the ar file, but it probably
|
||||||
// won't make much of a difference in speed).
|
// won't make much of a difference in speed).
|
||||||
if l.crt1Source != "" {
|
if l.crt1Source != "" {
|
||||||
srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, l.crt1Source)
|
srcpath := filepath.Join(sourceDir, l.crt1Source)
|
||||||
job.dependencies = append(job.dependencies, &compileJob{
|
job.dependencies = append(job.dependencies, &compileJob{
|
||||||
description: "compile " + srcpath,
|
description: "compile " + srcpath,
|
||||||
run: func(*compileJob) error {
|
run: func(*compileJob) error {
|
||||||
|
|||||||
+4
-4
@@ -8,22 +8,22 @@ extern "C" {
|
|||||||
|
|
||||||
bool tinygo_link_elf(int argc, char **argv) {
|
bool tinygo_link_elf(int argc, char **argv) {
|
||||||
std::vector<const char*> args(argv, argv + argc);
|
std::vector<const char*> args(argv, argv + argc);
|
||||||
return lld::elf::link(args, false, llvm::outs(), llvm::errs());
|
return lld::elf::link(args, llvm::outs(), llvm::errs(), false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tinygo_link_macho(int argc, char **argv) {
|
bool tinygo_link_macho(int argc, char **argv) {
|
||||||
std::vector<const char*> args(argv, argv + argc);
|
std::vector<const char*> args(argv, argv + argc);
|
||||||
return lld::macho::link(args, false, llvm::outs(), llvm::errs());
|
return lld::macho::link(args, llvm::outs(), llvm::errs(), false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tinygo_link_mingw(int argc, char **argv) {
|
bool tinygo_link_mingw(int argc, char **argv) {
|
||||||
std::vector<const char*> args(argv, argv + argc);
|
std::vector<const char*> args(argv, argv + argc);
|
||||||
return lld::mingw::link(args, false, llvm::outs(), llvm::errs());
|
return lld::mingw::link(args, llvm::outs(), llvm::errs(), false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
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, llvm::outs(), llvm::errs(), false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // external "C"
|
} // external "C"
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ var MinGW = Library{
|
|||||||
_, err = io.Copy(outf, inf)
|
_, err = io.Copy(outf, inf)
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
|
sourceDir: func() string { return "" }, // unused
|
||||||
cflags: func(target, headerPath string) []string {
|
cflags: func(target, headerPath string) []string {
|
||||||
// No flags necessary because there are no files to compile.
|
// No flags necessary because there are no files to compile.
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+1
-1
@@ -104,7 +104,7 @@ var Musl = Library{
|
|||||||
"-fno-stack-protector",
|
"-fno-stack-protector",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
sourceDir: "lib/musl/src",
|
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") },
|
||||||
librarySources: func(target string) []string {
|
librarySources: func(target string) []string {
|
||||||
arch := compileopts.MuslArchitecture(target)
|
arch := compileopts.MuslArchitecture(target)
|
||||||
globs := []string{
|
globs := []string{
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ var Picolibc = Library{
|
|||||||
"-I" + headerPath,
|
"-I" + headerPath,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
sourceDir: "lib/picolibc/newlib/libc",
|
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc") },
|
||||||
librarySources: func(target string) []string {
|
librarySources: func(target string) []string {
|
||||||
return picolibcSources
|
return picolibcSources
|
||||||
},
|
},
|
||||||
|
|||||||
+54
-1
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"debug/dwarf"
|
"debug/dwarf"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
|
"debug/macho"
|
||||||
"debug/pe"
|
"debug/pe"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -116,7 +117,7 @@ var (
|
|||||||
// alloc: heap allocations during init interpretation
|
// alloc: heap allocations during init interpretation
|
||||||
// pack: data created when storing a constant in an interface for example
|
// pack: data created when storing a constant in an interface for example
|
||||||
// string: buffer behind strings
|
// string: buffer behind strings
|
||||||
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|pack|string)(\.[0-9]+)?$`)
|
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|embedfsfiles|embedfsslice|embedslice|pack|string)(\.[0-9]+)?$`)
|
||||||
|
|
||||||
// Reflect sidetables. Created by the reflect lowering pass.
|
// Reflect sidetables. Created by the reflect lowering pass.
|
||||||
// See src/reflect/sidetables.go.
|
// See src/reflect/sidetables.go.
|
||||||
@@ -368,6 +369,58 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if file, err := macho.NewFile(f); err == nil {
|
||||||
|
// TODO: read DWARF information. On MacOS, DWARF debug information isn't
|
||||||
|
// stored in the executable but stays in the object files. The
|
||||||
|
// executable does however contain the object file paths that contain
|
||||||
|
// debug information.
|
||||||
|
|
||||||
|
// Read segments, for use while reading through sections.
|
||||||
|
segments := map[string]*macho.Segment{}
|
||||||
|
for _, load := range file.Loads {
|
||||||
|
switch load := load.(type) {
|
||||||
|
case *macho.Segment:
|
||||||
|
segments[load.Name] = load
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read MachO sections.
|
||||||
|
for _, section := range file.Sections {
|
||||||
|
sectionType := section.Flags & 0xff
|
||||||
|
sectionFlags := section.Flags >> 8
|
||||||
|
segment := segments[section.Seg]
|
||||||
|
// For the constants used here, see:
|
||||||
|
// https://github.com/llvm/llvm-project/blob/release/14.x/llvm/include/llvm/BinaryFormat/MachO.h
|
||||||
|
if sectionFlags&0x800000 != 0 { // S_ATTR_PURE_INSTRUCTIONS
|
||||||
|
// Section containing only instructions.
|
||||||
|
sections = append(sections, memorySection{
|
||||||
|
Address: section.Addr,
|
||||||
|
Size: uint64(section.Size),
|
||||||
|
Type: memoryCode,
|
||||||
|
})
|
||||||
|
} else if sectionType == 1 { // S_ZEROFILL
|
||||||
|
// Section filled with zeroes on demand.
|
||||||
|
sections = append(sections, memorySection{
|
||||||
|
Address: section.Addr,
|
||||||
|
Size: uint64(section.Size),
|
||||||
|
Type: memoryBSS,
|
||||||
|
})
|
||||||
|
} else if segment.Maxprot&0b011 == 0b001 { // --r (read-only data)
|
||||||
|
// Protection doesn't allow writes, so mark this section read-only.
|
||||||
|
sections = append(sections, memorySection{
|
||||||
|
Address: section.Addr,
|
||||||
|
Size: uint64(section.Size),
|
||||||
|
Type: memoryROData,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// The rest is assumed to be regular data.
|
||||||
|
sections = append(sections, memorySection{
|
||||||
|
Address: section.Addr,
|
||||||
|
Size: uint64(section.Size),
|
||||||
|
Type: memoryData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if file, err := pe.NewFile(f); err == nil {
|
} else if file, err := pe.NewFile(f); err == nil {
|
||||||
// Read DWARF information. The error is intentionally ignored.
|
// Read DWARF information. The error is intentionally ignored.
|
||||||
data, _ := file.DWARF()
|
data, _ := file.DWARF()
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func RunTool(tool string, args ...string) error {
|
|||||||
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 {
|
switch linker {
|
||||||
case "darwinnew":
|
case "darwin":
|
||||||
ok = C.tinygo_link_macho(C.int(len(args)), (**C.char)(buf))
|
ok = C.tinygo_link_macho(C.int(len(args)), (**C.char)(buf))
|
||||||
case "elf":
|
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))
|
||||||
|
|||||||
+365
-531
File diff suppressed because it is too large
Load Diff
+383
-199
@@ -72,7 +72,11 @@ var diagnosticSeverity = [...]string{
|
|||||||
C.CXDiagnostic_Fatal: "fatal",
|
C.CXDiagnostic_Fatal: "fatal",
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename string) {
|
// Alias so that cgo.go (which doesn't import Clang related stuff and is in
|
||||||
|
// theory decoupled from Clang) can also use this type.
|
||||||
|
type clangCursor = C.GoCXCursor
|
||||||
|
|
||||||
|
func (f *cgoFile) readNames(fragment string, cflags []string, filename string, callback func(map[string]clangCursor)) {
|
||||||
index := C.clang_createIndex(0, 0)
|
index := C.clang_createIndex(0, 0)
|
||||||
defer C.clang_disposeIndex(index)
|
defer C.clang_disposeIndex(index)
|
||||||
|
|
||||||
@@ -119,8 +123,8 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st
|
|||||||
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
||||||
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
|
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
|
||||||
location := C.clang_getDiagnosticLocation(diagnostic)
|
location := C.clang_getDiagnosticLocation(diagnostic)
|
||||||
pos := p.getClangLocationPosition(location, unit)
|
pos := f.getClangLocationPosition(location, unit)
|
||||||
p.addError(pos, severity+": "+spelling)
|
f.addError(pos, severity+": "+spelling)
|
||||||
}
|
}
|
||||||
for i := 0; i < numDiagnostics; i++ {
|
for i := 0; i < numDiagnostics; i++ {
|
||||||
diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
|
diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
|
||||||
@@ -135,7 +139,7 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract information required by CGo.
|
// Extract information required by CGo.
|
||||||
ref := storedRefs.Put(p)
|
ref := storedRefs.Put(f)
|
||||||
defer storedRefs.Remove(ref)
|
defer storedRefs.Remove(ref)
|
||||||
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
||||||
@@ -155,35 +159,64 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st
|
|||||||
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
|
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
|
||||||
|
|
||||||
// Hash the contents if it isn't hashed yet.
|
// Hash the contents if it isn't hashed yet.
|
||||||
if _, ok := p.visitedFiles[path]; !ok {
|
if _, ok := f.visitedFiles[path]; !ok {
|
||||||
// already stored
|
// already stored
|
||||||
sum := sha512.Sum512_224(data)
|
sum := sha512.Sum512_224(data)
|
||||||
p.visitedFiles[path] = sum[:]
|
f.visitedFiles[path] = sum[:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inclusionCallbackRef := storedRefs.Put(inclusionCallback)
|
inclusionCallbackRef := storedRefs.Put(inclusionCallback)
|
||||||
defer storedRefs.Remove(inclusionCallbackRef)
|
defer storedRefs.Remove(inclusionCallbackRef)
|
||||||
C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef))
|
C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef))
|
||||||
|
|
||||||
|
// Do all the C AST operations inside a callback. This makes sure that
|
||||||
|
// libclang related memory is only freed after it is not necessary anymore.
|
||||||
|
callback(f.names)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export tinygo_clang_globals_visitor
|
// Convert the AST node under the given Clang cursor to a Go AST node and return
|
||||||
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
// it.
|
||||||
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
|
func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaboratedTypeInfo) {
|
||||||
kind := C.tinygo_clang_getCursorKind(c)
|
kind := C.tinygo_clang_getCursorKind(c)
|
||||||
pos := p.getCursorPosition(c)
|
pos := f.getCursorPosition(c)
|
||||||
switch kind {
|
switch kind {
|
||||||
case C.CXCursor_FunctionDecl:
|
case C.CXCursor_FunctionDecl:
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
|
||||||
if _, required := p.missingSymbols[name]; !required {
|
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
|
||||||
cursorType := C.tinygo_clang_getCursorType(c)
|
cursorType := C.tinygo_clang_getCursorType(c)
|
||||||
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
||||||
fn := &functionInfo{
|
obj := &ast.Object{
|
||||||
pos: pos,
|
Kind: ast.Fun,
|
||||||
variadic: C.clang_isFunctionTypeVariadic(cursorType) != 0,
|
Name: "C." + name,
|
||||||
|
}
|
||||||
|
args := make([]*ast.Field, numArgs)
|
||||||
|
decl := &ast.FuncDecl{
|
||||||
|
Doc: &ast.CommentGroup{
|
||||||
|
List: []*ast.Comment{
|
||||||
|
{
|
||||||
|
Slash: pos - 1,
|
||||||
|
Text: "//export " + name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: &ast.FuncType{
|
||||||
|
Func: pos,
|
||||||
|
Params: &ast.FieldList{
|
||||||
|
Opening: pos,
|
||||||
|
List: args,
|
||||||
|
Closing: pos,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
|
||||||
|
decl.Doc.List = append(decl.Doc.List, &ast.Comment{
|
||||||
|
Slash: pos - 1,
|
||||||
|
Text: "//go:variadic",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
p.functions[name] = fn
|
|
||||||
for i := 0; i < numArgs; i++ {
|
for i := 0; i < numArgs; i++ {
|
||||||
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
||||||
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
||||||
@@ -191,50 +224,108 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
if argName == "" {
|
if argName == "" {
|
||||||
argName = "$" + strconv.Itoa(i)
|
argName = "$" + strconv.Itoa(i)
|
||||||
}
|
}
|
||||||
fn.args = append(fn.args, paramInfo{
|
args[i] = &ast.Field{
|
||||||
name: argName,
|
Names: []*ast.Ident{
|
||||||
typeExpr: p.makeDecayingASTType(argType, pos),
|
{
|
||||||
})
|
NamePos: pos,
|
||||||
|
Name: argName,
|
||||||
|
Obj: &ast.Object{
|
||||||
|
Kind: ast.Var,
|
||||||
|
Name: argName,
|
||||||
|
Decl: decl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: f.makeDecayingASTType(argType, pos),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
resultType := C.tinygo_clang_getCursorResultType(c)
|
resultType := C.tinygo_clang_getCursorResultType(c)
|
||||||
if resultType.kind != C.CXType_Void {
|
if resultType.kind != C.CXType_Void {
|
||||||
fn.results = &ast.FieldList{
|
decl.Type.Results = &ast.FieldList{
|
||||||
List: []*ast.Field{
|
List: []*ast.Field{
|
||||||
{
|
{
|
||||||
Type: p.makeASTType(resultType, pos),
|
Type: f.makeASTType(resultType, pos),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case C.CXCursor_StructDecl:
|
obj.Decl = decl
|
||||||
typ := C.tinygo_clang_getCursorType(c)
|
return decl, nil
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
case C.CXCursor_StructDecl, C.CXCursor_UnionDecl:
|
||||||
if _, required := p.missingSymbols["struct_"+name]; !required {
|
typ := f.makeASTRecordType(c, pos)
|
||||||
return C.CXChildVisit_Continue
|
typeName := "C." + name
|
||||||
|
typeExpr := typ.typeExpr
|
||||||
|
if typ.unionSize != 0 {
|
||||||
|
// Convert to a single-field struct type.
|
||||||
|
typeExpr = f.makeUnionField(typ)
|
||||||
}
|
}
|
||||||
p.makeASTType(typ, pos)
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Typ,
|
||||||
|
Name: typeName,
|
||||||
|
}
|
||||||
|
typeSpec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: typ.pos,
|
||||||
|
Name: typeName,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: typeExpr,
|
||||||
|
}
|
||||||
|
obj.Decl = typeSpec
|
||||||
|
return typeSpec, typ
|
||||||
case C.CXCursor_TypedefDecl:
|
case C.CXCursor_TypedefDecl:
|
||||||
typedefType := C.tinygo_clang_getCursorType(c)
|
typeName := "C." + name
|
||||||
name := getString(C.clang_getTypedefName(typedefType))
|
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||||
if _, required := p.missingSymbols[name]; !required {
|
obj := &ast.Object{
|
||||||
return C.CXChildVisit_Continue
|
Kind: ast.Typ,
|
||||||
|
Name: typeName,
|
||||||
}
|
}
|
||||||
p.makeASTType(typedefType, pos)
|
typeSpec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: typeName,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: f.makeASTType(underlyingType, pos),
|
||||||
|
}
|
||||||
|
if underlyingType.kind != C.CXType_Enum {
|
||||||
|
typeSpec.Assign = pos
|
||||||
|
}
|
||||||
|
obj.Decl = typeSpec
|
||||||
|
return typeSpec, nil
|
||||||
case C.CXCursor_VarDecl:
|
case C.CXCursor_VarDecl:
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
|
||||||
if _, required := p.missingSymbols[name]; !required {
|
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
|
||||||
cursorType := C.tinygo_clang_getCursorType(c)
|
cursorType := C.tinygo_clang_getCursorType(c)
|
||||||
p.globals[name] = globalInfo{
|
typeExpr := f.makeASTType(cursorType, pos)
|
||||||
typeExpr: p.makeASTType(cursorType, pos),
|
gen := &ast.GenDecl{
|
||||||
pos: pos,
|
TokPos: pos,
|
||||||
|
Tok: token.VAR,
|
||||||
|
Lparen: token.NoPos,
|
||||||
|
Rparen: token.NoPos,
|
||||||
|
Doc: &ast.CommentGroup{
|
||||||
|
List: []*ast.Comment{
|
||||||
|
{
|
||||||
|
Slash: pos - 1,
|
||||||
|
Text: "//go:extern " + name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Var,
|
||||||
|
Name: "C." + name,
|
||||||
|
}
|
||||||
|
valueSpec := &ast.ValueSpec{
|
||||||
|
Names: []*ast.Ident{{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
}},
|
||||||
|
Type: typeExpr,
|
||||||
|
}
|
||||||
|
obj.Decl = valueSpec
|
||||||
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
|
return gen, nil
|
||||||
case C.CXCursor_MacroDefinition:
|
case C.CXCursor_MacroDefinition:
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
|
||||||
if _, required := p.missingSymbols[name]; !required {
|
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
|
||||||
sourceRange := C.tinygo_clang_getCursorExtent(c)
|
sourceRange := C.tinygo_clang_getCursorExtent(c)
|
||||||
start := C.clang_getRangeStart(sourceRange)
|
start := C.clang_getRangeStart(sourceRange)
|
||||||
end := C.clang_getRangeEnd(sourceRange)
|
end := C.clang_getRangeEnd(sourceRange)
|
||||||
@@ -242,17 +333,17 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
var startOffset, endOffset C.unsigned
|
var startOffset, endOffset C.unsigned
|
||||||
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
|
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
|
||||||
if file == nil {
|
if file == nil {
|
||||||
p.addError(pos, "internal error: could not find file where macro is defined")
|
f.addError(pos, "internal error: could not find file where macro is defined")
|
||||||
break
|
return nil, nil
|
||||||
}
|
}
|
||||||
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
|
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
|
||||||
if file != endFile {
|
if file != endFile {
|
||||||
p.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
|
f.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
|
||||||
break
|
return nil, nil
|
||||||
}
|
}
|
||||||
if startOffset > endOffset {
|
if startOffset > endOffset {
|
||||||
p.addError(pos, "internal error: start offset of macro is after end offset")
|
f.addError(pos, "internal error: start offset of macro is after end offset")
|
||||||
break
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// read file contents and extract the relevant byte range
|
// read file contents and extract the relevant byte range
|
||||||
@@ -260,31 +351,94 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
var size C.size_t
|
var size C.size_t
|
||||||
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
||||||
if endOffset >= C.uint(size) {
|
if endOffset >= C.uint(size) {
|
||||||
p.addError(pos, "internal error: end offset of macro lies after end of file")
|
f.addError(pos, "internal error: end offset of macro lies after end of file")
|
||||||
break
|
return nil, nil
|
||||||
}
|
}
|
||||||
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
|
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
|
||||||
if !strings.HasPrefix(source, name) {
|
if !strings.HasPrefix(source, name) {
|
||||||
p.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
|
f.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
|
||||||
break
|
return nil, nil
|
||||||
}
|
}
|
||||||
value := source[len(name):]
|
value := source[len(name):]
|
||||||
// Try to convert this #define into a Go constant expression.
|
// Try to convert this #define into a Go constant expression.
|
||||||
expr, scannerError := parseConst(pos+token.Pos(len(name)), p.fset, value)
|
expr, scannerError := parseConst(pos+token.Pos(len(name)), f.fset, value)
|
||||||
if scannerError != nil {
|
if scannerError != nil {
|
||||||
p.errors = append(p.errors, *scannerError)
|
f.errors = append(f.errors, *scannerError)
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
if expr != nil {
|
|
||||||
// Parsing was successful.
|
gen := &ast.GenDecl{
|
||||||
p.constants[name] = constantInfo{expr, pos}
|
TokPos: token.NoPos,
|
||||||
|
Tok: token.CONST,
|
||||||
|
Lparen: token.NoPos,
|
||||||
|
Rparen: token.NoPos,
|
||||||
}
|
}
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Con,
|
||||||
|
Name: "C." + name,
|
||||||
|
}
|
||||||
|
valueSpec := &ast.ValueSpec{
|
||||||
|
Names: []*ast.Ident{{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
}},
|
||||||
|
Values: []ast.Expr{expr},
|
||||||
|
}
|
||||||
|
obj.Decl = valueSpec
|
||||||
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
|
return gen, nil
|
||||||
case C.CXCursor_EnumDecl:
|
case C.CXCursor_EnumDecl:
|
||||||
// Visit all enums, because the fields may be used even when the enum
|
obj := &ast.Object{
|
||||||
// type itself is not.
|
Kind: ast.Typ,
|
||||||
typ := C.tinygo_clang_getCursorType(c)
|
Name: "C." + name,
|
||||||
p.makeASTType(typ, pos)
|
}
|
||||||
|
underlying := C.tinygo_clang_getEnumDeclIntegerType(c)
|
||||||
|
// TODO: gc's CGo implementation uses types such as `uint32` for enums
|
||||||
|
// instead of types such as C.int, which are used here.
|
||||||
|
typeSpec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Assign: pos,
|
||||||
|
Type: f.makeASTType(underlying, pos),
|
||||||
|
}
|
||||||
|
obj.Decl = typeSpec
|
||||||
|
return typeSpec, nil
|
||||||
|
case C.CXCursor_EnumConstantDecl:
|
||||||
|
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
||||||
|
expr := &ast.BasicLit{
|
||||||
|
ValuePos: pos,
|
||||||
|
Kind: token.INT,
|
||||||
|
Value: strconv.FormatInt(int64(value), 10),
|
||||||
|
}
|
||||||
|
gen := &ast.GenDecl{
|
||||||
|
TokPos: token.NoPos,
|
||||||
|
Tok: token.CONST,
|
||||||
|
Lparen: token.NoPos,
|
||||||
|
Rparen: token.NoPos,
|
||||||
|
}
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Con,
|
||||||
|
Name: "C." + name,
|
||||||
|
}
|
||||||
|
valueSpec := &ast.ValueSpec{
|
||||||
|
Names: []*ast.Ident{{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
}},
|
||||||
|
Values: []ast.Expr{expr},
|
||||||
|
}
|
||||||
|
obj.Decl = valueSpec
|
||||||
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
|
return gen, nil
|
||||||
|
default:
|
||||||
|
f.addError(pos, fmt.Sprintf("internal error: unknown cursor type: %d", kind))
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getString(clangString C.CXString) (s string) {
|
func getString(clangString C.CXString) (s string) {
|
||||||
@@ -294,6 +448,49 @@ func getString(clangString C.CXString) (s string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//export tinygo_clang_globals_visitor
|
||||||
|
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||||
|
f := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoFile)
|
||||||
|
switch C.tinygo_clang_getCursorKind(c) {
|
||||||
|
case C.CXCursor_FunctionDecl:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
f.names[name] = c
|
||||||
|
case C.CXCursor_StructDecl:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
if name != "" {
|
||||||
|
f.names["struct_"+name] = c
|
||||||
|
}
|
||||||
|
case C.CXCursor_UnionDecl:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
if name != "" {
|
||||||
|
f.names["union_"+name] = c
|
||||||
|
}
|
||||||
|
case C.CXCursor_TypedefDecl:
|
||||||
|
typedefType := C.tinygo_clang_getCursorType(c)
|
||||||
|
name := getString(C.clang_getTypedefName(typedefType))
|
||||||
|
f.names[name] = c
|
||||||
|
case C.CXCursor_VarDecl:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
f.names[name] = c
|
||||||
|
case C.CXCursor_MacroDefinition:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
f.names[name] = c
|
||||||
|
case C.CXCursor_EnumDecl:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
if name != "" {
|
||||||
|
// Named enum, which can be referenced from Go using C.enum_foo.
|
||||||
|
f.names["enum_"+name] = c
|
||||||
|
}
|
||||||
|
// The enum fields are in global scope, so recurse to visit them.
|
||||||
|
return C.CXChildVisit_Recurse
|
||||||
|
case C.CXCursor_EnumConstantDecl:
|
||||||
|
// We arrive here because of the "Recurse" above.
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
f.names[name] = c
|
||||||
|
}
|
||||||
|
return C.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
|
||||||
// getCursorPosition returns a usable token.Pos from a libclang cursor.
|
// getCursorPosition returns a usable token.Pos from a libclang cursor.
|
||||||
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
||||||
return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor))
|
return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor))
|
||||||
@@ -391,7 +588,7 @@ func (p *cgoPackage) addErrorAt(position token.Position, msg string) {
|
|||||||
// makeDecayingASTType does the same as makeASTType but takes care of decaying
|
// makeDecayingASTType does the same as makeASTType but takes care of decaying
|
||||||
// types (arrays in function parameters, etc). It is otherwise identical to
|
// types (arrays in function parameters, etc). It is otherwise identical to
|
||||||
// makeASTType.
|
// makeASTType.
|
||||||
func (p *cgoPackage) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
// Strip typedefs, if any.
|
// Strip typedefs, if any.
|
||||||
underlyingType := typ
|
underlyingType := typ
|
||||||
if underlyingType.kind == C.CXType_Typedef {
|
if underlyingType.kind == C.CXType_Typedef {
|
||||||
@@ -417,15 +614,15 @@ func (p *cgoPackage) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
pointeeType := C.clang_getElementType(underlyingType)
|
pointeeType := C.clang_getElementType(underlyingType)
|
||||||
return &ast.StarExpr{
|
return &ast.StarExpr{
|
||||||
Star: pos,
|
Star: pos,
|
||||||
X: p.makeASTType(pointeeType, pos),
|
X: f.makeASTType(pointeeType, pos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return p.makeASTType(typ, pos)
|
return f.makeASTType(typ, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
||||||
// it converts a libclang type to a type in the Go AST.
|
// it converts a libclang type to a type in the Go AST.
|
||||||
func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
var typeName string
|
var typeName string
|
||||||
switch typ.kind {
|
switch typ.kind {
|
||||||
case C.CXType_Char_S, C.CXType_Char_U:
|
case C.CXType_Char_S, C.CXType_Char_U:
|
||||||
@@ -486,7 +683,7 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
}
|
}
|
||||||
return &ast.StarExpr{
|
return &ast.StarExpr{
|
||||||
Star: pos,
|
Star: pos,
|
||||||
X: p.makeASTType(pointeeType, pos),
|
X: f.makeASTType(pointeeType, pos),
|
||||||
}
|
}
|
||||||
case C.CXType_ConstantArray:
|
case C.CXType_ConstantArray:
|
||||||
return &ast.ArrayType{
|
return &ast.ArrayType{
|
||||||
@@ -496,7 +693,7 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
Kind: token.INT,
|
Kind: token.INT,
|
||||||
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
|
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
|
||||||
},
|
},
|
||||||
Elt: p.makeASTType(C.clang_getElementType(typ), pos),
|
Elt: f.makeASTType(C.clang_getElementType(typ), pos),
|
||||||
}
|
}
|
||||||
case C.CXType_FunctionProto:
|
case C.CXType_FunctionProto:
|
||||||
// Be compatible with gc, which uses the *[0]byte type for function
|
// Be compatible with gc, which uses the *[0]byte type for function
|
||||||
@@ -517,71 +714,21 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
}
|
}
|
||||||
case C.CXType_Typedef:
|
case C.CXType_Typedef:
|
||||||
name := getString(C.clang_getTypedefName(typ))
|
name := getString(C.clang_getTypedefName(typ))
|
||||||
if _, ok := p.typedefs[name]; !ok {
|
c := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
p.typedefs[name] = nil // don't recurse
|
|
||||||
c := C.tinygo_clang_getTypeDeclaration(typ)
|
|
||||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
|
||||||
expr := p.makeASTType(underlyingType, pos)
|
|
||||||
if strings.HasPrefix(name, "_Cgo_") {
|
|
||||||
expr := expr.(*ast.Ident)
|
|
||||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
|
||||||
switch expr.Name {
|
|
||||||
case "C.char":
|
|
||||||
if typeSize != 1 {
|
|
||||||
// This happens for some very special purpose architectures
|
|
||||||
// (DSPs etc.) that are not currently targeted.
|
|
||||||
// https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/
|
|
||||||
p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize))
|
|
||||||
}
|
|
||||||
switch underlyingType.kind {
|
|
||||||
case C.CXType_Char_S:
|
|
||||||
expr.Name = "int8"
|
|
||||||
case C.CXType_Char_U:
|
|
||||||
expr.Name = "uint8"
|
|
||||||
}
|
|
||||||
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
|
||||||
switch typeSize {
|
|
||||||
case 1:
|
|
||||||
expr.Name = "int8"
|
|
||||||
case 2:
|
|
||||||
expr.Name = "int16"
|
|
||||||
case 4:
|
|
||||||
expr.Name = "int32"
|
|
||||||
case 8:
|
|
||||||
expr.Name = "int64"
|
|
||||||
}
|
|
||||||
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
|
||||||
switch typeSize {
|
|
||||||
case 1:
|
|
||||||
expr.Name = "uint8"
|
|
||||||
case 2:
|
|
||||||
expr.Name = "uint16"
|
|
||||||
case 4:
|
|
||||||
expr.Name = "uint32"
|
|
||||||
case 8:
|
|
||||||
expr.Name = "uint64"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.typedefs[name] = &typedefInfo{
|
|
||||||
typeExpr: expr,
|
|
||||||
pos: pos,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &ast.Ident{
|
return &ast.Ident{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: "C." + name,
|
Name: f.getASTDeclName(name, c, false),
|
||||||
}
|
}
|
||||||
case C.CXType_Elaborated:
|
case C.CXType_Elaborated:
|
||||||
underlying := C.clang_Type_getNamedType(typ)
|
underlying := C.clang_Type_getNamedType(typ)
|
||||||
switch underlying.kind {
|
switch underlying.kind {
|
||||||
case C.CXType_Record:
|
case C.CXType_Record:
|
||||||
return p.makeASTType(underlying, pos)
|
return f.makeASTType(underlying, pos)
|
||||||
case C.CXType_Enum:
|
case C.CXType_Enum:
|
||||||
return p.makeASTType(underlying, pos)
|
return f.makeASTType(underlying, pos)
|
||||||
default:
|
default:
|
||||||
typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind))
|
typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind))
|
||||||
p.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
||||||
typeName = "<unknown>"
|
typeName = "<unknown>"
|
||||||
}
|
}
|
||||||
case C.CXType_Record:
|
case C.CXType_Record:
|
||||||
@@ -599,63 +746,46 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
}
|
}
|
||||||
if name == "" {
|
if name == "" {
|
||||||
// Anonymous record, probably inside a typedef.
|
// Anonymous record, probably inside a typedef.
|
||||||
typeInfo := p.makeASTRecordType(cursor, pos)
|
clangLocation := C.tinygo_clang_getCursorLocation(cursor)
|
||||||
if typeInfo.bitfields != nil || typeInfo.unionSize != 0 {
|
var file C.CXFile
|
||||||
// This record is a union or is a struct with bitfields, so we
|
var line C.unsigned
|
||||||
// have to declare it as a named type (for getters/setters to
|
var column C.unsigned
|
||||||
// work).
|
C.clang_getFileLocation(clangLocation, &file, &line, &column, nil)
|
||||||
p.anonStructNum++
|
location := token.Position{
|
||||||
cgoName := cgoRecordPrefix + strconv.Itoa(p.anonStructNum)
|
Filename: getString(C.clang_getFileName(file)),
|
||||||
p.elaboratedTypes[cgoName] = typeInfo
|
Line: int(line),
|
||||||
return &ast.Ident{
|
Column: int(column),
|
||||||
NamePos: pos,
|
|
||||||
Name: "C." + cgoName,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return typeInfo.typeExpr
|
if location.Filename == "" || location.Line == 0 {
|
||||||
|
// Not sure when this would happen, but protect from it anyway.
|
||||||
|
f.addError(pos, "could not find file/line information")
|
||||||
|
}
|
||||||
|
name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location)
|
||||||
} else {
|
} else {
|
||||||
cgoName := cgoRecordPrefix + name
|
name = cgoRecordPrefix + name
|
||||||
if _, ok := p.elaboratedTypes[cgoName]; !ok {
|
}
|
||||||
p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
return &ast.Ident{
|
||||||
p.elaboratedTypes[cgoName] = p.makeASTRecordType(cursor, pos)
|
NamePos: pos,
|
||||||
}
|
Name: f.getASTDeclName(name, cursor, false),
|
||||||
return &ast.Ident{
|
|
||||||
NamePos: pos,
|
|
||||||
Name: "C." + cgoName,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case C.CXType_Enum:
|
case C.CXType_Enum:
|
||||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||||
underlying := C.tinygo_clang_getEnumDeclIntegerType(cursor)
|
|
||||||
if name == "" {
|
if name == "" {
|
||||||
// anonymous enum
|
name = f.getUnnamedDeclName("_Ctype_enum___", cursor)
|
||||||
ref := storedRefs.Put(p)
|
|
||||||
defer storedRefs.Remove(ref)
|
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
|
|
||||||
return p.makeASTType(underlying, pos)
|
|
||||||
} else {
|
} else {
|
||||||
// named enum
|
name = "enum_" + name
|
||||||
if _, ok := p.enums[name]; !ok {
|
}
|
||||||
ref := storedRefs.Put(p)
|
return &ast.Ident{
|
||||||
defer storedRefs.Remove(ref)
|
NamePos: pos,
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
|
Name: f.getASTDeclName(name, cursor, false),
|
||||||
p.enums[name] = enumInfo{
|
|
||||||
typeExpr: p.makeASTType(underlying, pos),
|
|
||||||
pos: pos,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &ast.Ident{
|
|
||||||
NamePos: pos,
|
|
||||||
Name: "C.enum_" + name,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if typeName == "" {
|
if typeName == "" {
|
||||||
// Report this as an error.
|
// Report this as an error.
|
||||||
typeSpelling := getString(C.clang_getTypeSpelling(typ))
|
typeSpelling := getString(C.clang_getTypeSpelling(typ))
|
||||||
typeKindSpelling := getString(C.clang_getTypeKindSpelling(typ.kind))
|
typeKindSpelling := getString(C.clang_getTypeKindSpelling(typ.kind))
|
||||||
p.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling))
|
f.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling))
|
||||||
typeName = "C.<unknown>"
|
typeName = "C.<unknown>"
|
||||||
}
|
}
|
||||||
return &ast.Ident{
|
return &ast.Ident{
|
||||||
@@ -664,9 +794,80 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getIntegerType returns an AST node that defines types such as C.int.
|
||||||
|
func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSpec {
|
||||||
|
pos := p.getCursorPosition(cursor)
|
||||||
|
|
||||||
|
// Find a Go type that matches the size and signedness of the given C type.
|
||||||
|
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(cursor)
|
||||||
|
var goName string
|
||||||
|
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||||
|
switch name {
|
||||||
|
case "C.char":
|
||||||
|
if typeSize != 1 {
|
||||||
|
// This happens for some very special purpose architectures
|
||||||
|
// (DSPs etc.) that are not currently targeted.
|
||||||
|
// https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/
|
||||||
|
p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize))
|
||||||
|
}
|
||||||
|
switch underlyingType.kind {
|
||||||
|
case C.CXType_Char_S:
|
||||||
|
goName = "int8"
|
||||||
|
case C.CXType_Char_U:
|
||||||
|
goName = "uint8"
|
||||||
|
}
|
||||||
|
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
||||||
|
switch typeSize {
|
||||||
|
case 1:
|
||||||
|
goName = "int8"
|
||||||
|
case 2:
|
||||||
|
goName = "int16"
|
||||||
|
case 4:
|
||||||
|
goName = "int32"
|
||||||
|
case 8:
|
||||||
|
goName = "int64"
|
||||||
|
}
|
||||||
|
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
||||||
|
switch typeSize {
|
||||||
|
case 1:
|
||||||
|
goName = "uint8"
|
||||||
|
case 2:
|
||||||
|
goName = "uint16"
|
||||||
|
case 4:
|
||||||
|
goName = "uint32"
|
||||||
|
case 8:
|
||||||
|
goName = "uint64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if goName == "" { // should not happen
|
||||||
|
p.addError(pos, "internal error: did not find Go type for C type "+name)
|
||||||
|
goName = "int"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct an *ast.TypeSpec for this type.
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Typ,
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
spec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: name,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: goName,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
obj.Decl = spec
|
||||||
|
return spec
|
||||||
|
}
|
||||||
|
|
||||||
// makeASTRecordType parses a C record (struct or union) and translates it into
|
// makeASTRecordType parses a C record (struct or union) and translates it into
|
||||||
// a Go struct type.
|
// a Go struct type.
|
||||||
func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo {
|
func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo {
|
||||||
fieldList := &ast.FieldList{
|
fieldList := &ast.FieldList{
|
||||||
Opening: pos,
|
Opening: pos,
|
||||||
Closing: pos,
|
Closing: pos,
|
||||||
@@ -676,11 +877,11 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
|||||||
bitfieldNum := 0
|
bitfieldNum := 0
|
||||||
ref := storedRefs.Put(struct {
|
ref := storedRefs.Put(struct {
|
||||||
fieldList *ast.FieldList
|
fieldList *ast.FieldList
|
||||||
pkg *cgoPackage
|
file *cgoFile
|
||||||
inBitfield *bool
|
inBitfield *bool
|
||||||
bitfieldNum *int
|
bitfieldNum *int
|
||||||
bitfieldList *[]bitfieldInfo
|
bitfieldList *[]bitfieldInfo
|
||||||
}{fieldList, p, &inBitfield, &bitfieldNum, &bitfieldList})
|
}{fieldList, f, &inBitfield, &bitfieldNum, &bitfieldList})
|
||||||
defer storedRefs.Remove(ref)
|
defer storedRefs.Remove(ref)
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
||||||
renameFieldKeywords(fieldList)
|
renameFieldKeywords(fieldList)
|
||||||
@@ -709,13 +910,13 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
|||||||
}
|
}
|
||||||
if bitfieldList != nil {
|
if bitfieldList != nil {
|
||||||
// This is valid C... but please don't do this.
|
// This is valid C... but please don't do this.
|
||||||
p.addError(pos, "bitfield in a union is not supported")
|
f.addError(pos, "bitfield in a union is not supported")
|
||||||
}
|
}
|
||||||
typ := C.tinygo_clang_getCursorType(cursor)
|
typ := C.tinygo_clang_getCursorType(cursor)
|
||||||
alignInBytes := int64(C.clang_Type_getAlignOf(typ))
|
alignInBytes := int64(C.clang_Type_getAlignOf(typ))
|
||||||
sizeInBytes := int64(C.clang_Type_getSizeOf(typ))
|
sizeInBytes := int64(C.clang_Type_getSizeOf(typ))
|
||||||
if sizeInBytes == 0 {
|
if sizeInBytes == 0 {
|
||||||
p.addError(pos, "zero-length union is not supported")
|
f.addError(pos, "zero-length union is not supported")
|
||||||
}
|
}
|
||||||
typeInfo.unionSize = sizeInBytes
|
typeInfo.unionSize = sizeInBytes
|
||||||
typeInfo.unionAlign = alignInBytes
|
typeInfo.unionAlign = alignInBytes
|
||||||
@@ -723,7 +924,7 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
|||||||
default:
|
default:
|
||||||
cursorKind := C.tinygo_clang_getCursorKind(cursor)
|
cursorKind := C.tinygo_clang_getCursorKind(cursor)
|
||||||
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
||||||
p.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling))
|
f.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling))
|
||||||
return &elaboratedTypeInfo{
|
return &elaboratedTypeInfo{
|
||||||
typeExpr: &ast.StructType{
|
typeExpr: &ast.StructType{
|
||||||
Struct: pos,
|
Struct: pos,
|
||||||
@@ -737,17 +938,17 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
|||||||
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||||
passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct {
|
passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct {
|
||||||
fieldList *ast.FieldList
|
fieldList *ast.FieldList
|
||||||
pkg *cgoPackage
|
file *cgoFile
|
||||||
inBitfield *bool
|
inBitfield *bool
|
||||||
bitfieldNum *int
|
bitfieldNum *int
|
||||||
bitfieldList *[]bitfieldInfo
|
bitfieldList *[]bitfieldInfo
|
||||||
})
|
})
|
||||||
fieldList := passed.fieldList
|
fieldList := passed.fieldList
|
||||||
p := passed.pkg
|
f := passed.file
|
||||||
inBitfield := passed.inBitfield
|
inBitfield := passed.inBitfield
|
||||||
bitfieldNum := passed.bitfieldNum
|
bitfieldNum := passed.bitfieldNum
|
||||||
bitfieldList := passed.bitfieldList
|
bitfieldList := passed.bitfieldList
|
||||||
pos := p.getCursorPosition(c)
|
pos := f.getCursorPosition(c)
|
||||||
switch cursorKind := C.tinygo_clang_getCursorKind(c); cursorKind {
|
switch cursorKind := C.tinygo_clang_getCursorKind(c); cursorKind {
|
||||||
case C.CXCursor_FieldDecl:
|
case C.CXCursor_FieldDecl:
|
||||||
// Expected. This is a regular field.
|
// Expected. This is a regular field.
|
||||||
@@ -756,7 +957,7 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
|||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
default:
|
default:
|
||||||
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
||||||
p.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling))
|
f.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling))
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
@@ -767,14 +968,14 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
|||||||
}
|
}
|
||||||
typ := C.tinygo_clang_getCursorType(c)
|
typ := C.tinygo_clang_getCursorType(c)
|
||||||
field := &ast.Field{
|
field := &ast.Field{
|
||||||
Type: p.makeASTType(typ, p.getCursorPosition(c)),
|
Type: f.makeASTType(typ, f.getCursorPosition(c)),
|
||||||
}
|
}
|
||||||
offsetof := int64(C.clang_Type_getOffsetOf(C.tinygo_clang_getCursorType(parent), C.CString(name)))
|
offsetof := int64(C.clang_Type_getOffsetOf(C.tinygo_clang_getCursorType(parent), C.CString(name)))
|
||||||
alignOf := int64(C.clang_Type_getAlignOf(typ) * 8)
|
alignOf := int64(C.clang_Type_getAlignOf(typ) * 8)
|
||||||
bitfieldOffset := offsetof % alignOf
|
bitfieldOffset := offsetof % alignOf
|
||||||
if bitfieldOffset != 0 {
|
if bitfieldOffset != 0 {
|
||||||
if C.tinygo_clang_Cursor_isBitField(c) != 1 {
|
if C.tinygo_clang_Cursor_isBitField(c) != 1 {
|
||||||
p.addError(pos, "expected a bitfield")
|
f.addError(pos, "expected a bitfield")
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
if !*inBitfield {
|
if !*inBitfield {
|
||||||
@@ -821,23 +1022,6 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
|||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
//export tinygo_clang_enum_visitor
|
|
||||||
func tinygo_clang_enum_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
|
||||||
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
|
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
|
||||||
pos := p.getCursorPosition(c)
|
|
||||||
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
|
||||||
p.constants[name] = constantInfo{
|
|
||||||
expr: &ast.BasicLit{
|
|
||||||
ValuePos: pos,
|
|
||||||
Kind: token.INT,
|
|
||||||
Value: strconv.FormatInt(int64(value), 10),
|
|
||||||
},
|
|
||||||
pos: pos,
|
|
||||||
}
|
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
|
||||||
|
|
||||||
//export tinygo_clang_inclusion_visitor
|
//export tinygo_clang_inclusion_visitor
|
||||||
func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) {
|
func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) {
|
||||||
callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile))
|
callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile))
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
//go:build !byollvm && llvm11
|
|
||||||
// +build !byollvm,llvm11
|
|
||||||
|
|
||||||
package cgo
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux CFLAGS: -I/usr/lib/llvm-11/include
|
|
||||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@11/include
|
|
||||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@11/include
|
|
||||||
#cgo freebsd CFLAGS: -I/usr/local/llvm11/include
|
|
||||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-11/lib -lclang
|
|
||||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@11/lib -lclang -lffi
|
|
||||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@11/lib -lclang -lffi
|
|
||||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm11/lib -lclang
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
//go:build !byollvm && llvm12
|
|
||||||
// +build !byollvm,llvm12
|
|
||||||
|
|
||||||
package cgo
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux CFLAGS: -I/usr/lib/llvm-12/include
|
|
||||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@12/include
|
|
||||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@12/include
|
|
||||||
#cgo freebsd CFLAGS: -I/usr/local/llvm12/include
|
|
||||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-12/lib -lclang
|
|
||||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@12/lib -lclang -lffi
|
|
||||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@12/lib -lclang -lffi
|
|
||||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm12/lib -lclang
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//go:build !byollvm && !llvm11 && !llvm12
|
//go:build !byollvm && !llvm14
|
||||||
// +build !byollvm,!llvm11,!llvm12
|
// +build !byollvm,!llvm14
|
||||||
|
|
||||||
package cgo
|
package cgo
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
//go:build !byollvm && llvm14
|
||||||
|
// +build !byollvm,llvm14
|
||||||
|
|
||||||
|
package cgo
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo linux CFLAGS: -I/usr/lib/llvm-14/include
|
||||||
|
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@14/include
|
||||||
|
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@14/include
|
||||||
|
#cgo freebsd CFLAGS: -I/usr/local/llvm14/include
|
||||||
|
#cgo linux LDFLAGS: -L/usr/lib/llvm-14/lib -lclang
|
||||||
|
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@14/lib -lclang -lffi
|
||||||
|
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@14/lib -lclang -lffi
|
||||||
|
#cgo freebsd LDFLAGS: -L/usr/local/llvm14/lib -lclang
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
Vendored
+13
-20
@@ -24,23 +24,16 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
|||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
type C.int16_t = int16
|
type (
|
||||||
type C.int32_t = int32
|
C.char uint8
|
||||||
type C.int64_t = int64
|
C.schar int8
|
||||||
type C.int8_t = int8
|
C.uchar uint8
|
||||||
type C.uint16_t = uint16
|
C.short int16
|
||||||
type C.uint32_t = uint32
|
C.ushort uint16
|
||||||
type C.uint64_t = uint64
|
C.int int32
|
||||||
type C.uint8_t = uint8
|
C.uint uint32
|
||||||
type C.uintptr_t = uintptr
|
C.long int32
|
||||||
type C.char uint8
|
C.ulong uint32
|
||||||
type C.int int32
|
C.longlong int64
|
||||||
type C.long int32
|
C.ulonglong uint64
|
||||||
type C.longlong int64
|
)
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
|
|||||||
Vendored
+15
-22
@@ -24,26 +24,19 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
|||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
const C.bar = C.foo
|
type (
|
||||||
const C.foo = 3
|
C.char uint8
|
||||||
|
C.schar int8
|
||||||
|
C.uchar uint8
|
||||||
|
C.short int16
|
||||||
|
C.ushort uint16
|
||||||
|
C.int int32
|
||||||
|
C.uint uint32
|
||||||
|
C.long int32
|
||||||
|
C.ulong uint32
|
||||||
|
C.longlong int64
|
||||||
|
C.ulonglong uint64
|
||||||
|
)
|
||||||
|
|
||||||
type C.int16_t = int16
|
const C.foo = 3
|
||||||
type C.int32_t = int32
|
const C.bar = C.foo
|
||||||
type C.int64_t = int64
|
|
||||||
type C.int8_t = int8
|
|
||||||
type C.uint16_t = uint16
|
|
||||||
type C.uint32_t = uint32
|
|
||||||
type C.uint64_t = uint64
|
|
||||||
type C.uint8_t = uint8
|
|
||||||
type C.uintptr_t = uintptr
|
|
||||||
type C.char uint8
|
|
||||||
type C.int int32
|
|
||||||
type C.long int32
|
|
||||||
type C.longlong int64
|
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
|
|||||||
Vendored
+1
-1
@@ -27,7 +27,7 @@ import "C"
|
|||||||
//line errors.go:100
|
//line errors.go:100
|
||||||
var (
|
var (
|
||||||
// constant too large
|
// constant too large
|
||||||
_ C.uint8_t = 2 << 10
|
_ C.char = 2 << 10
|
||||||
|
|
||||||
// z member does not exist
|
// z member does not exist
|
||||||
_ C.point_t = C.point_t{z: 3}
|
_ C.point_t = C.point_t{z: 3}
|
||||||
|
|||||||
Vendored
+18
-24
@@ -6,7 +6,7 @@
|
|||||||
// testdata/errors.go:19:26: unexpected token ), expected end of expression
|
// testdata/errors.go:19:26: unexpected token ), expected end of expression
|
||||||
|
|
||||||
// Type checking errors after CGo processing:
|
// Type checking errors after CGo processing:
|
||||||
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as uint8 value in variable declaration (overflows)
|
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows)
|
||||||
// testdata/errors.go:105: unknown field z in struct literal
|
// testdata/errors.go:105: unknown field z in struct literal
|
||||||
// testdata/errors.go:108: undeclared name: C.SOME_CONST_1
|
// testdata/errors.go:108: undeclared name: C.SOME_CONST_1
|
||||||
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
|
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
|
||||||
@@ -38,29 +38,23 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
|||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
const C.SOME_CONST_3 = 1234
|
type (
|
||||||
|
C.char uint8
|
||||||
type C.int16_t = int16
|
C.schar int8
|
||||||
type C.int32_t = int32
|
C.uchar uint8
|
||||||
type C.int64_t = int64
|
C.short int16
|
||||||
type C.int8_t = int8
|
C.ushort uint16
|
||||||
type C.uint16_t = uint16
|
C.int int32
|
||||||
type C.uint32_t = uint32
|
C.uint uint32
|
||||||
type C.uint64_t = uint64
|
C.long int32
|
||||||
type C.uint8_t = uint8
|
C.ulong uint32
|
||||||
type C.uintptr_t = uintptr
|
C.longlong int64
|
||||||
type C.char uint8
|
C.ulonglong uint64
|
||||||
type C.int int32
|
)
|
||||||
type C.long int32
|
type C._Ctype_struct___0 struct {
|
||||||
type C.longlong int64
|
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
type C.point_t = struct {
|
|
||||||
x C.int
|
x C.int
|
||||||
y C.int
|
y C.int
|
||||||
}
|
}
|
||||||
|
type C.point_t = C._Ctype_struct___0
|
||||||
|
|
||||||
|
const C.SOME_CONST_3 = 1234
|
||||||
|
|||||||
Vendored
+14
-21
@@ -29,26 +29,19 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
|||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
C.char uint8
|
||||||
|
C.schar int8
|
||||||
|
C.uchar uint8
|
||||||
|
C.short int16
|
||||||
|
C.ushort uint16
|
||||||
|
C.int int32
|
||||||
|
C.uint uint32
|
||||||
|
C.long int32
|
||||||
|
C.ulong uint32
|
||||||
|
C.longlong int64
|
||||||
|
C.ulonglong uint64
|
||||||
|
)
|
||||||
|
|
||||||
const C.BAR = 3
|
const C.BAR = 3
|
||||||
const C.FOO_H = 1
|
const C.FOO_H = 1
|
||||||
|
|
||||||
type C.int16_t = int16
|
|
||||||
type C.int32_t = int32
|
|
||||||
type C.int64_t = int64
|
|
||||||
type C.int8_t = int8
|
|
||||||
type C.uint16_t = uint16
|
|
||||||
type C.uint32_t = uint32
|
|
||||||
type C.uint64_t = uint64
|
|
||||||
type C.uint8_t = uint8
|
|
||||||
type C.uintptr_t = uintptr
|
|
||||||
type C.char uint8
|
|
||||||
type C.int int32
|
|
||||||
type C.long int32
|
|
||||||
type C.longlong int64
|
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
|
|||||||
Vendored
+18
-23
@@ -24,41 +24,36 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
|||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
C.char uint8
|
||||||
|
C.schar int8
|
||||||
|
C.uchar uint8
|
||||||
|
C.short int16
|
||||||
|
C.ushort uint16
|
||||||
|
C.int int32
|
||||||
|
C.uint uint32
|
||||||
|
C.long int32
|
||||||
|
C.ulong uint32
|
||||||
|
C.longlong int64
|
||||||
|
C.ulonglong uint64
|
||||||
|
)
|
||||||
|
|
||||||
//export foo
|
//export foo
|
||||||
func C.foo(a C.int, b C.int) C.int
|
func C.foo(a C.int, b C.int) C.int
|
||||||
|
|
||||||
|
var C.foo$funcaddr unsafe.Pointer
|
||||||
|
|
||||||
//export variadic0
|
//export variadic0
|
||||||
//go:variadic
|
//go:variadic
|
||||||
func C.variadic0()
|
func C.variadic0()
|
||||||
|
|
||||||
|
var C.variadic0$funcaddr unsafe.Pointer
|
||||||
|
|
||||||
//export variadic2
|
//export variadic2
|
||||||
//go:variadic
|
//go:variadic
|
||||||
func C.variadic2(x C.int, y C.int)
|
func C.variadic2(x C.int, y C.int)
|
||||||
|
|
||||||
var C.foo$funcaddr unsafe.Pointer
|
|
||||||
var C.variadic0$funcaddr unsafe.Pointer
|
|
||||||
var C.variadic2$funcaddr unsafe.Pointer
|
var C.variadic2$funcaddr unsafe.Pointer
|
||||||
|
|
||||||
//go:extern someValue
|
//go:extern someValue
|
||||||
var C.someValue C.int
|
var C.someValue C.int
|
||||||
|
|
||||||
type C.int16_t = int16
|
|
||||||
type C.int32_t = int32
|
|
||||||
type C.int64_t = int64
|
|
||||||
type C.int8_t = int8
|
|
||||||
type C.uint16_t = uint16
|
|
||||||
type C.uint32_t = uint32
|
|
||||||
type C.uint64_t = uint64
|
|
||||||
type C.uint8_t = uint8
|
|
||||||
type C.uintptr_t = uintptr
|
|
||||||
type C.char uint8
|
|
||||||
type C.int int32
|
|
||||||
type C.long int32
|
|
||||||
type C.longlong int64
|
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
|
|||||||
Vendored
+98
-104
@@ -24,132 +24,126 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
|||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
const C.option2A = 20
|
type (
|
||||||
const C.optionA = 0
|
C.char uint8
|
||||||
const C.optionB = 1
|
C.schar int8
|
||||||
const C.optionC = -5
|
C.uchar uint8
|
||||||
const C.optionD = -4
|
C.short int16
|
||||||
const C.optionE = 10
|
C.ushort uint16
|
||||||
const C.optionF = 11
|
C.int int32
|
||||||
const C.optionG = 12
|
C.uint uint32
|
||||||
const C.unused1 = 5
|
C.long int32
|
||||||
|
C.ulong uint32
|
||||||
type C.int16_t = int16
|
C.longlong int64
|
||||||
type C.int32_t = int32
|
C.ulonglong uint64
|
||||||
type C.int64_t = int64
|
)
|
||||||
type C.int8_t = int8
|
|
||||||
type C.uint16_t = uint16
|
|
||||||
type C.uint32_t = uint32
|
|
||||||
type C.uint64_t = uint64
|
|
||||||
type C.uint8_t = uint8
|
|
||||||
type C.uintptr_t = uintptr
|
|
||||||
type C.char uint8
|
|
||||||
type C.int int32
|
|
||||||
type C.long int32
|
|
||||||
type C.longlong int64
|
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
type C.bitfield_t = C.struct_4
|
|
||||||
type C.myIntArray = [10]C.int
|
|
||||||
type C.myint = C.int
|
type C.myint = C.int
|
||||||
type C.option2_t = C.uint
|
type C._Ctype_struct___0 struct {
|
||||||
type C.option_t = C.enum_option
|
|
||||||
type C.point2d_t = struct {
|
|
||||||
x C.int
|
x C.int
|
||||||
y C.int
|
y C.int
|
||||||
}
|
}
|
||||||
type C.point3d_t = C.struct_point3d
|
type C.point2d_t = C._Ctype_struct___0
|
||||||
type C.struct_nested_t = struct {
|
|
||||||
begin C.point2d_t
|
|
||||||
end C.point2d_t
|
|
||||||
tag C.int
|
|
||||||
|
|
||||||
coord C.union_2
|
|
||||||
}
|
|
||||||
type C.types_t = struct {
|
|
||||||
f float32
|
|
||||||
d float64
|
|
||||||
ptr *C.int
|
|
||||||
}
|
|
||||||
type C.union1_t = struct{ i C.int }
|
|
||||||
type C.union2d_t = C.union_union2d
|
|
||||||
type C.union3_t = C.union_1
|
|
||||||
type C.union_nested_t = C.union_3
|
|
||||||
type C.unionarray_t = struct{ arr [10]C.uchar }
|
|
||||||
|
|
||||||
func (s *C.struct_4) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
|
||||||
func (s *C.struct_4) set_bitfield_a(value C.uchar) {
|
|
||||||
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
|
||||||
}
|
|
||||||
func (s *C.struct_4) bitfield_b() C.uchar {
|
|
||||||
return s.__bitfield_1 >> 5 & 0x1
|
|
||||||
}
|
|
||||||
func (s *C.struct_4) set_bitfield_b(value C.uchar) {
|
|
||||||
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
|
|
||||||
}
|
|
||||||
func (s *C.struct_4) bitfield_c() C.uchar {
|
|
||||||
return s.__bitfield_1 >> 6
|
|
||||||
}
|
|
||||||
func (s *C.struct_4) set_bitfield_c(value C.uchar,
|
|
||||||
|
|
||||||
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
|
||||||
|
|
||||||
type C.struct_4 struct {
|
|
||||||
start C.uchar
|
|
||||||
__bitfield_1 C.uchar
|
|
||||||
|
|
||||||
d C.uchar
|
|
||||||
e C.uchar
|
|
||||||
}
|
|
||||||
type C.struct_point3d struct {
|
type C.struct_point3d struct {
|
||||||
x C.int
|
x C.int
|
||||||
y C.int
|
y C.int
|
||||||
z C.int
|
z C.int
|
||||||
}
|
}
|
||||||
|
type C.point3d_t = C.struct_point3d
|
||||||
type C.struct_type1 struct {
|
type C.struct_type1 struct {
|
||||||
_type C.int
|
_type C.int
|
||||||
__type C.int
|
__type C.int
|
||||||
___type C.int
|
___type C.int
|
||||||
}
|
}
|
||||||
type C.struct_type2 struct{ _type C.int }
|
type C.struct_type2 struct{ _type C.int }
|
||||||
|
type C._Ctype_union___1 struct{ i C.int }
|
||||||
|
type C.union1_t = C._Ctype_union___1
|
||||||
|
type C._Ctype_union___2 struct{ $union uint64 }
|
||||||
|
|
||||||
func (union *C.union_1) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
func (union *C._Ctype_union___2) unionfield_i() *C.int {
|
||||||
func (union *C.union_1) unionfield_d() *float64 { return (*float64)(unsafe.Pointer(&union.$union)) }
|
return (*C.int)(unsafe.Pointer(&union.$union))
|
||||||
func (union *C.union_1) unionfield_s() *C.short { return (*C.short)(unsafe.Pointer(&union.$union)) }
|
|
||||||
|
|
||||||
type C.union_1 struct{ $union uint64 }
|
|
||||||
|
|
||||||
func (union *C.union_2) unionfield_area() *C.point2d_t {
|
|
||||||
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
}
|
||||||
func (union *C.union_2) unionfield_solid() *C.point3d_t {
|
func (union *C._Ctype_union___2) unionfield_d() *float64 {
|
||||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
return (*float64)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
func (union *C._Ctype_union___2) unionfield_s() *C.short {
|
||||||
|
return (*C.short)(unsafe.Pointer(&union.$union))
|
||||||
}
|
}
|
||||||
|
|
||||||
type C.union_2 struct{ $union [3]uint32 }
|
type C.union3_t = C._Ctype_union___2
|
||||||
|
type C.union_union2d struct{ $union [2]uint64 }
|
||||||
func (union *C.union_3) unionfield_point() *C.point3d_t {
|
|
||||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
|
||||||
func (union *C.union_3) unionfield_array() *C.unionarray_t {
|
|
||||||
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
|
||||||
func (union *C.union_3) unionfield_thing() *C.union3_t {
|
|
||||||
return (*C.union3_t)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
|
||||||
|
|
||||||
type C.union_3 struct{ $union [2]uint64 }
|
|
||||||
|
|
||||||
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||||
func (union *C.union_union2d) unionfield_d() *[2]float64 {
|
func (union *C.union_union2d) unionfield_d() *[2]float64 {
|
||||||
return (*[2]float64)(unsafe.Pointer(&union.$union))
|
return (*[2]float64)(unsafe.Pointer(&union.$union))
|
||||||
}
|
}
|
||||||
|
|
||||||
type C.union_union2d struct{ $union [2]uint64 }
|
type C.union2d_t = C.union_union2d
|
||||||
type C.enum_option C.int
|
type C._Ctype_union___3 struct{ arr [10]C.uchar }
|
||||||
type C.enum_unused C.uint
|
type C.unionarray_t = C._Ctype_union___3
|
||||||
|
type C._Ctype_union___5 struct{ $union [3]uint32 }
|
||||||
|
|
||||||
|
func (union *C._Ctype_union___5) unionfield_area() *C.point2d_t {
|
||||||
|
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
func (union *C._Ctype_union___5) unionfield_solid() *C.point3d_t {
|
||||||
|
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
|
||||||
|
type C._Ctype_struct___4 struct {
|
||||||
|
begin C.point2d_t
|
||||||
|
end C.point2d_t
|
||||||
|
tag C.int
|
||||||
|
|
||||||
|
coord C._Ctype_union___5
|
||||||
|
}
|
||||||
|
type C.struct_nested_t = C._Ctype_struct___4
|
||||||
|
type C._Ctype_union___6 struct{ $union [2]uint64 }
|
||||||
|
|
||||||
|
func (union *C._Ctype_union___6) unionfield_point() *C.point3d_t {
|
||||||
|
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
func (union *C._Ctype_union___6) unionfield_array() *C.unionarray_t {
|
||||||
|
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
func (union *C._Ctype_union___6) unionfield_thing() *C.union3_t {
|
||||||
|
return (*C.union3_t)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
|
||||||
|
type C.union_nested_t = C._Ctype_union___6
|
||||||
|
type C.enum_option = C.int
|
||||||
|
type C.option_t = C.enum_option
|
||||||
|
type C._Ctype_enum___7 = C.uint
|
||||||
|
type C.option2_t = C._Ctype_enum___7
|
||||||
|
type C._Ctype_struct___8 struct {
|
||||||
|
f float32
|
||||||
|
d float64
|
||||||
|
ptr *C.int
|
||||||
|
}
|
||||||
|
type C.types_t = C._Ctype_struct___8
|
||||||
|
type C.myIntArray = [10]C.int
|
||||||
|
type C._Ctype_struct___9 struct {
|
||||||
|
start C.uchar
|
||||||
|
__bitfield_1 C.uchar
|
||||||
|
|
||||||
|
d C.uchar
|
||||||
|
e C.uchar
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *C._Ctype_struct___9) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
||||||
|
func (s *C._Ctype_struct___9) set_bitfield_a(value C.uchar) {
|
||||||
|
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
||||||
|
}
|
||||||
|
func (s *C._Ctype_struct___9) bitfield_b() C.uchar {
|
||||||
|
return s.__bitfield_1 >> 5 & 0x1
|
||||||
|
}
|
||||||
|
func (s *C._Ctype_struct___9) set_bitfield_b(value C.uchar) {
|
||||||
|
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
|
||||||
|
}
|
||||||
|
func (s *C._Ctype_struct___9) bitfield_c() C.uchar {
|
||||||
|
return s.__bitfield_1 >> 6
|
||||||
|
}
|
||||||
|
func (s *C._Ctype_struct___9) set_bitfield_c(value C.uchar,
|
||||||
|
|
||||||
|
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
||||||
|
|
||||||
|
type C.bitfield_t = C._Ctype_struct___9
|
||||||
|
|||||||
+45
-15
@@ -10,6 +10,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/shlex"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -191,15 +192,6 @@ func (c *Config) UseThinLTO() bool {
|
|||||||
// through a plugin, but it's too much hassle to set up.
|
// through a plugin, but it's too much hassle to set up.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if len(parts) >= 2 && strings.HasPrefix(parts[2], "macos") {
|
|
||||||
// We use an external linker here at the moment.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if len(parts) >= 2 && parts[2] == "windows" {
|
|
||||||
// Linker error (undefined runtime.trackedGlobalsBitmap) when linking
|
|
||||||
// for Windows. Disable it for now until that's figured out and fixed.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Other architectures support ThinLTO.
|
// Other architectures support ThinLTO.
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -287,6 +279,7 @@ func (c *Config) CFlags() []string {
|
|||||||
path, _ := c.LibcPath("picolibc")
|
path, _ := c.LibcPath("picolibc")
|
||||||
cflags = append(cflags,
|
cflags = append(cflags,
|
||||||
"--sysroot="+path,
|
"--sysroot="+path,
|
||||||
|
"-isystem", filepath.Join(path, "include"), // necessary for Xtensa
|
||||||
"-isystem", filepath.Join(picolibcDir, "include"),
|
"-isystem", filepath.Join(picolibcDir, "include"),
|
||||||
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
|
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
|
||||||
)
|
)
|
||||||
@@ -393,6 +386,13 @@ func (c *Config) BinaryFormat(ext string) string {
|
|||||||
return c.Target.BinaryFormat
|
return c.Target.BinaryFormat
|
||||||
}
|
}
|
||||||
return "bin"
|
return "bin"
|
||||||
|
case ".img":
|
||||||
|
// Image file. Only defined for the ESP32 at the moment, where it is a
|
||||||
|
// full (runnable) image that can be used in the Espressif QEMU fork.
|
||||||
|
if c.Target.BinaryFormat != "" {
|
||||||
|
return c.Target.BinaryFormat + "-img"
|
||||||
|
}
|
||||||
|
return "bin"
|
||||||
case ".hex":
|
case ".hex":
|
||||||
// Similar to bin, but includes the start address and is thus usually a
|
// Similar to bin, but includes the start address and is thus usually a
|
||||||
// better format.
|
// better format.
|
||||||
@@ -493,13 +493,43 @@ func (c *Config) WasmAbi() string {
|
|||||||
return c.Target.WasmAbi
|
return c.Target.WasmAbi
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emulator returns the emulator target config
|
// EmulatorName is a shorthand to get the command for this emulator, something
|
||||||
func (c *Config) Emulator() []string {
|
// like qemu-system-arm or simavr.
|
||||||
var emulator []string
|
func (c *Config) EmulatorName() string {
|
||||||
for _, s := range c.Target.Emulator {
|
parts := strings.SplitN(c.Target.Emulator, " ", 2)
|
||||||
emulator = append(emulator, strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT")))
|
if len(parts) > 1 {
|
||||||
|
return parts[0]
|
||||||
}
|
}
|
||||||
return emulator
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// EmulatorFormat returns the binary format for the emulator and the associated
|
||||||
|
// file extension. An empty string means to pass directly whatever the linker
|
||||||
|
// produces directly without conversion (usually ELF format).
|
||||||
|
func (c *Config) EmulatorFormat() (format, fileExt string) {
|
||||||
|
switch {
|
||||||
|
case strings.Contains(c.Target.Emulator, "{img}"):
|
||||||
|
return "img", ".img"
|
||||||
|
default:
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emulator returns a ready-to-run command to run the given binary in an
|
||||||
|
// emulator. Give it the format (returned by EmulatorFormat()) and the path to
|
||||||
|
// the compiled binary.
|
||||||
|
func (c *Config) Emulator(format, binary string) ([]string, error) {
|
||||||
|
parts, err := shlex.Split(c.Target.Emulator)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse emulator command: %w", err)
|
||||||
|
}
|
||||||
|
var emulator []string
|
||||||
|
for _, s := range parts {
|
||||||
|
s = strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT"))
|
||||||
|
s = strings.ReplaceAll(s, "{"+format+"}", binary)
|
||||||
|
emulator = append(emulator, s)
|
||||||
|
}
|
||||||
|
return emulator, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type TestConfig struct {
|
type TestConfig struct {
|
||||||
|
|||||||
+13
-30
@@ -12,11 +12,9 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
"tinygo.org/x/go-llvm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Target specification for a given target. Used for bare metal targets.
|
// Target specification for a given target. Used for bare metal targets.
|
||||||
@@ -44,8 +42,8 @@ type TargetSpec struct {
|
|||||||
LDFlags []string `json:"ldflags"`
|
LDFlags []string `json:"ldflags"`
|
||||||
LinkerScript string `json:"linkerscript"`
|
LinkerScript string `json:"linkerscript"`
|
||||||
ExtraFiles []string `json:"extra-files"`
|
ExtraFiles []string `json:"extra-files"`
|
||||||
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
|
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
|
||||||
Emulator []string `json:"emulator" override:"copy"` // inherited Emulator must not be append
|
Emulator string `json:"emulator"`
|
||||||
FlashCommand string `json:"flash-command"`
|
FlashCommand string `json:"flash-command"`
|
||||||
GDB []string `json:"gdb"`
|
GDB []string `json:"gdb"`
|
||||||
PortReset string `json:"flash-1200-bps-reset"`
|
PortReset string `json:"flash-1200-bps-reset"`
|
||||||
@@ -59,6 +57,7 @@ type TargetSpec struct {
|
|||||||
OpenOCDTarget string `json:"openocd-target"`
|
OpenOCDTarget string `json:"openocd-target"`
|
||||||
OpenOCDTransport string `json:"openocd-transport"`
|
OpenOCDTransport string `json:"openocd-transport"`
|
||||||
OpenOCDCommands []string `json:"openocd-commands"`
|
OpenOCDCommands []string `json:"openocd-commands"`
|
||||||
|
OpenOCDVerify *bool `json:"openocd-verify"` // enable verify when flashing with openocd
|
||||||
JLinkDevice string `json:"jlink-device"`
|
JLinkDevice string `json:"jlink-device"`
|
||||||
CodeModel string `json:"code-model"`
|
CodeModel string `json:"code-model"`
|
||||||
RelocationModel string `json:"relocation-model"`
|
RelocationModel string `json:"relocation-model"`
|
||||||
@@ -89,19 +88,8 @@ func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
|
|||||||
if !src.IsNil() {
|
if !src.IsNil() {
|
||||||
dst.Set(src)
|
dst.Set(src)
|
||||||
}
|
}
|
||||||
case reflect.Slice: // for slices...
|
case reflect.Slice: // for slices, append the field
|
||||||
if src.Len() > 0 { // ... if not empty ...
|
dst.Set(reflect.AppendSlice(dst, src))
|
||||||
switch tag := field.Tag.Get("override"); tag {
|
|
||||||
case "copy":
|
|
||||||
// copy the field of child to spec
|
|
||||||
dst.Set(src)
|
|
||||||
case "append", "":
|
|
||||||
// or append the field of child to spec
|
|
||||||
dst.Set(reflect.AppendSlice(dst, src))
|
|
||||||
default:
|
|
||||||
panic("override mode must be 'copy' or 'append' (default). I don't know how to '" + tag + "'.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
panic("unknown field type : " + kind.String())
|
panic("unknown field type : " + kind.String())
|
||||||
}
|
}
|
||||||
@@ -258,7 +246,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||||
case "arm":
|
case "arm":
|
||||||
spec.CPU = "generic"
|
spec.CPU = "generic"
|
||||||
spec.CFlags = append(spec.CFlags, "-fno-unwind-tables")
|
spec.CFlags = append(spec.CFlags, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
||||||
switch strings.Split(triple, "-")[0] {
|
switch strings.Split(triple, "-")[0] {
|
||||||
case "armv5":
|
case "armv5":
|
||||||
spec.Features = "+armv5t,+strict-align,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
|
spec.Features = "+armv5t,+strict-align,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
|
||||||
@@ -277,7 +265,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
arch := strings.Split(triple, "-")[0]
|
arch := strings.Split(triple, "-")[0]
|
||||||
platformVersion := strings.TrimPrefix(strings.Split(triple, "-")[2], "macosx")
|
platformVersion := strings.TrimPrefix(strings.Split(triple, "-")[2], "macosx")
|
||||||
spec.LDFlags = append(spec.LDFlags,
|
spec.LDFlags = append(spec.LDFlags,
|
||||||
"-flavor", "darwinnew",
|
"-flavor", "darwin",
|
||||||
"-dead_strip",
|
"-dead_strip",
|
||||||
"-arch", arch,
|
"-arch", arch,
|
||||||
"-platform_version", "macos", platformVersion, platformVersion,
|
"-platform_version", "macos", platformVersion, platformVersion,
|
||||||
@@ -302,13 +290,8 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
"--image-base", "0x400000",
|
"--image-base", "0x400000",
|
||||||
"--gc-sections",
|
"--gc-sections",
|
||||||
"--no-insert-timestamp",
|
"--no-insert-timestamp",
|
||||||
|
"--no-dynamicbase",
|
||||||
)
|
)
|
||||||
llvmMajor, _ := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
|
|
||||||
if llvmMajor >= 12 {
|
|
||||||
// This flag was added in LLVM 12. At the same time, LLVM 12
|
|
||||||
// switched the default from --dynamicbase to --no-dynamicbase.
|
|
||||||
spec.LDFlags = append(spec.LDFlags, "--no-dynamicbase")
|
|
||||||
}
|
|
||||||
} 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
|
||||||
}
|
}
|
||||||
@@ -330,20 +313,20 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
case "386":
|
case "386":
|
||||||
// amd64 can _usually_ run 32-bit programs, so skip the emulator in that case.
|
// amd64 can _usually_ run 32-bit programs, so skip the emulator in that case.
|
||||||
if runtime.GOARCH != "amd64" {
|
if runtime.GOARCH != "amd64" {
|
||||||
spec.Emulator = []string{"qemu-i386"}
|
spec.Emulator = "qemu-i386 {}"
|
||||||
}
|
}
|
||||||
case "amd64":
|
case "amd64":
|
||||||
spec.Emulator = []string{"qemu-x86_64"}
|
spec.Emulator = "qemu-x86_64 {}"
|
||||||
case "arm":
|
case "arm":
|
||||||
spec.Emulator = []string{"qemu-arm"}
|
spec.Emulator = "qemu-arm {}"
|
||||||
case "arm64":
|
case "arm64":
|
||||||
spec.Emulator = []string{"qemu-aarch64"}
|
spec.Emulator = "qemu-aarch64 {}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if goos != runtime.GOOS {
|
if goos != runtime.GOOS {
|
||||||
if goos == "windows" {
|
if goos == "windows" {
|
||||||
spec.Emulator = []string{"wine"}
|
spec.Emulator = "wine {}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &spec, nil
|
return &spec, nil
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ func TestOverrideProperties(t *testing.T) {
|
|||||||
CPU: "baseCpu",
|
CPU: "baseCpu",
|
||||||
CFlags: []string{"-base-foo", "-base-bar"},
|
CFlags: []string{"-base-foo", "-base-bar"},
|
||||||
BuildTags: []string{"bt1", "bt2"},
|
BuildTags: []string{"bt1", "bt2"},
|
||||||
Emulator: []string{"be1", "be2"},
|
|
||||||
DefaultStackSize: 42,
|
DefaultStackSize: 42,
|
||||||
AutoStackSize: &baseAutoStackSize,
|
AutoStackSize: &baseAutoStackSize,
|
||||||
}
|
}
|
||||||
@@ -38,7 +37,6 @@ func TestOverrideProperties(t *testing.T) {
|
|||||||
GOOS: "",
|
GOOS: "",
|
||||||
CPU: "chlidCpu",
|
CPU: "chlidCpu",
|
||||||
CFlags: []string{"-child-foo", "-child-bar"},
|
CFlags: []string{"-child-foo", "-child-bar"},
|
||||||
Emulator: []string{"ce1", "ce2"},
|
|
||||||
AutoStackSize: &childAutoStackSize,
|
AutoStackSize: &childAutoStackSize,
|
||||||
DefaultStackSize: 64,
|
DefaultStackSize: 64,
|
||||||
}
|
}
|
||||||
@@ -57,9 +55,6 @@ func TestOverrideProperties(t *testing.T) {
|
|||||||
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)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(base.Emulator, []string{"ce1", "ce2"}) {
|
|
||||||
t.Errorf("Overriding failed : got %v", base.Emulator)
|
|
||||||
}
|
|
||||||
if *base.AutoStackSize != false {
|
if *base.AutoStackSize != false {
|
||||||
t.Errorf("Overriding failed : got %v", base.AutoStackSize)
|
t.Errorf("Overriding failed : got %v", base.AutoStackSize)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,30 +52,6 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
|||||||
ptr := b.getValue(call.Args[0])
|
ptr := b.getValue(call.Args[0])
|
||||||
old := b.getValue(call.Args[1])
|
old := b.getValue(call.Args[1])
|
||||||
newVal := b.getValue(call.Args[2])
|
newVal := b.getValue(call.Args[2])
|
||||||
if strings.HasSuffix(name, "64") {
|
|
||||||
if strings.HasPrefix(b.Triple, "thumb") {
|
|
||||||
// Work around a bug in LLVM, at least LLVM 11:
|
|
||||||
// https://reviews.llvm.org/D95891
|
|
||||||
// Check for thumbv6m, thumbv7, thumbv7em, and perhaps others.
|
|
||||||
// See also: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
|
|
||||||
compareAndSwap := b.mod.NamedFunction("__sync_val_compare_and_swap_8")
|
|
||||||
if compareAndSwap.IsNil() {
|
|
||||||
// Declare the function if it isn't already declared.
|
|
||||||
i64Type := b.ctx.Int64Type()
|
|
||||||
fnType := llvm.FunctionType(i64Type, []llvm.Type{llvm.PointerType(i64Type, 0), i64Type, i64Type}, false)
|
|
||||||
compareAndSwap = llvm.AddFunction(b.mod, "__sync_val_compare_and_swap_8", fnType)
|
|
||||||
}
|
|
||||||
actualOldValue := b.CreateCall(compareAndSwap, []llvm.Value{ptr, old, newVal}, "")
|
|
||||||
// The __sync_val_compare_and_swap_8 function returns the old
|
|
||||||
// value. However, we shouldn't return the old value, we should
|
|
||||||
// return whether the compare/exchange was successful. This is
|
|
||||||
// easily done by comparing the returned (actual) old value with
|
|
||||||
// the expected old value passed to
|
|
||||||
// __sync_val_compare_and_swap_8.
|
|
||||||
swapped := b.CreateICmp(llvm.IntEQ, old, actualOldValue, "")
|
|
||||||
return swapped, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true)
|
tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||||
swapped := b.CreateExtractValue(tuple, 1, "")
|
swapped := b.CreateExtractValue(tuple, 1, "")
|
||||||
return swapped, true
|
return swapped, true
|
||||||
|
|||||||
+202
-41
@@ -9,6 +9,7 @@ import (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -76,6 +77,7 @@ type compilerContext struct {
|
|||||||
program *ssa.Program
|
program *ssa.Program
|
||||||
diagnostics []error
|
diagnostics []error
|
||||||
astComments map[string]*ast.CommentGroup
|
astComments map[string]*ast.CommentGroup
|
||||||
|
embedGlobals map[string][]*loader.EmbedFile
|
||||||
pkg *types.Package
|
pkg *types.Package
|
||||||
packageDir string // directory for this package
|
packageDir string // directory for this package
|
||||||
runtimePkg *types.Package
|
runtimePkg *types.Package
|
||||||
@@ -250,6 +252,7 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
|
|||||||
func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
||||||
c := newCompilerContext(moduleName, machine, config, dumpSSA)
|
c := newCompilerContext(moduleName, machine, config, dumpSSA)
|
||||||
c.packageDir = pkg.OriginalDir()
|
c.packageDir = pkg.OriginalDir()
|
||||||
|
c.embedGlobals = pkg.EmbedGlobals
|
||||||
c.pkg = pkg.Pkg
|
c.pkg = pkg.Pkg
|
||||||
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
|
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
|
||||||
c.program = ssaPkg.Prog
|
c.program = ssaPkg.Prog
|
||||||
@@ -301,6 +304,7 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
c.dibuilder.Finalize()
|
c.dibuilder.Finalize()
|
||||||
|
c.dibuilder.Destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.mod, c.diagnostics
|
return c.mod, c.diagnostics
|
||||||
@@ -814,7 +818,9 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
|||||||
// Global variable.
|
// Global variable.
|
||||||
info := c.getGlobalInfo(member)
|
info := c.getGlobalInfo(member)
|
||||||
global := c.getGlobal(member)
|
global := c.getGlobal(member)
|
||||||
if !info.extern {
|
if files, ok := c.embedGlobals[member.Name()]; ok {
|
||||||
|
c.createEmbedGlobal(member, global, files)
|
||||||
|
} else if !info.extern {
|
||||||
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
|
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
if info.section != "" {
|
if info.section != "" {
|
||||||
@@ -848,6 +854,150 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createEmbedGlobal creates an initializer for a //go:embed global variable.
|
||||||
|
func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Value, files []*loader.EmbedFile) {
|
||||||
|
switch typ := member.Type().(*types.Pointer).Elem().Underlying().(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
// String type.
|
||||||
|
if typ.Kind() != types.String {
|
||||||
|
// This is checked at the AST level, so should be unreachable.
|
||||||
|
panic("expected a string type")
|
||||||
|
}
|
||||||
|
if len(files) != 1 {
|
||||||
|
c.addError(member.Pos(), fmt.Sprintf("//go:embed for a string should be given exactly one file, got %d", len(files)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strObj := c.getEmbedFileString(files[0])
|
||||||
|
global.SetInitializer(strObj)
|
||||||
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
|
|
||||||
|
case *types.Slice:
|
||||||
|
if typ.Elem().Underlying().(*types.Basic).Kind() != types.Byte {
|
||||||
|
// This is checked at the AST level, so should be unreachable.
|
||||||
|
panic("expected a byte slice")
|
||||||
|
}
|
||||||
|
if len(files) != 1 {
|
||||||
|
c.addError(member.Pos(), fmt.Sprintf("//go:embed for a string should be given exactly one file, got %d", len(files)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file := files[0]
|
||||||
|
bufferValue := c.ctx.ConstString(string(file.Data), false)
|
||||||
|
bufferGlobal := llvm.AddGlobal(c.mod, bufferValue.Type(), c.pkg.Path()+"$embedslice")
|
||||||
|
bufferGlobal.SetInitializer(bufferValue)
|
||||||
|
bufferGlobal.SetLinkage(llvm.InternalLinkage)
|
||||||
|
bufferGlobal.SetAlignment(1)
|
||||||
|
slicePtr := llvm.ConstInBoundsGEP(bufferGlobal, []llvm.Value{
|
||||||
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
|
})
|
||||||
|
sliceLen := llvm.ConstInt(c.uintptrType, file.Size, false)
|
||||||
|
sliceObj := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
|
||||||
|
global.SetInitializer(sliceObj)
|
||||||
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
|
|
||||||
|
case *types.Struct:
|
||||||
|
// Assume this is an embed.FS struct:
|
||||||
|
// https://cs.opensource.google/go/go/+/refs/tags/go1.18.2:src/embed/embed.go;l=148
|
||||||
|
// It looks like this:
|
||||||
|
// type FS struct {
|
||||||
|
// files *file
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Make a slice of the files, as they will appear in the binary. They
|
||||||
|
// are sorted in a special way to allow for binary searches, see
|
||||||
|
// src/embed/embed.go for details.
|
||||||
|
dirset := map[string]struct{}{}
|
||||||
|
var allFiles []*loader.EmbedFile
|
||||||
|
for _, file := range files {
|
||||||
|
allFiles = append(allFiles, file)
|
||||||
|
dirname := file.Name
|
||||||
|
for {
|
||||||
|
dirname, _ = path.Split(path.Clean(dirname))
|
||||||
|
if dirname == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if _, ok := dirset[dirname]; ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
dirset[dirname] = struct{}{}
|
||||||
|
allFiles = append(allFiles, &loader.EmbedFile{
|
||||||
|
Name: dirname,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Slice(allFiles, func(i, j int) bool {
|
||||||
|
dir1, name1 := path.Split(path.Clean(allFiles[i].Name))
|
||||||
|
dir2, name2 := path.Split(path.Clean(allFiles[j].Name))
|
||||||
|
if dir1 != dir2 {
|
||||||
|
return dir1 < dir2
|
||||||
|
}
|
||||||
|
return name1 < name2
|
||||||
|
})
|
||||||
|
|
||||||
|
// Make the backing array for the []files slice. This is a LLVM global.
|
||||||
|
embedFileStructType := c.getLLVMType(typ.Field(0).Type().(*types.Pointer).Elem().(*types.Slice).Elem())
|
||||||
|
var fileStructs []llvm.Value
|
||||||
|
for _, file := range allFiles {
|
||||||
|
fileStruct := llvm.ConstNull(embedFileStructType)
|
||||||
|
name := c.createConst(ssa.NewConst(constant.MakeString(file.Name), types.Typ[types.String]))
|
||||||
|
fileStruct = llvm.ConstInsertValue(fileStruct, name, []uint32{0}) // "name" field
|
||||||
|
if file.Hash != "" {
|
||||||
|
data := c.getEmbedFileString(file)
|
||||||
|
fileStruct = llvm.ConstInsertValue(fileStruct, data, []uint32{1}) // "data" field
|
||||||
|
}
|
||||||
|
fileStructs = append(fileStructs, fileStruct)
|
||||||
|
}
|
||||||
|
sliceDataInitializer := llvm.ConstArray(embedFileStructType, fileStructs)
|
||||||
|
sliceDataGlobal := llvm.AddGlobal(c.mod, sliceDataInitializer.Type(), c.pkg.Path()+"$embedfsfiles")
|
||||||
|
sliceDataGlobal.SetInitializer(sliceDataInitializer)
|
||||||
|
sliceDataGlobal.SetLinkage(llvm.InternalLinkage)
|
||||||
|
sliceDataGlobal.SetGlobalConstant(true)
|
||||||
|
sliceDataGlobal.SetUnnamedAddr(true)
|
||||||
|
sliceDataGlobal.SetAlignment(c.targetData.ABITypeAlignment(sliceDataInitializer.Type()))
|
||||||
|
|
||||||
|
// Create the slice object itself.
|
||||||
|
// Because embed.FS refers to it as *[]embed.file instead of a plain
|
||||||
|
// []embed.file, we have to store this as a global.
|
||||||
|
slicePtr := llvm.ConstInBoundsGEP(sliceDataGlobal, []llvm.Value{
|
||||||
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
|
})
|
||||||
|
sliceLen := llvm.ConstInt(c.uintptrType, uint64(len(fileStructs)), false)
|
||||||
|
sliceInitializer := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
|
||||||
|
sliceGlobal := llvm.AddGlobal(c.mod, sliceInitializer.Type(), c.pkg.Path()+"$embedfsslice")
|
||||||
|
sliceGlobal.SetInitializer(sliceInitializer)
|
||||||
|
sliceGlobal.SetLinkage(llvm.InternalLinkage)
|
||||||
|
sliceGlobal.SetGlobalConstant(true)
|
||||||
|
sliceGlobal.SetUnnamedAddr(true)
|
||||||
|
sliceGlobal.SetAlignment(c.targetData.ABITypeAlignment(sliceInitializer.Type()))
|
||||||
|
|
||||||
|
// Define the embed.FS struct. It has only one field: the files (as a
|
||||||
|
// *[]embed.file).
|
||||||
|
globalInitializer := llvm.ConstNull(c.getLLVMType(member.Type().(*types.Pointer).Elem()))
|
||||||
|
globalInitializer = llvm.ConstInsertValue(globalInitializer, sliceGlobal, []uint32{0})
|
||||||
|
global.SetInitializer(globalInitializer)
|
||||||
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
|
global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getEmbedFileString returns the (constant) string object with the contents of
|
||||||
|
// the given file. This is a llvm.Value of a regular Go string.
|
||||||
|
func (c *compilerContext) getEmbedFileString(file *loader.EmbedFile) llvm.Value {
|
||||||
|
dataGlobalName := "embed/file_" + file.Hash
|
||||||
|
dataGlobal := c.mod.NamedGlobal(dataGlobalName)
|
||||||
|
if dataGlobal.IsNil() {
|
||||||
|
dataGlobalType := llvm.ArrayType(c.ctx.Int8Type(), int(file.Size))
|
||||||
|
dataGlobal = llvm.AddGlobal(c.mod, dataGlobalType, dataGlobalName)
|
||||||
|
}
|
||||||
|
strPtr := llvm.ConstInBoundsGEP(dataGlobal, []llvm.Value{
|
||||||
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
|
})
|
||||||
|
strLen := llvm.ConstInt(c.uintptrType, file.Size, false)
|
||||||
|
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||||
|
}
|
||||||
|
|
||||||
// createFunction builds the LLVM IR implementation for this function. The
|
// createFunction builds the LLVM IR implementation for this function. The
|
||||||
// function must not yet be defined, otherwise this function will create a
|
// function must not yet be defined, otherwise this function will create a
|
||||||
// diagnostic.
|
// diagnostic.
|
||||||
@@ -957,10 +1107,10 @@ func (b *builder) createFunction() {
|
|||||||
} else {
|
} else {
|
||||||
fieldOffsets := b.expandFormalParamOffsets(llvmType)
|
fieldOffsets := b.expandFormalParamOffsets(llvmType)
|
||||||
for i, field := range fields {
|
for i, field := range fields {
|
||||||
expr := b.dibuilder.CreateExpression([]int64{
|
expr := b.dibuilder.CreateExpression([]uint64{
|
||||||
0x1000, // DW_OP_LLVM_fragment
|
0x1000, // DW_OP_LLVM_fragment
|
||||||
int64(fieldOffsets[i]) * 8, // offset in bits
|
fieldOffsets[i] * 8, // offset in bits
|
||||||
int64(b.targetData.TypeAllocSize(field.Type())) * 8, // size in bits
|
b.targetData.TypeAllocSize(field.Type()) * 8, // size in bits
|
||||||
})
|
})
|
||||||
b.dibuilder.InsertValueAtEnd(field, dbgParam, expr, loc, entryBlock)
|
b.dibuilder.InsertValueAtEnd(field, dbgParam, expr, loc, entryBlock)
|
||||||
}
|
}
|
||||||
@@ -1440,7 +1590,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
return b.emitSV64Call(instr.Args)
|
return b.emitSV64Call(instr.Args)
|
||||||
case strings.HasPrefix(name, "(device/riscv.CSR)."):
|
case strings.HasPrefix(name, "(device/riscv.CSR)."):
|
||||||
return b.emitCSROperation(instr)
|
return b.emitCSROperation(instr)
|
||||||
case strings.HasPrefix(name, "syscall.Syscall"):
|
case strings.HasPrefix(name, "syscall.Syscall") || strings.HasPrefix(name, "syscall.RawSyscall"):
|
||||||
return b.createSyscall(instr)
|
return b.createSyscall(instr)
|
||||||
case strings.HasPrefix(name, "syscall.rawSyscallNoError"):
|
case strings.HasPrefix(name, "syscall.rawSyscallNoError"):
|
||||||
return b.createRawSyscallNoError(instr)
|
return b.createRawSyscallNoError(instr)
|
||||||
@@ -1469,6 +1619,14 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
case *ssa.Function:
|
case *ssa.Function:
|
||||||
// Regular function call. No context is necessary.
|
// Regular function call. No context is necessary.
|
||||||
context = llvm.Undef(b.i8ptrType)
|
context = llvm.Undef(b.i8ptrType)
|
||||||
|
if info.variadic && len(fn.Params) == 0 {
|
||||||
|
// This matches Clang, see: https://godbolt.org/z/Gqv49xKMq
|
||||||
|
// Eventually we might be able to eliminate this special case
|
||||||
|
// entirely. For details, see:
|
||||||
|
// https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c/60521
|
||||||
|
fnType := llvm.FunctionType(callee.Type().ElementType().ReturnType(), nil, false)
|
||||||
|
callee = llvm.ConstBitCast(callee, llvm.PointerType(fnType, b.funcPtrAddrSpace))
|
||||||
|
}
|
||||||
case *ssa.MakeClosure:
|
case *ssa.MakeClosure:
|
||||||
// A call on a func value, but the callee is trivial to find. For
|
// A call on a func value, but the callee is trivial to find. For
|
||||||
// example: immediately applied functions.
|
// example: immediately applied functions.
|
||||||
@@ -1554,7 +1712,11 @@ func (c *compilerContext) maxSliceSize(elementType llvm.Type) uint64 {
|
|||||||
// Determine the maximum allowed size for a slice. The biggest possible
|
// Determine the maximum allowed size for a slice. The biggest possible
|
||||||
// pointer (starting from 0) would be maxPointerValue*sizeof(elementType) so
|
// pointer (starting from 0) would be maxPointerValue*sizeof(elementType) so
|
||||||
// divide by the element type to get the real maximum size.
|
// divide by the element type to get the real maximum size.
|
||||||
maxSize := maxPointerValue / c.targetData.TypeAllocSize(elementType)
|
elementSize := c.targetData.TypeAllocSize(elementType)
|
||||||
|
if elementSize == 0 {
|
||||||
|
elementSize = 1
|
||||||
|
}
|
||||||
|
maxSize := maxPointerValue / elementSize
|
||||||
|
|
||||||
// len(slice) is an int. Make sure the length remains small enough to fit in
|
// len(slice) is an int. Make sure the length remains small enough to fit in
|
||||||
// an int.
|
// an int.
|
||||||
@@ -2457,42 +2619,41 @@ func (b *builder) createBinOp(op token.Token, typ, ytyp types.Type, x, y llvm.Va
|
|||||||
}
|
}
|
||||||
|
|
||||||
// createConst creates a LLVM constant value from a Go constant.
|
// createConst creates a LLVM constant value from a Go constant.
|
||||||
func (b *builder) createConst(expr *ssa.Const) llvm.Value {
|
func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
|
||||||
switch typ := expr.Type().Underlying().(type) {
|
switch typ := expr.Type().Underlying().(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
llvmType := b.getLLVMType(typ)
|
llvmType := c.getLLVMType(typ)
|
||||||
if typ.Info()&types.IsBoolean != 0 {
|
if typ.Info()&types.IsBoolean != 0 {
|
||||||
b := constant.BoolVal(expr.Value)
|
|
||||||
n := uint64(0)
|
n := uint64(0)
|
||||||
if b {
|
if constant.BoolVal(expr.Value) {
|
||||||
n = 1
|
n = 1
|
||||||
}
|
}
|
||||||
return llvm.ConstInt(llvmType, n, false)
|
return llvm.ConstInt(llvmType, n, false)
|
||||||
} else if typ.Info()&types.IsString != 0 {
|
} else if typ.Info()&types.IsString != 0 {
|
||||||
str := constant.StringVal(expr.Value)
|
str := constant.StringVal(expr.Value)
|
||||||
strLen := llvm.ConstInt(b.uintptrType, uint64(len(str)), false)
|
strLen := llvm.ConstInt(c.uintptrType, uint64(len(str)), false)
|
||||||
var strPtr llvm.Value
|
var strPtr llvm.Value
|
||||||
if str != "" {
|
if str != "" {
|
||||||
objname := b.pkg.Path() + "$string"
|
objname := c.pkg.Path() + "$string"
|
||||||
global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
|
global := llvm.AddGlobal(c.mod, llvm.ArrayType(c.ctx.Int8Type(), len(str)), objname)
|
||||||
global.SetInitializer(b.ctx.ConstString(str, false))
|
global.SetInitializer(c.ctx.ConstString(str, false))
|
||||||
global.SetLinkage(llvm.InternalLinkage)
|
global.SetLinkage(llvm.InternalLinkage)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetUnnamedAddr(true)
|
global.SetUnnamedAddr(true)
|
||||||
global.SetAlignment(1)
|
global.SetAlignment(1)
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||||
strPtr = b.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
|
strPtr = llvm.ConstInBoundsGEP(global, []llvm.Value{zero, zero})
|
||||||
} else {
|
} else {
|
||||||
strPtr = llvm.ConstNull(b.i8ptrType)
|
strPtr = llvm.ConstNull(c.i8ptrType)
|
||||||
}
|
}
|
||||||
strObj := llvm.ConstNamedStruct(b.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||||
return strObj
|
return strObj
|
||||||
} else if typ.Kind() == types.UnsafePointer {
|
} else if typ.Kind() == types.UnsafePointer {
|
||||||
if !expr.IsNil() {
|
if !expr.IsNil() {
|
||||||
value, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
value, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
||||||
return llvm.ConstIntToPtr(llvm.ConstInt(b.uintptrType, value, false), b.i8ptrType)
|
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, value, false), c.i8ptrType)
|
||||||
}
|
}
|
||||||
return llvm.ConstNull(b.i8ptrType)
|
return llvm.ConstNull(c.i8ptrType)
|
||||||
} else if typ.Info()&types.IsUnsigned != 0 {
|
} else if typ.Info()&types.IsUnsigned != 0 {
|
||||||
n, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
n, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
||||||
return llvm.ConstInt(llvmType, n, false)
|
return llvm.ConstInt(llvmType, n, false)
|
||||||
@@ -2503,18 +2664,18 @@ func (b *builder) createConst(expr *ssa.Const) llvm.Value {
|
|||||||
n, _ := constant.Float64Val(expr.Value)
|
n, _ := constant.Float64Val(expr.Value)
|
||||||
return llvm.ConstFloat(llvmType, n)
|
return llvm.ConstFloat(llvmType, n)
|
||||||
} else if typ.Kind() == types.Complex64 {
|
} else if typ.Kind() == types.Complex64 {
|
||||||
r := b.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
|
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
|
||||||
i := b.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
|
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
|
||||||
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.FloatType(), b.ctx.FloatType()}, false))
|
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
|
||||||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
|
||||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
|
||||||
return cplx
|
return cplx
|
||||||
} else if typ.Kind() == types.Complex128 {
|
} else if typ.Kind() == types.Complex128 {
|
||||||
r := b.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
|
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
|
||||||
i := b.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
|
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
|
||||||
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false))
|
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
|
||||||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
|
||||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
|
||||||
return cplx
|
return cplx
|
||||||
} else {
|
} else {
|
||||||
panic("unknown constant of basic type: " + expr.String())
|
panic("unknown constant of basic type: " + expr.String())
|
||||||
@@ -2523,35 +2684,35 @@ func (b *builder) createConst(expr *ssa.Const) llvm.Value {
|
|||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil chan constant")
|
panic("expected nil chan constant")
|
||||||
}
|
}
|
||||||
return llvm.ConstNull(b.getLLVMType(expr.Type()))
|
return llvm.ConstNull(c.getLLVMType(expr.Type()))
|
||||||
case *types.Signature:
|
case *types.Signature:
|
||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil signature constant")
|
panic("expected nil signature constant")
|
||||||
}
|
}
|
||||||
return llvm.ConstNull(b.getLLVMType(expr.Type()))
|
return llvm.ConstNull(c.getLLVMType(expr.Type()))
|
||||||
case *types.Interface:
|
case *types.Interface:
|
||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil interface constant")
|
panic("expected nil interface constant")
|
||||||
}
|
}
|
||||||
// Create a generic nil interface with no dynamic type (typecode=0).
|
// Create a generic nil interface with no dynamic type (typecode=0).
|
||||||
fields := []llvm.Value{
|
fields := []llvm.Value{
|
||||||
llvm.ConstInt(b.uintptrType, 0, false),
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
llvm.ConstPointerNull(b.i8ptrType),
|
llvm.ConstPointerNull(c.i8ptrType),
|
||||||
}
|
}
|
||||||
return llvm.ConstNamedStruct(b.getLLVMRuntimeType("_interface"), fields)
|
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields)
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil pointer constant")
|
panic("expected nil pointer constant")
|
||||||
}
|
}
|
||||||
return llvm.ConstPointerNull(b.getLLVMType(typ))
|
return llvm.ConstPointerNull(c.getLLVMType(typ))
|
||||||
case *types.Slice:
|
case *types.Slice:
|
||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil slice constant")
|
panic("expected nil slice constant")
|
||||||
}
|
}
|
||||||
elemType := b.getLLVMType(typ.Elem())
|
elemType := c.getLLVMType(typ.Elem())
|
||||||
llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0))
|
llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0))
|
||||||
llvmLen := llvm.ConstInt(b.uintptrType, 0, false)
|
llvmLen := llvm.ConstInt(c.uintptrType, 0, false)
|
||||||
slice := b.ctx.ConstStruct([]llvm.Value{
|
slice := c.ctx.ConstStruct([]llvm.Value{
|
||||||
llvmPtr, // backing array
|
llvmPtr, // backing array
|
||||||
llvmLen, // len
|
llvmLen, // len
|
||||||
llvmLen, // cap
|
llvmLen, // cap
|
||||||
@@ -2562,7 +2723,7 @@ func (b *builder) createConst(expr *ssa.Const) llvm.Value {
|
|||||||
// I believe this is not allowed by the Go spec.
|
// I believe this is not allowed by the Go spec.
|
||||||
panic("non-nil map constant")
|
panic("non-nil map constant")
|
||||||
}
|
}
|
||||||
llvmType := b.getLLVMType(typ)
|
llvmType := c.getLLVMType(typ)
|
||||||
return llvm.ConstNull(llvmType)
|
return llvm.ConstNull(llvmType)
|
||||||
default:
|
default:
|
||||||
panic("unknown constant: " + expr.String())
|
panic("unknown constant: " + expr.String())
|
||||||
|
|||||||
@@ -28,18 +28,11 @@ type testCase struct {
|
|||||||
func TestCompiler(t *testing.T) {
|
func TestCompiler(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Check LLVM version.
|
// Determine LLVM version.
|
||||||
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
|
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("could not parse LLVM version:", llvm.Version)
|
t.Fatal("could not parse LLVM version:", llvm.Version)
|
||||||
}
|
}
|
||||||
if llvmMajor < 11 {
|
|
||||||
// It is likely this version needs to be bumped in the future.
|
|
||||||
// The goal is to at least test the LLVM version that's used by default
|
|
||||||
// in TinyGo and (if possible without too many workarounds) also some
|
|
||||||
// previous versions.
|
|
||||||
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine Go minor version (e.g. 16 in go1.16.3).
|
// Determine Go minor version (e.g. 16 in go1.16.3).
|
||||||
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||||
@@ -113,6 +106,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("failed to create target machine:", err)
|
t.Fatal("failed to create target machine:", err)
|
||||||
}
|
}
|
||||||
|
defer machine.Dispose()
|
||||||
|
|
||||||
// Load entire program AST into memory.
|
// Load entire program AST into memory.
|
||||||
lprogram, err := loader.Load(config, []string{"./testdata/" + tc.file}, config.ClangHeaders, types.Config{
|
lprogram, err := loader.Load(config, []string{"./testdata/" + tc.file}, config.ClangHeaders, types.Config{
|
||||||
@@ -221,14 +215,9 @@ func filterIrrelevantIRLines(lines []string) []string {
|
|||||||
if strings.HasPrefix(line, "source_filename = ") {
|
if strings.HasPrefix(line, "source_filename = ") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if llvmVersion < 12 && strings.HasPrefix(line, "attributes ") {
|
if llvmVersion < 14 && strings.HasPrefix(line, "target datalayout = ") {
|
||||||
// Ignore attribute groups. These may change between LLVM versions.
|
|
||||||
// Right now test outputs are for LLVM 12 and higher.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if llvmVersion < 13 && strings.HasPrefix(line, "target datalayout = ") {
|
|
||||||
// The datalayout string may vary betewen LLVM versions.
|
// The datalayout string may vary betewen LLVM versions.
|
||||||
// Right now test outputs are for LLVM 13 and higher.
|
// Right now test outputs are for LLVM 14 and higher.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
out = append(out, line)
|
out = append(out, line)
|
||||||
|
|||||||
@@ -98,7 +98,10 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
case llvm.IntegerTypeKind:
|
case llvm.IntegerTypeKind:
|
||||||
constraints = append(constraints, "r")
|
constraints = append(constraints, "r")
|
||||||
case llvm.PointerTypeKind:
|
case llvm.PointerTypeKind:
|
||||||
constraints = append(constraints, "*m")
|
// Memory references require a type in LLVM 14, probably as a
|
||||||
|
// preparation for opaque pointers.
|
||||||
|
err = b.makeError(instr.Pos(), "support for pointer operands was dropped in TinyGo 0.23")
|
||||||
|
return s
|
||||||
default:
|
default:
|
||||||
err = b.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name)
|
err = b.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name)
|
||||||
return s
|
return s
|
||||||
|
|||||||
+79
-45
@@ -152,6 +152,27 @@ func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
|
|||||||
return structGlobal
|
return structGlobal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var basicTypes = [...]string{
|
||||||
|
types.Bool: "bool",
|
||||||
|
types.Int: "int",
|
||||||
|
types.Int8: "int8",
|
||||||
|
types.Int16: "int16",
|
||||||
|
types.Int32: "int32",
|
||||||
|
types.Int64: "int64",
|
||||||
|
types.Uint: "uint",
|
||||||
|
types.Uint8: "uint8",
|
||||||
|
types.Uint16: "uint16",
|
||||||
|
types.Uint32: "uint32",
|
||||||
|
types.Uint64: "uint64",
|
||||||
|
types.Uintptr: "uintptr",
|
||||||
|
types.Float32: "float32",
|
||||||
|
types.Float64: "float64",
|
||||||
|
types.Complex64: "complex64",
|
||||||
|
types.Complex128: "complex128",
|
||||||
|
types.String: "string",
|
||||||
|
types.UnsafePointer: "unsafe.Pointer",
|
||||||
|
}
|
||||||
|
|
||||||
// getTypeCodeName returns a name for this type that can be used in the
|
// getTypeCodeName returns a name for this type that can be used in the
|
||||||
// interface lowering pass to assign type codes as expected by the reflect
|
// interface lowering pass to assign type codes as expected by the reflect
|
||||||
// package. See getTypeCodeNum.
|
// package. See getTypeCodeNum.
|
||||||
@@ -162,48 +183,7 @@ func getTypeCodeName(t types.Type) string {
|
|||||||
case *types.Array:
|
case *types.Array:
|
||||||
return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem())
|
return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem())
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
var kind string
|
return "basic:" + basicTypes[t.Kind()]
|
||||||
switch t.Kind() {
|
|
||||||
case types.Bool:
|
|
||||||
kind = "bool"
|
|
||||||
case types.Int:
|
|
||||||
kind = "int"
|
|
||||||
case types.Int8:
|
|
||||||
kind = "int8"
|
|
||||||
case types.Int16:
|
|
||||||
kind = "int16"
|
|
||||||
case types.Int32:
|
|
||||||
kind = "int32"
|
|
||||||
case types.Int64:
|
|
||||||
kind = "int64"
|
|
||||||
case types.Uint:
|
|
||||||
kind = "uint"
|
|
||||||
case types.Uint8:
|
|
||||||
kind = "uint8"
|
|
||||||
case types.Uint16:
|
|
||||||
kind = "uint16"
|
|
||||||
case types.Uint32:
|
|
||||||
kind = "uint32"
|
|
||||||
case types.Uint64:
|
|
||||||
kind = "uint64"
|
|
||||||
case types.Uintptr:
|
|
||||||
kind = "uintptr"
|
|
||||||
case types.Float32:
|
|
||||||
kind = "float32"
|
|
||||||
case types.Float64:
|
|
||||||
kind = "float64"
|
|
||||||
case types.Complex64:
|
|
||||||
kind = "complex64"
|
|
||||||
case types.Complex128:
|
|
||||||
kind = "complex128"
|
|
||||||
case types.String:
|
|
||||||
kind = "string"
|
|
||||||
case types.UnsafePointer:
|
|
||||||
kind = "unsafeptr"
|
|
||||||
default:
|
|
||||||
panic("unknown basic type: " + t.Name())
|
|
||||||
}
|
|
||||||
return "basic:" + kind
|
|
||||||
case *types.Chan:
|
case *types.Chan:
|
||||||
return "chan:" + getTypeCodeName(t.Elem())
|
return "chan:" + getTypeCodeName(t.Elem())
|
||||||
case *types.Interface:
|
case *types.Interface:
|
||||||
@@ -591,23 +571,77 @@ func signature(sig *types.Signature) string {
|
|||||||
if i > 0 {
|
if i > 0 {
|
||||||
s += ", "
|
s += ", "
|
||||||
}
|
}
|
||||||
s += sig.Params().At(i).Type().String()
|
s += typestring(sig.Params().At(i).Type())
|
||||||
}
|
}
|
||||||
s += ")"
|
s += ")"
|
||||||
}
|
}
|
||||||
if sig.Results().Len() == 0 {
|
if sig.Results().Len() == 0 {
|
||||||
// keep as-is
|
// keep as-is
|
||||||
} else if sig.Results().Len() == 1 {
|
} else if sig.Results().Len() == 1 {
|
||||||
s += " " + sig.Results().At(0).Type().String()
|
s += " " + typestring(sig.Results().At(0).Type())
|
||||||
} else {
|
} else {
|
||||||
s += " ("
|
s += " ("
|
||||||
for i := 0; i < sig.Results().Len(); i++ {
|
for i := 0; i < sig.Results().Len(); i++ {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
s += ", "
|
s += ", "
|
||||||
}
|
}
|
||||||
s += sig.Results().At(i).Type().String()
|
s += typestring(sig.Results().At(i).Type())
|
||||||
}
|
}
|
||||||
s += ")"
|
s += ")"
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// typestring returns a stable (human-readable) type string for the given type
|
||||||
|
// that can be used for interface equality checks. It is almost (but not
|
||||||
|
// exactly) the same as calling t.String(). The main difference is some
|
||||||
|
// normalization around `byte` vs `uint8` for example.
|
||||||
|
func typestring(t types.Type) string {
|
||||||
|
// See: https://github.com/golang/go/blob/master/src/go/types/typestring.go
|
||||||
|
switch t := t.(type) {
|
||||||
|
case *types.Array:
|
||||||
|
return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem())
|
||||||
|
case *types.Basic:
|
||||||
|
return basicTypes[t.Kind()]
|
||||||
|
case *types.Chan:
|
||||||
|
switch t.Dir() {
|
||||||
|
case types.SendRecv:
|
||||||
|
return "chan (" + typestring(t.Elem()) + ")"
|
||||||
|
case types.SendOnly:
|
||||||
|
return "chan<- (" + typestring(t.Elem()) + ")"
|
||||||
|
case types.RecvOnly:
|
||||||
|
return "<-chan (" + typestring(t.Elem()) + ")"
|
||||||
|
default:
|
||||||
|
panic("unknown channel direction")
|
||||||
|
}
|
||||||
|
case *types.Interface:
|
||||||
|
methods := make([]string, t.NumMethods())
|
||||||
|
for i := range methods {
|
||||||
|
method := t.Method(i)
|
||||||
|
methods[i] = method.Name() + signature(method.Type().(*types.Signature))
|
||||||
|
}
|
||||||
|
return "interface{" + strings.Join(methods, ";") + "}"
|
||||||
|
case *types.Map:
|
||||||
|
return "map[" + typestring(t.Key()) + "]" + typestring(t.Elem())
|
||||||
|
case *types.Named:
|
||||||
|
return t.String()
|
||||||
|
case *types.Pointer:
|
||||||
|
return "*" + typestring(t.Elem())
|
||||||
|
case *types.Signature:
|
||||||
|
return "func" + signature(t)
|
||||||
|
case *types.Slice:
|
||||||
|
return "[]" + typestring(t.Elem())
|
||||||
|
case *types.Struct:
|
||||||
|
fields := make([]string, t.NumFields())
|
||||||
|
for i := range fields {
|
||||||
|
field := t.Field(i)
|
||||||
|
fields[i] = field.Name() + " " + typestring(field.Type())
|
||||||
|
if tag := t.Tag(i); tag != "" {
|
||||||
|
fields[i] += " " + strconv.Quote(tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "struct{" + strings.Join(fields, ";") + "}"
|
||||||
|
default:
|
||||||
|
panic("unknown type: " + t.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func (c *checker) checkType(t llvm.Type, checked map[llvm.Type]struct{}, special
|
|||||||
return fmt.Errorf("type %q uses global context", t.String())
|
return fmt.Errorf("type %q uses global context", t.String())
|
||||||
default:
|
default:
|
||||||
// we used some other context by accident
|
// we used some other context by accident
|
||||||
return fmt.Errorf("type %q uses context %v instead of the main context %v", t.Context(), c.ctx)
|
return fmt.Errorf("type %q uses context %v instead of the main context %v", t.String(), t.Context(), c.ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this is a composite type, check the components of the type
|
// if this is a composite type, check the components of the type
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ func CreateEntryBlockAlloca(builder llvm.Builder, t llvm.Type, name string) llvm
|
|||||||
func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
|
func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||||
|
defer targetData.Dispose()
|
||||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||||
alloca = CreateEntryBlockAlloca(builder, t, name)
|
alloca = CreateEntryBlockAlloca(builder, t, name)
|
||||||
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
||||||
@@ -46,6 +47,7 @@ func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, n
|
|||||||
func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, inst llvm.Value, name string) llvm.Value {
|
func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, inst llvm.Value, name string) llvm.Value {
|
||||||
ctx := mod.Context()
|
ctx := mod.Context()
|
||||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||||
|
defer targetData.Dispose()
|
||||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||||
|
|
||||||
alloca := CreateEntryBlockAlloca(builder, t, name)
|
alloca := CreateEntryBlockAlloca(builder, t, name)
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ import (
|
|||||||
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needsStackObjects bool, values []llvm.Value) llvm.Value {
|
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needsStackObjects bool, values []llvm.Value) llvm.Value {
|
||||||
ctx := mod.Context()
|
ctx := mod.Context()
|
||||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||||
|
defer targetData.Dispose()
|
||||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||||
uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
|
uintptrType := ctx.IntType(targetData.PointerSize() * 8)
|
||||||
|
|
||||||
valueTypes := make([]llvm.Type, len(values))
|
valueTypes := make([]llvm.Type, len(values))
|
||||||
for i, value := range values {
|
for i, value := range values {
|
||||||
@@ -127,8 +128,9 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needs
|
|||||||
func EmitPointerUnpack(builder llvm.Builder, mod llvm.Module, ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
|
func EmitPointerUnpack(builder llvm.Builder, mod llvm.Module, ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
|
||||||
ctx := mod.Context()
|
ctx := mod.Context()
|
||||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||||
|
defer targetData.Dispose()
|
||||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||||
uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
|
uintptrType := ctx.IntType(targetData.PointerSize() * 8)
|
||||||
|
|
||||||
packedType := ctx.StructType(valueTypes, false)
|
packedType := ctx.StructType(valueTypes, false)
|
||||||
|
|
||||||
|
|||||||
+13
-1
@@ -10,6 +10,13 @@ import (
|
|||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// constants for hashmap algorithms; must match src/runtime/hashmap.go
|
||||||
|
const (
|
||||||
|
hashmapAlgorithmBinary = iota
|
||||||
|
hashmapAlgorithmString
|
||||||
|
hashmapAlgorithmInterface
|
||||||
|
)
|
||||||
|
|
||||||
// createMakeMap creates a new map object (runtime.hashmap) by allocating and
|
// createMakeMap creates a new map object (runtime.hashmap) by allocating and
|
||||||
// initializing an appropriately sized object.
|
// initializing an appropriately sized object.
|
||||||
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
||||||
@@ -17,22 +24,27 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
|||||||
keyType := mapType.Key().Underlying()
|
keyType := mapType.Key().Underlying()
|
||||||
llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
|
llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
|
||||||
var llvmKeyType llvm.Type
|
var llvmKeyType llvm.Type
|
||||||
|
var alg uint64 // must match values in src/runtime/hashmap.go
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// String keys.
|
// String keys.
|
||||||
llvmKeyType = b.getLLVMType(keyType)
|
llvmKeyType = b.getLLVMType(keyType)
|
||||||
|
alg = hashmapAlgorithmString
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
// Trivially comparable keys.
|
// Trivially comparable keys.
|
||||||
llvmKeyType = b.getLLVMType(keyType)
|
llvmKeyType = b.getLLVMType(keyType)
|
||||||
|
alg = hashmapAlgorithmBinary
|
||||||
} else {
|
} else {
|
||||||
// All other keys. Implemented as map[interface{}]valueType for ease of
|
// All other keys. Implemented as map[interface{}]valueType for ease of
|
||||||
// implementation.
|
// implementation.
|
||||||
llvmKeyType = b.getLLVMRuntimeType("_interface")
|
llvmKeyType = b.getLLVMRuntimeType("_interface")
|
||||||
|
alg = hashmapAlgorithmInterface
|
||||||
}
|
}
|
||||||
keySize := b.targetData.TypeAllocSize(llvmKeyType)
|
keySize := b.targetData.TypeAllocSize(llvmKeyType)
|
||||||
valueSize := b.targetData.TypeAllocSize(llvmValueType)
|
valueSize := b.targetData.TypeAllocSize(llvmValueType)
|
||||||
llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false)
|
llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false)
|
||||||
llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), valueSize, false)
|
llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), valueSize, false)
|
||||||
sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
|
sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
|
||||||
|
algEnum := llvm.ConstInt(b.ctx.Int8Type(), alg, false)
|
||||||
if expr.Reserve != nil {
|
if expr.Reserve != nil {
|
||||||
sizeHint = b.getValue(expr.Reserve)
|
sizeHint = b.getValue(expr.Reserve)
|
||||||
var err error
|
var err error
|
||||||
@@ -41,7 +53,7 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
|||||||
return llvm.Value{}, err
|
return llvm.Value{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "")
|
hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint, algEnum}, "")
|
||||||
return hashmap, nil
|
return hashmap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,11 @@ func (s *stdSizes) Alignof(T types.Type) int64 {
|
|||||||
if t.Info()&types.IsString != 0 {
|
if t.Info()&types.IsString != 0 {
|
||||||
return s.PtrSize
|
return s.PtrSize
|
||||||
}
|
}
|
||||||
|
case *types.Signature:
|
||||||
|
// Even though functions in tinygo are 2 pointers, they are not 2 pointer aligned
|
||||||
|
return s.PtrSize
|
||||||
}
|
}
|
||||||
|
|
||||||
a := s.Sizeof(T) // may be 0
|
a := s.Sizeof(T) // may be 0
|
||||||
// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
|
// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
|
||||||
if a < 1 {
|
if a < 1 {
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
; ModuleID = 'basic.go'
|
; ModuleID = 'basic.go'
|
||||||
source_filename = "basic.go"
|
source_filename = "basic.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%main.kv = type { float }
|
%main.kv = type { float }
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
; ModuleID = 'channel.go'
|
; ModuleID = 'channel.go'
|
||||||
source_filename = "channel.go"
|
source_filename = "channel.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
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* }
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
; ModuleID = 'float.go'
|
; ModuleID = 'float.go'
|
||||||
source_filename = "float.go"
|
source_filename = "float.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
; ModuleID = 'func.go'
|
; ModuleID = 'func.go'
|
||||||
source_filename = "func.go"
|
source_filename = "func.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
; ModuleID = 'gc.go'
|
; ModuleID = 'gc.go'
|
||||||
source_filename = "gc.go"
|
source_filename = "gc.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
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 }
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
; ModuleID = 'go1.17.go'
|
; ModuleID = 'go1.17.go'
|
||||||
source_filename = "go1.17.go"
|
source_filename = "go1.17.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
; ModuleID = 'goroutine.go'
|
; ModuleID = 'goroutine.go'
|
||||||
source_filename = "goroutine.go"
|
source_filename = "goroutine.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
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* }
|
||||||
|
|||||||
Vendored
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
; ModuleID = 'interface.go'
|
; ModuleID = 'interface.go'
|
||||||
source_filename = "interface.go"
|
source_filename = "interface.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
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 }
|
||||||
@@ -128,5 +128,5 @@ declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"
|
|||||||
attributes #0 = { nounwind }
|
attributes #0 = { nounwind }
|
||||||
attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" }
|
attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" }
|
||||||
attributes #2 = { "tinygo-methods"="reflect/methods.String() string" }
|
attributes #2 = { "tinygo-methods"="reflect/methods.String() string" }
|
||||||
attributes #3 = { "tinygo-invoke"="main.$methods.foo(int) byte" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) byte" }
|
attributes #3 = { "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
||||||
attributes #4 = { "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
attributes #4 = { "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
; ModuleID = 'intrinsics.go'
|
; ModuleID = 'intrinsics.go'
|
||||||
source_filename = "intrinsics.go"
|
source_filename = "intrinsics.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
; ModuleID = 'pointer.go'
|
; ModuleID = 'pointer.go'
|
||||||
source_filename = "pointer.go"
|
source_filename = "pointer.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
; ModuleID = 'pragma.go'
|
; ModuleID = 'pragma.go'
|
||||||
source_filename = "pragma.go"
|
source_filename = "pragma.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
@extern_global = external global [0 x i8], align 1
|
@extern_global = external global [0 x i8], align 1
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
; ModuleID = 'slice.go'
|
; ModuleID = 'slice.go'
|
||||||
source_filename = "slice.go"
|
source_filename = "slice.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
; ModuleID = 'string.go'
|
; ModuleID = 'string.go'
|
||||||
source_filename = "string.go"
|
source_filename = "string.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%runtime._string = type { i8*, i32 }
|
%runtime._string = type { i8*, i32 }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
module github.com/tinygo-org/tinygo
|
module github.com/tinygo-org/tinygo
|
||||||
|
|
||||||
go 1.15
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a
|
github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a
|
||||||
@@ -15,5 +15,5 @@ require (
|
|||||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
|
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
|
||||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9
|
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
tinygo.org/x/go-llvm v0.0.0-20220211075103-ee4aad45c3a1
|
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -80,5 +80,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
|||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20220211075103-ee4aad45c3a1 h1:6G8AxueDdqobCEqQrmHPLaEH1AZ1p6Y7rGElDNT7N98=
|
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f h1:9/J+NpFTpAhYcbh1mC4dr9W/aAPnwrUh8dmq21xWdSM=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20220211075103-ee4aad45c3a1/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||||
|
|||||||
+3
-3
@@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
// Version of TinyGo.
|
// Version of TinyGo.
|
||||||
// Update this value before release of new version of software.
|
// Update this value before release of new version of software.
|
||||||
const Version = "0.23.0-dev"
|
const Version = "0.24.0-dev"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// This variable is set at build time using -ldflags parameters.
|
// This variable is set at build time using -ldflags parameters.
|
||||||
@@ -58,9 +58,9 @@ func GorootVersionString(goroot string) (string, error) {
|
|||||||
return string(data), nil
|
return string(data), nil
|
||||||
|
|
||||||
} else if data, err := ioutil.ReadFile(filepath.Join(
|
} else if data, err := ioutil.ReadFile(filepath.Join(
|
||||||
goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
|
goroot, "src", "internal", "buildcfg", "zbootstrap.go")); err == nil {
|
||||||
|
|
||||||
r := regexp.MustCompile("const TheVersion = `(.*)`")
|
r := regexp.MustCompile("const version = `(.*)`")
|
||||||
matches := r.FindSubmatch(data)
|
matches := r.FindSubmatch(data)
|
||||||
if len(matches) != 2 {
|
if len(matches) != 2 {
|
||||||
return "", errors.New("Invalid go version output:\n" + string(data))
|
return "", errors.New("Invalid go version output:\n" + string(data))
|
||||||
|
|||||||
+18
-3
@@ -50,10 +50,17 @@ func newRunner(mod llvm.Module, debug bool) *runner {
|
|||||||
return &r
|
return &r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dispose deallocates all alloated LLVM resources.
|
||||||
|
func (r *runner) dispose() {
|
||||||
|
r.targetData.Dispose()
|
||||||
|
r.targetData = llvm.TargetData{}
|
||||||
|
}
|
||||||
|
|
||||||
// Run evaluates runtime.initAll function as much as possible at compile time.
|
// Run evaluates runtime.initAll function as much as possible at compile time.
|
||||||
// Set debug to true if it should print output while running.
|
// Set debug to true if it should print output while running.
|
||||||
func Run(mod llvm.Module, debug bool) error {
|
func Run(mod llvm.Module, debug bool) error {
|
||||||
r := newRunner(mod, debug)
|
r := newRunner(mod, debug)
|
||||||
|
defer r.dispose()
|
||||||
|
|
||||||
initAll := mod.NamedFunction("runtime.initAll")
|
initAll := mod.NamedFunction("runtime.initAll")
|
||||||
bb := initAll.EntryBasicBlock()
|
bb := initAll.EntryBasicBlock()
|
||||||
@@ -121,7 +128,10 @@ func Run(mod llvm.Module, debug bool) error {
|
|||||||
r.builder.CreateCall(fn, []llvm.Value{i8undef}, "")
|
r.builder.CreateCall(fn, []llvm.Value{i8undef}, "")
|
||||||
// Make sure that any globals touched by the package
|
// Make sure that any globals touched by the package
|
||||||
// initializer, won't be accessed by later package initializers.
|
// initializer, won't be accessed by later package initializers.
|
||||||
r.markExternalLoad(fn)
|
err := r.markExternalLoad(fn)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to interpret package %s: %w", r.pkgName, err)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return callErr
|
return callErr
|
||||||
@@ -193,6 +203,7 @@ func RunFunc(fn llvm.Value, debug bool) error {
|
|||||||
// Create and initialize *runner object.
|
// Create and initialize *runner object.
|
||||||
mod := fn.GlobalParent()
|
mod := fn.GlobalParent()
|
||||||
r := newRunner(mod, debug)
|
r := newRunner(mod, debug)
|
||||||
|
defer r.dispose()
|
||||||
initName := fn.Name()
|
initName := fn.Name()
|
||||||
if !strings.HasSuffix(initName, ".init") {
|
if !strings.HasSuffix(initName, ".init") {
|
||||||
return errorAt(fn, "interp: unexpected function name (expected *.init)")
|
return errorAt(fn, "interp: unexpected function name (expected *.init)")
|
||||||
@@ -288,12 +299,16 @@ func (r *runner) getFunction(llvmFn llvm.Value) *function {
|
|||||||
// variable. Another package initializer might read from the same global
|
// variable. Another package initializer might read from the same global
|
||||||
// variable. By marking this function as being run at runtime, that load
|
// variable. By marking this function as being run at runtime, that load
|
||||||
// instruction will need to be run at runtime instead of at compile time.
|
// instruction will need to be run at runtime instead of at compile time.
|
||||||
func (r *runner) markExternalLoad(llvmValue llvm.Value) {
|
func (r *runner) markExternalLoad(llvmValue llvm.Value) error {
|
||||||
mem := memoryView{r: r}
|
mem := memoryView{r: r}
|
||||||
mem.markExternalLoad(llvmValue)
|
err := mem.markExternalLoad(llvmValue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
for index, obj := range mem.objects {
|
for index, obj := range mem.objects {
|
||||||
if obj.marked > r.objects[index].marked {
|
if obj.marked > r.objects[index].marked {
|
||||||
r.objects[index].marked = obj.marked
|
r.objects[index].marked = obj.marked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-1
@@ -3,6 +3,7 @@ package interp
|
|||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -10,6 +11,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestInterp(t *testing.T) {
|
func TestInterp(t *testing.T) {
|
||||||
|
llvmVersion, err := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
|
||||||
|
if err != nil {
|
||||||
|
// Note: this should never happen and if it does, it will always happen
|
||||||
|
// for a particular build because llvm.Version is a constant.
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
for _, name := range []string{
|
for _, name := range []string{
|
||||||
"basic",
|
"basic",
|
||||||
"phi",
|
"phi",
|
||||||
@@ -19,7 +26,10 @@ func TestInterp(t *testing.T) {
|
|||||||
"revert",
|
"revert",
|
||||||
"alloc",
|
"alloc",
|
||||||
} {
|
} {
|
||||||
name := name // make tc local to this closure
|
name := name // make local to this closure
|
||||||
|
if name == "slice-copy" && llvmVersion < 14 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runTest(t, "testdata/"+name)
|
runTest(t, "testdata/"+name)
|
||||||
@@ -30,6 +40,7 @@ func TestInterp(t *testing.T) {
|
|||||||
func runTest(t *testing.T, pathPrefix string) {
|
func runTest(t *testing.T, pathPrefix string) {
|
||||||
// Read the input IR.
|
// Read the input IR.
|
||||||
ctx := llvm.NewContext()
|
ctx := llvm.NewContext()
|
||||||
|
defer ctx.Dispose()
|
||||||
buf, err := llvm.NewMemoryBufferFromFile(pathPrefix + ".ll")
|
buf, err := llvm.NewMemoryBufferFromFile(pathPrefix + ".ll")
|
||||||
os.Stat(pathPrefix + ".ll") // make sure this file is tracked by `go test` caching
|
os.Stat(pathPrefix + ".ll") // make sure this file is tracked by `go test` caching
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -39,6 +50,7 @@ func runTest(t *testing.T, pathPrefix string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not load module:\n%v", err)
|
t.Fatalf("could not load module:\n%v", err)
|
||||||
}
|
}
|
||||||
|
defer mod.Dispose()
|
||||||
|
|
||||||
// Perform the transform.
|
// Perform the transform.
|
||||||
err = Run(mod, false)
|
err = Run(mod, false)
|
||||||
|
|||||||
+32
-4
@@ -81,12 +81,28 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
if inst.opcode != llvm.PHI {
|
if inst.opcode != llvm.PHI {
|
||||||
for _, v := range inst.operands {
|
for _, v := range inst.operands {
|
||||||
if v, ok := v.(localValue); ok {
|
if v, ok := v.(localValue); ok {
|
||||||
if localVal := locals[fn.locals[v.value]]; localVal == nil {
|
index, ok := fn.locals[v.value]
|
||||||
|
if !ok {
|
||||||
|
// This is a localValue that is not local to the
|
||||||
|
// function. An example would be an inline assembly call
|
||||||
|
// operand.
|
||||||
|
isRuntimeInst = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
localVal := locals[index]
|
||||||
|
if localVal == nil {
|
||||||
|
// Trying to read a function-local value before it is
|
||||||
|
// set.
|
||||||
return nil, mem, r.errorAt(inst, errors.New("interp: local not defined"))
|
return nil, mem, r.errorAt(inst, errors.New("interp: local not defined"))
|
||||||
} else {
|
} else {
|
||||||
operands = append(operands, localVal)
|
operands = append(operands, localVal)
|
||||||
if _, ok := localVal.(localValue); ok {
|
if _, ok := localVal.(localValue); ok {
|
||||||
|
// The function-local value is still just a
|
||||||
|
// localValue (which can't be interpreted at compile
|
||||||
|
// time). Not sure whether this ever happens in
|
||||||
|
// practice.
|
||||||
isRuntimeInst = true
|
isRuntimeInst = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -219,6 +235,9 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, mem, err
|
return nil, mem, err
|
||||||
}
|
}
|
||||||
|
case callFn.name == "internal/task.Pause":
|
||||||
|
// Task scheduling isn't possible at compile time.
|
||||||
|
return nil, mem, r.errorAt(inst, errUnsupportedRuntimeInst)
|
||||||
case callFn.name == "runtime.nanotime" && r.pkgName == "time":
|
case callFn.name == "runtime.nanotime" && r.pkgName == "time":
|
||||||
// The time package contains a call to runtime.nanotime.
|
// The time package contains a call to runtime.nanotime.
|
||||||
// This appears to be to work around a limitation in Windows
|
// This appears to be to work around a limitation in Windows
|
||||||
@@ -926,12 +945,18 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
|
|||||||
args := operands[:len(operands)-1]
|
args := operands[:len(operands)-1]
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
if arg.Type().TypeKind() == llvm.PointerTypeKind {
|
if arg.Type().TypeKind() == llvm.PointerTypeKind {
|
||||||
mem.markExternalStore(arg)
|
err := mem.markExternalStore(arg)
|
||||||
|
if err != nil {
|
||||||
|
return r.errorAt(inst, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = r.builder.CreateCall(llvmFn, args, inst.name)
|
result = r.builder.CreateCall(llvmFn, args, inst.name)
|
||||||
case llvm.Load:
|
case llvm.Load:
|
||||||
mem.markExternalLoad(operands[0])
|
err := mem.markExternalLoad(operands[0])
|
||||||
|
if err != nil {
|
||||||
|
return r.errorAt(inst, err)
|
||||||
|
}
|
||||||
result = r.builder.CreateLoad(operands[0], inst.name)
|
result = r.builder.CreateLoad(operands[0], inst.name)
|
||||||
if inst.llvmInst.IsVolatile() {
|
if inst.llvmInst.IsVolatile() {
|
||||||
result.SetVolatile(true)
|
result.SetVolatile(true)
|
||||||
@@ -940,7 +965,10 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
|
|||||||
result.SetOrdering(ordering)
|
result.SetOrdering(ordering)
|
||||||
}
|
}
|
||||||
case llvm.Store:
|
case llvm.Store:
|
||||||
mem.markExternalStore(operands[1])
|
err := mem.markExternalStore(operands[1])
|
||||||
|
if err != nil {
|
||||||
|
return r.errorAt(inst, err)
|
||||||
|
}
|
||||||
result = r.builder.CreateStore(operands[0], operands[1])
|
result = r.builder.CreateStore(operands[0], operands[1])
|
||||||
if inst.llvmInst.IsVolatile() {
|
if inst.llvmInst.IsVolatile() {
|
||||||
result.SetVolatile(true)
|
result.SetVolatile(true)
|
||||||
|
|||||||
+41
-14
@@ -17,6 +17,7 @@ package interp
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -104,28 +105,28 @@ func (mv *memoryView) revert() {
|
|||||||
// means that the interpreter can still read from it, but cannot write to it as
|
// means that the interpreter can still read from it, but cannot write to it as
|
||||||
// that would mean the external read (done at runtime) reads from a state that
|
// that would mean the external read (done at runtime) reads from a state that
|
||||||
// would not exist had the whole initialization been done at runtime.
|
// would not exist had the whole initialization been done at runtime.
|
||||||
func (mv *memoryView) markExternalLoad(llvmValue llvm.Value) {
|
func (mv *memoryView) markExternalLoad(llvmValue llvm.Value) error {
|
||||||
mv.markExternal(llvmValue, 1)
|
return mv.markExternal(llvmValue, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// markExternalStore marks the given LLVM value as having an external write.
|
// markExternalStore marks the given LLVM value as having an external write.
|
||||||
// This means that the interpreter can no longer read from it or write to it, as
|
// This means that the interpreter can no longer read from it or write to it, as
|
||||||
// that would happen in a different order than if all initialization were
|
// that would happen in a different order than if all initialization were
|
||||||
// happening at runtime.
|
// happening at runtime.
|
||||||
func (mv *memoryView) markExternalStore(llvmValue llvm.Value) {
|
func (mv *memoryView) markExternalStore(llvmValue llvm.Value) error {
|
||||||
mv.markExternal(llvmValue, 2)
|
return mv.markExternal(llvmValue, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// markExternal is a helper for markExternalLoad and markExternalStore, and
|
// markExternal is a helper for markExternalLoad and markExternalStore, and
|
||||||
// should not be called directly.
|
// should not be called directly.
|
||||||
func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
|
func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
|
||||||
if llvmValue.IsUndef() || llvmValue.IsNull() {
|
if llvmValue.IsUndef() || llvmValue.IsNull() {
|
||||||
// Null and undef definitely don't contain (valid) pointers.
|
// Null and undef definitely don't contain (valid) pointers.
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
if !llvmValue.IsAInstruction().IsNil() || !llvmValue.IsAArgument().IsNil() {
|
if !llvmValue.IsAInstruction().IsNil() || !llvmValue.IsAArgument().IsNil() {
|
||||||
// These are considered external by default, there is nothing to mark.
|
// These are considered external by default, there is nothing to mark.
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !llvmValue.IsAGlobalValue().IsNil() {
|
if !llvmValue.IsAGlobalValue().IsNil() {
|
||||||
@@ -144,7 +145,10 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
|
|||||||
// Using mark '2' (which means read/write access) because
|
// Using mark '2' (which means read/write access) because
|
||||||
// even from an object that is only read from, the resulting
|
// even from an object that is only read from, the resulting
|
||||||
// loaded pointer can be written to.
|
// loaded pointer can be written to.
|
||||||
mv.markExternal(initializer, 2)
|
err := mv.markExternal(initializer, 2)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// This is a function. Go through all instructions and mark all
|
// This is a function. Go through all instructions and mark all
|
||||||
@@ -170,7 +174,10 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
|
|||||||
for i := 0; i < numOperands; i++ {
|
for i := 0; i < numOperands; i++ {
|
||||||
// Using mark '2' (which means read/write access)
|
// Using mark '2' (which means read/write access)
|
||||||
// because this might be a store instruction.
|
// because this might be a store instruction.
|
||||||
mv.markExternal(inst.Operand(i), 2)
|
err := mv.markExternal(inst.Operand(i), 2)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,9 +186,22 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
|
|||||||
} else if !llvmValue.IsAConstantExpr().IsNil() {
|
} else if !llvmValue.IsAConstantExpr().IsNil() {
|
||||||
switch llvmValue.Opcode() {
|
switch llvmValue.Opcode() {
|
||||||
case llvm.IntToPtr, llvm.PtrToInt, llvm.BitCast, llvm.GetElementPtr:
|
case llvm.IntToPtr, llvm.PtrToInt, llvm.BitCast, llvm.GetElementPtr:
|
||||||
mv.markExternal(llvmValue.Operand(0), mark)
|
err := mv.markExternal(llvmValue.Operand(0), mark)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case llvm.Add, llvm.Sub, llvm.Mul, llvm.UDiv, llvm.SDiv, llvm.URem, llvm.SRem, llvm.Shl, llvm.LShr, llvm.AShr, llvm.And, llvm.Or, llvm.Xor:
|
||||||
|
// Integer binary operators. Mark both operands.
|
||||||
|
err := mv.markExternal(llvmValue.Operand(0), mark)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = mv.markExternal(llvmValue.Operand(1), mark)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
panic("interp: unknown constant expression")
|
return fmt.Errorf("interp: unknown constant expression '%s'", instructionNameMap[llvmValue.Opcode()])
|
||||||
}
|
}
|
||||||
} else if !llvmValue.IsAInlineAsm().IsNil() {
|
} else if !llvmValue.IsAInlineAsm().IsNil() {
|
||||||
// Inline assembly can modify globals but only exported globals. Let's
|
// Inline assembly can modify globals but only exported globals. Let's
|
||||||
@@ -196,18 +216,25 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
|
|||||||
numElements := llvmType.StructElementTypesCount()
|
numElements := llvmType.StructElementTypesCount()
|
||||||
for i := 0; i < numElements; i++ {
|
for i := 0; i < numElements; i++ {
|
||||||
element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
|
element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
|
||||||
mv.markExternal(element, mark)
|
err := mv.markExternal(element, mark)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case llvm.ArrayTypeKind:
|
case llvm.ArrayTypeKind:
|
||||||
numElements := llvmType.ArrayLength()
|
numElements := llvmType.ArrayLength()
|
||||||
for i := 0; i < numElements; i++ {
|
for i := 0; i < numElements; i++ {
|
||||||
element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
|
element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
|
||||||
mv.markExternal(element, mark)
|
err := mv.markExternal(element, mark)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic("interp: unknown type kind in markExternalValue")
|
return errors.New("interp: unknown type kind in markExternalValue")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasExternalLoadOrStore returns true if this object has an external load or
|
// hasExternalLoadOrStore returns true if this object has an external load or
|
||||||
|
|||||||
Vendored
+1
-4
@@ -1,8 +1,6 @@
|
|||||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
target triple = "x86_64--linux"
|
target triple = "x86_64--linux"
|
||||||
|
|
||||||
@"main$alloc.1" = internal unnamed_addr constant [6 x i8] c"\05\00{\00\00\04", align 8
|
|
||||||
|
|
||||||
declare void @runtime.printuint8(i8) local_unnamed_addr
|
declare void @runtime.printuint8(i8) local_unnamed_addr
|
||||||
|
|
||||||
declare void @runtime.printint16(i16) local_unnamed_addr
|
declare void @runtime.printint16(i16) local_unnamed_addr
|
||||||
@@ -17,7 +15,6 @@ entry:
|
|||||||
call void @runtime.printuint8(i8 3)
|
call void @runtime.printuint8(i8 3)
|
||||||
call void @runtime.printuint8(i8 3)
|
call void @runtime.printuint8(i8 3)
|
||||||
call void @runtime.printint16(i16 5)
|
call void @runtime.printint16(i16 5)
|
||||||
%int16SliceDst.val = load i16, i16* bitcast ([6 x i8]* @"main$alloc.1" to i16*), align 2
|
call void @runtime.printint16(i16 5)
|
||||||
call void @runtime.printint16(i16 %int16SliceDst.val)
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|||||||
Submodule lib/compiler-rt deleted from 5bc79797e1
@@ -230,6 +230,7 @@ func pathsToOverride(needsSyscallPackage bool) map[string]bool {
|
|||||||
"device/": false,
|
"device/": false,
|
||||||
"examples/": false,
|
"examples/": false,
|
||||||
"internal/": true,
|
"internal/": true,
|
||||||
|
"internal/fuzz/": false,
|
||||||
"internal/bytealg/": false,
|
"internal/bytealg/": false,
|
||||||
"internal/reflectlite/": false,
|
"internal/reflectlite/": false,
|
||||||
"internal/task/": false,
|
"internal/task/": false,
|
||||||
|
|||||||
+276
-11
@@ -7,6 +7,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
|
"go/constant"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/scanner"
|
"go/scanner"
|
||||||
"go/token"
|
"go/token"
|
||||||
@@ -15,10 +16,12 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/cgo"
|
"github.com/tinygo-org/tinygo/cgo"
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
@@ -61,6 +64,9 @@ type PackageJSON struct {
|
|||||||
CgoFiles []string
|
CgoFiles []string
|
||||||
CFiles []string
|
CFiles []string
|
||||||
|
|
||||||
|
// Embedded files
|
||||||
|
EmbedFiles []string
|
||||||
|
|
||||||
// Dependency information
|
// Dependency information
|
||||||
Imports []string
|
Imports []string
|
||||||
ImportMap map[string]string
|
ImportMap map[string]string
|
||||||
@@ -77,13 +83,22 @@ type PackageJSON struct {
|
|||||||
type Package struct {
|
type Package struct {
|
||||||
PackageJSON
|
PackageJSON
|
||||||
|
|
||||||
program *Program
|
program *Program
|
||||||
Files []*ast.File
|
Files []*ast.File
|
||||||
FileHashes map[string][]byte
|
FileHashes map[string][]byte
|
||||||
CFlags []string // CFlags used during CGo preprocessing (only set if CGo is used)
|
CFlags []string // CFlags used during CGo preprocessing (only set if CGo is used)
|
||||||
CGoHeaders []string // text above 'import "C"' lines
|
CGoHeaders []string // text above 'import "C"' lines
|
||||||
Pkg *types.Package
|
EmbedGlobals map[string][]*EmbedFile
|
||||||
info types.Info
|
Pkg *types.Package
|
||||||
|
info types.Info
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmbedFile struct {
|
||||||
|
Name string
|
||||||
|
Size uint64
|
||||||
|
Hash string // hash of the file (as a hex string)
|
||||||
|
NeedsData bool // true if this file is embedded as a byte slice
|
||||||
|
Data []byte // contents of this file (only if NeedsData is set)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load loads the given package with all dependencies (including the runtime
|
// Load loads the given package with all dependencies (including the runtime
|
||||||
@@ -137,8 +152,9 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
|||||||
decoder := json.NewDecoder(buf)
|
decoder := json.NewDecoder(buf)
|
||||||
for {
|
for {
|
||||||
pkg := &Package{
|
pkg := &Package{
|
||||||
program: p,
|
program: p,
|
||||||
FileHashes: make(map[string][]byte),
|
FileHashes: make(map[string][]byte),
|
||||||
|
EmbedGlobals: make(map[string][]*EmbedFile),
|
||||||
info: types.Info{
|
info: types.Info{
|
||||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||||
Defs: make(map[*ast.Ident]types.Object),
|
Defs: make(map[*ast.Ident]types.Object),
|
||||||
@@ -357,15 +373,15 @@ func (p *Package) Check() error {
|
|||||||
return nil // already typechecked
|
return nil // already typechecked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prepare some state used during type checking.
|
||||||
var typeErrors []error
|
var typeErrors []error
|
||||||
checker := p.program.typeChecker // make a copy, because it will be modified
|
checker := p.program.typeChecker // make a copy, because it will be modified
|
||||||
checker.Error = func(err error) {
|
checker.Error = func(err error) {
|
||||||
typeErrors = append(typeErrors, err)
|
typeErrors = append(typeErrors, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do typechecking of the package.
|
|
||||||
checker.Importer = p
|
checker.Importer = p
|
||||||
|
|
||||||
|
// Do typechecking of the package.
|
||||||
packageName := p.ImportPath
|
packageName := p.ImportPath
|
||||||
if p == p.program.MainPkg() {
|
if p == p.program.MainPkg() {
|
||||||
if p.Name != "main" {
|
if p.Name != "main" {
|
||||||
@@ -382,6 +398,12 @@ func (p *Package) Check() error {
|
|||||||
return Errors{p, typeErrors}
|
return Errors{p, typeErrors}
|
||||||
}
|
}
|
||||||
p.Pkg = typesPkg
|
p.Pkg = typesPkg
|
||||||
|
|
||||||
|
p.extractEmbedLines(checker.Error)
|
||||||
|
if len(typeErrors) != 0 {
|
||||||
|
return Errors{p, typeErrors}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,6 +462,249 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extractEmbedLines finds all //go:embed lines in the package and matches them
|
||||||
|
// against EmbedFiles from `go list`.
|
||||||
|
func (p *Package) extractEmbedLines(addError func(error)) {
|
||||||
|
for _, file := range p.Files {
|
||||||
|
// Check for an `import "embed"` line at the start of the file.
|
||||||
|
// //go:embed lines are only valid if the given file itself imports the
|
||||||
|
// embed package. It is not valid if it is only imported in a separate
|
||||||
|
// Go file.
|
||||||
|
hasEmbed := false
|
||||||
|
for _, importSpec := range file.Imports {
|
||||||
|
if importSpec.Path.Value == `"embed"` {
|
||||||
|
hasEmbed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, decl := range file.Decls {
|
||||||
|
switch decl := decl.(type) {
|
||||||
|
case *ast.GenDecl:
|
||||||
|
if decl.Tok != token.VAR {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, spec := range decl.Specs {
|
||||||
|
spec := spec.(*ast.ValueSpec)
|
||||||
|
var doc *ast.CommentGroup
|
||||||
|
if decl.Lparen == token.NoPos {
|
||||||
|
// Plain 'var' declaration, like:
|
||||||
|
// //go:embed hello.txt
|
||||||
|
// var hello string
|
||||||
|
doc = decl.Doc
|
||||||
|
} else {
|
||||||
|
// Bigger 'var' declaration like:
|
||||||
|
// var (
|
||||||
|
// //go:embed hello.txt
|
||||||
|
// hello string
|
||||||
|
// )
|
||||||
|
doc = spec.Doc
|
||||||
|
}
|
||||||
|
if doc == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for //go:embed comments.
|
||||||
|
var allPatterns []string
|
||||||
|
for _, comment := range doc.List {
|
||||||
|
if comment.Text != "//go:embed" && !strings.HasPrefix(comment.Text, "//go:embed ") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !hasEmbed {
|
||||||
|
addError(types.Error{
|
||||||
|
Fset: p.program.fset,
|
||||||
|
Pos: comment.Pos() + 2,
|
||||||
|
Msg: "//go:embed only allowed in Go files that import \"embed\"",
|
||||||
|
})
|
||||||
|
// Continue, because otherwise we might run into
|
||||||
|
// issues below.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
patterns, err := p.parseGoEmbed(comment.Text[len("//go:embed"):], comment.Slash)
|
||||||
|
if err != nil {
|
||||||
|
addError(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(patterns) == 0 {
|
||||||
|
addError(types.Error{
|
||||||
|
Fset: p.program.fset,
|
||||||
|
Pos: comment.Pos() + 2,
|
||||||
|
Msg: "usage: //go:embed pattern...",
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, pattern := range patterns {
|
||||||
|
// Check that the pattern is well-formed.
|
||||||
|
// It must be valid: the Go toolchain has already
|
||||||
|
// checked for invalid patterns. But let's check
|
||||||
|
// anyway to be sure.
|
||||||
|
if _, err := path.Match(pattern, ""); err != nil {
|
||||||
|
addError(types.Error{
|
||||||
|
Fset: p.program.fset,
|
||||||
|
Pos: comment.Pos(),
|
||||||
|
Msg: "invalid pattern syntax",
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
allPatterns = append(allPatterns, pattern)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(allPatterns) != 0 {
|
||||||
|
// This is a //go:embed global. Do a few more checks.
|
||||||
|
if len(spec.Names) != 1 {
|
||||||
|
addError(types.Error{
|
||||||
|
Fset: p.program.fset,
|
||||||
|
Pos: spec.Names[1].NamePos,
|
||||||
|
Msg: "//go:embed cannot apply to multiple vars",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if spec.Values != nil {
|
||||||
|
addError(types.Error{
|
||||||
|
Fset: p.program.fset,
|
||||||
|
Pos: spec.Values[0].Pos(),
|
||||||
|
Msg: "//go:embed cannot apply to var with initializer",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
globalName := spec.Names[0].Name
|
||||||
|
globalType := p.Pkg.Scope().Lookup(globalName).Type()
|
||||||
|
valid, byteSlice := isValidEmbedType(globalType)
|
||||||
|
if !valid {
|
||||||
|
addError(types.Error{
|
||||||
|
Fset: p.program.fset,
|
||||||
|
Pos: spec.Type.Pos(),
|
||||||
|
Msg: "//go:embed cannot apply to var of type " + globalType.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match all //go:embed patterns against the embed files
|
||||||
|
// provided by `go list`.
|
||||||
|
for _, name := range p.EmbedFiles {
|
||||||
|
for _, pattern := range allPatterns {
|
||||||
|
if matchPattern(pattern, name) {
|
||||||
|
p.EmbedGlobals[globalName] = append(p.EmbedGlobals[globalName], &EmbedFile{
|
||||||
|
Name: name,
|
||||||
|
NeedsData: byteSlice,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchPattern returns true if (and only if) the given pattern would match the
|
||||||
|
// filename. The pattern could also match a parent directory of name, in which
|
||||||
|
// case hidden files do not match.
|
||||||
|
func matchPattern(pattern, name string) bool {
|
||||||
|
// Match this file.
|
||||||
|
matched, _ := path.Match(pattern, name)
|
||||||
|
if matched {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match parent directories.
|
||||||
|
dir := name
|
||||||
|
for {
|
||||||
|
dir, _ = path.Split(dir)
|
||||||
|
if dir == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
dir = path.Clean(dir)
|
||||||
|
if matched, _ := path.Match(pattern, dir); matched {
|
||||||
|
// Pattern matches the directory.
|
||||||
|
suffix := name[len(dir):]
|
||||||
|
if strings.Contains(suffix, "/_") || strings.Contains(suffix, "/.") {
|
||||||
|
// Pattern matches a hidden file.
|
||||||
|
// Hidden files are included when listed directly as a
|
||||||
|
// pattern, but not when they are part of a directory tree.
|
||||||
|
// Source:
|
||||||
|
// > If a pattern names a directory, all files in the
|
||||||
|
// > subtree rooted at that directory are embedded
|
||||||
|
// > (recursively), except that files with names beginning
|
||||||
|
// > with ‘.’ or ‘_’ are excluded.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseGoEmbed is like strings.Fields but for a //go:embed line. It parses
|
||||||
|
// regular fields and quoted fields (that may contain spaces).
|
||||||
|
func (p *Package) parseGoEmbed(args string, pos token.Pos) (patterns []string, err error) {
|
||||||
|
args = strings.TrimSpace(args)
|
||||||
|
initialLen := len(args)
|
||||||
|
for args != "" {
|
||||||
|
patternPos := pos + token.Pos(initialLen-len(args))
|
||||||
|
switch args[0] {
|
||||||
|
case '`', '"', '\\':
|
||||||
|
// Parse the next pattern using the Go scanner.
|
||||||
|
// This is perhaps a bit overkill, but it does correctly implement
|
||||||
|
// parsing of the various Go strings.
|
||||||
|
var sc scanner.Scanner
|
||||||
|
fset := &token.FileSet{}
|
||||||
|
file := fset.AddFile("", 0, len(args))
|
||||||
|
sc.Init(file, []byte(args), nil, 0)
|
||||||
|
_, tok, lit := sc.Scan()
|
||||||
|
if tok != token.STRING || sc.ErrorCount != 0 {
|
||||||
|
// Calculate start of token
|
||||||
|
return nil, types.Error{
|
||||||
|
Fset: p.program.fset,
|
||||||
|
Pos: patternPos,
|
||||||
|
Msg: "invalid quoted string in //go:embed",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pattern := constant.StringVal(constant.MakeFromLiteral(lit, tok, 0))
|
||||||
|
patterns = append(patterns, pattern)
|
||||||
|
args = strings.TrimLeftFunc(args[len(lit):], unicode.IsSpace)
|
||||||
|
default:
|
||||||
|
// The value is just a regular value.
|
||||||
|
// Split it at the first white space.
|
||||||
|
index := strings.IndexFunc(args, unicode.IsSpace)
|
||||||
|
if index < 0 {
|
||||||
|
index = len(args)
|
||||||
|
}
|
||||||
|
pattern := args[:index]
|
||||||
|
patterns = append(patterns, pattern)
|
||||||
|
args = strings.TrimLeftFunc(args[len(pattern):], unicode.IsSpace)
|
||||||
|
}
|
||||||
|
if _, err := path.Match(patterns[len(patterns)-1], ""); err != nil {
|
||||||
|
return nil, types.Error{
|
||||||
|
Fset: p.program.fset,
|
||||||
|
Pos: patternPos,
|
||||||
|
Msg: "invalid pattern syntax",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return patterns, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValidEmbedType returns whether the given Go type can be used as a
|
||||||
|
// //go:embed type. This is only true for embed.FS, strings, and byte slices.
|
||||||
|
// The second return value indicates that this is a byte slice, and therefore
|
||||||
|
// the contents of the file needs to be passed to the compiler.
|
||||||
|
func isValidEmbedType(typ types.Type) (valid, byteSlice bool) {
|
||||||
|
if typ.Underlying() == types.Typ[types.String] {
|
||||||
|
// string type
|
||||||
|
return true, false
|
||||||
|
}
|
||||||
|
if sliceType, ok := typ.Underlying().(*types.Slice); ok {
|
||||||
|
if elemType, ok := sliceType.Elem().Underlying().(*types.Basic); ok && elemType.Kind() == types.Byte {
|
||||||
|
// byte slice type
|
||||||
|
return true, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if namedType, ok := typ.(*types.Named); ok && namedType.String() == "embed.FS" {
|
||||||
|
// embed.FS type
|
||||||
|
return true, false
|
||||||
|
}
|
||||||
|
return false, false
|
||||||
|
}
|
||||||
|
|
||||||
// Import implements types.Importer. It loads and parses packages it encounters
|
// Import implements types.Importer. It loads and parses packages it encounters
|
||||||
// along the way, if needed.
|
// along the way, if needed.
|
||||||
func (p *Package) Import(to string) (*types.Package, error) {
|
func (p *Package) Import(to string) (*types.Package, error) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
@@ -199,8 +200,26 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pass test flags to the test binary.
|
||||||
|
var flags []string
|
||||||
|
if testVerbose {
|
||||||
|
flags = append(flags, "-test.v")
|
||||||
|
}
|
||||||
|
if testShort {
|
||||||
|
flags = append(flags, "-test.short")
|
||||||
|
}
|
||||||
|
if testRunRegexp != "" {
|
||||||
|
flags = append(flags, "-test.run="+testRunRegexp)
|
||||||
|
}
|
||||||
|
if testBenchRegexp != "" {
|
||||||
|
flags = append(flags, "-test.bench="+testBenchRegexp)
|
||||||
|
}
|
||||||
|
if testBenchTime != "" {
|
||||||
|
flags = append(flags, "-test.benchtime="+testBenchTime)
|
||||||
|
}
|
||||||
|
|
||||||
passed := false
|
passed := false
|
||||||
err = builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
|
err = buildAndRun(pkgName, config, os.Stdout, flags, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error {
|
||||||
if testCompileOnly || outpath != "" {
|
if testCompileOnly || outpath != "" {
|
||||||
// Write test binary to the specified file name.
|
// Write test binary to the specified file name.
|
||||||
if outpath == "" {
|
if outpath == "" {
|
||||||
@@ -216,27 +235,52 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the test.
|
// Tests are always run in the package directory.
|
||||||
config.Options.Semaphore <- struct{}{}
|
cmd.Dir = result.MainDir
|
||||||
defer func() {
|
|
||||||
<-config.Options.Semaphore
|
// Wasmtime needs a few extra flags to work.
|
||||||
}()
|
if config.EmulatorName() == "wasmtime" {
|
||||||
start := time.Now()
|
// Add directories to the module root, but skip the current working
|
||||||
var err error
|
// directory which is already added by buildAndRun.
|
||||||
passed, err = runPackageTest(config, stdout, stderr, result, testVerbose, testShort, testRunRegexp, testBenchRegexp, testBenchTime)
|
dirs := dirsToModuleRoot(result.MainDir, result.ModuleRoot)
|
||||||
if err != nil {
|
var args []string
|
||||||
return err
|
for _, d := range dirs[1:] {
|
||||||
|
args = append(args, "--dir="+d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new temp directory just for this run, announce it to os.TempDir() via TMPDIR
|
||||||
|
tmpdir, err := ioutil.TempDir("", "tinygotmp")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create temporary directory: %w", err)
|
||||||
|
}
|
||||||
|
args = append(args, "--dir="+tmpdir, "--env=TMPDIR="+tmpdir)
|
||||||
|
// TODO: add option to not delete temp dir for debugging?
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
|
||||||
|
// Insert new argments at the front of the command line argments.
|
||||||
|
args = append(args, cmd.Args[1:]...)
|
||||||
|
cmd.Args = append(cmd.Args[:1:1], args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run the test.
|
||||||
|
start := time.Now()
|
||||||
|
err = cmd.Run()
|
||||||
duration := time.Since(start)
|
duration := time.Since(start)
|
||||||
|
|
||||||
// Print the result.
|
// Print the result.
|
||||||
importPath := strings.TrimSuffix(result.ImportPath, ".test")
|
importPath := strings.TrimSuffix(result.ImportPath, ".test")
|
||||||
|
passed = err == nil
|
||||||
if passed {
|
if passed {
|
||||||
fmt.Fprintf(stdout, "ok \t%s\t%.3fs\n", importPath, duration.Seconds())
|
fmt.Fprintf(stdout, "ok \t%s\t%.3fs\n", importPath, duration.Seconds())
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(stdout, "FAIL\t%s\t%.3fs\n", importPath, duration.Seconds())
|
fmt.Fprintf(stdout, "FAIL\t%s\t%.3fs\n", importPath, duration.Seconds())
|
||||||
}
|
}
|
||||||
return nil
|
if _, ok := err.(*exec.ExitError); ok {
|
||||||
|
// Binary exited with a non-zero exit code, which means the test
|
||||||
|
// failed.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
})
|
})
|
||||||
if err, ok := err.(loader.NoTestFilesError); ok {
|
if err, ok := err.(loader.NoTestFilesError); ok {
|
||||||
fmt.Fprintf(stdout, "? \t%s\t[no test files]\n", err.ImportPath)
|
fmt.Fprintf(stdout, "? \t%s\t[no test files]\n", err.ImportPath)
|
||||||
@@ -259,81 +303,6 @@ func dirsToModuleRoot(maindir, modroot string) []string {
|
|||||||
return dirs
|
return dirs
|
||||||
}
|
}
|
||||||
|
|
||||||
// runPackageTest runs a test binary that was previously built. The return
|
|
||||||
// values are whether the test passed and any errors encountered while trying to
|
|
||||||
// run the binary.
|
|
||||||
func runPackageTest(config *compileopts.Config, stdout, stderr io.Writer, result builder.BuildResult, testVerbose, testShort bool, testRunRegexp string, testBenchRegexp string, testBenchTime string) (bool, error) {
|
|
||||||
var cmd *exec.Cmd
|
|
||||||
emulator := config.Emulator()
|
|
||||||
if len(emulator) == 0 {
|
|
||||||
// Run directly.
|
|
||||||
var flags []string
|
|
||||||
if testVerbose {
|
|
||||||
flags = append(flags, "-test.v")
|
|
||||||
}
|
|
||||||
if testShort {
|
|
||||||
flags = append(flags, "-test.short")
|
|
||||||
}
|
|
||||||
if testRunRegexp != "" {
|
|
||||||
flags = append(flags, "-test.run="+testRunRegexp)
|
|
||||||
}
|
|
||||||
if testBenchRegexp != "" {
|
|
||||||
flags = append(flags, "-test.bench="+testBenchRegexp)
|
|
||||||
}
|
|
||||||
if testBenchTime != "" {
|
|
||||||
flags = append(flags, "-test.benchtime="+testBenchTime)
|
|
||||||
}
|
|
||||||
cmd = executeCommand(config.Options, result.Binary, flags...)
|
|
||||||
} else {
|
|
||||||
// Run in an emulator.
|
|
||||||
args := append(emulator[1:], result.Binary)
|
|
||||||
if emulator[0] == "wasmtime" {
|
|
||||||
// create a new temp directory just for this run, announce it to os.TempDir() via TMPDIR
|
|
||||||
tmpdir, err := ioutil.TempDir("", "tinygotmp")
|
|
||||||
if err != nil {
|
|
||||||
return false, &commandError{"failed to create temporary directory", "tinygotmp", err}
|
|
||||||
}
|
|
||||||
args = append(args, "--dir="+tmpdir, "--env=TMPDIR="+tmpdir)
|
|
||||||
// TODO: add option to not delete temp dir for debugging?
|
|
||||||
defer os.RemoveAll(tmpdir)
|
|
||||||
|
|
||||||
// allow reading from directories up to module root
|
|
||||||
for _, d := range dirsToModuleRoot(result.MainDir, result.ModuleRoot) {
|
|
||||||
args = append(args, "--dir="+d)
|
|
||||||
}
|
|
||||||
|
|
||||||
// mark end of wasmtime arguments and start of program ones: --
|
|
||||||
args = append(args, "--")
|
|
||||||
if testVerbose {
|
|
||||||
args = append(args, "-test.v")
|
|
||||||
}
|
|
||||||
if testShort {
|
|
||||||
args = append(args, "-test.short")
|
|
||||||
}
|
|
||||||
if testRunRegexp != "" {
|
|
||||||
args = append(args, "-test.run="+testRunRegexp)
|
|
||||||
}
|
|
||||||
if testBenchRegexp != "" {
|
|
||||||
args = append(args, "-test.bench="+testBenchRegexp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cmd = executeCommand(config.Options, emulator[0], args...)
|
|
||||||
}
|
|
||||||
cmd.Dir = result.MainDir
|
|
||||||
cmd.Stdout = stdout
|
|
||||||
cmd.Stderr = stderr
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
if _, ok := err.(*exec.ExitError); ok {
|
|
||||||
// Binary exited with a non-zero exit code, which means the test
|
|
||||||
// failed.
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
return false, &commandError{"failed to run compiled binary", result.Binary, err}
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flash builds and flashes the built binary to the given serial port.
|
// Flash builds and flashes the built binary to the given serial port.
|
||||||
func Flash(pkgName, port string, options *compileopts.Options) error {
|
func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||||
config, err := builder.NewConfig(options)
|
config, err := builder.NewConfig(options)
|
||||||
@@ -453,7 +422,11 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+" reset exit")
|
exit := " reset exit"
|
||||||
|
if config.Target.OpenOCDVerify != nil && *config.Target.OpenOCDVerify {
|
||||||
|
exit = " verify" + exit
|
||||||
|
}
|
||||||
|
args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+exit)
|
||||||
cmd := executeCommand(config.Options, "openocd", args...)
|
cmd := executeCommand(config.Options, "openocd", args...)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
@@ -510,18 +483,19 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.Build(pkgName, "", config, func(result builder.BuildResult) error {
|
format, fileExt := config.EmulatorFormat()
|
||||||
|
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
|
||||||
// Find a good way to run GDB.
|
// Find a good way to run GDB.
|
||||||
gdbInterface, openocdInterface := config.Programmer()
|
gdbInterface, openocdInterface := config.Programmer()
|
||||||
switch gdbInterface {
|
switch gdbInterface {
|
||||||
case "msd", "command", "":
|
case "msd", "command", "":
|
||||||
emulator := config.Emulator()
|
emulator := config.EmulatorName()
|
||||||
if len(emulator) != 0 {
|
if emulator != "" {
|
||||||
if emulator[0] == "mgba" {
|
if emulator == "mgba" {
|
||||||
gdbInterface = "mgba"
|
gdbInterface = "mgba"
|
||||||
} else if emulator[0] == "simavr" {
|
} else if emulator == "simavr" {
|
||||||
gdbInterface = "simavr"
|
gdbInterface = "simavr"
|
||||||
} else if strings.HasPrefix(emulator[0], "qemu-system-") {
|
} else if strings.HasPrefix(emulator, "qemu-system-") {
|
||||||
gdbInterface = "qemu"
|
gdbInterface = "qemu"
|
||||||
} else {
|
} else {
|
||||||
// Assume QEMU as an emulator.
|
// Assume QEMU as an emulator.
|
||||||
@@ -540,6 +514,10 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
|
|||||||
port := ""
|
port := ""
|
||||||
var gdbCommands []string
|
var gdbCommands []string
|
||||||
var daemon *exec.Cmd
|
var daemon *exec.Cmd
|
||||||
|
emulator, err := config.Emulator(format, result.Binary)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
switch gdbInterface {
|
switch gdbInterface {
|
||||||
case "native":
|
case "native":
|
||||||
// Run GDB directly.
|
// Run GDB directly.
|
||||||
@@ -589,33 +567,29 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
|
|||||||
}
|
}
|
||||||
case "qemu":
|
case "qemu":
|
||||||
port = ":1234"
|
port = ":1234"
|
||||||
emulator := config.Emulator()
|
|
||||||
// Run in an emulator.
|
// Run in an emulator.
|
||||||
args := append(emulator[1:], result.Binary, "-s", "-S")
|
args := append(emulator[1:], "-s", "-S")
|
||||||
daemon = executeCommand(config.Options, emulator[0], args...)
|
daemon = executeCommand(config.Options, emulator[0], args...)
|
||||||
daemon.Stdout = os.Stdout
|
daemon.Stdout = os.Stdout
|
||||||
daemon.Stderr = os.Stderr
|
daemon.Stderr = os.Stderr
|
||||||
case "qemu-user":
|
case "qemu-user":
|
||||||
port = ":1234"
|
port = ":1234"
|
||||||
emulator := config.Emulator()
|
|
||||||
// Run in an emulator.
|
// Run in an emulator.
|
||||||
args := append(emulator[1:], "-g", "1234", result.Binary)
|
args := append(emulator[1:], "-g", "1234")
|
||||||
daemon = executeCommand(config.Options, emulator[0], args...)
|
daemon = executeCommand(config.Options, emulator[0], args...)
|
||||||
daemon.Stdout = os.Stdout
|
daemon.Stdout = os.Stdout
|
||||||
daemon.Stderr = os.Stderr
|
daemon.Stderr = os.Stderr
|
||||||
case "mgba":
|
case "mgba":
|
||||||
port = ":2345"
|
port = ":2345"
|
||||||
emulator := config.Emulator()
|
|
||||||
// Run in an emulator.
|
// Run in an emulator.
|
||||||
args := append(emulator[1:], result.Binary, "-g")
|
args := append(emulator[1:], "-g")
|
||||||
daemon = executeCommand(config.Options, emulator[0], args...)
|
daemon = executeCommand(config.Options, emulator[0], args...)
|
||||||
daemon.Stdout = os.Stdout
|
daemon.Stdout = os.Stdout
|
||||||
daemon.Stderr = os.Stderr
|
daemon.Stderr = os.Stderr
|
||||||
case "simavr":
|
case "simavr":
|
||||||
port = ":1234"
|
port = ":1234"
|
||||||
emulator := config.Emulator()
|
|
||||||
// Run in an emulator.
|
// Run in an emulator.
|
||||||
args := append(emulator[1:], "-g", result.Binary)
|
args := append(emulator[1:], "-g")
|
||||||
daemon = executeCommand(config.Options, emulator[0], args...)
|
daemon = executeCommand(config.Options, emulator[0], args...)
|
||||||
daemon.Stdout = os.Stdout
|
daemon.Stdout = os.Stdout
|
||||||
daemon.Stderr = os.Stderr
|
daemon.Stderr = os.Stderr
|
||||||
@@ -660,7 +634,7 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
|
|||||||
// Construct and execute a gdb or lldb command.
|
// Construct and execute a gdb or lldb command.
|
||||||
// By default: gdb -ex run <binary>
|
// By default: gdb -ex run <binary>
|
||||||
// Exit the debugger with Ctrl-D.
|
// Exit the debugger with Ctrl-D.
|
||||||
params := []string{result.Binary}
|
params := []string{result.Executable}
|
||||||
switch debugger {
|
switch debugger {
|
||||||
case "gdb":
|
case "gdb":
|
||||||
if port != "" {
|
if port != "" {
|
||||||
@@ -692,9 +666,9 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
|
|||||||
cmd.Stdin = os.Stdin
|
cmd.Stdin = os.Stdin
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
err := cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to run " + cmdName + " with", result.Binary, err}
|
return &commandError{"failed to run " + cmdName + " with", result.Executable, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@@ -704,44 +678,134 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
|
|||||||
// the options, it will run the program directly on the host or will run it in
|
// the options, it will run the program directly on the host or will run it in
|
||||||
// an emulator. For example, -target=wasm will cause the binary to be run inside
|
// an emulator. For example, -target=wasm will cause the binary to be run inside
|
||||||
// of a WebAssembly VM.
|
// of a WebAssembly VM.
|
||||||
func Run(pkgName string, options *compileopts.Options) error {
|
func Run(pkgName string, options *compileopts.Options, cmdArgs []string) error {
|
||||||
config, err := builder.NewConfig(options)
|
config, err := builder.NewConfig(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error {
|
return buildAndRun(pkgName, config, os.Stdout, cmdArgs, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error {
|
||||||
emulator := config.Emulator()
|
return cmd.Run()
|
||||||
if len(emulator) == 0 {
|
})
|
||||||
// Run directly.
|
}
|
||||||
cmd := executeCommand(config.Options, result.Binary)
|
|
||||||
cmd.Stdout = os.Stdout
|
// buildAndRun builds and runs the given program, writing output to stdout and
|
||||||
cmd.Stderr = os.Stderr
|
// errors to os.Stderr. It takes care of emulators (qemu, wasmtime, etc) and
|
||||||
err := cmd.Run()
|
// passes command line arguments and evironment variables in a way appropriate
|
||||||
if err != nil {
|
// for the given emulator.
|
||||||
if err, ok := err.(*exec.ExitError); ok && err.Exited() {
|
func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, cmdArgs, environmentVars []string, timeout time.Duration, run func(cmd *exec.Cmd, result builder.BuildResult) error) error {
|
||||||
// Workaround for QEMU which always exits with an error.
|
// Determine whether we're on a system that supports environment variables
|
||||||
return nil
|
// and command line parameters (operating systems, WASI) or not (baremetal,
|
||||||
}
|
// WebAssembly in the browser). If we're on a system without an environment,
|
||||||
return &commandError{"failed to run compiled binary", result.Binary, err}
|
// we need to pass command line arguments and environment variables through
|
||||||
}
|
// global variables (built into the binary directly) instead of the
|
||||||
return nil
|
// conventional way.
|
||||||
} else {
|
needsEnvInVars := config.GOOS() == "js"
|
||||||
// Run in an emulator.
|
for _, tag := range config.BuildTags() {
|
||||||
args := append(emulator[1:], result.Binary)
|
if tag == "baremetal" {
|
||||||
cmd := executeCommand(config.Options, emulator[0], args...)
|
needsEnvInVars = true
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
if err, ok := err.(*exec.ExitError); ok && err.Exited() {
|
|
||||||
// Workaround for QEMU which always exits with an error.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &commandError{"failed to run emulator with", result.Binary, err}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
var args, env []string
|
||||||
|
if needsEnvInVars {
|
||||||
|
runtimeGlobals := make(map[string]string)
|
||||||
|
if len(cmdArgs) != 0 {
|
||||||
|
runtimeGlobals["osArgs"] = strings.Join(cmdArgs, "\x00")
|
||||||
|
}
|
||||||
|
if len(environmentVars) != 0 {
|
||||||
|
runtimeGlobals["osEnv"] = strings.Join(environmentVars, "\x00")
|
||||||
|
}
|
||||||
|
if len(runtimeGlobals) != 0 {
|
||||||
|
// This sets the global variables like they would be set with
|
||||||
|
// `-ldflags="-X=runtime.osArgs=first\x00second`.
|
||||||
|
// The runtime package has two variables (osArgs and osEnv) that are
|
||||||
|
// both strings, from which the parameters and environment variables
|
||||||
|
// are read.
|
||||||
|
config.Options.GlobalValues = map[string]map[string]string{
|
||||||
|
"runtime": runtimeGlobals,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if config.EmulatorName() == "wasmtime" {
|
||||||
|
// Wasmtime needs some special flags to pass environment variables
|
||||||
|
// and allow reading from the current directory.
|
||||||
|
args = append(args, "--dir=.")
|
||||||
|
for _, v := range environmentVars {
|
||||||
|
args = append(args, "--env", v)
|
||||||
|
}
|
||||||
|
if len(cmdArgs) != 0 {
|
||||||
|
// mark end of wasmtime arguments and start of program ones: --
|
||||||
|
args = append(args, "--")
|
||||||
|
args = append(args, cmdArgs...)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Pass environment variables and command line parameters as usual.
|
||||||
|
// This also works on qemu-aarch64 etc.
|
||||||
|
args = cmdArgs
|
||||||
|
env = environmentVars
|
||||||
|
}
|
||||||
|
|
||||||
|
format, fileExt := config.EmulatorFormat()
|
||||||
|
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
|
||||||
|
// If needed, set a timeout on the command. This is done in tests so
|
||||||
|
// they don't waste resources on a stalled test.
|
||||||
|
var ctx context.Context
|
||||||
|
if timeout != 0 {
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
ctx, cancel = context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the command.
|
||||||
|
var name string
|
||||||
|
if config.Target.Emulator == "" {
|
||||||
|
name = result.Binary
|
||||||
|
} else {
|
||||||
|
emulator, err := config.Emulator(format, result.Binary)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
name = emulator[0]
|
||||||
|
emuArgs := append([]string(nil), emulator[1:]...)
|
||||||
|
args = append(emuArgs, args...)
|
||||||
|
}
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
if ctx != nil {
|
||||||
|
cmd = exec.CommandContext(ctx, name, args...)
|
||||||
|
} else {
|
||||||
|
cmd = exec.Command(name, args...)
|
||||||
|
}
|
||||||
|
cmd.Env = env
|
||||||
|
|
||||||
|
// Configure stdout/stderr. The stdout may go to a buffer, not a real
|
||||||
|
// stdout.
|
||||||
|
cmd.Stdout = stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
if config.EmulatorName() == "simavr" {
|
||||||
|
cmd.Stdout = nil // don't print initial load commands
|
||||||
|
cmd.Stderr = stdout
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a test, reserve CPU time for it so that increased
|
||||||
|
// parallelism doesn't blow up memory usage. If this isn't a test but
|
||||||
|
// simply `tinygo run`, then it is practically a no-op.
|
||||||
|
config.Options.Semaphore <- struct{}{}
|
||||||
|
defer func() {
|
||||||
|
<-config.Options.Semaphore
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Run binary.
|
||||||
|
if config.Options.PrintCommands != nil {
|
||||||
|
config.Options.PrintCommands(cmd.Path, cmd.Args...)
|
||||||
|
}
|
||||||
|
err := run(cmd, result)
|
||||||
|
if err != nil {
|
||||||
|
if ctx != nil && ctx.Err() == context.DeadlineExceeded {
|
||||||
|
stdout.Write([]byte(fmt.Sprintf("--- timeout of %s exceeded, terminating...\n", timeout)))
|
||||||
|
err = ctx.Err()
|
||||||
|
}
|
||||||
|
return &commandError{"failed to run compiled binary", result.Binary, err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1407,13 +1471,13 @@ func main() {
|
|||||||
handleCompilerError(err)
|
handleCompilerError(err)
|
||||||
}
|
}
|
||||||
case "run":
|
case "run":
|
||||||
if flag.NArg() != 1 {
|
if flag.NArg() < 1 {
|
||||||
fmt.Fprintln(os.Stderr, "No package specified.")
|
fmt.Fprintln(os.Stderr, "No package specified.")
|
||||||
usage(command)
|
usage(command)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
pkgName := filepath.ToSlash(flag.Arg(0))
|
pkgName := filepath.ToSlash(flag.Arg(0))
|
||||||
err := Run(pkgName, options)
|
err := Run(pkgName, options, flag.Args()[1:])
|
||||||
handleCompilerError(err)
|
handleCompilerError(err)
|
||||||
case "test":
|
case "test":
|
||||||
var pkgNames []string
|
var pkgNames []string
|
||||||
@@ -1509,7 +1573,7 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == nil {
|
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" {
|
||||||
// This doesn't look like a regular target file, but rather like
|
// This doesn't look like a regular target file, but rather like
|
||||||
// a parent target (such as targets/cortex-m.json).
|
// a parent target (such as targets/cortex-m.json).
|
||||||
continue
|
continue
|
||||||
|
|||||||
+48
-143
@@ -6,7 +6,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -14,7 +13,6 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -50,6 +48,7 @@ func TestBuild(t *testing.T) {
|
|||||||
"calls.go",
|
"calls.go",
|
||||||
"cgo/",
|
"cgo/",
|
||||||
"channel.go",
|
"channel.go",
|
||||||
|
"embed/",
|
||||||
"float.go",
|
"float.go",
|
||||||
"gc.go",
|
"gc.go",
|
||||||
"goroutines.go",
|
"goroutines.go",
|
||||||
@@ -66,7 +65,6 @@ func TestBuild(t *testing.T) {
|
|||||||
"stdlib.go",
|
"stdlib.go",
|
||||||
"string.go",
|
"string.go",
|
||||||
"structs.go",
|
"structs.go",
|
||||||
"testing.go",
|
|
||||||
"zeroalloc.go",
|
"zeroalloc.go",
|
||||||
}
|
}
|
||||||
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||||
@@ -76,6 +74,11 @@ func TestBuild(t *testing.T) {
|
|||||||
if minor >= 17 {
|
if minor >= 17 {
|
||||||
tests = append(tests, "go1.17.go")
|
tests = append(tests, "go1.17.go")
|
||||||
}
|
}
|
||||||
|
if minor >= 18 {
|
||||||
|
tests = append(tests, "testing_go118.go")
|
||||||
|
} else {
|
||||||
|
tests = append(tests, "testing.go")
|
||||||
|
}
|
||||||
|
|
||||||
if *testTarget != "" {
|
if *testTarget != "" {
|
||||||
// This makes it possible to run one specific test (instead of all),
|
// This makes it possible to run one specific test (instead of all),
|
||||||
@@ -144,46 +147,7 @@ func TestBuild(t *testing.T) {
|
|||||||
// LIBCLANG FATAL ERROR: Cannot select: t3: i16 = JumpTable<0>
|
// LIBCLANG FATAL ERROR: Cannot select: t3: i16 = JumpTable<0>
|
||||||
// This bug is non-deterministic.
|
// This bug is non-deterministic.
|
||||||
t.Skip("skipped due to non-deterministic backend bugs")
|
t.Skip("skipped due to non-deterministic backend bugs")
|
||||||
|
runPlatTests(optionsFromTarget("simavr", sema), tests, t)
|
||||||
var avrTests []string
|
|
||||||
for _, t := range tests {
|
|
||||||
switch t {
|
|
||||||
case "atomic.go":
|
|
||||||
// Requires GCC 11.2.0 or above for interface comparison.
|
|
||||||
// https://github.com/gcc-mirror/gcc/commit/f30dd607669212de135dec1f1d8a93b8954c327c
|
|
||||||
|
|
||||||
case "reflect.go":
|
|
||||||
// Reflect tests do not work due to type code issues.
|
|
||||||
|
|
||||||
case "gc.go":
|
|
||||||
// Does not pass due to high mark false positive rate.
|
|
||||||
|
|
||||||
case "json.go", "stdlib.go", "testing.go":
|
|
||||||
// Breaks interp.
|
|
||||||
|
|
||||||
case "map.go":
|
|
||||||
// Reflect size calculation crashes.
|
|
||||||
|
|
||||||
case "binop.go":
|
|
||||||
// Interface comparison results are inverted.
|
|
||||||
|
|
||||||
case "channel.go":
|
|
||||||
// Freezes after recv from closed channel.
|
|
||||||
|
|
||||||
case "float.go", "math.go", "print.go":
|
|
||||||
// Stuck in runtime.printfloat64.
|
|
||||||
|
|
||||||
case "interface.go":
|
|
||||||
// Several comparison tests fail.
|
|
||||||
|
|
||||||
case "cgo/":
|
|
||||||
// CGo does not work on AVR.
|
|
||||||
|
|
||||||
default:
|
|
||||||
avrTests = append(avrTests, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runPlatTests(optionsFromTarget("simavr", sema), avrTests, t)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
@@ -215,13 +179,44 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, name := range tests {
|
for _, name := range tests {
|
||||||
|
if options.Target == "simavr" {
|
||||||
|
// Not all tests are currently supported on AVR.
|
||||||
|
// Skip the ones that aren't.
|
||||||
|
switch name {
|
||||||
|
case "reflect.go":
|
||||||
|
// Reflect tests do not work due to type code issues.
|
||||||
|
continue
|
||||||
|
|
||||||
|
case "gc.go":
|
||||||
|
// Does not pass due to high mark false positive rate.
|
||||||
|
continue
|
||||||
|
|
||||||
|
case "json.go", "stdlib.go", "testing.go", "testing_go118.go":
|
||||||
|
// Breaks interp.
|
||||||
|
continue
|
||||||
|
|
||||||
|
case "channel.go":
|
||||||
|
// Freezes after recv from closed channel.
|
||||||
|
continue
|
||||||
|
|
||||||
|
case "math.go":
|
||||||
|
// Stuck somewhere, not sure what's happening.
|
||||||
|
continue
|
||||||
|
|
||||||
|
case "cgo/":
|
||||||
|
// CGo does not work on AVR.
|
||||||
|
continue
|
||||||
|
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
name := name // redefine to avoid race condition
|
name := name // redefine to avoid race condition
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runTest(name, options, t, nil, nil)
|
runTest(name, options, t, nil, nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if len(spec.Emulator) == 0 || spec.Emulator[0] != "simavr" {
|
if !strings.HasPrefix(spec.Emulator, "simavr ") {
|
||||||
t.Run("env.go", func(t *testing.T) {
|
t.Run("env.go", func(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"})
|
||||||
@@ -255,8 +250,8 @@ func emuCheck(t *testing.T, options compileopts.Options) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("failed to load target spec:", err)
|
t.Fatal("failed to load target spec:", err)
|
||||||
}
|
}
|
||||||
if len(spec.Emulator) != 0 {
|
if spec.Emulator != "" {
|
||||||
_, err := exec.LookPath(spec.Emulator[0])
|
_, err := exec.LookPath(strings.SplitN(spec.Emulator, " ", 2)[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, exec.ErrNotFound) {
|
if errors.Is(err, exec.ErrNotFound) {
|
||||||
t.Skipf("emulator not installed: %q", spec.Emulator[0])
|
t.Skipf("emulator not installed: %q", spec.Emulator[0])
|
||||||
@@ -320,117 +315,27 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
|||||||
t.Fatal("could not read expected output file:", err)
|
t.Fatal("could not read expected output file:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a temporary directory for test output files.
|
config, err := builder.NewConfig(&options)
|
||||||
tmpdir := t.TempDir()
|
|
||||||
|
|
||||||
// Determine whether we're on a system that supports environment variables
|
|
||||||
// and command line parameters (operating systems, WASI) or not (baremetal,
|
|
||||||
// WebAssembly in the browser). If we're on a system without an environment,
|
|
||||||
// we need to pass command line arguments and environment variables through
|
|
||||||
// global variables (built into the binary directly) instead of the
|
|
||||||
// conventional way.
|
|
||||||
spec, err := compileopts.LoadTarget(&options)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("failed to load target spec:", err)
|
t.Fatal(err)
|
||||||
}
|
|
||||||
needsEnvInVars := spec.GOOS == "js"
|
|
||||||
for _, tag := range spec.BuildTags {
|
|
||||||
if tag == "baremetal" {
|
|
||||||
needsEnvInVars = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if needsEnvInVars {
|
|
||||||
runtimeGlobals := make(map[string]string)
|
|
||||||
if len(cmdArgs) != 0 {
|
|
||||||
runtimeGlobals["osArgs"] = strings.Join(cmdArgs, "\x00")
|
|
||||||
}
|
|
||||||
if len(environmentVars) != 0 {
|
|
||||||
runtimeGlobals["osEnv"] = strings.Join(environmentVars, "\x00")
|
|
||||||
}
|
|
||||||
if len(runtimeGlobals) != 0 {
|
|
||||||
// This sets the global variables like they would be set with
|
|
||||||
// `-ldflags="-X=runtime.osArgs=first\x00second`.
|
|
||||||
// The runtime package has two variables (osArgs and osEnv) that are
|
|
||||||
// both strings, from which the parameters and environment variables
|
|
||||||
// are read.
|
|
||||||
options.GlobalValues = map[string]map[string]string{
|
|
||||||
"runtime": runtimeGlobals,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the test binary.
|
// Build the test binary.
|
||||||
binary := filepath.Join(tmpdir, "test")
|
stdout := &bytes.Buffer{}
|
||||||
if spec.GOOS == "windows" {
|
err = buildAndRun("./"+path, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
|
||||||
binary += ".exe"
|
return cmd.Run()
|
||||||
}
|
})
|
||||||
err = Build("./"+path, binary, &options)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printCompilerError(t.Log, err)
|
printCompilerError(t.Log, err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reserve CPU time for the test to run.
|
|
||||||
// This attempts to ensure that the test is not CPU-starved.
|
|
||||||
options.Semaphore <- struct{}{}
|
|
||||||
defer func() { <-options.Semaphore }()
|
|
||||||
|
|
||||||
// Create the test command, taking care of emulators etc.
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
|
||||||
defer cancel()
|
|
||||||
var cmd *exec.Cmd
|
|
||||||
|
|
||||||
// make sure any special vars in the emulator definition are rewritten
|
|
||||||
config := compileopts.Config{Target: spec}
|
|
||||||
emulator := config.Emulator()
|
|
||||||
|
|
||||||
if len(emulator) == 0 {
|
|
||||||
cmd = exec.CommandContext(ctx, binary)
|
|
||||||
} else {
|
|
||||||
args := append(emulator[1:], binary)
|
|
||||||
cmd = exec.CommandContext(ctx, emulator[0], args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(emulator) != 0 && emulator[0] == "wasmtime" {
|
|
||||||
// Allow reading from the current directory.
|
|
||||||
cmd.Args = append(cmd.Args, "--dir=.")
|
|
||||||
for _, v := range environmentVars {
|
|
||||||
cmd.Args = append(cmd.Args, "--env", v)
|
|
||||||
}
|
|
||||||
cmd.Args = append(cmd.Args, cmdArgs...)
|
|
||||||
} else {
|
|
||||||
if !needsEnvInVars {
|
|
||||||
cmd.Args = append(cmd.Args, cmdArgs...) // works on qemu-aarch64 etc
|
|
||||||
cmd.Env = append(cmd.Env, environmentVars...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the test.
|
|
||||||
stdout := &bytes.Buffer{}
|
|
||||||
if len(emulator) != 0 && emulator[0] == "simavr" {
|
|
||||||
cmd.Stdout = os.Stderr
|
|
||||||
cmd.Stderr = stdout
|
|
||||||
} else {
|
|
||||||
cmd.Stdout = stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
}
|
|
||||||
err = cmd.Start()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("failed to start:", err)
|
|
||||||
}
|
|
||||||
err = cmd.Wait()
|
|
||||||
|
|
||||||
if cerr := ctx.Err(); cerr == context.DeadlineExceeded {
|
|
||||||
stdout.WriteString("--- test ran too long, terminating...\n")
|
|
||||||
err = cerr
|
|
||||||
}
|
|
||||||
|
|
||||||
// putchar() prints CRLF, convert it to LF.
|
// putchar() prints CRLF, convert it to LF.
|
||||||
actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1)
|
actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1)
|
||||||
expected = bytes.Replace(expected, []byte{'\r', '\n'}, []byte{'\n'}, -1) // for Windows
|
expected = bytes.Replace(expected, []byte{'\r', '\n'}, []byte{'\n'}, -1) // for Windows
|
||||||
|
|
||||||
if len(emulator) != 0 && emulator[0] == "simavr" {
|
if config.EmulatorName() == "simavr" {
|
||||||
// Strip simavr log formatting.
|
// Strip simavr log formatting.
|
||||||
actual = bytes.Replace(actual, []byte{0x1b, '[', '3', '2', 'm'}, nil, -1)
|
actual = bytes.Replace(actual, []byte{0x1b, '[', '3', '2', 'm'}, nil, -1)
|
||||||
actual = bytes.Replace(actual, []byte{0x1b, '[', '0', 'm'}, nil, -1)
|
actual = bytes.Replace(actual, []byte{0x1b, '[', '0', 'm'}, nil, -1)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
package nxp
|
package nxp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"device/arm"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@@ -378,30 +379,6 @@ 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.
|
||||||
@@ -472,59 +449,178 @@ func (cfg ClockConfigSysPll) Configure(pfd ...uint32) {
|
|||||||
setSysPfd(pfd...)
|
setSysPfd(pfd...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PLL configuration for USB
|
func setSysPfd(value ...uint32) {
|
||||||
type ClockConfigUsbPll struct {
|
for i, val := range value {
|
||||||
Instance uint8 // USB PLL number (1 or 2)
|
pfd528 := CCM_ANALOG.PFD_528.Get() &
|
||||||
LoopDivider uint8 // PLL loop divider: 0 - Fout=Fref*20, 1 - Fout=Fref*22
|
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||||
Src uint8 // Pll clock source, reference _clock_pll_clk_src
|
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 (cfg ClockConfigUsbPll) Configure(pfd ...uint32) {
|
// 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 {
|
switch cfg.Instance {
|
||||||
case 1:
|
case 1:
|
||||||
|
usb = USB1 // Select USB1 HS PHY/PLL
|
||||||
// bypass PLL first
|
phy = USBPHY1 //
|
||||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
|
chrgDetectReg = &USB_ANALOG.USB1_CHRG_DETECT_SET
|
||||||
CCM_ANALOG.PLL_USB1.Set(
|
chrgDetectMsk = USB_ANALOG_USB1_CHRG_DETECT_SET_CHK_CHRG_B |
|
||||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
|
USB_ANALOG_USB1_CHRG_DETECT_SET_EN_B
|
||||||
CCM_ANALOG_PLL_USB1_BYPASS_Msk | src)
|
|
||||||
|
|
||||||
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)
|
|
||||||
CCM_ANALOG.PLL_USB1_SET.Set(
|
|
||||||
(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) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// disable bypass
|
|
||||||
CCM_ANALOG.PLL_USB1_CLR.Set(CCM_ANALOG_PLL_USB1_BYPASS_Msk)
|
|
||||||
|
|
||||||
// update PFDs after update
|
|
||||||
setUsb1Pfd(pfd...)
|
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
// bypass PLL first
|
usb = USB2 // Select USB2 HS PHY/PLL
|
||||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
|
phy = USBPHY2 //
|
||||||
CCM_ANALOG.PLL_USB2.Set(
|
chrgDetectReg = &USB_ANALOG.USB2_CHRG_DETECT_SET
|
||||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
|
chrgDetectMsk = USB_ANALOG_USB2_CHRG_DETECT_SET_CHK_CHRG_B |
|
||||||
CCM_ANALOG_PLL_USB2_BYPASS_Msk | src)
|
USB_ANALOG_USB2_CHRG_DETECT_SET_EN_B
|
||||||
|
default:
|
||||||
|
panic("nxp: invalid USB PHY")
|
||||||
|
}
|
||||||
|
|
||||||
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)
|
// Configure and enable USB PLL clocks
|
||||||
CCM_ANALOG.PLL_USB2.Set(
|
cfg.PllConfig.Configure()
|
||||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
|
|
||||||
CCM_ANALOG_PLL_USB2_ENABLE_Msk | CCM_ANALOG_PLL_USB2_POWER_Msk |
|
|
||||||
CCM_ANALOG_PLL_USB2_EN_USB_CLKS_Msk | sel)
|
|
||||||
|
|
||||||
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK_Msk) {
|
// 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
|
||||||
|
type ClockConfigUsbPll struct {
|
||||||
|
Instance uint8 // USB PLL number (1 or 2)
|
||||||
|
LoopDivider uint8 // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
|
||||||
|
Src uint8 // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
|
||||||
|
Pfd []uint32 // Phase fractional divisors (len=4, or nil for boot default)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg ClockConfigUsbPll) Configure() {
|
||||||
|
// select USB peripheral registers based on receiver's Instance
|
||||||
|
switch cfg.Instance {
|
||||||
|
case 1: // USB1 PLL
|
||||||
|
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
|
||||||
|
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) &
|
||||||
|
CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
|
||||||
|
CCM_ANALOG.PLL_USB1.Set(
|
||||||
|
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
|
||||||
|
CCM_ANALOG_PLL_USB1_BYPASS | 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)
|
||||||
|
if nil != cfg.Pfd {
|
||||||
|
setUsb1Pfd(cfg.Pfd...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 2: // USB2 PLL
|
||||||
|
if CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_ENABLE) {
|
||||||
|
// PLL already configured, enable USB clocks
|
||||||
|
CCM_ANALOG.PLL_USB2.SetBits(CCM_ANALOG_PLL_USB2_EN_USB_CLKS)
|
||||||
|
} else {
|
||||||
|
// bypass PLL first
|
||||||
|
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) &
|
||||||
|
CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
|
||||||
|
CCM_ANALOG.PLL_USB2.Set(
|
||||||
|
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
|
||||||
|
CCM_ANALOG_PLL_USB2_BYPASS | src)
|
||||||
|
// reconfigure PLL
|
||||||
|
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) &
|
||||||
|
CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk
|
||||||
|
CCM_ANALOG.PLL_USB2.Set(
|
||||||
|
(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_EN_USB_CLKS | sel)
|
||||||
|
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK) {
|
||||||
|
}
|
||||||
|
// disable bypass
|
||||||
|
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS)
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable 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`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -142,18 +142,14 @@ func (mpu *MPU_Type) Enable(enable bool) {
|
|||||||
if enable {
|
if enable {
|
||||||
mpu.CTRL.Set(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk)
|
mpu.CTRL.Set(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk)
|
||||||
SystemControl.SHCSR.SetBits(SCB_SHCSR_MEMFAULTENA_Msk)
|
SystemControl.SHCSR.SetBits(SCB_SHCSR_MEMFAULTENA_Msk)
|
||||||
arm.AsmFull(`
|
arm.Asm("dsb 0xF")
|
||||||
dsb 0xF
|
arm.Asm("isb 0xF")
|
||||||
isb 0xF
|
|
||||||
`, nil)
|
|
||||||
enableDcache(true)
|
enableDcache(true)
|
||||||
enableIcache(true)
|
enableIcache(true)
|
||||||
} else {
|
} else {
|
||||||
enableIcache(false)
|
enableIcache(false)
|
||||||
enableDcache(false)
|
enableDcache(false)
|
||||||
arm.AsmFull(`
|
arm.Asm("dmb 0xF")
|
||||||
dmb 0xF
|
|
||||||
`, nil)
|
|
||||||
SystemControl.SHCSR.ClearBits(SCB_SHCSR_MEMFAULTENA_Msk)
|
SystemControl.SHCSR.ClearBits(SCB_SHCSR_MEMFAULTENA_Msk)
|
||||||
mpu.CTRL.ClearBits(MPU_CTRL_ENABLE_Msk)
|
mpu.CTRL.ClearBits(MPU_CTRL_ENABLE_Msk)
|
||||||
}
|
}
|
||||||
@@ -188,31 +184,21 @@ func (mpu *MPU_Type) SetRASR(size RegionSize, access AccessPerms, ext Extension,
|
|||||||
func enableIcache(enable bool) {
|
func enableIcache(enable bool) {
|
||||||
if enable != SystemControl.CCR.HasBits(SCB_CCR_IC_Msk) {
|
if enable != SystemControl.CCR.HasBits(SCB_CCR_IC_Msk) {
|
||||||
if enable {
|
if enable {
|
||||||
arm.AsmFull(`
|
arm.Asm("dsb 0xF")
|
||||||
dsb 0xF
|
arm.Asm("isb 0xF")
|
||||||
isb 0xF
|
|
||||||
`, nil)
|
|
||||||
SystemControl.ICIALLU.Set(0)
|
SystemControl.ICIALLU.Set(0)
|
||||||
arm.AsmFull(`
|
arm.Asm("dsb 0xF")
|
||||||
dsb 0xF
|
arm.Asm("isb 0xF")
|
||||||
isb 0xF
|
|
||||||
`, nil)
|
|
||||||
SystemControl.CCR.SetBits(SCB_CCR_IC_Msk)
|
SystemControl.CCR.SetBits(SCB_CCR_IC_Msk)
|
||||||
arm.AsmFull(`
|
arm.Asm("dsb 0xF")
|
||||||
dsb 0xF
|
arm.Asm("isb 0xF")
|
||||||
isb 0xF
|
|
||||||
`, nil)
|
|
||||||
} else {
|
} else {
|
||||||
arm.AsmFull(`
|
arm.Asm("dsb 0xF")
|
||||||
dsb 0xF
|
arm.Asm("isb 0xF")
|
||||||
isb 0xF
|
|
||||||
`, nil)
|
|
||||||
SystemControl.CCR.ClearBits(SCB_CCR_IC_Msk)
|
SystemControl.CCR.ClearBits(SCB_CCR_IC_Msk)
|
||||||
SystemControl.ICIALLU.Set(0)
|
SystemControl.ICIALLU.Set(0)
|
||||||
arm.AsmFull(`
|
arm.Asm("dsb 0xF")
|
||||||
dsb 0xF
|
arm.Asm("isb 0xF")
|
||||||
isb 0xF
|
|
||||||
`, nil)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -227,9 +213,7 @@ func enableDcache(enable bool) {
|
|||||||
if enable != SystemControl.CCR.HasBits(SCB_CCR_DC_Msk) {
|
if enable != SystemControl.CCR.HasBits(SCB_CCR_DC_Msk) {
|
||||||
if enable {
|
if enable {
|
||||||
SystemControl.CSSELR.Set(0)
|
SystemControl.CSSELR.Set(0)
|
||||||
arm.AsmFull(`
|
arm.Asm("dsb 0xF")
|
||||||
dsb 0xF
|
|
||||||
`, nil)
|
|
||||||
ccsidr := SystemControl.CCSIDR.Get()
|
ccsidr := SystemControl.CCSIDR.Get()
|
||||||
sets := (ccsidr & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos
|
sets := (ccsidr & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos
|
||||||
for sets != 0 {
|
for sets != 0 {
|
||||||
@@ -242,23 +226,15 @@ func enableDcache(enable bool) {
|
|||||||
}
|
}
|
||||||
sets--
|
sets--
|
||||||
}
|
}
|
||||||
arm.AsmFull(`
|
arm.Asm("dsb 0xF")
|
||||||
dsb 0xF
|
|
||||||
`, nil)
|
|
||||||
SystemControl.CCR.SetBits(SCB_CCR_DC_Msk)
|
SystemControl.CCR.SetBits(SCB_CCR_DC_Msk)
|
||||||
arm.AsmFull(`
|
arm.Asm("dsb 0xF")
|
||||||
dsb 0xF
|
arm.Asm("isb 0xF")
|
||||||
isb 0xF
|
|
||||||
`, nil)
|
|
||||||
} else {
|
} else {
|
||||||
SystemControl.CSSELR.Set(0)
|
SystemControl.CSSELR.Set(0)
|
||||||
arm.AsmFull(`
|
arm.Asm("dsb 0xF")
|
||||||
dsb 0xF
|
|
||||||
`, nil)
|
|
||||||
SystemControl.CCR.ClearBits(SCB_CCR_DC_Msk)
|
SystemControl.CCR.ClearBits(SCB_CCR_DC_Msk)
|
||||||
arm.AsmFull(`
|
arm.Asm("dsb 0xF")
|
||||||
dsb 0xF
|
|
||||||
`, nil)
|
|
||||||
dcacheCcsidr.Set(SystemControl.CCSIDR.Get())
|
dcacheCcsidr.Set(SystemControl.CCSIDR.Get())
|
||||||
dcacheSets.Set((dcacheCcsidr.Get() & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos)
|
dcacheSets.Set((dcacheCcsidr.Get() & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos)
|
||||||
for dcacheSets.Get() != 0 {
|
for dcacheSets.Get() != 0 {
|
||||||
@@ -271,10 +247,88 @@ func enableDcache(enable bool) {
|
|||||||
}
|
}
|
||||||
dcacheSets.Set(dcacheSets.Get() - 1)
|
dcacheSets.Set(dcacheSets.Get() - 1)
|
||||||
}
|
}
|
||||||
arm.AsmFull(`
|
arm.Asm("dsb 0xF")
|
||||||
dsb 0xF
|
arm.Asm("isb 0xF")
|
||||||
isb 0xF
|
|
||||||
`, nil)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Reads multiple rp2040 ADC channels concurrently. Including the internal temperature sensor
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type celsius float32
|
||||||
|
|
||||||
|
func (c celsius) String() string {
|
||||||
|
return fmt.Sprintf("%4.1f℃", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rp2040 ADC is 12 bits. Reading are shifted <<4 to fill the 16-bit range.
|
||||||
|
var adcReading [3]uint16
|
||||||
|
|
||||||
|
func readADC(a machine.ADC, w time.Duration, i int) {
|
||||||
|
for {
|
||||||
|
adcReading[i] = a.Get()
|
||||||
|
time.Sleep(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
machine.InitADC()
|
||||||
|
a0 := machine.ADC{machine.ADC0} // GPIO26 input
|
||||||
|
a1 := machine.ADC{machine.ADC1} // GPIO27 input
|
||||||
|
a2 := machine.ADC{machine.ADC2} // GPIO28 input
|
||||||
|
t := machine.ADC_TEMP_SENSOR // Internal Temperature sensor
|
||||||
|
// Configure sets the GPIOs to PinAnalog mode
|
||||||
|
a0.Configure(machine.ADCConfig{})
|
||||||
|
a1.Configure(machine.ADCConfig{})
|
||||||
|
a2.Configure(machine.ADCConfig{})
|
||||||
|
// Configure powers on the temperature sensor
|
||||||
|
t.Configure(machine.ADCConfig{})
|
||||||
|
|
||||||
|
// Safe to read concurrently
|
||||||
|
go readADC(a0, 10*time.Millisecond, 0)
|
||||||
|
go readADC(a1, 17*time.Millisecond, 1)
|
||||||
|
go readADC(a2, 29*time.Millisecond, 2)
|
||||||
|
|
||||||
|
for {
|
||||||
|
fmt.Printf("ADC0: %5d ADC1: %5d ADC2: %5d Temp: %v\n\r", adcReading[0], adcReading[1], adcReading[2], celsius(float32(t.ReadTemperature())/1000))
|
||||||
|
time.Sleep(1000 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
// This is a echo console running on the os.Stdin and os.Stdout.
|
|
||||||
// Stdin and os.Stdout are connected to machine.Serial in the baremetal target.
|
|
||||||
//
|
|
||||||
// Serial can be switched with the -serial option as follows
|
|
||||||
// 1. tinygo flash -target wioterminal -serial usb examples/echo2
|
|
||||||
// 2. tinygo flash -target wioterminal -serial uart examples/echo2
|
|
||||||
//
|
|
||||||
// This example will also work with standard Go.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
fmt.Printf("Echo console enabled. Type something then press enter:\r\n")
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
|
||||||
|
|
||||||
for {
|
|
||||||
msg := ""
|
|
||||||
fmt.Scanf("%s\n", &msg)
|
|
||||||
fmt.Printf("You typed (scanf) : %s\r\n", msg)
|
|
||||||
|
|
||||||
if scanner.Scan() {
|
|
||||||
fmt.Printf("You typed (scanner) : %s\r\n", scanner.Text())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"machine/usb"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var keyboard = machine.USB.Keyboard()
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
for !machine.USB.Ready() {
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (GNU screen)
|
||||||
|
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 (GNU screen)
|
||||||
|
keyboard.Down(usb.KeyModifierCtrl)
|
||||||
|
// Ctrl-X is the prefix sequence in my GNU screen configuration
|
||||||
|
keyboard.Press(usb.KeyX)
|
||||||
|
keyboard.Up(usb.KeyModifierCtrl)
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
// Backslash (Prefix-\) is the GNU screen command to kill window
|
||||||
|
keyboard.Press(usb.KeyBackslash)
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
// Confirm "Kill window (Y/N)?" prompt
|
||||||
|
keyboard.Press(usb.KeyY)
|
||||||
|
keyboard.Press(usb.KeyEnter)
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
|
// Close terminal
|
||||||
|
keyboard.Down(usb.KeyModifierCtrl)
|
||||||
|
// Ctrl-D exits the shell (sends a resemblance of EOF, I believe?)
|
||||||
|
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"))
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// 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.Serial
|
||||||
|
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,133 @@
|
|||||||
|
// Package fuzz is a shim to allow compilation against Go 1.18.
|
||||||
|
// It only defines a single type to work with testing/internal/testdeps,
|
||||||
|
// and hide all the other new dependencies from this package.
|
||||||
|
package fuzz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CorpusEntry represents an individual input for fuzzing.
|
||||||
|
//
|
||||||
|
// We must use an equivalent type in the testing and testing/internal/testdeps
|
||||||
|
// packages, but testing can't import this package directly, and we don't want
|
||||||
|
// to export this type from testing. Instead, we use the same struct type and
|
||||||
|
// use a type alias (not a defined type) for convenience.
|
||||||
|
type CorpusEntry = struct {
|
||||||
|
Parent string
|
||||||
|
|
||||||
|
// Path is the path of the corpus file, if the entry was loaded from disk.
|
||||||
|
// For other entries, including seed values provided by f.Add, Path is the
|
||||||
|
// name of the test, e.g. seed#0 or its hash.
|
||||||
|
Path string
|
||||||
|
|
||||||
|
// Data is the raw input data. Data should only be populated for seed
|
||||||
|
// values. For on-disk corpus files, Data will be nil, as it will be loaded
|
||||||
|
// from disk using Path.
|
||||||
|
Data []byte
|
||||||
|
|
||||||
|
// Values is the unmarshaled values from a corpus file.
|
||||||
|
Values []any
|
||||||
|
|
||||||
|
Generation int
|
||||||
|
|
||||||
|
// IsSeed indicates whether this entry is part of the seed corpus.
|
||||||
|
IsSeed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// CoordinateFuzzingOpts is a set of arguments for CoordinateFuzzing.
|
||||||
|
// The zero value is valid for each field unless specified otherwise.
|
||||||
|
type CoordinateFuzzingOpts struct {
|
||||||
|
// Log is a writer for logging progress messages and warnings.
|
||||||
|
// If nil, io.Discard will be used instead.
|
||||||
|
Log io.Writer
|
||||||
|
|
||||||
|
// Timeout is the amount of wall clock time to spend fuzzing after the corpus
|
||||||
|
// has loaded. If zero, there will be no time limit.
|
||||||
|
Timeout time.Duration
|
||||||
|
|
||||||
|
// Limit is the number of random values to generate and test. If zero,
|
||||||
|
// there will be no limit on the number of generated values.
|
||||||
|
Limit int64
|
||||||
|
|
||||||
|
// MinimizeTimeout is the amount of wall clock time to spend minimizing
|
||||||
|
// after discovering a crasher. If zero, there will be no time limit. If
|
||||||
|
// MinimizeTimeout and MinimizeLimit are both zero, then minimization will
|
||||||
|
// be disabled.
|
||||||
|
MinimizeTimeout time.Duration
|
||||||
|
|
||||||
|
// MinimizeLimit is the maximum number of calls to the fuzz function to be
|
||||||
|
// made while minimizing after finding a crash. If zero, there will be no
|
||||||
|
// limit. Calls to the fuzz function made when minimizing also count toward
|
||||||
|
// Limit. If MinimizeTimeout and MinimizeLimit are both zero, then
|
||||||
|
// minimization will be disabled.
|
||||||
|
MinimizeLimit int64
|
||||||
|
|
||||||
|
// parallel is the number of worker processes to run in parallel. If zero,
|
||||||
|
// CoordinateFuzzing will run GOMAXPROCS workers.
|
||||||
|
Parallel int
|
||||||
|
|
||||||
|
// Seed is a list of seed values added by the fuzz target with testing.F.Add
|
||||||
|
// and in testdata.
|
||||||
|
Seed []CorpusEntry
|
||||||
|
|
||||||
|
// Types is the list of types which make up a corpus entry.
|
||||||
|
// Types must be set and must match values in Seed.
|
||||||
|
Types []reflect.Type
|
||||||
|
|
||||||
|
// CorpusDir is a directory where files containing values that crash the
|
||||||
|
// code being tested may be written. CorpusDir must be set.
|
||||||
|
CorpusDir string
|
||||||
|
|
||||||
|
// CacheDir is a directory containing additional "interesting" values.
|
||||||
|
// The fuzzer may derive new values from these, and may write new values here.
|
||||||
|
CacheDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CoordinateFuzzing creates several worker processes and communicates with
|
||||||
|
// them to test random inputs that could trigger crashes and expose bugs.
|
||||||
|
// The worker processes run the same binary in the same directory with the
|
||||||
|
// same environment variables as the coordinator process. Workers also run
|
||||||
|
// with the same arguments as the coordinator, except with the -test.fuzzworker
|
||||||
|
// flag prepended to the argument list.
|
||||||
|
//
|
||||||
|
// If a crash occurs, the function will return an error containing information
|
||||||
|
// about the crash, which can be reported to the user.
|
||||||
|
func CoordinateFuzzing(ctx context.Context, opts CoordinateFuzzingOpts) (err error) {
|
||||||
|
return errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadCorpus reads the corpus from the provided dir. The returned corpus
|
||||||
|
// entries are guaranteed to match the given types. Any malformed files will
|
||||||
|
// be saved in a MalformedCorpusError and returned, along with the most recent
|
||||||
|
// error.
|
||||||
|
func ReadCorpus(dir string, types []reflect.Type) ([]CorpusEntry, error) {
|
||||||
|
return nil, errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckCorpus verifies that the types in vals match the expected types
|
||||||
|
// provided.
|
||||||
|
func CheckCorpus(vals []any, types []reflect.Type) error {
|
||||||
|
return errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResetCoverage() {}
|
||||||
|
func SnapshotCoverage() {}
|
||||||
|
|
||||||
|
// RunFuzzWorker is called in a worker process to communicate with the
|
||||||
|
// coordinator process in order to fuzz random inputs. RunFuzzWorker loops
|
||||||
|
// until the coordinator tells it to stop.
|
||||||
|
//
|
||||||
|
// fn is a wrapper on the fuzz function. It may return an error to indicate
|
||||||
|
// a given input "crashed". The coordinator will also record a crasher if
|
||||||
|
// the function times out or terminates the process.
|
||||||
|
//
|
||||||
|
// RunFuzzWorker returns an error if it could not communicate with the
|
||||||
|
// coordinator process.
|
||||||
|
func RunFuzzWorker(ctx context.Context, fn func(CorpusEntry) error) error {
|
||||||
|
return errors.New("not implemented")
|
||||||
|
}
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
|
#ifdef __MACH__
|
||||||
|
.global _tinygo_startTask
|
||||||
|
_tinygo_startTask:
|
||||||
|
#else
|
||||||
.section .text.tinygo_startTask
|
.section .text.tinygo_startTask
|
||||||
.global tinygo_startTask
|
.global tinygo_startTask
|
||||||
.type tinygo_startTask, %function
|
.type tinygo_startTask, %function
|
||||||
tinygo_startTask:
|
tinygo_startTask:
|
||||||
|
#endif
|
||||||
.cfi_startproc
|
.cfi_startproc
|
||||||
// Small assembly stub for starting a goroutine. This is already run on the
|
// Small assembly stub for starting a goroutine. This is already run on the
|
||||||
// new stack, with the callee-saved registers already loaded.
|
// new stack, with the callee-saved registers already loaded.
|
||||||
@@ -23,13 +28,25 @@ tinygo_startTask:
|
|||||||
blr x19
|
blr x19
|
||||||
|
|
||||||
// After return, exit this goroutine. This is a tail call.
|
// After return, exit this goroutine. This is a tail call.
|
||||||
|
#ifdef __MACH__
|
||||||
|
b _tinygo_pause
|
||||||
|
#else
|
||||||
b tinygo_pause
|
b tinygo_pause
|
||||||
|
#endif
|
||||||
.cfi_endproc
|
.cfi_endproc
|
||||||
|
#ifndef __MACH__
|
||||||
.size tinygo_startTask, .-tinygo_startTask
|
.size tinygo_startTask, .-tinygo_startTask
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __MACH__
|
||||||
|
.global _tinygo_swapTask
|
||||||
|
_tinygo_swapTask:
|
||||||
|
#else
|
||||||
.global tinygo_swapTask
|
.global tinygo_swapTask
|
||||||
.type tinygo_swapTask, %function
|
.type tinygo_swapTask, %function
|
||||||
tinygo_swapTask:
|
tinygo_swapTask:
|
||||||
|
#endif
|
||||||
// This function gets the following parameters:
|
// This function gets the following parameters:
|
||||||
// x0 = newStack uintptr
|
// x0 = newStack uintptr
|
||||||
// x1 = oldStack *uintptr
|
// x1 = oldStack *uintptr
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
//go:build badger2040
|
||||||
|
// +build badger2040
|
||||||
|
|
||||||
|
// This contains the pin mappings for the Badger 2040 Connect board.
|
||||||
|
//
|
||||||
|
// For more information, see: https://shop.pimoroni.com/products/badger-2040
|
||||||
|
// Also
|
||||||
|
// - Badger 2040 schematic: https://cdn.shopify.com/s/files/1/0174/1800/files/badger_2040_schematic.pdf?v=1645702148
|
||||||
|
//
|
||||||
|
package machine
|
||||||
|
|
||||||
|
const (
|
||||||
|
LED Pin = GPIO25
|
||||||
|
|
||||||
|
BUTTON_A Pin = GPIO12
|
||||||
|
BUTTON_B Pin = GPIO13
|
||||||
|
BUTTON_C Pin = GPIO14
|
||||||
|
BUTTON_UP Pin = GPIO15
|
||||||
|
BUTTON_DOWN Pin = GPIO11
|
||||||
|
BUTTON_USER Pin = GPIO23
|
||||||
|
|
||||||
|
EPD_BUSY_PIN Pin = GPIO26
|
||||||
|
EPD_RESET_PIN Pin = GPIO21
|
||||||
|
EPD_DC_PIN Pin = GPIO20
|
||||||
|
EPD_CS_PIN Pin = GPIO17
|
||||||
|
EPD_SCK_PIN Pin = GPIO18
|
||||||
|
EPD_SDO_PIN Pin = GPIO19
|
||||||
|
|
||||||
|
VBUS_DETECT Pin = GPIO24
|
||||||
|
BATTERY Pin = GPIO29
|
||||||
|
ENABLE_3V3 Pin = GPIO10
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C pins
|
||||||
|
const (
|
||||||
|
I2C0_SDA_PIN Pin = GPIO4
|
||||||
|
I2C0_SCL_PIN Pin = GPIO5
|
||||||
|
|
||||||
|
I2C1_SDA_PIN Pin = NoPin
|
||||||
|
I2C1_SCL_PIN Pin = NoPin
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI pins.
|
||||||
|
const (
|
||||||
|
SPI0_SCK_PIN Pin = GPIO18
|
||||||
|
SPI0_SDO_PIN Pin = GPIO19
|
||||||
|
SPI0_SDI_PIN Pin = GPIO16
|
||||||
|
|
||||||
|
SPI1_SCK_PIN Pin = NoPin
|
||||||
|
SPI1_SDO_PIN Pin = NoPin
|
||||||
|
SPI1_SDI_PIN Pin = NoPin
|
||||||
|
)
|
||||||
|
|
||||||
|
// QSPI pins¿?
|
||||||
|
const (
|
||||||
|
/* TODO
|
||||||
|
SPI0_SD0_PIN Pin = QSPI_SD0
|
||||||
|
SPI0_SD1_PIN Pin = QSPI_SD1
|
||||||
|
SPI0_SD2_PIN Pin = QSPI_SD2
|
||||||
|
SPI0_SD3_PIN Pin = QSPI_SD3
|
||||||
|
SPI0_SCK_PIN Pin = QSPI_SCLKGPIO6
|
||||||
|
SPI0_CS_PIN Pin = QSPI_CS
|
||||||
|
|
||||||
|
*/
|
||||||
|
)
|
||||||
|
|
||||||
|
// Onboard crystal oscillator frequency, in MHz.
|
||||||
|
const (
|
||||||
|
xoscFreq = 12 // MHz
|
||||||
|
)
|
||||||
|
|
||||||
|
// USB CDC identifiers
|
||||||
|
const (
|
||||||
|
usb_STRING_PRODUCT = "Badger 2040"
|
||||||
|
usb_STRING_MANUFACTURER = "Pimoroni"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
usb_VID uint16 = 0x2e8a
|
||||||
|
usb_PID uint16 = 0x0003
|
||||||
|
)
|
||||||
@@ -42,6 +42,9 @@ const (
|
|||||||
SPIQ = IO17
|
SPIQ = IO17
|
||||||
U0RXD = IO20
|
U0RXD = IO20
|
||||||
U0TXD = IO21
|
U0TXD = IO21
|
||||||
|
|
||||||
|
UART_TX_PIN = U0TXD
|
||||||
|
UART_RX_PIN = U0RXD
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
//go:build nrf52840 || circuitplay_bluefruit || reelboard || clue || itsybitsy_nrf52840
|
||||||
|
// +build nrf52840 circuitplay_bluefruit reelboard clue itsybitsy_nrf52840
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
// Hardware pins
|
||||||
|
const (
|
||||||
|
P0_00 Pin = 0
|
||||||
|
P0_01 Pin = 1
|
||||||
|
P0_02 Pin = 2
|
||||||
|
P0_03 Pin = 3
|
||||||
|
P0_04 Pin = 4
|
||||||
|
P0_05 Pin = 5
|
||||||
|
P0_06 Pin = 6
|
||||||
|
P0_07 Pin = 7
|
||||||
|
P0_08 Pin = 8
|
||||||
|
P0_09 Pin = 9
|
||||||
|
P0_10 Pin = 10
|
||||||
|
P0_11 Pin = 11
|
||||||
|
P0_12 Pin = 12
|
||||||
|
P0_13 Pin = 13
|
||||||
|
P0_14 Pin = 14
|
||||||
|
P0_15 Pin = 15
|
||||||
|
P0_16 Pin = 16
|
||||||
|
P0_17 Pin = 17
|
||||||
|
P0_18 Pin = 18
|
||||||
|
P0_19 Pin = 19
|
||||||
|
P0_20 Pin = 20
|
||||||
|
P0_21 Pin = 21
|
||||||
|
P0_22 Pin = 22
|
||||||
|
P0_23 Pin = 23
|
||||||
|
P0_24 Pin = 24
|
||||||
|
P0_25 Pin = 25
|
||||||
|
P0_26 Pin = 26
|
||||||
|
P0_27 Pin = 27
|
||||||
|
P0_28 Pin = 28
|
||||||
|
P0_29 Pin = 29
|
||||||
|
P0_30 Pin = 30
|
||||||
|
P0_31 Pin = 31
|
||||||
|
P1_00 Pin = 32
|
||||||
|
P1_01 Pin = 33
|
||||||
|
P1_02 Pin = 34
|
||||||
|
P1_03 Pin = 35
|
||||||
|
P1_04 Pin = 36
|
||||||
|
P1_05 Pin = 37
|
||||||
|
P1_06 Pin = 38
|
||||||
|
P1_07 Pin = 39
|
||||||
|
P1_08 Pin = 40
|
||||||
|
P1_09 Pin = 41
|
||||||
|
P1_10 Pin = 42
|
||||||
|
P1_11 Pin = 43
|
||||||
|
P1_12 Pin = 44
|
||||||
|
P1_13 Pin = 45
|
||||||
|
P1_14 Pin = 46
|
||||||
|
P1_15 Pin = 47
|
||||||
|
)
|
||||||
@@ -5,6 +5,7 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/nxp"
|
"device/nxp"
|
||||||
|
"machine/usb"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -56,21 +57,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,6 +106,15 @@ 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 |
|
||||||
// #===========#===========#=============#===============#
|
// #===========#===========#=============#===============#
|
||||||
@@ -237,6 +247,8 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// #==================================================================#
|
||||||
|
// | SPI |
|
||||||
// #===========#==========#===============#===========================#
|
// #===========#==========#===============#===========================#
|
||||||
// | Interface | Hardware | Clock(Freq) | SDI/SDO/SCK/CS : Alt |
|
// | Interface | Hardware | Clock(Freq) | SDI/SDO/SCK/CS : Alt |
|
||||||
// #===========#==========#===============#=================-=========#
|
// #===========#==========#===============#=================-=========#
|
||||||
@@ -261,6 +273,70 @@ const (
|
|||||||
SPI3_CS_PIN = D36
|
SPI3_CS_PIN = D36
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
SPI0 = SPI1 // SPI0 is an alias of SPI1 (LPSPI4)
|
||||||
|
SPI1 = &_SPI1
|
||||||
|
_SPI1 = SPI{
|
||||||
|
Bus: nxp.LPSPI4,
|
||||||
|
muxSDI: muxSelect{ // D12 (PB1 [B0_01])
|
||||||
|
mux: nxp.IOMUXC_LPSPI4_SDI_SELECT_INPUT_DAISY_GPIO_B0_01_ALT3,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI4_SDI_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxSDO: muxSelect{ // D11 (PB2 [B0_02])
|
||||||
|
mux: nxp.IOMUXC_LPSPI4_SDO_SELECT_INPUT_DAISY_GPIO_B0_02_ALT3,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI4_SDO_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxSCK: muxSelect{ // D13 (PB3 [B0_03])
|
||||||
|
mux: nxp.IOMUXC_LPSPI4_SCK_SELECT_INPUT_DAISY_GPIO_B0_03_ALT3,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI4_SCK_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxCS: muxSelect{ // D10 (PB0 [B0_00])
|
||||||
|
mux: nxp.IOMUXC_LPSPI4_PCS0_SELECT_INPUT_DAISY_GPIO_B0_00_ALT3,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI4_PCS0_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
SPI2 = &_SPI2
|
||||||
|
_SPI2 = SPI{
|
||||||
|
Bus: nxp.LPSPI3,
|
||||||
|
muxSDI: muxSelect{ // D1 (PA2 [AD_B0_02])
|
||||||
|
mux: nxp.IOMUXC_LPSPI3_SDI_SELECT_INPUT_DAISY_GPIO_AD_B0_02_ALT7,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI3_SDI_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxSDO: muxSelect{ // D26 (PA30 [AD_B1_14])
|
||||||
|
mux: nxp.IOMUXC_LPSPI3_SDO_SELECT_INPUT_DAISY_GPIO_AD_B1_14_ALT2,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI3_SDO_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxSCK: muxSelect{ // D27 (PA31 [AD_B1_15])
|
||||||
|
mux: nxp.IOMUXC_LPSPI3_SCK_SELECT_INPUT_DAISY_GPIO_AD_B1_15,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI3_SCK_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxCS: muxSelect{ // D0 (PA3 [AD_B0_03])
|
||||||
|
mux: nxp.IOMUXC_LPSPI3_PCS0_SELECT_INPUT_DAISY_GPIO_AD_B0_03_ALT7,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI3_PCS0_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
SPI3 = &_SPI3
|
||||||
|
_SPI3 = SPI{
|
||||||
|
Bus: nxp.LPSPI1,
|
||||||
|
muxSDI: muxSelect{ // D34 (PC15 [SD_B0_03])
|
||||||
|
mux: nxp.IOMUXC_LPSPI1_SDI_SELECT_INPUT_DAISY_GPIO_SD_B0_03_ALT4,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI1_SDI_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxSDO: muxSelect{ // D35 (PC14 [SD_B0_02])
|
||||||
|
mux: nxp.IOMUXC_LPSPI1_SDO_SELECT_INPUT_DAISY_GPIO_SD_B0_02_ALT4,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI1_SDO_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxSCK: muxSelect{ // D37 (PC12 [SD_B0_00])
|
||||||
|
mux: nxp.IOMUXC_LPSPI1_SCK_SELECT_INPUT_DAISY_GPIO_SD_B0_00_ALT4,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI1_SCK_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxCS: muxSelect{ // D36 (PC13 [SD_B0_01])
|
||||||
|
mux: nxp.IOMUXC_LPSPI1_PCS0_SELECT_INPUT_DAISY_GPIO_SD_B0_01_ALT4,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI1_PCS0_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// #====================================================#
|
// #====================================================#
|
||||||
// | I2C |
|
// | I2C |
|
||||||
// #===========#==========#=============#===============#
|
// #===========#==========#=============#===============#
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
//go:build thingplus_rp2040
|
||||||
|
// +build thingplus_rp2040
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
const (
|
||||||
|
LED = GPIO25
|
||||||
|
|
||||||
|
// Onboard crystal oscillator frequency, in MHz.
|
||||||
|
xoscFreq = 12 // MHz
|
||||||
|
)
|
||||||
|
|
||||||
|
// GPIO Pins
|
||||||
|
const (
|
||||||
|
GP0 Pin = GPIO0 // TX
|
||||||
|
GP1 Pin = GPIO1 // RX
|
||||||
|
GP2 Pin = GPIO2 // SCK
|
||||||
|
GP3 Pin = GPIO3 // COPI
|
||||||
|
GP4 Pin = GPIO4 // CIPO
|
||||||
|
|
||||||
|
GP6 Pin = GPIO6 // SDA
|
||||||
|
GP7 Pin = GPIO7 // SCL (connected to GPIO23 as well)
|
||||||
|
GP8 Pin = GPIO8 // WS2812 RGB LED
|
||||||
|
GP9 Pin = GPIO9 // muSDcard DATA3 / CS
|
||||||
|
GP10 Pin = GPIO10 // muSDcard DATA2
|
||||||
|
GP11 Pin = GPIO11 // muSDcard DATA1
|
||||||
|
GP12 Pin = GPIO12 // muSDcard DATA0 / CIPO
|
||||||
|
|
||||||
|
GP14 Pin = GPIO14 // muSDcard CLK /SCLK
|
||||||
|
GP15 Pin = GPIO15 // muSDcard CMD / COPI
|
||||||
|
GP16 Pin = GPIO16 // 16
|
||||||
|
GP17 Pin = GPIO17 // 17
|
||||||
|
GP18 Pin = GPIO18 // 18
|
||||||
|
GP19 Pin = GPIO19 // 19
|
||||||
|
GP20 Pin = GPIO20 // 20
|
||||||
|
GP21 Pin = GPIO21 // 21
|
||||||
|
GP22 Pin = GPIO22 // 22
|
||||||
|
GP23 Pin = GPIO23 // Connected to GPIO7
|
||||||
|
GP25 Pin = GPIO25 // Status blue LED
|
||||||
|
GP26 Pin = GPIO26 // ADC0
|
||||||
|
GP27 Pin = GPIO27 // ADC1
|
||||||
|
GP28 Pin = GPIO28 // ADC2
|
||||||
|
GP29 Pin = GPIO29 // ADC3
|
||||||
|
)
|
||||||
|
|
||||||
|
// Analog pins
|
||||||
|
const (
|
||||||
|
A0 = GPIO26
|
||||||
|
A1 = GPIO27
|
||||||
|
A2 = GPIO28
|
||||||
|
A3 = GPIO29
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C Pins.
|
||||||
|
const (
|
||||||
|
I2C0_SCL_PIN = GPIO6 // N/A
|
||||||
|
I2C0_SDA_PIN = GPIO7 // N/A
|
||||||
|
|
||||||
|
I2C1_SDA_PIN = GPIO6
|
||||||
|
I2C1_SCL_PIN = GPIO7
|
||||||
|
|
||||||
|
SDA_PIN = I2C1_SDA_PIN
|
||||||
|
SCL_PIN = I2C1_SCL_PIN
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI default pins
|
||||||
|
const (
|
||||||
|
// Default Serial Clock Bus 0 for SPI communications
|
||||||
|
SPI0_SCK_PIN = GPIO2
|
||||||
|
// Default Serial Out Bus 0 for SPI communications
|
||||||
|
SPI0_SDO_PIN = GPIO3 // Tx
|
||||||
|
// Default Serial In Bus 0 for SPI communications
|
||||||
|
SPI0_SDI_PIN = GPIO4 // Rx
|
||||||
|
|
||||||
|
// Default Serial Clock Bus 1 for SPI communications to muSDcard
|
||||||
|
SPI1_SCK_PIN = GPIO14
|
||||||
|
// Default Serial Out Bus 1 for SPI communications to muSDcard
|
||||||
|
SPI1_SDO_PIN = GPIO15 // Tx
|
||||||
|
// Default Serial In Bus 1 for SPI communications to muSDcard
|
||||||
|
SPI1_SDI_PIN = GPIO12 // Rx
|
||||||
|
)
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
//go:build xiao_ble
|
||||||
|
// +build xiao_ble
|
||||||
|
|
||||||
|
// This file contains the pin mappings for the Seeed XIAO BLE nRF52840 [Sense] boards.
|
||||||
|
//
|
||||||
|
// Seeed XIAO BLE is an ultra-small size, ultra-low power Bluetooth development board based on the Nordic nRF52840.
|
||||||
|
// It features an onboard Bluetooth antenna, onboard battery charging chip, and 21*17.5mm thumb size, which makes it ideal for IoT projects.
|
||||||
|
//
|
||||||
|
// Seeed XIAO BLE nRF52840 Sense is a tiny Bluetooth LE development board designed for IoT and AI applications.
|
||||||
|
// It features an onboard antenna, 6 Dof IMU, microphone, all of which make it an ideal board to run AI using TinyML and TensorFlow Lite.
|
||||||
|
//
|
||||||
|
// SoftDevice (s140v7) is pre-flashed on this board already.
|
||||||
|
// See https://github.com/tinygo-org/bluetooth
|
||||||
|
//
|
||||||
|
// - https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html
|
||||||
|
// - https://www.seeedstudio.com/Seeed-XIAO-BLE-Sense-nRF52840-p-5253.html
|
||||||
|
//
|
||||||
|
// - https://wiki.seeedstudio.com/XIAO_BLE/
|
||||||
|
// - https://github.com/Seeed-Studio/ArduinoCore-mbed/tree/master/variants/SEEED_XIAO_NRF52840_SENSE
|
||||||
|
//
|
||||||
|
package machine
|
||||||
|
|
||||||
|
const HasLowFrequencyCrystal = true
|
||||||
|
|
||||||
|
// Digital Pins
|
||||||
|
const (
|
||||||
|
D0 Pin = P0_02
|
||||||
|
D1 Pin = P0_03
|
||||||
|
D2 Pin = P0_28
|
||||||
|
D3 Pin = P0_29
|
||||||
|
D4 Pin = P0_04
|
||||||
|
D5 Pin = P0_05
|
||||||
|
D6 Pin = P1_11
|
||||||
|
D7 Pin = P1_12
|
||||||
|
D8 Pin = P1_13
|
||||||
|
D9 Pin = P1_14
|
||||||
|
D10 Pin = P1_15
|
||||||
|
)
|
||||||
|
|
||||||
|
// Analog pins
|
||||||
|
const (
|
||||||
|
A0 Pin = P0_02
|
||||||
|
A1 Pin = P0_03
|
||||||
|
A2 Pin = P0_28
|
||||||
|
A3 Pin = P0_29
|
||||||
|
A4 Pin = P0_04
|
||||||
|
A5 Pin = P0_05
|
||||||
|
)
|
||||||
|
|
||||||
|
// Onboard LEDs
|
||||||
|
const (
|
||||||
|
LED = LED_CHG
|
||||||
|
LED1 = LED_RED
|
||||||
|
LED2 = LED_GREEN
|
||||||
|
LED3 = LED_BLUE
|
||||||
|
LED_CHG = P0_17
|
||||||
|
LED_RED = P0_26
|
||||||
|
LED_GREEN = P0_30
|
||||||
|
LED_BLUE = P0_06
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART0 pins
|
||||||
|
const (
|
||||||
|
UART_RX_PIN = P1_12
|
||||||
|
UART_TX_PIN = P1_11
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C pins
|
||||||
|
const (
|
||||||
|
// Defaults to internal
|
||||||
|
SDA_PIN = SDA1_PIN
|
||||||
|
SCL_PIN = SCL1_PIN
|
||||||
|
|
||||||
|
// I2C0 (external) pins
|
||||||
|
SDA0_PIN = P0_04
|
||||||
|
SCL0_PIN = P0_05
|
||||||
|
|
||||||
|
// I2C1 (internal) pins
|
||||||
|
SDA1_PIN = P0_07
|
||||||
|
SCL1_PIN = P0_27
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI pins
|
||||||
|
const (
|
||||||
|
SPI0_SCK_PIN = P1_13
|
||||||
|
SPI0_SDO_PIN = P1_14
|
||||||
|
SPI0_SDI_PIN = P1_15
|
||||||
|
)
|
||||||
|
|
||||||
|
// Peripherals
|
||||||
|
const (
|
||||||
|
LSM_PWR = P1_08 // IMU (LSM6DS3TR) power
|
||||||
|
LSM_INT = P0_11 // IMU (LSM6DS3TR) interrupt
|
||||||
|
|
||||||
|
MIC_PWR = P1_10 // Microphone (MSM261D3526H1CPM) power
|
||||||
|
MIC_CLK = P1_00
|
||||||
|
MIC_DIN = P0_16
|
||||||
|
)
|
||||||
|
|
||||||
|
// USB CDC identifiers
|
||||||
|
const (
|
||||||
|
usb_STRING_PRODUCT = "XIAO nRF52840 Sense"
|
||||||
|
usb_STRING_MANUFACTURER = "Seeed"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
usb_VID uint16 = 0x2886
|
||||||
|
usb_PID uint16 = 0x0045
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART0
|
||||||
|
)
|
||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
"device/sam"
|
"device/sam"
|
||||||
"errors"
|
"errors"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1975,719 +1974,10 @@ func (tcc *TCC) Set(channel uint8, value uint32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// USBCDC is the USB CDC aka serial over USB interface on the SAMD21.
|
func initUSB() {
|
||||||
type USBCDC struct {
|
// Configure USB D+/D- pins.
|
||||||
Buffer *RingBuffer
|
|
||||||
TxIdx volatile.Register8
|
|
||||||
waitTxc bool
|
|
||||||
waitTxcRetryCount uint8
|
|
||||||
sent bool
|
|
||||||
configured bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
// USB is a USB CDC interface.
|
|
||||||
USB = &USBCDC{Buffer: NewRingBuffer()}
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
usbcdcTxSizeMask uint8 = 0x3F
|
|
||||||
usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask
|
|
||||||
usbcdcTxBank1st uint8 = 0x00
|
|
||||||
usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1
|
|
||||||
usbcdcTxMaxRetriesAllowed uint8 = 5
|
|
||||||
)
|
|
||||||
|
|
||||||
// Flush flushes buffered data.
|
|
||||||
func (usbcdc *USBCDC) Flush() error {
|
|
||||||
if usbLineInfo.lineState > 0 {
|
|
||||||
idx := usbcdc.TxIdx.Get()
|
|
||||||
sz := idx & usbcdcTxSizeMask
|
|
||||||
bk := idx & usbcdcTxBankMask
|
|
||||||
if 0 < sz {
|
|
||||||
|
|
||||||
if usbcdc.waitTxc {
|
|
||||||
// waiting for the next flush(), because the transmission is not complete
|
|
||||||
usbcdc.waitTxcRetryCount++
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
usbcdc.waitTxc = true
|
|
||||||
usbcdc.waitTxcRetryCount = 0
|
|
||||||
|
|
||||||
// set the data
|
|
||||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][bk]))))
|
|
||||||
if bk == usbcdcTxBank1st {
|
|
||||||
usbcdc.TxIdx.Set(usbcdcTxBank2nd)
|
|
||||||
} else {
|
|
||||||
usbcdc.TxIdx.Set(usbcdcTxBank1st)
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean multi packet size of bytes already sent
|
|
||||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
|
||||||
|
|
||||||
// set count of bytes to be sent
|
|
||||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
|
||||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((uint32(sz) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
|
||||||
|
|
||||||
// clear transfer complete flag
|
|
||||||
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
|
||||||
|
|
||||||
// send data by setting bank ready
|
|
||||||
setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
|
||||||
usbcdc.sent = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteByte writes a byte of data to the USB CDC interface.
|
|
||||||
func (usbcdc *USBCDC) WriteByte(c byte) error {
|
|
||||||
// Supposedly to handle problem with Windows USB serial ports?
|
|
||||||
if usbLineInfo.lineState > 0 {
|
|
||||||
ok := false
|
|
||||||
for {
|
|
||||||
mask := interrupt.Disable()
|
|
||||||
|
|
||||||
idx := usbcdc.TxIdx.Get()
|
|
||||||
if (idx & usbcdcTxSizeMask) < usbcdcTxSizeMask {
|
|
||||||
udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][idx] = c
|
|
||||||
usbcdc.TxIdx.Set(idx + 1)
|
|
||||||
ok = true
|
|
||||||
}
|
|
||||||
|
|
||||||
interrupt.Restore(mask)
|
|
||||||
|
|
||||||
if ok {
|
|
||||||
break
|
|
||||||
} else if usbcdcTxMaxRetriesAllowed < usbcdc.waitTxcRetryCount {
|
|
||||||
mask := interrupt.Disable()
|
|
||||||
usbcdc.waitTxc = false
|
|
||||||
usbcdc.waitTxcRetryCount = 0
|
|
||||||
usbcdc.TxIdx.Set(0)
|
|
||||||
usbLineInfo.lineState = 0
|
|
||||||
interrupt.Restore(mask)
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
mask := interrupt.Disable()
|
|
||||||
if usbcdc.sent {
|
|
||||||
if usbcdc.waitTxc {
|
|
||||||
if (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) != 0 {
|
|
||||||
setEPSTATUSCLR(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
|
|
||||||
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
|
||||||
usbcdc.waitTxc = false
|
|
||||||
usbcdc.Flush()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
usbcdc.Flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
interrupt.Restore(mask)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (usbcdc *USBCDC) DTR() bool {
|
|
||||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_DTR) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (usbcdc *USBCDC) RTS() bool {
|
|
||||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_RTS) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// these are SAMD51 specific.
|
|
||||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0
|
|
||||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask = 0x3FFF
|
|
||||||
|
|
||||||
usb_DEVICE_PCKSIZE_SIZE_Pos = 28
|
|
||||||
usb_DEVICE_PCKSIZE_SIZE_Mask = 0x7
|
|
||||||
|
|
||||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14
|
|
||||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
usbEndpointDescriptors [8]usbDeviceDescriptor
|
|
||||||
|
|
||||||
udd_ep_in_cache_buffer [7][128]uint8
|
|
||||||
udd_ep_out_cache_buffer [7][128]uint8
|
|
||||||
|
|
||||||
isEndpointHalt = false
|
|
||||||
isRemoteWakeUpEnabled = false
|
|
||||||
endPoints = []uint32{usb_ENDPOINT_TYPE_CONTROL,
|
|
||||||
(usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn),
|
|
||||||
(usb_ENDPOINT_TYPE_BULK | usbEndpointOut),
|
|
||||||
(usb_ENDPOINT_TYPE_BULK | usbEndpointIn)}
|
|
||||||
|
|
||||||
usbConfiguration uint8
|
|
||||||
usbSetInterface uint8
|
|
||||||
usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Configure the USB CDC interface. The config is here for compatibility with the UART interface.
|
|
||||||
func (usbcdc *USBCDC) Configure(config UARTConfig) {
|
|
||||||
// reset USB interface
|
|
||||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_SWRST)
|
|
||||||
for sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) ||
|
|
||||||
sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_ENABLE) {
|
|
||||||
}
|
|
||||||
|
|
||||||
sam.USB_DEVICE.DESCADD.Set(uint32(uintptr(unsafe.Pointer(&usbEndpointDescriptors))))
|
|
||||||
|
|
||||||
// configure pins
|
|
||||||
USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom})
|
USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom})
|
||||||
USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom})
|
USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom})
|
||||||
|
|
||||||
// performs pad calibration from store fuses
|
|
||||||
handlePadCalibration()
|
|
||||||
|
|
||||||
// run in standby
|
|
||||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_RUNSTDBY)
|
|
||||||
|
|
||||||
// set full speed
|
|
||||||
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos)
|
|
||||||
|
|
||||||
// attach
|
|
||||||
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
|
|
||||||
|
|
||||||
// enable interrupt for end of reset
|
|
||||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
|
|
||||||
|
|
||||||
// enable interrupt for start of frame
|
|
||||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_SOF)
|
|
||||||
|
|
||||||
// enable USB
|
|
||||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
|
||||||
|
|
||||||
// enable IRQ at highest priority
|
|
||||||
interrupt.New(sam.IRQ_USB_OTHER, handleUSBIRQ).Enable()
|
|
||||||
interrupt.New(sam.IRQ_USB_SOF_HSOF, handleUSBIRQ).Enable()
|
|
||||||
interrupt.New(sam.IRQ_USB_TRCPT0, handleUSBIRQ).Enable()
|
|
||||||
interrupt.New(sam.IRQ_USB_TRCPT1, handleUSBIRQ).Enable()
|
|
||||||
|
|
||||||
usbcdc.configured = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configured returns whether usbcdc is configured or not.
|
|
||||||
func (usbcdc *USBCDC) Configured() bool {
|
|
||||||
return usbcdc.configured
|
|
||||||
}
|
|
||||||
|
|
||||||
func handlePadCalibration() {
|
|
||||||
// Load Pad Calibration data from non-volatile memory
|
|
||||||
// This requires registers that are not included in the SVD file.
|
|
||||||
// Modeled after defines from samd21g18a.h and nvmctrl.h:
|
|
||||||
//
|
|
||||||
// #define NVMCTRL_OTP4 0x00806020
|
|
||||||
//
|
|
||||||
// #define USB_FUSES_TRANSN_ADDR (NVMCTRL_OTP4 + 4)
|
|
||||||
// #define USB_FUSES_TRANSN_Pos 13 /**< \brief (NVMCTRL_OTP4) USB pad Transn calibration */
|
|
||||||
// #define USB_FUSES_TRANSN_Msk (0x1Fu << USB_FUSES_TRANSN_Pos)
|
|
||||||
// #define USB_FUSES_TRANSN(value) ((USB_FUSES_TRANSN_Msk & ((value) << USB_FUSES_TRANSN_Pos)))
|
|
||||||
|
|
||||||
// #define USB_FUSES_TRANSP_ADDR (NVMCTRL_OTP4 + 4)
|
|
||||||
// #define USB_FUSES_TRANSP_Pos 18 /**< \brief (NVMCTRL_OTP4) USB pad Transp calibration */
|
|
||||||
// #define USB_FUSES_TRANSP_Msk (0x1Fu << USB_FUSES_TRANSP_Pos)
|
|
||||||
// #define USB_FUSES_TRANSP(value) ((USB_FUSES_TRANSP_Msk & ((value) << USB_FUSES_TRANSP_Pos)))
|
|
||||||
|
|
||||||
// #define USB_FUSES_TRIM_ADDR (NVMCTRL_OTP4 + 4)
|
|
||||||
// #define USB_FUSES_TRIM_Pos 23 /**< \brief (NVMCTRL_OTP4) USB pad Trim calibration */
|
|
||||||
// #define USB_FUSES_TRIM_Msk (0x7u << USB_FUSES_TRIM_Pos)
|
|
||||||
// #define USB_FUSES_TRIM(value) ((USB_FUSES_TRIM_Msk & ((value) << USB_FUSES_TRIM_Pos)))
|
|
||||||
//
|
|
||||||
fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4))
|
|
||||||
calibTransN := uint16(fuse>>13) & uint16(0x1f)
|
|
||||||
calibTransP := uint16(fuse>>18) & uint16(0x1f)
|
|
||||||
calibTrim := uint16(fuse>>23) & uint16(0x7)
|
|
||||||
|
|
||||||
if calibTransN == 0x1f {
|
|
||||||
calibTransN = 5
|
|
||||||
}
|
|
||||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransN << sam.USB_DEVICE_PADCAL_TRANSN_Pos)
|
|
||||||
|
|
||||||
if calibTransP == 0x1f {
|
|
||||||
calibTransP = 29
|
|
||||||
}
|
|
||||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos)
|
|
||||||
|
|
||||||
if calibTrim == 0x7 {
|
|
||||||
calibTrim = 3
|
|
||||||
}
|
|
||||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleUSBIRQ(interrupt.Interrupt) {
|
|
||||||
// reset all interrupt flags
|
|
||||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
|
||||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
|
||||||
|
|
||||||
// End of reset
|
|
||||||
if (flags & sam.USB_DEVICE_INTFLAG_EORST) > 0 {
|
|
||||||
// Configure control endpoint
|
|
||||||
initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL)
|
|
||||||
|
|
||||||
// Enable Setup-Received interrupt
|
|
||||||
setEPINTENSET(0, sam.USB_DEVICE_ENDPOINT_EPINTENSET_RXSTP)
|
|
||||||
|
|
||||||
usbConfiguration = 0
|
|
||||||
|
|
||||||
// ack the End-Of-Reset interrupt
|
|
||||||
sam.USB_DEVICE.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_EORST)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start of frame
|
|
||||||
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
|
||||||
USB.Flush()
|
|
||||||
// if you want to blink LED showing traffic, this would be the place...
|
|
||||||
}
|
|
||||||
|
|
||||||
// Endpoint 0 Setup interrupt
|
|
||||||
if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP > 0 {
|
|
||||||
// ack setup received
|
|
||||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP)
|
|
||||||
|
|
||||||
// parse setup
|
|
||||||
setup := newUSBSetup(udd_ep_out_cache_buffer[0][:])
|
|
||||||
|
|
||||||
// Clear the Bank 0 ready flag on Control OUT
|
|
||||||
setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
|
||||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
|
||||||
|
|
||||||
ok := false
|
|
||||||
if (setup.bmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
|
||||||
// Standard Requests
|
|
||||||
ok = handleStandardSetup(setup)
|
|
||||||
} else {
|
|
||||||
// Class Interface Requests
|
|
||||||
if setup.wIndex == usb_CDC_ACM_INTERFACE {
|
|
||||||
ok = cdcSetup(setup)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok {
|
|
||||||
// set Bank1 ready
|
|
||||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
|
||||||
} else {
|
|
||||||
// Stall endpoint
|
|
||||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1 > 0 {
|
|
||||||
// ack the stall
|
|
||||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
|
||||||
|
|
||||||
// clear stall request
|
|
||||||
setEPINTENCLR(0, sam.USB_DEVICE_ENDPOINT_EPINTENCLR_STALL1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now the actual transfer handlers, ignore endpoint number 0 (setup)
|
|
||||||
var i uint32
|
|
||||||
for i = 1; i < uint32(len(endPoints)); i++ {
|
|
||||||
// Check if endpoint has a pending interrupt
|
|
||||||
epFlags := getEPINTFLAG(i)
|
|
||||||
if (epFlags&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) > 0 ||
|
|
||||||
(epFlags&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) > 0 {
|
|
||||||
switch i {
|
|
||||||
case usb_CDC_ENDPOINT_OUT:
|
|
||||||
handleEndpoint(i)
|
|
||||||
setEPINTFLAG(i, epFlags)
|
|
||||||
case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM:
|
|
||||||
setEPSTATUSCLR(i, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
|
|
||||||
setEPINTFLAG(i, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
|
||||||
|
|
||||||
if i == usb_CDC_ENDPOINT_IN {
|
|
||||||
USB.waitTxc = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func initEndpoint(ep, config uint32) {
|
|
||||||
switch config {
|
|
||||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn:
|
|
||||||
// set packet size
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
|
||||||
|
|
||||||
// set data buffer address
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
|
||||||
|
|
||||||
// set endpoint type
|
|
||||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
|
||||||
|
|
||||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointOut:
|
|
||||||
// set packet size
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
|
||||||
|
|
||||||
// set data buffer address
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
|
||||||
|
|
||||||
// set endpoint type
|
|
||||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
|
||||||
|
|
||||||
// receive interrupts when current transfer complete
|
|
||||||
setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
|
|
||||||
|
|
||||||
// set byte count to zero, we have not received anything yet
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
|
||||||
|
|
||||||
// ready for next transfer
|
|
||||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
|
||||||
|
|
||||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut:
|
|
||||||
// TODO: not really anything, seems like...
|
|
||||||
|
|
||||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointIn:
|
|
||||||
// set packet size
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
|
||||||
|
|
||||||
// set data buffer address
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
|
||||||
|
|
||||||
// set endpoint type
|
|
||||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
|
||||||
|
|
||||||
// NAK on endpoint IN, the bank is not yet filled in.
|
|
||||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
|
|
||||||
|
|
||||||
case usb_ENDPOINT_TYPE_CONTROL:
|
|
||||||
// Control OUT
|
|
||||||
// set packet size
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
|
||||||
|
|
||||||
// set data buffer address
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
|
||||||
|
|
||||||
// set endpoint type
|
|
||||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
|
||||||
|
|
||||||
// Control IN
|
|
||||||
// set packet size
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
|
||||||
|
|
||||||
// set data buffer address
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
|
||||||
|
|
||||||
// set endpoint type
|
|
||||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
|
||||||
|
|
||||||
// Prepare OUT endpoint for receive
|
|
||||||
// set multi packet size for expected number of receive bytes on control OUT
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
|
||||||
|
|
||||||
// set byte count to zero, we have not received anything yet
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
|
||||||
|
|
||||||
// NAK on endpoint OUT to show we are ready to receive control data
|
|
||||||
setEPSTATUSSET(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK0RDY)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleStandardSetup(setup usbSetup) bool {
|
|
||||||
switch setup.bRequest {
|
|
||||||
case usb_GET_STATUS:
|
|
||||||
buf := []byte{0, 0}
|
|
||||||
|
|
||||||
if setup.bmRequestType != 0 { // endpoint
|
|
||||||
// TODO: actually check if the endpoint in question is currently halted
|
|
||||||
if isEndpointHalt {
|
|
||||||
buf[0] = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sendUSBPacket(0, buf)
|
|
||||||
return true
|
|
||||||
|
|
||||||
case usb_CLEAR_FEATURE:
|
|
||||||
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
|
|
||||||
isRemoteWakeUpEnabled = false
|
|
||||||
} else if setup.wValueL == 0 { // ENDPOINTHALT
|
|
||||||
isEndpointHalt = false
|
|
||||||
}
|
|
||||||
sendZlp()
|
|
||||||
return true
|
|
||||||
|
|
||||||
case usb_SET_FEATURE:
|
|
||||||
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
|
|
||||||
isRemoteWakeUpEnabled = true
|
|
||||||
} else if setup.wValueL == 0 { // ENDPOINTHALT
|
|
||||||
isEndpointHalt = true
|
|
||||||
}
|
|
||||||
sendZlp()
|
|
||||||
return true
|
|
||||||
|
|
||||||
case usb_SET_ADDRESS:
|
|
||||||
// set packet size 64 with auto Zlp after transfer
|
|
||||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.Set((epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) |
|
|
||||||
uint32(1<<31)) // autozlp
|
|
||||||
|
|
||||||
// ack the transfer is complete from the request
|
|
||||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
|
||||||
|
|
||||||
// set bank ready for data
|
|
||||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
|
||||||
|
|
||||||
// wait for transfer to complete
|
|
||||||
timeout := 3000
|
|
||||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
|
|
||||||
timeout--
|
|
||||||
if timeout == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// last, set the device address to that requested by host
|
|
||||||
sam.USB_DEVICE.DADD.SetBits(setup.wValueL)
|
|
||||||
sam.USB_DEVICE.DADD.SetBits(sam.USB_DEVICE_DADD_ADDEN)
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
case usb_GET_DESCRIPTOR:
|
|
||||||
sendDescriptor(setup)
|
|
||||||
return true
|
|
||||||
|
|
||||||
case usb_SET_DESCRIPTOR:
|
|
||||||
return false
|
|
||||||
|
|
||||||
case usb_GET_CONFIGURATION:
|
|
||||||
buff := []byte{usbConfiguration}
|
|
||||||
sendUSBPacket(0, buff)
|
|
||||||
return true
|
|
||||||
|
|
||||||
case usb_SET_CONFIGURATION:
|
|
||||||
if setup.bmRequestType&usb_REQUEST_RECIPIENT == usb_REQUEST_DEVICE {
|
|
||||||
for i := 1; i < len(endPoints); i++ {
|
|
||||||
initEndpoint(uint32(i), endPoints[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
usbConfiguration = setup.wValueL
|
|
||||||
|
|
||||||
// Enable interrupt for CDC control messages from host (OUT packet)
|
|
||||||
setEPINTENSET(usb_CDC_ENDPOINT_ACM, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1)
|
|
||||||
|
|
||||||
// Enable interrupt for CDC data messages from host
|
|
||||||
setEPINTENSET(usb_CDC_ENDPOINT_OUT, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
|
|
||||||
|
|
||||||
sendZlp()
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
case usb_GET_INTERFACE:
|
|
||||||
buff := []byte{usbSetInterface}
|
|
||||||
sendUSBPacket(0, buff)
|
|
||||||
return true
|
|
||||||
|
|
||||||
case usb_SET_INTERFACE:
|
|
||||||
usbSetInterface = setup.wValueL
|
|
||||||
|
|
||||||
sendZlp()
|
|
||||||
return true
|
|
||||||
|
|
||||||
default:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cdcSetup(setup usbSetup) bool {
|
|
||||||
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
|
||||||
if setup.bRequest == usb_CDC_GET_LINE_CODING {
|
|
||||||
var b [cdcLineInfoSize]byte
|
|
||||||
b[0] = byte(usbLineInfo.dwDTERate)
|
|
||||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
|
||||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
|
||||||
b[3] = byte(usbLineInfo.dwDTERate >> 24)
|
|
||||||
b[4] = byte(usbLineInfo.bCharFormat)
|
|
||||||
b[5] = byte(usbLineInfo.bParityType)
|
|
||||||
b[6] = byte(usbLineInfo.bDataBits)
|
|
||||||
|
|
||||||
sendUSBPacket(0, b[:])
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
|
||||||
if setup.bRequest == usb_CDC_SET_LINE_CODING {
|
|
||||||
b, err := receiveUSBControlPacket()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
|
||||||
usbLineInfo.bCharFormat = b[4]
|
|
||||||
usbLineInfo.bParityType = b[5]
|
|
||||||
usbLineInfo.bDataBits = b[6]
|
|
||||||
}
|
|
||||||
|
|
||||||
if setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
|
||||||
usbLineInfo.lineState = setup.wValueL
|
|
||||||
}
|
|
||||||
|
|
||||||
if setup.bRequest == usb_CDC_SET_LINE_CODING || setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
|
||||||
// auto-reset into the bootloader
|
|
||||||
if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 {
|
|
||||||
ResetProcessor()
|
|
||||||
} else {
|
|
||||||
// TODO: cancel any reset
|
|
||||||
}
|
|
||||||
sendZlp()
|
|
||||||
}
|
|
||||||
|
|
||||||
if setup.bRequest == usb_CDC_SEND_BREAK {
|
|
||||||
// TODO: something with this value?
|
|
||||||
// breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL;
|
|
||||||
// return false;
|
|
||||||
sendZlp()
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:noinline
|
|
||||||
func sendUSBPacket(ep uint32, data []byte) {
|
|
||||||
copy(udd_ep_in_cache_buffer[ep][:], data)
|
|
||||||
|
|
||||||
// Set endpoint address for sending data
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
|
||||||
|
|
||||||
// clear multi-packet size which is total bytes already sent
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
|
||||||
|
|
||||||
// set byte count, which is total number of bytes to be sent
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(uint32((len(data) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos))
|
|
||||||
}
|
|
||||||
|
|
||||||
func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
|
||||||
var b [cdcLineInfoSize]byte
|
|
||||||
|
|
||||||
// address
|
|
||||||
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
|
||||||
|
|
||||||
// set byte count to zero
|
|
||||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
|
||||||
|
|
||||||
// set ready for next data
|
|
||||||
setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
|
||||||
|
|
||||||
// Wait until OUT transfer is ready.
|
|
||||||
timeout := 300000
|
|
||||||
for (getEPSTATUS(0) & sam.USB_DEVICE_ENDPOINT_EPSTATUS_BK0RDY) == 0 {
|
|
||||||
timeout--
|
|
||||||
if timeout == 0 {
|
|
||||||
return b, errUSBCDCReadTimeout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait until OUT transfer is completed.
|
|
||||||
timeout = 300000
|
|
||||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
|
|
||||||
timeout--
|
|
||||||
if timeout == 0 {
|
|
||||||
return b, errUSBCDCReadTimeout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get data
|
|
||||||
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
|
|
||||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
|
||||||
|
|
||||||
if bytesread != cdcLineInfoSize {
|
|
||||||
return b, errUSBCDCBytesRead
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
|
|
||||||
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleEndpoint(ep uint32) {
|
|
||||||
// get data
|
|
||||||
count := int((usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.Get() >>
|
|
||||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
|
||||||
|
|
||||||
// move to ring buffer
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
USB.Receive(byte((udd_ep_out_cache_buffer[ep][i] & 0xFF)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// set byte count to zero
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
|
||||||
|
|
||||||
// set multi packet size to 64
|
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
|
||||||
|
|
||||||
// set ready for next data
|
|
||||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendZlp() {
|
|
||||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
func epPacketSize(size uint16) uint32 {
|
|
||||||
switch size {
|
|
||||||
case 8:
|
|
||||||
return 0
|
|
||||||
case 16:
|
|
||||||
return 1
|
|
||||||
case 32:
|
|
||||||
return 2
|
|
||||||
case 64:
|
|
||||||
return 3
|
|
||||||
case 128:
|
|
||||||
return 4
|
|
||||||
case 256:
|
|
||||||
return 5
|
|
||||||
case 512:
|
|
||||||
return 6
|
|
||||||
case 1023:
|
|
||||||
return 7
|
|
||||||
default:
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getEPCFG(ep uint32) uint8 {
|
|
||||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Get()
|
|
||||||
}
|
|
||||||
|
|
||||||
func setEPCFG(ep uint32, val uint8) {
|
|
||||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Set(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setEPSTATUSCLR(ep uint32, val uint8) {
|
|
||||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSCLR.Set(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setEPSTATUSSET(ep uint32, val uint8) {
|
|
||||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSSET.Set(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getEPSTATUS(ep uint32) uint8 {
|
|
||||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUS.Get()
|
|
||||||
}
|
|
||||||
|
|
||||||
func getEPINTFLAG(ep uint32) uint8 {
|
|
||||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Get()
|
|
||||||
}
|
|
||||||
|
|
||||||
func setEPINTFLAG(ep uint32, val uint8) {
|
|
||||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Set(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setEPINTENCLR(ep uint32, val uint8) {
|
|
||||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENCLR.Set(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setEPINTENSET(ep uint32, val uint8) {
|
|
||||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENSET.Set(val)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetProcessor should perform a system reset in preparation
|
// ResetProcessor should perform a system reset in preparation
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/esp"
|
"device/esp"
|
||||||
|
"device/riscv"
|
||||||
|
"errors"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -214,18 +216,269 @@ func setupPinInterrupt() error {
|
|||||||
}).Enable()
|
}).Enable()
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultUART = UART0
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
DefaultUART = UART0
|
||||||
|
|
||||||
UART0 = &_UART0
|
UART0 = &_UART0
|
||||||
_UART0 = UART{Bus: esp.UART0, Buffer: NewRingBuffer()}
|
_UART0 = UART{Bus: esp.UART0, Buffer: NewRingBuffer()}
|
||||||
UART1 = &_UART1
|
UART1 = &_UART1
|
||||||
_UART1 = UART{Bus: esp.UART1, Buffer: NewRingBuffer()}
|
_UART1 = UART{Bus: esp.UART1, Buffer: NewRingBuffer()}
|
||||||
|
|
||||||
|
onceUart = sync.Once{}
|
||||||
|
errSamePins = errors.New("UART: invalid pin combination")
|
||||||
|
errWrongUART = errors.New("UART: unsupported UARTn")
|
||||||
|
errWrongBitSize = errors.New("UART: invalid data size")
|
||||||
|
errWrongStopBitSize = errors.New("UART: invalid bit size")
|
||||||
)
|
)
|
||||||
|
|
||||||
type UART struct {
|
type UART struct {
|
||||||
Bus *esp.UART_Type
|
Bus *esp.UART_Type
|
||||||
Buffer *RingBuffer
|
Buffer *RingBuffer
|
||||||
|
ParityErrorDetected bool // set when parity error detected
|
||||||
|
DataErrorDetected bool // set when data corruption detected
|
||||||
|
DataOverflowDetected bool // set when data overflow detected in UART FIFO buffer or RingBuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
type UARTStopBits int
|
||||||
|
|
||||||
|
const (
|
||||||
|
UARTStopBits_Default UARTStopBits = iota
|
||||||
|
UARTStopBits_1
|
||||||
|
UARTStopBits_1_5
|
||||||
|
UARTStopBits_2
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultDataBits = 8
|
||||||
|
defaultStopBit = 1
|
||||||
|
defaultParity = ParityNone
|
||||||
|
|
||||||
|
uartInterrupts = esp.UART_INT_ENA_RXFIFO_FULL_INT_ENA |
|
||||||
|
esp.UART_INT_ENA_PARITY_ERR_INT_ENA |
|
||||||
|
esp.UART_INT_ENA_FRM_ERR_INT_ENA |
|
||||||
|
esp.UART_INT_ENA_RXFIFO_OVF_INT_ENA |
|
||||||
|
esp.UART_INT_ENA_GLITCH_DET_INT_ENA
|
||||||
|
|
||||||
|
pplClockFreq = 80e6
|
||||||
|
)
|
||||||
|
|
||||||
|
type registerSet struct {
|
||||||
|
interruptMapReg *volatile.Register32
|
||||||
|
uartClockBitMask uint32
|
||||||
|
gpioMatrixSignal uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uart *UART) Configure(config UARTConfig) error {
|
||||||
|
if config.BaudRate == 0 {
|
||||||
|
config.BaudRate = 115200
|
||||||
|
}
|
||||||
|
if config.TX == config.RX {
|
||||||
|
return errSamePins
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case uart.Bus == esp.UART0:
|
||||||
|
return uart.configure(config, registerSet{
|
||||||
|
interruptMapReg: &esp.INTERRUPT_CORE0.UART_INTR_MAP,
|
||||||
|
uartClockBitMask: esp.SYSTEM_PERIP_CLK_EN0_UART_CLK_EN,
|
||||||
|
gpioMatrixSignal: 6,
|
||||||
|
})
|
||||||
|
case uart.Bus == esp.UART1:
|
||||||
|
return uart.configure(config, registerSet{
|
||||||
|
interruptMapReg: &esp.INTERRUPT_CORE0.UART1_INTR_MAP,
|
||||||
|
uartClockBitMask: esp.SYSTEM_PERIP_CLK_EN0_UART1_CLK_EN,
|
||||||
|
gpioMatrixSignal: 9,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return errWrongUART
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uart *UART) configure(config UARTConfig, regs registerSet) error {
|
||||||
|
|
||||||
|
initUARTClock(uart.Bus, regs)
|
||||||
|
|
||||||
|
// - disbale TX/RX clock to make sure the UART transmitter or receiver is not at work during configuration
|
||||||
|
uart.Bus.SetCLK_CONF_TX_SCLK_EN(0)
|
||||||
|
uart.Bus.SetCLK_CONF_RX_SCLK_EN(0)
|
||||||
|
|
||||||
|
// Configure static registers (Ref: Configuring URATn Communication)
|
||||||
|
|
||||||
|
// - default clock source: 1=APB_CLK, 2=FOSC_CLK, 3=XTAL_CLK
|
||||||
|
uart.Bus.SetCLK_CONF_SCLK_SEL(1)
|
||||||
|
// reset divisor of the divider via UART_SCLK_DIV_NUM, UART_SCLK_DIV_A, and UART_SCLK_DIV_B
|
||||||
|
uart.Bus.SetCLK_CONF_SCLK_DIV_NUM(0)
|
||||||
|
uart.Bus.SetCLK_CONF_SCLK_DIV_A(0)
|
||||||
|
uart.Bus.SetCLK_CONF_SCLK_DIV_B(0)
|
||||||
|
|
||||||
|
// - the baud rate
|
||||||
|
uart.SetBaudRate(config.BaudRate)
|
||||||
|
// - the data format
|
||||||
|
uart.SetFormat(defaultDataBits, defaultStopBit, defaultParity)
|
||||||
|
// - set UART mode
|
||||||
|
uart.Bus.SetRS485_CONF_RS485_EN(0)
|
||||||
|
uart.Bus.SetRS485_CONF_RS485TX_RX_EN(0)
|
||||||
|
uart.Bus.SetRS485_CONF_RS485RXBY_TX_EN(0)
|
||||||
|
uart.Bus.SetCONF0_IRDA_EN(0)
|
||||||
|
// - disable hw-flow control
|
||||||
|
uart.Bus.SetCONF0_TX_FLOW_EN(0)
|
||||||
|
uart.Bus.SetCONF1_RX_FLOW_EN(0)
|
||||||
|
|
||||||
|
// synchronize values into Core Clock
|
||||||
|
uart.Bus.SetID_REG_UPDATE(1)
|
||||||
|
|
||||||
|
uart.setupPins(config, regs)
|
||||||
|
uart.configureInterrupt(regs.interruptMapReg)
|
||||||
|
uart.enableTransmitter()
|
||||||
|
uart.enableReceiver()
|
||||||
|
|
||||||
|
// Start TX/RX
|
||||||
|
uart.Bus.SetCLK_CONF_TX_SCLK_EN(1)
|
||||||
|
uart.Bus.SetCLK_CONF_RX_SCLK_EN(1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uart *UART) SetFormat(dataBits, stopBits int, parity UARTParity) error {
|
||||||
|
if dataBits < 5 {
|
||||||
|
return errWrongBitSize
|
||||||
|
}
|
||||||
|
if stopBits > 1 {
|
||||||
|
return errWrongStopBitSize
|
||||||
|
}
|
||||||
|
// - data length
|
||||||
|
uart.Bus.SetCONF0_BIT_NUM(uint32(dataBits - 5))
|
||||||
|
// - stop bit
|
||||||
|
uart.Bus.SetCONF0_STOP_BIT_NUM(uint32(stopBits))
|
||||||
|
// - parity check
|
||||||
|
switch parity {
|
||||||
|
case ParityNone:
|
||||||
|
uart.Bus.SetCONF0_PARITY_EN(0)
|
||||||
|
case ParityEven:
|
||||||
|
uart.Bus.SetCONF0_PARITY_EN(1)
|
||||||
|
uart.Bus.SetCONF0_PARITY(0)
|
||||||
|
case ParityOdd:
|
||||||
|
uart.Bus.SetCONF0_PARITY_EN(1)
|
||||||
|
uart.Bus.SetCONF0_PARITY(1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initUARTClock(bus *esp.UART_Type, regs registerSet) {
|
||||||
|
uartClock := &esp.SYSTEM.PERIP_CLK_EN0
|
||||||
|
uartClockReset := &esp.SYSTEM.PERIP_RST_EN0
|
||||||
|
|
||||||
|
// Initialize/reset URATn (Ref: Initializing URATn)
|
||||||
|
// - enable the clock for UART RAM
|
||||||
|
uartClock.SetBits(esp.SYSTEM_PERIP_CLK_EN0_UART_MEM_CLK_EN)
|
||||||
|
// - enable APB_CLK for UARTn
|
||||||
|
uartClock.SetBits(regs.uartClockBitMask)
|
||||||
|
// - reset sequence
|
||||||
|
uartClockReset.ClearBits(regs.uartClockBitMask)
|
||||||
|
bus.SetCLK_CONF_RST_CORE(1)
|
||||||
|
uartClockReset.SetBits(regs.uartClockBitMask)
|
||||||
|
uartClockReset.ClearBits(regs.uartClockBitMask)
|
||||||
|
bus.SetCLK_CONF_RST_CORE(0)
|
||||||
|
// synchronize core register
|
||||||
|
bus.SetID_REG_UPDATE(0)
|
||||||
|
// enable RTC clock
|
||||||
|
esp.RTC_CNTL.SetRTC_CLK_CONF_DIG_CLK8M_EN(1)
|
||||||
|
// wait for Core Clock to ready for configuration
|
||||||
|
for bus.GetID_REG_UPDATE() > 0 {
|
||||||
|
riscv.Asm("nop")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uart *UART) SetBaudRate(baudRate uint32) {
|
||||||
|
// based on esp-idf
|
||||||
|
max_div := uint32((1 << 12) - 1)
|
||||||
|
sclk_div := (pplClockFreq + (max_div * baudRate) - 1) / (max_div * baudRate)
|
||||||
|
clk_div := (pplClockFreq << 4) / (baudRate * sclk_div)
|
||||||
|
uart.Bus.SetCLKDIV(clk_div >> 4)
|
||||||
|
uart.Bus.SetCLKDIV_FRAG(clk_div & 0xf)
|
||||||
|
uart.Bus.SetCLK_CONF_SCLK_DIV_NUM(sclk_div - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uart *UART) setupPins(config UARTConfig, regs registerSet) {
|
||||||
|
config.RX.Configure(PinConfig{Mode: PinInputPullup})
|
||||||
|
config.TX.Configure(PinConfig{Mode: PinInputPullup})
|
||||||
|
|
||||||
|
// link TX with GPIO signal X (technical reference manual 5.10) (this is not interrupt signal!)
|
||||||
|
config.TX.outFunc().Set(regs.gpioMatrixSignal)
|
||||||
|
// link RX with GPIO signal X and route signals via GPIO matrix (GPIO_SIGn_IN_SEL 0x40)
|
||||||
|
inFunc(regs.gpioMatrixSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SIG_IN_SEL | uint32(config.RX))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uart *UART) configureInterrupt(intrMapReg *volatile.Register32) { // Disable all UART interrupts
|
||||||
|
// Disable all UART interrupts
|
||||||
|
uart.Bus.INT_ENA.ClearBits(0x0ffff)
|
||||||
|
|
||||||
|
intrMapReg.Set(7)
|
||||||
|
onceUart.Do(func() {
|
||||||
|
_ = interrupt.New(7, func(i interrupt.Interrupt) {
|
||||||
|
UART0.serveInterrupt(0)
|
||||||
|
UART1.serveInterrupt(1)
|
||||||
|
}).Enable()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uart *UART) serveInterrupt(num int) {
|
||||||
|
// get interrupt status
|
||||||
|
interrutFlag := uart.Bus.INT_ST.Get()
|
||||||
|
if (interrutFlag & uartInterrupts) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// block UART interrupts while processing
|
||||||
|
uart.Bus.INT_ENA.ClearBits(uartInterrupts)
|
||||||
|
|
||||||
|
if interrutFlag&esp.UART_INT_ENA_RXFIFO_FULL_INT_ENA > 0 {
|
||||||
|
for uart.Bus.GetSTATUS_RXFIFO_CNT() > 0 {
|
||||||
|
b := uart.Bus.GetFIFO_RXFIFO_RD_BYTE()
|
||||||
|
if !uart.Buffer.Put(byte(b & 0xff)) {
|
||||||
|
uart.DataOverflowDetected = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if interrutFlag&esp.UART_INT_ENA_PARITY_ERR_INT_ENA > 0 {
|
||||||
|
uart.ParityErrorDetected = true
|
||||||
|
}
|
||||||
|
if 0 != interrutFlag&esp.UART_INT_ENA_FRM_ERR_INT_ENA {
|
||||||
|
uart.DataErrorDetected = true
|
||||||
|
}
|
||||||
|
if 0 != interrutFlag&esp.UART_INT_ENA_RXFIFO_OVF_INT_ENA {
|
||||||
|
uart.DataOverflowDetected = true
|
||||||
|
}
|
||||||
|
if 0 != interrutFlag&esp.UART_INT_ENA_GLITCH_DET_INT_ENA {
|
||||||
|
uart.DataErrorDetected = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the UART interrupt status
|
||||||
|
uart.Bus.INT_CLR.SetBits(interrutFlag)
|
||||||
|
uart.Bus.INT_CLR.ClearBits(interrutFlag)
|
||||||
|
// Enable interrupts
|
||||||
|
uart.Bus.INT_ENA.Set(uartInterrupts)
|
||||||
|
}
|
||||||
|
|
||||||
|
const uart_empty_thresh_default = 10
|
||||||
|
|
||||||
|
func (uart *UART) enableTransmitter() {
|
||||||
|
uart.Bus.SetCONF0_TXFIFO_RST(1)
|
||||||
|
uart.Bus.SetCONF0_TXFIFO_RST(0)
|
||||||
|
// TXINFO empty threshold is when txfifo_empty_int interrupt produced after the amount of data in Tx-FIFO is less than this register value.
|
||||||
|
uart.Bus.SetCONF1_TXFIFO_EMPTY_THRHD(uart_empty_thresh_default)
|
||||||
|
// we are not using interrut on TX since write we are waiting for FIFO to have space.
|
||||||
|
// uart.Bus.INT_ENA.SetBits(esp.UART_INT_ENA_TXFIFO_EMPTY_INT_ENA)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uart *UART) enableReceiver() {
|
||||||
|
uart.Bus.SetCONF0_RXFIFO_RST(1)
|
||||||
|
uart.Bus.SetCONF0_RXFIFO_RST(0)
|
||||||
|
// using value 1 so that we can start populate ring buffer with data as we get it
|
||||||
|
uart.Bus.SetCONF1_RXFIFO_FULL_THRHD(1)
|
||||||
|
// enable interrupts for:
|
||||||
|
uart.Bus.SetINT_ENA_RXFIFO_FULL_INT_ENA(1)
|
||||||
|
uart.Bus.SetINT_ENA_FRM_ERR_INT_ENA(1)
|
||||||
|
uart.Bus.SetINT_ENA_PARITY_ERR_INT_ENA(1)
|
||||||
|
uart.Bus.SetINT_ENA_GLITCH_DET_INT_ENA(1)
|
||||||
|
uart.Bus.SetINT_ENA_RXFIFO_OVF_INT_ENA(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uart *UART) WriteByte(b byte) error {
|
func (uart *UART) WriteByte(b byte) error {
|
||||||
|
|||||||
@@ -236,10 +236,10 @@ func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
|
|||||||
kendryte.GPIOHS.RISE_IE.ClearBits(1 << pin)
|
kendryte.GPIOHS.RISE_IE.ClearBits(1 << pin)
|
||||||
// Acknowledge interrupt atomically.
|
// Acknowledge interrupt atomically.
|
||||||
riscv.AsmFull(
|
riscv.AsmFull(
|
||||||
"amoor.w {}, {mask}, {reg}",
|
"amoor.w {}, {mask}, ({reg})",
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"mask": uint32(1 << pin),
|
"mask": uint32(1 << pin),
|
||||||
"reg": &kendryte.GPIOHS.RISE_IP.Reg,
|
"reg": uintptr(unsafe.Pointer(&kendryte.GPIOHS.RISE_IP.Reg)),
|
||||||
})
|
})
|
||||||
kendryte.GPIOHS.RISE_IE.SetBits(1 << pin)
|
kendryte.GPIOHS.RISE_IE.SetBits(1 << pin)
|
||||||
}
|
}
|
||||||
@@ -248,10 +248,10 @@ func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
|
|||||||
kendryte.GPIOHS.FALL_IE.ClearBits(1 << pin)
|
kendryte.GPIOHS.FALL_IE.ClearBits(1 << pin)
|
||||||
// Acknowledge interrupt atomically.
|
// Acknowledge interrupt atomically.
|
||||||
riscv.AsmFull(
|
riscv.AsmFull(
|
||||||
"amoor.w {}, {mask}, {reg}",
|
"amoor.w {}, {mask}, ({reg})",
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"mask": uint32(1 << pin),
|
"mask": uint32(1 << pin),
|
||||||
"reg": &kendryte.GPIOHS.FALL_IP.Reg,
|
"reg": uintptr(unsafe.Pointer(&kendryte.GPIOHS.FALL_IP.Reg)),
|
||||||
})
|
})
|
||||||
kendryte.GPIOHS.FALL_IE.SetBits(1 << pin)
|
kendryte.GPIOHS.FALL_IE.SetBits(1 << pin)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -883,3 +883,179 @@ func (p Pin) getMuxMode(config PinConfig) uint32 {
|
|||||||
panic("machine: invalid pin mode")
|
panic("machine: invalid pin mode")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// maximum ADC value for the currently configured resolution (used for scaling)
|
||||||
|
var adcMaximum uint32
|
||||||
|
|
||||||
|
// InitADC is not used by this machine. Use `(ADC).Configure()`.
|
||||||
|
func InitADC() {}
|
||||||
|
|
||||||
|
// Configure initializes the receiver's ADC peripheral and pin for analog input.
|
||||||
|
func (a ADC) Configure(config ADCConfig) {
|
||||||
|
// if not specified, use defaults: 10-bit resolution, 4 samples/conversion
|
||||||
|
const (
|
||||||
|
defaultResolution = uint32(10)
|
||||||
|
defaultSamples = uint32(4)
|
||||||
|
)
|
||||||
|
|
||||||
|
a.Pin.Configure(PinConfig{Mode: PinInputAnalog})
|
||||||
|
|
||||||
|
resolution, samples := config.Resolution, config.Samples
|
||||||
|
if 0 == resolution {
|
||||||
|
resolution = defaultResolution
|
||||||
|
}
|
||||||
|
if 0 == samples {
|
||||||
|
samples = defaultSamples
|
||||||
|
}
|
||||||
|
if resolution > 12 {
|
||||||
|
resolution = 12 // maximum resolution of 12 bits
|
||||||
|
}
|
||||||
|
adcMaximum = (uint32(1) << resolution) - 1
|
||||||
|
|
||||||
|
mode, average := a.mode(resolution, samples)
|
||||||
|
|
||||||
|
nxp.ADC1.CFG.Set(mode | nxp.ADC_CFG_ADHSC) // configure ADC1
|
||||||
|
nxp.ADC2.CFG.Set(mode | nxp.ADC_CFG_ADHSC) // configure ADC2
|
||||||
|
|
||||||
|
// begin calibration
|
||||||
|
nxp.ADC1.GC.Set(average | nxp.ADC_GC_CAL)
|
||||||
|
nxp.ADC2.GC.Set(average | nxp.ADC_GC_CAL)
|
||||||
|
|
||||||
|
for a.isCalibrating() {
|
||||||
|
} // wait for calibration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get performs a single ADC conversion, returning a 16-bit unsigned integer.
|
||||||
|
// The value returned will be scaled (uniformly distributed) if necessary so
|
||||||
|
// that it is always in the range [0..65535], regardless of the ADC's configured
|
||||||
|
// bit size (resolution).
|
||||||
|
func (a ADC) Get() uint16 {
|
||||||
|
if ch1, ch2, ok := a.Pin.getADCChannel(); ok {
|
||||||
|
for a.isCalibrating() {
|
||||||
|
} // wait for calibration
|
||||||
|
var val uint32
|
||||||
|
if noADCChannel != ch1 {
|
||||||
|
nxp.ADC1.HC0.Set(uint32(ch1))
|
||||||
|
for !nxp.ADC1.HS.HasBits(nxp.ADC_HS_COCO0) {
|
||||||
|
}
|
||||||
|
val = nxp.ADC1.R0.Get() & 0xFFFF
|
||||||
|
} else {
|
||||||
|
nxp.ADC2.HC0.Set(uint32(ch2))
|
||||||
|
for !nxp.ADC2.HS.HasBits(nxp.ADC_HS_COCO0) {
|
||||||
|
}
|
||||||
|
val = nxp.ADC2.R0.Get() & 0xFFFF
|
||||||
|
}
|
||||||
|
// should never be zero, but just in case, use UINT16_MAX so that the scalar
|
||||||
|
// gets factored out of the conversion result, leaving the original reading
|
||||||
|
// to be returned unaltered/unscaled.
|
||||||
|
if adcMaximum == 0 {
|
||||||
|
adcMaximum = 0xFFFF
|
||||||
|
}
|
||||||
|
// scale up to a 16-bit value
|
||||||
|
return uint16((val * 0xFFFF) / adcMaximum)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// mode constructs bit masks for mode and average - used in ADC configuration
|
||||||
|
// registers - from a given ADC bit size (resolution) and sample count.
|
||||||
|
func (a ADC) mode(resolution, samples uint32) (mode, average uint32) {
|
||||||
|
|
||||||
|
// use asynchronous clock (ADACK) (0 = IPG, 1 = IPG/2, or 3 = ADACK)
|
||||||
|
mode = (nxp.ADC_CFG_ADICLK_ADICLK_3 << nxp.ADC_CFG_ADICLK_Pos) & nxp.ADC_CFG_ADICLK_Msk
|
||||||
|
|
||||||
|
// input clock DIV2 (0 = DIV1, 1 = DIV2, 2 = DIV4, or 3 = DIV8)
|
||||||
|
mode |= (nxp.ADC_CFG_ADIV_ADIV_1 << nxp.ADC_CFG_ADIV_Pos) & nxp.ADC_CFG_ADIV_Msk
|
||||||
|
|
||||||
|
switch resolution {
|
||||||
|
case 8: // 8-bit conversion, sample period (ADC clocks) = 8
|
||||||
|
mode |= (nxp.ADC_CFG_MODE_MODE_0 << nxp.ADC_CFG_MODE_Pos) & nxp.ADC_CFG_MODE_Msk
|
||||||
|
mode |= (nxp.ADC_CFG_ADSTS_ADSTS_3 << nxp.ADC_CFG_ADSTS_Pos) & nxp.ADC_CFG_ADSTS_Msk
|
||||||
|
|
||||||
|
case 12: // 12-bit conversion, sample period (ADC clocks) = 24
|
||||||
|
mode |= (nxp.ADC_CFG_MODE_MODE_2 << nxp.ADC_CFG_MODE_Pos) & nxp.ADC_CFG_MODE_Msk
|
||||||
|
mode |= (nxp.ADC_CFG_ADSTS_ADSTS_3 << nxp.ADC_CFG_ADSTS_Pos) & nxp.ADC_CFG_ADSTS_Msk
|
||||||
|
mode |= nxp.ADC_CFG_ADLSMP
|
||||||
|
|
||||||
|
default: // 10-bit conversion, sample period (ADC clocks) = 20
|
||||||
|
mode |= (nxp.ADC_CFG_MODE_MODE_1 << nxp.ADC_CFG_MODE_Pos) & nxp.ADC_CFG_MODE_Msk
|
||||||
|
mode |= (nxp.ADC_CFG_ADSTS_ADSTS_2 << nxp.ADC_CFG_ADSTS_Pos) & nxp.ADC_CFG_ADSTS_Msk
|
||||||
|
mode |= nxp.ADC_CFG_ADLSMP
|
||||||
|
}
|
||||||
|
|
||||||
|
if samples >= 4 {
|
||||||
|
if samples >= 32 {
|
||||||
|
// 32 samples averaged
|
||||||
|
mode |= (nxp.ADC_CFG_AVGS_AVGS_3 << nxp.ADC_CFG_AVGS_Pos) & nxp.ADC_CFG_AVGS_Msk
|
||||||
|
} else if samples >= 16 {
|
||||||
|
// 16 samples averaged
|
||||||
|
mode |= (nxp.ADC_CFG_AVGS_AVGS_2 << nxp.ADC_CFG_AVGS_Pos) & nxp.ADC_CFG_AVGS_Msk
|
||||||
|
} else if samples >= 8 {
|
||||||
|
// 8 samples averaged
|
||||||
|
mode |= (nxp.ADC_CFG_AVGS_AVGS_1 << nxp.ADC_CFG_AVGS_Pos) & nxp.ADC_CFG_AVGS_Msk
|
||||||
|
} else {
|
||||||
|
// 4 samples averaged
|
||||||
|
mode |= (nxp.ADC_CFG_AVGS_AVGS_0 << nxp.ADC_CFG_AVGS_Pos) & nxp.ADC_CFG_AVGS_Msk
|
||||||
|
}
|
||||||
|
average = nxp.ADC_GC_AVGE
|
||||||
|
}
|
||||||
|
|
||||||
|
return mode, average
|
||||||
|
}
|
||||||
|
|
||||||
|
// isCalibrating returns true if and only if either one (or both) of ADC1 and
|
||||||
|
// ADC2 have their calibrating flags set. ADC reads must wait until these flags
|
||||||
|
// are clear before attempting a conversion.
|
||||||
|
func (a ADC) isCalibrating() bool {
|
||||||
|
return nxp.ADC1.GC.HasBits(nxp.ADC_GC_CAL) || nxp.ADC2.GC.HasBits(nxp.ADC_GC_CAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
const noADCChannel = uint8(0xFF)
|
||||||
|
|
||||||
|
// getADCChannel returns the input channel for ADC1/ADC2 of the receiver Pin p.
|
||||||
|
func (p Pin) getADCChannel() (adc1, adc2 uint8, ok bool) {
|
||||||
|
switch p {
|
||||||
|
case PA12: // [AD_B0_12]: ADC1_IN1 ~
|
||||||
|
return 1, noADCChannel, true
|
||||||
|
case PA13: // [AD_B0_13]: ADC1_IN2 ~
|
||||||
|
return 2, noADCChannel, true
|
||||||
|
case PA14: // [AD_B0_14]: ADC1_IN3 ~
|
||||||
|
return 3, noADCChannel, true
|
||||||
|
case PA15: // [AD_B0_15]: ADC1_IN4 ~
|
||||||
|
return 4, noADCChannel, true
|
||||||
|
case PA16: // [AD_B1_00]: ADC1_IN5 ADC2_IN5
|
||||||
|
return 5, 5, true
|
||||||
|
case PA17: // [AD_B1_01]: ADC1_IN6 ADC2_IN6
|
||||||
|
return 6, 6, true
|
||||||
|
case PA18: // [AD_B1_02]: ADC1_IN7 ADC2_IN7
|
||||||
|
return 7, 7, true
|
||||||
|
case PA19: // [AD_B1_03]: ADC1_IN8 ADC2_IN8
|
||||||
|
return 8, 8, true
|
||||||
|
case PA20: // [AD_B1_04]: ADC1_IN9 ADC2_IN9
|
||||||
|
return 9, 9, true
|
||||||
|
case PA21: // [AD_B1_05]: ADC1_IN10 ADC2_IN10
|
||||||
|
return 10, 10, true
|
||||||
|
case PA22: // [AD_B1_06]: ADC1_IN11 ADC2_IN11
|
||||||
|
return 11, 11, true
|
||||||
|
case PA23: // [AD_B1_07]: ADC1_IN12 ADC2_IN12
|
||||||
|
return 12, 12, true
|
||||||
|
case PA24: // [AD_B1_08]: ADC1_IN13 ADC2_IN13
|
||||||
|
return 13, 13, true
|
||||||
|
case PA25: // [AD_B1_09]: ADC1_IN14 ADC2_IN14
|
||||||
|
return 14, 14, true
|
||||||
|
case PA26: // [AD_B1_10]: ADC1_IN15 ADC2_IN15
|
||||||
|
return 15, 15, true
|
||||||
|
case PA27: // [AD_B1_11]: ADC1_IN0 ADC2_IN0
|
||||||
|
return 16, 16, true
|
||||||
|
case PA28: // [AD_B1_12]: ~ ADC2_IN1
|
||||||
|
return noADCChannel, 1, true
|
||||||
|
case PA29: // [AD_B1_13]: ~ ADC2_IN2
|
||||||
|
return noADCChannel, 2, true
|
||||||
|
case PA30: // [AD_B1_14]: ~ ADC2_IN3
|
||||||
|
return noADCChannel, 3, true
|
||||||
|
case PA31: // [AD_B1_15]: ~ ADC2_IN4
|
||||||
|
return noADCChannel, 4, true
|
||||||
|
default:
|
||||||
|
return noADCChannel, noADCChannel, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,252 @@
|
|||||||
|
//go:build mimxrt1062
|
||||||
|
// +build mimxrt1062
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
// SPI peripheral abstraction layer for the MIMXRT1062
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/nxp"
|
||||||
|
"errors"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPIConfig is used to store config info for SPI.
|
||||||
|
type SPIConfig struct {
|
||||||
|
Frequency uint32
|
||||||
|
SDI Pin
|
||||||
|
SDO Pin
|
||||||
|
SCK Pin
|
||||||
|
CS Pin
|
||||||
|
LSBFirst bool
|
||||||
|
Mode uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c SPIConfig) getPins() (di, do, ck, cs Pin) {
|
||||||
|
if 0 == c.SDI && 0 == c.SDO && 0 == c.SCK && 0 == c.CS {
|
||||||
|
// default pins if none specified
|
||||||
|
return SPI_SDI_PIN, SPI_SDO_PIN, SPI_SCK_PIN, SPI_CS_PIN
|
||||||
|
}
|
||||||
|
return c.SDI, c.SDO, c.SCK, c.CS
|
||||||
|
}
|
||||||
|
|
||||||
|
type SPI struct {
|
||||||
|
Bus *nxp.LPSPI_Type
|
||||||
|
|
||||||
|
// these hold the input selector ("daisy chain") values that select which pins
|
||||||
|
// are connected to the LPSPI device, and should be defined where the SPI
|
||||||
|
// instance is declared (e.g., in the board definition). see the godoc
|
||||||
|
// comments on type muxSelect for more details.
|
||||||
|
muxSDI, muxSDO, muxSCK, muxCS muxSelect
|
||||||
|
|
||||||
|
// these are copied from SPIConfig, during (*SPI).Configure(SPIConfig), and
|
||||||
|
// should be considered read-only for internal reference (i.e., modifying them
|
||||||
|
// will have no desirable effect).
|
||||||
|
sdi, sdo, sck, cs Pin
|
||||||
|
frequency uint32
|
||||||
|
|
||||||
|
// auxiliary state data used internally
|
||||||
|
configured bool
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
statusTxDataRequest = nxp.LPSPI_SR_TDF // Transmit data flag
|
||||||
|
statusRxDataReady = nxp.LPSPI_SR_RDF // Receive data flag
|
||||||
|
statusWordComplete = nxp.LPSPI_SR_WCF // Word Complete flag
|
||||||
|
statusFrameComplete = nxp.LPSPI_SR_FCF // Frame Complete flag
|
||||||
|
statusTransferComplete = nxp.LPSPI_SR_TCF // Transfer Complete flag
|
||||||
|
statusTransmitError = nxp.LPSPI_SR_TEF // Transmit Error flag (FIFO underrun)
|
||||||
|
statusReceiveError = nxp.LPSPI_SR_REF // Receive Error flag (FIFO overrun)
|
||||||
|
statusDataMatch = nxp.LPSPI_SR_DMF // Data Match flag
|
||||||
|
statusModuleBusy = nxp.LPSPI_SR_MBF // Module Busy flag
|
||||||
|
statusAll = nxp.LPSPI_SR_TDF | nxp.LPSPI_SR_RDF |
|
||||||
|
nxp.LPSPI_SR_WCF | nxp.LPSPI_SR_FCF | nxp.LPSPI_SR_TCF | nxp.LPSPI_SR_TEF |
|
||||||
|
nxp.LPSPI_SR_REF | nxp.LPSPI_SR_DMF | nxp.LPSPI_SR_MBF
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errSPINotConfigured = errors.New("SPI interface is not yet configured")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Configure is intended to setup an SPI interface for transmit/receive.
|
||||||
|
func (spi *SPI) Configure(config SPIConfig) {
|
||||||
|
|
||||||
|
const defaultSpiFreq = 4000000 // 4 MHz
|
||||||
|
|
||||||
|
// init pins
|
||||||
|
spi.sdi, spi.sdo, spi.sck, spi.cs = config.getPins()
|
||||||
|
|
||||||
|
// configure the mux and pad control registers
|
||||||
|
spi.sdi.Configure(PinConfig{Mode: PinModeSPISDI})
|
||||||
|
spi.sdo.Configure(PinConfig{Mode: PinModeSPISDO})
|
||||||
|
spi.sck.Configure(PinConfig{Mode: PinModeSPICLK})
|
||||||
|
spi.cs.Configure(PinConfig{Mode: PinModeSPICS})
|
||||||
|
|
||||||
|
// configure the mux input selector
|
||||||
|
spi.muxSDI.connect()
|
||||||
|
spi.muxSDO.connect()
|
||||||
|
spi.muxSCK.connect()
|
||||||
|
spi.muxCS.connect()
|
||||||
|
|
||||||
|
// software reset of LPSPI state registers
|
||||||
|
spi.Bus.CR.SetBits(nxp.LPSPI_CR_RST)
|
||||||
|
// also reset FIFOs (not performed by software reset above)
|
||||||
|
spi.Bus.CR.SetBits(nxp.LPSPI_CR_RRF | nxp.LPSPI_CR_RTF)
|
||||||
|
spi.Bus.CR.Set(0)
|
||||||
|
|
||||||
|
// set controller mode, and input data is sampled on delayed SCK edge
|
||||||
|
spi.Bus.CFGR1.Set(nxp.LPSPI_CFGR1_MASTER | nxp.LPSPI_CFGR1_SAMPLE)
|
||||||
|
|
||||||
|
spi.frequency = config.Frequency
|
||||||
|
if 0 == spi.frequency {
|
||||||
|
spi.frequency = defaultSpiFreq
|
||||||
|
}
|
||||||
|
|
||||||
|
// configure LPSPI clock divisor and CS assertion delays
|
||||||
|
div := spi.getClockDivisor(config.Frequency)
|
||||||
|
ccr := (div << nxp.LPSPI_CCR_SCKDIV_Pos) & nxp.LPSPI_CCR_SCKDIV_Msk
|
||||||
|
ccr |= ((div / 2) << nxp.LPSPI_CCR_DBT_Pos) & nxp.LPSPI_CCR_DBT_Msk
|
||||||
|
ccr |= ((div / 2) << nxp.LPSPI_CCR_PCSSCK_Pos) & nxp.LPSPI_CCR_PCSSCK_Msk
|
||||||
|
spi.Bus.CCR.Set(ccr)
|
||||||
|
|
||||||
|
// 8-bit frame size (words)
|
||||||
|
tcr := uint32(7)
|
||||||
|
if config.LSBFirst {
|
||||||
|
tcr |= nxp.LPSPI_TCR_LSBF
|
||||||
|
}
|
||||||
|
// set polarity and phase
|
||||||
|
switch config.Mode {
|
||||||
|
case Mode1:
|
||||||
|
tcr |= nxp.LPSPI_TCR_CPHA
|
||||||
|
case Mode2:
|
||||||
|
tcr |= nxp.LPSPI_TCR_CPOL
|
||||||
|
case Mode3:
|
||||||
|
tcr |= nxp.LPSPI_TCR_CPOL
|
||||||
|
tcr |= nxp.LPSPI_TCR_CPHA
|
||||||
|
}
|
||||||
|
spi.Bus.TCR.Set(tcr)
|
||||||
|
|
||||||
|
// clear FIFO water marks
|
||||||
|
spi.setWatermark(0, 0)
|
||||||
|
|
||||||
|
// enable LPSPI module
|
||||||
|
spi.Bus.CR.Set(nxp.LPSPI_CR_MEN)
|
||||||
|
|
||||||
|
spi.configured = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer writes/reads a single byte using the SPI interface.
|
||||||
|
func (spi *SPI) Transfer(w byte) (byte, error) {
|
||||||
|
if !spi.configured {
|
||||||
|
return 0, errSPINotConfigured
|
||||||
|
}
|
||||||
|
|
||||||
|
const readTryMax = 10000
|
||||||
|
|
||||||
|
for spi.Bus.SR.HasBits(statusModuleBusy) {
|
||||||
|
} // wait for SPI busy bit to clear
|
||||||
|
|
||||||
|
_, txFIFOSize := spi.getFIFOSize()
|
||||||
|
|
||||||
|
spi.flushFIFO(true, true)
|
||||||
|
spi.Bus.SR.Set(statusAll) // clear all status flags (W1C)
|
||||||
|
|
||||||
|
// enable LPSPI module
|
||||||
|
spi.Bus.CR.Set(nxp.LPSPI_CR_MEN)
|
||||||
|
|
||||||
|
// TODO: unnecessary since we just flushed the FIFO?
|
||||||
|
for { // wait for TX FIFO to not be full
|
||||||
|
if _, txFIFO := spi.getFIFOCount(); txFIFO < txFIFOSize {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write out byte to TX FIFO
|
||||||
|
spi.Bus.TDR.Set(uint32(w))
|
||||||
|
|
||||||
|
// try to read from RX FIFO if anything exists
|
||||||
|
didRead := false
|
||||||
|
data := byte(0)
|
||||||
|
for i := 0; !didRead && (i < readTryMax); i++ {
|
||||||
|
rxFIFO, _ := spi.getFIFOCount()
|
||||||
|
didRead = rxFIFO > 0
|
||||||
|
if didRead {
|
||||||
|
data = byte(spi.Bus.RDR.Get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if nothing was read, then wait for transfer complete flag to decide when
|
||||||
|
// we are finished
|
||||||
|
if !didRead {
|
||||||
|
for !spi.Bus.SR.HasBits(nxp.LPSPI_SR_TCF) {
|
||||||
|
} // wait for all transfers complete flag to set
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi *SPI) isHardwareCSPin(pin Pin) bool {
|
||||||
|
switch unsafe.Pointer(spi.Bus) {
|
||||||
|
case unsafe.Pointer(nxp.LPSPI1):
|
||||||
|
return SPI1_CS_PIN == pin
|
||||||
|
case unsafe.Pointer(nxp.LPSPI2):
|
||||||
|
return SPI2_CS_PIN == pin
|
||||||
|
case unsafe.Pointer(nxp.LPSPI3):
|
||||||
|
return SPI3_CS_PIN == pin
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi *SPI) hasHardwareCSPin() bool {
|
||||||
|
return spi.isHardwareCSPin(spi.cs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getClockDivisor finds the SPI prescalar that minimizes the error between
|
||||||
|
// requested frequency and possible frequencies available with the LPSPI clock.
|
||||||
|
// this routine is based on Teensyduino (libraries/SPI/SPI.cpp):
|
||||||
|
// `void SPIClass::setClockDivider_noInline(uint32_t clk)`
|
||||||
|
func (spi *SPI) getClockDivisor(freq uint32) uint32 {
|
||||||
|
const clock = 132000000 // LPSPI root clock frequency (PLL2)
|
||||||
|
d := uint32(clock)
|
||||||
|
if freq > 0 {
|
||||||
|
d /= freq
|
||||||
|
}
|
||||||
|
if d > 0 && clock/d > freq {
|
||||||
|
d++
|
||||||
|
}
|
||||||
|
if d > 257 {
|
||||||
|
return 255
|
||||||
|
}
|
||||||
|
if d > 2 {
|
||||||
|
return d - 2
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi *SPI) getFIFOSize() (rx, tx uint32) {
|
||||||
|
param := spi.Bus.PARAM.Get()
|
||||||
|
return uint32(1) << ((param & nxp.LPSPI_PARAM_RXFIFO_Msk) >> nxp.LPSPI_PARAM_RXFIFO_Pos),
|
||||||
|
uint32(1) << ((param & nxp.LPSPI_PARAM_TXFIFO_Msk) >> nxp.LPSPI_PARAM_TXFIFO_Pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi *SPI) getFIFOCount() (rx, tx uint32) {
|
||||||
|
fsr := spi.Bus.FSR.Get()
|
||||||
|
return (fsr & nxp.LPSPI_FSR_RXCOUNT_Msk) >> nxp.LPSPI_FSR_RXCOUNT_Pos,
|
||||||
|
(fsr & nxp.LPSPI_FSR_TXCOUNT_Msk) >> nxp.LPSPI_FSR_TXCOUNT_Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi *SPI) flushFIFO(rx, tx bool) {
|
||||||
|
var flush uint32
|
||||||
|
if rx {
|
||||||
|
flush |= nxp.LPSPI_CR_RRF
|
||||||
|
}
|
||||||
|
if tx {
|
||||||
|
flush |= nxp.LPSPI_CR_RTF
|
||||||
|
}
|
||||||
|
spi.Bus.CR.SetBits(flush)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi *SPI) setWatermark(rx, tx uint32) {
|
||||||
|
spi.Bus.FCR.Set(((rx << nxp.LPSPI_FCR_RXWATER_Pos) & nxp.LPSPI_FCR_RXWATER_Msk) |
|
||||||
|
((tx << nxp.LPSPI_FCR_TXWATER_Pos) & nxp.LPSPI_FCR_TXWATER_Msk))
|
||||||
|
}
|
||||||
@@ -7,58 +7,6 @@ import (
|
|||||||
"device/nrf"
|
"device/nrf"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Hardware pins
|
|
||||||
const (
|
|
||||||
P0_00 Pin = 0
|
|
||||||
P0_01 Pin = 1
|
|
||||||
P0_02 Pin = 2
|
|
||||||
P0_03 Pin = 3
|
|
||||||
P0_04 Pin = 4
|
|
||||||
P0_05 Pin = 5
|
|
||||||
P0_06 Pin = 6
|
|
||||||
P0_07 Pin = 7
|
|
||||||
P0_08 Pin = 8
|
|
||||||
P0_09 Pin = 9
|
|
||||||
P0_10 Pin = 10
|
|
||||||
P0_11 Pin = 11
|
|
||||||
P0_12 Pin = 12
|
|
||||||
P0_13 Pin = 13
|
|
||||||
P0_14 Pin = 14
|
|
||||||
P0_15 Pin = 15
|
|
||||||
P0_16 Pin = 16
|
|
||||||
P0_17 Pin = 17
|
|
||||||
P0_18 Pin = 18
|
|
||||||
P0_19 Pin = 19
|
|
||||||
P0_20 Pin = 20
|
|
||||||
P0_21 Pin = 21
|
|
||||||
P0_22 Pin = 22
|
|
||||||
P0_23 Pin = 23
|
|
||||||
P0_24 Pin = 24
|
|
||||||
P0_25 Pin = 25
|
|
||||||
P0_26 Pin = 26
|
|
||||||
P0_27 Pin = 27
|
|
||||||
P0_28 Pin = 28
|
|
||||||
P0_29 Pin = 29
|
|
||||||
P0_30 Pin = 30
|
|
||||||
P0_31 Pin = 31
|
|
||||||
P1_00 Pin = 32
|
|
||||||
P1_01 Pin = 33
|
|
||||||
P1_02 Pin = 34
|
|
||||||
P1_03 Pin = 35
|
|
||||||
P1_04 Pin = 36
|
|
||||||
P1_05 Pin = 37
|
|
||||||
P1_06 Pin = 38
|
|
||||||
P1_07 Pin = 39
|
|
||||||
P1_08 Pin = 40
|
|
||||||
P1_09 Pin = 41
|
|
||||||
P1_10 Pin = 42
|
|
||||||
P1_11 Pin = 43
|
|
||||||
P1_12 Pin = 44
|
|
||||||
P1_13 Pin = 45
|
|
||||||
P1_14 Pin = 46
|
|
||||||
P1_15 Pin = 47
|
|
||||||
)
|
|
||||||
|
|
||||||
// Get peripheral and pin number for this GPIO pin.
|
// Get peripheral and pin number for this GPIO pin.
|
||||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||||
if p >= 32 {
|
if p >= 32 {
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ var (
|
|||||||
ErrNotConfigured = errors.New("device has not been configured")
|
ErrNotConfigured = errors.New("device has not been configured")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:linkname gosched runtime.Gosched
|
||||||
|
func gosched()
|
||||||
|
|
||||||
// PutcharUART writes a byte to the UART synchronously, without using interrupts
|
// PutcharUART writes a byte to the UART synchronously, without using interrupts
|
||||||
// or calling the scheduler
|
// or calling the scheduler
|
||||||
func PutcharUART(u *UART, c byte) {
|
func PutcharUART(u *UART, c byte) {
|
||||||
|
|||||||
@@ -86,6 +86,11 @@ func ticks() uint64 {
|
|||||||
return timer.timeElapsed()
|
return timer.timeElapsed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname lightSleep runtime.machineLightSleep
|
||||||
|
func lightSleep(ticks uint64) {
|
||||||
|
timer.lightSleep(ticks)
|
||||||
|
}
|
||||||
|
|
||||||
// UART pins
|
// UART pins
|
||||||
const (
|
const (
|
||||||
UART_TX_PIN = UART0_TX_PIN
|
UART_TX_PIN = UART0_TX_PIN
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user