Compare commits

..

3 Commits

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

Some files were not shown because too many files have changed in this diff Show More