mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 14:48:40 +00:00
Compare commits
122 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6384ecace0 | |||
| 38881a2850 | |||
| 0f1cf14743 | |||
| 7c34f7704e | |||
| 377415a6c3 | |||
| 1e13c6d18f | |||
| 14121f4b0e | |||
| 7768195835 | |||
| c095b7e9c4 | |||
| 1f6d34d995 | |||
| 9951eb9990 | |||
| c8f77d26a8 | |||
| 8eaa9bd8d0 | |||
| 6061f3db92 | |||
| 410aa0e0d8 | |||
| c66836cf3a | |||
| ca9211b582 | |||
| cb7d470ba4 | |||
| 5baee9a8ee | |||
| c47f52b546 | |||
| a2588d8db3 | |||
| f529b6071d | |||
| 5557e97888 | |||
| d04b07fa8b | |||
| 7486277012 | |||
| 36d60b8349 | |||
| c55191283b | |||
| ad30085b93 | |||
| e9ca41735a | |||
| df1f83f4e1 | |||
| 5879d785a9 | |||
| 186018abeb | |||
| 828b9614c2 | |||
| f5e933cd4d | |||
| 0952b1b984 | |||
| ed55f56d85 | |||
| edb8766aab | |||
| 413bb55e2c | |||
| 0db1a4bec1 | |||
| 2867da164d | |||
| 7b8ae2d6b6 | |||
| 5b8127fd4e | |||
| cc3e785692 | |||
| 0ea5cfc8fe | |||
| 70f1a5429a | |||
| 2fb2113168 | |||
| cb39611389 | |||
| 45764325b4 | |||
| bb66232164 | |||
| 9679e7f1cc | |||
| 6cdfc298cf | |||
| 9a4bfd0e96 | |||
| ee3106be98 | |||
| 57f49af726 | |||
| 38a80b45d3 | |||
| e9003e2deb | |||
| 204659bdcd | |||
| 08ca1d13d0 | |||
| 53db436a7d | |||
| 0ad15551c8 | |||
| afd65e224a | |||
| 5f50c3b60b | |||
| 8858d49989 | |||
| cf39d8b2c8 | |||
| d92a31b440 | |||
| 9c77a38358 | |||
| d0445d6f83 | |||
| a40b11f535 | |||
| 8c90f4facf | |||
| c7c9a76af5 | |||
| 6984af43a0 | |||
| 81c56c3ab8 | |||
| 3d9a1ca22a | |||
| c2083014b3 | |||
| 603a81f194 | |||
| cfc32794a7 | |||
| 1d9c53d00e | |||
| 8d2a07b927 | |||
| ffe6dfd21b | |||
| cf21380264 | |||
| 534b3b0c0b | |||
| ceeb233ff6 | |||
| de3f0af829 | |||
| a511f18c64 | |||
| 731bd5cd1b | |||
| 4acf59546e | |||
| 2ee4d9aaa1 | |||
| b58b7c59ae | |||
| e14b6a7009 | |||
| a1a2d1ab81 | |||
| 07591178cd | |||
| 43bdc888dd | |||
| 4229e670ce | |||
| 76a7ad2a3e | |||
| e4f551ac7f | |||
| 338590cc75 | |||
| 22d70604d8 | |||
| c6add1e769 | |||
| c6609a02fa | |||
| a449c4813a | |||
| 19fb1bfafc | |||
| f91b6ad0df | |||
| 94459cefe5 | |||
| 803ba4f54d | |||
| 2919fa8b14 | |||
| 6420e90124 | |||
| 2d289addb7 | |||
| 4df145dcb4 | |||
| e065da20cb | |||
| d7c77b6761 | |||
| f51029484a | |||
| 03cfcbc17c | |||
| 649f49e000 | |||
| 3bcd4dc3e0 | |||
| 772c71ec27 | |||
| 9b896dc981 | |||
| d4189feca6 | |||
| 777048cfa9 | |||
| 2b215955ca | |||
| ce25f00769 | |||
| 1a59aecb63 | |||
| cca32e67a9 |
@@ -10,12 +10,12 @@ commands:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-16-v3
|
||||
- llvm-source-17-v1
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-16-v3
|
||||
key: llvm-source-17-v1
|
||||
paths:
|
||||
- llvm-project/clang/lib/Headers
|
||||
- llvm-project/clang/include
|
||||
@@ -75,10 +75,10 @@ commands:
|
||||
- run: go install -tags=llvm<<parameters.llvm>> .
|
||||
- restore_cache:
|
||||
keys:
|
||||
- wasi-libc-sysroot-systemclang-v6
|
||||
- wasi-libc-sysroot-systemclang-v7
|
||||
- run: make wasi-libc
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-systemclang-v6
|
||||
key: wasi-libc-sysroot-systemclang-v7
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
- when:
|
||||
@@ -105,9 +105,9 @@ jobs:
|
||||
- test-linux:
|
||||
llvm: "15"
|
||||
resource_class: large
|
||||
test-llvm17-go121:
|
||||
test-llvm17-go122:
|
||||
docker:
|
||||
- image: golang:1.21-bullseye
|
||||
- image: golang:1.22-bullseye
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "17"
|
||||
@@ -119,5 +119,5 @@ workflows:
|
||||
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
||||
# least the smoke tests still pass.
|
||||
- test-llvm15-go118
|
||||
# This tests the upcoming LLVM 17 support.
|
||||
- test-llvm17-go121
|
||||
# This tests LLVM 17 support when linking against system libraries.
|
||||
- test-llvm17-go122
|
||||
|
||||
@@ -14,26 +14,36 @@ concurrency:
|
||||
jobs:
|
||||
build-macos:
|
||||
name: build-macos
|
||||
runs-on: macos-11
|
||||
strategy:
|
||||
matrix:
|
||||
# macos-12: amd64 (oldest supported version as of 05-02-2024)
|
||||
# macos-14: arm64 (oldest arm64 version)
|
||||
os: [macos-12, macos-14]
|
||||
include:
|
||||
- os: macos-12
|
||||
goarch: amd64
|
||||
- os: macos-14
|
||||
goarch: arm64
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.22'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v3
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-16-macos-v1
|
||||
key: llvm-source-17-${{ matrix.os }}-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -44,7 +54,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v3
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -55,10 +65,10 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v3
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-16-macos-v1
|
||||
key: llvm-build-17-${{ matrix.os }}-v1
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -73,16 +83,16 @@ jobs:
|
||||
make llvm-build
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save LLVM build cache
|
||||
uses: actions/cache/save@v3
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||
path: llvm-build
|
||||
- name: Cache wasi-libc sysroot
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-v4
|
||||
key: wasi-libc-sysroot-${{ matrix.os }}-v1
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
@@ -98,7 +108,7 @@ jobs:
|
||||
run: make tinygo-test
|
||||
- name: Make release artifact
|
||||
shell: bash
|
||||
run: cp -p build/release.tar.gz build/tinygo.darwin-amd64.tar.gz
|
||||
run: cp -p build/release.tar.gz build/tinygo.darwin-${{ matrix.goarch }}.tar.gz
|
||||
- name: Publish release artifact
|
||||
# Note: this release artifact is double-zipped, see:
|
||||
# https://github.com/actions/upload-artifact/issues/39
|
||||
@@ -106,10 +116,10 @@ jobs:
|
||||
# - have a double-zipped artifact when downloaded from the UI
|
||||
# - have a very slow artifact upload
|
||||
# We're doing the former here, to keep artifact uploads fast.
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: darwin-amd64-double-zipped
|
||||
path: build/tinygo.darwin-amd64.tar.gz
|
||||
name: darwin-${{ matrix.goarch }}-double-zipped
|
||||
path: build/tinygo.darwin-${{ matrix.goarch }}.tar.gz
|
||||
- name: Smoke tests
|
||||
shell: bash
|
||||
run: make smoketest TINYGO=$(PWD)/build/tinygo
|
||||
@@ -120,31 +130,30 @@ jobs:
|
||||
matrix:
|
||||
version: [16, 17]
|
||||
steps:
|
||||
- name: Update Homebrew
|
||||
run: brew update
|
||||
- name: Set up Homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
- name: Fix Python symlinks
|
||||
run: |
|
||||
# Github runners have broken symlinks, so relink
|
||||
# see: https://github.com/actions/setup-python/issues/577
|
||||
brew unlink python
|
||||
brew link --overwrite python
|
||||
brew list -1 | grep python | while read formula; do brew unlink $formula; brew link --overwrite $formula; done
|
||||
- name: Install LLVM
|
||||
run: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@${{ matrix.version }}
|
||||
brew install llvm@${{ matrix.version }}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.22'
|
||||
cache: true
|
||||
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
||||
run: go install -tags=llvm${{ matrix.version }}
|
||||
- name: Check binary
|
||||
run: tinygo version
|
||||
- name: Build TinyGo (default LLVM)
|
||||
if: matrix.version == 16
|
||||
if: matrix.version == 17
|
||||
run: go install
|
||||
- name: Check binary
|
||||
if: matrix.version == 16
|
||||
if: matrix.version == 17
|
||||
run: tinygo version
|
||||
|
||||
@@ -19,15 +19,26 @@ jobs:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Free Disk space
|
||||
shell: bash
|
||||
run: |
|
||||
df -h
|
||||
sudo rm -rf /opt/hostedtoolcache
|
||||
sudo rm -rf /usr/local/lib/android
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
sudo rm -rf /opt/ghc
|
||||
sudo rm -rf /usr/local/graalvm
|
||||
sudo rm -rf /usr/local/share/boost
|
||||
df -h
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
tinygo/tinygo-dev
|
||||
@@ -36,24 +47,23 @@ jobs:
|
||||
type=sha,format=long
|
||||
type=raw,value=latest
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
- name: Log in to Github Container Registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-contexts: tinygo-llvm-build=docker-image://tinygo/llvm-16
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
- name: Trigger Drivers repo build on Github Actions
|
||||
|
||||
+44
-44
@@ -18,10 +18,10 @@ jobs:
|
||||
# statically linked binary.
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: golang:1.21-alpine
|
||||
image: golang:1.22-alpine
|
||||
steps:
|
||||
- name: Install apk dependencies
|
||||
# tar: needed for actions/cache@v3
|
||||
# tar: needed for actions/cache@v4
|
||||
# git+openssh: needed for checkout (I think?)
|
||||
# ruby: needed to install fpm
|
||||
run: apk add tar git openssh make g++ ruby
|
||||
@@ -29,21 +29,21 @@ jobs:
|
||||
# We're not on a multi-user machine, so this is safe.
|
||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v3
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-16-linux-alpine-v1
|
||||
key: llvm-source-17-linux-alpine-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v3
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -65,10 +65,10 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v3
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-16-linux-alpine-v1
|
||||
key: llvm-build-17-linux-alpine-v1
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -83,13 +83,13 @@ jobs:
|
||||
# Remove unnecessary object files (to reduce cache size).
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save LLVM build cache
|
||||
uses: actions/cache/save@v3
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||
path: llvm-build
|
||||
- name: Cache Binaryen
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-alpine-v1
|
||||
@@ -100,10 +100,10 @@ jobs:
|
||||
apk add cmake samurai python3
|
||||
make binaryen STATIC=1
|
||||
- name: Cache wasi-libc
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-linux-alpine-v1
|
||||
key: wasi-libc-sysroot-linux-alpine-v2
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
@@ -119,7 +119,7 @@ jobs:
|
||||
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_amd64.deb
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-amd64-double-zipped
|
||||
path: |
|
||||
@@ -131,11 +131,11 @@ jobs:
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.22'
|
||||
cache: true
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
@@ -144,7 +144,7 @@ jobs:
|
||||
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v14.0.4-x86_64-linux.tar.xz --strip-components=1 wasmtime-v14.0.4-x86_64-linux/*
|
||||
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
|
||||
- name: Download release artifact
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: linux-amd64-double-zipped
|
||||
- name: Extract release tarball
|
||||
@@ -161,7 +161,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install apt dependencies
|
||||
@@ -176,14 +176,14 @@ jobs:
|
||||
simavr \
|
||||
ninja-build
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.22'
|
||||
cache: true
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '16'
|
||||
node-version: '18'
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
|
||||
@@ -191,10 +191,10 @@ jobs:
|
||||
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v14.0.4-x86_64-linux.tar.xz --strip-components=1 wasmtime-v14.0.4-x86_64-linux/*
|
||||
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v3
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-16-linux-asserts-v1
|
||||
key: llvm-source-17-linux-asserts-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -205,7 +205,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v3
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -216,10 +216,10 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v3
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-16-linux-asserts-v1
|
||||
key: llvm-build-17-linux-asserts-v1
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -232,13 +232,13 @@ jobs:
|
||||
# Remove unnecessary object files (to reduce cache size).
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save LLVM build cache
|
||||
uses: actions/cache/save@v3
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||
path: llvm-build
|
||||
- name: Cache Binaryen
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-asserts-v1
|
||||
@@ -247,10 +247,10 @@ jobs:
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
run: make binaryen
|
||||
- name: Cache wasi-libc
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-linux-asserts-v5
|
||||
key: wasi-libc-sysroot-linux-asserts-v6
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
@@ -290,7 +290,7 @@ jobs:
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
@@ -299,15 +299,15 @@ jobs:
|
||||
g++-${{ matrix.toolchain }} \
|
||||
libc6-dev-${{ matrix.libc }}-cross
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.22'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v3
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-16-linux-v1
|
||||
key: llvm-source-17-linux-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -318,7 +318,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v3
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -329,10 +329,10 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v3
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-16-linux-${{ matrix.goarch }}-v1
|
||||
key: llvm-build-17-linux-${{ matrix.goarch }}-v1
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -347,13 +347,13 @@ jobs:
|
||||
# Remove unnecessary object files (to reduce cache size).
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save LLVM build cache
|
||||
uses: actions/cache/save@v3
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||
path: llvm-build
|
||||
- name: Cache Binaryen
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-${{ matrix.goarch }}-v3
|
||||
@@ -373,7 +373,7 @@ jobs:
|
||||
run: |
|
||||
make CROSS=${{ matrix.toolchain }}
|
||||
- name: Download amd64 release
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: linux-amd64-double-zipped
|
||||
- name: Extract amd64 release
|
||||
@@ -390,7 +390,7 @@ jobs:
|
||||
cp -p build/release.tar.gz /tmp/tinygo.linux-${{ matrix.goarch }}.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_${{ matrix.libc }}.deb
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-${{ matrix.goarch }}-double-zipped
|
||||
path: |
|
||||
|
||||
@@ -25,18 +25,18 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
tinygo/llvm-16
|
||||
ghcr.io/${{ github.repository_owner }}/llvm-16
|
||||
tinygo/llvm-17
|
||||
ghcr.io/${{ github.repository_owner }}/llvm-17
|
||||
tags: |
|
||||
type=sha,format=long
|
||||
type=raw,value=latest
|
||||
@@ -46,13 +46,13 @@ jobs:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
- name: Log in to Github Container Registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
target: tinygo-llvm-build
|
||||
context: .
|
||||
|
||||
@@ -16,22 +16,22 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Pull musl
|
||||
run: |
|
||||
git submodule update --init lib/musl
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v3
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-16-linux-nix-v1
|
||||
key: llvm-source-17-linux-nix-v1
|
||||
path: |
|
||||
llvm-project/compiler-rt
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v3
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
# Command that's part of sizediff.yml. This is put in a separate file so that it
|
||||
# still works after checking out the dev branch (that is, when going from LLVM
|
||||
# 16 to LLVM 17 for example, both Clang 16 and Clang 17 are installed).
|
||||
|
||||
echo 'deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends -y \
|
||||
llvm-17-dev \
|
||||
clang-17 \
|
||||
libclang-17-dev \
|
||||
lld-17
|
||||
@@ -18,32 +18,24 @@ jobs:
|
||||
run: |
|
||||
echo "$HOME/go/bin" >> $GITHUB_PATH
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # fetch all history (no sparse checkout)
|
||||
submodules: true
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
echo 'deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends -y \
|
||||
llvm-16-dev \
|
||||
clang-16 \
|
||||
libclang-16-dev \
|
||||
lld-16
|
||||
run: ./.github/workflows/sizediff-install-pkgs.sh
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-16-sizediff-v1
|
||||
key: llvm-source-17-sizediff-v1
|
||||
path: |
|
||||
llvm-project/compiler-rt
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: go-cache-linux-sizediff-v1-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
@@ -55,22 +47,25 @@ jobs:
|
||||
- name: Save HEAD
|
||||
run: git branch github-actions-saved-HEAD HEAD
|
||||
|
||||
# Compute sizes for the dev branch
|
||||
- name: Checkout dev branch
|
||||
run: git checkout --no-recurse-submodules `git merge-base HEAD origin/dev`
|
||||
- name: Build tinygo binary for the dev branch
|
||||
run: go install
|
||||
- name: Determine binary sizes on the dev branch
|
||||
run: (cd drivers; make smoke-test XTENSA=0 | tee sizes-dev.txt)
|
||||
|
||||
# Compute sizes for the PR branch
|
||||
- name: Checkout PR branch
|
||||
run: git checkout --no-recurse-submodules github-actions-saved-HEAD
|
||||
- name: Build tinygo binary for the PR branch
|
||||
run: go install
|
||||
- name: Determine binary sizes on the PR branch
|
||||
run: (cd drivers; make smoke-test XTENSA=0 | tee sizes-pr.txt)
|
||||
|
||||
# Compute sizes for the dev branch
|
||||
- name: Checkout dev branch
|
||||
run: |
|
||||
git reset --hard origin/dev
|
||||
git checkout --no-recurse-submodules `git merge-base HEAD origin/dev`
|
||||
- name: Install apt dependencies on the dev branch
|
||||
# this is only needed on a PR that changes the LLVM version
|
||||
run: ./.github/workflows/sizediff-install-pkgs.sh
|
||||
- name: Build tinygo binary for the dev branch
|
||||
run: go install
|
||||
- name: Determine binary sizes on the dev branch
|
||||
run: (cd drivers; make smoke-test XTENSA=0 | tee sizes-dev.txt)
|
||||
|
||||
# Create comment
|
||||
# TODO: add a summary, something like:
|
||||
# - overall size difference (percent)
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.3
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
@@ -29,19 +29,19 @@ jobs:
|
||||
run: |
|
||||
scoop install ninja binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.22'
|
||||
cache: true
|
||||
- name: Restore cached LLVM source
|
||||
uses: actions/cache/restore@v3
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-16-windows-v1
|
||||
key: llvm-source-17-windows-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save cached LLVM source
|
||||
uses: actions/cache/save@v3
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -63,10 +63,10 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore cached LLVM build
|
||||
uses: actions/cache/restore@v3
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-16-windows-v2
|
||||
key: llvm-build-17-windows-v1
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -80,16 +80,16 @@ jobs:
|
||||
# Remove unnecessary object files (to reduce cache size).
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save cached LLVM build
|
||||
uses: actions/cache/save@v3
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||
path: llvm-build
|
||||
- name: Cache wasi-libc sysroot
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-v4
|
||||
key: wasi-libc-sysroot-v5
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
@@ -116,7 +116,7 @@ jobs:
|
||||
# - have a dobule-zipped artifact when downloaded from the UI
|
||||
# - have a very slow artifact upload
|
||||
# We're doing the former here, to keep artifact uploads fast.
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-amd64-double-zipped
|
||||
path: build/release/release.zip
|
||||
@@ -126,7 +126,7 @@ jobs:
|
||||
needs: build-windows
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.3
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
@@ -139,14 +139,14 @@ jobs:
|
||||
run: |
|
||||
scoop install binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.22'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows-amd64-double-zipped
|
||||
path: build/
|
||||
@@ -163,20 +163,20 @@ jobs:
|
||||
needs: build-windows
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.3
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.22'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows-amd64-double-zipped
|
||||
path: build/
|
||||
@@ -192,7 +192,7 @@ jobs:
|
||||
needs: build-windows
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.3
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
@@ -205,14 +205,14 @@ jobs:
|
||||
run: |
|
||||
scoop install binaryen && scoop install wasmtime@14.0.4
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.22'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows-amd64-double-zipped
|
||||
path: build/
|
||||
|
||||
+7
-2
@@ -9,10 +9,11 @@
|
||||
url = https://github.com/avr-rust/avr-mcu.git
|
||||
[submodule "lib/cmsis-svd"]
|
||||
path = lib/cmsis-svd
|
||||
url = https://github.com/tinygo-org/cmsis-svd
|
||||
url = https://github.com/cmsis-svd/cmsis-svd-data.git
|
||||
branch = main
|
||||
[submodule "lib/wasi-libc"]
|
||||
path = lib/wasi-libc
|
||||
url = https://github.com/CraneStation/wasi-libc
|
||||
url = https://github.com/WebAssembly/wasi-libc
|
||||
[submodule "lib/picolibc"]
|
||||
path = lib/picolibc
|
||||
url = https://github.com/keith-packard/picolibc.git
|
||||
@@ -34,3 +35,7 @@
|
||||
[submodule "lib/renesas-svd"]
|
||||
path = lib/renesas-svd
|
||||
url = https://github.com/tinygo-org/renesas-svd.git
|
||||
[submodule "src/net"]
|
||||
path = src/net
|
||||
url = https://github.com/tinygo-org/net.git
|
||||
branch = dev
|
||||
|
||||
+102
@@ -1,3 +1,105 @@
|
||||
0.31.2
|
||||
---
|
||||
|
||||
* **general**
|
||||
* update the `net` submodule to updated version with `Buffers` implementation
|
||||
|
||||
* **compiler**
|
||||
* `syscall`: add wasm_unknown tag to some additional files so it can compile more code
|
||||
|
||||
* **standard library**
|
||||
* `runtime`: add Frame.Entry field
|
||||
|
||||
|
||||
0.31.1
|
||||
---
|
||||
|
||||
* **general**
|
||||
* fix Binaryen build in make task
|
||||
* update final build stage of Docker `dev` image to go1.22
|
||||
* only use GHA cache for building Docker `dev` image
|
||||
* update the `net` submodule to latest version
|
||||
|
||||
* **compiler**
|
||||
* `interp`: make getelementptr offsets signed
|
||||
* `interp`: return a proper error message when indexing out of range
|
||||
|
||||
|
||||
0.31.0
|
||||
---
|
||||
|
||||
* **general**
|
||||
* remove LLVM 14 support
|
||||
* add LLVM 17 support, and use it by default
|
||||
* add Nix flake support
|
||||
* update bundled Binaryen to version 116
|
||||
* add `ports` subcommand that lists available serial ports for `-port` and `-monitor`
|
||||
* support wasmtime version 14
|
||||
* add `-serial=rtt` for serial output over SWD
|
||||
* add Go 1.22 support and use it by default
|
||||
* change minimum Node.js version from 16 to 18
|
||||
* **compiler**
|
||||
* use the new LLVM pass manager
|
||||
* allow systems with more stack space to allocate larger values on the stack
|
||||
* `build`: fix a crash due to sharing GlobalValues between build instances
|
||||
* `cgo`: add `C._Bool` type
|
||||
* `cgo`: fix calling CGo callback inside generic function
|
||||
* `compileopts`: set `purego` build tag by default so that more packages can be built
|
||||
* `compileopts`: force-enable CGo to avoid build issues
|
||||
* `compiler`: fix crash on type assert on interfaces with no methods
|
||||
* `interp`: print LLVM instruction in traceback
|
||||
* `interp`: support runtime times by running them at runtime
|
||||
* `loader`: enforce Go language version in the type checker (this may break existing programs with an incorrect Go version in go.mod)
|
||||
* `transform`: fix bug in StringToBytes optimization pass
|
||||
* **standard library**
|
||||
* `crypto/tls`: stub out a lot of functions
|
||||
* `internal/task`, `machine`: make TinyGo code usable with "big Go" CGo
|
||||
* `machine`: implement `I2C.SetBaudRate` consistently across chips
|
||||
* `machine`: implement `SPI.Configure` consistently across chips
|
||||
* `machine`: add `DeviceID` for nrf, rp2040, sam, stm32
|
||||
* `machine`: use smaller UART buffer size on atmega chips
|
||||
* `machine/usb`: allow setting a serial number using a linker flag
|
||||
* `math`: support more math functions on baremetal (picolibc) systems
|
||||
* `net`: replace entire net package with a new one based on the netdev driver
|
||||
* `os/user`: add bare-bones implementation of this package
|
||||
* `reflect`: stub `CallSlice` and `FuncOf`
|
||||
* `reflect`: add `TypeFor[T]`
|
||||
* `reflect`: update `IsZero` to Go 1.22 semantics
|
||||
* `reflect`: move indirect values into interface when setting interfaces
|
||||
* `runtime`: stub `Breakpoint`
|
||||
* `sync`: implement trylock
|
||||
* **targets**
|
||||
* `atmega`: use UART double speed mode for fewer errors and higher throughput
|
||||
* `atmega328pb`: refactor to enable extra uart
|
||||
* `avr`: don't compile large parts of picolibc (math, stdio) for LLVM 17 support
|
||||
* `esp32`: switch over to the official SVD file
|
||||
* `esp32c3`: implement USB_SERIAL for USBCDC communication
|
||||
* `esp32c3`: implement I2C
|
||||
* `esp32c3`: implement RNG
|
||||
* `esp32c3`: add more ROM functions and update linker script for the in-progress wifi support
|
||||
* `esp32c3`: update to newer SVD files
|
||||
* `rp2040`: add support for UART hardware flow control
|
||||
* `rp2040`: add definition for `machine.PinToggle`
|
||||
* `rp2040`: set XOSC startup delay multiplier
|
||||
* `samd21`: add support for UART hardware flow control
|
||||
* `samd51`: add support for UART hardware flow control
|
||||
* `wasm`: increase default stack size to 64k for wasi/wasm targets
|
||||
* `wasm`: bump wasi-libc version to SDK 20
|
||||
* `wasm`: remove line of dead code in wasm_exec.js
|
||||
* **new targets/boards**
|
||||
* `qtpy-esp32c3`: add Adafruit QT Py ESP32-C3 board
|
||||
* `mksnanov3`: add support for the MKS Robin Nano V3.x
|
||||
* `nrf52840-generic`: add generic nrf52840 chip support
|
||||
* `thumby`: add support for Thumby
|
||||
* `wasm`: add new `wasm-unknown` target that doesn't depend on WASI or a browser
|
||||
* **boards**
|
||||
* `arduino-mkrwifi1010`, `arduino-nano33`, `nano-rp2040`, `matrixportal-m4`, `metro-m4-airlift`, `pybadge`, `pyportal`: add `ninafw` build tag and some constants for BLE support
|
||||
* `gopher-badge`: fix typo in USB product name
|
||||
* `nano-rp2040`: add UART1 and correct mappings for NINA via UART
|
||||
* `pico`: bump default stack size from 2kB to 8kB
|
||||
* `wioterminal`: expose UART4
|
||||
|
||||
|
||||
0.30.0
|
||||
---
|
||||
|
||||
|
||||
+22
-19
@@ -1,10 +1,15 @@
|
||||
# tinygo-llvm stage obtains the llvm source for TinyGo
|
||||
FROM golang:1.21 AS tinygo-llvm
|
||||
FROM golang:1.22 AS tinygo-llvm
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y apt-utils make cmake clang-15 ninja-build
|
||||
apt-get install -y apt-utils make cmake clang-15 ninja-build && \
|
||||
rm -rf \
|
||||
/var/lib/apt/lists/* \
|
||||
/var/log/* \
|
||||
/var/tmp/* \
|
||||
/tmp/*
|
||||
|
||||
COPY ./Makefile /tinygo/Makefile
|
||||
COPY ./GNUmakefile /tinygo/GNUmakefile
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
make llvm-source
|
||||
@@ -15,26 +20,24 @@ FROM tinygo-llvm AS tinygo-llvm-build
|
||||
RUN cd /tinygo/ && \
|
||||
make llvm-build
|
||||
|
||||
# tinygo-compiler stage builds the compiler itself
|
||||
FROM tinygo-llvm-build AS tinygo-compiler
|
||||
# tinygo-compiler-build stage builds the compiler itself
|
||||
FROM tinygo-llvm-build AS tinygo-compiler-build
|
||||
|
||||
COPY . /tinygo
|
||||
|
||||
# update submodules
|
||||
# build the compiler and tools
|
||||
RUN cd /tinygo/ && \
|
||||
rm -rf ./lib/*/ && \
|
||||
git submodule sync && \
|
||||
git submodule update --init --recursive --force
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
make
|
||||
|
||||
# tinygo-tools stage installs the needed dependencies to compile TinyGo programs for all platforms.
|
||||
FROM tinygo-compiler AS tinygo-tools
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
make wasi-libc binaryen && \
|
||||
git submodule update --init --recursive && \
|
||||
make gen-device -j4 && \
|
||||
cp build/* $GOPATH/bin/
|
||||
make build/release
|
||||
|
||||
# tinygo-compiler copies the compiler build over to a base Go container (without
|
||||
# all the build tools etc).
|
||||
FROM golang:1.22 AS tinygo-compiler
|
||||
|
||||
# Copy tinygo build.
|
||||
COPY --from=tinygo-compiler-build /tinygo/build/release/tinygo /tinygo
|
||||
|
||||
# Configure the container.
|
||||
ENV PATH="${PATH}:/tinygo/bin"
|
||||
CMD ["tinygo"]
|
||||
|
||||
+18
-8
@@ -238,7 +238,7 @@ gen-device-renesas: build/gen-device-svd
|
||||
|
||||
# Get LLVM sources.
|
||||
$(LLVM_PROJECTDIR)/llvm:
|
||||
git clone -b xtensa_release_16.x --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR)
|
||||
git clone -b xtensa_release_17.0.1 --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR)
|
||||
llvm-source: $(LLVM_PROJECTDIR)/llvm
|
||||
|
||||
# Configure LLVM.
|
||||
@@ -256,7 +256,7 @@ ifneq ($(USE_SYSTEM_BINARYEN),1)
|
||||
binaryen: build/wasm-opt$(EXE)
|
||||
build/wasm-opt$(EXE):
|
||||
mkdir -p build
|
||||
cd lib/binaryen && cmake -G Ninja . -DBUILD_STATIC_LIB=ON $(BINARYEN_OPTION) && ninja bin/wasm-opt$(EXE)
|
||||
cd lib/binaryen && cmake -G Ninja . -DBUILD_STATIC_LIB=ON -DBUILD_TESTS=OFF $(BINARYEN_OPTION) && ninja bin/wasm-opt$(EXE)
|
||||
cp lib/binaryen/bin/wasm-opt$(EXE) build/wasm-opt$(EXE)
|
||||
endif
|
||||
|
||||
@@ -269,14 +269,14 @@ lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
||||
|
||||
# Check for Node.js used during WASM tests.
|
||||
NODEJS_VERSION := $(word 1,$(subst ., ,$(shell node -v | cut -c 2-)))
|
||||
MIN_NODEJS_VERSION=16
|
||||
MIN_NODEJS_VERSION=18
|
||||
|
||||
.PHONY: check-nodejs-version
|
||||
check-nodejs-version:
|
||||
ifeq (, $(shell which node))
|
||||
@echo "Install NodeJS version 16+ to run tests."; exit 1;
|
||||
@echo "Install NodeJS version 18+ to run tests."; exit 1;
|
||||
endif
|
||||
@if [ $(NODEJS_VERSION) -lt $(MIN_NODEJS_VERSION) ]; then echo "Install NodeJS version 16+ to run tests."; exit 1; fi
|
||||
@if [ $(NODEJS_VERSION) -lt $(MIN_NODEJS_VERSION) ]; then echo "Install NodeJS version 18+ to run tests."; exit 1; fi
|
||||
|
||||
# Build the Go compiler.
|
||||
tinygo:
|
||||
@@ -670,6 +670,8 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=ae-rp2040 examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=thumby examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
# test pwm
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -684,6 +686,8 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/usb-midi
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nrf52840-s140v6-uf2-generic examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(STM32), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -717,7 +721,11 @@ ifneq ($(STM32), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=swan examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=mksnanov3 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
endif
|
||||
$(TINYGO) build -size short -o test.hex -target=atmega328pb examples/blinkm
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
|
||||
@@ -754,9 +762,7 @@ ifneq ($(XTENSA), 0)
|
||||
$(TINYGO) build -size short -o test.bin -target mch2022 examples/serial
|
||||
@$(MD5SUM) test.bin
|
||||
endif
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3 examples/serial
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-12f examples/serial
|
||||
$(TINYGO) build -size short -o test.bin -target=qtpy-esp32c3 examples/serial
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=m5stamp-c3 examples/serial
|
||||
@$(MD5SUM) test.bin
|
||||
@@ -769,6 +775,7 @@ endif
|
||||
ifneq ($(WASM), 0)
|
||||
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/export
|
||||
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/main
|
||||
$(TINYGO) build -size short -o wasm.wasm -target=wasm-unknown examples/hello-wasm-unknown
|
||||
endif
|
||||
# test various compiler flags
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -gc=none -scheduler=none examples/blinky1
|
||||
@@ -777,6 +784,8 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -serial=none examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -serial=rtt examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
|
||||
@$(MD5SUM) test.nro
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||
@@ -836,6 +845,7 @@ endif
|
||||
@cp -rp lib/musl/src/include build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/internal build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/legacy build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/linux build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/malloc build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/mman build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/math build/release/tinygo/lib/musl/src
|
||||
|
||||
+3
-15
@@ -197,6 +197,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
Scheduler: config.Scheduler(),
|
||||
AutomaticStackSize: config.AutomaticStackSize(),
|
||||
DefaultStackSize: config.StackSize(),
|
||||
MaxStackAlloc: config.MaxStackAlloc(),
|
||||
NeedsStackObjects: config.NeedsStackObjects(),
|
||||
Debug: !config.Options.SkipDWARF, // emit DWARF except when -internal-nodwarf is passed
|
||||
}
|
||||
@@ -807,21 +808,8 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
|
||||
// Run wasm-opt for wasm binaries
|
||||
if arch := strings.Split(config.Triple(), "-")[0]; arch == "wasm32" {
|
||||
var opt string
|
||||
switch config.Options.Opt {
|
||||
case "none", "0":
|
||||
opt = "-O0"
|
||||
case "1":
|
||||
opt = "-O1"
|
||||
case "2":
|
||||
opt = "-O2"
|
||||
case "s":
|
||||
opt = "-Os"
|
||||
case "z":
|
||||
opt = "-Oz"
|
||||
default:
|
||||
return fmt.Errorf("unknown opt level: %q", config.Options.Opt)
|
||||
}
|
||||
optLevel, _, _ := config.OptLevel()
|
||||
opt := "-" + optLevel
|
||||
|
||||
var args []string
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ func TestClangAttributes(t *testing.T) {
|
||||
"riscv-qemu",
|
||||
"wasi",
|
||||
"wasm",
|
||||
"wasm-unknown",
|
||||
}
|
||||
if hasBuiltinTools {
|
||||
// hasBuiltinTools is set when TinyGo is statically linked with LLVM,
|
||||
|
||||
+17
-6
@@ -1,5 +1,12 @@
|
||||
//go:build byollvm
|
||||
|
||||
// Source: https://github.com/llvm/llvm-project/blob/main/clang/tools/driver/cc1as_main.cpp
|
||||
// This file needs to be updated each LLVM release.
|
||||
// There are a few small modifications to make, like:
|
||||
// * ExecuteAssembler is made non-static.
|
||||
// * The struct AssemblerImplementation is moved to cc1as.h so it can be
|
||||
// included elsewhere.
|
||||
|
||||
//===-- cc1as.cpp - Clang Assembler --------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
@@ -21,8 +28,8 @@
|
||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||
#include "clang/Frontend/Utils.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/MC/MCAsmBackend.h"
|
||||
#include "llvm/MC/MCAsmInfo.h"
|
||||
@@ -46,7 +53,6 @@
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/FormattedStream.h"
|
||||
#include "llvm/Support/Host.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/Process.h"
|
||||
@@ -55,6 +61,8 @@
|
||||
#include "llvm/Support/TargetSelect.h"
|
||||
#include "llvm/Support/Timer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/TargetParser/Host.h"
|
||||
#include "llvm/TargetParser/Triple.h"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <system_error>
|
||||
@@ -151,8 +159,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
|
||||
for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ)) {
|
||||
auto Split = StringRef(Arg).split('=');
|
||||
Opts.DebugPrefixMap.insert(
|
||||
{std::string(Split.first), std::string(Split.second)});
|
||||
Opts.DebugPrefixMap.emplace_back(Split.first, Split.second);
|
||||
}
|
||||
|
||||
// Frontend Options
|
||||
@@ -225,6 +232,9 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
.Case("default", EmitDwarfUnwindType::Default);
|
||||
}
|
||||
|
||||
Opts.EmitCompactUnwindNonCanonical =
|
||||
Args.hasArg(OPT_femit_compact_unwind_non_canonical);
|
||||
|
||||
Opts.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file);
|
||||
|
||||
return Success;
|
||||
@@ -260,8 +270,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true);
|
||||
|
||||
if (std::error_code EC = Buffer.getError()) {
|
||||
Error = EC.message();
|
||||
return Diags.Report(diag::err_fe_error_reading) << Opts.InputFile;
|
||||
return Diags.Report(diag::err_fe_error_reading)
|
||||
<< Opts.InputFile << EC.message();
|
||||
}
|
||||
|
||||
SourceMgr SrcMgr;
|
||||
@@ -278,6 +288,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
|
||||
MCTargetOptions MCOptions;
|
||||
MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
|
||||
MCOptions.EmitCompactUnwindNonCanonical = Opts.EmitCompactUnwindNonCanonical;
|
||||
MCOptions.AsSecureLogFile = Opts.AsSecureLogFile;
|
||||
|
||||
std::unique_ptr<MCAsmInfo> MAI(
|
||||
|
||||
+9
-1
@@ -1,3 +1,6 @@
|
||||
// Source: https://github.com/llvm/llvm-project/blob/main/clang/tools/driver/cc1as_main.cpp
|
||||
// See cc1as.cpp for details.
|
||||
|
||||
//===-- cc1as.h - Clang Assembler ----------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
@@ -44,7 +47,7 @@ struct AssemblerInvocation {
|
||||
std::string DwarfDebugFlags;
|
||||
std::string DwarfDebugProducer;
|
||||
std::string DebugCompilationDir;
|
||||
std::map<const std::string, const std::string> DebugPrefixMap;
|
||||
llvm::SmallVector<std::pair<std::string, std::string>, 0> DebugPrefixMap;
|
||||
llvm::DebugCompressionType CompressDebugSections =
|
||||
llvm::DebugCompressionType::None;
|
||||
std::string MainFileName;
|
||||
@@ -89,6 +92,10 @@ struct AssemblerInvocation {
|
||||
/// Whether to emit DWARF unwind info.
|
||||
EmitDwarfUnwindType EmitDwarfUnwind;
|
||||
|
||||
// Whether to emit compact-unwind for non-canonical entries.
|
||||
// Note: maybe overriden by other constraints.
|
||||
unsigned EmitCompactUnwindNonCanonical : 1;
|
||||
|
||||
/// The name of the relocation model to use.
|
||||
std::string RelocationModel;
|
||||
|
||||
@@ -128,6 +135,7 @@ public:
|
||||
DwarfVersion = 0;
|
||||
EmbedBitcode = 0;
|
||||
EmitDwarfUnwind = EmitDwarfUnwindType::Default;
|
||||
EmitCompactUnwindNonCanonical = false;
|
||||
}
|
||||
|
||||
static bool CreateFromArgs(AssemblerInvocation &Res,
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
#include <clang/FrontendTool/Utils.h>
|
||||
#include <llvm/ADT/IntrusiveRefCntPtr.h>
|
||||
#include <llvm/Option/Option.h>
|
||||
#include <llvm/Support/Host.h>
|
||||
#include <llvm/TargetParser/Host.h>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace clang;
|
||||
|
||||
+2
-2
@@ -27,10 +27,10 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if major != 1 || minor < 18 || minor > 21 {
|
||||
if major != 1 || minor < 18 || minor > 22 {
|
||||
// Note: when this gets updated, also update the Go compatibility matrix:
|
||||
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
|
||||
return nil, fmt.Errorf("requires go version 1.18 through 1.21, got go%d.%d", major, minor)
|
||||
return nil, fmt.Errorf("requires go version 1.18 through 1.22, got go%d.%d", major, minor)
|
||||
}
|
||||
|
||||
return &compileopts.Config{
|
||||
|
||||
+9
-20
@@ -5,7 +5,11 @@
|
||||
#include <lld/Common/Driver.h>
|
||||
#include <llvm/Support/Parallel.h>
|
||||
|
||||
extern "C" {
|
||||
LLD_HAS_DRIVER(coff)
|
||||
LLD_HAS_DRIVER(elf)
|
||||
LLD_HAS_DRIVER(mingw)
|
||||
LLD_HAS_DRIVER(macho)
|
||||
LLD_HAS_DRIVER(wasm)
|
||||
|
||||
static void configure() {
|
||||
#if _WIN64
|
||||
@@ -16,28 +20,13 @@ static void configure() {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool tinygo_link_elf(int argc, char **argv) {
|
||||
configure();
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
return lld::elf::link(args, llvm::outs(), llvm::errs(), false, false);
|
||||
}
|
||||
extern "C" {
|
||||
|
||||
bool tinygo_link_macho(int argc, char **argv) {
|
||||
bool tinygo_link(int argc, char **argv) {
|
||||
configure();
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
return lld::macho::link(args, llvm::outs(), llvm::errs(), false, false);
|
||||
}
|
||||
|
||||
bool tinygo_link_mingw(int argc, char **argv) {
|
||||
configure();
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
return lld::mingw::link(args, llvm::outs(), llvm::errs(), false, false);
|
||||
}
|
||||
|
||||
bool tinygo_link_wasm(int argc, char **argv) {
|
||||
configure();
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
return lld::wasm::link(args, llvm::outs(), llvm::errs(), false, false);
|
||||
lld::Result r = lld::lldMain(args, llvm::outs(), llvm::errs(), LLD_ALL_DRIVERS);
|
||||
return !r.retCode;
|
||||
}
|
||||
|
||||
} // external "C"
|
||||
|
||||
@@ -119,6 +119,7 @@ var Musl = Library{
|
||||
"internal/syscall_ret.c",
|
||||
"internal/vdso.c",
|
||||
"legacy/*.c",
|
||||
"linux/*.c",
|
||||
"malloc/*.c",
|
||||
"malloc/mallocng/*.c",
|
||||
"mman/*.c",
|
||||
|
||||
@@ -337,6 +337,12 @@ var picolibcSourcesLarge = []string{
|
||||
"libm/common/math_err_may_uflow.c",
|
||||
"libm/common/math_err_check_uflow.c",
|
||||
"libm/common/math_err_check_oflow.c",
|
||||
"libm/common/math_errf_divzerof.c",
|
||||
"libm/common/math_errf_invalidf.c",
|
||||
"libm/common/math_errf_may_uflowf.c",
|
||||
"libm/common/math_errf_oflowf.c",
|
||||
"libm/common/math_errf_uflowf.c",
|
||||
"libm/common/math_errf_with_errnof.c",
|
||||
"libm/common/math_inexact.c",
|
||||
"libm/common/math_inexactf.c",
|
||||
"libm/common/log.c",
|
||||
|
||||
@@ -41,9 +41,9 @@ func TestBinarySize(t *testing.T) {
|
||||
// This is a small number of very diverse targets that we want to test.
|
||||
tests := []sizeTest{
|
||||
// microcontrollers
|
||||
{"hifive1b", "examples/echo", 4484, 280, 0, 2252},
|
||||
{"hifive1b", "examples/echo", 4476, 280, 0, 2252},
|
||||
{"microbit", "examples/serial", 2724, 388, 8, 2256},
|
||||
{"wioterminal", "examples/pininterrupt", 6000, 1484, 116, 6816},
|
||||
{"wioterminal", "examples/pininterrupt", 5996, 1484, 116, 6816},
|
||||
|
||||
// TODO: also check wasm. Right now this is difficult, because
|
||||
// wasm binaries are run through wasm-opt and therefore the
|
||||
|
||||
@@ -12,10 +12,7 @@ import (
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
bool tinygo_clang_driver(int argc, char **argv);
|
||||
bool tinygo_link_elf(int argc, char **argv);
|
||||
bool tinygo_link_macho(int argc, char **argv);
|
||||
bool tinygo_link_mingw(int argc, char **argv);
|
||||
bool tinygo_link_wasm(int argc, char **argv);
|
||||
bool tinygo_link(int argc, char **argv);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -26,16 +23,7 @@ const hasBuiltinTools = true
|
||||
// This version actually runs the tools because TinyGo was compiled while
|
||||
// linking statically with LLVM (with the byollvm build tag).
|
||||
func RunTool(tool string, args ...string) error {
|
||||
linker := "elf"
|
||||
if tool == "ld.lld" && len(args) >= 2 {
|
||||
if args[0] == "-m" && (args[1] == "i386pep" || args[1] == "arm64pe") {
|
||||
linker = "mingw"
|
||||
} else if args[0] == "-flavor" {
|
||||
linker = args[1]
|
||||
args = args[2:]
|
||||
}
|
||||
}
|
||||
args = append([]string{"tinygo:" + tool}, args...)
|
||||
args = append([]string{tool}, args...)
|
||||
|
||||
var cflag *C.char
|
||||
buf := C.calloc(C.size_t(len(args)), C.size_t(unsafe.Sizeof(cflag)))
|
||||
@@ -51,19 +39,8 @@ func RunTool(tool string, args ...string) error {
|
||||
switch tool {
|
||||
case "clang":
|
||||
ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf))
|
||||
case "ld.lld":
|
||||
switch linker {
|
||||
case "darwin":
|
||||
ok = C.tinygo_link_macho(C.int(len(args)), (**C.char)(buf))
|
||||
case "elf":
|
||||
ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf))
|
||||
case "mingw":
|
||||
ok = C.tinygo_link_mingw(C.int(len(args)), (**C.char)(buf))
|
||||
default:
|
||||
return errors.New("unknown linker: " + linker)
|
||||
}
|
||||
case "wasm-ld":
|
||||
ok = C.tinygo_link_wasm(C.int(len(args)), (**C.char)(buf))
|
||||
case "ld.lld", "wasm-ld":
|
||||
ok = C.tinygo_link(C.int(len(args)), (**C.char)(buf))
|
||||
default:
|
||||
return errors.New("unknown tool: " + tool)
|
||||
}
|
||||
|
||||
+10
-2
@@ -25,9 +25,15 @@ import (
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
// Function that's only defined in Go 1.22.
|
||||
var setASTFileFields = func(f *ast.File, start, end token.Pos) {
|
||||
}
|
||||
|
||||
// cgoPackage holds all CGo-related information of a package.
|
||||
type cgoPackage struct {
|
||||
generated *ast.File
|
||||
packageName string
|
||||
cgoFiles []*ast.File
|
||||
generatedPos token.Pos
|
||||
errors []error
|
||||
currentDir string // current working directory
|
||||
@@ -165,8 +171,9 @@ func GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
// functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file
|
||||
// hashes of the accessed C header files. If there is one or more error, it
|
||||
// returns these in the []error slice but still modifies the AST.
|
||||
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string) (*ast.File, []string, []string, []string, map[string][]byte, []error) {
|
||||
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string) ([]*ast.File, []string, []string, []string, map[string][]byte, []error) {
|
||||
p := &cgoPackage{
|
||||
packageName: files[0].Name.Name,
|
||||
currentDir: dir,
|
||||
importPath: importPath,
|
||||
fset: fset,
|
||||
@@ -202,6 +209,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
||||
// This is always a bug in the cgo package.
|
||||
panic("unexpected error: " + err.Error())
|
||||
}
|
||||
p.cgoFiles = append(p.cgoFiles, p.generated)
|
||||
// If the Comments field is not set to nil, the go/format package will get
|
||||
// confused about where comments should go.
|
||||
p.generated.Comments = nil
|
||||
@@ -332,7 +340,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
||||
// Print the newly generated in-memory AST, for debugging.
|
||||
//ast.Print(fset, p.generated)
|
||||
|
||||
return p.generated, p.cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors
|
||||
return p.cgoFiles, p.cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors
|
||||
}
|
||||
|
||||
func (p *cgoPackage) newCGoFile(file *ast.File, index int) *cgoFile {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
//go:build go1.22
|
||||
|
||||
package cgo
|
||||
|
||||
// Code specifically for Go 1.22.
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
func init() {
|
||||
setASTFileFields = func(f *ast.File, start, end token.Pos) {
|
||||
f.FileStart = start
|
||||
f.FileEnd = end
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -55,7 +55,7 @@ func TestCGo(t *testing.T) {
|
||||
}
|
||||
|
||||
// Process the AST with CGo.
|
||||
cgoAST, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags)
|
||||
cgoFiles, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags)
|
||||
|
||||
// Check the AST for type errors.
|
||||
var typecheckErrors []error
|
||||
@@ -66,7 +66,7 @@ func TestCGo(t *testing.T) {
|
||||
Importer: simpleImporter{},
|
||||
Sizes: types.SizesFor("gccgo", "arm"),
|
||||
}
|
||||
_, err = config.Check("", fset, []*ast.File{f, cgoAST}, nil)
|
||||
_, err = config.Check("", fset, append([]*ast.File{f}, cgoFiles...), nil)
|
||||
if err != nil && len(typecheckErrors) == 0 {
|
||||
// Only report errors when no type errors are found (an
|
||||
// unexpected condition).
|
||||
@@ -91,7 +91,7 @@ func TestCGo(t *testing.T) {
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
err = format.Node(buf, fset, cgoAST)
|
||||
err = format.Node(buf, fset, cgoFiles[0])
|
||||
if err != nil {
|
||||
t.Errorf("could not write out CGo AST: %v", err)
|
||||
}
|
||||
|
||||
@@ -589,6 +589,13 @@ func (p *cgoPackage) getClangLocationPosition(location C.CXSourceLocation, tu C.
|
||||
f := p.fset.AddFile(filename, -1, int(size))
|
||||
f.SetLines(lines)
|
||||
p.tokenFiles[filename] = f
|
||||
// Add dummy file AST, to satisfy the type checker.
|
||||
astFile := &ast.File{
|
||||
Package: f.Pos(0),
|
||||
Name: ast.NewIdent(p.packageName),
|
||||
}
|
||||
setASTFileFields(astFile, f.Pos(0), f.Pos(int(size)))
|
||||
p.cgoFiles = append(p.cgoFiles, astFile)
|
||||
}
|
||||
positionFile := p.tokenFiles[filename]
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !byollvm && !llvm15 && !llvm17
|
||||
//go:build !byollvm && llvm16
|
||||
|
||||
package cgo
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !byollvm && llvm17
|
||||
//go:build !byollvm && !llvm15 && !llvm16
|
||||
|
||||
package cgo
|
||||
|
||||
|
||||
+15
-11
@@ -74,7 +74,12 @@ func (c *Config) GOARM() string {
|
||||
|
||||
// BuildTags returns the complete list of build tags used during this build.
|
||||
func (c *Config) BuildTags() []string {
|
||||
tags := append(c.Target.BuildTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
|
||||
tags := append(c.Target.BuildTags, []string{
|
||||
"tinygo", // that's the compiler
|
||||
"purego", // to get various crypto packages to work
|
||||
"math_big_pure_go", // to get math/big to work
|
||||
"gc." + c.GC(), "scheduler." + c.Scheduler(), // used inside the runtime package
|
||||
"serial." + c.Serial()}...) // used inside the machine package
|
||||
for i := 1; i <= c.GoMinorVersion; i++ {
|
||||
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
||||
}
|
||||
@@ -82,12 +87,6 @@ func (c *Config) BuildTags() []string {
|
||||
return tags
|
||||
}
|
||||
|
||||
// CgoEnabled returns true if (and only if) CGo is enabled. It is true by
|
||||
// default and false if CGO_ENABLED is set to "0".
|
||||
func (c *Config) CgoEnabled() bool {
|
||||
return goenv.Get("CGO_ENABLED") == "1"
|
||||
}
|
||||
|
||||
// GC returns the garbage collection strategy in use on this platform. Valid
|
||||
// values are "none", "leaking", "conservative" and "precise".
|
||||
func (c *Config) GC() string {
|
||||
@@ -189,6 +188,15 @@ func (c *Config) StackSize() uint64 {
|
||||
return c.Target.DefaultStackSize
|
||||
}
|
||||
|
||||
// MaxStackAlloc returns the size of the maximum allocation to put on the stack vs heap.
|
||||
func (c *Config) MaxStackAlloc() uint64 {
|
||||
if c.StackSize() > 32*1024 {
|
||||
return 1024
|
||||
}
|
||||
|
||||
return 256
|
||||
}
|
||||
|
||||
// RP2040BootPatch returns whether the RP2040 boot patch should be applied that
|
||||
// calculates and patches in the checksum for the 2nd stage bootloader.
|
||||
func (c *Config) RP2040BootPatch() bool {
|
||||
@@ -272,10 +280,6 @@ func (c *Config) CFlags(libclang bool) []string {
|
||||
cflags = append(cflags,
|
||||
"-resource-dir="+resourceDir,
|
||||
)
|
||||
if strings.HasPrefix(c.Triple(), "xtensa") {
|
||||
// workaround needed in LLVM 16, see: https://github.com/espressif/llvm-project/issues/83
|
||||
cflags = append(cflags, "-isystem", filepath.Join(resourceDir, "include"))
|
||||
}
|
||||
}
|
||||
switch c.Target.Libc {
|
||||
case "darwin-libSystem":
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
var (
|
||||
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
|
||||
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
|
||||
validSerialOptions = []string{"none", "uart", "usb"}
|
||||
validSerialOptions = []string{"none", "uart", "usb", "rtt"}
|
||||
validPrintSizeOptions = []string{"none", "short", "full"}
|
||||
validPanicStrategyOptions = []string{"print", "trap"}
|
||||
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
|
||||
@@ -23,6 +23,7 @@ type Options struct {
|
||||
GOOS string // environment variable
|
||||
GOARCH string // environment variable
|
||||
GOARM string // environment variable (only used with GOARCH=arm)
|
||||
Directory string // working dir, leave it unset to use the current working dir
|
||||
Target string
|
||||
Opt string
|
||||
GC string
|
||||
@@ -48,7 +49,6 @@ type Options struct {
|
||||
Programmer string
|
||||
OpenOCDCommands []string
|
||||
LLVMFeatures string
|
||||
Directory string
|
||||
PrintJSON bool
|
||||
Monitor bool
|
||||
BaudRate int
|
||||
|
||||
+19
-3
@@ -301,10 +301,10 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
switch goarch {
|
||||
case "386":
|
||||
spec.CPU = "pentium4"
|
||||
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||
spec.Features = "+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||
case "amd64":
|
||||
spec.CPU = "x86-64"
|
||||
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||
spec.Features = "+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||
case "arm":
|
||||
spec.CPU = "generic"
|
||||
spec.CFlags = append(spec.CFlags, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
||||
@@ -320,8 +320,10 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
spec.CPU = "generic"
|
||||
if goos == "darwin" {
|
||||
spec.Features = "+neon"
|
||||
} else { // windows, linux
|
||||
} else if goos == "windows" {
|
||||
spec.Features = "+neon,-fmv"
|
||||
} else { // linux
|
||||
spec.Features = "+neon,-fmv,-outline-atomics"
|
||||
}
|
||||
case "wasm":
|
||||
spec.CPU = "generic"
|
||||
@@ -349,6 +351,20 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
spec.RTLib = "compiler-rt"
|
||||
spec.Libc = "musl"
|
||||
spec.LDFlags = append(spec.LDFlags, "--gc-sections")
|
||||
if goarch == "arm64" {
|
||||
// Disable outline atomics. For details, see:
|
||||
// https://cpufun.substack.com/p/atomics-in-aarch64
|
||||
// A better way would be to fully support outline atomics, which
|
||||
// makes atomics slightly more efficient on systems with many cores.
|
||||
// But the instructions are only supported on newer aarch64 CPUs, so
|
||||
// this feature is normally put in a system library which does
|
||||
// feature detection for you.
|
||||
// We take the lazy way out and simply disable this feature, instead
|
||||
// of enabling it in compiler-rt (which is a bit more complicated).
|
||||
// We don't really need this feature anyway as we don't even support
|
||||
// proper threading.
|
||||
spec.CFlags = append(spec.CFlags, "-mno-outline-atomics")
|
||||
}
|
||||
} else if goos == "windows" {
|
||||
spec.Linker = "ld.lld"
|
||||
spec.Libc = "mingw-w64"
|
||||
|
||||
@@ -53,6 +53,7 @@ type Config struct {
|
||||
Scheduler string
|
||||
AutomaticStackSize bool
|
||||
DefaultStackSize uint64
|
||||
MaxStackAlloc uint64
|
||||
NeedsStackObjects bool
|
||||
Debug bool // Whether to emit debug information in the LLVM module.
|
||||
}
|
||||
@@ -1997,8 +1998,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
case *ssa.Alloc:
|
||||
typ := b.getLLVMType(expr.Type().Underlying().(*types.Pointer).Elem())
|
||||
size := b.targetData.TypeAllocSize(typ)
|
||||
// Move all "large" allocations to the heap. This value is also transform.maxStackAlloc.
|
||||
if expr.Heap || size > 256 {
|
||||
// Move all "large" allocations to the heap.
|
||||
if expr.Heap || size > b.MaxStackAlloc {
|
||||
// Calculate ^uintptr(0)
|
||||
maxSize := llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)).ZExtValue()
|
||||
if size > maxSize {
|
||||
@@ -3261,7 +3262,11 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
|
||||
// Instead of a load from the global, create a bitcast of the
|
||||
// function pointer itself.
|
||||
name := strings.TrimSuffix(unop.X.(*ssa.Global).Name(), "$funcaddr")
|
||||
_, fn := b.getFunction(b.fn.Pkg.Members[name].(*ssa.Function))
|
||||
pkg := b.fn.Pkg
|
||||
if pkg == nil {
|
||||
pkg = b.fn.Origin().Pkg
|
||||
}
|
||||
_, fn := b.getFunction(pkg.Members[name].(*ssa.Function))
|
||||
if fn.IsNil() {
|
||||
return llvm.Value{}, b.makeError(unop.Pos(), "cgo function not found: "+name)
|
||||
}
|
||||
|
||||
+18
-12
@@ -684,19 +684,25 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
|
||||
actualTypeNum := b.CreateExtractValue(itf, 0, "interface.type")
|
||||
commaOk := llvm.Value{}
|
||||
if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
|
||||
// Type assert on interface type.
|
||||
// This is a call to an interface type assert function.
|
||||
// The interface lowering pass will define this function by filling it
|
||||
// with a type switch over all concrete types that implement this
|
||||
// interface, and returning whether it's one of the matched types.
|
||||
// This is very different from how interface asserts are implemented in
|
||||
// the main Go compiler, where the runtime checks whether the type
|
||||
// implements each method of the interface. See:
|
||||
// https://research.swtch.com/interfaces
|
||||
fn := b.getInterfaceImplementsFunc(expr.AssertedType)
|
||||
commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "")
|
||||
|
||||
if intf, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
|
||||
if intf.Empty() {
|
||||
// intf is the empty interface => no methods
|
||||
// This type assertion always succeeds, so we can just set commaOk to true.
|
||||
commaOk = llvm.ConstInt(b.ctx.Int1Type(), 1, true)
|
||||
} else {
|
||||
// Type assert on interface type with methods.
|
||||
// This is a call to an interface type assert function.
|
||||
// The interface lowering pass will define this function by filling it
|
||||
// with a type switch over all concrete types that implement this
|
||||
// interface, and returning whether it's one of the matched types.
|
||||
// This is very different from how interface asserts are implemented in
|
||||
// the main Go compiler, where the runtime checks whether the type
|
||||
// implements each method of the interface. See:
|
||||
// https://research.swtch.com/interfaces
|
||||
fn := b.getInterfaceImplementsFunc(expr.AssertedType)
|
||||
commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "")
|
||||
}
|
||||
} else {
|
||||
name, _ := getTypeCodeName(expr.AssertedType)
|
||||
globalName := "reflect/types.typeid:" + name
|
||||
|
||||
Generated
+4
-4
@@ -20,16 +20,16 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1696983906,
|
||||
"narHash": "sha256-L7GyeErguS7Pg4h8nK0wGlcUTbfUMDu+HMf1UcyP72k=",
|
||||
"lastModified": 1703068421,
|
||||
"narHash": "sha256-WSw5Faqlw75McIflnl5v7qVD/B3S2sLh+968bpOGrWA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "bd1cde45c77891214131cbbea5b1203e485a9d51",
|
||||
"rev": "d65bceaee0fb1e64363f7871bc43dc1c6ecad99f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-23.05",
|
||||
"ref": "nixos-23.11",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
inputs = {
|
||||
# Use a recent stable release, but fix the version to make it reproducible.
|
||||
# This version should be updated from time to time.
|
||||
nixpkgs.url = "nixpkgs/nixos-23.05";
|
||||
nixpkgs.url = "nixpkgs/nixos-23.11";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
@@ -49,11 +49,12 @@
|
||||
buildInputs = [
|
||||
# These dependencies are required for building tinygo (go install).
|
||||
go
|
||||
llvmPackages_16.llvm
|
||||
llvmPackages_16.libclang
|
||||
llvmPackages_17.llvm
|
||||
llvmPackages_17.libclang
|
||||
llvmPackages_17.libcxx
|
||||
# Additional dependencies needed at runtime, for building and/or
|
||||
# flashing.
|
||||
llvmPackages_16.lld
|
||||
llvmPackages_17.lld
|
||||
avrdude
|
||||
binaryen
|
||||
# Additional dependencies needed for on-chip debugging.
|
||||
@@ -68,7 +69,7 @@
|
||||
# Without setting these explicitly, Homebrew versions might be used
|
||||
# or the default `ar` and `nm` tools might be used (which don't
|
||||
# support wasi).
|
||||
export CLANG="clang-16 -resource-dir ${llvmPackages_16.clang.cc.lib}/lib/clang/16"
|
||||
export CLANG="clang-17 -resource-dir ${llvmPackages_17.clang.cc.lib}/lib/clang/17"
|
||||
export LLVM_AR=llvm-ar
|
||||
export LLVM_NM=llvm-nm
|
||||
|
||||
@@ -77,7 +78,7 @@
|
||||
export MD5SUM=md5sum
|
||||
|
||||
# Ugly hack to make the Clang resources directory available.
|
||||
export GOFLAGS="\"-ldflags=-X github.com/tinygo-org/tinygo/goenv.clangResourceDir=${llvmPackages_16.clang.cc.lib}/lib/clang/16"\"
|
||||
export GOFLAGS="\"-ldflags=-X github.com/tinygo-org/tinygo/goenv.clangResourceDir=${llvmPackages_17.clang.cc.lib}/lib/clang/17"\"
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,10 +15,11 @@ require (
|
||||
github.com/mattn/go-tty v0.0.4
|
||||
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3
|
||||
go.bug.st/serial v1.6.0
|
||||
golang.org/x/sys v0.11.0
|
||||
golang.org/x/tools v0.12.0
|
||||
golang.org/x/net v0.20.0
|
||||
golang.org/x/sys v0.16.0
|
||||
golang.org/x/tools v0.17.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
tinygo.org/x/go-llvm v0.0.0-20231014233752-75a8a9fe6f74
|
||||
tinygo.org/x/go-llvm v0.0.0-20240106122909-c2c543540318
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -30,4 +31,5 @@ require (
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
)
|
||||
|
||||
@@ -48,7 +48,9 @@ github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
go.bug.st/serial v1.6.0 h1:mAbRGN4cKE2J5gMwsMHC2KQisdLRQssO9WSM+rbZJ8A=
|
||||
go.bug.st/serial v1.6.0/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -56,14 +58,16 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
|
||||
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
tinygo.org/x/go-llvm v0.0.0-20231014233752-75a8a9fe6f74 h1:tW8XhLI9gUZLL+2pG0HYb5dc6bpMj1aqtESpizXPnMY=
|
||||
tinygo.org/x/go-llvm v0.0.0-20231014233752-75a8a9fe6f74/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||
tinygo.org/x/go-llvm v0.0.0-20240106122909-c2c543540318 h1:4KjZvPtcN1UwobevcGbdzeinx0L1i8HDdJu84bu7NI8=
|
||||
tinygo.org/x/go-llvm v0.0.0-20240106122909-c2c543540318/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||
|
||||
+2
-5
@@ -142,11 +142,8 @@ func Get(name string) string {
|
||||
}
|
||||
return filepath.Join(dir, "tinygo")
|
||||
case "CGO_ENABLED":
|
||||
val := os.Getenv("CGO_ENABLED")
|
||||
if val == "1" || val == "0" {
|
||||
return val
|
||||
}
|
||||
// Default to enabling CGo.
|
||||
// Always enable CGo. It is required by a number of targets, including
|
||||
// macOS and the rp2040.
|
||||
return "1"
|
||||
case "TINYGOROOT":
|
||||
return sourceDir()
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
// Version of TinyGo.
|
||||
// Update this value before release of new version of software.
|
||||
const version = "0.31.0-dev"
|
||||
const version = "0.31.2"
|
||||
|
||||
var (
|
||||
// This variable is set at build time using -ldflags parameters.
|
||||
|
||||
+27
-10
@@ -239,7 +239,8 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
// already be emitted in initAll.
|
||||
continue
|
||||
case strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet" || callFn.name == "runtime.hashmapInterfaceHash" ||
|
||||
callFn.name == "os.runtime_args" || callFn.name == "internal/task.start" || callFn.name == "internal/task.Current":
|
||||
callFn.name == "os.runtime_args" || callFn.name == "internal/task.start" || callFn.name == "internal/task.Current" ||
|
||||
callFn.name == "time.startTimer" || callFn.name == "time.stopTimer" || callFn.name == "time.resetTimer":
|
||||
// These functions should be run at runtime. Specifically:
|
||||
// * Print and panic functions are best emitted directly without
|
||||
// interpreting them, otherwise we get a ton of putchar (etc.)
|
||||
@@ -252,6 +253,8 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
// at runtime.
|
||||
// * internal/task.start, internal/task.Current: start and read shcheduler state,
|
||||
// which is modified elsewhere.
|
||||
// * Timer functions access runtime internal state which may
|
||||
// not be initialized.
|
||||
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||
if err != nil {
|
||||
return nil, mem, err
|
||||
@@ -424,7 +427,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
methodSetPtr, err := mem.load(typecodePtr.addOffset(-int64(r.pointerSize)), r.pointerSize).asPointer(r)
|
||||
typecodePtrOffset, err := typecodePtr.addOffset(-int64(r.pointerSize))
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err) // unlikely
|
||||
}
|
||||
methodSetPtr, err := mem.load(typecodePtrOffset, r.pointerSize).asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
@@ -470,7 +477,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
methodSetPtr, err := mem.load(typecodePtr.addOffset(-int64(r.pointerSize)), r.pointerSize).asPointer(r)
|
||||
typecodePtrOffset, err := typecodePtr.addOffset(-int64(r.pointerSize))
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
methodSetPtr, err := mem.load(typecodePtrOffset, r.pointerSize).asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
@@ -633,11 +644,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
case llvm.GetElementPtr:
|
||||
// GetElementPtr does pointer arithmetic, changing the offset of the
|
||||
// pointer into the underlying object.
|
||||
var offset uint64
|
||||
var offset int64
|
||||
for i := 1; i < len(operands); i += 2 {
|
||||
index := operands[i].Uint()
|
||||
elementSize := operands[i+1].Uint()
|
||||
if int64(elementSize) < 0 {
|
||||
index := operands[i].Int()
|
||||
elementSize := operands[i+1].Int()
|
||||
if elementSize < 0 {
|
||||
// This is a struct field.
|
||||
offset += index
|
||||
} else {
|
||||
@@ -651,11 +662,14 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
// GEP on fixed pointer value (for example, memory-mapped I/O).
|
||||
ptrValue := operands[0].Uint() + offset
|
||||
ptrValue := operands[0].Uint() + uint64(offset)
|
||||
locals[inst.localIndex] = makeLiteralInt(ptrValue, int(operands[0].len(r)*8))
|
||||
continue
|
||||
}
|
||||
ptr = ptr.addOffset(int64(offset))
|
||||
ptr, err = ptr.addOffset(int64(offset))
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
locals[inst.localIndex] = ptr
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"gep:", operands, "->", ptr)
|
||||
@@ -753,7 +767,10 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
if inst.opcode == llvm.Add {
|
||||
// This likely means this is part of a
|
||||
// unsafe.Pointer(uintptr(ptr) + offset) pattern.
|
||||
lhsPtr = lhsPtr.addOffset(int64(rhs.Uint()))
|
||||
lhsPtr, err = lhsPtr.addOffset(int64(rhs.Uint()))
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
locals[inst.localIndex] = lhsPtr
|
||||
} else if inst.opcode == llvm.Xor && rhs.Uint() == 0 {
|
||||
// Special workaround for strings.noescape, see
|
||||
|
||||
+3
-3
@@ -517,12 +517,12 @@ func (v pointerValue) offset() uint32 {
|
||||
// addOffset essentially does a GEP operation (pointer arithmetic): it adds the
|
||||
// offset to the pointer. It also checks that the offset doesn't overflow the
|
||||
// maximum offset size (which is 4GB).
|
||||
func (v pointerValue) addOffset(offset int64) pointerValue {
|
||||
func (v pointerValue) addOffset(offset int64) (pointerValue, error) {
|
||||
result := pointerValue{v.pointer + uint64(offset)}
|
||||
if checks && v.index() != result.index() {
|
||||
panic("interp: offset out of range")
|
||||
return result, fmt.Errorf("interp: offset %d out of range for object %v", offset, v)
|
||||
}
|
||||
return result
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (v pointerValue) len(r *runner) uint32 {
|
||||
|
||||
Vendored
+7
@@ -10,6 +10,8 @@ target triple = "x86_64--linux"
|
||||
@main.exposedValue1 = global i16 0
|
||||
@main.exposedValue2 = global i16 0
|
||||
@main.insertedValue = global {i8, i32, {float, {i64, i16}}} zeroinitializer
|
||||
@main.gepArray = global [8 x i8] zeroinitializer
|
||||
@main.negativeGEP = global ptr null
|
||||
|
||||
declare void @runtime.printint64(i64) unnamed_addr
|
||||
|
||||
@@ -88,6 +90,11 @@ entry:
|
||||
%agg2 = insertvalue {i8, i32, {float, {i64, i16}}} %agg, i64 5, 2, 1, 0
|
||||
store {i8, i32, {float, {i64, i16}}} %agg2, ptr @main.insertedValue
|
||||
|
||||
; negative GEP instruction
|
||||
%ngep1 = getelementptr [8 x i8], ptr @main.negativeGEP, i32 0, i32 5
|
||||
%ngep2 = getelementptr [8 x i8], ptr %ngep1, i32 0, i32 -3
|
||||
store ptr %ngep2, ptr @main.negativeGEP
|
||||
|
||||
ret void
|
||||
}
|
||||
|
||||
|
||||
Vendored
+2
@@ -9,6 +9,8 @@ target triple = "x86_64--linux"
|
||||
@main.exposedValue1 = global i16 0
|
||||
@main.exposedValue2 = local_unnamed_addr global i16 0
|
||||
@main.insertedValue = local_unnamed_addr global { i8, i32, { float, { i64, i16 } } } zeroinitializer
|
||||
@main.gepArray = local_unnamed_addr global [8 x i8] zeroinitializer
|
||||
@main.negativeGEP = global ptr getelementptr inbounds (i8, ptr @main.negativeGEP, i64 2)
|
||||
|
||||
declare void @runtime.printint64(i64) unnamed_addr
|
||||
|
||||
|
||||
+1
-1
Submodule lib/cmsis-svd updated: df75ff974c...40327a4d2d
+1
-1
Submodule lib/wasi-libc updated: 30094b6ed0...1dfe5c302d
@@ -232,6 +232,7 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
|
||||
"": true,
|
||||
"crypto/": true,
|
||||
"crypto/rand/": false,
|
||||
"crypto/tls/": false,
|
||||
"device/": false,
|
||||
"examples/": false,
|
||||
"internal/": true,
|
||||
@@ -241,7 +242,9 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
|
||||
"internal/task/": false,
|
||||
"machine/": false,
|
||||
"net/": true,
|
||||
"net/http/": false,
|
||||
"os/": true,
|
||||
"os/user/": false,
|
||||
"reflect/": false,
|
||||
"runtime/": false,
|
||||
"sync/": true,
|
||||
|
||||
+1
-5
@@ -22,12 +22,8 @@ func List(config *compileopts.Config, extraArgs, pkgs []string) (*exec.Cmd, erro
|
||||
args = append(args, "-tags", strings.Join(config.BuildTags(), " "))
|
||||
}
|
||||
args = append(args, pkgs...)
|
||||
cgoEnabled := "0"
|
||||
if config.CgoEnabled() {
|
||||
cgoEnabled = "1"
|
||||
}
|
||||
cmd := exec.Command(filepath.Join(goenv.Get("GOROOT"), "bin", "go"), args...)
|
||||
cmd.Env = append(os.Environ(), "GOROOT="+goroot, "GOOS="+config.GOOS(), "GOARCH="+config.GOARCH(), "CGO_ENABLED="+cgoEnabled)
|
||||
cmd.Env = append(os.Environ(), "GOROOT="+goroot, "GOOS="+config.GOOS(), "GOARCH="+config.GOARCH(), "CGO_ENABLED=1")
|
||||
if config.Options.Directory != "" {
|
||||
cmd.Dir = config.Options.Directory
|
||||
}
|
||||
|
||||
+21
-1
@@ -27,6 +27,8 @@ import (
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
var initFileVersions = func(info *types.Info) {}
|
||||
|
||||
// Program holds all packages and some metadata about the program as a whole.
|
||||
type Program struct {
|
||||
config *compileopts.Config
|
||||
@@ -378,6 +380,24 @@ func (p *Package) Check() error {
|
||||
typeErrors = append(typeErrors, err)
|
||||
}
|
||||
checker.Importer = p
|
||||
if p.Module.GoVersion != "" {
|
||||
// Setting the Go version for a module makes sure the type checker
|
||||
// errors out on language features not supported in that particular
|
||||
// version.
|
||||
checker.GoVersion = "go" + p.Module.GoVersion
|
||||
} else {
|
||||
// Version is not known, so use the currently installed Go version.
|
||||
// This is needed for `tinygo run` for example.
|
||||
// Normally we'd use goenv.GorootVersionString(), but for compatibility
|
||||
// with Go 1.20 and below we need a version in the form of "go1.12" (no
|
||||
// patch version).
|
||||
major, minor, err := goenv.GetGorootVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
checker.GoVersion = fmt.Sprintf("go%d.%d", major, minor)
|
||||
}
|
||||
initFileVersions(&p.info)
|
||||
|
||||
// Do typechecking of the package.
|
||||
packageName := p.ImportPath
|
||||
@@ -447,7 +467,7 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
||||
if errs != nil {
|
||||
fileErrs = append(fileErrs, errs...)
|
||||
}
|
||||
files = append(files, generated)
|
||||
files = append(files, generated...)
|
||||
p.program.LDFlags = append(p.program.LDFlags, ldflags...)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
//go:build go1.22
|
||||
|
||||
// types.Info.FileVersions was added in Go 1.22, so we can only initialize it
|
||||
// when built with Go 1.22.
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initFileVersions = func(info *types.Info) {
|
||||
info.FileVersions = make(map[*ast.File]string)
|
||||
}
|
||||
}
|
||||
@@ -532,7 +532,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
return fmt.Errorf("unknown flash method: %s", flashMethod)
|
||||
}
|
||||
if options.Monitor {
|
||||
return Monitor(result.Executable, "", options)
|
||||
return Monitor(result.Executable, "", config)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1225,6 +1225,7 @@ func usage(command string) {
|
||||
fmt.Fprintln(os.Stderr, " gdb: run/flash and immediately enter GDB")
|
||||
fmt.Fprintln(os.Stderr, " lldb: run/flash and immediately enter LLDB")
|
||||
fmt.Fprintln(os.Stderr, " monitor: open communication port")
|
||||
fmt.Fprintln(os.Stderr, " ports: list available serial ports")
|
||||
fmt.Fprintln(os.Stderr, " env: list environment variables used during build")
|
||||
fmt.Fprintln(os.Stderr, " list: run go list using the TinyGo root")
|
||||
fmt.Fprintln(os.Stderr, " clean: empty cache directory ("+goenv.Get("GOCACHE")+")")
|
||||
@@ -1437,7 +1438,6 @@ func main() {
|
||||
llvmFeatures := flag.String("llvm-features", "", "comma separated LLVM features to enable")
|
||||
cpuprofile := flag.String("cpuprofile", "", "cpuprofile output")
|
||||
monitor := flag.Bool("monitor", false, "enable serial monitor")
|
||||
info := flag.Bool("info", false, "print information")
|
||||
baudrate := flag.Int("baudrate", 115200, "baudrate of serial monitor")
|
||||
|
||||
// Internal flags, that are only intended for TinyGo development.
|
||||
@@ -1733,15 +1733,19 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
case "monitor":
|
||||
if *info {
|
||||
serialPortInfo, err := ListSerialPorts()
|
||||
handleCompilerError(err)
|
||||
for _, s := range serialPortInfo {
|
||||
fmt.Printf("%s %4s %4s %s\n", s.Name, s.VID, s.PID, s.Target)
|
||||
}
|
||||
} else {
|
||||
err := Monitor("", *port, options)
|
||||
handleCompilerError(err)
|
||||
config, err := builder.NewConfig(options)
|
||||
handleCompilerError(err)
|
||||
err = Monitor("", *port, config)
|
||||
handleCompilerError(err)
|
||||
case "ports":
|
||||
serialPortInfo, err := ListSerialPorts()
|
||||
handleCompilerError(err)
|
||||
if len(serialPortInfo) == 0 {
|
||||
fmt.Println("No serial ports found.")
|
||||
}
|
||||
fmt.Printf("%-20s %-9s %s\n", "Port", "ID", "Boards")
|
||||
for _, s := range serialPortInfo {
|
||||
fmt.Printf("%-20s %4s:%4s %s\n", s.Name, s.VID, s.PID, s.Target)
|
||||
}
|
||||
case "targets":
|
||||
specs, err := compileopts.GetTargetSpecs()
|
||||
|
||||
+8
-1
@@ -69,6 +69,7 @@ func TestBuild(t *testing.T) {
|
||||
"json.go",
|
||||
"map.go",
|
||||
"math.go",
|
||||
"oldgo/",
|
||||
"print.go",
|
||||
"reflect.go",
|
||||
"slice.go",
|
||||
@@ -90,6 +91,9 @@ func TestBuild(t *testing.T) {
|
||||
if minor >= 21 {
|
||||
tests = append(tests, "go1.21.go")
|
||||
}
|
||||
if minor >= 22 {
|
||||
tests = append(tests, "go1.22/")
|
||||
}
|
||||
|
||||
if *testTarget != "" {
|
||||
// This makes it possible to run one specific test (instead of all),
|
||||
@@ -333,8 +337,11 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
||||
path := TESTDATA + "/" + name
|
||||
// Get the expected output for this test.
|
||||
txtpath := path[:len(path)-3] + ".txt"
|
||||
pkgName := "./" + path
|
||||
if path[len(path)-1] == '/' {
|
||||
txtpath = path + "out.txt"
|
||||
options.Directory = path
|
||||
pkgName = "."
|
||||
}
|
||||
expected, err := os.ReadFile(txtpath)
|
||||
if err != nil {
|
||||
@@ -348,7 +355,7 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
||||
|
||||
// Build the test binary.
|
||||
stdout := &bytes.Buffer{}
|
||||
_, err = buildAndRun("./"+path, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
|
||||
_, err = buildAndRun(pkgName, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
|
||||
return cmd.Run()
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
+143
-32
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"debug/dwarf"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"regexp"
|
||||
@@ -17,7 +19,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/mattn/go-tty"
|
||||
"github.com/tinygo-org/tinygo/builder"
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
|
||||
"go.bug.st/serial"
|
||||
@@ -25,44 +26,151 @@ import (
|
||||
)
|
||||
|
||||
// Monitor connects to the given port and reads/writes the serial port.
|
||||
func Monitor(executable, port string, options *compileopts.Options) error {
|
||||
config, err := builder.NewConfig(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func Monitor(executable, port string, config *compileopts.Config) error {
|
||||
const timeout = time.Second * 3
|
||||
var exit func() // function to be called before exiting
|
||||
var serialConn io.ReadWriter
|
||||
|
||||
wait := 300
|
||||
for i := 0; i <= wait; i++ {
|
||||
port, err = getDefaultPort(port, config.Target.SerialPort)
|
||||
if config.Options.Serial == "rtt" {
|
||||
// Use the RTT interface, which is documented (in part) here:
|
||||
// https://wiki.segger.com/RTT
|
||||
|
||||
// Try to find the "machine.rttSerialInstance" symbol, which is the RTT
|
||||
// control block.
|
||||
file, err := elf.Open(executable)
|
||||
if err != nil {
|
||||
if i < wait {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
continue
|
||||
return fmt.Errorf("could not open ELF file to determine RTT control block: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
symbols, err := file.Symbols()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read ELF symbol table to determine RTT control block: %w", err)
|
||||
}
|
||||
var address uint64
|
||||
for _, symbol := range symbols {
|
||||
if symbol.Name == "machine.rttSerialInstance" {
|
||||
address = symbol.Value
|
||||
break
|
||||
}
|
||||
}
|
||||
if address == 0 {
|
||||
return fmt.Errorf("could not find RTT control block in ELF file")
|
||||
}
|
||||
|
||||
// Start an openocd process in the background.
|
||||
args, err := config.OpenOCDConfiguration()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
br := options.BaudRate
|
||||
if br <= 0 {
|
||||
br = 115200
|
||||
}
|
||||
|
||||
wait = 300
|
||||
var p serial.Port
|
||||
for i := 0; i <= wait; i++ {
|
||||
p, err = serial.Open(port, &serial.Mode{BaudRate: br})
|
||||
args = append(args,
|
||||
"-c", fmt.Sprintf("rtt setup 0x%x 16 \"SEGGER RTT\"", address),
|
||||
"-c", "init",
|
||||
"-c", "rtt server start 0 0")
|
||||
cmd := executeCommand(config.Options, "openocd", args...)
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
if i < wait {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
break
|
||||
cmd.Stdout = os.Stdout
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cmd.Process.Kill()
|
||||
exit = func() {
|
||||
// Make sure the openocd process is terminated at exit.
|
||||
// This does not happen through the defer above when exiting through
|
||||
// os.Exit.
|
||||
cmd.Process.Kill()
|
||||
}
|
||||
|
||||
// Read the stderr, which logs various important messages we need.
|
||||
r := bufio.NewReader(stderr)
|
||||
var telnet net.Conn
|
||||
var timeoutAt time.Time
|
||||
for {
|
||||
// Read the next line from the openocd process.
|
||||
lineBytes, err := r.ReadBytes('\n')
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
line := string(lineBytes)
|
||||
|
||||
if line == "Info : rtt: No control block found\n" {
|
||||
// Message that is sent back when OpenOCD can't find the control
|
||||
// block after a 'rtt start' message.
|
||||
if time.Now().After(timeoutAt) {
|
||||
return fmt.Errorf("RTT timeout (could not locate RTT control block at 0x%08x)", address)
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
telnet.Write([]byte("rtt start\r\n"))
|
||||
} else if strings.HasPrefix(line, "Info : Listening on port") {
|
||||
// We need two different ports for controlling OpenOCD
|
||||
// (typically port 4444) and the RTT channel 0 socket (arbitrary
|
||||
// port).
|
||||
var port int
|
||||
var protocol string
|
||||
fmt.Sscanf(line, "Info : Listening on port %d for %s connections\n", &port, &protocol)
|
||||
if protocol == "telnet" && telnet == nil {
|
||||
// Connect to the "telnet" command line interface.
|
||||
telnet, err = net.Dial("tcp4", fmt.Sprintf("localhost:%d", port))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Tell OpenOCD to start scanning for the RTT control block.
|
||||
telnet.Write([]byte("rtt start\r\n"))
|
||||
// Also make sure we will time out if the control block just
|
||||
// can't be found.
|
||||
timeoutAt = time.Now().Add(timeout)
|
||||
} else if protocol == "rtt" {
|
||||
// Connect to the RTT channel, for both stdin and stdout.
|
||||
conn, err := net.Dial("tcp4", fmt.Sprintf("localhost:%d", port))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serialConn = conn
|
||||
}
|
||||
} else if strings.HasPrefix(line, "Info : rtt: Control block found at") {
|
||||
// Connection established!
|
||||
break
|
||||
}
|
||||
}
|
||||
} else { // -serial=uart or -serial=usb
|
||||
var err error
|
||||
wait := 300
|
||||
for i := 0; i <= wait; i++ {
|
||||
port, err = getDefaultPort(port, config.Target.SerialPort)
|
||||
if err != nil {
|
||||
if i < wait {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
br := config.Options.BaudRate
|
||||
if br <= 0 {
|
||||
br = 115200
|
||||
}
|
||||
|
||||
wait = 300
|
||||
var p serial.Port
|
||||
for i := 0; i <= wait; i++ {
|
||||
p, err = serial.Open(port, &serial.Mode{BaudRate: br})
|
||||
if err != nil {
|
||||
if i < wait {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
serialConn = p
|
||||
break
|
||||
}
|
||||
defer p.Close()
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
tty, err := tty.Open()
|
||||
if err != nil {
|
||||
@@ -77,6 +185,9 @@ func Monitor(executable, port string, options *compileopts.Options) error {
|
||||
go func() {
|
||||
<-sig
|
||||
tty.Close()
|
||||
if exit != nil {
|
||||
exit()
|
||||
}
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
@@ -88,7 +199,7 @@ func Monitor(executable, port string, options *compileopts.Options) error {
|
||||
buf := make([]byte, 100*1024)
|
||||
var line []byte
|
||||
for {
|
||||
n, err := p.Read(buf)
|
||||
n, err := serialConn.Read(buf)
|
||||
if err != nil {
|
||||
errCh <- fmt.Errorf("read error: %w", err)
|
||||
return
|
||||
@@ -124,7 +235,7 @@ func Monitor(executable, port string, options *compileopts.Options) error {
|
||||
if r == 0 {
|
||||
continue
|
||||
}
|
||||
p.Write([]byte(string(r)))
|
||||
serialConn.Write([]byte(string(r)))
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
//go:build nrf || stm32 || (sam && atsamd51) || (sam && atsame5x)
|
||||
//go:build nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3
|
||||
|
||||
// If you update the above build constraint, you'll probably also need to update
|
||||
// src/runtime/rand_hwrng.go.
|
||||
|
||||
package rand
|
||||
|
||||
|
||||
@@ -0,0 +1,447 @@
|
||||
// TINYGO: The following is copied and modified from Go 1.19.3 official implementation.
|
||||
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CurveID is the type of a TLS identifier for an elliptic curve. See
|
||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8.
|
||||
//
|
||||
// In TLS 1.3, this type is called NamedGroup, but at this time this library
|
||||
// only supports Elliptic Curve based groups. See RFC 8446, Section 4.2.7.
|
||||
type CurveID uint16
|
||||
|
||||
// ConnectionState records basic TLS details about the connection.
|
||||
type ConnectionState struct {
|
||||
// TINYGO: empty; TLS connection offloaded to device
|
||||
}
|
||||
|
||||
// ClientAuthType declares the policy the server will follow for
|
||||
// TLS Client Authentication.
|
||||
type ClientAuthType int
|
||||
|
||||
// ClientSessionCache is a cache of ClientSessionState objects that can be used
|
||||
// by a client to resume a TLS session with a given server. ClientSessionCache
|
||||
// implementations should expect to be called concurrently from different
|
||||
// goroutines. Up to TLS 1.2, only ticket-based resumption is supported, not
|
||||
// SessionID-based resumption. In TLS 1.3 they were merged into PSK modes, which
|
||||
// are supported via this interface.
|
||||
type ClientSessionCache interface {
|
||||
// Get searches for a ClientSessionState associated with the given key.
|
||||
// On return, ok is true if one was found.
|
||||
Get(sessionKey string) (session *ClientSessionState, ok bool)
|
||||
|
||||
// Put adds the ClientSessionState to the cache with the given key. It might
|
||||
// get called multiple times in a connection if a TLS 1.3 server provides
|
||||
// more than one session ticket. If called with a nil *ClientSessionState,
|
||||
// it should remove the cache entry.
|
||||
Put(sessionKey string, cs *ClientSessionState)
|
||||
}
|
||||
|
||||
//go:generate stringer -type=SignatureScheme,CurveID,ClientAuthType -output=common_string.go
|
||||
|
||||
// SignatureScheme identifies a signature algorithm supported by TLS. See
|
||||
// RFC 8446, Section 4.2.3.
|
||||
type SignatureScheme uint16
|
||||
|
||||
// ClientHelloInfo contains information from a ClientHello message in order to
|
||||
// guide application logic in the GetCertificate and GetConfigForClient callbacks.
|
||||
type ClientHelloInfo struct {
|
||||
// CipherSuites lists the CipherSuites supported by the client (e.g.
|
||||
// TLS_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256).
|
||||
CipherSuites []uint16
|
||||
|
||||
// ServerName indicates the name of the server requested by the client
|
||||
// in order to support virtual hosting. ServerName is only set if the
|
||||
// client is using SNI (see RFC 4366, Section 3.1).
|
||||
ServerName string
|
||||
|
||||
// SupportedCurves lists the elliptic curves supported by the client.
|
||||
// SupportedCurves is set only if the Supported Elliptic Curves
|
||||
// Extension is being used (see RFC 4492, Section 5.1.1).
|
||||
SupportedCurves []CurveID
|
||||
|
||||
// SupportedPoints lists the point formats supported by the client.
|
||||
// SupportedPoints is set only if the Supported Point Formats Extension
|
||||
// is being used (see RFC 4492, Section 5.1.2).
|
||||
SupportedPoints []uint8
|
||||
|
||||
// SignatureSchemes lists the signature and hash schemes that the client
|
||||
// is willing to verify. SignatureSchemes is set only if the Signature
|
||||
// Algorithms Extension is being used (see RFC 5246, Section 7.4.1.4.1).
|
||||
SignatureSchemes []SignatureScheme
|
||||
|
||||
// SupportedProtos lists the application protocols supported by the client.
|
||||
// SupportedProtos is set only if the Application-Layer Protocol
|
||||
// Negotiation Extension is being used (see RFC 7301, Section 3.1).
|
||||
//
|
||||
// Servers can select a protocol by setting Config.NextProtos in a
|
||||
// GetConfigForClient return value.
|
||||
SupportedProtos []string
|
||||
|
||||
// SupportedVersions lists the TLS versions supported by the client.
|
||||
// For TLS versions less than 1.3, this is extrapolated from the max
|
||||
// version advertised by the client, so values other than the greatest
|
||||
// might be rejected if used.
|
||||
SupportedVersions []uint16
|
||||
|
||||
// Conn is the underlying net.Conn for the connection. Do not read
|
||||
// from, or write to, this connection; that will cause the TLS
|
||||
// connection to fail.
|
||||
Conn net.Conn
|
||||
|
||||
// config is embedded by the GetCertificate or GetConfigForClient caller,
|
||||
// for use with SupportsCertificate.
|
||||
config *Config
|
||||
|
||||
// ctx is the context of the handshake that is in progress.
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// CertificateRequestInfo contains information from a server's
|
||||
// CertificateRequest message, which is used to demand a certificate and proof
|
||||
// of control from a client.
|
||||
type CertificateRequestInfo struct {
|
||||
// AcceptableCAs contains zero or more, DER-encoded, X.501
|
||||
// Distinguished Names. These are the names of root or intermediate CAs
|
||||
// that the server wishes the returned certificate to be signed by. An
|
||||
// empty slice indicates that the server has no preference.
|
||||
AcceptableCAs [][]byte
|
||||
|
||||
// SignatureSchemes lists the signature schemes that the server is
|
||||
// willing to verify.
|
||||
SignatureSchemes []SignatureScheme
|
||||
|
||||
// Version is the TLS version that was negotiated for this connection.
|
||||
Version uint16
|
||||
|
||||
// ctx is the context of the handshake that is in progress.
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// RenegotiationSupport enumerates the different levels of support for TLS
|
||||
// renegotiation. TLS renegotiation is the act of performing subsequent
|
||||
// handshakes on a connection after the first. This significantly complicates
|
||||
// the state machine and has been the source of numerous, subtle security
|
||||
// issues. Initiating a renegotiation is not supported, but support for
|
||||
// accepting renegotiation requests may be enabled.
|
||||
//
|
||||
// Even when enabled, the server may not change its identity between handshakes
|
||||
// (i.e. the leaf certificate must be the same). Additionally, concurrent
|
||||
// handshake and application data flow is not permitted so renegotiation can
|
||||
// only be used with protocols that synchronise with the renegotiation, such as
|
||||
// HTTPS.
|
||||
//
|
||||
// Renegotiation is not defined in TLS 1.3.
|
||||
type RenegotiationSupport int
|
||||
|
||||
// A Config structure is used to configure a TLS client or server.
|
||||
// After one has been passed to a TLS function it must not be
|
||||
// modified. A Config may be reused; the tls package will also not
|
||||
// modify it.
|
||||
type Config struct {
|
||||
// Rand provides the source of entropy for nonces and RSA blinding.
|
||||
// If Rand is nil, TLS uses the cryptographic random reader in package
|
||||
// crypto/rand.
|
||||
// The Reader must be safe for use by multiple goroutines.
|
||||
Rand io.Reader
|
||||
|
||||
// Time returns the current time as the number of seconds since the epoch.
|
||||
// If Time is nil, TLS uses time.Now.
|
||||
Time func() time.Time
|
||||
|
||||
// Certificates contains one or more certificate chains to present to the
|
||||
// other side of the connection. The first certificate compatible with the
|
||||
// peer's requirements is selected automatically.
|
||||
//
|
||||
// Server configurations must set one of Certificates, GetCertificate or
|
||||
// GetConfigForClient. Clients doing client-authentication may set either
|
||||
// Certificates or GetClientCertificate.
|
||||
//
|
||||
// Note: if there are multiple Certificates, and they don't have the
|
||||
// optional field Leaf set, certificate selection will incur a significant
|
||||
// per-handshake performance cost.
|
||||
Certificates []Certificate
|
||||
|
||||
// NameToCertificate maps from a certificate name to an element of
|
||||
// Certificates. Note that a certificate name can be of the form
|
||||
// '*.example.com' and so doesn't have to be a domain name as such.
|
||||
//
|
||||
// Deprecated: NameToCertificate only allows associating a single
|
||||
// certificate with a given name. Leave this field nil to let the library
|
||||
// select the first compatible chain from Certificates.
|
||||
NameToCertificate map[string]*Certificate
|
||||
|
||||
// GetCertificate returns a Certificate based on the given
|
||||
// ClientHelloInfo. It will only be called if the client supplies SNI
|
||||
// information or if Certificates is empty.
|
||||
//
|
||||
// If GetCertificate is nil or returns nil, then the certificate is
|
||||
// retrieved from NameToCertificate. If NameToCertificate is nil, the
|
||||
// best element of Certificates will be used.
|
||||
//
|
||||
// Once a Certificate is returned it should not be modified.
|
||||
GetCertificate func(*ClientHelloInfo) (*Certificate, error)
|
||||
|
||||
// GetClientCertificate, if not nil, is called when a server requests a
|
||||
// certificate from a client. If set, the contents of Certificates will
|
||||
// be ignored.
|
||||
//
|
||||
// If GetClientCertificate returns an error, the handshake will be
|
||||
// aborted and that error will be returned. Otherwise
|
||||
// GetClientCertificate must return a non-nil Certificate. If
|
||||
// Certificate.Certificate is empty then no certificate will be sent to
|
||||
// the server. If this is unacceptable to the server then it may abort
|
||||
// the handshake.
|
||||
//
|
||||
// GetClientCertificate may be called multiple times for the same
|
||||
// connection if renegotiation occurs or if TLS 1.3 is in use.
|
||||
//
|
||||
// Once a Certificate is returned it should not be modified.
|
||||
GetClientCertificate func(*CertificateRequestInfo) (*Certificate, error)
|
||||
|
||||
// GetConfigForClient, if not nil, is called after a ClientHello is
|
||||
// received from a client. It may return a non-nil Config in order to
|
||||
// change the Config that will be used to handle this connection. If
|
||||
// the returned Config is nil, the original Config will be used. The
|
||||
// Config returned by this callback may not be subsequently modified.
|
||||
//
|
||||
// If GetConfigForClient is nil, the Config passed to Server() will be
|
||||
// used for all connections.
|
||||
//
|
||||
// If SessionTicketKey was explicitly set on the returned Config, or if
|
||||
// SetSessionTicketKeys was called on the returned Config, those keys will
|
||||
// be used. Otherwise, the original Config keys will be used (and possibly
|
||||
// rotated if they are automatically managed).
|
||||
GetConfigForClient func(*ClientHelloInfo) (*Config, error)
|
||||
|
||||
// VerifyPeerCertificate, if not nil, is called after normal
|
||||
// certificate verification by either a TLS client or server. It
|
||||
// receives the raw ASN.1 certificates provided by the peer and also
|
||||
// any verified chains that normal processing found. If it returns a
|
||||
// non-nil error, the handshake is aborted and that error results.
|
||||
//
|
||||
// If normal verification fails then the handshake will abort before
|
||||
// considering this callback. If normal verification is disabled (on the
|
||||
// client when InsecureSkipVerify is set, or on a server when ClientAuth is
|
||||
// RequestClientCert or RequireAnyClientCert), then this callback will be
|
||||
// considered but the verifiedChains argument will always be nil. When
|
||||
// ClientAuth is NoClientCert, this callback is not called on the server.
|
||||
// rawCerts may be empty on the server if ClientAuth is RequestClientCert or
|
||||
// VerifyClientCertIfGiven.
|
||||
//
|
||||
// This callback is not invoked on resumed connections, as certificates are
|
||||
// not re-verified on resumption.
|
||||
//
|
||||
// verifiedChains and its contents should not be modified.
|
||||
VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
|
||||
|
||||
// VerifyConnection, if not nil, is called after normal certificate
|
||||
// verification and after VerifyPeerCertificate by either a TLS client
|
||||
// or server. If it returns a non-nil error, the handshake is aborted
|
||||
// and that error results.
|
||||
//
|
||||
// If normal verification fails then the handshake will abort before
|
||||
// considering this callback. This callback will run for all connections,
|
||||
// including resumptions, regardless of InsecureSkipVerify or ClientAuth
|
||||
// settings.
|
||||
VerifyConnection func(ConnectionState) error
|
||||
|
||||
// RootCAs defines the set of root certificate authorities
|
||||
// that clients use when verifying server certificates.
|
||||
// If RootCAs is nil, TLS uses the host's root CA set.
|
||||
RootCAs *x509.CertPool
|
||||
|
||||
// NextProtos is a list of supported application level protocols, in
|
||||
// order of preference. If both peers support ALPN, the selected
|
||||
// protocol will be one from this list, and the connection will fail
|
||||
// if there is no mutually supported protocol. If NextProtos is empty
|
||||
// or the peer doesn't support ALPN, the connection will succeed and
|
||||
// ConnectionState.NegotiatedProtocol will be empty.
|
||||
NextProtos []string
|
||||
|
||||
// ServerName is used to verify the hostname on the returned
|
||||
// certificates unless InsecureSkipVerify is given. It is also included
|
||||
// in the client's handshake to support virtual hosting unless it is
|
||||
// an IP address.
|
||||
ServerName string
|
||||
|
||||
// ClientAuth determines the server's policy for
|
||||
// TLS Client Authentication. The default is NoClientCert.
|
||||
ClientAuth ClientAuthType
|
||||
|
||||
// ClientCAs defines the set of root certificate authorities
|
||||
// that servers use if required to verify a client certificate
|
||||
// by the policy in ClientAuth.
|
||||
ClientCAs *x509.CertPool
|
||||
|
||||
// InsecureSkipVerify controls whether a client verifies the server's
|
||||
// certificate chain and host name. If InsecureSkipVerify is true, crypto/tls
|
||||
// accepts any certificate presented by the server and any host name in that
|
||||
// certificate. In this mode, TLS is susceptible to machine-in-the-middle
|
||||
// attacks unless custom verification is used. This should be used only for
|
||||
// testing or in combination with VerifyConnection or VerifyPeerCertificate.
|
||||
InsecureSkipVerify bool
|
||||
|
||||
// CipherSuites is a list of enabled TLS 1.0–1.2 cipher suites. The order of
|
||||
// the list is ignored. Note that TLS 1.3 ciphersuites are not configurable.
|
||||
//
|
||||
// If CipherSuites is nil, a safe default list is used. The default cipher
|
||||
// suites might change over time.
|
||||
CipherSuites []uint16
|
||||
|
||||
// PreferServerCipherSuites is a legacy field and has no effect.
|
||||
//
|
||||
// It used to control whether the server would follow the client's or the
|
||||
// server's preference. Servers now select the best mutually supported
|
||||
// cipher suite based on logic that takes into account inferred client
|
||||
// hardware, server hardware, and security.
|
||||
//
|
||||
// Deprecated: PreferServerCipherSuites is ignored.
|
||||
PreferServerCipherSuites bool
|
||||
|
||||
// SessionTicketsDisabled may be set to true to disable session ticket and
|
||||
// PSK (resumption) support. Note that on clients, session ticket support is
|
||||
// also disabled if ClientSessionCache is nil.
|
||||
SessionTicketsDisabled bool
|
||||
|
||||
// SessionTicketKey is used by TLS servers to provide session resumption.
|
||||
// See RFC 5077 and the PSK mode of RFC 8446. If zero, it will be filled
|
||||
// with random data before the first server handshake.
|
||||
//
|
||||
// Deprecated: if this field is left at zero, session ticket keys will be
|
||||
// automatically rotated every day and dropped after seven days. For
|
||||
// customizing the rotation schedule or synchronizing servers that are
|
||||
// terminating connections for the same host, use SetSessionTicketKeys.
|
||||
SessionTicketKey [32]byte
|
||||
|
||||
// ClientSessionCache is a cache of ClientSessionState entries for TLS
|
||||
// session resumption. It is only used by clients.
|
||||
ClientSessionCache ClientSessionCache
|
||||
|
||||
// UnwrapSession is called on the server to turn a ticket/identity
|
||||
// previously produced by [WrapSession] into a usable session.
|
||||
//
|
||||
// UnwrapSession will usually either decrypt a session state in the ticket
|
||||
// (for example with [Config.EncryptTicket]), or use the ticket as a handle
|
||||
// to recover a previously stored state. It must use [ParseSessionState] to
|
||||
// deserialize the session state.
|
||||
//
|
||||
// If UnwrapSession returns an error, the connection is terminated. If it
|
||||
// returns (nil, nil), the session is ignored. crypto/tls may still choose
|
||||
// not to resume the returned session.
|
||||
UnwrapSession func(identity []byte, cs ConnectionState) (*SessionState, error)
|
||||
|
||||
// WrapSession is called on the server to produce a session ticket/identity.
|
||||
//
|
||||
// WrapSession must serialize the session state with [SessionState.Bytes].
|
||||
// It may then encrypt the serialized state (for example with
|
||||
// [Config.DecryptTicket]) and use it as the ticket, or store the state and
|
||||
// return a handle for it.
|
||||
//
|
||||
// If WrapSession returns an error, the connection is terminated.
|
||||
//
|
||||
// Warning: the return value will be exposed on the wire and to clients in
|
||||
// plaintext. The application is in charge of encrypting and authenticating
|
||||
// it (and rotating keys) or returning high-entropy identifiers. Failing to
|
||||
// do so correctly can compromise current, previous, and future connections
|
||||
// depending on the protocol version.
|
||||
WrapSession func(ConnectionState, *SessionState) ([]byte, error)
|
||||
|
||||
// MinVersion contains the minimum TLS version that is acceptable.
|
||||
//
|
||||
// By default, TLS 1.2 is currently used as the minimum when acting as a
|
||||
// client, and TLS 1.0 when acting as a server. TLS 1.0 is the minimum
|
||||
// supported by this package, both as a client and as a server.
|
||||
//
|
||||
// The client-side default can temporarily be reverted to TLS 1.0 by
|
||||
// including the value "x509sha1=1" in the GODEBUG environment variable.
|
||||
// Note that this option will be removed in Go 1.19 (but it will still be
|
||||
// possible to set this field to VersionTLS10 explicitly).
|
||||
MinVersion uint16
|
||||
|
||||
// MaxVersion contains the maximum TLS version that is acceptable.
|
||||
//
|
||||
// By default, the maximum version supported by this package is used,
|
||||
// which is currently TLS 1.3.
|
||||
MaxVersion uint16
|
||||
|
||||
// CurvePreferences contains the elliptic curves that will be used in
|
||||
// an ECDHE handshake, in preference order. If empty, the default will
|
||||
// be used. The client will use the first preference as the type for
|
||||
// its key share in TLS 1.3. This may change in the future.
|
||||
CurvePreferences []CurveID
|
||||
|
||||
// DynamicRecordSizingDisabled disables adaptive sizing of TLS records.
|
||||
// When true, the largest possible TLS record size is always used. When
|
||||
// false, the size of TLS records may be adjusted in an attempt to
|
||||
// improve latency.
|
||||
DynamicRecordSizingDisabled bool
|
||||
|
||||
// Renegotiation controls what types of renegotiation are supported.
|
||||
// The default, none, is correct for the vast majority of applications.
|
||||
Renegotiation RenegotiationSupport
|
||||
|
||||
// KeyLogWriter optionally specifies a destination for TLS master secrets
|
||||
// in NSS key log format that can be used to allow external programs
|
||||
// such as Wireshark to decrypt TLS connections.
|
||||
// See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format.
|
||||
// Use of KeyLogWriter compromises security and should only be
|
||||
// used for debugging.
|
||||
KeyLogWriter io.Writer
|
||||
|
||||
// mutex protects sessionTicketKeys and autoSessionTicketKeys.
|
||||
mutex sync.RWMutex
|
||||
// sessionTicketKeys contains zero or more ticket keys. If set, it means
|
||||
// the keys were set with SessionTicketKey or SetSessionTicketKeys. The
|
||||
// first key is used for new tickets and any subsequent keys can be used to
|
||||
// decrypt old tickets. The slice contents are not protected by the mutex
|
||||
// and are immutable.
|
||||
sessionTicketKeys []ticketKey
|
||||
// autoSessionTicketKeys is like sessionTicketKeys but is owned by the
|
||||
// auto-rotation logic. See Config.ticketKeys.
|
||||
autoSessionTicketKeys []ticketKey
|
||||
}
|
||||
|
||||
// ticketKey is the internal representation of a session ticket key.
|
||||
type ticketKey struct {
|
||||
aesKey [16]byte
|
||||
hmacKey [16]byte
|
||||
// created is the time at which this ticket key was created. See Config.ticketKeys.
|
||||
created time.Time
|
||||
}
|
||||
|
||||
// A Certificate is a chain of one or more certificates, leaf first.
|
||||
type Certificate struct {
|
||||
Certificate [][]byte
|
||||
// PrivateKey contains the private key corresponding to the public key in
|
||||
// Leaf. This must implement crypto.Signer with an RSA, ECDSA or Ed25519 PublicKey.
|
||||
// For a server up to TLS 1.2, it can also implement crypto.Decrypter with
|
||||
// an RSA PublicKey.
|
||||
PrivateKey crypto.PrivateKey
|
||||
// SupportedSignatureAlgorithms is an optional list restricting what
|
||||
// signature algorithms the PrivateKey can be used for.
|
||||
SupportedSignatureAlgorithms []SignatureScheme
|
||||
// OCSPStaple contains an optional OCSP response which will be served
|
||||
// to clients that request it.
|
||||
OCSPStaple []byte
|
||||
// SignedCertificateTimestamps contains an optional list of Signed
|
||||
// Certificate Timestamps which will be served to clients that request it.
|
||||
SignedCertificateTimestamps [][]byte
|
||||
// Leaf is the parsed form of the leaf certificate, which may be initialized
|
||||
// using x509.ParseCertificate to reduce per-handshake processing. If nil,
|
||||
// the leaf certificate will be parsed as needed.
|
||||
Leaf *x509.Certificate
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
// A SessionState is a resumable session.
|
||||
type SessionState struct {
|
||||
}
|
||||
|
||||
// ClientSessionState contains the state needed by a client to
|
||||
// resume a previous TLS session.
|
||||
type ClientSessionState struct {
|
||||
ticket []byte
|
||||
session *SessionState
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// TINYGO: The following is copied and modified from Go 1.21.4 official implementation.
|
||||
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package tls partially implements TLS 1.2, as specified in RFC 5246,
|
||||
// and TLS 1.3, as specified in RFC 8446.
|
||||
package tls
|
||||
|
||||
// BUG(agl): The crypto/tls package only implements some countermeasures
|
||||
// against Lucky13 attacks on CBC-mode encryption, and only on SHA1
|
||||
// variants. See http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and
|
||||
// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Client returns a new TLS client side connection
|
||||
// using conn as the underlying transport.
|
||||
// The config cannot be nil: users must set either ServerName or
|
||||
// InsecureSkipVerify in the config.
|
||||
func Client(conn net.Conn, config *Config) *net.TLSConn {
|
||||
panic("tls.Client() not implemented")
|
||||
return nil
|
||||
}
|
||||
|
||||
// A listener implements a network listener (net.Listener) for TLS connections.
|
||||
type listener struct {
|
||||
net.Listener
|
||||
config *Config
|
||||
}
|
||||
|
||||
// NewListener creates a Listener which accepts connections from an inner
|
||||
// Listener and wraps each connection with Server.
|
||||
// The configuration config must be non-nil and must include
|
||||
// at least one certificate or else set GetCertificate.
|
||||
func NewListener(inner net.Listener, config *Config) net.Listener {
|
||||
l := new(listener)
|
||||
l.Listener = inner
|
||||
l.config = config
|
||||
return l
|
||||
}
|
||||
|
||||
// DialWithDialer connects to the given network address using dialer.Dial and
|
||||
// then initiates a TLS handshake, returning the resulting TLS connection. Any
|
||||
// timeout or deadline given in the dialer apply to connection and TLS
|
||||
// handshake as a whole.
|
||||
//
|
||||
// DialWithDialer interprets a nil configuration as equivalent to the zero
|
||||
// configuration; see the documentation of Config for the defaults.
|
||||
//
|
||||
// DialWithDialer uses context.Background internally; to specify the context,
|
||||
// use Dialer.DialContext with NetDialer set to the desired dialer.
|
||||
func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*net.TLSConn, error) {
|
||||
switch network {
|
||||
case "tcp", "tcp4":
|
||||
default:
|
||||
return nil, fmt.Errorf("Network %s not supported", network)
|
||||
}
|
||||
|
||||
return net.DialTLS(addr)
|
||||
}
|
||||
|
||||
// Dial connects to the given network address using net.Dial
|
||||
// and then initiates a TLS handshake, returning the resulting
|
||||
// TLS connection.
|
||||
// Dial interprets a nil configuration as equivalent to
|
||||
// the zero configuration; see the documentation of Config
|
||||
// for the defaults.
|
||||
func Dial(network, addr string, config *Config) (*net.TLSConn, error) {
|
||||
return DialWithDialer(new(net.Dialer), network, addr, config)
|
||||
}
|
||||
|
||||
// Dialer dials TLS connections given a configuration and a Dialer for the
|
||||
// underlying connection.
|
||||
type Dialer struct {
|
||||
// NetDialer is the optional dialer to use for the TLS connections'
|
||||
// underlying TCP connections.
|
||||
// A nil NetDialer is equivalent to the net.Dialer zero value.
|
||||
NetDialer *net.Dialer
|
||||
|
||||
// Config is the TLS configuration to use for new connections.
|
||||
// A nil configuration is equivalent to the zero
|
||||
// configuration; see the documentation of Config for the
|
||||
// defaults.
|
||||
Config *Config
|
||||
}
|
||||
|
||||
// DialContext connects to the given network address and initiates a TLS
|
||||
// handshake, returning the resulting TLS connection.
|
||||
//
|
||||
// The provided Context must be non-nil. If the context expires before
|
||||
// the connection is complete, an error is returned. Once successfully
|
||||
// connected, any expiration of the context will not affect the
|
||||
// connection.
|
||||
//
|
||||
// The returned Conn, if any, will always be of type *Conn.
|
||||
func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return nil, errors.New("tls:DialContext not implmented")
|
||||
}
|
||||
|
||||
// LoadX509KeyPair reads and parses a public/private key pair from a pair
|
||||
// of files. The files must contain PEM encoded data. The certificate file
|
||||
// may contain intermediate certificates following the leaf certificate to
|
||||
// form a certificate chain. On successful return, Certificate.Leaf will
|
||||
// be nil because the parsed form of the certificate is not retained.
|
||||
func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
|
||||
return Certificate{}, errors.New("tls:LoadX509KeyPair not implemented")
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// this is intended to be used as wasm32-unknown-unknown module.
|
||||
// to compile it, run:
|
||||
// tinygo build -size short -o hello-unknown.wasm -target wasm-unknown -gc=leaking -no-debug ./src/examples/hello-wasm-unknown/
|
||||
package main
|
||||
|
||||
var x int32
|
||||
|
||||
//go:wasmimport hosted echo_i32
|
||||
func echo(x int32)
|
||||
|
||||
//go:export update
|
||||
func update() {
|
||||
x++
|
||||
echo(x)
|
||||
}
|
||||
|
||||
func main() {
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// to override the USB Manufacturer or Product names:
|
||||
//
|
||||
// tinygo flash -target circuitplay-express -ldflags="-X main.usbManufacturer='TinyGopher Labs' -X main.usbProduct='GopherKeyboard'" examples/hid-keyboard
|
||||
// tinygo flash -target circuitplay-express -ldflags="-X main.usbManufacturer='TinyGopher Labs' -X main.usbProduct='GopherKeyboard' -X main.usbSerial='XXXXX'" examples/hid-keyboard
|
||||
//
|
||||
// you can also override the VID/PID. however, only set this if you know what you are doing,
|
||||
// since changing it can make it difficult to reflash some devices.
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
var usbVID, usbPID string
|
||||
var usbManufacturer, usbProduct string
|
||||
var usbManufacturer, usbProduct, usbSerial string
|
||||
|
||||
func main() {
|
||||
button := machine.BUTTON
|
||||
@@ -49,4 +49,8 @@ func init() {
|
||||
if usbProduct != "" {
|
||||
usb.Product = usbProduct
|
||||
}
|
||||
|
||||
if usbSerial != "" {
|
||||
usb.Serial = usbSerial
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
package bytealg
|
||||
|
||||
// Some code in this file has been copied from the Go source code, and has
|
||||
// copyright of their original authors:
|
||||
//
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
//
|
||||
// This is indicated specifically in the file.
|
||||
|
||||
const (
|
||||
// Index can search any valid length of string.
|
||||
|
||||
@@ -125,10 +134,6 @@ func IndexString(str, sub string) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// The following code has been copied from the Go 1.15 release tree.
|
||||
|
||||
// PrimeRK is the prime base used in Rabin-Karp algorithm.
|
||||
@@ -136,6 +141,8 @@ const PrimeRK = 16777619
|
||||
|
||||
// HashStrBytes returns the hash and the appropriate multiplicative
|
||||
// factor for use in Rabin-Karp algorithm.
|
||||
//
|
||||
// This function was removed in Go 1.22.
|
||||
func HashStrBytes(sep []byte) (uint32, uint32) {
|
||||
hash := uint32(0)
|
||||
for i := 0; i < len(sep); i++ {
|
||||
@@ -153,7 +160,9 @@ func HashStrBytes(sep []byte) (uint32, uint32) {
|
||||
|
||||
// HashStr returns the hash and the appropriate multiplicative
|
||||
// factor for use in Rabin-Karp algorithm.
|
||||
func HashStr(sep string) (uint32, uint32) {
|
||||
//
|
||||
// This function was removed in Go 1.22.
|
||||
func HashStr[T string | []byte](sep T) (uint32, uint32) {
|
||||
hash := uint32(0)
|
||||
for i := 0; i < len(sep); i++ {
|
||||
hash = hash*PrimeRK + uint32(sep[i])
|
||||
@@ -170,6 +179,8 @@ func HashStr(sep string) (uint32, uint32) {
|
||||
|
||||
// HashStrRevBytes returns the hash of the reverse of sep and the
|
||||
// appropriate multiplicative factor for use in Rabin-Karp algorithm.
|
||||
//
|
||||
// This function was removed in Go 1.22.
|
||||
func HashStrRevBytes(sep []byte) (uint32, uint32) {
|
||||
hash := uint32(0)
|
||||
for i := len(sep) - 1; i >= 0; i-- {
|
||||
@@ -187,7 +198,9 @@ func HashStrRevBytes(sep []byte) (uint32, uint32) {
|
||||
|
||||
// HashStrRev returns the hash of the reverse of sep and the
|
||||
// appropriate multiplicative factor for use in Rabin-Karp algorithm.
|
||||
func HashStrRev(sep string) (uint32, uint32) {
|
||||
//
|
||||
// Copied from the Go 1.22rc1 source tree.
|
||||
func HashStrRev[T string | []byte](sep T) (uint32, uint32) {
|
||||
hash := uint32(0)
|
||||
for i := len(sep) - 1; i >= 0; i-- {
|
||||
hash = hash*PrimeRK + uint32(sep[i])
|
||||
@@ -204,6 +217,8 @@ func HashStrRev(sep string) (uint32, uint32) {
|
||||
|
||||
// IndexRabinKarpBytes uses the Rabin-Karp search algorithm to return the index of the
|
||||
// first occurence of substr in s, or -1 if not present.
|
||||
//
|
||||
// This function was removed in Go 1.22.
|
||||
func IndexRabinKarpBytes(s, sep []byte) int {
|
||||
// Rabin-Karp search
|
||||
hashsep, pow := HashStrBytes(sep)
|
||||
@@ -228,16 +243,18 @@ func IndexRabinKarpBytes(s, sep []byte) int {
|
||||
}
|
||||
|
||||
// IndexRabinKarp uses the Rabin-Karp search algorithm to return the index of the
|
||||
// first occurence of substr in s, or -1 if not present.
|
||||
func IndexRabinKarp(s, substr string) int {
|
||||
// first occurrence of sep in s, or -1 if not present.
|
||||
//
|
||||
// Copied from the Go 1.22rc1 source tree.
|
||||
func IndexRabinKarp[T string | []byte](s, sep T) int {
|
||||
// Rabin-Karp search
|
||||
hashss, pow := HashStr(substr)
|
||||
n := len(substr)
|
||||
hashss, pow := HashStr(sep)
|
||||
n := len(sep)
|
||||
var h uint32
|
||||
for i := 0; i < n; i++ {
|
||||
h = h*PrimeRK + uint32(s[i])
|
||||
}
|
||||
if h == hashss && s[:n] == substr {
|
||||
if h == hashss && string(s[:n]) == string(sep) {
|
||||
return 0
|
||||
}
|
||||
for i := n; i < len(s); {
|
||||
@@ -245,7 +262,7 @@ func IndexRabinKarp(s, substr string) int {
|
||||
h += uint32(s[i])
|
||||
h -= pow * uint32(s[i-n])
|
||||
i++
|
||||
if h == hashss && s[i-n:i] == substr {
|
||||
if h == hashss && string(s[i-n:i]) == string(sep) {
|
||||
return i - n
|
||||
}
|
||||
}
|
||||
@@ -261,3 +278,50 @@ func MakeNoZero(n int) []byte {
|
||||
// malloc function implemented in the runtime).
|
||||
return make([]byte, n)
|
||||
}
|
||||
|
||||
// Copied from the Go 1.22rc1 source tree.
|
||||
func LastIndexByte(s []byte, c byte) int {
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
if s[i] == c {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Copied from the Go 1.22rc1 source tree.
|
||||
func LastIndexByteString(s string, c byte) int {
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
if s[i] == c {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// LastIndexRabinKarp uses the Rabin-Karp search algorithm to return the last index of the
|
||||
// occurrence of sep in s, or -1 if not present.
|
||||
//
|
||||
// Copied from the Go 1.22rc1 source tree.
|
||||
func LastIndexRabinKarp[T string | []byte](s, sep T) int {
|
||||
// Rabin-Karp search from the end of the string
|
||||
hashss, pow := HashStrRev(sep)
|
||||
n := len(sep)
|
||||
last := len(s) - n
|
||||
var h uint32
|
||||
for i := len(s) - 1; i >= last; i-- {
|
||||
h = h*PrimeRK + uint32(s[i])
|
||||
}
|
||||
if h == hashss && string(s[last:]) == string(sep) {
|
||||
return last
|
||||
}
|
||||
for i := last - 1; i >= 0; i-- {
|
||||
h *= PrimeRK
|
||||
h += uint32(s[i])
|
||||
h -= pow * uint32(s[i+n])
|
||||
if h == hashss && string(s[i:i+n]) == string(sep) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build tinygo
|
||||
|
||||
// Only generate .debug_frame, don't generate .eh_frame.
|
||||
.cfi_sections .debug_frame
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build tinygo
|
||||
|
||||
.section .bss.tinygo_systemStack
|
||||
.global tinygo_systemStack
|
||||
.type tinygo_systemStack, %object
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build tinygo
|
||||
|
||||
// Only generate .debug_frame, don't generate .eh_frame.
|
||||
.cfi_sections .debug_frame
|
||||
|
||||
|
||||
@@ -52,17 +52,17 @@ func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
switchToTask(s.sp)
|
||||
tinygo_switchToTask(s.sp)
|
||||
}
|
||||
|
||||
//export tinygo_switchToTask
|
||||
func switchToTask(uintptr)
|
||||
func tinygo_switchToTask(uintptr)
|
||||
|
||||
//export tinygo_switchToScheduler
|
||||
func switchToScheduler(*uintptr)
|
||||
func tinygo_switchToScheduler(*uintptr)
|
||||
|
||||
func (s *state) pause() {
|
||||
switchToScheduler(&s.sp)
|
||||
tinygo_switchToScheduler(&s.sp)
|
||||
}
|
||||
|
||||
// SystemStack returns the system stack pointer. On Cortex-M, it is always
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build tinygo
|
||||
|
||||
.section .text.tinygo_startTask,"ax",@progbits
|
||||
.global tinygo_startTask
|
||||
.type tinygo_startTask, %function
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build tinygo
|
||||
|
||||
.section .text.tinygo_startTask,"ax",@progbits
|
||||
.global tinygo_startTask
|
||||
.type tinygo_startTask, %function
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build tinygo
|
||||
|
||||
.section .text.tinygo_startTask
|
||||
.global tinygo_startTask
|
||||
.type tinygo_startTask, %function
|
||||
|
||||
@@ -2,11 +2,6 @@
|
||||
|
||||
package machine
|
||||
|
||||
// Return the current CPU frequency in hertz.
|
||||
func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
// Digital pins, marked as plain numbers on the board.
|
||||
const (
|
||||
D0 = PD0 // RX
|
||||
|
||||
@@ -2,11 +2,6 @@
|
||||
|
||||
package machine
|
||||
|
||||
// Return the current CPU frequency in hertz.
|
||||
func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
// Digital pins.
|
||||
const (
|
||||
D0 = PD0 // RX0
|
||||
|
||||
@@ -63,6 +63,9 @@ var UART1 = &sercomUSART3
|
||||
// UART2 on the Arduino Nano 33 connects to the normal TX/RX pins.
|
||||
var UART2 = &sercomUSART5
|
||||
|
||||
// UART_NINA on the Arduino Nano 33 connects to the NINA HCI.
|
||||
var UART_NINA = &sercomUSART2
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN Pin = A4 // SDA: SERCOM4/PAD[1]
|
||||
@@ -99,8 +102,17 @@ const (
|
||||
NINA_GPIO0 Pin = PA27
|
||||
NINA_RESETN Pin = PA08
|
||||
NINA_ACK Pin = PA28
|
||||
NINA_TX Pin = PA22
|
||||
NINA_RX Pin = PA23
|
||||
NINA_TX Pin = PA12
|
||||
NINA_RX Pin = PA13
|
||||
NINA_RTS Pin = PA14
|
||||
NINA_CTS Pin = PA15
|
||||
)
|
||||
|
||||
// NINA-W102 settings
|
||||
const (
|
||||
NINA_BAUDRATE = 912600
|
||||
NINA_RESET_INVERTED = true
|
||||
NINA_SOFT_FLOWCONTROL = false
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
package machine
|
||||
|
||||
// Return the current CPU frequency in hertz.
|
||||
func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
const (
|
||||
// Note: start at port B because there is no port A.
|
||||
portB Pin = iota * 8
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
//go:build avr && atmega328pb
|
||||
|
||||
package machine
|
||||
|
||||
// Return the current CPU frequency in hertz.
|
||||
func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
const (
|
||||
// Note: start at port B because there is no port A.
|
||||
portB Pin = iota * 8
|
||||
portC
|
||||
portD
|
||||
portE
|
||||
)
|
||||
|
||||
const (
|
||||
PB0 = portB + 0
|
||||
PB1 = portB + 1 // peripherals: Timer1 channel A
|
||||
PB2 = portB + 2 // peripherals: Timer1 channel B
|
||||
PB3 = portB + 3 // peripherals: Timer2 channel A
|
||||
PB4 = portB + 4
|
||||
PB5 = portB + 5
|
||||
PB6 = portB + 6
|
||||
PB7 = portB + 7
|
||||
PC0 = portC + 0
|
||||
PC1 = portC + 1
|
||||
PC2 = portC + 2
|
||||
PC3 = portC + 3
|
||||
PC4 = portC + 4
|
||||
PC5 = portC + 5
|
||||
PC6 = portC + 6
|
||||
PC7 = portC + 7
|
||||
PD0 = portD + 0
|
||||
PD1 = portD + 1
|
||||
PD2 = portD + 2
|
||||
PD3 = portD + 3 // peripherals: Timer2 channel B
|
||||
PD4 = portD + 4
|
||||
PD5 = portD + 5 // peripherals: Timer0 channel B
|
||||
PD6 = portD + 6 // peripherals: Timer0 channel A
|
||||
PD7 = portD + 7
|
||||
PE0 = portE + 0
|
||||
PE1 = portE + 1
|
||||
PE2 = portE + 2
|
||||
PE3 = portE + 3
|
||||
PE4 = portE + 4
|
||||
PE5 = portE + 5
|
||||
PE6 = portE + 6
|
||||
PE7 = portE + 7
|
||||
)
|
||||
@@ -73,7 +73,7 @@ const (
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Gopher Badger"
|
||||
usb_STRING_PRODUCT = "Gopher Badge"
|
||||
usb_STRING_MANUFACTURER = "TinyGo"
|
||||
)
|
||||
|
||||
|
||||
@@ -62,6 +62,15 @@ var (
|
||||
UART2 = &sercomUSART0
|
||||
|
||||
DefaultUART = UART1
|
||||
|
||||
UART_NINA = UART2
|
||||
)
|
||||
|
||||
// NINA-W102 settings
|
||||
const (
|
||||
NINA_BAUDRATE = 115200
|
||||
NINA_RESET_INVERTED = true
|
||||
NINA_SOFT_FLOWCONTROL = true
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -70,9 +79,12 @@ const (
|
||||
NINA_GPIO0 = PB01
|
||||
NINA_RESETN = PB05
|
||||
|
||||
// pins used for the ESP32 connection do not allow hardware
|
||||
// flow control, which is required. have to emulate with software.
|
||||
NINA_TX = PA04
|
||||
NINA_RX = PA07
|
||||
NINA_RTS = PB23
|
||||
NINA_CTS = NINA_ACK
|
||||
NINA_RTS = NINA_GPIO0
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
//go:build mksnanov3
|
||||
|
||||
// The MKS Robin Nano V3.X board.
|
||||
// Documented at https://github.com/makerbase-mks/MKS-Robin-Nano-V3.X.
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// LED is also wired to the SD card card detect (CD) pin.
|
||||
const LED = PD12
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART_TX_PIN = PB10
|
||||
UART_RX_PIN = PB11
|
||||
)
|
||||
|
||||
// EXP1 and EXP2 expansion ports for connecting
|
||||
// the MKS TS35 V2.0 expansion board.
|
||||
const (
|
||||
BEEPER = EXP1_1
|
||||
|
||||
// LCD pins.
|
||||
LCD_DC = EXP1_8
|
||||
LCD_CS = EXP1_7
|
||||
LCD_RS = EXP1_4
|
||||
LCD_BACKLIGHT = EXP1_3
|
||||
|
||||
// Touch pins. Note that some pins are shared with the
|
||||
// LCD SPI1 interface.
|
||||
TOUCH_CLK = EXP2_2
|
||||
TOUCH_CS = EXP1_5
|
||||
TOUCH_DIN = EXP2_6
|
||||
TOUCH_DOUT = EXP2_1
|
||||
TOUCH_IRQ = EXP1_6
|
||||
|
||||
BUTTON = BUTTON_JOG
|
||||
BUTTON_JOG = EXP1_2
|
||||
BUTTON_JOG_CCW = EXP2_3
|
||||
BUTTON_JOG_CW = EXP2_5
|
||||
|
||||
EXP1_1 = PC5
|
||||
EXP1_2 = PE13
|
||||
EXP1_3 = PD13
|
||||
EXP1_4 = PC6
|
||||
EXP1_5 = PE14
|
||||
EXP1_6 = PE15
|
||||
EXP1_7 = PD11
|
||||
EXP1_8 = PD10
|
||||
|
||||
EXP2_1 = PA6
|
||||
EXP2_2 = PA5
|
||||
EXP2_3 = PE8
|
||||
EXP2_4 = PE10
|
||||
EXP2_5 = PE11
|
||||
EXP2_6 = PA7
|
||||
EXP2_7 = PE12
|
||||
)
|
||||
|
||||
var (
|
||||
UART3 = &_UART3
|
||||
_UART3 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART3,
|
||||
TxAltFuncSelector: AF7_USART1_2_3,
|
||||
RxAltFuncSelector: AF7_USART1_2_3,
|
||||
}
|
||||
DefaultUART = UART3
|
||||
)
|
||||
|
||||
// set up RX IRQ handler. Follow similar pattern for other UARTx instances
|
||||
func init() {
|
||||
UART3.Interrupt = interrupt.New(stm32.IRQ_USART3, _UART3.handleInterrupt)
|
||||
}
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI1_SCK_PIN = EXP2_2
|
||||
SPI1_SDI_PIN = EXP2_1
|
||||
SPI1_SDO_PIN = EXP2_6
|
||||
SPI0_SCK_PIN = SPI1_SCK_PIN
|
||||
SPI0_SDI_PIN = SPI1_SDI_PIN
|
||||
SPI0_SDO_PIN = SPI1_SDO_PIN
|
||||
)
|
||||
|
||||
// Since the first interface is named SPI1, both SPI0 and SPI1 refer to SPI1.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: stm32.SPI1,
|
||||
AltFuncSelector: AF5_SPI1_SPI2,
|
||||
}
|
||||
SPI1 = &SPI0
|
||||
)
|
||||
|
||||
const (
|
||||
I2C0_SCL_PIN = PB6
|
||||
I2C0_SDA_PIN = PB7
|
||||
)
|
||||
|
||||
var (
|
||||
I2C0 = &I2C{
|
||||
Bus: stm32.I2C1,
|
||||
AltFuncSelector: AF4_I2C1_2_3,
|
||||
}
|
||||
)
|
||||
|
||||
// Motor control pins.
|
||||
const (
|
||||
X_ENABLE = PE4
|
||||
X_STEP = PE3
|
||||
X_DIR = PE2
|
||||
X_DIAG = PA15
|
||||
X_UART = PD5
|
||||
|
||||
Y_ENABLE = PE1
|
||||
Y_STEP = PE0
|
||||
Y_DIR = PB9
|
||||
Y_DIAG = PD2
|
||||
Y_UART = PD7
|
||||
|
||||
Z_ENABLE = PB8
|
||||
Z_STEP = PB5
|
||||
Z_DIR = PB4
|
||||
Z_DIAG = PC8
|
||||
Z_UART = PD4
|
||||
|
||||
E0_ENABLE = PB3
|
||||
E0_STEP = PD6
|
||||
E0_DIR = PD3
|
||||
E0_DIAG = PC4
|
||||
E0_UART = PD9
|
||||
|
||||
E1_ENABLE = PA3
|
||||
E1_STEP = PD15
|
||||
E1_DIR = PA1
|
||||
E1_DIAG = PE7
|
||||
E1_UART = PD8
|
||||
)
|
||||
@@ -87,8 +87,17 @@ const (
|
||||
NINA_GPIO0 Pin = GPIO2
|
||||
NINA_RESETN Pin = GPIO3
|
||||
|
||||
NINA_TX Pin = GPIO9
|
||||
NINA_RX Pin = GPIO8
|
||||
NINA_TX Pin = GPIO8
|
||||
NINA_RX Pin = GPIO9
|
||||
NINA_CTS Pin = GPIO10
|
||||
NINA_RTS Pin = GPIO11
|
||||
)
|
||||
|
||||
// NINA-W102 settings
|
||||
const (
|
||||
NINA_BAUDRATE = 115200
|
||||
NINA_RESET_INVERTED = true
|
||||
NINA_SOFT_FLOWCONTROL = false
|
||||
)
|
||||
|
||||
// Onboard crystal oscillator frequency, in MHz.
|
||||
@@ -123,10 +132,20 @@ var (
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: rp.UART0,
|
||||
}
|
||||
|
||||
UART1 = &_UART1
|
||||
_UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: rp.UART1,
|
||||
}
|
||||
|
||||
// UART_NINA on the Arduino Nano RP2040 connects to the NINA HCI.
|
||||
UART_NINA = UART1
|
||||
)
|
||||
|
||||
var DefaultUART = UART0
|
||||
|
||||
func init() {
|
||||
UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.handleInterrupt)
|
||||
UART1.Interrupt = interrupt.New(rp.IRQ_UART1_IRQ, _UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
//go:build nrf52840 && nrf52840_generic
|
||||
|
||||
package machine
|
||||
|
||||
var (
|
||||
LED = NoPin
|
||||
SDA_PIN = NoPin
|
||||
SCL_PIN = NoPin
|
||||
UART_TX_PIN = NoPin
|
||||
UART_RX_PIN = NoPin
|
||||
SPI0_SCK_PIN = NoPin
|
||||
SPI0_SDO_PIN = NoPin
|
||||
SPI0_SDI_PIN = NoPin
|
||||
|
||||
// https://pid.codes/org/TinyGo/
|
||||
usb_VID uint16 = 0x1209
|
||||
usb_PID uint16 = 0x9090
|
||||
|
||||
usb_STRING_MANUFACTURER = "TinyGo"
|
||||
usb_STRING_PRODUCT = "nRF52840 Generic board"
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultUART = UART0
|
||||
)
|
||||
@@ -130,3 +130,33 @@ var (
|
||||
usb_VID uint16 = 0x239A
|
||||
usb_PID uint16 = 0x8033
|
||||
)
|
||||
|
||||
// NINA-W102 settings when using AirLift WiFi FeatherWing
|
||||
const (
|
||||
NINA_BAUDRATE = 115200
|
||||
NINA_RESET_INVERTED = true
|
||||
NINA_SOFT_FLOWCONTROL = true
|
||||
)
|
||||
|
||||
const (
|
||||
NINA_CS = D13
|
||||
NINA_ACK = D11
|
||||
NINA_GPIO0 = D10
|
||||
NINA_RESETN = D12
|
||||
|
||||
// pins used for the ESP32 connection do not allow hardware
|
||||
// flow control, which is required. have to emulate with software.
|
||||
NINA_TX = UART_TX_PIN
|
||||
NINA_RX = UART_RX_PIN
|
||||
NINA_CTS = NINA_ACK
|
||||
NINA_RTS = NINA_GPIO0
|
||||
|
||||
NINA_SDO = SPI0_SDO_PIN
|
||||
NINA_SDI = SPI0_SDI_PIN
|
||||
NINA_SCK = SPI0_SCK_PIN
|
||||
)
|
||||
|
||||
var (
|
||||
NINA_SPI = SPI0
|
||||
UART_NINA = UART1
|
||||
)
|
||||
|
||||
@@ -53,9 +53,12 @@ const (
|
||||
NINA_GPIO0 = D6
|
||||
NINA_RESETN = D7
|
||||
|
||||
// pins used for the ESP32 connection do not allow hardware
|
||||
// flow control, which is required. have to emulate with software.
|
||||
NINA_TX = D1
|
||||
NINA_RX = D0
|
||||
NINA_RTS = D51
|
||||
NINA_CTS = NINA_ACK
|
||||
NINA_RTS = NINA_GPIO0
|
||||
|
||||
LCD_DATA0 = D34
|
||||
|
||||
@@ -111,6 +114,15 @@ var (
|
||||
UART1 = &sercomUSART4
|
||||
|
||||
DefaultUART = UART1
|
||||
|
||||
UART_NINA = UART1
|
||||
)
|
||||
|
||||
// NINA-W102 settings
|
||||
const (
|
||||
NINA_BAUDRATE = 115200
|
||||
NINA_RESET_INVERTED = true
|
||||
NINA_SOFT_FLOWCONTROL = true
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
//go:build qtpy_esp32c3
|
||||
|
||||
// This file contains the pin mappings for the Adafruit QtPy ESP32C3 boards.
|
||||
//
|
||||
// https://learn.adafruit.com/adafruit-qt-py-esp32-c3-wifi-dev-board/pinouts
|
||||
package machine
|
||||
|
||||
// Digital Pins
|
||||
const (
|
||||
D0 = GPIO4
|
||||
D1 = GPIO3
|
||||
D2 = GPIO1
|
||||
D3 = GPIO0
|
||||
)
|
||||
|
||||
// Analog pins (ADC1)
|
||||
const (
|
||||
A0 = GPIO4
|
||||
A1 = GPIO3
|
||||
A2 = GPIO1
|
||||
A3 = GPIO0
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
RX_PIN = GPIO20
|
||||
TX_PIN = GPIO21
|
||||
|
||||
UART_RX_PIN = RX_PIN
|
||||
UART_TX_PIN = TX_PIN
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = GPIO5
|
||||
SCL_PIN = GPIO6
|
||||
|
||||
I2C0_SDA_PIN = SDA_PIN
|
||||
I2C0_SCL_PIN = SCL_PIN
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SCK_PIN = GPIO10
|
||||
MI_PIN = GPIO8
|
||||
MO_PIN = GPIO7
|
||||
|
||||
SPI_SCK_PIN = SCK_PIN
|
||||
SPI_SDI_PIN = MI_PIN
|
||||
SPI_SDO_PIN = MO_PIN
|
||||
)
|
||||
|
||||
const (
|
||||
NEOPIXEL = GPIO2
|
||||
WS2812 = GPIO2
|
||||
|
||||
// also used for boot button.
|
||||
// set it to be an input-with-pullup
|
||||
BUTTON = GPIO9
|
||||
)
|
||||
@@ -0,0 +1,94 @@
|
||||
//go:build thumby
|
||||
|
||||
// This contains the pin mappings for the Thumby.
|
||||
//
|
||||
// https://thumby.us/
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/rp"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
const (
|
||||
THUMBY_SCK_PIN = I2C1_SDA_PIN
|
||||
THUMBY_SDA_PIN = I2C1_SCL_PIN
|
||||
|
||||
THUMBY_CS_PIN = GPIO16
|
||||
THUMBY_DC_PIN = GPIO17
|
||||
THUMBY_RESET_PIN = GPIO20
|
||||
|
||||
THUMBY_LINK_TX_PIN = UART0_TX_PIN
|
||||
THUMBY_LINK_RX_PIN = UART0_RX_PIN
|
||||
THUMBY_LINK_PU_PIN = GPIO2
|
||||
|
||||
THUMBY_BTN_LDPAD_PIN = GPIO3
|
||||
THUMBY_BTN_RDPAD_PIN = GPIO5
|
||||
THUMBY_BTN_UDPAD_PIN = GPIO4
|
||||
THUMBY_BTN_DDPAD_PIN = GPIO6
|
||||
THUMBY_BTN_B_PIN = GPIO24
|
||||
THUMBY_BTN_A_PIN = GPIO27
|
||||
|
||||
THUMBY_AUDIO_PIN = GPIO28
|
||||
|
||||
THUMBY_SCREEN_RESET_PIN = GPIO20
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
I2C0_SDA_PIN Pin = NoPin
|
||||
I2C0_SCL_PIN Pin = NoPin
|
||||
|
||||
I2C1_SDA_PIN Pin = GPIO18
|
||||
I2C1_SCL_PIN Pin = GPIO19
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = GPIO18
|
||||
SPI0_SDO_PIN = GPIO19
|
||||
SPI0_SDI_PIN = GPIO16
|
||||
|
||||
SPI1_SCK_PIN = NoPin
|
||||
SPI1_SDO_PIN = NoPin
|
||||
SPI1_SDI_PIN = NoPin
|
||||
)
|
||||
|
||||
// Onboard crystal oscillator frequency, in MHz.
|
||||
const (
|
||||
xoscFreq = 12 // MHz
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Thumby"
|
||||
usb_STRING_MANUFACTURER = "TinyCircuits"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x2E8A
|
||||
usb_PID uint16 = 0x0005
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART0_TX_PIN = GPIO0
|
||||
UART0_RX_PIN = GPIO1
|
||||
UART_TX_PIN = UART0_TX_PIN
|
||||
UART_RX_PIN = UART0_RX_PIN
|
||||
)
|
||||
|
||||
// UART on the Thumby
|
||||
var (
|
||||
UART0 = &_UART0
|
||||
_UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: rp.UART0,
|
||||
}
|
||||
)
|
||||
|
||||
var DefaultUART = UART0
|
||||
|
||||
func init() {
|
||||
UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.handleInterrupt)
|
||||
}
|
||||
@@ -353,6 +353,9 @@ var (
|
||||
|
||||
// RTL8720D (tx: PB24, rx: PC24)
|
||||
UART3 = &sercomUSART0
|
||||
|
||||
// Right-hand grove port (tx: D0, rx: D1)
|
||||
UART4 = &sercomUSART4
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
const bufferSize = 128
|
||||
|
||||
// RingBuffer is ring buffer implementation inspired by post at
|
||||
// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php
|
||||
type RingBuffer struct {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
//go:build atmega
|
||||
|
||||
package machine
|
||||
|
||||
const bufferSize = 32
|
||||
@@ -0,0 +1,5 @@
|
||||
//go:build !atmega
|
||||
|
||||
package machine
|
||||
|
||||
const bufferSize = 128
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
//go:build atmega || nrf || sam || stm32 || fe310 || k210 || rp2040 || mimxrt1062
|
||||
//go:build atmega || nrf || sam || stm32 || fe310 || k210 || rp2040 || mimxrt1062 || (esp32c3 && !m5stamp_c3)
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -11,10 +11,19 @@ import (
|
||||
|
||||
// I2C on AVR.
|
||||
type I2C struct {
|
||||
}
|
||||
srReg *volatile.Register8
|
||||
brReg *volatile.Register8
|
||||
crReg *volatile.Register8
|
||||
drReg *volatile.Register8
|
||||
|
||||
// I2C0 is the only I2C interface on most AVRs.
|
||||
var I2C0 *I2C = nil
|
||||
srPS0 byte
|
||||
srPS1 byte
|
||||
crEN byte
|
||||
crINT byte
|
||||
crSTO byte
|
||||
crEA byte
|
||||
crSTA byte
|
||||
}
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
type I2CConfig struct {
|
||||
@@ -37,16 +46,16 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
// SetBaudRate sets the communication speed for I2C.
|
||||
func (i2c *I2C) SetBaudRate(br uint32) error {
|
||||
// Initialize twi prescaler and bit rate.
|
||||
avr.TWSR.SetBits((avr.TWSR_TWPS0 | avr.TWSR_TWPS1))
|
||||
i2c.srReg.SetBits((i2c.srPS0 | i2c.srPS1))
|
||||
|
||||
// twi bit rate formula from atmega128 manual pg. 204:
|
||||
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
|
||||
// NOTE: TWBR should be 10 or higher for controller mode.
|
||||
// It is 72 for a 16mhz board with 100kHz TWI
|
||||
avr.TWBR.Set(uint8(((CPUFrequency() / br) - 16) / 2))
|
||||
i2c.brReg.Set(uint8(((CPUFrequency() / br) - 16) / 2))
|
||||
|
||||
// Enable twi module.
|
||||
avr.TWCR.Set(avr.TWCR_TWEN)
|
||||
i2c.crReg.Set(i2c.crEN)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -77,10 +86,10 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||
// start starts an I2C communication session.
|
||||
func (i2c *I2C) start(address uint8, write bool) {
|
||||
// Clear TWI interrupt flag, put start condition on SDA, and enable TWI.
|
||||
avr.TWCR.Set((avr.TWCR_TWINT | avr.TWCR_TWSTA | avr.TWCR_TWEN))
|
||||
i2c.crReg.Set((i2c.crINT | i2c.crSTA | i2c.crEN))
|
||||
|
||||
// Wait till start condition is transmitted.
|
||||
for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
|
||||
for !i2c.crReg.HasBits(i2c.crINT) {
|
||||
}
|
||||
|
||||
// Write 7-bit shifted peripheral address.
|
||||
@@ -94,23 +103,23 @@ func (i2c *I2C) start(address uint8, write bool) {
|
||||
// stop ends an I2C communication session.
|
||||
func (i2c *I2C) stop() {
|
||||
// Send stop condition.
|
||||
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWSTO)
|
||||
i2c.crReg.Set(i2c.crEN | i2c.crINT | i2c.crSTO)
|
||||
|
||||
// Wait for stop condition to be executed on bus.
|
||||
for !avr.TWCR.HasBits(avr.TWCR_TWSTO) {
|
||||
for !i2c.crReg.HasBits(i2c.crSTO) {
|
||||
}
|
||||
}
|
||||
|
||||
// writeByte writes a single byte to the I2C bus.
|
||||
func (i2c *I2C) writeByte(data byte) error {
|
||||
// Write data to register.
|
||||
avr.TWDR.Set(data)
|
||||
i2c.drReg.Set(data)
|
||||
|
||||
// Clear TWI interrupt flag and enable TWI.
|
||||
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT)
|
||||
i2c.crReg.Set(i2c.crEN | i2c.crINT)
|
||||
|
||||
// Wait till data is transmitted.
|
||||
for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
|
||||
for !i2c.crReg.HasBits(i2c.crINT) {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -118,13 +127,13 @@ func (i2c *I2C) writeByte(data byte) error {
|
||||
// readByte reads a single byte from the I2C bus.
|
||||
func (i2c *I2C) readByte() byte {
|
||||
// Clear TWI interrupt flag and enable TWI.
|
||||
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWEA)
|
||||
i2c.crReg.Set(i2c.crEN | i2c.crINT | i2c.crEA)
|
||||
|
||||
// Wait till read request is transmitted.
|
||||
for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
|
||||
for !i2c.crReg.HasBits(i2c.crINT) {
|
||||
}
|
||||
|
||||
return byte(avr.TWDR.Get())
|
||||
return byte(i2c.drReg.Get())
|
||||
}
|
||||
|
||||
// Always use UART0 as the serial output.
|
||||
@@ -170,10 +179,18 @@ func (uart *UART) Configure(config UARTConfig) {
|
||||
config.BaudRate = 9600
|
||||
}
|
||||
|
||||
// Set baud rate based on prescale formula from
|
||||
// https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_wrong_baud_rate.html
|
||||
// ((F_CPU + UART_BAUD_RATE * 8L) / (UART_BAUD_RATE * 16L) - 1)
|
||||
ps := ((CPUFrequency()+config.BaudRate*8)/(config.BaudRate*16) - 1)
|
||||
// Prescale formula for u2x mode from AVR MiniCore source code.
|
||||
// Same as formula from specification but taking into account rounding error.
|
||||
ps := (CPUFrequency()/4/config.BaudRate - 1) / 2
|
||||
uart.statusRegA.SetBits(avr.UCSR0A_U2X0)
|
||||
|
||||
// Hardcoded exception for 57600 for compatibility with older bootloaders.
|
||||
// Also, prescale cannot be > 4095, so switch back to non-u2x mode if the baud rate is too low.
|
||||
if (CPUFrequency() == 16000000 && config.BaudRate == 57600) || ps > 0xfff {
|
||||
ps = (CPUFrequency()/8/config.BaudRate - 1) / 2
|
||||
uart.statusRegA.ClearBits(avr.UCSR0A_U2X0)
|
||||
}
|
||||
|
||||
uart.baudRegH.Set(uint8(ps >> 8))
|
||||
uart.baudRegL.Set(uint8(ps & 0xff))
|
||||
|
||||
@@ -221,6 +238,17 @@ type SPI struct {
|
||||
spdr *volatile.Register8
|
||||
spsr *volatile.Register8
|
||||
|
||||
spcrR0 byte
|
||||
spcrR1 byte
|
||||
spcrCPHA byte
|
||||
spcrCPOL byte
|
||||
spcrDORD byte
|
||||
spcrSPE byte
|
||||
spcrMSTR byte
|
||||
|
||||
spsrI2X byte
|
||||
spsrSPIF byte
|
||||
|
||||
// The io pins for the SPIx port set by the chip
|
||||
sck Pin
|
||||
sdi Pin
|
||||
@@ -264,39 +292,39 @@ func (s SPI) Configure(config SPIConfig) error {
|
||||
|
||||
switch {
|
||||
case frequencyDivider >= 128:
|
||||
s.spcr.SetBits(avr.SPCR_SPR0 | avr.SPCR_SPR1)
|
||||
s.spcr.SetBits(s.spcrR0 | s.spcrR1)
|
||||
case frequencyDivider >= 64:
|
||||
s.spcr.SetBits(avr.SPCR_SPR1)
|
||||
s.spcr.SetBits(s.spcrR1)
|
||||
case frequencyDivider >= 32:
|
||||
s.spcr.SetBits(avr.SPCR_SPR1)
|
||||
s.spsr.SetBits(avr.SPSR_SPI2X)
|
||||
s.spcr.SetBits(s.spcrR1)
|
||||
s.spsr.SetBits(s.spsrI2X)
|
||||
case frequencyDivider >= 16:
|
||||
s.spcr.SetBits(avr.SPCR_SPR0)
|
||||
s.spcr.SetBits(s.spcrR0)
|
||||
case frequencyDivider >= 8:
|
||||
s.spcr.SetBits(avr.SPCR_SPR0)
|
||||
s.spsr.SetBits(avr.SPSR_SPI2X)
|
||||
s.spcr.SetBits(s.spcrR0)
|
||||
s.spsr.SetBits(s.spsrI2X)
|
||||
case frequencyDivider >= 4:
|
||||
// The clock is already set to all 0's.
|
||||
default: // defaults to fastest which is /2
|
||||
s.spsr.SetBits(avr.SPSR_SPI2X)
|
||||
s.spsr.SetBits(s.spsrI2X)
|
||||
}
|
||||
|
||||
switch config.Mode {
|
||||
case Mode1:
|
||||
s.spcr.SetBits(avr.SPCR_CPHA)
|
||||
s.spcr.SetBits(s.spcrCPHA)
|
||||
case Mode2:
|
||||
s.spcr.SetBits(avr.SPCR_CPOL)
|
||||
s.spcr.SetBits(s.spcrCPHA)
|
||||
case Mode3:
|
||||
s.spcr.SetBits(avr.SPCR_CPHA | avr.SPCR_CPOL)
|
||||
s.spcr.SetBits(s.spcrCPHA | s.spcrCPOL)
|
||||
default: // default is mode 0
|
||||
}
|
||||
|
||||
if config.LSBFirst {
|
||||
s.spcr.SetBits(avr.SPCR_DORD)
|
||||
s.spcr.SetBits(s.spcrDORD)
|
||||
}
|
||||
|
||||
// enable SPI, set controller, set clock rate
|
||||
s.spcr.SetBits(avr.SPCR_SPE | avr.SPCR_MSTR)
|
||||
s.spcr.SetBits(s.spcrSPE | s.spcrMSTR)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -305,7 +333,7 @@ func (s SPI) Configure(config SPIConfig) error {
|
||||
func (s SPI) Transfer(b byte) (byte, error) {
|
||||
s.spdr.Set(uint8(b))
|
||||
|
||||
for !s.spsr.HasBits(avr.SPSR_SPIF) {
|
||||
for !s.spsr.HasBits(s.spsrSPIF) {
|
||||
}
|
||||
|
||||
return byte(s.spdr.Get()), nil
|
||||
|
||||
@@ -0,0 +1,548 @@
|
||||
//go:build avr && (atmega328p || atmega328pb)
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/avr"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
// PWM is one PWM peripheral, which consists of a counter and two output
|
||||
// channels (that can be connected to two fixed pins). You can set the frequency
|
||||
// using SetPeriod, but only for all the channels in this PWM peripheral at
|
||||
// once.
|
||||
type PWM struct {
|
||||
num uint8
|
||||
}
|
||||
|
||||
var (
|
||||
Timer0 = PWM{0} // 8 bit timer for PD5 and PD6
|
||||
Timer1 = PWM{1} // 16 bit timer for PB1 and PB2
|
||||
Timer2 = PWM{2} // 8 bit timer for PB3 and PD3
|
||||
)
|
||||
|
||||
// Configure enables and configures this PWM.
|
||||
//
|
||||
// For the two 8 bit timers, there is only a limited number of periods
|
||||
// available, namely the CPU frequency divided by 256 and again divided by 1, 8,
|
||||
// 64, 256, or 1024. For a MCU running at 16MHz, this would be a period of 16µs,
|
||||
// 128µs, 1024µs, 4096µs, or 16384µs.
|
||||
func (pwm PWM) Configure(config PWMConfig) error {
|
||||
switch pwm.num {
|
||||
case 0, 2: // 8-bit timers (Timer/counter 0 and Timer/counter 2)
|
||||
// Calculate the timer prescaler.
|
||||
// While we could configure a flexible top, that would sacrifice one of
|
||||
// the PWM output compare registers and thus a PWM channel. I've chosen
|
||||
// to instead limit this timer to a fixed number of frequencies.
|
||||
var prescaler uint8
|
||||
switch config.Period {
|
||||
case 0, (uint64(1e9) * 256 * 1) / uint64(CPUFrequency()):
|
||||
prescaler = 1
|
||||
case (uint64(1e9) * 256 * 8) / uint64(CPUFrequency()):
|
||||
prescaler = 2
|
||||
case (uint64(1e9) * 256 * 64) / uint64(CPUFrequency()):
|
||||
prescaler = 3
|
||||
case (uint64(1e9) * 256 * 256) / uint64(CPUFrequency()):
|
||||
prescaler = 4
|
||||
case (uint64(1e9) * 256 * 1024) / uint64(CPUFrequency()):
|
||||
prescaler = 5
|
||||
default:
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
|
||||
if pwm.num == 0 {
|
||||
avr.TCCR0B.Set(prescaler)
|
||||
// Set the PWM mode to fast PWM (mode = 3).
|
||||
avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01)
|
||||
// monotonic timer is using the same time as PWM:0
|
||||
// we must adust internal settings of monotonic timer when PWM:0 settings changed
|
||||
adjustMonotonicTimer()
|
||||
} else {
|
||||
avr.TCCR2B.Set(prescaler)
|
||||
// Set the PWM mode to fast PWM (mode = 3).
|
||||
avr.TCCR2A.Set(avr.TCCR2A_WGM20 | avr.TCCR2A_WGM21)
|
||||
}
|
||||
case 1: // Timer/counter 1
|
||||
// The top value is the number of PWM ticks a PWM period takes. It is
|
||||
// initially picked assuming an unlimited counter top and no PWM
|
||||
// prescaler.
|
||||
var top uint64
|
||||
if config.Period == 0 {
|
||||
// Use a top appropriate for LEDs. Picking a relatively low period
|
||||
// here (0xff) for consistency with the other timers.
|
||||
top = 0xff
|
||||
} else {
|
||||
// The formula below calculates the following formula, optimized:
|
||||
// top = period * (CPUFrequency() / 1e9)
|
||||
// By dividing the CPU frequency first (an operation that is easily
|
||||
// optimized away) the period has less chance of overflowing.
|
||||
top = config.Period * (uint64(CPUFrequency()) / 1000000) / 1000
|
||||
}
|
||||
|
||||
avr.TCCR1A.Set(avr.TCCR1A_WGM11)
|
||||
|
||||
// The ideal PWM period may be larger than would fit in the PWM counter,
|
||||
// which is 16 bits (see maxTop). Therefore, try to make the PWM clock
|
||||
// speed lower with a prescaler to make the top value fit the maximum
|
||||
// top value.
|
||||
const maxTop = 0x10000
|
||||
switch {
|
||||
case top <= maxTop:
|
||||
avr.TCCR1B.Set(3<<3 | 1) // no prescaling
|
||||
case top/8 <= maxTop:
|
||||
avr.TCCR1B.Set(3<<3 | 2) // divide by 8
|
||||
top /= 8
|
||||
case top/64 <= maxTop:
|
||||
avr.TCCR1B.Set(3<<3 | 3) // divide by 64
|
||||
top /= 64
|
||||
case top/256 <= maxTop:
|
||||
avr.TCCR1B.Set(3<<3 | 4) // divide by 256
|
||||
top /= 256
|
||||
case top/1024 <= maxTop:
|
||||
avr.TCCR1B.Set(3<<3 | 5) // divide by 1024
|
||||
top /= 1024
|
||||
default:
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
|
||||
// A top of 0x10000 is at 100% duty cycle. Subtract one because the
|
||||
// counter counts from 0, not 1 (avoiding an off-by-one).
|
||||
top -= 1
|
||||
|
||||
avr.ICR1H.Set(uint8(top >> 8))
|
||||
avr.ICR1L.Set(uint8(top))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetPeriod updates the period of this PWM peripheral.
|
||||
// To set a particular frequency, use the following formula:
|
||||
//
|
||||
// period = 1e9 / frequency
|
||||
//
|
||||
// If you use a period of 0, a period that works well for LEDs will be picked.
|
||||
//
|
||||
// SetPeriod will not change the prescaler, but also won't change the current
|
||||
// value in any of the channels. This means that you may need to update the
|
||||
// value for the particular channel.
|
||||
//
|
||||
// Note that you cannot pick any arbitrary period after the PWM peripheral has
|
||||
// been configured. If you want to switch between frequencies, pick the lowest
|
||||
// frequency (longest period) once when calling Configure and adjust the
|
||||
// frequency here as needed.
|
||||
func (pwm PWM) SetPeriod(period uint64) error {
|
||||
if pwm.num != 1 {
|
||||
return ErrPWMPeriodTooLong // TODO better error message
|
||||
}
|
||||
|
||||
// The top value is the number of PWM ticks a PWM period takes. It is
|
||||
// initially picked assuming an unlimited counter top and no PWM
|
||||
// prescaler.
|
||||
var top uint64
|
||||
if period == 0 {
|
||||
// Use a top appropriate for LEDs. Picking a relatively low period
|
||||
// here (0xff) for consistency with the other timers.
|
||||
top = 0xff
|
||||
} else {
|
||||
// The formula below calculates the following formula, optimized:
|
||||
// top = period * (CPUFrequency() / 1e9)
|
||||
// By dividing the CPU frequency first (an operation that is easily
|
||||
// optimized away) the period has less chance of overflowing.
|
||||
top = period * (uint64(CPUFrequency()) / 1000000) / 1000
|
||||
}
|
||||
|
||||
prescaler := avr.TCCR1B.Get() & 0x7
|
||||
switch prescaler {
|
||||
case 1:
|
||||
top /= 1
|
||||
case 2:
|
||||
top /= 8
|
||||
case 3:
|
||||
top /= 64
|
||||
case 4:
|
||||
top /= 256
|
||||
case 5:
|
||||
top /= 1024
|
||||
}
|
||||
|
||||
// A top of 0x10000 is at 100% duty cycle. Subtract one because the counter
|
||||
// counts from 0, not 1 (avoiding an off-by-one).
|
||||
top -= 1
|
||||
|
||||
if top > 0xffff {
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
|
||||
// Warning: this change is not atomic!
|
||||
avr.ICR1H.Set(uint8(top >> 8))
|
||||
avr.ICR1L.Set(uint8(top))
|
||||
|
||||
// ... and because of that, set the counter back to zero to avoid most of
|
||||
// the effects of this non-atomicity.
|
||||
avr.TCNT1H.Set(0)
|
||||
avr.TCNT1L.Set(0)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Top returns the current counter top, for use in duty cycle calculation. It
|
||||
// will only change with a call to Configure or SetPeriod, otherwise it is
|
||||
// constant.
|
||||
//
|
||||
// The value returned here is hardware dependent. In general, it's best to treat
|
||||
// it as an opaque value that can be divided by some number and passed to Set
|
||||
// (see Set documentation for more information).
|
||||
func (pwm PWM) Top() uint32 {
|
||||
if pwm.num == 1 {
|
||||
// Timer 1 has a configurable top value.
|
||||
low := avr.ICR1L.Get()
|
||||
high := avr.ICR1H.Get()
|
||||
return uint32(high)<<8 | uint32(low) + 1
|
||||
}
|
||||
// Other timers go from 0 to 0xff (0x100 or 256 in total).
|
||||
return 256
|
||||
}
|
||||
|
||||
// Counter returns the current counter value of the timer in this PWM
|
||||
// peripheral. It may be useful for debugging.
|
||||
func (pwm PWM) Counter() uint32 {
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
return uint32(avr.TCNT0.Get())
|
||||
case 1:
|
||||
mask := interrupt.Disable()
|
||||
low := avr.TCNT1L.Get()
|
||||
high := avr.TCNT1H.Get()
|
||||
interrupt.Restore(mask)
|
||||
return uint32(high)<<8 | uint32(low)
|
||||
case 2:
|
||||
return uint32(avr.TCNT2.Get())
|
||||
}
|
||||
// Unknown PWM.
|
||||
return 0
|
||||
}
|
||||
|
||||
// Period returns the used PWM period in nanoseconds. It might deviate slightly
|
||||
// from the configured period due to rounding.
|
||||
func (pwm PWM) Period() uint64 {
|
||||
var prescaler uint8
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
prescaler = avr.TCCR0B.Get() & 0x7
|
||||
case 1:
|
||||
prescaler = avr.TCCR1B.Get() & 0x7
|
||||
case 2:
|
||||
prescaler = avr.TCCR2B.Get() & 0x7
|
||||
}
|
||||
top := uint64(pwm.Top())
|
||||
switch prescaler {
|
||||
case 1: // prescaler 1
|
||||
return 1 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
case 2: // prescaler 8
|
||||
return 8 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
case 3: // prescaler 64
|
||||
return 64 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
case 4: // prescaler 256
|
||||
return 256 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
case 5: // prescaler 1024
|
||||
return 1024 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
default: // unknown clock source
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Channel returns a PWM channel for the given pin.
|
||||
func (pwm PWM) Channel(pin Pin) (uint8, error) {
|
||||
pin.Configure(PinConfig{Mode: PinOutput})
|
||||
pin.Low()
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
switch pin {
|
||||
case PD6: // channel A
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||
return 0, nil
|
||||
case PD5: // channel B
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||
return 1, nil
|
||||
}
|
||||
case 1:
|
||||
switch pin {
|
||||
case PB1: // channel A
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||
return 0, nil
|
||||
case PB2: // channel B
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||
return 1, nil
|
||||
}
|
||||
case 2:
|
||||
switch pin {
|
||||
case PB3: // channel A
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||
return 0, nil
|
||||
case PD3: // channel B
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||
return 1, nil
|
||||
}
|
||||
}
|
||||
return 0, ErrInvalidOutputPin
|
||||
}
|
||||
|
||||
// SetInverting sets whether to invert the output of this channel.
|
||||
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
|
||||
// the time and low for the rest. Inverting flips the output as if a NOT gate
|
||||
// was placed at the output, meaning that the output would be 25% low and 75%
|
||||
// high with a duty cycle of 25%.
|
||||
//
|
||||
// Note: the invert state may not be applied on the AVR until the next call to
|
||||
// ch.Set().
|
||||
func (pwm PWM) SetInverting(channel uint8, inverting bool) {
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
switch channel {
|
||||
case 0: // channel A
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 6) // PB6 high
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A0)
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 6) // PB6 low
|
||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A0)
|
||||
}
|
||||
case 1: // channel B
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 5) // PB5 high
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B0)
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 5) // PB5 low
|
||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B0)
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
// Note: the COM1A0/COM1B0 bit is not set with the configuration below.
|
||||
// It will be set the following call to Set(), however.
|
||||
switch channel {
|
||||
case 0: // channel A, PB1
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 1) // PB1 high
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 1) // PB1 low
|
||||
}
|
||||
case 1: // channel B, PB2
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 2) // PB2 high
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 2) // PB2 low
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
switch channel {
|
||||
case 0: // channel A
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 3) // PB3 high
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A0)
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 3) // PB3 low
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A0)
|
||||
}
|
||||
case 1: // channel B
|
||||
if inverting {
|
||||
avr.PORTD.SetBits(1 << 3) // PD3 high
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B0)
|
||||
} else {
|
||||
avr.PORTD.ClearBits(1 << 3) // PD3 low
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set updates the channel value. This is used to control the channel duty
|
||||
// cycle, in other words the fraction of time the channel output is high (or low
|
||||
// when inverted). For example, to set it to a 25% duty cycle, use:
|
||||
//
|
||||
// pwm.Set(channel, pwm.Top() / 4)
|
||||
//
|
||||
// pwm.Set(channel, 0) will set the output to low and pwm.Set(channel,
|
||||
// pwm.Top()) will set the output to high, assuming the output isn't inverted.
|
||||
func (pwm PWM) Set(channel uint8, value uint32) {
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
value := uint16(value)
|
||||
switch channel {
|
||||
case 0: // channel A
|
||||
if value == 0 {
|
||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A1)
|
||||
} else {
|
||||
avr.OCR0A.Set(uint8(value - 1))
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||
}
|
||||
case 1: // channel B
|
||||
if value == 0 {
|
||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B1)
|
||||
} else {
|
||||
avr.OCR0B.Set(uint8(value) - 1)
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||
}
|
||||
}
|
||||
// monotonic timer is using the same time as PWM:0
|
||||
// we must adust internal settings of monotonic timer when PWM:0 settings changed
|
||||
adjustMonotonicTimer()
|
||||
case 1:
|
||||
mask := interrupt.Disable()
|
||||
switch channel {
|
||||
case 0: // channel A, PB1
|
||||
if value == 0 {
|
||||
avr.TCCR1A.ClearBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR1AH.Set(uint8(value >> 8))
|
||||
avr.OCR1AL.Set(uint8(value))
|
||||
if avr.PORTB.HasBits(1 << 1) { // is PB1 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||
}
|
||||
}
|
||||
case 1: // channel B, PB2
|
||||
if value == 0 {
|
||||
avr.TCCR1A.ClearBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR1BH.Set(uint8(value >> 8))
|
||||
avr.OCR1BL.Set(uint8(value))
|
||||
if avr.PORTB.HasBits(1 << 2) { // is PB2 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||
}
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
case 2:
|
||||
value := uint16(value)
|
||||
switch channel {
|
||||
case 0: // channel A
|
||||
if value == 0 {
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A1)
|
||||
} else {
|
||||
avr.OCR2A.Set(uint8(value - 1))
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||
}
|
||||
case 1: // channel B
|
||||
if value == 0 {
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B1)
|
||||
} else {
|
||||
avr.OCR2B.Set(uint8(value - 1))
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pin Change Interrupts
|
||||
type PinChange uint8
|
||||
|
||||
const (
|
||||
PinRising PinChange = 1 << iota
|
||||
PinFalling
|
||||
PinToggle = PinRising | PinFalling
|
||||
)
|
||||
|
||||
func (pin Pin) SetInterrupt(pinChange PinChange, callback func(Pin)) (err error) {
|
||||
|
||||
switch {
|
||||
case pin >= PB0 && pin <= PB7:
|
||||
// PCMSK0 - PCINT0-7
|
||||
pinStates[0] = avr.PINB.Get()
|
||||
pinIndex := pin - PB0
|
||||
if pinChange&PinRising > 0 {
|
||||
pinCallbacks[0][pinIndex][0] = callback
|
||||
}
|
||||
if pinChange&PinFalling > 0 {
|
||||
pinCallbacks[0][pinIndex][1] = callback
|
||||
}
|
||||
if callback != nil {
|
||||
avr.PCMSK0.SetBits(1 << pinIndex)
|
||||
} else {
|
||||
avr.PCMSK0.ClearBits(1 << pinIndex)
|
||||
}
|
||||
avr.PCICR.SetBits(avr.PCICR_PCIE0)
|
||||
interrupt.New(avr.IRQ_PCINT0, handlePCINT0Interrupts)
|
||||
case pin >= PC0 && pin <= PC7:
|
||||
// PCMSK1 - PCINT8-14
|
||||
pinStates[1] = avr.PINC.Get()
|
||||
pinIndex := pin - PC0
|
||||
if pinChange&PinRising > 0 {
|
||||
pinCallbacks[1][pinIndex][0] = callback
|
||||
}
|
||||
if pinChange&PinFalling > 0 {
|
||||
pinCallbacks[1][pinIndex][1] = callback
|
||||
}
|
||||
if callback != nil {
|
||||
avr.PCMSK1.SetBits(1 << pinIndex)
|
||||
} else {
|
||||
avr.PCMSK1.ClearBits(1 << pinIndex)
|
||||
}
|
||||
avr.PCICR.SetBits(avr.PCICR_PCIE1)
|
||||
interrupt.New(avr.IRQ_PCINT1, handlePCINT1Interrupts)
|
||||
case pin >= PD0 && pin <= PD7:
|
||||
// PCMSK2 - PCINT16-23
|
||||
pinStates[2] = avr.PIND.Get()
|
||||
pinIndex := pin - PD0
|
||||
if pinChange&PinRising > 0 {
|
||||
pinCallbacks[2][pinIndex][0] = callback
|
||||
}
|
||||
if pinChange&PinFalling > 0 {
|
||||
pinCallbacks[2][pinIndex][1] = callback
|
||||
}
|
||||
if callback != nil {
|
||||
avr.PCMSK2.SetBits(1 << pinIndex)
|
||||
} else {
|
||||
avr.PCMSK2.ClearBits(1 << pinIndex)
|
||||
}
|
||||
avr.PCICR.SetBits(avr.PCICR_PCIE2)
|
||||
interrupt.New(avr.IRQ_PCINT2, handlePCINT2Interrupts)
|
||||
default:
|
||||
return ErrInvalidInputPin
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var pinCallbacks [3][8][2]func(Pin)
|
||||
var pinStates [3]uint8
|
||||
|
||||
func handlePCINTInterrupts(intr uint8, port *volatile.Register8) {
|
||||
current := port.Get()
|
||||
change := pinStates[intr] ^ current
|
||||
pinStates[intr] = current
|
||||
for i := uint8(0); i < 8; i++ {
|
||||
if (change>>i)&0x01 != 0x01 {
|
||||
continue
|
||||
}
|
||||
pin := Pin(intr*8 + i)
|
||||
value := pin.Get()
|
||||
if value && pinCallbacks[intr][i][0] != nil {
|
||||
pinCallbacks[intr][i][0](pin)
|
||||
}
|
||||
if !value && pinCallbacks[intr][i][1] != nil {
|
||||
pinCallbacks[intr][i][1](pin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handlePCINT0Interrupts(intr interrupt.Interrupt) {
|
||||
handlePCINTInterrupts(0, avr.PINB)
|
||||
}
|
||||
|
||||
func handlePCINT1Interrupts(intr interrupt.Interrupt) {
|
||||
handlePCINTInterrupts(1, avr.PINC)
|
||||
}
|
||||
|
||||
func handlePCINT2Interrupts(intr interrupt.Interrupt) {
|
||||
handlePCINTInterrupts(2, avr.PIND)
|
||||
}
|
||||
@@ -4,12 +4,49 @@ package machine
|
||||
|
||||
import (
|
||||
"device/avr"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
const irq_USART0_RX = avr.IRQ_USART_RX
|
||||
|
||||
// I2C0 is the only I2C interface on most AVRs.
|
||||
var I2C0 = &I2C{
|
||||
srReg: avr.TWSR,
|
||||
brReg: avr.TWBR,
|
||||
crReg: avr.TWCR,
|
||||
drReg: avr.TWDR,
|
||||
srPS0: avr.TWSR_TWPS0,
|
||||
srPS1: avr.TWSR_TWPS1,
|
||||
crEN: avr.TWCR_TWEN,
|
||||
crINT: avr.TWCR_TWINT,
|
||||
crSTO: avr.TWCR_TWSTO,
|
||||
crEA: avr.TWCR_TWEA,
|
||||
crSTA: avr.TWCR_TWSTA,
|
||||
}
|
||||
|
||||
// SPI configuration
|
||||
var SPI0 = SPI{
|
||||
spcr: avr.SPCR,
|
||||
spdr: avr.SPDR,
|
||||
spsr: avr.SPSR,
|
||||
|
||||
spcrR0: avr.SPCR_SPR0,
|
||||
spcrR1: avr.SPCR_SPR1,
|
||||
spcrCPHA: avr.SPCR_CPHA,
|
||||
spcrCPOL: avr.SPCR_CPOL,
|
||||
spcrDORD: avr.SPCR_DORD,
|
||||
spcrSPE: avr.SPCR_SPE,
|
||||
spcrMSTR: avr.SPCR_MSTR,
|
||||
|
||||
spsrI2X: avr.SPSR_SPI2X,
|
||||
spsrSPIF: avr.SPSR_SPIF,
|
||||
|
||||
sck: PB5,
|
||||
sdo: PB3,
|
||||
sdi: PB4,
|
||||
cs: PB2,
|
||||
}
|
||||
|
||||
// getPortMask returns the PORTx register and mask for the pin.
|
||||
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||
switch {
|
||||
@@ -21,552 +58,3 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||
return avr.PORTD, 1 << uint8(p-portD)
|
||||
}
|
||||
}
|
||||
|
||||
// PWM is one PWM peripheral, which consists of a counter and two output
|
||||
// channels (that can be connected to two fixed pins). You can set the frequency
|
||||
// using SetPeriod, but only for all the channels in this PWM peripheral at
|
||||
// once.
|
||||
type PWM struct {
|
||||
num uint8
|
||||
}
|
||||
|
||||
var (
|
||||
Timer0 = PWM{0} // 8 bit timer for PD5 and PD6
|
||||
Timer1 = PWM{1} // 16 bit timer for PB1 and PB2
|
||||
Timer2 = PWM{2} // 8 bit timer for PB3 and PD3
|
||||
)
|
||||
|
||||
// Configure enables and configures this PWM.
|
||||
//
|
||||
// For the two 8 bit timers, there is only a limited number of periods
|
||||
// available, namely the CPU frequency divided by 256 and again divided by 1, 8,
|
||||
// 64, 256, or 1024. For a MCU running at 16MHz, this would be a period of 16µs,
|
||||
// 128µs, 1024µs, 4096µs, or 16384µs.
|
||||
func (pwm PWM) Configure(config PWMConfig) error {
|
||||
switch pwm.num {
|
||||
case 0, 2: // 8-bit timers (Timer/counter 0 and Timer/counter 2)
|
||||
// Calculate the timer prescaler.
|
||||
// While we could configure a flexible top, that would sacrifice one of
|
||||
// the PWM output compare registers and thus a PWM channel. I've chosen
|
||||
// to instead limit this timer to a fixed number of frequencies.
|
||||
var prescaler uint8
|
||||
switch config.Period {
|
||||
case 0, (uint64(1e9) * 256 * 1) / uint64(CPUFrequency()):
|
||||
prescaler = 1
|
||||
case (uint64(1e9) * 256 * 8) / uint64(CPUFrequency()):
|
||||
prescaler = 2
|
||||
case (uint64(1e9) * 256 * 64) / uint64(CPUFrequency()):
|
||||
prescaler = 3
|
||||
case (uint64(1e9) * 256 * 256) / uint64(CPUFrequency()):
|
||||
prescaler = 4
|
||||
case (uint64(1e9) * 256 * 1024) / uint64(CPUFrequency()):
|
||||
prescaler = 5
|
||||
default:
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
|
||||
if pwm.num == 0 {
|
||||
avr.TCCR0B.Set(prescaler)
|
||||
// Set the PWM mode to fast PWM (mode = 3).
|
||||
avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01)
|
||||
// monotonic timer is using the same time as PWM:0
|
||||
// we must adust internal settings of monotonic timer when PWM:0 settings changed
|
||||
adjustMonotonicTimer()
|
||||
} else {
|
||||
avr.TCCR2B.Set(prescaler)
|
||||
// Set the PWM mode to fast PWM (mode = 3).
|
||||
avr.TCCR2A.Set(avr.TCCR2A_WGM20 | avr.TCCR2A_WGM21)
|
||||
}
|
||||
case 1: // Timer/counter 1
|
||||
// The top value is the number of PWM ticks a PWM period takes. It is
|
||||
// initially picked assuming an unlimited counter top and no PWM
|
||||
// prescaler.
|
||||
var top uint64
|
||||
if config.Period == 0 {
|
||||
// Use a top appropriate for LEDs. Picking a relatively low period
|
||||
// here (0xff) for consistency with the other timers.
|
||||
top = 0xff
|
||||
} else {
|
||||
// The formula below calculates the following formula, optimized:
|
||||
// top = period * (CPUFrequency() / 1e9)
|
||||
// By dividing the CPU frequency first (an operation that is easily
|
||||
// optimized away) the period has less chance of overflowing.
|
||||
top = config.Period * (uint64(CPUFrequency()) / 1000000) / 1000
|
||||
}
|
||||
|
||||
avr.TCCR1A.Set(avr.TCCR1A_WGM11)
|
||||
|
||||
// The ideal PWM period may be larger than would fit in the PWM counter,
|
||||
// which is 16 bits (see maxTop). Therefore, try to make the PWM clock
|
||||
// speed lower with a prescaler to make the top value fit the maximum
|
||||
// top value.
|
||||
const maxTop = 0x10000
|
||||
switch {
|
||||
case top <= maxTop:
|
||||
avr.TCCR1B.Set(3<<3 | 1) // no prescaling
|
||||
case top/8 <= maxTop:
|
||||
avr.TCCR1B.Set(3<<3 | 2) // divide by 8
|
||||
top /= 8
|
||||
case top/64 <= maxTop:
|
||||
avr.TCCR1B.Set(3<<3 | 3) // divide by 64
|
||||
top /= 64
|
||||
case top/256 <= maxTop:
|
||||
avr.TCCR1B.Set(3<<3 | 4) // divide by 256
|
||||
top /= 256
|
||||
case top/1024 <= maxTop:
|
||||
avr.TCCR1B.Set(3<<3 | 5) // divide by 1024
|
||||
top /= 1024
|
||||
default:
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
|
||||
// A top of 0x10000 is at 100% duty cycle. Subtract one because the
|
||||
// counter counts from 0, not 1 (avoiding an off-by-one).
|
||||
top -= 1
|
||||
|
||||
avr.ICR1H.Set(uint8(top >> 8))
|
||||
avr.ICR1L.Set(uint8(top))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetPeriod updates the period of this PWM peripheral.
|
||||
// To set a particular frequency, use the following formula:
|
||||
//
|
||||
// period = 1e9 / frequency
|
||||
//
|
||||
// If you use a period of 0, a period that works well for LEDs will be picked.
|
||||
//
|
||||
// SetPeriod will not change the prescaler, but also won't change the current
|
||||
// value in any of the channels. This means that you may need to update the
|
||||
// value for the particular channel.
|
||||
//
|
||||
// Note that you cannot pick any arbitrary period after the PWM peripheral has
|
||||
// been configured. If you want to switch between frequencies, pick the lowest
|
||||
// frequency (longest period) once when calling Configure and adjust the
|
||||
// frequency here as needed.
|
||||
func (pwm PWM) SetPeriod(period uint64) error {
|
||||
if pwm.num != 1 {
|
||||
return ErrPWMPeriodTooLong // TODO better error message
|
||||
}
|
||||
|
||||
// The top value is the number of PWM ticks a PWM period takes. It is
|
||||
// initially picked assuming an unlimited counter top and no PWM
|
||||
// prescaler.
|
||||
var top uint64
|
||||
if period == 0 {
|
||||
// Use a top appropriate for LEDs. Picking a relatively low period
|
||||
// here (0xff) for consistency with the other timers.
|
||||
top = 0xff
|
||||
} else {
|
||||
// The formula below calculates the following formula, optimized:
|
||||
// top = period * (CPUFrequency() / 1e9)
|
||||
// By dividing the CPU frequency first (an operation that is easily
|
||||
// optimized away) the period has less chance of overflowing.
|
||||
top = period * (uint64(CPUFrequency()) / 1000000) / 1000
|
||||
}
|
||||
|
||||
prescaler := avr.TCCR1B.Get() & 0x7
|
||||
switch prescaler {
|
||||
case 1:
|
||||
top /= 1
|
||||
case 2:
|
||||
top /= 8
|
||||
case 3:
|
||||
top /= 64
|
||||
case 4:
|
||||
top /= 256
|
||||
case 5:
|
||||
top /= 1024
|
||||
}
|
||||
|
||||
// A top of 0x10000 is at 100% duty cycle. Subtract one because the counter
|
||||
// counts from 0, not 1 (avoiding an off-by-one).
|
||||
top -= 1
|
||||
|
||||
if top > 0xffff {
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
|
||||
// Warning: this change is not atomic!
|
||||
avr.ICR1H.Set(uint8(top >> 8))
|
||||
avr.ICR1L.Set(uint8(top))
|
||||
|
||||
// ... and because of that, set the counter back to zero to avoid most of
|
||||
// the effects of this non-atomicity.
|
||||
avr.TCNT1H.Set(0)
|
||||
avr.TCNT1L.Set(0)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Top returns the current counter top, for use in duty cycle calculation. It
|
||||
// will only change with a call to Configure or SetPeriod, otherwise it is
|
||||
// constant.
|
||||
//
|
||||
// The value returned here is hardware dependent. In general, it's best to treat
|
||||
// it as an opaque value that can be divided by some number and passed to Set
|
||||
// (see Set documentation for more information).
|
||||
func (pwm PWM) Top() uint32 {
|
||||
if pwm.num == 1 {
|
||||
// Timer 1 has a configurable top value.
|
||||
low := avr.ICR1L.Get()
|
||||
high := avr.ICR1H.Get()
|
||||
return uint32(high)<<8 | uint32(low) + 1
|
||||
}
|
||||
// Other timers go from 0 to 0xff (0x100 or 256 in total).
|
||||
return 256
|
||||
}
|
||||
|
||||
// Counter returns the current counter value of the timer in this PWM
|
||||
// peripheral. It may be useful for debugging.
|
||||
func (pwm PWM) Counter() uint32 {
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
return uint32(avr.TCNT0.Get())
|
||||
case 1:
|
||||
mask := interrupt.Disable()
|
||||
low := avr.TCNT1L.Get()
|
||||
high := avr.TCNT1H.Get()
|
||||
interrupt.Restore(mask)
|
||||
return uint32(high)<<8 | uint32(low)
|
||||
case 2:
|
||||
return uint32(avr.TCNT2.Get())
|
||||
}
|
||||
// Unknown PWM.
|
||||
return 0
|
||||
}
|
||||
|
||||
// Period returns the used PWM period in nanoseconds. It might deviate slightly
|
||||
// from the configured period due to rounding.
|
||||
func (pwm PWM) Period() uint64 {
|
||||
var prescaler uint8
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
prescaler = avr.TCCR0B.Get() & 0x7
|
||||
case 1:
|
||||
prescaler = avr.TCCR1B.Get() & 0x7
|
||||
case 2:
|
||||
prescaler = avr.TCCR2B.Get() & 0x7
|
||||
}
|
||||
top := uint64(pwm.Top())
|
||||
switch prescaler {
|
||||
case 1: // prescaler 1
|
||||
return 1 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
case 2: // prescaler 8
|
||||
return 8 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
case 3: // prescaler 64
|
||||
return 64 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
case 4: // prescaler 256
|
||||
return 256 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
case 5: // prescaler 1024
|
||||
return 1024 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
default: // unknown clock source
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Channel returns a PWM channel for the given pin.
|
||||
func (pwm PWM) Channel(pin Pin) (uint8, error) {
|
||||
pin.Configure(PinConfig{Mode: PinOutput})
|
||||
pin.Low()
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
switch pin {
|
||||
case PD6: // channel A
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||
return 0, nil
|
||||
case PD5: // channel B
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||
return 1, nil
|
||||
}
|
||||
case 1:
|
||||
switch pin {
|
||||
case PB1: // channel A
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||
return 0, nil
|
||||
case PB2: // channel B
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||
return 1, nil
|
||||
}
|
||||
case 2:
|
||||
switch pin {
|
||||
case PB3: // channel A
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||
return 0, nil
|
||||
case PD3: // channel B
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||
return 1, nil
|
||||
}
|
||||
}
|
||||
return 0, ErrInvalidOutputPin
|
||||
}
|
||||
|
||||
// SetInverting sets whether to invert the output of this channel.
|
||||
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
|
||||
// the time and low for the rest. Inverting flips the output as if a NOT gate
|
||||
// was placed at the output, meaning that the output would be 25% low and 75%
|
||||
// high with a duty cycle of 25%.
|
||||
//
|
||||
// Note: the invert state may not be applied on the AVR until the next call to
|
||||
// ch.Set().
|
||||
func (pwm PWM) SetInverting(channel uint8, inverting bool) {
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
switch channel {
|
||||
case 0: // channel A
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 6) // PB6 high
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A0)
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 6) // PB6 low
|
||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A0)
|
||||
}
|
||||
case 1: // channel B
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 5) // PB5 high
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B0)
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 5) // PB5 low
|
||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B0)
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
// Note: the COM1A0/COM1B0 bit is not set with the configuration below.
|
||||
// It will be set the following call to Set(), however.
|
||||
switch channel {
|
||||
case 0: // channel A, PB1
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 1) // PB1 high
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 1) // PB1 low
|
||||
}
|
||||
case 1: // channel B, PB2
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 2) // PB2 high
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 2) // PB2 low
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
switch channel {
|
||||
case 0: // channel A
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 3) // PB3 high
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A0)
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 3) // PB3 low
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A0)
|
||||
}
|
||||
case 1: // channel B
|
||||
if inverting {
|
||||
avr.PORTD.SetBits(1 << 3) // PD3 high
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B0)
|
||||
} else {
|
||||
avr.PORTD.ClearBits(1 << 3) // PD3 low
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set updates the channel value. This is used to control the channel duty
|
||||
// cycle, in other words the fraction of time the channel output is high (or low
|
||||
// when inverted). For example, to set it to a 25% duty cycle, use:
|
||||
//
|
||||
// pwm.Set(channel, pwm.Top() / 4)
|
||||
//
|
||||
// pwm.Set(channel, 0) will set the output to low and pwm.Set(channel,
|
||||
// pwm.Top()) will set the output to high, assuming the output isn't inverted.
|
||||
func (pwm PWM) Set(channel uint8, value uint32) {
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
value := uint16(value)
|
||||
switch channel {
|
||||
case 0: // channel A
|
||||
if value == 0 {
|
||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A1)
|
||||
} else {
|
||||
avr.OCR0A.Set(uint8(value - 1))
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||
}
|
||||
case 1: // channel B
|
||||
if value == 0 {
|
||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B1)
|
||||
} else {
|
||||
avr.OCR0B.Set(uint8(value) - 1)
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||
}
|
||||
}
|
||||
// monotonic timer is using the same time as PWM:0
|
||||
// we must adust internal settings of monotonic timer when PWM:0 settings changed
|
||||
adjustMonotonicTimer()
|
||||
case 1:
|
||||
mask := interrupt.Disable()
|
||||
switch channel {
|
||||
case 0: // channel A, PB1
|
||||
if value == 0 {
|
||||
avr.TCCR1A.ClearBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR1AH.Set(uint8(value >> 8))
|
||||
avr.OCR1AL.Set(uint8(value))
|
||||
if avr.PORTB.HasBits(1 << 1) { // is PB1 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||
}
|
||||
}
|
||||
case 1: // channel B, PB2
|
||||
if value == 0 {
|
||||
avr.TCCR1A.ClearBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR1BH.Set(uint8(value >> 8))
|
||||
avr.OCR1BL.Set(uint8(value))
|
||||
if avr.PORTB.HasBits(1 << 2) { // is PB2 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||
}
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
case 2:
|
||||
value := uint16(value)
|
||||
switch channel {
|
||||
case 0: // channel A
|
||||
if value == 0 {
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A1)
|
||||
} else {
|
||||
avr.OCR2A.Set(uint8(value - 1))
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||
}
|
||||
case 1: // channel B
|
||||
if value == 0 {
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B1)
|
||||
} else {
|
||||
avr.OCR2B.Set(uint8(value - 1))
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SPI configuration
|
||||
var SPI0 = SPI{
|
||||
spcr: avr.SPCR,
|
||||
spdr: avr.SPDR,
|
||||
spsr: avr.SPSR,
|
||||
sck: PB5,
|
||||
sdo: PB3,
|
||||
sdi: PB4,
|
||||
cs: PB2}
|
||||
|
||||
// Pin Change Interrupts
|
||||
type PinChange uint8
|
||||
|
||||
const (
|
||||
PinRising PinChange = 1 << iota
|
||||
PinFalling
|
||||
PinToggle = PinRising | PinFalling
|
||||
)
|
||||
|
||||
func (pin Pin) SetInterrupt(pinChange PinChange, callback func(Pin)) (err error) {
|
||||
|
||||
switch {
|
||||
case pin >= PB0 && pin <= PB7:
|
||||
// PCMSK0 - PCINT0-7
|
||||
pinStates[0] = avr.PINB.Get()
|
||||
pinIndex := pin - PB0
|
||||
if pinChange&PinRising > 0 {
|
||||
pinCallbacks[0][pinIndex][0] = callback
|
||||
}
|
||||
if pinChange&PinFalling > 0 {
|
||||
pinCallbacks[0][pinIndex][1] = callback
|
||||
}
|
||||
if callback != nil {
|
||||
avr.PCMSK0.SetBits(1 << pinIndex)
|
||||
} else {
|
||||
avr.PCMSK0.ClearBits(1 << pinIndex)
|
||||
}
|
||||
avr.PCICR.SetBits(avr.PCICR_PCIE0)
|
||||
interrupt.New(avr.IRQ_PCINT0, handlePCINT0Interrupts)
|
||||
case pin >= PC0 && pin <= PC7:
|
||||
// PCMSK1 - PCINT8-14
|
||||
pinStates[1] = avr.PINC.Get()
|
||||
pinIndex := pin - PC0
|
||||
if pinChange&PinRising > 0 {
|
||||
pinCallbacks[1][pinIndex][0] = callback
|
||||
}
|
||||
if pinChange&PinFalling > 0 {
|
||||
pinCallbacks[1][pinIndex][1] = callback
|
||||
}
|
||||
if callback != nil {
|
||||
avr.PCMSK1.SetBits(1 << pinIndex)
|
||||
} else {
|
||||
avr.PCMSK1.ClearBits(1 << pinIndex)
|
||||
}
|
||||
avr.PCICR.SetBits(avr.PCICR_PCIE1)
|
||||
interrupt.New(avr.IRQ_PCINT1, handlePCINT1Interrupts)
|
||||
case pin >= PD0 && pin <= PD7:
|
||||
// PCMSK2 - PCINT16-23
|
||||
pinStates[2] = avr.PIND.Get()
|
||||
pinIndex := pin - PD0
|
||||
if pinChange&PinRising > 0 {
|
||||
pinCallbacks[2][pinIndex][0] = callback
|
||||
}
|
||||
if pinChange&PinFalling > 0 {
|
||||
pinCallbacks[2][pinIndex][1] = callback
|
||||
}
|
||||
if callback != nil {
|
||||
avr.PCMSK2.SetBits(1 << pinIndex)
|
||||
} else {
|
||||
avr.PCMSK2.ClearBits(1 << pinIndex)
|
||||
}
|
||||
avr.PCICR.SetBits(avr.PCICR_PCIE2)
|
||||
interrupt.New(avr.IRQ_PCINT2, handlePCINT2Interrupts)
|
||||
default:
|
||||
return ErrInvalidInputPin
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var pinCallbacks [3][8][2]func(Pin)
|
||||
var pinStates [3]uint8
|
||||
|
||||
func handlePCINTInterrupts(intr uint8, port *volatile.Register8) {
|
||||
current := port.Get()
|
||||
change := pinStates[intr] ^ current
|
||||
pinStates[intr] = current
|
||||
for i := uint8(0); i < 8; i++ {
|
||||
if (change>>i)&0x01 != 0x01 {
|
||||
continue
|
||||
}
|
||||
pin := Pin(intr*8 + i)
|
||||
value := pin.Get()
|
||||
if value && pinCallbacks[intr][i][0] != nil {
|
||||
pinCallbacks[intr][i][0](pin)
|
||||
}
|
||||
if !value && pinCallbacks[intr][i][1] != nil {
|
||||
pinCallbacks[intr][i][1](pin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handlePCINT0Interrupts(intr interrupt.Interrupt) {
|
||||
handlePCINTInterrupts(0, avr.PINB)
|
||||
}
|
||||
|
||||
func handlePCINT1Interrupts(intr interrupt.Interrupt) {
|
||||
handlePCINTInterrupts(1, avr.PINC)
|
||||
}
|
||||
|
||||
func handlePCINT2Interrupts(intr interrupt.Interrupt) {
|
||||
handlePCINTInterrupts(2, avr.PIND)
|
||||
}
|
||||
|
||||
@@ -4,10 +4,105 @@ package machine
|
||||
|
||||
import (
|
||||
"device/avr"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
const irq_USART0_RX = avr.IRQ_USART0_RX
|
||||
const irq_USART1_RX = avr.IRQ_USART1_RX
|
||||
|
||||
var (
|
||||
UART1 = &_UART1
|
||||
_UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
|
||||
dataReg: avr.UDR1,
|
||||
baudRegH: avr.UBRR1H,
|
||||
baudRegL: avr.UBRR1L,
|
||||
statusRegA: avr.UCSR1A,
|
||||
statusRegB: avr.UCSR1B,
|
||||
statusRegC: avr.UCSR1C,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Register the UART interrupt.
|
||||
interrupt.New(irq_USART1_RX, _UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C0 is the only I2C interface on most AVRs.
|
||||
var I2C0 = &I2C{
|
||||
srReg: avr.TWSR0,
|
||||
brReg: avr.TWBR0,
|
||||
crReg: avr.TWCR0,
|
||||
drReg: avr.TWDR0,
|
||||
srPS0: avr.TWSR0_TWPS0,
|
||||
srPS1: avr.TWSR0_TWPS1,
|
||||
crEN: avr.TWCR0_TWEN,
|
||||
crINT: avr.TWCR0_TWINT,
|
||||
crSTO: avr.TWCR0_TWSTO,
|
||||
crEA: avr.TWCR0_TWEA,
|
||||
crSTA: avr.TWCR0_TWSTA,
|
||||
}
|
||||
|
||||
var I2C1 = &I2C{
|
||||
srReg: avr.TWSR1,
|
||||
brReg: avr.TWBR1,
|
||||
crReg: avr.TWCR1,
|
||||
drReg: avr.TWDR1,
|
||||
srPS0: avr.TWSR1_TWPS10,
|
||||
srPS1: avr.TWSR1_TWPS11,
|
||||
crEN: avr.TWCR1_TWEN1,
|
||||
crINT: avr.TWCR1_TWINT1,
|
||||
crSTO: avr.TWCR1_TWSTO1,
|
||||
crEA: avr.TWCR1_TWEA1,
|
||||
crSTA: avr.TWCR1_TWSTA1,
|
||||
}
|
||||
|
||||
// SPI configuration
|
||||
var SPI0 = SPI{
|
||||
spcr: avr.SPCR0,
|
||||
spdr: avr.SPDR0,
|
||||
spsr: avr.SPSR0,
|
||||
|
||||
spcrR0: avr.SPCR0_SPR0,
|
||||
spcrR1: avr.SPCR0_SPR1,
|
||||
spcrCPHA: avr.SPCR0_CPHA,
|
||||
spcrCPOL: avr.SPCR0_CPOL,
|
||||
spcrDORD: avr.SPCR0_DORD,
|
||||
spcrSPE: avr.SPCR0_SPE,
|
||||
spcrMSTR: avr.SPCR0_MSTR,
|
||||
|
||||
spsrI2X: avr.SPSR0_SPI2X,
|
||||
spsrSPIF: avr.SPSR0_SPIF,
|
||||
|
||||
sck: PB5,
|
||||
sdo: PB3,
|
||||
sdi: PB4,
|
||||
cs: PB2,
|
||||
}
|
||||
|
||||
var SPI1 = SPI{
|
||||
spcr: avr.SPCR1,
|
||||
spdr: avr.SPDR1,
|
||||
spsr: avr.SPSR1,
|
||||
|
||||
spcrR0: avr.SPCR1_SPR10,
|
||||
spcrR1: avr.SPCR1_SPR11,
|
||||
spcrCPHA: avr.SPCR1_CPHA1,
|
||||
spcrCPOL: avr.SPCR1_CPOL1,
|
||||
spcrDORD: avr.SPCR1_DORD1,
|
||||
spcrSPE: avr.SPCR1_SPE1,
|
||||
spcrMSTR: avr.SPCR1_MSTR1,
|
||||
|
||||
spsrI2X: avr.SPSR1_SPI2X1,
|
||||
spsrSPIF: avr.SPSR1_SPIF1,
|
||||
|
||||
sck: PC1,
|
||||
sdo: PE3,
|
||||
sdi: PC0,
|
||||
cs: PE2,
|
||||
}
|
||||
|
||||
// getPortMask returns the PORTx register and mask for the pin.
|
||||
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||
@@ -16,94 +111,9 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||
return avr.PORTB, 1 << uint8(p-portB)
|
||||
case p >= PC0 && p <= PC7: // port C
|
||||
return avr.PORTC, 1 << uint8(p-portC)
|
||||
default: // port D
|
||||
case p >= PD0 && p <= PD7: // port D
|
||||
return avr.PORTD, 1 << uint8(p-portD)
|
||||
default: // port E
|
||||
return avr.PORTE, 1 << uint8(p-portE)
|
||||
}
|
||||
}
|
||||
|
||||
// InitPWM initializes the registers needed for PWM.
|
||||
func InitPWM() {
|
||||
// use waveform generation
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_WGM00)
|
||||
|
||||
// set timer 0 prescale factor to 64
|
||||
avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00)
|
||||
|
||||
// set timer 1 prescale factor to 64
|
||||
avr.TCCR1B.SetBits(avr.TCCR1B_CS11)
|
||||
|
||||
// put timer 1 in 8-bit phase correct pwm mode
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_WGM10)
|
||||
|
||||
// set timer 2 prescale factor to 64
|
||||
avr.TCCR2B.SetBits(avr.TCCR2B_CS22)
|
||||
|
||||
// configure timer 2 for phase correct pwm (8-bit)
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_WGM20)
|
||||
}
|
||||
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() error {
|
||||
switch pwm.Pin / 8 {
|
||||
case 0: // port B
|
||||
avr.DDRB.SetBits(1 << uint8(pwm.Pin))
|
||||
case 2: // port D
|
||||
avr.DDRD.SetBits(1 << uint8(pwm.Pin-16))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
|
||||
// 8-bit value ranging from 0 to 255.
|
||||
func (pwm PWM) Set(value uint16) {
|
||||
value8 := uint8(value >> 8)
|
||||
switch pwm.Pin {
|
||||
case PD3:
|
||||
// connect pwm to pin on timer 2, channel B
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||
avr.OCR2B.Set(value8) // set pwm duty
|
||||
case PD5:
|
||||
// connect pwm to pin on timer 0, channel B
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||
avr.OCR0B.Set(value8) // set pwm duty
|
||||
case PD6:
|
||||
// connect pwm to pin on timer 0, channel A
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||
avr.OCR0A.Set(value8) // set pwm duty
|
||||
case PB1:
|
||||
// connect pwm to pin on timer 1, channel A
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||
avr.OCR1AL.Set(value8) // set pwm duty
|
||||
case PB2:
|
||||
// connect pwm to pin on timer 1, channel B
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||
avr.OCR1BL.Set(value8) // set pwm duty
|
||||
case PB3:
|
||||
// connect pwm to pin on timer 2, channel A
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||
avr.OCR2A.Set(value8) // set pwm duty
|
||||
default:
|
||||
panic("Invalid PWM pin")
|
||||
}
|
||||
}
|
||||
|
||||
// SPI configuration
|
||||
var SPI0 = SPI{
|
||||
spcr: avr.SPCR0,
|
||||
spdr: avr.SPDR0,
|
||||
spsr: avr.SPSR0,
|
||||
sck: PB5,
|
||||
sdo: PB3,
|
||||
sdi: PB4,
|
||||
cs: PB2}
|
||||
|
||||
var SPI1 = SPI{
|
||||
spcr: avr.SPCR1,
|
||||
spdr: avr.SPDR1,
|
||||
spsr: avr.SPSR1,
|
||||
sck: PC1,
|
||||
sdo: PE3,
|
||||
sdi: PC0,
|
||||
cs: PE2}
|
||||
|
||||
@@ -534,16 +534,16 @@ func (uart *UART) Configure(config UARTConfig) error {
|
||||
if !ok {
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
var txPinOut uint32
|
||||
var txPadOut uint32
|
||||
// See table 25-9 of the datasheet (page 459) for how pads are mapped to
|
||||
// pinout values.
|
||||
switch txPad {
|
||||
case 0:
|
||||
txPinOut = 0
|
||||
txPadOut = 0
|
||||
case 2:
|
||||
txPinOut = 1
|
||||
txPadOut = 1
|
||||
default:
|
||||
// TODO: flow control (RTS/CTS)
|
||||
// this should be a flow control (RTS/CTS) pin
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
|
||||
@@ -554,12 +554,35 @@ func (uart *UART) Configure(config UARTConfig) error {
|
||||
}
|
||||
// As you can see in table 25-8 on page 459 of the datasheet, input pins
|
||||
// are mapped directly.
|
||||
rxPinOut := rxPad
|
||||
rxPadOut := rxPad
|
||||
|
||||
// configure pins
|
||||
config.TX.Configure(PinConfig{Mode: txPinMode})
|
||||
config.RX.Configure(PinConfig{Mode: rxPinMode})
|
||||
|
||||
// configure RTS/CTS pins if provided
|
||||
if config.RTS != 0 && config.CTS != 0 {
|
||||
rtsPinMode, _, ok := findPinPadMapping(uart.SERCOM, config.RTS)
|
||||
if !ok {
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
|
||||
ctsPinMode, _, ok := findPinPadMapping(uart.SERCOM, config.CTS)
|
||||
if !ok {
|
||||
return ErrInvalidInputPin
|
||||
}
|
||||
|
||||
// See table 25-9 of the datasheet (page 459) for how pads are mapped to
|
||||
// pinout values.
|
||||
if txPadOut == 1 {
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
txPadOut = 2
|
||||
|
||||
config.RTS.Configure(PinConfig{Mode: rtsPinMode})
|
||||
config.CTS.Configure(PinConfig{Mode: ctsPinMode})
|
||||
}
|
||||
|
||||
// reset SERCOM0
|
||||
uart.Bus.CTRLA.SetBits(sam.SERCOM_USART_CTRLA_SWRST)
|
||||
for uart.Bus.CTRLA.HasBits(sam.SERCOM_USART_CTRLA_SWRST) ||
|
||||
@@ -592,8 +615,8 @@ func (uart *UART) Configure(config UARTConfig) error {
|
||||
// set UART pads. This is not same as pins...
|
||||
// SERCOM_USART_CTRLA_TXPO(txPad) |
|
||||
// SERCOM_USART_CTRLA_RXPO(rxPad);
|
||||
uart.Bus.CTRLA.SetBits((txPinOut << sam.SERCOM_USART_CTRLA_TXPO_Pos) |
|
||||
(rxPinOut << sam.SERCOM_USART_CTRLA_RXPO_Pos))
|
||||
uart.Bus.CTRLA.SetBits((txPadOut << sam.SERCOM_USART_CTRLA_TXPO_Pos) |
|
||||
(rxPadOut << sam.SERCOM_USART_CTRLA_RXPO_Pos))
|
||||
|
||||
// Enable Transceiver and Receiver
|
||||
//sercom->USART.CTRLB.reg |= SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_RXEN ;
|
||||
|
||||
@@ -1015,14 +1015,14 @@ func (uart *UART) Configure(config UARTConfig) error {
|
||||
if !ok {
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
var txPinOut uint32
|
||||
var txPadOut uint32
|
||||
// See CTRLA.RXPO bits of the SERCOM USART peripheral (page 945-946) for how
|
||||
// pads are mapped to pinout values.
|
||||
switch txPad {
|
||||
case 0:
|
||||
txPinOut = 0
|
||||
txPadOut = 0
|
||||
default:
|
||||
// TODO: flow control (RTS/CTS)
|
||||
// should be flow control (RTS/CTS) pin
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
|
||||
@@ -1033,12 +1033,32 @@ func (uart *UART) Configure(config UARTConfig) error {
|
||||
}
|
||||
// As you can see in the CTRLA.RXPO bits of the SERCOM USART peripheral
|
||||
// (page 945), input pins are mapped directly.
|
||||
rxPinOut := rxPad
|
||||
rxPadOut := rxPad
|
||||
|
||||
// configure pins
|
||||
config.TX.Configure(PinConfig{Mode: txPinMode})
|
||||
config.RX.Configure(PinConfig{Mode: rxPinMode})
|
||||
|
||||
// configure RTS/CTS pins if provided
|
||||
if config.RTS != 0 && config.CTS != 0 {
|
||||
rtsPinMode, _, ok := findPinPadMapping(uart.SERCOM, config.RTS)
|
||||
if !ok {
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
|
||||
ctsPinMode, _, ok := findPinPadMapping(uart.SERCOM, config.CTS)
|
||||
if !ok {
|
||||
return ErrInvalidInputPin
|
||||
}
|
||||
|
||||
// See CTRLA.RXPO bits of the SERCOM USART peripheral (page 945-946) for how
|
||||
// pads are mapped to pinout values.
|
||||
txPadOut = 2
|
||||
|
||||
config.RTS.Configure(PinConfig{Mode: rtsPinMode})
|
||||
config.CTS.Configure(PinConfig{Mode: ctsPinMode})
|
||||
}
|
||||
|
||||
// reset SERCOM
|
||||
uart.Bus.CTRLA.SetBits(sam.SERCOM_USART_INT_CTRLA_SWRST)
|
||||
for uart.Bus.CTRLA.HasBits(sam.SERCOM_USART_INT_CTRLA_SWRST) ||
|
||||
@@ -1075,8 +1095,8 @@ func (uart *UART) Configure(config UARTConfig) error {
|
||||
// set UART pads. This is not same as pins...
|
||||
// SERCOM_USART_CTRLA_TXPO(txPad) |
|
||||
// SERCOM_USART_CTRLA_RXPO(rxPad);
|
||||
uart.Bus.CTRLA.SetBits((txPinOut << sam.SERCOM_USART_INT_CTRLA_TXPO_Pos) |
|
||||
(rxPinOut << sam.SERCOM_USART_INT_CTRLA_RXPO_Pos))
|
||||
uart.Bus.CTRLA.SetBits((txPadOut << sam.SERCOM_USART_INT_CTRLA_TXPO_Pos) |
|
||||
(rxPadOut << sam.SERCOM_USART_INT_CTRLA_RXPO_Pos))
|
||||
|
||||
// Enable Transceiver and Receiver
|
||||
//sercom->USART.CTRLB.reg |= SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_RXEN ;
|
||||
|
||||
@@ -237,13 +237,13 @@ func (p Pin) mux() *volatile.Register32 {
|
||||
case 27:
|
||||
return &esp.IO_MUX.GPIO27
|
||||
case 14:
|
||||
return &esp.IO_MUX.MTMS
|
||||
return &esp.IO_MUX.GPIO14
|
||||
case 12:
|
||||
return &esp.IO_MUX.MTDI
|
||||
return &esp.IO_MUX.GPIO12
|
||||
case 13:
|
||||
return &esp.IO_MUX.MTCK
|
||||
return &esp.IO_MUX.GPIO13
|
||||
case 15:
|
||||
return &esp.IO_MUX.MTDO
|
||||
return &esp.IO_MUX.GPIO15
|
||||
case 2:
|
||||
return &esp.IO_MUX.GPIO2
|
||||
case 0:
|
||||
@@ -255,17 +255,17 @@ func (p Pin) mux() *volatile.Register32 {
|
||||
case 17:
|
||||
return &esp.IO_MUX.GPIO17
|
||||
case 9:
|
||||
return &esp.IO_MUX.SD_DATA2
|
||||
return &esp.IO_MUX.GPIO9
|
||||
case 10:
|
||||
return &esp.IO_MUX.SD_DATA3
|
||||
return &esp.IO_MUX.GPIO10
|
||||
case 11:
|
||||
return &esp.IO_MUX.SD_CMD
|
||||
return &esp.IO_MUX.GPIO11
|
||||
case 6:
|
||||
return &esp.IO_MUX.SD_CLK
|
||||
return &esp.IO_MUX.GPIO6
|
||||
case 7:
|
||||
return &esp.IO_MUX.SD_DATA0
|
||||
return &esp.IO_MUX.GPIO7
|
||||
case 8:
|
||||
return &esp.IO_MUX.SD_DATA1
|
||||
return &esp.IO_MUX.GPIO8
|
||||
case 5:
|
||||
return &esp.IO_MUX.GPIO5
|
||||
case 18:
|
||||
@@ -279,9 +279,9 @@ func (p Pin) mux() *volatile.Register32 {
|
||||
case 22:
|
||||
return &esp.IO_MUX.GPIO22
|
||||
case 3:
|
||||
return &esp.IO_MUX.U0RXD
|
||||
return &esp.IO_MUX.GPIO3
|
||||
case 1:
|
||||
return &esp.IO_MUX.U0TXD
|
||||
return &esp.IO_MUX.GPIO1
|
||||
case 23:
|
||||
return &esp.IO_MUX.GPIO23
|
||||
case 24:
|
||||
@@ -320,7 +320,8 @@ func (uart *UART) writeByte(b byte) error {
|
||||
// many bytes there are in the transmit buffer. Wait until there are
|
||||
// less than 128 bytes in this buffer (the default buffer size).
|
||||
}
|
||||
uart.Bus.TX_FIFO.Set(b)
|
||||
// Write to the TX_FIFO register.
|
||||
(*volatile.Register8)(unsafe.Add(unsafe.Pointer(uart.Bus), 0x200C0000)).Set(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,10 @@ func (p Pin) outFunc() *volatile.Register32 {
|
||||
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.GPIO.FUNC0_OUT_SEL_CFG), uintptr(p)*4))
|
||||
}
|
||||
|
||||
func (p Pin) pinReg() *volatile.Register32 {
|
||||
return (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.GPIO.PIN0)) + uintptr(p)*4)))
|
||||
}
|
||||
|
||||
// inFunc returns the FUNCy_IN_SEL_CFG register used for configuring the input
|
||||
// function selection.
|
||||
func inFunc(signal uint32) *volatile.Register32 {
|
||||
@@ -188,7 +192,7 @@ func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) (err error) {
|
||||
|
||||
if callback == nil {
|
||||
// Disable this pin interrupt
|
||||
p.pin().ClearBits(esp.GPIO_PIN_PIN_INT_TYPE_Msk | esp.GPIO_PIN_PIN_INT_ENA_Msk)
|
||||
p.pin().ClearBits(esp.GPIO_PIN_INT_TYPE_Msk | esp.GPIO_PIN_INT_ENA_Msk)
|
||||
|
||||
if pinCallbacks[p] != nil {
|
||||
pinCallbacks[p] = nil
|
||||
@@ -212,8 +216,8 @@ func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) (err error) {
|
||||
}
|
||||
|
||||
p.pin().Set(
|
||||
(p.pin().Get() & ^uint32(esp.GPIO_PIN_PIN_INT_TYPE_Msk|esp.GPIO_PIN_PIN_INT_ENA_Msk)) |
|
||||
uint32(change)<<esp.GPIO_PIN_PIN_INT_TYPE_Pos | uint32(1)<<esp.GPIO_PIN_PIN_INT_ENA_Pos)
|
||||
(p.pin().Get() & ^uint32(esp.GPIO_PIN_INT_TYPE_Msk|esp.GPIO_PIN_INT_ENA_Msk)) |
|
||||
uint32(change)<<esp.GPIO_PIN_INT_TYPE_Pos | uint32(1)<<esp.GPIO_PIN_INT_ENA_Pos)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -391,7 +395,7 @@ func initUARTClock(bus *esp.UART_Type, regs registerSet) {
|
||||
// synchronize core register
|
||||
bus.SetID_REG_UPDATE(0)
|
||||
// enable RTC clock
|
||||
esp.RTC_CNTL.SetRTC_CLK_CONF_DIG_CLK8M_EN(1)
|
||||
esp.RTC_CNTL.SetCLK_CONF_DIG_CLK8M_EN(1)
|
||||
// wait for Core Clock to ready for configuration
|
||||
for bus.GetID_REG_UPDATE() > 0 {
|
||||
riscv.Asm("nop")
|
||||
@@ -415,7 +419,7 @@ func (uart *UART) setupPins(config UARTConfig, regs registerSet) {
|
||||
// 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))
|
||||
inFunc(regs.gpioMatrixSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.RX))
|
||||
}
|
||||
|
||||
func (uart *UART) configureInterrupt(intrMapReg *volatile.Register32) { // Disable all UART interrupts
|
||||
@@ -504,3 +508,130 @@ func (uart *UART) writeByte(b byte) error {
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {}
|
||||
|
||||
type Serialer interface {
|
||||
WriteByte(c byte) error
|
||||
Write(data []byte) (n int, err error)
|
||||
Configure(config UARTConfig) error
|
||||
Buffered() int
|
||||
ReadByte() (byte, error)
|
||||
DTR() bool
|
||||
RTS() bool
|
||||
}
|
||||
|
||||
// USB Serial/JTAG Controller
|
||||
// See esp32-c3_technical_reference_manual_en.pdf
|
||||
// pg. 736
|
||||
type USB_DEVICE struct {
|
||||
Bus *esp.USB_DEVICE_Type
|
||||
}
|
||||
|
||||
var (
|
||||
_USBCDC = &USB_DEVICE{
|
||||
Bus: esp.USB_DEVICE,
|
||||
}
|
||||
|
||||
USBCDC Serialer = _USBCDC
|
||||
)
|
||||
|
||||
var (
|
||||
errUSBWrongSize = errors.New("USB: invalid write size")
|
||||
errUSBCouldNotWriteAllData = errors.New("USB: could not write all data")
|
||||
errUSBBufferEmpty = errors.New("USB: read buffer empty")
|
||||
)
|
||||
|
||||
func (usbdev *USB_DEVICE) Configure(config UARTConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (usbdev *USB_DEVICE) WriteByte(c byte) error {
|
||||
if usbdev.Bus.GetEP1_CONF_SERIAL_IN_EP_DATA_FREE() == 0 {
|
||||
return errUSBCouldNotWriteAllData
|
||||
}
|
||||
|
||||
usbdev.Bus.SetEP1_RDWR_BYTE(uint32(c))
|
||||
usbdev.flush()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (usbdev *USB_DEVICE) Write(data []byte) (n int, err error) {
|
||||
if len(data) == 0 || len(data) > 64 {
|
||||
return 0, errUSBWrongSize
|
||||
}
|
||||
|
||||
for i, c := range data {
|
||||
if usbdev.Bus.GetEP1_CONF_SERIAL_IN_EP_DATA_FREE() == 0 {
|
||||
if i > 0 {
|
||||
usbdev.flush()
|
||||
}
|
||||
|
||||
return i, errUSBCouldNotWriteAllData
|
||||
}
|
||||
usbdev.Bus.SetEP1_RDWR_BYTE(uint32(c))
|
||||
}
|
||||
|
||||
usbdev.flush()
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
func (usbdev *USB_DEVICE) Buffered() int {
|
||||
return int(usbdev.Bus.GetEP1_CONF_SERIAL_OUT_EP_DATA_AVAIL())
|
||||
}
|
||||
|
||||
func (usbdev *USB_DEVICE) ReadByte() (byte, error) {
|
||||
if usbdev.Bus.GetEP1_CONF_SERIAL_OUT_EP_DATA_AVAIL() != 0 {
|
||||
return byte(usbdev.Bus.GetEP1_RDWR_BYTE()), nil
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (usbdev *USB_DEVICE) DTR() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (usbdev *USB_DEVICE) RTS() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (usbdev *USB_DEVICE) flush() {
|
||||
usbdev.Bus.SetEP1_CONF_WR_DONE(1)
|
||||
for usbdev.Bus.GetEP1_CONF_SERIAL_IN_EP_DATA_FREE() == 0 {
|
||||
}
|
||||
}
|
||||
|
||||
// GetRNG returns 32-bit random numbers using the ESP32-C3 true random number generator,
|
||||
// Random numbers are generated based on the thermal noise in the system and the
|
||||
// asynchronous clock mismatch.
|
||||
// For maximum entropy also make sure that the SAR_ADC is enabled.
|
||||
// See esp32-c3_technical_reference_manual_en.pdf p.524
|
||||
func GetRNG() (ret uint32, err error) {
|
||||
// ensure ADC clock is initialized
|
||||
initADCClock()
|
||||
|
||||
// ensure fast RTC clock is enabled
|
||||
if esp.RTC_CNTL.GetCLK_CONF_DIG_CLK8M_EN() == 0 {
|
||||
esp.RTC_CNTL.SetCLK_CONF_DIG_CLK8M_EN(1)
|
||||
}
|
||||
|
||||
return esp.APB_CTRL.GetRND_DATA(), nil
|
||||
}
|
||||
|
||||
func initADCClock() {
|
||||
if esp.APB_SARADC.GetCLKM_CONF_CLK_EN() == 1 {
|
||||
return
|
||||
}
|
||||
|
||||
// only support ADC_CTRL_CLK set to 1
|
||||
esp.APB_SARADC.SetCLKM_CONF_CLK_SEL(1)
|
||||
|
||||
esp.APB_SARADC.SetCTRL_SARADC_SAR_CLK_GATED(1)
|
||||
|
||||
esp.APB_SARADC.SetCLKM_CONF_CLKM_DIV_NUM(15)
|
||||
esp.APB_SARADC.SetCLKM_CONF_CLKM_DIV_B(1)
|
||||
esp.APB_SARADC.SetCLKM_CONF_CLKM_DIV_A(0)
|
||||
|
||||
esp.APB_SARADC.SetCTRL_SARADC_SAR_CLK_DIV(1)
|
||||
esp.APB_SARADC.SetCLKM_CONF_CLK_EN(1)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,353 @@
|
||||
//go:build esp32c3 && !m5stamp_c3
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/esp"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
I2C0 = &I2C{}
|
||||
)
|
||||
|
||||
type I2C struct{}
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
type I2CConfig struct {
|
||||
Frequency uint32 // in Hz
|
||||
SCL Pin
|
||||
SDA Pin
|
||||
}
|
||||
|
||||
const (
|
||||
clkXTAL = 0
|
||||
clkFOSC = 1
|
||||
clkXTALFrequency = uint32(40e6)
|
||||
clkFOSCFrequency = uint32(17.5e6)
|
||||
i2cClkSourceFrequency = clkXTALFrequency
|
||||
i2cClkSource = clkXTAL
|
||||
)
|
||||
|
||||
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = 400 * KHz
|
||||
}
|
||||
if config.SCL == 0 {
|
||||
config.SCL = SCL_PIN
|
||||
}
|
||||
if config.SDA == 0 {
|
||||
config.SDA = SDA_PIN
|
||||
}
|
||||
|
||||
i2c.initClock(config)
|
||||
i2c.initNoiseFilter()
|
||||
i2c.initPins(config)
|
||||
i2c.initFrequency(config)
|
||||
i2c.startMaster()
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (i2c *I2C) initClock(config I2CConfig) {
|
||||
// reset I2C clock
|
||||
esp.SYSTEM.SetPERIP_RST_EN0_I2C_EXT0_RST(1)
|
||||
esp.SYSTEM.SetPERIP_CLK_EN0_I2C_EXT0_CLK_EN(1)
|
||||
esp.SYSTEM.SetPERIP_RST_EN0_I2C_EXT0_RST(0)
|
||||
// disable interrupts
|
||||
esp.I2C0.INT_ENA.ClearBits(0x3fff)
|
||||
esp.I2C0.INT_CLR.ClearBits(0x3fff)
|
||||
|
||||
esp.I2C0.SetCLK_CONF_SCLK_SEL(i2cClkSource)
|
||||
esp.I2C0.SetCLK_CONF_SCLK_ACTIVE(1)
|
||||
esp.I2C0.SetCLK_CONF_SCLK_DIV_NUM(i2cClkSourceFrequency / (config.Frequency * 1024))
|
||||
esp.I2C0.SetCTR_CLK_EN(1)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (i2c *I2C) initNoiseFilter() {
|
||||
esp.I2C0.FILTER_CFG.Set(0x377)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (i2c *I2C) initPins(config I2CConfig) {
|
||||
var muxConfig uint32
|
||||
const function = 1 // function 1 is just GPIO
|
||||
|
||||
// SDA
|
||||
muxConfig = function << esp.IO_MUX_GPIO_MCU_SEL_Pos
|
||||
// Make this pin an input pin (always).
|
||||
muxConfig |= esp.IO_MUX_GPIO_FUN_IE
|
||||
// Set drive strength: 0 is lowest, 3 is highest.
|
||||
muxConfig |= 1 << esp.IO_MUX_GPIO_FUN_DRV_Pos
|
||||
config.SDA.mux().Set(muxConfig)
|
||||
config.SDA.outFunc().Set(54)
|
||||
inFunc(54).Set(uint32(esp.GPIO_FUNC_IN_SEL_CFG_SEL | config.SDA))
|
||||
config.SDA.Set(true)
|
||||
// Configure the pad with the given IO mux configuration.
|
||||
config.SDA.pinReg().SetBits(esp.GPIO_PIN_PAD_DRIVER)
|
||||
|
||||
esp.GPIO.ENABLE.SetBits(1 << int(config.SDA))
|
||||
esp.I2C0.SetCTR_SDA_FORCE_OUT(1)
|
||||
|
||||
// SCL
|
||||
muxConfig = function << esp.IO_MUX_GPIO_MCU_SEL_Pos
|
||||
// Make this pin an input pin (always).
|
||||
muxConfig |= esp.IO_MUX_GPIO_FUN_IE
|
||||
// Set drive strength: 0 is lowest, 3 is highest.
|
||||
muxConfig |= 1 << esp.IO_MUX_GPIO_FUN_DRV_Pos
|
||||
config.SCL.mux().Set(muxConfig)
|
||||
config.SCL.outFunc().Set(53)
|
||||
inFunc(53).Set(uint32(config.SCL))
|
||||
config.SCL.Set(true)
|
||||
// Configure the pad with the given IO mux configuration.
|
||||
config.SCL.pinReg().SetBits(esp.GPIO_PIN_PAD_DRIVER)
|
||||
|
||||
esp.GPIO.ENABLE.SetBits(1 << int(config.SCL))
|
||||
esp.I2C0.SetCTR_SCL_FORCE_OUT(1)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (i2c *I2C) initFrequency(config I2CConfig) {
|
||||
|
||||
clkmDiv := i2cClkSourceFrequency/(config.Frequency*1024) + 1
|
||||
sclkFreq := i2cClkSourceFrequency / clkmDiv
|
||||
halfCycle := sclkFreq / config.Frequency / 2
|
||||
//SCL
|
||||
sclLow := halfCycle
|
||||
sclWaitHigh := uint32(0)
|
||||
if config.Frequency > 50000 {
|
||||
sclWaitHigh = halfCycle / 8 // compensate the time when freq > 50K
|
||||
}
|
||||
sclHigh := halfCycle - sclWaitHigh
|
||||
// SDA
|
||||
sdaHold := halfCycle / 4
|
||||
sda_sample := halfCycle / 2
|
||||
setup := halfCycle
|
||||
hold := halfCycle
|
||||
|
||||
esp.I2C0.SetSCL_LOW_PERIOD(sclLow - 1)
|
||||
esp.I2C0.SetSCL_HIGH_PERIOD(sclHigh)
|
||||
esp.I2C0.SetSCL_HIGH_PERIOD_SCL_WAIT_HIGH_PERIOD(25)
|
||||
esp.I2C0.SetSCL_RSTART_SETUP_TIME(setup)
|
||||
esp.I2C0.SetSCL_STOP_SETUP_TIME(setup)
|
||||
esp.I2C0.SetSCL_START_HOLD_TIME(hold - 1)
|
||||
esp.I2C0.SetSCL_STOP_HOLD_TIME(hold - 1)
|
||||
esp.I2C0.SetSDA_SAMPLE_TIME(sda_sample)
|
||||
esp.I2C0.SetSDA_HOLD_TIME(sdaHold)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (i2c *I2C) startMaster() {
|
||||
// FIFO mode for data
|
||||
esp.I2C0.SetFIFO_CONF_NONFIFO_EN(0)
|
||||
// Reset TX & RX buffers
|
||||
esp.I2C0.SetFIFO_CONF_RX_FIFO_RST(1)
|
||||
esp.I2C0.SetFIFO_CONF_RX_FIFO_RST(0)
|
||||
esp.I2C0.SetFIFO_CONF_TX_FIFO_RST(1)
|
||||
esp.I2C0.SetFIFO_CONF_TX_FIFO_RST(0)
|
||||
// set timeout value
|
||||
esp.I2C0.TO.Set(0x10)
|
||||
// enable master mode
|
||||
esp.I2C0.CTR.Set(0x113)
|
||||
esp.I2C0.SetCTR_CONF_UPGATE(1)
|
||||
resetMaster()
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func resetMaster() {
|
||||
// reset FSM
|
||||
esp.I2C0.SetCTR_FSM_RST(1)
|
||||
// clear the bus
|
||||
esp.I2C0.SetSCL_SP_CONF_SCL_RST_SLV_NUM(9)
|
||||
esp.I2C0.SetSCL_SP_CONF_SCL_RST_SLV_EN(1)
|
||||
esp.I2C0.SetSCL_STRETCH_CONF_SLAVE_SCL_STRETCH_EN(1)
|
||||
esp.I2C0.SetCTR_CONF_UPGATE(1)
|
||||
esp.I2C0.FILTER_CFG.Set(0x377)
|
||||
// wait for SCL_RST_SLV_EN
|
||||
for esp.I2C0.GetSCL_SP_CONF_SCL_RST_SLV_EN() != 0 {
|
||||
}
|
||||
esp.I2C0.SetSCL_SP_CONF_SCL_RST_SLV_NUM(0)
|
||||
}
|
||||
|
||||
type i2cCommandType = uint32
|
||||
type i2cAck = uint32
|
||||
|
||||
const (
|
||||
i2cCMD_RSTART i2cCommandType = 6 << 11
|
||||
i2cCMD_WRITE i2cCommandType = 1<<11 | 1<<8 // WRITE + ack_check_en
|
||||
i2cCMD_READ i2cCommandType = 3<<11 | 1<<8 // READ + ack_check_en
|
||||
i2cCMD_READLAST i2cCommandType = 3<<11 | 5<<8 // READ + ack_check_en + NACK
|
||||
i2cCMD_STOP i2cCommandType = 2 << 11
|
||||
i2cCMD_END i2cCommandType = 4 << 11
|
||||
)
|
||||
|
||||
type i2cCommand struct {
|
||||
cmd i2cCommandType
|
||||
data []byte
|
||||
head int
|
||||
}
|
||||
|
||||
//go:linkname nanotime runtime.nanotime
|
||||
func nanotime() int64
|
||||
|
||||
func (i2c *I2C) transmit(addr uint16, cmd []i2cCommand, timeoutMS int) error {
|
||||
const intMask = esp.I2C_INT_STATUS_END_DETECT_INT_ST_Msk | esp.I2C_INT_STATUS_TRANS_COMPLETE_INT_ST_Msk | esp.I2C_INT_STATUS_TIME_OUT_INT_ST_Msk | esp.I2C_INT_STATUS_NACK_INT_ST_Msk
|
||||
esp.I2C0.INT_CLR.SetBits(intMask)
|
||||
esp.I2C0.INT_ENA.SetBits(intMask)
|
||||
esp.I2C0.SetCTR_CONF_UPGATE(1)
|
||||
|
||||
defer func() {
|
||||
esp.I2C0.INT_CLR.SetBits(intMask)
|
||||
esp.I2C0.INT_ENA.ClearBits(intMask)
|
||||
}()
|
||||
|
||||
timeoutNS := int64(timeoutMS) * 1000000
|
||||
needAddress := true
|
||||
needRestart := false
|
||||
readLast := false
|
||||
var readTo []byte
|
||||
for cmdIdx, reg := 0, &esp.I2C0.COMD0; cmdIdx < len(cmd); {
|
||||
c := &cmd[cmdIdx]
|
||||
|
||||
switch c.cmd {
|
||||
case i2cCMD_RSTART:
|
||||
reg.Set(i2cCMD_RSTART)
|
||||
reg = nextAddress(reg)
|
||||
cmdIdx++
|
||||
|
||||
case i2cCMD_WRITE:
|
||||
count := 32
|
||||
if needAddress {
|
||||
needAddress = false
|
||||
esp.I2C0.SetDATA_FIFO_RDATA((uint32(addr) & 0x7f) << 1)
|
||||
count--
|
||||
esp.I2C0.SLAVE_ADDR.Set(uint32(addr))
|
||||
esp.I2C0.SetCTR_CONF_UPGATE(1)
|
||||
}
|
||||
for ; count > 0 && c.head < len(c.data); count, c.head = count-1, c.head+1 {
|
||||
esp.I2C0.SetDATA_FIFO_RDATA(uint32(c.data[c.head]))
|
||||
}
|
||||
reg.Set(i2cCMD_WRITE | uint32(32-count))
|
||||
reg = nextAddress(reg)
|
||||
|
||||
if c.head < len(c.data) {
|
||||
reg.Set(i2cCMD_END)
|
||||
reg = nil
|
||||
} else {
|
||||
cmdIdx++
|
||||
}
|
||||
needRestart = true
|
||||
|
||||
case i2cCMD_READ:
|
||||
if needAddress {
|
||||
needAddress = false
|
||||
esp.I2C0.SetDATA_FIFO_RDATA((uint32(addr)&0x7f)<<1 | 1)
|
||||
esp.I2C0.SLAVE_ADDR.Set(uint32(addr))
|
||||
reg.Set(i2cCMD_WRITE | 1)
|
||||
reg = nextAddress(reg)
|
||||
}
|
||||
if needRestart {
|
||||
// We need to send RESTART again after i2cCMD_WRITE.
|
||||
reg.Set(i2cCMD_RSTART)
|
||||
|
||||
reg = nextAddress(reg)
|
||||
reg.Set(i2cCMD_WRITE | 1)
|
||||
|
||||
reg = nextAddress(reg)
|
||||
esp.I2C0.SetDATA_FIFO_RDATA((uint32(addr)&0x7f)<<1 | 1)
|
||||
needRestart = false
|
||||
}
|
||||
count := 32
|
||||
bytes := len(c.data) - c.head
|
||||
// Only last byte in sequence must be sent with ACK set to 1 to indicate end of data.
|
||||
split := bytes <= count
|
||||
if split {
|
||||
bytes--
|
||||
}
|
||||
if bytes > 32 {
|
||||
bytes = 32
|
||||
}
|
||||
reg.Set(i2cCMD_READ | uint32(bytes))
|
||||
reg = nextAddress(reg)
|
||||
|
||||
if split {
|
||||
readLast = true
|
||||
reg.Set(i2cCMD_READLAST | 1)
|
||||
reg = nextAddress(reg)
|
||||
readTo = c.data[c.head : c.head+bytes+1] // read bytes + 1 last byte
|
||||
cmdIdx++
|
||||
} else {
|
||||
reg.Set(i2cCMD_END)
|
||||
readTo = c.data[c.head : c.head+bytes]
|
||||
reg = nil
|
||||
}
|
||||
|
||||
case i2cCMD_STOP:
|
||||
reg.Set(i2cCMD_STOP)
|
||||
reg = nil
|
||||
cmdIdx++
|
||||
}
|
||||
if reg == nil {
|
||||
// transmit now
|
||||
esp.I2C0.SetCTR_CONF_UPGATE(1)
|
||||
esp.I2C0.SetCTR_TRANS_START(1)
|
||||
end := nanotime() + timeoutNS
|
||||
var mask uint32
|
||||
for mask = esp.I2C0.INT_STATUS.Get(); mask&intMask == 0; mask = esp.I2C0.INT_STATUS.Get() {
|
||||
if nanotime() > end {
|
||||
if readTo != nil {
|
||||
return errI2CReadTimeout
|
||||
}
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case mask&esp.I2C_INT_STATUS_NACK_INT_ST_Msk != 0 && !readLast:
|
||||
return errI2CAckExpected
|
||||
case mask&esp.I2C_INT_STATUS_TIME_OUT_INT_ST_Msk != 0:
|
||||
if readTo != nil {
|
||||
return errI2CReadTimeout
|
||||
}
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
esp.I2C0.INT_CLR.SetBits(intMask)
|
||||
for i := 0; i < len(readTo); i++ {
|
||||
readTo[i] = byte(esp.I2C0.GetDATA_FIFO_RDATA() & 0xff)
|
||||
c.head++
|
||||
}
|
||||
readTo = nil
|
||||
reg = &esp.I2C0.COMD0
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||
func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) {
|
||||
// timeout in microseconds.
|
||||
const timeout = 40 // 40ms is a reasonable time for a real-time system.
|
||||
|
||||
cmd := make([]i2cCommand, 0, 8)
|
||||
cmd = append(cmd, i2cCommand{cmd: i2cCMD_RSTART})
|
||||
if len(w) > 0 {
|
||||
cmd = append(cmd, i2cCommand{cmd: i2cCMD_WRITE, data: w})
|
||||
}
|
||||
if len(r) > 0 {
|
||||
cmd = append(cmd, i2cCommand{cmd: i2cCMD_READ, data: r})
|
||||
}
|
||||
cmd = append(cmd, i2cCommand{cmd: i2cCMD_STOP})
|
||||
|
||||
return i2c.transmit(addr, cmd, timeout)
|
||||
}
|
||||
|
||||
func (i2c *I2C) SetBaudRate(br uint32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func nextAddress(reg *volatile.Register32) *volatile.Register32 {
|
||||
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(reg), 4))
|
||||
}
|
||||
@@ -201,7 +201,7 @@ func (spi SPI) Configure(config SPIConfig) error {
|
||||
|
||||
// configure esp32c3 gpio pin matrix
|
||||
config.SDI.Configure(PinConfig{Mode: PinInput})
|
||||
inFunc(FSPIQ_IN_IDX).Set(esp.GPIO_FUNC_IN_SEL_CFG_SIG_IN_SEL | uint32(config.SDI))
|
||||
inFunc(FSPIQ_IN_IDX).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.SDI))
|
||||
config.SDO.Configure(PinConfig{Mode: PinOutput})
|
||||
config.SDO.outFunc().Set(FSPID_OUT_IDX)
|
||||
config.SCK.Configure(PinConfig{Mode: PinOutput})
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
//go:build nrf52840 && nrf52840_lfxtal_false
|
||||
|
||||
package machine
|
||||
|
||||
const HasLowFrequencyCrystal = false
|
||||
@@ -0,0 +1,5 @@
|
||||
//go:build nrf52840 && ((nrf52840_generic && !nrf52840_lfxtal_false) || nrf52840_lfxtal_true)
|
||||
|
||||
package machine
|
||||
|
||||
const HasLowFrequencyCrystal = true
|
||||
@@ -241,6 +241,8 @@ const (
|
||||
PinFalling PinChange = 4 << iota
|
||||
// Edge rising
|
||||
PinRising
|
||||
|
||||
PinToggle = PinFalling | PinRising
|
||||
)
|
||||
|
||||
// Callbacks to be called for pins configured with SetInterrupt.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user