mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-03 10:37:46 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ec9e041a40 | |||
| bf613175e7 | |||
| 4d62cca267 | |||
| a63aa143bb | |||
| ed0fb774b9 | |||
| 9be956f4bc | |||
| 48d741f68a | |||
| 35e6b8a67d | |||
| 8a31ae14a0 | |||
| 26ee3fb93e | |||
| 8872d5ebfc | |||
| faf455283d | |||
| 562b4c5020 | |||
| 7460734522 | |||
| 3be7100e89 | |||
| aedaf7d925 | |||
| 256e84f912 | |||
| 0a87846bd8 | |||
| 073862ee92 | |||
| c820d83ae2 |
@@ -108,7 +108,7 @@ jobs:
|
||||
- image: golang:1.25-bullseye
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "21"
|
||||
llvm: "20"
|
||||
resource_class: large
|
||||
|
||||
workflows:
|
||||
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.25.1'
|
||||
go-version: '1.25.4'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v4
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
- name: Build TinyGo release tarball
|
||||
run: make release -j3
|
||||
- name: Test stdlib packages
|
||||
run: make tinygo-test
|
||||
run: make tinygo-test TEST_ADDITIONAL_FLAGS="-scheduler=tasks"
|
||||
- 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
|
||||
@@ -117,7 +117,7 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
version: [16, 17, 18, 19, 20, 21]
|
||||
version: [16, 17, 18, 19, 20]
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
@@ -134,15 +134,15 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.25.1'
|
||||
go-version: '1.25.4'
|
||||
cache: true
|
||||
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
||||
run: go install -tags=llvm${{ matrix.version }}
|
||||
- name: Check binary
|
||||
run: tinygo version
|
||||
- name: Build TinyGo (default LLVM)
|
||||
if: matrix.version == 21
|
||||
if: matrix.version == 20
|
||||
run: go install
|
||||
- name: Check binary
|
||||
if: matrix.version == 21
|
||||
if: matrix.version == 20
|
||||
run: tinygo version
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
sudo rm -rf /usr/local/share/boost
|
||||
df -h
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up Docker Buildx
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
|
||||
+28
-23
@@ -16,7 +16,15 @@ jobs:
|
||||
# Build Linux binaries, ready for release.
|
||||
# This runs inside an Alpine Linux container so we can more easily create a
|
||||
# statically linked binary.
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
goarch: [ amd64, arm64 ]
|
||||
include:
|
||||
- goarch: amd64
|
||||
os: ubuntu-latest
|
||||
- goarch: arm64
|
||||
os: ubuntu-24.04-arm # TODO: use a -latest variant?
|
||||
runs-on: ${{ matrix.os }}
|
||||
container:
|
||||
image: golang:1.25-alpine
|
||||
outputs:
|
||||
@@ -31,7 +39,7 @@ jobs:
|
||||
# We're not on a multi-user machine, so this is safe.
|
||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
- name: Extract TinyGo version
|
||||
@@ -40,7 +48,7 @@ jobs:
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }}
|
||||
key: go-cache-linux-alpine-${{ matrix.goarch }}-v1-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
@@ -73,7 +81,7 @@ jobs:
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-20-linux-alpine-v1
|
||||
key: llvm-build-19-linux-alpine-${{ matrix.goarch }}-v1
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -97,7 +105,7 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-alpine-v1
|
||||
key: binaryen-linux-alpine-${{ matrix.goarch }}-v1
|
||||
path: build/wasm-opt
|
||||
- name: Build Binaryen
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
@@ -116,28 +124,28 @@ jobs:
|
||||
- name: Build TinyGo release
|
||||
run: |
|
||||
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
|
||||
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.goarch }}.deb
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-amd64-double-zipped-${{ steps.version.outputs.version }}
|
||||
name: linux-${{ matrix.goarch }}-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
|
||||
/tmp/tinygo${{ steps.version.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
||||
/tmp/tinygo_${{ steps.version.outputs.version }}-${{ matrix.goarch }}.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@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.25.0'
|
||||
go-version: '1.25.4'
|
||||
cache: true
|
||||
- name: Install wasmtime
|
||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||
@@ -164,7 +172,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install apt dependencies
|
||||
@@ -179,9 +187,9 @@ jobs:
|
||||
simavr \
|
||||
ninja-build
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.25.0'
|
||||
go-version: '1.25.4'
|
||||
cache: true
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
@@ -272,11 +280,8 @@ jobs:
|
||||
# part of the release tarball.
|
||||
strategy:
|
||||
matrix:
|
||||
goarch: [ arm, arm64 ]
|
||||
goarch: [ arm ]
|
||||
include:
|
||||
- goarch: arm64
|
||||
toolchain: aarch64-linux-gnu
|
||||
libc: arm64
|
||||
- goarch: arm
|
||||
toolchain: arm-linux-gnueabihf
|
||||
libc: armhf
|
||||
@@ -284,7 +289,7 @@ jobs:
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Get TinyGo version
|
||||
id: version
|
||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||
@@ -296,9 +301,9 @@ jobs:
|
||||
g++-${{ matrix.toolchain }} \
|
||||
libc6-dev-${{ matrix.libc }}-cross
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.25.0'
|
||||
go-version: '1.25.4'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v4
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up Docker Buildx
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
target: tinygo-llvm-build
|
||||
context: .
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
# See: https://github.com/tinygo-org/tinygo/pull/4516#issuecomment-2416363668
|
||||
run: sudo apt-get remove llvm-18
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Pull musl, bdwgc
|
||||
run: |
|
||||
git submodule update --init lib/musl lib/bdwgc
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
# still works after checking out the dev branch (that is, when going from LLVM
|
||||
# 16 to LLVM 17 for example, both Clang 16 and Clang 17 are installed).
|
||||
|
||||
echo 'deb https://apt.llvm.org/noble/ llvm-toolchain-noble-21 main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
||||
echo 'deb https://apt.llvm.org/noble/ llvm-toolchain-noble-20 main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends -y \
|
||||
llvm-21-dev \
|
||||
clang-21 \
|
||||
libclang-21-dev \
|
||||
lld-21
|
||||
llvm-20-dev \
|
||||
clang-20 \
|
||||
libclang-20-dev \
|
||||
lld-20
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
run: |
|
||||
echo "$HOME/go/bin" >> $GITHUB_PATH
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0 # fetch all history (no sparse checkout)
|
||||
submodules: true
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
run: |
|
||||
scoop install ninja binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
- name: Extract TinyGo version
|
||||
@@ -39,9 +39,9 @@ jobs:
|
||||
shell: bash
|
||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.25.0'
|
||||
go-version: '1.25.4'
|
||||
cache: true
|
||||
- name: Restore cached LLVM source
|
||||
uses: actions/cache/restore@v4
|
||||
@@ -143,11 +143,11 @@ jobs:
|
||||
run: |
|
||||
scoop install binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.25.0'
|
||||
go-version: '1.25.4'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -173,11 +173,11 @@ jobs:
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.25.0'
|
||||
go-version: '1.25.4'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -209,11 +209,11 @@ jobs:
|
||||
run: |
|
||||
scoop install binaryen && scoop install wasmtime@29.0.1
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.25.0'
|
||||
go-version: '1.25.4'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
+4
-1
@@ -481,6 +481,7 @@ TEST_IOFS := false
|
||||
endif
|
||||
|
||||
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic'
|
||||
TEST_ADDITIONAL_FLAGS ?=
|
||||
|
||||
# Test known-working standard library packages.
|
||||
# TODO: parallelize, and only show failing tests (no implied -v flag).
|
||||
@@ -488,7 +489,7 @@ TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generi
|
||||
tinygo-test:
|
||||
@# TestExtraMethods: used by many crypto packages and uses reflect.Type.Method which is not implemented.
|
||||
@# TestParseAndBytesRoundTrip/P256/Generic: relies on t.Skip() which is not implemented
|
||||
$(TINYGO) test $(TEST_SKIP_FLAG) $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
||||
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
||||
@# io/fs requires os.ReadDir, not yet supported on windows or wasi. It also
|
||||
@# requires a large stack-size. Hence, io/fs is only run conditionally.
|
||||
@# For more details, see the comments on issue #3143.
|
||||
@@ -621,6 +622,8 @@ smoketest: testchdir
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/device-id
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico2-ice examples/blinky1
|
||||
@$(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 examples/blinky1
|
||||
|
||||
+6
-3
@@ -30,7 +30,10 @@ var BoehmGC = Library{
|
||||
// Use a minimal environment.
|
||||
"-DNO_MSGBOX_ON_ERROR", // don't call MessageBoxA on Windows
|
||||
"-DDONT_USE_ATEXIT",
|
||||
"-DNO_GETENV",
|
||||
"-DNO_GETENV", // smaller binary, more predictable configuration
|
||||
"-DNO_CLOCK", // don't use system clock
|
||||
"-DNO_DEBUGGING", // reduce code size
|
||||
"-DGC_NO_FINALIZATION", // finalization is not used at the moment
|
||||
|
||||
// Special flag to work around the lack of __data_start in ld.lld.
|
||||
// TODO: try to fix this in LLVM/lld directly so we don't have to
|
||||
@@ -39,6 +42,8 @@ var BoehmGC = Library{
|
||||
|
||||
// Do not scan the stack. We have our own mechanism to do this.
|
||||
"-DSTACK_NOT_SCANNED",
|
||||
"-DNO_PROC_STAT", // we scan the stack manually (don't read /proc/self/stat on Linux)
|
||||
"-DSTACKBOTTOM=0", // dummy value, we scan the stack manually
|
||||
|
||||
// Assertions can be enabled while debugging GC issues.
|
||||
//"-DGC_ASSERTIONS",
|
||||
@@ -63,7 +68,6 @@ var BoehmGC = Library{
|
||||
"blacklst.c",
|
||||
"dbg_mlc.c",
|
||||
"dyn_load.c",
|
||||
"finalize.c",
|
||||
"headers.c",
|
||||
"mach_dep.c",
|
||||
"malloc.c",
|
||||
@@ -71,7 +75,6 @@ var BoehmGC = Library{
|
||||
"mark_rts.c",
|
||||
"misc.c",
|
||||
"new_hblk.c",
|
||||
"obj_map.c",
|
||||
"os_dep.c",
|
||||
"reclaim.c",
|
||||
}
|
||||
|
||||
+7
-4
@@ -33,10 +33,13 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if gorootMajor != 1 || gorootMinor < minorMin || gorootMinor > minorMax {
|
||||
// Note: when this gets updated, also update the Go compatibility matrix:
|
||||
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
|
||||
return nil, fmt.Errorf("requires go version 1.%d through 1.%d, got go%d.%d", minorMin, minorMax, gorootMajor, gorootMinor)
|
||||
|
||||
if options.GoCompatibility {
|
||||
if gorootMajor != 1 || gorootMinor < minorMin || gorootMinor > minorMax {
|
||||
// Note: when this gets updated, also update the Go compatibility matrix:
|
||||
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
|
||||
return nil, fmt.Errorf("requires go version 1.%d through 1.%d, got go%d.%d", minorMin, minorMax, gorootMajor, gorootMinor)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the Go toolchain version isn't too new, if we haven't been
|
||||
|
||||
@@ -43,8 +43,8 @@ func TestBinarySize(t *testing.T) {
|
||||
tests := []sizeTest{
|
||||
// microcontrollers
|
||||
{"hifive1b", "examples/echo", 3884, 280, 0, 2268},
|
||||
{"microbit", "examples/serial", 2924, 388, 8, 2272},
|
||||
{"wioterminal", "examples/pininterrupt", 7365, 1491, 116, 6912},
|
||||
{"microbit", "examples/serial", 2844, 360, 8, 2272},
|
||||
{"wioterminal", "examples/pininterrupt", 7349, 1491, 116, 6912},
|
||||
|
||||
// TODO: also check wasm. Right now this is difficult, because
|
||||
// wasm binaries are run through wasm-opt and therefore the
|
||||
|
||||
@@ -21,10 +21,6 @@ import (
|
||||
)
|
||||
|
||||
/*
|
||||
// Hide a warning in LLVM 21 that doesn't apply to us (appears to be a side
|
||||
// effect of how CGo processes C header files).
|
||||
#cgo CFLAGS: -Wno-deprecated-declarations
|
||||
|
||||
#include <clang-c/Index.h> // If this fails, libclang headers aren't available. Please take a look here: https://tinygo.org/docs/guides/build/
|
||||
#include <llvm/Config/llvm-config.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !byollvm && llvm20
|
||||
//go:build !byollvm && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19
|
||||
|
||||
package cgo
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
//go:build !byollvm && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm20
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-21 -I/usr/include/llvm-c-21 -I/usr/lib/llvm-21/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@21/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@21/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm21/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-21/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@21/lib -lclang
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@21/lib -lclang
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm21/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -226,7 +226,7 @@ func (c *Config) StackSize() uint64 {
|
||||
|
||||
// MaxStackAlloc returns the size of the maximum allocation to put on the stack vs heap.
|
||||
func (c *Config) MaxStackAlloc() uint64 {
|
||||
if c.StackSize() > 32*1024 {
|
||||
if c.StackSize() >= 16*1024 {
|
||||
return 1024
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ type Options struct {
|
||||
WITPackage string // pass through to wasm-tools component embed invocation
|
||||
WITWorld string // pass through to wasm-tools component embed -w option
|
||||
ExtLDFlags []string
|
||||
GoCompatibility bool // enable to check for Go version compatibility
|
||||
}
|
||||
|
||||
// Verify performs a validation on the given options, raising an error if options are not valid.
|
||||
|
||||
@@ -32,6 +32,9 @@ const (
|
||||
// Whether this is a full or partial Go parameter (int, slice, etc).
|
||||
// The extra context parameter is not a Go parameter.
|
||||
paramIsGoParam = 1 << iota
|
||||
|
||||
// Whether this is a readonly parameter (for example, a string pointer).
|
||||
paramIsReadonly
|
||||
)
|
||||
|
||||
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
||||
@@ -167,6 +170,7 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
|
||||
continue
|
||||
}
|
||||
suffix := strconv.Itoa(i)
|
||||
isString := false
|
||||
if goType != nil {
|
||||
// Try to come up with a good suffix for this struct field,
|
||||
// depending on which Go type it's based on.
|
||||
@@ -183,12 +187,16 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
|
||||
suffix = []string{"r", "i"}[i]
|
||||
case types.String:
|
||||
suffix = []string{"data", "len"}[i]
|
||||
isString = true
|
||||
}
|
||||
case *types.Signature:
|
||||
suffix = []string{"context", "funcptr"}[i]
|
||||
}
|
||||
}
|
||||
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
||||
if isString {
|
||||
subInfos[0].flags |= paramIsReadonly
|
||||
}
|
||||
paramInfos = append(paramInfos, subInfos...)
|
||||
}
|
||||
return paramInfos
|
||||
|
||||
@@ -84,7 +84,6 @@ type compilerContext struct {
|
||||
funcPtrType llvm.Type // pointer in function address space (1 for AVR, 0 elsewhere)
|
||||
funcPtrAddrSpace int
|
||||
uintptrType llvm.Type
|
||||
nocaptureAttr llvm.Attribute
|
||||
program *ssa.Program
|
||||
diagnostics []error
|
||||
functionInfos map[*ssa.Function]functionInfo
|
||||
@@ -136,13 +135,6 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
|
||||
c.funcPtrType = dummyFunc.Type()
|
||||
dummyFunc.EraseFromParentAsFunction()
|
||||
|
||||
// The attribute "nocapture" changed to "captures(none)" in LLVM 21.
|
||||
if llvmutil.Version() < 21 {
|
||||
c.nocaptureAttr = c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)
|
||||
} else {
|
||||
c.nocaptureAttr = c.ctx.CreateEnumAttribute(llvm.AttributeKindID("captures"), 0)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
|
||||
@@ -38,10 +38,10 @@ func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
|
||||
// provided immediately. For example:
|
||||
//
|
||||
// arm.AsmFull(
|
||||
// "str {value}, {result}",
|
||||
// "str {value}, [{result}]",
|
||||
// map[string]interface{}{
|
||||
// "value": 1
|
||||
// "result": &dest,
|
||||
// "value": 1,
|
||||
// "result": uintptr(unsafe.Pointer(&dest)),
|
||||
// })
|
||||
func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) {
|
||||
asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
|
||||
|
||||
@@ -250,6 +250,9 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
|
||||
func hashmapIsBinaryKey(keyType types.Type) bool {
|
||||
switch keyType := keyType.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
// TODO: unsafe.Pointer is also a binary key, but to support that we
|
||||
// need to fix an issue with interp first (see
|
||||
// https://github.com/tinygo-org/tinygo/pull/4898).
|
||||
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0
|
||||
case *types.Pointer:
|
||||
return true
|
||||
|
||||
+17
-9
@@ -139,7 +139,13 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
||||
// not.
|
||||
// (It may be safe to add the nocapture parameter to the context
|
||||
// parameter, but I'd like to stay on the safe side here).
|
||||
llvmFn.AddAttributeAtIndex(i+1, c.nocaptureAttr)
|
||||
nocapture := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)
|
||||
llvmFn.AddAttributeAtIndex(i+1, nocapture)
|
||||
}
|
||||
if paramInfo.flags¶mIsReadonly != 0 && paramInfo.llvmType.TypeKind() == llvm.PointerTypeKind {
|
||||
// Readonly pointer parameters (like strings) benefit from being marked as readonly.
|
||||
readonly := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)
|
||||
llvmFn.AddAttributeAtIndex(i+1, readonly)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +158,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
||||
// Mark it as noreturn so LLVM can optimize away code.
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noreturn"), 0))
|
||||
case "internal/abi.NoEscape":
|
||||
llvmFn.AddAttributeAtIndex(1, c.nocaptureAttr)
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
case "runtime.alloc":
|
||||
// Tell the optimizer that runtime.alloc is an allocator, meaning that it
|
||||
// returns values that are never null and never alias to an existing value.
|
||||
@@ -174,25 +180,25 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
||||
case "runtime.sliceAppend":
|
||||
// Appending a slice will only read the to-be-appended slice, it won't
|
||||
// be modified.
|
||||
llvmFn.AddAttributeAtIndex(2, c.nocaptureAttr)
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||
case "runtime.sliceCopy":
|
||||
// Copying a slice won't capture any of the parameters.
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("writeonly"), 0))
|
||||
llvmFn.AddAttributeAtIndex(1, c.nocaptureAttr)
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||
llvmFn.AddAttributeAtIndex(2, c.nocaptureAttr)
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
case "runtime.stringFromBytes":
|
||||
llvmFn.AddAttributeAtIndex(1, c.nocaptureAttr)
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||
case "runtime.stringFromRunes":
|
||||
llvmFn.AddAttributeAtIndex(1, c.nocaptureAttr)
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||
case "runtime.trackPointer":
|
||||
// This function is necessary for tracking pointers on the stack in a
|
||||
// portable way (see gc_stack_portable.go). Indicate to the optimizer
|
||||
// that the only thing we'll do is read the pointer.
|
||||
llvmFn.AddAttributeAtIndex(1, c.nocaptureAttr)
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||
case "__mulsi3", "__divmodsi4", "__udivmodsi4":
|
||||
if strings.Split(c.Triple, "-")[0] == "avr" {
|
||||
@@ -227,9 +233,11 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
||||
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", info.wasmName))
|
||||
}
|
||||
nocaptureKind := llvm.AttributeKindID("nocapture")
|
||||
nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0)
|
||||
for i, typ := range paramTypes {
|
||||
if typ.TypeKind() == llvm.PointerTypeKind {
|
||||
llvmFn.AddAttributeAtIndex(i+1, c.nocaptureAttr)
|
||||
llvmFn.AddAttributeAtIndex(i+1, nocapture)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,14 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
return b.CreateCall(fnType, target, args, ""), nil
|
||||
|
||||
case b.GOARCH == "arm" && b.GOOS == "linux":
|
||||
if arch := b.archFamily(); arch != "arm" {
|
||||
// Some targets pretend to be linux/arm for compatibility but aren't
|
||||
// actually such a system. Make sure we emit an error instead of
|
||||
// creating inline assembly that will fail to compile.
|
||||
// See: https://github.com/tinygo-org/tinygo/issues/4959
|
||||
return llvm.Value{}, b.makeError(call.Pos(), "system calls are not supported: target emulates a linux/arm system on "+arch)
|
||||
}
|
||||
|
||||
// Implement the EABI system call convention for Linux.
|
||||
// Source: syscall(2) man page.
|
||||
args := []llvm.Value{}
|
||||
|
||||
Vendored
+1
-1
@@ -50,7 +50,7 @@ unsafe.String.throw: ; preds = %entry
|
||||
declare void @runtime.unsafeSlicePanic(ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.unsafeStringData(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
|
||||
define hidden ptr @main.unsafeStringData(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
|
||||
|
||||
Vendored
+3
-3
@@ -77,7 +77,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._string @main.minString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
||||
define hidden %runtime._string @main.minString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||
@@ -91,7 +91,7 @@ entry:
|
||||
ret %runtime._string %5
|
||||
}
|
||||
|
||||
declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1
|
||||
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||
@@ -116,7 +116,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._string @main.maxString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
||||
define hidden %runtime._string @main.maxString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||
|
||||
Vendored
+8
-8
@@ -31,13 +31,13 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.stringLen(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
|
||||
define hidden i32 @main.stringLen(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret i32 %s.len
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.stringIndex(ptr %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #2 {
|
||||
define hidden i8 @main.stringIndex(ptr readonly %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%.not = icmp ult i32 %index, %s.len
|
||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||
@@ -55,16 +55,16 @@ lookup.throw: ; preds = %entry
|
||||
declare void @runtime.lookupPanic(ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.stringCompareEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
||||
define hidden i1 @main.stringCompareEqual(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
declare i1 @runtime.stringEqual(ptr, i32, ptr, i32, ptr) #1
|
||||
declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.stringCompareUnequal(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
||||
define hidden i1 @main.stringCompareUnequal(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
|
||||
%1 = xor i1 %0, true
|
||||
@@ -72,16 +72,16 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.stringCompareLarger(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
||||
define hidden i1 @main.stringCompareLarger(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #3
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1
|
||||
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.stringLookup(ptr %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #2 {
|
||||
define hidden i8 @main.stringLookup(ptr readonly %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = zext i8 %x to i32
|
||||
%.not = icmp ugt i32 %s.len, %0
|
||||
|
||||
@@ -18,7 +18,7 @@ require (
|
||||
golang.org/x/sys v0.30.0
|
||||
golang.org/x/tools v0.30.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
tinygo.org/x/go-llvm v0.0.0-20250916101410-63740cfada08
|
||||
tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -58,5 +58,5 @@ 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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
tinygo.org/x/go-llvm v0.0.0-20250916101410-63740cfada08 h1:vvEVYF4Qb38C25U8Ae/1QUWlCSp4pIE0PR+/BwNPvBU=
|
||||
tinygo.org/x/go-llvm v0.0.0-20250916101410-63740cfada08/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||
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=
|
||||
|
||||
@@ -577,7 +577,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
// runtime instead of at compile time. But we need to
|
||||
// revert any changes made by the call first.
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"!! revert because of error:", callErr.Err)
|
||||
fmt.Fprintln(os.Stderr, indent+"!! revert because of error:", callErr.Error())
|
||||
}
|
||||
callMem.revert()
|
||||
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||
|
||||
+1
-1
Submodule lib/bdwgc updated: 1166f11f7d...7577ca7c2d
@@ -1635,6 +1635,7 @@ func main() {
|
||||
cpuprofile := flag.String("cpuprofile", "", "cpuprofile output")
|
||||
monitor := flag.Bool("monitor", false, "enable serial monitor")
|
||||
baudrate := flag.Int("baudrate", 115200, "baudrate of serial monitor")
|
||||
gocompatibility := flag.Bool("go-compatibility", true, "enable to check for Go versions compatibility, you can also configure this by setting the TINYGO_GOCOMPATIBILITY environment variable")
|
||||
|
||||
// Internal flags, that are only intended for TinyGo development.
|
||||
printIR := flag.Bool("internal-printir", false, "print LLVM IR")
|
||||
@@ -1712,6 +1713,16 @@ func main() {
|
||||
ocdCommands = strings.Split(*ocdCommandsString, ",")
|
||||
}
|
||||
|
||||
val, ok := os.LookupEnv("TINYGO_GOCOMPATIBILITY")
|
||||
if ok {
|
||||
b, err := strconv.ParseBool(val)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "could not parse TINYGO_GOCOMPATIBILITY value %q: %v\n", val, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
*gocompatibility = b
|
||||
}
|
||||
|
||||
options := &compileopts.Options{
|
||||
GOOS: goenv.Get("GOOS"),
|
||||
GOARCH: goenv.Get("GOARCH"),
|
||||
@@ -1748,6 +1759,7 @@ func main() {
|
||||
Timeout: *timeout,
|
||||
WITPackage: witPackage,
|
||||
WITWorld: witWorld,
|
||||
GoCompatibility: *gocompatibility,
|
||||
}
|
||||
if *printCommands {
|
||||
options.PrintCommands = printCommand
|
||||
|
||||
@@ -48,10 +48,10 @@ func Asm(asm string)
|
||||
// recognizes template values in the form {name}, like so:
|
||||
//
|
||||
// arm.AsmFull(
|
||||
// "str {value}, {result}",
|
||||
// "str {value}, [{result}]",
|
||||
// map[string]interface{}{
|
||||
// "value": 1
|
||||
// "result": &dest,
|
||||
// "value": 1,
|
||||
// "result": uintptr(unsafe.Pointer(&dest)),
|
||||
// })
|
||||
//
|
||||
// You can use {} in the asm string (which expands to a register) to set the
|
||||
|
||||
@@ -1,29 +1,6 @@
|
||||
.syntax unified
|
||||
.cfi_sections .debug_frame
|
||||
|
||||
.section .text.HardFault_Handler
|
||||
.global HardFault_Handler
|
||||
.type HardFault_Handler, %function
|
||||
HardFault_Handler:
|
||||
.cfi_startproc
|
||||
// Put the old stack pointer in the first argument, for easy debugging. This
|
||||
// is especially useful on Cortex-M0, which supports far fewer debug
|
||||
// facilities.
|
||||
mov r0, sp
|
||||
|
||||
// Load the default stack pointer from address 0 so that we can call normal
|
||||
// functions again that expect a working stack. However, it will corrupt the
|
||||
// old stack so the function below must not attempt to recover from this
|
||||
// fault.
|
||||
movs r3, #0
|
||||
ldr r3, [r3]
|
||||
mov sp, r3
|
||||
|
||||
// Continue handling this error in Go.
|
||||
bl handleHardFault
|
||||
.cfi_endproc
|
||||
.size HardFault_Handler, .-HardFault_Handler
|
||||
|
||||
// This is a convenience function for semihosting support.
|
||||
// At some point, this should be replaced by inline assembly.
|
||||
.section .text.SemihostingCall
|
||||
|
||||
@@ -10,10 +10,10 @@ func Asm(asm string)
|
||||
// recognizes template values in the form {name}, like so:
|
||||
//
|
||||
// arm.AsmFull(
|
||||
// "str {value}, {result}",
|
||||
// "str {value}, [{result}]",
|
||||
// map[string]interface{}{
|
||||
// "value": 1
|
||||
// "result": &dest,
|
||||
// "value": 1,
|
||||
// "result": uintptr(unsafe.Pointer(&dest)),
|
||||
// })
|
||||
//
|
||||
// You can use {} in the asm string (which expands to a register) to set the
|
||||
|
||||
+3
-3
@@ -10,10 +10,10 @@ func Asm(asm string)
|
||||
// recognizes template values in the form {name}, like so:
|
||||
//
|
||||
// arm.AsmFull(
|
||||
// "str {value}, {result}",
|
||||
// "str {value}, [{result}]",
|
||||
// map[string]interface{}{
|
||||
// "value": 1
|
||||
// "result": &dest,
|
||||
// "value": 1,
|
||||
// "result": uintptr(unsafe.Pointer(&dest)),
|
||||
// })
|
||||
//
|
||||
// You can use {} in the asm string (which expands to a register) to set the
|
||||
|
||||
@@ -10,10 +10,10 @@ func Asm(asm string)
|
||||
// recognizes template values in the form {name}, like so:
|
||||
//
|
||||
// arm.AsmFull(
|
||||
// "st {value}, {result}",
|
||||
// "st {value}, [{result}]",
|
||||
// map[string]interface{}{
|
||||
// "value": 1
|
||||
// "result": &dest,
|
||||
// "value": 1,
|
||||
// "result": uintptr(unsafe.Pointer(&dest)),
|
||||
// })
|
||||
//
|
||||
// You can use {} in the asm string (which expands to a register) to set the
|
||||
|
||||
@@ -15,8 +15,8 @@ const (
|
||||
P09 Pin = 9
|
||||
P10 Pin = 10
|
||||
P11 Pin = 11
|
||||
P12 Pin = 12
|
||||
P13 Pin = 13
|
||||
P12 Pin = 12 // peripherals: I2C0 SDA
|
||||
P13 Pin = 13 // peripherals: I2C0 SCL
|
||||
P14 Pin = 14
|
||||
P15 Pin = 15
|
||||
P16 Pin = 16
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
//go:build pico2_ice
|
||||
|
||||
// Most of the info is from
|
||||
// https://pico2-ice.tinyvision.ai/md_pinout.html although
|
||||
// (2025-09-07) RP4 appears twice in that pinout - the schematic is
|
||||
// more clear. Consistent with other RPi boards, we use GPn instead of
|
||||
// RPn to reference the RPi connected pins.
|
||||
|
||||
package machine
|
||||
|
||||
// GPIO pins
|
||||
const (
|
||||
GP0 = GPIO0
|
||||
GP1 = GPIO1
|
||||
GP2 = GPIO2
|
||||
GP3 = GPIO3
|
||||
GP4 = GPIO4
|
||||
GP5 = GPIO5
|
||||
GP6 = GPIO6
|
||||
GP7 = GPIO7
|
||||
GP8 = GPIO8
|
||||
GP9 = GPIO9
|
||||
GP10 = GPIO10
|
||||
GP11 = GPIO11
|
||||
GP12 = GPIO12
|
||||
GP13 = GPIO13
|
||||
GP14 = GPIO14
|
||||
GP15 = GPIO15
|
||||
GP16 = GPIO16
|
||||
GP17 = GPIO17
|
||||
GP18 = GPIO18
|
||||
GP19 = GPIO19
|
||||
GP20 = GPIO20
|
||||
GP21 = GPIO21
|
||||
GP22 = GPIO22
|
||||
GP23 = GPIO23
|
||||
GP24 = GPIO24
|
||||
GP25 = GPIO25
|
||||
GP26 = GPIO26
|
||||
GP27 = GPIO27
|
||||
GP28 = GPIO28
|
||||
GP29 = GPIO29
|
||||
GP30 = GPIO30
|
||||
GP31 = GPIO31
|
||||
GP32 = GPIO32
|
||||
GP33 = GPIO33
|
||||
GP34 = GPIO34
|
||||
GP35 = GPIO35
|
||||
GP36 = GPIO36
|
||||
GP37 = GPIO37
|
||||
GP38 = GPIO38
|
||||
GP39 = GPIO39
|
||||
GP40 = GPIO40
|
||||
GP41 = GPIO41
|
||||
GP42 = GPIO42
|
||||
GP43 = GPIO43
|
||||
GP44 = GPIO44
|
||||
GP45 = GPIO45
|
||||
GP46 = GPIO46
|
||||
GP47 = GPIO47
|
||||
|
||||
// RPi pins shared with ICE. The ICE number is what appears on
|
||||
// the board silkscreen.
|
||||
ICE9 = GP28
|
||||
ICE11 = GP29
|
||||
ICE14 = GP7
|
||||
ICE15 = GP6
|
||||
ICE16 = GP5
|
||||
ICE17 = GP4
|
||||
ICE18 = GP27
|
||||
ICE19 = GP23
|
||||
ICE20 = GP22
|
||||
ICE21 = GP26
|
||||
ICE23 = GP25
|
||||
ICE25 = GP30
|
||||
ICE26 = GP24
|
||||
ICE27 = GP20
|
||||
|
||||
// FPGA Clock pin.
|
||||
ICE35_G0 = GP21
|
||||
|
||||
// Silkscreen & Pinout names
|
||||
ICE_SSN = ICE16
|
||||
ICE_SO = ICE14
|
||||
ICE_SI = ICE17
|
||||
ICE_CK = ICE15
|
||||
SD = GP2
|
||||
SC = GP3
|
||||
|
||||
FPGA_RSTN = GP31
|
||||
A3 = GP32
|
||||
A1 = GP33
|
||||
A4 = GP34
|
||||
A2 = GP35
|
||||
B3 = GP36
|
||||
B1 = GP37
|
||||
B4 = GP38
|
||||
B2 = GP39
|
||||
N0 = GP40 // On the board these are labeled "~0"
|
||||
N1 = GP41
|
||||
N2 = GP42
|
||||
N3 = GP43
|
||||
N4 = GP44
|
||||
N5 = GP45
|
||||
N6 = GP46
|
||||
|
||||
// Functions from Schematic.
|
||||
ICE_DONE = GP40
|
||||
USB_BOOT = GP42
|
||||
|
||||
// Button
|
||||
SW1 = GP42
|
||||
BOOTSEL = GP42
|
||||
|
||||
// Tricolor LEDs
|
||||
LED_RED = GP1
|
||||
LED_GREEN = GP0
|
||||
LED_BLUE = GP9
|
||||
|
||||
// Onboard LED
|
||||
LED = LED_GREEN
|
||||
|
||||
// Onboard crystal oscillator frequency, in MHz.
|
||||
xoscFreq = 12 // MHz
|
||||
)
|
||||
|
||||
// This board does not define default i2c pins.
|
||||
const (
|
||||
I2C0_SDA_PIN = NoPin
|
||||
I2C0_SCL_PIN = NoPin
|
||||
I2C1_SDA_PIN = NoPin
|
||||
I2C1_SCL_PIN = NoPin
|
||||
)
|
||||
|
||||
// SPI default pins
|
||||
const (
|
||||
// Default Serial Clock Bus 0 for SPI communications
|
||||
SPI0_SCK_PIN = GPIO18
|
||||
// Default Serial Out Bus 0 for SPI communications
|
||||
SPI0_SDO_PIN = GPIO19 // Tx
|
||||
// Default Serial In Bus 0 for SPI communications
|
||||
SPI0_SDI_PIN = GPIO16 // Rx
|
||||
|
||||
// Default Serial Clock Bus 1 for SPI communications
|
||||
SPI1_SCK_PIN = GPIO10
|
||||
// Default Serial Out Bus 1 for SPI communications
|
||||
SPI1_SDO_PIN = GPIO11 // Tx
|
||||
// Default Serial In Bus 1 for SPI communications
|
||||
SPI1_SDI_PIN = GPIO12 // Rx
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART0_TX_PIN = GPIO0
|
||||
UART0_RX_PIN = GPIO1
|
||||
UART1_TX_PIN = GPIO8
|
||||
UART1_RX_PIN = GPIO9
|
||||
UART_TX_PIN = UART0_TX_PIN
|
||||
UART_RX_PIN = UART0_RX_PIN
|
||||
)
|
||||
|
||||
var DefaultUART = UART0
|
||||
|
||||
// USB identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Pico2"
|
||||
usb_STRING_MANUFACTURER = "Raspberry Pi"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x2E8A
|
||||
usb_PID uint16 = 0x000A
|
||||
)
|
||||
@@ -69,3 +69,5 @@ const eraseBlockSizeValue = 4096
|
||||
func eraseBlockSize() int64 {
|
||||
return eraseBlockSizeValue
|
||||
}
|
||||
|
||||
const spiMaxBufferSize = 255 // from the datasheet: TXD.MAXCNT and RXD.MAXCNT
|
||||
|
||||
@@ -90,3 +90,5 @@ const eraseBlockSizeValue = 4096
|
||||
func eraseBlockSize() int64 {
|
||||
return eraseBlockSizeValue
|
||||
}
|
||||
|
||||
const spiMaxBufferSize = 0xffff // from the datasheet: TXD.MAXCNT and RXD.MAXCNT
|
||||
|
||||
@@ -108,3 +108,5 @@ const eraseBlockSizeValue = 4096
|
||||
func eraseBlockSize() int64 {
|
||||
return eraseBlockSizeValue
|
||||
}
|
||||
|
||||
const spiMaxBufferSize = 0xffff // from the datasheet: TXD.MAXCNT and RXD.MAXCNT
|
||||
|
||||
@@ -301,18 +301,16 @@ func (spi *SPI) Transfer(w byte) (byte, error) {
|
||||
// padded until they fit: if len(w) > len(r) the extra bytes received will be
|
||||
// dropped and if len(w) < len(r) extra 0 bytes will be sent.
|
||||
func (spi *SPI) Tx(w, r []byte) error {
|
||||
// Unfortunately the hardware (on the nrf52832) only supports up to 255
|
||||
// bytes in the buffers, so if either w or r is longer than that the
|
||||
// transfer needs to be broken up in pieces.
|
||||
// The nrf52840 supports far larger buffers however, which isn't yet
|
||||
// supported.
|
||||
// Unfortunately the hardware (on the nrf52832) only supports a limited
|
||||
// amount of bytes in the buffers (depending on the chip), so if either w or
|
||||
// r is longer than that the transfer needs to be broken up in pieces.
|
||||
for len(r) != 0 || len(w) != 0 {
|
||||
// Prepare the SPI transfer: set the DMA pointers and lengths.
|
||||
// read buffer
|
||||
nr := uint32(len(r))
|
||||
if nr > 0 {
|
||||
if nr > 255 {
|
||||
nr = 255
|
||||
if nr > spiMaxBufferSize {
|
||||
nr = spiMaxBufferSize
|
||||
}
|
||||
spi.Bus.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&r[0]))))
|
||||
r = r[nr:]
|
||||
@@ -322,8 +320,8 @@ func (spi *SPI) Tx(w, r []byte) error {
|
||||
// write buffer
|
||||
nw := uint32(len(w))
|
||||
if nw > 0 {
|
||||
if nw > 255 {
|
||||
nw = 255
|
||||
if nw > spiMaxBufferSize {
|
||||
nw = spiMaxBufferSize
|
||||
}
|
||||
spi.Bus.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&w[0]))))
|
||||
w = w[nw:]
|
||||
|
||||
@@ -124,6 +124,31 @@ const (
|
||||
fnXIP pinFunc = 0
|
||||
)
|
||||
|
||||
// validPins confirms that the SPI pin selection is a legitimate one
|
||||
// for the the 2040 chip.
|
||||
func (spi *SPI) validPins(config SPIConfig) error {
|
||||
var okSDI, okSDO, okSCK bool
|
||||
switch spi.Bus {
|
||||
case rp.SPI0:
|
||||
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16 || config.SDI == 20
|
||||
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19 || config.SDO == 23
|
||||
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18 || config.SCK == 22
|
||||
case rp.SPI1:
|
||||
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 24 || config.SDI == 28
|
||||
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27
|
||||
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26
|
||||
}
|
||||
switch {
|
||||
case !okSDI:
|
||||
return errSPIInvalidSDI
|
||||
case !okSDO:
|
||||
return errSPIInvalidSDO
|
||||
case !okSCK:
|
||||
return errSPIInvalidSCK
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Configure configures the gpio pin as per mode.
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
if p == NoPin {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/rp"
|
||||
|
||||
// Analog pins on RP2350a.
|
||||
const (
|
||||
ADC0 Pin = GPIO26
|
||||
@@ -12,3 +14,28 @@ const (
|
||||
// fifth ADC channel.
|
||||
thermADC = 30
|
||||
)
|
||||
|
||||
// validPins confirms that the SPI pin selection is a legitimate one
|
||||
// for the the 2350a chip.
|
||||
func (spi *SPI) validPins(config SPIConfig) error {
|
||||
var okSDI, okSDO, okSCK bool
|
||||
switch spi.Bus {
|
||||
case rp.SPI0:
|
||||
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16 || config.SDI == 20
|
||||
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19 || config.SDO == 23
|
||||
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18 || config.SCK == 22
|
||||
case rp.SPI1:
|
||||
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 24 || config.SDI == 28
|
||||
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27
|
||||
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26
|
||||
}
|
||||
switch {
|
||||
case !okSDI:
|
||||
return errSPIInvalidSDI
|
||||
case !okSDO:
|
||||
return errSPIInvalidSDO
|
||||
case !okSCK:
|
||||
return errSPIInvalidSCK
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/rp"
|
||||
|
||||
// RP2350B has additional pins.
|
||||
|
||||
const (
|
||||
@@ -46,3 +48,28 @@ var (
|
||||
PWM10 = getPWMGroup(10)
|
||||
PWM11 = getPWMGroup(11)
|
||||
)
|
||||
|
||||
// validPins confirms that the SPI pin selection is a legitimate one
|
||||
// for the the 2350b chip.
|
||||
func (spi *SPI) validPins(config SPIConfig) error {
|
||||
var okSDI, okSDO, okSCK bool
|
||||
switch spi.Bus {
|
||||
case rp.SPI0:
|
||||
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16 || config.SDI == 20 || config.SDI == 32 || config.SDI == 36
|
||||
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19 || config.SDO == 23 || config.SDO == 35 || config.SDO == 39
|
||||
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18 || config.SCK == 22 || config.SCK == 34 || config.SCK == 38
|
||||
case rp.SPI1:
|
||||
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 24 || config.SDI == 28 || config.SDI == 40 || config.SDI == 44
|
||||
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27 || config.SDO == 31 || config.SDO == 43 || config.SDO == 47
|
||||
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26 || config.SCK == 30 || config.SCK == 42 || config.SCK == 46
|
||||
}
|
||||
switch {
|
||||
case !okSDI:
|
||||
return errSPIInvalidSDI
|
||||
case !okSDO:
|
||||
return errSPIInvalidSDO
|
||||
case !okSCK:
|
||||
return errSPIInvalidSCK
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ func (spi *SPI) SetBaudRate(br uint32) error {
|
||||
var prescale, postdiv uint32
|
||||
freq := CPUFrequency()
|
||||
for prescale = 2; prescale < 255; prescale += 2 {
|
||||
if freq < (prescale+2)*256*br {
|
||||
if uint64(freq) < uint64((prescale+2)*256)*uint64(br) {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -165,27 +165,9 @@ func (spi *SPI) Configure(config SPIConfig) error {
|
||||
config.SDI = SPI1_SDI_PIN
|
||||
}
|
||||
}
|
||||
var okSDI, okSDO, okSCK bool
|
||||
switch spi.Bus {
|
||||
case rp.SPI0:
|
||||
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16 || config.SDI == 20
|
||||
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19 || config.SDO == 23
|
||||
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18 || config.SCK == 22
|
||||
case rp.SPI1:
|
||||
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 24 || config.SDI == 28
|
||||
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27
|
||||
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26
|
||||
if err := spi.validPins(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case !okSDI:
|
||||
return errSPIInvalidSDI
|
||||
case !okSDO:
|
||||
return errSPIInvalidSDO
|
||||
case !okSCK:
|
||||
return errSPIInvalidSCK
|
||||
}
|
||||
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = defaultBaud
|
||||
}
|
||||
|
||||
@@ -8,16 +8,21 @@ import "unsafe"
|
||||
// code linked from other languages can allocate memory without colliding with
|
||||
// our GC allocations.
|
||||
|
||||
var allocs = make(map[uintptr][]byte)
|
||||
// Map of allocations, where the key is the allocated pointer and the value is
|
||||
// the size of the allocation.
|
||||
// TODO: make this a map[unsafe.Pointer]uintptr, since that results in slightly
|
||||
// smaller binaries. But for that to work, unsafe.Pointer needs to be seen as a
|
||||
// binary key (which it is not at the moment).
|
||||
// See https://github.com/tinygo-org/tinygo/pull/4898 for details.
|
||||
var allocs = make(map[*byte]uintptr)
|
||||
|
||||
//export malloc
|
||||
func libc_malloc(size uintptr) unsafe.Pointer {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
buf := make([]byte, size)
|
||||
ptr := unsafe.Pointer(&buf[0])
|
||||
allocs[uintptr(ptr)] = buf
|
||||
ptr := alloc(size, nil)
|
||||
allocs[(*byte)(ptr)] = size
|
||||
return ptr
|
||||
}
|
||||
|
||||
@@ -26,8 +31,8 @@ func libc_free(ptr unsafe.Pointer) {
|
||||
if ptr == nil {
|
||||
return
|
||||
}
|
||||
if _, ok := allocs[uintptr(ptr)]; ok {
|
||||
delete(allocs, uintptr(ptr))
|
||||
if _, ok := allocs[(*byte)(ptr)]; ok {
|
||||
delete(allocs, (*byte)(ptr))
|
||||
} else {
|
||||
panic("free: invalid pointer")
|
||||
}
|
||||
@@ -48,18 +53,20 @@ func libc_realloc(oldPtr unsafe.Pointer, size uintptr) unsafe.Pointer {
|
||||
|
||||
// It's hard to optimize this to expand the current buffer with our GC, but
|
||||
// it is theoretically possible. For now, just always allocate fresh.
|
||||
buf := make([]byte, size)
|
||||
// TODO: we could skip this if the new allocation is smaller than the old.
|
||||
ptr := alloc(size, nil)
|
||||
|
||||
if oldPtr != nil {
|
||||
if oldBuf, ok := allocs[uintptr(oldPtr)]; ok {
|
||||
copy(buf, oldBuf)
|
||||
delete(allocs, uintptr(oldPtr))
|
||||
if oldSize, ok := allocs[(*byte)(oldPtr)]; ok {
|
||||
oldBuf := unsafe.Slice((*byte)(oldPtr), oldSize)
|
||||
newBuf := unsafe.Slice((*byte)(ptr), size)
|
||||
copy(newBuf, oldBuf)
|
||||
delete(allocs, (*byte)(oldPtr))
|
||||
} else {
|
||||
panic("realloc: invalid pointer")
|
||||
}
|
||||
}
|
||||
|
||||
ptr := unsafe.Pointer(&buf[0])
|
||||
allocs[uintptr(ptr)] = buf
|
||||
allocs[(*byte)(ptr)] = size
|
||||
return ptr
|
||||
}
|
||||
|
||||
@@ -93,6 +93,9 @@ func runtimePanicAt(addr unsafe.Pointer, msg string) {
|
||||
trap()
|
||||
}
|
||||
if hasReturnAddr {
|
||||
// Note: the string "panic: runtime error at " is also used in
|
||||
// runtime_cortexm_hardfault.go. It is kept the same so that the string
|
||||
// can be deduplicated by the compiler.
|
||||
printstring("panic: runtime error at ")
|
||||
printptr(uintptr(addr) - callInstSize)
|
||||
printstring(": ")
|
||||
|
||||
@@ -57,6 +57,12 @@ func memzero(ptr unsafe.Pointer, size uintptr)
|
||||
// the current stack pointer in a platform-independent way.
|
||||
func stacksave() unsafe.Pointer
|
||||
|
||||
// Special LLVM intrinsic that returns the SP register on entry to the calling
|
||||
// function.
|
||||
//
|
||||
//export llvm.sponentry.p0
|
||||
func llvm_sponentry() unsafe.Pointer
|
||||
|
||||
//export strlen
|
||||
func strlen(ptr unsafe.Pointer) uintptr
|
||||
|
||||
|
||||
@@ -273,6 +273,9 @@ func nanosecondsToTicks(ns int64) timeUnit {
|
||||
func sleepTicks(d timeUnit) {
|
||||
for d != 0 {
|
||||
ticks := uint32(d)
|
||||
if d > 0xffff_ffff {
|
||||
ticks = 0xffff_ffff
|
||||
}
|
||||
if !timerSleep(ticks) {
|
||||
// Bail out early to handle a non-time interrupt.
|
||||
return
|
||||
|
||||
@@ -266,6 +266,9 @@ func nanosecondsToTicks(ns int64) timeUnit {
|
||||
func sleepTicks(d timeUnit) {
|
||||
for d != 0 {
|
||||
ticks := uint32(d)
|
||||
if d > 0xffff_ffff {
|
||||
ticks = 0xffff_ffff
|
||||
}
|
||||
if !timerSleep(ticks) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,39 +2,25 @@
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// This function is called at HardFault.
|
||||
// Before this function is called, the stack pointer is reset to the initial
|
||||
// stack pointer (loaded from address 0x0) and the previous stack pointer is
|
||||
// passed as an argument to this function. This allows for easy inspection of
|
||||
// the stack the moment a HardFault occurs, but it means that the stack will be
|
||||
// corrupted by this function and thus this handler must not attempt to recover.
|
||||
//
|
||||
// For details, see:
|
||||
// https://community.arm.com/developer/ip-products/system/f/embedded-forum/3257/debugging-a-cortex-m0-hard-fault
|
||||
// https://blog.feabhas.com/2013/02/developing-a-generic-hard-fault-handler-for-arm-cortex-m3cortex-m4/
|
||||
//
|
||||
//export handleHardFault
|
||||
func handleHardFault(sp *interruptStack) {
|
||||
print("fatal error: ")
|
||||
if uintptr(unsafe.Pointer(sp)) < 0x20000000 {
|
||||
print("stack overflow")
|
||||
} else {
|
||||
// TODO: try to find the cause of the hard fault. Especially on
|
||||
// Cortex-M3 and higher it is possible to find more detailed information
|
||||
// in special status registers.
|
||||
print("HardFault")
|
||||
}
|
||||
print(" with sp=", sp)
|
||||
if uintptr(unsafe.Pointer(&sp.PC)) >= 0x20000000 {
|
||||
// Only print the PC if it points into memory.
|
||||
// It may not point into memory during a stack overflow, so check that
|
||||
// first before accessing the stack.
|
||||
print(" pc=", sp.PC)
|
||||
}
|
||||
//export HardFault_Handler
|
||||
func HardFault_Handler() {
|
||||
// Obtain the stack pointer as it was on entry to the HardFault. It contains
|
||||
// the registers that were pushed by the NVIC and that we can now read back
|
||||
// to print the PC value at the time of the hard fault, for example.
|
||||
sp := (*interruptStack)(llvm_sponentry())
|
||||
|
||||
// Note: by reusing the string "panic: runtime error at " we save a little
|
||||
// bit in terms of code size as the string can be deduplicated.
|
||||
print("panic: runtime error at ", sp.PC, ": HardFault with sp=", sp)
|
||||
// TODO: try to find the cause of the hard fault. Especially on Cortex-M3
|
||||
// and higher it is possible to find more detailed information in special
|
||||
// status registers.
|
||||
println()
|
||||
abort()
|
||||
}
|
||||
|
||||
@@ -18,8 +18,13 @@ const (
|
||||
|
||||
// See runtime_cortexm_hardfault.go
|
||||
//
|
||||
//go:export handleHardFault
|
||||
func handleHardFault(sp *interruptStack) {
|
||||
//export HardFault_Handler
|
||||
func HardFault_Handler() {
|
||||
// Obtain the stack pointer as it was on entry to the HardFault. It contains
|
||||
// the registers that were pushed by the NVIC and that we can now read back
|
||||
// to print the PC value at the time of the hard fault, for example.
|
||||
sp := (*interruptStack)(llvm_sponentry())
|
||||
|
||||
fault := GetFaultStatus()
|
||||
spValid := !fault.Bus().ImpreciseDataBusError()
|
||||
|
||||
|
||||
@@ -78,6 +78,9 @@ func buffered() int {
|
||||
func sleepTicks(d timeUnit) {
|
||||
for d != 0 {
|
||||
ticks := uint32(d) & 0x7fffff // 23 bits (to be on the safe side)
|
||||
if d > 0x7fffff {
|
||||
ticks = 0x7fffff
|
||||
}
|
||||
rtc_sleep(ticks)
|
||||
d -= timeUnit(ticks)
|
||||
}
|
||||
|
||||
@@ -81,6 +81,9 @@ func buffered() int {
|
||||
func sleepTicks(d timeUnit) {
|
||||
for d != 0 {
|
||||
ticks := uint32(d) & 0x7fffff // 23 bits (to be on the safe side)
|
||||
if d > 0x7fffff {
|
||||
ticks = 0x7fffff
|
||||
}
|
||||
rtc_sleep(ticks)
|
||||
d -= timeUnit(ticks)
|
||||
}
|
||||
|
||||
@@ -212,6 +212,7 @@ func writeStream(stream *wasiStream, buf *byte, count uint, offset int64) int {
|
||||
return -1
|
||||
}
|
||||
remaining -= len
|
||||
src = src[len:]
|
||||
}
|
||||
|
||||
return int(count)
|
||||
|
||||
+28
-5
@@ -10,6 +10,7 @@ package testing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
@@ -78,6 +79,9 @@ type common struct {
|
||||
tempDir string
|
||||
tempDirErr error
|
||||
tempDirSeq int32
|
||||
|
||||
ctx context.Context
|
||||
cancelCtx context.CancelFunc
|
||||
}
|
||||
|
||||
type logger struct {
|
||||
@@ -152,6 +156,7 @@ func fmtDuration(d time.Duration) string {
|
||||
// TB is the interface common to T and B.
|
||||
type TB interface {
|
||||
Cleanup(func())
|
||||
Context() context.Context
|
||||
Error(args ...interface{})
|
||||
Errorf(format string, args ...interface{})
|
||||
Fail()
|
||||
@@ -307,6 +312,15 @@ func (c *common) Cleanup(f func()) {
|
||||
c.cleanups = append(c.cleanups, f)
|
||||
}
|
||||
|
||||
// Context returns a context that is canceled just before
|
||||
// Cleanup-registered functions are called.
|
||||
//
|
||||
// Cleanup functions can wait for any resources
|
||||
// that shut down on [context.Context.Done] before the test or benchmark completes.
|
||||
func (c *common) Context() context.Context {
|
||||
return c.ctx
|
||||
}
|
||||
|
||||
// TempDir returns a temporary directory for the test to use.
|
||||
// The directory is automatically removed by Cleanup when the test and
|
||||
// all its subtests complete.
|
||||
@@ -447,6 +461,9 @@ func (c *common) runCleanup() {
|
||||
if cleanup == nil {
|
||||
return
|
||||
}
|
||||
if c.cancelCtx != nil {
|
||||
c.cancelCtx()
|
||||
}
|
||||
cleanup()
|
||||
}
|
||||
}
|
||||
@@ -488,12 +505,15 @@ func (t *T) Run(name string, f func(t *T)) bool {
|
||||
}
|
||||
|
||||
// Create a subtest.
|
||||
ctx, cancelCtx := context.WithCancel(context.Background())
|
||||
sub := T{
|
||||
common: common{
|
||||
output: &logger{logToStdout: flagVerbose},
|
||||
name: testName,
|
||||
parent: &t.common,
|
||||
level: t.level + 1,
|
||||
output: &logger{logToStdout: flagVerbose},
|
||||
name: testName,
|
||||
parent: &t.common,
|
||||
level: t.level + 1,
|
||||
ctx: ctx,
|
||||
cancelCtx: cancelCtx,
|
||||
},
|
||||
context: t.context,
|
||||
}
|
||||
@@ -606,9 +626,12 @@ func runTests(matchString func(pat, str string) (bool, error), tests []InternalT
|
||||
ok = true
|
||||
|
||||
ctx := newTestContext(newMatcher(matchString, flagRunRegexp, "-test.run", flagSkipRegexp))
|
||||
runCtx, cancelCtx := context.WithCancel(context.Background())
|
||||
t := &T{
|
||||
common: common{
|
||||
output: &logger{logToStdout: flagVerbose},
|
||||
output: &logger{logToStdout: flagVerbose},
|
||||
ctx: runCtx,
|
||||
cancelCtx: cancelCtx,
|
||||
},
|
||||
context: ctx,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"inherits": [
|
||||
"rp2350b"
|
||||
],
|
||||
"build-tags": ["pico2_ice"],
|
||||
"serial-port": ["2e8a:000A"],
|
||||
"default-stack-size": 8192,
|
||||
"ldflags": [
|
||||
"--defsym=__flash_size=4M"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user