mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-31 09:07:46 +00:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5e50bd60a5 | |||
| 73fa5cd7bc | |||
| 1be7582106 | |||
| d5b7cdbca3 | |||
| c769262398 | |||
| 64caab1ade | |||
| 78914382c3 | |||
| 15e37ff927 | |||
| 91234060be | |||
| b33b6ce293 | |||
| 8911abbc6d | |||
| bc77922d47 | |||
| 34efc3a381 | |||
| 5c46efb4d1 | |||
| d6343d9d5c | |||
| e368551919 | |||
| 8b4624a420 | |||
| 9fd1b7b1a8 | |||
| a38829c692 | |||
| 1dbc6c83c4 | |||
| 9a6071920f | |||
| 0964176b4d | |||
| 1ffa9a4adf | |||
| 8c5886060f | |||
| 3bb092d4d4 | |||
| 20e62dea6f | |||
| 1b5d312c68 | |||
| 435ddc5f2d | |||
| 1de1f87c6b | |||
| 778164c98e | |||
| b3da00ac1f | |||
| e454ad1b61 | |||
| f845b469b1 | |||
| 35adbffa54 | |||
| 01f3d3eb59 | |||
| 0e43146d32 | |||
| 93f40992c1 | |||
| c6b47fe6a0 | |||
| 536deaa00a | |||
| df1d639deb |
+10
-8
@@ -92,16 +92,20 @@ commands:
|
||||
- /go/pkg/mod
|
||||
|
||||
jobs:
|
||||
test-llvm15-go122:
|
||||
test-oldest:
|
||||
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
||||
# least the smoke tests still pass.
|
||||
docker:
|
||||
- image: golang:1.22-bullseye
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "15"
|
||||
resource_class: large
|
||||
test-llvm20-go124:
|
||||
test-newest:
|
||||
# This tests the latest supported LLVM version when linking against system
|
||||
# libraries.
|
||||
docker:
|
||||
- image: golang:1.24-bullseye
|
||||
- image: golang:1.25-bullseye
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "20"
|
||||
@@ -110,8 +114,6 @@ jobs:
|
||||
workflows:
|
||||
test-all:
|
||||
jobs:
|
||||
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
||||
# least the smoke tests still pass.
|
||||
- test-llvm15-go122
|
||||
# This tests LLVM 20 support when linking against system libraries.
|
||||
- test-llvm20-go124
|
||||
- test-oldest
|
||||
# disable this test, since CircleCI seems unable to download due to rate-limits on Dockerhub.
|
||||
# - test-newest
|
||||
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24'
|
||||
go-version: '1.25.0'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v4
|
||||
@@ -134,7 +134,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24'
|
||||
go-version: '1.25.0'
|
||||
cache: true
|
||||
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
||||
run: go install -tags=llvm${{ matrix.version }}
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
# statically linked binary.
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: golang:1.24-alpine
|
||||
image: golang:1.25-alpine
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
@@ -137,7 +137,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24'
|
||||
go-version: '1.25.0'
|
||||
cache: true
|
||||
- name: Install wasmtime
|
||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||
@@ -181,7 +181,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24'
|
||||
go-version: '1.25.0'
|
||||
cache: true
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
@@ -298,7 +298,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24'
|
||||
go-version: '1.25.0'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v4
|
||||
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24'
|
||||
go-version: '1.25.0'
|
||||
cache: true
|
||||
- name: Restore cached LLVM source
|
||||
uses: actions/cache/restore@v4
|
||||
@@ -147,7 +147,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24'
|
||||
go-version: '1.25.0'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -177,7 +177,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24'
|
||||
go-version: '1.25.0'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -213,7 +213,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24'
|
||||
go-version: '1.25.0'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
+8
-2
@@ -85,11 +85,17 @@ Try running TinyGo:
|
||||
|
||||
./build/tinygo help
|
||||
|
||||
Also, make sure the `tinygo` binary really is statically linked. Check this
|
||||
using `ldd` (not to be confused with `lld`):
|
||||
Also, make sure the `tinygo` binary really is statically linked. The command to check for
|
||||
dynamic dependencies differs depending on your operating system.
|
||||
|
||||
On Linux, use `ldd` (not to be confused with `lld`):
|
||||
|
||||
ldd ./build/tinygo
|
||||
|
||||
On macOS, use otool -L:
|
||||
|
||||
otool -L ./build/tinygo
|
||||
|
||||
The result should not contain libclang or libLLVM.
|
||||
|
||||
## Make a release tarball
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
# tinygo-llvm stage obtains the llvm source for TinyGo
|
||||
FROM golang:1.24 AS tinygo-llvm
|
||||
FROM golang:1.25 AS tinygo-llvm
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y apt-utils make cmake clang-15 ninja-build && \
|
||||
apt-get install -y apt-utils make cmake clang-17 ninja-build && \
|
||||
rm -rf \
|
||||
/var/lib/apt/lists/* \
|
||||
/var/log/* \
|
||||
@@ -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.24 AS tinygo-compiler
|
||||
FROM golang:1.25 AS tinygo-compiler
|
||||
|
||||
# Copy tinygo build.
|
||||
COPY --from=tinygo-compiler-build /tinygo/build/release/tinygo /tinygo
|
||||
|
||||
+24
-11
@@ -187,7 +187,10 @@ fmt-check: ## Warn if any source needs reformatting
|
||||
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
|
||||
|
||||
|
||||
gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-kendryte gen-device-nxp gen-device-rp gen-device-renesas ## Generate microcontroller-specific sources
|
||||
gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-kendryte gen-device-nxp gen-device-rp ## Generate microcontroller-specific sources
|
||||
ifneq ($(RENESAS), 0)
|
||||
gen-device: gen-device-renesas
|
||||
endif
|
||||
ifneq ($(STM32), 0)
|
||||
gen-device: gen-device-stm32
|
||||
endif
|
||||
@@ -457,11 +460,15 @@ TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
|
||||
TEST_IOFS := false
|
||||
endif
|
||||
|
||||
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic'
|
||||
|
||||
# Test known-working standard library packages.
|
||||
# TODO: parallelize, and only show failing tests (no implied -v flag).
|
||||
.PHONY: tinygo-test
|
||||
tinygo-test:
|
||||
$(TINYGO) test $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
||||
@# 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)
|
||||
@# 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.
|
||||
@@ -469,7 +476,7 @@ ifeq ($(TEST_IOFS),true)
|
||||
$(TINYGO) test -stack-size=6MB io/fs
|
||||
endif
|
||||
tinygo-test-fast:
|
||||
$(TINYGO) test $(TEST_PACKAGES_HOST)
|
||||
$(TINYGO) test $(TEST_SKIP_FLAG) $(TEST_PACKAGES_HOST)
|
||||
tinygo-bench:
|
||||
$(TINYGO) test -bench . $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
||||
tinygo-bench-fast:
|
||||
@@ -477,18 +484,18 @@ tinygo-bench-fast:
|
||||
|
||||
# Same thing, except for wasi rather than the current platform.
|
||||
tinygo-test-wasm:
|
||||
$(TINYGO) test -target wasm $(TEST_PACKAGES_WASM)
|
||||
$(TINYGO) test -target wasm $(TEST_SKIP_FLAG) $(TEST_PACKAGES_WASM)
|
||||
tinygo-test-wasi:
|
||||
$(TINYGO) test -target wasip1 $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
|
||||
$(TINYGO) test -target wasip1 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
|
||||
tinygo-test-wasip1:
|
||||
GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
|
||||
GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
|
||||
tinygo-test-wasip1-fast:
|
||||
$(TINYGO) test -target=wasip1 $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
|
||||
$(TINYGO) test -target=wasip1 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
|
||||
|
||||
tinygo-test-wasip2-slow:
|
||||
$(TINYGO) test -target=wasip2 $(TEST_PACKAGES_SLOW)
|
||||
$(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_SLOW)
|
||||
tinygo-test-wasip2-fast:
|
||||
$(TINYGO) test -target=wasip2 $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
|
||||
$(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
|
||||
|
||||
tinygo-test-wasip2-sum-slow:
|
||||
TINYGO=$(TINYGO) \
|
||||
@@ -514,7 +521,7 @@ tinygo-bench-wasip2-fast:
|
||||
|
||||
# Run tests on riscv-qemu since that one provides a large amount of memory.
|
||||
tinygo-test-baremetal:
|
||||
$(TINYGO) test -target riscv-qemu $(TEST_PACKAGES_BAREMETAL)
|
||||
$(TINYGO) test -target riscv-qemu $(TEST_SKIP_FLAG) $(TEST_PACKAGES_BAREMETAL)
|
||||
|
||||
# Test external packages in a large corpus.
|
||||
test-corpus:
|
||||
@@ -778,6 +785,8 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico-plus2 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=metro-rp2350 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=waveshare-rp2040-tiny examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
# test pwm
|
||||
@@ -837,6 +846,8 @@ ifneq ($(STM32), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=mksnanov3 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32l0x1 examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
endif
|
||||
$(TINYGO) build -size short -o test.hex -target=atmega328pb examples/blinkm
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -907,7 +918,7 @@ endif
|
||||
$(TINYGO) build -size short -o test.hex -target=hw-651 examples/machinetest
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=hw-651-s110v8 examples/machinetest
|
||||
@$(MD5SUM) test.hex
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(WASM), 0)
|
||||
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/export
|
||||
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/main
|
||||
@@ -958,6 +969,7 @@ build/release: tinygo gen-device $(if $(filter 1,$(USE_SYSTEM_BINARYEN)),,binary
|
||||
@mkdir -p build/release/tinygo/lib/nrfx
|
||||
@mkdir -p build/release/tinygo/lib/picolibc/newlib/libc
|
||||
@mkdir -p build/release/tinygo/lib/picolibc/newlib/libm
|
||||
@mkdir -p build/release/tinygo/lib/wasi-libc/dlmalloc
|
||||
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-bottom-half
|
||||
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch
|
||||
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@@ -1029,6 +1041,7 @@ endif
|
||||
@cp -rp lib/picolibc/newlib/libm/common build/release/tinygo/lib/picolibc/newlib/libm
|
||||
@cp -rp lib/picolibc/newlib/libm/math build/release/tinygo/lib/picolibc/newlib/libm
|
||||
@cp -rp lib/picolibc-stdio.c build/release/tinygo/lib
|
||||
@cp -rp lib/wasi-libc/dlmalloc/src build/release/tinygo/lib/wasi-libc/dlmalloc
|
||||
@cp -rp lib/wasi-libc/libc-bottom-half/cloudlibc build/release/tinygo/lib/wasi-libc/libc-bottom-half
|
||||
@cp -rp lib/wasi-libc/libc-bottom-half/headers build/release/tinygo/lib/wasi-libc/libc-bottom-half
|
||||
@cp -rp lib/wasi-libc/libc-bottom-half/sources build/release/tinygo/lib/wasi-libc/libc-bottom-half
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
|
||||
// Version range supported by TinyGo.
|
||||
const minorMin = 19
|
||||
const minorMax = 24
|
||||
const minorMax = 25
|
||||
|
||||
// Check that we support this Go toolchain version.
|
||||
gorootMajor, gorootMinor, err := goenv.GetGorootVersion()
|
||||
|
||||
@@ -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", 4556, 280, 0, 2264},
|
||||
{"microbit", "examples/serial", 2920, 388, 8, 2272},
|
||||
{"wioterminal", "examples/pininterrupt", 7379, 1489, 116, 6912},
|
||||
{"hifive1b", "examples/echo", 4580, 280, 0, 2264},
|
||||
{"microbit", "examples/serial", 2928, 388, 8, 2272},
|
||||
{"wioterminal", "examples/pininterrupt", 7387, 1489, 116, 6912},
|
||||
|
||||
// TODO: also check wasm. Right now this is difficult, because
|
||||
// wasm binaries are run through wasm-opt and therefore the
|
||||
|
||||
@@ -189,7 +189,7 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
||||
case "wasip2":
|
||||
options.Target = "wasip2"
|
||||
default:
|
||||
return nil, errors.New("GOARCH=wasm but GOOS is not set correctly. Please set GOOS to wasm, wasip1, or wasip2.")
|
||||
return nil, errors.New("GOARCH=wasm but GOOS is not set correctly. Please set GOOS to js, wasip1, or wasip2.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,7 +369,7 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
||||
return nil, fmt.Errorf("invalid GOMIPS=%s: must be hardfloat or softfloat", options.GOMIPS)
|
||||
}
|
||||
case "wasm":
|
||||
return nil, fmt.Errorf("GOARCH=wasm but GOOS is unset. Please set GOOS to wasm, wasip1, or wasip2.")
|
||||
return nil, fmt.Errorf("GOARCH=wasm but GOOS is unset. Please set GOOS to js, wasip1, or wasip2.")
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown GOARCH=%s", options.GOARCH)
|
||||
}
|
||||
@@ -385,7 +385,7 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
||||
platformVersion = "11.0.0" // first macosx platform with arm64 support
|
||||
}
|
||||
llvmvendor = "apple"
|
||||
spec.Scheduler = "tasks"
|
||||
spec.Scheduler = "threads"
|
||||
spec.Linker = "ld.lld"
|
||||
spec.Libc = "darwin-libSystem"
|
||||
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer to
|
||||
@@ -399,6 +399,7 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
||||
)
|
||||
spec.ExtraFiles = append(spec.ExtraFiles,
|
||||
"src/internal/futex/futex_darwin.c",
|
||||
"src/internal/task/task_threads.c",
|
||||
"src/runtime/os_darwin.c",
|
||||
"src/runtime/runtime_unix.c",
|
||||
"src/runtime/signal.c")
|
||||
|
||||
@@ -121,6 +121,29 @@ func (b *builder) createKeepAliveImpl() {
|
||||
b.CreateRetVoid()
|
||||
}
|
||||
|
||||
// createAbiEscapeImpl implements the generic internal/abi.Escape function. It
|
||||
// currently only supports pointer types.
|
||||
func (b *builder) createAbiEscapeImpl() {
|
||||
b.createFunctionStart(true)
|
||||
|
||||
// The first parameter is assumed to be a pointer. This is checked at the
|
||||
// call site of createAbiEscapeImpl.
|
||||
pointerValue := b.getValue(b.fn.Params[0], getPos(b.fn))
|
||||
|
||||
// Create an equivalent of the following C code, which is basically just a
|
||||
// nop but ensures the pointerValue is kept alive:
|
||||
//
|
||||
// __asm__ __volatile__("" : : "r"(pointerValue))
|
||||
//
|
||||
// It should be portable to basically everything as the "r" register type
|
||||
// exists basically everywhere.
|
||||
asmType := llvm.FunctionType(b.dataPtrType, []llvm.Type{b.dataPtrType}, false)
|
||||
asmFn := llvm.InlineAsm(asmType, "", "=r,0", true, false, 0, false)
|
||||
result := b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "")
|
||||
|
||||
b.CreateRet(result)
|
||||
}
|
||||
|
||||
var mathToLLVMMapping = map[string]string{
|
||||
"math.Ceil": "llvm.ceil.f64",
|
||||
"math.Exp": "llvm.exp.f64",
|
||||
|
||||
@@ -253,6 +253,23 @@ func (c *compilerContext) maybeCreateSyntheticFunction(fn *ssa.Function, llvmFn
|
||||
// The exception is the package initializer, which does appear in the
|
||||
// *ssa.Package members and so shouldn't be created here.
|
||||
if fn.Synthetic != "" && fn.Synthetic != "package initializer" && fn.Synthetic != "generic function" && fn.Synthetic != "range-over-func yield" {
|
||||
if origin := fn.Origin(); origin != nil && origin.RelString(nil) == "internal/abi.Escape" {
|
||||
// This is a special implementation or internal/abi.Escape, which
|
||||
// can only really be implemented in the compiler.
|
||||
// For simplicity we'll only implement pointer parameters for now.
|
||||
if _, ok := fn.Params[0].Type().Underlying().(*types.Pointer); ok {
|
||||
irbuilder := c.ctx.NewBuilder()
|
||||
defer irbuilder.Dispose()
|
||||
b := newBuilder(c, irbuilder, fn)
|
||||
b.createAbiEscapeImpl()
|
||||
llvmFn.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
llvmFn.SetUnnamedAddr(true)
|
||||
}
|
||||
// If the parameter is not of a pointer type, it will be left
|
||||
// unimplemented. This will result in a linker error if the function
|
||||
// is really called, making it clear it needs to be implemented.
|
||||
return
|
||||
}
|
||||
if len(fn.Blocks) == 0 {
|
||||
c.addError(fn.Pos(), "missing function body")
|
||||
return
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
// Version of TinyGo.
|
||||
// Update this value before release of new version of software.
|
||||
const version = "0.38.0"
|
||||
const version = "0.39.0-dev"
|
||||
|
||||
// Return TinyGo version, either in the form 0.30.0 or as a development version
|
||||
// (like 0.30.0-dev-abcd012).
|
||||
|
||||
@@ -291,7 +291,8 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
|
||||
})
|
||||
|
||||
if testConfig.CompileOnly {
|
||||
return true, nil
|
||||
// Return the compiler error, if there is one.
|
||||
return true, err
|
||||
}
|
||||
|
||||
importPath := strings.TrimSuffix(result.ImportPath, ".test")
|
||||
@@ -339,8 +340,17 @@ func dirsToModuleRootAbs(maindir, modroot string) []string {
|
||||
return dirs
|
||||
}
|
||||
|
||||
// validateOutputFormat checks if the output file extension matches the expected format
|
||||
func validateOutputFormat(outpath, expectedExt string) error {
|
||||
actualExt := filepath.Ext(outpath)
|
||||
if actualExt != expectedExt {
|
||||
return fmt.Errorf("output format %s does not match target format %s", actualExt, expectedExt)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flash builds and flashes the built binary to the given serial port.
|
||||
func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
func Flash(pkgName, port, outpath string, options *compileopts.Options) error {
|
||||
config, err := builder.NewConfig(options)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -389,13 +399,24 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
if !options.Work {
|
||||
defer os.RemoveAll(tmpdir)
|
||||
}
|
||||
|
||||
// Validate output format before building
|
||||
if outpath != "" {
|
||||
if err := validateOutputFormat(outpath, fileExt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Build the binary.
|
||||
result, err := builder.Build(pkgName, fileExt, tmpdir, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Save output file if specified (after build, before flashing)
|
||||
if outpath != "" {
|
||||
if err := copyFile(result.Binary, outpath); err != nil {
|
||||
return fmt.Errorf("failed to save output file: %v", err)
|
||||
}
|
||||
}
|
||||
// do we need port reset to put MCU into bootloader mode?
|
||||
if config.Target.PortReset == "true" && flashMethod != "openocd" {
|
||||
port, err := getDefaultPort(port, config.Target.SerialPort)
|
||||
@@ -1297,6 +1318,11 @@ extension at all.`
|
||||
(https://tinygo.org/docs/reference/microcontrollers/).
|
||||
Examples: "arduino-nano", "d1mini", "xiao".
|
||||
|
||||
-o={filename}:
|
||||
Save the built binary to the specified output file. The file
|
||||
format must match the target's expected format (e.g., .hex,
|
||||
.uf2). Both flashing and saving will be performed.
|
||||
|
||||
-monitor:
|
||||
Start the serial monitor (see below) immediately after
|
||||
flashing. However, some microcontrollers need a split second
|
||||
@@ -1627,7 +1653,7 @@ func main() {
|
||||
flag.BoolVar(&flagTest, "test", false, "supply -test flag to go list")
|
||||
}
|
||||
var outpath string
|
||||
if command == "help" || command == "build" || command == "test" {
|
||||
if command == "help" || command == "build" || command == "test" || command == "flash" {
|
||||
flag.StringVar(&outpath, "o", "", "output filename")
|
||||
}
|
||||
|
||||
@@ -1778,7 +1804,7 @@ func main() {
|
||||
case "flash", "gdb", "lldb":
|
||||
pkgName := filepath.ToSlash(flag.Arg(0))
|
||||
if command == "flash" {
|
||||
err := Flash(pkgName, *port, options)
|
||||
err := Flash(pkgName, *port, outpath, options)
|
||||
printBuildOutput(err, *flagJSON)
|
||||
} else {
|
||||
if !options.Debug {
|
||||
@@ -1864,6 +1890,7 @@ func main() {
|
||||
wd = ""
|
||||
}
|
||||
diagnostics.CreateDiagnostics(err).WriteTo(os.Stderr, wd)
|
||||
os.Exit(1)
|
||||
}
|
||||
if !passed {
|
||||
select {
|
||||
|
||||
@@ -424,10 +424,12 @@ func optionsFromOSARCH(osarch string, sema chan struct{}) compileopts.Options {
|
||||
}
|
||||
|
||||
func runTest(name string, options compileopts.Options, t *testing.T, cmdArgs, environmentVars []string) {
|
||||
t.Helper()
|
||||
runTestWithConfig(name, t, options, cmdArgs, environmentVars)
|
||||
}
|
||||
|
||||
func runTestWithConfig(name string, t *testing.T, options compileopts.Options, cmdArgs, environmentVars []string) {
|
||||
t.Helper()
|
||||
// Get the expected output for this test.
|
||||
// Note: not using filepath.Join as it strips the path separator at the end
|
||||
// of the path.
|
||||
@@ -876,6 +878,7 @@ func TestWasmExit(t *testing.T) {
|
||||
|
||||
// Check whether the output of a test equals the expected output.
|
||||
func checkOutput(t *testing.T, filename string, actual []byte) {
|
||||
t.Helper()
|
||||
expectedOutput, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatal("could not read output file:", err)
|
||||
@@ -884,6 +887,7 @@ func checkOutput(t *testing.T, filename string, actual []byte) {
|
||||
}
|
||||
|
||||
func checkOutputData(t *testing.T, expectedOutput, actual []byte) {
|
||||
t.Helper()
|
||||
expectedOutput = bytes.ReplaceAll(expectedOutput, []byte("\r\n"), []byte("\n"))
|
||||
actual = bytes.ReplaceAll(actual, []byte("\r\n"), []byte("\n"))
|
||||
|
||||
|
||||
@@ -63,6 +63,10 @@ func SecCertificateCopyData(cert CFRef) ([]byte, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func SecTrustCopyCertificateChain(trustObj CFRef) (CFRef, error) {
|
||||
return 0, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func SecTrustEvaluateWithError(trustObj CFRef) (int, error) {
|
||||
return 0, errors.New("not implemented")
|
||||
}
|
||||
|
||||
@@ -8,3 +8,10 @@ import "unsafe"
|
||||
func NoEscape(p unsafe.Pointer) unsafe.Pointer {
|
||||
return p
|
||||
}
|
||||
|
||||
func Escape[T any](x T) T {
|
||||
// This function is either implemented in the compiler, or left undefined
|
||||
// for some variation of T. The body of this function should not be compiled
|
||||
// as-is.
|
||||
panic("internal/abi.Escape: unreachable (implemented in the compiler)")
|
||||
}
|
||||
|
||||
@@ -5,29 +5,20 @@ import "unsafe"
|
||||
// Internal constants for gc layout
|
||||
// See runtime/gc_precise.go
|
||||
|
||||
var (
|
||||
NoPtrs unsafe.Pointer
|
||||
Pointer unsafe.Pointer
|
||||
String unsafe.Pointer
|
||||
Slice unsafe.Pointer
|
||||
type Layout uintptr
|
||||
|
||||
const (
|
||||
// 16-bit int => bits = 4
|
||||
// 32-bit int => bits = 5
|
||||
// 64-bit int => bits = 6
|
||||
sizeBits = 4 + unsafe.Sizeof(uintptr(0))/4
|
||||
|
||||
sizeShift = sizeBits + 1
|
||||
|
||||
NoPtrs = Layout(uintptr(0b0<<sizeShift) | uintptr(0b1<<1) | uintptr(1))
|
||||
Pointer = Layout(uintptr(0b1<<sizeShift) | uintptr(0b1<<1) | uintptr(1))
|
||||
String = Layout(uintptr(0b01<<sizeShift) | uintptr(0b10<<1) | uintptr(1))
|
||||
Slice = Layout(uintptr(0b001<<sizeShift) | uintptr(0b11<<1) | uintptr(1))
|
||||
)
|
||||
|
||||
func init() {
|
||||
var sizeBits uintptr
|
||||
|
||||
switch unsafe.Sizeof(uintptr(0)) {
|
||||
case 8:
|
||||
sizeBits = 6
|
||||
case 4:
|
||||
sizeBits = 5
|
||||
case 2:
|
||||
sizeBits = 4
|
||||
}
|
||||
|
||||
var sizeShift = sizeBits + 1
|
||||
|
||||
NoPtrs = unsafe.Pointer(uintptr(0b0<<sizeShift) | uintptr(0b1<<1) | uintptr(1))
|
||||
Pointer = unsafe.Pointer(uintptr(0b1<<sizeShift) | uintptr(0b1<<1) | uintptr(1))
|
||||
String = unsafe.Pointer(uintptr(0b01<<sizeShift) | uintptr(0b10<<1) | uintptr(1))
|
||||
Slice = unsafe.Pointer(uintptr(0b001<<sizeShift) | uintptr(0b11<<1) | uintptr(1))
|
||||
}
|
||||
func (l Layout) AsPtr() unsafe.Pointer { return unsafe.Pointer(l) }
|
||||
|
||||
@@ -708,16 +708,16 @@ func (r *RawType) gcLayout() unsafe.Pointer {
|
||||
kind := r.Kind()
|
||||
|
||||
if kind < String {
|
||||
return gclayout.NoPtrs
|
||||
return gclayout.NoPtrs.AsPtr()
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case Pointer, UnsafePointer, Chan, Map:
|
||||
return gclayout.Pointer
|
||||
return gclayout.Pointer.AsPtr()
|
||||
case String:
|
||||
return gclayout.String
|
||||
return gclayout.String.AsPtr()
|
||||
case Slice:
|
||||
return gclayout.Slice
|
||||
return gclayout.Slice.AsPtr()
|
||||
}
|
||||
|
||||
// Unknown (for now); let the conservative pointer scanning handle it
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
//go:build darwin
|
||||
|
||||
package task
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// MacOS uses a pointer so unsafe.Pointer should be fine:
|
||||
//
|
||||
// typedef struct _opaque_pthread_t *__darwin_pthread_t;
|
||||
// typedef __darwin_pthread_t pthread_t;
|
||||
type threadID unsafe.Pointer
|
||||
@@ -2,16 +2,28 @@
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// BDWGC also uses SIGRTMIN+6 on Linux, which seems like a reasonable choice.
|
||||
#ifdef __linux__
|
||||
#include <semaphore.h>
|
||||
|
||||
// BDWGC also uses SIGRTMIN+6 on Linux, which seems like a reasonable choice.
|
||||
#define taskPauseSignal (SIGRTMIN + 6)
|
||||
#endif
|
||||
|
||||
#elif __APPLE__
|
||||
#include <dispatch/dispatch.h>
|
||||
// SIGIO is for interrupt-driven I/O.
|
||||
// I don't think anybody should be using this nowadays, so I think we can
|
||||
// repurpose it as a signal for GC.
|
||||
// BDWGC uses a special way to pause/resume other threads on MacOS, which may be
|
||||
// better but needs more work. Using signal keeps the code similar between Linux
|
||||
// and MacOS.
|
||||
#define taskPauseSignal SIGIO
|
||||
|
||||
#endif // __linux__, __APPLE__
|
||||
|
||||
// Pointer to the current task.Task structure.
|
||||
// Ideally the entire task.Task structure would be a thread-local variable but
|
||||
@@ -23,7 +35,11 @@ struct state_pass {
|
||||
void *args;
|
||||
void *task;
|
||||
uintptr_t *stackTop;
|
||||
#if __APPLE__
|
||||
dispatch_semaphore_t startlock;
|
||||
#else
|
||||
sem_t startlock;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Handle the GC pause in Go.
|
||||
@@ -41,8 +57,8 @@ void tinygo_task_init(void *mainTask, pthread_t *thread, int *numCPU, void *cont
|
||||
// Register the "GC pause" signal for the entire process.
|
||||
// Using pthread_kill, we can still send the signal to a specific thread.
|
||||
struct sigaction act = { 0 };
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
act.sa_handler = &tinygo_task_gc_pause;
|
||||
act.sa_handler = tinygo_task_gc_pause;
|
||||
act.sa_flags = SA_RESTART;
|
||||
sigaction(taskPauseSignal, &act, NULL);
|
||||
|
||||
// Obtain the number of CPUs available on program start (for NumCPU).
|
||||
@@ -69,7 +85,11 @@ static void* start_wrapper(void *arg) {
|
||||
|
||||
// Notify the caller that the thread has successfully started and
|
||||
// initialized.
|
||||
#if __APPLE__
|
||||
dispatch_semaphore_signal(state->startlock);
|
||||
#else
|
||||
sem_post(&state->startlock);
|
||||
#endif
|
||||
|
||||
// Run the goroutine function.
|
||||
start(args);
|
||||
@@ -81,7 +101,7 @@ static void* start_wrapper(void *arg) {
|
||||
};
|
||||
|
||||
// Start a new goroutine in an OS thread.
|
||||
int tinygo_task_start(uintptr_t fn, void *args, void *task, pthread_t *thread, uintptr_t *stackTop, void *context) {
|
||||
int tinygo_task_start(uintptr_t fn, void *args, void *task, pthread_t *thread, uintptr_t *stackTop, uintptr_t stackSize, void *context) {
|
||||
// Sanity check. Should get optimized away.
|
||||
if (sizeof(pthread_t) != sizeof(void*)) {
|
||||
__builtin_trap();
|
||||
@@ -93,11 +113,23 @@ int tinygo_task_start(uintptr_t fn, void *args, void *task, pthread_t *thread, u
|
||||
.task = task,
|
||||
.stackTop = stackTop,
|
||||
};
|
||||
#if __APPLE__
|
||||
state.startlock = dispatch_semaphore_create(0);
|
||||
#else
|
||||
sem_init(&state.startlock, 0, 0);
|
||||
int result = pthread_create(thread, NULL, &start_wrapper, &state);
|
||||
#endif
|
||||
pthread_attr_t attrs;
|
||||
pthread_attr_init(&attrs);
|
||||
pthread_attr_setstacksize(&attrs, stackSize);
|
||||
int result = pthread_create(thread, &attrs, &start_wrapper, &state);
|
||||
pthread_attr_destroy(&attrs);
|
||||
|
||||
// Wait until the thread has been created and read all state_pass variables.
|
||||
#if __APPLE__
|
||||
dispatch_semaphore_wait(state.startlock, DISPATCH_TIME_FOREVER);
|
||||
#else
|
||||
sem_wait(&state.startlock);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||
// and the stop-the-world GC won't see threads that haven't started yet or
|
||||
// are not fully started yet.
|
||||
activeTaskLock.Lock()
|
||||
errCode := tinygo_task_start(fn, args, t, &t.state.thread, &t.state.stackTop)
|
||||
errCode := tinygo_task_start(fn, args, t, &t.state.thread, &t.state.stackTop, stackSize)
|
||||
if errCode != 0 {
|
||||
runtimePanic("could not start thread")
|
||||
}
|
||||
@@ -220,7 +220,7 @@ func gcScanGlobals()
|
||||
var stackScanLock PMutex
|
||||
|
||||
//export tinygo_task_gc_pause
|
||||
func tingyo_task_gc_pause() {
|
||||
func tingyo_task_gc_pause(sig int32) {
|
||||
// Wait until we get the signal to start scanning the stack.
|
||||
Current().state.gcSem.Wait()
|
||||
|
||||
@@ -266,7 +266,7 @@ func tinygo_task_init(t *Task, thread *threadID, numCPU *int32)
|
||||
// Here same as for tinygo_task_init.
|
||||
//
|
||||
//go:linkname tinygo_task_start tinygo_task_start
|
||||
func tinygo_task_start(fn uintptr, args unsafe.Pointer, t *Task, thread *threadID, stackTop *uintptr) int32
|
||||
func tinygo_task_start(fn uintptr, args unsafe.Pointer, t *Task, thread *threadID, stackTop *uintptr, stackSize uintptr) int32
|
||||
|
||||
// Pause the thread by sending it a signal.
|
||||
//
|
||||
|
||||
@@ -27,8 +27,8 @@ const (
|
||||
PC1 = portC + 1
|
||||
PC2 = portC + 2
|
||||
PC3 = portC + 3
|
||||
PC4 = portC + 4
|
||||
PC5 = portC + 5
|
||||
PC4 = portC + 4 // peripherals: I2C0 SDA
|
||||
PC5 = portC + 5 // peripherals: I2C0 SCL
|
||||
PC6 = portC + 6
|
||||
PC7 = portC + 7
|
||||
PD0 = portD + 0
|
||||
|
||||
@@ -28,8 +28,8 @@ const (
|
||||
PC1 = portC + 1
|
||||
PC2 = portC + 2
|
||||
PC3 = portC + 3
|
||||
PC4 = portC + 4
|
||||
PC5 = portC + 5
|
||||
PC4 = portC + 4 // peripherals: I2C0 SDA
|
||||
PC5 = portC + 5 // peripherals: I2C0 SCL
|
||||
PC6 = portC + 6
|
||||
PC7 = portC + 7
|
||||
PD0 = portD + 0
|
||||
@@ -40,8 +40,8 @@ const (
|
||||
PD5 = portD + 5 // peripherals: Timer0 channel B
|
||||
PD6 = portD + 6 // peripherals: Timer0 channel A
|
||||
PD7 = portD + 7
|
||||
PE0 = portE + 0
|
||||
PE1 = portE + 1
|
||||
PE0 = portE + 0 // peripherals: I2C1 SDA
|
||||
PE1 = portE + 1 // peripherals: I2C1 SCL
|
||||
PE2 = portE + 2
|
||||
PE3 = portE + 3
|
||||
PE4 = portE + 4
|
||||
|
||||
@@ -7,32 +7,36 @@ func CPUFrequency() uint32 {
|
||||
return 48000000
|
||||
}
|
||||
|
||||
// Note that the below pins have a few I2C pins listed that according to the
|
||||
// datasheet don't support I2C yet they are used in practice on boards from
|
||||
// Adafruit and Arduino. See machine_atsamd21_simulator.go for details.
|
||||
|
||||
// Hardware pins
|
||||
const (
|
||||
PA00 Pin = 0 // peripherals: TCC2 channel 0
|
||||
PA01 Pin = 1 // peripherals: TCC2 channel 1
|
||||
PA00 Pin = 0 // peripherals: TCC2 channel 0, sercomI2CM1 SDA
|
||||
PA01 Pin = 1 // peripherals: TCC2 channel 1, sercomI2CM1 SCL
|
||||
PA02 Pin = 2
|
||||
PA03 Pin = 3
|
||||
PA04 Pin = 4 // peripherals: TCC0 channel 0
|
||||
PA05 Pin = 5 // peripherals: TCC0 channel 1
|
||||
PA06 Pin = 6 // peripherals: TCC1 channel 0
|
||||
PA07 Pin = 7 // peripherals: TCC1 channel 1
|
||||
PA08 Pin = 8 // peripherals: TCC0 channel 0, TCC1 channel 2
|
||||
PA09 Pin = 9 // peripherals: TCC0 channel 1, TCC1 channel 3
|
||||
PA08 Pin = 8 // peripherals: TCC0 channel 0, TCC1 channel 2, sercomI2CM0 SDA, sercomI2CM2 SDA
|
||||
PA09 Pin = 9 // peripherals: TCC0 channel 1, TCC1 channel 3, sercomI2CM0 SCL, sercomI2CM2 SCL
|
||||
PA10 Pin = 10 // peripherals: TCC1 channel 0, TCC0 channel 2
|
||||
PA11 Pin = 11 // peripherals: TCC1 channel 1, TCC0 channel 3
|
||||
PA12 Pin = 12 // peripherals: TCC2 channel 0, TCC0 channel 2
|
||||
PA13 Pin = 13 // peripherals: TCC2 channel 1, TCC0 channel 3
|
||||
PA12 Pin = 12 // peripherals: TCC2 channel 0, TCC0 channel 2, sercomI2CM2 SDA, sercomI2CM4 SDA
|
||||
PA13 Pin = 13 // peripherals: TCC2 channel 1, TCC0 channel 3, sercomI2CM2 SCL, sercomI2CM4 SCL
|
||||
PA14 Pin = 14 // peripherals: TCC0 channel 0
|
||||
PA15 Pin = 15 // peripherals: TCC0 channel 1
|
||||
PA16 Pin = 16 // peripherals: TCC2 channel 0, TCC0 channel 2
|
||||
PA17 Pin = 17 // peripherals: TCC2 channel 1, TCC0 channel 3
|
||||
PA16 Pin = 16 // peripherals: TCC2 channel 0, TCC0 channel 2, sercomI2CM1 SDA, sercomI2CM3 SDA
|
||||
PA17 Pin = 17 // peripherals: TCC2 channel 1, TCC0 channel 3, sercomI2CM1 SCL, sercomI2CM3 SCL
|
||||
PA18 Pin = 18 // peripherals: TCC0 channel 2
|
||||
PA19 Pin = 19 // peripherals: TCC0 channel 3
|
||||
PA20 Pin = 20 // peripherals: TCC0 channel 2
|
||||
PA21 Pin = 21 // peripherals: TCC0 channel 3
|
||||
PA22 Pin = 22 // peripherals: TCC0 channel 0
|
||||
PA23 Pin = 23 // peripherals: TCC0 channel 1
|
||||
PA22 Pin = 22 // peripherals: TCC0 channel 0, sercomI2CM3 SDA, sercomI2CM5 SDA
|
||||
PA23 Pin = 23 // peripherals: TCC0 channel 1, sercomI2CM3 SCL, sercomI2CM5 SCL
|
||||
PA24 Pin = 24 // peripherals: TCC1 channel 2
|
||||
PA25 Pin = 25 // peripherals: TCC1 channel 3
|
||||
PA26 Pin = 26
|
||||
@@ -43,22 +47,22 @@ const (
|
||||
PA31 Pin = 31 // peripherals: TCC1 channel 1
|
||||
PB00 Pin = 32
|
||||
PB01 Pin = 33
|
||||
PB02 Pin = 34
|
||||
PB03 Pin = 35
|
||||
PB02 Pin = 34 // peripherals: sercomI2CM5 SDA
|
||||
PB03 Pin = 35 // peripherals: sercomI2CM5 SCL
|
||||
PB04 Pin = 36
|
||||
PB05 Pin = 37
|
||||
PB06 Pin = 38
|
||||
PB07 Pin = 39
|
||||
PB08 Pin = 40
|
||||
PB09 Pin = 41
|
||||
PB08 Pin = 40 // peripherals: sercomI2CM4 SDA
|
||||
PB09 Pin = 41 // peripherals: sercomI2CM4 SCL
|
||||
PB10 Pin = 42 // peripherals: TCC0 channel 0
|
||||
PB11 Pin = 43 // peripherals: TCC0 channel 1
|
||||
PB12 Pin = 44 // peripherals: TCC0 channel 2
|
||||
PB13 Pin = 45 // peripherals: TCC0 channel 3
|
||||
PB12 Pin = 44 // peripherals: TCC0 channel 2, sercomI2CM4 SDA
|
||||
PB13 Pin = 45 // peripherals: TCC0 channel 3, sercomI2CM4 SCL
|
||||
PB14 Pin = 46
|
||||
PB15 Pin = 47
|
||||
PB16 Pin = 48 // peripherals: TCC0 channel 0
|
||||
PB17 Pin = 49 // peripherals: TCC0 channel 1
|
||||
PB16 Pin = 48 // peripherals: TCC0 channel 0, sercomI2CM5 SDA
|
||||
PB17 Pin = 49 // peripherals: TCC0 channel 1, sercomI2CM5 SCL
|
||||
PB18 Pin = 50
|
||||
PB19 Pin = 51
|
||||
PB20 Pin = 52
|
||||
@@ -71,6 +75,6 @@ const (
|
||||
PB27 Pin = 59
|
||||
PB28 Pin = 60
|
||||
PB29 Pin = 61
|
||||
PB30 Pin = 62 // peripherals: TCC0 channel 0, TCC1 channel 2
|
||||
PB31 Pin = 63 // peripherals: TCC0 channel 1, TCC1 channel 3
|
||||
PB30 Pin = 62 // peripherals: TCC0 channel 0, TCC1 channel 2, sercomI2CM5 SDA
|
||||
PB31 Pin = 63 // peripherals: TCC0 channel 1, TCC1 channel 3, sercomI2CM5 SCL
|
||||
)
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
//go:build metro_rp2350
|
||||
|
||||
package machine
|
||||
|
||||
// GPIO pins
|
||||
const (
|
||||
GP0 Pin = GPIO0
|
||||
GP1 Pin = GPIO1
|
||||
GP2 Pin = GPIO2
|
||||
GP3 Pin = GPIO3
|
||||
GP4 Pin = GPIO4
|
||||
GP5 Pin = GPIO5
|
||||
GP6 Pin = GPIO6
|
||||
GP7 Pin = GPIO7
|
||||
GP8 Pin = GPIO8
|
||||
GP9 Pin = GPIO9
|
||||
GP10 Pin = GPIO10
|
||||
GP11 Pin = GPIO11
|
||||
GP12 Pin = GPIO12
|
||||
GP13 Pin = GPIO13
|
||||
GP14 Pin = GPIO14
|
||||
GP15 Pin = GPIO15
|
||||
GP16 Pin = GPIO16
|
||||
GP17 Pin = GPIO17
|
||||
GP18 Pin = GPIO18
|
||||
GP19 Pin = GPIO19
|
||||
GP20 Pin = GPIO20
|
||||
GP21 Pin = GPIO21
|
||||
GP22 Pin = GPIO22
|
||||
GP23 Pin = GPIO23
|
||||
GP24 Pin = GPIO24
|
||||
GP25 Pin = GPIO25
|
||||
GP26 Pin = GPIO26
|
||||
GP27 Pin = GPIO27
|
||||
GP28 Pin = GPIO28
|
||||
GP29 Pin = GPIO29
|
||||
GP30 Pin = GPIO30
|
||||
GP31 Pin = GPIO31
|
||||
GP32 Pin = GPIO32
|
||||
GP33 Pin = GPIO33
|
||||
GP34 Pin = GPIO34
|
||||
GP35 Pin = GPIO35
|
||||
GP36 Pin = GPIO36
|
||||
GP37 Pin = GPIO37
|
||||
GP38 Pin = GPIO38
|
||||
GP39 Pin = GPIO39
|
||||
GP40 Pin = GPIO40
|
||||
GP41 Pin = GPIO41
|
||||
GP42 Pin = GPIO42
|
||||
GP43 Pin = GPIO43
|
||||
GP44 Pin = GPIO44
|
||||
GP45 Pin = GPIO45
|
||||
GP46 Pin = GPIO46
|
||||
|
||||
// Boot button
|
||||
BUTTON Pin = GPIO24
|
||||
|
||||
// Onboard LED
|
||||
LED Pin = GPIO23
|
||||
|
||||
// Onboard NeoPixel
|
||||
NEOPIXEL Pin = GPIO25
|
||||
WS2812 Pin = GPIO25
|
||||
|
||||
// Onboard crystal oscillator frequency, in MHz.
|
||||
xoscFreq = 12 // MHz
|
||||
)
|
||||
|
||||
// Arduino-header digital pins
|
||||
const (
|
||||
RX Pin = GPIO1
|
||||
TX Pin = GPIO0
|
||||
D2 Pin = GPIO2
|
||||
D3 Pin = GPIO3
|
||||
D4 Pin = GPIO4
|
||||
D5 Pin = GPIO5
|
||||
D6 Pin = GPIO6
|
||||
D7 Pin = GPIO7
|
||||
D8 Pin = GPIO8
|
||||
D9 Pin = GPIO9
|
||||
D10 Pin = GPIO10
|
||||
D11 Pin = GPIO11
|
||||
D22 Pin = GPIO22
|
||||
D23 Pin = GPIO23
|
||||
)
|
||||
|
||||
// Arduino-header analog pins
|
||||
const (
|
||||
A0 Pin = GPIO41
|
||||
A1 Pin = GPIO42
|
||||
A2 Pin = GPIO43
|
||||
A3 Pin = GPIO44
|
||||
A4 Pin = GPIO45
|
||||
A5 Pin = GPIO46
|
||||
)
|
||||
|
||||
// I2C Default pins on Raspberry Pico.
|
||||
const (
|
||||
I2C0_SDA_PIN = GP20
|
||||
I2C0_SCL_PIN = GP21
|
||||
|
||||
I2C1_SDA_PIN = GP2
|
||||
I2C1_SCL_PIN = GP3
|
||||
)
|
||||
|
||||
// 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 = GPIO30
|
||||
// Default Serial Out Bus 1 for SPI communications
|
||||
SPI1_SDO_PIN = GPIO31 // Tx
|
||||
// Default Serial In Bus 1 for SPI communications
|
||||
SPI1_SDI_PIN = GPIO28 // Rx
|
||||
|
||||
// SPI header pins
|
||||
MOSI Pin = SPI1_SDO_PIN
|
||||
MISO Pin = SPI1_SDI_PIN
|
||||
SCK Pin = SPI1_SCK_PIN
|
||||
)
|
||||
|
||||
// SD card reader pins
|
||||
const (
|
||||
SD_SCK = GPIO34
|
||||
SD_MOSI = GPIO35
|
||||
SD_MISO = GPIO36
|
||||
SDIO_DATA1 = GPIO37
|
||||
SDIO_DATA2 = GPIO38
|
||||
SD_CS = GPIO39
|
||||
SD_CARD_DETECT = GPIO40
|
||||
)
|
||||
|
||||
// HSTX pins
|
||||
const (
|
||||
CKN Pin = GPIO15
|
||||
CKP Pin = GPIO14
|
||||
D0N Pin = GPIO19
|
||||
D0P Pin = GPIO18
|
||||
D1N Pin = GPIO17
|
||||
D1P Pin = GPIO16
|
||||
D2N Pin = GPIO13
|
||||
D2P Pin = GPIO12
|
||||
D26 Pin = GPIO26
|
||||
D27 Pin = GPIO27
|
||||
SCL Pin = GPIO21
|
||||
SDA Pin = GPIO20
|
||||
)
|
||||
|
||||
// USB host header pins
|
||||
const (
|
||||
USB_HOST_DATA_PLUS Pin = GPIO32
|
||||
USB_HOST_DATA_MINUS Pin = GPIO33
|
||||
USB_HOST_5V_POWER Pin = GPIO29
|
||||
)
|
||||
|
||||
// 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 = "Metro RP2350"
|
||||
usb_STRING_MANUFACTURER = "Adafruit"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x239A
|
||||
usb_PID uint16 = 0x814E
|
||||
)
|
||||
@@ -31,25 +31,24 @@ const (
|
||||
GP27 = GPIO27
|
||||
GP28 = GPIO28
|
||||
GP29 = GPIO29
|
||||
GP30 = GPIO30 // peripherals: PWM7 channel A
|
||||
GP31 = GPIO31 // peripherals: PWM7 channel B
|
||||
GP32 = GPIO32 // peripherals: PWM8 channel A
|
||||
GP33 = GPIO33 // peripherals: PWM8 channel B
|
||||
GP34 = GPIO34 // peripherals: PWM9 channel A
|
||||
GP35 = GPIO35 // peripherals: PWM9 channel B
|
||||
GP36 = GPIO36 // peripherals: PWM10 channel A
|
||||
GP37 = GPIO37 // peripherals: PWM10 channel B
|
||||
GP38 = GPIO38 // peripherals: PWM11 channel A
|
||||
GP39 = GPIO39 // peripherals: PWM11 channel B
|
||||
GP40 = GPIO40 // peripherals: PWM8 channel A
|
||||
GP41 = GPIO41 // peripherals: PWM8 channel B
|
||||
GP42 = GPIO42 // peripherals: PWM9 channel A
|
||||
GP43 = GPIO43 // peripherals: PWM9 channel B
|
||||
GP44 = GPIO44 // peripherals: PWM10 channel A
|
||||
GP45 = GPIO45 // peripherals: PWM10 channel B
|
||||
GP46 = GPIO46 // peripherals: PWM11 channel A
|
||||
GP47 = GPIO47 // peripherals: PWM11 channel B
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
var DefaultUART = UART0
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
//go:build stm32l0x1 && !nucleol031k6
|
||||
|
||||
// This file is for the bare STM32L0x1 (not for any specific board that is based
|
||||
// on the STM32L0x1).
|
||||
|
||||
package machine
|
||||
|
||||
const (
|
||||
I2C0_SCL_PIN = NoPin
|
||||
I2C0_SDA_PIN = NoPin
|
||||
|
||||
UART_TX_PIN = NoPin
|
||||
UART_RX_PIN = NoPin
|
||||
|
||||
SPI0_SDI_PIN = NoPin
|
||||
SPI0_SDO_PIN = NoPin
|
||||
SPI0_SCK_PIN = NoPin
|
||||
)
|
||||
@@ -279,7 +279,7 @@ var (
|
||||
// #===========#==========#===============#=================-=========#
|
||||
// | SPI1 | LPSPI4 | PLL2(132 MHz) | D12/D11/D13/D10 : 3/3/3/3 |
|
||||
// | SPI2 | LPSPI3 | PLL2(132 MHz) | D1/D26/D27/D0 : 7/2/2/7 |
|
||||
// | SPI3 | LPSPI1 | PLL2(132 MHz) | D34/D35/D37/D36 : 4/4/4/4 |
|
||||
// | SPI3 | LPSPI1 | PLL2(132 MHz) | D42/D43/D45/D44 : 4/4/4/4 |
|
||||
// #===========#==========#===============#=================-=========#
|
||||
const (
|
||||
SPI1_SDI_PIN = D12
|
||||
@@ -292,10 +292,52 @@ const (
|
||||
SPI2_SCK_PIN = D27
|
||||
SPI2_CS_PIN = D0
|
||||
|
||||
SPI3_SDI_PIN = D34
|
||||
SPI3_SDO_PIN = D35
|
||||
SPI3_SCK_PIN = D37
|
||||
SPI3_CS_PIN = D36
|
||||
SPI3_SDI_PIN = D42
|
||||
SPI3_SDO_PIN = D43
|
||||
SPI3_SCK_PIN = D45
|
||||
SPI3_CS_PIN = D44
|
||||
)
|
||||
|
||||
var (
|
||||
SPI0 = SPI1 // SPI0 is an alias of SPI1 (LPSPI4)
|
||||
SPI1 = &SPI{
|
||||
Bus: nxp.LPSPI4,
|
||||
muxSDI: muxSelect{ // D12 (PB1 [B0_01])
|
||||
mux: nxp.IOMUXC_LPSPI4_SDI_SELECT_INPUT_DAISY_GPIO_B0_01_ALT3,
|
||||
sel: &nxp.IOMUXC.LPSPI4_SDI_SELECT_INPUT,
|
||||
},
|
||||
muxSDO: muxSelect{ // D11 (PB2 [B0_02])
|
||||
mux: nxp.IOMUXC_LPSPI4_SDO_SELECT_INPUT_DAISY_GPIO_B0_02_ALT3,
|
||||
sel: &nxp.IOMUXC.LPSPI4_SDO_SELECT_INPUT,
|
||||
},
|
||||
muxSCK: muxSelect{ // D13 (PB3 [B0_03])
|
||||
mux: nxp.IOMUXC_LPSPI4_SCK_SELECT_INPUT_DAISY_GPIO_B0_03_ALT3,
|
||||
sel: &nxp.IOMUXC.LPSPI4_SCK_SELECT_INPUT,
|
||||
},
|
||||
muxCS: muxSelect{ // D10 (PB0 [B0_00])
|
||||
mux: nxp.IOMUXC_LPSPI4_PCS0_SELECT_INPUT_DAISY_GPIO_B0_00_ALT3,
|
||||
sel: &nxp.IOMUXC.LPSPI4_PCS0_SELECT_INPUT,
|
||||
},
|
||||
}
|
||||
SPI2 = &SPI{
|
||||
Bus: nxp.LPSPI3,
|
||||
muxSDI: muxSelect{ // D1 (PA2 [AD_B0_02])
|
||||
mux: nxp.IOMUXC_LPSPI3_SDI_SELECT_INPUT_DAISY_GPIO_AD_B0_02_ALT7,
|
||||
sel: &nxp.IOMUXC.LPSPI3_SDI_SELECT_INPUT,
|
||||
},
|
||||
muxSDO: muxSelect{ // D26 (PA30 [AD_B1_14])
|
||||
mux: nxp.IOMUXC_LPSPI3_SDO_SELECT_INPUT_DAISY_GPIO_AD_B1_14_ALT2,
|
||||
sel: &nxp.IOMUXC.LPSPI3_SDO_SELECT_INPUT,
|
||||
},
|
||||
muxSCK: muxSelect{ // D27 (PA31 [AD_B1_15])
|
||||
mux: nxp.IOMUXC_LPSPI3_SCK_SELECT_INPUT_DAISY_GPIO_AD_B1_15,
|
||||
sel: &nxp.IOMUXC.LPSPI3_SCK_SELECT_INPUT,
|
||||
},
|
||||
muxCS: muxSelect{ // D0 (PA3 [AD_B0_03])
|
||||
mux: nxp.IOMUXC_LPSPI3_PCS0_SELECT_INPUT_DAISY_GPIO_AD_B0_03_ALT7,
|
||||
sel: &nxp.IOMUXC.LPSPI3_PCS0_SELECT_INPUT,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// #====================================================#
|
||||
@@ -317,3 +359,49 @@ const (
|
||||
I2C3_SDA_PIN = D25
|
||||
I2C3_SCL_PIN = D24
|
||||
)
|
||||
|
||||
var (
|
||||
I2C0 = I2C1 // I2C0 is an alias for I2C1 (LPI2C1)
|
||||
I2C1 = &_I2C1
|
||||
_I2C1 = I2C{
|
||||
Bus: nxp.LPI2C1,
|
||||
sda: I2C1_SDA_PIN, // D18 (PA17 [AD_B1_01])
|
||||
scl: I2C1_SCL_PIN, // D19 (PA16 [AD_B1_00])
|
||||
muxSDA: muxSelect{
|
||||
mux: nxp.IOMUXC_LPI2C1_SDA_SELECT_INPUT_DAISY_GPIO_AD_B1_01_ALT3,
|
||||
sel: &nxp.IOMUXC.LPI2C1_SDA_SELECT_INPUT,
|
||||
},
|
||||
muxSCL: muxSelect{
|
||||
mux: nxp.IOMUXC_LPI2C1_SCL_SELECT_INPUT_DAISY_GPIO_AD_B1_00_ALT3,
|
||||
sel: &nxp.IOMUXC.LPI2C1_SCL_SELECT_INPUT,
|
||||
},
|
||||
}
|
||||
I2C2 = &_I2C2
|
||||
_I2C2 = I2C{
|
||||
Bus: nxp.LPI2C3,
|
||||
sda: I2C2_SDA_PIN, // D17 (PA22 [AD_B1_06])
|
||||
scl: I2C2_SCL_PIN, // D16 (PA23 [AD_B1_07])
|
||||
muxSDA: muxSelect{
|
||||
mux: nxp.IOMUXC_LPI2C3_SDA_SELECT_INPUT_DAISY_GPIO_AD_B1_06_ALT1,
|
||||
sel: &nxp.IOMUXC.LPI2C3_SDA_SELECT_INPUT,
|
||||
},
|
||||
muxSCL: muxSelect{
|
||||
mux: nxp.IOMUXC_LPI2C3_SCL_SELECT_INPUT_DAISY_GPIO_AD_B1_07_ALT1,
|
||||
sel: &nxp.IOMUXC.LPI2C3_SCL_SELECT_INPUT,
|
||||
},
|
||||
}
|
||||
I2C3 = &_I2C3
|
||||
_I2C3 = I2C{
|
||||
Bus: nxp.LPI2C4,
|
||||
sda: I2C3_SDA_PIN, // D25 (PA13 [AD_B0_13])
|
||||
scl: I2C3_SCL_PIN, // D24 (PA12 [AD_B0_12])
|
||||
muxSDA: muxSelect{
|
||||
mux: nxp.IOMUXC_LPI2C4_SDA_SELECT_INPUT_DAISY_GPIO_AD_B0_13_ALT0,
|
||||
sel: &nxp.IOMUXC.LPI2C4_SDA_SELECT_INPUT,
|
||||
},
|
||||
muxSCL: muxSelect{
|
||||
mux: nxp.IOMUXC_LPI2C4_SCL_SELECT_INPUT_DAISY_GPIO_AD_B0_12_ALT0,
|
||||
sel: &nxp.IOMUXC.LPI2C4_SCL_SELECT_INPUT,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
+14
-11
@@ -26,17 +26,20 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
errI2CWriteTimeout = errors.New("I2C timeout during write")
|
||||
errI2CReadTimeout = errors.New("I2C timeout during read")
|
||||
errI2CBusReadyTimeout = errors.New("I2C timeout on bus ready")
|
||||
errI2CSignalStartTimeout = errors.New("I2C timeout on signal start")
|
||||
errI2CSignalReadTimeout = errors.New("I2C timeout on signal read")
|
||||
errI2CSignalStopTimeout = errors.New("I2C timeout on signal stop")
|
||||
errI2CAckExpected = errors.New("I2C error: expected ACK not NACK")
|
||||
errI2CBusError = errors.New("I2C bus error")
|
||||
errI2COverflow = errors.New("I2C receive buffer overflow")
|
||||
errI2COverread = errors.New("I2C transmit buffer overflow")
|
||||
errI2CNotImplemented = errors.New("I2C operation not yet implemented")
|
||||
errI2CWriteTimeout = errors.New("i2c: timeout during write")
|
||||
errI2CReadTimeout = errors.New("i2c: timeout during read")
|
||||
errI2CBusReadyTimeout = errors.New("i2c: timeout on bus ready")
|
||||
errI2CSignalStartTimeout = errors.New("i2c: timeout on signal start")
|
||||
errI2CSignalReadTimeout = errors.New("i2c: timeout on signal read")
|
||||
errI2CSignalStopTimeout = errors.New("i2c: timeout on signal stop")
|
||||
errI2CAckExpected = errors.New("i2c: error: expected ACK not NACK")
|
||||
errI2CBusError = errors.New("i2c: bus error")
|
||||
errI2COverflow = errors.New("i2c: receive buffer overflow")
|
||||
errI2COverread = errors.New("i2c: transmit buffer overflow")
|
||||
errI2CNotImplemented = errors.New("i2c: operation not yet implemented")
|
||||
errI2CNoDevices = errors.New("i2c: bus has no devices") // simulator only
|
||||
errI2CMultipleDevices = errors.New("i2c: bus has address conflict") // simulator only
|
||||
errI2CWrongAddress = errors.New("i2c: bus has devices but none with this address") // simulator only
|
||||
)
|
||||
|
||||
// I2CTargetEvent reflects events on the I2C bus
|
||||
|
||||
@@ -49,8 +49,8 @@ const (
|
||||
PC5 = portC + 5
|
||||
PC6 = portC + 6
|
||||
PC7 = portC + 7
|
||||
PD0 = portD + 0
|
||||
PD1 = portD + 1
|
||||
PD0 = portD + 0 // peripherals: I2C0 SCL
|
||||
PD1 = portD + 1 // peripherals: I2C0 SDA
|
||||
PD2 = portD + 2
|
||||
PD3 = portD + 3
|
||||
PD7 = portD + 7
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
//go:build !baremetal && (arduino || arduino_nano)
|
||||
|
||||
package machine
|
||||
|
||||
var I2C0 = &I2C{Bus: 0, PinsSDA: []Pin{PC4}, PinsSCL: []Pin{PC5}}
|
||||
@@ -43,3 +43,21 @@ var TCC2 = &timerType{
|
||||
{PA01, PA13, PA17}, // channel 1
|
||||
},
|
||||
}
|
||||
|
||||
var (
|
||||
// According to the datasheet, only some pins have I2C support. However it
|
||||
// looks like many boards just use any SERCOM I2C instance, even if the
|
||||
// datasheet says those don't support I2C. I guess they do work in practice,
|
||||
// then.
|
||||
// These are:
|
||||
// * PA00/PA01 for the Adafruit Circuit Playground Express (I2C1, SERCOM1).
|
||||
// * PB02/PB03 for the Adafruit Circuit Playground Express (I2C0, SERCOM5).
|
||||
// * PB08/PB09 for the Arduino Nano 33 IoT (I2C0, SERCOM4).
|
||||
// https://cdn.sparkfun.com/datasheets/Dev/Arduino/Boards/Atmel-42181-SAM-D21_Datasheet.pdf
|
||||
sercomI2CM0 = &I2C{Bus: 0, PinsSDA: []Pin{PA08}, PinsSCL: []Pin{PA09}}
|
||||
sercomI2CM1 = &I2C{Bus: 1, PinsSDA: []Pin{PA00, PA16}, PinsSCL: []Pin{PA01, PA17}}
|
||||
sercomI2CM2 = &I2C{Bus: 2, PinsSDA: []Pin{PA08, PA12}, PinsSCL: []Pin{PA09, PA13}}
|
||||
sercomI2CM3 = &I2C{Bus: 3, PinsSDA: []Pin{PA16, PA22}, PinsSCL: []Pin{PA17, PA23}}
|
||||
sercomI2CM4 = &I2C{Bus: 4, PinsSDA: []Pin{PA12, PB08, PB12}, PinsSCL: []Pin{PA13, PB09, PB13}}
|
||||
sercomI2CM5 = &I2C{Bus: 5, PinsSDA: []Pin{PA22, PB02, PB16, PB30}, PinsSCL: []Pin{PA23, PB03, PB17, PB31}}
|
||||
)
|
||||
|
||||
@@ -81,22 +81,22 @@ const (
|
||||
PA05 Pin = 5
|
||||
PA06 Pin = 6
|
||||
PA07 Pin = 7
|
||||
PA08 Pin = 8 // peripherals: TCC0 channel 0, TCC1 channel 4
|
||||
PA09 Pin = 9 // peripherals: TCC0 channel 1, TCC1 channel 5
|
||||
PA08 Pin = 8 // peripherals: TCC0 channel 0, TCC1 channel 4, sercomI2CM0 SDA, sercomI2CM2 SDA
|
||||
PA09 Pin = 9 // peripherals: TCC0 channel 1, TCC1 channel 5, sercomI2CM0 SCL, sercomI2CM2 SCL
|
||||
PA10 Pin = 10 // peripherals: TCC0 channel 2, TCC1 channel 6
|
||||
PA11 Pin = 11 // peripherals: TCC0 channel 3, TCC1 channel 7
|
||||
PA12 Pin = 12 // peripherals: TCC0 channel 6, TCC1 channel 2
|
||||
PA13 Pin = 13 // peripherals: TCC0 channel 7, TCC1 channel 3
|
||||
PA12 Pin = 12 // peripherals: TCC0 channel 6, TCC1 channel 2, sercomI2CM2 SDA, sercomI2CM4 SDA
|
||||
PA13 Pin = 13 // peripherals: TCC0 channel 7, TCC1 channel 3, sercomI2CM2 SCL, sercomI2CM4 SCL
|
||||
PA14 Pin = 14 // peripherals: TCC2 channel 0, TCC1 channel 2
|
||||
PA15 Pin = 15 // peripherals: TCC2 channel 1, TCC1 channel 3
|
||||
PA16 Pin = 16 // peripherals: TCC1 channel 0, TCC0 channel 4
|
||||
PA17 Pin = 17 // peripherals: TCC1 channel 1, TCC0 channel 5
|
||||
PA16 Pin = 16 // peripherals: TCC1 channel 0, TCC0 channel 4, sercomI2CM1 SDA, sercomI2CM3 SDA
|
||||
PA17 Pin = 17 // peripherals: TCC1 channel 1, TCC0 channel 5, sercomI2CM1 SCL, sercomI2CM3 SCL
|
||||
PA18 Pin = 18 // peripherals: TCC1 channel 2, TCC0 channel 6
|
||||
PA19 Pin = 19 // peripherals: TCC1 channel 3, TCC0 channel 7
|
||||
PA20 Pin = 20 // peripherals: TCC1 channel 4, TCC0 channel 0
|
||||
PA21 Pin = 21 // peripherals: TCC1 channel 5, TCC0 channel 1
|
||||
PA22 Pin = 22 // peripherals: TCC1 channel 6, TCC0 channel 2
|
||||
PA23 Pin = 23 // peripherals: TCC1 channel 7, TCC0 channel 3
|
||||
PA22 Pin = 22 // peripherals: TCC1 channel 6, TCC0 channel 2, sercomI2CM3 SDA, sercomI2CM5 SDA
|
||||
PA23 Pin = 23 // peripherals: TCC1 channel 7, TCC0 channel 3, sercomI2CM3 SCL, sercomI2CM5 SCL
|
||||
PA24 Pin = 24 // peripherals: TCC2 channel 2
|
||||
PA25 Pin = 25 // peripherals: TCC2 channel 3
|
||||
PA26 Pin = 26
|
||||
@@ -177,8 +177,8 @@ const (
|
||||
PD05 Pin = 101
|
||||
PD06 Pin = 102
|
||||
PD07 Pin = 103
|
||||
PD08 Pin = 104 // peripherals: TCC0 channel 1
|
||||
PD09 Pin = 105 // peripherals: TCC0 channel 2
|
||||
PD08 Pin = 104 // peripherals: TCC0 channel 1, sercomI2CM6 SDA, sercomI2CM7 SDA
|
||||
PD09 Pin = 105 // peripherals: TCC0 channel 2, sercomI2CM6 SCL, sercomI2CM7 SCL
|
||||
PD10 Pin = 106 // peripherals: TCC0 channel 3
|
||||
PD11 Pin = 107 // peripherals: TCC0 channel 4
|
||||
PD12 Pin = 108 // peripherals: TCC0 channel 5
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
//go:build !baremetal && hifive1b
|
||||
|
||||
package machine
|
||||
|
||||
var I2C0 = &I2C{Bus: 0, PinsSDA: []Pin{P12}, PinsSCL: []Pin{P13}}
|
||||
@@ -4,6 +4,8 @@ package machine
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// Dummy machine package that calls out to external functions.
|
||||
@@ -227,7 +229,9 @@ func adcRead(pin Pin) uint16
|
||||
|
||||
// I2C is a generic implementation of the Inter-IC communication protocol.
|
||||
type I2C struct {
|
||||
Bus uint8
|
||||
Bus uint8
|
||||
PinsSCL []Pin
|
||||
PinsSDA []Pin
|
||||
}
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
@@ -239,7 +243,21 @@ type I2CConfig struct {
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
i2cConfigure(i2c.Bus, config.SCL, config.SDA)
|
||||
if i2c.PinsSCL != nil {
|
||||
matchSCL := slices.Index(i2c.PinsSCL, config.SCL) >= 0
|
||||
matchSDA := slices.Index(i2c.PinsSDA, config.SDA) >= 0
|
||||
if !matchSCL && !matchSDA {
|
||||
return errors.New("i2c: SCL and SDA pins are incorrect for this I2C instance")
|
||||
} else if !matchSCL {
|
||||
return errors.New("i2c: SCL pin is incorrect for this I2C instance")
|
||||
} else if !matchSDA {
|
||||
return errors.New("i2c: SDA pin is incorrect for this I2C instance")
|
||||
}
|
||||
}
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = 100 * KHz
|
||||
}
|
||||
i2cConfigure(i2c.Bus, config.SCL, config.SDA, config.Frequency)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -261,19 +279,29 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||
rptr = &r[0]
|
||||
rlen = len(r)
|
||||
}
|
||||
i2cTransfer(i2c.Bus, wptr, wlen, rptr, rlen)
|
||||
// TODO: do something with the returned error code.
|
||||
return nil
|
||||
errCode := i2cTransfer(i2c.Bus, addr, wptr, wlen, rptr, rlen)
|
||||
switch errCode {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return errI2CNoDevices
|
||||
case 2:
|
||||
return errI2CMultipleDevices
|
||||
case 3:
|
||||
return errI2CWrongAddress
|
||||
default:
|
||||
return errI2CBusError // unknown error code
|
||||
}
|
||||
}
|
||||
|
||||
//export __tinygo_i2c_configure
|
||||
func i2cConfigure(bus uint8, scl Pin, sda Pin)
|
||||
func i2cConfigure(bus uint8, scl Pin, sda Pin, frequency uint32)
|
||||
|
||||
//export __tinygo_i2c_set_baud_rate
|
||||
func i2cSetBaudRate(bus uint8, br uint32)
|
||||
|
||||
//export __tinygo_i2c_transfer
|
||||
func i2cTransfer(bus uint8, w *byte, wlen int, r *byte, rlen int) int
|
||||
func i2cTransfer(bus uint8, addr uint16, w *byte, wlen int, r *byte, rlen int) int
|
||||
|
||||
type UART struct {
|
||||
Bus uint8
|
||||
@@ -336,15 +364,6 @@ var (
|
||||
sercomUSART4 = UART{4}
|
||||
sercomUSART5 = UART{5}
|
||||
|
||||
sercomI2CM0 = &I2C{0}
|
||||
sercomI2CM1 = &I2C{1}
|
||||
sercomI2CM2 = &I2C{2}
|
||||
sercomI2CM3 = &I2C{3}
|
||||
sercomI2CM4 = &I2C{4}
|
||||
sercomI2CM5 = &I2C{5}
|
||||
sercomI2CM6 = &I2C{6}
|
||||
sercomI2CM7 = &I2C{7}
|
||||
|
||||
sercomSPIM0 = &SPI{0}
|
||||
sercomSPIM1 = &SPI{1}
|
||||
sercomSPIM2 = &SPI{2}
|
||||
|
||||
@@ -10,5 +10,4 @@ var (
|
||||
UART1 = hardwareUART1
|
||||
SPI0 = &SPI{0}
|
||||
SPI1 = &SPI{1}
|
||||
I2C0 = &I2C{0}
|
||||
)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
//go:build !baremetal && (microbit || pca10031 || hw_651)
|
||||
|
||||
package machine
|
||||
|
||||
var I2C0 = &I2C{Bus: 0}
|
||||
var I2C1 = &I2C{Bus: 1}
|
||||
@@ -58,3 +58,6 @@ var PWM3 = &timerType{
|
||||
nil, // channel 3
|
||||
},
|
||||
}
|
||||
|
||||
var I2C0 = &I2C{Bus: 0}
|
||||
var I2C1 = &I2C{Bus: 1}
|
||||
|
||||
@@ -94,3 +94,15 @@ var PWM7 = &timerType{
|
||||
{GPIO15}, // channel B (1)
|
||||
},
|
||||
}
|
||||
|
||||
var I2C0 = &I2C{
|
||||
Bus: 0,
|
||||
PinsSCL: []Pin{GPIO1, GPIO5, GPIO9, GPIO13, GPIO17, GPIO21},
|
||||
PinsSDA: []Pin{GPIO0, GPIO4, GPIO8, GPIO12, GPIO16, GPIO20},
|
||||
}
|
||||
|
||||
var I2C1 = &I2C{
|
||||
Bus: 0,
|
||||
PinsSCL: []Pin{GPIO3, GPIO7, GPIO11, GPIO15, GPIO19, GPIO27},
|
||||
PinsSDA: []Pin{GPIO2, GPIO6, GPIO10, GPIO14, GPIO18, GPIO26},
|
||||
}
|
||||
|
||||
@@ -150,6 +150,8 @@ func (p Pin) Configure(config PinConfig) {
|
||||
case PinAnalog:
|
||||
p.setFunc(fnNULL)
|
||||
p.pulloff()
|
||||
// Disable digital input.
|
||||
p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_IE)
|
||||
case PinUART:
|
||||
p.setFunc(fnUART)
|
||||
case PinPWM:
|
||||
|
||||
@@ -5,24 +5,24 @@ package machine
|
||||
// RP2350B has additional pins.
|
||||
|
||||
const (
|
||||
GPIO30 Pin = 30 // peripherals: PWM7 channel A
|
||||
GPIO31 Pin = 31 // peripherals: PWM7 channel B
|
||||
GPIO32 Pin = 32 // peripherals: PWM8 channel A
|
||||
GPIO33 Pin = 33 // peripherals: PWM8 channel B
|
||||
GPIO34 Pin = 34 // peripherals: PWM9 channel A
|
||||
GPIO35 Pin = 35 // peripherals: PWM9 channel B
|
||||
GPIO36 Pin = 36 // peripherals: PWM10 channel A
|
||||
GPIO37 Pin = 37 // peripherals: PWM10 channel B
|
||||
GPIO38 Pin = 38 // peripherals: PWM11 channel A
|
||||
GPIO39 Pin = 39 // peripherals: PWM11 channel B
|
||||
GPIO40 Pin = 40 // peripherals: PWM8 channel A
|
||||
GPIO41 Pin = 41 // peripherals: PWM8 channel B
|
||||
GPIO42 Pin = 42 // peripherals: PWM9 channel A
|
||||
GPIO43 Pin = 43 // peripherals: PWM9 channel B
|
||||
GPIO44 Pin = 44 // peripherals: PWM10 channel A
|
||||
GPIO45 Pin = 45 // peripherals: PWM10 channel B
|
||||
GPIO46 Pin = 46 // peripherals: PWM11 channel A
|
||||
GPIO47 Pin = 47 // peripherals: PWM11 channel B
|
||||
GPIO30 Pin = 30 // peripherals: PWM7 channel A, I2C1 SDA
|
||||
GPIO31 Pin = 31 // peripherals: PWM7 channel B, I2C1 SCL
|
||||
GPIO32 Pin = 32 // peripherals: PWM8 channel A, I2C0 SDA
|
||||
GPIO33 Pin = 33 // peripherals: PWM8 channel B, I2C0 SCL
|
||||
GPIO34 Pin = 34 // peripherals: PWM9 channel A, I2C1 SDA
|
||||
GPIO35 Pin = 35 // peripherals: PWM9 channel B, I2C1 SCL
|
||||
GPIO36 Pin = 36 // peripherals: PWM10 channel A, I2C0 SDA
|
||||
GPIO37 Pin = 37 // peripherals: PWM10 channel B, I2C0 SCL
|
||||
GPIO38 Pin = 38 // peripherals: PWM11 channel A, I2C1 SDA
|
||||
GPIO39 Pin = 39 // peripherals: PWM11 channel B, I2C1 SCL
|
||||
GPIO40 Pin = 40 // peripherals: PWM8 channel A, I2C0 SDA
|
||||
GPIO41 Pin = 41 // peripherals: PWM8 channel B, I2C0 SCL
|
||||
GPIO42 Pin = 42 // peripherals: PWM9 channel A, I2C1 SDA
|
||||
GPIO43 Pin = 43 // peripherals: PWM9 channel B, I2C1 SCL
|
||||
GPIO44 Pin = 44 // peripherals: PWM10 channel A, I2C0 SDA
|
||||
GPIO45 Pin = 45 // peripherals: PWM10 channel B, I2C0 SCL
|
||||
GPIO46 Pin = 46 // peripherals: PWM11 channel A, I2C1 SDA
|
||||
GPIO47 Pin = 47 // peripherals: PWM11 channel B, I2C1 SCL
|
||||
)
|
||||
|
||||
// Analog pins on 2350b.
|
||||
|
||||
@@ -73,14 +73,15 @@ func (c ADCChannel) Configure(config ADCConfig) error {
|
||||
func (c ADCChannel) getOnce() uint16 {
|
||||
// Make it safe to sample multiple ADC channels in separate go routines.
|
||||
adcLock.Lock()
|
||||
rp.ADC.CS.ReplaceBits(uint32(c), 0b111, rp.ADC_CS_AINSEL_Pos)
|
||||
rp.ADC.CS.ReplaceBits(uint32(c)<<rp.ADC_CS_AINSEL_Pos, rp.ADC_CS_AINSEL_Msk, 0)
|
||||
rp.ADC.CS.SetBits(rp.ADC_CS_START_ONCE)
|
||||
|
||||
waitForReady()
|
||||
v := rp.ADC.RESULT.Get()
|
||||
adcLock.Unlock()
|
||||
|
||||
// rp2040 is a 12-bit ADC, scale raw reading to 16-bits.
|
||||
return uint16(rp.ADC.RESULT.Get()) << 4
|
||||
return uint16(v) << 4
|
||||
}
|
||||
|
||||
// getVoltage does a one-shot sample and returns a millivolts reading.
|
||||
|
||||
@@ -4,34 +4,34 @@ package machine
|
||||
|
||||
const (
|
||||
// GPIO pins
|
||||
GPIO0 Pin = 0 // peripherals: PWM0 channel A
|
||||
GPIO1 Pin = 1 // peripherals: PWM0 channel B
|
||||
GPIO2 Pin = 2 // peripherals: PWM1 channel A
|
||||
GPIO3 Pin = 3 // peripherals: PWM1 channel B
|
||||
GPIO4 Pin = 4 // peripherals: PWM2 channel A
|
||||
GPIO5 Pin = 5 // peripherals: PWM2 channel B
|
||||
GPIO6 Pin = 6 // peripherals: PWM3 channel A
|
||||
GPIO7 Pin = 7 // peripherals: PWM3 channel B
|
||||
GPIO8 Pin = 8 // peripherals: PWM4 channel A
|
||||
GPIO9 Pin = 9 // peripherals: PWM4 channel B
|
||||
GPIO10 Pin = 10 // peripherals: PWM5 channel A
|
||||
GPIO11 Pin = 11 // peripherals: PWM5 channel B
|
||||
GPIO12 Pin = 12 // peripherals: PWM6 channel A
|
||||
GPIO13 Pin = 13 // peripherals: PWM6 channel B
|
||||
GPIO14 Pin = 14 // peripherals: PWM7 channel A
|
||||
GPIO15 Pin = 15 // peripherals: PWM7 channel B
|
||||
GPIO16 Pin = 16 // peripherals: PWM0 channel A
|
||||
GPIO17 Pin = 17 // peripherals: PWM0 channel B
|
||||
GPIO18 Pin = 18 // peripherals: PWM1 channel A
|
||||
GPIO19 Pin = 19 // peripherals: PWM1 channel B
|
||||
GPIO20 Pin = 20 // peripherals: PWM2 channel A
|
||||
GPIO21 Pin = 21 // peripherals: PWM2 channel B
|
||||
GPIO0 Pin = 0 // peripherals: PWM0 channel A, I2C0 SDA
|
||||
GPIO1 Pin = 1 // peripherals: PWM0 channel B, I2C0 SCL
|
||||
GPIO2 Pin = 2 // peripherals: PWM1 channel A, I2C1 SDA
|
||||
GPIO3 Pin = 3 // peripherals: PWM1 channel B, I2C1 SCL
|
||||
GPIO4 Pin = 4 // peripherals: PWM2 channel A, I2C0 SDA
|
||||
GPIO5 Pin = 5 // peripherals: PWM2 channel B, I2C0 SCL
|
||||
GPIO6 Pin = 6 // peripherals: PWM3 channel A, I2C1 SDA
|
||||
GPIO7 Pin = 7 // peripherals: PWM3 channel B, I2C1 SCL
|
||||
GPIO8 Pin = 8 // peripherals: PWM4 channel A, I2C0 SDA
|
||||
GPIO9 Pin = 9 // peripherals: PWM4 channel B, I2C0 SCL
|
||||
GPIO10 Pin = 10 // peripherals: PWM5 channel A, I2C1 SDA
|
||||
GPIO11 Pin = 11 // peripherals: PWM5 channel B, I2C1 SCL
|
||||
GPIO12 Pin = 12 // peripherals: PWM6 channel A, I2C0 SDA
|
||||
GPIO13 Pin = 13 // peripherals: PWM6 channel B, I2C0 SCL
|
||||
GPIO14 Pin = 14 // peripherals: PWM7 channel A, I2C1 SDA
|
||||
GPIO15 Pin = 15 // peripherals: PWM7 channel B, I2C1 SCL
|
||||
GPIO16 Pin = 16 // peripherals: PWM0 channel A, I2C0 SDA
|
||||
GPIO17 Pin = 17 // peripherals: PWM0 channel B, I2C0 SCL
|
||||
GPIO18 Pin = 18 // peripherals: PWM1 channel A, I2C1 SDA
|
||||
GPIO19 Pin = 19 // peripherals: PWM1 channel B, I2C1 SCL
|
||||
GPIO20 Pin = 20 // peripherals: PWM2 channel A, I2C0 SDA
|
||||
GPIO21 Pin = 21 // peripherals: PWM2 channel B, I2C0 SCL
|
||||
GPIO22 Pin = 22 // peripherals: PWM3 channel A
|
||||
GPIO23 Pin = 23 // peripherals: PWM3 channel B
|
||||
GPIO24 Pin = 24 // peripherals: PWM4 channel A
|
||||
GPIO25 Pin = 25 // peripherals: PWM4 channel B
|
||||
GPIO26 Pin = 26 // peripherals: PWM5 channel A
|
||||
GPIO27 Pin = 27 // peripherals: PWM5 channel B
|
||||
GPIO26 Pin = 26 // peripherals: PWM5 channel A, I2C1 SDA
|
||||
GPIO27 Pin = 27 // peripherals: PWM5 channel B, I2C1 SCL
|
||||
GPIO28 Pin = 28 // peripherals: PWM6 channel A
|
||||
GPIO29 Pin = 29 // peripherals: PWM6 channel B
|
||||
)
|
||||
|
||||
@@ -209,6 +209,21 @@ const (
|
||||
KeyF23 Keycode = 114 | 0xF000
|
||||
KeyF24 Keycode = 115 | 0xF000
|
||||
|
||||
// International keys for Japanese and other language keyboards
|
||||
KeyInternational1 Keycode = 0x87 | 0xF000 // JIS "\" and "_"
|
||||
KeyInternational2 Keycode = 0x88 | 0xF000 // JIS Katakana/Hiragana
|
||||
KeyInternational3 Keycode = 0x89 | 0xF000 // JIS "¥" and "|"
|
||||
KeyInternational4 Keycode = 0x8A | 0xF000 // JIS Henkan
|
||||
KeyInternational5 Keycode = 0x8B | 0xF000 // JIS Muhenkan
|
||||
KeyInternational6 Keycode = 0x8C | 0xF000 // JIS Numpad ","
|
||||
|
||||
// Language keys for input method switching
|
||||
KeyLanguage1 Keycode = 0x90 | 0xF000 // Hangul/English
|
||||
KeyLanguage2 Keycode = 0x91 | 0xF000 // Hanja
|
||||
KeyLanguage3 Keycode = 0x92 | 0xF000 // JIS Katakana
|
||||
KeyLanguage4 Keycode = 0x93 | 0xF000 // JIS Hiragana
|
||||
KeyLanguage5 Keycode = 0x94 | 0xF000 // JIS Zenkaku/Hankaku
|
||||
|
||||
KeyUpArrow Keycode = KeyUp
|
||||
KeyDownArrow Keycode = KeyDown
|
||||
KeyLeftArrow Keycode = KeyLeft
|
||||
@@ -222,6 +237,34 @@ const (
|
||||
KeyRightShift Keycode = KeyModifierRightShift
|
||||
KeyRightAlt Keycode = KeyModifierRightAlt
|
||||
KeyRightGUI Keycode = KeyModifierRightGUI
|
||||
|
||||
// QMK compatibility aliases for international keys
|
||||
KeyInt1 Keycode = KeyInternational1
|
||||
KeyInt2 Keycode = KeyInternational2
|
||||
KeyInt3 Keycode = KeyInternational3
|
||||
KeyInt4 Keycode = KeyInternational4
|
||||
KeyInt5 Keycode = KeyInternational5
|
||||
KeyInt6 Keycode = KeyInternational6
|
||||
|
||||
// QMK compatibility aliases for language keys
|
||||
KeyLng1 Keycode = KeyLanguage1
|
||||
KeyLng2 Keycode = KeyLanguage2
|
||||
KeyLng3 Keycode = KeyLanguage3
|
||||
KeyLng4 Keycode = KeyLanguage4
|
||||
KeyLng5 Keycode = KeyLanguage5
|
||||
|
||||
// Common keyboard layout aliases
|
||||
KeyRo Keycode = KeyInternational1 // Japanese "ろ"
|
||||
KeyKatakanaHiragana Keycode = KeyInternational2 // Japanese Katakana/Hiragana
|
||||
KeyYen Keycode = KeyInternational3 // Japanese "¥"
|
||||
KeyHenkan Keycode = KeyInternational4 // Japanese Henkan
|
||||
KeyMuhenkan Keycode = KeyInternational5 // Japanese Muhenkan
|
||||
KeyKpJpComma Keycode = KeyInternational6 // Japanese Numpad ","
|
||||
KeyHangeul Keycode = KeyLanguage1 // Korean Hangul/English
|
||||
KeyHanja Keycode = KeyLanguage2 // Korean Hanja
|
||||
KeyKatakana Keycode = KeyLanguage3 // Japanese Katakana
|
||||
KeyHiragana Keycode = KeyLanguage4 // Japanese Hiragana
|
||||
KeyZenkakuHankaku Keycode = KeyLanguage5 // Japanese Zenkaku/Hankaku
|
||||
)
|
||||
|
||||
// Keycodes for layout US English (0x0904)
|
||||
|
||||
+1
-1
Submodule src/net updated: 77be3968d1...983d88dd7a
@@ -127,6 +127,11 @@ type Method struct {
|
||||
Index int // index for Type.Method
|
||||
}
|
||||
|
||||
// IsExported reports whether the method is exported.
|
||||
func (m Method) IsExported() bool {
|
||||
return m.PkgPath == ""
|
||||
}
|
||||
|
||||
// The following Type type has been copied almost entirely from
|
||||
// https://github.com/golang/go/blob/go1.15/src/reflect/type.go#L27-L212.
|
||||
// Some methods have been commented out as they haven't yet been implemented.
|
||||
|
||||
@@ -89,7 +89,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
|
||||
gcLock.Lock()
|
||||
needsResumeWorld = false
|
||||
var ptr unsafe.Pointer
|
||||
if layout == gclayout.NoPtrs {
|
||||
if layout == gclayout.NoPtrs.AsPtr() {
|
||||
// This object is entirely pointer free, for example make([]int, ...).
|
||||
// Make sure the GC knows this so it doesn't scan the object
|
||||
// unnecessarily to improve performance.
|
||||
|
||||
@@ -106,6 +106,33 @@ func UnlockOSThread() {
|
||||
// point of the call.
|
||||
func KeepAlive(x interface{})
|
||||
|
||||
// AddCleanup is a dummy cleanup implementation. It doesn't do any cleaning up.
|
||||
//
|
||||
// We base this on the following loophole in the official runtime.AddCleanup
|
||||
// documentation:
|
||||
//
|
||||
// > The cleanup(arg) call is not always guaranteed to run; in particular it is
|
||||
// > not guaranteed to run before program exit.
|
||||
//
|
||||
// So it's technically correct (the best kind of correct) to not run any
|
||||
// cleanups. But of course, this can lead to resource leaks so cleanups may need
|
||||
// to be implemented eventually.
|
||||
func AddCleanup[T, S any](ptr *T, cleanup func(S), arg S) Cleanup {
|
||||
return Cleanup{}
|
||||
}
|
||||
|
||||
type Cleanup struct{}
|
||||
|
||||
func (c Cleanup) Stop() {}
|
||||
|
||||
//go:linkname registerWeakPointer weak.runtime_registerWeakPointer
|
||||
func registerWeakPointer(ptr unsafe.Pointer) unsafe.Pointer {
|
||||
// TODO: unimplemented.
|
||||
// I hope not implementing this won't break anything, like packages that
|
||||
// expect weak pointers to be GC'd before they actually are.
|
||||
return ptr
|
||||
}
|
||||
|
||||
var godebugUpdate func(string, string)
|
||||
|
||||
//go:linkname godebug_setUpdate internal/godebug.setUpdate
|
||||
|
||||
@@ -0,0 +1,367 @@
|
||||
//go:build rp2040 || rp2350
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/rp"
|
||||
"internal/task"
|
||||
"machine"
|
||||
"machine/usb/cdc"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const numCPU = 2
|
||||
|
||||
// machineTicks is provided by package machine.
|
||||
func machineTicks() uint64
|
||||
|
||||
// machineLightSleep is provided by package machine.
|
||||
func machineLightSleep(uint64)
|
||||
|
||||
// ticks returns the number of ticks (microseconds) elapsed since power up.
|
||||
func ticks() timeUnit {
|
||||
t := machineTicks()
|
||||
return timeUnit(t)
|
||||
}
|
||||
|
||||
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||
return int64(ticks) * 1000
|
||||
}
|
||||
|
||||
func nanosecondsToTicks(ns int64) timeUnit {
|
||||
return timeUnit(ns / 1000)
|
||||
}
|
||||
|
||||
func sleepTicks(d timeUnit) {
|
||||
if hasScheduler {
|
||||
// With scheduler, sleepTicks may return early if an interrupt or
|
||||
// event fires - so scheduler can schedule any go routines now
|
||||
// eligible to run
|
||||
machineLightSleep(uint64(d))
|
||||
return
|
||||
}
|
||||
|
||||
// Busy loop
|
||||
sleepUntil := ticks() + d
|
||||
for ticks() < sleepUntil {
|
||||
}
|
||||
}
|
||||
|
||||
// Currently sleeping core, or 0xff.
|
||||
// Must only be accessed with the scheduler lock held.
|
||||
var sleepingCore uint8 = 0xff
|
||||
|
||||
// Return whether another core is sleeping.
|
||||
// May only be called with the scheduler lock held.
|
||||
func hasSleepingCore() bool {
|
||||
return sleepingCore != 0xff
|
||||
}
|
||||
|
||||
// Almost identical to sleepTicks, except that it will unlock/lock the scheduler
|
||||
// while sleeping and is interruptible by interruptSleepTicksMulticore.
|
||||
// This may only be called with the scheduler lock held.
|
||||
func sleepTicksMulticore(d timeUnit) {
|
||||
sleepingCore = uint8(currentCPU())
|
||||
|
||||
// Note: interruptSleepTicksMulticore will be able to interrupt this, since
|
||||
// it executes the "sev" instruction which would make sleepTicks return
|
||||
// immediately without sleeping. Even if it happens while configuring the
|
||||
// sleep operation.
|
||||
|
||||
schedulerLock.Unlock()
|
||||
sleepTicks(d)
|
||||
schedulerLock.Lock()
|
||||
|
||||
sleepingCore = 0xff
|
||||
}
|
||||
|
||||
// Interrupt an ongoing call to sleepTicksMulticore on another core.
|
||||
func interruptSleepTicksMulticore(wakeup timeUnit) {
|
||||
arm.Asm("sev")
|
||||
}
|
||||
|
||||
// Number of cores that are currently in schedulerUnlockAndWait.
|
||||
// It is possible for both cores to be sleeping, if the program is waiting for
|
||||
// an interrupt (or is deadlocked).
|
||||
var waitingCore uint8
|
||||
|
||||
// Put the scheduler to sleep, since there are no tasks to run.
|
||||
// This will unlock the scheduler lock, and must be called with the scheduler
|
||||
// lock held.
|
||||
func schedulerUnlockAndWait() {
|
||||
waitingCore++
|
||||
schedulerLock.Unlock()
|
||||
arm.Asm("wfe")
|
||||
schedulerLock.Lock()
|
||||
waitingCore--
|
||||
}
|
||||
|
||||
// Wake another core, if one is sleeping. Must be called with the scheduler lock
|
||||
// held.
|
||||
func schedulerWake() {
|
||||
if waitingCore != 0 {
|
||||
arm.Asm("sev")
|
||||
}
|
||||
}
|
||||
|
||||
// Return the current core number: 0 or 1.
|
||||
func currentCPU() uint32 {
|
||||
return rp.SIO.CPUID.Get()
|
||||
}
|
||||
|
||||
// Start the secondary cores for this chip.
|
||||
// On the RP2040/RP2350, there is only one other core to start.
|
||||
func startSecondaryCores() {
|
||||
// Start the second core of the RP2040/RP2350.
|
||||
// See sections 2.8.2 and 5.3 in the datasheets for RP2040 and RP2350 respectively.
|
||||
seq := 0
|
||||
for {
|
||||
cmd := core1StartSequence[seq]
|
||||
if cmd == 0 {
|
||||
multicore_fifo_drain()
|
||||
arm.Asm("sev")
|
||||
}
|
||||
multicore_fifo_push_blocking(cmd)
|
||||
response := multicore_fifo_pop_blocking()
|
||||
if cmd != response {
|
||||
seq = 0
|
||||
continue
|
||||
}
|
||||
seq = seq + 1
|
||||
if seq >= len(core1StartSequence) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Enable the FIFO interrupt for the GC stop the world phase.
|
||||
// We can only do this after we don't need the FIFO anymore for starting the
|
||||
// second core.
|
||||
intr := interrupt.New(sioIrqFifoProc0, func(intr interrupt.Interrupt) {
|
||||
switch rp.SIO.FIFO_RD.Get() {
|
||||
case 1:
|
||||
gcInterruptHandler(0)
|
||||
}
|
||||
})
|
||||
intr.Enable()
|
||||
intr.SetPriority(0xff)
|
||||
}
|
||||
|
||||
var core1StartSequence = [...]uint32{
|
||||
0, 0, 1,
|
||||
uint32(uintptr(unsafe.Pointer(&__isr_vector))),
|
||||
uint32(uintptr(unsafe.Pointer(&stack1TopSymbol))),
|
||||
uint32(exportedFuncPtr(runCore1)),
|
||||
}
|
||||
|
||||
//go:extern __isr_vector
|
||||
var __isr_vector [0]uint32
|
||||
|
||||
//go:extern _stack1_top
|
||||
var stack1TopSymbol [0]uint32
|
||||
|
||||
// The function that is started on the second core.
|
||||
//
|
||||
//export tinygo_runCore1
|
||||
func runCore1() {
|
||||
// Clear sticky bit that seems to have been set while starting this core.
|
||||
rp.SIO.FIFO_ST.Set(rp.SIO_FIFO_ST_ROE)
|
||||
|
||||
// Enable the FIFO interrupt, mainly used for the stop-the-world phase of
|
||||
// the GC.
|
||||
// Use the lowest possible priority (highest priority value), so that other
|
||||
// interrupts can still happen while the GC is running.
|
||||
intr := interrupt.New(sioIrqFifoProc1, func(intr interrupt.Interrupt) {
|
||||
switch rp.SIO.FIFO_RD.Get() {
|
||||
case 1:
|
||||
gcInterruptHandler(1)
|
||||
}
|
||||
})
|
||||
intr.Enable()
|
||||
intr.SetPriority(0xff)
|
||||
|
||||
// Now start running the scheduler on this core.
|
||||
schedulerLock.Lock()
|
||||
scheduler(false)
|
||||
schedulerLock.Unlock()
|
||||
|
||||
// The main function returned.
|
||||
exit(0)
|
||||
}
|
||||
|
||||
// The below multicore_fifo_* functions have been translated from the Raspberry
|
||||
// Pi Pico SDK.
|
||||
|
||||
func multicore_fifo_rvalid() bool {
|
||||
return rp.SIO.FIFO_ST.Get()&rp.SIO_FIFO_ST_VLD != 0
|
||||
}
|
||||
|
||||
func multicore_fifo_wready() bool {
|
||||
return rp.SIO.FIFO_ST.Get()&rp.SIO_FIFO_ST_RDY != 0
|
||||
}
|
||||
|
||||
func multicore_fifo_drain() {
|
||||
for multicore_fifo_rvalid() {
|
||||
rp.SIO.FIFO_RD.Get()
|
||||
}
|
||||
}
|
||||
|
||||
func multicore_fifo_push_blocking(data uint32) {
|
||||
for !multicore_fifo_wready() {
|
||||
}
|
||||
rp.SIO.FIFO_WR.Set(data)
|
||||
arm.Asm("sev")
|
||||
}
|
||||
|
||||
func multicore_fifo_pop_blocking() uint32 {
|
||||
for !multicore_fifo_rvalid() {
|
||||
arm.Asm("wfe")
|
||||
}
|
||||
|
||||
return rp.SIO.FIFO_RD.Get()
|
||||
}
|
||||
|
||||
// Value used to communicate between the GC core and the other (paused) cores.
|
||||
var gcSignalWait volatile.Register8
|
||||
|
||||
// The GC interrupted this core for the stop-the-world phase.
|
||||
// This function handles that, and only returns after the stop-the-world phase
|
||||
// ended.
|
||||
func gcInterruptHandler(hartID uint32) {
|
||||
// Let the GC know we're ready.
|
||||
gcScanState.Add(1)
|
||||
arm.Asm("sev")
|
||||
|
||||
// Wait until we get a signal to start scanning.
|
||||
for gcSignalWait.Get() == 0 {
|
||||
arm.Asm("wfe")
|
||||
}
|
||||
gcSignalWait.Set(0)
|
||||
|
||||
// Scan the stack(s) of this core.
|
||||
scanCurrentStack()
|
||||
if !task.OnSystemStack() {
|
||||
// Mark system stack.
|
||||
markRoots(task.SystemStack(), coreStackTop(hartID))
|
||||
}
|
||||
|
||||
// Signal we've finished scanning.
|
||||
gcScanState.Store(1)
|
||||
arm.Asm("sev")
|
||||
|
||||
// Wait until we get a signal that the stop-the-world phase has ended.
|
||||
for gcSignalWait.Get() == 0 {
|
||||
arm.Asm("wfe")
|
||||
}
|
||||
gcSignalWait.Set(0)
|
||||
|
||||
// Signal we received the signal and are going to exit the interrupt.
|
||||
gcScanState.Add(1)
|
||||
arm.Asm("sev")
|
||||
}
|
||||
|
||||
// Pause the given core by sending it an interrupt.
|
||||
func gcPauseCore(core uint32) {
|
||||
rp.SIO.FIFO_WR.Set(1)
|
||||
}
|
||||
|
||||
// Signal the given core that it can resume one step.
|
||||
// This is called twice after gcPauseCore: the first time to scan the stack of
|
||||
// the core, and the second time to end the stop-the-world phase.
|
||||
func gcSignalCore(core uint32) {
|
||||
gcSignalWait.Set(1)
|
||||
arm.Asm("sev")
|
||||
}
|
||||
|
||||
// Returns the stack top (highest address) of the system stack of the given
|
||||
// core.
|
||||
func coreStackTop(core uint32) uintptr {
|
||||
switch core {
|
||||
case 0:
|
||||
return uintptr(unsafe.Pointer(&stackTopSymbol))
|
||||
case 1:
|
||||
return uintptr(unsafe.Pointer(&stack1TopSymbol))
|
||||
default:
|
||||
runtimePanic("unexpected core")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// These spinlocks are needed by the runtime.
|
||||
var (
|
||||
printLock = spinLock{id: 0}
|
||||
schedulerLock = spinLock{id: 1}
|
||||
atomicsLock = spinLock{id: 2}
|
||||
futexLock = spinLock{id: 3}
|
||||
)
|
||||
|
||||
// A hardware spinlock, one of the 32 spinlocks defined in the SIO peripheral.
|
||||
type spinLock struct {
|
||||
id uint8
|
||||
}
|
||||
|
||||
// Return the spinlock register: rp.SIO.SPINLOCKx
|
||||
func (l *spinLock) spinlock() *volatile.Register32 {
|
||||
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&rp.SIO.SPINLOCK0), l.id*4))
|
||||
}
|
||||
|
||||
func (l *spinLock) Lock() {
|
||||
// Wait for the lock to be available.
|
||||
spinlock := l.spinlock()
|
||||
for spinlock.Get() == 0 {
|
||||
// TODO: use wfe and send an event when unlocking so the CPU can go to
|
||||
// sleep while waiting for the lock.
|
||||
// Unfortunately when doing that, time.Sleep() seems to hang somewhere.
|
||||
// This needs some debugging to figure out.
|
||||
}
|
||||
}
|
||||
|
||||
func (l *spinLock) Unlock() {
|
||||
l.spinlock().Set(0)
|
||||
}
|
||||
|
||||
// Wait until a signal is received, indicating that it can resume from the
|
||||
// spinloop.
|
||||
func spinLoopWait() {
|
||||
arm.Asm("wfe")
|
||||
}
|
||||
|
||||
func waitForEvents() {
|
||||
arm.Asm("wfe")
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
// machineInit is provided by package machine.
|
||||
func machineInit()
|
||||
|
||||
func init() {
|
||||
machineInit()
|
||||
|
||||
cdc.EnableUSBCDC()
|
||||
machine.USBDev.Configure(machine.UARTConfig{})
|
||||
machine.InitSerial()
|
||||
}
|
||||
|
||||
//export Reset_Handler
|
||||
func main() {
|
||||
preinit()
|
||||
run()
|
||||
exit(0)
|
||||
}
|
||||
@@ -3,365 +3,10 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/rp"
|
||||
"internal/task"
|
||||
"machine"
|
||||
"machine/usb/cdc"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const numCPU = 2
|
||||
|
||||
// machineTicks is provided by package machine.
|
||||
func machineTicks() uint64
|
||||
|
||||
// machineLightSleep is provided by package machine.
|
||||
func machineLightSleep(uint64)
|
||||
|
||||
// ticks returns the number of ticks (microseconds) elapsed since power up.
|
||||
func ticks() timeUnit {
|
||||
t := machineTicks()
|
||||
return timeUnit(t)
|
||||
}
|
||||
|
||||
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||
return int64(ticks) * 1000
|
||||
}
|
||||
|
||||
func nanosecondsToTicks(ns int64) timeUnit {
|
||||
return timeUnit(ns / 1000)
|
||||
}
|
||||
|
||||
func sleepTicks(d timeUnit) {
|
||||
if hasScheduler {
|
||||
// With scheduler, sleepTicks may return early if an interrupt or
|
||||
// event fires - so scheduler can schedule any go routines now
|
||||
// eligible to run
|
||||
machineLightSleep(uint64(d))
|
||||
return
|
||||
}
|
||||
|
||||
// Busy loop
|
||||
sleepUntil := ticks() + d
|
||||
for ticks() < sleepUntil {
|
||||
}
|
||||
}
|
||||
|
||||
// Currently sleeping core, or 0xff.
|
||||
// Must only be accessed with the scheduler lock held.
|
||||
var sleepingCore uint8 = 0xff
|
||||
|
||||
// Return whether another core is sleeping.
|
||||
// May only be called with the scheduler lock held.
|
||||
func hasSleepingCore() bool {
|
||||
return sleepingCore != 0xff
|
||||
}
|
||||
|
||||
// Almost identical to sleepTicks, except that it will unlock/lock the scheduler
|
||||
// while sleeping and is interruptible by interruptSleepTicksMulticore.
|
||||
// This may only be called with the scheduler lock held.
|
||||
func sleepTicksMulticore(d timeUnit) {
|
||||
sleepingCore = uint8(currentCPU())
|
||||
|
||||
// Note: interruptSleepTicksMulticore will be able to interrupt this, since
|
||||
// it executes the "sev" instruction which would make sleepTicks return
|
||||
// immediately without sleeping. Even if it happens while configuring the
|
||||
// sleep operation.
|
||||
|
||||
schedulerLock.Unlock()
|
||||
sleepTicks(d)
|
||||
schedulerLock.Lock()
|
||||
|
||||
sleepingCore = 0xff
|
||||
}
|
||||
|
||||
// Interrupt an ongoing call to sleepTicksMulticore on another core.
|
||||
func interruptSleepTicksMulticore(wakeup timeUnit) {
|
||||
arm.Asm("sev")
|
||||
}
|
||||
|
||||
// Number of cores that are currently in schedulerUnlockAndWait.
|
||||
// It is possible for both cores to be sleeping, if the program is waiting for
|
||||
// an interrupt (or is deadlocked).
|
||||
var waitingCore uint8
|
||||
|
||||
// Put the scheduler to sleep, since there are no tasks to run.
|
||||
// This will unlock the scheduler lock, and must be called with the scheduler
|
||||
// lock held.
|
||||
func schedulerUnlockAndWait() {
|
||||
waitingCore++
|
||||
schedulerLock.Unlock()
|
||||
arm.Asm("wfe")
|
||||
schedulerLock.Lock()
|
||||
waitingCore--
|
||||
}
|
||||
|
||||
// Wake another core, if one is sleeping. Must be called with the scheduler lock
|
||||
// held.
|
||||
func schedulerWake() {
|
||||
if waitingCore != 0 {
|
||||
arm.Asm("sev")
|
||||
}
|
||||
}
|
||||
|
||||
// Return the current core number: 0 or 1.
|
||||
func currentCPU() uint32 {
|
||||
return rp.SIO.CPUID.Get()
|
||||
}
|
||||
|
||||
// Start the secondary cores for this chip.
|
||||
// On the RP2040, there is only one other core to start.
|
||||
func startSecondaryCores() {
|
||||
// Start the second core of the RP2040.
|
||||
// See section 2.8.2 in the datasheet.
|
||||
seq := 0
|
||||
for {
|
||||
cmd := core1StartSequence[seq]
|
||||
if cmd == 0 {
|
||||
multicore_fifo_drain()
|
||||
arm.Asm("sev")
|
||||
}
|
||||
multicore_fifo_push_blocking(cmd)
|
||||
response := multicore_fifo_pop_blocking()
|
||||
if cmd != response {
|
||||
seq = 0
|
||||
continue
|
||||
}
|
||||
seq = seq + 1
|
||||
if seq >= len(core1StartSequence) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Enable the FIFO interrupt for the GC stop the world phase.
|
||||
// We can only do this after we don't need the FIFO anymore for starting the
|
||||
// second core.
|
||||
intr := interrupt.New(rp.IRQ_SIO_IRQ_PROC0, func(intr interrupt.Interrupt) {
|
||||
switch rp.SIO.FIFO_RD.Get() {
|
||||
case 1:
|
||||
gcInterruptHandler(0)
|
||||
}
|
||||
})
|
||||
intr.Enable()
|
||||
intr.SetPriority(0xff)
|
||||
}
|
||||
|
||||
var core1StartSequence = [...]uint32{
|
||||
0, 0, 1,
|
||||
uint32(uintptr(unsafe.Pointer(&__isr_vector))),
|
||||
uint32(uintptr(unsafe.Pointer(&stack1TopSymbol))),
|
||||
uint32(exportedFuncPtr(runCore1)),
|
||||
}
|
||||
|
||||
//go:extern __isr_vector
|
||||
var __isr_vector [0]uint32
|
||||
|
||||
//go:extern _stack1_top
|
||||
var stack1TopSymbol [0]uint32
|
||||
|
||||
// The function that is started on the second core.
|
||||
//
|
||||
//export tinygo_runCore1
|
||||
func runCore1() {
|
||||
// Clear sticky bit that seems to have been set while starting this core.
|
||||
rp.SIO.FIFO_ST.Set(rp.SIO_FIFO_ST_ROE)
|
||||
|
||||
// Enable the FIFO interrupt, mainly used for the stop-the-world phase of
|
||||
// the GC.
|
||||
// Use the lowest possible priority (highest priority value), so that other
|
||||
// interrupts can still happen while the GC is running.
|
||||
intr := interrupt.New(rp.IRQ_SIO_IRQ_PROC1, func(intr interrupt.Interrupt) {
|
||||
switch rp.SIO.FIFO_RD.Get() {
|
||||
case 1:
|
||||
gcInterruptHandler(1)
|
||||
}
|
||||
})
|
||||
intr.Enable()
|
||||
intr.SetPriority(0xff)
|
||||
|
||||
// Now start running the scheduler on this core.
|
||||
schedulerLock.Lock()
|
||||
scheduler(false)
|
||||
schedulerLock.Unlock()
|
||||
|
||||
// The main function returned.
|
||||
exit(0)
|
||||
}
|
||||
|
||||
// The below multicore_fifo_* functions have been translated from the Raspberry
|
||||
// Pi Pico SDK.
|
||||
|
||||
func multicore_fifo_rvalid() bool {
|
||||
return rp.SIO.FIFO_ST.Get()&rp.SIO_FIFO_ST_VLD != 0
|
||||
}
|
||||
|
||||
func multicore_fifo_wready() bool {
|
||||
return rp.SIO.FIFO_ST.Get()&rp.SIO_FIFO_ST_RDY != 0
|
||||
}
|
||||
|
||||
func multicore_fifo_drain() {
|
||||
for multicore_fifo_rvalid() {
|
||||
rp.SIO.FIFO_RD.Get()
|
||||
}
|
||||
}
|
||||
|
||||
func multicore_fifo_push_blocking(data uint32) {
|
||||
for !multicore_fifo_wready() {
|
||||
}
|
||||
rp.SIO.FIFO_WR.Set(data)
|
||||
arm.Asm("sev")
|
||||
}
|
||||
|
||||
func multicore_fifo_pop_blocking() uint32 {
|
||||
for !multicore_fifo_rvalid() {
|
||||
arm.Asm("wfe")
|
||||
}
|
||||
|
||||
return rp.SIO.FIFO_RD.Get()
|
||||
}
|
||||
|
||||
// Value used to communicate between the GC core and the other (paused) cores.
|
||||
var gcSignalWait volatile.Register8
|
||||
|
||||
// The GC interrupted this core for the stop-the-world phase.
|
||||
// This function handles that, and only returns after the stop-the-world phase
|
||||
// ended.
|
||||
func gcInterruptHandler(hartID uint32) {
|
||||
// Let the GC know we're ready.
|
||||
gcScanState.Add(1)
|
||||
arm.Asm("sev")
|
||||
|
||||
// Wait until we get a signal to start scanning.
|
||||
for gcSignalWait.Get() == 0 {
|
||||
arm.Asm("wfe")
|
||||
}
|
||||
gcSignalWait.Set(0)
|
||||
|
||||
// Scan the stack(s) of this core.
|
||||
scanCurrentStack()
|
||||
if !task.OnSystemStack() {
|
||||
// Mark system stack.
|
||||
markRoots(task.SystemStack(), coreStackTop(hartID))
|
||||
}
|
||||
|
||||
// Signal we've finished scanning.
|
||||
gcScanState.Store(1)
|
||||
arm.Asm("sev")
|
||||
|
||||
// Wait until we get a signal that the stop-the-world phase has ended.
|
||||
for gcSignalWait.Get() == 0 {
|
||||
arm.Asm("wfe")
|
||||
}
|
||||
gcSignalWait.Set(0)
|
||||
|
||||
// Signal we received the signal and are going to exit the interrupt.
|
||||
gcScanState.Add(1)
|
||||
arm.Asm("sev")
|
||||
}
|
||||
|
||||
// Pause the given core by sending it an interrupt.
|
||||
func gcPauseCore(core uint32) {
|
||||
rp.SIO.FIFO_WR.Set(1)
|
||||
}
|
||||
|
||||
// Signal the given core that it can resume one step.
|
||||
// This is called twice after gcPauseCore: the first time to scan the stack of
|
||||
// the core, and the second time to end the stop-the-world phase.
|
||||
func gcSignalCore(core uint32) {
|
||||
gcSignalWait.Set(1)
|
||||
arm.Asm("sev")
|
||||
}
|
||||
|
||||
// Returns the stack top (highest address) of the system stack of the given
|
||||
// core.
|
||||
func coreStackTop(core uint32) uintptr {
|
||||
switch core {
|
||||
case 0:
|
||||
return uintptr(unsafe.Pointer(&stackTopSymbol))
|
||||
case 1:
|
||||
return uintptr(unsafe.Pointer(&stack1TopSymbol))
|
||||
default:
|
||||
runtimePanic("unexpected core")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// These spinlocks are needed by the runtime.
|
||||
var (
|
||||
printLock = spinLock{id: 0}
|
||||
schedulerLock = spinLock{id: 1}
|
||||
atomicsLock = spinLock{id: 2}
|
||||
futexLock = spinLock{id: 3}
|
||||
const (
|
||||
sioIrqFifoProc0 = rp.IRQ_SIO_IRQ_PROC0
|
||||
sioIrqFifoProc1 = rp.IRQ_SIO_IRQ_PROC1
|
||||
)
|
||||
|
||||
// A hardware spinlock, one of the 32 spinlocks defined in the SIO peripheral.
|
||||
type spinLock struct {
|
||||
id uint8
|
||||
}
|
||||
|
||||
// Return the spinlock register: rp.SIO.SPINLOCKx
|
||||
func (l *spinLock) spinlock() *volatile.Register32 {
|
||||
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&rp.SIO.SPINLOCK0), l.id*4))
|
||||
}
|
||||
|
||||
func (l *spinLock) Lock() {
|
||||
// Wait for the lock to be available.
|
||||
spinlock := l.spinlock()
|
||||
for spinlock.Get() == 0 {
|
||||
// TODO: use wfe and send an event when unlocking so the CPU can go to
|
||||
// sleep while waiting for the lock.
|
||||
// Unfortunately when doing that, time.Sleep() seems to hang somewhere.
|
||||
// This needs some debugging to figure out.
|
||||
}
|
||||
}
|
||||
|
||||
func (l *spinLock) Unlock() {
|
||||
l.spinlock().Set(0)
|
||||
}
|
||||
|
||||
// Wait until a signal is received, indicating that it can resume from the
|
||||
// spinloop.
|
||||
func spinLoopWait() {
|
||||
arm.Asm("wfe")
|
||||
}
|
||||
|
||||
func waitForEvents() {
|
||||
arm.Asm("wfe")
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
// machineInit is provided by package machine.
|
||||
func machineInit()
|
||||
|
||||
func init() {
|
||||
machineInit()
|
||||
|
||||
cdc.EnableUSBCDC()
|
||||
machine.USBDev.Configure(machine.UARTConfig{})
|
||||
machine.InitSerial()
|
||||
}
|
||||
|
||||
//export Reset_Handler
|
||||
func main() {
|
||||
preinit()
|
||||
run()
|
||||
exit(0)
|
||||
}
|
||||
|
||||
@@ -3,84 +3,14 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"machine"
|
||||
"machine/usb/cdc"
|
||||
"device/rp"
|
||||
)
|
||||
|
||||
// machineTicks is provided by package machine.
|
||||
func machineTicks() uint64
|
||||
|
||||
// machineLightSleep is provided by package machine.
|
||||
func machineLightSleep(uint64)
|
||||
|
||||
// ticks returns the number of ticks (microseconds) elapsed since power up.
|
||||
func ticks() timeUnit {
|
||||
t := machineTicks()
|
||||
return timeUnit(t)
|
||||
}
|
||||
|
||||
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||
return int64(ticks) * 1000
|
||||
}
|
||||
|
||||
func nanosecondsToTicks(ns int64) timeUnit {
|
||||
return timeUnit(ns / 1000)
|
||||
}
|
||||
|
||||
func sleepTicks(d timeUnit) {
|
||||
if d <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if hasScheduler {
|
||||
// With scheduler, sleepTicks may return early if an interrupt or
|
||||
// event fires - so scheduler can schedule any go routines now
|
||||
// eligible to run
|
||||
machineLightSleep(uint64(d))
|
||||
return
|
||||
}
|
||||
|
||||
// Busy loop
|
||||
sleepUntil := ticks() + d
|
||||
for ticks() < sleepUntil {
|
||||
}
|
||||
}
|
||||
|
||||
func waitForEvents() {
|
||||
arm.Asm("wfe")
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
// machineInit is provided by package machine.
|
||||
func machineInit()
|
||||
|
||||
func init() {
|
||||
machineInit()
|
||||
|
||||
cdc.EnableUSBCDC()
|
||||
machine.USBDev.Configure(machine.UARTConfig{})
|
||||
machine.InitSerial()
|
||||
}
|
||||
|
||||
//export Reset_Handler
|
||||
func main() {
|
||||
preinit()
|
||||
run()
|
||||
exit(0)
|
||||
}
|
||||
const (
|
||||
// On RP2040 each core has a different IRQ number: SIO_IRQ_PROC0 and SIO_IRQ_PROC1.
|
||||
// On RP2350 both cores share the same irq number (SIO_IRQ_PROC) just with a
|
||||
// different SIO interrupt output routed to that IRQ input on each core.
|
||||
// https://www.raspberrypi.com/documentation/pico-sdk/high_level.html#group_pico_multicore_1ga1413ebfa65114c6f408f4675897ac5ee
|
||||
sioIrqFifoProc0 = rp.IRQ_SIO_IRQ_FIFO
|
||||
sioIrqFifoProc1 = rp.IRQ_SIO_IRQ_FIFO
|
||||
)
|
||||
|
||||
@@ -77,7 +77,6 @@ func addSleepTask(t *task.Task, duration timeUnit) {
|
||||
panic("runtime: addSleepTask: expected next task to be nil")
|
||||
}
|
||||
}
|
||||
t.Data = uint64(duration)
|
||||
now := ticks()
|
||||
if sleepQueue == nil {
|
||||
scheduleLog(" -> sleep new queue")
|
||||
@@ -85,6 +84,7 @@ func addSleepTask(t *task.Task, duration timeUnit) {
|
||||
// set new base time
|
||||
sleepQueueBaseTime = now
|
||||
}
|
||||
t.Data = uint64(duration + (now - sleepQueueBaseTime))
|
||||
|
||||
// Add to sleep queue.
|
||||
q := &sleepQueue
|
||||
|
||||
@@ -51,7 +51,7 @@ func sliceGrow(oldBuf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr)
|
||||
var layout unsafe.Pointer
|
||||
// less type info here; can only go off element size
|
||||
if elemSize < unsafe.Sizeof(uintptr(0)) {
|
||||
layout = gclayout.NoPtrs
|
||||
layout = gclayout.NoPtrs.AsPtr()
|
||||
}
|
||||
|
||||
buf := alloc(newCap*elemSize, layout)
|
||||
|
||||
@@ -60,7 +60,7 @@ func stringConcat(x, y _string) _string {
|
||||
return x
|
||||
} else {
|
||||
length := x.length + y.length
|
||||
buf := alloc(length, gclayout.NoPtrs)
|
||||
buf := alloc(length, gclayout.NoPtrs.AsPtr())
|
||||
memcpy(buf, unsafe.Pointer(x.ptr), x.length)
|
||||
memcpy(unsafe.Add(buf, x.length), unsafe.Pointer(y.ptr), y.length)
|
||||
return _string{ptr: (*byte)(buf), length: length}
|
||||
@@ -73,7 +73,7 @@ func stringFromBytes(x struct {
|
||||
len uintptr
|
||||
cap uintptr
|
||||
}) _string {
|
||||
buf := alloc(x.len, gclayout.NoPtrs)
|
||||
buf := alloc(x.len, gclayout.NoPtrs.AsPtr())
|
||||
memcpy(buf, unsafe.Pointer(x.ptr), x.len)
|
||||
return _string{ptr: (*byte)(buf), length: x.len}
|
||||
}
|
||||
@@ -84,7 +84,7 @@ func stringToBytes(x _string) (slice struct {
|
||||
len uintptr
|
||||
cap uintptr
|
||||
}) {
|
||||
buf := alloc(x.length, gclayout.NoPtrs)
|
||||
buf := alloc(x.length, gclayout.NoPtrs.AsPtr())
|
||||
memcpy(buf, unsafe.Pointer(x.ptr), x.length)
|
||||
slice.ptr = (*byte)(buf)
|
||||
slice.len = x.length
|
||||
@@ -101,7 +101,7 @@ func stringFromRunes(runeSlice []rune) (s _string) {
|
||||
}
|
||||
|
||||
// Allocate memory for the string.
|
||||
s.ptr = (*byte)(alloc(s.length, gclayout.NoPtrs))
|
||||
s.ptr = (*byte)(alloc(s.length, gclayout.NoPtrs.AsPtr()))
|
||||
|
||||
// Encode runes to UTF-8 and store the resulting bytes in the string.
|
||||
index := uintptr(0)
|
||||
|
||||
@@ -46,3 +46,9 @@ func timerCallback(tn *timerNode, delta int64) {
|
||||
addTimer(tn)
|
||||
}
|
||||
}
|
||||
|
||||
//go:linkname time_runtimeIsBubbled time.runtimeIsBubbled
|
||||
func time_runtimeIsBubbled() bool {
|
||||
// We don't currently support bubbles.
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -70,3 +70,15 @@ func (m *Map) Range(f func(key, value interface{}) bool) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Swap replaces the value for the given key, and returns the old value if any.
|
||||
func (m *Map) Swap(key, value any) (previous any, loaded bool) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
if m.m == nil {
|
||||
m.m = make(map[interface{}]interface{})
|
||||
}
|
||||
previous, loaded = m.m[key]
|
||||
m.m[key] = value
|
||||
return
|
||||
}
|
||||
|
||||
@@ -17,3 +17,22 @@ func TestMapLoadAndDelete(t *testing.T) {
|
||||
t.Errorf("LoadAndDelete returned %v, %v, want nil, false", v, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapSwap(t *testing.T) {
|
||||
var sm sync.Map
|
||||
sm.Store("present", "value")
|
||||
|
||||
if v, ok := sm.Swap("present", "value2"); !ok || v != "value" {
|
||||
t.Errorf("Swap returned %v, %v, want value, true", v, ok)
|
||||
}
|
||||
if v, ok := sm.Load("present"); !ok || v != "value2" {
|
||||
t.Errorf("Load after Swap returned %v, %v, want value2, true", v, ok)
|
||||
}
|
||||
|
||||
if v, ok := sm.Swap("new", "foo"); ok || v != nil {
|
||||
t.Errorf("Swap returned %v, %v, want nil, false", v, ok)
|
||||
}
|
||||
if v, ok := sm.Load("present"); !ok || v != "value2" {
|
||||
t.Errorf("Load after Swap returned %v, %v, want foo, true", v, ok)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,6 +500,10 @@ func (b *B) RunParallel(body func(*PB)) {
|
||||
return
|
||||
}
|
||||
|
||||
func (b *B) Loop() bool {
|
||||
panic("unimplemented: testing.B.Loop")
|
||||
}
|
||||
|
||||
// Benchmark benchmarks a single function. It is useful for creating
|
||||
// custom benchmarks that do not use the "go test" command.
|
||||
//
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"inherits": [
|
||||
"rp2350b"
|
||||
],
|
||||
"build-tags": ["metro_rp2350"],
|
||||
"serial-port": ["239a:814e"],
|
||||
"default-stack-size": 8192,
|
||||
"ldflags": [
|
||||
"--defsym=__flash_size=16M"
|
||||
]
|
||||
}
|
||||
+3
-1
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"inherits": ["cortex-m33"],
|
||||
"build-tags": ["rp2350", "rp"],
|
||||
"scheduler": "cores",
|
||||
"flash-1200-bps-reset": "true",
|
||||
"flash-method": "msd",
|
||||
"serial": "usb",
|
||||
@@ -13,7 +14,8 @@
|
||||
"targets/rp2350_embedded_block.s"
|
||||
],
|
||||
"ldflags": [
|
||||
"--defsym=__flash_size=2M"
|
||||
"--defsym=__flash_size=2M",
|
||||
"--defsym=__num_stacks=2"
|
||||
],
|
||||
"linkerscript": "targets/rp2350.ld",
|
||||
"openocd-interface": "picoprobe",
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
MEMORY
|
||||
{
|
||||
FLASH_TEXT (rx) : ORIGIN = 0x08000000, LENGTH = 32K
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 8K
|
||||
}
|
||||
|
||||
_stack_size = 2K;
|
||||
|
||||
INCLUDE "targets/arm.ld"
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"inherits": [
|
||||
"cortex-m0plus"
|
||||
],
|
||||
"build-tags": [
|
||||
"stm32l031",
|
||||
"stm32l0x1",
|
||||
"stm32l0",
|
||||
"stm32"
|
||||
],
|
||||
"linkerscript": "targets/stm32l031x6.ld",
|
||||
"extra-files": [
|
||||
"src/device/stm32/stm32l0x1.s"
|
||||
],
|
||||
"flash-method": "openocd",
|
||||
"openocd-interface": "cmsis-dap",
|
||||
"openocd-target": "stm32l0"
|
||||
}
|
||||
Vendored
+7
-3
@@ -14,9 +14,9 @@ func init() {
|
||||
func main() {
|
||||
println("main 1")
|
||||
go sub()
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
println("main 2")
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
println("main 3")
|
||||
|
||||
// Await a blocking call.
|
||||
@@ -103,7 +103,7 @@ func acquire(m *sync.Mutex, wg *sync.WaitGroup) {
|
||||
|
||||
func sub() {
|
||||
println("sub 1")
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
println("sub 2")
|
||||
}
|
||||
|
||||
@@ -175,10 +175,13 @@ func testGoOnBuiltins() {
|
||||
|
||||
var once sync.Once
|
||||
|
||||
var waitChan = make(chan struct{})
|
||||
|
||||
func testGoOnInterface(f Itf) {
|
||||
go f.Nowait()
|
||||
time.Sleep(time.Millisecond)
|
||||
go f.Wait()
|
||||
<-waitChan
|
||||
time.Sleep(time.Millisecond * 2)
|
||||
println("done with 'go on interface'")
|
||||
}
|
||||
@@ -204,6 +207,7 @@ func (f Foo) Nowait() {
|
||||
|
||||
func (f Foo) Wait() {
|
||||
println("called: Foo.Wait")
|
||||
close(waitChan)
|
||||
time.Sleep(time.Microsecond)
|
||||
println(" ...waited")
|
||||
}
|
||||
|
||||
@@ -36,6 +36,13 @@ func chromectx(t *testing.T) context.Context {
|
||||
// see https://chromium.googlesource.com/chromium/src/+/main/docs/security/apparmor-userns-restrictions.md
|
||||
opts := append(chromedp.DefaultExecAllocatorOptions[:],
|
||||
chromedp.NoSandbox,
|
||||
chromedp.Flag("disable-web-security", true),
|
||||
chromedp.Flag("safebrowsing-disable-auto-update", true),
|
||||
chromedp.IgnoreCertErrors,
|
||||
chromedp.Flag("disable-sync", true),
|
||||
chromedp.Flag("disable-default-apps", true),
|
||||
chromedp.NoFirstRun,
|
||||
chromedp.Headless,
|
||||
)
|
||||
|
||||
allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
|
||||
|
||||
Reference in New Issue
Block a user