mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-31 17:17:47 +00:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dc449882ad | |||
| a158d3194f | |||
| 806498f099 | |||
| e3bc6da9e4 | |||
| 3e2471d934 | |||
| a545f17d2e | |||
| f4375d0452 | |||
| 756cdf44ed | |||
| 9037bf8bf0 | |||
| bfe9ee378f | |||
| 37a4fa205c | |||
| 59cc7d4c42 | |||
| ab64e215dd | |||
| 253dbe335a | |||
| 0ef86e1534 | |||
| 72270f9052 | |||
| 67ec722a74 | |||
| 62294feb56 | |||
| f1e25a18d2 | |||
| a2f886a67a | |||
| f791c821ff | |||
| a93f0ed12a | |||
| c25dd0a972 | |||
| 215dd3f0be | |||
| c51f5cea0b | |||
| 395ee2d338 | |||
| 069e4f0d98 | |||
| d1eb642d45 | |||
| 5f2e72f371 | |||
| 5b581d83b3 | |||
| d845f1e1b2 | |||
| 00d46bd25d | |||
| 01d2ef327d | |||
| 4da1f6bcbe | |||
| 14ddba8519 | |||
| a7b205c26c | |||
| c83f712c17 | |||
| fffad84a63 | |||
| e075e0591d | |||
| 46d2696363 | |||
| e98dfd6c46 | |||
| 9126b95737 | |||
| 3871b831af | |||
| 7755f2385c | |||
| 04601a29e8 | |||
| 9aadea930f | |||
| 6efa94035e | |||
| db8d80755f | |||
| dc91c96305 | |||
| 08b3a4576d | |||
| acba0748f1 | |||
| ef72c5bb4e | |||
| 93cc03b15a | |||
| fdc4bbbfad | |||
| dd4e9e86e7 | |||
| ad32d26511 | |||
| 4619896c19 | |||
| cec237917f | |||
| 4d2a6d2bbe | |||
| 6d5f4c4be2 | |||
| 0cb5d336f4 | |||
| 91ee4d0030 | |||
| 785eb93b62 | |||
| e041a8ed88 |
@@ -27,7 +27,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.21'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v3
|
||||
@@ -108,7 +108,7 @@ jobs:
|
||||
# We're doing the former here, to keep artifact uploads fast.
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: release-double-zipped
|
||||
name: darwin-amd64-double-zipped
|
||||
path: build/tinygo.darwin-amd64.tar.gz
|
||||
- name: Smoke tests
|
||||
shell: bash
|
||||
@@ -126,7 +126,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.21'
|
||||
cache: true
|
||||
- name: Build TinyGo
|
||||
run: go install
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
# statically linked binary.
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: golang:1.20-alpine
|
||||
image: golang:1.21-alpine
|
||||
steps:
|
||||
- name: Install apk dependencies
|
||||
# tar: needed for actions/cache@v3
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.21'
|
||||
cache: true
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
@@ -153,6 +153,7 @@ jobs:
|
||||
tar -C ~/lib -xf tinygo.linux-amd64.tar.gz
|
||||
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
|
||||
- run: make tinygo-test-wasi-fast
|
||||
- run: make tinygo-test-wasip1-fast
|
||||
- run: make smoketest
|
||||
assert-test-linux:
|
||||
# Run all tests that can run on Linux, with LLVM assertions enabled to catch
|
||||
@@ -177,12 +178,12 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.21'
|
||||
cache: true
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14'
|
||||
node-version: '16'
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
|
||||
@@ -290,7 +291,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.21'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v3
|
||||
@@ -407,7 +408,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.21'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v3
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.21'
|
||||
cache: true
|
||||
- name: Restore cached LLVM source
|
||||
uses: actions/cache/restore@v3
|
||||
@@ -118,7 +118,7 @@ jobs:
|
||||
# We're doing the former here, to keep artifact uploads fast.
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: release-double-zipped
|
||||
name: windows-amd64-double-zipped
|
||||
path: build/release/release.zip
|
||||
|
||||
smoke-test-windows:
|
||||
@@ -143,12 +143,12 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.21'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: release-double-zipped
|
||||
name: windows-amd64-double-zipped
|
||||
path: build/
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
@@ -173,12 +173,12 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.21'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: release-double-zipped
|
||||
name: windows-amd64-double-zipped
|
||||
path: build/
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
@@ -209,12 +209,12 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.21'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: release-double-zipped
|
||||
name: windows-amd64-double-zipped
|
||||
path: build/
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
|
||||
@@ -31,3 +31,6 @@
|
||||
[submodule "lib/macos-minimal-sdk"]
|
||||
path = lib/macos-minimal-sdk
|
||||
url = https://github.com/aykevl/macos-minimal-sdk.git
|
||||
[submodule "lib/renesas-svd"]
|
||||
path = lib/renesas-svd
|
||||
url = https://github.com/tinygo-org/renesas-svd.git
|
||||
|
||||
@@ -1,3 +1,74 @@
|
||||
0.29.0
|
||||
---
|
||||
|
||||
* **general**
|
||||
- Go 1.21 support
|
||||
- use https for renesas submodule #3856
|
||||
- ci: rename release-double-zipped to something more useful
|
||||
- ci: update Node.js from version 14 to version 16
|
||||
- ci: switch GH actions builds to use Go 1.21 final release
|
||||
- docker: update clang to version 15
|
||||
- docker: use Go 1.21 for Docker dev container build
|
||||
- `main`: add target JSON file in `tinygo info` output
|
||||
- `main`: improve detection of filesystems
|
||||
- `main`: use `go env` instead of doing all detection manually
|
||||
- make: add make task to generate Renesas device wrappers
|
||||
- make: add task to check NodeJS version before running tests
|
||||
- add submodule for Renesas SVD file mirror repo
|
||||
- update to go-serial package v1.6.0
|
||||
- `testing`: add Testing function
|
||||
- `tools/gen-device-svd`: small changes needed for Renesas MCUs
|
||||
* **compiler**
|
||||
- `builder`: update message for max supported Go version
|
||||
- `compiler,reflect`: NumMethods reports exported methods only
|
||||
- `compiler`: add compiler-rt and wasm symbols to table
|
||||
- `compiler`: add compiler-rt to wasm.json
|
||||
- `compiler`: add min and max builtin support
|
||||
- `compiler`: implement clear builtin for maps
|
||||
- `compiler`: implement clear builtin for slices
|
||||
- `compiler`: improve panic message when a runtime call is unavailable
|
||||
- `compiler`: update .ll test output
|
||||
- `loader`: merge go.env file which is now required starting in Go 1.21 to correctly get required packages
|
||||
* **standard library**
|
||||
- `os`: define ErrNoDeadline
|
||||
- `reflect`: Add FieldByNameFunc
|
||||
- `reflect`: add SetZero
|
||||
- `reflect`: fix iterating over maps with interface{} keys
|
||||
- `reflect`: implement Value.Grow
|
||||
- `reflect`: remove unecessary heap allocations
|
||||
- `reflect`: use .key() instead of a type assert
|
||||
- `sync`: add implementation from upstream Go for OnceFunc, OnceValue, and OnceValues
|
||||
* **targets**
|
||||
- `machine`: UART refactor (#3832)
|
||||
- `machine/avr`: pin change interrupt
|
||||
- `machine/macropad_rp2040`: add machine.BUTTON
|
||||
- `machine/nrf`: add I2C timeout
|
||||
- `machine/nrf`: wait for stop condition after reading from the I2C bus
|
||||
- `machine/nRF52`: set SPI TX/RX lengths even data is empty. Fixes #3868 (#3877)
|
||||
- `machine/rp2040`: add missing suffix to CMD_READ_STATUS
|
||||
- `machine/rp2040`: add NoPin support
|
||||
- `machine/rp2040`: move flash related functions into separate file from C imports for correct - LSP. Fixes #3852
|
||||
- `machine/rp2040`: wait for 1000 us after flash reset to avoid issues with busy USB bus
|
||||
- `machine/samd51,rp2040,nrf528xx,stm32`: implement watchdog
|
||||
- `machine/samd51`: fix i2cTimeout was decreasing due to cache activation
|
||||
- `machine/usb`: Add support for HID Keyboard LEDs
|
||||
- `machine/usb`: allow USB Endpoint settings to be changed externally
|
||||
- `machine/usb`: refactor endpoint configuration
|
||||
- `machine/usb`: remove usbDescriptorConfig
|
||||
- `machine/usb/hid,joystick`: fix hidreport (3) (#3802)
|
||||
- `machine/usb/hid`: add RxHandler interface
|
||||
- `machine/usb/hid`: rename Handler() to TxHandler()
|
||||
- `wasi`: allow zero inodes when reading directories
|
||||
- `wasm`: add support for GOOS=wasip1
|
||||
- `wasm`: fix functions exported through //export
|
||||
- `wasm`: remove i64 workaround, use BigInt instead
|
||||
- `example`: adjust time offset
|
||||
- `example`: simplify pininterrupt
|
||||
* **boards**
|
||||
- `targets`: add AKIZUKI DENSHI AE-RP2040
|
||||
- `targets`: adding new uf2 target for PCA10056 (#3765)
|
||||
|
||||
|
||||
0.28.0
|
||||
---
|
||||
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
# tinygo-llvm stage obtains the llvm source for TinyGo
|
||||
FROM golang:1.20 AS tinygo-llvm
|
||||
FROM golang:1.21 AS tinygo-llvm
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y apt-utils make cmake clang-11 ninja-build
|
||||
apt-get install -y apt-utils make cmake clang-15 ninja-build
|
||||
|
||||
COPY ./Makefile /tinygo/Makefile
|
||||
|
||||
|
||||
@@ -232,6 +232,10 @@ gen-device-rp: build/gen-device-svd
|
||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/RaspberryPi lib/cmsis-svd/data/RaspberryPi/ src/device/rp/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/rp
|
||||
|
||||
gen-device-renesas: build/gen-device-svd
|
||||
./build/gen-device-svd -source=https://github.com/tinygo-org/renesas-svd lib/renesas-svd/ src/device/renesas/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/renesas
|
||||
|
||||
# Get LLVM sources.
|
||||
$(LLVM_PROJECTDIR)/llvm:
|
||||
git clone -b xtensa_release_15.x --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR)
|
||||
@@ -263,12 +267,22 @@ lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
||||
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
||||
cd lib/wasi-libc && make -j4 EXTRA_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC=$(CLANG) AR=$(LLVM_AR) NM=$(LLVM_NM)
|
||||
|
||||
# Check for Node.js used during WASM tests.
|
||||
NODEJS_VERSION := $(word 1,$(subst ., ,$(shell node -v | cut -c 2-)))
|
||||
MIN_NODEJS_VERSION=16
|
||||
|
||||
.PHONY: check-nodejs-version
|
||||
check-nodejs-version:
|
||||
ifeq (, $(shell which node))
|
||||
@echo "Install NodeJS version 16+ to run tests."; exit 1;
|
||||
endif
|
||||
@if [ $(NODEJS_VERSION) -lt $(MIN_NODEJS_VERSION) ]; then echo "Install NodeJS version 16+ to run tests."; exit 1; fi
|
||||
|
||||
# Build the Go compiler.
|
||||
tinygo:
|
||||
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
|
||||
test: wasi-libc
|
||||
test: wasi-libc check-nodejs-version
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags "byollvm osusergo" ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||
|
||||
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
|
||||
@@ -413,8 +427,12 @@ tinygo-bench-fast:
|
||||
# Same thing, except for wasi rather than the current platform.
|
||||
tinygo-test-wasi:
|
||||
$(TINYGO) test -target wasi $(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
|
||||
tinygo-test-wasi-fast:
|
||||
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
|
||||
tinygo-test-wasip1-fast:
|
||||
GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
|
||||
tinygo-bench-wasi:
|
||||
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
|
||||
tinygo-bench-wasi-fast:
|
||||
@@ -478,12 +496,16 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/test
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/time-offset
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-mouse
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/i2c-target
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/watchdog
|
||||
@$(MD5SUM) test.hex
|
||||
# test simulated boards on play.tinygo.org
|
||||
ifneq ($(WASM), 0)
|
||||
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
||||
@@ -642,6 +664,8 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=gopher-badge examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=ae-rp2040 examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
# test pwm
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
|
||||
+19
-1
@@ -12,6 +12,7 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
wasm "github.com/aykevl/go-wasm"
|
||||
"github.com/blakesmith/ar"
|
||||
)
|
||||
|
||||
@@ -74,8 +75,25 @@ func makeArchive(arfile *os.File, objs []string) error {
|
||||
fileIndex int
|
||||
}{symbol.Name, i})
|
||||
}
|
||||
} else if dbg, err := wasm.Parse(objfile); err == nil {
|
||||
for _, s := range dbg.Sections {
|
||||
switch section := s.(type) {
|
||||
case *wasm.SectionImport:
|
||||
for _, ln := range section.Entries {
|
||||
|
||||
if ln.Kind != wasm.ExtKindFunction {
|
||||
// Not a function
|
||||
continue
|
||||
}
|
||||
symbolTable = append(symbolTable, struct {
|
||||
name string
|
||||
fileIndex int
|
||||
}{ln.Field, i})
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("failed to open file %s as ELF or PE/COFF: %w", objpath, err)
|
||||
return fmt.Errorf("failed to open file %s as WASM, ELF or PE/COFF: %w", objpath, err)
|
||||
}
|
||||
|
||||
// Close file, to avoid issues with too many open files (especially on
|
||||
|
||||
+11
-14
@@ -217,19 +217,27 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
var packageJobs []*compileJob
|
||||
packageActionIDJobs := make(map[string]*compileJob)
|
||||
|
||||
if config.Options.GlobalValues == nil {
|
||||
config.Options.GlobalValues = make(map[string]map[string]string)
|
||||
}
|
||||
if config.Options.GlobalValues["runtime"]["buildVersion"] == "" {
|
||||
version := goenv.Version
|
||||
if strings.HasSuffix(goenv.Version, "-dev") && goenv.GitSha1 != "" {
|
||||
version += "-" + goenv.GitSha1
|
||||
}
|
||||
if config.Options.GlobalValues == nil {
|
||||
config.Options.GlobalValues = make(map[string]map[string]string)
|
||||
}
|
||||
if config.Options.GlobalValues["runtime"] == nil {
|
||||
config.Options.GlobalValues["runtime"] = make(map[string]string)
|
||||
}
|
||||
config.Options.GlobalValues["runtime"]["buildVersion"] = version
|
||||
}
|
||||
if config.TestConfig.CompileTestBinary {
|
||||
// The testing.testBinary is set to "1" when in a test.
|
||||
// This is needed for testing.Testing() to work correctly.
|
||||
if config.Options.GlobalValues["testing"] == nil {
|
||||
config.Options.GlobalValues["testing"] = make(map[string]string)
|
||||
}
|
||||
config.Options.GlobalValues["testing"]["testBinary"] = "1"
|
||||
}
|
||||
|
||||
var embedFileObjects []*compileJob
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
@@ -1054,17 +1062,6 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Browsers cannot handle external functions that have type i64 because it
|
||||
// cannot be represented exactly in JavaScript (JS only has doubles). To
|
||||
// keep functions interoperable, pass int64 types as pointers to
|
||||
// stack-allocated values.
|
||||
if config.WasmAbi() == "js" {
|
||||
err := transform.ExternalInt64AsPtr(mod, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Optimization levels here are roughly the same as Clang, but probably not
|
||||
// exactly.
|
||||
optLevel, sizeLevel, inlinerThreshold := config.OptLevels()
|
||||
|
||||
@@ -60,6 +60,7 @@ func TestClangAttributes(t *testing.T) {
|
||||
{GOOS: "darwin", GOARCH: "arm64"},
|
||||
{GOOS: "windows", GOARCH: "amd64"},
|
||||
{GOOS: "windows", GOARCH: "arm64"},
|
||||
{GOOS: "wasip1", GOARCH: "wasm"},
|
||||
} {
|
||||
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
|
||||
if options.GOARCH == "arm" {
|
||||
|
||||
+4
-10
@@ -1,7 +1,6 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
@@ -24,19 +23,14 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
spec.OpenOCDCommands = options.OpenOCDCommands
|
||||
}
|
||||
|
||||
goroot := goenv.Get("GOROOT")
|
||||
if goroot == "" {
|
||||
return nil, errors.New("cannot locate $GOROOT, please set it manually")
|
||||
}
|
||||
|
||||
major, minor, err := goenv.GetGorootVersion(goroot)
|
||||
major, minor, err := goenv.GetGorootVersion()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||
return nil, err
|
||||
}
|
||||
if major != 1 || minor < 18 || minor > 20 {
|
||||
if major != 1 || minor < 18 || minor > 21 {
|
||||
// Note: when this gets updated, also update the Go compatibility matrix:
|
||||
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
|
||||
return nil, fmt.Errorf("requires go version 1.18 through 1.20, got go%d.%d", major, minor)
|
||||
return nil, fmt.Errorf("requires go version 1.18 through 1.21, got go%d.%d", major, minor)
|
||||
}
|
||||
|
||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
|
||||
@@ -43,7 +43,7 @@ func TestBinarySize(t *testing.T) {
|
||||
// microcontrollers
|
||||
{"hifive1b", "examples/echo", 4612, 280, 0, 2252},
|
||||
{"microbit", "examples/serial", 2724, 388, 8, 2256},
|
||||
{"wioterminal", "examples/pininterrupt", 6159, 1485, 116, 6816},
|
||||
{"wioterminal", "examples/pininterrupt", 6039, 1485, 116, 6816},
|
||||
|
||||
// TODO: also check wasm. Right now this is difficult, because
|
||||
// wasm binaries are run through wasm-opt and therefore the
|
||||
|
||||
@@ -492,11 +492,6 @@ func (c *Config) RelocationModel() string {
|
||||
return "static"
|
||||
}
|
||||
|
||||
// WasmAbi returns the WASM ABI which is specified in the target JSON file.
|
||||
func (c *Config) WasmAbi() string {
|
||||
return c.Target.WasmAbi
|
||||
}
|
||||
|
||||
// EmulatorName is a shorthand to get the command for this emulator, something
|
||||
// like qemu-system-arm or simavr.
|
||||
func (c *Config) EmulatorName() string {
|
||||
|
||||
+70
-41
@@ -23,46 +23,45 @@ import (
|
||||
// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/spec/struct.TargetOptions.html
|
||||
// https://github.com/shepmaster/rust-arduino-blink-led-no-core-with-cargo/blob/master/blink/arduino.json
|
||||
type TargetSpec struct {
|
||||
Inherits []string `json:"inherits"`
|
||||
Triple string `json:"llvm-target"`
|
||||
CPU string `json:"cpu"`
|
||||
ABI string `json:"target-abi"` // rougly equivalent to -mabi= flag
|
||||
Features string `json:"features"`
|
||||
GOOS string `json:"goos"`
|
||||
GOARCH string `json:"goarch"`
|
||||
BuildTags []string `json:"build-tags"`
|
||||
GC string `json:"gc"`
|
||||
Scheduler string `json:"scheduler"`
|
||||
Serial string `json:"serial"` // which serial output to use (uart, usb, none)
|
||||
Linker string `json:"linker"`
|
||||
RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt)
|
||||
Libc string `json:"libc"`
|
||||
AutoStackSize *bool `json:"automatic-stack-size"` // Determine stack size automatically at compile time.
|
||||
DefaultStackSize uint64 `json:"default-stack-size"` // Default stack size if the size couldn't be determined at compile time.
|
||||
CFlags []string `json:"cflags"`
|
||||
LDFlags []string `json:"ldflags"`
|
||||
LinkerScript string `json:"linkerscript"`
|
||||
ExtraFiles []string `json:"extra-files"`
|
||||
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
|
||||
Emulator string `json:"emulator"`
|
||||
FlashCommand string `json:"flash-command"`
|
||||
GDB []string `json:"gdb"`
|
||||
PortReset string `json:"flash-1200-bps-reset"`
|
||||
SerialPort []string `json:"serial-port"` // serial port IDs in the form "vid:pid"
|
||||
FlashMethod string `json:"flash-method"`
|
||||
FlashVolume []string `json:"msd-volume-name"`
|
||||
FlashFilename string `json:"msd-firmware-name"`
|
||||
UF2FamilyID string `json:"uf2-family-id"`
|
||||
BinaryFormat string `json:"binary-format"`
|
||||
OpenOCDInterface string `json:"openocd-interface"`
|
||||
OpenOCDTarget string `json:"openocd-target"`
|
||||
OpenOCDTransport string `json:"openocd-transport"`
|
||||
OpenOCDCommands []string `json:"openocd-commands"`
|
||||
OpenOCDVerify *bool `json:"openocd-verify"` // enable verify when flashing with openocd
|
||||
JLinkDevice string `json:"jlink-device"`
|
||||
CodeModel string `json:"code-model"`
|
||||
RelocationModel string `json:"relocation-model"`
|
||||
WasmAbi string `json:"wasm-abi"`
|
||||
Inherits []string `json:"inherits,omitempty"`
|
||||
Triple string `json:"llvm-target,omitempty"`
|
||||
CPU string `json:"cpu,omitempty"`
|
||||
ABI string `json:"target-abi,omitempty"` // rougly equivalent to -mabi= flag
|
||||
Features string `json:"features,omitempty"`
|
||||
GOOS string `json:"goos,omitempty"`
|
||||
GOARCH string `json:"goarch,omitempty"`
|
||||
BuildTags []string `json:"build-tags,omitempty"`
|
||||
GC string `json:"gc,omitempty"`
|
||||
Scheduler string `json:"scheduler,omitempty"`
|
||||
Serial string `json:"serial,omitempty"` // which serial output to use (uart, usb, none)
|
||||
Linker string `json:"linker,omitempty"`
|
||||
RTLib string `json:"rtlib,omitempty"` // compiler runtime library (libgcc, compiler-rt)
|
||||
Libc string `json:"libc,omitempty"`
|
||||
AutoStackSize *bool `json:"automatic-stack-size,omitempty"` // Determine stack size automatically at compile time.
|
||||
DefaultStackSize uint64 `json:"default-stack-size,omitempty"` // Default stack size if the size couldn't be determined at compile time.
|
||||
CFlags []string `json:"cflags,omitempty"`
|
||||
LDFlags []string `json:"ldflags,omitempty"`
|
||||
LinkerScript string `json:"linkerscript,omitempty"`
|
||||
ExtraFiles []string `json:"extra-files,omitempty"`
|
||||
RP2040BootPatch *bool `json:"rp2040-boot-patch,omitempty"` // Patch RP2040 2nd stage bootloader checksum
|
||||
Emulator string `json:"emulator,omitempty"`
|
||||
FlashCommand string `json:"flash-command,omitempty"`
|
||||
GDB []string `json:"gdb,omitempty"`
|
||||
PortReset string `json:"flash-1200-bps-reset,omitempty"`
|
||||
SerialPort []string `json:"serial-port,omitempty"` // serial port IDs in the form "vid:pid"
|
||||
FlashMethod string `json:"flash-method,omitempty"`
|
||||
FlashVolume []string `json:"msd-volume-name,omitempty"`
|
||||
FlashFilename string `json:"msd-firmware-name,omitempty"`
|
||||
UF2FamilyID string `json:"uf2-family-id,omitempty"`
|
||||
BinaryFormat string `json:"binary-format,omitempty"`
|
||||
OpenOCDInterface string `json:"openocd-interface,omitempty"`
|
||||
OpenOCDTarget string `json:"openocd-target,omitempty"`
|
||||
OpenOCDTransport string `json:"openocd-transport,omitempty"`
|
||||
OpenOCDCommands []string `json:"openocd-commands,omitempty"`
|
||||
OpenOCDVerify *bool `json:"openocd-verify,omitempty"` // enable verify when flashing with openocd
|
||||
JLinkDevice string `json:"jlink-device,omitempty"`
|
||||
CodeModel string `json:"code-model,omitempty"`
|
||||
RelocationModel string `json:"relocation-model,omitempty"`
|
||||
}
|
||||
|
||||
// overrideProperties overrides all properties that are set in child into itself using reflection.
|
||||
@@ -192,12 +191,15 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid GOARM=%s, must be 5, 6, or 7", options.GOARM)
|
||||
}
|
||||
case "wasm":
|
||||
llvmarch = "wasm32"
|
||||
default:
|
||||
llvmarch = options.GOARCH
|
||||
}
|
||||
llvmvendor := "unknown"
|
||||
llvmos := options.GOOS
|
||||
if llvmos == "darwin" {
|
||||
switch llvmos {
|
||||
case "darwin":
|
||||
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer
|
||||
// to iOS!
|
||||
llvmos = "macosx10.12.0"
|
||||
@@ -208,6 +210,8 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
||||
llvmos = "macosx11.0.0"
|
||||
}
|
||||
llvmvendor = "apple"
|
||||
case "wasip1":
|
||||
llvmos = "wasi"
|
||||
}
|
||||
// Target triples (which actually have four components, but are called
|
||||
// triples for historical reasons) have the form:
|
||||
@@ -278,6 +282,15 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
case "arm64":
|
||||
spec.CPU = "generic"
|
||||
spec.Features = "+neon"
|
||||
case "wasm":
|
||||
spec.CPU = "generic"
|
||||
spec.Features = "+bulk-memory,+nontrapping-fptoint,+sign-ext"
|
||||
spec.BuildTags = append(spec.BuildTags, "tinygo.wasm")
|
||||
spec.CFlags = append(spec.CFlags,
|
||||
"-mbulk-memory",
|
||||
"-mnontrapping-fptoint",
|
||||
"-msign-ext",
|
||||
)
|
||||
}
|
||||
if goos == "darwin" {
|
||||
spec.Linker = "ld.lld"
|
||||
@@ -321,6 +334,22 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
"--no-insert-timestamp",
|
||||
"--no-dynamicbase",
|
||||
)
|
||||
} else if goos == "wasip1" {
|
||||
spec.GC = "" // use default GC
|
||||
spec.Scheduler = "asyncify"
|
||||
spec.Linker = "wasm-ld"
|
||||
spec.RTLib = "compiler-rt"
|
||||
spec.Libc = "wasi-libc"
|
||||
spec.DefaultStackSize = 1024 * 16 // 16kB
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"--stack-first",
|
||||
"--no-demangle",
|
||||
)
|
||||
spec.Emulator = "wasmtime --mapdir=/tmp::{tmpDir} {}"
|
||||
spec.ExtraFiles = append(spec.ExtraFiles,
|
||||
"src/runtime/asm_tinygowasm.S",
|
||||
"src/internal/task/task_asyncify_wasm.S",
|
||||
)
|
||||
} else {
|
||||
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
||||
}
|
||||
|
||||
+5
-1
@@ -36,7 +36,11 @@ const (
|
||||
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
||||
// createRuntimeInvoke instead.
|
||||
func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name string, isInvoke bool) llvm.Value {
|
||||
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
|
||||
member := b.program.ImportedPackage("runtime").Members[fnName]
|
||||
if member == nil {
|
||||
panic("unknown runtime call: " + fnName)
|
||||
}
|
||||
fn := member.(*ssa.Function)
|
||||
fnType, llvmFn := b.getFunction(fn)
|
||||
if llvmFn.IsNil() {
|
||||
panic("trying to call non-existent function: " + fn.RelString(nil))
|
||||
|
||||
@@ -1600,6 +1600,45 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
||||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx, nil
|
||||
case "clear":
|
||||
value := argValues[0]
|
||||
switch typ := argTypes[0].Underlying().(type) {
|
||||
case *types.Slice:
|
||||
elementType := b.getLLVMType(typ.Elem())
|
||||
elementSize := b.targetData.TypeAllocSize(elementType)
|
||||
elementAlign := b.targetData.ABITypeAlignment(elementType)
|
||||
|
||||
// The pointer to the data to be cleared.
|
||||
llvmBuf := b.CreateExtractValue(value, 0, "buf")
|
||||
if llvmBuf.Type() != b.i8ptrType { // compatibility with LLVM 14
|
||||
llvmBuf = b.CreateBitCast(llvmBuf, b.i8ptrType, "")
|
||||
}
|
||||
|
||||
// The length (in bytes) to be cleared.
|
||||
llvmLen := b.CreateExtractValue(value, 1, "len")
|
||||
llvmLen = b.CreateMul(llvmLen, llvm.ConstInt(llvmLen.Type(), elementSize, false), "")
|
||||
|
||||
// Do the clear operation using the LLVM memset builtin.
|
||||
// This is also correct for nil slices: in those cases, len will be
|
||||
// 0 which means the memset call is a no-op (according to the LLVM
|
||||
// LangRef).
|
||||
memset := b.getMemsetFunc()
|
||||
call := b.createCall(memset.GlobalValueType(), memset, []llvm.Value{
|
||||
llvmBuf, // dest
|
||||
llvm.ConstInt(b.ctx.Int8Type(), 0, false), // val
|
||||
llvmLen, // len
|
||||
llvm.ConstInt(b.ctx.Int1Type(), 0, false), // isVolatile
|
||||
}, "")
|
||||
call.AddCallSiteAttribute(1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elementAlign)))
|
||||
|
||||
return llvm.Value{}, nil
|
||||
case *types.Map:
|
||||
m := argValues[0]
|
||||
b.createMapClear(m)
|
||||
return llvm.Value{}, nil
|
||||
default:
|
||||
return llvm.Value{}, b.makeError(pos, "unsupported type in clear builtin: "+typ.String())
|
||||
}
|
||||
case "copy":
|
||||
dst := argValues[0]
|
||||
src := argValues[1]
|
||||
@@ -1637,6 +1676,24 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
||||
llvmLen = b.CreateZExt(llvmLen, b.intType, "len.int")
|
||||
}
|
||||
return llvmLen, nil
|
||||
case "min", "max":
|
||||
// min and max builtins, added in Go 1.21.
|
||||
// We can simply reuse the existing binop comparison code, which has all
|
||||
// the edge cases figured out already.
|
||||
tok := token.LSS
|
||||
if callName == "max" {
|
||||
tok = token.GTR
|
||||
}
|
||||
result := argValues[0]
|
||||
typ := argTypes[0]
|
||||
for _, arg := range argValues[1:] {
|
||||
cmp, err := b.createBinOp(tok, typ, typ, result, arg, pos)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result = b.CreateSelect(cmp, result, arg, "")
|
||||
}
|
||||
return result, nil
|
||||
case "print", "println":
|
||||
for i, value := range argValues {
|
||||
if i >= 1 && callName == "println" {
|
||||
|
||||
@@ -29,7 +29,7 @@ func TestCompiler(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Determine Go minor version (e.g. 16 in go1.16.3).
|
||||
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||
_, goMinor, err := goenv.GetGorootVersion()
|
||||
if err != nil {
|
||||
t.Fatal("could not read Go version:", err)
|
||||
}
|
||||
@@ -54,6 +54,9 @@ func TestCompiler(t *testing.T) {
|
||||
if goMinor >= 20 {
|
||||
tests = append(tests, testCase{"go1.20.go", "", ""})
|
||||
}
|
||||
if goMinor >= 21 {
|
||||
tests = append(tests, testCase{"go1.21.go", "", ""})
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
name := tc.file
|
||||
|
||||
+17
-8
@@ -128,6 +128,15 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
hasMethodSet = false
|
||||
}
|
||||
|
||||
var numMethods int
|
||||
if hasMethodSet {
|
||||
for i := 0; i < ms.Len(); i++ {
|
||||
if ms.At(i).Obj().Exported() {
|
||||
numMethods++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Short-circuit all the global pointer logic here for pointers to pointers.
|
||||
if typ, ok := typ.(*types.Pointer); ok {
|
||||
if _, ok := typ.Elem().(*types.Pointer); ok {
|
||||
@@ -277,11 +286,11 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
}
|
||||
pkgPathPtr := c.pkgPathPtr(pkgpath)
|
||||
typeFields = []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(ms.Len()), false), // numMethods
|
||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||
c.getTypeCode(typ.Underlying()), // underlying
|
||||
pkgPathPtr, // pkgpath pointer
|
||||
c.ctx.ConstString(pkgname+"."+name+"\x00", false), // name
|
||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
|
||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||
c.getTypeCode(typ.Underlying()), // underlying
|
||||
pkgPathPtr, // pkgpath pointer
|
||||
c.ctx.ConstString(pkgname+"."+name+"\x00", false), // name
|
||||
}
|
||||
metabyte |= 1 << 5 // "named" flag
|
||||
case *types.Chan:
|
||||
@@ -308,7 +317,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
}
|
||||
case *types.Pointer:
|
||||
typeFields = []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(ms.Len()), false), // numMethods
|
||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
|
||||
c.getTypeCode(typ.Elem()),
|
||||
}
|
||||
case *types.Array:
|
||||
@@ -337,8 +346,8 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
llvmStructType := c.getLLVMType(typ)
|
||||
size := c.targetData.TypeStoreSize(llvmStructType)
|
||||
typeFields = []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(ms.Len()), false), // numMethods
|
||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
|
||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||
pkgPathPtr,
|
||||
llvm.ConstInt(c.ctx.Int32Type(), uint64(size), false), // size
|
||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(typ.NumFields()), false), // numFields
|
||||
|
||||
+15
-9
@@ -70,15 +70,7 @@ func (b *builder) createMemoryCopyImpl() {
|
||||
// regular libc memset calls if they aren't optimized out in a different way.
|
||||
func (b *builder) createMemoryZeroImpl() {
|
||||
b.createFunctionStart(true)
|
||||
fnName := "llvm.memset.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
if llvmutil.Major() < 15 { // compatibility with LLVM 14
|
||||
fnName = "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
}
|
||||
llvmFn := b.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.ctx.Int8Type(), b.uintptrType, b.ctx.Int1Type()}, false)
|
||||
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
||||
}
|
||||
llvmFn := b.getMemsetFunc()
|
||||
params := []llvm.Value{
|
||||
b.getValue(b.fn.Params[0], getPos(b.fn)),
|
||||
llvm.ConstInt(b.ctx.Int8Type(), 0, false),
|
||||
@@ -89,6 +81,20 @@ func (b *builder) createMemoryZeroImpl() {
|
||||
b.CreateRetVoid()
|
||||
}
|
||||
|
||||
// Return the llvm.memset.p0.i8 function declaration.
|
||||
func (c *compilerContext) getMemsetFunc() llvm.Value {
|
||||
fnName := "llvm.memset.p0.i" + strconv.Itoa(c.uintptrType.IntTypeWidth())
|
||||
if llvmutil.Major() < 15 { // compatibility with LLVM 14
|
||||
fnName = "llvm.memset.p0i8.i" + strconv.Itoa(c.uintptrType.IntTypeWidth())
|
||||
}
|
||||
llvmFn := c.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType, c.ctx.Int8Type(), c.uintptrType, c.ctx.Int1Type()}, false)
|
||||
llvmFn = llvm.AddFunction(c.mod, fnName, fnType)
|
||||
}
|
||||
return llvmFn
|
||||
}
|
||||
|
||||
// createKeepAlive creates the runtime.KeepAlive function. It is implemented
|
||||
// using inline assembly.
|
||||
func (b *builder) createKeepAliveImpl() {
|
||||
|
||||
@@ -185,6 +185,11 @@ func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos tok
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the given map.
|
||||
func (b *builder) createMapClear(m llvm.Value) {
|
||||
b.createRuntimeCall("hashmapClear", []llvm.Value{m}, "")
|
||||
}
|
||||
|
||||
// createMapIteratorNext lowers the *ssa.Next instruction for iterating over a
|
||||
// map. It returns a tuple of {bool, key, value} with the result of the
|
||||
// iteration.
|
||||
|
||||
+14
-33
@@ -23,9 +23,9 @@ import (
|
||||
// The linkName value contains a valid link name, even if //go:linkname is not
|
||||
// present.
|
||||
type functionInfo struct {
|
||||
module string // go:wasm-module
|
||||
importName string // go:linkname, go:export - The name the developer assigns
|
||||
linkName string // go:linkname, go:export - The name that we map for the particular module -> importName
|
||||
wasmModule string // go:wasm-module
|
||||
wasmName string // wasm-export-name or wasm-import-name in the IR
|
||||
linkName string // go:linkname, go:export - the IR function name
|
||||
section string // go:section - object file section name
|
||||
exported bool // go:export, CGo
|
||||
interrupt bool // go:interrupt
|
||||
@@ -194,20 +194,14 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
||||
// External/exported functions may not retain pointer values.
|
||||
// https://golang.org/cmd/cgo/#hdr-Passing_pointers
|
||||
if info.exported {
|
||||
if c.archFamily() == "wasm32" {
|
||||
if c.archFamily() == "wasm32" && len(fn.Blocks) == 0 {
|
||||
// We need to add the wasm-import-module and the wasm-import-name
|
||||
// attributes.
|
||||
module := info.module
|
||||
if module == "" {
|
||||
module = "env"
|
||||
if info.wasmModule != "" {
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-module", info.wasmModule))
|
||||
}
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-module", module))
|
||||
|
||||
name := info.importName
|
||||
if name == "" {
|
||||
name = info.linkName
|
||||
}
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", name))
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", info.wasmName))
|
||||
}
|
||||
nocaptureKind := llvm.AttributeKindID("nocapture")
|
||||
nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0)
|
||||
@@ -260,10 +254,6 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
||||
return
|
||||
}
|
||||
if decl, ok := f.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil {
|
||||
|
||||
// Our importName for a wasm module (if we are compiling to wasm), or llvm link name
|
||||
var importName string
|
||||
|
||||
for _, comment := range decl.Doc.List {
|
||||
text := comment.Text
|
||||
if strings.HasPrefix(text, "//export ") {
|
||||
@@ -281,7 +271,8 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
||||
continue
|
||||
}
|
||||
|
||||
importName = parts[1]
|
||||
info.linkName = parts[1]
|
||||
info.wasmName = info.linkName
|
||||
info.exported = true
|
||||
case "//go:interrupt":
|
||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
||||
@@ -289,10 +280,11 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
||||
}
|
||||
case "//go:wasm-module":
|
||||
// Alternative comment for setting the import module.
|
||||
// This is deprecated, use //go:wasmimport instead.
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
info.module = parts[1]
|
||||
info.wasmModule = parts[1]
|
||||
case "//go:wasmimport":
|
||||
// Import a WebAssembly function, for example a WASI function.
|
||||
// Original proposal: https://github.com/golang/go/issues/38248
|
||||
@@ -302,8 +294,8 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
||||
}
|
||||
c.checkWasmImport(f, comment.Text)
|
||||
info.exported = true
|
||||
info.module = parts[1]
|
||||
info.importName = parts[2]
|
||||
info.wasmModule = parts[1]
|
||||
info.wasmName = parts[2]
|
||||
case "//go:inline":
|
||||
info.inline = inlineHint
|
||||
case "//go:noinline":
|
||||
@@ -347,17 +339,6 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the importName for our exported function if we have one
|
||||
if importName != "" {
|
||||
if info.module == "" {
|
||||
info.linkName = importName
|
||||
} else {
|
||||
// WebAssembly import
|
||||
info.importName = importName
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,7 +348,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
||||
// The list of allowed types is based on this proposal:
|
||||
// https://github.com/golang/go/issues/59149
|
||||
func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) {
|
||||
if c.pkg.Path() == "runtime" {
|
||||
if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" {
|
||||
// The runtime is a special case. Allow all kinds of parameters
|
||||
// (importantly, including pointers).
|
||||
return
|
||||
|
||||
Vendored
+65
@@ -0,0 +1,65 @@
|
||||
package main
|
||||
|
||||
func min1(a int) int {
|
||||
return min(a)
|
||||
}
|
||||
|
||||
func min2(a, b int) int {
|
||||
return min(a, b)
|
||||
}
|
||||
|
||||
func min3(a, b, c int) int {
|
||||
return min(a, b, c)
|
||||
}
|
||||
|
||||
func min4(a, b, c, d int) int {
|
||||
return min(a, b, c, d)
|
||||
}
|
||||
|
||||
func minUint8(a, b uint8) uint8 {
|
||||
return min(a, b)
|
||||
}
|
||||
|
||||
func minUnsigned(a, b uint) uint {
|
||||
return min(a, b)
|
||||
}
|
||||
|
||||
func minFloat32(a, b float32) float32 {
|
||||
return min(a, b)
|
||||
}
|
||||
|
||||
func minFloat64(a, b float64) float64 {
|
||||
return min(a, b)
|
||||
}
|
||||
|
||||
func minString(a, b string) string {
|
||||
return min(a, b)
|
||||
}
|
||||
|
||||
func maxInt(a, b int) int {
|
||||
return max(a, b)
|
||||
}
|
||||
|
||||
func maxUint(a, b uint) uint {
|
||||
return max(a, b)
|
||||
}
|
||||
|
||||
func maxFloat32(a, b float32) float32 {
|
||||
return max(a, b)
|
||||
}
|
||||
|
||||
func maxString(a, b string) string {
|
||||
return max(a, b)
|
||||
}
|
||||
|
||||
func clearSlice(s []int) {
|
||||
clear(s)
|
||||
}
|
||||
|
||||
func clearZeroSizedSlice(s []struct{}) {
|
||||
clear(s)
|
||||
}
|
||||
|
||||
func clearMap(m map[string]int) {
|
||||
clear(m)
|
||||
}
|
||||
Vendored
+179
@@ -0,0 +1,179 @@
|
||||
; ModuleID = 'go1.21.go'
|
||||
source_filename = "go1.21.go"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%runtime._string = type { ptr, i32 }
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret i32 %a
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.min3(i32 %a, i32 %b, i32 %c, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
|
||||
ret i32 %1
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.min4(i32 %a, i32 %b, i32 %c, i32 %d, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
|
||||
%2 = call i32 @llvm.smin.i32(i32 %1, i32 %d)
|
||||
ret i32 %2
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i8 @llvm.umin.i8(i8 %a, i8 %b)
|
||||
ret i8 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.umin.i32(i32 %a, i32 %b)
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = fcmp olt float %a, %b
|
||||
%1 = select i1 %0, float %a, float %b
|
||||
ret float %1
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = fcmp olt double %a, %b
|
||||
%1 = select i1 %0, double %a, double %b
|
||||
ret double %1
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._string @main.minString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
|
||||
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
|
||||
%stackalloc = alloca i8, align 1
|
||||
%4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #5
|
||||
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
||||
%6 = extractvalue %runtime._string %5, 0
|
||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
|
||||
ret %runtime._string %5
|
||||
}
|
||||
|
||||
declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.smax.i32(i32 %a, i32 %b)
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.umax.i32(i32 %a, i32 %b)
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = fcmp ogt float %a, %b
|
||||
%1 = select i1 %0, float %a, float %b
|
||||
ret float %1
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._string @main.maxString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
|
||||
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
|
||||
%stackalloc = alloca i8, align 1
|
||||
%4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #5
|
||||
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
||||
%6 = extractvalue %runtime._string %5, 0
|
||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
|
||||
ret %runtime._string %5
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.clearSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = shl i32 %s.len, 2
|
||||
call void @llvm.memset.p0.i32(ptr align 4 %s.data, i8 0, i32 %0, i1 false)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: argmemonly nocallback nofree nounwind willreturn writeonly
|
||||
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.clearMap(ptr dereferenceable_or_null(40) %m, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
call void @runtime.hashmapClear(ptr %m, ptr undef) #5
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
|
||||
declare i32 @llvm.smin.i32(i32, i32) #4
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
|
||||
declare i8 @llvm.umin.i8(i8, i8) #4
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
|
||||
declare i32 @llvm.umin.i32(i32, i32) #4
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
|
||||
declare i32 @llvm.smax.i32(i32, i32) #4
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
|
||||
declare i32 @llvm.umax.i32(i32, i32) #4
|
||||
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #3 = { argmemonly nocallback nofree nounwind willreturn writeonly }
|
||||
attributes #4 = { nocallback nofree nosync nounwind readnone speculatable willreturn }
|
||||
attributes #5 = { nounwind }
|
||||
Vendored
+1
-1
@@ -9,7 +9,7 @@ target triple = "wasm32-unknown-wasi"
|
||||
@"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 -62, ptr @"reflect/types.type:pointer:basic:int" }, align 4
|
||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:int" }, align 4
|
||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:error" }, align 4
|
||||
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [7 x i8] } { i8 116, i16 1, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", [7 x i8] c".error\00" }, align 4
|
||||
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [7 x i8] } { i8 116, i16 0, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", [7 x i8] c".error\00" }, align 4
|
||||
@"reflect/types.type.pkgpath.empty" = linkonce_odr unnamed_addr constant [1 x i8] zeroinitializer, align 1
|
||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||
|
||||
Vendored
+13
@@ -62,6 +62,19 @@ func exportedFunctionInSection() {
|
||||
//go:wasmimport modulename import1
|
||||
func declaredImport()
|
||||
|
||||
// Legacy way of importing a function.
|
||||
//
|
||||
//go:wasm-module foobar
|
||||
//export imported
|
||||
func foobarImport()
|
||||
|
||||
// The wasm-module pragma is not functional here, but it should be safe.
|
||||
//
|
||||
//go:wasm-module foobar
|
||||
//export exported
|
||||
func foobarExportModule() {
|
||||
}
|
||||
|
||||
// This function should not: it's only a declaration and not a definition.
|
||||
//
|
||||
//go:section .special_function_section
|
||||
|
||||
Vendored
+13
-3
@@ -6,7 +6,7 @@ target triple = "wasm32-unknown-wasi"
|
||||
@extern_global = external global [0 x i8], align 1
|
||||
@main.alignedGlobal = hidden global [4 x i32] zeroinitializer, align 32
|
||||
@main.alignedGlobal16 = hidden global [4 x i32] zeroinitializer, align 16
|
||||
@llvm.used = appending global [2 x ptr] [ptr @extern_func, ptr @exportedFunctionInSection]
|
||||
@llvm.used = appending global [3 x ptr] [ptr @extern_func, ptr @exportedFunctionInSection, ptr @exported]
|
||||
@main.globalInSection = hidden global i32 0, section ".special_global_section", align 4
|
||||
@undefinedGlobalNotInSection = external global i32, align 4
|
||||
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
|
||||
@@ -62,13 +62,23 @@ entry:
|
||||
|
||||
declare void @main.declaredImport() #7
|
||||
|
||||
declare void @imported() #8
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define void @exported() #9 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.undefinedFunctionNotInSection(ptr) #1
|
||||
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #3 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" "wasm-import-module"="env" "wasm-import-name"="extern_func" }
|
||||
attributes #3 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" }
|
||||
attributes #4 = { inlinehint nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #6 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" "wasm-import-module"="env" "wasm-import-name"="exportedFunctionInSection" }
|
||||
attributes #6 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" }
|
||||
attributes #7 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-import-module"="modulename" "wasm-import-name"="import1" }
|
||||
attributes #8 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-import-module"="foobar" "wasm-import-name"="imported" }
|
||||
attributes #9 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exported" }
|
||||
|
||||
@@ -14,7 +14,7 @@ require (
|
||||
github.com/mattn/go-colorable v0.1.8
|
||||
github.com/mattn/go-tty v0.0.4
|
||||
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3
|
||||
go.bug.st/serial v1.3.5
|
||||
go.bug.st/serial v1.6.0
|
||||
golang.org/x/sys v0.4.0
|
||||
golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
|
||||
@@ -12,6 +12,8 @@ github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moA
|
||||
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
|
||||
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/deadprogram/go-serial v0.0.0-20230717164825-4529b3232919 h1:Hi7G1bCG70NwlyqGswJKEHoIi4hJVN1SsmfwZ+DQHtw=
|
||||
github.com/deadprogram/go-serial v0.0.0-20230717164825-4529b3232919/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
@@ -46,8 +48,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs=
|
||||
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
go.bug.st/serial v1.3.5 h1:k50SqGZCnHZ2MiBQgzccXWG+kd/XpOs1jUljpDDKzaE=
|
||||
go.bug.st/serial v1.3.5/go.mod h1:z8CesKorE90Qr/oRSJiEuvzYRKol9r/anJZEb5kt304=
|
||||
go.bug.st/serial v1.6.0 h1:mAbRGN4cKE2J5gMwsMHC2KQisdLRQssO9WSM+rbZJ8A=
|
||||
go.bug.st/serial v1.6.0/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
|
||||
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
||||
+53
-99
@@ -4,15 +4,16 @@ package goenv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Keys is a slice of all available environment variable keys.
|
||||
@@ -37,6 +38,53 @@ func init() {
|
||||
// directory.
|
||||
var TINYGOROOT string
|
||||
|
||||
// Variables read from a `go env` command invocation.
|
||||
var goEnvVars struct {
|
||||
GOPATH string
|
||||
GOROOT string
|
||||
GOVERSION string
|
||||
}
|
||||
|
||||
var goEnvVarsOnce sync.Once
|
||||
var goEnvVarsErr error // error returned from cmd.Run
|
||||
|
||||
// Make sure goEnvVars is fresh. This can be called multiple times, the first
|
||||
// time will update all environment variables in goEnvVars.
|
||||
func readGoEnvVars() error {
|
||||
goEnvVarsOnce.Do(func() {
|
||||
cmd := exec.Command("go", "env", "-json", "GOPATH", "GOROOT", "GOVERSION")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
// Check for "command not found" error.
|
||||
if execErr, ok := err.(*exec.Error); ok {
|
||||
goEnvVarsErr = fmt.Errorf("could not find '%s' command: %w", execErr.Name, execErr.Err)
|
||||
return
|
||||
}
|
||||
// It's perhaps a bit ugly to handle this error here, but I couldn't
|
||||
// think of a better place further up in the call chain.
|
||||
if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() != 0 {
|
||||
if len(exitErr.Stderr) != 0 {
|
||||
// The 'go' command exited with an error message. Print that
|
||||
// message and exit, so we behave in a similar way.
|
||||
os.Stderr.Write(exitErr.Stderr)
|
||||
os.Exit(exitErr.ExitCode())
|
||||
}
|
||||
}
|
||||
// Other errors. Not sure whether there are any, but just in case.
|
||||
goEnvVarsErr = err
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(output, &goEnvVars)
|
||||
if err != nil {
|
||||
// This should never happen if we have a sane Go toolchain
|
||||
// installed.
|
||||
goEnvVarsErr = fmt.Errorf("unexpected error while unmarshalling `go env` output: %w", err)
|
||||
}
|
||||
})
|
||||
|
||||
return goEnvVarsErr
|
||||
}
|
||||
|
||||
// Get returns a single environment variable, possibly calculating it on-demand.
|
||||
// The empty string is returned for unknown environment variables.
|
||||
func Get(name string) string {
|
||||
@@ -70,15 +118,11 @@ func Get(name string) string {
|
||||
// especially when floating point instructions are involved.
|
||||
return "6"
|
||||
case "GOROOT":
|
||||
return getGoroot()
|
||||
readGoEnvVars()
|
||||
return goEnvVars.GOROOT
|
||||
case "GOPATH":
|
||||
if dir := os.Getenv("GOPATH"); dir != "" {
|
||||
return dir
|
||||
}
|
||||
|
||||
// fallback
|
||||
home := getHomeDir()
|
||||
return filepath.Join(home, "go")
|
||||
readGoEnvVars()
|
||||
return goEnvVars.GOPATH
|
||||
case "GOCACHE":
|
||||
// Get the cache directory, usually ~/.cache/tinygo
|
||||
dir, err := os.UserCacheDir()
|
||||
@@ -240,93 +284,3 @@ func isSourceDir(root string) bool {
|
||||
_, err = os.Stat(filepath.Join(root, "src/device/arm/arm.go"))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func getHomeDir() string {
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
panic("cannot get current user: " + err.Error())
|
||||
}
|
||||
if u.HomeDir == "" {
|
||||
// This is very unlikely, so panic here.
|
||||
// Not the nicest solution, however.
|
||||
panic("could not find home directory")
|
||||
}
|
||||
return u.HomeDir
|
||||
}
|
||||
|
||||
// getGoroot returns an appropriate GOROOT from various sources. If it can't be
|
||||
// found, it returns an empty string.
|
||||
func getGoroot() string {
|
||||
// An explicitly set GOROOT always has preference.
|
||||
goroot := os.Getenv("GOROOT")
|
||||
if goroot != "" {
|
||||
// Convert to the standard GOROOT being referenced, if it's a TinyGo cache.
|
||||
return getStandardGoroot(goroot)
|
||||
}
|
||||
|
||||
// Check for the location of the 'go' binary and base GOROOT on that.
|
||||
binpath, err := exec.LookPath("go")
|
||||
if err == nil {
|
||||
binpath, err = filepath.EvalSymlinks(binpath)
|
||||
if err == nil {
|
||||
goroot := filepath.Dir(filepath.Dir(binpath))
|
||||
if isGoroot(goroot) {
|
||||
return goroot
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check what GOROOT was at compile time.
|
||||
if isGoroot(runtime.GOROOT()) {
|
||||
return runtime.GOROOT()
|
||||
}
|
||||
|
||||
// Check for some standard locations, as a last resort.
|
||||
var candidates []string
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
candidates = []string{
|
||||
"/usr/local/go", // manually installed
|
||||
"/usr/lib/go", // from the distribution
|
||||
"/snap/go/current/", // installed using snap
|
||||
}
|
||||
case "darwin":
|
||||
candidates = []string{
|
||||
"/usr/local/go", // manually installed
|
||||
"/usr/local/opt/go/libexec", // from Homebrew
|
||||
}
|
||||
}
|
||||
|
||||
for _, candidate := range candidates {
|
||||
if isGoroot(candidate) {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
|
||||
// Can't find GOROOT...
|
||||
return ""
|
||||
}
|
||||
|
||||
// isGoroot checks whether the given path looks like a GOROOT.
|
||||
func isGoroot(goroot string) bool {
|
||||
_, err := os.Stat(filepath.Join(goroot, "src", "runtime", "internal", "sys", "zversion.go"))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// getStandardGoroot returns the physical path to a real, standard Go GOROOT
|
||||
// implied by the given path.
|
||||
// If the given path appears to be a TinyGo cached GOROOT, it returns the path
|
||||
// referenced by symlinks contained in the cache. Otherwise, it returns the
|
||||
// given path as-is.
|
||||
func getStandardGoroot(path string) string {
|
||||
// Check if the "bin" subdirectory of our given GOROOT is a symlink, and then
|
||||
// return the _parent_ directory of its destination.
|
||||
if dest, err := os.Readlink(filepath.Join(path, "bin")); nil == err {
|
||||
// Clean the destination to remove any trailing slashes, so that
|
||||
// filepath.Dir will always return the parent.
|
||||
// (because both "/foo" and "/foo/" are valid symlink destinations,
|
||||
// but filepath.Dir would return "/" and "/foo", respectively)
|
||||
return filepath.Dir(filepath.Clean(dest))
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
+8
-26
@@ -4,15 +4,12 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Version of TinyGo.
|
||||
// Update this value before release of new version of software.
|
||||
const Version = "0.28.1"
|
||||
const Version = "0.29.0"
|
||||
|
||||
var (
|
||||
// This variable is set at build time using -ldflags parameters.
|
||||
@@ -22,8 +19,8 @@ var (
|
||||
|
||||
// GetGorootVersion returns the major and minor version for a given GOROOT path.
|
||||
// If the goroot cannot be determined, (0, 0) is returned.
|
||||
func GetGorootVersion(goroot string) (major, minor int, err error) {
|
||||
s, err := GorootVersionString(goroot)
|
||||
func GetGorootVersion() (major, minor int, err error) {
|
||||
s, err := GorootVersionString()
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
@@ -51,24 +48,9 @@ func GetGorootVersion(goroot string) (major, minor int, err error) {
|
||||
}
|
||||
|
||||
// GorootVersionString returns the version string as reported by the Go
|
||||
// toolchain for the given GOROOT path. It is usually of the form `go1.x.y` but
|
||||
// can have some variations (for beta releases, for example).
|
||||
func GorootVersionString(goroot string) (string, error) {
|
||||
if data, err := os.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
|
||||
return string(data), nil
|
||||
|
||||
} else if data, err := os.ReadFile(filepath.Join(
|
||||
goroot, "src", "internal", "buildcfg", "zbootstrap.go")); err == nil {
|
||||
|
||||
r := regexp.MustCompile("const version = `(.*)`")
|
||||
matches := r.FindSubmatch(data)
|
||||
if len(matches) != 2 {
|
||||
return "", errors.New("Invalid go version output:\n" + string(data))
|
||||
}
|
||||
|
||||
return string(matches[1]), nil
|
||||
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
// toolchain. It is usually of the form `go1.x.y` but can have some variations
|
||||
// (for beta releases, for example).
|
||||
func GorootVersionString() (string, error) {
|
||||
err := readGoEnvVars()
|
||||
return goEnvVars.GOVERSION, err
|
||||
}
|
||||
|
||||
Submodule
+1
Submodule lib/renesas-svd added at 03d7688085
@@ -207,6 +207,11 @@ func listGorootMergeLinks(goroot, tinygoroot string, overrides map[string]bool)
|
||||
merges[dir] = filepath.Join(goroot, dir)
|
||||
}
|
||||
|
||||
// Required starting in Go 1.21 due to https://github.com/golang/go/issues/61928
|
||||
if _, err := os.Stat(filepath.Join(goroot, "go.env")); err == nil {
|
||||
merges["go.env"] = filepath.Join(goroot, "go.env")
|
||||
}
|
||||
|
||||
return merges, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"regexp"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -946,112 +947,130 @@ func touchSerialPortAt1200bps(port string) (err error) {
|
||||
}
|
||||
|
||||
func flashUF2UsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
|
||||
// find standard UF2 info path
|
||||
infoPaths := make([]string, 0, len(volumes))
|
||||
for _, volume := range volumes {
|
||||
switch runtime.GOOS {
|
||||
case "linux", "freebsd":
|
||||
fi, err := os.Stat("/run/media")
|
||||
if err != nil || !fi.IsDir() {
|
||||
infoPaths = append(infoPaths, "/media/*/"+volume+"/INFO_UF2.TXT")
|
||||
} else {
|
||||
infoPaths = append(infoPaths, "/run/media/*/"+volume+"/INFO_UF2.TXT")
|
||||
}
|
||||
case "darwin":
|
||||
infoPaths = append(infoPaths, "/Volumes/"+volume+"/INFO_UF2.TXT")
|
||||
case "windows":
|
||||
path, err := windowsFindUSBDrive(volume, options)
|
||||
if err == nil {
|
||||
infoPaths = append(infoPaths, path+"/INFO_UF2.TXT")
|
||||
for start := time.Now(); time.Since(start) < options.Timeout; {
|
||||
// Find a UF2 mount point.
|
||||
mounts, err := findFATMounts(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, mount := range mounts {
|
||||
for _, volume := range volumes {
|
||||
if mount.name != volume {
|
||||
continue
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(mount.path, "INFO_UF2.TXT")); err != nil {
|
||||
// No INFO_UF2.TXT found, which is expected on a UF2
|
||||
// filesystem.
|
||||
continue
|
||||
}
|
||||
// Found the filesystem, so flash the device!
|
||||
return moveFile(tmppath, filepath.Join(mount.path, "flash.uf2"))
|
||||
}
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
|
||||
d, err := locateDevice(volumes, infoPaths, options.Timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return moveFile(tmppath, filepath.Dir(d)+"/flash.uf2")
|
||||
return errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
|
||||
}
|
||||
|
||||
func flashHexUsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
|
||||
// find expected volume path
|
||||
destPaths := make([]string, 0, len(volumes))
|
||||
for _, volume := range volumes {
|
||||
switch runtime.GOOS {
|
||||
case "linux", "freebsd":
|
||||
fi, err := os.Stat("/run/media")
|
||||
if err != nil || !fi.IsDir() {
|
||||
destPaths = append(destPaths, "/media/*/"+volume)
|
||||
} else {
|
||||
destPaths = append(destPaths, "/run/media/*/"+volume)
|
||||
}
|
||||
case "darwin":
|
||||
destPaths = append(destPaths, "/Volumes/"+volume)
|
||||
case "windows":
|
||||
path, err := windowsFindUSBDrive(volume, options)
|
||||
if err == nil {
|
||||
destPaths = append(destPaths, path+"/")
|
||||
for start := time.Now(); time.Since(start) < options.Timeout; {
|
||||
// Find all mount points.
|
||||
mounts, err := findFATMounts(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, mount := range mounts {
|
||||
for _, volume := range volumes {
|
||||
if mount.name != volume {
|
||||
continue
|
||||
}
|
||||
// Found the filesystem, so flash the device!
|
||||
return moveFile(tmppath, filepath.Join(mount.path, "flash.hex"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d, err := locateDevice(volumes, destPaths, options.Timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return moveFile(tmppath, d+"/flash.hex")
|
||||
}
|
||||
|
||||
func locateDevice(volumes, paths []string, timeout time.Duration) (string, error) {
|
||||
var d []string
|
||||
var err error
|
||||
for start := time.Now(); time.Since(start) < timeout; {
|
||||
for _, path := range paths {
|
||||
d, err = filepath.Glob(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if d != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if d != nil {
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
if d == nil {
|
||||
return "", errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
|
||||
}
|
||||
return d[0], nil
|
||||
return errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
|
||||
}
|
||||
|
||||
func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, error) {
|
||||
cmd := executeCommand(options, "wmic",
|
||||
"PATH", "Win32_LogicalDisk", "WHERE", "VolumeName = '"+volume+"'",
|
||||
"get", "DeviceID,VolumeName,FileSystem,DriveType")
|
||||
type mountPoint struct {
|
||||
name string
|
||||
path string
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(out.String(), "\n") {
|
||||
words := strings.Fields(line)
|
||||
if len(words) >= 3 {
|
||||
if words[1] == "2" && words[2] == "FAT" {
|
||||
return words[0], nil
|
||||
}
|
||||
// Find all the mount points on the system that use the FAT filesystem.
|
||||
func findFATMounts(options *compileopts.Options) ([]mountPoint, error) {
|
||||
var points []mountPoint
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
list, err := os.ReadDir("/Volumes")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not list mount points: %w", err)
|
||||
}
|
||||
for _, elem := range list {
|
||||
// TODO: find a way to check for the filesystem type.
|
||||
// (Only return FAT filesystems).
|
||||
points = append(points, mountPoint{
|
||||
name: elem.Name(),
|
||||
path: filepath.Join("/Volumes", elem.Name()),
|
||||
})
|
||||
}
|
||||
sort.Slice(points, func(i, j int) bool {
|
||||
return points[i].path < points[j].name
|
||||
})
|
||||
return points, nil
|
||||
case "linux":
|
||||
tab, err := os.ReadFile("/proc/mounts") // symlink to /proc/self/mounts on my system
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not list mount points: %w", err)
|
||||
}
|
||||
for _, line := range strings.Split(string(tab), "\n") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) <= 2 {
|
||||
continue
|
||||
}
|
||||
fstype := fields[2]
|
||||
if fstype != "vfat" {
|
||||
continue
|
||||
}
|
||||
points = append(points, mountPoint{
|
||||
name: filepath.Base(fields[1]),
|
||||
path: fields[1],
|
||||
})
|
||||
}
|
||||
return points, nil
|
||||
case "windows":
|
||||
// Obtain a list of all currently mounted volumes.
|
||||
cmd := executeCommand(options, "wmic",
|
||||
"PATH", "Win32_LogicalDisk",
|
||||
"get", "DeviceID,VolumeName,FileSystem,DriveType")
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not list mount points: %w", err)
|
||||
}
|
||||
|
||||
// Extract data to convert to a []mountPoint slice.
|
||||
for _, line := range strings.Split(out.String(), "\n") {
|
||||
words := strings.Fields(line)
|
||||
if len(words) < 3 {
|
||||
continue
|
||||
}
|
||||
if words[1] != "2" || words[2] != "FAT" {
|
||||
// - DriveType 2 is removable (which we're looking for).
|
||||
// - We only want to return FAT filesystems.
|
||||
continue
|
||||
}
|
||||
points = append(points, mountPoint{
|
||||
name: words[3],
|
||||
path: words[0],
|
||||
})
|
||||
}
|
||||
return points, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown GOOS for listing mount points: %s", runtime.GOOS)
|
||||
}
|
||||
return "", errors.New("unable to locate a USB device to be flashed")
|
||||
}
|
||||
|
||||
// getDefaultPort returns the default serial port depending on the operating system.
|
||||
@@ -1777,15 +1796,17 @@ func main() {
|
||||
}
|
||||
if flagJSON {
|
||||
json, _ := json.MarshalIndent(struct {
|
||||
GOROOT string `json:"goroot"`
|
||||
GOOS string `json:"goos"`
|
||||
GOARCH string `json:"goarch"`
|
||||
GOARM string `json:"goarm"`
|
||||
BuildTags []string `json:"build_tags"`
|
||||
GC string `json:"garbage_collector"`
|
||||
Scheduler string `json:"scheduler"`
|
||||
LLVMTriple string `json:"llvm_triple"`
|
||||
Target *compileopts.TargetSpec `json:"target"`
|
||||
GOROOT string `json:"goroot"`
|
||||
GOOS string `json:"goos"`
|
||||
GOARCH string `json:"goarch"`
|
||||
GOARM string `json:"goarm"`
|
||||
BuildTags []string `json:"build_tags"`
|
||||
GC string `json:"garbage_collector"`
|
||||
Scheduler string `json:"scheduler"`
|
||||
LLVMTriple string `json:"llvm_triple"`
|
||||
}{
|
||||
Target: config.Target,
|
||||
GOROOT: cachedGOROOT,
|
||||
GOOS: config.GOOS(),
|
||||
GOARCH: config.GOARCH(),
|
||||
@@ -1852,7 +1873,7 @@ func main() {
|
||||
usage(command)
|
||||
case "version":
|
||||
goversion := "<unknown>"
|
||||
if s, err := goenv.GorootVersionString(goenv.Get("GOROOT")); err == nil {
|
||||
if s, err := goenv.GorootVersionString(); err == nil {
|
||||
goversion = s
|
||||
}
|
||||
version := goenv.Version
|
||||
|
||||
+24
-2
@@ -34,6 +34,16 @@ var supportedLinuxArches = map[string]string{
|
||||
"X86Linux": "linux/386",
|
||||
"ARMLinux": "linux/arm/6",
|
||||
"ARM64Linux": "linux/arm64",
|
||||
"WASIp1": "wasip1/wasm",
|
||||
}
|
||||
|
||||
func init() {
|
||||
major, _, _ := goenv.GetGorootVersion()
|
||||
if major < 21 {
|
||||
// Go 1.20 backwards compatibility.
|
||||
// Should be removed once we drop support for Go 1.20.
|
||||
delete(supportedLinuxArches, "WASIp1")
|
||||
}
|
||||
}
|
||||
|
||||
var sema = make(chan struct{}, runtime.NumCPU())
|
||||
@@ -71,6 +81,16 @@ func TestBuild(t *testing.T) {
|
||||
"zeroalloc.go",
|
||||
}
|
||||
|
||||
// Go 1.21 made some changes to the language, which we can only test when
|
||||
// we're actually on Go 1.21.
|
||||
_, minor, err := goenv.GetGorootVersion()
|
||||
if err != nil {
|
||||
t.Fatal("could not get version:", minor)
|
||||
}
|
||||
if minor >= 21 {
|
||||
tests = append(tests, "go1.21.go")
|
||||
}
|
||||
|
||||
if *testTarget != "" {
|
||||
// This makes it possible to run one specific test (instead of all),
|
||||
// which is especially useful to quickly check whether some changes
|
||||
@@ -166,6 +186,8 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
||||
t.Fatal("failed to load target spec:", err)
|
||||
}
|
||||
|
||||
isWebAssembly := options.Target == "wasi" || options.Target == "wasm" || (options.Target == "" && options.GOARCH == "wasm")
|
||||
|
||||
for _, name := range tests {
|
||||
if options.GOOS == "linux" && (options.GOARCH == "arm" || options.GOARCH == "386") {
|
||||
switch name {
|
||||
@@ -216,7 +238,7 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
||||
runTest("env.go", options, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
|
||||
})
|
||||
}
|
||||
if options.Target == "wasi" || options.Target == "wasm" {
|
||||
if isWebAssembly {
|
||||
t.Run("alias.go-scheduler-none", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
options := compileopts.Options(options)
|
||||
@@ -236,7 +258,7 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
||||
runTest("rand.go", options, t, nil, nil)
|
||||
})
|
||||
}
|
||||
if options.Target != "wasi" && options.Target != "wasm" {
|
||||
if !isWebAssembly {
|
||||
// The recover() builtin isn't supported yet on WebAssembly and Windows.
|
||||
t.Run("recover.go", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
//go:build arduino
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
const (
|
||||
button = machine.D2
|
||||
buttonMode = machine.PinInputPullup
|
||||
buttonPinChange = machine.PinRising
|
||||
)
|
||||
@@ -5,6 +5,7 @@ package main
|
||||
import "machine"
|
||||
|
||||
const (
|
||||
button = machine.BUTTON
|
||||
buttonMode = machine.PinInputPulldown
|
||||
buttonPinChange = machine.PinFalling
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ package main
|
||||
import "machine"
|
||||
|
||||
const (
|
||||
button = machine.BUTTON
|
||||
buttonMode = machine.PinInputPullup
|
||||
buttonPinChange = machine.PinRising
|
||||
)
|
||||
|
||||
@@ -8,18 +8,14 @@ package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"runtime/volatile"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
button = machine.BUTTON
|
||||
led = machine.LED
|
||||
led = machine.LED
|
||||
)
|
||||
|
||||
func main() {
|
||||
var lightLed volatile.Register8
|
||||
lightLed.Set(0)
|
||||
|
||||
// Configure the LED, defaulting to on (usually setting the pin to low will
|
||||
// turn the LED on).
|
||||
@@ -33,13 +29,7 @@ func main() {
|
||||
|
||||
// Set an interrupt on this pin.
|
||||
err := button.SetInterrupt(buttonPinChange, func(machine.Pin) {
|
||||
if lightLed.Get() != 0 {
|
||||
lightLed.Set(0)
|
||||
led.Low()
|
||||
} else {
|
||||
lightLed.Set(1)
|
||||
led.High()
|
||||
}
|
||||
led.Set(!led.Get())
|
||||
})
|
||||
if err != nil {
|
||||
println("could not configure pin interrupt:", err.Error())
|
||||
|
||||
@@ -5,6 +5,7 @@ package main
|
||||
import "machine"
|
||||
|
||||
const (
|
||||
button = machine.BUTTON
|
||||
buttonMode = machine.PinInputPulldown
|
||||
buttonPinChange = machine.PinRising | machine.PinFalling
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ package main
|
||||
import "machine"
|
||||
|
||||
const (
|
||||
button = machine.BUTTON
|
||||
buttonMode = machine.PinInput
|
||||
buttonPinChange = machine.PinFalling
|
||||
)
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
// This example demonstrates how to set the system time.
|
||||
//
|
||||
// Usually, boards don't keep time on power cycles and restarts, it resets to Unix epoch.
|
||||
// The system time can be set by calling runtime.AdjustTimeOffset().
|
||||
//
|
||||
// Possible time sources: an external RTC clock or network (WiFi access point or NTP server)
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
const myTime = "2006-01-02T15:04:05Z" // this is an example time you source somewhere
|
||||
|
||||
func main() {
|
||||
|
||||
// measure how many nanoseconds the internal clock is behind
|
||||
timeOfMeasurement := time.Now()
|
||||
actualTime, _ := time.Parse(time.RFC3339, myTime)
|
||||
offset := actualTime.Sub(timeOfMeasurement)
|
||||
|
||||
// adjust internal clock by adding the offset to the internal clock
|
||||
runtime.AdjustTimeOffset(int64(offset))
|
||||
|
||||
for {
|
||||
fmt.Printf("%v\r\n", time.Now().Format(time.RFC3339))
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
//sleep for 2 secs for console
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
config := machine.WatchdogConfig{
|
||||
TimeoutMillis: 1000,
|
||||
}
|
||||
|
||||
println("configuring watchdog for max 1 second updates")
|
||||
machine.Watchdog.Configure(config)
|
||||
|
||||
// From this point the watchdog is running and Update must be
|
||||
// called periodically, per the config
|
||||
machine.Watchdog.Start()
|
||||
|
||||
// This loop should complete because watchdog update is called
|
||||
// every 100ms.
|
||||
start := time.Now()
|
||||
println("updating watchdog for 3 seconds")
|
||||
for i := 0; i < 30; i++ {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
machine.Watchdog.Update()
|
||||
fmt.Printf("alive @ %v\n", time.Now().Sub(start))
|
||||
}
|
||||
|
||||
// This loop should cause a watchdog reset after 1s since
|
||||
// there is no update call.
|
||||
start = time.Now()
|
||||
println("entering tight loop")
|
||||
for {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
fmt.Printf("alive @ %v\n", time.Now().Sub(start))
|
||||
}
|
||||
}
|
||||
@@ -251,3 +251,13 @@ func IndexRabinKarp(s, substr string) int {
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// MakeNoZero makes a slice of length and capacity n without zeroing the bytes.
|
||||
// It is the caller's responsibility to ensure uninitialized bytes
|
||||
// do not leak to the end user.
|
||||
func MakeNoZero(n int) []byte {
|
||||
// Note: this does zero the buffer even though that's not necessary.
|
||||
// For performance reasons we might want to change this (similar to the
|
||||
// malloc function implemented in the runtime).
|
||||
return make([]byte, n)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
//go:build ae_rp2040
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/rp"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// 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
|
||||
GP26 Pin = GPIO26
|
||||
GP27 Pin = GPIO27
|
||||
GP28 Pin = GPIO28
|
||||
GP29 Pin = GPIO29
|
||||
|
||||
// Onboard crystal oscillator frequency, in MHz.
|
||||
xoscFreq = 12 // MHz
|
||||
)
|
||||
|
||||
// I2C Default pins on Raspberry Pico.
|
||||
const (
|
||||
I2C0_SDA_PIN = GP4
|
||||
I2C0_SCL_PIN = GP5
|
||||
|
||||
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 = GPIO10
|
||||
// Default Serial Out Bus 1 for SPI communications
|
||||
SPI1_SDO_PIN = GPIO11 // Tx
|
||||
// Default Serial In Bus 1 for SPI communications
|
||||
SPI1_SDI_PIN = GPIO12 // Rx
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART0_TX_PIN = GPIO0
|
||||
UART0_RX_PIN = GPIO1
|
||||
UART1_TX_PIN = GPIO8
|
||||
UART1_RX_PIN = GPIO9
|
||||
UART_TX_PIN = UART0_TX_PIN
|
||||
UART_RX_PIN = UART0_RX_PIN
|
||||
)
|
||||
|
||||
// UART on the RP2040
|
||||
var (
|
||||
UART0 = &_UART0
|
||||
_UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: rp.UART0,
|
||||
}
|
||||
|
||||
UART1 = &_UART1
|
||||
_UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: rp.UART1,
|
||||
}
|
||||
)
|
||||
|
||||
var DefaultUART = UART0
|
||||
|
||||
func init() {
|
||||
UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.handleInterrupt)
|
||||
UART1.Interrupt = interrupt.New(rp.IRQ_UART1_IRQ, _UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// USB identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "AE-RP2040"
|
||||
usb_STRING_MANUFACTURER = "AKIZUKI DENSHI"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x2E8A
|
||||
usb_PID uint16 = 0x000A
|
||||
)
|
||||
@@ -16,6 +16,7 @@ const (
|
||||
|
||||
const (
|
||||
SWITCH = GPIO0
|
||||
BUTTON = GPIO0
|
||||
|
||||
KEY1 = GPIO1
|
||||
KEY2 = GPIO2
|
||||
|
||||
@@ -97,7 +97,7 @@ func (i2c *I2C) stop() {
|
||||
}
|
||||
|
||||
// writeByte writes a single byte to the I2C bus.
|
||||
func (i2c *I2C) writeByte(data byte) {
|
||||
func (i2c *I2C) writeByte(data byte) error {
|
||||
// Write data to register.
|
||||
avr.TWDR.Set(data)
|
||||
|
||||
@@ -107,6 +107,7 @@ func (i2c *I2C) writeByte(data byte) {
|
||||
// Wait till data is transmitted.
|
||||
for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// readByte reads a single byte from the I2C bus.
|
||||
@@ -190,7 +191,7 @@ func (uart *UART) handleInterrupt(intr interrupt.Interrupt) {
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the UART.
|
||||
func (uart *UART) WriteByte(c byte) error {
|
||||
func (uart *UART) writeByte(c byte) error {
|
||||
// Wait until UART buffer is not busy.
|
||||
for !uart.statusRegA.HasBits(avr.UCSR0A_UDRE0) {
|
||||
}
|
||||
@@ -198,6 +199,8 @@ func (uart *UART) WriteByte(c byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {}
|
||||
|
||||
// SPIConfig is used to store config info for SPI.
|
||||
type SPIConfig struct {
|
||||
Frequency uint32
|
||||
|
||||
@@ -466,3 +466,107 @@ var SPI0 = SPI{
|
||||
sdo: PB3,
|
||||
sdi: PB4,
|
||||
cs: PB2}
|
||||
|
||||
// Pin Change Interrupts
|
||||
type PinChange uint8
|
||||
|
||||
const (
|
||||
PinRising PinChange = 1 << iota
|
||||
PinFalling
|
||||
PinToggle = PinRising | PinFalling
|
||||
)
|
||||
|
||||
func (pin Pin) SetInterrupt(pinChange PinChange, callback func(Pin)) (err error) {
|
||||
|
||||
switch {
|
||||
case pin >= PB0 && pin <= PB7:
|
||||
// PCMSK0 - PCINT0-7
|
||||
pinStates[0] = avr.PINB.Get()
|
||||
pinIndex := pin - PB0
|
||||
if pinChange&PinRising > 0 {
|
||||
pinCallbacks[0][pinIndex][0] = callback
|
||||
}
|
||||
if pinChange&PinFalling > 0 {
|
||||
pinCallbacks[0][pinIndex][1] = callback
|
||||
}
|
||||
if callback != nil {
|
||||
avr.PCMSK0.SetBits(1 << pinIndex)
|
||||
} else {
|
||||
avr.PCMSK0.ClearBits(1 << pinIndex)
|
||||
}
|
||||
avr.PCICR.SetBits(avr.PCICR_PCIE0)
|
||||
interrupt.New(avr.IRQ_PCINT0, handlePCINT0Interrupts)
|
||||
case pin >= PC0 && pin <= PC7:
|
||||
// PCMSK1 - PCINT8-14
|
||||
pinStates[1] = avr.PINC.Get()
|
||||
pinIndex := pin - PC0
|
||||
if pinChange&PinRising > 0 {
|
||||
pinCallbacks[1][pinIndex][0] = callback
|
||||
}
|
||||
if pinChange&PinFalling > 0 {
|
||||
pinCallbacks[1][pinIndex][1] = callback
|
||||
}
|
||||
if callback != nil {
|
||||
avr.PCMSK1.SetBits(1 << pinIndex)
|
||||
} else {
|
||||
avr.PCMSK1.ClearBits(1 << pinIndex)
|
||||
}
|
||||
avr.PCICR.SetBits(avr.PCICR_PCIE1)
|
||||
interrupt.New(avr.IRQ_PCINT1, handlePCINT1Interrupts)
|
||||
case pin >= PD0 && pin <= PD7:
|
||||
// PCMSK2 - PCINT16-23
|
||||
pinStates[2] = avr.PIND.Get()
|
||||
pinIndex := pin - PD0
|
||||
if pinChange&PinRising > 0 {
|
||||
pinCallbacks[2][pinIndex][0] = callback
|
||||
}
|
||||
if pinChange&PinFalling > 0 {
|
||||
pinCallbacks[2][pinIndex][1] = callback
|
||||
}
|
||||
if callback != nil {
|
||||
avr.PCMSK2.SetBits(1 << pinIndex)
|
||||
} else {
|
||||
avr.PCMSK2.ClearBits(1 << pinIndex)
|
||||
}
|
||||
avr.PCICR.SetBits(avr.PCICR_PCIE2)
|
||||
interrupt.New(avr.IRQ_PCINT2, handlePCINT2Interrupts)
|
||||
default:
|
||||
return ErrInvalidInputPin
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var pinCallbacks [3][8][2]func(Pin)
|
||||
var pinStates [3]uint8
|
||||
|
||||
func handlePCINTInterrupts(intr uint8, port *volatile.Register8) {
|
||||
current := port.Get()
|
||||
change := pinStates[intr] ^ current
|
||||
pinStates[intr] = current
|
||||
for i := uint8(0); i < 8; i++ {
|
||||
if (change>>i)&0x01 != 0x01 {
|
||||
continue
|
||||
}
|
||||
pin := Pin(intr*8 + i)
|
||||
value := pin.Get()
|
||||
if value && pinCallbacks[intr][i][0] != nil {
|
||||
pinCallbacks[intr][i][0](pin)
|
||||
}
|
||||
if !value && pinCallbacks[intr][i][1] != nil {
|
||||
pinCallbacks[intr][i][1](pin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handlePCINT0Interrupts(intr interrupt.Interrupt) {
|
||||
handlePCINTInterrupts(0, avr.PINB)
|
||||
}
|
||||
|
||||
func handlePCINT1Interrupts(intr interrupt.Interrupt) {
|
||||
handlePCINTInterrupts(1, avr.PINC)
|
||||
}
|
||||
|
||||
func handlePCINT2Interrupts(intr interrupt.Interrupt) {
|
||||
handlePCINTInterrupts(2, avr.PIND)
|
||||
}
|
||||
|
||||
@@ -626,7 +626,7 @@ func (uart *UART) SetBaudRate(br uint32) {
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the UART.
|
||||
func (uart *UART) WriteByte(c byte) error {
|
||||
func (uart *UART) writeByte(c byte) error {
|
||||
// wait until ready to receive
|
||||
for !uart.Bus.INTFLAG.HasBits(sam.SERCOM_USART_INTFLAG_DRE) {
|
||||
}
|
||||
@@ -634,6 +634,8 @@ func (uart *UART) WriteByte(c byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {}
|
||||
|
||||
// handleInterrupt should be called from the appropriate interrupt handler for
|
||||
// this UART instance.
|
||||
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
|
||||
@@ -1114,7 +1114,7 @@ func (uart *UART) SetBaudRate(br uint32) {
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the UART.
|
||||
func (uart *UART) WriteByte(c byte) error {
|
||||
func (uart *UART) writeByte(c byte) error {
|
||||
// wait until ready to receive
|
||||
for !uart.Bus.INTFLAG.HasBits(sam.SERCOM_USART_INT_INTFLAG_DRE) {
|
||||
}
|
||||
@@ -1122,6 +1122,8 @@ func (uart *UART) WriteByte(c byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {}
|
||||
|
||||
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
// should reset IRQ
|
||||
uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF)))
|
||||
@@ -1162,7 +1164,7 @@ const (
|
||||
wireCmdStop = 3
|
||||
)
|
||||
|
||||
const i2cTimeout = 1000
|
||||
const i2cTimeout = 28000 // about 210us
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
@@ -2297,3 +2299,52 @@ func checkFlashError() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Watchdog provides access to the hardware watchdog available
|
||||
// in the SAMD51.
|
||||
var Watchdog = &watchdogImpl{}
|
||||
|
||||
const (
|
||||
// WatchdogMaxTimeout in milliseconds (16s)
|
||||
WatchdogMaxTimeout = (16384 * 1000) / 1024 // CYC16384/1024kHz
|
||||
)
|
||||
|
||||
type watchdogImpl struct{}
|
||||
|
||||
// Configure the watchdog.
|
||||
//
|
||||
// This method should not be called after the watchdog is started and on
|
||||
// some platforms attempting to reconfigure after starting the watchdog
|
||||
// is explicitly forbidden / will not work.
|
||||
func (wd *watchdogImpl) Configure(config WatchdogConfig) error {
|
||||
// 1.024kHz clock
|
||||
cycles := int((int64(config.TimeoutMillis) * 1024) / 1000)
|
||||
|
||||
// period is expressed as a power-of-two, starting at 8 / 1024ths of a second
|
||||
period := uint8(0)
|
||||
cfgCycles := 8
|
||||
for cfgCycles < cycles {
|
||||
period++
|
||||
cfgCycles <<= 1
|
||||
|
||||
if period >= 0xB {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
sam.WDT.CONFIG.Set(period << sam.WDT_CONFIG_PER_Pos)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Starts the watchdog.
|
||||
func (wd *watchdogImpl) Start() error {
|
||||
sam.WDT.CTRLA.SetBits(sam.WDT_CTRLA_ENABLE)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update the watchdog, indicating that `source` is healthy.
|
||||
func (wd *watchdogImpl) Update() {
|
||||
// 0xA5 = magic value (see datasheet)
|
||||
sam.WDT.CLEAR.Set(0xA5)
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ func (uart *UART) Configure(config UARTConfig) {
|
||||
uart.Bus.CLKDIV.Set(peripheralClock / config.BaudRate)
|
||||
}
|
||||
|
||||
func (uart *UART) WriteByte(b byte) error {
|
||||
func (uart *UART) writeByte(b byte) error {
|
||||
for (uart.Bus.STATUS.Get()>>16)&0xff >= 128 {
|
||||
// Read UART_TXFIFO_CNT from the status register, which indicates how
|
||||
// many bytes there are in the transmit buffer. Wait until there are
|
||||
@@ -324,6 +324,8 @@ func (uart *UART) WriteByte(b byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {}
|
||||
|
||||
// Serial Peripheral Interface on the ESP32.
|
||||
type SPI struct {
|
||||
Bus *esp.SPI_Type
|
||||
|
||||
@@ -493,7 +493,7 @@ func (uart *UART) enableReceiver() {
|
||||
uart.Bus.SetINT_ENA_RXFIFO_OVF_INT_ENA(1)
|
||||
}
|
||||
|
||||
func (uart *UART) WriteByte(b byte) error {
|
||||
func (uart *UART) writeByte(b byte) error {
|
||||
for (uart.Bus.STATUS.Get()&esp.UART_STATUS_TXFIFO_CNT_Msk)>>esp.UART_STATUS_TXFIFO_CNT_Pos >= 128 {
|
||||
// Read UART_TXFIFO_CNT from the status register, which indicates how
|
||||
// many bytes there are in the transmit buffer. Wait until there are
|
||||
@@ -502,3 +502,5 @@ func (uart *UART) WriteByte(b byte) error {
|
||||
uart.Bus.FIFO.Set(uint32(b))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {}
|
||||
|
||||
@@ -181,12 +181,14 @@ func (uart *UART) Configure(config UARTConfig) {
|
||||
esp.UART0.UART_CLKDIV.Set(CPUFrequency() / config.BaudRate)
|
||||
}
|
||||
|
||||
// WriteByte writes a single byte to the output buffer. Note that the hardware
|
||||
// writeByte writes a single byte to the output buffer. Note that the hardware
|
||||
// includes a buffer of 128 bytes which will be used first.
|
||||
func (uart *UART) WriteByte(c byte) error {
|
||||
func (uart *UART) writeByte(c byte) error {
|
||||
for (esp.UART0.UART_STATUS.Get()>>16)&0xff >= 128 {
|
||||
// Wait until the TX buffer has room.
|
||||
}
|
||||
esp.UART0.UART_FIFO.Set(uint32(c))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {}
|
||||
|
||||
@@ -111,13 +111,16 @@ func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
uart.Receive(c)
|
||||
}
|
||||
|
||||
func (uart *UART) WriteByte(c byte) {
|
||||
func (uart *UART) writeByte(c byte) error {
|
||||
for sifive.UART0.TXDATA.Get()&sifive.UART_TXDATA_FULL != 0 {
|
||||
}
|
||||
|
||||
sifive.UART0.TXDATA.Set(uint32(c))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {}
|
||||
|
||||
// SPI on the FE310. The normal SPI0 is actually a quad-SPI meant for flash, so it is best
|
||||
// to use SPI1 or SPI2 port for most applications.
|
||||
type SPI struct {
|
||||
|
||||
@@ -392,13 +392,16 @@ func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
uart.Receive(c)
|
||||
}
|
||||
|
||||
func (uart *UART) WriteByte(c byte) {
|
||||
func (uart *UART) writeByte(c byte) error {
|
||||
for uart.Bus.TXDATA.Get()&kendryte.UARTHS_TXDATA_FULL != 0 {
|
||||
}
|
||||
|
||||
uart.Bus.TXDATA.Set(uint32(c))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {}
|
||||
|
||||
type SPI struct {
|
||||
Bus *kendryte.SPI_Type
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ func (uart *UART) Sync() error {
|
||||
}
|
||||
|
||||
// WriteByte writes a single byte of data to the UART interface.
|
||||
func (uart *UART) WriteByte(c byte) error {
|
||||
func (uart *UART) writeByte(c byte) error {
|
||||
uart.startTransmitting()
|
||||
for !uart.txBuffer.Put(c) {
|
||||
}
|
||||
@@ -181,6 +181,8 @@ func (uart *UART) WriteByte(c byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {}
|
||||
|
||||
// getBaudRateDivisor finds the greatest over-sampling factor (4..32) and
|
||||
// corresponding baud rate divisor (1..8191) that best partition a given baud
|
||||
// rate into equal intervals.
|
||||
|
||||
@@ -186,7 +186,7 @@ func (uart *UART) SetBaudRate(br uint32) {
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the UART.
|
||||
func (uart *UART) WriteByte(c byte) error {
|
||||
func (uart *UART) writeByte(c byte) error {
|
||||
nrf.UART0.EVENTS_TXDRDY.Set(0)
|
||||
nrf.UART0.TXD.Set(uint32(c))
|
||||
for nrf.UART0.EVENTS_TXDRDY.Get() == 0 {
|
||||
@@ -194,6 +194,8 @@ func (uart *UART) WriteByte(c byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {}
|
||||
|
||||
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
if nrf.UART0.EVENTS_RXDRDY.Get() != 0 {
|
||||
uart.Receive(byte(nrf.UART0.RXD.Get()))
|
||||
@@ -201,6 +203,8 @@ func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
}
|
||||
}
|
||||
|
||||
const i2cTimeout = 0xffff // this is around 29ms on a nrf52
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
type I2CConfig struct {
|
||||
Frequency uint32
|
||||
@@ -259,11 +263,17 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
}
|
||||
|
||||
// signalStop sends a stop signal to the I2C peripheral and waits for confirmation.
|
||||
func (i2c *I2C) signalStop() {
|
||||
func (i2c *I2C) signalStop() error {
|
||||
tries := 0
|
||||
i2c.Bus.TASKS_STOP.Set(1)
|
||||
for i2c.Bus.EVENTS_STOPPED.Get() == 0 {
|
||||
tries++
|
||||
if tries >= i2cTimeout {
|
||||
return errI2CSignalStopTimeout
|
||||
}
|
||||
}
|
||||
i2c.Bus.EVENTS_STOPPED.Set(0)
|
||||
return nil
|
||||
}
|
||||
|
||||
var rngStarted = false
|
||||
|
||||
@@ -214,3 +214,46 @@ func twisError(val uint32) error {
|
||||
|
||||
return errI2CBusError
|
||||
}
|
||||
|
||||
var (
|
||||
Watchdog = &watchdogImpl{}
|
||||
)
|
||||
|
||||
const (
|
||||
// WatchdogMaxTimeout in milliseconds (approx 36h)
|
||||
WatchdogMaxTimeout = (0xffffffff * 1000) / 32768
|
||||
)
|
||||
|
||||
type watchdogImpl struct {
|
||||
}
|
||||
|
||||
// Configure the watchdog.
|
||||
//
|
||||
// This method should not be called after the watchdog is started and on
|
||||
// some platforms attempting to reconfigure after starting the watchdog
|
||||
// is explicitly forbidden / will not work.
|
||||
func (wd *watchdogImpl) Configure(config WatchdogConfig) error {
|
||||
// 32.768kHz counter
|
||||
crv := int32((int64(config.TimeoutMillis) * 32768) / 1000)
|
||||
nrf.WDT.CRV.Set(uint32(crv))
|
||||
|
||||
// One source
|
||||
nrf.WDT.RREN.Set(0x1)
|
||||
|
||||
// Run during sleep
|
||||
nrf.WDT.CONFIG.Set(nrf.WDT_CONFIG_SLEEP_Run)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Starts the watchdog.
|
||||
func (wd *watchdogImpl) Start() error {
|
||||
nrf.WDT.TASKS_START.Set(nrf.WDT_TASKS_START_TASKS_START)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update the watchdog, indicating that `source` is healthy.
|
||||
func (wd *watchdogImpl) Update() {
|
||||
// 0x6E524635 = magic value from datasheet
|
||||
nrf.WDT.RR[0].Set(0x6E524635)
|
||||
}
|
||||
|
||||
@@ -288,24 +288,27 @@ func (spi SPI) Tx(w, r []byte) error {
|
||||
// supported.
|
||||
for len(r) != 0 || len(w) != 0 {
|
||||
// Prepare the SPI transfer: set the DMA pointers and lengths.
|
||||
if len(r) != 0 {
|
||||
// read buffer
|
||||
nr := uint32(len(r))
|
||||
if nr > 0 {
|
||||
if nr > 255 {
|
||||
nr = 255
|
||||
}
|
||||
spi.Bus.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&r[0]))))
|
||||
n := uint32(len(r))
|
||||
if n > 255 {
|
||||
n = 255
|
||||
}
|
||||
spi.Bus.RXD.MAXCNT.Set(n)
|
||||
r = r[n:]
|
||||
r = r[nr:]
|
||||
}
|
||||
if len(w) != 0 {
|
||||
spi.Bus.RXD.MAXCNT.Set(nr)
|
||||
|
||||
// write buffer
|
||||
nw := uint32(len(w))
|
||||
if nw > 0 {
|
||||
if nw > 255 {
|
||||
nw = 255
|
||||
}
|
||||
spi.Bus.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&w[0]))))
|
||||
n := uint32(len(w))
|
||||
if n > 255 {
|
||||
n = 255
|
||||
}
|
||||
spi.Bus.TXD.MAXCNT.Set(n)
|
||||
w = w[n:]
|
||||
w = w[nw:]
|
||||
}
|
||||
spi.Bus.TXD.MAXCNT.Set(nw)
|
||||
|
||||
// Do the transfer.
|
||||
// Note: this can be improved by not waiting until the transfer is
|
||||
|
||||
@@ -70,11 +70,21 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) {
|
||||
i2c.Bus.SHORTS.Set(nrf.TWI_SHORTS_BB_SUSPEND_Disabled)
|
||||
}
|
||||
|
||||
// Stop explicitly when no reads were executed, stoping unconditionally would be a mistake.
|
||||
// It may execute after I2C peripheral has already been stopped by the shortcut in the read block,
|
||||
// so stop task will trigger first thing in a subsequent transaction, hanging it.
|
||||
if len(r) == 0 {
|
||||
i2c.signalStop()
|
||||
// Stop the I2C transaction after the write.
|
||||
err = i2c.signalStop()
|
||||
} else {
|
||||
// The last byte read has already stopped the transaction, via
|
||||
// TWI_SHORTS_BB_STOP. But we still need to wait until we receive the
|
||||
// STOPPED event.
|
||||
tries := 0
|
||||
for i2c.Bus.EVENTS_STOPPED.Get() == 0 {
|
||||
tries++
|
||||
if tries >= i2cTimeout {
|
||||
return errI2CSignalStopTimeout
|
||||
}
|
||||
}
|
||||
i2c.Bus.EVENTS_STOPPED.Set(0)
|
||||
}
|
||||
|
||||
return
|
||||
@@ -82,12 +92,17 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) {
|
||||
|
||||
// writeByte writes a single byte to the I2C bus and waits for confirmation.
|
||||
func (i2c *I2C) writeByte(data byte) error {
|
||||
tries := 0
|
||||
i2c.Bus.TXD.Set(uint32(data))
|
||||
for i2c.Bus.EVENTS_TXDSENT.Get() == 0 {
|
||||
if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 {
|
||||
i2c.Bus.EVENTS_ERROR.Set(0)
|
||||
return errI2CBusError
|
||||
}
|
||||
tries++
|
||||
if tries >= i2cTimeout {
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
}
|
||||
i2c.Bus.EVENTS_TXDSENT.Set(0)
|
||||
return nil
|
||||
@@ -95,11 +110,16 @@ func (i2c *I2C) writeByte(data byte) error {
|
||||
|
||||
// readByte reads a single byte from the I2C bus when it is ready.
|
||||
func (i2c *I2C) readByte() (byte, error) {
|
||||
tries := 0
|
||||
for i2c.Bus.EVENTS_RXDREADY.Get() == 0 {
|
||||
if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 {
|
||||
i2c.Bus.EVENTS_ERROR.Set(0)
|
||||
return 0, errI2CBusError
|
||||
}
|
||||
tries++
|
||||
if tries >= i2cTimeout {
|
||||
return 0, errI2CReadTimeout
|
||||
}
|
||||
}
|
||||
i2c.Bus.EVENTS_RXDREADY.Set(0)
|
||||
return byte(i2c.Bus.RXD.Get()), nil
|
||||
|
||||
@@ -292,7 +292,7 @@ func (u *UART) handleStatusInterrupt(interrupt.Interrupt) {
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the UART.
|
||||
func (u *UART) WriteByte(c byte) error {
|
||||
func (u *UART) writeByte(c byte) error {
|
||||
if !u.Configured {
|
||||
return ErrNotConfigured
|
||||
}
|
||||
@@ -305,3 +305,5 @@ func (u *UART) WriteByte(c byte) error {
|
||||
u.C2.Set(uartC2TXActive)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {}
|
||||
|
||||
@@ -182,7 +182,7 @@ func (clk *clock) configure(src, auxsrc, srcFreq, freq uint32) {
|
||||
// Must be called before any other clock function.
|
||||
func (clks *clocksType) init() {
|
||||
// Start the watchdog tick
|
||||
watchdog.startTick(xoscFreq)
|
||||
Watchdog.startTick(xoscFreq)
|
||||
|
||||
// Disable resus that may be enabled from previous software
|
||||
clks.resus.ctrl.Set(0)
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
//go:build rp2040
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// EnterBootloader should perform a system reset in preparation
|
||||
// to switch to the bootloader to flash new firmware.
|
||||
func EnterBootloader() {
|
||||
enterBootloader()
|
||||
}
|
||||
|
||||
// compile-time check for ensuring we fulfill BlockDevice interface
|
||||
var _ BlockDevice = flashBlockDevice{}
|
||||
|
||||
var Flash flashBlockDevice
|
||||
|
||||
type flashBlockDevice struct {
|
||||
}
|
||||
|
||||
// ReadAt reads the given number of bytes from the block device.
|
||||
func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
if readAddress(off) > FlashDataEnd() {
|
||||
return 0, errFlashCannotReadPastEOF
|
||||
}
|
||||
|
||||
data := unsafe.Slice((*byte)(unsafe.Pointer(readAddress(off))), len(p))
|
||||
copy(p, data)
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// WriteAt writes the given number of bytes to the block device.
|
||||
// Only word (32 bits) length data can be programmed.
|
||||
// If the length of p is not long enough it will be padded with 0xFF bytes.
|
||||
// This method assumes that the destination is already erased.
|
||||
func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
|
||||
return f.writeAt(p, off)
|
||||
}
|
||||
|
||||
// Size returns the number of bytes in this block device.
|
||||
func (f flashBlockDevice) Size() int64 {
|
||||
return int64(FlashDataEnd() - FlashDataStart())
|
||||
}
|
||||
|
||||
const writeBlockSize = 1 << 8
|
||||
|
||||
// WriteBlockSize returns the block size in which data can be written to
|
||||
// memory. It can be used by a client to optimize writes, non-aligned writes
|
||||
// should always work correctly.
|
||||
func (f flashBlockDevice) WriteBlockSize() int64 {
|
||||
return writeBlockSize
|
||||
}
|
||||
|
||||
const eraseBlockSizeValue = 1 << 12
|
||||
|
||||
func eraseBlockSize() int64 {
|
||||
return eraseBlockSizeValue
|
||||
}
|
||||
|
||||
// EraseBlockSize returns the smallest erasable area on this particular chip
|
||||
// in bytes. This is used for the block size in EraseBlocks.
|
||||
func (f flashBlockDevice) EraseBlockSize() int64 {
|
||||
return eraseBlockSize()
|
||||
}
|
||||
|
||||
// EraseBlocks erases the given number of blocks. An implementation may
|
||||
// transparently coalesce ranges of blocks into larger bundles if the chip
|
||||
// supports this. The start and len parameters are in block numbers, use
|
||||
// EraseBlockSize to map addresses to blocks.
|
||||
func (f flashBlockDevice) EraseBlocks(start, length int64) error {
|
||||
return f.eraseBlocks(start, length)
|
||||
}
|
||||
|
||||
// pad data if needed so it is long enough for correct byte alignment on writes.
|
||||
func (f flashBlockDevice) pad(p []byte) []byte {
|
||||
overflow := int64(len(p)) % f.WriteBlockSize()
|
||||
if overflow == 0 {
|
||||
return p
|
||||
}
|
||||
|
||||
padding := bytes.Repeat([]byte{0xff}, int(f.WriteBlockSize()-overflow))
|
||||
return append(p, padding...)
|
||||
}
|
||||
|
||||
// return the correct address to be used for write
|
||||
func writeAddress(off int64) uintptr {
|
||||
return readAddress(off) - uintptr(memoryStart)
|
||||
}
|
||||
|
||||
// return the correct address to be used for reads
|
||||
func readAddress(off int64) uintptr {
|
||||
return FlashDataStart() + uintptr(off)
|
||||
}
|
||||
@@ -174,6 +174,9 @@ func (p Pin) init() {
|
||||
|
||||
// Configure configures the gpio pin as per mode.
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
if p == NoPin {
|
||||
return
|
||||
}
|
||||
p.init()
|
||||
mask := uint32(1) << p
|
||||
switch config.Mode {
|
||||
@@ -213,6 +216,9 @@ func (p Pin) Configure(config PinConfig) {
|
||||
|
||||
// Set drives the pin high if value is true else drives it low.
|
||||
func (p Pin) Set(value bool) {
|
||||
if p == NoPin {
|
||||
return
|
||||
}
|
||||
if value {
|
||||
p.set()
|
||||
} else {
|
||||
@@ -251,6 +257,9 @@ var (
|
||||
// nil func to unset the pin change interrupt. If you do so, the change
|
||||
// parameter is ignored and can be set to any value (such as 0).
|
||||
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
|
||||
if p == NoPin {
|
||||
return nil
|
||||
}
|
||||
if p > 31 || p < 0 {
|
||||
return ErrInvalidInputPin
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -136,40 +135,14 @@ void ram_func flash_erase_blocks(uint32_t offset, size_t count)
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// EnterBootloader should perform a system reset in preparation
|
||||
// to switch to the bootloader to flash new firmware.
|
||||
func EnterBootloader() {
|
||||
func enterBootloader() {
|
||||
C.reset_usb_boot(0, 0)
|
||||
}
|
||||
|
||||
// Flash related code
|
||||
const memoryStart = C.XIP_BASE // memory start for purpose of erase
|
||||
|
||||
// compile-time check for ensuring we fulfill BlockDevice interface
|
||||
var _ BlockDevice = flashBlockDevice{}
|
||||
|
||||
var Flash flashBlockDevice
|
||||
|
||||
type flashBlockDevice struct {
|
||||
}
|
||||
|
||||
// ReadAt reads the given number of bytes from the block device.
|
||||
func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
if readAddress(off) > FlashDataEnd() {
|
||||
return 0, errFlashCannotReadPastEOF
|
||||
}
|
||||
|
||||
data := unsafe.Slice((*byte)(unsafe.Pointer(readAddress(off))), len(p))
|
||||
copy(p, data)
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// WriteAt writes the given number of bytes to the block device.
|
||||
// Only word (32 bits) length data can be programmed.
|
||||
// If the length of p is not long enough it will be padded with 0xFF bytes.
|
||||
// This method assumes that the destination is already erased.
|
||||
func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
|
||||
func (f flashBlockDevice) writeAt(p []byte, off int64) (n int, err error) {
|
||||
if writeAddress(off)+uintptr(C.XIP_BASE) > FlashDataEnd() {
|
||||
return 0, errFlashCannotWritePastEOF
|
||||
}
|
||||
@@ -190,37 +163,7 @@ func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
|
||||
return len(padded), nil
|
||||
}
|
||||
|
||||
// Size returns the number of bytes in this block device.
|
||||
func (f flashBlockDevice) Size() int64 {
|
||||
return int64(FlashDataEnd() - FlashDataStart())
|
||||
}
|
||||
|
||||
const writeBlockSize = 1 << 8
|
||||
|
||||
// WriteBlockSize returns the block size in which data can be written to
|
||||
// memory. It can be used by a client to optimize writes, non-aligned writes
|
||||
// should always work correctly.
|
||||
func (f flashBlockDevice) WriteBlockSize() int64 {
|
||||
return writeBlockSize
|
||||
}
|
||||
|
||||
const eraseBlockSizeValue = 1 << 12
|
||||
|
||||
func eraseBlockSize() int64 {
|
||||
return eraseBlockSizeValue
|
||||
}
|
||||
|
||||
// EraseBlockSize returns the smallest erasable area on this particular chip
|
||||
// in bytes. This is used for the block size in EraseBlocks.
|
||||
func (f flashBlockDevice) EraseBlockSize() int64 {
|
||||
return eraseBlockSize()
|
||||
}
|
||||
|
||||
// EraseBlocks erases the given number of blocks. An implementation may
|
||||
// transparently coalesce ranges of blocks into larger bundles if the chip
|
||||
// supports this. The start and len parameters are in block numbers, use
|
||||
// EraseBlockSize to map addresses to blocks.
|
||||
func (f flashBlockDevice) EraseBlocks(start, length int64) error {
|
||||
func (f flashBlockDevice) eraseBlocks(start, length int64) error {
|
||||
address := writeAddress(start * f.EraseBlockSize())
|
||||
if address+uintptr(C.XIP_BASE) > FlashDataEnd() {
|
||||
return errFlashCannotErasePastEOF
|
||||
@@ -233,24 +176,3 @@ func (f flashBlockDevice) EraseBlocks(start, length int64) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// pad data if needed so it is long enough for correct byte alignment on writes.
|
||||
func (f flashBlockDevice) pad(p []byte) []byte {
|
||||
overflow := int64(len(p)) % f.WriteBlockSize()
|
||||
if overflow == 0 {
|
||||
return p
|
||||
}
|
||||
|
||||
padding := bytes.Repeat([]byte{0xff}, int(f.WriteBlockSize()-overflow))
|
||||
return append(p, padding...)
|
||||
}
|
||||
|
||||
// return the correct address to be used for write
|
||||
func writeAddress(off int64) uintptr {
|
||||
return readAddress(off) - uintptr(C.XIP_BASE)
|
||||
}
|
||||
|
||||
// return the correct address to be used for reads
|
||||
func readAddress(off int64) uintptr {
|
||||
return FlashDataStart() + uintptr(off)
|
||||
}
|
||||
|
||||
@@ -86,9 +86,10 @@ func (uart *UART) SetBaudRate(br uint32) {
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the UART.
|
||||
func (uart *UART) WriteByte(c byte) error {
|
||||
func (uart *UART) writeByte(c byte) error {
|
||||
// wait until buffer is not full
|
||||
for uart.Bus.UARTFR.HasBits(rp.UART0_UARTFR_TXFF) {
|
||||
gosched()
|
||||
}
|
||||
|
||||
// write data
|
||||
@@ -96,6 +97,12 @@ func (uart *UART) WriteByte(c byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {
|
||||
for uart.Bus.UARTFR.HasBits(rp.UART0_UARTFR_BUSY) {
|
||||
gosched()
|
||||
}
|
||||
}
|
||||
|
||||
// SetFormat for number of data bits, stop bits, and parity for the UART.
|
||||
func (uart *UART) SetFormat(databits, stopbits uint8, parity UARTParity) error {
|
||||
var pen, pev uint8
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/rp"
|
||||
)
|
||||
|
||||
@@ -87,6 +88,7 @@ func hw_enumeration_fix_force_ls_j() {
|
||||
rp.USBCTRL_REGS.USB_MUXING.Set(rp.USBCTRL_REGS_USB_MUXING_TO_DIGITAL_PAD | rp.USBCTRL_REGS_USB_MUXING_SOFTCON)
|
||||
|
||||
// LS_J is now forced but while loop here just to check
|
||||
waitCycles(125000)
|
||||
|
||||
// if timer pool disabled, or no timer available, have to busy wait.
|
||||
hw_enumeration_fix_finish()
|
||||
@@ -109,3 +111,10 @@ func hw_enumeration_fix_finish() {
|
||||
// Restore the pad ctrl value
|
||||
padsBank0.io[dp].Set(padCtrlPrev)
|
||||
}
|
||||
|
||||
func waitCycles(n int) {
|
||||
for n > 0 {
|
||||
arm.Asm("nop")
|
||||
n--
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,24 +4,67 @@ package machine
|
||||
|
||||
import (
|
||||
"device/rp"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type watchdogType struct {
|
||||
ctrl volatile.Register32
|
||||
load volatile.Register32
|
||||
reason volatile.Register32
|
||||
scratch [8]volatile.Register32
|
||||
tick volatile.Register32
|
||||
// Watchdog provides access to the hardware watchdog available
|
||||
// in the RP2040.
|
||||
var Watchdog = &watchdogImpl{}
|
||||
|
||||
const (
|
||||
// WatchdogMaxTimeout in milliseconds (approx 8.3s).
|
||||
//
|
||||
// Nominal 1us per watchdog tick, 24-bit counter,
|
||||
// but due to errata two ticks consumed per 1us.
|
||||
// See: Errata RP2040-E1
|
||||
WatchdogMaxTimeout = (rp.WATCHDOG_LOAD_LOAD_Msk / 1000) / 2
|
||||
)
|
||||
|
||||
type watchdogImpl struct {
|
||||
// The value to reset the counter to on each Update
|
||||
loadValue uint32
|
||||
}
|
||||
|
||||
var watchdog = (*watchdogType)(unsafe.Pointer(rp.WATCHDOG))
|
||||
// Configure the watchdog.
|
||||
//
|
||||
// This method should not be called after the watchdog is started and on
|
||||
// some platforms attempting to reconfigure after starting the watchdog
|
||||
// is explicitly forbidden / will not work.
|
||||
func (wd *watchdogImpl) Configure(config WatchdogConfig) error {
|
||||
// x2 due to errata RP2040-E1
|
||||
wd.loadValue = config.TimeoutMillis * 1000 * 2
|
||||
if wd.loadValue > rp.WATCHDOG_LOAD_LOAD_Msk {
|
||||
wd.loadValue = rp.WATCHDOG_LOAD_LOAD_Msk
|
||||
}
|
||||
|
||||
rp.WATCHDOG.CTRL.ClearBits(rp.WATCHDOG_CTRL_ENABLE)
|
||||
|
||||
// Reset everything apart from ROSC and XOSC
|
||||
rp.PSM.WDSEL.Set(0x0001ffff &^ (rp.PSM_WDSEL_ROSC | rp.PSM_WDSEL_XOSC))
|
||||
|
||||
// Pause watchdog during debug
|
||||
rp.WATCHDOG.CTRL.SetBits(rp.WATCHDOG_CTRL_PAUSE_DBG0 | rp.WATCHDOG_CTRL_PAUSE_DBG1 | rp.WATCHDOG_CTRL_PAUSE_JTAG)
|
||||
|
||||
// Load initial counter
|
||||
rp.WATCHDOG.LOAD.Set(wd.loadValue)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Starts the watchdog.
|
||||
func (wd *watchdogImpl) Start() error {
|
||||
rp.WATCHDOG.CTRL.SetBits(rp.WATCHDOG_CTRL_ENABLE)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update the watchdog, indicating that the app is healthy.
|
||||
func (wd *watchdogImpl) Update() {
|
||||
rp.WATCHDOG.LOAD.Set(wd.loadValue)
|
||||
}
|
||||
|
||||
// startTick starts the watchdog tick.
|
||||
// cycles needs to be a divider that when applied to the xosc input,
|
||||
// produces a 1MHz clock. So if the xosc frequency is 12MHz,
|
||||
// this will need to be 12.
|
||||
func (wd *watchdogType) startTick(cycles uint32) {
|
||||
wd.tick.Set(cycles | rp.WATCHDOG_TICK_ENABLE)
|
||||
func (wd *watchdogImpl) startTick(cycles uint32) {
|
||||
rp.WATCHDOG.TICK.Set(cycles | rp.WATCHDOG_TICK_ENABLE)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
//go:build stm32
|
||||
|
||||
package machine
|
||||
|
||||
import "device/stm32"
|
||||
|
||||
var (
|
||||
Watchdog = &watchdogImpl{}
|
||||
)
|
||||
|
||||
const (
|
||||
// WatchdogMaxTimeout in milliseconds (32.768s)
|
||||
//
|
||||
// Timeout is based on 12-bit counter with /256 divider on
|
||||
// 32.768kHz clock. See 21.3.3 of RM0090 for table.
|
||||
WatchdogMaxTimeout = ((0xfff + 1) * 256 * 1024) / 32768
|
||||
)
|
||||
|
||||
const (
|
||||
// Enable access to PR, RLR and WINR registers (0x5555)
|
||||
iwdgKeyEnable = 0x5555
|
||||
// Reset the watchdog value (0xAAAA)
|
||||
iwdgKeyReset = 0xaaaa
|
||||
// Start the watchdog (0xCCCC)
|
||||
iwdgKeyStart = 0xcccc
|
||||
// Divide by 256
|
||||
iwdgDiv256 = 6
|
||||
)
|
||||
|
||||
type watchdogImpl struct {
|
||||
}
|
||||
|
||||
// Configure the watchdog.
|
||||
//
|
||||
// This method should not be called after the watchdog is started and on
|
||||
// some platforms attempting to reconfigure after starting the watchdog
|
||||
// is explicitly forbidden / will not work.
|
||||
func (wd *watchdogImpl) Configure(config WatchdogConfig) error {
|
||||
|
||||
// Enable configuration of IWDG
|
||||
stm32.IWDG.KR.Set(iwdgKeyEnable)
|
||||
|
||||
// Unconditionally divide by /256 since we don't really need accuracy
|
||||
// less than 8ms
|
||||
stm32.IWDG.PR.Set(iwdgDiv256)
|
||||
|
||||
timeout := config.TimeoutMillis
|
||||
if timeout > WatchdogMaxTimeout {
|
||||
timeout = WatchdogMaxTimeout
|
||||
}
|
||||
|
||||
// Set reload value based on /256 divider
|
||||
stm32.IWDG.RLR.Set(((config.TimeoutMillis*32768 + (256 * 1024) - 1) / (256 * 1024)) - 1)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Starts the watchdog.
|
||||
func (wd *watchdogImpl) Start() error {
|
||||
stm32.IWDG.KR.Set(iwdgKeyStart)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update the watchdog, indicating that `source` is healthy.
|
||||
func (wd *watchdogImpl) Update() {
|
||||
stm32.IWDG.KR.Set(iwdgKeyReset)
|
||||
}
|
||||
@@ -74,10 +74,12 @@ func (uart *UART) SetBaudRate(br uint32) {
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the UART.
|
||||
func (uart *UART) WriteByte(c byte) error {
|
||||
func (uart *UART) writeByte(c byte) error {
|
||||
uart.txReg.Set(uint32(c))
|
||||
|
||||
for !uart.statusReg.HasBits(uart.txEmptyFlag) {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {}
|
||||
|
||||
+20
-4
@@ -59,11 +59,27 @@ func (uart *UART) Read(data []byte) (n int, err error) {
|
||||
return size, nil
|
||||
}
|
||||
|
||||
// Write data to the UART.
|
||||
func (uart *UART) Write(data []byte) (n int, err error) {
|
||||
for _, v := range data {
|
||||
uart.WriteByte(v)
|
||||
// WriteByte writes a byte of data over the UART's Tx.
|
||||
// This function blocks until the data is finished being sent.
|
||||
func (uart *UART) WriteByte(c byte) error {
|
||||
err := uart.writeByte(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uart.flush() // flush() blocks until all data has been transmitted.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write data over the UART's Tx.
|
||||
// This function blocks until the data is finished being sent.
|
||||
func (uart *UART) Write(data []byte) (n int, err error) {
|
||||
for i, v := range data {
|
||||
err = uart.writeByte(v)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
uart.flush() // flush() blocks until all data has been transmitted.
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
|
||||
+48
-54
@@ -29,9 +29,7 @@ type Serialer interface {
|
||||
RTS() bool
|
||||
}
|
||||
|
||||
var usbDescriptor = descriptor.CDC
|
||||
|
||||
var usbDescriptorConfig uint8 = usb.DescriptorConfigCDC
|
||||
var usbDescriptor descriptor.Descriptor
|
||||
|
||||
func usbVendorID() uint16 {
|
||||
if usb.VendorID != 0 {
|
||||
@@ -138,18 +136,6 @@ func sendDescriptor(setup usb.Setup) {
|
||||
sendUSBPacket(0, usbDescriptor.Configuration, setup.WLength)
|
||||
return
|
||||
case descriptor.TypeDevice:
|
||||
// composite descriptor
|
||||
switch {
|
||||
case (usbDescriptorConfig & usb.DescriptorConfigHID) > 0:
|
||||
usbDescriptor = descriptor.CDCHID
|
||||
case (usbDescriptorConfig & usb.DescriptorConfigMIDI) > 0:
|
||||
usbDescriptor = descriptor.CDCMIDI
|
||||
case (usbDescriptorConfig & usb.DescriptorConfigJoystick) > 0:
|
||||
usbDescriptor = descriptor.CDCJoystick
|
||||
default:
|
||||
usbDescriptor = descriptor.CDC
|
||||
}
|
||||
|
||||
usbDescriptor.Configure(usbVendorID(), usbProductID())
|
||||
sendUSBPacket(0, usbDescriptor.Device, setup.WLength)
|
||||
return
|
||||
@@ -273,48 +259,56 @@ func handleStandardSetup(setup usb.Setup) bool {
|
||||
}
|
||||
|
||||
func EnableCDC(txHandler func(), rxHandler func([]byte), setupHandler func(usb.Setup) bool) {
|
||||
usbDescriptorConfig |= usb.DescriptorConfigCDC
|
||||
endPoints[usb.CDC_ENDPOINT_ACM] = (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn)
|
||||
endPoints[usb.CDC_ENDPOINT_OUT] = (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut)
|
||||
endPoints[usb.CDC_ENDPOINT_IN] = (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn)
|
||||
usbRxHandler[usb.CDC_ENDPOINT_OUT] = rxHandler
|
||||
usbTxHandler[usb.CDC_ENDPOINT_IN] = txHandler
|
||||
usbSetupHandler[usb.CDC_ACM_INTERFACE] = setupHandler // 0x02 (Communications and CDC Control)
|
||||
usbSetupHandler[usb.CDC_DATA_INTERFACE] = nil // 0x0A (CDC-Data)
|
||||
if len(usbDescriptor.Device) == 0 {
|
||||
usbDescriptor = descriptor.CDC
|
||||
}
|
||||
// Initialization of endpoints is required even for non-CDC
|
||||
ConfigureUSBEndpoint(usbDescriptor,
|
||||
[]usb.EndpointConfig{
|
||||
{
|
||||
Index: usb.CDC_ENDPOINT_ACM,
|
||||
IsIn: true,
|
||||
Type: usb.ENDPOINT_TYPE_INTERRUPT,
|
||||
},
|
||||
{
|
||||
Index: usb.CDC_ENDPOINT_OUT,
|
||||
IsIn: false,
|
||||
Type: usb.ENDPOINT_TYPE_BULK,
|
||||
RxHandler: rxHandler,
|
||||
},
|
||||
{
|
||||
Index: usb.CDC_ENDPOINT_IN,
|
||||
IsIn: true,
|
||||
Type: usb.ENDPOINT_TYPE_BULK,
|
||||
TxHandler: txHandler,
|
||||
},
|
||||
},
|
||||
[]usb.SetupConfig{
|
||||
{
|
||||
Index: usb.CDC_ACM_INTERFACE,
|
||||
Handler: setupHandler,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// EnableHID enables HID. This function must be executed from the init().
|
||||
func EnableHID(txHandler func(), rxHandler func([]byte), setupHandler func(usb.Setup) bool) {
|
||||
usbDescriptorConfig |= usb.DescriptorConfigHID
|
||||
endPoints[usb.HID_ENDPOINT_IN] = (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn)
|
||||
usbTxHandler[usb.HID_ENDPOINT_IN] = txHandler
|
||||
usbSetupHandler[usb.HID_INTERFACE] = setupHandler // 0x03 (HID - Human Interface Device)
|
||||
}
|
||||
func ConfigureUSBEndpoint(desc descriptor.Descriptor, epSettings []usb.EndpointConfig, setup []usb.SetupConfig) {
|
||||
usbDescriptor = desc
|
||||
|
||||
// EnableMIDI enables MIDI. This function must be executed from the init().
|
||||
func EnableMIDI(txHandler func(), rxHandler func([]byte), setupHandler func(usb.Setup) bool) {
|
||||
usbDescriptorConfig |= usb.DescriptorConfigMIDI
|
||||
endPoints[usb.MIDI_ENDPOINT_OUT] = (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut)
|
||||
endPoints[usb.MIDI_ENDPOINT_IN] = (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn)
|
||||
usbRxHandler[usb.MIDI_ENDPOINT_OUT] = rxHandler
|
||||
usbTxHandler[usb.MIDI_ENDPOINT_IN] = txHandler
|
||||
}
|
||||
|
||||
// EnableJoystick enables HID. This function must be executed from the init().
|
||||
func EnableJoystick(txHandler func(), rxHandler func([]byte), setupHandler func(usb.Setup) bool, hidDesc []byte) {
|
||||
class, err := descriptor.FindClassHIDType(descriptor.CDCJoystick.Configuration, descriptor.ClassHIDJoystick.Bytes())
|
||||
if err != nil {
|
||||
// TODO: some way to notify about error
|
||||
return
|
||||
for _, ep := range epSettings {
|
||||
if ep.IsIn {
|
||||
endPoints[ep.Index] = uint32(ep.Type | usb.EndpointIn)
|
||||
if ep.TxHandler != nil {
|
||||
usbTxHandler[ep.Index] = ep.TxHandler
|
||||
}
|
||||
} else {
|
||||
endPoints[ep.Index] = uint32(ep.Type | usb.EndpointOut)
|
||||
if ep.RxHandler != nil {
|
||||
usbRxHandler[ep.Index] = ep.RxHandler
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class.ClassLength(uint16(len(hidDesc)))
|
||||
descriptor.CDCJoystick.HID[2] = hidDesc
|
||||
|
||||
usbDescriptorConfig |= usb.DescriptorConfigJoystick
|
||||
endPoints[usb.HID_ENDPOINT_OUT] = (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointOut)
|
||||
usbRxHandler[usb.HID_ENDPOINT_OUT] = rxHandler
|
||||
endPoints[usb.HID_ENDPOINT_IN] = (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn)
|
||||
usbTxHandler[usb.HID_ENDPOINT_IN] = txHandler
|
||||
usbSetupHandler[usb.HID_INTERFACE] = setupHandler // 0x03 (HID - Human Interface Device)
|
||||
for _, s := range setup {
|
||||
usbSetupHandler[s.Index] = s.Handler
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package midi
|
||||
import (
|
||||
"machine"
|
||||
"machine/usb"
|
||||
"machine/usb/descriptor"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -40,7 +41,23 @@ func newMidi() *midi {
|
||||
m := &midi{
|
||||
buf: NewRingBuffer(),
|
||||
}
|
||||
machine.EnableMIDI(m.Handler, m.RxHandler, nil)
|
||||
machine.ConfigureUSBEndpoint(descriptor.CDCMIDI,
|
||||
[]usb.EndpointConfig{
|
||||
{
|
||||
Index: usb.MIDI_ENDPOINT_OUT,
|
||||
IsIn: false,
|
||||
Type: usb.ENDPOINT_TYPE_BULK,
|
||||
RxHandler: m.RxHandler,
|
||||
},
|
||||
{
|
||||
Index: usb.MIDI_ENDPOINT_IN,
|
||||
IsIn: true,
|
||||
Type: usb.ENDPOINT_TYPE_BULK,
|
||||
TxHandler: m.Handler,
|
||||
},
|
||||
},
|
||||
[]usb.SetupConfig{},
|
||||
)
|
||||
return m
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package usb
|
||||
|
||||
type EndpointConfig struct {
|
||||
Index uint8
|
||||
IsIn bool
|
||||
TxHandler func()
|
||||
RxHandler func([]byte)
|
||||
Type uint8
|
||||
}
|
||||
|
||||
type SetupConfig struct {
|
||||
Index uint8
|
||||
Handler func(Setup) bool
|
||||
}
|
||||
@@ -26,7 +26,7 @@ var interfaceHID = [interfaceTypeLen]byte{
|
||||
TypeInterface,
|
||||
0x02, // InterfaceNumber
|
||||
0x00, // AlternateSetting
|
||||
0x01, // NumEndpoints
|
||||
0x02, // NumEndpoints
|
||||
0x03, // InterfaceClass
|
||||
0x00, // InterfaceSubClass
|
||||
0x00, // InterfaceProtocol
|
||||
@@ -103,7 +103,7 @@ var classHID = [ClassHIDTypeLen]byte{
|
||||
0x00, // CountryCode
|
||||
0x01, // NumDescriptors
|
||||
0x22, // ClassType
|
||||
0x7E, // ClassLength L
|
||||
0x90, // ClassLength L
|
||||
0x00, // ClassLength H
|
||||
}
|
||||
|
||||
@@ -128,6 +128,7 @@ var CDCHID = Descriptor{
|
||||
InterfaceHID.Bytes(),
|
||||
ClassHID.Bytes(),
|
||||
EndpointEP4IN.Bytes(),
|
||||
EndpointEP5OUT.Bytes(),
|
||||
}),
|
||||
HID: map[uint16][]byte{
|
||||
2: Append([][]byte{
|
||||
@@ -147,6 +148,15 @@ var CDCHID = Descriptor{
|
||||
HIDReportCount(1),
|
||||
HIDReportSize(8),
|
||||
HIDInputConstVarAbs,
|
||||
HIDReportCount(3),
|
||||
HIDReportSize(1),
|
||||
HIDUsagePageLED,
|
||||
HIDUsageMinimum(1),
|
||||
HIDUsageMaximum(3),
|
||||
HIDOutputDataVarAbs,
|
||||
HIDReportCount(5),
|
||||
HIDReportSize(1),
|
||||
HIDOutputConstVarAbs,
|
||||
HIDReportCount(6),
|
||||
HIDReportSize(8),
|
||||
HIDLogicalMinimum(0),
|
||||
|
||||
@@ -3,21 +3,29 @@ package descriptor
|
||||
const (
|
||||
hidUsagePage = 0x05
|
||||
hidUsage = 0x09
|
||||
hidLogicalMinimum = 0x15
|
||||
hidLogicalMaximum = 0x25
|
||||
hidUsageMinimum = 0x19
|
||||
hidUsageMaximum = 0x29
|
||||
hidPhysicalMinimum = 0x35
|
||||
hidPhysicalMaximum = 0x46
|
||||
hidLogicalMinimum = 0x14
|
||||
hidLogicalMaximum = 0x24
|
||||
hidUsageMinimum = 0x18
|
||||
hidUsageMaximum = 0x28
|
||||
hidPhysicalMinimum = 0x34
|
||||
hidPhysicalMaximum = 0x44
|
||||
hidUnitExponent = 0x55
|
||||
hidUnit = 0x65
|
||||
hidCollection = 0xa1
|
||||
hidInput = 0x81
|
||||
hidOutput = 0x91
|
||||
hidReportSize = 0x75
|
||||
hidReportCount = 0x95
|
||||
hidReportID = 0x85
|
||||
)
|
||||
|
||||
const (
|
||||
hidSizeValue0 = 0x00
|
||||
hidSizeValue1 = 0x01
|
||||
hidSizeValue2 = 0x02
|
||||
hidSizeValue4 = 0x03
|
||||
)
|
||||
|
||||
var (
|
||||
HIDUsagePageGenericDesktop = []byte{hidUsagePage, 0x01}
|
||||
HIDUsagePageSimulationControls = []byte{hidUsagePage, 0x02}
|
||||
@@ -114,6 +122,12 @@ var (
|
||||
|
||||
// Input (Data, Variable, Relative), 2 position bytes (X & Y)
|
||||
HIDInputDataVarRel = []byte{hidInput, 0x06}
|
||||
|
||||
// Output (Data, Variable, Absolute), Modifier byte
|
||||
HIDOutputDataVarAbs = []byte{hidOutput, 0x02}
|
||||
|
||||
// Output (Const, Variable, Absolute), Modifier byte
|
||||
HIDOutputConstVarAbs = []byte{hidOutput, 0x03}
|
||||
)
|
||||
|
||||
func HIDReportSize(size int) []byte {
|
||||
@@ -129,51 +143,69 @@ func HIDReportID(id int) []byte {
|
||||
}
|
||||
|
||||
func HIDLogicalMinimum(min int) []byte {
|
||||
if min > 255 {
|
||||
return []byte{hidLogicalMinimum + 1, uint8(min), uint8(min >> 8)}
|
||||
switch {
|
||||
case min < -32767 || 65535 < min:
|
||||
return []byte{hidLogicalMinimum + hidSizeValue4, uint8(min), uint8(min >> 8), uint8(min >> 16), uint8(min >> 24)}
|
||||
case min < -127 || 255 < min:
|
||||
return []byte{hidLogicalMinimum + hidSizeValue2, uint8(min), uint8(min >> 8)}
|
||||
default:
|
||||
return []byte{hidLogicalMinimum + hidSizeValue1, byte(min)}
|
||||
}
|
||||
|
||||
return []byte{hidLogicalMinimum, byte(min)}
|
||||
}
|
||||
|
||||
func HIDLogicalMaximum(max int) []byte {
|
||||
if max > 255 {
|
||||
return []byte{hidLogicalMaximum + 1, uint8(max), uint8(max >> 8)}
|
||||
switch {
|
||||
case max < -32767 || 65535 < max:
|
||||
return []byte{hidLogicalMaximum + hidSizeValue4, uint8(max), uint8(max >> 8), uint8(max >> 16), uint8(max >> 24)}
|
||||
case max < -127 || 255 < max:
|
||||
return []byte{hidLogicalMaximum + hidSizeValue2, uint8(max), uint8(max >> 8)}
|
||||
default:
|
||||
return []byte{hidLogicalMaximum + hidSizeValue1, byte(max)}
|
||||
}
|
||||
|
||||
return []byte{hidLogicalMaximum, byte(max)}
|
||||
}
|
||||
|
||||
func HIDUsageMinimum(min int) []byte {
|
||||
if min > 255 {
|
||||
return []byte{hidUsageMinimum + 1, uint8(min), uint8(min >> 8)}
|
||||
switch {
|
||||
case min < -32767 || 65535 < min:
|
||||
return []byte{hidUsageMinimum + hidSizeValue4, uint8(min), uint8(min >> 8), uint8(min >> 16), uint8(min >> 24)}
|
||||
case min < -127 || 255 < min:
|
||||
return []byte{hidUsageMinimum + hidSizeValue2, uint8(min), uint8(min >> 8)}
|
||||
default:
|
||||
return []byte{hidUsageMinimum + hidSizeValue1, byte(min)}
|
||||
}
|
||||
|
||||
return []byte{hidUsageMinimum, byte(min)}
|
||||
}
|
||||
|
||||
func HIDUsageMaximum(max int) []byte {
|
||||
if max > 255 {
|
||||
return []byte{hidUsageMaximum + 1, uint8(max), uint8(max >> 8)}
|
||||
switch {
|
||||
case max < -32767 || 65535 < max:
|
||||
return []byte{hidUsageMaximum + hidSizeValue4, uint8(max), uint8(max >> 8), uint8(max >> 16), uint8(max >> 24)}
|
||||
case max < -127 || 255 < max:
|
||||
return []byte{hidUsageMaximum + hidSizeValue2, uint8(max), uint8(max >> 8)}
|
||||
default:
|
||||
return []byte{hidUsageMaximum + hidSizeValue1, byte(max)}
|
||||
}
|
||||
|
||||
return []byte{hidUsageMaximum, byte(max)}
|
||||
}
|
||||
|
||||
func HIDPhysicalMinimum(min int) []byte {
|
||||
if min > 255 {
|
||||
return []byte{hidPhysicalMinimum + 1, uint8(min), uint8(min >> 8)}
|
||||
switch {
|
||||
case min < -32767 || 65535 < min:
|
||||
return []byte{hidPhysicalMinimum + hidSizeValue4, uint8(min), uint8(min >> 8), uint8(min >> 16), uint8(min >> 24)}
|
||||
case min < -127 || 255 < min:
|
||||
return []byte{hidPhysicalMinimum + hidSizeValue2, uint8(min), uint8(min >> 8)}
|
||||
default:
|
||||
return []byte{hidPhysicalMinimum + hidSizeValue1, byte(min)}
|
||||
}
|
||||
|
||||
return []byte{hidPhysicalMinimum, byte(min)}
|
||||
}
|
||||
|
||||
func HIDPhysicalMaximum(max int) []byte {
|
||||
if max > 255 {
|
||||
return []byte{hidPhysicalMaximum + 1, uint8(max), uint8(max >> 8)}
|
||||
switch {
|
||||
case max < -32767 || 65535 < max:
|
||||
return []byte{hidPhysicalMaximum + hidSizeValue4, uint8(max), uint8(max >> 8), uint8(max >> 16), uint8(max >> 24)}
|
||||
case max < -127 || 255 < max:
|
||||
return []byte{hidPhysicalMaximum + hidSizeValue2, uint8(max), uint8(max >> 8)}
|
||||
default:
|
||||
return []byte{hidPhysicalMaximum + hidSizeValue1, byte(max)}
|
||||
}
|
||||
|
||||
return []byte{hidPhysicalMaximum, byte(max)}
|
||||
}
|
||||
|
||||
func HIDUnitExponent(exp int) []byte {
|
||||
|
||||
@@ -80,10 +80,7 @@ var JoystickDefaultHIDReport = Append([][]byte{
|
||||
HIDLogicalMaximum(1),
|
||||
HIDReportSize(1),
|
||||
HIDReportCount(16),
|
||||
HIDInputDataVarAbs,
|
||||
HIDReportCount(1),
|
||||
HIDReportSize(3),
|
||||
HIDUnitExponent(-16),
|
||||
HIDUnitExponent(0),
|
||||
HIDUnit(0),
|
||||
HIDInputDataVarAbs,
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"machine"
|
||||
"machine/usb"
|
||||
"machine/usb/descriptor"
|
||||
)
|
||||
|
||||
// from usb-hid.go
|
||||
@@ -22,7 +23,8 @@ const (
|
||||
)
|
||||
|
||||
type hidDevicer interface {
|
||||
Handler() bool
|
||||
TxHandler() bool
|
||||
RxHandler([]byte) bool
|
||||
}
|
||||
|
||||
var devices [5]hidDevicer
|
||||
@@ -32,19 +34,50 @@ var size int
|
||||
// calls machine.EnableHID for USB configuration
|
||||
func SetHandler(d hidDevicer) {
|
||||
if size == 0 {
|
||||
machine.EnableHID(handler, nil, setupHandler)
|
||||
machine.ConfigureUSBEndpoint(descriptor.CDCHID,
|
||||
[]usb.EndpointConfig{
|
||||
{
|
||||
Index: usb.HID_ENDPOINT_OUT,
|
||||
IsIn: false,
|
||||
Type: usb.ENDPOINT_TYPE_INTERRUPT,
|
||||
RxHandler: rxHandler,
|
||||
},
|
||||
{
|
||||
Index: usb.HID_ENDPOINT_IN,
|
||||
IsIn: true,
|
||||
Type: usb.ENDPOINT_TYPE_INTERRUPT,
|
||||
TxHandler: txHandler,
|
||||
},
|
||||
},
|
||||
[]usb.SetupConfig{
|
||||
{
|
||||
Index: usb.HID_INTERFACE,
|
||||
Handler: setupHandler,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
devices[size] = d
|
||||
size++
|
||||
}
|
||||
|
||||
func handler() {
|
||||
func txHandler() {
|
||||
for _, d := range devices {
|
||||
if d == nil {
|
||||
continue
|
||||
}
|
||||
if done := d.Handler(); done {
|
||||
if done := d.TxHandler(); done {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func rxHandler(b []byte) {
|
||||
for _, d := range devices {
|
||||
if d == nil {
|
||||
continue
|
||||
}
|
||||
if done := d.RxHandler(b); done {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package joystick
|
||||
import (
|
||||
"machine"
|
||||
"machine/usb"
|
||||
"machine/usb/descriptor"
|
||||
"machine/usb/hid"
|
||||
)
|
||||
|
||||
@@ -32,18 +33,50 @@ func UseSettings(def Definitions, rxHandlerFunc func(b []byte), setupFunc func(s
|
||||
if setupFunc == nil {
|
||||
setupFunc = hid.DefaultSetupHandler
|
||||
}
|
||||
machine.EnableJoystick(js.handler, rxHandlerFunc, setupFunc, hidDesc)
|
||||
if rxHandlerFunc == nil {
|
||||
rxHandlerFunc = js.rxHandlerFunc
|
||||
}
|
||||
if len(hidDesc) == 0 {
|
||||
hidDesc = descriptor.JoystickDefaultHIDReport
|
||||
}
|
||||
class, err := descriptor.FindClassHIDType(descriptor.CDCJoystick.Configuration, descriptor.ClassHIDJoystick.Bytes())
|
||||
if err != nil {
|
||||
// TODO: some way to notify about error
|
||||
return nil
|
||||
}
|
||||
|
||||
class.ClassLength(uint16(len(hidDesc)))
|
||||
descriptor.CDCJoystick.HID[2] = hidDesc
|
||||
|
||||
machine.ConfigureUSBEndpoint(descriptor.CDCJoystick,
|
||||
[]usb.EndpointConfig{
|
||||
{
|
||||
Index: usb.HID_ENDPOINT_OUT,
|
||||
IsIn: false,
|
||||
Type: usb.ENDPOINT_TYPE_INTERRUPT,
|
||||
RxHandler: rxHandlerFunc,
|
||||
},
|
||||
{
|
||||
Index: usb.HID_ENDPOINT_IN,
|
||||
IsIn: true,
|
||||
Type: usb.ENDPOINT_TYPE_INTERRUPT,
|
||||
TxHandler: js.handler,
|
||||
},
|
||||
},
|
||||
[]usb.SetupConfig{
|
||||
{
|
||||
Index: usb.HID_INTERFACE,
|
||||
Handler: setupFunc,
|
||||
},
|
||||
},
|
||||
)
|
||||
Joystick = js
|
||||
return js
|
||||
}
|
||||
|
||||
func newDefaultJoystick() *joystick {
|
||||
def := DefaultDefinitions()
|
||||
js := &joystick{
|
||||
State: def.NewState(),
|
||||
buf: hid.NewRingBuffer(),
|
||||
}
|
||||
machine.EnableJoystick(js.handler, js.rxHandler, hid.DefaultSetupHandler, def.Descriptor())
|
||||
js := UseSettings(def, nil, nil, nil)
|
||||
return js
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ func newKeyboard() *keyboard {
|
||||
}
|
||||
}
|
||||
|
||||
func (kb *keyboard) Handler() bool {
|
||||
func (kb *keyboard) TxHandler() bool {
|
||||
kb.waitTxc = false
|
||||
if b, ok := kb.buf.Get(); ok {
|
||||
kb.waitTxc = true
|
||||
@@ -91,6 +91,13 @@ func (kb *keyboard) Handler() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (kb *keyboard) RxHandler(b []byte) bool {
|
||||
if len(b) >= 2 && b[0] == 2 /* ReportID */ {
|
||||
kb.led = b[1]
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (kb *keyboard) tx(b []byte) {
|
||||
if machine.USBDev.InitEndpointComplete {
|
||||
if kb.waitTxc {
|
||||
@@ -102,6 +109,18 @@ func (kb *keyboard) tx(b []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
func (kb *keyboard) NumLockLed() bool {
|
||||
return kb.led&1 != 0
|
||||
}
|
||||
|
||||
func (kb *keyboard) CapsLockLed() bool {
|
||||
return kb.led&2 != 0
|
||||
}
|
||||
|
||||
func (kb *keyboard) ScrollLockLed() bool {
|
||||
return kb.led&4 != 0
|
||||
}
|
||||
|
||||
func (kb *keyboard) ready() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func newMouse() *mouse {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mouse) Handler() bool {
|
||||
func (m *mouse) TxHandler() bool {
|
||||
m.waitTxc = false
|
||||
if b, ok := m.buf.Get(); ok {
|
||||
m.waitTxc = true
|
||||
@@ -57,6 +57,10 @@ func (m *mouse) Handler() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *mouse) RxHandler(b []byte) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *mouse) tx(b []byte) {
|
||||
if machine.USBDev.InitEndpointComplete {
|
||||
if m.waitTxc {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
//go:build nrf52840 || nrf52833 || rp2040 || atsamd51 || atsame5x || stm32
|
||||
|
||||
package machine
|
||||
|
||||
// WatchdogConfig holds configuration for the watchdog timer.
|
||||
type WatchdogConfig struct {
|
||||
// The timeout (in milliseconds) before the watchdog fires.
|
||||
//
|
||||
// If the requested timeout exceeds `MaxTimeout` it will be rounded
|
||||
// down.
|
||||
TimeoutMillis uint32
|
||||
}
|
||||
|
||||
// watchdog must be implemented by any platform supporting watchdog functionality
|
||||
type watchdog interface {
|
||||
// Configure the watchdog.
|
||||
//
|
||||
// This method should not be called after the watchdog is started and on
|
||||
// some platforms attempting to reconfigure after starting the watchdog
|
||||
// is explicitly forbidden / will not work.
|
||||
Configure(config WatchdogConfig) error
|
||||
|
||||
// Starts the watchdog.
|
||||
Start() error
|
||||
|
||||
// Update the watchdog, indicating that the app is healthy.
|
||||
Update()
|
||||
}
|
||||
|
||||
// Ensure required public symbols var exists and meets interface spec
|
||||
var _ = watchdog(Watchdog)
|
||||
|
||||
// Ensure required public constants exist
|
||||
const _ = WatchdogMaxTimeout
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && !baremetal && !wasi
|
||||
//go:build linux && !baremetal && !wasi && !wasip1
|
||||
|
||||
package os
|
||||
|
||||
|
||||
+1
-4
@@ -6,7 +6,7 @@
|
||||
// fairly similar: we use fdopendir, fdclosedir, and readdir from wasi-libc in
|
||||
// a similar way that the darwin code uses functions from libc.
|
||||
|
||||
//go:build wasi
|
||||
//go:build wasi || wasip1
|
||||
|
||||
package os
|
||||
|
||||
@@ -53,9 +53,6 @@ func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEn
|
||||
if dirent == nil { // EOF
|
||||
break
|
||||
}
|
||||
if dirent.Ino == 0 {
|
||||
continue
|
||||
}
|
||||
name := dirent.Name()
|
||||
// Check for useless names before allocating a string.
|
||||
if string(name) == "." || string(name) == ".." {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build darwin || linux
|
||||
//go:build darwin || linux || wasip1
|
||||
|
||||
package os_test
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@ var (
|
||||
// The following code is copied from the official implementation.
|
||||
// src/internal/poll/fd.go
|
||||
|
||||
// ErrNoDeadline is returned when a request is made to set a deadline
|
||||
// on a file type that does not use the poller.
|
||||
var ErrNoDeadline = errors.New("file type does not support deadline")
|
||||
|
||||
// ErrDeadlineExceeded is returned for an expired deadline.
|
||||
// This is exported by the os package as os.ErrDeadlineExceeded.
|
||||
var ErrDeadlineExceeded error = &DeadlineExceededError{}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows
|
||||
//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || wasip1 || windows
|
||||
|
||||
package os
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build baremetal || (wasm && !wasi)
|
||||
//go:build baremetal || (wasm && !wasi && !wasip1)
|
||||
|
||||
package os
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
//go:build darwin || (linux && !baremetal)
|
||||
//go:build darwin || (linux && !baremetal) || wasip1
|
||||
|
||||
// target wasi sets GOOS=linux and thus the +linux build tag,
|
||||
// even though it doesn't show up in "tinygo info target -wasi"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build windows || darwin || (linux && !baremetal)
|
||||
//go:build windows || darwin || (linux && !baremetal) || wasip1
|
||||
|
||||
package os_test
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !wasi
|
||||
//go:build !wasi && !wasip1
|
||||
|
||||
package os_test
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build wasi
|
||||
//go:build wasi || wasip1
|
||||
|
||||
package os_test
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build windows || darwin || (linux && !baremetal)
|
||||
//go:build windows || darwin || (linux && !baremetal) || wasip1
|
||||
|
||||
package os_test
|
||||
|
||||
@@ -23,7 +23,6 @@ var dot = []string{
|
||||
"os_test.go",
|
||||
"types.go",
|
||||
"stat_darwin.go",
|
||||
"stat_linux.go",
|
||||
}
|
||||
|
||||
func randomName() string {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !baremetal && !js && !wasi
|
||||
//go:build !baremetal && !js && !wasi && !wasip1
|
||||
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !windows && !baremetal && !js && !wasi
|
||||
//go:build !windows && !baremetal && !js && !wasi && !wasip1
|
||||
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
//go:build !baremetal && !js && !wasi
|
||||
//go:build !baremetal && !js && !wasi && !wasip1
|
||||
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !baremetal && !js && !wasi
|
||||
//go:build !baremetal && !js && !wasi && !wasip1
|
||||
|
||||
package os
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build baremetal || js || wasi
|
||||
//go:build baremetal || js || wasi || wasip1
|
||||
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
//go:build linux && !baremetal
|
||||
//go:build (linux && !baremetal) || wasip1
|
||||
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Note: this file is used for both Linux and WASI.
|
||||
// Eventually it might be better to spit it up, and make the syscall constants
|
||||
// match the typical WASI constants instead of the Linux-equivalents used here.
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build baremetal || (wasm && !wasi)
|
||||
//go:build baremetal || (wasm && !wasi && !wasip1)
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
//go:build darwin || (linux && !baremetal)
|
||||
//go:build darwin || (linux && !baremetal) || wasip1
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user