mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 14:48:40 +00:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e18433a46 | |||
| c5cb58c9b8 | |||
| ef05ba2ea1 | |||
| 25f5f76a48 | |||
| a5292ecb0f | |||
| ab087d6f27 | |||
| fd1d10c9b7 | |||
| 5c37d1ba61 | |||
| 610dd19c40 | |||
| 44ca224056 | |||
| c1cddffbe9 | |||
| 9bbad6700b | |||
| 24f965425d | |||
| b6b723aeec | |||
| 934d5f41bf | |||
| 4b0e858964 | |||
| 32378537b8 | |||
| f5b2a08b15 | |||
| f23e18a8de | |||
| 66d7099c96 | |||
| bef0dc5d21 | |||
| e79cdc1122 | |||
| f0256cab18 | |||
| 5d8e071bfb | |||
| a0069b6282 | |||
| 707d37a4c1 | |||
| 1876b65b18 | |||
| 1fe934e8a8 | |||
| 8bd2233b57 | |||
| ff58c50118 |
@@ -96,7 +96,7 @@ jobs:
|
||||
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
||||
# least the smoke tests still pass.
|
||||
docker:
|
||||
- image: golang:1.23-bullseye
|
||||
- image: golang:1.22-bullseye
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "15"
|
||||
@@ -105,7 +105,7 @@ jobs:
|
||||
# This tests the latest supported LLVM version when linking against system
|
||||
# libraries.
|
||||
docker:
|
||||
- image: golang:1.26-bookworm
|
||||
- image: golang:1.25-bullseye
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "20"
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
run: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
- name: Extract TinyGo version
|
||||
@@ -40,10 +40,10 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.25.7'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-20-${{ matrix.os }}-v1
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-20-${{ matrix.os }}-v2
|
||||
@@ -85,7 +85,7 @@ jobs:
|
||||
make llvm-build
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save LLVM build cache
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||
@@ -101,11 +101,16 @@ jobs:
|
||||
- name: Make release artifact
|
||||
run: cp -p build/release.tar.gz build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
# Note: this release artifact is double-zipped, see:
|
||||
# https://github.com/actions/upload-artifact/issues/39
|
||||
# We can essentially pick one of these:
|
||||
# - 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@v4
|
||||
with:
|
||||
name: darwin-${{ matrix.goarch }}-double-zipped-${{ steps.version.outputs.version }}
|
||||
path: build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz
|
||||
archive: false
|
||||
- name: Smoke tests
|
||||
run: make smoketest TINYGO=$(PWD)/build/tinygo
|
||||
test-macos-homebrew:
|
||||
@@ -116,7 +121,7 @@ jobs:
|
||||
version: [16, 17, 18, 19, 20]
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@main
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
- name: Fix Python symlinks
|
||||
run: |
|
||||
# Github runners have broken symlinks, so relink
|
||||
@@ -125,13 +130,12 @@ jobs:
|
||||
- name: Install LLVM
|
||||
run: |
|
||||
brew install llvm@${{ matrix.version }}
|
||||
brew link llvm@${{ matrix.version }}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.25.7'
|
||||
cache: true
|
||||
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
||||
run: go install -tags=llvm${{ matrix.version }}
|
||||
|
||||
@@ -31,14 +31,14 @@ jobs:
|
||||
sudo rm -rf /usr/local/share/boost
|
||||
df -h
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v6
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
tinygo/tinygo-dev
|
||||
@@ -47,18 +47,18 @@ jobs:
|
||||
type=sha,format=long
|
||||
type=raw,value=latest
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v4
|
||||
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@v4
|
||||
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@v7
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
|
||||
+48
-58
@@ -18,12 +18,12 @@ jobs:
|
||||
# statically linked binary.
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: golang:1.26-alpine
|
||||
image: golang:1.25-alpine
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: Install apk dependencies
|
||||
# tar: needed for actions/cache@v5
|
||||
# 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-dev mold
|
||||
@@ -31,24 +31,24 @@ 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@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
- name: Extract TinyGo version
|
||||
id: version
|
||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: go-cache-linux-alpine-v2-${{ hashFiles('go.mod') }}
|
||||
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@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-20-linux-alpine-v2
|
||||
key: llvm-source-20-linux-alpine-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -70,10 +70,10 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-20-linux-alpine-v2
|
||||
key: llvm-build-20-linux-alpine-v1
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -88,16 +88,16 @@ 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@v5
|
||||
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@v5
|
||||
uses: actions/cache@v4
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-alpine-v2
|
||||
key: binaryen-linux-alpine-v1
|
||||
path: build/wasm-opt
|
||||
- name: Build Binaryen
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
@@ -118,31 +118,26 @@ jobs:
|
||||
make release deb -j3 STATIC=1
|
||||
cp -p build/release.tar.gz /tmp/tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
||||
- name: "Publish release artifact: tarball"
|
||||
uses: actions/upload-artifact@v7
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
|
||||
path: /tmp/tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
|
||||
archive: false
|
||||
- name: "Publish release artifact: Debian package"
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
||||
path: /tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
||||
archive: false
|
||||
name: linux-amd64-double-zipped-${{ steps.version.outputs.version }}
|
||||
path: |
|
||||
/tmp/tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
|
||||
/tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
||||
test-linux-build:
|
||||
# Test the binaries built in the build-linux job by running the smoke tests.
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.25.7'
|
||||
cache: true
|
||||
- name: Install wasmtime
|
||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||
@@ -151,9 +146,9 @@ jobs:
|
||||
- name: Install wasm-tools
|
||||
uses: bytecodealliance/actions/wasm-tools/setup@v1
|
||||
- name: Download release artifact
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz
|
||||
name: linux-amd64-double-zipped-${{ needs.build-linux.outputs.version }}
|
||||
- name: Extract release tarball
|
||||
run: |
|
||||
mkdir -p ~/lib
|
||||
@@ -169,7 +164,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install apt dependencies
|
||||
@@ -186,10 +181,10 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.25.7'
|
||||
cache: true
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v6
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '18'
|
||||
- name: Install wasmtime
|
||||
@@ -199,7 +194,7 @@ jobs:
|
||||
- name: Setup `wasm-tools`
|
||||
uses: bytecodealliance/actions/wasm-tools/setup@v1
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-20-linux-asserts-v1
|
||||
@@ -213,7 +208,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -224,7 +219,7 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-20-linux-asserts-v1
|
||||
@@ -240,13 +235,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@v5
|
||||
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@v5
|
||||
uses: actions/cache@v4
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-asserts-v1
|
||||
@@ -289,7 +284,7 @@ jobs:
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
- name: Get TinyGo version
|
||||
id: version
|
||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||
@@ -303,10 +298,10 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.25.7'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-20-linux-v1
|
||||
@@ -320,7 +315,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -331,7 +326,7 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-20-linux-${{ matrix.goarch }}-v1
|
||||
@@ -349,13 +344,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@v5
|
||||
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@v5
|
||||
uses: actions/cache@v4
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-${{ matrix.goarch }}-v4
|
||||
@@ -375,9 +370,9 @@ jobs:
|
||||
run: |
|
||||
make CROSS=${{ matrix.toolchain }}
|
||||
- name: Download amd64 release
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz
|
||||
name: linux-amd64-double-zipped-${{ needs.build-linux.outputs.version }}
|
||||
- name: Extract amd64 release
|
||||
run: |
|
||||
mkdir -p build/release
|
||||
@@ -389,17 +384,12 @@ jobs:
|
||||
- name: Create ${{ matrix.goarch }} release
|
||||
run: |
|
||||
make release deb RELEASEONLY=1 DEB_ARCH=${{ matrix.libc }}
|
||||
cp -p build/release.tar.gz /tmp/tinygo${{ needs.build-linux.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_${{ needs.build-linux.outputs.version }}_${{ matrix.libc }}.deb
|
||||
- name: "Publish release artifact: tarball"
|
||||
uses: actions/upload-artifact@v7
|
||||
cp -p build/release.tar.gz /tmp/tinygo${{ steps.version.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_${{ steps.version.outputs.version }}_${{ matrix.libc }}.deb
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-${{ matrix.goarch }}-double-zipped-${{ needs.build-linux.outputs.version }}
|
||||
path: /tmp/tinygo${{ needs.build-linux.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
||||
archive: false
|
||||
- name: "Publish release artifact: Debian package"
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: linux-${{ matrix.goarch }}-double-zipped-${{ needs.build-linux.outputs.version }}
|
||||
path: /tmp/tinygo_${{ needs.build-linux.outputs.version }}_${{ matrix.libc }}.deb
|
||||
archive: false
|
||||
name: linux-${{ matrix.goarch }}-double-zipped-${{ steps.version.outputs.version }}
|
||||
path: |
|
||||
/tmp/tinygo${{ steps.version.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
||||
/tmp/tinygo_${{ steps.version.outputs.version }}_${{ matrix.libc }}.deb
|
||||
|
||||
@@ -25,14 +25,14 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v6
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
tinygo/llvm-20
|
||||
@@ -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@v4
|
||||
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@v7
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
target: tinygo-llvm-build
|
||||
context: .
|
||||
|
||||
@@ -21,12 +21,12 @@ jobs:
|
||||
# See: https://github.com/tinygo-org/tinygo/pull/4516#issuecomment-2416363668
|
||||
run: sudo apt-get remove llvm-18
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
- name: Pull musl, bdwgc
|
||||
run: |
|
||||
git submodule update --init lib/musl lib/bdwgc
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-20-linux-nix-v1
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
|
||||
@@ -20,14 +20,14 @@ jobs:
|
||||
run: |
|
||||
echo "$HOME/go/bin" >> $GITHUB_PATH
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0 # fetch all history (no sparse checkout)
|
||||
submodules: true
|
||||
- name: Install apt dependencies
|
||||
run: ./.github/workflows/sizediff-install-pkgs.sh
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-20-sizediff-v1
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: go-cache-linux-sizediff-v2-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
|
||||
@@ -24,13 +24,14 @@ jobs:
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- uses: MinoruSekine/setup-scoop@v4
|
||||
with:
|
||||
scoop_update: 'false'
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
scoop config use_external_7zip true
|
||||
scoop install ninja binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
- name: Extract TinyGo version
|
||||
@@ -40,10 +41,10 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.25.7'
|
||||
cache: true
|
||||
- name: Restore cached LLVM source
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-20-windows-v1
|
||||
@@ -57,7 +58,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save cached LLVM source
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -68,7 +69,7 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore cached LLVM build
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-20-windows-v2
|
||||
@@ -85,21 +86,20 @@ 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@v5
|
||||
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 Go cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: go-cache-windows-v2-${{ hashFiles('go.mod') }}
|
||||
key: go-cache-windows-v1-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
C:/Users/runneradmin/AppData/Local/go-build
|
||||
C:/Users/runneradmin/go/pkg/mod
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
scoop config use_external_7zip true
|
||||
scoop install wasmtime@29.0.1
|
||||
- name: make gen-device
|
||||
run: make -j3 gen-device
|
||||
@@ -114,11 +114,16 @@ jobs:
|
||||
working-directory: build/release
|
||||
run: 7z -tzip a tinygo${{ steps.version.outputs.version }}.windows-amd64.zip tinygo
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
# Note: this release artifact is double-zipped, see:
|
||||
# https://github.com/actions/upload-artifact/issues/39
|
||||
# We can essentially pick one of these:
|
||||
# - 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@v4
|
||||
with:
|
||||
name: tinygo${{ steps.version.outputs.version }}.windows-amd64.zip
|
||||
name: windows-amd64-double-zipped-${{ steps.version.outputs.version }}
|
||||
path: build/release/tinygo${{ steps.version.outputs.version }}.windows-amd64.zip
|
||||
archive: false
|
||||
|
||||
smoke-test-windows:
|
||||
runs-on: windows-2022
|
||||
@@ -131,24 +136,28 @@ jobs:
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- uses: MinoruSekine/setup-scoop@v4
|
||||
with:
|
||||
scoop_update: 'false'
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
scoop config use_external_7zip true
|
||||
scoop install binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.25.7'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: tinygo${{ needs.build-windows.outputs.version }}.windows-amd64.zip
|
||||
name: windows-amd64-double-zipped-${{ needs.build-windows.outputs.version }}
|
||||
path: build/
|
||||
# This build is already unzipped.
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: 7z x tinygo*.windows-amd64.zip -r
|
||||
- name: Smoke tests
|
||||
shell: bash
|
||||
run: make smoketest TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||
@@ -164,18 +173,21 @@ jobs:
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.25.7'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: tinygo${{ needs.build-windows.outputs.version }}.windows-amd64.zip
|
||||
name: windows-amd64-double-zipped-${{ needs.build-windows.outputs.version }}
|
||||
path: build/
|
||||
# This build is already unzipped.
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: 7z x tinygo*.windows-amd64.zip -r
|
||||
- name: Test stdlib packages
|
||||
run: make tinygo-test TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||
|
||||
@@ -190,23 +202,27 @@ jobs:
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- uses: MinoruSekine/setup-scoop@v4
|
||||
with:
|
||||
scoop_update: 'false'
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
scoop config use_external_7zip true
|
||||
scoop install binaryen wasmtime@29.0.1
|
||||
scoop install binaryen && scoop install wasmtime@29.0.1
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.25.7'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: tinygo${{ needs.build-windows.outputs.version }}.windows-amd64.zip
|
||||
name: windows-amd64-double-zipped-${{ needs.build-windows.outputs.version }}
|
||||
path: build/
|
||||
# This build is already unzipped.
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: 7z x tinygo*.windows-amd64.zip -r
|
||||
- name: Test stdlib packages on wasip1
|
||||
run: make tinygo-test-wasip1-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||
|
||||
-148
@@ -1,151 +1,3 @@
|
||||
0.41.0
|
||||
---
|
||||
* **general**
|
||||
- go.mod, compiler: bump minimum Go version to 1.23
|
||||
- builder: support Go 1.26 now
|
||||
- feat: add inheritable-only field to filter processor-level targets (#5270)
|
||||
- feature: add esp32flash flash method for esp32s3/esp32c3/esp32/esp8266
|
||||
- flashing: introduce flash method 'esp32jtag' for esp32c3/esp32s3 targets
|
||||
- fashing: add "adb" flash method using Android Debug Bridge
|
||||
- main: auto-use best available flashing options on esp32
|
||||
- espflasher: update to espflasher 0.6.0
|
||||
* **compiler**
|
||||
- implement method-set based AssignableTo and Implements (#5304)
|
||||
- simplify createObjectLayout
|
||||
- implement copy directly
|
||||
- fix min/max on floats by using intrinsics
|
||||
- add element layout to sliceAppend
|
||||
- add intrinsic for crypto/internal/constanttime.boolToUint8
|
||||
- fix SyscallN handling for Windows on Go 1.26+
|
||||
* **core**
|
||||
- reflect: add TypeAssert
|
||||
- reflect: fix TypeAssert for structs
|
||||
- sync: add WaitGroup.Go
|
||||
- os: add UserCacheDir and UserConfigDir
|
||||
- os: implement Lchown function for changing file ownership (#5161)
|
||||
- testing: add b.ReportMetric()
|
||||
- errors: add errors package to passing list
|
||||
- internal/gclayout: use correct lengths
|
||||
- internal/abi: add EscapeNonString, EscapeToResultNonString
|
||||
- internal, runtime: add internal fips bool
|
||||
- internal/itoa: resurrect from Go stdlib
|
||||
- internal/syscall/unix: implement GetRandom for WASI targets
|
||||
- interp: make ptrtoint size mismatch a recoverable error
|
||||
- loader: support "all:" prefix in //go:embed patterns
|
||||
- loader, crypto/internal/entropy: fix 32 MiB RAM overflow on microcontrollers
|
||||
- loader, unicode/utf8: fix hiBits overflow on 16-bit AVR targets
|
||||
- builder: order embedded files deterministically
|
||||
- builder: fix panic in findPackagePath when using -size short
|
||||
- builder: fix SIGSEGV when stripping duplicate function definitions
|
||||
- builder, runtime: fix duplicate symbol error with Go 1.26+ on Windows
|
||||
- builder: use target-specific -march for RISC-V library compilation
|
||||
- transform: modify output format from the -print-allocs flag to match the go coverage tool format
|
||||
- cgo: allow for changes to 'short-enums' flag
|
||||
- wasm: add more stubbed file operations
|
||||
* **machine**
|
||||
- esp32c3: implement BlockDevice for esp32c3 flash
|
||||
- esp32c3: clear GPIO STATUS before dispatching pin callbacks
|
||||
- esp32c3: implement USBDevice using interrupts
|
||||
- esp32c3: add Enable stub for QEMU test target
|
||||
- esp32c3: fix interrupt disable handling
|
||||
- esp32c3: clear MCAUSE after handling interrupt
|
||||
- esp32c3: map missing IRAM sections in linker script
|
||||
- esp32c3: fix to use PROVIDE() for WiFi/BLE ROM function addresses to allow blob overrides
|
||||
- esp32c3: add WiFi/BLE ROM linker support
|
||||
- esp32c3: use TIMG0 alarm interrupt for sleepTicks instead of busy-waiting
|
||||
- esp32s3: implement Pin.SetInterrupt for GPIO pin change interrupts
|
||||
- esp32s3: use edge-triggered CPU interrupt for GPIO pin interrupts
|
||||
- esp32s3: add interrupt support (#5244)
|
||||
- esp32s3: switch USB implementation to use interrupts instead of polling
|
||||
- esp32s3: replace inline ISR with full interrupt vector handler
|
||||
- esp32s3: use TIMG0 alarm interrupt for sleepTicks
|
||||
- esp32s3: improve exception handlers, add procPin/procUnpin, and linker wrap flags
|
||||
- esp32s3: fix Xtensa register window spill using recursive call4 in task switching
|
||||
- esp32s3: update linker script and boot assembly for multi-page flash XIP mapping
|
||||
- esp32s3: add flash XIP boot assembly with cache/MMU init
|
||||
- esp32s3: implement SPI (#5169)
|
||||
- esp32s3: implement I2C interface (#5210)
|
||||
- esp32s3: implement RNG based on onboard hardware random number generator
|
||||
- esp32s3,esp32c3: add USB serial support
|
||||
- esp32s3,esp32c3: add txStalled flag to skip USB serial spin when no host
|
||||
- esp32s3,esp32c3: make USB Serial/JTAG writes non-blocking when FIFO is full
|
||||
- esp32c3/esp32s3: refactor ADC implementation to reduce code duplication
|
||||
- esp32s3,esp32c3: add ADC support (#5231)
|
||||
- esp32s3,esp32c3: add PWM support (#5215)
|
||||
- esp32c3/esp32s3: refactoring and corrections for SPI implementation
|
||||
- esp32xx: add WASM simulator support for ESP32-C3/ESP32-S3 targets
|
||||
- esp32: default SPI CS pin to NoPin when unset
|
||||
- esp: fix tinygo_scanCurrentStack to spill register windows
|
||||
- stm32: fix UART interrupt storm caused by uncleared overrun error
|
||||
- stm32: fix PWM problem due to register shifting
|
||||
- stm32: add STM32U585 target definition and runtime
|
||||
- stm32: add STM32U5 GPIO, pin interrupts, and UART support
|
||||
- stm32: add STM32U5 I2C support
|
||||
- stm32: add STM32U5 SPI support
|
||||
- stm32u585: implement ADC
|
||||
- stm32g0b1: add support (#5150)
|
||||
- stm32g0b1: add Watchdog + ADC support (#5158)
|
||||
- stm32l0: add flash support
|
||||
- stm32l0x1,l0x2: TIM: adapt to 32-bit register access
|
||||
- stm32g0: sync with updated stm32 device files
|
||||
- attiny85: add USI-based SPI support (#5181)
|
||||
- attiny85: add PWM support (#5171)
|
||||
- rp: add Close function to UART to allow for removing all system resources/power usage
|
||||
- rp: use blockReset() and unresetBlockWait() helper functions for peripheral reset/unreset
|
||||
- rp2: prevent PWM Period method overflow
|
||||
- rp2: add per-byte timeout budget for I2C (#5189)
|
||||
- feather-m0: export UART0 and pins
|
||||
- usb/cdc: better ring buffer implementation (#5209)
|
||||
- fix: replace ! with ~ for register mask
|
||||
- atmega328p_simulator: add correct build tag for arduino_uno after renaming
|
||||
* **net**
|
||||
- update to Go 1.26.2 net package with UDP and JS improvements
|
||||
* **runtime**
|
||||
- implement fminimum/fmaximum
|
||||
- make timeoffset atomic
|
||||
- add MemStats.HeapObjects
|
||||
- handle GODEBUG on wasip2 (#5312)
|
||||
- fix Darwin syscall return value truncation and lost arguments
|
||||
- add syscall.runtimeClearenv for Go 1.26
|
||||
- split syscall functions for darwin changes for 1.26
|
||||
- fix: init heap before random number seed on wasm platforms
|
||||
* **targets**
|
||||
- add esp32s3-supermini board target
|
||||
- add support for Seeedstudio Xiao-RP2350 board
|
||||
- add Shrike Lite board (#5170)
|
||||
- rename arduino target to arduino-uno
|
||||
- add pins/setup for Arduino UNO Q board QWIIC connector
|
||||
- correct pin mapping for Arduino UNO Q STM32 MCU
|
||||
- add stm32u585 openocd commands for flashing on Arduino UNO Q
|
||||
- correct name/tag use for esp32s3-wroom1 board
|
||||
- modify esp32, esp32s3, esp32c3, & esp8266 targets to use built-in esp32flash
|
||||
- switch rp2040/rp2350 to default to tasks scheduler
|
||||
- change default stack size for esp32c3 and esp32s3 to 8196
|
||||
- swan: rename group build tag stm32l4x5 => stm32l4y5 to avoid clash with stm32 device name
|
||||
- nucleo-f722ze: add build-tag stm32f722
|
||||
- fix: set stm32u5x clock rate to default
|
||||
- create generic targets for esp32 boards
|
||||
* **libs**
|
||||
- stm32-svd: update submodule
|
||||
- update tools/gen-device-svd for recent changes to stm32-svd file structure (#5212)
|
||||
* **build/test**
|
||||
- build: use Golang 1.26 for all builds
|
||||
- build: actually use the version of llvm we are supposed to be testing by using brew link command
|
||||
- build: let scoop run updates to resolve binaryen install failures on Windows
|
||||
- ci: don't double zip release artifacts
|
||||
- test: increase default timeout from 1 to 2 minutes to prevent spurious CI fails
|
||||
- testdata: fix flaky timers test by draining ticker channel after Stop
|
||||
- testdata: more corpus entries (#5182)
|
||||
- add soypat/lneto to corpus
|
||||
- GNUmakefile: add context and expvar to stdlib tests
|
||||
- GNUmakefile: add encoding/xml to stdlib tests on Linux
|
||||
- main (test): skip mipsle test if the emulator is missing
|
||||
- make: remove machine without board from smoketest
|
||||
- stm32: add Arduino UNO Q to smoketests
|
||||
- flake: bump to nixpkgs 25.11
|
||||
- chore: update all github actions to latest versions
|
||||
- fix: use external 7zip for scoop installs on Windows
|
||||
|
||||
0.40.1
|
||||
---
|
||||
* **machine**
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
# tinygo-llvm stage obtains the llvm source for TinyGo
|
||||
FROM golang:1.26 AS tinygo-llvm
|
||||
FROM golang:1.25 AS tinygo-llvm
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y apt-utils make cmake clang-17 ninja-build && \
|
||||
@@ -33,7 +33,7 @@ RUN cd /tinygo/ && \
|
||||
|
||||
# tinygo-compiler copies the compiler build over to a base Go container (without
|
||||
# all the build tools etc).
|
||||
FROM golang:1.26 AS tinygo-compiler
|
||||
FROM golang:1.25 AS tinygo-compiler
|
||||
|
||||
# Copy tinygo build.
|
||||
COPY --from=tinygo-compiler-build /tinygo/build/release/tinygo /tinygo
|
||||
|
||||
+9
-62
@@ -289,13 +289,13 @@ WASM_TOOLS_MODULE=go.bytecodealliance.org
|
||||
.PHONY: wasi-syscall
|
||||
wasi-syscall: wasi-cm
|
||||
rm -rf ./src/internal/wasi/*
|
||||
go run $(WASM_TOOLS_MODULE)/cmd/wit-bindgen-go generate --versioned -o ./src/internal -p internal --cm internal/cm ./lib/wasi-cli/wit
|
||||
go run -modfile ./internal/wasm-tools/go.mod $(WASM_TOOLS_MODULE)/cmd/wit-bindgen-go generate --versioned -o ./src/internal -p internal --cm internal/cm ./lib/wasi-cli/wit
|
||||
|
||||
# Copy package cm into src/internal/cm
|
||||
.PHONY: wasi-cm
|
||||
wasi-cm:
|
||||
rm -rf ./src/internal/cm/*
|
||||
rsync -rv --delete --exclude go.mod --exclude '*_test.go' --exclude '*_json.go' --exclude '*.md' --exclude LICENSE $(shell go list -m -f {{.Dir}} $(WASM_TOOLS_MODULE)/cm)/ ./src/internal/cm
|
||||
rsync -rv --delete --exclude go.mod --exclude '*_test.go' --exclude '*_json.go' --exclude '*.md' --exclude LICENSE $(shell go list -modfile ./internal/wasm-tools/go.mod -m -f {{.Dir}} $(WASM_TOOLS_MODULE)/cm)/ ./src/internal/cm
|
||||
|
||||
# Check for Node.js used during WASM tests.
|
||||
MIN_NODEJS_VERSION=18
|
||||
@@ -339,17 +339,14 @@ TEST_PACKAGES_FAST = \
|
||||
embed/internal/embedtest \
|
||||
encoding \
|
||||
encoding/ascii85 \
|
||||
errors \
|
||||
encoding/asn1 \
|
||||
encoding/base32 \
|
||||
encoding/base64 \
|
||||
encoding/csv \
|
||||
encoding/hex \
|
||||
expvar \
|
||||
go/ast \
|
||||
go/format \
|
||||
go/scanner \
|
||||
go/token \
|
||||
go/version \
|
||||
hash \
|
||||
hash/adler32 \
|
||||
@@ -362,7 +359,6 @@ TEST_PACKAGES_FAST = \
|
||||
math/cmplx \
|
||||
net/http/internal/ascii \
|
||||
net/mail \
|
||||
net/url \
|
||||
os \
|
||||
path \
|
||||
reflect \
|
||||
@@ -383,7 +379,6 @@ TEST_PACKAGES_FAST = \
|
||||
# crypto/des fails on wasi, needs panic()/recover()
|
||||
# crypto/hmac fails on wasi, it exits with a "slice out of range" panic
|
||||
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows
|
||||
# encoding/xml takes a minute on linux and gives a stack overflow on wasi
|
||||
# image requires recover(), which is not yet supported on wasi
|
||||
# io/ioutil requires os.ReadDir, which is not yet supported on windows or wasi
|
||||
# mime: fail on wasi; neds panic()/recover()
|
||||
@@ -401,13 +396,11 @@ TEST_PACKAGES_FAST = \
|
||||
TEST_PACKAGES_LINUX := \
|
||||
archive/zip \
|
||||
compress/flate \
|
||||
context \
|
||||
crypto/aes \
|
||||
crypto/des \
|
||||
crypto/hmac \
|
||||
debug/dwarf \
|
||||
debug/plan9obj \
|
||||
encoding/xml \
|
||||
image \
|
||||
io/ioutil \
|
||||
mime \
|
||||
@@ -444,7 +437,6 @@ TEST_PACKAGES_NONWASM = \
|
||||
crypto/ecdsa \
|
||||
debug/macho \
|
||||
embed/internal/embedtest \
|
||||
expvar \
|
||||
go/format \
|
||||
os \
|
||||
testing \
|
||||
@@ -488,7 +480,7 @@ TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
|
||||
TEST_IOFS := false
|
||||
endif
|
||||
|
||||
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic|TestAsValidation'
|
||||
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic'
|
||||
TEST_ADDITIONAL_FLAGS ?=
|
||||
|
||||
# Test known-working standard library packages.
|
||||
@@ -634,7 +626,7 @@ smoketest: testchdir
|
||||
@$(MD5SUM) test.hex
|
||||
# test simulated boards on play.tinygo.org
|
||||
ifneq ($(WASM), 0)
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=arduino_uno examples/blinky1
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=hifive1b examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
@@ -652,8 +644,6 @@ ifneq ($(WASM), 0)
|
||||
@$(MD5SUM) test.wasm
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=pico examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=xiao_esp32s3 examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
endif
|
||||
# test all targets/boards
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
|
||||
@@ -826,8 +816,6 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=vicharak_shrike-lite examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=xiao-rp2350 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
# test pwm
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -887,24 +875,18 @@ ifneq ($(STM32), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32l0x1 examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno-q examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno-q examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno-q examples/blinkm
|
||||
@$(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/machinetest
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno examples/blinky1
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-leonardo examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno examples/pwm
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno -scheduler=tasks examples/blinky1
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-mega1280 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -923,18 +905,10 @@ endif
|
||||
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(XTENSA), 0)
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32-generic examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-generic examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-generic examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32-mini32 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target m5stack-core2 examples/machinetest
|
||||
@@ -947,39 +921,13 @@ ifneq ($(XTENSA), 0)
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target mch2022 examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
# xiao-esp32s3
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/mcp3008
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/pwm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/adc
|
||||
@$(MD5SUM) test.bin
|
||||
# esp32s3-supermini
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-supermini examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-supermini examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-supermini examples/mcp3008
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-supermini examples/adc
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-wroom1 examples/mcp3008
|
||||
@$(MD5SUM) test.bin
|
||||
endif
|
||||
# esp32c3-supermini
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/mcp3008
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/pwm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/adc
|
||||
@$(MD5SUM) test.bin
|
||||
|
||||
$(TINYGO) build -size short -o test.bin -target=esp-c3-32s-kit examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=qtpy-esp32c3 examples/machinetest
|
||||
@@ -992,7 +940,6 @@ endif
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-12f examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
|
||||
$(TINYGO) build -size short -o test.bin -target=makerfabs-esp32c3spi35 examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
|
||||
@@ -1186,7 +1133,7 @@ endif
|
||||
|
||||
.PHONY: tools
|
||||
tools:
|
||||
go generate -tags tools ./
|
||||
cd internal/tools && go generate -tags tools ./
|
||||
|
||||
LINTDIRS=src/os/ src/reflect/
|
||||
.PHONY: lint
|
||||
|
||||
@@ -34,10 +34,10 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
The above program can be compiled and run without modification on an [Arduino Uno](https://tinygo.org/docs/reference/microcontrollers/boards/arduino-uno), an [Adafruit Circuit Playground Express](https://tinygo.org/docs/reference/microcontrollers/featured/circuitplay-express), a [Seeed Studio XIAO-ESP32S3](https://tinygo.org/docs/reference/microcontrollers/featured/xiao-esp32s3) or any of the many supported boards that have a built-in LED, just by setting the correct TinyGo compiler target. For example, this compiles and flashes an Arduino Uno:
|
||||
The above program can be compiled and run without modification on an Arduino Uno, an Adafruit ItsyBitsy M0, or any of the supported boards that have a built-in LED, just by setting the correct TinyGo compiler target. For example, this compiles and flashes an Arduino Uno:
|
||||
|
||||
```shell
|
||||
tinygo flash -target arduino-uno examples/blinky1
|
||||
tinygo flash -target arduino examples/blinky1
|
||||
```
|
||||
|
||||
## WebAssembly
|
||||
@@ -77,7 +77,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
||||
|
||||
### Embedded
|
||||
|
||||
You can compile TinyGo programs for over 150 different microcontroller boards.
|
||||
You can compile TinyGo programs for over 94 different microcontroller boards.
|
||||
|
||||
For more information, please see https://tinygo.org/docs/reference/microcontrollers/
|
||||
|
||||
@@ -142,7 +142,7 @@ Non-goals:
|
||||
|
||||
## Why this project exists
|
||||
|
||||
> We never expected Go to be an embedded language, and so it’s got serious problems...
|
||||
> We never expected Go to be an embedded language and so its got serious problems...
|
||||
|
||||
-- Rob Pike, [GopherCon 2014 Opening Keynote](https://www.youtube.com/watch?v=VoS7DsT1rdM&feature=youtu.be&t=2799)
|
||||
|
||||
|
||||
@@ -546,23 +546,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load bitcode file: %w", err)
|
||||
}
|
||||
// Resolve duplicate function definitions before linking.
|
||||
// This can happen when a newer Go version adds a function
|
||||
// body in a standard library package that was previously
|
||||
// just a declaration provided by //go:linkname from the
|
||||
// runtime. In that case, keep the existing (runtime)
|
||||
// definition by weakening the new one's linkage so the
|
||||
// LLVM linker discards it in favor of the existing one.
|
||||
for fn := pkgMod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
if fn.IsDeclaration() {
|
||||
continue
|
||||
}
|
||||
existing := mod.NamedFunction(fn.Name())
|
||||
if existing.IsNil() || existing.IsDeclaration() {
|
||||
continue
|
||||
}
|
||||
fn.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
}
|
||||
err = llvm.LinkModules(mod, pkgMod)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to link module: %w", err)
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ struct AssemblerInvocation {
|
||||
EmitDwarfUnwindType EmitDwarfUnwind;
|
||||
|
||||
// Whether to emit compact-unwind for non-canonical entries.
|
||||
// Note: maybe overridden by other constraints.
|
||||
// Note: maybe overriden by other constraints.
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned EmitCompactUnwindNonCanonical : 1;
|
||||
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
|
||||
// Version range supported by TinyGo.
|
||||
const minorMin = 19
|
||||
const minorMax = 26
|
||||
const minorMax = 25
|
||||
|
||||
// Check that we support this Go toolchain version.
|
||||
gorootMajor, gorootMinor, err := goenv.GetGorootVersion()
|
||||
|
||||
+2
-16
@@ -167,9 +167,9 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
// double.
|
||||
args = append(args, "-mdouble=64")
|
||||
case "riscv32":
|
||||
args = append(args, "-march="+riscvMarch(config, "rv32imac"), "-fforce-enable-int128")
|
||||
args = append(args, "-march=rv32imac", "-fforce-enable-int128")
|
||||
case "riscv64":
|
||||
args = append(args, "-march="+riscvMarch(config, "rv64gc"))
|
||||
args = append(args, "-march=rv64gc")
|
||||
case "mips":
|
||||
args = append(args, "-fno-pic")
|
||||
}
|
||||
@@ -298,17 +298,3 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
once.Do(unlock)
|
||||
}, nil
|
||||
}
|
||||
|
||||
// riscvMarch returns the -march value for RISC-V library compilation.
|
||||
// It extracts the value from the target's cflags if present, otherwise
|
||||
// falls back to the provided default. This ensures libraries are compiled
|
||||
// with the correct ISA extensions for each target (e.g. rv32imc for
|
||||
// ESP32-C3 which lacks the atomic extension).
|
||||
func riscvMarch(config *compileopts.Config, defaultMarch string) string {
|
||||
for _, flag := range config.Target.CFlags {
|
||||
if strings.HasPrefix(flag, "-march=") {
|
||||
return flag[len("-march="):]
|
||||
}
|
||||
}
|
||||
return defaultMarch
|
||||
}
|
||||
|
||||
+1
-5
@@ -954,11 +954,7 @@ func findPackagePath(path string, packagePathMap map[string]string) (packagePath
|
||||
libPath := strings.TrimPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "lib")+string(os.PathSeparator))
|
||||
parts := strings.SplitN(libPath, string(os.PathSeparator), 2)
|
||||
packagePath = "C " + parts[0]
|
||||
if len(parts) > 1 {
|
||||
filename = parts[1]
|
||||
} else {
|
||||
filename = parts[0]
|
||||
}
|
||||
filename = parts[1]
|
||||
} else if prefix := filepath.Join(goenv.Get("TINYGOROOT"), "llvm-project", "compiler-rt"); strings.HasPrefix(path, prefix) {
|
||||
packagePath = "C compiler-rt"
|
||||
filename = strings.TrimPrefix(path, prefix+string(os.PathSeparator))
|
||||
|
||||
@@ -42,9 +42,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", 3680, 280, 0, 2252},
|
||||
{"hifive1b", "examples/echo", 3668, 280, 0, 2244},
|
||||
{"microbit", "examples/serial", 2694, 342, 8, 2248},
|
||||
{"wioterminal", "examples/pininterrupt", 7074, 1510, 120, 7248},
|
||||
{"wioterminal", "examples/pininterrupt", 6837, 1491, 120, 6888},
|
||||
|
||||
// TODO: also check wasm. Right now this is difficult, because
|
||||
// wasm binaries are run through wasm-opt and therefore the
|
||||
|
||||
@@ -78,7 +78,6 @@ var validCompilerFlags = []*regexp.Regexp{
|
||||
re(`-f(no-)?(pic|PIC|pie|PIE)`),
|
||||
re(`-f(no-)?plt`),
|
||||
re(`-f(no-)?rtti`),
|
||||
re(`-f(no-)?short-enums`),
|
||||
re(`-f(no-)?split-stack`),
|
||||
re(`-f(no-)?stack-(.+)`),
|
||||
re(`-f(no-)?strict-aliasing`),
|
||||
|
||||
@@ -536,7 +536,7 @@ func (c *Config) Programmer() (method, openocdInterface string) {
|
||||
case "":
|
||||
// No configuration supplied.
|
||||
return c.Target.FlashMethod, c.Target.OpenOCDInterface
|
||||
case "openocd", "msd", "command", "adb":
|
||||
case "openocd", "msd", "command":
|
||||
// The -programmer flag only specifies the flash method.
|
||||
return c.Options.Programmer, c.Target.OpenOCDInterface
|
||||
case "bmp":
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
// https://github.com/shepmaster/rust-arduino-blink-led-no-core-with-cargo/blob/master/blink/arduino.json
|
||||
type TargetSpec struct {
|
||||
Inherits []string `json:"inherits,omitempty"`
|
||||
InheritableOnly bool `json:"inheritable-only"` // this target is only meant to be inherited from, not used directly
|
||||
Triple string `json:"llvm-target,omitempty"`
|
||||
CPU string `json:"cpu,omitempty"`
|
||||
ABI string `json:"target-abi,omitempty"` // roughly equivalent to -mabi= flag
|
||||
@@ -64,9 +63,6 @@ type TargetSpec struct {
|
||||
OpenOCDCommands []string `json:"openocd-commands,omitempty"`
|
||||
OpenOCDVerify *bool `json:"openocd-verify,omitempty"` // enable verify when flashing with openocd
|
||||
JLinkDevice string `json:"jlink-device,omitempty"`
|
||||
ADBPreCommands []string `json:"adb-pre-commands,omitempty"`
|
||||
ADBPushRemote string `json:"adb-push-remote,omitempty"`
|
||||
ADBPostCommands []string `json:"adb-post-commands,omitempty"`
|
||||
CodeModel string `json:"code-model,omitempty"`
|
||||
RelocationModel string `json:"relocation-model,omitempty"`
|
||||
WITPackage string `json:"wit-package,omitempty"`
|
||||
@@ -152,11 +148,6 @@ func (spec *TargetSpec) loadFromGivenStr(str string) error {
|
||||
|
||||
// resolveInherits loads inherited targets, recursively.
|
||||
func (spec *TargetSpec) resolveInherits() error {
|
||||
// Save InheritableOnly before resolving, since it must not propagate
|
||||
// from parent to child (a board target should not become inheritable-only
|
||||
// just because its parent processor target is).
|
||||
inheritableOnly := spec.InheritableOnly
|
||||
|
||||
// First create a new spec with all the inherited properties.
|
||||
newSpec := &TargetSpec{}
|
||||
for _, name := range spec.Inherits {
|
||||
@@ -182,9 +173,6 @@ func (spec *TargetSpec) resolveInherits() error {
|
||||
}
|
||||
*spec = *newSpec
|
||||
|
||||
// Restore InheritableOnly from the original spec, not from parents.
|
||||
spec.InheritableOnly = inheritableOnly
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -251,17 +239,10 @@ func GetTargetSpecs() (map[string]*TargetSpec, error) {
|
||||
continue
|
||||
}
|
||||
path := filepath.Join(dir, entry.Name())
|
||||
|
||||
spec, err := LoadTarget(&Options{Target: path})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not list target: %w", err)
|
||||
}
|
||||
|
||||
if spec.InheritableOnly {
|
||||
// Skip targets that are only meant to be inherited from, not used directly.
|
||||
continue
|
||||
}
|
||||
|
||||
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" {
|
||||
// This doesn't look like a regular target file, but rather like
|
||||
// a parent target (such as targets/cortex-m.json).
|
||||
|
||||
@@ -23,37 +23,6 @@ func TestLoadTarget(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTargetSpecs_InheritableOnlyTargetsExcluded(t *testing.T) {
|
||||
specs, err := GetTargetSpecs()
|
||||
if err != nil {
|
||||
t.Fatal("GetTargetSpecs failed:", err)
|
||||
}
|
||||
|
||||
// Inheritable-only processor-level targets should not appear in the listing.
|
||||
inheritableOnlyTargets := []string{"esp32", "esp32c3", "esp32s3", "esp8266", "rp2040", "rp2350", "rp2350b"}
|
||||
for _, name := range inheritableOnlyTargets {
|
||||
if _, ok := specs[name]; ok {
|
||||
t.Errorf("inheritable-only target %q should not appear in GetTargetSpecs", name)
|
||||
}
|
||||
}
|
||||
|
||||
// Board targets that inherit from inheritable-only targets should still appear.
|
||||
boardTargets := []string{"esp32-coreboard-v2", "pico"}
|
||||
for _, name := range boardTargets {
|
||||
if _, ok := specs[name]; !ok {
|
||||
t.Errorf("board target %q should appear in GetTargetSpecs", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadTarget_InheritableOnlyTargetStillLoadable(t *testing.T) {
|
||||
// Inheritable-only targets should still be loadable directly (for building).
|
||||
_, err := LoadTarget(&Options{Target: "esp32"})
|
||||
if err != nil {
|
||||
t.Errorf("LoadTarget should still load inheritable-only target esp32: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOverrideProperties(t *testing.T) {
|
||||
baseAutoStackSize := true
|
||||
base := &TargetSpec{
|
||||
|
||||
@@ -873,10 +873,6 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||
// with a LLVM intrinsic.
|
||||
continue
|
||||
}
|
||||
if ok := b.defineCryptoIntrinsic(); ok {
|
||||
// Body of this function was replaced
|
||||
continue
|
||||
}
|
||||
if member.Blocks == nil {
|
||||
// Try to define this as an intrinsic function.
|
||||
b.defineIntrinsicFunction()
|
||||
@@ -1980,14 +1976,10 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
return b.emitSV64Call(instr.Args, getPos(instr))
|
||||
case strings.HasPrefix(name, "(device/riscv.CSR)."):
|
||||
return b.emitCSROperation(instr)
|
||||
case (strings.HasPrefix(name, "syscall.Syscall") || strings.HasPrefix(name, "syscall.RawSyscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.Syscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscall")) && name != "syscall.SyscallN":
|
||||
case strings.HasPrefix(name, "syscall.Syscall") || strings.HasPrefix(name, "syscall.RawSyscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.Syscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscall"):
|
||||
if b.GOOS != "darwin" {
|
||||
return b.createSyscall(instr)
|
||||
}
|
||||
case name == "syscall.syscalln":
|
||||
if b.GOOS == "windows" {
|
||||
return b.createSyscalln(instr)
|
||||
}
|
||||
case strings.HasPrefix(name, "syscall.rawSyscallNoError") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscallNoError"):
|
||||
return b.createRawSyscallNoError(instr)
|
||||
case name == "runtime.supportsRecover":
|
||||
|
||||
+36
-152
@@ -10,7 +10,6 @@ import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -18,12 +17,6 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// numMethodHasMethodSet is a flag in bit 15 of the numMethod field (uint16) in
|
||||
// Named, Pointer, and Struct type descriptors. When set, an inline method set
|
||||
// is present in the type descriptor. Must match the constant in
|
||||
// src/internal/reflectlite/type.go.
|
||||
const numMethodHasMethodSet = 0x8000
|
||||
|
||||
// Type kinds for basic types.
|
||||
// They must match the constants for the Kind type in src/reflect/type.go.
|
||||
var basicTypes = [...]uint8{
|
||||
@@ -190,16 +183,6 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
typeFieldTypes := []*types.Var{
|
||||
types.NewVar(token.NoPos, nil, "kind", types.Typ[types.Int8]),
|
||||
}
|
||||
// Compute the method set value for types that support methods.
|
||||
var methods []*types.Func
|
||||
for i := 0; i < ms.Len(); i++ {
|
||||
methods = append(methods, ms.At(i).Obj().(*types.Func))
|
||||
}
|
||||
methodSetType := types.NewStruct([]*types.Var{
|
||||
types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
|
||||
types.NewVar(token.NoPos, nil, "methods", types.NewArray(types.Typ[types.UnsafePointer], int64(len(methods)))),
|
||||
}, nil)
|
||||
methodSetValue := c.getMethodSetValue(methods)
|
||||
switch typ := typ.(type) {
|
||||
case *types.Basic:
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
@@ -216,13 +199,6 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "underlying", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "pkgpath", types.Typ[types.UnsafePointer]),
|
||||
)
|
||||
if len(methods) > 0 {
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "methods", methodSetType),
|
||||
)
|
||||
}
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "name", types.NewArray(types.Typ[types.Int8], int64(len(pkgname)+1+len(name)+1))),
|
||||
)
|
||||
case *types.Chan:
|
||||
@@ -242,11 +218,6 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
types.NewVar(token.NoPos, nil, "numMethods", types.Typ[types.Uint16]),
|
||||
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
|
||||
)
|
||||
if len(methods) > 0 {
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "methods", methodSetType),
|
||||
)
|
||||
}
|
||||
case *types.Array:
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "numMethods", types.Typ[types.Uint16]),
|
||||
@@ -271,16 +242,11 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
types.NewVar(token.NoPos, nil, "numFields", types.Typ[types.Uint16]),
|
||||
types.NewVar(token.NoPos, nil, "fields", types.NewArray(c.getRuntimeType("structField"), int64(typ.NumFields()))),
|
||||
)
|
||||
if len(methods) > 0 {
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "methods", methodSetType),
|
||||
)
|
||||
}
|
||||
case *types.Interface:
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "methods", methodSetType),
|
||||
)
|
||||
// TODO: methods
|
||||
case *types.Signature:
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
@@ -326,24 +292,14 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
pkgname = pkg.Name()
|
||||
}
|
||||
pkgPathPtr := c.pkgPathPtr(pkgpath)
|
||||
namedNumMethods := uint64(numMethods)
|
||||
if namedNumMethods&numMethodHasMethodSet != 0 {
|
||||
panic("numMethods overflow: too many exported methods on named type " + name)
|
||||
}
|
||||
if len(methods) > 0 {
|
||||
namedNumMethods |= numMethodHasMethodSet
|
||||
}
|
||||
typeFields = []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int16Type(), namedNumMethods, false), // numMethods
|
||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||
c.getTypeCode(typ.Underlying()), // underlying
|
||||
pkgPathPtr, // pkgpath pointer
|
||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
|
||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||
c.getTypeCode(typ.Underlying()), // underlying
|
||||
pkgPathPtr, // pkgpath pointer
|
||||
c.ctx.ConstString(pkgname+"."+name+"\x00", false), // name
|
||||
}
|
||||
if len(methods) > 0 {
|
||||
typeFields = append(typeFields, methodSetValue) // methods
|
||||
}
|
||||
typeFields = append(typeFields, c.ctx.ConstString(pkgname+"."+name+"\x00", false)) // name
|
||||
metabyte |= 1 << 5 // "named" flag
|
||||
metabyte |= 1 << 5 // "named" flag
|
||||
case *types.Chan:
|
||||
var dir reflectChanDir
|
||||
switch typ.Dir() {
|
||||
@@ -367,20 +323,10 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
c.getTypeCode(typ.Elem()), // elementType
|
||||
}
|
||||
case *types.Pointer:
|
||||
ptrNumMethods := uint64(numMethods)
|
||||
if ptrNumMethods&numMethodHasMethodSet != 0 {
|
||||
panic("numMethods overflow: too many exported methods on pointer type")
|
||||
}
|
||||
if len(methods) > 0 {
|
||||
ptrNumMethods |= numMethodHasMethodSet
|
||||
}
|
||||
typeFields = []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int16Type(), ptrNumMethods, false), // numMethods
|
||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
|
||||
c.getTypeCode(typ.Elem()),
|
||||
}
|
||||
if len(methods) > 0 {
|
||||
typeFields = append(typeFields, methodSetValue)
|
||||
}
|
||||
case *types.Array:
|
||||
typeFields = []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int16Type(), 0, false), // numMethods
|
||||
@@ -407,16 +353,9 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
|
||||
llvmStructType := c.getLLVMType(typ)
|
||||
size := c.targetData.TypeStoreSize(llvmStructType)
|
||||
structNumMethods := uint64(numMethods)
|
||||
if structNumMethods&numMethodHasMethodSet != 0 {
|
||||
panic("numMethods overflow: too many exported methods on struct type")
|
||||
}
|
||||
if len(methods) > 0 {
|
||||
structNumMethods |= numMethodHasMethodSet
|
||||
}
|
||||
typeFields = []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int16Type(), structNumMethods, false), // numMethods
|
||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
|
||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||
pkgPathPtr,
|
||||
llvm.ConstInt(c.ctx.Int32Type(), uint64(size), false), // size
|
||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(typ.NumFields()), false), // numFields
|
||||
@@ -468,14 +407,9 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
}))
|
||||
}
|
||||
typeFields = append(typeFields, llvm.ConstArray(structFieldType, fields))
|
||||
if len(methods) > 0 {
|
||||
typeFields = append(typeFields, methodSetValue)
|
||||
}
|
||||
case *types.Interface:
|
||||
typeFields = []llvm.Value{
|
||||
c.getTypeCode(types.NewPointer(typ)),
|
||||
methodSetValue,
|
||||
}
|
||||
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
|
||||
// TODO: methods
|
||||
case *types.Signature:
|
||||
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
|
||||
// TODO: params, return values, etc
|
||||
@@ -762,11 +696,17 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
// 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 an interface type with methods.
|
||||
// Create a call to a declared-but-not-defined function that will
|
||||
// be lowered by the interface lowering pass into a type-ID
|
||||
// comparison chain.
|
||||
commaOk = b.createInterfaceTypeAssert(intf, actualTypeNum)
|
||||
// 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)
|
||||
@@ -843,58 +783,20 @@ func (c *compilerContext) getMethodsString(itf *types.Interface) string {
|
||||
return strings.Join(methods, "; ")
|
||||
}
|
||||
|
||||
// getMethodSetValue creates the method set struct value for a list of methods.
|
||||
// The struct contains a length and a sorted array of method signature pointers.
|
||||
func (c *compilerContext) getMethodSetValue(methods []*types.Func) llvm.Value {
|
||||
// Create a sorted list of method signature global names.
|
||||
type methodRef struct {
|
||||
name string
|
||||
value llvm.Value
|
||||
// getInterfaceImplementsFunc returns a declared function that works as a type
|
||||
// switch. The interface lowering pass will define this function.
|
||||
func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) llvm.Value {
|
||||
s, _ := getTypeCodeName(assertedType.Underlying())
|
||||
fnName := s + ".$typeassert"
|
||||
llvmFn := c.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.dataPtrType}, false)
|
||||
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
|
||||
c.addStandardDeclaredAttributes(llvmFn)
|
||||
methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-methods", methods))
|
||||
}
|
||||
var refs []methodRef
|
||||
for _, method := range methods {
|
||||
name := method.Name()
|
||||
if !token.IsExported(name) {
|
||||
name = method.Pkg().Path() + "." + name
|
||||
}
|
||||
s, _ := getTypeCodeName(method.Type())
|
||||
globalName := "reflect/types.signature:" + name + ":" + s
|
||||
value := c.mod.NamedGlobal(globalName)
|
||||
if value.IsNil() {
|
||||
value = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), globalName)
|
||||
value.SetInitializer(llvm.ConstNull(c.ctx.Int8Type()))
|
||||
value.SetGlobalConstant(true)
|
||||
value.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
value.SetAlignment(1)
|
||||
if c.Debug {
|
||||
file := c.getDIFile("<Go type>")
|
||||
diglobal := c.dibuilder.CreateGlobalVariableExpression(file, llvm.DIGlobalVariableExpression{
|
||||
Name: globalName,
|
||||
File: file,
|
||||
Line: 1,
|
||||
Type: c.getDIType(types.Typ[types.Uint8]),
|
||||
LocalToUnit: false,
|
||||
Expr: c.dibuilder.CreateExpression(nil),
|
||||
AlignInBits: 8,
|
||||
})
|
||||
value.AddMetadata(0, diglobal)
|
||||
}
|
||||
}
|
||||
refs = append(refs, methodRef{globalName, value})
|
||||
}
|
||||
sort.Slice(refs, func(i, j int) bool {
|
||||
return refs[i].name < refs[j].name
|
||||
})
|
||||
|
||||
var values []llvm.Value
|
||||
for _, ref := range refs {
|
||||
values = append(values, ref.value)
|
||||
}
|
||||
|
||||
return c.ctx.ConstStruct([]llvm.Value{
|
||||
llvm.ConstInt(c.uintptrType, uint64(len(values)), false),
|
||||
llvm.ConstArray(c.dataPtrType, values),
|
||||
}, false)
|
||||
return llvmFn
|
||||
}
|
||||
|
||||
// getInvokeFunction returns the thunk to call the given interface method. The
|
||||
@@ -921,24 +823,6 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
|
||||
return llvmFn
|
||||
}
|
||||
|
||||
// createInterfaceTypeAssert creates a call to a declared-but-not-defined
|
||||
// $typeassert function for the given interface. This function will be defined
|
||||
// by the interface lowering pass as a type-ID comparison chain, avoiding the
|
||||
// need for runtime.typeImplementsMethodSet at compile time.
|
||||
func (b *builder) createInterfaceTypeAssert(intf *types.Interface, actualType llvm.Value) llvm.Value {
|
||||
s, _ := getTypeCodeName(intf)
|
||||
fnName := s + ".$typeassert"
|
||||
llvmFn := b.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
llvmFnType := llvm.FunctionType(b.ctx.Int1Type(), []llvm.Type{b.dataPtrType}, false)
|
||||
llvmFn = llvm.AddFunction(b.mod, fnName, llvmFnType)
|
||||
b.addStandardDeclaredAttributes(llvmFn)
|
||||
methods := b.getMethodsString(intf)
|
||||
llvmFn.AddFunctionAttr(b.ctx.CreateStringAttribute("tinygo-methods", methods))
|
||||
}
|
||||
return b.CreateCall(llvmFn.GlobalValueType(), llvmFn, []llvm.Value{actualType}, "")
|
||||
}
|
||||
|
||||
// getInterfaceInvokeWrapper returns a wrapper for the given method so it can be
|
||||
// invoked from an interface. The wrapper takes in a pointer to the underlying
|
||||
// value, dereferences or unpacks it if necessary, and calls the real method.
|
||||
|
||||
@@ -208,23 +208,6 @@ func (b *builder) defineMathOp() {
|
||||
b.CreateRet(result)
|
||||
}
|
||||
|
||||
func (b *builder) defineCryptoIntrinsic() bool {
|
||||
if b.fn.Pkg.Pkg.Path() != "crypto/internal/constanttime" {
|
||||
return false
|
||||
}
|
||||
|
||||
switch b.fn.Name() {
|
||||
case "boolToUint8":
|
||||
b.createFunctionStart(true)
|
||||
param := b.getValue(b.fn.Params[0], b.fn.Pos())
|
||||
result := b.CreateZExt(param, b.ctx.Int8Type(), "")
|
||||
b.CreateRet(result)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Implement most math/bits functions.
|
||||
//
|
||||
// This implements all the functions that operate on bits. It does not yet
|
||||
|
||||
+1
-1
@@ -540,7 +540,7 @@ func (c *compilerContext) isValidWasmType(typ types.Type, site wasmSite) bool {
|
||||
}
|
||||
for i := 0; i < typ.NumFields(); i++ {
|
||||
ftyp := typ.Field(i).Type()
|
||||
if types.Unalias(ftyp).String() == "structs.HostLayout" {
|
||||
if ftyp.String() == "structs.HostLayout" {
|
||||
hasHostLayout = true
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -349,130 +349,6 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// createSyscalln emits instructions for the syscall.syscalln function on
|
||||
// Windows. This handles the variadic calling convention used in Go 1.26+:
|
||||
//
|
||||
// func syscalln(fn, n uintptr, args ...uintptr) (r1, r2 uintptr, err Errno)
|
||||
//
|
||||
// The function generates a switch on n to dispatch to the correct fixed-argument
|
||||
// function pointer call with SetLastError(0)/GetLastError() wrapping.
|
||||
func (b *builder) createSyscalln(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
const maxArgs = 18 // Windows syscalls support up to 18 args
|
||||
|
||||
isI386 := strings.HasPrefix(b.Triple, "i386-")
|
||||
|
||||
// Get the function pointer (call.Args[0]) and n (call.Args[1]).
|
||||
fn := b.getValue(call.Args[0], getPos(call))
|
||||
fnPtr := b.CreateIntToPtr(fn, b.dataPtrType, "")
|
||||
n := b.getValue(call.Args[1], getPos(call))
|
||||
|
||||
// Get the variadic args slice (call.Args[2]).
|
||||
// In SSA, the variadic slice is the third argument.
|
||||
var argsPtr llvm.Value
|
||||
if len(call.Args) > 2 {
|
||||
argsSlice := b.getValue(call.Args[2], getPos(call))
|
||||
argsPtr = b.CreateExtractValue(argsSlice, 0, "args.data")
|
||||
} else {
|
||||
argsPtr = llvm.ConstNull(b.dataPtrType)
|
||||
}
|
||||
|
||||
// Prepare SetLastError and GetLastError.
|
||||
setLastError := b.mod.NamedFunction("SetLastError")
|
||||
if setLastError.IsNil() {
|
||||
llvmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.ctx.Int32Type()}, false)
|
||||
setLastError = llvm.AddFunction(b.mod, "SetLastError", llvmType)
|
||||
if isI386 {
|
||||
setLastError.SetFunctionCallConv(llvm.X86StdcallCallConv)
|
||||
}
|
||||
}
|
||||
getLastError := b.mod.NamedFunction("GetLastError")
|
||||
if getLastError.IsNil() {
|
||||
llvmType := llvm.FunctionType(b.ctx.Int32Type(), nil, false)
|
||||
getLastError = llvm.AddFunction(b.mod, "GetLastError", llvmType)
|
||||
if isI386 {
|
||||
getLastError.SetFunctionCallConv(llvm.X86StdcallCallConv)
|
||||
}
|
||||
}
|
||||
|
||||
retType := b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false)
|
||||
|
||||
// Create the merge block where all cases converge.
|
||||
mergeBB := b.insertBasicBlock("syscalln.merge")
|
||||
|
||||
// Create the default (panic) block.
|
||||
panicBB := b.insertBasicBlock("syscalln.panic")
|
||||
|
||||
// Create the switch on n.
|
||||
sw := b.CreateSwitch(n, panicBB, maxArgs+1)
|
||||
|
||||
// We'll collect blocks and values for the PHI node.
|
||||
var incomingVals []llvm.Value
|
||||
var incomingBlocks []llvm.BasicBlock
|
||||
|
||||
// Generate a case for each arg count 0..maxArgs.
|
||||
for i := 0; i <= maxArgs; i++ {
|
||||
caseBB := b.insertBasicBlock("syscalln.case" + strconv.Itoa(i))
|
||||
sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), caseBB)
|
||||
b.SetInsertPointAtEnd(caseBB)
|
||||
|
||||
// Load args[0] through args[i-1] from the slice data pointer.
|
||||
var params []llvm.Value
|
||||
var paramTypes []llvm.Type
|
||||
for j := 0; j < i; j++ {
|
||||
gep := b.CreateInBoundsGEP(b.uintptrType, argsPtr, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(j), false),
|
||||
}, "")
|
||||
arg := b.CreateLoad(b.uintptrType, gep, "")
|
||||
params = append(params, arg)
|
||||
paramTypes = append(paramTypes, b.uintptrType)
|
||||
}
|
||||
|
||||
// SetLastError(0)
|
||||
setCall := b.CreateCall(setLastError.GlobalValueType(), setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
|
||||
var sp llvm.Value
|
||||
if isI386 {
|
||||
setCall.SetInstructionCallConv(llvm.X86StdcallCallConv)
|
||||
sp = b.readStackPointer()
|
||||
}
|
||||
|
||||
// Call fn(args...)
|
||||
fnType := llvm.FunctionType(b.uintptrType, paramTypes, false)
|
||||
syscallResult := b.CreateCall(fnType, fnPtr, params, "")
|
||||
if isI386 {
|
||||
syscallResult.SetInstructionCallConv(llvm.X86StdcallCallConv)
|
||||
b.writeStackPointer(sp)
|
||||
}
|
||||
|
||||
// err = GetLastError()
|
||||
errResult := b.CreateCall(getLastError.GlobalValueType(), getLastError, nil, "err")
|
||||
if isI386 {
|
||||
errResult.SetInstructionCallConv(llvm.X86StdcallCallConv)
|
||||
}
|
||||
if b.uintptrType != b.ctx.Int32Type() {
|
||||
errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr")
|
||||
}
|
||||
|
||||
// Build {r1, 0, err}
|
||||
result := llvm.ConstNull(retType)
|
||||
result = b.CreateInsertValue(result, syscallResult, 0, "")
|
||||
result = b.CreateInsertValue(result, errResult, 2, "")
|
||||
|
||||
incomingVals = append(incomingVals, result)
|
||||
incomingBlocks = append(incomingBlocks, b.Builder.GetInsertBlock())
|
||||
b.CreateBr(mergeBB)
|
||||
}
|
||||
|
||||
// Panic block for n > maxArgs.
|
||||
b.SetInsertPointAtEnd(panicBB)
|
||||
b.CreateUnreachable()
|
||||
|
||||
// Merge block: PHI node to select the result.
|
||||
b.SetInsertPointAtEnd(mergeBB)
|
||||
phi := b.CreatePHI(retType, "syscalln.result")
|
||||
phi.AddIncoming(incomingVals, incomingBlocks)
|
||||
return phi, nil
|
||||
}
|
||||
|
||||
// createRawSyscallNoError emits instructions for the Linux-specific
|
||||
// syscall.rawSyscallNoError function.
|
||||
func (b *builder) createRawSyscallNoError(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
|
||||
Vendored
+3
-5
@@ -9,14 +9,12 @@ target triple = "wasm32-unknown-wasi"
|
||||
@"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 -62, ptr @"reflect/types.type:pointer:basic:int" }, align 4
|
||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:int" }, align 4
|
||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:error" }, align 4
|
||||
@"reflect/types.signature:Error:func:{}{basic:string}" = linkonce_odr constant i8 0, align 1
|
||||
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [7 x i8] } { i8 116, i16 -32767, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] }, [7 x i8] c".error\00" }, align 4
|
||||
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [7 x i8] } { i8 116, i16 1, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", [7 x i8] c".error\00" }, align 4
|
||||
@"reflect/types.type.pkgpath.empty" = linkonce_odr unnamed_addr constant [1 x i8] zeroinitializer, align 1
|
||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] } }, align 4
|
||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.signature:String:func:{}{basic:string}" = linkonce_odr constant i8 0, align 1
|
||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:String:func:{}{basic:string}"] } }, align 4
|
||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.typeid:basic:int" = external constant i8
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
|
||||
@@ -1,53 +1,29 @@
|
||||
module github.com/tinygo-org/tinygo
|
||||
|
||||
go 1.23.0
|
||||
go 1.22.0
|
||||
|
||||
require (
|
||||
github.com/aykevl/go-wasm v0.0.2-0.20250317121156-42b86c494139
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
||||
github.com/gofrs/flock v0.8.1
|
||||
github.com/golangci/misspell v0.6.0
|
||||
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
|
||||
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf
|
||||
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
|
||||
github.com/mattn/go-colorable v0.1.13
|
||||
github.com/mattn/go-tty v0.0.4
|
||||
github.com/mgechev/revive v1.3.9
|
||||
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3
|
||||
github.com/tetratelabs/wazero v1.9.0
|
||||
go.bug.st/serial v1.6.4
|
||||
go.bytecodealliance.org v0.6.2
|
||||
go.bytecodealliance.org/cm v0.2.2
|
||||
github.com/tetratelabs/wazero v1.6.0
|
||||
go.bug.st/serial v1.6.0
|
||||
golang.org/x/net v0.35.0
|
||||
golang.org/x/sys v0.30.0
|
||||
golang.org/x/tools v0.30.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
tinygo.org/x/espflasher v0.6.0
|
||||
tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
github.com/chavacava/garif v0.1.0 // indirect
|
||||
github.com/coreos/go-semver v0.3.1 // indirect
|
||||
github.com/creack/goselect v0.1.2 // indirect
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
|
||||
github.com/fatih/color v1.17.0 // indirect
|
||||
github.com/fatih/structtag v1.2.0 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/regclient/regclient v0.8.2 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||
github.com/urfave/cli/v3 v3.0.0-beta1 // indirect
|
||||
golang.org/x/mod v0.23.0 // indirect
|
||||
github.com/stretchr/testify v1.8.4 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,36 +1,17 @@
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/aykevl/go-wasm v0.0.2-0.20250317121156-42b86c494139 h1:2O/WuAt8J5id3khcAtVB90czG80m+v0sfkLE07GrCVg=
|
||||
github.com/aykevl/go-wasm v0.0.2-0.20250317121156-42b86c494139/go.mod h1:7sXyiaA0WtSogCu67R2252fQpVmJMh9JWJ9ddtGkpWw=
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
||||
github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc=
|
||||
github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww=
|
||||
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
|
||||
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
|
||||
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
|
||||
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs=
|
||||
github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
|
||||
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg=
|
||||
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf h1:FtEj8sfIcaaBfAKrE1Cwb61YDtYq9JxChK1c7AKce7s=
|
||||
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf/go.mod h1:yrqSXGoD/4EKfF26AOGzscPOgTTJcyAwM2rpixWT+t4=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892 h1:6J+qramlHVLmiBOgRiBOnQkno8uprqG6YFFQTt6uYIw=
|
||||
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892/go.mod h1:Pb6XcsXyropB9LNHhnqaknG/vEwYztLkQzVCHv8sQ3M=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
@@ -42,57 +23,18 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-tty v0.0.4 h1:NVikla9X8MN0SQAqCYzpGyXv0jY7MNl3HOWD2dkle7E=
|
||||
github.com/mattn/go-tty v0.0.4/go.mod h1:u5GGXBtZU6RQoKV8gY5W6UhMudbR5vXnUe7j3pxse28=
|
||||
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 h1:zpIH83+oKzcpryru8ceC6BxnoG8TBrhgAvRg8obzup0=
|
||||
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg=
|
||||
github.com/mgechev/revive v1.3.9 h1:18Y3R4a2USSBF+QZKFQwVkBROUda7uoBlkEuBD+YD1A=
|
||||
github.com/mgechev/revive v1.3.9/go.mod h1:+uxEIr5UH0TjXWHTno3xh4u7eg6jDpXKzQccA9UGhHU=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/olareg/olareg v0.1.1 h1:Ui7q93zjcoF+U9U71sgqgZWByDoZOpqHitUXEu2xV+g=
|
||||
github.com/olareg/olareg v0.1.1/go.mod h1:w8NP4SWrHHtxsFaUiv1lnCnYPm4sN1seCd2h7FK/dc0=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/regclient/regclient v0.8.2 h1:23BQ3jWgKYHHIXUhp/S9laVJDHDoOQaQCzXMJ4undVE=
|
||||
github.com/regclient/regclient v0.8.2/go.mod h1:uGyetv0o6VLyRDjtfeBqp/QBwRLJ3Hcn07/+8QbhNcM=
|
||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs=
|
||||
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
|
||||
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
|
||||
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/urfave/cli/v3 v3.0.0-beta1 h1:6DTaaUarcM0wX7qj5Hcvs+5Dm3dyUTBbEwIWAjcw9Zg=
|
||||
github.com/urfave/cli/v3 v3.0.0-beta1/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y=
|
||||
go.bug.st/serial v1.6.4 h1:7FmqNPgVp3pu2Jz5PoPtbZ9jJO5gnEnZIvnI1lzve8A=
|
||||
go.bug.st/serial v1.6.4/go.mod h1:nofMJxTeNVny/m6+KaafC6vJGj3miwQZ6vW4BZUGJPI=
|
||||
go.bytecodealliance.org v0.6.2 h1:Jy4u5DVmSkXgsnwojBhJ+AD/YsJsR3VzVnxF0xRCqTQ=
|
||||
go.bytecodealliance.org v0.6.2/go.mod h1:gqjTJm0y9NSksG4py/lSjIQ/SNuIlOQ+hCIEPQwtJgA=
|
||||
go.bytecodealliance.org/cm v0.2.2 h1:M9iHS6qs884mbQbIjtLX1OifgyPG9DuMs2iwz8G4WQA=
|
||||
go.bytecodealliance.org/cm v0.2.2/go.mod h1:JD5vtVNZv7sBoQQkvBvAAVKJPhR/bqBH7yYXTItMfZI=
|
||||
github.com/tetratelabs/wazero v1.6.0 h1:z0H1iikCdP8t+q341xqepY4EWvHEw8Es7tlqiVzlP3g=
|
||||
github.com/tetratelabs/wazero v1.6.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A=
|
||||
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.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
@@ -102,7 +44,6 @@ golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
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=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
@@ -115,10 +56,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
|
||||
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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
tinygo.org/x/espflasher v0.6.0 h1:CHbGMHAIWq1tB8FKd/QwBpNFw1jvHHxpmvZiF+QOYUo=
|
||||
tinygo.org/x/espflasher v0.6.0/go.mod h1:tr5u08HoE67WD5zxJesCiiVF/R1b6Akz3yXwh5zah8U=
|
||||
tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74 h1:ovavgTdIBWCH8YWlcfq9gkpoyT1+IxMKSn+Df27QwE8=
|
||||
tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
// Version of TinyGo.
|
||||
// Update this value before release of new version of software.
|
||||
const version = "0.41.0"
|
||||
const version = "0.41.0-dev"
|
||||
|
||||
// Return TinyGo version, either in the form 0.30.0 or as a development version
|
||||
// (like 0.30.0-dev-abcd012).
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// TODO: remove this (by merging it into the top-level go.mod)
|
||||
// once the top level go.mod specifies a go new enough to make our version of misspell happy.
|
||||
|
||||
module tools
|
||||
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/golangci/misspell v0.6.0
|
||||
github.com/mgechev/revive v1.3.9
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
github.com/chavacava/garif v0.1.0 // indirect
|
||||
github.com/fatih/color v1.17.0 // indirect
|
||||
github.com/fatih/structtag v1.2.0 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.23.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,56 @@
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc=
|
||||
github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs=
|
||||
github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 h1:zpIH83+oKzcpryru8ceC6BxnoG8TBrhgAvRg8obzup0=
|
||||
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg=
|
||||
github.com/mgechev/revive v1.3.9 h1:18Y3R4a2USSBF+QZKFQwVkBROUda7uoBlkEuBD+YD1A=
|
||||
github.com/mgechev/revive v1.3.9/go.mod h1:+uxEIr5UH0TjXWHTno3xh4u7eg6jDpXKzQccA9UGhHU=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.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.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
|
||||
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -2,15 +2,12 @@
|
||||
|
||||
// Install tools specified in go.mod.
|
||||
// See https://marcofranssen.nl/manage-go-tools-via-go-modules for idiom.
|
||||
package main
|
||||
package tools
|
||||
|
||||
import (
|
||||
_ "github.com/golangci/misspell"
|
||||
_ "github.com/mgechev/revive"
|
||||
_ "go.bytecodealliance.org/cm"
|
||||
_ "go.bytecodealliance.org/cmd/wit-bindgen-go"
|
||||
)
|
||||
|
||||
//go:generate go install github.com/golangci/misspell/cmd/misspell
|
||||
//go:generate go install github.com/mgechev/revive
|
||||
//go:generate go install go.bytecodealliance.org/cmd/wit-bindgen-go
|
||||
@@ -0,0 +1,5 @@
|
||||
# wasm-tools directory
|
||||
|
||||
This directory has a separate `go.mod` file because the `wasm-tools-go` module requires Go 1.22, while TinyGo itself supports Go 1.19.
|
||||
|
||||
When the minimum Go version for TinyGo is 1.22, this directory can be folded into `internal/tools` and the `go.mod` and `go.sum` files deleted.
|
||||
@@ -0,0 +1,22 @@
|
||||
module github.com/tinygo-org/tinygo/internal/wasm-tools
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
go.bytecodealliance.org v0.6.2
|
||||
go.bytecodealliance.org/cm v0.2.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/coreos/go-semver v0.3.1 // indirect
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/regclient/regclient v0.8.2 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/tetratelabs/wazero v1.9.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||
github.com/urfave/cli/v3 v3.0.0-beta1 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,48 @@
|
||||
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
|
||||
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/olareg/olareg v0.1.1 h1:Ui7q93zjcoF+U9U71sgqgZWByDoZOpqHitUXEu2xV+g=
|
||||
github.com/olareg/olareg v0.1.1/go.mod h1:w8NP4SWrHHtxsFaUiv1lnCnYPm4sN1seCd2h7FK/dc0=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/regclient/regclient v0.8.2 h1:23BQ3jWgKYHHIXUhp/S9laVJDHDoOQaQCzXMJ4undVE=
|
||||
github.com/regclient/regclient v0.8.2/go.mod h1:uGyetv0o6VLyRDjtfeBqp/QBwRLJ3Hcn07/+8QbhNcM=
|
||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
|
||||
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
|
||||
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/urfave/cli/v3 v3.0.0-beta1 h1:6DTaaUarcM0wX7qj5Hcvs+5Dm3dyUTBbEwIWAjcw9Zg=
|
||||
github.com/urfave/cli/v3 v3.0.0-beta1/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y=
|
||||
go.bytecodealliance.org v0.6.2 h1:Jy4u5DVmSkXgsnwojBhJ+AD/YsJsR3VzVnxF0xRCqTQ=
|
||||
go.bytecodealliance.org v0.6.2/go.mod h1:gqjTJm0y9NSksG4py/lSjIQ/SNuIlOQ+hCIEPQwtJgA=
|
||||
go.bytecodealliance.org/cm v0.2.2 h1:M9iHS6qs884mbQbIjtLX1OifgyPG9DuMs2iwz8G4WQA=
|
||||
go.bytecodealliance.org/cm v0.2.2/go.mod h1:JD5vtVNZv7sBoQQkvBvAAVKJPhR/bqBH7yYXTItMfZI=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
||||
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -0,0 +1,12 @@
|
||||
//go:build tools
|
||||
|
||||
// Install tools specified in go.mod.
|
||||
// See https://marcofranssen.nl/manage-go-tools-via-go-modules for idiom.
|
||||
package tools
|
||||
|
||||
import (
|
||||
_ "go.bytecodealliance.org/cm"
|
||||
_ "go.bytecodealliance.org/cmd/wit-bindgen-go"
|
||||
)
|
||||
|
||||
//go:generate go install go.bytecodealliance.org/cmd/wit-bindgen-go
|
||||
+1
-1
@@ -29,7 +29,7 @@ var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not
|
||||
func isRecoverableError(err error) bool {
|
||||
return err == errIntegerAsPointer || err == errUnsupportedInst ||
|
||||
err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated ||
|
||||
err == errLoopUnrolled || err == errInvalidPtrToIntSize
|
||||
err == errLoopUnrolled
|
||||
}
|
||||
|
||||
// ErrorLine is one line in a traceback. The position may be missing.
|
||||
|
||||
@@ -413,6 +413,64 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
// Special function that will trigger an error.
|
||||
// This is used to test error reporting.
|
||||
return nil, mem, r.errorAt(inst, errors.New("test error"))
|
||||
case strings.HasSuffix(callFn.name, ".$typeassert"):
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"interface assert:", operands[1:])
|
||||
}
|
||||
|
||||
// Load various values for the interface implements check below.
|
||||
typecodePtr, err := operands[1].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
// typecodePtr always point to the numMethod field in the type
|
||||
// description struct. The methodSet, when present, comes right
|
||||
// before the numMethod field (the compiler doesn't generate
|
||||
// method sets for concrete types without methods).
|
||||
// Considering that the compiler doesn't emit interface type
|
||||
// asserts for interfaces with no methods (as the always succeed)
|
||||
// then if the offset is zero, this assert must always fail.
|
||||
if typecodePtr.offset() == 0 {
|
||||
locals[inst.localIndex] = literalValue{uint8(0)}
|
||||
break
|
||||
}
|
||||
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)
|
||||
}
|
||||
methodSet := mem.get(methodSetPtr.index()).llvmGlobal.Initializer()
|
||||
numMethods := int(r.builder.CreateExtractValue(methodSet, 0, "").ZExtValue())
|
||||
llvmFn := inst.llvmInst.CalledValue()
|
||||
methodSetAttr := llvmFn.GetStringAttributeAtIndex(-1, "tinygo-methods")
|
||||
methodSetString := methodSetAttr.GetStringValue()
|
||||
|
||||
// Make a set of all the methods on the concrete type, for
|
||||
// easier checking in the next step.
|
||||
concreteTypeMethods := map[string]struct{}{}
|
||||
for i := 0; i < numMethods; i++ {
|
||||
methodInfo := r.builder.CreateExtractValue(methodSet, 1, "")
|
||||
name := r.builder.CreateExtractValue(methodInfo, i, "").Name()
|
||||
concreteTypeMethods[name] = struct{}{}
|
||||
}
|
||||
|
||||
// Check whether all interface methods are also in the list
|
||||
// of defined methods calculated above. This is the interface
|
||||
// assert itself.
|
||||
assertOk := uint8(1) // i1 true
|
||||
for _, name := range strings.Split(methodSetString, "; ") {
|
||||
if _, ok := concreteTypeMethods[name]; !ok {
|
||||
// There is a method on the interface that is not
|
||||
// implemented by the type. The assertion will fail.
|
||||
assertOk = 0 // i1 false
|
||||
break
|
||||
}
|
||||
}
|
||||
// If assertOk is still 1, the assertion succeeded.
|
||||
locals[inst.localIndex] = literalValue{assertOk}
|
||||
case strings.HasSuffix(callFn.name, "$invoke"):
|
||||
// This thunk is the interface method dispatcher: it is called
|
||||
// with all regular parameters and a type code. It will then
|
||||
|
||||
+1
-1
Submodule lib/stm32-svd updated: bfed180b63...e6db8e32d5
@@ -245,7 +245,6 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
|
||||
"internal/cm/": false,
|
||||
"internal/futex/": false,
|
||||
"internal/fuzz/": false,
|
||||
"internal/itoa": false,
|
||||
"internal/reflectlite/": false,
|
||||
"internal/gclayout": false,
|
||||
"internal/task/": false,
|
||||
@@ -268,31 +267,11 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
|
||||
paths["crypto/internal/boring/sig/"] = false
|
||||
}
|
||||
|
||||
if goMinor >= 26 {
|
||||
// Go 1.26 added a CPU jitter entropy source for FIPS 140-3 that
|
||||
// allocates a 32 MiB global buffer (ScratchBuffer [1<<25]byte).
|
||||
// This is fine on systems with virtual memory, but causes RAM
|
||||
// overflow on microcontrollers. Replace with a zero-size stub
|
||||
// since TinyGo targets never use FIPS jitter entropy.
|
||||
paths["crypto/internal/entropy/"] = true
|
||||
paths["crypto/internal/entropy/v1.0.0/"] = false
|
||||
}
|
||||
|
||||
if needsSyscallPackage {
|
||||
paths["syscall/"] = true // include syscall/js
|
||||
paths["internal/syscall/"] = true
|
||||
paths["internal/syscall/unix/"] = false
|
||||
}
|
||||
|
||||
if goMinor >= 26 {
|
||||
// Go 1.26 added SWAR optimizations to unicode/utf8 that use
|
||||
// constants assuming at least 32-bit uintptr. TinyGo supports
|
||||
// 16-bit targets (AVR) where these constants overflow, so we
|
||||
// provide a patched version.
|
||||
paths["unicode/"] = true
|
||||
paths["unicode/utf8/"] = false
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
|
||||
+41
-55
@@ -549,7 +549,7 @@ func (p *Package) extractEmbedLines(addError func(error)) {
|
||||
}
|
||||
|
||||
// Look for //go:embed comments.
|
||||
var allPatterns []embedPattern
|
||||
var allPatterns []string
|
||||
for _, comment := range doc.List {
|
||||
if comment.Text != "//go:embed" && !strings.HasPrefix(comment.Text, "//go:embed ") {
|
||||
continue
|
||||
@@ -577,7 +577,21 @@ func (p *Package) extractEmbedLines(addError func(error)) {
|
||||
})
|
||||
continue
|
||||
}
|
||||
allPatterns = append(allPatterns, patterns...)
|
||||
for _, pattern := range patterns {
|
||||
// Check that the pattern is well-formed.
|
||||
// It must be valid: the Go toolchain has already
|
||||
// checked for invalid patterns. But let's check
|
||||
// anyway to be sure.
|
||||
if _, err := path.Match(pattern, ""); err != nil {
|
||||
addError(types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: comment.Pos(),
|
||||
Msg: "invalid pattern syntax",
|
||||
})
|
||||
continue
|
||||
}
|
||||
allPatterns = append(allPatterns, pattern)
|
||||
}
|
||||
}
|
||||
|
||||
if len(allPatterns) != 0 {
|
||||
@@ -610,8 +624,8 @@ func (p *Package) extractEmbedLines(addError func(error)) {
|
||||
// Match all //go:embed patterns against the embed files
|
||||
// provided by `go list`.
|
||||
for _, name := range p.EmbedFiles {
|
||||
for _, ep := range allPatterns {
|
||||
if ep.Match(name) {
|
||||
for _, pattern := range allPatterns {
|
||||
if matchPattern(pattern, name) {
|
||||
p.EmbedGlobals[globalName] = append(p.EmbedGlobals[globalName], &EmbedFile{
|
||||
Name: name,
|
||||
NeedsData: byteSlice,
|
||||
@@ -627,37 +641,13 @@ func (p *Package) extractEmbedLines(addError func(error)) {
|
||||
}
|
||||
}
|
||||
|
||||
// embedPattern represents a valid //go:embed pattern.
|
||||
// See: https://pkg.go.dev/embed#hdr-Directives
|
||||
type embedPattern struct {
|
||||
pattern string // glob pattern without "all:" prefix
|
||||
includeHidden bool // true if "all:" prefix was present
|
||||
}
|
||||
|
||||
// newEmbedPattern parses and validates a //go:embed pattern.
|
||||
// The "all:" prefix (if present) is stripped and reflected in includeHidden.
|
||||
// Returns an error if the pattern syntax is invalid.
|
||||
func newEmbedPattern(s string) (embedPattern, error) {
|
||||
ep := embedPattern{pattern: s}
|
||||
if strings.HasPrefix(s, "all:") {
|
||||
ep.pattern = s[4:]
|
||||
ep.includeHidden = true
|
||||
}
|
||||
if _, err := path.Match(ep.pattern, ""); err != nil {
|
||||
return embedPattern{}, err
|
||||
}
|
||||
return ep, nil
|
||||
}
|
||||
|
||||
// Match returns true if (and only if) the pattern matches the given filename.
|
||||
// The pattern could also match a parent directory of name, in which case hidden
|
||||
// files do not match (unless includeHidden is true).
|
||||
func (ep embedPattern) Match(name string) bool {
|
||||
pattern := ep.pattern
|
||||
includeHidden := ep.includeHidden
|
||||
|
||||
// matchPattern returns true if (and only if) the given pattern would match the
|
||||
// filename. The pattern could also match a parent directory of name, in which
|
||||
// case hidden files do not match.
|
||||
func matchPattern(pattern, name string) bool {
|
||||
// Match this file.
|
||||
if matched, _ := path.Match(pattern, name); matched {
|
||||
matched, _ := path.Match(pattern, name)
|
||||
if matched {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -671,20 +661,17 @@ func (ep embedPattern) Match(name string) bool {
|
||||
dir = path.Clean(dir)
|
||||
if matched, _ := path.Match(pattern, dir); matched {
|
||||
// Pattern matches the directory.
|
||||
if !includeHidden {
|
||||
suffix := name[len(dir):]
|
||||
if strings.Contains(suffix, "/_") || strings.Contains(suffix, "/.") {
|
||||
// Pattern matches a hidden file.
|
||||
// Hidden files are included when listed directly as a
|
||||
// pattern, or when the "all:" prefix is used, but not
|
||||
// when they are part of a directory tree without "all:".
|
||||
// Source:
|
||||
// > If a pattern names a directory, all files in the
|
||||
// > subtree rooted at that directory are embedded
|
||||
// > (recursively), except that files with names beginning
|
||||
// > with ‘.’ or ‘_’ are excluded.
|
||||
return false
|
||||
}
|
||||
suffix := name[len(dir):]
|
||||
if strings.Contains(suffix, "/_") || strings.Contains(suffix, "/.") {
|
||||
// Pattern matches a hidden file.
|
||||
// Hidden files are included when listed directly as a
|
||||
// pattern, but not when they are part of a directory tree.
|
||||
// Source:
|
||||
// > If a pattern names a directory, all files in the
|
||||
// > subtree rooted at that directory are embedded
|
||||
// > (recursively), except that files with names beginning
|
||||
// > with ‘.’ or ‘_’ are excluded.
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -693,12 +680,11 @@ func (ep embedPattern) Match(name string) bool {
|
||||
|
||||
// parseGoEmbed is like strings.Fields but for a //go:embed line. It parses
|
||||
// regular fields and quoted fields (that may contain spaces).
|
||||
func (p *Package) parseGoEmbed(args string, pos token.Pos) (patterns []embedPattern, err error) {
|
||||
func (p *Package) parseGoEmbed(args string, pos token.Pos) (patterns []string, err error) {
|
||||
args = strings.TrimSpace(args)
|
||||
initialLen := len(args)
|
||||
for args != "" {
|
||||
patternPos := pos + token.Pos(initialLen-len(args))
|
||||
var pattern string
|
||||
switch args[0] {
|
||||
case '`', '"', '\\':
|
||||
// Parse the next pattern using the Go scanner.
|
||||
@@ -717,7 +703,8 @@ func (p *Package) parseGoEmbed(args string, pos token.Pos) (patterns []embedPatt
|
||||
Msg: "invalid quoted string in //go:embed",
|
||||
}
|
||||
}
|
||||
pattern = constant.StringVal(constant.MakeFromLiteral(lit, tok, 0))
|
||||
pattern := constant.StringVal(constant.MakeFromLiteral(lit, tok, 0))
|
||||
patterns = append(patterns, pattern)
|
||||
args = strings.TrimLeftFunc(args[len(lit):], unicode.IsSpace)
|
||||
default:
|
||||
// The value is just a regular value.
|
||||
@@ -726,18 +713,17 @@ func (p *Package) parseGoEmbed(args string, pos token.Pos) (patterns []embedPatt
|
||||
if index < 0 {
|
||||
index = len(args)
|
||||
}
|
||||
pattern = args[:index]
|
||||
pattern := args[:index]
|
||||
patterns = append(patterns, pattern)
|
||||
args = strings.TrimLeftFunc(args[len(pattern):], unicode.IsSpace)
|
||||
}
|
||||
ep, err := newEmbedPattern(pattern)
|
||||
if err != nil {
|
||||
if _, err := path.Match(patterns[len(patterns)-1], ""); err != nil {
|
||||
return nil, types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: patternPos,
|
||||
Msg: "invalid pattern syntax",
|
||||
}
|
||||
}
|
||||
patterns = append(patterns, ep)
|
||||
}
|
||||
return patterns, nil
|
||||
}
|
||||
|
||||
@@ -32,15 +32,12 @@ import (
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
"golang.org/x/tools/go/buildutil"
|
||||
"tinygo.org/x/espflasher/pkg/espflasher"
|
||||
"tinygo.org/x/go-llvm"
|
||||
|
||||
"go.bug.st/serial"
|
||||
"go.bug.st/serial/enumerator"
|
||||
)
|
||||
|
||||
var errInheritableOnly = errors.New("target is inheritable-only, which means it cannot be used directly for building or flashing")
|
||||
|
||||
// commandError is an error type to wrap os/exec.Command errors. This provides
|
||||
// some more information regarding what went wrong while running a command.
|
||||
type commandError struct {
|
||||
@@ -142,10 +139,6 @@ func printCommand(cmd string, args ...string) {
|
||||
|
||||
// Build compiles and links the given package and writes it to outpath.
|
||||
func Build(pkgName, outpath string, config *compileopts.Config) error {
|
||||
if config.Target != nil && config.Target.InheritableOnly {
|
||||
return errInheritableOnly
|
||||
}
|
||||
|
||||
// Create a temporary directory for intermediary files.
|
||||
tmpdir, err := os.MkdirTemp("", "tinygo")
|
||||
if err != nil {
|
||||
@@ -363,10 +356,6 @@ func Flash(pkgName, port, outpath string, options *compileopts.Options) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.Target != nil && config.Target.InheritableOnly {
|
||||
return errInheritableOnly
|
||||
}
|
||||
|
||||
// determine the type of file to compile
|
||||
var fileExt string
|
||||
|
||||
@@ -396,10 +385,6 @@ func Flash(pkgName, port, outpath string, options *compileopts.Options) error {
|
||||
fileExt = ".hex"
|
||||
case "bmp":
|
||||
fileExt = ".elf"
|
||||
case "adb":
|
||||
fileExt = ".hex"
|
||||
case "esp32flash", "esp32jtag":
|
||||
fileExt = ".bin"
|
||||
case "native":
|
||||
return errors.New("unknown flash method \"native\" - did you miss a -target flag?")
|
||||
default:
|
||||
@@ -534,56 +519,6 @@ func Flash(pkgName, port, outpath string, options *compileopts.Options) error {
|
||||
if err != nil {
|
||||
return &commandError{"failed to flash", result.Binary, err}
|
||||
}
|
||||
case "adb":
|
||||
// Run pre-flash adb shell commands.
|
||||
for _, preCmd := range config.Target.ADBPreCommands {
|
||||
cmd := executeCommand(config.Options, "adb", "shell", preCmd)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return &commandError{"failed to run adb pre-command", preCmd, err}
|
||||
}
|
||||
}
|
||||
|
||||
// Push the binary to the device.
|
||||
if config.Target.ADBPushRemote == "" {
|
||||
return errors.New("invalid target file: flash-method was set to \"adb\" but no adb-push-remote was set")
|
||||
}
|
||||
cmd := executeCommand(config.Options, "adb", "push", result.Binary, config.Target.ADBPushRemote)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return &commandError{"adb push failed to " + config.Target.ADBPushRemote, result.Binary, err}
|
||||
}
|
||||
|
||||
// Run post-flash adb shell commands.
|
||||
for _, postCmd := range config.Target.ADBPostCommands {
|
||||
postCmd = strings.ReplaceAll(postCmd, "{remote}", config.Target.ADBPushRemote)
|
||||
cmd := executeCommand(config.Options, "adb", "shell", postCmd)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return &commandError{"failed to run adb post-command", postCmd, err}
|
||||
}
|
||||
}
|
||||
case "esp32flash":
|
||||
port, err := getDefaultPort(port, config.Target.SerialPort)
|
||||
if err != nil {
|
||||
return &commandError{"failed to find port", port, err}
|
||||
}
|
||||
|
||||
if err := flashBinUsingEsp32(port, classicReset, result.Binary, config.Options); err != nil {
|
||||
return &commandError{"failed to flash", result.Binary, err}
|
||||
}
|
||||
case "esp32jtag":
|
||||
port, err := getDefaultPort(port, config.Target.SerialPort)
|
||||
if err != nil {
|
||||
return &commandError{"failed to find port", port, err}
|
||||
}
|
||||
|
||||
if err := flashBinUsingEsp32(port, jtagReset, result.Binary, config.Options); err != nil {
|
||||
return &commandError{"failed to flash", result.Binary, err}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown flash method: %s", flashMethod)
|
||||
}
|
||||
@@ -830,10 +765,6 @@ func Run(pkgName string, options *compileopts.Options, cmdArgs []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.Target != nil && config.Target.InheritableOnly {
|
||||
return errInheritableOnly
|
||||
}
|
||||
|
||||
_, err = buildAndRun(pkgName, config, os.Stdout, cmdArgs, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error {
|
||||
return cmd.Run()
|
||||
})
|
||||
@@ -1088,68 +1019,6 @@ func flashHexUsingMSD(volumes []string, tmppath string, options *compileopts.Opt
|
||||
return errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
|
||||
}
|
||||
|
||||
const (
|
||||
defaultReset = "default"
|
||||
classicReset = "classic"
|
||||
jtagReset = "jtag"
|
||||
)
|
||||
|
||||
func flashBinUsingEsp32(port, resetMode, tmppath string, options *compileopts.Options) error {
|
||||
opts := espflasher.DefaultOptions()
|
||||
opts.Compress = true
|
||||
opts.Logger = &espflasher.StdoutLogger{W: os.Stdout}
|
||||
if options.BaudRate != 0 {
|
||||
opts.FlashBaudRate = options.BaudRate
|
||||
}
|
||||
|
||||
if resetMode == jtagReset {
|
||||
opts.ResetMode = espflasher.ResetUSBJTAG
|
||||
}
|
||||
|
||||
flasher, err := espflasher.New(port, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer flasher.Close()
|
||||
|
||||
chipName := flasher.ChipName()
|
||||
offset := uint32(0x0)
|
||||
if chipName == "ESP32" {
|
||||
offset = 0x1000
|
||||
}
|
||||
|
||||
// Read the firmware binary
|
||||
data, err := os.ReadFile(tmppath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := flasher.EraseFlash(); err != nil {
|
||||
return fmt.Errorf("erase failed: %v", err)
|
||||
}
|
||||
|
||||
progress := func(current, total int) {
|
||||
pct := float64(current) / float64(total) * 100
|
||||
bar := int(pct / 2)
|
||||
fmt.Printf("\r[%-50s] %6.1f%%", strings.Repeat("#", bar)+strings.Repeat(".", 50-bar), pct)
|
||||
if current >= total {
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
// Flash with progress reporting
|
||||
err = flasher.FlashImage(data, offset, progress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// Reset the device to run the new firmware
|
||||
flasher.Reset()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type mountPoint struct {
|
||||
name string
|
||||
path string
|
||||
|
||||
+1
-4
@@ -107,9 +107,6 @@ func TestBuild(t *testing.T) {
|
||||
if minor >= 23 {
|
||||
tests = append(tests, "go1.23/")
|
||||
}
|
||||
if minor >= 24 {
|
||||
tests = append(tests, "typealias.go")
|
||||
}
|
||||
|
||||
if *testTarget != "" {
|
||||
// This makes it possible to run one specific test (instead of all),
|
||||
@@ -454,7 +451,7 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
||||
|
||||
// Build the test binary.
|
||||
stdout := &bytes.Buffer{}
|
||||
_, err = buildAndRun(pkgName, config, stdout, cmdArgs, environmentVars, 2*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 {
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
// Copyright 2025 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.
|
||||
|
||||
// Stripped-down version of the entropy package for TinyGo.
|
||||
//
|
||||
// The upstream Go 1.26 package allocates a [1<<25]byte (32 MiB) global buffer
|
||||
// for CPU jitter-based SP 800-90B entropy collection. This is fine on systems
|
||||
// with virtual memory where BSS pages are lazily backed, but it causes a fatal
|
||||
// "RAM overflowed" error on microcontrollers and other memory-constrained
|
||||
// targets.
|
||||
//
|
||||
// Because FIPS 140-3 jitter entropy is never used on TinyGo targets (the DRBG
|
||||
// falls through to sysrand.Read when fips140.Enabled is false), this overlay
|
||||
// replaces the 32 MiB buffer with a zero-size type.
|
||||
package entropy
|
||||
|
||||
// Version returns the version of the entropy source.
|
||||
func Version() string {
|
||||
return "v1.0.0"
|
||||
}
|
||||
|
||||
// ScratchBuffer is a large buffer in upstream Go (32 MiB). TinyGo replaces it
|
||||
// with a zero-size type since the CPU jitter entropy source is not used.
|
||||
type ScratchBuffer [0]byte
|
||||
|
||||
// Seed returns a 384-bit seed with full entropy.
|
||||
// On TinyGo targets this is never called because FIPS mode is not enabled.
|
||||
func Seed(memory *ScratchBuffer) ([48]byte, error) {
|
||||
panic("entropy: CPU jitter entropy source is not supported on TinyGo targets")
|
||||
}
|
||||
|
||||
// Samples collects entropy samples. Not supported on TinyGo targets.
|
||||
func Samples(samples []uint8, memory *ScratchBuffer) error {
|
||||
panic("entropy: CPU jitter entropy source is not supported on TinyGo targets")
|
||||
}
|
||||
|
||||
// SHA384 computes SHA-384 over 1024 bytes. Not supported on TinyGo targets.
|
||||
func SHA384(p *[1024]byte) [48]byte {
|
||||
panic("entropy: CPU jitter entropy source is not supported on TinyGo targets")
|
||||
}
|
||||
|
||||
// TestingOnlySHA384 computes SHA-384 over arbitrary bytes. Not supported on TinyGo targets.
|
||||
func TestingOnlySHA384(p []byte) [48]byte {
|
||||
panic("entropy: CPU jitter entropy source is not supported on TinyGo targets")
|
||||
}
|
||||
|
||||
// RepetitionCountTest implements the repetition count test from SP 800-90B.
|
||||
func RepetitionCountTest(samples []uint8) error {
|
||||
panic("entropy: CPU jitter entropy source is not supported on TinyGo targets")
|
||||
}
|
||||
|
||||
// AdaptiveProportionTest implements the adaptive proportion test from SP 800-90B.
|
||||
func AdaptiveProportionTest(samples []uint8) error {
|
||||
panic("entropy: CPU jitter entropy source is not supported on TinyGo targets")
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || esp32s3 || tkey || (tinygo.riscv32 && virt)
|
||||
//go:build nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || tkey || (tinygo.riscv32 && virt)
|
||||
|
||||
// If you update the above build constraint, you'll probably also need to update
|
||||
// src/runtime/rand_hwrng.go.
|
||||
|
||||
@@ -1,397 +0,0 @@
|
||||
// Startup code for the ESP32-S3 (Xtensa LX7, windowed ABI).
|
||||
//
|
||||
// The ROM bootloader loads IRAM/DRAM segments into SRAM but does NOT
|
||||
// configure flash cache/MMU. We must:
|
||||
// 1. Set up the windowed-ABI register file and stack pointer.
|
||||
// 2. Set VECBASE and clear PS.EXCM (needed for callx4 window overflows).
|
||||
// 3. Disable watchdog timers.
|
||||
// 4. Configure the flash cache and MMU so IROM/DROM are accessible.
|
||||
// 5. Jump to runtime.main (in IROM).
|
||||
//
|
||||
// Cache/MMU init sequence (from NuttX esp_loader.c / ESP-IDF bootloader / esp-hal):
|
||||
// Phase A — configure cache modes:
|
||||
// a. rom_config_instruction_cache_mode(16KB, 8-way, 32B)
|
||||
// b. rom_Cache_Suspend_DCache()
|
||||
// c. rom_config_data_cache_mode(32KB, 8-way, 32B)
|
||||
// d. Cache_Resume_DCache(0)
|
||||
// Phase B — map flash pages:
|
||||
// e. Disable caches
|
||||
// f. Cache_MMU_Init() — reset all MMU entries to invalid
|
||||
// g. Cache_Set_IDROM_MMU_Size() — set IROM/DROM entry split
|
||||
// h. Write MMU entries mapping flash page 0 for IROM and DROM
|
||||
// i. Clear bus-shut bits
|
||||
// j. Enable caches + isync
|
||||
|
||||
#define PS_WOE 0x00040000
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Boot entry point — placed in IRAM by the linker.
|
||||
// -----------------------------------------------------------------------
|
||||
.section .text.call_start_cpu0
|
||||
.literal_position
|
||||
.align 4
|
||||
.Lstack_top_addr:
|
||||
.long _stack_top
|
||||
.Lmain_addr:
|
||||
.long main
|
||||
.Lvector_table_addr:
|
||||
.long _vector_table
|
||||
// WDT register addresses
|
||||
.Lwdt_key:
|
||||
.long 0x50D83AA1
|
||||
.Lrtc_wdt_protect:
|
||||
.long 0x600080B0
|
||||
.Lrtc_wdt_config0:
|
||||
.long 0x60008098
|
||||
.Ltimg0_wdt_protect:
|
||||
.long 0x6001F064
|
||||
.Ltimg0_wdt_config0:
|
||||
.long 0x6001F048
|
||||
.Ltimg1_wdt_protect:
|
||||
.long 0x60020064
|
||||
.Ltimg1_wdt_config0:
|
||||
.long 0x60020048
|
||||
.Lswd_protect:
|
||||
.long 0x600080B8
|
||||
.Lswd_key:
|
||||
.long 0x8F1D312A
|
||||
.Lswd_conf:
|
||||
.long 0x600080B4
|
||||
.Lswd_disable:
|
||||
.long 0x40000000
|
||||
// ROM function addresses (from ESP-IDF esp32s3.rom.ld)
|
||||
.Lrom_config_icache:
|
||||
.long 0x40001a1c
|
||||
.Lrom_config_dcache:
|
||||
.long 0x40001a28
|
||||
.Lrom_suspend_dcache:
|
||||
.long 0x400018b4
|
||||
.LCache_Resume_DCache:
|
||||
.long 0x400018c0
|
||||
.LCache_Disable_ICache:
|
||||
.long 0x4000186c
|
||||
.LCache_Disable_DCache:
|
||||
.long 0x40001884
|
||||
.LCache_MMU_Init:
|
||||
.long 0x40001998
|
||||
.LCache_Set_IDROM_MMU_Size:
|
||||
.long 0x40001914
|
||||
.LCache_Enable_ICache:
|
||||
.long 0x40001878
|
||||
.LCache_Enable_DCache:
|
||||
.long 0x40001890
|
||||
// Cache/MMU register addresses
|
||||
.Lmmu_table_base:
|
||||
.long 0x600C5000
|
||||
.Licache_ctrl1_reg:
|
||||
.long 0x600C4064
|
||||
.Ldcache_ctrl1_reg:
|
||||
.long 0x600C4004
|
||||
// End-of-section symbols for multi-page MMU mapping.
|
||||
.Lirom_end:
|
||||
.long _irom_end
|
||||
.Ldrom_end:
|
||||
.long _drom_end
|
||||
.Lirom_base:
|
||||
.long 0x42000000
|
||||
.Ldrom_base:
|
||||
.long 0x3C000000
|
||||
|
||||
.global call_start_cpu0
|
||||
call_start_cpu0:
|
||||
|
||||
// ---- 1. Windowed-ABI register file setup ----
|
||||
|
||||
// Disable WOE so we can safely manipulate WINDOWSTART.
|
||||
rsr.ps a2
|
||||
movi a3, ~(PS_WOE)
|
||||
and a2, a2, a3
|
||||
wsr.ps a2
|
||||
rsync
|
||||
|
||||
// Set WINDOWSTART to 1 << WINDOWBASE (mark only current window as valid).
|
||||
rsr.windowbase a2
|
||||
ssl a2
|
||||
movi a2, 1
|
||||
sll a2, a2
|
||||
wsr.windowstart a2
|
||||
rsync
|
||||
|
||||
// Load stack pointer.
|
||||
l32r a1, .Lstack_top_addr
|
||||
|
||||
// Re-enable WOE.
|
||||
rsr.ps a2
|
||||
movi a3, PS_WOE
|
||||
or a2, a2, a3
|
||||
wsr.ps a2
|
||||
rsync
|
||||
|
||||
// Enable FPU (coprocessor 0).
|
||||
movi a2, 1
|
||||
wsr.cpenable a2
|
||||
rsync
|
||||
|
||||
// ---- 2. Disable all watchdog timers (IMMEDIATELY, before any delay) ----
|
||||
l32r a3, .Lwdt_key
|
||||
movi a4, 0
|
||||
|
||||
// RTC WDT
|
||||
l32r a2, .Lrtc_wdt_protect
|
||||
memw
|
||||
s32i a3, a2, 0
|
||||
l32r a5, .Lrtc_wdt_config0
|
||||
memw
|
||||
s32i a4, a5, 0
|
||||
memw
|
||||
s32i a4, a2, 0
|
||||
|
||||
// TIMG0 WDT
|
||||
l32r a2, .Ltimg0_wdt_protect
|
||||
memw
|
||||
s32i a3, a2, 0
|
||||
l32r a5, .Ltimg0_wdt_config0
|
||||
memw
|
||||
s32i a4, a5, 0
|
||||
memw
|
||||
s32i a4, a2, 0
|
||||
|
||||
// TIMG1 WDT
|
||||
l32r a2, .Ltimg1_wdt_protect
|
||||
memw
|
||||
s32i a3, a2, 0
|
||||
l32r a5, .Ltimg1_wdt_config0
|
||||
memw
|
||||
s32i a4, a5, 0
|
||||
memw
|
||||
s32i a4, a2, 0
|
||||
|
||||
// Super WDT
|
||||
l32r a2, .Lswd_protect
|
||||
l32r a3, .Lswd_key
|
||||
memw
|
||||
s32i a3, a2, 0
|
||||
l32r a5, .Lswd_conf
|
||||
l32r a6, .Lswd_disable
|
||||
memw
|
||||
s32i a6, a5, 0
|
||||
memw
|
||||
s32i a4, a2, 0
|
||||
|
||||
// ---- 3. Set VECBASE and clear PS.EXCM ----
|
||||
// VECBASE must be set before any callx4 so that window overflow
|
||||
// exceptions (triggered by register window rotation) route to our
|
||||
// handlers in IRAM, not the ROM's default vectors.
|
||||
l32r a8, .Lvector_table_addr
|
||||
wsr.vecbase a8
|
||||
rsync
|
||||
|
||||
// Clear PS.EXCM (bit 4) and PS.INTLEVEL (bits 0-3).
|
||||
// The ROM bootloader may leave EXCM=1; with EXCM set any callx4
|
||||
// window overflow would become a double exception.
|
||||
// Set PS.UM (bit 5) so level-1 exceptions route to User vector.
|
||||
rsr.ps a2
|
||||
movi a3, ~0x1F
|
||||
and a2, a2, a3
|
||||
movi a3, 0x20
|
||||
or a2, a2, a3
|
||||
wsr.ps a2
|
||||
rsync
|
||||
|
||||
// ---- 4. Configure flash cache and MMU ----
|
||||
//
|
||||
// ROM function calls use callx4 (windowed ABI):
|
||||
// a4 = target address (overwritten with return addr by call mechanism)
|
||||
// a5 = stack pointer for callee (becomes callee's a1 via entry)
|
||||
// a6 = first argument (becomes callee's a2)
|
||||
// a7 = second argument (becomes callee's a3)
|
||||
// a8 = third argument (becomes callee's a4)
|
||||
// Registers a0-a3 are preserved across callx4; a4-a11 may be clobbered.
|
||||
|
||||
// Phase A: Configure cache modes (required for cache hardware to function).
|
||||
// Without this, the cache doesn't know its size/associativity/line-size
|
||||
// and cannot service flash accesses.
|
||||
|
||||
// 4a. Configure ICache mode: 16KB, 8-way, 32-byte line
|
||||
movi a6, 0x4000 // cache_size = 16KB
|
||||
movi a7, 8 // ways = 8
|
||||
movi a8, 32 // line_size = 32
|
||||
mov a5, a1
|
||||
l32r a4, .Lrom_config_icache
|
||||
callx4 a4
|
||||
|
||||
// 4b. Suspend DCache before configuring it
|
||||
mov a5, a1
|
||||
l32r a4, .Lrom_suspend_dcache
|
||||
callx4 a4
|
||||
|
||||
// 4c. Configure DCache mode: 32KB, 8-way, 32-byte line
|
||||
movi a6, 0x8000 // cache_size = 32KB
|
||||
movi a7, 8 // ways = 8
|
||||
movi a8, 32 // line_size = 32
|
||||
mov a5, a1
|
||||
l32r a4, .Lrom_config_dcache
|
||||
callx4 a4
|
||||
|
||||
// 4d. Resume DCache
|
||||
movi a6, 0
|
||||
mov a5, a1
|
||||
l32r a4, .LCache_Resume_DCache
|
||||
callx4 a4
|
||||
|
||||
// Phase B: Map flash pages into MMU.
|
||||
|
||||
// 4e. Disable ICache
|
||||
mov a5, a1
|
||||
l32r a4, .LCache_Disable_ICache
|
||||
callx4 a4
|
||||
|
||||
// 4f. Disable DCache
|
||||
mov a5, a1
|
||||
l32r a4, .LCache_Disable_DCache
|
||||
callx4 a4
|
||||
|
||||
// 4g. Initialize MMU (resets all 512 entries to invalid = 0x4000)
|
||||
mov a5, a1
|
||||
l32r a4, .LCache_MMU_Init
|
||||
callx4 a4
|
||||
|
||||
// 4h. Set IDROM MMU size: even 256/256 split.
|
||||
// Each entry is 4 bytes, so 256 entries = 0x400 bytes per region.
|
||||
movi a6, 0x400 // irom_mmu_size (256 entries × 4 bytes)
|
||||
movi a7, 0x400 // drom_mmu_size (256 entries × 4 bytes)
|
||||
mov a5, a1
|
||||
l32r a4, .LCache_Set_IDROM_MMU_Size
|
||||
callx4 a4
|
||||
|
||||
// 4i. Map flash pages for IROM and DROM using identity mapping.
|
||||
// MMU table at 0x600C5000: entries 0-255 = ICache, 256-511 = DCache.
|
||||
// Entry value N = flash page N (SOC_MMU_VALID = 0 on S3).
|
||||
// Each 64KB page needs one 4-byte entry.
|
||||
//
|
||||
// IROM: map pages 0..N where N = (_irom_end - 0x42000000) >> 16
|
||||
// DROM: map pages 0..M where M = (_drom_end - 0x3C000000) >> 16
|
||||
|
||||
l32r a8, .Lmmu_table_base // a8 = 0x600C5000
|
||||
|
||||
// --- IROM pages ---
|
||||
l32r a2, .Lirom_end // a2 = _irom_end (VMA in 0x42xxxxxx)
|
||||
l32r a3, .Lirom_base // a3 = 0x42000000
|
||||
sub a2, a2, a3 // a2 = byte offset past IROM base
|
||||
srli a2, a2, 16 // a2 = last page index
|
||||
addi a2, a2, 1 // a2 = number of pages to map
|
||||
movi a9, 0 // a9 = page counter (and entry value)
|
||||
mov a10, a8 // a10 = current MMU entry pointer
|
||||
.Lirom_loop:
|
||||
s32i a9, a10, 0
|
||||
addi a9, a9, 1
|
||||
addi a10, a10, 4
|
||||
blt a9, a2, .Lirom_loop
|
||||
|
||||
// --- DROM pages ---
|
||||
l32r a2, .Ldrom_end // a2 = _drom_end (VMA in 0x3Cxxxxxx)
|
||||
l32r a3, .Ldrom_base // a3 = 0x3C000000
|
||||
sub a2, a2, a3 // a2 = byte offset past DROM base
|
||||
srli a2, a2, 16 // a2 = last page index
|
||||
addi a2, a2, 1 // a2 = number of pages to map
|
||||
movi a9, 0 // a9 = page counter (and entry value)
|
||||
addmi a10, a8, 0x400 // a10 = 0x600C5400 (DCache entry 256)
|
||||
.Ldrom_loop:
|
||||
s32i a9, a10, 0
|
||||
addi a9, a9, 1
|
||||
addi a10, a10, 4
|
||||
blt a9, a2, .Ldrom_loop
|
||||
memw
|
||||
|
||||
// 4j. Clear bus-shut bits so core 0 can access ICache and DCache buses.
|
||||
l32r a8, .Licache_ctrl1_reg // 0x600C4064
|
||||
movi a9, 0
|
||||
s32i a9, a8, 0 // Clear all ICACHE_CTRL1 shut bits
|
||||
l32r a8, .Ldcache_ctrl1_reg // 0x600C4004
|
||||
s32i a9, a8, 0 // Clear all DCACHE_CTRL1 shut bits
|
||||
memw
|
||||
|
||||
// 4k. Enable ICache (arg: autoload = 0)
|
||||
movi a6, 0
|
||||
mov a5, a1
|
||||
l32r a4, .LCache_Enable_ICache
|
||||
callx4 a4
|
||||
|
||||
// 4l. Enable DCache (arg: autoload = 0)
|
||||
movi a6, 0
|
||||
mov a5, a1
|
||||
l32r a4, .LCache_Enable_DCache
|
||||
callx4 a4
|
||||
|
||||
// Flush instruction pipeline so new cache/MMU config takes effect.
|
||||
isync
|
||||
|
||||
// ---- 5. Jump to main (in IROM) ----
|
||||
// Re-clear PS.EXCM in case ROM calls changed processor state.
|
||||
rsr.ps a2
|
||||
movi a3, ~0x1F
|
||||
and a2, a2, a3
|
||||
movi a3, 0x20
|
||||
or a2, a2, a3
|
||||
wsr.ps a2
|
||||
rsync
|
||||
|
||||
mov a5, a1
|
||||
l32r a4, .Lmain_addr
|
||||
callx4 a4
|
||||
|
||||
// If main returns, loop forever.
|
||||
1: j 1b
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// tinygo_scanCurrentStack — Spill all Xtensa register windows to the
|
||||
// stack, then call tinygo_scanstack(sp) so the conservative GC can
|
||||
// discover live heap pointers that are currently in physical registers.
|
||||
//
|
||||
// On RISC-V / ARM the equivalent function pushes callee-saved registers
|
||||
// before the call. On Xtensa windowed ABI the same effect is achieved
|
||||
// by forcing hardware window-overflow for every occupied pane: each
|
||||
// overflow saves the four registers in that pane to the stack frame
|
||||
// pointed to by the pane's a1 (sp). After all panes are flushed, a
|
||||
// scan from the current sp to stackTop covers every live value.
|
||||
// -----------------------------------------------------------------------
|
||||
.section .text.tinygo_scanCurrentStack
|
||||
|
||||
.global tinygo_scanCurrentStack
|
||||
tinygo_scanCurrentStack:
|
||||
entry a1, 48
|
||||
|
||||
// Disable interrupts while flushing register windows.
|
||||
rsr a4, PS
|
||||
s32i a4, a1, 0 // save PS for later restore
|
||||
rsil a4, 3 // XCHAL_EXCM_LEVEL
|
||||
|
||||
// Flush all register windows using recursive call4.
|
||||
// For NAREG=64 (16 panes), 15 recursive levels cover all panes
|
||||
// except the current one (which is kept active).
|
||||
movi a6, 15
|
||||
call4 .Lscan_spill
|
||||
|
||||
// Restore interrupts.
|
||||
l32i a4, a1, 0
|
||||
wsr.ps a4
|
||||
rsync
|
||||
|
||||
// Pass current sp to tinygo_scanstack.
|
||||
// call4 maps caller's a5→callee's a1 (stack ptr for callee's entry)
|
||||
// and caller's a6→callee's a2 (first argument = sp).
|
||||
mov a5, a1 // callee's a1 = valid stack pointer
|
||||
mov a6, a1 // callee's a2 = sp argument
|
||||
call4 tinygo_scanstack
|
||||
|
||||
retw
|
||||
|
||||
.balign 4
|
||||
.Lscan_spill:
|
||||
entry a1, 16
|
||||
beqz a2, .Lscan_spill_done
|
||||
addi a2, a2, -1
|
||||
mov a6, a2
|
||||
call4 .Lscan_spill
|
||||
.Lscan_spill_done:
|
||||
retw
|
||||
@@ -75,10 +75,6 @@ handleInterruptASM:
|
||||
SFREG f30,(30 + 16)*REGSIZE(sp)
|
||||
SFREG f31,(31 + 16)*REGSIZE(sp)
|
||||
#endif
|
||||
// Save ra to a global so handleException can print the caller of the NULL
|
||||
// function pointer.
|
||||
la t0, tinygo_saved_ra
|
||||
SREG ra, 0(t0)
|
||||
call handleInterrupt
|
||||
#ifdef __riscv_flen
|
||||
LFREG f0, (31 + 16)*REGSIZE(sp)
|
||||
@@ -132,10 +128,3 @@ handleInterruptASM:
|
||||
LREG ra, 0*REGSIZE(sp)
|
||||
addi sp, sp, NREG*REGSIZE
|
||||
mret
|
||||
|
||||
.section .bss.tinygo_saved_ra
|
||||
.global tinygo_saved_ra
|
||||
.type tinygo_saved_ra,@object
|
||||
.align 2
|
||||
tinygo_saved_ra:
|
||||
.space REGSIZE
|
||||
|
||||
+12
-2
@@ -5,15 +5,25 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// This example assumes that an analog sensor such as a rotary dial is connected to pin ADC0.
|
||||
// When the dial is turned past the midway point, the built-in LED will light up.
|
||||
|
||||
func main() {
|
||||
machine.InitADC()
|
||||
|
||||
led := machine.LED
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
sensor := machine.ADC{machine.ADC2}
|
||||
sensor.Configure(machine.ADCConfig{})
|
||||
|
||||
for {
|
||||
val := sensor.Get()
|
||||
println(val)
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
if val < 0x8000 {
|
||||
led.Low()
|
||||
} else {
|
||||
led.High()
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build arduino_uno
|
||||
//go:build arduino
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
//go:build xiao_esp32s3
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
const (
|
||||
button = machine.D1
|
||||
buttonMode = machine.PinInputPullup
|
||||
buttonPinChange = machine.PinFalling
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
//go:build arduino_uno_q
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = &machine.TIM3
|
||||
pinA = machine.D3 // PB0 = TIM3_CH3
|
||||
pinB = machine.D6 // PB1 = TIM3_CH4
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build arduino_uno
|
||||
//go:build arduino
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
//go:build esp32c3_supermini
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = machine.PWM0
|
||||
pinA = machine.GPIO5
|
||||
pinB = machine.GPIO6
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
//go:build pico2 || pico_plus2
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = machine.PWM0 // Pin 25 (LED on pico2) corresponds to PWM0.
|
||||
//pinA = machine.LED
|
||||
pinA = machine.GPIO16
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
//go:build xiao_esp32s3
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = machine.PWM0
|
||||
pinA = machine.D0
|
||||
pinB = machine.D1
|
||||
)
|
||||
@@ -1,29 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
time.Sleep(2 * time.Second) // connect via serial
|
||||
buf1 := makeBuffer('|', 600)
|
||||
buf2 := makeBuffer('/', 600)
|
||||
println("start")
|
||||
serialWrite(buf1)
|
||||
serialWrite(buf2)
|
||||
}
|
||||
|
||||
func makeBuffer(sep byte, size int) []byte {
|
||||
buf := make([]byte, size)
|
||||
for i := 0; i < size-5; i += 5 {
|
||||
buf[i] = sep
|
||||
strconv.AppendInt(buf[i+1:i+1:i+5], int64(i), 10)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func serialWrite(b []byte) {
|
||||
machine.Serial.Write(b)
|
||||
}
|
||||
@@ -15,35 +15,3 @@ func Escape[T any](x T) T {
|
||||
// as-is.
|
||||
panic("internal/abi.Escape: unreachable (implemented in the compiler)")
|
||||
}
|
||||
|
||||
// EscapeNonString forces v to be on the heap, if v contains a
|
||||
// non-string pointer.
|
||||
//
|
||||
// This is used in hash/maphash.Comparable. We cannot hash pointers
|
||||
// to local variables on stack, as their addresses might change on
|
||||
// stack growth. Strings are okay as the hash depends on only the
|
||||
// content, not the pointer.
|
||||
//
|
||||
// This is essentially
|
||||
//
|
||||
// if hasNonStringPointers(T) { Escape(v) }
|
||||
//
|
||||
// Implemented as a compiler intrinsic.
|
||||
func EscapeNonString[T any](v T) { panic("intrinsic") }
|
||||
|
||||
// EscapeToResultNonString models a data flow edge from v to the result,
|
||||
// if v contains a non-string pointer. If v contains only string pointers,
|
||||
// it returns a copy of v, but is not modeled as a data flow edge
|
||||
// from the escape analysis's perspective.
|
||||
//
|
||||
// This is used in unique.clone, to model the data flow edge on the
|
||||
// value with strings excluded, because strings are cloned (by
|
||||
// content).
|
||||
//
|
||||
// TODO: probably we should define this as a intrinsic and EscapeNonString
|
||||
// could just be "heap = EscapeToResultNonString(v)". This way we can model
|
||||
// an edge to the result but not necessarily heap.
|
||||
func EscapeToResultNonString[T any](v T) T {
|
||||
EscapeNonString(v)
|
||||
return *(*T)(NoEscape(unsafe.Pointer(&v)))
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Simple conversions to avoid depending on strconv.
|
||||
|
||||
package itoa
|
||||
|
||||
// Itoa converts val to a decimal string.
|
||||
func Itoa(val int) string {
|
||||
if val < 0 {
|
||||
return "-" + Uitoa(uint(-val))
|
||||
}
|
||||
return Uitoa(uint(val))
|
||||
}
|
||||
|
||||
// Uitoa converts val to a decimal string.
|
||||
func Uitoa(val uint) string {
|
||||
if val == 0 { // avoid string allocation
|
||||
return "0"
|
||||
}
|
||||
var buf [20]byte // big enough for 64bit value base 10
|
||||
i := len(buf) - 1
|
||||
for val >= 10 {
|
||||
q := val / 10
|
||||
buf[i] = byte('0' + val - q*10)
|
||||
i--
|
||||
val = q
|
||||
}
|
||||
// val < 10
|
||||
buf[i] = byte('0' + val)
|
||||
return string(buf[i:])
|
||||
}
|
||||
|
||||
const hex = "0123456789abcdef"
|
||||
|
||||
// Uitox converts val (a uint) to a hexadecimal string.
|
||||
func Uitox(val uint) string {
|
||||
if val == 0 { // avoid string allocation
|
||||
return "0x0"
|
||||
}
|
||||
var buf [20]byte // big enough for 64bit value base 16 + 0x
|
||||
i := len(buf) - 1
|
||||
for val >= 16 {
|
||||
q := val / 16
|
||||
buf[i] = hex[val%16]
|
||||
i--
|
||||
val = q
|
||||
}
|
||||
// val < 16
|
||||
buf[i] = hex[val%16]
|
||||
i--
|
||||
buf[i] = 'x'
|
||||
i--
|
||||
buf[i] = '0'
|
||||
return string(buf[i:])
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package itoa_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"internal/itoa"
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
minInt64 int64 = math.MinInt64
|
||||
maxInt64 int64 = math.MaxInt64
|
||||
maxUint64 uint64 = math.MaxUint64
|
||||
)
|
||||
|
||||
func TestItoa(t *testing.T) {
|
||||
tests := []int{int(minInt64), math.MinInt32, -999, -100, -1, 0, 1, 100, 999, math.MaxInt32, int(maxInt64)}
|
||||
for _, tt := range tests {
|
||||
got := itoa.Itoa(tt)
|
||||
want := fmt.Sprint(tt)
|
||||
if want != got {
|
||||
t.Fatalf("Itoa(%d) = %s, want %s", tt, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUitoa(t *testing.T) {
|
||||
tests := []uint{0, 1, 100, 999, math.MaxUint32, uint(maxUint64)}
|
||||
for _, tt := range tests {
|
||||
got := itoa.Uitoa(tt)
|
||||
want := fmt.Sprint(tt)
|
||||
if want != got {
|
||||
t.Fatalf("Uitoa(%d) = %s, want %s", tt, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUitox(t *testing.T) {
|
||||
tests := []uint{0, 1, 15, 100, 999, math.MaxUint32, uint(maxUint64)}
|
||||
for _, tt := range tests {
|
||||
got := itoa.Uitox(tt)
|
||||
want := fmt.Sprintf("%#x", tt)
|
||||
if want != got {
|
||||
t.Fatalf("Uitox(%x) = %s, want %s", tt, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,10 +157,6 @@ const (
|
||||
flagIsBinary = 128 // flag that is set if this type uses the hashmap binary algorithm
|
||||
)
|
||||
|
||||
// Flag in the numMethod field (uint16) of Pointer and Struct type descriptors,
|
||||
// indicating that an inline method set is present in the type descriptor.
|
||||
const numMethodHasMethodSet = 0x8000
|
||||
|
||||
// The base type struct. All type structs start with this.
|
||||
type RawType struct {
|
||||
meta uint8 // metadata byte, contains kind and flags (see constants above)
|
||||
@@ -175,22 +171,16 @@ type elemType struct {
|
||||
elem *RawType
|
||||
}
|
||||
|
||||
// ptrType is the type descriptor for pointer types.
|
||||
// The numMethod field stores the number of exported methods in the lower bits,
|
||||
// with bit 15 (numMethodHasMethodSet) indicating whether the methods field is
|
||||
// present. When the flag is clear, the methods field does not exist in the
|
||||
// actual type descriptor and must not be accessed.
|
||||
type ptrType struct {
|
||||
RawType
|
||||
numMethod uint16
|
||||
elem *RawType
|
||||
methods methodSet // only present when numMethod & numMethodHasMethodSet != 0
|
||||
}
|
||||
|
||||
type interfaceType struct {
|
||||
RawType
|
||||
ptrTo *RawType
|
||||
methods methodSet
|
||||
ptrTo *RawType
|
||||
// TODO: methods
|
||||
}
|
||||
|
||||
type arrayType struct {
|
||||
@@ -210,19 +200,13 @@ type mapType struct {
|
||||
key *RawType
|
||||
}
|
||||
|
||||
// namedType is the type descriptor for named types. The numMethod field uses
|
||||
// bit 15 (numMethodHasMethodSet) to indicate whether an inline method set is
|
||||
// present after pkg. When the flag is set, a methodSet follows at
|
||||
// unsafe.Sizeof(namedType{}), and the name string follows after the method
|
||||
// set's entries. When clear, the name string starts directly at that offset.
|
||||
type namedType struct {
|
||||
RawType
|
||||
numMethod uint16
|
||||
ptrTo *RawType
|
||||
elem *RawType
|
||||
pkg *byte
|
||||
// if numMethod & numMethodHasMethodSet != 0: methodSet follows here
|
||||
// name (null-terminated "pkg.Name\0") follows after the method set (or directly here)
|
||||
name [1]byte
|
||||
}
|
||||
|
||||
// Type for struct types. The numField value is intentionally put before ptrTo
|
||||
@@ -232,10 +216,6 @@ type namedType struct {
|
||||
// The fields array isn't necessarily 1 structField long, instead it is as long
|
||||
// as numFields. The array is given a length of 1 to satisfy the Go type
|
||||
// checker.
|
||||
// The numMethod field stores the number of exported methods in the lower bits,
|
||||
// with bit 15 (numMethodHasMethodSet) indicating whether an inline method set
|
||||
// follows the fields array. When the flag is clear, no method set is present
|
||||
// and the type descriptor ends after the last structField entry.
|
||||
type structType struct {
|
||||
RawType
|
||||
numMethod uint16
|
||||
@@ -244,7 +224,6 @@ type structType struct {
|
||||
size uint32
|
||||
numField uint16
|
||||
fields [1]structField // the remaining fields are all of type structField
|
||||
// methods methodSet follows after fields, only when numMethod & numMethodHasMethodSet != 0
|
||||
}
|
||||
|
||||
type structField struct {
|
||||
@@ -252,12 +231,6 @@ type structField struct {
|
||||
data unsafe.Pointer // various bits of information, packed in a byte array
|
||||
}
|
||||
|
||||
// Method set, as emitted by the compiler.
|
||||
type methodSet struct {
|
||||
length uintptr
|
||||
methods [0]unsafe.Pointer // variable number of method signature pointers
|
||||
}
|
||||
|
||||
// Equivalent to (go/types.Type).Underlying(): if this is a named type return
|
||||
// the underlying type, else just return the type itself.
|
||||
func (t *RawType) underlying() *RawType {
|
||||
@@ -760,38 +733,21 @@ func (t *RawType) FieldAlign() int {
|
||||
// AssignableTo returns whether a value of type t can be assigned to a variable
|
||||
// of type u.
|
||||
func (t *RawType) AssignableTo(u Type) bool {
|
||||
u_raw := u.(*RawType)
|
||||
if t == u_raw {
|
||||
if t == u.(*RawType) {
|
||||
return true
|
||||
}
|
||||
|
||||
if t.underlying() == u.(*RawType).underlying() && (!t.isNamed() || !u.(*RawType).isNamed()) {
|
||||
return true
|
||||
}
|
||||
|
||||
if u.Kind() == Interface && u.NumMethod() == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if u.Kind() == Interface {
|
||||
// T is an interface type and x implements T.
|
||||
u_itf := (*interfaceType)(unsafe.Pointer(u_raw.underlying()))
|
||||
return typeImplementsMethodSet(unsafe.Pointer(t), unsafe.Pointer(&u_itf.methods))
|
||||
panic("reflect: unimplemented: AssignableTo with interface")
|
||||
}
|
||||
|
||||
t_named := t.isNamed()
|
||||
u_named := u_raw.isNamed()
|
||||
if t_named && u_named {
|
||||
return false
|
||||
}
|
||||
if t.underlying() == u_raw.underlying() {
|
||||
return true
|
||||
}
|
||||
|
||||
if t.Kind() == Chan && u_raw.Kind() == Chan {
|
||||
t_chan := (*elemType)(unsafe.Pointer(t.underlying()))
|
||||
u_chan := (*elemType)(unsafe.Pointer(u_raw.underlying()))
|
||||
if t_chan.elem != u_chan.elem {
|
||||
return false
|
||||
}
|
||||
if t_chan.ChanDir() != BothDir {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -799,85 +755,7 @@ func (t *RawType) Implements(u Type) bool {
|
||||
if u.Kind() != Interface {
|
||||
panic("reflect: non-interface type passed to Type.Implements")
|
||||
}
|
||||
u_itf := (*interfaceType)(unsafe.Pointer(u.(*RawType).underlying()))
|
||||
return typeImplementsMethodSet(unsafe.Pointer(t), unsafe.Pointer(&u_itf.methods))
|
||||
}
|
||||
|
||||
// typeImplementsMethodSet checks whether the concrete type (identified by its
|
||||
// typecode pointer) implements the given method set. Both the concrete type's
|
||||
// method set and the asserted method set are sorted arrays of method signature
|
||||
// pointers, so comparison is O(n+m).
|
||||
//
|
||||
//go:linkname typeImplementsMethodSet runtime.typeImplementsMethodSet
|
||||
func typeImplementsMethodSet(concreteType, assertedMethodSet unsafe.Pointer) bool {
|
||||
if concreteType == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
const ptrSize = unsafe.Sizeof((*byte)(nil))
|
||||
itfNumMethod := *(*uintptr)(assertedMethodSet)
|
||||
if itfNumMethod == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// Pull the method set out of the concrete type.
|
||||
var methods *methodSet
|
||||
metaByte := *(*uint8)(concreteType)
|
||||
if metaByte&flagNamed != 0 {
|
||||
ct := (*namedType)(concreteType)
|
||||
if ct.numMethod&numMethodHasMethodSet == 0 {
|
||||
return false
|
||||
}
|
||||
methods = (*methodSet)(unsafe.Add(unsafe.Pointer(ct), unsafe.Sizeof(*ct)))
|
||||
} else if metaByte&kindMask == uint8(Interface) {
|
||||
ct := (*interfaceType)(concreteType)
|
||||
methods = &ct.methods
|
||||
} else if metaByte&kindMask == uint8(Pointer) {
|
||||
ct := (*ptrType)(concreteType)
|
||||
if ct.numMethod&numMethodHasMethodSet == 0 {
|
||||
return false
|
||||
}
|
||||
methods = &ct.methods
|
||||
} else if metaByte&kindMask == uint8(Struct) {
|
||||
ct := (*structType)(concreteType)
|
||||
if ct.numMethod&numMethodHasMethodSet == 0 {
|
||||
return false
|
||||
}
|
||||
// For struct types, the method set follows after the variable-length
|
||||
// fields array. We need to compute its offset dynamically.
|
||||
fieldSize := unsafe.Sizeof(structField{})
|
||||
methodsPtr := unsafe.Add(unsafe.Pointer(&ct.fields[0]), uintptr(ct.numField)*fieldSize)
|
||||
methods = (*methodSet)(methodsPtr)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
concreteTypePtr := unsafe.Pointer(&methods.methods)
|
||||
concreteTypeEnd := unsafe.Add(concreteTypePtr, uintptr(methods.length)*ptrSize)
|
||||
|
||||
// Iterate over each method in the interface method set, and check whether
|
||||
// the method exists in the method set of the concrete type.
|
||||
// Both method sets are sorted, so we can use a linear scan.
|
||||
assertedTypePtr := unsafe.Add(assertedMethodSet, ptrSize)
|
||||
assertedTypeEnd := unsafe.Add(assertedTypePtr, itfNumMethod*ptrSize)
|
||||
for assertedTypePtr != assertedTypeEnd {
|
||||
assertedMethod := *(*unsafe.Pointer)(assertedTypePtr)
|
||||
|
||||
for {
|
||||
if concreteTypePtr == concreteTypeEnd {
|
||||
return false
|
||||
}
|
||||
concreteMethod := *(*unsafe.Pointer)(concreteTypePtr)
|
||||
concreteTypePtr = unsafe.Add(concreteTypePtr, ptrSize)
|
||||
if concreteMethod == assertedMethod {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
assertedTypePtr = unsafe.Add(assertedTypePtr, ptrSize)
|
||||
}
|
||||
|
||||
return true
|
||||
return t.AssignableTo(u)
|
||||
}
|
||||
|
||||
// Comparable returns whether values of this type can be compared to each other.
|
||||
@@ -904,14 +782,14 @@ func (t *RawType) ChanDir() ChanDir {
|
||||
func (t *RawType) NumMethod() int {
|
||||
|
||||
if t.isNamed() {
|
||||
return int((*namedType)(unsafe.Pointer(t)).numMethod & ^uint16(numMethodHasMethodSet))
|
||||
return int((*namedType)(unsafe.Pointer(t)).numMethod)
|
||||
}
|
||||
|
||||
switch t.Kind() {
|
||||
case Pointer:
|
||||
return int((*ptrType)(unsafe.Pointer(t)).numMethod & ^uint16(numMethodHasMethodSet))
|
||||
return int((*ptrType)(unsafe.Pointer(t)).numMethod)
|
||||
case Struct:
|
||||
return int((*structType)(unsafe.Pointer(t)).numMethod & ^uint16(numMethodHasMethodSet))
|
||||
return int((*structType)(unsafe.Pointer(t)).numMethod)
|
||||
case Interface:
|
||||
//FIXME: Use len(methods)
|
||||
return (*interfaceType)(unsafe.Pointer(t)).ptrTo.NumMethod()
|
||||
@@ -938,14 +816,7 @@ func readStringZ(data unsafe.Pointer) string {
|
||||
|
||||
func (t *RawType) name() string {
|
||||
ntype := (*namedType)(unsafe.Pointer(t))
|
||||
// The name follows after the fixed fields (and optionally the method set).
|
||||
ptr := unsafe.Add(unsafe.Pointer(ntype), unsafe.Sizeof(*ntype))
|
||||
if ntype.numMethod&numMethodHasMethodSet != 0 {
|
||||
ms := (*methodSet)(ptr)
|
||||
// Skip past the length field and the method pointer entries.
|
||||
ptr = unsafe.Add(ptr, unsafe.Sizeof(uintptr(0))+uintptr(ms.length)*unsafe.Sizeof(unsafe.Pointer(nil)))
|
||||
}
|
||||
return readStringZ(ptr)
|
||||
return readStringZ(unsafe.Pointer(&ntype.name[0]))
|
||||
}
|
||||
|
||||
func (t *RawType) Name() string {
|
||||
|
||||
@@ -138,7 +138,7 @@ func TypeAssert[T any](v Value) (T, bool) {
|
||||
var zero T
|
||||
return zero, false
|
||||
}
|
||||
if !v.isIndirect() && v.typecode.Size() <= unsafe.Sizeof(uintptr(0)) {
|
||||
if !v.isIndirect() {
|
||||
return *(*T)(unsafe.Pointer(&v.value)), true
|
||||
}
|
||||
return *(*T)(v.value), true
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
//go:build !tinygo.wasm
|
||||
|
||||
package unix
|
||||
|
||||
import "syscall"
|
||||
|
||||
type GetRandomFlag uintptr
|
||||
|
||||
const (
|
||||
@@ -12,7 +8,5 @@ const (
|
||||
)
|
||||
|
||||
func GetRandom(p []byte, flags GetRandomFlag) (n int, err error) {
|
||||
// Not supported on most TinyGo targets.
|
||||
// On real Linux the sysrand package will fall back to /dev/urandom.
|
||||
return 0, syscall.ENOSYS
|
||||
panic("todo: unix.GetRandom")
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
//go:build tinygo.wasm
|
||||
|
||||
package unix
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type GetRandomFlag uintptr
|
||||
|
||||
const (
|
||||
GRND_NONBLOCK GetRandomFlag = 0x0001
|
||||
GRND_RANDOM GetRandomFlag = 0x0002
|
||||
)
|
||||
|
||||
func GetRandom(p []byte, flags GetRandomFlag) (n int, err error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
libc_arc4random_buf(unsafe.Pointer(&p[0]), uint(len(p)))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// void arc4random_buf(void *buf, size_t buflen);
|
||||
//
|
||||
//export arc4random_buf
|
||||
func libc_arc4random_buf(buf unsafe.Pointer, buflen uint)
|
||||
@@ -22,8 +22,7 @@ type Task struct {
|
||||
state state
|
||||
|
||||
// This is needed for some crypto packages.
|
||||
FipsIndicator uint8
|
||||
FipsOnlyBypass bool
|
||||
FipsIndicator uint8
|
||||
|
||||
// State of the goroutine: running, paused, or must-resume-next-pause.
|
||||
// This extra field doesn't increase memory usage on 32-bit CPUs and above,
|
||||
|
||||
@@ -46,40 +46,26 @@ tinygo_swapTask:
|
||||
// arbitrary register while registers are flushed.
|
||||
rsil a4, 3 // XCHAL_EXCM_LEVEL
|
||||
|
||||
// Save the old PS (in a4) to the stack because call4 clobbers a4-a7.
|
||||
s32i a4, sp, 4
|
||||
|
||||
// Flush all register windows to the stack using recursive call4.
|
||||
//
|
||||
// The previous ROTW-based approach (borrowed from Zephyr) does NOT
|
||||
// actually trigger window overflow exceptions on Xtensa LX7 — ROTW
|
||||
// simply modifies WindowBase without saving any registers to the stack.
|
||||
// This leaves stale window data in the physical register file after a
|
||||
// goroutine switch, corrupting the overflow save chain when the new
|
||||
// goroutine's deeper calls trigger overflow of old goroutine windows.
|
||||
//
|
||||
// Instead, we recursively call a small function via call4. Each call4 +
|
||||
// entry triggers the hardware window-overflow mechanism for any occupied
|
||||
// pane being reused, correctly saving registers to the stack.
|
||||
// For NAREG=64 (16 panes), 15 recursive levels cover all panes except
|
||||
// the current one (tinygo_swapTask), which must stay active.
|
||||
movi a6, 15
|
||||
call4 .Lspill_windows
|
||||
|
||||
// After the recursive spill returns, the physical register file still
|
||||
// has WindowStart bits set for the spill helper frames.
|
||||
// We will clear WindowStart completely (to 0) right before the retw.n
|
||||
// below, after the stack switch is done. This prevents stale overflow
|
||||
// when the new goroutine's calls rotate back into these panes.
|
||||
// Flush all unsaved registers to the stack.
|
||||
// This trick has been borrowed from the Zephyr project:
|
||||
// https://github.com/zephyrproject-rtos/zephyr/blob/d79b003758/arch/xtensa/include/xtensa-asm2-s.h#L17
|
||||
and a12, a12, a12
|
||||
rotw 3
|
||||
and a12, a12, a12
|
||||
rotw 3
|
||||
and a12, a12, a12
|
||||
rotw 3
|
||||
and a12, a12, a12
|
||||
rotw 3
|
||||
and a12, a12, a12
|
||||
rotw 4
|
||||
|
||||
// Restore interrupts.
|
||||
l32i a4, sp, 4 // reload saved PS
|
||||
wsr.ps a4
|
||||
rsync
|
||||
|
||||
// At this point, the following is true:
|
||||
// WindowStart == 1 << WindowBase
|
||||
// All other windows have been properly flushed to their stacks.
|
||||
// Therefore, we don't need to do this manually.
|
||||
// It also means that the stack pointer can now be safely modified.
|
||||
|
||||
// Save a0, which stores the return address and the parent register window
|
||||
@@ -97,30 +83,6 @@ tinygo_swapTask:
|
||||
// register also stores the parent register window.
|
||||
l32i.n a0, sp, 0
|
||||
|
||||
// Clear ALL WindowStart bits. With all windows spilled to the stack,
|
||||
// we must ensure no stale WS bits remain: the retw.n below will trigger
|
||||
// underflow4 to load the new goroutine's registers from the new stack
|
||||
// (which sets the appropriate WS bit via rfwu). Any stale WS bits
|
||||
// (from spill helpers or the old goroutine) would cause spurious
|
||||
// overflows of garbage register values into memory.
|
||||
movi a5, 0
|
||||
wsr a5, WINDOWSTART
|
||||
rsync
|
||||
|
||||
// Return into the new stack. This instruction will trigger a window
|
||||
// underflow, reloading the saved registers from the stack.
|
||||
retw.n
|
||||
|
||||
// Recursive helper for flushing all register windows.
|
||||
// Parameter: a2 = remaining recursion depth (passed via caller's a6).
|
||||
// Each call4 + entry triggers the hardware overflow mechanism for any
|
||||
// occupied pane at the new WindowBase position.
|
||||
.balign 4
|
||||
.Lspill_windows:
|
||||
entry a1, 16
|
||||
beqz a2, .Lspill_done
|
||||
addi a2, a2, -1
|
||||
mov a6, a2
|
||||
call4 .Lspill_windows
|
||||
.Lspill_done:
|
||||
retw.n
|
||||
|
||||
@@ -74,8 +74,3 @@ func (s *state) pause() {
|
||||
func SystemStack() uintptr {
|
||||
return systemStack
|
||||
}
|
||||
|
||||
//export tinygo_task_current
|
||||
func tinygo_task_current() unsafe.Pointer {
|
||||
return unsafe.Pointer(Current())
|
||||
}
|
||||
|
||||
@@ -71,8 +71,3 @@ func (s *state) pause() {
|
||||
func SystemStack() uintptr {
|
||||
return *runtime_systemStackPtr()
|
||||
}
|
||||
|
||||
//export tinygo_task_current
|
||||
func tinygo_task_current() unsafe.Pointer {
|
||||
return unsafe.Pointer(Current())
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ var (
|
||||
|
||||
// FDCAN1 on PD0 (RX) / PD1 (TX) with onboard transceiver
|
||||
CAN1 = &_CAN1
|
||||
_CAN1 = FDCAN{
|
||||
_CAN1 = CAN{
|
||||
Bus: stm32.FDCAN1,
|
||||
TxAltFuncSelect: AF3_FDCAN1_FDCAN2,
|
||||
RxAltFuncSelect: AF3_FDCAN1_FDCAN2,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build arduino_uno
|
||||
//go:build arduino
|
||||
|
||||
package machine
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
//go:build arduino_uno_q
|
||||
|
||||
// Arduino UNO Q board with STM32U585 processor.
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Arduino Pins
|
||||
A0 = PA4
|
||||
A1 = PA5
|
||||
A2 = PA6
|
||||
A3 = PA7
|
||||
A4 = PC1
|
||||
A5 = PC0
|
||||
|
||||
// ADC pin aliases
|
||||
ADC0 = A0
|
||||
ADC1 = A1
|
||||
ADC2 = A2
|
||||
ADC3 = A3
|
||||
ADC4 = A4
|
||||
ADC5 = A5
|
||||
|
||||
D0 = PB7 // USART1 RX, PWM TIM4_CH2
|
||||
D1 = PB6 // USART1 TX, PWM TIM4_CH1
|
||||
D2 = PB3 // PWM TIM2_CH2
|
||||
D3 = PB0 // PWM TIM3_CH3
|
||||
D4 = PA12
|
||||
D5 = PA11 // PWM TIM1_CH4
|
||||
D6 = PB1 // PWM TIM3_CH4
|
||||
D7 = PB2
|
||||
D8 = PB4 // PWM TIM3_CH1
|
||||
D9 = PB8 // PWM TIM4_CH3 / TIM16_CH1
|
||||
D10 = PB9 // PWM TIM4_CH4 / TIM17_CH1
|
||||
D11 = PB15 // PWM TIM15_CH2
|
||||
D12 = PB14 // PWM TIM15_CH1
|
||||
D13 = PB13
|
||||
D18 = PC1
|
||||
D19 = PC0
|
||||
D20 = PB10 // I2C2 SCL, PWM TIM2_CH3
|
||||
D21 = PB11 // I2C2 SDA, PWM TIM2_CH4
|
||||
)
|
||||
|
||||
const (
|
||||
LED = LED3_R
|
||||
LED3_R = PH10
|
||||
LED3_G = PH11
|
||||
LED3_B = PH12
|
||||
LED4_R = PH13
|
||||
LED4_G = PH14
|
||||
LED4_B = PH15
|
||||
)
|
||||
|
||||
const (
|
||||
// Default UART pins (LPUART1 active via ST-LINK virtual COM port)
|
||||
UART_TX_PIN = PG7
|
||||
UART_RX_PIN = PG8
|
||||
|
||||
// USART1 pins (Arduino header D1/D0)
|
||||
UART1_TX_PIN = D1
|
||||
UART1_RX_PIN = D0
|
||||
|
||||
// LPUART1 pins (active via ST-LINK virtual COM port)
|
||||
UART2_TX_PIN = PG7
|
||||
UART2_RX_PIN = PG8
|
||||
|
||||
// I2C pins, also connected to I2C2
|
||||
I2C0_SCL_PIN = D20
|
||||
I2C0_SDA_PIN = D21
|
||||
// QWIIC connector pins, also connected to I2C4
|
||||
I2C1_SCL_PIN = PD12
|
||||
I2C1_SDA_PIN = PD13
|
||||
|
||||
// SPI pins
|
||||
SPI1_SCK_PIN = PA5
|
||||
SPI1_SDI_PIN = PA6
|
||||
SPI1_SDO_PIN = PA7
|
||||
SPI0_SCK_PIN = SPI1_SCK_PIN
|
||||
SPI0_SDI_PIN = SPI1_SDI_PIN
|
||||
SPI0_SDO_PIN = SPI1_SDO_PIN
|
||||
)
|
||||
|
||||
var (
|
||||
// USART1 on PB6/PB7 (Arduino header D1/D0).
|
||||
UART1 = &_UART1
|
||||
_UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART1,
|
||||
TxAltFuncSelector: AF7_USART1_2_3,
|
||||
RxAltFuncSelector: AF7_USART1_2_3,
|
||||
}
|
||||
|
||||
// LPUART1 on PG7/PG8 (active via ST-LINK virtual COM port).
|
||||
UART2 = &_UART2
|
||||
_UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: (*stm32.USART_Type)(unsafe.Pointer(stm32.LPUART1)),
|
||||
TxAltFuncSelector: AF8_UART4_5_LPUART1_SDMMC1,
|
||||
RxAltFuncSelector: AF8_UART4_5_LPUART1_SDMMC1,
|
||||
}
|
||||
DefaultUART = UART2
|
||||
|
||||
// I2C2 is documented, alias to I2C0 as well
|
||||
I2C2 = &I2C{
|
||||
Bus: stm32.I2C2,
|
||||
AltFuncSelector: AF4_I2C1_2_3_4,
|
||||
}
|
||||
I2C0 = I2C2
|
||||
|
||||
// I2C4 is is connected to the QWIIC connector, alias to I2C1 as well
|
||||
I2C4 = &I2C{
|
||||
Bus: stm32.I2C4,
|
||||
AltFuncSelector: AF4_I2C1_2_3_4,
|
||||
}
|
||||
I2C1 = I2C4
|
||||
|
||||
// SPI1 is documented, alias to SPI0 as well
|
||||
SPI1 = &SPI{
|
||||
Bus: stm32.SPI1,
|
||||
AltFuncSelector: AF5_SPI1_2_3_OCTOSPI1_OCTOSPI2,
|
||||
}
|
||||
SPI0 = SPI1
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(stm32.IRQ_USART1, _UART1.handleInterrupt)
|
||||
UART2.Interrupt = interrupt.New(stm32.IRQ_LPUART1, _UART2.handleInterrupt)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build (avr && atmega328p) || arduino_uno || arduino_nano
|
||||
//go:build (avr && atmega328p) || arduino || arduino_nano
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
//go:build esp32_generic
|
||||
|
||||
package machine
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = NoPin
|
||||
SCL_PIN = NoPin
|
||||
)
|
||||
@@ -30,6 +30,9 @@ const (
|
||||
|
||||
// ADC pins
|
||||
const (
|
||||
ADC0 Pin = ADC1_0
|
||||
ADC1 Pin = ADC2_0
|
||||
|
||||
ADC1_0 Pin = IO0
|
||||
ADC1_1 Pin = IO1
|
||||
ADC1_2 Pin = IO2
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
//go:build esp32c3_generic
|
||||
|
||||
package machine
|
||||
|
||||
const (
|
||||
SDA_PIN = NoPin
|
||||
SCL_PIN = NoPin
|
||||
)
|
||||
@@ -1,6 +1,6 @@
|
||||
//go:build esp32c3_supermini
|
||||
|
||||
// This file contains the pin mappings for the ESP32C3 supermini board.
|
||||
// This file contains the pin mappings for the ESP32 supermini boards.
|
||||
//
|
||||
// - https://web.archive.org/web/20240805232453/https://dl.artronshop.co.th/ESP32-C3%20SuperMini%20datasheet.pdf
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
//go:build esp32s3_generic
|
||||
|
||||
package machine
|
||||
|
||||
const (
|
||||
SCL_PIN = NoPin
|
||||
SDA_PIN = NoPin
|
||||
|
||||
SPI1_SCK_PIN = NoPin // SCK
|
||||
SPI1_MOSI_PIN = NoPin // SDO (MOSI)
|
||||
SPI1_MISO_PIN = NoPin // SDI (MISO)
|
||||
SPI1_CS_PIN = NoPin // CS
|
||||
|
||||
SPI2_SCK_PIN = NoPin // SCK
|
||||
SPI2_MOSI_PIN = NoPin // SDO (MOSI)
|
||||
SPI2_MISO_PIN = NoPin // SDI (MISO)
|
||||
SPI2_CS_PIN = NoPin // CS
|
||||
)
|
||||
@@ -1,88 +0,0 @@
|
||||
//go:build esp32s3_supermini
|
||||
|
||||
// This file contains the pin mappings for the ESP32S3 supermini board.
|
||||
//
|
||||
// - https://www.nologo.tech/product/esp32/esp32s3/esp32s3supermini/esp32S3SuperMini.html
|
||||
|
||||
package machine
|
||||
|
||||
// Digital Pins
|
||||
const (
|
||||
IO1 = GPIO1
|
||||
IO2 = GPIO2
|
||||
IO3 = GPIO3
|
||||
IO4 = GPIO4
|
||||
IO5 = GPIO5
|
||||
IO6 = GPIO6
|
||||
IO7 = GPIO7
|
||||
IO8 = GPIO8
|
||||
IO9 = GPIO9
|
||||
IO10 = GPIO10
|
||||
IO11 = GPIO11
|
||||
IO12 = GPIO12
|
||||
IO13 = GPIO13
|
||||
IO14 = GPIO14
|
||||
IO15 = GPIO15
|
||||
IO16 = GPIO16
|
||||
IO17 = GPIO17
|
||||
IO18 = GPIO18
|
||||
IO21 = GPIO21
|
||||
IO33 = GPIO33
|
||||
IO34 = GPIO34
|
||||
IO35 = GPIO35
|
||||
IO36 = GPIO36
|
||||
IO37 = GPIO37
|
||||
IO38 = GPIO38
|
||||
IO39 = GPIO39
|
||||
IO40 = GPIO40
|
||||
IO41 = GPIO41
|
||||
IO42 = GPIO42
|
||||
IO43 = GPIO43
|
||||
IO44 = GPIO44
|
||||
IO45 = GPIO45
|
||||
IO46 = GPIO46
|
||||
IO47 = GPIO47
|
||||
IO48 = GPIO48
|
||||
)
|
||||
|
||||
// Built-in LED
|
||||
const LED = GPIO48
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A1 = GPIO1
|
||||
A2 = GPIO2
|
||||
A3 = GPIO3
|
||||
A4 = GPIO4
|
||||
A5 = GPIO5
|
||||
A6 = GPIO6
|
||||
A7 = GPIO7
|
||||
A8 = GPIO8
|
||||
A9 = GPIO9
|
||||
A10 = GPIO10
|
||||
A11 = GPIO11
|
||||
A12 = GPIO12
|
||||
A13 = GPIO13
|
||||
A14 = GPIO14
|
||||
A15 = GPIO15
|
||||
A16 = GPIO16
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = GPIO8
|
||||
SCL_PIN = GPIO9
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI1_SCK_PIN = GPIO4
|
||||
SPI1_MISO_PIN = GPIO5
|
||||
SPI1_MOSI_PIN = GPIO6
|
||||
SPI1_CS_PIN = GPIO7
|
||||
|
||||
SPI2_SCK_PIN = NoPin
|
||||
SPI2_MOSI_PIN = NoPin
|
||||
SPI2_MISO_PIN = NoPin
|
||||
SPI2_CS_PIN = NoPin
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
//go:build esp32s3_wroom1
|
||||
|
||||
package machine
|
||||
|
||||
const (
|
||||
SPI1_SCK_PIN = GPIO12 // SCK
|
||||
SPI1_MOSI_PIN = GPIO11 // SDO (MOSI)
|
||||
SPI1_MISO_PIN = GPIO13 // SDI (MISO)
|
||||
SPI1_CS_PIN = GPIO10 // CS
|
||||
|
||||
SPI2_SCK_PIN = GPIO36 // SCK
|
||||
SPI2_MOSI_PIN = GPIO35 // SDO (MOSI)
|
||||
SPI2_MISO_PIN = GPIO37 // SDI (MISO)
|
||||
SPI2_CS_PIN = GPIO34 // CS
|
||||
)
|
||||
@@ -107,7 +107,7 @@ var (
|
||||
|
||||
// FDCAN1 on PA11 (TX) / PA12 (RX)
|
||||
CAN1 = &_CAN1
|
||||
_CAN1 = FDCAN{
|
||||
_CAN1 = CAN{
|
||||
Bus: stm32.FDCAN1,
|
||||
TxAltFuncSelect: AF9_FDCAN1_FDCAN2,
|
||||
RxAltFuncSelect: AF9_FDCAN1_FDCAN2,
|
||||
@@ -116,7 +116,7 @@ var (
|
||||
|
||||
// FDCAN2 on PD12 (TX) / PD13 (RX)
|
||||
CAN2 = &_CAN2
|
||||
_CAN2 = FDCAN{
|
||||
_CAN2 = CAN{
|
||||
Bus: stm32.FDCAN2,
|
||||
TxAltFuncSelect: AF3_FDCAN1_FDCAN2,
|
||||
RxAltFuncSelect: AF3_FDCAN1_FDCAN2,
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
//go:build xiao_rp2350
|
||||
|
||||
// This file contains the pin mappings for the Seeed XIAO RP2350 boards.
|
||||
//
|
||||
// XIAO RP2350 is a microcontroller using the Raspberry Pi RP2350 chip.
|
||||
//
|
||||
// - https://wiki.seeedstudio.com/XIAO-RP2350/
|
||||
package machine
|
||||
|
||||
// Digital Pins
|
||||
const (
|
||||
D0 Pin = GPIO26
|
||||
D1 Pin = GPIO27
|
||||
D2 Pin = GPIO28
|
||||
D3 Pin = GPIO5
|
||||
D4 Pin = GPIO6
|
||||
D5 Pin = GPIO7
|
||||
D6 Pin = GPIO0
|
||||
D7 Pin = GPIO1
|
||||
D8 Pin = GPIO2
|
||||
D9 Pin = GPIO4
|
||||
D10 Pin = GPIO3
|
||||
D11 Pin = GPIO21
|
||||
D12 Pin = GPIO20
|
||||
D13 Pin = GPIO17
|
||||
D14 Pin = GPIO16
|
||||
D15 Pin = GPIO11
|
||||
D16 Pin = GPIO12
|
||||
D17 Pin = GPIO10
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 Pin = D0
|
||||
A1 Pin = D1
|
||||
A2 Pin = D2
|
||||
)
|
||||
|
||||
// Onboard LEDs
|
||||
const (
|
||||
NEOPIXEL = GPIO22
|
||||
WS2812 = GPIO22
|
||||
NEO_PWR = GPIO23
|
||||
NEOPIXEL_POWER = GPIO23
|
||||
|
||||
LED = GPIO25
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
I2C0_SDA_PIN Pin = D14
|
||||
I2C0_SCL_PIN Pin = D13
|
||||
|
||||
I2C1_SDA_PIN Pin = D4
|
||||
I2C1_SCL_PIN Pin = D5
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN Pin = D8
|
||||
SPI0_SDO_PIN Pin = D10
|
||||
SPI0_SDI_PIN Pin = D9
|
||||
|
||||
SPI1_SCK_PIN Pin = D17
|
||||
SPI1_SDO_PIN Pin = D15
|
||||
SPI1_SDI_PIN Pin = D16
|
||||
)
|
||||
|
||||
// Onboard crystal oscillator frequency, in MHz.
|
||||
const (
|
||||
xoscFreq = 12 // MHz
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART0_TX_PIN = GPIO0
|
||||
UART0_RX_PIN = GPIO1
|
||||
UART_TX_PIN = UART0_TX_PIN
|
||||
UART_RX_PIN = UART0_RX_PIN
|
||||
)
|
||||
|
||||
var DefaultUART = UART0
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "XIAO RP2350"
|
||||
usb_STRING_MANUFACTURER = "Seeed"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x2e8a
|
||||
usb_PID uint16 = 0x000a
|
||||
)
|
||||
@@ -0,0 +1,102 @@
|
||||
//go:build stm32g0
|
||||
|
||||
package machine
|
||||
|
||||
// unexported functions here are implemented in the device file
|
||||
// and added to the build tags of this file.
|
||||
|
||||
// These types are an alias for documentation purposes exclusively. We wish
|
||||
// the interface to be used by other ecosystems besides TinyGo which is why
|
||||
// we need these types to be a primitive types at the interface level.
|
||||
// If these types are defined at machine or machine/can level they are not
|
||||
// usable by non-TinyGo projects. This is not good news for fostering wider adoption
|
||||
// of our API in "big-Go" embedded system projects like TamaGo and periph.io
|
||||
type (
|
||||
// CAN IDs in tinygo are represented as 30 bit integers where
|
||||
// bits 1..29 store the actual ID and the 30th bit stores the IDE bit (if extended ID).
|
||||
// We include the extended ID bit in the ID itself to make comparison of IDs easier for users
|
||||
// since two identical IDs where one is extended and one is not are NOT equivalent IDs.
|
||||
canID = uint32
|
||||
// CAN flags bitmask are defined below.
|
||||
canFlags = uint32
|
||||
)
|
||||
|
||||
// CAN ID definitions.
|
||||
const (
|
||||
canIDStdMask canID = (1 << 11) - 1
|
||||
canIDExtendedMask canID = (1 << 29) - 1
|
||||
canIDExtendedBit canID = 1 << 30
|
||||
)
|
||||
|
||||
// CAN Flag bit definitions.
|
||||
const (
|
||||
canFlagBRS canFlags = 1 << 0 // Bit Rate Switch active on tx/rx of frame.
|
||||
canFlagFDF canFlags = 1 << 1 // Is a FD Frame.
|
||||
canFlagRTR canFlags = 1 << 2 // is a retransmission request frame.
|
||||
canFlagESI canFlags = 1 << 3 // Error status indicator active on tx/rx of frame.
|
||||
canFlagIDE canFlags = 1 << 4 // Extended ID.
|
||||
)
|
||||
|
||||
// TxFIFOLevel returns amount of CAN frames stored for transmission and total Tx fifo length.
|
||||
func (can *CAN) TxFIFOLevel() (level int, maxlevel int) {
|
||||
return can.txFIFOLevel()
|
||||
}
|
||||
|
||||
// Tx puts a CAN frame in TxFIFO for transmission. Returns error if TxFIFO is full.
|
||||
func (can *CAN) Tx(id canID, flags canFlags, data []byte) error {
|
||||
return can.tx(id, flags, data)
|
||||
}
|
||||
|
||||
// RxFIFOLevel returns amount of CAN frames received and stored and total Rx fifo length.
|
||||
// If the hardware is interrupt driven RxFIFOLevel should return 0,0.
|
||||
func (can *CAN) RxFIFOLevel() (level int, maxlevel int) {
|
||||
return can.rxFIFOLevel()
|
||||
}
|
||||
|
||||
type canRxCallback = func(data []byte, id canID, timestamp uint32, flags canFlags)
|
||||
|
||||
// SetRxCallback sets the receive callback. See [canFlags] for information on how bits are layed out.
|
||||
func (can *CAN) SetRxCallback(cb canRxCallback) {
|
||||
can.setRxCallback(cb)
|
||||
}
|
||||
|
||||
// RxPoll is called periodically for poll driven drivers. If the driver is interrupt driven
|
||||
// then RxPoll is a no-op and may return nil. Users may determine if a CAN is interrupt driven by
|
||||
// checking if RxFIFOLevel returns 0,0.
|
||||
func (can *CAN) RxPoll() error {
|
||||
return can.rxPoll()
|
||||
}
|
||||
|
||||
// DLC to bytes lookup table
|
||||
var dlcToBytes = [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64}
|
||||
|
||||
// dlcToLength converts a DLC value to actual byte length
|
||||
func dlcToLength(dlc byte) uint8 {
|
||||
if dlc > 15 {
|
||||
dlc = 15
|
||||
}
|
||||
return dlcToBytes[dlc]
|
||||
}
|
||||
|
||||
// lengthToDLC converts a byte length to DLC value
|
||||
func lengthToDLC(length uint8) (dlc byte) {
|
||||
switch {
|
||||
case length <= 8:
|
||||
dlc = length
|
||||
case length <= 12:
|
||||
dlc = 9
|
||||
case length <= 16:
|
||||
dlc = 10
|
||||
case length <= 20:
|
||||
dlc = 11
|
||||
case length <= 24:
|
||||
dlc = 12
|
||||
case length <= 32:
|
||||
dlc = 13
|
||||
case length <= 48:
|
||||
dlc = 14
|
||||
default:
|
||||
dlc = 15
|
||||
}
|
||||
return dlc
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build esp32c3 || nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l0 || stm32l4 || stm32wlx || atsamd21 || atsamd51 || atsame5x || rp2040 || rp2350
|
||||
//go:build nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l0 || stm32l4 || stm32wlx || atsamd21 || atsamd51 || atsame5x || rp2040 || rp2350
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
//go:build !baremetal || atmega || nrf || sam || stm32 || fe310 || k210 || rp2040 || rp2350 || mimxrt1062 || (esp32c3 && !m5stamp_c3) || esp32 || esp32s3
|
||||
//go:build !baremetal || atmega || nrf || sam || stm32 || fe310 || k210 || rp2040 || rp2350 || mimxrt1062 || (esp32c3 && !m5stamp_c3) || esp32
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !baremetal && (arduino_uno || arduino_nano)
|
||||
//go:build !baremetal && (arduino || arduino_nano)
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"device/sam"
|
||||
"errors"
|
||||
"internal/binary"
|
||||
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -27,29 +27,16 @@ const (
|
||||
PinInput
|
||||
PinInputPullup
|
||||
PinInputPulldown
|
||||
PinAnalog
|
||||
)
|
||||
|
||||
const (
|
||||
GPIO0 Pin = 0
|
||||
GPIO1 Pin = 1
|
||||
GPIO2 Pin = 2
|
||||
GPIO3 Pin = 3
|
||||
GPIO4 Pin = 4
|
||||
GPIO5 Pin = 5
|
||||
GPIO6 Pin = 6
|
||||
)
|
||||
|
||||
const (
|
||||
ADC0 Pin = GPIO0
|
||||
ADC1 Pin = GPIO1
|
||||
ADC2 Pin = GPIO2
|
||||
ADC3 Pin = GPIO3
|
||||
ADC4 Pin = GPIO4
|
||||
ADC5 Pin = GPIO5 // avoid when WiFi is used.
|
||||
)
|
||||
|
||||
const (
|
||||
GPIO0 Pin = 0
|
||||
GPIO1 Pin = 1
|
||||
GPIO2 Pin = 2
|
||||
GPIO3 Pin = 3
|
||||
GPIO4 Pin = 4
|
||||
GPIO5 Pin = 5
|
||||
GPIO6 Pin = 6
|
||||
GPIO7 Pin = 7
|
||||
GPIO8 Pin = 8
|
||||
GPIO9 Pin = 9
|
||||
@@ -89,15 +76,13 @@ func (p Pin) Configure(config PinConfig) {
|
||||
const function = 1 // function 1 is GPIO for every pin
|
||||
muxConfig |= function << esp.IO_MUX_GPIO_MCU_SEL_Pos
|
||||
|
||||
// FUN_IE: disable for PinAnalog (high-Z for ADC)
|
||||
if config.Mode != PinAnalog {
|
||||
muxConfig |= esp.IO_MUX_GPIO_FUN_IE
|
||||
}
|
||||
// Make this pin an input pin (always).
|
||||
muxConfig |= esp.IO_MUX_GPIO_FUN_IE
|
||||
|
||||
// Set drive strength: 0 is lowest, 3 is highest.
|
||||
muxConfig |= 2 << esp.IO_MUX_GPIO_FUN_DRV_Pos
|
||||
|
||||
// Select pull mode (no pulls for PinAnalog).
|
||||
// Select pull mode.
|
||||
if config.Mode == PinInputPullup {
|
||||
muxConfig |= esp.IO_MUX_GPIO_FUN_WPU
|
||||
} else if config.Mode == PinInputPulldown {
|
||||
@@ -114,27 +99,12 @@ func (p Pin) Configure(config PinConfig) {
|
||||
case PinOutput:
|
||||
// Set the 'output enable' bit.
|
||||
esp.GPIO.ENABLE_W1TS.Set(1 << p)
|
||||
case PinInput, PinInputPullup, PinInputPulldown, PinAnalog:
|
||||
case PinInput, PinInputPullup, PinInputPulldown:
|
||||
// Clear the 'output enable' bit.
|
||||
esp.GPIO.ENABLE_W1TC.Set(1 << p)
|
||||
}
|
||||
}
|
||||
|
||||
// configure is the same as Configure, but allows setting a specific GPIO matrix signal.
|
||||
func (p Pin) configure(config PinConfig, signal uint32) {
|
||||
p.Configure(config)
|
||||
if signal == 256 {
|
||||
return
|
||||
}
|
||||
if config.Mode == PinOutput {
|
||||
p.outFunc().Set(signal)
|
||||
} else {
|
||||
inFunc(signal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(p))
|
||||
}
|
||||
}
|
||||
|
||||
func initI2CExt1Clock() {}
|
||||
|
||||
// outFunc returns the FUNCx_OUT_SEL_CFG register used for configuring the
|
||||
// output function selection.
|
||||
func (p Pin) outFunc() *volatile.Register32 {
|
||||
@@ -261,13 +231,13 @@ func setupPinInterrupt() error {
|
||||
esp.INTERRUPT_CORE0.GPIO_INTERRUPT_PRO_MAP.Set(cpuInterruptFromPin)
|
||||
return interrupt.New(cpuInterruptFromPin, func(interrupt.Interrupt) {
|
||||
status := esp.GPIO.STATUS.Get()
|
||||
// Clear before processing so new edges during callbacks are not lost.
|
||||
esp.GPIO.STATUS_W1TC.SetBits(status)
|
||||
for i, mask := 0, uint32(1); i < maxPin; i, mask = i+1, mask<<1 {
|
||||
if (status&mask) != 0 && pinCallbacks[i] != nil {
|
||||
pinCallbacks[i](Pin(i))
|
||||
}
|
||||
}
|
||||
// clear interrupt bit
|
||||
esp.GPIO.STATUS_W1TC.SetBits(status)
|
||||
}).Enable()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
//go:build esp32c3 && !m5stamp_c3
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/esp"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// newRegI2C returns the regI2C configured for ESP32-C3: hostID=0, drefInit=1.
|
||||
func newRegI2C() regI2C { return regI2C{hostID: 0, drefInit: 1} }
|
||||
|
||||
const (
|
||||
// ADC attenuation values for ESP32-C3 APB_SARADC.
|
||||
// 0 dB : ~0 .. 1.1 V
|
||||
// 11 dB : ~0 .. 3.3 V (matches typical VDD)
|
||||
atten0dB = 0
|
||||
atten11dB = 3
|
||||
)
|
||||
|
||||
func InitADC() {
|
||||
esp.SYSTEM.SetPERIP_RST_EN0_APB_SARADC_RST(1)
|
||||
esp.SYSTEM.SetPERIP_CLK_EN0_APB_SARADC_CLK_EN(1)
|
||||
esp.SYSTEM.SetPERIP_RST_EN0_APB_SARADC_RST(0)
|
||||
|
||||
esp.RTC_CNTL.SetANA_CONF_SAR_I2C_PU(1)
|
||||
esp.RTC_CNTL.SetSENSOR_CTRL_FORCE_XPD_SAR(1)
|
||||
esp.APB_SARADC.SetCTRL_SARADC_XPD_SAR_FORCE(1)
|
||||
esp.APB_SARADC.SetFSM_WAIT_SARADC_XPD_WAIT(8)
|
||||
esp.APB_SARADC.SetFSM_WAIT_SARADC_RSTB_WAIT(8)
|
||||
esp.APB_SARADC.SetFSM_WAIT_SARADC_STANDBY_WAIT(100)
|
||||
esp.APB_SARADC.SetCLKM_CONF_CLK_SEL(2)
|
||||
esp.APB_SARADC.SetCLKM_CONF_CLKM_DIV_NUM(1)
|
||||
esp.APB_SARADC.SetCLKM_CONF_CLKM_DIV_B(0)
|
||||
esp.APB_SARADC.SetCLKM_CONF_CLKM_DIV_A(0)
|
||||
esp.APB_SARADC.SetCLKM_CONF_CLK_EN(1)
|
||||
|
||||
adcSelfCalibrate()
|
||||
}
|
||||
|
||||
// ESP32-C3: ADC1 = GPIO0–GPIO4 (ch 0–4), ADC2 = GPIO5 (ch 0). ADC2 shares with Wi‑Fi;
|
||||
// readings may be noisy when Wi‑Fi is active.
|
||||
func (a ADC) Configure(config ADCConfig) error {
|
||||
if a.Pin > 5 {
|
||||
return errors.New("invalid ADC pin for ESP32-C3")
|
||||
}
|
||||
a.Pin.Configure(PinConfig{Mode: PinAnalog})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a ADC) Get() uint16 {
|
||||
if a.Pin > 5 {
|
||||
return 0
|
||||
}
|
||||
adc1 := a.Pin <= 4
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_ATTEN(atten11dB)
|
||||
esp.APB_SARADC.SetINT_CLR_APB_SARADC1_DONE_INT_CLR(1)
|
||||
esp.APB_SARADC.SetINT_CLR_APB_SARADC2_DONE_INT_CLR(1)
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
|
||||
var raw uint32
|
||||
if adc1 {
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_CHANNEL(uint32(a.Pin))
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC1_ONETIME_SAMPLE(1)
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(1)
|
||||
for esp.APB_SARADC.GetINT_RAW_APB_SARADC1_DONE_INT_RAW() == 0 {
|
||||
}
|
||||
raw = esp.APB_SARADC.GetSAR1DATA_STATUS_APB_SARADC1_DATA()
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC1_ONETIME_SAMPLE(0)
|
||||
} else {
|
||||
// ADC2: GPIO5 = channel 0. Grant arbiter to ADC2 first, then set channel and start.
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC1_ONETIME_SAMPLE(0)
|
||||
esp.APB_SARADC.SetARB_CTRL_ADC_ARB_APB_FORCE(1)
|
||||
esp.APB_SARADC.SetARB_CTRL_ADC_ARB_GRANT_FORCE(1)
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_CHANNEL(8) // (1<<3)|0 for ADC2 channel 0
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC2_ONETIME_SAMPLE(1)
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(1)
|
||||
for esp.APB_SARADC.GetINT_RAW_APB_SARADC2_DONE_INT_RAW() == 0 {
|
||||
}
|
||||
raw = esp.APB_SARADC.GetSAR2DATA_STATUS_APB_SARADC2_DATA()
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC2_ONETIME_SAMPLE(0)
|
||||
esp.APB_SARADC.SetARB_CTRL_ADC_ARB_APB_FORCE(0)
|
||||
esp.APB_SARADC.SetARB_CTRL_ADC_ARB_GRANT_FORCE(0)
|
||||
}
|
||||
return uint16(raw&0xfff) << 4
|
||||
}
|
||||
|
||||
// adcSelfCalibration
|
||||
const (
|
||||
adcCalTimesC3 = 15
|
||||
adcCalRtcMagicC3 = uint32(0xADC1C401)
|
||||
adcCalInitMinC3 = uint32(1000)
|
||||
adcCalInitMaxC3 = uint32(4096)
|
||||
)
|
||||
|
||||
// selfCalibrate sets ADC1/ADC2 init code from RTC or runs self-calibration (GND).
|
||||
// eFuse is not used: on ESP32-C3 the ADC calibration fields in BLK2 are often unprogrammed.
|
||||
func adcSelfCalibrate() {
|
||||
reg := newRegI2C()
|
||||
reg.sarEnable()
|
||||
|
||||
var adc1Code uint32
|
||||
if saved, ok := restoreFromRTC(); ok {
|
||||
adc1Code = saved
|
||||
} else {
|
||||
calSetupADC1()
|
||||
reg.calibrationInit(0)
|
||||
reg.calibrationPrepare(0)
|
||||
adc1Code = reg.calibrateBinarySearch(0, adcCalTimesC3, readADC1)
|
||||
if adc1Code < adcCalInitMinC3 {
|
||||
adc1Code = adcCalInitMinC3
|
||||
}
|
||||
if adc1Code > adcCalInitMaxC3 {
|
||||
adc1Code = adcCalInitMaxC3
|
||||
}
|
||||
saveToRTC(adc1Code)
|
||||
reg.calibrationFinish(0)
|
||||
}
|
||||
|
||||
applyADC1Code(reg, adc1Code)
|
||||
applyADC2Code(reg, adc1Code)
|
||||
}
|
||||
|
||||
// calSetupADC1 configures APB_SARADC for oneshot sampling on ADC1 channel 0
|
||||
// with fixed attenuation. This is used only during self‑calibration.
|
||||
func calSetupADC1() {
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_ATTEN(atten11dB)
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_CHANNEL(0)
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC1_ONETIME_SAMPLE(1)
|
||||
}
|
||||
|
||||
// calSetupADC2 configures APB_SARADC for oneshot sampling on ADC2 (GPIO5, ch 0).
|
||||
// On C3, onetime_channel = (unit<<3)|channel → ADC2 ch0 = 8.
|
||||
func calSetupADC2() {
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_ATTEN(atten11dB)
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_CHANNEL(8) // (1<<3)|0 for ADC2
|
||||
esp.APB_SARADC.SetARB_CTRL_ADC_ARB_APB_FORCE(1)
|
||||
esp.APB_SARADC.SetARB_CTRL_ADC_ARB_GRANT_FORCE(1)
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC2_ONETIME_SAMPLE(1)
|
||||
}
|
||||
|
||||
// readADC1 performs a single ADC1 conversion using the APB_SARADC
|
||||
// oneshot path and returns the raw 12‑bit result (0..4095).
|
||||
func readADC1() uint32 {
|
||||
esp.APB_SARADC.SetINT_CLR_APB_SARADC1_DONE_INT_CLR(1)
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(1)
|
||||
for esp.APB_SARADC.GetINT_RAW_APB_SARADC1_DONE_INT_RAW() == 0 {
|
||||
}
|
||||
raw := esp.APB_SARADC.GetSAR1DATA_STATUS_APB_SARADC1_DATA() & 0xfff
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
|
||||
return uint32(raw)
|
||||
}
|
||||
|
||||
// readADC2 performs a single ADC2 conversion and returns the raw 12‑bit result (0..4095).
|
||||
func readADC2() uint32 {
|
||||
esp.APB_SARADC.SetINT_CLR_APB_SARADC2_DONE_INT_CLR(1)
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(1)
|
||||
for esp.APB_SARADC.GetINT_RAW_APB_SARADC2_DONE_INT_RAW() == 0 {
|
||||
}
|
||||
raw := esp.APB_SARADC.GetSAR2DATA_STATUS_APB_SARADC2_DATA() & 0xfff
|
||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
|
||||
esp.APB_SARADC.SetARB_CTRL_ADC_ARB_APB_FORCE(0)
|
||||
esp.APB_SARADC.SetARB_CTRL_ADC_ARB_GRANT_FORCE(0)
|
||||
return uint32(raw)
|
||||
}
|
||||
|
||||
func restoreFromRTC() (uint32, bool) {
|
||||
if esp.RTC_CNTL.GetSTORE0() != adcCalRtcMagicC3 {
|
||||
return 0, false
|
||||
}
|
||||
code := esp.RTC_CNTL.GetSTORE1()
|
||||
if code < adcCalInitMinC3 || code > adcCalInitMaxC3 {
|
||||
return 0, false
|
||||
}
|
||||
return code, true
|
||||
}
|
||||
|
||||
func saveToRTC(code uint32) {
|
||||
if code < adcCalInitMinC3 || code > adcCalInitMaxC3 {
|
||||
return
|
||||
}
|
||||
esp.RTC_CNTL.SetSTORE0(adcCalRtcMagicC3)
|
||||
esp.RTC_CNTL.SetSTORE1(code)
|
||||
}
|
||||
|
||||
// applyADC1Code sets ADC1 init code and finishes calibration.
|
||||
func applyADC1Code(reg regI2C, code uint32) {
|
||||
calSetupADC1()
|
||||
reg.calibrationInit(0)
|
||||
reg.calibrationPrepare(0)
|
||||
reg.setCalibrationParam(0, code)
|
||||
reg.calibrationFinish(0)
|
||||
}
|
||||
|
||||
// applyADC2Code sets ADC2 init code and finishes calibration. On C3 eFuse V1
|
||||
// there is no separate ADC2 calibration; IDF uses ADC1 init code for both units.
|
||||
func applyADC2Code(reg regI2C, code uint32) {
|
||||
reg.calibrationInit(1)
|
||||
reg.calibrationPrepare(1)
|
||||
reg.setCalibrationParam(1, code)
|
||||
reg.calibrationFinish(1)
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
//go:build esp32c3
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
/*
|
||||
#include <stdint.h>
|
||||
extern int esp_rom_spiflash_read(uint32_t src_addr, uint32_t *data, uint32_t len);
|
||||
extern int esp_rom_spiflash_write(uint32_t dest_addr, const uint32_t *data, uint32_t len);
|
||||
extern int esp_rom_spiflash_erase_sector(uint32_t sector_num);
|
||||
extern int esp_rom_spiflash_unlock(void);
|
||||
extern void Cache_Invalidate_Addr(uint32_t addr, uint32_t size);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// compile-time check for ensuring we fulfill BlockDevice interface
|
||||
var _ BlockDevice = flashBlockDevice{}
|
||||
|
||||
var Flash flashBlockDevice
|
||||
|
||||
type flashBlockDevice struct {
|
||||
}
|
||||
|
||||
// ReadAt reads the given number of bytes from the block device.
|
||||
func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
if readAddress(off)+uintptr(len(p)) > FlashDataEnd() {
|
||||
return 0, errFlashCannotReadPastEOF
|
||||
}
|
||||
|
||||
data := unsafe.Slice((*byte)(unsafe.Add(unsafe.Pointer(FlashDataStart()), off)), len(p))
|
||||
copy(p, data)
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// WriteAt writes the given number of bytes to the block device.
|
||||
// Only word (32 bits) length data can be programmed.
|
||||
// If the length of p is not long enough it will be padded with 0xFF bytes.
|
||||
// This method assumes that the destination is already erased.
|
||||
func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
|
||||
return f.writeAt(p, off)
|
||||
}
|
||||
|
||||
// Size returns the number of bytes in this block device.
|
||||
func (f flashBlockDevice) Size() int64 {
|
||||
return int64(FlashDataEnd() - FlashDataStart())
|
||||
}
|
||||
|
||||
const writeBlockSize = 4
|
||||
|
||||
// WriteBlockSize returns the block size in which data can be written to
|
||||
// memory. It can be used by a client to optimize writes, non-aligned writes
|
||||
// should always work correctly.
|
||||
func (f flashBlockDevice) WriteBlockSize() int64 {
|
||||
return writeBlockSize
|
||||
}
|
||||
|
||||
const eraseBlockSizeValue = 1 << 12
|
||||
|
||||
func eraseBlockSize() int64 {
|
||||
return eraseBlockSizeValue
|
||||
}
|
||||
|
||||
// EraseBlockSize returns the smallest erasable area on this particular chip
|
||||
// in bytes. This is used for the block size in EraseBlocks.
|
||||
func (f flashBlockDevice) EraseBlockSize() int64 {
|
||||
return eraseBlockSize()
|
||||
}
|
||||
|
||||
// EraseBlocks erases the given number of blocks. An implementation may
|
||||
// transparently coalesce ranges of blocks into larger bundles if the chip
|
||||
// supports this. The start and len parameters are in block numbers, use
|
||||
// EraseBlockSize to map addresses to blocks.
|
||||
func (f flashBlockDevice) EraseBlocks(start, length int64) error {
|
||||
return f.eraseBlocks(start, length)
|
||||
}
|
||||
|
||||
// return the correct address to be used for reads
|
||||
func readAddress(off int64) uintptr {
|
||||
return FlashDataStart() + uintptr(off)
|
||||
}
|
||||
|
||||
const flashDROMStart = 0x3C000000
|
||||
|
||||
// return the correct physical address to be used for write/erase
|
||||
func writeAddress(off int64) uint32 {
|
||||
// DROM maps 1:1 with flash physical offset, starting at 0x3C000000.
|
||||
return uint32(readAddress(off) - flashDROMStart)
|
||||
}
|
||||
|
||||
func (f flashBlockDevice) writeAt(p []byte, off int64) (n int, err error) {
|
||||
if readAddress(off)+uintptr(len(p)) > FlashDataEnd() {
|
||||
return 0, errFlashCannotWritePastEOF
|
||||
}
|
||||
|
||||
address := writeAddress(off)
|
||||
padded := flashPad(p, int(f.WriteBlockSize()))
|
||||
|
||||
state := interrupt.Disable()
|
||||
defer interrupt.Restore(state)
|
||||
|
||||
C.esp_rom_spiflash_unlock()
|
||||
res := C.esp_rom_spiflash_write(C.uint32_t(address), (*C.uint32_t)(unsafe.Pointer(&padded[0])), C.uint32_t(len(padded)))
|
||||
C.Cache_Invalidate_Addr(C.uint32_t(readAddress(off)), C.uint32_t(len(padded)))
|
||||
if res != 0 {
|
||||
return 0, errFlashCannotWriteData
|
||||
}
|
||||
|
||||
return len(padded), nil
|
||||
}
|
||||
|
||||
func (f flashBlockDevice) eraseBlocks(start, length int64) error {
|
||||
address := writeAddress(start * f.EraseBlockSize())
|
||||
if uintptr(unsafe.Add(unsafe.Pointer(uintptr(address)+flashDROMStart), length*f.EraseBlockSize())) > FlashDataEnd() {
|
||||
return errFlashCannotErasePastEOF
|
||||
}
|
||||
|
||||
state := interrupt.Disable()
|
||||
defer interrupt.Restore(state)
|
||||
|
||||
C.esp_rom_spiflash_unlock()
|
||||
sector := address / uint32(f.EraseBlockSize())
|
||||
|
||||
for i := int64(0); i < length; i++ {
|
||||
res := C.esp_rom_spiflash_erase_sector(C.uint32_t(sector + uint32(i)))
|
||||
C.Cache_Invalidate_Addr(C.uint32_t(readAddress((start+i)*f.EraseBlockSize())), C.uint32_t(f.EraseBlockSize()))
|
||||
if res != 0 {
|
||||
return errFlashCannotErasePage
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -4,18 +4,350 @@ package machine
|
||||
|
||||
import (
|
||||
"device/esp"
|
||||
)
|
||||
|
||||
const (
|
||||
I2CEXT0_SCL_OUT_IDX = 53
|
||||
I2CEXT0_SDA_OUT_IDX = 54
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
I2C0 = &I2C{
|
||||
Bus: esp.I2C0,
|
||||
funcSCL: I2CEXT0_SCL_OUT_IDX,
|
||||
funcSDA: I2CEXT0_SDA_OUT_IDX,
|
||||
useExt1: false,
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
//go:build esp32c3
|
||||
|
||||
package machine
|
||||
|
||||
import "device/esp"
|
||||
|
||||
// LEDC PWM for ESP32-C3: 4 timers (PWM0–PWM3), 6 channels per timer; each timer has its own frequency.
|
||||
// Range: frequency from a few Hz up to ~40 MHz (at 1-bit resolution); duty resolution 1–15 bits
|
||||
// (higher frequency gives lower resolution). Clock source: APB 80 MHz. Low-speed mode only.
|
||||
// See ESP-IDF LEDC driver, TRM LED PWM Controller.
|
||||
|
||||
// GPIO matrix output signal indices for LEDC (soc/gpio_sig_map.h)
|
||||
const (
|
||||
LEDC_LS_SIG_OUT0_IDX = 45
|
||||
)
|
||||
|
||||
const ledcChannelsC3 = 6
|
||||
|
||||
var (
|
||||
PWM0 = &LEDCPWM{SigOutBase: LEDC_LS_SIG_OUT0_IDX, NumChannels: ledcChannelsC3, timerNum: 0}
|
||||
PWM1 = &LEDCPWM{SigOutBase: LEDC_LS_SIG_OUT0_IDX, NumChannels: ledcChannelsC3, timerNum: 1}
|
||||
PWM2 = &LEDCPWM{SigOutBase: LEDC_LS_SIG_OUT0_IDX, NumChannels: ledcChannelsC3, timerNum: 2}
|
||||
PWM3 = &LEDCPWM{SigOutBase: LEDC_LS_SIG_OUT0_IDX, NumChannels: ledcChannelsC3, timerNum: 3}
|
||||
)
|
||||
|
||||
// chanOp implements LEDC low-speed channel ops for ESP32-C3 (channels 0–5 only).
|
||||
func (pwm *LEDCPWM) chanOp(ch uint8, op ledcChanOp, duty uint32, inverting bool) {
|
||||
invVal := uint32(0)
|
||||
if inverting {
|
||||
invVal = 1
|
||||
}
|
||||
switch ch {
|
||||
case 0:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH0_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH0_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH0_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH0_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH0_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH0_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH0_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH0_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH0_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH0_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 1:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH1_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH1_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH1_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH1_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH1_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH1_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH1_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH1_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH1_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH1_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 2:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH2_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH2_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH2_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH2_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH2_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH2_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH2_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH2_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH2_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH2_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 3:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH3_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH3_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH3_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH3_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH3_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH3_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH3_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH3_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH3_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH3_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 4:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH4_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH4_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH4_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH4_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH4_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH4_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH4_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH4_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH4_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH4_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 5:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH5_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH5_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH5_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH5_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH5_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH5_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH5_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH5_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH5_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH5_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
SPI_MODE0 = uint8(0)
|
||||
SPI_MODE1 = uint8(1)
|
||||
SPI_MODE2 = uint8(2)
|
||||
SPI_MODE3 = uint8(3)
|
||||
|
||||
FSPICLK_IN_IDX = uint32(63)
|
||||
FSPICLK_OUT_IDX = uint32(63)
|
||||
FSPIQ_IN_IDX = uint32(64)
|
||||
@@ -48,9 +53,66 @@ type SPI struct {
|
||||
var (
|
||||
// SPI0 and SPI1 are reserved for use by the caching system etc.
|
||||
SPI2 = &SPI{esp.SPI2}
|
||||
SPI0 = SPI2
|
||||
)
|
||||
|
||||
// SPIConfig is used to store config info for SPI.
|
||||
type SPIConfig struct {
|
||||
Frequency uint32
|
||||
SCK Pin // Serial Clock
|
||||
SDO Pin // Serial Data Out (MOSI)
|
||||
SDI Pin // Serial Data In (MISO)
|
||||
CS Pin // Chip Select (optional)
|
||||
LSBFirst bool // MSB is default
|
||||
Mode uint8 // SPI_MODE0 is default
|
||||
}
|
||||
|
||||
// Compute the SPI bus frequency from the CPU frequency.
|
||||
func freqToClockDiv(hz uint32) uint32 {
|
||||
fcpu := CPUFrequency()
|
||||
if hz >= fcpu { // maximum frequency
|
||||
return 1 << 31
|
||||
}
|
||||
if hz < (fcpu / (16 * 64)) { // minimum frequency
|
||||
return 15<<18 | 63<<12 | 31<<6 | 63 // pre=15, n=63
|
||||
}
|
||||
|
||||
// iterate looking for an exact match
|
||||
// or iterate all 16 prescaler options
|
||||
// looking for the smallest error
|
||||
var bestPre, bestN, bestErr uint32
|
||||
bestN = 1
|
||||
bestErr = 0xffffffff
|
||||
q := uint32(float32(pplClockFreq)/float32(hz) + float32(0.5))
|
||||
for p := uint32(0); p < 16; p++ {
|
||||
n := q/(p+1) - 1
|
||||
if n < 1 { // prescaler became too large, stop enum
|
||||
break
|
||||
}
|
||||
if n > 63 { // prescaler too small, skip to next
|
||||
continue
|
||||
}
|
||||
|
||||
freq := fcpu / ((p + 1) * (n + 1))
|
||||
if freq == hz { // exact match
|
||||
return p<<18 | n<<12 | (n/2)<<6 | n
|
||||
}
|
||||
|
||||
var err uint32
|
||||
if freq < hz {
|
||||
err = hz - freq
|
||||
} else {
|
||||
err = freq - hz
|
||||
}
|
||||
if err < bestErr {
|
||||
bestErr = err
|
||||
bestPre = p
|
||||
bestN = n
|
||||
}
|
||||
}
|
||||
|
||||
return bestPre<<18 | bestN<<12 | (bestN/2)<<6 | bestN
|
||||
}
|
||||
|
||||
// Configure and make the SPI peripheral ready to use.
|
||||
func (spi *SPI) Configure(config SPIConfig) error {
|
||||
// right now this is only setup to work for the esp32c3 spi2 bus
|
||||
@@ -109,16 +171,16 @@ func (spi *SPI) Configure(config SPIConfig) error {
|
||||
|
||||
// set spi2 data mode
|
||||
switch config.Mode {
|
||||
case Mode0:
|
||||
case SPI_MODE0:
|
||||
spi.Bus.SetMISC_CK_IDLE_EDGE(0)
|
||||
spi.Bus.SetUSER_CK_OUT_EDGE(0)
|
||||
case Mode1:
|
||||
case SPI_MODE1:
|
||||
spi.Bus.SetMISC_CK_IDLE_EDGE(0)
|
||||
spi.Bus.SetUSER_CK_OUT_EDGE(1)
|
||||
case Mode2:
|
||||
case SPI_MODE2:
|
||||
spi.Bus.SetMISC_CK_IDLE_EDGE(1)
|
||||
spi.Bus.SetUSER_CK_OUT_EDGE(1)
|
||||
case Mode3:
|
||||
case SPI_MODE3:
|
||||
spi.Bus.SetMISC_CK_IDLE_EDGE(1)
|
||||
spi.Bus.SetUSER_CK_OUT_EDGE(0)
|
||||
default:
|
||||
@@ -137,12 +199,6 @@ func (spi *SPI) Configure(config SPIConfig) error {
|
||||
// configure SPI bus clock
|
||||
spi.Bus.CLOCK.Set(freqToClockDiv(config.Frequency))
|
||||
|
||||
// Default CS to NoPin so that an unset CS field (zero value = GPIO0)
|
||||
// does not accidentally configure GPIO0 as the chip select output.
|
||||
if config.CS == 0 {
|
||||
config.CS = NoPin
|
||||
}
|
||||
|
||||
// configure esp32c3 gpio pin matrix
|
||||
config.SDI.Configure(PinConfig{Mode: PinInput})
|
||||
inFunc(FSPIQ_IN_IDX).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.SDI))
|
||||
@@ -197,7 +253,36 @@ func (spi *SPI) Tx(w, r []byte) error {
|
||||
|
||||
// Fill tx buffer.
|
||||
transferWords := (*[16]volatile.Register32)(unsafe.Pointer(uintptr(unsafe.Pointer(&spi.Bus.W0))))
|
||||
spiTxFillBuffer(transferWords, w)
|
||||
if len(w) >= 64 {
|
||||
// We can fill the entire 64-byte transfer buffer with data.
|
||||
// This loop is slightly faster than the loop below.
|
||||
for i := 0; i < 16; i++ {
|
||||
word := uint32(w[i*4]) | uint32(w[i*4+1])<<8 | uint32(w[i*4+2])<<16 | uint32(w[i*4+3])<<24
|
||||
transferWords[i].Set(word)
|
||||
}
|
||||
} else {
|
||||
// We can't fill the entire transfer buffer, so we need to be a bit
|
||||
// more careful.
|
||||
// Note that parts of the transfer buffer that aren't used still
|
||||
// need to be set to zero, otherwise we might be transferring
|
||||
// garbage from a previous transmission if w is smaller than r.
|
||||
for i := 0; i < 16; i++ {
|
||||
var word uint32
|
||||
if i*4+3 < len(w) {
|
||||
word |= uint32(w[i*4+3]) << 24
|
||||
}
|
||||
if i*4+2 < len(w) {
|
||||
word |= uint32(w[i*4+2]) << 16
|
||||
}
|
||||
if i*4+1 < len(w) {
|
||||
word |= uint32(w[i*4+1]) << 8
|
||||
}
|
||||
if i*4+0 < len(w) {
|
||||
word |= uint32(w[i*4+0]) << 0
|
||||
}
|
||||
transferWords[i].Set(word)
|
||||
}
|
||||
}
|
||||
|
||||
// Do the transfer.
|
||||
spi.Bus.SetMS_DLEN_MS_DATA_BITLEN(uint32(chunkSize)*8 - 1)
|
||||
|
||||
@@ -1,285 +0,0 @@
|
||||
//go:build esp32c3
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/esp"
|
||||
"errors"
|
||||
"machine/usb"
|
||||
"machine/usb/descriptor"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// USB Serial/JTAG Controller
|
||||
// See esp32-c3_technical_reference_manual_en.pdf pg. 736
|
||||
//
|
||||
// The ESP32-C3 has a built-in USB Serial/JTAG controller that provides a
|
||||
// CDC-ACM serial port. The USB protocol and enumeration are handled entirely
|
||||
// in hardware; software only reads/writes the EP1 FIFO.
|
||||
|
||||
const cpuInterruptFromUSB = 10
|
||||
|
||||
type USB_DEVICE struct {
|
||||
Bus *esp.USB_DEVICE_Type
|
||||
Buffer *RingBuffer
|
||||
txPending bool // unflushed data in the EP1 TX FIFO
|
||||
txStalled bool // set when flushAndWait fails (no host reading); cleared when FIFO becomes writable
|
||||
}
|
||||
|
||||
var (
|
||||
_USBCDC = &USB_DEVICE{
|
||||
Bus: esp.USB_DEVICE,
|
||||
Buffer: NewRingBuffer(),
|
||||
}
|
||||
|
||||
USBCDC Serialer = _USBCDC
|
||||
)
|
||||
|
||||
var (
|
||||
errUSBWrongSize = errors.New("USB: invalid write size")
|
||||
errUSBCouldNotWriteAllData = errors.New("USB: could not write all data")
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
var usbConfigured bool
|
||||
|
||||
// USBDevice provides a stub USB device for the ESP32-C3. The hardware
|
||||
// only supports a fixed-function CDC-ACM serial port, so the programmable
|
||||
// USB device features are no-ops.
|
||||
type USBDevice struct {
|
||||
initcomplete bool
|
||||
InitEndpointComplete bool
|
||||
}
|
||||
|
||||
var USBDev = &USBDevice{}
|
||||
|
||||
func (dev *USBDevice) SetStallEPIn(ep uint32) {}
|
||||
func (dev *USBDevice) SetStallEPOut(ep uint32) {}
|
||||
func (dev *USBDevice) ClearStallEPIn(ep uint32) {}
|
||||
func (dev *USBDevice) ClearStallEPOut(ep uint32) {}
|
||||
|
||||
// initUSB is intentionally empty — the interp phase evaluates init()
|
||||
// functions at compile time and cannot access hardware registers.
|
||||
// Actual hardware setup is deferred to the first Configure() call.
|
||||
func initUSB() {}
|
||||
|
||||
// Configure initialises the USB Serial/JTAG controller clock, pads, and
|
||||
// interrupt so that received data is buffered automatically.
|
||||
func (usbdev *USB_DEVICE) Configure(config UARTConfig) error {
|
||||
if usbConfigured {
|
||||
return nil
|
||||
}
|
||||
usbConfigured = true
|
||||
|
||||
// Enable the USB_DEVICE peripheral clock.
|
||||
// Do NOT reset the peripheral — the ROM bootloader has already
|
||||
// configured the USB Serial/JTAG controller and the host may
|
||||
// already be connected. Resetting would drop the USB link.
|
||||
esp.SYSTEM.SetPERIP_CLK_EN0_USB_DEVICE_CLK_EN(1)
|
||||
esp.SYSTEM.SetPERIP_RST_EN0_USB_DEVICE_RST(0)
|
||||
|
||||
// Ensure internal PHY is selected and USB pads are enabled.
|
||||
usbdev.Bus.SetCONF0_PHY_SEL(0)
|
||||
usbdev.Bus.SetCONF0_USB_PAD_ENABLE(1)
|
||||
usbdev.Bus.SetCONF0_DP_PULLUP(1)
|
||||
|
||||
// Clear any pending interrupts.
|
||||
usbdev.Bus.INT_CLR.Set(0xFFFFFFFF)
|
||||
|
||||
// Enable the RX-packet-received interrupt.
|
||||
usbdev.Bus.SetINT_ENA_SERIAL_OUT_RECV_PKT_INT_ENA(1)
|
||||
|
||||
// Map the USB peripheral interrupt to CPU interrupt cpuInterruptFromUSB.
|
||||
esp.INTERRUPT_CORE0.SetUSB_INTR_MAP(cpuInterruptFromUSB)
|
||||
|
||||
_ = interrupt.New(cpuInterruptFromUSB, func(interrupt.Interrupt) {
|
||||
_USBCDC.handleInterrupt()
|
||||
}).Enable()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureConfigured triggers lazy initialization on first use.
|
||||
func (usbdev *USB_DEVICE) ensureConfigured() {
|
||||
if !usbConfigured {
|
||||
usbdev.Configure(UARTConfig{})
|
||||
}
|
||||
}
|
||||
|
||||
// handleInterrupt drains the hardware RX FIFO into the software ring buffer.
|
||||
func (usbdev *USB_DEVICE) handleInterrupt() {
|
||||
// Read INT_ST while INT_ENA is still set (INT_ST = INT_RAW & INT_ENA).
|
||||
intStatus := usbdev.Bus.INT_ST.Get()
|
||||
|
||||
// Disable the RX interrupt to prevent re-triggering while we drain.
|
||||
usbdev.Bus.SetINT_ENA_SERIAL_OUT_RECV_PKT_INT_ENA(0)
|
||||
|
||||
if intStatus&esp.USB_DEVICE_INT_ST_SERIAL_OUT_RECV_PKT_INT_ST != 0 {
|
||||
// Drain all available bytes from the EP1 OUT FIFO.
|
||||
// Use EP1.Get() directly — the generated GetEP1_RDWR_BYTE is
|
||||
// functionally identical, but a direct load makes the FIFO-pop
|
||||
// intent explicit.
|
||||
for usbdev.Bus.GetEP1_CONF_SERIAL_OUT_EP_DATA_AVAIL() != 0 {
|
||||
b := byte(usbdev.Bus.EP1.Get())
|
||||
usbdev.Buffer.Put(b)
|
||||
}
|
||||
// Clear the interrupt.
|
||||
usbdev.Bus.SetINT_CLR_SERIAL_OUT_RECV_PKT_INT_CLR(1)
|
||||
}
|
||||
|
||||
// Re-enable the RX interrupt.
|
||||
usbdev.Bus.SetINT_ENA_SERIAL_OUT_RECV_PKT_INT_ENA(1)
|
||||
}
|
||||
|
||||
func (usbdev *USB_DEVICE) WriteByte(c byte) error {
|
||||
usbdev.ensureConfigured()
|
||||
if usbdev.Bus.GetEP1_CONF_SERIAL_IN_EP_DATA_FREE() == 0 {
|
||||
// FIFO locked by a pending USB transfer.
|
||||
if usbdev.txStalled {
|
||||
// Previously failed — skip the expensive spin and drop
|
||||
// the byte. When a host reconnects SERIAL_IN_EP_DATA_FREE
|
||||
// goes back to 1, clearing the stall on the next call.
|
||||
return errUSBCouldNotWriteAllData
|
||||
}
|
||||
// First time the FIFO is full: wait briefly for the host to
|
||||
// read the previous packet.
|
||||
if !usbdev.flushAndWait() {
|
||||
usbdev.txStalled = true
|
||||
return errUSBCouldNotWriteAllData
|
||||
}
|
||||
}
|
||||
usbdev.txStalled = false
|
||||
|
||||
// Use EP1.Set() (direct store) instead of SetEP1_RDWR_BYTE which
|
||||
// does a read-modify-write — the read side-effect pops a byte from
|
||||
// the RX FIFO.
|
||||
usbdev.Bus.EP1.Set(uint32(c))
|
||||
|
||||
// Only signal WR_DONE on newline to batch bytes into a single USB
|
||||
// packet. The FIFO-full path above also flushes when the 64-byte
|
||||
// FIFO fills up.
|
||||
if c == '\n' {
|
||||
usbdev.flush()
|
||||
usbdev.txPending = false
|
||||
} else {
|
||||
usbdev.txPending = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (usbdev *USB_DEVICE) Write(data []byte) (n int, err error) {
|
||||
usbdev.ensureConfigured()
|
||||
if len(data) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
for i, c := range data {
|
||||
if usbdev.Bus.GetEP1_CONF_SERIAL_IN_EP_DATA_FREE() == 0 {
|
||||
if usbdev.txStalled {
|
||||
return i, errUSBCouldNotWriteAllData
|
||||
}
|
||||
if !usbdev.flushAndWait() {
|
||||
usbdev.txStalled = true
|
||||
return i, errUSBCouldNotWriteAllData
|
||||
}
|
||||
}
|
||||
usbdev.txStalled = false
|
||||
usbdev.Bus.EP1.Set(uint32(c))
|
||||
}
|
||||
|
||||
usbdev.flush()
|
||||
usbdev.txPending = false
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
// Buffered returns the number of bytes waiting in the receive ring buffer.
|
||||
func (usbdev *USB_DEVICE) Buffered() int {
|
||||
usbdev.ensureConfigured()
|
||||
// Flush any pending TX data so callers like echo loops don't
|
||||
// need to explicitly flush after WriteByte.
|
||||
if usbdev.txPending {
|
||||
usbdev.flush()
|
||||
usbdev.txPending = false
|
||||
}
|
||||
return int(usbdev.Buffer.Used())
|
||||
}
|
||||
|
||||
// ReadByte returns a byte from the receive ring buffer.
|
||||
func (usbdev *USB_DEVICE) ReadByte() (byte, error) {
|
||||
b, ok := usbdev.Buffer.Get()
|
||||
if !ok {
|
||||
return 0, nil
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (usbdev *USB_DEVICE) DTR() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (usbdev *USB_DEVICE) RTS() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// flush signals WR_DONE to tell the hardware to send the data that has
|
||||
// been written to the EP1 FIFO. Returns immediately without waiting.
|
||||
func (usbdev *USB_DEVICE) flush() {
|
||||
usbdev.Bus.SetEP1_CONF_WR_DONE(1)
|
||||
}
|
||||
|
||||
// FlushSerial flushes any pending USB serial TX data. Called from the
|
||||
// runtime (e.g. before sleeping) to ensure data from print() without
|
||||
// a trailing newline gets sent promptly.
|
||||
func FlushSerial() {
|
||||
if _USBCDC.txPending {
|
||||
_USBCDC.flush()
|
||||
_USBCDC.txPending = false
|
||||
}
|
||||
}
|
||||
|
||||
// flushAndWait signals WR_DONE and waits for the EP1 FIFO to become
|
||||
// writable again. The timeout covers a few USB frames so that data gets
|
||||
// through when a host is connected. Returns false if the FIFO is still
|
||||
// locked after the timeout (no host reading).
|
||||
func (usbdev *USB_DEVICE) flushAndWait() bool {
|
||||
usbdev.Bus.SetEP1_CONF_WR_DONE(1)
|
||||
for i := 0; i < 50000; i++ {
|
||||
if usbdev.Bus.GetEP1_CONF_SERIAL_IN_EP_DATA_FREE() != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// The ESP32-C3 USB Serial/JTAG controller is fixed-function hardware.
|
||||
// It only provides a CDC-ACM serial port; the USB protocol and endpoint
|
||||
// configuration are handled entirely in silicon. The functions below
|
||||
// are no-op stubs so that higher-level USB packages (HID, MIDI, …)
|
||||
// compile, but they cannot add real endpoints on this hardware.
|
||||
|
||||
// ConfigureUSBEndpoint is a no-op on ESP32-C3 — the hardware does not
|
||||
// support programmable USB endpoints.
|
||||
func ConfigureUSBEndpoint(desc descriptor.Descriptor, epSettings []usb.EndpointConfig, setup []usb.SetupConfig) {
|
||||
}
|
||||
|
||||
// SendZlp is a no-op on ESP32-C3 — the hardware handles control
|
||||
// transfers internally.
|
||||
func SendZlp() {
|
||||
}
|
||||
|
||||
// SendUSBInPacket is a no-op on ESP32-C3 — the hardware does not
|
||||
// support arbitrary IN endpoints. Returns false to indicate the
|
||||
// packet was not sent.
|
||||
func SendUSBInPacket(ep uint32, data []byte) bool {
|
||||
return false
|
||||
}
|
||||
@@ -5,9 +5,7 @@ package machine
|
||||
import (
|
||||
"device/esp"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -72,7 +70,6 @@ const (
|
||||
PinInput
|
||||
PinInputPullup
|
||||
PinInputPulldown
|
||||
PinAnalog
|
||||
)
|
||||
|
||||
// Hardware pin numbers
|
||||
@@ -124,29 +121,6 @@ const (
|
||||
GPIO48 Pin = 48
|
||||
)
|
||||
|
||||
const (
|
||||
ADC0 Pin = GPIO1
|
||||
ADC2 Pin = GPIO2
|
||||
ADC3 Pin = GPIO3
|
||||
ADC4 Pin = GPIO4
|
||||
ADC5 Pin = GPIO5
|
||||
ADC6 Pin = GPIO6
|
||||
ADC7 Pin = GPIO7
|
||||
ADC8 Pin = GPIO8
|
||||
ADC9 Pin = GPIO9
|
||||
ADC10 Pin = GPIO10
|
||||
ADC11 Pin = GPIO11
|
||||
ADC12 Pin = GPIO12
|
||||
ADC13 Pin = GPIO13
|
||||
ADC14 Pin = GPIO14
|
||||
ADC15 Pin = GPIO15
|
||||
ADC16 Pin = GPIO16
|
||||
ADC17 Pin = GPIO17
|
||||
ADC18 Pin = GPIO18
|
||||
ADC19 Pin = GPIO19
|
||||
ADC20 Pin = GPIO20
|
||||
)
|
||||
|
||||
// Configure this pin with the given configuration.
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
// Output function 256 is a special value reserved for use as a regular GPIO
|
||||
@@ -172,10 +146,8 @@ func (p Pin) configure(config PinConfig, signal uint32) {
|
||||
// MCU_SEL: Function 1 is always GPIO
|
||||
ioConfig |= (1 << esp.IO_MUX_GPIO_MCU_SEL_Pos)
|
||||
|
||||
// FUN_IE: disable for PinAnalog (high-Z for ADC), enable for digital
|
||||
if config.Mode != PinAnalog {
|
||||
ioConfig |= esp.IO_MUX_GPIO_FUN_IE
|
||||
}
|
||||
// FUN_IE: Make this pin an input pin (always set for GPIO operation)
|
||||
ioConfig |= esp.IO_MUX_GPIO_FUN_IE
|
||||
|
||||
// DRV: Set drive strength to 20 mA as a default. Pins 17 and 18 are special
|
||||
var drive uint32
|
||||
@@ -186,7 +158,7 @@ func (p Pin) configure(config PinConfig, signal uint32) {
|
||||
}
|
||||
ioConfig |= (drive << esp.IO_MUX_GPIO_FUN_DRV_Pos)
|
||||
|
||||
// WPU/WPD: no pulls for PinAnalog
|
||||
// WPU/WPD: Select pull mode.
|
||||
if config.Mode == PinInputPullup {
|
||||
ioConfig |= esp.IO_MUX_GPIO_FUN_WPU
|
||||
} else if config.Mode == PinInputPulldown {
|
||||
@@ -209,14 +181,14 @@ func (p Pin) configure(config PinConfig, signal uint32) {
|
||||
// output signal, or the special value 256 which indicates regular GPIO
|
||||
// usage.
|
||||
p.outFunc().Set(signal)
|
||||
case PinInput, PinInputPullup, PinInputPulldown, PinAnalog:
|
||||
case PinInput, PinInputPullup, PinInputPulldown:
|
||||
// Clear the 'output enable' bit.
|
||||
if p < 32 {
|
||||
esp.GPIO.ENABLE_W1TC.Set(1 << p)
|
||||
} else {
|
||||
esp.GPIO.ENABLE1_W1TC.Set(1 << (p - 32))
|
||||
}
|
||||
if signal != 256 && config.Mode != PinAnalog {
|
||||
if signal != 256 {
|
||||
// Signal is a peripheral function (not a simple GPIO). Connect this
|
||||
// signal to the pin.
|
||||
// Note that outFunc and inFunc work in the opposite direction.
|
||||
@@ -301,105 +273,6 @@ func (p Pin) Get() bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (p Pin) pinReg() *volatile.Register32 {
|
||||
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.GPIO.PIN0), uintptr(p)*4))
|
||||
}
|
||||
|
||||
const maxPin = 49
|
||||
|
||||
// cpuInterruptFromPin selects an edge-triggered CPU interrupt line for GPIO.
|
||||
// CPU interrupt 10 is edge-triggered level-1 on the Xtensa LX7, which prevents
|
||||
// the ISR from re-entering continuously when other peripherals (e.g. SPI via
|
||||
// the GPIO Matrix) keep GPIO.STATUS bits asserted.
|
||||
const cpuInterruptFromPin = 10
|
||||
|
||||
type PinChange uint8
|
||||
|
||||
// Pin change interrupt constants for SetInterrupt.
|
||||
const (
|
||||
PinRising PinChange = iota + 1
|
||||
PinFalling
|
||||
PinToggle
|
||||
)
|
||||
|
||||
// SetInterrupt sets an interrupt to be executed when a particular pin changes
|
||||
// state. The pin should already be configured as an input, including a pull up
|
||||
// or down if no external pull is provided.
|
||||
//
|
||||
// You can pass a nil func to unset the pin change interrupt. If you do so,
|
||||
// the change parameter is ignored and can be set to any value (such as 0).
|
||||
// If the pin is already configured with a callback, you must first unset
|
||||
// this pins interrupt before you can set a new callback.
|
||||
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) (err error) {
|
||||
if p >= maxPin {
|
||||
return ErrInvalidInputPin
|
||||
}
|
||||
|
||||
if callback == nil {
|
||||
// Disable this pin interrupt
|
||||
p.pinReg().ClearBits(esp.GPIO_PIN_INT_TYPE_Msk | esp.GPIO_PIN_INT_ENA_Msk)
|
||||
|
||||
if pinCallbacks[p] != nil {
|
||||
pinCallbacks[p] = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if pinCallbacks[p] != nil {
|
||||
// The pin was already configured.
|
||||
// To properly re-configure a pin, unset it first and set a new
|
||||
// configuration.
|
||||
return ErrNoPinChangeChannel
|
||||
}
|
||||
pinCallbacks[p] = callback
|
||||
|
||||
onceSetupPinInterrupt.Do(func() {
|
||||
err = setupPinInterrupt()
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.pinReg().Set(
|
||||
(p.pinReg().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
|
||||
}
|
||||
|
||||
var (
|
||||
pinCallbacks [maxPin]func(Pin)
|
||||
onceSetupPinInterrupt sync.Once
|
||||
)
|
||||
|
||||
func setupPinInterrupt() error {
|
||||
esp.INTERRUPT_CORE0.SetGPIO_INTERRUPT_PRO_MAP(cpuInterruptFromPin)
|
||||
return interrupt.New(cpuInterruptFromPin, func(interrupt.Interrupt) {
|
||||
// Read and immediately clear interrupt status bits.
|
||||
// Clearing before processing is critical for edge-triggered CPU
|
||||
// interrupts: any new GPIO events that arrive during callback
|
||||
// execution will set fresh STATUS bits, generating a new edge
|
||||
// on the CPU interrupt line so they are not lost.
|
||||
status := esp.GPIO.STATUS.Get()
|
||||
status1 := esp.GPIO.STATUS1.Get()
|
||||
esp.GPIO.STATUS_W1TC.Set(status)
|
||||
esp.GPIO.STATUS1_W1TC.Set(status1)
|
||||
|
||||
// Check status for GPIO0-31
|
||||
for i, mask := 0, uint32(1); i < 32; i, mask = i+1, mask<<1 {
|
||||
if (status&mask) != 0 && pinCallbacks[i] != nil {
|
||||
pinCallbacks[i](Pin(i))
|
||||
}
|
||||
}
|
||||
// Check status for GPIO32-48
|
||||
for i, mask := 32, uint32(1); i < maxPin; i, mask = i+1, mask<<1 {
|
||||
if (status1&mask) != 0 && pinCallbacks[i] != nil {
|
||||
pinCallbacks[i](Pin(i))
|
||||
}
|
||||
}
|
||||
}).Enable()
|
||||
}
|
||||
|
||||
var DefaultUART = UART0
|
||||
|
||||
var (
|
||||
@@ -435,38 +308,3 @@ func (uart *UART) writeByte(b byte) error {
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {}
|
||||
|
||||
// GetRNG returns 32-bit random numbers using the ESP32-S3 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-s3_technical_reference_manual_en.pdf p.920
|
||||
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.RNG.DATA.Get(), 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)
|
||||
}
|
||||
|
||||
@@ -1,432 +0,0 @@
|
||||
//go:build esp32s3
|
||||
|
||||
// ESP32-S3: 2 SAR ADCs, 12-bit hardware; Get() returns 0..65520 (scaled from 12-bit).
|
||||
// Pin mapping: ADC1 = GPIO 1..10 (channel = GPIO-1); ADC2 = GPIO 11..20 (channel = GPIO-11).
|
||||
// Get() returns raw, uncalibrated ADC values; accurate 0–3.3V mapping should be done
|
||||
// either by a two-point calibration in user code or by using the eFuse-based
|
||||
// calibration logic (see IDF adc_cali / our ADCSelfCalibrate implementation).
|
||||
//
|
||||
// Registers used (TRM / IDF):
|
||||
// SYSTEM: PERIP_RST_EN0.APB_SARADC_RST, PERIP_CLK_EN0.APB_SARADC_CLK_EN
|
||||
// RTC_CNTL: ANA_CONF.SAR_I2C_PU, I2C_RESET_POR_FORCE_PU
|
||||
// ADC1 RTC path (oneshot, TRM/IDF):
|
||||
// SENS.SAR_MEAS1_MUX.SAR1_DIG_FORCE = 0 → ADC1 under RTC (not digital/APB)
|
||||
// SENS.SAR_MEAS1_CTRL2.MEAS1_START_FORCE = 1, SAR1_EN_PAD_FORCE = 1 → SW triggers and selects channel
|
||||
// Per conversion: set attenuation (SAR_ATTEN1), channel (SAR1_EN_PAD), then MEAS1_START_SAR 0→1; wait MEAS1_DONE_SAR; read MEAS1_DATA_SAR.
|
||||
// SENS.SAR_MEAS1_CTRL1: amp/ref (FORCE_XPD_AMP etc). SAR_MEAS1_CTRL2: MEAS1_DONE_SAR (done), MEAS1_START_SAR (start), MEAS1_DATA_SAR (12-bit result).
|
||||
// APB_SARADC: FSM_WAIT, CLKM, etc. used for clock/shared logic; ADC2 uses ARB_CTRL.
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/esp"
|
||||
"errors"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// newRegI2C returns the regI2C configured for ESP32-S3: hostID=1, drefInit=4.
|
||||
func newRegI2C() regI2C { return regI2C{hostID: 1, drefInit: 4} }
|
||||
|
||||
var adcDigiRefMv uint32
|
||||
|
||||
func InitADC() {
|
||||
// SYSTEM: reset and enable APB_SARADC clock so SAR registers are accessible.
|
||||
esp.SYSTEM.SetPERIP_RST_EN0_APB_SARADC_RST(1)
|
||||
esp.SYSTEM.SetPERIP_CLK_EN0_APB_SARADC_CLK_EN(1)
|
||||
esp.SYSTEM.SetPERIP_RST_EN0_APB_SARADC_RST(0)
|
||||
|
||||
// SENS.SAR_PERI_CLK_GATE_CONF: enable SENS SAR peripheral clock (matches Arduino/IDF runtime state).
|
||||
esp.SENS.SetSAR_PERI_CLK_GATE_CONF_SARADC_CLK_EN(1)
|
||||
|
||||
// RTC_CNTL.ANA_CONF: keep internal SAR I2C (regI2C analog bus) powered and out of reset.
|
||||
esp.RTC_CNTL.SetANA_CONF_I2C_RESET_POR_FORCE_PD(0)
|
||||
esp.RTC_CNTL.SetANA_CONF_SAR_I2C_PU(1)
|
||||
esp.RTC_CNTL.SetANA_CONF_I2C_RESET_POR_FORCE_PU(1)
|
||||
|
||||
// SENS.SAR_POWER: power up SAR analog block and enable SAR internal clock.
|
||||
esp.SENS.SetSAR_POWER_XPD_SAR_FORCE_XPD_SAR(3)
|
||||
esp.SENS.SetSAR_POWER_XPD_SAR_SARCLK_EN(1)
|
||||
|
||||
// SENS.SAR_MEAS1_CTRL1: force ADC1 front-end amplifier and reference on in RTC oneshot mode.
|
||||
esp.SENS.SetSAR_MEAS1_CTRL1_FORCE_XPD_AMP(3)
|
||||
esp.SENS.SetSAR_MEAS1_CTRL1_AMP_RST_FB_FORCE(3)
|
||||
esp.SENS.SetSAR_MEAS1_CTRL1_AMP_SHORT_REF_FORCE(3)
|
||||
esp.SENS.SetSAR_MEAS1_CTRL1_AMP_SHORT_REF_GND_FORCE(3)
|
||||
|
||||
// SENS.SAR_AMP_CTRL1/2: amplifier/reference settling timings (same as cold-boot defaults).
|
||||
esp.SENS.SetSAR_AMP_CTRL1_SAR_AMP_WAIT1(10)
|
||||
esp.SENS.SetSAR_AMP_CTRL1_SAR_AMP_WAIT2(10)
|
||||
esp.SENS.SetSAR_AMP_CTRL2_SAR_XPD_SAR_AMP_FSM_IDLE(1)
|
||||
esp.SENS.SetSAR_AMP_CTRL2_SAR_AMP_SHORT_REF_GND_FSM_IDLE(1)
|
||||
|
||||
// ADC2 uses the same InitADC() as ADC1 (shared APB_SARADC clock/FSM).
|
||||
// SENS.SAR_MEAS2_CTRL1: ADC2 FSM wait timings for power-up/reset/standby.
|
||||
esp.SENS.SetSAR_MEAS2_CTRL1_SAR_SAR2_XPD_WAIT(8)
|
||||
esp.SENS.SetSAR_MEAS2_CTRL1_SAR_SAR2_RSTB_WAIT(8)
|
||||
esp.SENS.SetSAR_MEAS2_CTRL1_SAR_SAR2_STANDBY_WAIT(100)
|
||||
esp.SENS.SetSAR_MEAS2_CTRL1_SAR_SAR2_RSTB_FORCE(3)
|
||||
|
||||
// SENS.SAR_MEAS1_MUX / SAR_MEAS1_CTRL2: route ADC1 to RTC controller and use SW to select channel/start.
|
||||
esp.SENS.SetSAR_MEAS1_MUX_SAR1_DIG_FORCE(0) // 0 = controlled by RTC/SENS, not digital/APB.
|
||||
esp.SENS.SetSAR_MEAS1_CTRL2_MEAS1_START_FORCE(1) // SW triggers conversion.
|
||||
esp.SENS.SetSAR_MEAS1_CTRL2_SAR1_EN_PAD_FORCE(1) // SW selects which ADC1 pad is enabled.
|
||||
|
||||
// APB_SARADC: shared FSM/clock config used by both ADC units and the ADC2 arbiter.
|
||||
esp.APB_SARADC.SetFSM_WAIT_SARADC_XPD_WAIT(8)
|
||||
esp.APB_SARADC.SetFSM_WAIT_SARADC_RSTB_WAIT(8)
|
||||
esp.APB_SARADC.SetFSM_WAIT_SARADC_STANDBY_WAIT(100)
|
||||
esp.APB_SARADC.SetCTRL_SARADC_XPD_SAR_FORCE(3)
|
||||
esp.APB_SARADC.SetCTRL_SARADC_SAR_CLK_GATED(1)
|
||||
esp.APB_SARADC.SetCTRL2_SARADC_SAR1_INV(0)
|
||||
esp.APB_SARADC.SetCTRL2_SARADC_SAR2_INV(0)
|
||||
esp.APB_SARADC.SetCLKM_CONF_CLK_SEL(2)
|
||||
esp.APB_SARADC.SetCLKM_CONF_CLKM_DIV_NUM(1)
|
||||
esp.APB_SARADC.SetCLKM_CONF_CLKM_DIV_B(0)
|
||||
esp.APB_SARADC.SetCLKM_CONF_CLKM_DIV_A(0)
|
||||
esp.APB_SARADC.SetCLKM_CONF_CLK_EN(1)
|
||||
esp.APB_SARADC.SetFILTER_CTRL1_FILTER_FACTOR0(0)
|
||||
esp.APB_SARADC.SetFILTER_CTRL1_FILTER_FACTOR1(0)
|
||||
|
||||
adcSelfCalibrate()
|
||||
adcDigiRefMv = getDigiRef()
|
||||
}
|
||||
|
||||
const (
|
||||
attenDefault = 3 // 11 dB, ~0..3.3 V (IDF ADC_ATTEN_DB_12)
|
||||
)
|
||||
|
||||
func setSensAtten1(ch, atten uint32) {
|
||||
// SENS.SAR_ATTEN1: 2 bits per channel
|
||||
v := esp.SENS.GetSAR_ATTEN1()
|
||||
v &^= 3 << (ch * 2)
|
||||
v |= (atten & 3) << (ch * 2)
|
||||
esp.SENS.SetSAR_ATTEN1(v)
|
||||
}
|
||||
|
||||
func setSensAtten2(ch, atten uint32) {
|
||||
// SENS.SAR_ATTEN2: 2 bits per channel
|
||||
v := esp.SENS.GetSAR_ATTEN2()
|
||||
v &^= 3 << (ch * 2)
|
||||
v |= (atten & 3) << (ch * 2)
|
||||
esp.SENS.SetSAR_ATTEN2(v)
|
||||
}
|
||||
|
||||
func (a ADC) Configure(config ADCConfig) error {
|
||||
if a.Pin < 1 || a.Pin > 20 {
|
||||
return errors.New("invalid ADC pin for ESP32-S3")
|
||||
}
|
||||
a.Pin.Configure(PinConfig{Mode: PinAnalog})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a ADC) Get() uint16 {
|
||||
if a.Pin < 1 || a.Pin > 20 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var ch uint32
|
||||
var raw uint32
|
||||
if a.Pin <= 10 {
|
||||
ch = uint32(a.Pin - 1) // GPIO1→ch0 … GPIO10→ch9
|
||||
esp.SENS.SetSAR_MEAS1_MUX_SAR1_DIG_FORCE(0)
|
||||
esp.SENS.SetSAR_MEAS1_CTRL2_MEAS1_START_FORCE(1)
|
||||
esp.SENS.SetSAR_MEAS1_CTRL2_SAR1_EN_PAD_FORCE(1)
|
||||
setSensAtten1(ch, attenDefault)
|
||||
esp.SENS.SetSAR_MEAS1_CTRL2_SAR1_EN_PAD(1 << ch)
|
||||
for esp.SENS.GetSAR_SLAVE_ADDR1_SAR_SARADC_MEAS_STATUS() != 0 {
|
||||
}
|
||||
esp.SENS.SetSAR_MEAS1_CTRL2_MEAS1_START_SAR(0)
|
||||
esp.SENS.SetSAR_MEAS1_CTRL2_MEAS1_START_SAR(1)
|
||||
for esp.SENS.GetSAR_MEAS1_CTRL2_MEAS1_DONE_SAR() == 0 {
|
||||
}
|
||||
raw = esp.SENS.GetSAR_MEAS1_CTRL2_MEAS1_DATA_SAR()
|
||||
} else {
|
||||
ch = uint32(a.Pin - 11) // GPIO11→ch0 … GPIO20→ch9
|
||||
// SENS.SAR_MEAS2_CTRL2: force SW control, select channel
|
||||
esp.SENS.SetSAR_MEAS2_CTRL2_MEAS2_START_FORCE(1)
|
||||
esp.SENS.SetSAR_MEAS2_CTRL2_SAR2_EN_PAD_FORCE(1)
|
||||
esp.SENS.SetSAR_MEAS2_CTRL2_SAR2_EN_PAD(1 << ch)
|
||||
setSensAtten2(ch, attenDefault)
|
||||
// APB_SARADC.ARB_CTRL: grant ADC2 to APB for oneshot
|
||||
esp.APB_SARADC.SetARB_CTRL_ADC_ARB_APB_FORCE(1)
|
||||
esp.APB_SARADC.SetARB_CTRL_ADC_ARB_GRANT_FORCE(1)
|
||||
// SENS.SAR_MEAS2_CTRL2.MEAS2_START_SAR: one-shot start
|
||||
esp.SENS.SetSAR_MEAS2_CTRL2_MEAS2_START_SAR(0)
|
||||
esp.SENS.SetSAR_MEAS2_CTRL2_MEAS2_START_SAR(1)
|
||||
for esp.SENS.GetSAR_MEAS2_CTRL2_MEAS2_DONE_SAR() == 0 {
|
||||
}
|
||||
raw = esp.SENS.GetSAR_MEAS2_CTRL2_MEAS2_DATA_SAR()
|
||||
esp.APB_SARADC.SetARB_CTRL_ADC_ARB_APB_FORCE(0)
|
||||
esp.APB_SARADC.SetARB_CTRL_ADC_ARB_GRANT_FORCE(0)
|
||||
}
|
||||
|
||||
return uint16(raw&0xfff) << 4
|
||||
}
|
||||
|
||||
func (a ADC) GetVoltage() (raw uint32, v float64) {
|
||||
const samples = 4
|
||||
var sum uint32
|
||||
for i := 0; i < samples; i++ {
|
||||
sum += uint32(a.Get())
|
||||
}
|
||||
raw = sum / samples
|
||||
|
||||
// Default full-scale for 11 dB is approximately 3.3 V assuming
|
||||
// Vref ≈ 1.1 V and gain ≈ 3. If eFuse provided a per-chip DIGI_REF
|
||||
// (Vref in mV) via adcCalibration, use it to adjust the
|
||||
// full-scale range instead.
|
||||
scale := 3.3
|
||||
if adcDigiRefMv != 0 {
|
||||
scale = 3.0 * float64(adcDigiRefMv) / 1000.0
|
||||
}
|
||||
|
||||
v = float64(raw) / 65520.0 * scale
|
||||
return raw, v
|
||||
}
|
||||
|
||||
// ADC hardware self-calibration for ESP32-S3.
|
||||
//
|
||||
// Mapping to ESP-IDF (adc_hal_common.c, hal/esp32s3/adc_ll.h):
|
||||
// - adc_hal_self_calibration() → ADCSelfCalibrate()
|
||||
// - adc_ll_calibration_init() → regI2C.calibrationInit (DREF=4);
|
||||
// in IDF it is not called from self_cal, we call it explicitly.
|
||||
// - adc_ll_calibration_prepare() → SarEnable + calibrationPrepare (ENCAL_GND=1)
|
||||
// - adc_ll_calibration_finish() → calibrationFinish (ENCAL_GND=0)
|
||||
// - adc_ll_set_calibration_param() → setCalibrationParam()
|
||||
// - read_cal_channel() → adcCalibration.readADC1():
|
||||
// wait for meas_status==0, start 0→1, wait done, read data
|
||||
// (similar to adc_oneshot_ll_start + get_raw_result).
|
||||
// - Loop: 10 iterations, code 0..4096, binary search on self_cal==0; drop min/max;
|
||||
// rounding (remainder%8 < 4 without +1, otherwise +1) — same as in adc_hal_common.c.
|
||||
// - raw_check_valid: for ADC1 in IDF always true — we do not check it.
|
||||
//
|
||||
// Differences:
|
||||
// - regI2C: not ROM helper but direct access to 0x6000E000 (protocol like I2C_RTC_CONFIG2).
|
||||
// - cal_setup: same SENS/atten/controller fields, but through our registers.
|
||||
// - Result is stored only in hardware for the current session (not in eFuse).
|
||||
// - eFuse V1: init_code and digi_ref are taken from eFuse — same idea as Arduino/IDF.
|
||||
|
||||
const (
|
||||
adcCalTimes = 10
|
||||
adcCalRtcMagic = uint32(0xADC1C401)
|
||||
adcCalInitMin = uint32(2000)
|
||||
adcCalInitMax = uint32(3900)
|
||||
adcDigiRefMinMv = uint32(920)
|
||||
adcDigiRefMaxMv = uint32(1150)
|
||||
)
|
||||
|
||||
// adcCalibration encapsulates the self-calibration flow for ADC1
|
||||
// and remembers per-chip calibration data (such as DIGI_REF) when it is
|
||||
// available from eFuse.
|
||||
func adcSelfCalibrate() {
|
||||
reg := newRegI2C()
|
||||
f := fuse{}
|
||||
|
||||
if vref, ok := f.adc1DigiRefAtten3(); ok {
|
||||
adcDigiRefMv = vref
|
||||
}
|
||||
|
||||
if saved, ok := restoreFromRTC(); ok {
|
||||
reg.sarEnable()
|
||||
reg.calibrationInit(0)
|
||||
adc1CalibrateHigh(reg, saved)
|
||||
return
|
||||
}
|
||||
|
||||
initCode, useEfuse := f.adc1InitCodeAtten3()
|
||||
adc1CalibrationSetup(reg)
|
||||
|
||||
if useEfuse {
|
||||
saveToRTC(initCode)
|
||||
adc1CalibrateHigh(reg, initCode)
|
||||
return
|
||||
}
|
||||
|
||||
finalCode := reg.calibrateBinarySearch(0, adcCalTimes, readADC1)
|
||||
saveToRTC(finalCode)
|
||||
adc1CalibrateHigh(reg, finalCode)
|
||||
}
|
||||
|
||||
func getDigiRef() uint32 {
|
||||
return adcDigiRefMv
|
||||
}
|
||||
|
||||
func adc1CalibrationSetup(reg regI2C) {
|
||||
reg.sarEnable()
|
||||
|
||||
esp.SENS.SetSAR_MEAS1_MUX_SAR1_DIG_FORCE(0)
|
||||
esp.SENS.SetSAR_MEAS1_CTRL2_MEAS1_START_FORCE(0)
|
||||
esp.SENS.SetSAR_MEAS2_CTRL2_MEAS2_START_FORCE(0)
|
||||
esp.SENS.SetSAR_MEAS1_CTRL2_SAR1_EN_PAD(0)
|
||||
setSensAtten1(0, attenDefault)
|
||||
esp.SENS.SetSAR_MEAS1_CTRL2_MEAS1_START_FORCE(1)
|
||||
esp.SENS.SetSAR_MEAS1_CTRL2_SAR1_EN_PAD_FORCE(1)
|
||||
|
||||
reg.calibrationInit(0)
|
||||
reg.calibrationPrepare(0)
|
||||
}
|
||||
|
||||
func adc1CalibrateHigh(reg regI2C, code uint32) {
|
||||
reg.setCalibrationParam(0, code)
|
||||
reg.calibrationFinish(0)
|
||||
adc1StartWithPadForce()
|
||||
}
|
||||
|
||||
func adc1StartWithPadForce() {
|
||||
esp.SENS.SetSAR_MEAS1_CTRL2_SAR1_EN_PAD_FORCE(1)
|
||||
esp.SENS.SetSAR_MEAS1_CTRL2_MEAS1_START_FORCE(1)
|
||||
}
|
||||
|
||||
// readADC1 performs one ADC1 conversion via RTC path (used during calibration).
|
||||
// Internal GND is connected via ENCAL_GND, so the pin input is disconnected.
|
||||
// Matches IDF: wait conversion idle (meas_status==0), then start 0→1, wait done, read data.
|
||||
func readADC1() uint32 {
|
||||
for esp.SENS.GetSAR_SLAVE_ADDR1_SAR_SARADC_MEAS_STATUS() != 0 {
|
||||
}
|
||||
esp.SENS.SetSAR_MEAS1_CTRL2_MEAS1_START_SAR(0)
|
||||
esp.SENS.SetSAR_MEAS1_CTRL2_MEAS1_START_SAR(1)
|
||||
for esp.SENS.GetSAR_MEAS1_CTRL2_MEAS1_DONE_SAR() == 0 {
|
||||
}
|
||||
return uint32(esp.SENS.GetSAR_MEAS1_CTRL2_MEAS1_DATA_SAR() & 0xfff)
|
||||
}
|
||||
|
||||
func restoreFromRTC() (uint32, bool) {
|
||||
if esp.RTC_CNTL.GetSTORE0() != adcCalRtcMagic {
|
||||
return 0, false
|
||||
}
|
||||
code := esp.RTC_CNTL.GetSTORE1()
|
||||
if code < adcCalInitMin || code > adcCalInitMax {
|
||||
return 0, false
|
||||
}
|
||||
return code, true
|
||||
}
|
||||
|
||||
func saveToRTC(code uint32) {
|
||||
esp.RTC_CNTL.SetSTORE0(adcCalRtcMagic)
|
||||
esp.RTC_CNTL.SetSTORE1(code)
|
||||
}
|
||||
|
||||
// fuse
|
||||
const (
|
||||
// Base address for eFuse controller (EFUSE_BLKx region in TRM).
|
||||
efuseBase = uintptr(0x60007000)
|
||||
|
||||
// EFUSE_*_REG offsets mirror ESP-IDF's efuse_reg.h layout.
|
||||
efuseClkReg = efuseBase + 0x1c8
|
||||
efuseConfReg = efuseBase + 0x1cc
|
||||
efuseCmdReg = efuseBase + 0x1d4
|
||||
efuseDacConfReg = efuseBase + 0x1e8
|
||||
efuseWrTimConf1Reg = efuseBase + 0x1f4
|
||||
efuseWrTimConf2Reg = efuseBase + 0x1f8
|
||||
efuseRdData4Reg = efuseBase + 0x6c // EFUSE_RD_WR_DIS_REG / RD_DATA4
|
||||
efuseRdData5Reg = efuseBase + 0x70 // EFUSE_RD_REPEAT_DATA1_REG / RD_DATA5
|
||||
efuseRdData7Reg = efuseBase + 0x78 // EFUSE_RD_REPEAT_DATA3_REG / RD_DATA7
|
||||
|
||||
// Read opcode and clock enable bit used by EFUSE HAL (see efuse_ll).
|
||||
efuseReadOpCode = uint32(0x5AA5)
|
||||
efuseClkEnBit = uint32(1 << 16)
|
||||
efuseBlkVersionV1 = 1 // EFUSE_BLK_VERSION major version = 1
|
||||
|
||||
// SYSTEM_PERIP_CLK_EN0 register and EFUSE clock gate bit.
|
||||
systemPeripClkEn0 = uintptr(0x600C0018)
|
||||
systemEfuseClkEnBit = uint32(1 << 14)
|
||||
)
|
||||
|
||||
type fuse struct{}
|
||||
|
||||
// adc1InitCodeAtten3 extracts the ADC1 INIT_CODE (offset trim) for
|
||||
// attenuation index 3 (typically 11 dB) from EFUSE_BLK2. This mirrors
|
||||
// the logic used by ESP-IDF's ADC calibration HAL for ESP32-S3.
|
||||
//
|
||||
// The code is built from four differential eFuse fields (diff0..diff3)
|
||||
// and constant offsets (1850, 90, 70) as described in Espressif's
|
||||
// internal calibration formulas.
|
||||
func (f *fuse) adc1InitCodeAtten3() (uint32, bool) {
|
||||
for try := 0; try < 2; try++ {
|
||||
f.triggerReadSequence()
|
||||
data4, data5, blkVer := f.readBlock2Data4Data5()
|
||||
if blkVer != efuseBlkVersionV1 {
|
||||
continue
|
||||
}
|
||||
diff0 := (data4 >> 21) & 0xFF
|
||||
diff1 := (data4 >> 29) | ((data5 & 7) << 3)
|
||||
diff2 := (data5 >> 3) & 0x3F
|
||||
diff3 := (data5 >> 9) & 0x3F
|
||||
icode0 := diff0 + 1850
|
||||
icode1 := diff1 + icode0 + 90
|
||||
icode2 := diff2 + icode1
|
||||
icode3 := diff3 + icode2 + 70
|
||||
if icode3 >= adcCalInitMin && icode3 <= adcCalInitMax {
|
||||
return icode3, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// adc1DigiRefAtten3 reads the digital reference (DIGI_REF) for
|
||||
// ADC1 at attenuation index 3 from EFUSE_BLK2 / RD_DATA7. This is
|
||||
// similar to what the ESP-IDF ADC calibration HAL uses when present.
|
||||
func (f *fuse) adc1DigiRefAtten3() (uint32, bool) {
|
||||
f.triggerReadSequence()
|
||||
_, _, blkVer := f.readBlock2Data4Data5()
|
||||
if blkVer != efuseBlkVersionV1 {
|
||||
return 0, false
|
||||
}
|
||||
data7 := f.readBlock2Data7()
|
||||
diff3 := (data7 >> 1) & 0xFF
|
||||
digiRef := diff3 + 900
|
||||
if digiRef < adcDigiRefMinMv || digiRef > adcDigiRefMaxMv {
|
||||
return 0, false
|
||||
}
|
||||
return digiRef, true
|
||||
}
|
||||
|
||||
// triggerReadSequence performs one eFuse read operation using the
|
||||
// controller's timing/opcode sequence. This roughly corresponds to
|
||||
// the low-level logic in the ESP-IDF eFuse HAL (see efuse_ll_* in
|
||||
// the IDF sources and the "eFuse Manager" docs:
|
||||
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/system/efuse.html).
|
||||
func (f *fuse) triggerReadSequence() {
|
||||
clk := (*volatile.Register32)(unsafe.Pointer(systemPeripClkEn0))
|
||||
clk.Set(clk.Get() | systemEfuseClkEnBit)
|
||||
efuseClk := (*volatile.Register32)(unsafe.Pointer(efuseClkReg))
|
||||
efuseClk.Set(efuseClk.Get() | efuseClkEnBit)
|
||||
dac := (*volatile.Register32)(unsafe.Pointer(efuseDacConfReg))
|
||||
dac.Set(0x28 | (0xFF << 9))
|
||||
(*volatile.Register32)(unsafe.Pointer(efuseWrTimConf1Reg)).Set(0x3000 << 8)
|
||||
(*volatile.Register32)(unsafe.Pointer(efuseWrTimConf2Reg)).Set(0x190)
|
||||
(*volatile.Register32)(unsafe.Pointer(efuseConfReg)).Set(efuseReadOpCode)
|
||||
cmd := (*volatile.Register32)(unsafe.Pointer(efuseCmdReg))
|
||||
cmd.Set(1)
|
||||
for cmd.Get()&1 != 0 {
|
||||
}
|
||||
}
|
||||
|
||||
// readBlock2Data4Data5 reads the EFUSE_BLK2 data words that contain
|
||||
// ADC calibration and version information. It returns RD_DATA4,
|
||||
// RD_DATA5 and the decoded block version (BLK_VERSION).
|
||||
//
|
||||
// Layout is derived from the ESP32-S3 TRM and IDF eFuse tables.
|
||||
func (f *fuse) readBlock2Data4Data5() (data4, data5 uint32, blkVer uint8) {
|
||||
data4 = (*volatile.Register32)(unsafe.Pointer(efuseRdData4Reg)).Get()
|
||||
data5 = (*volatile.Register32)(unsafe.Pointer(efuseRdData5Reg)).Get()
|
||||
blkVer = uint8(data4 & 3)
|
||||
return data4, data5, blkVer
|
||||
}
|
||||
|
||||
// readBlock2Data7 reads RD_DATA7 from EFUSE_BLK2, which for ADC
|
||||
// calibration contains additional reference (DIGI_REF) data fields.
|
||||
func (f *fuse) readBlock2Data7() uint32 {
|
||||
return (*volatile.Register32)(unsafe.Pointer(efuseRdData7Reg)).Get()
|
||||
}
|
||||
|
||||
// readAdcCalibBlock2 triggers an eFuse read and returns the raw
|
||||
// EFUSE_BLK2 words used for ADC calibration (RD_DATA4/5) along
|
||||
// with the decoded block version. This is a small helper similar
|
||||
// in spirit to the internal IDF helpers around EFUSE_BLK2.
|
||||
func (f *fuse) readAdcCalibBlock2() (data4, data5 uint32, blkVer uint8) {
|
||||
f.triggerReadSequence()
|
||||
return f.readBlock2Data4Data5()
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
//go:build esp32s3
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/esp"
|
||||
)
|
||||
|
||||
const (
|
||||
I2CEXT0_SCL_OUT_IDX = 89
|
||||
I2CEXT0_SDA_OUT_IDX = 90
|
||||
I2CEXT1_SCL_OUT_IDX = 91
|
||||
I2CEXT1_SDA_OUT_IDX = 92
|
||||
)
|
||||
|
||||
var (
|
||||
I2C0 = &I2C{
|
||||
Bus: esp.I2C0,
|
||||
funcSCL: I2CEXT0_SCL_OUT_IDX,
|
||||
funcSDA: I2CEXT0_SDA_OUT_IDX,
|
||||
useExt1: false,
|
||||
}
|
||||
I2C1 = &I2C{
|
||||
Bus: esp.I2C1,
|
||||
funcSCL: I2CEXT1_SCL_OUT_IDX,
|
||||
funcSDA: I2CEXT1_SDA_OUT_IDX,
|
||||
useExt1: true,
|
||||
}
|
||||
)
|
||||
|
||||
func initI2CExt1Clock() {
|
||||
esp.SYSTEM.SetPERIP_RST_EN0_I2C_EXT1_RST(1)
|
||||
esp.SYSTEM.SetPERIP_CLK_EN0_I2C_EXT1_CLK_EN(1)
|
||||
esp.SYSTEM.SetPERIP_RST_EN0_I2C_EXT1_RST(0)
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
//go:build esp32s3
|
||||
|
||||
package machine
|
||||
|
||||
import "device/esp"
|
||||
|
||||
// LEDC PWM for ESP32-S3: 4 timers (PWM0–PWM3), 8 channels per timer; each timer has its own frequency.
|
||||
// Range: frequency from a few Hz up to ~40 MHz (at 1-bit resolution); duty resolution 1–15 bits
|
||||
// (higher frequency gives lower resolution). Clock source: APB 80 MHz. Low-speed mode only.
|
||||
// Duty must not equal 2^resolution (overflow risk). See ESP-IDF LEDC driver, TRM LED PWM Controller.
|
||||
|
||||
// GPIO matrix output signal indices for LEDC (soc/gpio_sig_map.h)
|
||||
const (
|
||||
LEDC_LS_SIG_OUT0_IDX = 73
|
||||
)
|
||||
|
||||
const ledcChannelsS3 = 8
|
||||
|
||||
var (
|
||||
PWM0 = &LEDCPWM{SigOutBase: LEDC_LS_SIG_OUT0_IDX, NumChannels: ledcChannelsS3, timerNum: 0}
|
||||
PWM1 = &LEDCPWM{SigOutBase: LEDC_LS_SIG_OUT0_IDX, NumChannels: ledcChannelsS3, timerNum: 1}
|
||||
PWM2 = &LEDCPWM{SigOutBase: LEDC_LS_SIG_OUT0_IDX, NumChannels: ledcChannelsS3, timerNum: 2}
|
||||
PWM3 = &LEDCPWM{SigOutBase: LEDC_LS_SIG_OUT0_IDX, NumChannels: ledcChannelsS3, timerNum: 3}
|
||||
)
|
||||
|
||||
// chanOp implements LEDC low-speed channel ops for ESP32-S3 (channels 0–7).
|
||||
func (pwm *LEDCPWM) chanOp(ch uint8, op ledcChanOp, duty uint32, inverting bool) {
|
||||
invVal := uint32(0)
|
||||
if inverting {
|
||||
invVal = 1
|
||||
}
|
||||
switch ch {
|
||||
case 0:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH0_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH0_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH0_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH0_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH0_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH0_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH0_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH0_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH0_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH0_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 1:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH1_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH1_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH1_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH1_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH1_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH1_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH1_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH1_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH1_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH1_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 2:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH2_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH2_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH2_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH2_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH2_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH2_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH2_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH2_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH2_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH2_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 3:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH3_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH3_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH3_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH3_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH3_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH3_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH3_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH3_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH3_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH3_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 4:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH4_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH4_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH4_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH4_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH4_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH4_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH4_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH4_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH4_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH4_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 5:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH5_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH5_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH5_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH5_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH5_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH5_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH5_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH5_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH5_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH5_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 6:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH6_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH6_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH6_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH6_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH6_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH6_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH6_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH6_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH6_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH6_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH6_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH6_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH6_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH6_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH6_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH6_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH6_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH6_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 7:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH7_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH7_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH7_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH7_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH7_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH7_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH7_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH7_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH7_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH7_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH7_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH7_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH7_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH7_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH7_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH7_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH7_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH7_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
SPI_MODE0 = uint8(0)
|
||||
SPI_MODE1 = uint8(1)
|
||||
SPI_MODE2 = uint8(2)
|
||||
SPI_MODE3 = uint8(3)
|
||||
|
||||
// ESP32-S3 PLL clock frequency (same as ESP32-C3)
|
||||
pplClockFreq = 80e6
|
||||
|
||||
@@ -59,6 +64,16 @@ var (
|
||||
SPI1 = &SPI{Bus: esp.SPI3, busID: 3} // Secondary SPI (HSPI)
|
||||
)
|
||||
|
||||
type SPIConfig struct {
|
||||
Frequency uint32
|
||||
SCK Pin // Serial Clock
|
||||
SDO Pin // Serial Data Out (MOSI)
|
||||
SDI Pin // Serial Data In (MISO)
|
||||
CS Pin // Chip Select (optional)
|
||||
LSBFirst bool // MSB is default
|
||||
Mode uint8 // SPI_MODE0 is default
|
||||
}
|
||||
|
||||
// Configure and make the SPI peripheral ready to use.
|
||||
// Implementation following ESP-IDF HAL with GPIO Matrix routing
|
||||
func (spi *SPI) Configure(config SPIConfig) error {
|
||||
@@ -67,12 +82,6 @@ func (spi *SPI) Configure(config SPIConfig) error {
|
||||
config.Frequency = SPI_DEFAULT_FREQUENCY
|
||||
}
|
||||
|
||||
// Default CS to NoPin so that an unset CS field (zero value = GPIO0)
|
||||
// does not accidentally configure GPIO0 as the chip select output.
|
||||
if config.CS == 0 {
|
||||
config.CS = NoPin
|
||||
}
|
||||
|
||||
switch spi.busID {
|
||||
case 2: // SPI2 (FSPI)
|
||||
if config.SCK == 0 {
|
||||
@@ -223,14 +232,14 @@ func (spi *SPI) Configure(config SPIConfig) error {
|
||||
|
||||
// Configure SPI mode (CPOL/CPHA) following ESP-IDF HAL
|
||||
switch config.Mode {
|
||||
case Mode0:
|
||||
case SPI_MODE0:
|
||||
// CPOL=0, CPHA=0 (default)
|
||||
case Mode1:
|
||||
case SPI_MODE1:
|
||||
bus.SetUSER_CK_OUT_EDGE(1) // CPHA=1
|
||||
case Mode2:
|
||||
case SPI_MODE2:
|
||||
bus.SetMISC_CK_IDLE_EDGE(1) // CPOL=1
|
||||
bus.SetUSER_CK_OUT_EDGE(1) // CPHA=1
|
||||
case Mode3:
|
||||
case SPI_MODE3:
|
||||
bus.SetMISC_CK_IDLE_EDGE(1) // CPOL=1
|
||||
}
|
||||
|
||||
@@ -310,7 +319,36 @@ func (spi *SPI) Tx(w, r []byte) error {
|
||||
|
||||
// Fill tx buffer.
|
||||
transferWords := (*[16]volatile.Register32)(unsafe.Add(unsafe.Pointer(&bus.W0), 0))
|
||||
spiTxFillBuffer(transferWords, w)
|
||||
if len(w) >= 64 {
|
||||
// We can fill the entire 64-byte transfer buffer with data.
|
||||
// This loop is slightly faster than the loop below.
|
||||
for i := 0; i < 16; i++ {
|
||||
word := uint32(w[i*4]) | uint32(w[i*4+1])<<8 | uint32(w[i*4+2])<<16 | uint32(w[i*4+3])<<24
|
||||
transferWords[i].Set(word)
|
||||
}
|
||||
} else {
|
||||
// We can't fill the entire transfer buffer, so we need to be a bit
|
||||
// more careful.
|
||||
// Note that parts of the transfer buffer that aren't used still
|
||||
// need to be set to zero, otherwise we might be transferring
|
||||
// garbage from a previous transmission if w is smaller than r.
|
||||
for i := 0; i < 16; i++ {
|
||||
var word uint32
|
||||
if i*4+3 < len(w) {
|
||||
word |= uint32(w[i*4+3]) << 24
|
||||
}
|
||||
if i*4+2 < len(w) {
|
||||
word |= uint32(w[i*4+2]) << 16
|
||||
}
|
||||
if i*4+1 < len(w) {
|
||||
word |= uint32(w[i*4+1]) << 8
|
||||
}
|
||||
if i*4+0 < len(w) {
|
||||
word |= uint32(w[i*4+0]) << 0
|
||||
}
|
||||
transferWords[i].Set(word)
|
||||
}
|
||||
}
|
||||
|
||||
// Do the transfer.
|
||||
bus.SetMS_DLEN_MS_DATA_BITLEN(uint32(chunkSize)*8 - 1)
|
||||
@@ -350,6 +388,58 @@ func (spi *SPI) Tx(w, r []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compute the SPI bus frequency from the APB clock frequency.
|
||||
// Note: APB clock is always 80MHz on ESP32-S3, independent of CPU frequency.
|
||||
// Ported from ESP32-C3 implementation for better accuracy.
|
||||
func freqToClockDiv(hz uint32) uint32 {
|
||||
// Use APB clock frequency (80MHz), not CPU frequency!
|
||||
// SPI peripheral is connected to APB bus which stays at 80MHz
|
||||
const apbFreq = pplClockFreq // 80MHz
|
||||
|
||||
if hz >= apbFreq { // maximum frequency
|
||||
return 1 << 31
|
||||
}
|
||||
if hz < (apbFreq / (16 * 64)) { // minimum frequency
|
||||
return 15<<18 | 63<<12 | 31<<6 | 63 // pre=15, n=63
|
||||
}
|
||||
|
||||
// iterate looking for an exact match
|
||||
// or iterate all 16 prescaler options
|
||||
// looking for the smallest error
|
||||
var bestPre, bestN, bestErr uint32
|
||||
bestN = 1
|
||||
bestErr = 0xffffffff
|
||||
q := uint32(float32(apbFreq)/float32(hz) + float32(0.5))
|
||||
for p := uint32(0); p < 16; p++ {
|
||||
n := q/(p+1) - 1
|
||||
if n < 1 { // prescaler became too large, stop enum
|
||||
break
|
||||
}
|
||||
if n > 63 { // prescaler too small, skip to next
|
||||
continue
|
||||
}
|
||||
|
||||
freq := apbFreq / ((p + 1) * (n + 1))
|
||||
if freq == hz { // exact match
|
||||
return p<<18 | n<<12 | (n/2)<<6 | n
|
||||
}
|
||||
|
||||
var err uint32
|
||||
if freq < hz {
|
||||
err = hz - freq
|
||||
} else {
|
||||
err = freq - hz
|
||||
}
|
||||
if err < bestErr {
|
||||
bestErr = err
|
||||
bestPre = p
|
||||
bestN = n
|
||||
}
|
||||
}
|
||||
|
||||
return bestPre<<18 | bestN<<12 | (bestN/2)<<6 | bestN
|
||||
}
|
||||
|
||||
// isDefaultSPIPins checks if the given pins match the default SPI pin configuration
|
||||
// that supports IO MUX direct connection for better performance
|
||||
func isDefaultSPIPins(busID uint8, config SPIConfig) bool {
|
||||
|
||||
@@ -1,235 +0,0 @@
|
||||
//go:build esp32s3 || (esp32c3 && !m5stamp_c3)
|
||||
|
||||
// Shared regI2C-based ADC calibration helpers for ESP32-S3 and ESP32-C3.
|
||||
//
|
||||
// The internal I2C bus ("regI2C") and SAR ADC trim register layout are
|
||||
// identical across both chips; chip-specific differences (host ID, DREF
|
||||
// init value, calibration iterations) are captured in the regI2C struct
|
||||
// fields, keeping each target file free of duplicated low-level code.
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/esp"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// regI2C wraps the internal I2C bus used for SAR ADC calibration registers.
|
||||
// Fields hold chip-specific parameters that differ between ESP32-S3 and ESP32-C3.
|
||||
type regI2C struct {
|
||||
// hostID is the I2C_SAR_ADC_HOSTID (1 for ESP32-S3, 0 for ESP32-C3).
|
||||
hostID uint8
|
||||
// drefInit is the DREF reference value written during calibrationInit
|
||||
// (4 for ESP32-S3, 1 for ESP32-C3).
|
||||
drefInit uint8
|
||||
}
|
||||
|
||||
// SAR ADC I2C register layout constants shared across ESP32-S3 and ESP32-C3.
|
||||
// Source: ESP-IDF soc/regi2c_saradc.h
|
||||
const (
|
||||
// i2cSarADC is the I2C_SAR_ADC block address on the internal bus.
|
||||
i2cSarADC = uint8(0x69)
|
||||
|
||||
// DREF (reference) bitfields for ADC1 and ADC2.
|
||||
adc1DrefAddr = uint8(0x2)
|
||||
adc1DrefMSB = uint8(6)
|
||||
adc1DrefLSB = uint8(4)
|
||||
adc2DrefAddr = uint8(0x5)
|
||||
adc2DrefMSB = uint8(6)
|
||||
adc2DrefLSB = uint8(4)
|
||||
|
||||
// ENCAL_GND: routes internal ground to ADC input during self-calibration.
|
||||
adc1EncalGndAddr = uint8(0x7)
|
||||
adc1EncalGndMSB = uint8(5)
|
||||
adc1EncalGndLSB = uint8(5)
|
||||
adc2EncalGndAddr = uint8(0x7)
|
||||
adc2EncalGndMSB = uint8(7)
|
||||
adc2EncalGndLSB = uint8(7)
|
||||
|
||||
// INIT_CODE (offset) high/low for ADC1 and ADC2.
|
||||
adc1InitCodeHighAddr = uint8(0x1)
|
||||
adc1InitCodeHighMSB = uint8(3)
|
||||
adc1InitCodeHighLSB = uint8(0)
|
||||
adc1InitCodeLowAddr = uint8(0x0)
|
||||
adc1InitCodeLowMSB = uint8(7)
|
||||
adc1InitCodeLowLSB = uint8(0)
|
||||
adc2InitCodeHighAddr = uint8(0x4)
|
||||
adc2InitCodeHighMSB = uint8(3)
|
||||
adc2InitCodeHighLSB = uint8(0)
|
||||
adc2InitCodeLowAddr = uint8(0x3)
|
||||
adc2InitCodeLowMSB = uint8(7)
|
||||
adc2InitCodeLowLSB = uint8(0)
|
||||
|
||||
// ANA_CONFIG / ANA_CONFIG2: enable analog SAR I2C domain.
|
||||
anaConfigReg = uintptr(0x6000E044)
|
||||
i2cSarEnMask = uint32(1 << 18)
|
||||
anaConfig2Reg = uintptr(0x6000E048)
|
||||
anaSarCfg2En = uint32(1 << 16)
|
||||
|
||||
// REGI2C master control register and helper masks.
|
||||
i2cMstCtrlReg = uintptr(0x6000E000)
|
||||
i2cMstBusyBit = uint32(1 << 25)
|
||||
i2cMstWrCntlBit = uint32(1 << 24)
|
||||
i2cMstDataMask = uint32(0xFF << 16)
|
||||
i2cMstDataShift = 16
|
||||
i2cMstBusyTimeout = 10000
|
||||
|
||||
// adcCalOffsetRange is the binary search upper bound (12-bit full scale).
|
||||
adcCalOffsetRange = uint32(4096)
|
||||
|
||||
// adcCalMaxIterations is the maximum number of calibration iterations
|
||||
// supported by calibrateBinarySearch. Must be >= max(S3=10, C3=15).
|
||||
adcCalMaxIterations = 16
|
||||
)
|
||||
|
||||
// waitIdle polls the REGI2C master BUSY bit until it clears or a
|
||||
// timeout expires, matching the busy-wait helper in ESP-IDF's regi2c_ctrl.c.
|
||||
func (r regI2C) waitIdle(reg *volatile.Register32) bool {
|
||||
for i := 0; i < i2cMstBusyTimeout; i++ {
|
||||
if reg.Get()&i2cMstBusyBit == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// writeMask is a software implementation of the IDF REGI2C_WRITE_MASK macro.
|
||||
// It reads the current byte at regAddr on the SAR ADC I2C block, updates
|
||||
// only the [msb:lsb] bitfield, and writes it back via the internal I2C master.
|
||||
func (r regI2C) writeMask(regAddr, msb, lsb, data uint8) {
|
||||
reg := (*volatile.Register32)(unsafe.Pointer(i2cMstCtrlReg))
|
||||
if !r.waitIdle(reg) {
|
||||
return
|
||||
}
|
||||
reg.Set(uint32(i2cSarADC) | uint32(regAddr)<<8)
|
||||
if !r.waitIdle(reg) {
|
||||
return
|
||||
}
|
||||
cur := (reg.Get() & i2cMstDataMask) >> i2cMstDataShift
|
||||
mask := uint32(1<<(msb-lsb+1)-1) << lsb
|
||||
cur &^= mask
|
||||
cur |= uint32(data&(1<<(msb-lsb+1)-1)) << lsb
|
||||
reg.Set(uint32(i2cSarADC) | uint32(regAddr)<<8 | i2cMstWrCntlBit | (cur<<i2cMstDataShift)&i2cMstDataMask)
|
||||
r.waitIdle(reg)
|
||||
}
|
||||
|
||||
// sarEnable enables the analog SAR I2C domain before any regI2C access,
|
||||
// matching the prologue in adc_ll_calibration_prepare().
|
||||
func (r regI2C) sarEnable() {
|
||||
cfg := (*volatile.Register32)(unsafe.Pointer(anaConfigReg))
|
||||
cfg2 := (*volatile.Register32)(unsafe.Pointer(anaConfig2Reg))
|
||||
esp.RTC_CNTL.SetANA_CONF_SAR_I2C_PU(1)
|
||||
cfg.Set(cfg.Get() &^ i2cSarEnMask)
|
||||
cfg2.Set(cfg2.Get() | anaSarCfg2En)
|
||||
}
|
||||
|
||||
// calibrationInit sets the DREF reference for the selected ADC unit to
|
||||
// the chip-specific init value before running self-calibration.
|
||||
// Corresponds to adc_ll_calibration_init() in ESP-IDF.
|
||||
func (r regI2C) calibrationInit(adcN uint8) {
|
||||
if adcN == 0 {
|
||||
r.writeMask(adc1DrefAddr, adc1DrefMSB, adc1DrefLSB, r.drefInit)
|
||||
} else {
|
||||
r.writeMask(adc2DrefAddr, adc2DrefMSB, adc2DrefLSB, r.drefInit)
|
||||
}
|
||||
}
|
||||
|
||||
// calibrationPrepare enables ENCAL_GND so that the ADC input is
|
||||
// internally shorted to ground during self-calibration.
|
||||
// Corresponds to the ENCAL_GND part of adc_ll_calibration_prepare().
|
||||
func (r regI2C) calibrationPrepare(adcN uint8) {
|
||||
if adcN == 0 {
|
||||
r.writeMask(adc1EncalGndAddr, adc1EncalGndMSB, adc1EncalGndLSB, 1)
|
||||
} else {
|
||||
r.writeMask(adc2EncalGndAddr, adc2EncalGndMSB, adc2EncalGndLSB, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// calibrationFinish clears ENCAL_GND to reconnect the ADC input to the
|
||||
// external pad after self-calibration.
|
||||
// Corresponds to adc_ll_calibration_finish() in ESP-IDF.
|
||||
func (r regI2C) calibrationFinish(adcN uint8) {
|
||||
if adcN == 0 {
|
||||
r.writeMask(adc1EncalGndAddr, adc1EncalGndMSB, adc1EncalGndLSB, 0)
|
||||
} else {
|
||||
r.writeMask(adc2EncalGndAddr, adc2EncalGndMSB, adc2EncalGndLSB, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// setCalibrationParam writes the INIT_CODE (offset trim) for the selected
|
||||
// ADC unit via the regI2C bitfields.
|
||||
// Corresponds to adc_ll_set_calibration_param() in ESP-IDF.
|
||||
func (r regI2C) setCalibrationParam(adcN uint8, param uint32) {
|
||||
msb := uint8(param >> 8)
|
||||
lsb := uint8(param & 0xFF)
|
||||
if adcN == 0 {
|
||||
r.writeMask(adc1InitCodeHighAddr, adc1InitCodeHighMSB, adc1InitCodeHighLSB, msb)
|
||||
r.writeMask(adc1InitCodeLowAddr, adc1InitCodeLowMSB, adc1InitCodeLowLSB, lsb)
|
||||
} else {
|
||||
r.writeMask(adc2InitCodeHighAddr, adc2InitCodeHighMSB, adc2InitCodeHighLSB, msb)
|
||||
r.writeMask(adc2InitCodeLowAddr, adc2InitCodeLowMSB, adc2InitCodeLowLSB, lsb)
|
||||
}
|
||||
}
|
||||
|
||||
// calibrateBinarySearch runs the ADC self-calibration binary search loop.
|
||||
// It performs 'iterations' rounds of binary search to find the optimal offset
|
||||
// code, drops the min/max outliers, and returns the rounded mean of the
|
||||
// remaining values. This matches adc_hal_self_calibration() in ESP-IDF.
|
||||
//
|
||||
// The readADC callback must perform a single conversion using the target's
|
||||
// oneshot path (SENS or APB_SARADC) and return the raw 12-bit result.
|
||||
// During calibration, ENCAL_GND is active so the ADC reads its internal ground.
|
||||
func (r regI2C) calibrateBinarySearch(adcN uint8, iterations int, readADC func() uint32) uint32 {
|
||||
if iterations > adcCalMaxIterations {
|
||||
iterations = adcCalMaxIterations
|
||||
}
|
||||
var codeList [adcCalMaxIterations]uint32
|
||||
var codeSum uint32
|
||||
|
||||
for rpt := 0; rpt < iterations; rpt++ {
|
||||
codeH := adcCalOffsetRange
|
||||
codeL := uint32(0)
|
||||
chkCode := (codeH + codeL) / 2
|
||||
r.setCalibrationParam(adcN, chkCode)
|
||||
selfCal := readADC()
|
||||
|
||||
for codeH-codeL > 1 {
|
||||
if selfCal == 0 {
|
||||
codeH = chkCode
|
||||
} else {
|
||||
codeL = chkCode
|
||||
}
|
||||
chkCode = (codeH + codeL) / 2
|
||||
r.setCalibrationParam(adcN, chkCode)
|
||||
selfCal = readADC()
|
||||
if codeH-codeL == 1 {
|
||||
chkCode++
|
||||
r.setCalibrationParam(adcN, chkCode)
|
||||
selfCal = readADC()
|
||||
}
|
||||
}
|
||||
codeList[rpt] = chkCode
|
||||
codeSum += chkCode
|
||||
}
|
||||
|
||||
// Drop min and max outliers, then average with IDF-style rounding.
|
||||
codeMin := codeList[0]
|
||||
codeMax := codeList[0]
|
||||
for i := 0; i < iterations; i++ {
|
||||
if codeList[i] < codeMin {
|
||||
codeMin = codeList[i]
|
||||
}
|
||||
if codeList[i] > codeMax {
|
||||
codeMax = codeList[i]
|
||||
}
|
||||
}
|
||||
remaining := codeSum - codeMax - codeMin
|
||||
divisor := uint32(iterations - 2)
|
||||
finalCode := remaining / divisor
|
||||
if remaining%divisor >= 4 {
|
||||
finalCode++
|
||||
}
|
||||
|
||||
return finalCode
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user