Compare commits

..

66 Commits

Author SHA1 Message Date
deadprogram dc449882ad docs: update CHANGELOG for release 0.29
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-25 15:57:40 +02:00
deadprogram a158d3194f all: update version for 0.29 release
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-25 15:57:40 +02:00
Pertti Erkkilä 806498f099 nRF52: set SPI TX/RX lengths even data is empty. Fixes #3868 (#3877)
machine/hrf: Set SPI TX/RX lengths even data is empty. Fixes #3868
2023-08-24 13:15:18 +02:00
deadprogram e3bc6da9e4 make: add task to check NodeJS version before running tests
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-19 20:04:10 +02:00
Pierre Constantineau 3e2471d934 adding new uf2 target for PCA10056 (#3765)
targets: adding new uf2 target for PCA10056
2023-08-19 11:06:17 +02:00
Ayke van Laethem a545f17d2e wasm: add support for GOOS=wasip1
This adds true GOOS=wasip1 support in addition to our existing
-target=wasi support. The old support for WASI isn't removed, but should
be treated as deprecated and will likely be removed eventually to reduce
the test burden.
2023-08-17 18:16:54 +02:00
Kenneth Bell f4375d0452 samd51,rp2040,nrf528xx,stm32: implement watchdog 2023-08-15 11:50:07 +02:00
deadprogram 756cdf44ed loader: merge go.env file which is now required starting in Go 1.21 to correctly get required packages
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-13 17:11:11 +02:00
Ayke van Laethem 9037bf8bf0 main: add target JSON file in tinygo info output
It looks like this on my system, for example:

    {
      "target": {
        "llvm-target": "aarch64-unknown-linux",
        "cpu": "generic",
        "features": "+neon",
        "goos": "linux",
        "goarch": "arm64",
        "build-tags": [
          "linux",
          "arm64"
        ],
        "gc": "precise",
        "scheduler": "tasks",
        "linker": "ld.lld",
        "rtlib": "compiler-rt",
        "libc": "musl",
        "default-stack-size": 65536,
        "ldflags": [
          "--gc-sections"
        ],
        "extra-files": [
          "src/runtime/asm_arm64.S",
          "src/internal/task/task_stack_arm64.S"
        ],
        "gdb": [
          "gdb"
        ],
        "flash-1200-bps-reset": "false"
      },
      "goroot": "/home/ayke/.cache/tinygo/goroot-23c311bcaa05f188affa3c42310aba343acc82562d5e5f04dea9d5b79ac35f7e",
      "goos": "linux",
      "goarch": "arm64",
      "goarm": "6",
      ...
    }

This can be very useful while working on the automatically generated
target object for example (in my case, GOOS=wasip1).
2023-08-13 15:27:21 +02:00
deadprogram bfe9ee378f rp2040: move flash related functions into separate file from C imports for correct LSP. Fixes #3852
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-13 14:08:57 +02:00
deadprogram 37a4fa205c modules: update to go-serial package v1.6.0
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-11 18:21:05 +02:00
deadprogram 59cc7d4c42 docker: use Go 1.21 for Docker dev container build
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-11 17:35:43 +02:00
deadprogram ab64e215dd build: switch GH actions builds to use Go 1.21 final release
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-10 12:45:52 +02:00
deadprogram 253dbe335a builder: update message for max supported Go version
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-09 19:27:15 +02:00
Unrud 0ef86e1534 Add support for HID Keyboard LEDs 2023-08-07 14:00:32 +02:00
Kenneth Bell 72270f9052 all: use https for renesas submodule #3856 2023-08-07 10:29:31 +02:00
sago35 67ec722a74 board: add AKIZUKI DENSHI AE-RP2040 2023-08-04 17:53:01 +02:00
Ayke van Laethem 62294feb56 compiler: improve panic message when a runtime call is unavailable
This should not happen under normal circumstances. It can still happen
when there is a mismatch between TinyGo version and the associated
runtime, or while developing the compiler package.
2023-08-04 11:59:11 +02:00
Ayke van Laethem f1e25a18d2 compiler: implement clear builtin for maps 2023-08-04 11:59:11 +02:00
Ayke van Laethem a2f886a67a compiler: implement clear builtin for slices 2023-08-04 11:59:11 +02:00
Ayke van Laethem f791c821ff compiler: add min and max builtin support 2023-08-04 11:59:11 +02:00
Ayke van Laethem a93f0ed12a all: Go 1.21 support 2023-08-04 11:59:11 +02:00
Ayke van Laethem c25dd0a972 testing: add Testing function
This is new in Go 1.21.
2023-08-04 11:59:11 +02:00
sago35 215dd3f0be machine/usb/hid: rename Handler() to TxHandler() 2023-08-04 09:43:53 +02:00
sago35 c51f5cea0b machine/usb/hid: add RxHandler interface 2023-08-04 09:43:53 +02:00
sago35 395ee2d338 machine/usb: refactor endpoint configuration 2023-08-02 09:16:29 +02:00
sago35 069e4f0d98 machine/usb: allow USB Endpoint settings to be changed externally 2023-08-02 09:16:29 +02:00
sago35 d1eb642d45 machine/usb: remove usbDescriptorConfig 2023-08-01 15:24:42 +02:00
Charlie Haley 5f2e72f371 compiler: add compiler-rt to wasm.json 2023-08-01 09:56:58 +02:00
Charlie Haley 5b581d83b3 compiler: add compiler-rt and wasm symbols to table 2023-07-31 14:52:26 +02:00
Ayke van Laethem d845f1e1b2 wasm: fix functions exported through //export
When a function is exported using //export, but also had a
//go:wasm-module pragma, the //export name was ignored. The
//go:wasm-module doesn't actually do anything besides breaking the
export (exported functions don't have a module name).

I've refactored and cleaned up the code, and in the process removed this
weird edge case.
2023-07-28 13:57:24 +02:00
Yurii Soldak 00d46bd25d avr: pin change interrupt 2023-07-27 18:15:22 +02:00
deadprogram 01d2ef327d sync: add implementation from upstream Go for OnceFunc, OnceValue, and OnceValues
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-07-20 08:09:03 +02:00
soypat 4da1f6bcbe rp2040:add NoPin support 2023-07-16 16:25:30 +02:00
Ayke van Laethem 14ddba8519 nrf: add I2C timeout
This commit adds I2C timeouts for nrf51 and nrf52 (but not yet for
others like nrf52840).

Tested on the PineTime, where I now got a timeout instead of hanging and
resetting due to a watchdog reset.
2023-07-16 10:27:33 +02:00
Patricio Whittingslow a7b205c26c machine.UART refactor (#3832)
* add gosched calls to UART

* add UART.flush() stubs for all supported architectures

* add comment un uart.go on flush functionality

* uart.writeByte as base of UART usage

* fix NXP having duplicate WriteByte

* fix writeByte not returning error on some platforms

* add flush method for fe310 device

* check for error in WriteByte call to writeByte
2023-07-15 11:24:53 -03:00
deadprogram c83f712c17 machine/rp2040: wait for 1000 us after flash reset to avoid issues with busy USB bus
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-07-07 22:13:26 +02:00
Ayke van Laethem fffad84a63 reflect: add SetZero
This was added in Go 1.20 and is required by encoding/json starting with
Go 1.21.
2023-07-07 18:12:57 +02:00
Ayke van Laethem e075e0591d main: use go env instead of doing all detection manually
This replaces our own manual detection of various variables (GOROOT,
GOPATH, Go version) with a simple call to `go env`.

If the `go` command is not found:

    error: could not find 'go' command: executable file not found in $PATH

If the Go version is too old:

    error: requires go version 1.18 through 1.20, got go1.17

If the Go tool itself outputs an error (using GOROOT=foobar here):

    go: cannot find GOROOT directory: foobar

This does break the case where `go` wasn't available in $PATH but we
would detect it anyway (via some hardcoded OS-dependent paths). I'm not
sure we want to fix that: I think it's better to tell users "make sure
`go version` prints the right value" than to do some automagic detection
of Go binary locations.
2023-07-07 16:55:59 +02:00
Achille Roussel 46d2696363 wasi: allow zero inodes when reading directories
Signed-off-by: Achille Roussel <achille.roussel@gmail.com>
2023-07-07 09:56:38 +02:00
Ayke van Laethem e98dfd6c46 reflect: implement Value.Grow
This was added in Go 1.20 and becomes necessary for encoding/json in Go
1.21.
2023-07-07 08:10:47 +02:00
sago35 9126b95737 machine/samd51: fix i2cTimeout was decreasing due to cache activation 2023-07-06 21:35:54 +02:00
deadprogram 3871b831af make: add make task to generate Renesas device wrappers
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-07-05 13:30:21 +02:00
deadprogram 7755f2385c tools/gen-device-svd: small changes needed for Renesas MCUs
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-07-05 13:30:21 +02:00
deadprogram 04601a29e8 modules: add submodule for Renesas SVD file mirror repo
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-07-05 13:30:21 +02:00
Ayke van Laethem 9aadea930f main: improve detection of filesystems
This is a rewrite of how filesystems are detected. Specifically, it
fixes an issue on Linux where the location of the FAT filesystem can
vary between distributions (for example, we supported most distros by
checking two different paths, but NixOS uses a different path): it now
uses the data in /proc/mounts instead which should be universal.
2023-07-03 13:37:38 +02:00
sago35 6efa94035e machine/macropad_rp2040: add machine.BUTTON 2023-07-03 11:44:44 +02:00
Mansour Behabadi db8d80755f rp2040: add missing suffix to CMD_READ_STATUS 2023-07-03 10:52:33 +02:00
Yurii Soldak dc91c96305 example: adjust time offset 2023-07-02 18:25:20 +02:00
Damian Gryski 08b3a4576d compiler: update .ll test output 2023-07-02 15:35:42 +02:00
Damian Gryski acba0748f1 compiler,reflect: NumMethods reports exported methods only
Fixes #3796
2023-07-02 15:35:42 +02:00
Damian Gryski ef72c5bb4e reflect: fix iterating over maps with interface{} keys
Fixes #3794
2023-07-02 11:47:08 +02:00
Rado M 93cc03b15a docker: update clang to version 15 2023-07-02 10:54:23 +02:00
Tyler Rockwood fdc4bbbfad reflect: Add FieldByNameFunc
This adds FieldByNameFunc, which some libraries like reflect2 need.

For my usecase I could also just stub FieldByNameFunc to panic, but
figured that it would work OK to just make it work. I'm not sure if
the overhead to FieldByName using a closure is acceptable.

Signed-off-by: Tyler Rockwood <rockwood@redpanda.com>
2023-07-02 09:14:36 +02:00
soypat dd4e9e86e7 reflect: remove unecessary heap allocations 2023-07-01 21:03:10 +02:00
sago35 ad32d26511 machine/usb/hid,joystick: fix hidreport (3) (#3802)
* machine/usb/hid,joystick: fix hidreport (3) and handling of logical, usage, and physical minimum/maximum values
2023-07-01 11:58:23 +02:00
Ayke van Laethem 4619896c19 nrf: wait for stop condition after reading from the I2C bus
Found while working on the PineTime. For some reason it still kind of
works in most cases, but I was hitting this issue when interacting with
two different I2C devices (the touch sensor and the BMA421).
2023-06-22 10:41:05 +02:00
Yurii Soldak cec237917f example: simplify pininterrupt 2023-06-18 10:59:23 +02:00
Ayke van Laethem 4d2a6d2bbe wasm: remove i64 workaround, use BigInt instead
Browsers previously didn't support the WebAssembly i64 type, so we had
to work around that limitation by converting the LLVM i64 type to
something else. Some people used a pair of i32 values, but we used a
pointer to a stack allocated i64.

Now however, all major browsers and Node.js do support WebAssembly
BigInt integration so that i64 values can be passed back and forth
between WebAssembly and JavaScript easily. Therefore, I think the time
has come to drop support for this workaround.

For more information: https://v8.dev/features/wasm-bigint (note that
TinyGo has used a slightly different way of passing i64 values between
JS and Wasm).

For information on browser support: https://webassembly.org/roadmap/
2023-06-17 18:08:09 +02:00
Ayke van Laethem 6d5f4c4be2 ci: update Node.js from version 14 to version 16
Node.js 14 is not maintained anymore, so we can drop support for it.
2023-06-17 18:08:09 +02:00
Ayke van Laethem 0cb5d336f4 reflect: use .key() instead of a type assert
This should be ever so slightly more efficient.
2023-06-17 15:58:03 +02:00
Stepan Rakitin 91ee4d0030 os: define ErrNoDeadline 2023-06-17 10:38:48 +02:00
Ayke van Laethem 785eb93b62 ci: rename release-double-zipped to something more useful
The Linux artifacts have clear names (linux-amd64-double-zipped etc),
but the MacOS and Windows ones didn't. This patch renames these artifact
names to be more readable, especially when downloading the artifacts.
2023-06-16 21:32:22 +02:00
sago35 e041a8ed88 goenv: update to new v0.29.0 development version 2023-06-16 11:36:41 +02:00
soypat d01d85930d rp2040: add spi busy waits on read and read/write transactions 2023-06-11 22:24:26 +02:00
Ayke van Laethem ac821d8295 goenv: fix version number to 0.28.1 (without -dev) 2023-06-11 18:56:38 +02:00
147 changed files with 2587 additions and 1057 deletions
+3 -3
View File
@@ -27,7 +27,7 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.21'
cache: true cache: true
- name: Restore LLVM source cache - name: Restore LLVM source cache
uses: actions/cache/restore@v3 uses: actions/cache/restore@v3
@@ -108,7 +108,7 @@ jobs:
# We're doing the former here, to keep artifact uploads fast. # We're doing the former here, to keep artifact uploads fast.
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
name: release-double-zipped name: darwin-amd64-double-zipped
path: build/tinygo.darwin-amd64.tar.gz path: build/tinygo.darwin-amd64.tar.gz
- name: Smoke tests - name: Smoke tests
shell: bash shell: bash
@@ -126,7 +126,7 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.21'
cache: true cache: true
- name: Build TinyGo - name: Build TinyGo
run: go install run: go install
+7 -6
View File
@@ -18,7 +18,7 @@ jobs:
# statically linked binary. # statically linked binary.
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: golang:1.20-alpine image: golang:1.21-alpine
steps: steps:
- name: Install apk dependencies - name: Install apk dependencies
# tar: needed for actions/cache@v3 # tar: needed for actions/cache@v3
@@ -135,7 +135,7 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.21'
cache: true cache: true
- name: Install wasmtime - name: Install wasmtime
run: | run: |
@@ -153,6 +153,7 @@ jobs:
tar -C ~/lib -xf tinygo.linux-amd64.tar.gz tar -C ~/lib -xf tinygo.linux-amd64.tar.gz
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
- run: make tinygo-test-wasi-fast - run: make tinygo-test-wasi-fast
- run: make tinygo-test-wasip1-fast
- run: make smoketest - run: make smoketest
assert-test-linux: assert-test-linux:
# Run all tests that can run on Linux, with LLVM assertions enabled to catch # Run all tests that can run on Linux, with LLVM assertions enabled to catch
@@ -177,12 +178,12 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.21'
cache: true cache: true
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: '14' node-version: '16'
- name: Install wasmtime - name: Install wasmtime
run: | run: |
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
@@ -290,7 +291,7 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.21'
cache: true cache: true
- name: Restore LLVM source cache - name: Restore LLVM source cache
uses: actions/cache/restore@v3 uses: actions/cache/restore@v3
@@ -407,7 +408,7 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.21'
cache: true cache: true
- name: Restore LLVM source cache - name: Restore LLVM source cache
uses: actions/cache/restore@v3 uses: actions/cache/restore@v3
+8 -8
View File
@@ -35,7 +35,7 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.21'
cache: true cache: true
- name: Restore cached LLVM source - name: Restore cached LLVM source
uses: actions/cache/restore@v3 uses: actions/cache/restore@v3
@@ -118,7 +118,7 @@ jobs:
# We're doing the former here, to keep artifact uploads fast. # We're doing the former here, to keep artifact uploads fast.
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: release-double-zipped name: windows-amd64-double-zipped
path: build/release/release.zip path: build/release/release.zip
smoke-test-windows: smoke-test-windows:
@@ -143,12 +143,12 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.21'
cache: true cache: true
- name: Download TinyGo build - name: Download TinyGo build
uses: actions/download-artifact@v2 uses: actions/download-artifact@v2
with: with:
name: release-double-zipped name: windows-amd64-double-zipped
path: build/ path: build/
- name: Unzip TinyGo build - name: Unzip TinyGo build
shell: bash shell: bash
@@ -173,12 +173,12 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.21'
cache: true cache: true
- name: Download TinyGo build - name: Download TinyGo build
uses: actions/download-artifact@v2 uses: actions/download-artifact@v2
with: with:
name: release-double-zipped name: windows-amd64-double-zipped
path: build/ path: build/
- name: Unzip TinyGo build - name: Unzip TinyGo build
shell: bash shell: bash
@@ -209,12 +209,12 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.21'
cache: true cache: true
- name: Download TinyGo build - name: Download TinyGo build
uses: actions/download-artifact@v2 uses: actions/download-artifact@v2
with: with:
name: release-double-zipped name: windows-amd64-double-zipped
path: build/ path: build/
- name: Unzip TinyGo build - name: Unzip TinyGo build
shell: bash shell: bash
+3
View File
@@ -31,3 +31,6 @@
[submodule "lib/macos-minimal-sdk"] [submodule "lib/macos-minimal-sdk"]
path = lib/macos-minimal-sdk path = lib/macos-minimal-sdk
url = https://github.com/aykevl/macos-minimal-sdk.git 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
+71
View File
@@ -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 0.28.0
--- ---
+2 -2
View File
@@ -1,8 +1,8 @@
# tinygo-llvm stage obtains the llvm source for TinyGo # 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 && \ 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 COPY ./Makefile /tinygo/Makefile
+25 -1
View File
@@ -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/ ./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 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. # Get LLVM sources.
$(LLVM_PROJECTDIR)/llvm: $(LLVM_PROJECTDIR)/llvm:
git clone -b xtensa_release_15.x --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR) 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 @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) 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. # Build the Go compiler.
tinygo: 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 @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`" . 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 . 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 # 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. # Same thing, except for wasi rather than the current platform.
tinygo-test-wasi: tinygo-test-wasi:
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_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-wasi-fast:
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) ./tests/runtime_wasi $(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-bench-wasi:
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) $(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
tinygo-bench-wasi-fast: tinygo-bench-wasi-fast:
@@ -478,12 +496,16 @@ smoketest:
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/test $(TINYGO) build -size short -o test.hex -target=pca10040 examples/test
@$(MD5SUM) test.hex @$(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 $(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-mouse
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-keyboard $(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-keyboard
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/i2c-target $(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/i2c-target
@$(MD5SUM) test.hex @$(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 # test simulated boards on play.tinygo.org
ifneq ($(WASM), 0) ifneq ($(WASM), 0)
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1 $(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
@@ -642,6 +664,8 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=gopher-badge examples/blinky1 $(TINYGO) build -size short -o test.hex -target=gopher-badge examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=ae-rp2040 examples/echo
@$(MD5SUM) test.hex
# test pwm # test pwm
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm $(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
+19 -1
View File
@@ -12,6 +12,7 @@ import (
"path/filepath" "path/filepath"
"time" "time"
wasm "github.com/aykevl/go-wasm"
"github.com/blakesmith/ar" "github.com/blakesmith/ar"
) )
@@ -74,8 +75,25 @@ func makeArchive(arfile *os.File, objs []string) error {
fileIndex int fileIndex int
}{symbol.Name, i}) }{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 { } 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 // Close file, to avoid issues with too many open files (especially on
+11 -14
View File
@@ -217,19 +217,27 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
var packageJobs []*compileJob var packageJobs []*compileJob
packageActionIDJobs := make(map[string]*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"] == "" { if config.Options.GlobalValues["runtime"]["buildVersion"] == "" {
version := goenv.Version version := goenv.Version
if strings.HasSuffix(goenv.Version, "-dev") && goenv.GitSha1 != "" { if strings.HasSuffix(goenv.Version, "-dev") && goenv.GitSha1 != "" {
version += "-" + 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 { if config.Options.GlobalValues["runtime"] == nil {
config.Options.GlobalValues["runtime"] = make(map[string]string) config.Options.GlobalValues["runtime"] = make(map[string]string)
} }
config.Options.GlobalValues["runtime"]["buildVersion"] = version 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 var embedFileObjects []*compileJob
for _, pkg := range lprogram.Sorted() { for _, pkg := range lprogram.Sorted() {
@@ -1054,17 +1062,6 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
return err 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 // Optimization levels here are roughly the same as Clang, but probably not
// exactly. // exactly.
optLevel, sizeLevel, inlinerThreshold := config.OptLevels() optLevel, sizeLevel, inlinerThreshold := config.OptLevels()
+1
View File
@@ -60,6 +60,7 @@ func TestClangAttributes(t *testing.T) {
{GOOS: "darwin", GOARCH: "arm64"}, {GOOS: "darwin", GOARCH: "arm64"},
{GOOS: "windows", GOARCH: "amd64"}, {GOOS: "windows", GOARCH: "amd64"},
{GOOS: "windows", GOARCH: "arm64"}, {GOOS: "windows", GOARCH: "arm64"},
{GOOS: "wasip1", GOARCH: "wasm"},
} { } {
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
if options.GOARCH == "arm" { if options.GOARCH == "arm" {
+4 -10
View File
@@ -1,7 +1,6 @@
package builder package builder
import ( import (
"errors"
"fmt" "fmt"
"github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/compileopts"
@@ -24,19 +23,14 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
spec.OpenOCDCommands = options.OpenOCDCommands spec.OpenOCDCommands = options.OpenOCDCommands
} }
goroot := goenv.Get("GOROOT") major, minor, err := goenv.GetGorootVersion()
if goroot == "" {
return nil, errors.New("cannot locate $GOROOT, please set it manually")
}
major, minor, err := goenv.GetGorootVersion(goroot)
if err != nil { 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: // 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 // 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")) clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
+1 -1
View File
@@ -43,7 +43,7 @@ func TestBinarySize(t *testing.T) {
// microcontrollers // microcontrollers
{"hifive1b", "examples/echo", 4612, 280, 0, 2252}, {"hifive1b", "examples/echo", 4612, 280, 0, 2252},
{"microbit", "examples/serial", 2724, 388, 8, 2256}, {"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 // TODO: also check wasm. Right now this is difficult, because
// wasm binaries are run through wasm-opt and therefore the // wasm binaries are run through wasm-opt and therefore the
-5
View File
@@ -492,11 +492,6 @@ func (c *Config) RelocationModel() string {
return "static" 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 // EmulatorName is a shorthand to get the command for this emulator, something
// like qemu-system-arm or simavr. // like qemu-system-arm or simavr.
func (c *Config) EmulatorName() string { func (c *Config) EmulatorName() string {
+70 -41
View File
@@ -23,46 +23,45 @@ import (
// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/spec/struct.TargetOptions.html // 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 // https://github.com/shepmaster/rust-arduino-blink-led-no-core-with-cargo/blob/master/blink/arduino.json
type TargetSpec struct { type TargetSpec struct {
Inherits []string `json:"inherits"` Inherits []string `json:"inherits,omitempty"`
Triple string `json:"llvm-target"` Triple string `json:"llvm-target,omitempty"`
CPU string `json:"cpu"` CPU string `json:"cpu,omitempty"`
ABI string `json:"target-abi"` // rougly equivalent to -mabi= flag ABI string `json:"target-abi,omitempty"` // rougly equivalent to -mabi= flag
Features string `json:"features"` Features string `json:"features,omitempty"`
GOOS string `json:"goos"` GOOS string `json:"goos,omitempty"`
GOARCH string `json:"goarch"` GOARCH string `json:"goarch,omitempty"`
BuildTags []string `json:"build-tags"` BuildTags []string `json:"build-tags,omitempty"`
GC string `json:"gc"` GC string `json:"gc,omitempty"`
Scheduler string `json:"scheduler"` Scheduler string `json:"scheduler,omitempty"`
Serial string `json:"serial"` // which serial output to use (uart, usb, none) Serial string `json:"serial,omitempty"` // which serial output to use (uart, usb, none)
Linker string `json:"linker"` Linker string `json:"linker,omitempty"`
RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt) RTLib string `json:"rtlib,omitempty"` // compiler runtime library (libgcc, compiler-rt)
Libc string `json:"libc"` Libc string `json:"libc,omitempty"`
AutoStackSize *bool `json:"automatic-stack-size"` // Determine stack size automatically at compile time. AutoStackSize *bool `json:"automatic-stack-size,omitempty"` // 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. DefaultStackSize uint64 `json:"default-stack-size,omitempty"` // Default stack size if the size couldn't be determined at compile time.
CFlags []string `json:"cflags"` CFlags []string `json:"cflags,omitempty"`
LDFlags []string `json:"ldflags"` LDFlags []string `json:"ldflags,omitempty"`
LinkerScript string `json:"linkerscript"` LinkerScript string `json:"linkerscript,omitempty"`
ExtraFiles []string `json:"extra-files"` ExtraFiles []string `json:"extra-files,omitempty"`
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum RP2040BootPatch *bool `json:"rp2040-boot-patch,omitempty"` // Patch RP2040 2nd stage bootloader checksum
Emulator string `json:"emulator"` Emulator string `json:"emulator,omitempty"`
FlashCommand string `json:"flash-command"` FlashCommand string `json:"flash-command,omitempty"`
GDB []string `json:"gdb"` GDB []string `json:"gdb,omitempty"`
PortReset string `json:"flash-1200-bps-reset"` PortReset string `json:"flash-1200-bps-reset,omitempty"`
SerialPort []string `json:"serial-port"` // serial port IDs in the form "vid:pid" SerialPort []string `json:"serial-port,omitempty"` // serial port IDs in the form "vid:pid"
FlashMethod string `json:"flash-method"` FlashMethod string `json:"flash-method,omitempty"`
FlashVolume []string `json:"msd-volume-name"` FlashVolume []string `json:"msd-volume-name,omitempty"`
FlashFilename string `json:"msd-firmware-name"` FlashFilename string `json:"msd-firmware-name,omitempty"`
UF2FamilyID string `json:"uf2-family-id"` UF2FamilyID string `json:"uf2-family-id,omitempty"`
BinaryFormat string `json:"binary-format"` BinaryFormat string `json:"binary-format,omitempty"`
OpenOCDInterface string `json:"openocd-interface"` OpenOCDInterface string `json:"openocd-interface,omitempty"`
OpenOCDTarget string `json:"openocd-target"` OpenOCDTarget string `json:"openocd-target,omitempty"`
OpenOCDTransport string `json:"openocd-transport"` OpenOCDTransport string `json:"openocd-transport,omitempty"`
OpenOCDCommands []string `json:"openocd-commands"` OpenOCDCommands []string `json:"openocd-commands,omitempty"`
OpenOCDVerify *bool `json:"openocd-verify"` // enable verify when flashing with openocd OpenOCDVerify *bool `json:"openocd-verify,omitempty"` // enable verify when flashing with openocd
JLinkDevice string `json:"jlink-device"` JLinkDevice string `json:"jlink-device,omitempty"`
CodeModel string `json:"code-model"` CodeModel string `json:"code-model,omitempty"`
RelocationModel string `json:"relocation-model"` RelocationModel string `json:"relocation-model,omitempty"`
WasmAbi string `json:"wasm-abi"`
} }
// overrideProperties overrides all properties that are set in child into itself using reflection. // overrideProperties overrides all properties that are set in child into itself using reflection.
@@ -192,12 +191,15 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
default: default:
return nil, fmt.Errorf("invalid GOARM=%s, must be 5, 6, or 7", options.GOARM) return nil, fmt.Errorf("invalid GOARM=%s, must be 5, 6, or 7", options.GOARM)
} }
case "wasm":
llvmarch = "wasm32"
default: default:
llvmarch = options.GOARCH llvmarch = options.GOARCH
} }
llvmvendor := "unknown" llvmvendor := "unknown"
llvmos := options.GOOS llvmos := options.GOOS
if llvmos == "darwin" { switch llvmos {
case "darwin":
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer // Use macosx* instead of darwin, otherwise darwin/arm64 will refer
// to iOS! // to iOS!
llvmos = "macosx10.12.0" llvmos = "macosx10.12.0"
@@ -208,6 +210,8 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
llvmos = "macosx11.0.0" llvmos = "macosx11.0.0"
} }
llvmvendor = "apple" llvmvendor = "apple"
case "wasip1":
llvmos = "wasi"
} }
// Target triples (which actually have four components, but are called // Target triples (which actually have four components, but are called
// triples for historical reasons) have the form: // triples for historical reasons) have the form:
@@ -278,6 +282,15 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
case "arm64": case "arm64":
spec.CPU = "generic" spec.CPU = "generic"
spec.Features = "+neon" 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" { if goos == "darwin" {
spec.Linker = "ld.lld" spec.Linker = "ld.lld"
@@ -321,6 +334,22 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
"--no-insert-timestamp", "--no-insert-timestamp",
"--no-dynamicbase", "--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 { } else {
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
} }
+5 -1
View File
@@ -36,7 +36,11 @@ const (
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or // createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
// createRuntimeInvoke instead. // createRuntimeInvoke instead.
func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name string, isInvoke bool) llvm.Value { 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) fnType, llvmFn := b.getFunction(fn)
if llvmFn.IsNil() { if llvmFn.IsNil() {
panic("trying to call non-existent function: " + fn.RelString(nil)) panic("trying to call non-existent function: " + fn.RelString(nil))
+57
View File
@@ -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, r, 0, "")
cplx = b.CreateInsertValue(cplx, i, 1, "") cplx = b.CreateInsertValue(cplx, i, 1, "")
return cplx, nil 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": case "copy":
dst := argValues[0] dst := argValues[0]
src := argValues[1] 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") llvmLen = b.CreateZExt(llvmLen, b.intType, "len.int")
} }
return llvmLen, nil 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": case "print", "println":
for i, value := range argValues { for i, value := range argValues {
if i >= 1 && callName == "println" { if i >= 1 && callName == "println" {
+4 -1
View File
@@ -29,7 +29,7 @@ func TestCompiler(t *testing.T) {
t.Parallel() t.Parallel()
// Determine Go minor version (e.g. 16 in go1.16.3). // 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 { if err != nil {
t.Fatal("could not read Go version:", err) t.Fatal("could not read Go version:", err)
} }
@@ -54,6 +54,9 @@ func TestCompiler(t *testing.T) {
if goMinor >= 20 { if goMinor >= 20 {
tests = append(tests, testCase{"go1.20.go", "", ""}) tests = append(tests, testCase{"go1.20.go", "", ""})
} }
if goMinor >= 21 {
tests = append(tests, testCase{"go1.21.go", "", ""})
}
for _, tc := range tests { for _, tc := range tests {
name := tc.file name := tc.file
+17 -8
View File
@@ -128,6 +128,15 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
hasMethodSet = false 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. // Short-circuit all the global pointer logic here for pointers to pointers.
if typ, ok := typ.(*types.Pointer); ok { if typ, ok := typ.(*types.Pointer); ok {
if _, ok := typ.Elem().(*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) pkgPathPtr := c.pkgPathPtr(pkgpath)
typeFields = []llvm.Value{ typeFields = []llvm.Value{
llvm.ConstInt(c.ctx.Int16Type(), uint64(ms.Len()), false), // numMethods llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
c.getTypeCode(types.NewPointer(typ)), // ptrTo c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Underlying()), // underlying c.getTypeCode(typ.Underlying()), // underlying
pkgPathPtr, // pkgpath pointer pkgPathPtr, // pkgpath pointer
c.ctx.ConstString(pkgname+"."+name+"\x00", false), // name c.ctx.ConstString(pkgname+"."+name+"\x00", false), // name
} }
metabyte |= 1 << 5 // "named" flag metabyte |= 1 << 5 // "named" flag
case *types.Chan: case *types.Chan:
@@ -308,7 +317,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
} }
case *types.Pointer: case *types.Pointer:
typeFields = []llvm.Value{ 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()), c.getTypeCode(typ.Elem()),
} }
case *types.Array: case *types.Array:
@@ -337,8 +346,8 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
llvmStructType := c.getLLVMType(typ) llvmStructType := c.getLLVMType(typ)
size := c.targetData.TypeStoreSize(llvmStructType) size := c.targetData.TypeStoreSize(llvmStructType)
typeFields = []llvm.Value{ typeFields = []llvm.Value{
llvm.ConstInt(c.ctx.Int16Type(), uint64(ms.Len()), false), // numMethods llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
c.getTypeCode(types.NewPointer(typ)), // ptrTo c.getTypeCode(types.NewPointer(typ)), // ptrTo
pkgPathPtr, pkgPathPtr,
llvm.ConstInt(c.ctx.Int32Type(), uint64(size), false), // size llvm.ConstInt(c.ctx.Int32Type(), uint64(size), false), // size
llvm.ConstInt(c.ctx.Int16Type(), uint64(typ.NumFields()), false), // numFields llvm.ConstInt(c.ctx.Int16Type(), uint64(typ.NumFields()), false), // numFields
+15 -9
View File
@@ -70,15 +70,7 @@ func (b *builder) createMemoryCopyImpl() {
// regular libc memset calls if they aren't optimized out in a different way. // regular libc memset calls if they aren't optimized out in a different way.
func (b *builder) createMemoryZeroImpl() { func (b *builder) createMemoryZeroImpl() {
b.createFunctionStart(true) b.createFunctionStart(true)
fnName := "llvm.memset.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth()) llvmFn := b.getMemsetFunc()
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)
}
params := []llvm.Value{ params := []llvm.Value{
b.getValue(b.fn.Params[0], getPos(b.fn)), b.getValue(b.fn.Params[0], getPos(b.fn)),
llvm.ConstInt(b.ctx.Int8Type(), 0, false), llvm.ConstInt(b.ctx.Int8Type(), 0, false),
@@ -89,6 +81,20 @@ func (b *builder) createMemoryZeroImpl() {
b.CreateRetVoid() 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 // createKeepAlive creates the runtime.KeepAlive function. It is implemented
// using inline assembly. // using inline assembly.
func (b *builder) createKeepAliveImpl() { func (b *builder) createKeepAliveImpl() {
+5
View File
@@ -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 // createMapIteratorNext lowers the *ssa.Next instruction for iterating over a
// map. It returns a tuple of {bool, key, value} with the result of the // map. It returns a tuple of {bool, key, value} with the result of the
// iteration. // iteration.
+14 -33
View File
@@ -23,9 +23,9 @@ import (
// The linkName value contains a valid link name, even if //go:linkname is not // The linkName value contains a valid link name, even if //go:linkname is not
// present. // present.
type functionInfo struct { type functionInfo struct {
module string // go:wasm-module wasmModule string // go:wasm-module
importName string // go:linkname, go:export - The name the developer assigns wasmName string // wasm-export-name or wasm-import-name in the IR
linkName string // go:linkname, go:export - The name that we map for the particular module -> importName linkName string // go:linkname, go:export - the IR function name
section string // go:section - object file section name section string // go:section - object file section name
exported bool // go:export, CGo exported bool // go:export, CGo
interrupt bool // go:interrupt 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. // External/exported functions may not retain pointer values.
// https://golang.org/cmd/cgo/#hdr-Passing_pointers // https://golang.org/cmd/cgo/#hdr-Passing_pointers
if info.exported { 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 // We need to add the wasm-import-module and the wasm-import-name
// attributes. // attributes.
module := info.module if info.wasmModule != "" {
if module == "" { llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-module", info.wasmModule))
module = "env"
} }
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-module", module))
name := info.importName llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", info.wasmName))
if name == "" {
name = info.linkName
}
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", name))
} }
nocaptureKind := llvm.AttributeKindID("nocapture") nocaptureKind := llvm.AttributeKindID("nocapture")
nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0) nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0)
@@ -260,10 +254,6 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
return return
} }
if decl, ok := f.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil { 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 { for _, comment := range decl.Doc.List {
text := comment.Text text := comment.Text
if strings.HasPrefix(text, "//export ") { if strings.HasPrefix(text, "//export ") {
@@ -281,7 +271,8 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
continue continue
} }
importName = parts[1] info.linkName = parts[1]
info.wasmName = info.linkName
info.exported = true info.exported = true
case "//go:interrupt": case "//go:interrupt":
if hasUnsafeImport(f.Pkg.Pkg) { if hasUnsafeImport(f.Pkg.Pkg) {
@@ -289,10 +280,11 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
} }
case "//go:wasm-module": case "//go:wasm-module":
// Alternative comment for setting the import module. // Alternative comment for setting the import module.
// This is deprecated, use //go:wasmimport instead.
if len(parts) != 2 { if len(parts) != 2 {
continue continue
} }
info.module = parts[1] info.wasmModule = parts[1]
case "//go:wasmimport": case "//go:wasmimport":
// Import a WebAssembly function, for example a WASI function. // Import a WebAssembly function, for example a WASI function.
// Original proposal: https://github.com/golang/go/issues/38248 // 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) c.checkWasmImport(f, comment.Text)
info.exported = true info.exported = true
info.module = parts[1] info.wasmModule = parts[1]
info.importName = parts[2] info.wasmName = parts[2]
case "//go:inline": case "//go:inline":
info.inline = inlineHint info.inline = inlineHint
case "//go:noinline": 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: // The list of allowed types is based on this proposal:
// https://github.com/golang/go/issues/59149 // https://github.com/golang/go/issues/59149
func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) { 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 // The runtime is a special case. Allow all kinds of parameters
// (importantly, including pointers). // (importantly, including pointers).
return return
+65
View File
@@ -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)
}
+179
View File
@@ -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 }
+1 -1
View File
@@ -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: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: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: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.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:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4 @"reflect/types.type:pointer:interface:{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
+13
View File
@@ -62,6 +62,19 @@ func exportedFunctionInSection() {
//go:wasmimport modulename import1 //go:wasmimport modulename import1
func declaredImport() 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. // This function should not: it's only a declaration and not a definition.
// //
//go:section .special_function_section //go:section .special_function_section
+13 -3
View File
@@ -6,7 +6,7 @@ target triple = "wasm32-unknown-wasi"
@extern_global = external global [0 x i8], align 1 @extern_global = external global [0 x i8], align 1
@main.alignedGlobal = hidden global [4 x i32] zeroinitializer, align 32 @main.alignedGlobal = hidden global [4 x i32] zeroinitializer, align 32
@main.alignedGlobal16 = hidden global [4 x i32] zeroinitializer, align 16 @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 @main.globalInSection = hidden global i32 0, section ".special_global_section", align 4
@undefinedGlobalNotInSection = external global i32, align 4 @undefinedGlobalNotInSection = external global i32, align 4
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024 @main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
@@ -62,13 +62,23 @@ entry:
declare void @main.declaredImport() #7 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 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 #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 #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "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 #4 = { inlinehint nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #5 = { noinline 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 #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" }
+1 -1
View File
@@ -14,7 +14,7 @@ require (
github.com/mattn/go-colorable v0.1.8 github.com/mattn/go-colorable v0.1.8
github.com/mattn/go-tty v0.0.4 github.com/mattn/go-tty v0.0.4
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 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/sys v0.4.0
golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8 golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
+4 -2
View File
@@ -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 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= 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/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 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= 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 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs=
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA= github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 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.6.0 h1:mAbRGN4cKE2J5gMwsMHC2KQisdLRQssO9WSM+rbZJ8A=
go.bug.st/serial v1.3.5/go.mod h1:z8CesKorE90Qr/oRSJiEuvzYRKol9r/anJZEb5kt304= 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/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+53 -99
View File
@@ -4,15 +4,16 @@ package goenv
import ( import (
"bytes" "bytes"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/fs" "io/fs"
"os" "os"
"os/exec" "os/exec"
"os/user"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings" "strings"
"sync"
) )
// Keys is a slice of all available environment variable keys. // Keys is a slice of all available environment variable keys.
@@ -37,6 +38,53 @@ func init() {
// directory. // directory.
var TINYGOROOT string 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. // Get returns a single environment variable, possibly calculating it on-demand.
// The empty string is returned for unknown environment variables. // The empty string is returned for unknown environment variables.
func Get(name string) string { func Get(name string) string {
@@ -70,15 +118,11 @@ func Get(name string) string {
// especially when floating point instructions are involved. // especially when floating point instructions are involved.
return "6" return "6"
case "GOROOT": case "GOROOT":
return getGoroot() readGoEnvVars()
return goEnvVars.GOROOT
case "GOPATH": case "GOPATH":
if dir := os.Getenv("GOPATH"); dir != "" { readGoEnvVars()
return dir return goEnvVars.GOPATH
}
// fallback
home := getHomeDir()
return filepath.Join(home, "go")
case "GOCACHE": case "GOCACHE":
// Get the cache directory, usually ~/.cache/tinygo // Get the cache directory, usually ~/.cache/tinygo
dir, err := os.UserCacheDir() dir, err := os.UserCacheDir()
@@ -240,93 +284,3 @@ func isSourceDir(root string) bool {
_, err = os.Stat(filepath.Join(root, "src/device/arm/arm.go")) _, err = os.Stat(filepath.Join(root, "src/device/arm/arm.go"))
return err == nil 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
View File
@@ -4,15 +4,12 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"os"
"path/filepath"
"regexp"
"strings" "strings"
) )
// Version of TinyGo. // Version of TinyGo.
// Update this value before release of new version of software. // Update this value before release of new version of software.
const Version = "0.28.0-dev" const Version = "0.29.0"
var ( var (
// This variable is set at build time using -ldflags parameters. // 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. // GetGorootVersion returns the major and minor version for a given GOROOT path.
// If the goroot cannot be determined, (0, 0) is returned. // If the goroot cannot be determined, (0, 0) is returned.
func GetGorootVersion(goroot string) (major, minor int, err error) { func GetGorootVersion() (major, minor int, err error) {
s, err := GorootVersionString(goroot) s, err := GorootVersionString()
if err != nil { if err != nil {
return 0, 0, err 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 // 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 // toolchain. It is usually of the form `go1.x.y` but can have some variations
// can have some variations (for beta releases, for example). // (for beta releases, for example).
func GorootVersionString(goroot string) (string, error) { func GorootVersionString() (string, error) {
if data, err := os.ReadFile(filepath.Join(goroot, "VERSION")); err == nil { err := readGoEnvVars()
return string(data), nil return goEnvVars.GOVERSION, err
} 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
}
} }
+1
Submodule lib/renesas-svd added at 03d7688085
+5
View File
@@ -207,6 +207,11 @@ func listGorootMergeLinks(goroot, tinygoroot string, overrides map[string]bool)
merges[dir] = filepath.Join(goroot, dir) 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 return merges, nil
} }
+121 -100
View File
@@ -19,6 +19,7 @@ import (
"regexp" "regexp"
"runtime" "runtime"
"runtime/pprof" "runtime/pprof"
"sort"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@@ -946,112 +947,130 @@ func touchSerialPortAt1200bps(port string) (err error) {
} }
func flashUF2UsingMSD(volumes []string, tmppath string, options *compileopts.Options) error { func flashUF2UsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
// find standard UF2 info path for start := time.Now(); time.Since(start) < options.Timeout; {
infoPaths := make([]string, 0, len(volumes)) // Find a UF2 mount point.
for _, volume := range volumes { mounts, err := findFATMounts(options)
switch runtime.GOOS { if err != nil {
case "linux", "freebsd": return err
fi, err := os.Stat("/run/media") }
if err != nil || !fi.IsDir() { for _, mount := range mounts {
infoPaths = append(infoPaths, "/media/*/"+volume+"/INFO_UF2.TXT") for _, volume := range volumes {
} else { if mount.name != volume {
infoPaths = append(infoPaths, "/run/media/*/"+volume+"/INFO_UF2.TXT") continue
} }
case "darwin": if _, err := os.Stat(filepath.Join(mount.path, "INFO_UF2.TXT")); err != nil {
infoPaths = append(infoPaths, "/Volumes/"+volume+"/INFO_UF2.TXT") // No INFO_UF2.TXT found, which is expected on a UF2
case "windows": // filesystem.
path, err := windowsFindUSBDrive(volume, options) continue
if err == nil { }
infoPaths = append(infoPaths, path+"/INFO_UF2.TXT") // Found the filesystem, so flash the device!
return moveFile(tmppath, filepath.Join(mount.path, "flash.uf2"))
} }
} }
time.Sleep(500 * time.Millisecond)
} }
return errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
d, err := locateDevice(volumes, infoPaths, options.Timeout)
if err != nil {
return err
}
return moveFile(tmppath, filepath.Dir(d)+"/flash.uf2")
} }
func flashHexUsingMSD(volumes []string, tmppath string, options *compileopts.Options) error { func flashHexUsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
// find expected volume path for start := time.Now(); time.Since(start) < options.Timeout; {
destPaths := make([]string, 0, len(volumes)) // Find all mount points.
for _, volume := range volumes { mounts, err := findFATMounts(options)
switch runtime.GOOS { if err != nil {
case "linux", "freebsd": return err
fi, err := os.Stat("/run/media") }
if err != nil || !fi.IsDir() { for _, mount := range mounts {
destPaths = append(destPaths, "/media/*/"+volume) for _, volume := range volumes {
} else { if mount.name != volume {
destPaths = append(destPaths, "/run/media/*/"+volume) continue
} }
case "darwin": // Found the filesystem, so flash the device!
destPaths = append(destPaths, "/Volumes/"+volume) return moveFile(tmppath, filepath.Join(mount.path, "flash.hex"))
case "windows":
path, err := windowsFindUSBDrive(volume, options)
if err == nil {
destPaths = append(destPaths, path+"/")
} }
} }
}
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) time.Sleep(500 * time.Millisecond)
} }
if d == nil { return errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
return "", errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
}
return d[0], nil
} }
func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, error) { type mountPoint struct {
cmd := executeCommand(options, "wmic", name string
"PATH", "Win32_LogicalDisk", "WHERE", "VolumeName = '"+volume+"'", path string
"get", "DeviceID,VolumeName,FileSystem,DriveType") }
var out bytes.Buffer // Find all the mount points on the system that use the FAT filesystem.
cmd.Stdout = &out func findFATMounts(options *compileopts.Options) ([]mountPoint, error) {
err := cmd.Run() var points []mountPoint
if err != nil { switch runtime.GOOS {
return "", err case "darwin":
} list, err := os.ReadDir("/Volumes")
if err != nil {
for _, line := range strings.Split(out.String(), "\n") { return nil, fmt.Errorf("could not list mount points: %w", err)
words := strings.Fields(line)
if len(words) >= 3 {
if words[1] == "2" && words[2] == "FAT" {
return words[0], nil
}
} }
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. // getDefaultPort returns the default serial port depending on the operating system.
@@ -1777,15 +1796,17 @@ func main() {
} }
if flagJSON { if flagJSON {
json, _ := json.MarshalIndent(struct { json, _ := json.MarshalIndent(struct {
GOROOT string `json:"goroot"` Target *compileopts.TargetSpec `json:"target"`
GOOS string `json:"goos"` GOROOT string `json:"goroot"`
GOARCH string `json:"goarch"` GOOS string `json:"goos"`
GOARM string `json:"goarm"` GOARCH string `json:"goarch"`
BuildTags []string `json:"build_tags"` GOARM string `json:"goarm"`
GC string `json:"garbage_collector"` BuildTags []string `json:"build_tags"`
Scheduler string `json:"scheduler"` GC string `json:"garbage_collector"`
LLVMTriple string `json:"llvm_triple"` Scheduler string `json:"scheduler"`
LLVMTriple string `json:"llvm_triple"`
}{ }{
Target: config.Target,
GOROOT: cachedGOROOT, GOROOT: cachedGOROOT,
GOOS: config.GOOS(), GOOS: config.GOOS(),
GOARCH: config.GOARCH(), GOARCH: config.GOARCH(),
@@ -1852,7 +1873,7 @@ func main() {
usage(command) usage(command)
case "version": case "version":
goversion := "<unknown>" goversion := "<unknown>"
if s, err := goenv.GorootVersionString(goenv.Get("GOROOT")); err == nil { if s, err := goenv.GorootVersionString(); err == nil {
goversion = s goversion = s
} }
version := goenv.Version version := goenv.Version
+24 -2
View File
@@ -34,6 +34,16 @@ var supportedLinuxArches = map[string]string{
"X86Linux": "linux/386", "X86Linux": "linux/386",
"ARMLinux": "linux/arm/6", "ARMLinux": "linux/arm/6",
"ARM64Linux": "linux/arm64", "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()) var sema = make(chan struct{}, runtime.NumCPU())
@@ -71,6 +81,16 @@ func TestBuild(t *testing.T) {
"zeroalloc.go", "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 != "" { if *testTarget != "" {
// This makes it possible to run one specific test (instead of all), // This makes it possible to run one specific test (instead of all),
// which is especially useful to quickly check whether some changes // 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) t.Fatal("failed to load target spec:", err)
} }
isWebAssembly := options.Target == "wasi" || options.Target == "wasm" || (options.Target == "" && options.GOARCH == "wasm")
for _, name := range tests { for _, name := range tests {
if options.GOOS == "linux" && (options.GOARCH == "arm" || options.GOARCH == "386") { if options.GOOS == "linux" && (options.GOARCH == "arm" || options.GOARCH == "386") {
switch name { 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"}) 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.Run("alias.go-scheduler-none", func(t *testing.T) {
t.Parallel() t.Parallel()
options := compileopts.Options(options) 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) 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. // The recover() builtin isn't supported yet on WebAssembly and Windows.
t.Run("recover.go", func(t *testing.T) { t.Run("recover.go", func(t *testing.T) {
t.Parallel() t.Parallel()
+11
View File
@@ -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" import "machine"
const ( const (
button = machine.BUTTON
buttonMode = machine.PinInputPulldown buttonMode = machine.PinInputPulldown
buttonPinChange = machine.PinFalling buttonPinChange = machine.PinFalling
) )
+1
View File
@@ -5,6 +5,7 @@ package main
import "machine" import "machine"
const ( const (
button = machine.BUTTON
buttonMode = machine.PinInputPullup buttonMode = machine.PinInputPullup
buttonPinChange = machine.PinRising buttonPinChange = machine.PinRising
) )
+2 -12
View File
@@ -8,18 +8,14 @@ package main
import ( import (
"machine" "machine"
"runtime/volatile"
"time" "time"
) )
const ( const (
button = machine.BUTTON led = machine.LED
led = machine.LED
) )
func main() { func main() {
var lightLed volatile.Register8
lightLed.Set(0)
// Configure the LED, defaulting to on (usually setting the pin to low will // Configure the LED, defaulting to on (usually setting the pin to low will
// turn the LED on). // turn the LED on).
@@ -33,13 +29,7 @@ func main() {
// Set an interrupt on this pin. // Set an interrupt on this pin.
err := button.SetInterrupt(buttonPinChange, func(machine.Pin) { err := button.SetInterrupt(buttonPinChange, func(machine.Pin) {
if lightLed.Get() != 0 { led.Set(!led.Get())
lightLed.Set(0)
led.Low()
} else {
lightLed.Set(1)
led.High()
}
}) })
if err != nil { if err != nil {
println("could not configure pin interrupt:", err.Error()) println("could not configure pin interrupt:", err.Error())
+1
View File
@@ -5,6 +5,7 @@ package main
import "machine" import "machine"
const ( const (
button = machine.BUTTON
buttonMode = machine.PinInputPulldown buttonMode = machine.PinInputPulldown
buttonPinChange = machine.PinRising | machine.PinFalling buttonPinChange = machine.PinRising | machine.PinFalling
) )
+1
View File
@@ -5,6 +5,7 @@ package main
import "machine" import "machine"
const ( const (
button = machine.BUTTON
buttonMode = machine.PinInput buttonMode = machine.PinInput
buttonPinChange = machine.PinFalling buttonPinChange = machine.PinFalling
) )
+32
View File
@@ -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)
}
}
+42
View File
@@ -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))
}
}
+10
View File
@@ -251,3 +251,13 @@ func IndexRabinKarp(s, substr string) int {
} }
return -1 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)
}
+111
View File
@@ -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
)
+1
View File
@@ -16,6 +16,7 @@ const (
const ( const (
SWITCH = GPIO0 SWITCH = GPIO0
BUTTON = GPIO0
KEY1 = GPIO1 KEY1 = GPIO1
KEY2 = GPIO2 KEY2 = GPIO2
+5 -2
View File
@@ -97,7 +97,7 @@ func (i2c *I2C) stop() {
} }
// writeByte writes a single byte to the I2C bus. // 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. // Write data to register.
avr.TWDR.Set(data) avr.TWDR.Set(data)
@@ -107,6 +107,7 @@ func (i2c *I2C) writeByte(data byte) {
// Wait till data is transmitted. // Wait till data is transmitted.
for !avr.TWCR.HasBits(avr.TWCR_TWINT) { for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
} }
return nil
} }
// readByte reads a single byte from the I2C bus. // 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. // 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. // Wait until UART buffer is not busy.
for !uart.statusRegA.HasBits(avr.UCSR0A_UDRE0) { for !uart.statusRegA.HasBits(avr.UCSR0A_UDRE0) {
} }
@@ -198,6 +199,8 @@ func (uart *UART) WriteByte(c byte) error {
return nil return nil
} }
func (uart *UART) flush() {}
// SPIConfig is used to store config info for SPI. // SPIConfig is used to store config info for SPI.
type SPIConfig struct { type SPIConfig struct {
Frequency uint32 Frequency uint32
+104
View File
@@ -466,3 +466,107 @@ var SPI0 = SPI{
sdo: PB3, sdo: PB3,
sdi: PB4, sdi: PB4,
cs: PB2} 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)
}
+3 -1
View File
@@ -626,7 +626,7 @@ func (uart *UART) SetBaudRate(br uint32) {
} }
// WriteByte writes a byte of data to the UART. // 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 // wait until ready to receive
for !uart.Bus.INTFLAG.HasBits(sam.SERCOM_USART_INTFLAG_DRE) { for !uart.Bus.INTFLAG.HasBits(sam.SERCOM_USART_INTFLAG_DRE) {
} }
@@ -634,6 +634,8 @@ func (uart *UART) WriteByte(c byte) error {
return nil return nil
} }
func (uart *UART) flush() {}
// handleInterrupt should be called from the appropriate interrupt handler for // handleInterrupt should be called from the appropriate interrupt handler for
// this UART instance. // this UART instance.
func (uart *UART) handleInterrupt(interrupt.Interrupt) { func (uart *UART) handleInterrupt(interrupt.Interrupt) {
+53 -2
View File
@@ -1114,7 +1114,7 @@ func (uart *UART) SetBaudRate(br uint32) {
} }
// WriteByte writes a byte of data to the UART. // 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 // wait until ready to receive
for !uart.Bus.INTFLAG.HasBits(sam.SERCOM_USART_INT_INTFLAG_DRE) { for !uart.Bus.INTFLAG.HasBits(sam.SERCOM_USART_INT_INTFLAG_DRE) {
} }
@@ -1122,6 +1122,8 @@ func (uart *UART) WriteByte(c byte) error {
return nil return nil
} }
func (uart *UART) flush() {}
func (uart *UART) handleInterrupt(interrupt.Interrupt) { func (uart *UART) handleInterrupt(interrupt.Interrupt) {
// should reset IRQ // should reset IRQ
uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF))) uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF)))
@@ -1162,7 +1164,7 @@ const (
wireCmdStop = 3 wireCmdStop = 3
) )
const i2cTimeout = 1000 const i2cTimeout = 28000 // about 210us
// Configure is intended to setup the I2C interface. // Configure is intended to setup the I2C interface.
func (i2c *I2C) Configure(config I2CConfig) error { func (i2c *I2C) Configure(config I2CConfig) error {
@@ -2297,3 +2299,52 @@ func checkFlashError() error {
return nil 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)
}
+3 -1
View File
@@ -314,7 +314,7 @@ func (uart *UART) Configure(config UARTConfig) {
uart.Bus.CLKDIV.Set(peripheralClock / config.BaudRate) 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 { for (uart.Bus.STATUS.Get()>>16)&0xff >= 128 {
// Read UART_TXFIFO_CNT from the status register, which indicates how // Read UART_TXFIFO_CNT from the status register, which indicates how
// many bytes there are in the transmit buffer. Wait until there are // 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 return nil
} }
func (uart *UART) flush() {}
// Serial Peripheral Interface on the ESP32. // Serial Peripheral Interface on the ESP32.
type SPI struct { type SPI struct {
Bus *esp.SPI_Type Bus *esp.SPI_Type
+3 -1
View File
@@ -493,7 +493,7 @@ func (uart *UART) enableReceiver() {
uart.Bus.SetINT_ENA_RXFIFO_OVF_INT_ENA(1) 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 { 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 // Read UART_TXFIFO_CNT from the status register, which indicates how
// many bytes there are in the transmit buffer. Wait until there are // 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)) uart.Bus.FIFO.Set(uint32(b))
return nil return nil
} }
func (uart *UART) flush() {}
+4 -2
View File
@@ -181,12 +181,14 @@ func (uart *UART) Configure(config UARTConfig) {
esp.UART0.UART_CLKDIV.Set(CPUFrequency() / config.BaudRate) 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. // 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 { for (esp.UART0.UART_STATUS.Get()>>16)&0xff >= 128 {
// Wait until the TX buffer has room. // Wait until the TX buffer has room.
} }
esp.UART0.UART_FIFO.Set(uint32(c)) esp.UART0.UART_FIFO.Set(uint32(c))
return nil return nil
} }
func (uart *UART) flush() {}
+4 -1
View File
@@ -111,13 +111,16 @@ func (uart *UART) handleInterrupt(interrupt.Interrupt) {
uart.Receive(c) 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 { for sifive.UART0.TXDATA.Get()&sifive.UART_TXDATA_FULL != 0 {
} }
sifive.UART0.TXDATA.Set(uint32(c)) 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 // 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. // to use SPI1 or SPI2 port for most applications.
type SPI struct { type SPI struct {
+4 -1
View File
@@ -392,13 +392,16 @@ func (uart *UART) handleInterrupt(interrupt.Interrupt) {
uart.Receive(c) 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 { for uart.Bus.TXDATA.Get()&kendryte.UARTHS_TXDATA_FULL != 0 {
} }
uart.Bus.TXDATA.Set(uint32(c)) uart.Bus.TXDATA.Set(uint32(c))
return nil
} }
func (uart *UART) flush() {}
type SPI struct { type SPI struct {
Bus *kendryte.SPI_Type Bus *kendryte.SPI_Type
} }
+3 -1
View File
@@ -173,7 +173,7 @@ func (uart *UART) Sync() error {
} }
// WriteByte writes a single byte of data to the UART interface. // 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() uart.startTransmitting()
for !uart.txBuffer.Put(c) { for !uart.txBuffer.Put(c) {
} }
@@ -181,6 +181,8 @@ func (uart *UART) WriteByte(c byte) error {
return nil return nil
} }
func (uart *UART) flush() {}
// getBaudRateDivisor finds the greatest over-sampling factor (4..32) and // getBaudRateDivisor finds the greatest over-sampling factor (4..32) and
// corresponding baud rate divisor (1..8191) that best partition a given baud // corresponding baud rate divisor (1..8191) that best partition a given baud
// rate into equal intervals. // rate into equal intervals.
+12 -2
View File
@@ -186,7 +186,7 @@ func (uart *UART) SetBaudRate(br uint32) {
} }
// WriteByte writes a byte of data to the UART. // 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.EVENTS_TXDRDY.Set(0)
nrf.UART0.TXD.Set(uint32(c)) nrf.UART0.TXD.Set(uint32(c))
for nrf.UART0.EVENTS_TXDRDY.Get() == 0 { for nrf.UART0.EVENTS_TXDRDY.Get() == 0 {
@@ -194,6 +194,8 @@ func (uart *UART) WriteByte(c byte) error {
return nil return nil
} }
func (uart *UART) flush() {}
func (uart *UART) handleInterrupt(interrupt.Interrupt) { func (uart *UART) handleInterrupt(interrupt.Interrupt) {
if nrf.UART0.EVENTS_RXDRDY.Get() != 0 { if nrf.UART0.EVENTS_RXDRDY.Get() != 0 {
uart.Receive(byte(nrf.UART0.RXD.Get())) 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. // I2CConfig is used to store config info for I2C.
type I2CConfig struct { type I2CConfig struct {
Frequency uint32 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. // 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) i2c.Bus.TASKS_STOP.Set(1)
for i2c.Bus.EVENTS_STOPPED.Get() == 0 { for i2c.Bus.EVENTS_STOPPED.Get() == 0 {
tries++
if tries >= i2cTimeout {
return errI2CSignalStopTimeout
}
} }
i2c.Bus.EVENTS_STOPPED.Set(0) i2c.Bus.EVENTS_STOPPED.Set(0)
return nil
} }
var rngStarted = false var rngStarted = false
+43
View File
@@ -214,3 +214,46 @@ func twisError(val uint32) error {
return errI2CBusError 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)
}
+17 -14
View File
@@ -288,24 +288,27 @@ func (spi SPI) Tx(w, r []byte) error {
// supported. // supported.
for len(r) != 0 || len(w) != 0 { for len(r) != 0 || len(w) != 0 {
// Prepare the SPI transfer: set the DMA pointers and lengths. // 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])))) spi.Bus.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&r[0]))))
n := uint32(len(r)) r = r[nr:]
if n > 255 {
n = 255
}
spi.Bus.RXD.MAXCNT.Set(n)
r = r[n:]
} }
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])))) spi.Bus.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&w[0]))))
n := uint32(len(w)) w = w[nw:]
if n > 255 {
n = 255
}
spi.Bus.TXD.MAXCNT.Set(n)
w = w[n:]
} }
spi.Bus.TXD.MAXCNT.Set(nw)
// Do the transfer. // Do the transfer.
// Note: this can be improved by not waiting until the transfer is // Note: this can be improved by not waiting until the transfer is
+24 -4
View File
@@ -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) 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 { 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 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. // writeByte writes a single byte to the I2C bus and waits for confirmation.
func (i2c *I2C) writeByte(data byte) error { func (i2c *I2C) writeByte(data byte) error {
tries := 0
i2c.Bus.TXD.Set(uint32(data)) i2c.Bus.TXD.Set(uint32(data))
for i2c.Bus.EVENTS_TXDSENT.Get() == 0 { for i2c.Bus.EVENTS_TXDSENT.Get() == 0 {
if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 { if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 {
i2c.Bus.EVENTS_ERROR.Set(0) i2c.Bus.EVENTS_ERROR.Set(0)
return errI2CBusError return errI2CBusError
} }
tries++
if tries >= i2cTimeout {
return errI2CWriteTimeout
}
} }
i2c.Bus.EVENTS_TXDSENT.Set(0) i2c.Bus.EVENTS_TXDSENT.Set(0)
return nil 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. // readByte reads a single byte from the I2C bus when it is ready.
func (i2c *I2C) readByte() (byte, error) { func (i2c *I2C) readByte() (byte, error) {
tries := 0
for i2c.Bus.EVENTS_RXDREADY.Get() == 0 { for i2c.Bus.EVENTS_RXDREADY.Get() == 0 {
if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 { if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 {
i2c.Bus.EVENTS_ERROR.Set(0) i2c.Bus.EVENTS_ERROR.Set(0)
return 0, errI2CBusError return 0, errI2CBusError
} }
tries++
if tries >= i2cTimeout {
return 0, errI2CReadTimeout
}
} }
i2c.Bus.EVENTS_RXDREADY.Set(0) i2c.Bus.EVENTS_RXDREADY.Set(0)
return byte(i2c.Bus.RXD.Get()), nil return byte(i2c.Bus.RXD.Get()), nil
+3 -1
View File
@@ -292,7 +292,7 @@ func (u *UART) handleStatusInterrupt(interrupt.Interrupt) {
} }
// WriteByte writes a byte of data to the UART. // 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 { if !u.Configured {
return ErrNotConfigured return ErrNotConfigured
} }
@@ -305,3 +305,5 @@ func (u *UART) WriteByte(c byte) error {
u.C2.Set(uartC2TXActive) u.C2.Set(uartC2TXActive)
return nil return nil
} }
func (uart *UART) flush() {}
+1 -1
View File
@@ -182,7 +182,7 @@ func (clk *clock) configure(src, auxsrc, srcFreq, freq uint32) {
// Must be called before any other clock function. // Must be called before any other clock function.
func (clks *clocksType) init() { func (clks *clocksType) init() {
// Start the watchdog tick // Start the watchdog tick
watchdog.startTick(xoscFreq) Watchdog.startTick(xoscFreq)
// Disable resus that may be enabled from previous software // Disable resus that may be enabled from previous software
clks.resus.ctrl.Set(0) clks.resus.ctrl.Set(0)
+97
View File
@@ -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)
}
+9
View File
@@ -174,6 +174,9 @@ func (p Pin) init() {
// Configure configures the gpio pin as per mode. // Configure configures the gpio pin as per mode.
func (p Pin) Configure(config PinConfig) { func (p Pin) Configure(config PinConfig) {
if p == NoPin {
return
}
p.init() p.init()
mask := uint32(1) << p mask := uint32(1) << p
switch config.Mode { 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. // Set drives the pin high if value is true else drives it low.
func (p Pin) Set(value bool) { func (p Pin) Set(value bool) {
if p == NoPin {
return
}
if value { if value {
p.set() p.set()
} else { } else {
@@ -251,6 +257,9 @@ var (
// nil func to unset the pin change interrupt. If you do so, the change // 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). // parameter is ignored and can be set to any value (such as 0).
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error { func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
if p == NoPin {
return nil
}
if p > 31 || p < 0 { if p > 31 || p < 0 {
return ErrInvalidInputPin return ErrInvalidInputPin
} }
+3 -81
View File
@@ -3,7 +3,6 @@
package machine package machine
import ( import (
"bytes"
"runtime/interrupt" "runtime/interrupt"
"unsafe" "unsafe"
) )
@@ -136,40 +135,14 @@ void ram_func flash_erase_blocks(uint32_t offset, size_t count)
*/ */
import "C" import "C"
// EnterBootloader should perform a system reset in preparation func enterBootloader() {
// to switch to the bootloader to flash new firmware.
func EnterBootloader() {
C.reset_usb_boot(0, 0) C.reset_usb_boot(0, 0)
} }
// Flash related code // Flash related code
const memoryStart = C.XIP_BASE // memory start for purpose of erase const memoryStart = C.XIP_BASE // memory start for purpose of erase
// compile-time check for ensuring we fulfill BlockDevice interface func (f flashBlockDevice) writeAt(p []byte, off int64) (n int, err error) {
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) {
if writeAddress(off)+uintptr(C.XIP_BASE) > FlashDataEnd() { if writeAddress(off)+uintptr(C.XIP_BASE) > FlashDataEnd() {
return 0, errFlashCannotWritePastEOF return 0, errFlashCannotWritePastEOF
} }
@@ -190,37 +163,7 @@ func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
return len(padded), nil return len(padded), nil
} }
// Size returns the number of bytes in this block device. func (f flashBlockDevice) eraseBlocks(start, length int64) error {
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 {
address := writeAddress(start * f.EraseBlockSize()) address := writeAddress(start * f.EraseBlockSize())
if address+uintptr(C.XIP_BASE) > FlashDataEnd() { if address+uintptr(C.XIP_BASE) > FlashDataEnd() {
return errFlashCannotErasePastEOF return errFlashCannotErasePastEOF
@@ -233,24 +176,3 @@ func (f flashBlockDevice) EraseBlocks(start, length int64) error {
return nil 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)
}
+6 -1
View File
@@ -368,6 +368,9 @@ func (spi SPI) rx(rx []byte, txrepeat byte) error {
continue continue
} }
} }
for spi.isBusy() {
gosched()
}
return nil return nil
} }
@@ -397,6 +400,8 @@ func (spi SPI) txrx(tx, rx []byte) error {
// Transaction ended early due to timeout // Transaction ended early due to timeout
return ErrSPITimeout return ErrSPITimeout
} }
for spi.isBusy() {
gosched()
}
return nil return nil
} }
+8 -1
View File
@@ -86,9 +86,10 @@ func (uart *UART) SetBaudRate(br uint32) {
} }
// WriteByte writes a byte of data to the UART. // 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 // wait until buffer is not full
for uart.Bus.UARTFR.HasBits(rp.UART0_UARTFR_TXFF) { for uart.Bus.UARTFR.HasBits(rp.UART0_UARTFR_TXFF) {
gosched()
} }
// write data // write data
@@ -96,6 +97,12 @@ func (uart *UART) WriteByte(c byte) error {
return nil 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. // SetFormat for number of data bits, stop bits, and parity for the UART.
func (uart *UART) SetFormat(databits, stopbits uint8, parity UARTParity) error { func (uart *UART) SetFormat(databits, stopbits uint8, parity UARTParity) error {
var pen, pev uint8 var pen, pev uint8
@@ -3,6 +3,7 @@
package machine package machine
import ( import (
"device/arm"
"device/rp" "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) 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 // 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. // if timer pool disabled, or no timer available, have to busy wait.
hw_enumeration_fix_finish() hw_enumeration_fix_finish()
@@ -109,3 +111,10 @@ func hw_enumeration_fix_finish() {
// Restore the pad ctrl value // Restore the pad ctrl value
padsBank0.io[dp].Set(padCtrlPrev) padsBank0.io[dp].Set(padCtrlPrev)
} }
func waitCycles(n int) {
for n > 0 {
arm.Asm("nop")
n--
}
}
+54 -11
View File
@@ -4,24 +4,67 @@ package machine
import ( import (
"device/rp" "device/rp"
"runtime/volatile"
"unsafe"
) )
type watchdogType struct { // Watchdog provides access to the hardware watchdog available
ctrl volatile.Register32 // in the RP2040.
load volatile.Register32 var Watchdog = &watchdogImpl{}
reason volatile.Register32
scratch [8]volatile.Register32 const (
tick volatile.Register32 // 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. // startTick starts the watchdog tick.
// cycles needs to be a divider that when applied to the xosc input, // cycles needs to be a divider that when applied to the xosc input,
// produces a 1MHz clock. So if the xosc frequency is 12MHz, // produces a 1MHz clock. So if the xosc frequency is 12MHz,
// this will need to be 12. // this will need to be 12.
func (wd *watchdogType) startTick(cycles uint32) { func (wd *watchdogImpl) startTick(cycles uint32) {
wd.tick.Set(cycles | rp.WATCHDOG_TICK_ENABLE) rp.WATCHDOG.TICK.Set(cycles | rp.WATCHDOG_TICK_ENABLE)
} }
+66
View File
@@ -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)
}
+3 -1
View File
@@ -74,10 +74,12 @@ func (uart *UART) SetBaudRate(br uint32) {
} }
// WriteByte writes a byte of data to the UART. // 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)) uart.txReg.Set(uint32(c))
for !uart.statusReg.HasBits(uart.txEmptyFlag) { for !uart.statusReg.HasBits(uart.txEmptyFlag) {
} }
return nil return nil
} }
func (uart *UART) flush() {}
+20 -4
View File
@@ -59,11 +59,27 @@ func (uart *UART) Read(data []byte) (n int, err error) {
return size, nil return size, nil
} }
// Write data to the UART. // WriteByte writes a byte of data over the UART's Tx.
func (uart *UART) Write(data []byte) (n int, err error) { // This function blocks until the data is finished being sent.
for _, v := range data { func (uart *UART) WriteByte(c byte) error {
uart.WriteByte(v) 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 return len(data), nil
} }
+48 -54
View File
@@ -29,9 +29,7 @@ type Serialer interface {
RTS() bool RTS() bool
} }
var usbDescriptor = descriptor.CDC var usbDescriptor descriptor.Descriptor
var usbDescriptorConfig uint8 = usb.DescriptorConfigCDC
func usbVendorID() uint16 { func usbVendorID() uint16 {
if usb.VendorID != 0 { if usb.VendorID != 0 {
@@ -138,18 +136,6 @@ func sendDescriptor(setup usb.Setup) {
sendUSBPacket(0, usbDescriptor.Configuration, setup.WLength) sendUSBPacket(0, usbDescriptor.Configuration, setup.WLength)
return return
case descriptor.TypeDevice: 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()) usbDescriptor.Configure(usbVendorID(), usbProductID())
sendUSBPacket(0, usbDescriptor.Device, setup.WLength) sendUSBPacket(0, usbDescriptor.Device, setup.WLength)
return return
@@ -273,48 +259,56 @@ func handleStandardSetup(setup usb.Setup) bool {
} }
func EnableCDC(txHandler func(), rxHandler func([]byte), setupHandler func(usb.Setup) bool) { func EnableCDC(txHandler func(), rxHandler func([]byte), setupHandler func(usb.Setup) bool) {
usbDescriptorConfig |= usb.DescriptorConfigCDC if len(usbDescriptor.Device) == 0 {
endPoints[usb.CDC_ENDPOINT_ACM] = (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn) usbDescriptor = descriptor.CDC
endPoints[usb.CDC_ENDPOINT_OUT] = (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut) }
endPoints[usb.CDC_ENDPOINT_IN] = (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn) // Initialization of endpoints is required even for non-CDC
usbRxHandler[usb.CDC_ENDPOINT_OUT] = rxHandler ConfigureUSBEndpoint(usbDescriptor,
usbTxHandler[usb.CDC_ENDPOINT_IN] = txHandler []usb.EndpointConfig{
usbSetupHandler[usb.CDC_ACM_INTERFACE] = setupHandler // 0x02 (Communications and CDC Control) {
usbSetupHandler[usb.CDC_DATA_INTERFACE] = nil // 0x0A (CDC-Data) 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 ConfigureUSBEndpoint(desc descriptor.Descriptor, epSettings []usb.EndpointConfig, setup []usb.SetupConfig) {
func EnableHID(txHandler func(), rxHandler func([]byte), setupHandler func(usb.Setup) bool) { usbDescriptor = desc
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)
}
// EnableMIDI enables MIDI. This function must be executed from the init(). for _, ep := range epSettings {
func EnableMIDI(txHandler func(), rxHandler func([]byte), setupHandler func(usb.Setup) bool) { if ep.IsIn {
usbDescriptorConfig |= usb.DescriptorConfigMIDI endPoints[ep.Index] = uint32(ep.Type | usb.EndpointIn)
endPoints[usb.MIDI_ENDPOINT_OUT] = (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut) if ep.TxHandler != nil {
endPoints[usb.MIDI_ENDPOINT_IN] = (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn) usbTxHandler[ep.Index] = ep.TxHandler
usbRxHandler[usb.MIDI_ENDPOINT_OUT] = rxHandler }
usbTxHandler[usb.MIDI_ENDPOINT_IN] = txHandler } else {
} endPoints[ep.Index] = uint32(ep.Type | usb.EndpointOut)
if ep.RxHandler != nil {
// EnableJoystick enables HID. This function must be executed from the init(). usbRxHandler[ep.Index] = ep.RxHandler
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
} }
class.ClassLength(uint16(len(hidDesc))) for _, s := range setup {
descriptor.CDCJoystick.HID[2] = hidDesc usbSetupHandler[s.Index] = s.Handler
}
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)
} }
+18 -1
View File
@@ -3,6 +3,7 @@ package midi
import ( import (
"machine" "machine"
"machine/usb" "machine/usb"
"machine/usb/descriptor"
) )
const ( const (
@@ -40,7 +41,23 @@ func newMidi() *midi {
m := &midi{ m := &midi{
buf: NewRingBuffer(), 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 return m
} }
+14
View File
@@ -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
}
+12 -2
View File
@@ -26,7 +26,7 @@ var interfaceHID = [interfaceTypeLen]byte{
TypeInterface, TypeInterface,
0x02, // InterfaceNumber 0x02, // InterfaceNumber
0x00, // AlternateSetting 0x00, // AlternateSetting
0x01, // NumEndpoints 0x02, // NumEndpoints
0x03, // InterfaceClass 0x03, // InterfaceClass
0x00, // InterfaceSubClass 0x00, // InterfaceSubClass
0x00, // InterfaceProtocol 0x00, // InterfaceProtocol
@@ -103,7 +103,7 @@ var classHID = [ClassHIDTypeLen]byte{
0x00, // CountryCode 0x00, // CountryCode
0x01, // NumDescriptors 0x01, // NumDescriptors
0x22, // ClassType 0x22, // ClassType
0x7E, // ClassLength L 0x90, // ClassLength L
0x00, // ClassLength H 0x00, // ClassLength H
} }
@@ -128,6 +128,7 @@ var CDCHID = Descriptor{
InterfaceHID.Bytes(), InterfaceHID.Bytes(),
ClassHID.Bytes(), ClassHID.Bytes(),
EndpointEP4IN.Bytes(), EndpointEP4IN.Bytes(),
EndpointEP5OUT.Bytes(),
}), }),
HID: map[uint16][]byte{ HID: map[uint16][]byte{
2: Append([][]byte{ 2: Append([][]byte{
@@ -147,6 +148,15 @@ var CDCHID = Descriptor{
HIDReportCount(1), HIDReportCount(1),
HIDReportSize(8), HIDReportSize(8),
HIDInputConstVarAbs, HIDInputConstVarAbs,
HIDReportCount(3),
HIDReportSize(1),
HIDUsagePageLED,
HIDUsageMinimum(1),
HIDUsageMaximum(3),
HIDOutputDataVarAbs,
HIDReportCount(5),
HIDReportSize(1),
HIDOutputConstVarAbs,
HIDReportCount(6), HIDReportCount(6),
HIDReportSize(8), HIDReportSize(8),
HIDLogicalMinimum(0), HIDLogicalMinimum(0),
+62 -30
View File
@@ -3,21 +3,29 @@ package descriptor
const ( const (
hidUsagePage = 0x05 hidUsagePage = 0x05
hidUsage = 0x09 hidUsage = 0x09
hidLogicalMinimum = 0x15 hidLogicalMinimum = 0x14
hidLogicalMaximum = 0x25 hidLogicalMaximum = 0x24
hidUsageMinimum = 0x19 hidUsageMinimum = 0x18
hidUsageMaximum = 0x29 hidUsageMaximum = 0x28
hidPhysicalMinimum = 0x35 hidPhysicalMinimum = 0x34
hidPhysicalMaximum = 0x46 hidPhysicalMaximum = 0x44
hidUnitExponent = 0x55 hidUnitExponent = 0x55
hidUnit = 0x65 hidUnit = 0x65
hidCollection = 0xa1 hidCollection = 0xa1
hidInput = 0x81 hidInput = 0x81
hidOutput = 0x91
hidReportSize = 0x75 hidReportSize = 0x75
hidReportCount = 0x95 hidReportCount = 0x95
hidReportID = 0x85 hidReportID = 0x85
) )
const (
hidSizeValue0 = 0x00
hidSizeValue1 = 0x01
hidSizeValue2 = 0x02
hidSizeValue4 = 0x03
)
var ( var (
HIDUsagePageGenericDesktop = []byte{hidUsagePage, 0x01} HIDUsagePageGenericDesktop = []byte{hidUsagePage, 0x01}
HIDUsagePageSimulationControls = []byte{hidUsagePage, 0x02} HIDUsagePageSimulationControls = []byte{hidUsagePage, 0x02}
@@ -114,6 +122,12 @@ var (
// Input (Data, Variable, Relative), 2 position bytes (X & Y) // Input (Data, Variable, Relative), 2 position bytes (X & Y)
HIDInputDataVarRel = []byte{hidInput, 0x06} 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 { func HIDReportSize(size int) []byte {
@@ -129,51 +143,69 @@ func HIDReportID(id int) []byte {
} }
func HIDLogicalMinimum(min int) []byte { func HIDLogicalMinimum(min int) []byte {
if min > 255 { switch {
return []byte{hidLogicalMinimum + 1, uint8(min), uint8(min >> 8)} 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 { func HIDLogicalMaximum(max int) []byte {
if max > 255 { switch {
return []byte{hidLogicalMaximum + 1, uint8(max), uint8(max >> 8)} 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 { func HIDUsageMinimum(min int) []byte {
if min > 255 { switch {
return []byte{hidUsageMinimum + 1, uint8(min), uint8(min >> 8)} 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 { func HIDUsageMaximum(max int) []byte {
if max > 255 { switch {
return []byte{hidUsageMaximum + 1, uint8(max), uint8(max >> 8)} 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 { func HIDPhysicalMinimum(min int) []byte {
if min > 255 { switch {
return []byte{hidPhysicalMinimum + 1, uint8(min), uint8(min >> 8)} 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 { func HIDPhysicalMaximum(max int) []byte {
if max > 255 { switch {
return []byte{hidPhysicalMaximum + 1, uint8(max), uint8(max >> 8)} 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 { func HIDUnitExponent(exp int) []byte {
+1 -4
View File
@@ -80,10 +80,7 @@ var JoystickDefaultHIDReport = Append([][]byte{
HIDLogicalMaximum(1), HIDLogicalMaximum(1),
HIDReportSize(1), HIDReportSize(1),
HIDReportCount(16), HIDReportCount(16),
HIDInputDataVarAbs, HIDUnitExponent(0),
HIDReportCount(1),
HIDReportSize(3),
HIDUnitExponent(-16),
HIDUnit(0), HIDUnit(0),
HIDInputDataVarAbs, HIDInputDataVarAbs,
+37 -4
View File
@@ -4,6 +4,7 @@ import (
"errors" "errors"
"machine" "machine"
"machine/usb" "machine/usb"
"machine/usb/descriptor"
) )
// from usb-hid.go // from usb-hid.go
@@ -22,7 +23,8 @@ const (
) )
type hidDevicer interface { type hidDevicer interface {
Handler() bool TxHandler() bool
RxHandler([]byte) bool
} }
var devices [5]hidDevicer var devices [5]hidDevicer
@@ -32,19 +34,50 @@ var size int
// calls machine.EnableHID for USB configuration // calls machine.EnableHID for USB configuration
func SetHandler(d hidDevicer) { func SetHandler(d hidDevicer) {
if size == 0 { 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 devices[size] = d
size++ size++
} }
func handler() { func txHandler() {
for _, d := range devices { for _, d := range devices {
if d == nil { if d == nil {
continue 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 return
} }
} }
+39 -6
View File
@@ -3,6 +3,7 @@ package joystick
import ( import (
"machine" "machine"
"machine/usb" "machine/usb"
"machine/usb/descriptor"
"machine/usb/hid" "machine/usb/hid"
) )
@@ -32,18 +33,50 @@ func UseSettings(def Definitions, rxHandlerFunc func(b []byte), setupFunc func(s
if setupFunc == nil { if setupFunc == nil {
setupFunc = hid.DefaultSetupHandler 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 Joystick = js
return js return js
} }
func newDefaultJoystick() *joystick { func newDefaultJoystick() *joystick {
def := DefaultDefinitions() def := DefaultDefinitions()
js := &joystick{ js := UseSettings(def, nil, nil, nil)
State: def.NewState(),
buf: hid.NewRingBuffer(),
}
machine.EnableJoystick(js.handler, js.rxHandler, hid.DefaultSetupHandler, def.Descriptor())
return js return js
} }
+20 -1
View File
@@ -81,7 +81,7 @@ func newKeyboard() *keyboard {
} }
} }
func (kb *keyboard) Handler() bool { func (kb *keyboard) TxHandler() bool {
kb.waitTxc = false kb.waitTxc = false
if b, ok := kb.buf.Get(); ok { if b, ok := kb.buf.Get(); ok {
kb.waitTxc = true kb.waitTxc = true
@@ -91,6 +91,13 @@ func (kb *keyboard) Handler() bool {
return false 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) { func (kb *keyboard) tx(b []byte) {
if machine.USBDev.InitEndpointComplete { if machine.USBDev.InitEndpointComplete {
if kb.waitTxc { 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 { func (kb *keyboard) ready() bool {
return true return true
} }
+5 -1
View File
@@ -47,7 +47,7 @@ func newMouse() *mouse {
} }
} }
func (m *mouse) Handler() bool { func (m *mouse) TxHandler() bool {
m.waitTxc = false m.waitTxc = false
if b, ok := m.buf.Get(); ok { if b, ok := m.buf.Get(); ok {
m.waitTxc = true m.waitTxc = true
@@ -57,6 +57,10 @@ func (m *mouse) Handler() bool {
return false return false
} }
func (m *mouse) RxHandler(b []byte) bool {
return false
}
func (m *mouse) tx(b []byte) { func (m *mouse) tx(b []byte) {
if machine.USBDev.InitEndpointComplete { if machine.USBDev.InitEndpointComplete {
if m.waitTxc { if m.waitTxc {
+34
View File
@@ -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
View File
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build linux && !baremetal && !wasi //go:build linux && !baremetal && !wasi && !wasip1
package os package os
+1 -4
View File
@@ -6,7 +6,7 @@
// fairly similar: we use fdopendir, fdclosedir, and readdir from wasi-libc in // fairly similar: we use fdopendir, fdclosedir, and readdir from wasi-libc in
// a similar way that the darwin code uses functions from libc. // a similar way that the darwin code uses functions from libc.
//go:build wasi //go:build wasi || wasip1
package os package os
@@ -53,9 +53,6 @@ func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEn
if dirent == nil { // EOF if dirent == nil { // EOF
break break
} }
if dirent.Ino == 0 {
continue
}
name := dirent.Name() name := dirent.Name()
// Check for useless names before allocating a string. // Check for useless names before allocating a string.
if string(name) == "." || string(name) == ".." { if string(name) == "." || string(name) == ".." {
+1 -1
View File
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build darwin || linux //go:build darwin || linux || wasip1
package os_test package os_test
+4
View File
@@ -32,6 +32,10 @@ var (
// The following code is copied from the official implementation. // The following code is copied from the official implementation.
// src/internal/poll/fd.go // 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. // ErrDeadlineExceeded is returned for an expired deadline.
// This is exported by the os package as os.ErrDeadlineExceeded. // This is exported by the os package as os.ErrDeadlineExceeded.
var ErrDeadlineExceeded error = &DeadlineExceededError{} var ErrDeadlineExceeded error = &DeadlineExceededError{}
+1 -1
View File
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // 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 package os
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build baremetal || (wasm && !wasi) //go:build baremetal || (wasm && !wasi && !wasip1)
package os package os
+1 -1
View File
@@ -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, // target wasi sets GOOS=linux and thus the +linux build tag,
// even though it doesn't show up in "tinygo info target -wasi" // even though it doesn't show up in "tinygo info target -wasi"
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build windows || darwin || (linux && !baremetal) //go:build windows || darwin || (linux && !baremetal) || wasip1
package os_test package os_test
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build !wasi //go:build !wasi && !wasip1
package os_test package os_test
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build wasi //go:build wasi || wasip1
package os_test package os_test
+1 -2
View File
@@ -1,4 +1,4 @@
//go:build windows || darwin || (linux && !baremetal) //go:build windows || darwin || (linux && !baremetal) || wasip1
package os_test package os_test
@@ -23,7 +23,6 @@ var dot = []string{
"os_test.go", "os_test.go",
"types.go", "types.go",
"stat_darwin.go", "stat_darwin.go",
"stat_linux.go",
} }
func randomName() string { func randomName() string {
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build !baremetal && !js && !wasi //go:build !baremetal && !js && !wasi && !wasip1
// Copyright 2009 The Go Authors. All rights reserved. // Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
+1 -1
View File
@@ -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. // Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build !baremetal && !js && !wasi //go:build !baremetal && !js && !wasi && !wasip1
// Copyright 2009 The Go Authors. All rights reserved. // Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
+1 -1
View File
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !baremetal && !js && !wasi //go:build !baremetal && !js && !wasi && !wasip1
package os package os
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build baremetal || js || wasi //go:build baremetal || js || wasi || wasip1
// Copyright 2009 The Go Authors. All rights reserved. // Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // 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. // Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // 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 package os
import ( import (
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build baremetal || (wasm && !wasi) //go:build baremetal || (wasm && !wasi && !wasip1)
// Copyright 2016 The Go Authors. All rights reserved. // Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // 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