Compare commits

..

30 Commits

Author SHA1 Message Date
Patricio Whittingslow 6e18433a46 clean up implementation 2026-03-29 16:34:10 -03:00
Patricio Whittingslow c5cb58c9b8 take @knieriem suggestions and apply them to can.go 2026-03-01 20:52:00 -03:00
Patricio Whittingslow ef05ba2ea1 delete old can 2026-02-21 09:20:47 -03:00
Patricio Whittingslow 25f5f76a48 add interrupts 2026-02-21 09:19:32 -03:00
Patricio Whittingslow a5292ecb0f full CAN API refactor 2026-02-20 14:19:32 -03:00
Patricio Whittingslow ab087d6f27 new CAN API demo 2026-02-20 13:20:03 -03:00
Micah Cowell fd1d10c9b7 export UART0 and pins 2026-02-19 07:17:51 +01:00
Nia Waldvogel 5c37d1ba61 runtime: implement fminimum/fmaximum
The compiler may generate calls to fminimum/fmaximum on some platforms.
Neither of the libm implementations we statically link against have these functions yet.
Implement them ourselves.
2026-02-18 15:13:01 -05:00
Dima Jolkin 610dd19c40 esp32s3-usbserial: move InitSerial to init method 2026-02-17 20:42:23 +01:00
Dima Jolkin 44ca224056 esp32s3-usbserial: common usbserial for both esp32c3 & esp32s3 2026-02-17 20:42:23 +01:00
Dima Jolkin c1cddffbe9 esp32s3-usbserial: split usb 2026-02-17 20:42:23 +01:00
Dima Jolkin 9bbad6700b esp32s3-usbserial: added usbserial printing 2026-02-17 20:42:23 +01:00
deadprogram 24f965425d make: remove machine without board from smoketest
Signed-off-by: deadprogram <ron@hybridgroup.com>
2026-02-16 20:51:03 +01:00
deadprogram b6b723aeec targets: correct name/tag use for esp32s3-wroom1 board
Signed-off-by: deadprogram <ron@hybridgroup.com>
2026-02-16 20:51:03 +01:00
deadprogram 934d5f41bf fix: init heap before random number seed on wasm platforms
This changes the order for initialization of the random number
seed generation on wasm platforms until after the heap has been
initialized. Should fix #5198

Signed-off-by: deadprogram <ron@hybridgroup.com>
2026-02-16 08:38:17 +01:00
Dima 4b0e858964 Esp32s3 implement spi (#5169)
* esp32s3 spi

* stabilization freq cpu

* cheange clacl freq for spi

* fix linters

* esp32s3-spi: change default pins for esp32s3 xiao

* set default configuration

* esp32s3-spi: extends smoketests for esp32s3
2026-02-15 14:50:39 +01:00
deadprogram 32378537b8 sponsorship: add explicit callout/link in README to help out TinyGo
Signed-off-by: deadprogram <ron@hybridgroup.com>
2026-02-12 12:40:27 +01:00
Elias Naur f5b2a08b15 builder: order embedded files deterministically 2026-02-11 19:27:57 +01:00
Elias Naur f23e18a8de flake.*: bump to nixpkgs 25.11
Bump the GitHub Actions Nix install as well; nixpkgs 25.11 requires a
newer nix command.
2026-02-10 17:24:35 +01:00
deadprogram 66d7099c96 build: update CI builds to use latest Go 1.25.7 release
Signed-off-by: deadprogram <ron@hybridgroup.com>
2026-02-04 18:56:52 +01:00
Matthew Hiles bef0dc5d21 Add per-byte timeout budget for rp2 I2C (#5189)
* Add per-byte timeout budget for rp2 I2C

* run goimports
2026-02-02 14:09:34 +01:00
Yaj e79cdc1122 targets: Add Shrike Lite board (#5170)
* feat: Add Vicharak Shrike Lite

* Add shrike-lite to smoketest
2026-01-24 08:04:22 +01:00
robo f0256cab18 Fix syntax for building with TinyGo 2026-01-23 18:01:52 +00:00
Jesús Espino 5d8e071bfb machine/attiny85: add USI-based SPI support (#5181)
* machine/attiny85: add USI-based SPI support

Implement SPI communication for ATTiny85 using the USI (Universal Serial
Interface) hardware in three-wire mode. The ATTiny85 lacks dedicated SPI
hardware but can emulate SPI using the USI module with software clock
strobing.

Implementation details:
- Configure USI in three-wire mode for SPI operation
- Use clock strobing technique to shift data in/out
- Pin mapping: PB2 (SCK), PB1 (MOSI/DO), PB0 (MISO/DI)
- Support both Transfer() and Tx() methods

The implementation uses the USI control register (USICR) to toggle the
clock pin, which triggers automatic bit shifting in hardware. This is
more efficient than pure software bit-banging.

Current limitations:
- Frequency configuration not yet implemented (runs at max software speed)
- Only SPI Mode 0 (CPOL=0, CPHA=0) supported
- Only MSB-first bit order supported

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

Co-authored-by: Ona <no-reply@ona.com>

* machine/attiny85: add SPI frequency configuration support

Add software-based frequency control for USI SPI. The ATtiny85 USI lacks
hardware prescalers, so frequency is controlled via delay loops between
clock toggles.

- Calculate delay cycles based on requested frequency and CPU clock
- Fast path (no delay) when frequency is 0 or max speed requested
- Delay loop uses nop instructions for timing control

Co-authored-by: Ona <no-reply@ona.com>

* machine/attiny85: add SPI mode configuration support

Add support for all 4 SPI modes (Mode 0-3) using USI hardware:
- Mode 0 (CPOL=0, CPHA=0): Clock idle low, sample on rising edge
- Mode 1 (CPOL=0, CPHA=1): Clock idle low, sample on falling edge
- Mode 2 (CPOL=1, CPHA=0): Clock idle high, sample on falling edge
- Mode 3 (CPOL=1, CPHA=1): Clock idle high, sample on rising edge

CPOL is controlled by setting the clock pin idle state.
CPHA is controlled via the USICS0 bit in USICR.

Co-authored-by: Ona <no-reply@ona.com>

* machine/attiny85: add LSB-first bit order support

Add software-based LSB-first support for USI SPI. The USI hardware only
supports MSB-first, so bit reversal is done in software before sending
and after receiving.

Uses an efficient parallel bit swap algorithm (3 operations) to reverse
the byte.

Co-authored-by: Ona <no-reply@ona.com>

* GNUmakefile: add mcp3008 SPI example to digispark smoketest

Test the USI-based SPI implementation for ATtiny85/digispark.

Co-authored-by: Ona <no-reply@ona.com>

* machine/attiny85: minimize SPI RAM footprint

Reduce SPI struct from ~14 bytes to 1 byte to fit in ATtiny85's limited
512 bytes of RAM.

Changes:
- Remove register pointers (use avr.USIDR/USISR/USICR directly)
- Remove pin fields (USI pins are fixed: PB0/PB1/PB2)
- Remove CS pin management (user must handle CS)
- Remove frequency control (runs at max speed)
- Remove LSBFirst support

The SPI struct now only stores the USICR configuration byte.

Co-authored-by: Ona <no-reply@ona.com>

* Revert "machine/attiny85: minimize SPI RAM footprint"

This reverts commit 387ccad494.

Co-authored-by: Ona <no-reply@ona.com>

* machine/attiny85: reduce SPI RAM usage by 10 bytes

Remove unnecessary fields from SPI struct while keeping all functionality:
- Remove register pointers (use avr.USIDR/USISR/USICR directly)
- Remove pin fields (USI pins are fixed: PB0/PB1/PB2)
- Remove CS pin (user must manage it, standard practice)

Kept functional fields:
- delayCycles for frequency control
- usicrValue for SPI mode support
- lsbFirst for bit order support

SPI struct reduced from 14 bytes to 4 bytes.

Co-authored-by: Ona <no-reply@ona.com>

---------

Co-authored-by: Ona <no-reply@ona.com>
2026-01-17 21:22:15 +01:00
Damian Gryski a0069b6282 testdata: more corpus entries (#5182)
* testdata: more corpus entries

* testdata: remove skipwasi for dchest/siphash build issues
2026-01-17 17:59:13 +01:00
deadprogram 707d37a4c1 chore: update version to 0.41.0-dev
Signed-off-by: deadprogram <ron@hybridgroup.com>
2026-01-14 13:09:19 +00:00
Nia Waldvogel 1876b65b18 compiler: simplify createObjectLayout
This simplifies the process of constructing and encoding layout bitmaps.
Instead of creating big integers and merging them, we can create a pre-sized bitmap and set positions within it.

This also changes the encoding logic to allow larger layouts to be encoded inline.
We would previously not encode a layout inline unless the size was less than the width of the data field.
This is overly conservative.
A layout can be encoded inline as long as:
1. The size fits within the size field.
2. All set bits in the bitmap fit into the data field.
2026-01-14 08:52:09 +00:00
deadprogram 1fe934e8a8 machine/rp: add Close function to UART to allow for removing all system resources/power usage
Signed-off-by: deadprogram <ron@hybridgroup.com>
2026-01-13 22:58:37 +00:00
deadprogram 8bd2233b57 machine/rp: use the blockReset() and unresetBlockWait() helper functions for all peripheral reset/unreset operations
Signed-off-by: deadprogram <ron@hybridgroup.com>
2026-01-13 08:55:10 +00:00
Jesús Espino ff58c50118 machine: add attiny85 pwm support (#5171)
* machine/attiny85: add PWM support for Timer0 and Timer1

Add complete PWM implementation for ATtiny85, supporting both Timer0
and Timer1 with their respective output channels:
- Timer0: 8-bit timer for pins PB0 (OC0A) and PB1 (OC0B)
- Timer1: 8-bit high-speed timer for pins PB1 (OC1A) and PB4 (OC1B)

Timer1 provides more flexible period control with configurable top value
(OCR1C) and extended prescaler options (1-16384), making it well-suited
for LED PWM control and other applications requiring variable frequencies.

Implements full PWM interface including Configure, SetPeriod, Channel,
Set, SetInverting, Top, Counter, and Period methods.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* machine/digispark: document PWM support on pins

Add documentation to the Digispark board file indicating which pins
support PWM output:
- P0 (PB0): Timer0 channel A
- P1 (PB1): Timer0 channel B or Timer1 channel A
- P4 (PB4): Timer1 channel B

Includes package comment explaining Timer0 vs Timer1 capabilities,
with Timer1 recommended for more flexible frequency control.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* machine/attiny85: optimize PWM prescaler lookups

Replace verbose switch statements with more efficient implementations:

- SetPeriod: Use bit shift (top >>= prescaler-1) instead of 15-case
  switch for dividing uint64 by power-of-2 prescaler values

- Period: Replace switch statements with compact uint16 lookup tables
  for both Timer0 and Timer1, casting to uint64 only when needed

This addresses review feedback about inefficient switch-based lookups.
On AVR, this approach is significantly smaller:
- Bit shifts for uint64 division: ~34 bytes vs ~140 bytes
- uint16 tables: 22 bytes code + 32/16 bytes data vs ~140 bytes
- Total savings: ~190 bytes (68% reduction)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* examples/pwm: add digispark support and smoketest

Add digispark.go configuration for PWM example using Timer1 with pins P1 (LED) and P4. Also add digispark PWM example to GNUmakefile smoketests.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

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

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