mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 22:58:41 +00:00
Compare commits
98 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ce0abac5a3 | |||
| 43d7f48de4 | |||
| f7f2082979 | |||
| 9b1a19f184 | |||
| 1ad6953858 | |||
| 3e8cdb62c9 | |||
| 720a54a0fe | |||
| de45da5df4 | |||
| c9fc95f6f3 | |||
| 2721ab146f | |||
| 81c723db1a | |||
| 9a20af5b59 | |||
| 4e8e3f348f | |||
| 496452b676 | |||
| e86ca2080d | |||
| e2bf7bbb49 | |||
| 4d1d0c2d3b | |||
| 2281b6a3f5 | |||
| 4321923641 | |||
| 4e4b5955db | |||
| 169d5f17b8 | |||
| 9ed5eae6a9 | |||
| e2c55e3d26 | |||
| d3f5b51cd8 | |||
| 2396c22658 | |||
| d61d5d7ab1 | |||
| c5a896771d | |||
| 0c880ec44c | |||
| 64d51b215f | |||
| f103e910d7 | |||
| 40afeea569 | |||
| 97122972fb | |||
| 72064e12db | |||
| 3c31a3110f | |||
| 0e73790d67 | |||
| bcbc241d81 | |||
| 5e2a8024a3 | |||
| b92fad8da6 | |||
| 5c8d4e54d6 | |||
| fed433c046 | |||
| 734613c20e | |||
| 1570adac1c | |||
| c72f9eb08c | |||
| 19c7965fc5 | |||
| c248418dbe | |||
| 4ca2d3f0cf | |||
| 35015a7918 | |||
| bde73fc214 | |||
| ab2a81cc52 | |||
| 2a98433c8e | |||
| 4918395f88 | |||
| 67ac4fdd8e | |||
| e69131c0d3 | |||
| 9f4459cee1 | |||
| 3c55689566 | |||
| 95f509b109 | |||
| dda576e80b | |||
| da505a6b17 | |||
| 424d775bf4 | |||
| 726d735ad3 | |||
| b9fd6cee6f | |||
| 76fb3bd177 | |||
| 29ca1147f5 | |||
| 473644d918 | |||
| 38fc340802 | |||
| b5f028e1f7 | |||
| aa40ddc48b | |||
| 6bcb40fe01 | |||
| e907db1481 | |||
| 6b8940421e | |||
| 2c71f08922 | |||
| 7e64bc8f77 | |||
| 1461563e3f | |||
| acdaa72365 | |||
| 01f5c1d455 | |||
| 00f3a65903 | |||
| b4815192a6 | |||
| c54e1cc955 | |||
| 7801921cc0 | |||
| afc6bd5cdd | |||
| ae2cbbf851 | |||
| ccd79ee289 | |||
| 171d0fe123 | |||
| 5ed0f67e1c | |||
| 8ce3cfad40 | |||
| 904fa852f6 | |||
| d0b2585e82 | |||
| 23e88bfb15 | |||
| 6389e45d99 | |||
| 9342e73ae1 | |||
| fc4857e98c | |||
| f66492a338 | |||
| 445fd37bef | |||
| a9ba6ebad9 | |||
| 565ff99c31 | |||
| ceeba528e7 | |||
| 16c2d84c49 | |||
| f00bb63330 |
+19
-6
@@ -37,6 +37,13 @@ commands:
|
||||
sudo tar -C /usr/local -xf node-v10.15.1-linux-x64.tar.xz
|
||||
sudo ln -s /usr/local/node-v10.15.1-linux-x64/bin/node /usr/bin/node
|
||||
rm node-v10.15.1-linux-x64.tar.xz
|
||||
install-chrome:
|
||||
steps:
|
||||
- run:
|
||||
name: "Install Chrome"
|
||||
command: |
|
||||
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
sudo apt install ./google-chrome-stable_current_amd64.deb
|
||||
llvm-source-linux:
|
||||
steps:
|
||||
- restore_cache:
|
||||
@@ -71,6 +78,7 @@ commands:
|
||||
- apt-dependencies:
|
||||
llvm: "<<parameters.llvm>>"
|
||||
- install-node
|
||||
- install-chrome
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
@@ -85,9 +93,11 @@ commands:
|
||||
key: wasi-libc-sysroot-systemclang-v1
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
- run: tinygo clean
|
||||
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./interp ./transform .
|
||||
- run: make gen-device -j4
|
||||
- run: make smoketest
|
||||
- run: make wasmtest
|
||||
- save_cache:
|
||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
@@ -204,13 +214,21 @@ commands:
|
||||
- run:
|
||||
name: "Test TinyGo"
|
||||
command: make test
|
||||
- run:
|
||||
name: "Install fpm"
|
||||
command: |
|
||||
sudo apt-get install ruby ruby-dev
|
||||
sudo gem install --no-document fpm
|
||||
- run:
|
||||
name: "Build TinyGo release"
|
||||
command: |
|
||||
make release -j3
|
||||
make release deb -j3
|
||||
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_amd64.deb
|
||||
- store_artifacts:
|
||||
path: /tmp/tinygo.linux-amd64.tar.gz
|
||||
- store_artifacts:
|
||||
path: /tmp/tinygo_amd64.deb
|
||||
- save_cache:
|
||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
@@ -294,11 +312,6 @@ commands:
|
||||
tar -C /usr/local/opt -xf /tmp/tinygo.darwin-amd64.tar.gz
|
||||
ln -s /usr/local/opt/tinygo/bin/tinygo /usr/local/bin/tinygo
|
||||
tinygo version
|
||||
- run:
|
||||
name: "Download SiFive GNU toolchain"
|
||||
command: |
|
||||
curl -O https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-apple-darwin.tar.gz
|
||||
sudo tar -C /usr/local --strip-components=1 -xf riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-apple-darwin.tar.gz
|
||||
- run: make smoketest AVR=0
|
||||
- save_cache:
|
||||
key: go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
0.13.1
|
||||
---
|
||||
* **standard library**
|
||||
- `runtime`: do not put scheduler and GC code in the same section
|
||||
- `runtime`: copy stack scan assembly for GBA
|
||||
* **boards**
|
||||
- `gameboy-advance`: always use ARM mode instead of Thumb mode
|
||||
|
||||
|
||||
0.13.0
|
||||
---
|
||||
* **command line**
|
||||
|
||||
+2
-5
@@ -4,7 +4,7 @@ FROM golang:1.14 AS tinygo-base
|
||||
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
|
||||
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-10 main" >> /etc/apt/sources.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y llvm-10-dev libclang-10-dev git
|
||||
apt-get install -y llvm-10-dev libclang-10-dev lld-10 git
|
||||
|
||||
COPY . /tinygo
|
||||
|
||||
@@ -27,10 +27,7 @@ COPY --from=tinygo-base /go/bin/tinygo /go/bin/tinygo
|
||||
COPY --from=tinygo-base /tinygo/src /tinygo/src
|
||||
COPY --from=tinygo-base /tinygo/targets /tinygo/targets
|
||||
|
||||
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
|
||||
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-10 main" >> /etc/apt/sources.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y libllvm10 lld-10
|
||||
RUN apt-get install -y libllvm10 lld-10
|
||||
|
||||
# tinygo-avr stage installs the needed dependencies to compile TinyGo programs for AVR microcontrollers.
|
||||
FROM tinygo-base AS tinygo-avr
|
||||
|
||||
@@ -182,7 +182,7 @@ tinygo-test:
|
||||
.PHONY: smoketest
|
||||
smoketest:
|
||||
$(TINYGO) version
|
||||
# test all examples
|
||||
# test all examples (except pwm)
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/adc
|
||||
@@ -203,7 +203,7 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit examples/microbit-blink
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pwm
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -231,6 +231,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit-s110v8 examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nrf52840-mdk examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10031 examples/blinky1
|
||||
@@ -291,11 +293,18 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=reelboard-s140v7 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pygamer examples/blinky1
|
||||
$(TINYGO) build -size short -o test.hex -target=xiao examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(AVR), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-nano examples/blinky1
|
||||
@@ -310,7 +319,10 @@ endif
|
||||
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/export
|
||||
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/main
|
||||
|
||||
release: tinygo gen-device wasi-libc
|
||||
wasmtest:
|
||||
$(GO) test ./tests/wasm
|
||||
|
||||
build/release: tinygo gen-device wasi-libc
|
||||
@mkdir -p build/release/tinygo/bin
|
||||
@mkdir -p build/release/tinygo/lib/clang/include
|
||||
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
||||
@@ -345,4 +357,13 @@ release: tinygo gen-device wasi-libc
|
||||
./build/tinygo build-library -target=armv6m-none-eabi -o build/release/tinygo/pkg/armv6m-none-eabi/picolibc.a picolibc
|
||||
./build/tinygo build-library -target=armv7m-none-eabi -o build/release/tinygo/pkg/armv7m-none-eabi/picolibc.a picolibc
|
||||
./build/tinygo build-library -target=armv7em-none-eabi -o build/release/tinygo/pkg/armv7em-none-eabi/picolibc.a picolibc
|
||||
|
||||
release: build/release
|
||||
tar -czf build/release.tar.gz -C build/release tinygo
|
||||
|
||||
deb: build/release
|
||||
@mkdir -p build/release-deb/usr/local/bin
|
||||
@mkdir -p build/release-deb/usr/local/lib
|
||||
cp -ar build/release/tinygo build/release-deb/usr/local/lib/tinygo
|
||||
ln -sf ../lib/tinygo/bin/tinygo build/release-deb/usr/local/bin/tinygo
|
||||
fpm -f -s dir -t deb -n tinygo -v $(shell grep "const Version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb
|
||||
|
||||
@@ -43,7 +43,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
||||
|
||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||
|
||||
The following 32 microcontroller boards are currently supported:
|
||||
The following 34 microcontroller boards are currently supported:
|
||||
|
||||
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
|
||||
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
||||
@@ -72,6 +72,8 @@ The following 32 microcontroller boards are currently supported:
|
||||
* [Particle Xenon](https://docs.particle.io/datasheets/discontinued/xenon-datasheet/)
|
||||
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
|
||||
* [PineTime DevKit](https://www.pine64.org/pinetime/)
|
||||
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
||||
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
|
||||
* [SiFIve HiFive1](https://www.sifive.com/boards/hifive1)
|
||||
* [ST Micro "Nucleo F103RB"](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
||||
* [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ jobs:
|
||||
script: |
|
||||
export PATH="$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
||||
unset GOROOT
|
||||
make release -j4
|
||||
make build/release -j4
|
||||
- publish: $(System.DefaultWorkingDirectory)/build/release/tinygo
|
||||
displayName: Publish zip as artifact
|
||||
artifact: tinygo
|
||||
|
||||
+5
-1
@@ -32,7 +32,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mod, extraFiles, errs := compiler.Compile(pkgName, machine, config)
|
||||
mod, extraFiles, extraLDFlags, errs := compiler.Compile(pkgName, machine, config)
|
||||
if errs != nil {
|
||||
return newMultiError(errs)
|
||||
}
|
||||
@@ -187,6 +187,10 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
||||
ldflags = append(ldflags, outpath)
|
||||
}
|
||||
|
||||
if len(extraLDFlags) > 0 {
|
||||
ldflags = append(ldflags, extraLDFlags...)
|
||||
}
|
||||
|
||||
// Link the object files together.
|
||||
err = link(config.Target.Linker, ldflags...)
|
||||
if err != nil {
|
||||
|
||||
+8
-2
@@ -16,8 +16,10 @@ var genericBuiltins = []string{
|
||||
"addvsi3.c",
|
||||
"addvti3.c",
|
||||
"apple_versioning.c",
|
||||
"ashlsi3.c",
|
||||
"ashldi3.c",
|
||||
"ashlti3.c",
|
||||
"ashrsi3.c",
|
||||
"ashrdi3.c",
|
||||
"ashrti3.c",
|
||||
"bswapdi2.c",
|
||||
@@ -72,7 +74,9 @@ var genericBuiltins = []string{
|
||||
"floatunsisf.c",
|
||||
"floatuntidf.c",
|
||||
"floatuntisf.c",
|
||||
//"int_util.c",
|
||||
"fp_mode.c",
|
||||
"int_util.c",
|
||||
"lshrsi3.c",
|
||||
"lshrdi3.c",
|
||||
"lshrti3.c",
|
||||
"moddi3.c",
|
||||
@@ -86,6 +90,7 @@ var genericBuiltins = []string{
|
||||
"muloti4.c",
|
||||
"mulsc3.c",
|
||||
"mulsf3.c",
|
||||
"mulsi3.c",
|
||||
"multi3.c",
|
||||
"multf3.c",
|
||||
"mulvdi3.c",
|
||||
@@ -108,6 +113,7 @@ var genericBuiltins = []string{
|
||||
"powidf2.c",
|
||||
"powisf2.c",
|
||||
"powitf2.c",
|
||||
"powixf2.c",
|
||||
"subdf3.c",
|
||||
"subsf3.c",
|
||||
"subvdi3.c",
|
||||
@@ -159,7 +165,7 @@ var aeabiBuiltins = []string{
|
||||
var CompilerRT = Library{
|
||||
name: "compiler-rt",
|
||||
cflags: func() []string { return []string{"-Werror", "-Wall", "-std=c11", "-nostdlibinc"} },
|
||||
sourceDir: "lib/compiler-rt/lib/builtins",
|
||||
sourceDir: "llvm-project.master/compiler-rt/lib/builtins",
|
||||
sources: func(target string) []string {
|
||||
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
|
||||
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
if goroot == "" {
|
||||
return nil, errors.New("cannot locate $GOROOT, please set it manually")
|
||||
}
|
||||
major, minor, err := getGorootVersion(goroot)
|
||||
major, minor, err := goenv.GetGorootVersion(goroot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||
}
|
||||
|
||||
@@ -1,71 +1,13 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// getGorootVersion returns the major and minor version for a given GOROOT path.
|
||||
// If the goroot cannot be determined, (0, 0) is returned.
|
||||
func getGorootVersion(goroot string) (major, minor int, err error) {
|
||||
s, err := GorootVersionString(goroot)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
if s == "" || s[:2] != "go" {
|
||||
return 0, 0, errors.New("could not parse Go version: version does not start with 'go' prefix")
|
||||
}
|
||||
|
||||
parts := strings.Split(s[2:], ".")
|
||||
if len(parts) < 2 {
|
||||
return 0, 0, errors.New("could not parse Go version: version has less than two parts")
|
||||
}
|
||||
|
||||
// Ignore the errors, we don't really handle errors here anyway.
|
||||
var trailing string
|
||||
n, err := fmt.Sscanf(s, "go%d.%d%s", &major, &minor, &trailing)
|
||||
if n == 2 && err == io.EOF {
|
||||
// Means there were no trailing characters (i.e., not an alpha/beta)
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("failed to parse version: %s", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GorootVersionString returns the version string as reported by the Go
|
||||
// toolchain for the given GOROOT path. It is usually of the form `go1.x.y` but
|
||||
// can have some variations (for beta releases, for example).
|
||||
func GorootVersionString(goroot string) (string, error) {
|
||||
if data, err := ioutil.ReadFile(filepath.Join(
|
||||
goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
|
||||
|
||||
r := regexp.MustCompile("const TheVersion = `(.*)`")
|
||||
matches := r.FindSubmatch(data)
|
||||
if len(matches) != 2 {
|
||||
return "", errors.New("Invalid go version output:\n" + string(data))
|
||||
}
|
||||
|
||||
return string(matches[1]), nil
|
||||
|
||||
} else if data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
|
||||
return string(data), nil
|
||||
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// getClangHeaderPath returns the path to the built-in Clang headers. It tries
|
||||
// multiple locations, which should make it find the directory when installed in
|
||||
// various ways.
|
||||
|
||||
+4
-1
@@ -69,11 +69,14 @@ func (l *Library) Load(target string) (path string, err error) {
|
||||
// Precalculate the flags to the compiler invocation.
|
||||
args := append(l.cflags(), "-c", "-Oz", "-g", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
|
||||
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
||||
args = append(args, "-fshort-enums", "-fomit-frame-pointer")
|
||||
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft")
|
||||
}
|
||||
if strings.HasPrefix(target, "riscv32-") {
|
||||
args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
|
||||
}
|
||||
if strings.HasPrefix(target, "avr-") {
|
||||
args = append(args, "-mdouble=64", "-mmcu=atmega1284p")
|
||||
}
|
||||
|
||||
// Compile all sources.
|
||||
var objs []string
|
||||
|
||||
+1
-2
@@ -123,8 +123,7 @@ func objcopy(infile, outfile string) error {
|
||||
if err != nil {
|
||||
return objcopyError{"failed to create .hex file", err}
|
||||
}
|
||||
mem.DumpIntelHex(f, 16) // TODO: handle error
|
||||
return nil
|
||||
return mem.DumpIntelHex(f, 16)
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ var Picolibc = Library{
|
||||
name: "picolibc",
|
||||
cflags: func() []string {
|
||||
picolibcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc")
|
||||
return []string{"-Werror", "-Wall", "-std=gnu11", "-D_COMPILING_NEWLIB", "--sysroot=" + picolibcDir, "-I" + picolibcDir + "/tinystdio", "-I" + goenv.Get("TINYGOROOT") + "/lib/picolibc-include"}
|
||||
return []string{"-Werror", "-Wall", "-std=gnu11", "-D_COMPILING_NEWLIB", "-nostdlibinc", "-Xclang", "-internal-isystem", "-Xclang", picolibcDir + "/include", "-I" + picolibcDir + "/tinystdio", "-I" + goenv.Get("TINYGOROOT") + "/lib/picolibc-include"}
|
||||
},
|
||||
sourceDir: "lib/picolibc/newlib/libc",
|
||||
sources: func(target string) []string {
|
||||
|
||||
+17
-3
@@ -41,6 +41,7 @@ type cgoPackage struct {
|
||||
elaboratedTypes map[string]*elaboratedTypeInfo
|
||||
enums map[string]enumInfo
|
||||
anonStructNum int
|
||||
ldflags []string
|
||||
}
|
||||
|
||||
// constantInfo stores some information about a CGo constant found by libclang
|
||||
@@ -156,7 +157,7 @@ typedef unsigned long long _Cgo_ulonglong;
|
||||
// newly created *ast.File that should be added to the list of to-be-parsed
|
||||
// files. If there is one or more error, it returns these in the []error slice
|
||||
// but still modifies the AST.
|
||||
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []error) {
|
||||
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, []error) {
|
||||
p := &cgoPackage{
|
||||
dir: dir,
|
||||
fset: fset,
|
||||
@@ -183,7 +184,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
||||
// Find the absolute path for this package.
|
||||
packagePath, err := filepath.Abs(fset.File(files[0].Pos()).Name())
|
||||
if err != nil {
|
||||
return nil, []error{
|
||||
return nil, nil, []error{
|
||||
scanner.Error{
|
||||
Pos: fset.Position(files[0].Pos()),
|
||||
Msg: "cgo: cannot find absolute path: " + err.Error(), // TODO: wrap this error
|
||||
@@ -359,6 +360,19 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
||||
}
|
||||
makePathsAbsolute(flags, packagePath)
|
||||
cflags = append(cflags, flags...)
|
||||
case "LDFLAGS":
|
||||
flags, err := shlex.Split(value)
|
||||
if err != nil {
|
||||
// TODO: find the exact location where the error happened.
|
||||
p.addErrorAfter(comment.Slash, comment.Text[:lineStart+colon+1], "failed to parse flags in #cgo line: "+err.Error())
|
||||
continue
|
||||
}
|
||||
if err := checkLinkerFlags(name, flags); err != nil {
|
||||
p.addErrorAfter(comment.Slash, comment.Text[:lineStart+colon+1], err.Error())
|
||||
continue
|
||||
}
|
||||
makePathsAbsolute(flags, packagePath)
|
||||
p.ldflags = append(p.ldflags, flags...)
|
||||
default:
|
||||
startPos := strings.LastIndex(line[4:colon], name) + 4
|
||||
p.addErrorAfter(comment.Slash, comment.Text[:lineStart+startPos], "invalid #cgo line: "+name)
|
||||
@@ -412,7 +426,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
||||
// Print the newly generated in-memory AST, for debugging.
|
||||
//ast.Print(fset, p.generated)
|
||||
|
||||
return p.generated, p.errors
|
||||
return p.generated, p.ldflags, p.errors
|
||||
}
|
||||
|
||||
// makePathsAbsolute converts some common path compiler flags (-I, -L) from
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ func TestCGo(t *testing.T) {
|
||||
}
|
||||
|
||||
// Process the AST with CGo.
|
||||
cgoAST, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
|
||||
cgoAST, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
|
||||
|
||||
// Check the AST for type errors.
|
||||
var typecheckErrors []error
|
||||
|
||||
+3
-3
@@ -246,9 +246,9 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||
}
|
||||
value := source[len(name):]
|
||||
// Try to convert this #define into a Go constant expression.
|
||||
expr, err := parseConst(pos+token.Pos(len(name)), p.fset, value)
|
||||
if err != nil {
|
||||
p.errors = append(p.errors, err)
|
||||
expr, scannerError := parseConst(pos+token.Pos(len(name)), p.fset, value)
|
||||
if scannerError != nil {
|
||||
p.errors = append(p.errors, *scannerError)
|
||||
}
|
||||
if expr != nil {
|
||||
// Parsing was successful.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// +build !byollvm
|
||||
// +build !llvm9
|
||||
// +build !llvm9,!llvm11
|
||||
|
||||
package cgo
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// +build !byollvm
|
||||
// +build llvm11
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-11/include
|
||||
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@11/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm11/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-11/lib -lclang
|
||||
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@11/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm11/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
Vendored
+7
@@ -21,6 +21,13 @@ package main
|
||||
#if defined(NOTDEFINED)
|
||||
#warning flag must not be defined
|
||||
#endif
|
||||
|
||||
// Check Compiler flags
|
||||
#cgo LDFLAGS: -lc
|
||||
|
||||
// This flag is not valid ldflags
|
||||
#cgo LDFLAGS: -does-not-exists
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
|
||||
Vendored
+1
@@ -1,6 +1,7 @@
|
||||
// CGo errors:
|
||||
// testdata/flags.go:5:7: invalid #cgo line: NOFLAGS
|
||||
// testdata/flags.go:8:13: invalid flag: -fdoes-not-exist
|
||||
// testdata/flags.go:29:14: invalid flag: -does-not-exists
|
||||
|
||||
package main
|
||||
|
||||
|
||||
+12
-3
@@ -129,8 +129,8 @@ func (c *Config) NeedsStackObjects() bool {
|
||||
}
|
||||
}
|
||||
|
||||
// Scheduler returns the scheduler implementation. Valid values are "coroutines"
|
||||
// and "tasks".
|
||||
// Scheduler returns the scheduler implementation. Valid values are "none",
|
||||
//"coroutines" and "tasks".
|
||||
func (c *Config) Scheduler() string {
|
||||
if c.Options.Scheduler != "" {
|
||||
return c.Options.Scheduler
|
||||
@@ -173,7 +173,7 @@ func (c *Config) CFlags() []string {
|
||||
}
|
||||
if c.Target.Libc == "picolibc" {
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
cflags = append(cflags, "--sysroot="+filepath.Join(root, "lib", "picolibc", "newlib", "libc"))
|
||||
cflags = append(cflags, "-nostdlibinc", "-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "picolibc", "newlib", "libc", "include"))
|
||||
cflags = append(cflags, "-I"+filepath.Join(root, "lib/picolibc-include"))
|
||||
}
|
||||
return cflags
|
||||
@@ -272,6 +272,15 @@ func (c *Config) OpenOCDConfiguration() (args []string, err error) {
|
||||
return args, nil
|
||||
}
|
||||
|
||||
// CodeModel returns the code model used on this platform.
|
||||
func (c *Config) CodeModel() string {
|
||||
if c.Target.CodeModel != "" {
|
||||
return c.Target.CodeModel
|
||||
}
|
||||
|
||||
return "default"
|
||||
}
|
||||
|
||||
type TestConfig struct {
|
||||
CompileTestBinary bool
|
||||
// TODO: Filter the test functions to run, include verbose flag, etc
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
package compileopts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
validGCOptions = []string{"none", "leaking", "extalloc", "conservative"}
|
||||
validSchedulerOptions = []string{"none", "tasks", "coroutines"}
|
||||
validPrintSizeOptions = []string{"none", "short", "full"}
|
||||
validPanicStrategyOptions = []string{"print", "trap"}
|
||||
)
|
||||
|
||||
// Options contains extra options to give to the compiler. These options are
|
||||
// usually passed from the command line.
|
||||
type Options struct {
|
||||
@@ -21,3 +33,53 @@ type Options struct {
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
}
|
||||
|
||||
// Verify performs a validation on the given options, raising an error if options are not valid.
|
||||
func (o *Options) Verify() error {
|
||||
if o.GC != "" {
|
||||
valid := isInArray(validGCOptions, o.GC)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid gc option '%s': valid values are %s`,
|
||||
o.GC,
|
||||
strings.Join(validGCOptions, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
if o.Scheduler != "" {
|
||||
valid := isInArray(validSchedulerOptions, o.Scheduler)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid scheduler option '%s': valid values are %s`,
|
||||
o.Scheduler,
|
||||
strings.Join(validSchedulerOptions, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
if o.PrintSizes != "" {
|
||||
valid := isInArray(validPrintSizeOptions, o.PrintSizes)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid size option '%s': valid values are %s`,
|
||||
o.PrintSizes,
|
||||
strings.Join(validPrintSizeOptions, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
if o.PanicStrategy != "" {
|
||||
valid := isInArray(validPanicStrategyOptions, o.PanicStrategy)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid panic option '%s': valid values are %s`,
|
||||
o.PanicStrategy,
|
||||
strings.Join(validPanicStrategyOptions, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isInArray(arr []string, item string) bool {
|
||||
for _, i := range arr {
|
||||
if i == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
package compileopts_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
)
|
||||
|
||||
func TestVerifyOptions(t *testing.T) {
|
||||
|
||||
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, extalloc, conservative`)
|
||||
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, coroutines`)
|
||||
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full`)
|
||||
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
opts compileopts.Options
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "OptionsEmpty",
|
||||
opts: compileopts.Options{},
|
||||
},
|
||||
{
|
||||
name: "InvalidGCOption",
|
||||
opts: compileopts.Options{
|
||||
GC: "incorrect",
|
||||
},
|
||||
expectedError: expectedGCError,
|
||||
},
|
||||
{
|
||||
name: "GCOptionNone",
|
||||
opts: compileopts.Options{
|
||||
GC: "none",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GCOptionLeaking",
|
||||
opts: compileopts.Options{
|
||||
GC: "leaking",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GCOptionExtalloc",
|
||||
opts: compileopts.Options{
|
||||
GC: "extalloc",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GCOptionConservative",
|
||||
opts: compileopts.Options{
|
||||
GC: "conservative",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "InvalidSchedulerOption",
|
||||
opts: compileopts.Options{
|
||||
Scheduler: "incorrect",
|
||||
},
|
||||
expectedError: expectedSchedulerError,
|
||||
},
|
||||
{
|
||||
name: "SchedulerOptionNone",
|
||||
opts: compileopts.Options{
|
||||
Scheduler: "none",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SchedulerOptionTasks",
|
||||
opts: compileopts.Options{
|
||||
Scheduler: "tasks",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SchedulerOptionCoroutines",
|
||||
opts: compileopts.Options{
|
||||
Scheduler: "coroutines",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "InvalidPrintSizeOption",
|
||||
opts: compileopts.Options{
|
||||
PrintSizes: "incorrect",
|
||||
},
|
||||
expectedError: expectedPrintSizeError,
|
||||
},
|
||||
{
|
||||
name: "PrintSizeOptionNone",
|
||||
opts: compileopts.Options{
|
||||
PrintSizes: "none",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "PrintSizeOptionShort",
|
||||
opts: compileopts.Options{
|
||||
PrintSizes: "short",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "PrintSizeOptionFull",
|
||||
opts: compileopts.Options{
|
||||
PrintSizes: "full",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "InvalidPanicOption",
|
||||
opts: compileopts.Options{
|
||||
PanicStrategy: "incorrect",
|
||||
},
|
||||
expectedError: expectedPanicStrategyError,
|
||||
},
|
||||
{
|
||||
name: "PanicOptionPrint",
|
||||
opts: compileopts.Options{
|
||||
PanicStrategy: "print",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "PanicOptionTrap",
|
||||
opts: compileopts.Options{
|
||||
PanicStrategy: "trap",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := tc.opts.Verify()
|
||||
if tc.expectedError != err {
|
||||
if tc.expectedError.Error() != err.Error() {
|
||||
t.Errorf("expected %v, got %v", tc.expectedError, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ type TargetSpec struct {
|
||||
OpenOCDTarget string `json:"openocd-target"`
|
||||
OpenOCDTransport string `json:"openocd-transport"`
|
||||
JLinkDevice string `json:"jlink-device"`
|
||||
CodeModel string `json:"code-model"`
|
||||
}
|
||||
|
||||
// copyProperties copies all properties that are set in spec2 into itself.
|
||||
@@ -130,6 +131,9 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
|
||||
if spec2.JLinkDevice != "" {
|
||||
spec.JLinkDevice = spec2.JLinkDevice
|
||||
}
|
||||
if spec2.CodeModel != "" {
|
||||
spec.CodeModel = spec2.CodeModel
|
||||
}
|
||||
}
|
||||
|
||||
// load reads a target specification from the JSON in the given io.Reader. It
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// createAtomicOp lowers an atomic library call by lowering it as an LLVM atomic
|
||||
// operation. It returns the result of the operation and true if the call could
|
||||
// be lowered inline, and false otherwise.
|
||||
func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
||||
name := call.Value.(*ssa.Function).Name()
|
||||
switch name {
|
||||
case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
val := b.getValue(call.Args[1])
|
||||
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpAdd, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||
// Return the new value, not the original value returned by atomicrmw.
|
||||
return b.CreateAdd(oldVal, val, ""), true
|
||||
case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
val := b.getValue(call.Args[1])
|
||||
isPointer := val.Type().TypeKind() == llvm.PointerTypeKind
|
||||
if isPointer {
|
||||
// atomicrmw only supports integers, so cast to an integer.
|
||||
val = b.CreatePtrToInt(val, b.uintptrType, "")
|
||||
ptr = b.CreateBitCast(ptr, llvm.PointerType(val.Type(), 0), "")
|
||||
}
|
||||
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpXchg, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||
if isPointer {
|
||||
oldVal = b.CreateIntToPtr(oldVal, b.i8ptrType, "")
|
||||
}
|
||||
return oldVal, true
|
||||
case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
old := b.getValue(call.Args[1])
|
||||
newVal := b.getValue(call.Args[2])
|
||||
tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||
swapped := b.CreateExtractValue(tuple, 1, "")
|
||||
return swapped, true
|
||||
case "LoadInt32", "LoadInt64", "LoadUint32", "LoadUint64", "LoadUintptr", "LoadPointer":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
val := b.CreateLoad(ptr, "")
|
||||
val.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
|
||||
val.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
|
||||
return val, true
|
||||
case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
val := b.getValue(call.Args[1])
|
||||
store := b.CreateStore(val, ptr)
|
||||
store.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
|
||||
store.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
|
||||
return store, true
|
||||
default:
|
||||
return llvm.Value{}, false
|
||||
}
|
||||
}
|
||||
+61
-22
@@ -2,6 +2,7 @@ package compiler
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"strconv"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
@@ -13,6 +14,14 @@ import (
|
||||
// a struct contains more fields, it is passed as a struct without expanding.
|
||||
const maxFieldsPerParam = 3
|
||||
|
||||
// paramInfo contains some information collected about a function parameter,
|
||||
// useful while declaring or defining a function.
|
||||
type paramInfo struct {
|
||||
llvmType llvm.Type
|
||||
name string // name, possibly with suffixes for e.g. struct fields
|
||||
flags paramFlags
|
||||
}
|
||||
|
||||
// paramFlags identifies parameter attributes for flags. Most importantly, it
|
||||
// determines which parameters are dereferenceable_or_null and which aren't.
|
||||
type paramFlags uint8
|
||||
@@ -48,19 +57,23 @@ func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm
|
||||
|
||||
// Expand an argument type to a list that can be used in a function call
|
||||
// parameter list.
|
||||
func expandFormalParamType(t llvm.Type, goType types.Type) ([]llvm.Type, []paramFlags) {
|
||||
func expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
||||
switch t.TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
fields, fieldFlags := flattenAggregateType(t, goType)
|
||||
if len(fields) <= maxFieldsPerParam {
|
||||
return fields, fieldFlags
|
||||
fieldInfos := flattenAggregateType(t, name, goType)
|
||||
if len(fieldInfos) <= maxFieldsPerParam {
|
||||
return fieldInfos
|
||||
} else {
|
||||
// failed to lower
|
||||
return []llvm.Type{t}, []paramFlags{getTypeFlags(goType)}
|
||||
}
|
||||
default:
|
||||
// TODO: split small arrays
|
||||
return []llvm.Type{t}, []paramFlags{getTypeFlags(goType)}
|
||||
}
|
||||
// TODO: split small arrays
|
||||
return []paramInfo{
|
||||
{
|
||||
llvmType: t,
|
||||
name: name,
|
||||
flags: getTypeFlags(goType),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,10 +104,10 @@ func (b *builder) expandFormalParamOffsets(t llvm.Type) []uint64 {
|
||||
func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
|
||||
switch v.Type().TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
fieldTypes, _ := flattenAggregateType(v.Type(), nil)
|
||||
if len(fieldTypes) <= maxFieldsPerParam {
|
||||
fieldInfos := flattenAggregateType(v.Type(), "", nil)
|
||||
if len(fieldInfos) <= maxFieldsPerParam {
|
||||
fields := b.flattenAggregate(v)
|
||||
if len(fields) != len(fieldTypes) {
|
||||
if len(fields) != len(fieldInfos) {
|
||||
panic("type and value param lowering don't match")
|
||||
}
|
||||
return fields
|
||||
@@ -110,23 +123,49 @@ func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
|
||||
|
||||
// Try to flatten a struct type to a list of types. Returns a 1-element slice
|
||||
// with the passed in type if this is not possible.
|
||||
func flattenAggregateType(t llvm.Type, goType types.Type) ([]llvm.Type, []paramFlags) {
|
||||
func flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
||||
typeFlags := getTypeFlags(goType)
|
||||
switch t.TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
fields := make([]llvm.Type, 0, t.StructElementTypesCount())
|
||||
fieldFlags := make([]paramFlags, 0, cap(fields))
|
||||
paramInfos := make([]paramInfo, 0, t.StructElementTypesCount())
|
||||
for i, subfield := range t.StructElementTypes() {
|
||||
subfields, subfieldFlags := flattenAggregateType(subfield, extractSubfield(goType, i))
|
||||
for i := range subfieldFlags {
|
||||
subfieldFlags[i] |= typeFlags
|
||||
suffix := strconv.Itoa(i)
|
||||
if goType != nil {
|
||||
// Try to come up with a good suffix for this struct field,
|
||||
// depending on which Go type it's based on.
|
||||
switch goType := goType.Underlying().(type) {
|
||||
case *types.Interface:
|
||||
suffix = []string{"typecode", "value"}[i]
|
||||
case *types.Slice:
|
||||
suffix = []string{"data", "len", "cap"}[i]
|
||||
case *types.Struct:
|
||||
suffix = goType.Field(i).Name()
|
||||
case *types.Basic:
|
||||
switch goType.Kind() {
|
||||
case types.Complex64, types.Complex128:
|
||||
suffix = []string{"r", "i"}[i]
|
||||
case types.String:
|
||||
suffix = []string{"data", "len"}[i]
|
||||
}
|
||||
case *types.Signature:
|
||||
suffix = []string{"context", "funcptr"}[i]
|
||||
}
|
||||
}
|
||||
fields = append(fields, subfields...)
|
||||
fieldFlags = append(fieldFlags, subfieldFlags...)
|
||||
subInfos := flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
||||
for i := range subInfos {
|
||||
subInfos[i].flags |= typeFlags
|
||||
}
|
||||
paramInfos = append(paramInfos, subInfos...)
|
||||
}
|
||||
return fields, fieldFlags
|
||||
return paramInfos
|
||||
default:
|
||||
return []llvm.Type{t}, []paramFlags{typeFlags}
|
||||
return []paramInfo{
|
||||
{
|
||||
llvmType: t,
|
||||
name: name,
|
||||
flags: typeFlags,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,7 +265,7 @@ func (b *builder) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Val
|
||||
func (b *builder) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) {
|
||||
switch t.TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
flattened, _ := flattenAggregateType(t, nil)
|
||||
flattened := flattenAggregateType(t, "", nil)
|
||||
if len(flattened) <= maxFieldsPerParam {
|
||||
value := llvm.ConstNull(t)
|
||||
for i, subtyp := range t.StructElementTypes() {
|
||||
|
||||
+17
-7
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func (b *builder) createMakeChan(expr *ssa.MakeChan) llvm.Value {
|
||||
elementSize := b.targetData.TypeAllocSize(b.getLLVMType(expr.Type().(*types.Chan).Elem()))
|
||||
elementSize := b.targetData.TypeAllocSize(b.getLLVMType(expr.Type().Underlying().(*types.Chan).Elem()))
|
||||
elementSizeValue := llvm.ConstInt(b.uintptrType, elementSize, false)
|
||||
bufSize := b.getValue(expr.Size)
|
||||
b.createChanBoundsCheck(elementSize, bufSize, expr.Size.Type().Underlying().(*types.Basic), expr.Pos())
|
||||
@@ -35,27 +35,37 @@ func (b *builder) createChanSend(instr *ssa.Send) {
|
||||
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
|
||||
b.CreateStore(chanValue, valueAlloca)
|
||||
|
||||
// Do the send.
|
||||
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast}, "")
|
||||
// Allocate blockedlist buffer.
|
||||
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
||||
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
||||
|
||||
// End the lifetime of the alloca.
|
||||
// Do the send.
|
||||
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
|
||||
|
||||
// End the lifetime of the allocas.
|
||||
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
||||
// https://bugs.llvm.org/show_bug.cgi?id=41742
|
||||
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||
}
|
||||
|
||||
// createChanRecv emits a pseudo chan receive operation. It is lowered to the
|
||||
// actual channel receive operation during goroutine lowering.
|
||||
func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
||||
valueType := b.getLLVMType(unop.X.Type().(*types.Chan).Elem())
|
||||
valueType := b.getLLVMType(unop.X.Type().Underlying().(*types.Chan).Elem())
|
||||
ch := b.getValue(unop.X)
|
||||
|
||||
// Allocate memory to receive into.
|
||||
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
|
||||
|
||||
// Allocate blockedlist buffer.
|
||||
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
||||
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
||||
|
||||
// Do the receive.
|
||||
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast}, "")
|
||||
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
|
||||
received := b.CreateLoad(valueAlloca, "chan.received")
|
||||
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||
|
||||
if unop.CommaOk {
|
||||
@@ -117,7 +127,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
switch state.Dir {
|
||||
case types.RecvOnly:
|
||||
// Make sure the receive buffer is big enough and has the correct alignment.
|
||||
llvmType := b.getLLVMType(state.Chan.Type().(*types.Chan).Elem())
|
||||
llvmType := b.getLLVMType(state.Chan.Type().Underlying().(*types.Chan).Elem())
|
||||
if size := b.targetData.TypeAllocSize(llvmType); size > recvbufSize {
|
||||
recvbufSize = size
|
||||
}
|
||||
|
||||
+73
-101
@@ -91,7 +91,25 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
|
||||
return llvm.TargetMachine{}, err
|
||||
}
|
||||
features := strings.Join(config.Features(), ",")
|
||||
machine := target.CreateTargetMachine(config.Triple(), config.CPU(), features, llvm.CodeGenLevelDefault, llvm.RelocStatic, llvm.CodeModelDefault)
|
||||
|
||||
var codeModel llvm.CodeModel
|
||||
|
||||
switch config.CodeModel() {
|
||||
case "default":
|
||||
codeModel = llvm.CodeModelDefault
|
||||
case "tiny":
|
||||
codeModel = llvm.CodeModelTiny
|
||||
case "small":
|
||||
codeModel = llvm.CodeModelSmall
|
||||
case "kernel":
|
||||
codeModel = llvm.CodeModelKernel
|
||||
case "medium":
|
||||
codeModel = llvm.CodeModelMedium
|
||||
case "large":
|
||||
codeModel = llvm.CodeModelLarge
|
||||
}
|
||||
|
||||
machine := target.CreateTargetMachine(config.Triple(), config.CPU(), features, llvm.CodeGenLevelDefault, llvm.RelocStatic, codeModel)
|
||||
return machine, nil
|
||||
}
|
||||
|
||||
@@ -103,7 +121,7 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
|
||||
// violation. Eventually, this Compile function should only compile a single
|
||||
// package and not the whole program, and loading of the program (including CGo
|
||||
// processing) should be moved outside the compiler package.
|
||||
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (llvm.Module, []string, []error) {
|
||||
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (mod llvm.Module, extrafiles []string, extraldflags []string, errors []error) {
|
||||
c := &compilerContext{
|
||||
Config: config,
|
||||
difiles: make(map[string]llvm.Metadata),
|
||||
@@ -137,64 +155,26 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
|
||||
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
|
||||
dummyFunc.EraseFromParentAsFunction()
|
||||
|
||||
// Prefix the GOPATH with the system GOROOT, as GOROOT is already set to
|
||||
// the TinyGo root.
|
||||
overlayGopath := goenv.Get("GOPATH")
|
||||
if overlayGopath == "" {
|
||||
overlayGopath = goenv.Get("GOROOT")
|
||||
} else {
|
||||
overlayGopath = goenv.Get("GOROOT") + string(filepath.ListSeparator) + overlayGopath
|
||||
}
|
||||
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return c.mod, nil, []error{err}
|
||||
return c.mod, nil, nil, []error{err}
|
||||
}
|
||||
goroot, err := loader.GetCachedGoroot(c.Config)
|
||||
if err != nil {
|
||||
return c.mod, nil, nil, []error{err}
|
||||
}
|
||||
lprogram := &loader.Program{
|
||||
Build: &build.Context{
|
||||
GOARCH: c.GOARCH(),
|
||||
GOOS: c.GOOS(),
|
||||
GOROOT: goenv.Get("GOROOT"),
|
||||
GOROOT: goroot,
|
||||
GOPATH: goenv.Get("GOPATH"),
|
||||
CgoEnabled: c.CgoEnabled(),
|
||||
UseAllFiles: false,
|
||||
Compiler: "gc", // must be one of the recognized compilers
|
||||
BuildTags: c.BuildTags(),
|
||||
},
|
||||
OverlayBuild: &build.Context{
|
||||
GOARCH: c.GOARCH(),
|
||||
GOOS: c.GOOS(),
|
||||
GOROOT: goenv.Get("TINYGOROOT"),
|
||||
GOPATH: overlayGopath,
|
||||
CgoEnabled: c.CgoEnabled(),
|
||||
UseAllFiles: false,
|
||||
Compiler: "gc", // must be one of the recognized compilers
|
||||
BuildTags: c.BuildTags(),
|
||||
},
|
||||
OverlayPath: func(path string) string {
|
||||
// Return the (overlay) import path when it should be overlaid, and
|
||||
// "" if it should not.
|
||||
if strings.HasPrefix(path, tinygoPath+"/src/") {
|
||||
// Avoid issues with packages that are imported twice, one from
|
||||
// GOPATH and one from TINYGOPATH.
|
||||
path = path[len(tinygoPath+"/src/"):]
|
||||
}
|
||||
switch path {
|
||||
case "machine", "os", "reflect", "runtime", "runtime/interrupt", "runtime/volatile", "sync", "testing", "internal/reflectlite", "internal/task":
|
||||
return path
|
||||
default:
|
||||
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
||||
return path
|
||||
} else if path == "syscall" {
|
||||
for _, tag := range c.BuildTags() {
|
||||
if tag == "baremetal" || tag == "darwin" {
|
||||
return path
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
},
|
||||
Tests: c.TestConfig.CompileTestBinary,
|
||||
TypeChecker: types.Config{
|
||||
Sizes: &stdSizes{
|
||||
IntSize: int64(c.targetData.TypeAllocSize(c.intType)),
|
||||
@@ -208,38 +188,22 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
|
||||
ClangHeaders: c.ClangHeaders,
|
||||
}
|
||||
|
||||
if strings.HasSuffix(pkgName, ".go") {
|
||||
_, err = lprogram.ImportFile(pkgName)
|
||||
if err != nil {
|
||||
return c.mod, nil, []error{err}
|
||||
}
|
||||
} else {
|
||||
_, err = lprogram.Import(pkgName, wd, token.Position{
|
||||
Filename: "build command-line-arguments",
|
||||
})
|
||||
if err != nil {
|
||||
return c.mod, nil, []error{err}
|
||||
}
|
||||
}
|
||||
|
||||
_, err = lprogram.Import("runtime", "", token.Position{
|
||||
Filename: "build default import",
|
||||
})
|
||||
err = lprogram.Load(pkgName)
|
||||
if err != nil {
|
||||
return c.mod, nil, []error{err}
|
||||
return c.mod, nil, nil, []error{err}
|
||||
}
|
||||
|
||||
err = lprogram.Parse(c.TestConfig.CompileTestBinary)
|
||||
err = lprogram.Parse()
|
||||
if err != nil {
|
||||
return c.mod, nil, []error{err}
|
||||
return c.mod, nil, nil, []error{err}
|
||||
}
|
||||
|
||||
c.ir = ir.NewProgram(lprogram, pkgName)
|
||||
c.ir = ir.NewProgram(lprogram)
|
||||
|
||||
// Run a simple dead code elimination pass.
|
||||
err = c.ir.SimpleDCE()
|
||||
if err != nil {
|
||||
return c.mod, nil, []error{err}
|
||||
return c.mod, nil, nil, []error{err}
|
||||
}
|
||||
|
||||
// Initialize debug information.
|
||||
@@ -378,12 +342,15 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
|
||||
// Gather the list of (C) file paths that should be included in the build.
|
||||
var extraFiles []string
|
||||
for _, pkg := range c.ir.LoaderProgram.Sorted() {
|
||||
for _, file := range pkg.CFiles {
|
||||
extraFiles = append(extraFiles, filepath.Join(pkg.Package.Dir, file))
|
||||
for _, file := range pkg.OtherFiles {
|
||||
switch strings.ToLower(filepath.Ext(file)) {
|
||||
case ".c":
|
||||
extraFiles = append(extraFiles, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c.mod, extraFiles, c.diagnostics
|
||||
return c.mod, extraFiles, lprogram.LDFlags, c.diagnostics
|
||||
}
|
||||
|
||||
// getLLVMRuntimeType obtains a named type from the runtime package and returns
|
||||
@@ -738,21 +705,23 @@ func (c *compilerContext) createFunctionDeclaration(f *ir.Function) {
|
||||
retType = c.ctx.StructType(results, false)
|
||||
}
|
||||
|
||||
var paramTypes []llvm.Type
|
||||
var paramTypeVariants []paramFlags
|
||||
var paramInfos []paramInfo
|
||||
for _, param := range f.Params {
|
||||
paramType := c.getLLVMType(param.Type())
|
||||
paramTypeFragments, paramTypeFragmentVariants := expandFormalParamType(paramType, param.Type())
|
||||
paramTypes = append(paramTypes, paramTypeFragments...)
|
||||
paramTypeVariants = append(paramTypeVariants, paramTypeFragmentVariants...)
|
||||
paramFragmentInfos := expandFormalParamType(paramType, param.Name(), param.Type())
|
||||
paramInfos = append(paramInfos, paramFragmentInfos...)
|
||||
}
|
||||
|
||||
// Add an extra parameter as the function context. This context is used in
|
||||
// closures and bound methods, but should be optimized away when not used.
|
||||
if !f.IsExported() {
|
||||
paramTypes = append(paramTypes, c.i8ptrType) // context
|
||||
paramTypes = append(paramTypes, c.i8ptrType) // parent coroutine
|
||||
paramTypeVariants = append(paramTypeVariants, 0, 0)
|
||||
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "context", flags: 0})
|
||||
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "parentHandle", flags: 0})
|
||||
}
|
||||
|
||||
var paramTypes []llvm.Type
|
||||
for _, info := range paramInfos {
|
||||
paramTypes = append(paramTypes, info.llvmType)
|
||||
}
|
||||
|
||||
fnType := llvm.FunctionType(retType, paramTypes, false)
|
||||
@@ -764,12 +733,12 @@ func (c *compilerContext) createFunctionDeclaration(f *ir.Function) {
|
||||
}
|
||||
|
||||
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
|
||||
for i, typ := range paramTypes {
|
||||
if paramTypeVariants[i]¶mIsDeferenceableOrNull == 0 {
|
||||
for i, info := range paramInfos {
|
||||
if info.flags¶mIsDeferenceableOrNull == 0 {
|
||||
continue
|
||||
}
|
||||
if typ.TypeKind() == llvm.PointerTypeKind {
|
||||
el := typ.ElementType()
|
||||
if info.llvmType.TypeKind() == llvm.PointerTypeKind {
|
||||
el := info.llvmType.ElementType()
|
||||
size := c.targetData.TypeAllocSize(el)
|
||||
if size == 0 {
|
||||
// dereferenceable_or_null(0) appears to be illegal in LLVM.
|
||||
@@ -911,9 +880,10 @@ func (b *builder) createFunctionDefinition() {
|
||||
for _, param := range b.fn.Params {
|
||||
llvmType := b.getLLVMType(param.Type())
|
||||
fields := make([]llvm.Value, 0, 1)
|
||||
fieldFragments, _ := expandFormalParamType(llvmType, nil)
|
||||
for range fieldFragments {
|
||||
fields = append(fields, b.fn.LLVMFn.Param(llvmParamIndex))
|
||||
for _, info := range expandFormalParamType(llvmType, param.Name(), param.Type()) {
|
||||
param := b.fn.LLVMFn.Param(llvmParamIndex)
|
||||
param.SetName(info.name)
|
||||
fields = append(fields, param)
|
||||
llvmParamIndex++
|
||||
}
|
||||
b.locals[param] = b.collapseFormalParam(llvmType, fields)
|
||||
@@ -1101,7 +1071,7 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
||||
// goroutine:
|
||||
// * The function context, for closures.
|
||||
// * The function pointer (for tasks).
|
||||
funcPtr, context := b.decodeFuncValue(b.getValue(instr.Call.Value), instr.Call.Value.Type().(*types.Signature))
|
||||
funcPtr, context := b.decodeFuncValue(b.getValue(instr.Call.Value), instr.Call.Value.Type().Underlying().(*types.Signature))
|
||||
params = append(params, context) // context parameter
|
||||
switch b.Scheduler() {
|
||||
case "none", "coroutines":
|
||||
@@ -1198,9 +1168,7 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
|
||||
var llvmCap llvm.Value
|
||||
switch args[0].Type().(type) {
|
||||
case *types.Chan:
|
||||
// Channel. Buffered channels haven't been implemented yet so always
|
||||
// return 0.
|
||||
llvmCap = llvm.ConstInt(b.intType, 0, false)
|
||||
llvmCap = b.createRuntimeCall("chanCap", []llvm.Value{value}, "cap")
|
||||
case *types.Slice:
|
||||
llvmCap = b.CreateExtractValue(value, 2, "cap")
|
||||
default:
|
||||
@@ -1256,9 +1224,7 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
|
||||
// string or slice
|
||||
llvmLen = b.CreateExtractValue(value, 1, "len")
|
||||
case *types.Chan:
|
||||
// Channel. Buffered channels haven't been implemented yet so always
|
||||
// return 0.
|
||||
llvmLen = llvm.ConstInt(b.intType, 0, false)
|
||||
llvmLen = b.createRuntimeCall("chanLen", []llvm.Value{value}, "len")
|
||||
case *types.Map:
|
||||
llvmLen = b.createRuntimeCall("hashmapLen", []llvm.Value{value}, "len")
|
||||
default:
|
||||
@@ -1361,11 +1327,9 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
return b.createMemoryCopyCall(fn, instr.Args)
|
||||
case name == "runtime.memzero":
|
||||
return b.createMemoryZeroCall(instr.Args)
|
||||
case name == "device/arm.ReadRegister" || name == "device/riscv.ReadRegister":
|
||||
return b.createReadRegister(name, instr.Args)
|
||||
case name == "device/arm.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
|
||||
case name == "device.Asm" || name == "device/arm.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
|
||||
return b.createInlineAsm(instr.Args)
|
||||
case name == "device/arm.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
|
||||
case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
|
||||
return b.createInlineAsmFull(instr)
|
||||
case strings.HasPrefix(name, "device/arm.SVCall"):
|
||||
return b.emitSVCall(instr.Args)
|
||||
@@ -1377,6 +1341,14 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
return b.createVolatileLoad(instr)
|
||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||
return b.createVolatileStore(instr)
|
||||
case strings.HasPrefix(name, "sync/atomic."):
|
||||
val, ok := b.createAtomicOp(instr)
|
||||
if ok {
|
||||
// This call could be lowered as an atomic operation.
|
||||
return val, nil
|
||||
}
|
||||
// This call couldn't be lowered as an atomic operation, it's
|
||||
// probably something else. Continue as usual.
|
||||
case name == "runtime/interrupt.New":
|
||||
return b.createInterruptGlobal(instr)
|
||||
}
|
||||
@@ -1570,7 +1542,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
index := b.getValue(expr.Index)
|
||||
|
||||
// Check bounds.
|
||||
arrayLen := expr.X.Type().(*types.Array).Len()
|
||||
arrayLen := expr.X.Type().Underlying().(*types.Array).Len()
|
||||
arrayLenLLVM := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false)
|
||||
b.createLookupBoundsCheck(arrayLenLLVM, index, expr.Index.Type())
|
||||
|
||||
@@ -1682,8 +1654,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
}
|
||||
|
||||
// Bounds checking.
|
||||
lenType := expr.Len.Type().(*types.Basic)
|
||||
capType := expr.Cap.Type().(*types.Basic)
|
||||
lenType := expr.Len.Type().Underlying().(*types.Basic)
|
||||
capType := expr.Cap.Type().Underlying().(*types.Basic)
|
||||
b.createSliceBoundsCheck(maxSize, sliceLen, sliceCap, sliceCap, lenType, capType, capType)
|
||||
|
||||
// Allocate the backing array.
|
||||
|
||||
+6
-4
@@ -125,13 +125,15 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
|
||||
// The receiver is not an interface, but a i8* type.
|
||||
recv = c.i8ptrType
|
||||
}
|
||||
recvFragments, _ := expandFormalParamType(recv, nil)
|
||||
paramTypes = append(paramTypes, recvFragments...)
|
||||
for _, info := range expandFormalParamType(recv, "", nil) {
|
||||
paramTypes = append(paramTypes, info.llvmType)
|
||||
}
|
||||
}
|
||||
for i := 0; i < typ.Params().Len(); i++ {
|
||||
subType := c.getLLVMType(typ.Params().At(i).Type())
|
||||
paramTypeFragments, _ := expandFormalParamType(subType, nil)
|
||||
paramTypes = append(paramTypes, paramTypeFragments...)
|
||||
for _, info := range expandFormalParamType(subType, "", nil) {
|
||||
paramTypes = append(paramTypes, info.llvmType)
|
||||
}
|
||||
}
|
||||
// All functions take these parameters at the end.
|
||||
paramTypes = append(paramTypes, c.i8ptrType) // context
|
||||
|
||||
+41
-41
@@ -13,27 +13,6 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// This is a compiler builtin, which reads the given register by name:
|
||||
//
|
||||
// func ReadRegister(name string) uintptr
|
||||
//
|
||||
// The register name must be a constant, for example "sp".
|
||||
func (b *builder) createReadRegister(name string, args []ssa.Value) (llvm.Value, error) {
|
||||
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{}, false)
|
||||
regname := constant.StringVal(args[0].(*ssa.Const).Value)
|
||||
var asm string
|
||||
switch name {
|
||||
case "device/arm.ReadRegister":
|
||||
asm = "mov $0, " + regname
|
||||
case "device/riscv.ReadRegister":
|
||||
asm = "mv $0, " + regname
|
||||
default:
|
||||
panic("unknown architecture")
|
||||
}
|
||||
target := llvm.InlineAsm(fnType, asm, "=r", false, false, 0)
|
||||
return b.CreateCall(target, nil, ""), nil
|
||||
}
|
||||
|
||||
// This is a compiler builtin, which emits a piece of inline assembly with no
|
||||
// operands or return values. It is useful for trivial instructions, like wfi in
|
||||
// ARM or sleep in AVR.
|
||||
@@ -52,7 +31,7 @@ func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
|
||||
// This is a compiler builtin, which allows assembly to be called in a flexible
|
||||
// way.
|
||||
//
|
||||
// func AsmFull(asm string, regs map[string]interface{})
|
||||
// func AsmFull(asm string, regs map[string]interface{}) uintptr
|
||||
//
|
||||
// The asm parameter must be a constant string. The regs parameter must be
|
||||
// provided immediately. For example:
|
||||
@@ -66,24 +45,24 @@ func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
|
||||
func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) {
|
||||
asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
|
||||
registers := map[string]llvm.Value{}
|
||||
registerMap := instr.Args[1].(*ssa.MakeMap)
|
||||
for _, r := range *registerMap.Referrers() {
|
||||
switch r := r.(type) {
|
||||
case *ssa.DebugRef:
|
||||
// ignore
|
||||
case *ssa.MapUpdate:
|
||||
if r.Block() != registerMap.Block() {
|
||||
return llvm.Value{}, b.makeError(instr.Pos(), "register value map must be created in the same basic block")
|
||||
if registerMap, ok := instr.Args[1].(*ssa.MakeMap); ok {
|
||||
for _, r := range *registerMap.Referrers() {
|
||||
switch r := r.(type) {
|
||||
case *ssa.DebugRef:
|
||||
// ignore
|
||||
case *ssa.MapUpdate:
|
||||
if r.Block() != registerMap.Block() {
|
||||
return llvm.Value{}, b.makeError(instr.Pos(), "register value map must be created in the same basic block")
|
||||
}
|
||||
key := constant.StringVal(r.Key.(*ssa.Const).Value)
|
||||
registers[key] = b.getValue(r.Value.(*ssa.MakeInterface).X)
|
||||
case *ssa.Call:
|
||||
if r.Common() == instr {
|
||||
break
|
||||
}
|
||||
default:
|
||||
return llvm.Value{}, b.makeError(instr.Pos(), "don't know how to handle argument to inline assembly: "+r.String())
|
||||
}
|
||||
key := constant.StringVal(r.Key.(*ssa.Const).Value)
|
||||
//println("value:", r.Value.(*ssa.MakeInterface).X.String())
|
||||
registers[key] = b.getValue(r.Value.(*ssa.MakeInterface).X)
|
||||
case *ssa.Call:
|
||||
if r.Common() == instr {
|
||||
break
|
||||
}
|
||||
default:
|
||||
return llvm.Value{}, b.makeError(instr.Pos(), "don't know how to handle argument to inline assembly: "+r.String())
|
||||
}
|
||||
}
|
||||
// TODO: handle dollar signs in asm string
|
||||
@@ -92,6 +71,15 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
argTypes := []llvm.Type{}
|
||||
args := []llvm.Value{}
|
||||
constraints := []string{}
|
||||
hasOutput := false
|
||||
asmString = regexp.MustCompile("\\{\\}").ReplaceAllStringFunc(asmString, func(s string) string {
|
||||
hasOutput = true
|
||||
return "$0"
|
||||
})
|
||||
if hasOutput {
|
||||
constraints = append(constraints, "=&r")
|
||||
registerNumbers[""] = 0
|
||||
}
|
||||
asmString = regexp.MustCompile("\\{[a-zA-Z]+\\}").ReplaceAllStringFunc(asmString, func(s string) string {
|
||||
// TODO: skip strings like {r4} etc. that look like ARM push/pop
|
||||
// instructions.
|
||||
@@ -121,9 +109,21 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
fnType := llvm.FunctionType(b.ctx.VoidType(), argTypes, false)
|
||||
var outputType llvm.Type
|
||||
if hasOutput {
|
||||
outputType = b.uintptrType
|
||||
} else {
|
||||
outputType = b.ctx.VoidType()
|
||||
}
|
||||
fnType := llvm.FunctionType(outputType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, asmString, strings.Join(constraints, ","), true, false, 0)
|
||||
return b.CreateCall(target, args, ""), nil
|
||||
result := b.CreateCall(target, args, "")
|
||||
if hasOutput {
|
||||
return result, nil
|
||||
} else {
|
||||
// Make sure we return something valid.
|
||||
return llvm.ConstInt(b.uintptrType, 0, false), nil
|
||||
}
|
||||
}
|
||||
|
||||
// This is a compiler builtin which emits an inline SVCall instruction. It can
|
||||
|
||||
@@ -446,7 +446,10 @@ func (c *compilerContext) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
|
||||
|
||||
// Get the expanded receiver type.
|
||||
receiverType := c.getLLVMType(f.Params[0].Type())
|
||||
expandedReceiverType, _ := expandFormalParamType(receiverType, nil)
|
||||
var expandedReceiverType []llvm.Type
|
||||
for _, info := range expandFormalParamType(receiverType, "", nil) {
|
||||
expandedReceiverType = append(expandedReceiverType, info.llvmType)
|
||||
}
|
||||
|
||||
// Does this method even need any wrapping?
|
||||
if len(expandedReceiverType) == 1 && receiverType.TypeKind() == llvm.PointerTypeKind {
|
||||
|
||||
@@ -4,10 +4,11 @@ go 1.11
|
||||
|
||||
require (
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
||||
github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac
|
||||
github.com/chromedp/chromedp v0.5.3
|
||||
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
|
||||
github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46
|
||||
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
|
||||
go.bug.st/serial v1.0.0
|
||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
|
||||
google.golang.org/appengine v1.4.0 // indirect
|
||||
tinygo.org/x/go-llvm v0.0.0-20200401165421-8d120882fc7a
|
||||
tinygo.org/x/go-llvm v0.0.0-20200503224449-70c558526021
|
||||
)
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
||||
github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac h1:T7V5BXqnYd55Hj/g5uhDYumg9Fp3rMTS6bykYtTIFX4=
|
||||
github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g=
|
||||
github.com/chromedp/chromedp v0.5.3 h1:F9LafxmYpsQhWQBdCs+6Sret1zzeeFyHS5LkRF//Ffg=
|
||||
github.com/chromedp/chromedp v0.5.3/go.mod h1:YLdPtndaHQ4rCpSpBG+IPpy9JvX0VD+7aaLxYgYj28w=
|
||||
github.com/creack/goselect v0.1.1 h1:tiSSgKE1eJtxs1h/VgGQWuXUP0YS4CDIFMp6vaI1ls0=
|
||||
github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
|
||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg=
|
||||
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
|
||||
github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46 h1:wXG2bA8fO7Vv7lLk2PihFMTqmbT173Tje39oKzQ50Mo=
|
||||
github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46/go.mod h1:Pb6XcsXyropB9LNHhnqaknG/vEwYztLkQzVCHv8sQ3M=
|
||||
github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08 h1:V0an7KRw92wmJysvFvtqtKMAPmvS5O0jtB0nYo6t+gs=
|
||||
github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08/go.mod h1:dFWs1zEqDjFtnBXsd1vPOZaLsESovai349994nHx3e0=
|
||||
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892 h1:6J+qramlHVLmiBOgRiBOnQkno8uprqG6YFFQTt6uYIw=
|
||||
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892/go.mod h1:Pb6XcsXyropB9LNHhnqaknG/vEwYztLkQzVCHv8sQ3M=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
@@ -17,43 +30,21 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef h1:ymc9FeDom3RIEA3coKokSllBB1hRcMT0tZ1W3Jf9Ids=
|
||||
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2 h1:0sfSpGSa544Fwnbot3Oxq/U6SXqjty6Jy/3wRhVS7ig=
|
||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
tinygo.org/x/go-llvm v0.0.0-20190224120431-7707ae5d1261 h1:rJS2Hga39YAnm7DE4qrPm6Dr/67EOojL0XPzvbEeBiw=
|
||||
tinygo.org/x/go-llvm v0.0.0-20190224120431-7707ae5d1261/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20190818154551-95bc4ffe1add h1:dFjMH1sLhYADg8UQm7DB56B7e+TfvAmWmEZLhyv3r/w=
|
||||
tinygo.org/x/go-llvm v0.0.0-20190818154551-95bc4ffe1add/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191103182207-90b6e4bdc0b9 h1:d6rAX39a3C0pKrY5HcojEGyN8w9ocU0v7X28lC/TRKU=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191103182207-90b6e4bdc0b9/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191103200204-37e93e3f04e2 h1:Q5Hv3e5cLMGkiYwYgZL1Zrv6nb/EY+DJpRWrdO6ws6o=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191103200204-37e93e3f04e2/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191113125529-bad6d01809e8 h1:9Bfvso+tTVQg16UzOA614NaYA4x8vsRBNtd3eBrXwp0=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191113125529-bad6d01809e8/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191124211856-b2db3df3f257 h1:o8VDylrMN7gWemBMu8rEyuogKPhcLTdx5KrUAp9macc=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191124211856-b2db3df3f257/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191215173731-ad71f3d24aae h1:s8J5EyxCkHxXB08UI3gk9W9IS/ekizRvSX+PfZxnAB0=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191215173731-ad71f3d24aae/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20200104190746-1ff21df33566 h1:a4y30bTf7U0zDA75v2PTL+XQ2OzJetj19gK8XwQpUNY=
|
||||
tinygo.org/x/go-llvm v0.0.0-20200104190746-1ff21df33566/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20200226165415-53522ab6713d h1:mtgZh/e8a3wxneQFuLXoQYO//1mvlki02yZ1JCwMKp4=
|
||||
tinygo.org/x/go-llvm v0.0.0-20200226165415-53522ab6713d/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20200401165421-8d120882fc7a h1:Ugje2Lxuv8CFncHzs5W+hWfJvPsM+W4K0zRvzFbLvoE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20200401165421-8d120882fc7a/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20200503224449-70c558526021 h1:d8T98WXGjrTgDmMXgxa6nb9EAYXGXwnzXygnJl6d+ac=
|
||||
tinygo.org/x/go-llvm v0.0.0-20200503224449-70c558526021/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package goenv
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Version of TinyGo.
|
||||
// Update this value before release of new version of software.
|
||||
const Version = "0.14.0-dev"
|
||||
|
||||
// GetGorootVersion returns the major and minor version for a given GOROOT path.
|
||||
// If the goroot cannot be determined, (0, 0) is returned.
|
||||
func GetGorootVersion(goroot string) (major, minor int, err error) {
|
||||
s, err := GorootVersionString(goroot)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
if s == "" || s[:2] != "go" {
|
||||
return 0, 0, errors.New("could not parse Go version: version does not start with 'go' prefix")
|
||||
}
|
||||
|
||||
parts := strings.Split(s[2:], ".")
|
||||
if len(parts) < 2 {
|
||||
return 0, 0, errors.New("could not parse Go version: version has less than two parts")
|
||||
}
|
||||
|
||||
// Ignore the errors, we don't really handle errors here anyway.
|
||||
var trailing string
|
||||
n, err := fmt.Sscanf(s, "go%d.%d%s", &major, &minor, &trailing)
|
||||
if n == 2 && err == io.EOF {
|
||||
// Means there were no trailing characters (i.e., not an alpha/beta)
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("failed to parse version: %s", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GorootVersionString returns the version string as reported by the Go
|
||||
// toolchain for the given GOROOT path. It is usually of the form `go1.x.y` but
|
||||
// can have some variations (for beta releases, for example).
|
||||
func GorootVersionString(goroot string) (string, error) {
|
||||
if data, err := ioutil.ReadFile(filepath.Join(
|
||||
goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
|
||||
|
||||
r := regexp.MustCompile("const TheVersion = `(.*)`")
|
||||
matches := r.FindSubmatch(data)
|
||||
if len(matches) != 2 {
|
||||
return "", errors.New("Invalid go version output:\n" + string(data))
|
||||
}
|
||||
|
||||
return string(matches[1]), nil
|
||||
|
||||
} else if data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
|
||||
return string(data), nil
|
||||
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
@@ -63,73 +63,14 @@ const (
|
||||
)
|
||||
|
||||
// Create and initialize a new *Program from a *ssa.Program.
|
||||
func NewProgram(lprogram *loader.Program, mainPath string) *Program {
|
||||
func NewProgram(lprogram *loader.Program) *Program {
|
||||
program := lprogram.LoadSSA()
|
||||
program.Build()
|
||||
|
||||
// Find the main package, which is a bit difficult when running a .go file
|
||||
// directly.
|
||||
mainPkg := program.ImportedPackage(mainPath)
|
||||
if mainPkg == nil {
|
||||
for _, pkgInfo := range program.AllPackages() {
|
||||
if pkgInfo.Pkg.Name() == "main" {
|
||||
if mainPkg != nil {
|
||||
panic("more than one main package found")
|
||||
}
|
||||
mainPkg = pkgInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
mainPkg := program.ImportedPackage(lprogram.MainPkg.PkgPath)
|
||||
if mainPkg == nil {
|
||||
panic("could not find main package")
|
||||
}
|
||||
|
||||
// Make a list of packages in import order.
|
||||
packageList := []*ssa.Package{}
|
||||
packageSet := map[string]struct{}{}
|
||||
worklist := []string{"runtime", mainPath}
|
||||
for len(worklist) != 0 {
|
||||
pkgPath := worklist[0]
|
||||
var pkg *ssa.Package
|
||||
if pkgPath == mainPath {
|
||||
pkg = mainPkg // necessary for compiling individual .go files
|
||||
} else {
|
||||
pkg = program.ImportedPackage(pkgPath)
|
||||
}
|
||||
if pkg == nil {
|
||||
// Non-SSA package (e.g. cgo).
|
||||
packageSet[pkgPath] = struct{}{}
|
||||
worklist = worklist[1:]
|
||||
continue
|
||||
}
|
||||
if _, ok := packageSet[pkgPath]; ok {
|
||||
// Package already in the final package list.
|
||||
worklist = worklist[1:]
|
||||
continue
|
||||
}
|
||||
|
||||
unsatisfiedImports := make([]string, 0)
|
||||
imports := pkg.Pkg.Imports()
|
||||
for _, pkg := range imports {
|
||||
if _, ok := packageSet[pkg.Path()]; ok {
|
||||
continue
|
||||
}
|
||||
unsatisfiedImports = append(unsatisfiedImports, pkg.Path())
|
||||
}
|
||||
if len(unsatisfiedImports) == 0 {
|
||||
// All dependencies of this package are satisfied, so add this
|
||||
// package to the list.
|
||||
packageList = append(packageList, pkg)
|
||||
packageSet[pkgPath] = struct{}{}
|
||||
worklist = worklist[1:]
|
||||
} else {
|
||||
// Prepend all dependencies to the worklist and reconsider this
|
||||
// package (by not removing it from the worklist). At that point, it
|
||||
// must be possible to add it to packageList.
|
||||
worklist = append(unsatisfiedImports, worklist...)
|
||||
}
|
||||
}
|
||||
|
||||
p := &Program{
|
||||
Program: program,
|
||||
LoaderProgram: lprogram,
|
||||
@@ -137,8 +78,8 @@ func NewProgram(lprogram *loader.Program, mainPath string) *Program {
|
||||
functionMap: make(map[*ssa.Function]*Function),
|
||||
}
|
||||
|
||||
for _, pkg := range packageList {
|
||||
p.AddPackage(pkg)
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
p.AddPackage(program.ImportedPackage(pkg.PkgPath))
|
||||
}
|
||||
|
||||
return p
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
package loader
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Errors contains a list of parser errors or a list of typechecker errors for
|
||||
// the given package.
|
||||
type Errors struct {
|
||||
@@ -15,25 +10,3 @@ type Errors struct {
|
||||
func (e Errors) Error() string {
|
||||
return "could not compile: " + e.Errs[0].Error()
|
||||
}
|
||||
|
||||
// ImportCycleErrors is returned when encountering an import cycle. The list of
|
||||
// packages is a list from the root package to the leaf package that imports one
|
||||
// of the packages in the list.
|
||||
type ImportCycleError struct {
|
||||
Packages []string
|
||||
ImportPositions []token.Position
|
||||
}
|
||||
|
||||
func (e *ImportCycleError) Error() string {
|
||||
var msg strings.Builder
|
||||
msg.WriteString("import cycle:\n\t")
|
||||
msg.WriteString(strings.Join(e.Packages, "\n\t"))
|
||||
msg.WriteString("\n at ")
|
||||
for i, pos := range e.ImportPositions {
|
||||
if i > 0 {
|
||||
msg.WriteString(", ")
|
||||
}
|
||||
msg.WriteString(pos.String())
|
||||
}
|
||||
return msg.String()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
package loader
|
||||
|
||||
// This file constructs a new temporary GOROOT directory by merging both the
|
||||
// standard Go GOROOT and the GOROOT from TinyGo using symlinks.
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
// GetCachedGoroot creates a new GOROOT by merging both the standard GOROOT and
|
||||
// the GOROOT from TinyGo using lots of symbolic links.
|
||||
func GetCachedGoroot(config *compileopts.Config) (string, error) {
|
||||
goroot := goenv.Get("GOROOT")
|
||||
if goroot == "" {
|
||||
return "", errors.New("could not determine GOROOT")
|
||||
}
|
||||
tinygoroot := goenv.Get("TINYGOROOT")
|
||||
if tinygoroot == "" {
|
||||
return "", errors.New("could not determine TINYGOROOT")
|
||||
}
|
||||
|
||||
// Determine the location of the cached GOROOT.
|
||||
version, err := goenv.GorootVersionString(goroot)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// This hash is really a cache key, that contains (hopefully) enough
|
||||
// information to make collisions unlikely during development.
|
||||
// By including the Go version and TinyGo version, cache collisions should
|
||||
// not happen outside of development.
|
||||
hash := sha512.New512_256()
|
||||
fmt.Fprintln(hash, goroot)
|
||||
fmt.Fprintln(hash, version)
|
||||
fmt.Fprintln(hash, goenv.Version)
|
||||
fmt.Fprintln(hash, tinygoroot)
|
||||
gorootsHash := hash.Sum(nil)
|
||||
gorootsHashHex := hex.EncodeToString(gorootsHash[:])
|
||||
cachedgoroot := filepath.Join(goenv.Get("GOCACHE"), "goroot-"+version+"-"+gorootsHashHex)
|
||||
if needsSyscallPackage(config.BuildTags()) {
|
||||
cachedgoroot += "-syscall"
|
||||
}
|
||||
|
||||
if _, err := os.Stat(cachedgoroot); err == nil {
|
||||
return cachedgoroot, nil
|
||||
}
|
||||
tmpgoroot := cachedgoroot + ".tmp" + strconv.Itoa(rand.Int())
|
||||
err = os.MkdirAll(tmpgoroot, 0777)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Remove the temporary directory if it wasn't moved to the right place
|
||||
// (for example, when there was an error).
|
||||
defer os.RemoveAll(tmpgoroot)
|
||||
|
||||
for _, name := range []string{"bin", "lib", "pkg"} {
|
||||
err = symlink(filepath.Join(goroot, name), filepath.Join(tmpgoroot, name))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
err = mergeDirectory(goroot, tinygoroot, tmpgoroot, "", pathsToOverride(needsSyscallPackage(config.BuildTags())))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = os.Rename(tmpgoroot, cachedgoroot)
|
||||
if err != nil {
|
||||
if os.IsExist(err) {
|
||||
// Another invocation of TinyGo also seems to have created a GOROOT.
|
||||
// Use that one instead. Our new GOROOT will be automatically
|
||||
// deleted by the defer above.
|
||||
return cachedgoroot, nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
return cachedgoroot, nil
|
||||
}
|
||||
|
||||
// mergeDirectory merges two roots recursively. The tmpgoroot is the directory
|
||||
// that will be created by this call by either symlinking the directory from
|
||||
// goroot or tinygoroot, or by creating the directory and merging the contents.
|
||||
func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides map[string]bool) error {
|
||||
if mergeSubdirs, ok := overrides[importPath+"/"]; ok {
|
||||
if !mergeSubdirs {
|
||||
// This directory and all subdirectories should come from the TinyGo
|
||||
// root, so simply make a symlink.
|
||||
newname := filepath.Join(tmpgoroot, "src", importPath)
|
||||
oldname := filepath.Join(tinygoroot, "src", importPath)
|
||||
return symlink(oldname, newname)
|
||||
}
|
||||
|
||||
// Merge subdirectories. Start by making the directory to merge.
|
||||
err := os.Mkdir(filepath.Join(tmpgoroot, "src", importPath), 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Symlink all files from TinyGo, and symlink directories from TinyGo
|
||||
// that need to be overridden.
|
||||
tinygoEntries, err := ioutil.ReadDir(filepath.Join(tinygoroot, "src", importPath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, e := range tinygoEntries {
|
||||
if e.IsDir() {
|
||||
// A directory, so merge this thing.
|
||||
err := mergeDirectory(goroot, tinygoroot, tmpgoroot, path.Join(importPath, e.Name()), overrides)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// A file, so symlink this.
|
||||
newname := filepath.Join(tmpgoroot, "src", importPath, e.Name())
|
||||
oldname := filepath.Join(tinygoroot, "src", importPath, e.Name())
|
||||
err := symlink(oldname, newname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Symlink all directories from $GOROOT that are not part of the TinyGo
|
||||
// overrides.
|
||||
gorootEntries, err := ioutil.ReadDir(filepath.Join(goroot, "src", importPath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, e := range gorootEntries {
|
||||
if !e.IsDir() {
|
||||
// Don't merge in files from Go. Otherwise we'd end up with a
|
||||
// weird syscall package with files from both roots.
|
||||
continue
|
||||
}
|
||||
if _, ok := overrides[path.Join(importPath, e.Name())+"/"]; ok {
|
||||
// Already included above, so don't bother trying to create this
|
||||
// symlink.
|
||||
continue
|
||||
}
|
||||
newname := filepath.Join(tmpgoroot, "src", importPath, e.Name())
|
||||
oldname := filepath.Join(goroot, "src", importPath, e.Name())
|
||||
err := symlink(oldname, newname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// needsSyscallPackage returns whether the syscall package should be overriden
|
||||
// with the TinyGo version. This is the case on some targets.
|
||||
func needsSyscallPackage(buildTags []string) bool {
|
||||
for _, tag := range buildTags {
|
||||
if tag == "baremetal" || tag == "darwin" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// The boolean indicates whether to merge the subdirs. True means merge, false
|
||||
// means use the TinyGo version.
|
||||
func pathsToOverride(needsSyscallPackage bool) map[string]bool {
|
||||
paths := map[string]bool{
|
||||
"/": true,
|
||||
"device/": false,
|
||||
"examples/": false,
|
||||
"internal/": true,
|
||||
"internal/bytealg/": false,
|
||||
"internal/reflectlite/": false,
|
||||
"internal/task/": false,
|
||||
"machine/": false,
|
||||
"os/": true,
|
||||
"reflect/": false,
|
||||
"runtime/": false,
|
||||
"sync/": true,
|
||||
"testing/": false,
|
||||
}
|
||||
if needsSyscallPackage {
|
||||
paths["syscall/"] = true // include syscall/js
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
// symlink creates a symlink or something similar. On Unix-like systems, it
|
||||
// always creates a symlink. On Windows, it tries to create a symlink and if
|
||||
// that fails, creates a hardlink or directory junction instead.
|
||||
//
|
||||
// Note that while Windows 10 does support symlinks and allows them to be
|
||||
// created using os.Symlink, it requires developer mode to be enabled.
|
||||
// Therefore provide a fallback for when symlinking is not possible.
|
||||
// Unfortunately this fallback only works when TinyGo is installed on the same
|
||||
// filesystem as the TinyGo cache and the Go installation (which is usually the
|
||||
// C drive).
|
||||
func symlink(oldname, newname string) error {
|
||||
symlinkErr := os.Symlink(oldname, newname)
|
||||
if runtime.GOOS == "windows" && symlinkErr != nil {
|
||||
// Fallback for when developer mode is disabled.
|
||||
// Note that we return the symlink error even if something else fails
|
||||
// later on. This is because symlinks are the easiest to support
|
||||
// (they're also used on Linux and MacOS) and enabling them is easy:
|
||||
// just enable developer mode.
|
||||
st, err := os.Stat(oldname)
|
||||
if err != nil {
|
||||
return symlinkErr
|
||||
}
|
||||
if st.IsDir() {
|
||||
// Make a directory junction. There may be a way to do this
|
||||
// programmatically, but it involves a lot of magic. Use the mklink
|
||||
// command built into cmd instead (mklink is a builtin, not an
|
||||
// external command).
|
||||
err := exec.Command("cmd", "/k", "mklink", "/J", newname, oldname).Run()
|
||||
if err != nil {
|
||||
return symlinkErr
|
||||
}
|
||||
} else {
|
||||
// Make a hard link.
|
||||
err := os.Link(oldname, newname)
|
||||
if err != nil {
|
||||
return symlinkErr
|
||||
}
|
||||
}
|
||||
return nil // success
|
||||
}
|
||||
return symlinkErr
|
||||
}
|
||||
+168
-221
@@ -3,6 +3,7 @@ package loader
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
@@ -12,114 +13,191 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/tinygo-org/tinygo/cgo"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
// Program holds all packages and some metadata about the program as a whole.
|
||||
type Program struct {
|
||||
mainPkg string
|
||||
Build *build.Context
|
||||
OverlayBuild *build.Context
|
||||
OverlayPath func(path string) string
|
||||
Tests bool
|
||||
Packages map[string]*Package
|
||||
MainPkg *Package
|
||||
sorted []*Package
|
||||
fset *token.FileSet
|
||||
TypeChecker types.Config
|
||||
Dir string // current working directory (for error reporting)
|
||||
TINYGOROOT string // root of the TinyGo installation or root of the source code
|
||||
CFlags []string
|
||||
LDFlags []string
|
||||
ClangHeaders string
|
||||
}
|
||||
|
||||
// Package holds a loaded package, its imports, and its parsed files.
|
||||
type Package struct {
|
||||
*Program
|
||||
*build.Package
|
||||
Imports map[string]*Package
|
||||
Importing bool
|
||||
Files []*ast.File
|
||||
Pkg *types.Package
|
||||
*packages.Package
|
||||
Files []*ast.File
|
||||
Pkg *types.Package
|
||||
types.Info
|
||||
}
|
||||
|
||||
// Import loads the given package relative to srcDir (for the vendor directory).
|
||||
// It only loads the current package without recursion.
|
||||
func (p *Program) Import(path, srcDir string, pos token.Position) (*Package, error) {
|
||||
// Load loads the given package with all dependencies (including the runtime
|
||||
// package). Call .Parse() afterwards to parse all Go files (including CGo
|
||||
// processing, if necessary).
|
||||
func (p *Program) Load(importPath string) error {
|
||||
if p.Packages == nil {
|
||||
p.Packages = make(map[string]*Package)
|
||||
}
|
||||
|
||||
// Load this package.
|
||||
ctx := p.Build
|
||||
if newPath := p.OverlayPath(path); newPath != "" {
|
||||
ctx = p.OverlayBuild
|
||||
path = newPath
|
||||
}
|
||||
buildPkg, err := ctx.Import(path, srcDir, build.ImportComment)
|
||||
err := p.loadPackage(importPath)
|
||||
if err != nil {
|
||||
return nil, scanner.Error{
|
||||
Pos: pos,
|
||||
Msg: err.Error(), // TODO: define a new error type that will wrap the inner error
|
||||
}
|
||||
return err
|
||||
}
|
||||
if existingPkg, ok := p.Packages[buildPkg.ImportPath]; ok {
|
||||
// Already imported, or at least started the import.
|
||||
return existingPkg, nil
|
||||
p.MainPkg = p.sorted[len(p.sorted)-1]
|
||||
if _, ok := p.Packages["runtime"]; !ok {
|
||||
// The runtime package wasn't loaded. Although `go list -deps` seems to
|
||||
// return the full dependency list, there is no way to get those
|
||||
// packages from the go/packages package. Therefore load the runtime
|
||||
// manually and add it to the list of to-be-compiled packages
|
||||
// (duplicates are already filtered).
|
||||
return p.loadPackage("runtime")
|
||||
}
|
||||
p.sorted = nil // invalidate the sorted order of packages
|
||||
pkg := p.newPackage(buildPkg)
|
||||
p.Packages[buildPkg.ImportPath] = pkg
|
||||
|
||||
if p.mainPkg == "" {
|
||||
p.mainPkg = buildPkg.ImportPath
|
||||
}
|
||||
|
||||
return pkg, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// ImportFile loads and parses the import statements in the given path and
|
||||
// creates a pseudo-package out of it.
|
||||
func (p *Program) ImportFile(path string) (*Package, error) {
|
||||
if p.Packages == nil {
|
||||
p.Packages = make(map[string]*Package)
|
||||
func (p *Program) loadPackage(importPath string) error {
|
||||
cgoEnabled := "0"
|
||||
if p.Build.CgoEnabled {
|
||||
cgoEnabled = "1"
|
||||
}
|
||||
if _, ok := p.Packages[path]; ok {
|
||||
// unlikely
|
||||
return nil, errors.New("loader: cannot import file that is already imported as package: " + path)
|
||||
}
|
||||
|
||||
file, err := p.parseFile(path, parser.ImportsOnly)
|
||||
pkgs, err := packages.Load(&packages.Config{
|
||||
Mode: packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps,
|
||||
Env: append(os.Environ(), "GOROOT="+p.Build.GOROOT, "GOOS="+p.Build.GOOS, "GOARCH="+p.Build.GOARCH, "CGO_ENABLED="+cgoEnabled),
|
||||
BuildFlags: []string{"-tags", strings.Join(p.Build.BuildTags, " ")},
|
||||
Tests: p.Tests,
|
||||
}, importPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
buildPkg := &build.Package{
|
||||
Dir: filepath.Dir(path),
|
||||
ImportPath: path,
|
||||
GoFiles: []string{filepath.Base(path)},
|
||||
var pkg *packages.Package
|
||||
if p.Tests {
|
||||
// We need the second package. Quoting from the docs:
|
||||
// > For example, when using the go command, loading "fmt" with Tests=true
|
||||
// > returns four packages, with IDs "fmt" (the standard package),
|
||||
// > "fmt [fmt.test]" (the package as compiled for the test),
|
||||
// > "fmt_test" (the test functions from source files in package fmt_test),
|
||||
// > and "fmt.test" (the test binary).
|
||||
pkg = pkgs[1]
|
||||
} else {
|
||||
if len(pkgs) != 1 {
|
||||
return fmt.Errorf("expected exactly one package while importing %s, got %d", importPath, len(pkgs))
|
||||
}
|
||||
pkg = pkgs[0]
|
||||
}
|
||||
for _, importSpec := range file.Imports {
|
||||
buildPkg.Imports = append(buildPkg.Imports, importSpec.Path.Value[1:len(importSpec.Path.Value)-1])
|
||||
}
|
||||
p.sorted = nil // invalidate the sorted order of packages
|
||||
pkg := p.newPackage(buildPkg)
|
||||
p.Packages[buildPkg.ImportPath] = pkg
|
||||
var importError *Errors
|
||||
var addPackages func(pkg *packages.Package)
|
||||
addPackages = func(pkg *packages.Package) {
|
||||
if _, ok := p.Packages[pkg.PkgPath]; ok {
|
||||
return
|
||||
}
|
||||
pkg2 := p.newPackage(pkg)
|
||||
p.Packages[pkg.PkgPath] = pkg2
|
||||
if len(pkg.Errors) != 0 {
|
||||
if importError != nil {
|
||||
// There was another error reported already. Do not report
|
||||
// errors from multiple packages at once.
|
||||
return
|
||||
}
|
||||
importError = &Errors{
|
||||
Pkg: pkg2,
|
||||
}
|
||||
for _, err := range pkg.Errors {
|
||||
pos := token.Position{}
|
||||
fields := strings.Split(err.Pos, ":")
|
||||
if len(fields) >= 2 {
|
||||
// There is some file/line/column information.
|
||||
if n, err := strconv.Atoi(fields[len(fields)-2]); err == nil {
|
||||
// Format: filename.go:line:colum
|
||||
pos.Filename = strings.Join(fields[:len(fields)-2], ":")
|
||||
pos.Line = n
|
||||
pos.Column, _ = strconv.Atoi(fields[len(fields)-1])
|
||||
} else {
|
||||
// Format: filename.go:line
|
||||
pos.Filename = strings.Join(fields[:len(fields)-1], ":")
|
||||
pos.Line, _ = strconv.Atoi(fields[len(fields)-1])
|
||||
}
|
||||
pos.Filename = p.getOriginalPath(pos.Filename)
|
||||
}
|
||||
importError.Errs = append(importError.Errs, scanner.Error{
|
||||
Pos: pos,
|
||||
Msg: err.Msg,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if p.mainPkg == "" {
|
||||
p.mainPkg = buildPkg.ImportPath
|
||||
}
|
||||
// Get the list of imports (sorted alphabetically).
|
||||
names := make([]string, 0, len(pkg.Imports))
|
||||
for name := range pkg.Imports {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
return pkg, nil
|
||||
// Add all the imports.
|
||||
for _, name := range names {
|
||||
addPackages(pkg.Imports[name])
|
||||
}
|
||||
|
||||
p.sorted = append(p.sorted, pkg2)
|
||||
}
|
||||
addPackages(pkg)
|
||||
if importError != nil {
|
||||
return *importError
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getOriginalPath looks whether this path is in the generated GOROOT and if so,
|
||||
// replaces the path with the original path (in GOROOT or TINYGOROOT). Otherwise
|
||||
// the input path is returned.
|
||||
func (p *Program) getOriginalPath(path string) string {
|
||||
originalPath := path
|
||||
if strings.HasPrefix(path, p.Build.GOROOT+string(filepath.Separator)) {
|
||||
// If this file is part of the synthetic GOROOT, try to infer the
|
||||
// original path.
|
||||
relpath := path[len(filepath.Join(p.Build.GOROOT, "src"))+1:]
|
||||
realgorootPath := filepath.Join(goenv.Get("GOROOT"), "src", relpath)
|
||||
if _, err := os.Stat(realgorootPath); err == nil {
|
||||
originalPath = realgorootPath
|
||||
}
|
||||
maybeInTinyGoRoot := false
|
||||
for prefix := range pathsToOverride(needsSyscallPackage(p.Build.BuildTags)) {
|
||||
if !strings.HasPrefix(relpath, prefix) {
|
||||
continue
|
||||
}
|
||||
maybeInTinyGoRoot = true
|
||||
}
|
||||
if maybeInTinyGoRoot {
|
||||
tinygoPath := filepath.Join(p.TINYGOROOT, "src", relpath)
|
||||
if _, err := os.Stat(tinygoPath); err == nil {
|
||||
originalPath = tinygoPath
|
||||
}
|
||||
}
|
||||
}
|
||||
return originalPath
|
||||
}
|
||||
|
||||
// newPackage instantiates a new *Package object with initialized members.
|
||||
func (p *Program) newPackage(pkg *build.Package) *Package {
|
||||
func (p *Program) newPackage(pkg *packages.Package) *Package {
|
||||
return &Package{
|
||||
Program: p,
|
||||
Package: pkg,
|
||||
Imports: make(map[string]*Package, len(pkg.Imports)),
|
||||
Info: types.Info{
|
||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||
Defs: make(map[*ast.Ident]types.Object),
|
||||
@@ -134,87 +212,25 @@ func (p *Program) newPackage(pkg *build.Package) *Package {
|
||||
// Sorted returns a list of all packages, sorted in a way that no packages come
|
||||
// before the packages they depend upon.
|
||||
func (p *Program) Sorted() []*Package {
|
||||
if p.sorted == nil {
|
||||
p.sort()
|
||||
}
|
||||
return p.sorted
|
||||
}
|
||||
|
||||
func (p *Program) sort() {
|
||||
p.sorted = nil
|
||||
packageList := make([]*Package, 0, len(p.Packages))
|
||||
packageSet := make(map[string]struct{}, len(p.Packages))
|
||||
worklist := make([]string, 0, len(p.Packages))
|
||||
for path := range p.Packages {
|
||||
worklist = append(worklist, path)
|
||||
}
|
||||
sort.Strings(worklist)
|
||||
for len(worklist) != 0 {
|
||||
pkgPath := worklist[0]
|
||||
pkg := p.Packages[pkgPath]
|
||||
|
||||
if _, ok := packageSet[pkgPath]; ok {
|
||||
// Package already in the final package list.
|
||||
worklist = worklist[1:]
|
||||
continue
|
||||
}
|
||||
|
||||
unsatisfiedImports := make([]string, 0)
|
||||
for _, pkg := range pkg.Imports {
|
||||
if _, ok := packageSet[pkg.ImportPath]; ok {
|
||||
continue
|
||||
}
|
||||
unsatisfiedImports = append(unsatisfiedImports, pkg.ImportPath)
|
||||
}
|
||||
sort.Strings(unsatisfiedImports)
|
||||
if len(unsatisfiedImports) == 0 {
|
||||
// All dependencies of this package are satisfied, so add this
|
||||
// package to the list.
|
||||
packageList = append(packageList, pkg)
|
||||
packageSet[pkgPath] = struct{}{}
|
||||
worklist = worklist[1:]
|
||||
} else {
|
||||
// Prepend all dependencies to the worklist and reconsider this
|
||||
// package (by not removing it from the worklist). At that point, it
|
||||
// must be possible to add it to packageList.
|
||||
worklist = append(unsatisfiedImports, worklist...)
|
||||
}
|
||||
}
|
||||
|
||||
p.sorted = packageList
|
||||
}
|
||||
|
||||
// Parse recursively imports all packages, parses them, and typechecks them.
|
||||
// Parse parses all packages and typechecks them.
|
||||
//
|
||||
// The returned error may be an Errors error, which contains a list of errors.
|
||||
//
|
||||
// Idempotent.
|
||||
func (p *Program) Parse(compileTestBinary bool) error {
|
||||
includeTests := compileTestBinary
|
||||
|
||||
// Load all imports
|
||||
for _, pkg := range p.Sorted() {
|
||||
err := pkg.importRecursively(includeTests)
|
||||
if err != nil {
|
||||
if err, ok := err.(*ImportCycleError); ok {
|
||||
if pkg.ImportPath != err.Packages[0] {
|
||||
err.Packages = append([]string{pkg.ImportPath}, err.Packages...)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Program) Parse() error {
|
||||
// Parse all packages.
|
||||
for _, pkg := range p.Sorted() {
|
||||
err := pkg.Parse(includeTests)
|
||||
err := pkg.Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if compileTestBinary {
|
||||
err := p.SwapTestMain()
|
||||
if p.Tests {
|
||||
err := p.swapTestMain()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -231,7 +247,7 @@ func (p *Program) Parse(compileTestBinary bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Program) SwapTestMain() error {
|
||||
func (p *Program) swapTestMain() error {
|
||||
var tests []string
|
||||
|
||||
isTestFunc := func(f *ast.FuncDecl) bool {
|
||||
@@ -241,8 +257,7 @@ func (p *Program) SwapTestMain() error {
|
||||
}
|
||||
return false
|
||||
}
|
||||
mainPkg := p.Packages[p.mainPkg]
|
||||
for _, f := range mainPkg.Files {
|
||||
for _, f := range p.MainPkg.Files {
|
||||
for i, d := range f.Decls {
|
||||
switch v := d.(type) {
|
||||
case *ast.FuncDecl:
|
||||
@@ -293,7 +308,7 @@ func main () {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := filepath.Join(p.mainPkg, "$testmain.go")
|
||||
path := filepath.Join(p.MainPkg.Dir, "$testmain.go")
|
||||
|
||||
if p.fset == nil {
|
||||
p.fset = token.NewFileSet()
|
||||
@@ -303,7 +318,7 @@ func main () {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mainPkg.Files = append(mainPkg.Files, newMain)
|
||||
p.MainPkg.Files = append(p.MainPkg.Files, newMain)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -319,34 +334,27 @@ func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer rd.Close()
|
||||
relpath := path
|
||||
if filepath.IsAbs(path) {
|
||||
rp, err := filepath.Rel(p.Dir, path)
|
||||
if err == nil {
|
||||
relpath = rp
|
||||
}
|
||||
}
|
||||
return parser.ParseFile(p.fset, relpath, rd, mode)
|
||||
return parser.ParseFile(p.fset, p.getOriginalPath(path), rd, mode)
|
||||
}
|
||||
|
||||
// Parse parses and typechecks this package.
|
||||
//
|
||||
// Idempotent.
|
||||
func (p *Package) Parse(includeTests bool) error {
|
||||
func (p *Package) Parse() error {
|
||||
if len(p.Files) != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load the AST.
|
||||
// TODO: do this in parallel.
|
||||
if p.ImportPath == "unsafe" {
|
||||
if p.PkgPath == "unsafe" {
|
||||
// Special case for the unsafe package. Don't even bother loading
|
||||
// the files.
|
||||
p.Pkg = types.Unsafe
|
||||
return nil
|
||||
}
|
||||
|
||||
files, err := p.parseFiles(includeTests)
|
||||
files, err := p.parseFiles()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -373,7 +381,7 @@ func (p *Package) Check() error {
|
||||
// Do typechecking of the package.
|
||||
checker.Importer = p
|
||||
|
||||
typesPkg, err := checker.Check(p.ImportPath, p.fset, p.Files, &p.Info)
|
||||
typesPkg, err := checker.Check(p.PkgPath, p.fset, p.Files, &p.Info)
|
||||
if err != nil {
|
||||
if err, ok := err.(Errors); ok {
|
||||
return err
|
||||
@@ -385,22 +393,14 @@ func (p *Package) Check() error {
|
||||
}
|
||||
|
||||
// parseFiles parses the loaded list of files and returns this list.
|
||||
func (p *Package) parseFiles(includeTests bool) ([]*ast.File, error) {
|
||||
func (p *Package) parseFiles() ([]*ast.File, error) {
|
||||
// TODO: do this concurrently.
|
||||
var files []*ast.File
|
||||
var fileErrs []error
|
||||
|
||||
var gofiles []string
|
||||
if includeTests {
|
||||
gofiles = make([]string, 0, len(p.GoFiles)+len(p.TestGoFiles))
|
||||
gofiles = append(gofiles, p.GoFiles...)
|
||||
gofiles = append(gofiles, p.TestGoFiles...)
|
||||
} else {
|
||||
gofiles = p.GoFiles
|
||||
}
|
||||
|
||||
for _, file := range gofiles {
|
||||
f, err := p.parseFile(filepath.Join(p.Package.Dir, file), parser.ParseComments)
|
||||
var cgoFiles []*ast.File
|
||||
for _, file := range p.GoFiles {
|
||||
f, err := p.parseFile(file, parser.ParseComments)
|
||||
if err != nil {
|
||||
fileErrs = append(fileErrs, err)
|
||||
continue
|
||||
@@ -409,27 +409,24 @@ func (p *Package) parseFiles(includeTests bool) ([]*ast.File, error) {
|
||||
fileErrs = append(fileErrs, err)
|
||||
continue
|
||||
}
|
||||
for _, importSpec := range f.Imports {
|
||||
if importSpec.Path.Value == `"C"` {
|
||||
cgoFiles = append(cgoFiles, f)
|
||||
}
|
||||
}
|
||||
files = append(files, f)
|
||||
}
|
||||
for _, file := range p.CgoFiles {
|
||||
path := filepath.Join(p.Package.Dir, file)
|
||||
f, err := p.parseFile(path, parser.ParseComments)
|
||||
if err != nil {
|
||||
fileErrs = append(fileErrs, err)
|
||||
continue
|
||||
}
|
||||
files = append(files, f)
|
||||
}
|
||||
if len(p.CgoFiles) != 0 {
|
||||
cflags := append(p.CFlags, "-I"+p.Package.Dir)
|
||||
if len(cgoFiles) != 0 {
|
||||
cflags := append(p.CFlags, "-I"+filepath.Dir(p.GoFiles[0]))
|
||||
if p.ClangHeaders != "" {
|
||||
cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.ClangHeaders)
|
||||
}
|
||||
generated, errs := cgo.Process(files, p.Program.Dir, p.fset, cflags)
|
||||
generated, ldflags, errs := cgo.Process(files, p.Program.Dir, p.fset, cflags)
|
||||
if errs != nil {
|
||||
fileErrs = append(fileErrs, errs...)
|
||||
}
|
||||
files = append(files, generated)
|
||||
p.LDFlags = append(p.LDFlags, ldflags...)
|
||||
}
|
||||
if len(fileErrs) != 0 {
|
||||
return nil, Errors{p, fileErrs}
|
||||
@@ -445,58 +442,8 @@ func (p *Package) Import(to string) (*types.Package, error) {
|
||||
return types.Unsafe, nil
|
||||
}
|
||||
if _, ok := p.Imports[to]; ok {
|
||||
return p.Imports[to].Pkg, nil
|
||||
return p.Packages[p.Imports[to].PkgPath].Pkg, nil
|
||||
} else {
|
||||
return nil, errors.New("package not imported: " + to)
|
||||
}
|
||||
}
|
||||
|
||||
// importRecursively calls Program.Import() on all imported packages, and calls
|
||||
// importRecursively() on the imported packages as well.
|
||||
//
|
||||
// Idempotent.
|
||||
func (p *Package) importRecursively(includeTests bool) error {
|
||||
p.Importing = true
|
||||
|
||||
imports := p.Package.Imports
|
||||
if includeTests {
|
||||
imports = append(imports, p.Package.TestImports...)
|
||||
}
|
||||
|
||||
for _, to := range imports {
|
||||
if to == "C" {
|
||||
// Do CGo processing in a later stage.
|
||||
continue
|
||||
}
|
||||
if _, ok := p.Imports[to]; ok {
|
||||
continue
|
||||
}
|
||||
// Find error location.
|
||||
var pos token.Position
|
||||
if len(p.Package.ImportPos[to]) > 0 {
|
||||
pos = p.Package.ImportPos[to][0]
|
||||
} else {
|
||||
pos = token.Position{Filename: p.Package.ImportPath}
|
||||
}
|
||||
importedPkg, err := p.Program.Import(to, p.Package.Dir, pos)
|
||||
if err != nil {
|
||||
if err, ok := err.(*ImportCycleError); ok {
|
||||
err.Packages = append([]string{p.ImportPath}, err.Packages...)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if importedPkg.Importing {
|
||||
return &ImportCycleError{[]string{p.ImportPath, importedPkg.ImportPath}, p.ImportPos[to]}
|
||||
}
|
||||
err = importedPkg.importRecursively(false)
|
||||
if err != nil {
|
||||
if err, ok := err.(*ImportCycleError); ok {
|
||||
err.Packages = append([]string{p.ImportPath}, err.Packages...)
|
||||
}
|
||||
return err
|
||||
}
|
||||
p.Imports[to] = importedPkg
|
||||
}
|
||||
p.Importing = false
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -124,6 +124,7 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
|
||||
|
||||
// Test runs the tests in the given package.
|
||||
func Test(pkgName string, options *compileopts.Options) error {
|
||||
options.TestConfig.CompileTestBinary = true
|
||||
config, err := builder.NewConfig(options)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -135,7 +136,6 @@ func Test(pkgName string, options *compileopts.Options) error {
|
||||
// For details: https://github.com/golang/go/issues/21360
|
||||
config.Target.BuildTags = append(config.Target.BuildTags, "test")
|
||||
|
||||
options.TestConfig.CompileTestBinary = true
|
||||
return builder.Build(pkgName, ".elf", config, func(tmppath string) error {
|
||||
cmd := exec.Command(tmppath)
|
||||
cmd.Stdout = os.Stdout
|
||||
@@ -310,10 +310,12 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
switch gdbInterface {
|
||||
case "msd", "command", "":
|
||||
if len(config.Target.Emulator) != 0 {
|
||||
// Assume QEMU as an emulator.
|
||||
if config.Target.Emulator[0] == "mgba" {
|
||||
gdbInterface = "mgba"
|
||||
} else if config.Target.Emulator[0] == "simavr" {
|
||||
gdbInterface = "simavr"
|
||||
} else {
|
||||
// Assume QEMU as an emulator.
|
||||
gdbInterface = "qemu"
|
||||
}
|
||||
} else if openocdInterface != "" && config.Target.OpenOCDTarget != "" {
|
||||
@@ -379,6 +381,14 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
daemon = exec.Command(config.Target.Emulator[0], args...)
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
case "simavr":
|
||||
gdbCommands = append(gdbCommands, "target remote :1234")
|
||||
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], "-g", tmppath)
|
||||
daemon = exec.Command(config.Target.Emulator[0], args...)
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
case "msd":
|
||||
return errors.New("gdb is not supported for drag-and-drop programmable devices")
|
||||
default:
|
||||
@@ -479,6 +489,13 @@ func touchSerialPortAt1200bps(port string) (err error) {
|
||||
// Open port
|
||||
p, e := serial.Open(port, &serial.Mode{BaudRate: 1200})
|
||||
if e != nil {
|
||||
if runtime.GOOS == `windows` {
|
||||
se, ok := e.(*serial.PortError)
|
||||
if ok && se.Code() == serial.InvalidSerialPort {
|
||||
// InvalidSerialPort error occurs when transitioning to boot
|
||||
return nil
|
||||
}
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
err = e
|
||||
continue
|
||||
@@ -491,6 +508,8 @@ func touchSerialPortAt1200bps(port string) (err error) {
|
||||
return fmt.Errorf("opening port: %s", err)
|
||||
}
|
||||
|
||||
const maxMSDRetries = 10
|
||||
|
||||
func flashUF2UsingMSD(volume, tmppath string) error {
|
||||
// find standard UF2 info path
|
||||
var infoPath string
|
||||
@@ -507,15 +526,12 @@ func flashUF2UsingMSD(volume, tmppath string) error {
|
||||
infoPath = path + "/INFO_UF2.TXT"
|
||||
}
|
||||
|
||||
d, err := filepath.Glob(infoPath)
|
||||
d, err := locateDevice(volume, infoPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d == nil {
|
||||
return errors.New("unable to locate UF2 device: " + volume)
|
||||
}
|
||||
|
||||
return moveFile(tmppath, filepath.Dir(d[0])+"/flash.uf2")
|
||||
return moveFile(tmppath, filepath.Dir(d)+"/flash.uf2")
|
||||
}
|
||||
|
||||
func flashHexUsingMSD(volume, tmppath string) error {
|
||||
@@ -534,15 +550,31 @@ func flashHexUsingMSD(volume, tmppath string) error {
|
||||
destPath = path + "/"
|
||||
}
|
||||
|
||||
d, err := filepath.Glob(destPath)
|
||||
d, err := locateDevice(volume, destPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d == nil {
|
||||
return errors.New("unable to locate device: " + volume)
|
||||
}
|
||||
|
||||
return moveFile(tmppath, d[0]+"/flash.hex")
|
||||
return moveFile(tmppath, d+"/flash.hex")
|
||||
}
|
||||
|
||||
func locateDevice(volume, path string) (string, error) {
|
||||
var d []string
|
||||
var err error
|
||||
for i := 0; i < maxMSDRetries; i++ {
|
||||
d, err = filepath.Glob(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if d != nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
if d == nil {
|
||||
return "", errors.New("unable to locate device: " + volume)
|
||||
}
|
||||
return d[0], nil
|
||||
}
|
||||
|
||||
func windowsFindUSBDrive(volume string) (string, error) {
|
||||
@@ -603,29 +635,18 @@ func getDefaultPort() (port string, err error) {
|
||||
case "freebsd":
|
||||
portPath = "/dev/cuaU*"
|
||||
case "windows":
|
||||
cmd := exec.Command("wmic",
|
||||
"PATH", "Win32_SerialPort", "WHERE", "Caption LIKE 'USB Serial%'", "GET", "DeviceID")
|
||||
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
ports, err := serial.GetPortsList()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if out.String() == "No Instance(s) Available." {
|
||||
if len(ports) == 0 {
|
||||
return "", errors.New("no serial ports available")
|
||||
} else if len(ports) > 1 {
|
||||
return "", errors.New("multiple serial ports available - use -port flag")
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(out.String(), "\n") {
|
||||
words := strings.Fields(line)
|
||||
if len(words) == 1 {
|
||||
if strings.Contains(words[0], "COM") {
|
||||
return words[0], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", errors.New("unable to locate a serial port")
|
||||
return ports[0], nil
|
||||
default:
|
||||
return "", errors.New("unable to search for a default USB device to be flashed on this OS")
|
||||
}
|
||||
@@ -641,9 +662,39 @@ func getDefaultPort() (port string, err error) {
|
||||
return d[0], nil
|
||||
}
|
||||
|
||||
// runGoList runs the `go list` command but using the configuration used for
|
||||
// TinyGo.
|
||||
func runGoList(config *compileopts.Config, flagJSON, flagDeps bool, pkgs []string) error {
|
||||
goroot, err := loader.GetCachedGoroot(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args := []string{"list"}
|
||||
if flagJSON {
|
||||
args = append(args, "-json")
|
||||
}
|
||||
if flagDeps {
|
||||
args = append(args, "-deps")
|
||||
}
|
||||
if len(config.BuildTags()) != 0 {
|
||||
args = append(args, "-tags", strings.Join(config.BuildTags(), " "))
|
||||
}
|
||||
args = append(args, pkgs...)
|
||||
cgoEnabled := "0"
|
||||
if config.CgoEnabled() {
|
||||
cgoEnabled = "1"
|
||||
}
|
||||
cmd := exec.Command("go", args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Env = append(os.Environ(), "GOROOT="+goroot, "GOOS="+config.GOOS(), "GOARCH="+config.GOARCH(), "CGO_ENABLED="+cgoEnabled)
|
||||
cmd.Run()
|
||||
return nil
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
||||
fmt.Fprintln(os.Stderr, "version:", version)
|
||||
fmt.Fprintln(os.Stderr, "version:", goenv.Version)
|
||||
fmt.Fprintf(os.Stderr, "usage: %s command [-printir] [-target=<target>] -o <output> <input>\n", os.Args[0])
|
||||
fmt.Fprintln(os.Stderr, "\ncommands:")
|
||||
fmt.Fprintln(os.Stderr, " build: compile packages and dependencies")
|
||||
@@ -652,12 +703,27 @@ func usage() {
|
||||
fmt.Fprintln(os.Stderr, " flash: compile and flash to the device")
|
||||
fmt.Fprintln(os.Stderr, " gdb: run/flash and immediately enter GDB")
|
||||
fmt.Fprintln(os.Stderr, " env: list environment variables used during build")
|
||||
fmt.Fprintln(os.Stderr, " list: run go list using the TinyGo root")
|
||||
fmt.Fprintln(os.Stderr, " clean: empty cache directory ("+goenv.Get("GOCACHE")+")")
|
||||
fmt.Fprintln(os.Stderr, " help: print this help text")
|
||||
fmt.Fprintln(os.Stderr, "\nflags:")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
// try to make the path relative to the current working directory. If any error
|
||||
// occurs, this error is ignored and the absolute path is returned instead.
|
||||
func tryToMakePathRelative(dir string) string {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return dir
|
||||
}
|
||||
relpath, err := filepath.Rel(wd, dir)
|
||||
if err != nil {
|
||||
return dir
|
||||
}
|
||||
return relpath
|
||||
}
|
||||
|
||||
// printCompilerError prints compiler errors using the provided logger function
|
||||
// (similar to fmt.Println).
|
||||
//
|
||||
@@ -665,8 +731,24 @@ func usage() {
|
||||
// to limitations in the LLVM bindings.
|
||||
func printCompilerError(logln func(...interface{}), err error) {
|
||||
switch err := err.(type) {
|
||||
case types.Error, scanner.Error:
|
||||
case types.Error:
|
||||
printCompilerError(logln, scanner.Error{
|
||||
Pos: err.Fset.Position(err.Pos),
|
||||
Msg: err.Msg,
|
||||
})
|
||||
case scanner.Error:
|
||||
if !strings.HasPrefix(err.Pos.Filename, filepath.Join(goenv.Get("GOROOT"), "src")) && !strings.HasPrefix(err.Pos.Filename, filepath.Join(goenv.Get("TINYGOROOT"), "src")) {
|
||||
// This file is not from the standard library (either the GOROOT or
|
||||
// the TINYGOROOT). Make the path relative, for easier reading.
|
||||
// Ignore any errors in the process (falling back to the absolute
|
||||
// path).
|
||||
err.Pos.Filename = tryToMakePathRelative(err.Pos.Filename)
|
||||
}
|
||||
logln(err)
|
||||
case scanner.ErrorList:
|
||||
for _, scannerErr := range err {
|
||||
printCompilerError(logln, *scannerErr)
|
||||
}
|
||||
case *interp.Error:
|
||||
logln("#", err.ImportPath)
|
||||
logln(err.Error())
|
||||
@@ -683,13 +765,13 @@ func printCompilerError(logln func(...interface{}), err error) {
|
||||
}
|
||||
}
|
||||
case loader.Errors:
|
||||
logln("#", err.Pkg.ImportPath)
|
||||
logln("#", err.Pkg.PkgPath)
|
||||
for _, err := range err.Errs {
|
||||
logln(err)
|
||||
printCompilerError(logln, err)
|
||||
}
|
||||
case *builder.MultiError:
|
||||
for _, err := range err.Errs {
|
||||
logln(err)
|
||||
printCompilerError(logln, err)
|
||||
}
|
||||
default:
|
||||
logln("error:", err)
|
||||
@@ -706,11 +788,18 @@ func handleCompilerError(err error) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
command := os.Args[1]
|
||||
|
||||
outpath := flag.String("o", "", "output filename")
|
||||
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
|
||||
gc := flag.String("gc", "", "garbage collector to use (none, leaking, extalloc, conservative)")
|
||||
panicStrategy := flag.String("panic", "print", "panic strategy (print, trap)")
|
||||
scheduler := flag.String("scheduler", "", "which scheduler to use (coroutines, tasks)")
|
||||
scheduler := flag.String("scheduler", "", "which scheduler to use (none, coroutines, tasks)")
|
||||
printIR := flag.Bool("printir", false, "print LLVM IR")
|
||||
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
|
||||
verifyIR := flag.Bool("verifyir", false, "run extra verification steps on LLVM IR")
|
||||
@@ -726,12 +815,11 @@ func main() {
|
||||
wasmAbi := flag.String("wasm-abi", "js", "WebAssembly ABI conventions: js (no i64 params) or generic")
|
||||
heapSize := flag.String("heap-size", "1M", "default heap size in bytes (only supported by WebAssembly)")
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
|
||||
usage()
|
||||
os.Exit(1)
|
||||
var flagJSON, flagDeps *bool
|
||||
if command == "list" {
|
||||
flagJSON = flag.Bool("json", false, "print data in JSON format")
|
||||
flagDeps = flag.Bool("deps", false, "")
|
||||
}
|
||||
command := os.Args[1]
|
||||
|
||||
// Early command processing, before commands are interpreted by the Go flag
|
||||
// library.
|
||||
@@ -770,12 +858,6 @@ func main() {
|
||||
options.LDFlags = strings.Split(*ldFlags, " ")
|
||||
}
|
||||
|
||||
if *panicStrategy != "print" && *panicStrategy != "trap" {
|
||||
fmt.Fprintln(os.Stderr, "Panic strategy must be either print or trap.")
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var err error
|
||||
if options.HeapSize, err = parseSize(*heapSize); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Could not read heap size:", *heapSize)
|
||||
@@ -785,6 +867,13 @@ func main() {
|
||||
|
||||
os.Setenv("CC", "clang -target="+*target)
|
||||
|
||||
err = options.Verify()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch command {
|
||||
case "build":
|
||||
if *outpath == "" {
|
||||
@@ -794,7 +883,7 @@ func main() {
|
||||
}
|
||||
pkgName := "."
|
||||
if flag.NArg() == 1 {
|
||||
pkgName = flag.Arg(0)
|
||||
pkgName = filepath.ToSlash(flag.Arg(0))
|
||||
} else if flag.NArg() > 1 {
|
||||
fmt.Fprintln(os.Stderr, "build only accepts a single positional argument: package name, but multiple were specified")
|
||||
usage()
|
||||
@@ -803,6 +892,7 @@ func main() {
|
||||
if options.Target == "" && filepath.Ext(*outpath) == ".wasm" {
|
||||
options.Target = "wasm"
|
||||
}
|
||||
|
||||
err := Build(pkgName, *outpath, options)
|
||||
handleCompilerError(err)
|
||||
case "build-library":
|
||||
@@ -839,8 +929,9 @@ func main() {
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
pkgName := filepath.ToSlash(flag.Arg(0))
|
||||
if command == "flash" {
|
||||
err := Flash(flag.Arg(0), *port, options)
|
||||
err := Flash(pkgName, *port, options)
|
||||
handleCompilerError(err)
|
||||
} else {
|
||||
if !options.Debug {
|
||||
@@ -848,7 +939,7 @@ func main() {
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
err := FlashGDB(flag.Arg(0), *ocdOutput, options)
|
||||
err := FlashGDB(pkgName, *ocdOutput, options)
|
||||
handleCompilerError(err)
|
||||
}
|
||||
case "run":
|
||||
@@ -857,12 +948,13 @@ func main() {
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
err := Run(flag.Arg(0), options)
|
||||
pkgName := filepath.ToSlash(flag.Arg(0))
|
||||
err := Run(pkgName, options)
|
||||
handleCompilerError(err)
|
||||
case "test":
|
||||
pkgName := "."
|
||||
if flag.NArg() == 1 {
|
||||
pkgName = flag.Arg(0)
|
||||
pkgName = filepath.ToSlash(flag.Arg(0))
|
||||
} else if flag.NArg() > 1 {
|
||||
fmt.Fprintln(os.Stderr, "test only accepts a single positional argument: package name, but multiple were specified")
|
||||
usage()
|
||||
@@ -895,6 +987,18 @@ func main() {
|
||||
fmt.Printf("build tags: %s\n", strings.Join(config.BuildTags(), " "))
|
||||
fmt.Printf("garbage collector: %s\n", config.GC())
|
||||
fmt.Printf("scheduler: %s\n", config.Scheduler())
|
||||
case "list":
|
||||
config, err := builder.NewConfig(options)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
err = runGoList(config, *flagJSON, *flagDeps, flag.Args())
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "failed to run `go list`:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
case "clean":
|
||||
// remove cache directory
|
||||
err := os.RemoveAll(goenv.Get("GOCACHE"))
|
||||
@@ -906,10 +1010,10 @@ func main() {
|
||||
usage()
|
||||
case "version":
|
||||
goversion := "<unknown>"
|
||||
if s, err := builder.GorootVersionString(goenv.Get("GOROOT")); err == nil {
|
||||
if s, err := goenv.GorootVersionString(goenv.Get("GOROOT")); err == nil {
|
||||
goversion = s
|
||||
}
|
||||
fmt.Printf("tinygo version %s %s/%s (using go version %s and LLVM version %s)\n", version, runtime.GOOS, runtime.GOARCH, goversion, llvm.Version)
|
||||
fmt.Printf("tinygo version %s %s/%s (using go version %s and LLVM version %s)\n", goenv.Version, runtime.GOOS, runtime.GOARCH, goversion, llvm.Version)
|
||||
case "env":
|
||||
if flag.NArg() == 0 {
|
||||
// Show all environment variables.
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ func TestCompiler(t *testing.T) {
|
||||
t.Run("ARM64Linux", func(t *testing.T) {
|
||||
runPlatTests("aarch64--linux-gnu", matches, t)
|
||||
})
|
||||
goVersion, err := builder.GorootVersionString(goenv.Get("GOROOT"))
|
||||
goVersion, err := goenv.GorootVersionString(goenv.Get("GOROOT"))
|
||||
if err != nil {
|
||||
t.Error("could not get Go version:", err)
|
||||
return
|
||||
|
||||
+13
-15
@@ -52,11 +52,10 @@ func Asm(asm string)
|
||||
// "value": 1
|
||||
// "result": &dest,
|
||||
// })
|
||||
func AsmFull(asm string, regs map[string]interface{})
|
||||
|
||||
// ReadRegister returns the contents of the specified register. The register
|
||||
// must be a processor register, reachable with the "mov" instruction.
|
||||
func ReadRegister(name string) uintptr
|
||||
//
|
||||
// You can use {} in the asm string (which expands to a register) to set the
|
||||
// return value.
|
||||
func AsmFull(asm string, regs map[string]interface{}) uintptr
|
||||
|
||||
// Run the following system call (SVCall) with 0 arguments.
|
||||
func SVCall0(num uintptr) uintptr
|
||||
@@ -192,22 +191,21 @@ func SetPriority(irq uint32, priority uint32) {
|
||||
NVIC.IPR[regnum].Set((uint32(NVIC.IPR[regnum].Get()) &^ mask) | priority)
|
||||
}
|
||||
|
||||
// DisableInterrupts disables all interrupts, and returns the old state.
|
||||
//
|
||||
// TODO: it doesn't actually return the old state, meaning that it cannot be
|
||||
// nested.
|
||||
// DisableInterrupts disables all interrupts, and returns the old interrupt
|
||||
// state.
|
||||
func DisableInterrupts() uintptr {
|
||||
Asm("cpsid if")
|
||||
return 0
|
||||
return AsmFull(`
|
||||
mrs {}, PRIMASK
|
||||
cpsid i
|
||||
`, nil)
|
||||
}
|
||||
|
||||
// EnableInterrupts enables all interrupts again. The value passed in must be
|
||||
// the mask returned by DisableInterrupts.
|
||||
//
|
||||
// TODO: it doesn't actually use the old state, meaning that it cannot be
|
||||
// nested.
|
||||
func EnableInterrupts(mask uintptr) {
|
||||
Asm("cpsie if")
|
||||
AsmFull("msr PRIMASK, {mask}", map[string]interface{}{
|
||||
"mask": mask,
|
||||
})
|
||||
}
|
||||
|
||||
// SystemReset performs a hard system reset.
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package device
|
||||
|
||||
// Run the given assembly code. The code will be marked as having side effects,
|
||||
// as it doesn't produce output and thus would normally be eliminated by the
|
||||
// optimizer.
|
||||
func Asm(asm string)
|
||||
|
||||
// Run the given inline assembly. The code will be marked as having side
|
||||
// effects, as it would otherwise be optimized away. The inline assembly string
|
||||
// recognizes template values in the form {name}, like so:
|
||||
//
|
||||
// arm.AsmFull(
|
||||
// "str {value}, {result}",
|
||||
// map[string]interface{}{
|
||||
// "value": 1
|
||||
// "result": &dest,
|
||||
// })
|
||||
//
|
||||
// You can use {} in the asm string (which expands to a register) to set the
|
||||
// return value.
|
||||
func AsmFull(asm string, regs map[string]interface{}) uintptr
|
||||
@@ -15,4 +15,7 @@ func Asm(asm string)
|
||||
// "value": 1
|
||||
// "result": &dest,
|
||||
// })
|
||||
func AsmFull(asm string, regs map[string]interface{})
|
||||
//
|
||||
// You can use {} in the asm string (which expands to a register) to set the
|
||||
// return value.
|
||||
func AsmFull(asm string, regs map[string]interface{}) uintptr
|
||||
|
||||
@@ -5,6 +5,33 @@ package riscv
|
||||
// optimizer.
|
||||
func Asm(asm string)
|
||||
|
||||
// ReadRegister returns the contents of the specified register. The register
|
||||
// must be a processor register, reachable with the "mov" instruction.
|
||||
func ReadRegister(name string) uintptr
|
||||
// Run the given inline assembly. The code will be marked as having side
|
||||
// effects, as it would otherwise be optimized away. The inline assembly string
|
||||
// recognizes template values in the form {name}, like so:
|
||||
//
|
||||
// arm.AsmFull(
|
||||
// "st {value}, {result}",
|
||||
// map[string]interface{}{
|
||||
// "value": 1
|
||||
// "result": &dest,
|
||||
// })
|
||||
//
|
||||
// You can use {} in the asm string (which expands to a register) to set the
|
||||
// return value.
|
||||
func AsmFull(asm string, regs map[string]interface{}) uintptr
|
||||
|
||||
// DisableInterrupts disables all interrupts, and returns the old interrupt
|
||||
// state.
|
||||
func DisableInterrupts() uintptr {
|
||||
// Note: this can be optimized with a CSRRW instruction, which atomically
|
||||
// swaps the value and returns the old value.
|
||||
mask := MIE.Get()
|
||||
MIE.Set(0)
|
||||
return mask
|
||||
}
|
||||
|
||||
// EnableInterrupts enables all interrupts again. The value passed in must be
|
||||
// the mask returned by DisableInterrupts.
|
||||
func EnableInterrupts(mask uintptr) {
|
||||
MIE.Set(mask)
|
||||
}
|
||||
|
||||
@@ -5,16 +5,14 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// This example assumes that the button is connected to pin 8. Change the value
|
||||
// below to use a different pin.
|
||||
const (
|
||||
led = machine.LED
|
||||
button = machine.Pin(8)
|
||||
button = machine.BUTTON
|
||||
)
|
||||
|
||||
func main() {
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
button.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
|
||||
for {
|
||||
if button.Get() {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// +build circuitplay_express
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
const (
|
||||
buttonMode = machine.PinInputPulldown
|
||||
buttonPinChange = machine.PinFalling
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
// +build pca10040
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
const (
|
||||
buttonMode = machine.PinInputPullup
|
||||
buttonPinChange = machine.PinRising
|
||||
)
|
||||
@@ -0,0 +1,52 @@
|
||||
package main
|
||||
|
||||
// This example demonstrates how to use pin change interrupts.
|
||||
//
|
||||
// This is only an example and should not be copied directly in any serious
|
||||
// circuit, because it lacks an important feature: debouncing.
|
||||
// See: https://en.wikipedia.org/wiki/Switch#Contact_bounce
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"runtime/volatile"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
button = machine.BUTTON
|
||||
led = machine.LED
|
||||
)
|
||||
|
||||
func main() {
|
||||
var lightLed volatile.Register8
|
||||
lightLed.Set(0)
|
||||
|
||||
// Configure the LED, defaulting to on (usually setting the pin to low will
|
||||
// turn the LED on).
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
led.Low()
|
||||
|
||||
// Make sure the pin is configured as a pullup to avoid floating inputs.
|
||||
// Pullup works for most buttons, as most buttons short to ground when
|
||||
// pressed.
|
||||
button.Configure(machine.PinConfig{Mode: buttonMode})
|
||||
|
||||
// Set an interrupt on this pin.
|
||||
err := button.SetInterrupt(buttonPinChange, func(machine.Pin) {
|
||||
if lightLed.Get() != 0 {
|
||||
lightLed.Set(0)
|
||||
led.Low()
|
||||
} else {
|
||||
lightLed.Set(1)
|
||||
led.High()
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
println("could not configure pin interrupt:", err.Error())
|
||||
}
|
||||
|
||||
// Make sure the program won't exit.
|
||||
for {
|
||||
time.Sleep(time.Hour)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
const (
|
||||
buttonMode = machine.PinInput
|
||||
buttonPinChange = machine.PinFalling
|
||||
)
|
||||
+16
-6
@@ -8,9 +8,9 @@ import (
|
||||
// This example assumes that an RGB LED is connected to pins 3, 5 and 6 on an Arduino.
|
||||
// Change the values below to use different pins.
|
||||
const (
|
||||
redPin = 3
|
||||
greenPin = 5
|
||||
bluePin = 6
|
||||
redPin = machine.D3
|
||||
greenPin = machine.D5
|
||||
bluePin = machine.D6
|
||||
)
|
||||
|
||||
// cycleColor is just a placeholder until math/rand or some equivalent is working.
|
||||
@@ -28,13 +28,16 @@ func main() {
|
||||
machine.InitPWM()
|
||||
|
||||
red := machine.PWM{redPin}
|
||||
red.Configure()
|
||||
err := red.Configure()
|
||||
checkError(err, "failed to configure red pin")
|
||||
|
||||
green := machine.PWM{greenPin}
|
||||
green.Configure()
|
||||
err = green.Configure()
|
||||
checkError(err, "failed to configure green pin")
|
||||
|
||||
blue := machine.PWM{bluePin}
|
||||
blue.Configure()
|
||||
err = blue.Configure()
|
||||
checkError(err, "failed to configure blue pin")
|
||||
|
||||
var rc uint8
|
||||
var gc uint8 = 20
|
||||
@@ -52,3 +55,10 @@ func main() {
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
}
|
||||
}
|
||||
|
||||
func checkError(err error, msg string) {
|
||||
if err != nil {
|
||||
print(msg, ": ", err.Error())
|
||||
println()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package bytealg
|
||||
|
||||
const (
|
||||
// Index can search any valid length of string.
|
||||
|
||||
MaxLen = int(-1) >> 31
|
||||
MaxBruteForce = MaxLen
|
||||
)
|
||||
|
||||
// Compare two byte slices.
|
||||
// Returns -1 if the first differing byte is lower in a, or 1 if the first differing byte is greater in b.
|
||||
// If the byte slices are equal, returns 0.
|
||||
// If the lengths are different and there are no differing bytes, compares based on length.
|
||||
func Compare(a, b []byte) int {
|
||||
// Compare for differing bytes.
|
||||
for i := 0; i < len(a) && i < len(b); i++ {
|
||||
switch {
|
||||
case a[0] < b[0]:
|
||||
return -1
|
||||
case a[0] > b[0]:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// Compare lengths.
|
||||
switch {
|
||||
case len(a) > len(b):
|
||||
return 1
|
||||
case len(a) < len(b):
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Count the number of instances of a byte in a slice.
|
||||
func Count(b []byte, c byte) int {
|
||||
// Use a simple implementation, as there is no intrinsic that does this like we want.
|
||||
n := 0
|
||||
for _, v := range b {
|
||||
if v == c {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Count the number of instances of a byte in a string.
|
||||
func CountString(s string, c byte) int {
|
||||
// Use a simple implementation, as there is no intrinsic that does this like we want.
|
||||
// Currently, the compiler does not generate zero-copy byte-string conversions, so this needs to be seperate from Count.
|
||||
n := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == c {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Cutover is not reachable in TinyGo, but must exist as it is referenced.
|
||||
func Cutover(n int) int {
|
||||
// Setting MaxLen and MaxBruteForce should force a different path to be taken.
|
||||
// This should never be called.
|
||||
panic("cutover is unreachable")
|
||||
}
|
||||
|
||||
// Equal checks if two byte slices are equal.
|
||||
// It is equivalent to bytes.Equal.
|
||||
func Equal(a, b []byte) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, v := range a {
|
||||
if v != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Index finds the base index of the first instance of the byte sequence b in a.
|
||||
// If a does not contain b, this returns -1.
|
||||
func Index(a, b []byte) int {
|
||||
for i := 0; i <= len(a)-len(b); i++ {
|
||||
if Equal(a[i:i+len(b)], b) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Index finds the index of the first instance of the specified byte in the slice.
|
||||
// If the byte is not found, this returns -1.
|
||||
func IndexByte(b []byte, c byte) int {
|
||||
for i, v := range b {
|
||||
if v == c {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Index finds the index of the first instance of the specified byte in the string.
|
||||
// If the byte is not found, this returns -1.
|
||||
func IndexByteString(s string, c byte) int {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == c {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Index finds the base index of the first instance of a substring in a string.
|
||||
// If the substring is not found, this returns -1.
|
||||
func IndexString(str, sub string) int {
|
||||
for i := 0; i <= len(str)-len(sub); i++ {
|
||||
if str[i:i+len(sub)] == sub {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
@@ -68,8 +68,8 @@ func (s *Stack) Pop() *Task {
|
||||
t := s.top
|
||||
if t != nil {
|
||||
s.top = t.Next
|
||||
t.Next = nil
|
||||
}
|
||||
t.Next = nil
|
||||
return t
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ package task
|
||||
|
||||
import "unsafe"
|
||||
|
||||
const stackSize = 256
|
||||
const stackSize = 1024
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see scheduler_avr.S that relies on the
|
||||
|
||||
@@ -7,21 +7,39 @@ func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
// Digital pins, marked as plain numbers on the board.
|
||||
const (
|
||||
D0 = PD0 // RX
|
||||
D1 = PD1 // TX
|
||||
D2 = PD2
|
||||
D3 = PD3
|
||||
D4 = PD4
|
||||
D5 = PD5
|
||||
D6 = PD6
|
||||
D7 = PD7
|
||||
D8 = PB0
|
||||
D9 = PB1
|
||||
D10 = PB2
|
||||
D11 = PB3
|
||||
D12 = PB4
|
||||
D13 = PB5
|
||||
)
|
||||
|
||||
// LED on the Arduino
|
||||
const LED Pin = 13
|
||||
const LED Pin = D13
|
||||
|
||||
// ADC on the Arduino
|
||||
const (
|
||||
ADC0 Pin = 0
|
||||
ADC1 Pin = 1
|
||||
ADC2 Pin = 2
|
||||
ADC3 Pin = 3
|
||||
ADC4 Pin = 4 // Used by TWI for SDA
|
||||
ADC5 Pin = 5 // Used by TWI for SCL
|
||||
ADC0 Pin = PC0
|
||||
ADC1 Pin = PC1
|
||||
ADC2 Pin = PC2
|
||||
ADC3 Pin = PC3
|
||||
ADC4 Pin = PC4 // Used by TWI for SDA
|
||||
ADC5 Pin = PC5 // Used by TWI for SCL
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART_TX_PIN Pin = 1
|
||||
UART_RX_PIN Pin = 0
|
||||
UART_TX_PIN Pin = PD1
|
||||
UART_RX_PIN Pin = PD0
|
||||
)
|
||||
|
||||
@@ -7,21 +7,39 @@ func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
// Digital pins.
|
||||
const (
|
||||
D0 = PD0 // RX0
|
||||
D1 = PD1 // TX1
|
||||
D2 = PD2
|
||||
D3 = PD3
|
||||
D4 = PD4
|
||||
D5 = PD5
|
||||
D6 = PD6
|
||||
D7 = PD7
|
||||
D8 = PB0
|
||||
D9 = PB1
|
||||
D10 = PB2
|
||||
D11 = PB3
|
||||
D12 = PB4
|
||||
D13 = PB5
|
||||
)
|
||||
|
||||
// LED on the Arduino
|
||||
const LED Pin = 13
|
||||
const LED Pin = D13
|
||||
|
||||
// ADC on the Arduino
|
||||
const (
|
||||
ADC0 Pin = 0
|
||||
ADC1 Pin = 1
|
||||
ADC2 Pin = 2
|
||||
ADC3 Pin = 3
|
||||
ADC4 Pin = 4 // Used by TWI for SDA
|
||||
ADC5 Pin = 5 // Used by TWI for SCL
|
||||
ADC0 Pin = PC0
|
||||
ADC1 Pin = PC1
|
||||
ADC2 Pin = PC2
|
||||
ADC3 Pin = PC3
|
||||
ADC4 Pin = PC4 // Used by TWI for SDA
|
||||
ADC5 Pin = PC5 // Used by TWI for SCL
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART_TX_PIN Pin = 1
|
||||
UART_RX_PIN Pin = 0
|
||||
UART_TX_PIN Pin = PD1
|
||||
UART_RX_PIN Pin = PD0
|
||||
)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// +build avr,atmega328p arduino arduino_nano
|
||||
|
||||
package machine
|
||||
|
||||
const (
|
||||
// Note: start at port B because there is no port A.
|
||||
portB Pin = iota * 8
|
||||
portC
|
||||
portD
|
||||
)
|
||||
|
||||
const (
|
||||
PB0 = portB + 0
|
||||
PB1 = portB + 1
|
||||
PB2 = portB + 2
|
||||
PB3 = portB + 3
|
||||
PB4 = portB + 4
|
||||
PB5 = portB + 5
|
||||
PB6 = portB + 6
|
||||
PB7 = portB + 7
|
||||
PC0 = portC + 0
|
||||
PC1 = portC + 1
|
||||
PC2 = portC + 2
|
||||
PC3 = portC + 3
|
||||
PC4 = portC + 4
|
||||
PC5 = portC + 5
|
||||
PC6 = portC + 6
|
||||
PC7 = portC + 7
|
||||
PD0 = portD + 0
|
||||
PD1 = portD + 1
|
||||
PD2 = portD + 2
|
||||
PD3 = portD + 3
|
||||
PD4 = portD + 4
|
||||
PD5 = portD + 5
|
||||
PD6 = portD + 6
|
||||
PD7 = portD + 7
|
||||
)
|
||||
@@ -1,9 +1,7 @@
|
||||
// +build sam,atsamd51,feather_m4
|
||||
// +build feather_m4
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
|
||||
@@ -47,13 +45,11 @@ const (
|
||||
USBCDC_DP_PIN = PA25
|
||||
)
|
||||
|
||||
// UART1 pins
|
||||
const (
|
||||
UART_TX_PIN = D1
|
||||
UART_RX_PIN = D0
|
||||
)
|
||||
|
||||
// UART2 pins
|
||||
const (
|
||||
UART2_TX_PIN = A4
|
||||
UART2_RX_PIN = A5
|
||||
@@ -65,14 +61,6 @@ const (
|
||||
SCL_PIN = D21 // SCL: SERCOM2/PAD[1]
|
||||
)
|
||||
|
||||
// I2C on the Feather M4.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = D25 // SCK: SERCOM1/PAD[1]
|
||||
@@ -80,14 +68,6 @@ const (
|
||||
SPI0_MISO_PIN = D23 // MISO: SERCOM1/PAD[2]
|
||||
)
|
||||
|
||||
// SPI on the Feather M4.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Adafruit Feather M4"
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// +build sam,atsamd51,feather_m4
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM5_USART_INT,
|
||||
SERCOM: 5,
|
||||
}
|
||||
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART_INT,
|
||||
SERCOM: 0,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, UART1.handleInterrupt)
|
||||
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART2.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the Feather M4.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the Feather M4.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
@@ -37,8 +37,8 @@ const (
|
||||
|
||||
const (
|
||||
// TODO: figure out the pin numbers for these.
|
||||
UART_TX_PIN = NoPin
|
||||
UART_RX_PIN = NoPin
|
||||
UART_TX_PIN = D1
|
||||
UART_RX_PIN = D0
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
// +build sam,atsamd51,itsybitsy_m4
|
||||
// +build itsybitsy_m4
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
|
||||
@@ -51,30 +49,17 @@ const (
|
||||
UART_RX_PIN = D0
|
||||
)
|
||||
|
||||
// UART1 var is on SERCOM3, defined in atsamd51.go
|
||||
|
||||
// UART2 pins
|
||||
const (
|
||||
UART2_TX_PIN = A4
|
||||
UART2_RX_PIN = D2
|
||||
)
|
||||
|
||||
// UART2 var is on SERCOM0, defined in atsamd51.go
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = PA12 // SDA: SERCOM2/PAD[0]
|
||||
SCL_PIN = PA13 // SCL: SERCOM2/PAD[1]
|
||||
)
|
||||
|
||||
// I2C on the ItsyBitsy M4.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = PA01 // SCK: SERCOM1/PAD[1]
|
||||
@@ -82,14 +67,6 @@ const (
|
||||
SPI0_MISO_PIN = PB23 // MISO: SERCOM1/PAD[3]
|
||||
)
|
||||
|
||||
// SPI on the ItsyBitsy M4.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Adafruit ItsyBitsy M4"
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// +build sam,atsamd51,itsybitsy_m4
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM3_USART_INT,
|
||||
SERCOM: 3,
|
||||
}
|
||||
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART_INT,
|
||||
SERCOM: 0,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM3_2, UART1.handleInterrupt)
|
||||
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART2.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the ItsyBitsy M4.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the ItsyBitsy M4.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
@@ -1,9 +1,7 @@
|
||||
// +build sam,atsamd51,metro_m4_airlift
|
||||
// +build metro_m4_airlift
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
|
||||
@@ -53,7 +51,10 @@ const (
|
||||
UART_RX_PIN = D0
|
||||
)
|
||||
|
||||
// Note: UART1 is on SERCOM3, defined in machine_atsamd51.go
|
||||
const (
|
||||
UART2_TX_PIN = PA04
|
||||
UART2_RX_PIN = PA07
|
||||
)
|
||||
|
||||
const (
|
||||
NINA_CS = PA15
|
||||
@@ -66,23 +67,12 @@ const (
|
||||
NINA_RTS = PB23
|
||||
)
|
||||
|
||||
// UART2 is on SERCOM0, defined in machine_atsamd51.go, and connects to the
|
||||
// onboard ESP32-WROOM chip.
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = PB02 // SDA: SERCOM5/PAD[0]
|
||||
SCL_PIN = PB03 // SCL: SERCOM5/PAD[1]
|
||||
)
|
||||
|
||||
// I2C on the Metro M4.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM5_I2CM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = PA13 // SCK: SERCOM2/PAD[1]
|
||||
@@ -94,29 +84,12 @@ const (
|
||||
NINA_SCK = SPI0_SCK_PIN
|
||||
)
|
||||
|
||||
// SPI on the Metro M4.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM2_SPIM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
NINA_SPI = SPI0
|
||||
)
|
||||
|
||||
const (
|
||||
SPI1_SCK_PIN = D12 // MISO: SERCOM1/PAD[1]
|
||||
SPI1_MOSI_PIN = D11 // MOSI: SERCOM1/PAD[3]
|
||||
SPI1_MISO_PIN = D13 // SCK: SERCOM1/PAD[0]
|
||||
)
|
||||
|
||||
// SPI1 on the Metro M4 on pins 11,12,13
|
||||
var (
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Adafruit Metro M4 Airlift Lite"
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// +build sam,atsamd51,metro_m4_airlift
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM3_USART_INT,
|
||||
SERCOM: 3,
|
||||
}
|
||||
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART_INT,
|
||||
SERCOM: 0,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM3_2, UART1.handleInterrupt)
|
||||
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART2.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the Metro M4.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM5_I2CM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the Metro M4.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM2_SPIM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
NINA_SPI = SPI0
|
||||
)
|
||||
|
||||
// SPI1 on the Metro M4 on pins 11,12,13
|
||||
var (
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
@@ -1,9 +1,7 @@
|
||||
// +build sam,atsamd51,pybadge
|
||||
// +build pybadge
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
|
||||
@@ -80,30 +78,17 @@ const (
|
||||
UART_RX_PIN = D0
|
||||
)
|
||||
|
||||
// UART1 var is on SERCOM3, defined in atsamd51.go
|
||||
|
||||
// UART2 pins
|
||||
const (
|
||||
UART2_TX_PIN = A4
|
||||
UART2_RX_PIN = A5
|
||||
)
|
||||
|
||||
// UART2 var is on SERCOM0, defined in atsamd51.go
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = PA12 // SDA: SERCOM2/PAD[0]
|
||||
SCL_PIN = PA13 // SCL: SERCOM2/PAD[1]
|
||||
)
|
||||
|
||||
// I2C on the ItsyBitsy M4.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = PA17 // SCK: SERCOM1/PAD[1]
|
||||
@@ -111,14 +96,6 @@ const (
|
||||
SPI0_MISO_PIN = PB22 // MISO: SERCOM1/PAD[2]
|
||||
)
|
||||
|
||||
// SPI on the PyBadge.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
// TFT SPI pins
|
||||
const (
|
||||
SPI1_SCK_PIN = PB13 // SCK: SERCOM4/PAD[1]
|
||||
@@ -126,14 +103,6 @@ const (
|
||||
SPI1_MISO_PIN = NoPin
|
||||
)
|
||||
|
||||
// TFT SPI on the PyBadge.
|
||||
var (
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM4_SPIM,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Adafruit pyBadge M4"
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// +build sam,atsamd51,pybadge
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM5_USART_INT,
|
||||
SERCOM: 5,
|
||||
}
|
||||
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART_INT,
|
||||
SERCOM: 0,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, UART1.handleInterrupt)
|
||||
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART2.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the ItsyBitsy M4.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the PyBadge.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
// TFT SPI on the PyBadge.
|
||||
var (
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM4_SPIM,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,147 @@
|
||||
// +build sam,atsamd51,pygamer
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 = PB17 // UART0 RX/PWM available
|
||||
D1 = PB16 // UART0 TX/PWM available
|
||||
D2 = PB03
|
||||
D3 = PB02
|
||||
D4 = PA14 // PWM available
|
||||
D5 = PA16 // PWM available
|
||||
D6 = PA18 // PWM available
|
||||
D7 = PB14 // CS for microSD card slot
|
||||
D8 = PA15 // built-in neopixel
|
||||
D9 = PA19 // PWM available
|
||||
D10 = PA20 // can be used for PWM or UART1 TX
|
||||
D11 = PA21 // can be used for PWM or UART1 RX
|
||||
D12 = PA22 // PWM available
|
||||
D13 = PA23 // PWM available
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 = PA02 // ADC/AIN[0]
|
||||
A1 = PA05 // ADC/AIN[2]
|
||||
A2 = PB08 // ADC/AIN[3]
|
||||
A3 = PB09 // ADC/AIN[4]
|
||||
A4 = PA04 // ADC/AIN[5]
|
||||
A5 = PA06 // ADC/AIN[6]
|
||||
A6 = PB01 // ADC/AIN[12]/VMEAS
|
||||
A7 = PB04 // ADC/AIN[6]/LIGHT
|
||||
A8 = D2 // ADC/AIN[14]
|
||||
A9 = D3 // ADC/AIN[15]
|
||||
)
|
||||
|
||||
const (
|
||||
LED = D13
|
||||
NEOPIXELS = D8
|
||||
|
||||
SD_CS = D7
|
||||
|
||||
LIGHTSENSOR = A7
|
||||
|
||||
BUTTON_LATCH = PB00
|
||||
BUTTON_OUT = PB30
|
||||
BUTTON_CLK = PB31
|
||||
|
||||
JOYY = PB06
|
||||
JOYX = PB07
|
||||
|
||||
TFT_DC = PB05
|
||||
TFT_CS = PB12
|
||||
TFT_RST = PA00
|
||||
TFT_LITE = PA01
|
||||
|
||||
SPEAKER_ENABLE = PA27
|
||||
)
|
||||
|
||||
const (
|
||||
BUTTON_SELECT_MASK = 16
|
||||
BUTTON_START_MASK = 32
|
||||
BUTTON_A_MASK = 64
|
||||
BUTTON_B_MASK = 128
|
||||
)
|
||||
|
||||
// UART0 aka USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
USBCDC_DP_PIN = PA25
|
||||
)
|
||||
|
||||
// UART1 pins
|
||||
const (
|
||||
UART_TX_PIN = D1
|
||||
UART_RX_PIN = D0
|
||||
)
|
||||
|
||||
// UART1 var is on SERCOM3, defined in atsamd51.go
|
||||
|
||||
// UART2 pins
|
||||
const (
|
||||
UART2_TX_PIN = A4
|
||||
UART2_RX_PIN = A5
|
||||
)
|
||||
|
||||
// UART2 var is on SERCOM0, defined in atsamd51.go
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = PA12 // SDA: SERCOM2/PAD[0]
|
||||
SCL_PIN = PA13 // SCL: SERCOM2/PAD[1]
|
||||
)
|
||||
|
||||
// I2C on the PyGamer.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = PA17 // SCK: SERCOM1/PAD[1]
|
||||
SPI0_MOSI_PIN = PB23 // MOSI: SERCOM1/PAD[3]
|
||||
SPI0_MISO_PIN = PB22 // MISO: SERCOM1/PAD[2]
|
||||
)
|
||||
|
||||
// SPI on the PyGamer.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
// TFT SPI pins
|
||||
const (
|
||||
SPI1_SCK_PIN = PB13 // SCK: SERCOM4/PAD[1]
|
||||
SPI1_MOSI_PIN = PB15 // MOSI: SERCOM4/PAD[3]
|
||||
SPI1_MISO_PIN = NoPin
|
||||
)
|
||||
|
||||
// TFT SPI on the PyGamer.
|
||||
var (
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM4_SPIM,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Adafruit pyGamer M4"
|
||||
usb_STRING_MANUFACTURER = "Adafruit"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x239A
|
||||
usb_PID uint16 = 0x8033
|
||||
)
|
||||
@@ -1,11 +1,7 @@
|
||||
// +build sam,atsamd51,pyportal
|
||||
// +build pyportal
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
)
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
|
||||
@@ -104,10 +100,10 @@ const (
|
||||
USBCDC_DP_PIN = PA25
|
||||
)
|
||||
|
||||
// TODO: add configuration for UART on SERCOM4 that is connected to TX/RX of ESP32
|
||||
// UART1 aka NINA_TX/NINA_RX
|
||||
const (
|
||||
UART_TX_PIN = NoPin
|
||||
UART_RX_PIN = NoPin
|
||||
UART_TX_PIN = D1
|
||||
UART_RX_PIN = D0
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
@@ -116,14 +112,6 @@ const (
|
||||
SCL_PIN = PB03 // SCL: SERCOM2/PAD[1]
|
||||
)
|
||||
|
||||
// I2C on the PyPortal.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM5_I2CM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = PA13 // SCK: SERCOM1/PAD[1]
|
||||
@@ -135,15 +123,6 @@ const (
|
||||
NINA_SCK = SPI0_SCK_PIN
|
||||
)
|
||||
|
||||
// SPI on the PyPortal.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM2_SPIM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
NINA_SPI = SPI0
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Adafruit PyPortal M4"
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// +build sam,atsamd51,pyportal
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM4_USART_INT,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM4_2, UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the PyPortal.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM5_I2CM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the PyPortal.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM2_SPIM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
NINA_SPI = SPI0
|
||||
)
|
||||
@@ -0,0 +1,392 @@
|
||||
// +build wioterminal
|
||||
|
||||
package machine
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
|
||||
const (
|
||||
ADC0 = A0
|
||||
ADC1 = A1
|
||||
ADC2 = A2
|
||||
ADC3 = A3
|
||||
ADC4 = A4
|
||||
ADC5 = A5
|
||||
ADC6 = A6
|
||||
ADC7 = A7
|
||||
ADC8 = A8
|
||||
|
||||
LED = PIN_LED
|
||||
BUTTON = BUTTON_1
|
||||
)
|
||||
|
||||
const (
|
||||
// https://github.com/Seeed-Studio/ArduinoCore-samd/blob/master/variants/wio_terminal/variant.h
|
||||
|
||||
// LEDs
|
||||
PIN_LED_13 = PA15
|
||||
PIN_LED_RXL = PA15
|
||||
PIN_LED_TXL = PA15
|
||||
PIN_LED = PIN_LED_13
|
||||
PIN_LED2 = PIN_LED_RXL
|
||||
PIN_LED3 = PIN_LED_TXL
|
||||
LED_BUILTIN = PIN_LED_13
|
||||
PIN_NEOPIXEL = PA15
|
||||
|
||||
//Digital PINs
|
||||
D0 = PB08
|
||||
D1 = PB09
|
||||
D2 = PA07
|
||||
D3 = PB04
|
||||
D4 = PB05
|
||||
D5 = PB06
|
||||
D6 = PA04
|
||||
D7 = PB07
|
||||
D8 = PA06
|
||||
|
||||
//Analog PINs
|
||||
A0 = PB08 // ADC/AIN[0]
|
||||
A1 = PB09 // ADC/AIN[2]
|
||||
A2 = PA07 // ADC/AIN[3]
|
||||
A3 = PB04 // ADC/AIN[4]
|
||||
A4 = PB05 // ADC/AIN[5]
|
||||
A5 = PB06 // ADC/AIN[10]
|
||||
A6 = PA04 // ADC/AIN[10]
|
||||
A7 = PB07 // ADC/AIN[10]
|
||||
A8 = PA06 // ADC/AIN[10]
|
||||
|
||||
// 3.3V || 5V
|
||||
// BCM2 || 5V
|
||||
// BCM3 || GND
|
||||
// BCM4 || BCM14
|
||||
// GND || BCM15
|
||||
// BCM17 || BCM18
|
||||
// BCM27 || GND
|
||||
// BCM22 || BCM23
|
||||
// GND || BCM24
|
||||
// BCM10 || GND
|
||||
// BCM9 || BCM25
|
||||
// BCM11 || BCM8
|
||||
// GND || BCM7
|
||||
// BCM0 || BCM1
|
||||
// BCM5 || GND
|
||||
// BCM6 || BCM12
|
||||
// BCM13 || GND
|
||||
// BCM19 || BCM16
|
||||
// BCM26 || BCM20
|
||||
// GND || BCM21
|
||||
|
||||
//PIN DEFINE FOR RPI
|
||||
BCM0 = PA13 // I2C Wire1
|
||||
BCM1 = PA12 // I2C Wire1
|
||||
BCM2 = PA17 // I2C Wire2
|
||||
BCM3 = PA16 // I2C Wire2
|
||||
BCM4 = PB14 // GCLK
|
||||
BCM5 = PB12 // GCLK
|
||||
BCM6 = PB13 // GCLK
|
||||
BCM7 = PA05 // DAC1
|
||||
BCM8 = PB01 // SPI SS
|
||||
BCM9 = PB00 // SPI MISO
|
||||
BCM10 = PB02 // SPI MOSI
|
||||
BCM11 = PB03 // SPI SCK
|
||||
BCM12 = PB06
|
||||
BCM13 = PA07
|
||||
BCM14 = PB27 // UART Serial1
|
||||
BCM15 = PB26 // UART Serial1
|
||||
BCM16 = PB07
|
||||
BCM17 = PA02 // DAC0
|
||||
BCM18 = PB28 // FPC Digital & AD pins
|
||||
BCM19 = PA20 // WIO_IR
|
||||
BCM20 = PA21 // I2S SDO
|
||||
BCM21 = PA22 // I2S SDI
|
||||
BCM22 = PB09
|
||||
BCM23 = PA07
|
||||
BCM24 = PB04
|
||||
BCM25 = PB05
|
||||
BCM26 = PA06
|
||||
BCM27 = PB08
|
||||
|
||||
// FPC NEW DEFINE
|
||||
FPC1 = PB28 // FPC Digital & AD pins
|
||||
FPC2 = PB17
|
||||
FPC3 = PB29
|
||||
FPC4 = PA14
|
||||
FPC5 = PC01
|
||||
FPC6 = PC02
|
||||
FPC7 = PC03
|
||||
FPC8 = PC04
|
||||
FPC9 = PC31
|
||||
FPC10 = PD00
|
||||
|
||||
// RPI Analog RPIs
|
||||
RPI_A0 = PB08
|
||||
RPI_A1 = PB09
|
||||
RPI_A2 = PA07
|
||||
RPI_A3 = PB04
|
||||
RPI_A4 = PB05
|
||||
RPI_A5 = PB06
|
||||
RPI_A6 = PA04
|
||||
RPI_A7 = PB07
|
||||
RPI_A8 = PA06
|
||||
|
||||
PIN_DAC0 = PA02
|
||||
PIN_DAC1 = PA05
|
||||
|
||||
DAC0 = PIN_DAC0
|
||||
DAC1 = PIN_DAC1
|
||||
|
||||
// FPO Analog RPIs
|
||||
//FPC_A7 = FPC_D7
|
||||
//FPC_A8 = FPC_D8
|
||||
//FPC_A9 = FPC_D9
|
||||
//FPC_A11 = FPC_D11
|
||||
//FPC_A12 = FPC_D12
|
||||
//FPC_A13 = FPC_D13
|
||||
|
||||
// USB
|
||||
PIN_USB_DM = PA24
|
||||
PIN_USB_DP = PA25
|
||||
PIN_USB_HOST_ENABLE = PA27
|
||||
|
||||
// BUTTON
|
||||
BUTTON_1 = PC26
|
||||
BUTTON_2 = PC27
|
||||
BUTTON_3 = PC28
|
||||
WIO_KEY_A = PC26
|
||||
WIO_KEY_B = PC27
|
||||
WIO_KEY_C = PC28
|
||||
|
||||
// SWITCH
|
||||
SWITCH_X = PD20
|
||||
SWITCH_Y = PD12
|
||||
SWITCH_Z = PD09
|
||||
SWITCH_B = PD08
|
||||
SWITCH_U = PD10
|
||||
|
||||
WIO_5S_UP = PD20
|
||||
WIO_5S_LEFT = PD12
|
||||
WIO_5S_RIGHT = PD09
|
||||
WIO_5S_DOWN = PD08
|
||||
WIO_5S_PRESS = PD10
|
||||
|
||||
// IRQ0 : RTL8720D
|
||||
IRQ0 = PC20
|
||||
|
||||
// BUZZER_CTR
|
||||
BUZZER_CTR = PD11
|
||||
WIO_BUZZER = PD11
|
||||
|
||||
// MIC_INPUT
|
||||
MIC_INPUT = PC30
|
||||
WIO_MIC = PC30
|
||||
|
||||
// GCLK
|
||||
GCLK0 = PB14
|
||||
GCLK1 = PB12
|
||||
GCLK2 = PB13
|
||||
|
||||
// Serial interfaces
|
||||
// Serial1
|
||||
PIN_SERIAL1_RX = PB27
|
||||
PIN_SERIAL1_TX = PB26
|
||||
|
||||
// Serial2 : RTL8720D
|
||||
PIN_SERIAL2_RX = PC23
|
||||
PIN_SERIAL2_TX = PC22
|
||||
|
||||
// Wire Interfaces
|
||||
// I2C Wire2
|
||||
// I2C1
|
||||
PIN_WIRE_SDA = PA17
|
||||
PIN_WIRE_SCL = PA16
|
||||
SDA = PIN_WIRE_SDA
|
||||
SCL = PIN_WIRE_SCL
|
||||
|
||||
// I2C Wire1
|
||||
// I2C0 : LIS3DHTR and ATECC608
|
||||
PIN_WIRE1_SDA = PA13
|
||||
PIN_WIRE1_SCL = PA12
|
||||
|
||||
SDA1 = PIN_WIRE1_SDA
|
||||
SCL1 = PIN_WIRE1_SCL
|
||||
|
||||
PIN_GYROSCOPE_WIRE_SDA = PIN_WIRE1_SDA
|
||||
PIN_GYROSCOPE_WIRE_SCL = PIN_WIRE1_SCL
|
||||
GYROSCOPE_INT1 = PC21
|
||||
|
||||
WIO_LIS3DH_SDA = PIN_WIRE1_SDA
|
||||
WIO_LIS3DH_SCL = PIN_WIRE1_SCL
|
||||
WIO_LIS3DH_INT = PC21
|
||||
|
||||
// SPI
|
||||
PIN_SPI_MISO = PB00
|
||||
PIN_SPI_MOSI = PB02
|
||||
PIN_SPI_SCK = PB03
|
||||
PIN_SPI_SS = PB01
|
||||
|
||||
SS = PIN_SPI_SS
|
||||
MOSI = PIN_SPI_MOSI
|
||||
MISO = PIN_SPI_MISO
|
||||
SCK = PIN_SPI_SCK
|
||||
|
||||
// SPI1 RTL8720D_SPI
|
||||
PIN_SPI1_MISO = PC24
|
||||
PIN_SPI1_MOSI = PB24
|
||||
PIN_SPI1_SCK = PB25
|
||||
PIN_SPI1_SS = PC25
|
||||
|
||||
SS1 = PIN_SPI1_SS
|
||||
MOSI1 = PIN_SPI1_MOSI
|
||||
MISO1 = PIN_SPI1_MISO
|
||||
SCK1 = PIN_SPI1_SCK
|
||||
|
||||
// SPI2 SD_SPI
|
||||
PIN_SPI2_MISO = PC18
|
||||
PIN_SPI2_MOSI = PC16
|
||||
PIN_SPI2_SCK = PC17
|
||||
PIN_SPI2_SS = PC19
|
||||
|
||||
SS2 = PIN_SPI2_SS
|
||||
MOSI2 = PIN_SPI2_MOSI
|
||||
MISO2 = PIN_SPI2_MISO
|
||||
SCK2 = PIN_SPI2_SCK
|
||||
|
||||
// SPI3 LCD_SPI
|
||||
PIN_SPI3_MISO = PB18
|
||||
PIN_SPI3_MOSI = PB19
|
||||
PIN_SPI3_SCK = PB20
|
||||
PIN_SPI3_SS = PB21
|
||||
|
||||
SS3 = PIN_SPI3_SS
|
||||
MOSI3 = PIN_SPI3_MOSI
|
||||
MISO3 = PIN_SPI3_MISO
|
||||
SCK3 = PIN_SPI3_SCK
|
||||
|
||||
// Needed for SD library
|
||||
SDCARD_MISO_PIN = PIN_SPI2_MISO
|
||||
SDCARD_MOSI_PIN = PIN_SPI2_MOSI
|
||||
SDCARD_SCK_PIN = PIN_SPI2_SCK
|
||||
SDCARD_SS_PIN = PIN_SPI2_SS
|
||||
SDCARD_DET_PIN = PD21
|
||||
|
||||
LCD_MISO_PIN = PIN_SPI3_MISO
|
||||
LCD_MOSI_PIN = PIN_SPI3_MOSI
|
||||
LCD_SCK_PIN = PIN_SPI3_SCK
|
||||
LCD_SS_PIN = PIN_SPI3_SS
|
||||
LCD_DC = PC06
|
||||
LCD_RESET = PC07
|
||||
LCD_BACKLIGHT = PC05
|
||||
|
||||
// 4 WIRE LCD TOUCH
|
||||
LCD_XL = PC10
|
||||
LCD_YU = PC11
|
||||
LCD_XR = PC12
|
||||
LCD_YD = PC13
|
||||
|
||||
// Needed for RTL8720D
|
||||
RTL8720D_MISO_PIN = PIN_SPI1_MISO
|
||||
RTL8720D_MOSI_PIN = PIN_SPI1_MOSI
|
||||
RTL8720D_SCK_PIN = PIN_SPI1_SCK
|
||||
RTL8720D_SS_PIN = PIN_SPI1_SS
|
||||
|
||||
//QSPI Pins
|
||||
PIN_QSPI_IO0 = PA08
|
||||
PIN_QSPI_IO1 = PA09
|
||||
PIN_QSPI_IO2 = PA10
|
||||
PIN_QSPI_IO3 = PA11
|
||||
PIN_QSPI_SCK = PB10
|
||||
PIN_QSPI_CS = PB11
|
||||
|
||||
// I2S Interfaces
|
||||
PIN_I2S_FS = PA20
|
||||
PIN_I2S_SCK = PB16
|
||||
PIN_I2S_SDO = PA22
|
||||
PIN_I2S_SDI = PA21
|
||||
|
||||
I2S_LRCLK = PA20
|
||||
I2S_BLCK = PB16
|
||||
I2S_SDOUT = PA22
|
||||
I2S_SDIN = PA21
|
||||
|
||||
// RTL8720D Interfaces
|
||||
RTL8720D_CHIP_PU = PA18
|
||||
RTL8720D_GPIO0 = PA19 // SYNC
|
||||
|
||||
// SWD
|
||||
SWDCLK = PA30
|
||||
SWDIO = PA31
|
||||
SWO = PB30
|
||||
|
||||
// light sensor
|
||||
WIO_LIGHT = PD01
|
||||
|
||||
// ir sensor
|
||||
WIO_IR = PB31
|
||||
|
||||
// OUTPUT_CTR
|
||||
OUTPUT_CTR_5V = PC14
|
||||
OUTPUT_CTR_3V3 = PC15
|
||||
)
|
||||
|
||||
// UART0 aka USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PIN_USB_DM
|
||||
USBCDC_DP_PIN = PIN_USB_DP
|
||||
)
|
||||
|
||||
// UART1 pins
|
||||
const (
|
||||
UART_TX_PIN = PIN_SERIAL1_TX
|
||||
UART_RX_PIN = PIN_SERIAL1_RX
|
||||
)
|
||||
|
||||
// UART2 pins RTL8720D
|
||||
const (
|
||||
UART2_TX_PIN = PIN_SERIAL2_TX
|
||||
UART2_RX_PIN = PIN_SERIAL2_RX
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA0_PIN = PIN_WIRE_SDA // SDA: SERCOM3/PAD[0]
|
||||
SCL0_PIN = PIN_WIRE_SCL // SCL: SERCOM3/PAD[1]
|
||||
|
||||
SDA1_PIN = PIN_WIRE1_SDA // SDA: SERCOM4/PAD[0]
|
||||
SCL1_PIN = PIN_WIRE1_SCL // SCL: SERCOM4/PAD[1]
|
||||
|
||||
SDA_PIN = SDA0_PIN
|
||||
SCL_PIN = SCL0_PIN
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = SCK // SCK: SERCOM5/PAD[1]
|
||||
SPI0_MOSI_PIN = MOSI // MOSI: SERCOM5/PAD[0]
|
||||
SPI0_MISO_PIN = MISO // MISO: SERCOM5/PAD[2]
|
||||
|
||||
// RTL8720D
|
||||
SPI1_SCK_PIN = SCK1 // SCK: SERCOM0/PAD[1]
|
||||
SPI1_MOSI_PIN = MOSI1 // MOSI: SERCOM0/PAD[0]
|
||||
SPI1_MISO_PIN = MISO1 // MISO: SERCOM0/PAD[2]
|
||||
|
||||
// SD
|
||||
SPI2_SCK_PIN = SCK2 // SCK: SERCOM6/PAD[1]
|
||||
SPI2_MOSI_PIN = MOSI2 // MOSI: SERCOM6/PAD[0]
|
||||
SPI2_MISO_PIN = MISO2 // MISO: SERCOM6/PAD[2]
|
||||
|
||||
// LCD
|
||||
SPI3_SCK_PIN = SCK3 // SCK: SERCOM7/PAD[1]
|
||||
SPI3_MOSI_PIN = MOSI3 // MOSI: SERCOM7/PAD[3]
|
||||
SPI3_MISO_PIN = MISO3 // MISO: SERCOM7/PAD[2]
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Seeed Wio Terminal"
|
||||
usb_STRING_MANUFACTURER = "Seeed"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x2886
|
||||
usb_PID uint16 = 0x802D
|
||||
)
|
||||
@@ -0,0 +1,67 @@
|
||||
// +build sam,atsamd51,wioterminal
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM2_USART_INT,
|
||||
SERCOM: 2,
|
||||
}
|
||||
|
||||
// RTL8720D
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM1_USART_INT,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM2_2, UART1.handleInterrupt)
|
||||
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM1_2, UART2.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the Wio Terminal
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM4_I2CM,
|
||||
SERCOM: 4,
|
||||
}
|
||||
|
||||
I2C1 = I2C{
|
||||
Bus: sam.SERCOM4_I2CM,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the Wio Terminal
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM5_SPIM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
|
||||
// RTL8720D
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM0_SPIM,
|
||||
SERCOM: 0,
|
||||
}
|
||||
|
||||
// SD
|
||||
SPI2 = SPI{
|
||||
Bus: sam.SERCOM6_SPIM,
|
||||
SERCOM: 6,
|
||||
}
|
||||
|
||||
// LCD
|
||||
SPI3 = SPI{
|
||||
Bus: sam.SERCOM7_SPIM,
|
||||
SERCOM: 7,
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,126 @@
|
||||
// +build sam,atsamd21,xiao
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 = PA02 // can be used for PWM or DAC
|
||||
D1 = PA04 // PWM available
|
||||
D2 = PA10 // PWM available
|
||||
D3 = PA11 // PWM available
|
||||
D4 = PA08 // can be used for PWM or I2C SDA
|
||||
D5 = PA09 // can be used for PWM or I2C SCL
|
||||
D6 = PB08 // can be used for PWM or UART1 TX
|
||||
D7 = PB09 // can be used for PWM or UART1 RX
|
||||
D8 = PA07 // can be used for PWM or SPI SCK
|
||||
D9 = PA05 // can be used for PWM or SPI MISO
|
||||
D10 = PA06 // can be used for PWM or SPI MOSI
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 = PA02 // ADC/AIN[0]
|
||||
A1 = PA04 // ADC/AIN[4]
|
||||
A2 = PA10 // ADC/AIN[18]
|
||||
A3 = PA11 // ADC/AIN[19]
|
||||
A4 = PA08 // ADC/AIN[16]
|
||||
A5 = PA09 // ADC/AIN[17]
|
||||
A6 = PB08 // ADC/AIN[2]
|
||||
A7 = PB09 // ADC/AIN[3]
|
||||
A8 = PA07 // ADC/AIN[7]
|
||||
A9 = PA05 // ADC/AIN[6]
|
||||
A10 = PA06 // ADC/AIN[5]
|
||||
)
|
||||
|
||||
const (
|
||||
LED = PA17
|
||||
LED_RXL = PA18
|
||||
LED_TXL = PA19
|
||||
LED2 = LED_RXL
|
||||
LED3 = LED_TXL
|
||||
)
|
||||
|
||||
// UART0 aka USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
USBCDC_DP_PIN = PA25
|
||||
)
|
||||
|
||||
// UART1 pins
|
||||
const (
|
||||
UART_TX_PIN = D6
|
||||
UART_RX_PIN = D7
|
||||
)
|
||||
|
||||
// UART1 on the Xiao
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM4_USART,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM4, UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = PA08 // SDA: SERCOM2/PAD[0]
|
||||
SCL_PIN = PA09 // SCL: SERCOM2/PAD[1]
|
||||
)
|
||||
|
||||
// I2C on the Xiao
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = PA07 // SCK: SERCOM0/PAD[3]
|
||||
SPI0_MOSI_PIN = PA06 // MOSI: SERCOM0/PAD[2]
|
||||
SPI0_MISO_PIN = PA05 // MISO: SERCOM0/PAD[1]
|
||||
)
|
||||
|
||||
// SPI on the Xiao
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM0_SPI,
|
||||
SERCOM: 0,
|
||||
}
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
const (
|
||||
I2S_SCK_PIN = PA10
|
||||
I2S_SD_PIN = PA08
|
||||
I2S_WS_PIN = NoPin // TODO: figure out what this is on Xiao
|
||||
)
|
||||
|
||||
// I2S on the Xiao
|
||||
var (
|
||||
I2S0 = I2S{Bus: sam.I2S}
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Seeed XIAO M0"
|
||||
usb_STRING_MANUFACTURER = "Seeed"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x2886
|
||||
usb_PID uint16 = 0x802F
|
||||
)
|
||||
@@ -3,10 +3,11 @@ package machine
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrInvalidInputPin = errors.New("machine: invalid input pin")
|
||||
ErrInvalidOutputPin = errors.New("machine: invalid output pin")
|
||||
ErrInvalidClockPin = errors.New("machine: invalid clock pin")
|
||||
ErrInvalidDataPin = errors.New("machine: invalid data pin")
|
||||
ErrInvalidInputPin = errors.New("machine: invalid input pin")
|
||||
ErrInvalidOutputPin = errors.New("machine: invalid output pin")
|
||||
ErrInvalidClockPin = errors.New("machine: invalid clock pin")
|
||||
ErrInvalidDataPin = errors.New("machine: invalid data pin")
|
||||
ErrNoPinChangeChannel = errors.New("machine: no channel available for pin interrupt")
|
||||
)
|
||||
|
||||
type PinConfig struct {
|
||||
|
||||
@@ -7,72 +7,6 @@ import (
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// InitPWM initializes the registers needed for PWM.
|
||||
func InitPWM() {
|
||||
// use waveform generation
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_WGM00)
|
||||
|
||||
// set timer 0 prescale factor to 64
|
||||
avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00)
|
||||
|
||||
// set timer 1 prescale factor to 64
|
||||
avr.TCCR1B.SetBits(avr.TCCR1B_CS11)
|
||||
|
||||
// put timer 1 in 8-bit phase correct pwm mode
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_WGM10)
|
||||
|
||||
// set timer 2 prescale factor to 64
|
||||
avr.TCCR2B.SetBits(avr.TCCR2B_CS22)
|
||||
|
||||
// configure timer 2 for phase correct pwm (8-bit)
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_WGM20)
|
||||
}
|
||||
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() {
|
||||
if pwm.Pin < 8 {
|
||||
avr.DDRD.SetBits(1 << uint8(pwm.Pin))
|
||||
} else {
|
||||
avr.DDRB.SetBits(1 << uint8(pwm.Pin-8))
|
||||
}
|
||||
}
|
||||
|
||||
// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
|
||||
// 8-bit value ranging from 0 to 255.
|
||||
func (pwm PWM) Set(value uint16) {
|
||||
value8 := uint8(value >> 8)
|
||||
switch pwm.Pin {
|
||||
case 3:
|
||||
// connect pwm to pin on timer 2, channel B
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||
avr.OCR2B.Set(value8) // set pwm duty
|
||||
case 5:
|
||||
// connect pwm to pin on timer 0, channel B
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||
avr.OCR0B.Set(value8) // set pwm duty
|
||||
case 6:
|
||||
// connect pwm to pin on timer 0, channel A
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||
avr.OCR0A.Set(value8) // set pwm duty
|
||||
case 9:
|
||||
// connect pwm to pin on timer 1, channel A
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||
avr.OCR1AL.Set(value8) // set pwm duty
|
||||
case 10:
|
||||
// connect pwm to pin on timer 1, channel B
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||
avr.OCR1BL.Set(value8) // set pwm duty
|
||||
case 11:
|
||||
// connect pwm to pin on timer 2, channel A
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||
avr.OCR2A.Set(value8) // set pwm duty
|
||||
default:
|
||||
panic("Invalid PWM pin")
|
||||
}
|
||||
}
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
type I2CConfig struct {
|
||||
Frequency uint32
|
||||
|
||||
@@ -14,12 +14,58 @@ func CPUFrequency() uint32 {
|
||||
return 20000000
|
||||
}
|
||||
|
||||
const (
|
||||
portA Pin = iota * 8
|
||||
portB
|
||||
portC
|
||||
portD
|
||||
)
|
||||
|
||||
const (
|
||||
PA0 = portA + 0
|
||||
PA1 = portA + 1
|
||||
PA2 = portA + 2
|
||||
PA3 = portA + 3
|
||||
PA4 = portA + 4
|
||||
PA5 = portA + 5
|
||||
PA6 = portA + 6
|
||||
PA7 = portA + 7
|
||||
PB0 = portB + 0
|
||||
PB1 = portB + 1
|
||||
PB2 = portB + 2
|
||||
PB3 = portB + 3
|
||||
PB4 = portB + 4
|
||||
PB5 = portB + 5
|
||||
PB6 = portB + 6
|
||||
PB7 = portB + 7
|
||||
PC0 = portC + 0
|
||||
PC1 = portC + 1
|
||||
PC2 = portC + 2
|
||||
PC3 = portC + 3
|
||||
PC4 = portC + 4
|
||||
PC5 = portC + 5
|
||||
PC6 = portC + 6
|
||||
PC7 = portC + 7
|
||||
PD0 = portD + 0
|
||||
PD1 = portD + 1
|
||||
PD2 = portD + 2
|
||||
PD3 = portD + 3
|
||||
PD4 = portD + 4
|
||||
PD5 = portD + 5
|
||||
PD6 = portD + 6
|
||||
PD7 = portD + 7
|
||||
)
|
||||
|
||||
// getPortMask returns the PORTx register and mask for the pin.
|
||||
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||
if p < 8 {
|
||||
return avr.PORTD, 1 << uint8(p)
|
||||
} else if p < 14 {
|
||||
return avr.PORTB, 1 << uint8(p-8)
|
||||
} else {
|
||||
return avr.PORTC, 1 << uint8(p-14)
|
||||
switch {
|
||||
case p >= PA0 && p <= PA7:
|
||||
return avr.PORTA, 1 << uint8(p-portA)
|
||||
case p >= PB0 && p <= PB7:
|
||||
return avr.PORTB, 1 << uint8(p-portB)
|
||||
case p >= PC0 && p <= PC7:
|
||||
return avr.PORTC, 1 << uint8(p-portC)
|
||||
default:
|
||||
return avr.PORTD, 1 << uint8(p-portD)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,53 +96,32 @@ const (
|
||||
PL7 = portE + 7
|
||||
)
|
||||
|
||||
// Configure sets the pin to input or output.
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
register, _, mask := p.getRegisterPortMask()
|
||||
if config.Mode == PinOutput { // set output bit
|
||||
register.SetBits(mask)
|
||||
} else { // configure input: clear output bit
|
||||
register.ClearBits(mask)
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the current value of a GPIO pin.
|
||||
func (p Pin) Get() bool {
|
||||
_, port, mask := p.getRegisterPortMask()
|
||||
return (port.Get() & mask) > 0
|
||||
}
|
||||
|
||||
// getPortMask returns the PORTx register and mask for the pin.
|
||||
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||
_, port, mask := p.getRegisterPortMask()
|
||||
return port, mask
|
||||
}
|
||||
|
||||
// getRegisterPortMask returns the register, port, and mask for the pin
|
||||
func (p Pin) getRegisterPortMask() (*volatile.Register8, *volatile.Register8, uint8) {
|
||||
switch {
|
||||
case p >= PA0 && p <= PA7:
|
||||
return avr.DDRA, avr.PORTA, 1 << uint8(p-portA)
|
||||
return avr.PORTA, 1 << uint8(p-portA)
|
||||
case p >= PB0 && p <= PB7:
|
||||
return avr.DDRB, avr.PORTB, 1 << uint8(p-portB)
|
||||
return avr.PORTB, 1 << uint8(p-portB)
|
||||
case p >= PC0 && p <= PC7:
|
||||
return avr.DDRC, avr.PORTC, 1 << uint8(p-portC)
|
||||
return avr.PORTC, 1 << uint8(p-portC)
|
||||
case p >= PD0 && p <= PD7:
|
||||
return avr.DDRD, avr.PORTD, 1 << uint8(p-portD)
|
||||
return avr.PORTD, 1 << uint8(p-portD)
|
||||
case p >= PE0 && p <= PE6:
|
||||
return avr.DDRE, avr.PORTE, 1 << uint8(p-portE)
|
||||
return avr.PORTE, 1 << uint8(p-portE)
|
||||
case p >= PF0 && p <= PF7:
|
||||
return avr.DDRF, avr.PORTF, 1 << uint8(p-portF)
|
||||
return avr.PORTF, 1 << uint8(p-portF)
|
||||
case p >= PG0 && p <= PG5:
|
||||
return avr.DDRG, avr.PORTG, 1 << uint8(p-portG)
|
||||
return avr.PORTG, 1 << uint8(p-portG)
|
||||
case p >= PH0 && p <= PH6:
|
||||
return avr.DDRH, avr.PORTH, 1 << uint8(p-portH)
|
||||
return avr.PORTH, 1 << uint8(p-portH)
|
||||
case p >= PJ0 && p <= PJ1:
|
||||
return avr.DDRJ, avr.PORTJ, 1 << uint8(p-portJ)
|
||||
return avr.PORTJ, 1 << uint8(p-portJ)
|
||||
case p >= PK0 && p <= PK7:
|
||||
return avr.DDRK, avr.PORTK, 1 << uint8(p-portK)
|
||||
return avr.PORTK, 1 << uint8(p-portK)
|
||||
case p >= PL0 && p <= PL7:
|
||||
return avr.DDRL, avr.PORTL, 1 << uint8(p-portL)
|
||||
return avr.PORTL, 1 << uint8(p-portL)
|
||||
default:
|
||||
return avr.DDRB, avr.PORTA, 255
|
||||
return avr.PORTA, 255
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,47 +9,82 @@ import (
|
||||
|
||||
const irq_USART0_RX = avr.IRQ_USART_RX
|
||||
|
||||
// Configure sets the pin to input or output.
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
if config.Mode == PinOutput { // set output bit
|
||||
if p < 8 {
|
||||
avr.DDRD.SetBits(1 << uint8(p))
|
||||
} else if p < 14 {
|
||||
avr.DDRB.SetBits(1 << uint8(p-8))
|
||||
} else {
|
||||
avr.DDRC.SetBits(1 << uint8(p-14))
|
||||
}
|
||||
} else { // configure input: clear output bit
|
||||
if p < 8 {
|
||||
avr.DDRD.ClearBits(1 << uint8(p))
|
||||
} else if p < 14 {
|
||||
avr.DDRB.ClearBits(1 << uint8(p-8))
|
||||
} else {
|
||||
avr.DDRC.ClearBits(1 << uint8(p-14))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the current value of a GPIO pin.
|
||||
func (p Pin) Get() bool {
|
||||
if p < 8 {
|
||||
val := avr.PIND.Get() & (1 << uint8(p))
|
||||
return (val > 0)
|
||||
} else if p < 14 {
|
||||
val := avr.PINB.Get() & (1 << uint8(p-8))
|
||||
return (val > 0)
|
||||
} else {
|
||||
val := avr.PINC.Get() & (1 << uint8(p-14))
|
||||
return (val > 0)
|
||||
}
|
||||
}
|
||||
|
||||
// getPortMask returns the PORTx register and mask for the pin.
|
||||
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||
if p < 8 {
|
||||
return avr.PORTD, 1 << uint8(p)
|
||||
} else if p < 14 {
|
||||
return avr.PORTB, 1 << uint8(p-8)
|
||||
} else {
|
||||
return avr.PORTC, 1 << uint8(p-14)
|
||||
switch {
|
||||
case p >= PB0 && p <= PB7: // port B
|
||||
return avr.PORTB, 1 << uint8(p-portB)
|
||||
case p >= PC0 && p <= PC7: // port C
|
||||
return avr.PORTC, 1 << uint8(p-portC)
|
||||
default: // port D
|
||||
return avr.PORTD, 1 << uint8(p-portD)
|
||||
}
|
||||
}
|
||||
|
||||
// InitPWM initializes the registers needed for PWM.
|
||||
func InitPWM() {
|
||||
// use waveform generation
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_WGM00)
|
||||
|
||||
// set timer 0 prescale factor to 64
|
||||
avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00)
|
||||
|
||||
// set timer 1 prescale factor to 64
|
||||
avr.TCCR1B.SetBits(avr.TCCR1B_CS11)
|
||||
|
||||
// put timer 1 in 8-bit phase correct pwm mode
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_WGM10)
|
||||
|
||||
// set timer 2 prescale factor to 64
|
||||
avr.TCCR2B.SetBits(avr.TCCR2B_CS22)
|
||||
|
||||
// configure timer 2 for phase correct pwm (8-bit)
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_WGM20)
|
||||
}
|
||||
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() error {
|
||||
switch pwm.Pin / 8 {
|
||||
case 0: // port B
|
||||
avr.DDRB.SetBits(1 << uint8(pwm.Pin))
|
||||
case 2: // port D
|
||||
avr.DDRD.SetBits(1 << uint8(pwm.Pin-16))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
|
||||
// 8-bit value ranging from 0 to 255.
|
||||
func (pwm PWM) Set(value uint16) {
|
||||
value8 := uint8(value >> 8)
|
||||
switch pwm.Pin {
|
||||
case PD3:
|
||||
// connect pwm to pin on timer 2, channel B
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||
avr.OCR2B.Set(value8) // set pwm duty
|
||||
case PD5:
|
||||
// connect pwm to pin on timer 0, channel B
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||
avr.OCR0B.Set(value8) // set pwm duty
|
||||
case PD6:
|
||||
// connect pwm to pin on timer 0, channel A
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||
avr.OCR0A.Set(value8) // set pwm duty
|
||||
case PB1:
|
||||
// connect pwm to pin on timer 1, channel A
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||
avr.OCR1AL.Set(value8) // set pwm duty
|
||||
case PB2:
|
||||
// connect pwm to pin on timer 1, channel B
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||
avr.OCR1BL.Set(value8) // set pwm duty
|
||||
case PB3:
|
||||
// connect pwm to pin on timer 2, channel A
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||
avr.OCR2A.Set(value8) // set pwm duty
|
||||
default:
|
||||
panic("Invalid PWM pin")
|
||||
}
|
||||
}
|
||||
|
||||
+175
-19
@@ -33,6 +33,26 @@ const (
|
||||
PinInputPulldown PinMode = 12
|
||||
)
|
||||
|
||||
type PinChange uint8
|
||||
|
||||
// Pin change interrupt constants for SetInterrupt.
|
||||
const (
|
||||
PinRising PinChange = sam.EIC_CONFIG_SENSE0_RISE
|
||||
PinFalling PinChange = sam.EIC_CONFIG_SENSE0_FALL
|
||||
PinToggle PinChange = sam.EIC_CONFIG_SENSE0_BOTH
|
||||
)
|
||||
|
||||
// Callbacks to be called for pins configured with SetInterrupt. Unfortunately,
|
||||
// we also need to keep track of which interrupt channel is used by which pin,
|
||||
// as the only alternative would be iterating through all pins.
|
||||
//
|
||||
// We're using the magic constant 16 here because the SAM D21 has 16 interrupt
|
||||
// channels configurable for pins.
|
||||
var (
|
||||
interruptPins [16]Pin // warning: the value is invalid when pinCallbacks[i] is not set!
|
||||
pinCallbacks [16]func(Pin)
|
||||
)
|
||||
|
||||
const (
|
||||
pinPadMapSERCOM0Pad0 byte = (0x10 << 1) | 0x00
|
||||
pinPadMapSERCOM1Pad0 byte = (0x20 << 1) | 0x00
|
||||
@@ -144,6 +164,114 @@ func findPinPadMapping(sercom uint8, pin Pin) (pinMode PinMode, pad uint32, ok b
|
||||
return
|
||||
}
|
||||
|
||||
// SetInterrupt sets an interrupt to be executed when a particular pin changes
|
||||
// state.
|
||||
//
|
||||
// This call will replace a previously set callback on this pin. You can pass a
|
||||
// nil func to unset the pin change interrupt. If you do so, the change
|
||||
// parameter is ignored and can be set to any value (such as 0).
|
||||
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
|
||||
// Most pins follow a common pattern where the EXTINT value is the pin
|
||||
// number modulo 16. However, there are a few exceptions, as you can see
|
||||
// below.
|
||||
extint := uint8(0)
|
||||
switch p {
|
||||
case PA08:
|
||||
// Connected to NMI. This is not currently supported.
|
||||
return ErrInvalidInputPin
|
||||
case PA24:
|
||||
extint = 12
|
||||
case PA25:
|
||||
extint = 13
|
||||
case PA27:
|
||||
extint = 15
|
||||
case PA28:
|
||||
extint = 8
|
||||
case PA30:
|
||||
extint = 10
|
||||
case PA31:
|
||||
extint = 11
|
||||
default:
|
||||
// All other pins follow a normal pattern.
|
||||
extint = uint8(p) % 16
|
||||
}
|
||||
|
||||
if callback == nil {
|
||||
// Disable this pin interrupt (if it was enabled).
|
||||
sam.EIC.INTENCLR.Set(1 << extint)
|
||||
if pinCallbacks[extint] != nil {
|
||||
pinCallbacks[extint] = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if pinCallbacks[extint] != nil {
|
||||
// The pin was already configured.
|
||||
// To properly re-configure a pin, unset it first and set a new
|
||||
// configuration.
|
||||
return ErrNoPinChangeChannel
|
||||
}
|
||||
pinCallbacks[extint] = callback
|
||||
interruptPins[extint] = p
|
||||
|
||||
if sam.EIC.CTRL.Get() == 0 {
|
||||
// EIC peripheral has not yet been initialized. Initialize it now.
|
||||
|
||||
// The EIC needs two clocks: CLK_EIC_APB and GCLK_EIC. CLK_EIC_APB is
|
||||
// enabled by default, so doesn't have to be re-enabled. The other is
|
||||
// required for detecting edges and must be enabled manually.
|
||||
sam.GCLK.CLKCTRL.Set(sam.GCLK_CLKCTRL_ID_EIC<<sam.GCLK_CLKCTRL_ID_Pos |
|
||||
sam.GCLK_CLKCTRL_GEN_GCLK0<<sam.GCLK_CLKCTRL_GEN_Pos |
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
|
||||
// should not be necessary (CLKCTRL is not synchronized)
|
||||
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
||||
}
|
||||
|
||||
sam.EIC.CTRL.Set(sam.EIC_CTRL_ENABLE)
|
||||
for sam.EIC.STATUS.HasBits(sam.EIC_STATUS_SYNCBUSY) {
|
||||
}
|
||||
}
|
||||
|
||||
// Configure this pin. Set the 4 bits of the EIC.CONFIGx register to the
|
||||
// sense value (filter bit set to 0, sense bits set to the change value).
|
||||
addr := &sam.EIC.CONFIG0
|
||||
if extint >= 8 {
|
||||
addr = &sam.EIC.CONFIG1
|
||||
}
|
||||
pos := (extint % 8) * 4 // bit position in register
|
||||
addr.Set((addr.Get() &^ (0xf << pos)) | uint32(change)<<pos)
|
||||
|
||||
// Enable external interrupt for this pin.
|
||||
sam.EIC.INTENSET.Set(1 << extint)
|
||||
|
||||
// Set the PMUXEN flag, while keeping the INEN and PULLEN flags (if they
|
||||
// were set before). This avoids clearing the pin pull mode while
|
||||
// configuring the pin interrupt.
|
||||
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | (p.getPinCfg() & (sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)))
|
||||
if p&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
p.setPMux(val | (sam.PORT_PMUX0_PMUXO_A << sam.PORT_PMUX0_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
p.setPMux(val | (sam.PORT_PMUX0_PMUXE_A << sam.PORT_PMUX0_PMUXE_Pos))
|
||||
}
|
||||
|
||||
interrupt.New(sam.IRQ_EIC, func(interrupt.Interrupt) {
|
||||
flags := sam.EIC.INTFLAG.Get()
|
||||
sam.EIC.INTFLAG.Set(flags) // clear interrupt
|
||||
for i := uint(0); i < 16; i++ { // there are 16 channels
|
||||
if flags&(1<<i) != 0 {
|
||||
pinCallbacks[i](interruptPins[i])
|
||||
}
|
||||
}
|
||||
}).Enable()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitADC initializes the ADC.
|
||||
func InitADC() {
|
||||
// ADC Bias Calibration
|
||||
@@ -260,6 +388,16 @@ func (a ADC) getADCChannel() uint8 {
|
||||
switch a.Pin {
|
||||
case PA02:
|
||||
return 0
|
||||
case PA03:
|
||||
return 1
|
||||
case PB04:
|
||||
return 12
|
||||
case PB05:
|
||||
return 13
|
||||
case PB06:
|
||||
return 14
|
||||
case PB07:
|
||||
return 15
|
||||
case PB08:
|
||||
return 2
|
||||
case PB09:
|
||||
@@ -272,14 +410,22 @@ func (a ADC) getADCChannel() uint8 {
|
||||
return 6
|
||||
case PA07:
|
||||
return 7
|
||||
case PA08:
|
||||
return 16
|
||||
case PA09:
|
||||
return 17
|
||||
case PA10:
|
||||
return 18
|
||||
case PA11:
|
||||
return 19
|
||||
case PB00:
|
||||
return 8
|
||||
case PB01:
|
||||
return 9
|
||||
case PB02:
|
||||
return 10
|
||||
case PB03:
|
||||
return 11
|
||||
case PA09:
|
||||
return 17
|
||||
case PA11:
|
||||
return 19
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
@@ -1090,9 +1236,12 @@ func InitPWM() {
|
||||
}
|
||||
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() {
|
||||
func (pwm PWM) Configure() error {
|
||||
// figure out which TCCX timer for this pin
|
||||
timer := pwm.getTimer()
|
||||
if timer == nil {
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
|
||||
// disable timer
|
||||
timer.CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
|
||||
@@ -1139,12 +1288,19 @@ func (pwm PWM) Configure() {
|
||||
val := pwm.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_PMUX0_PMUXE_Pos))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set turns on the duty cycle for a PWM pin using the provided value.
|
||||
func (pwm PWM) Set(value uint16) {
|
||||
// figure out which TCCX timer for this pin
|
||||
timer := pwm.getTimer()
|
||||
if timer == nil {
|
||||
// The Configure call above cannot have succeeded, so simply ignore this
|
||||
// error.
|
||||
return
|
||||
}
|
||||
|
||||
// disable output
|
||||
timer.CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
|
||||
@@ -1154,7 +1310,7 @@ func (pwm PWM) Set(value uint16) {
|
||||
}
|
||||
|
||||
// Set PWM signal to output duty cycle
|
||||
pwm.setChannel(uint32(value))
|
||||
pwm.setChannel(timer, uint32(value))
|
||||
|
||||
// Wait for synchronization on all channels
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0 |
|
||||
@@ -1223,32 +1379,32 @@ func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||
}
|
||||
|
||||
// setChannel sets the value for the correct channel for PWM on this pin
|
||||
func (pwm PWM) setChannel(val uint32) {
|
||||
func (pwm PWM) setChannel(timer *sam.TCC_Type, val uint32) {
|
||||
switch pwm.Pin {
|
||||
case 6:
|
||||
pwm.getTimer().CC0.Set(val)
|
||||
timer.CC0.Set(val)
|
||||
case 7:
|
||||
pwm.getTimer().CC1.Set(val)
|
||||
timer.CC1.Set(val)
|
||||
case 8:
|
||||
pwm.getTimer().CC0.Set(val)
|
||||
timer.CC0.Set(val)
|
||||
case 9:
|
||||
pwm.getTimer().CC1.Set(val)
|
||||
timer.CC1.Set(val)
|
||||
case 14:
|
||||
pwm.getTimer().CC0.Set(val)
|
||||
timer.CC0.Set(val)
|
||||
case 15:
|
||||
pwm.getTimer().CC1.Set(val)
|
||||
timer.CC1.Set(val)
|
||||
case 16:
|
||||
pwm.getTimer().CC2.Set(val)
|
||||
timer.CC2.Set(val)
|
||||
case 17:
|
||||
pwm.getTimer().CC3.Set(val)
|
||||
timer.CC3.Set(val)
|
||||
case 18:
|
||||
pwm.getTimer().CC2.Set(val)
|
||||
timer.CC2.Set(val)
|
||||
case 19:
|
||||
pwm.getTimer().CC3.Set(val)
|
||||
timer.CC3.Set(val)
|
||||
case 20:
|
||||
pwm.getTimer().CC2.Set(val)
|
||||
timer.CC2.Set(val)
|
||||
case 21:
|
||||
pwm.getTimer().CC3.Set(val)
|
||||
timer.CC3.Set(val)
|
||||
default:
|
||||
return // not supported on this pin
|
||||
}
|
||||
|
||||
+310
-62
@@ -44,6 +44,26 @@ const (
|
||||
PinInputPulldown PinMode = 18
|
||||
)
|
||||
|
||||
type PinChange uint8
|
||||
|
||||
// Pin change interrupt constants for SetInterrupt.
|
||||
const (
|
||||
PinRising PinChange = sam.EIC_CONFIG_SENSE0_RISE
|
||||
PinFalling PinChange = sam.EIC_CONFIG_SENSE0_FALL
|
||||
PinToggle PinChange = sam.EIC_CONFIG_SENSE0_BOTH
|
||||
)
|
||||
|
||||
// Callbacks to be called for pins configured with SetInterrupt. Unfortunately,
|
||||
// we also need to keep track of which interrupt channel is used by which pin,
|
||||
// as the only alternative would be iterating through all pins.
|
||||
//
|
||||
// We're using the magic constant 16 here because the SAM D21 has 16 interrupt
|
||||
// channels configurable for pins.
|
||||
var (
|
||||
interruptPins [16]Pin // warning: the value is invalid when pinCallbacks[i] is not set!
|
||||
pinCallbacks [16]func(Pin)
|
||||
)
|
||||
|
||||
// Hardware pins
|
||||
const (
|
||||
PA00 Pin = 0
|
||||
@@ -110,6 +130,70 @@ const (
|
||||
PB29 Pin = 61
|
||||
PB30 Pin = 62
|
||||
PB31 Pin = 63
|
||||
PC00 Pin = 64
|
||||
PC01 Pin = 65
|
||||
PC02 Pin = 66
|
||||
PC03 Pin = 67
|
||||
PC04 Pin = 68
|
||||
PC05 Pin = 69
|
||||
PC06 Pin = 70
|
||||
PC07 Pin = 71
|
||||
PC08 Pin = 72
|
||||
PC09 Pin = 73
|
||||
PC10 Pin = 74
|
||||
PC11 Pin = 75
|
||||
PC12 Pin = 76
|
||||
PC13 Pin = 77
|
||||
PC14 Pin = 78
|
||||
PC15 Pin = 79
|
||||
PC16 Pin = 80
|
||||
PC17 Pin = 81
|
||||
PC18 Pin = 82
|
||||
PC19 Pin = 83
|
||||
PC20 Pin = 84
|
||||
PC21 Pin = 85
|
||||
PC22 Pin = 86
|
||||
PC23 Pin = 87
|
||||
PC24 Pin = 88
|
||||
PC25 Pin = 89
|
||||
PC26 Pin = 90
|
||||
PC27 Pin = 91
|
||||
PC28 Pin = 92
|
||||
PC29 Pin = 93
|
||||
PC30 Pin = 94
|
||||
PC31 Pin = 95
|
||||
PD00 Pin = 96
|
||||
PD01 Pin = 97
|
||||
PD02 Pin = 98
|
||||
PD03 Pin = 99
|
||||
PD04 Pin = 100
|
||||
PD05 Pin = 101
|
||||
PD06 Pin = 102
|
||||
PD07 Pin = 103
|
||||
PD08 Pin = 104
|
||||
PD09 Pin = 105
|
||||
PD10 Pin = 106
|
||||
PD11 Pin = 107
|
||||
PD12 Pin = 108
|
||||
PD13 Pin = 109
|
||||
PD14 Pin = 110
|
||||
PD15 Pin = 111
|
||||
PD16 Pin = 112
|
||||
PD17 Pin = 113
|
||||
PD18 Pin = 114
|
||||
PD19 Pin = 115
|
||||
PD20 Pin = 116
|
||||
PD21 Pin = 117
|
||||
PD22 Pin = 118
|
||||
PD23 Pin = 119
|
||||
PD24 Pin = 120
|
||||
PD25 Pin = 121
|
||||
PD26 Pin = 122
|
||||
PD27 Pin = 123
|
||||
PD28 Pin = 124
|
||||
PD29 Pin = 125
|
||||
PD30 Pin = 126
|
||||
PD31 Pin = 127
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -180,7 +264,7 @@ const (
|
||||
// easy to check whether a nibble is set at all.
|
||||
//
|
||||
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/60001507E.pdf
|
||||
var pinPadMapping = [32]uint16{
|
||||
var pinPadMapping = [64]uint16{
|
||||
// page 32
|
||||
PA00 / 2: 0 | pinPadMapSERCOM1AltPad0,
|
||||
|
||||
@@ -188,28 +272,28 @@ var pinPadMapping = [32]uint16{
|
||||
PB08 / 2: 0 | pinPadMapSERCOM4AltPad0,
|
||||
PA04 / 2: 0 | pinPadMapSERCOM0AltPad0,
|
||||
PA06 / 2: 0 | pinPadMapSERCOM0AltPad2,
|
||||
//PC04 / 2: pinPadMapSERCOM6Pad0 | 0,
|
||||
//PC06 / 2: pinPadMapSERCOM6Pad2 | 0,
|
||||
PC04 / 2: pinPadMapSERCOM6Pad0 | 0,
|
||||
PC06 / 2: pinPadMapSERCOM6Pad2 | 0,
|
||||
PA08 / 2: pinPadMapSERCOM0Pad0 | pinPadMapSERCOM2AltPad1,
|
||||
PA10 / 2: pinPadMapSERCOM0Pad2 | pinPadMapSERCOM2AltPad2,
|
||||
PB10 / 2: 0 | pinPadMapSERCOM4AltPad2,
|
||||
PB12 / 2: pinPadMapSERCOM4Pad0 | 0,
|
||||
PB14 / 2: pinPadMapSERCOM4Pad2 | 0,
|
||||
//PD08 / 2: pinPadMapSERCOM7Pad0 | pinPadMapSERCOM6AltPad1,
|
||||
//PD10 / 2: pinPadMapSERCOM7Pad2 | pinPadMapSERCOM6AltPad2,
|
||||
//PC10 / 2: pinPadMapSERCOM6Pad2 | pinPadMapSERCOM7AltPad2,
|
||||
PD08 / 2: pinPadMapSERCOM7Pad0 | pinPadMapSERCOM6AltPad1,
|
||||
PD10 / 2: pinPadMapSERCOM7Pad2 | pinPadMapSERCOM6AltPad2,
|
||||
PC10 / 2: pinPadMapSERCOM6Pad2 | pinPadMapSERCOM7AltPad2,
|
||||
|
||||
// page 34
|
||||
//PC12 / 2: pinPadMapSERCOM7Pad0 | pinPadMapSERCOM6AltPad1,
|
||||
//PC14 / 2: pinPadMapSERCOM7Pad2 | pinPadMapSERCOM6AltPad2,
|
||||
PC12 / 2: pinPadMapSERCOM7Pad0 | pinPadMapSERCOM6AltPad1,
|
||||
PC14 / 2: pinPadMapSERCOM7Pad2 | pinPadMapSERCOM6AltPad2,
|
||||
PA12 / 2: pinPadMapSERCOM2Pad0 | pinPadMapSERCOM4AltPad1,
|
||||
PA14 / 2: pinPadMapSERCOM2Pad2 | pinPadMapSERCOM4AltPad2,
|
||||
PA16 / 2: pinPadMapSERCOM1Pad0 | pinPadMapSERCOM3AltPad1,
|
||||
PA18 / 2: pinPadMapSERCOM1Pad2 | pinPadMapSERCOM3AltPad2,
|
||||
//PC16 / 2: pinPadMapSERCOM6Pad0 | pinPadMapSERCOM0AltPad1,
|
||||
//PC18 / 2: pinPadMapSERCOM6Pad2 | pinPadMapSERCOM0AltPad2,
|
||||
//PC22 / 2: pinPadMapSERCOM1Pad0 | pinPadMapSERCOM3AltPad1,
|
||||
//PD20 / 2: pinPadMapSERCOM1Pad2 | pinPadMapSERCOM3AltPad2,
|
||||
PC16 / 2: pinPadMapSERCOM6Pad0 | pinPadMapSERCOM0AltPad1,
|
||||
PC18 / 2: pinPadMapSERCOM6Pad2 | pinPadMapSERCOM0AltPad2,
|
||||
PC22 / 2: pinPadMapSERCOM1Pad0 | pinPadMapSERCOM3AltPad1,
|
||||
PD20 / 2: pinPadMapSERCOM1Pad2 | pinPadMapSERCOM3AltPad2,
|
||||
PB16 / 2: pinPadMapSERCOM5Pad0 | 0,
|
||||
PB18 / 2: pinPadMapSERCOM5Pad2 | pinPadMapSERCOM7AltPad2,
|
||||
|
||||
@@ -222,7 +306,7 @@ var pinPadMapping = [32]uint16{
|
||||
PB24 / 2: pinPadMapSERCOM0Pad0 | pinPadMapSERCOM2AltPad1,
|
||||
PB26 / 2: pinPadMapSERCOM2Pad0 | pinPadMapSERCOM4AltPad1,
|
||||
PB28 / 2: pinPadMapSERCOM2Pad2 | pinPadMapSERCOM4AltPad2,
|
||||
//PC24 / 2: pinPadMapSERCOM0Pad2 | pinPadMapSERCOM2AltPad2,
|
||||
PC24 / 2: pinPadMapSERCOM0Pad2 | pinPadMapSERCOM2AltPad2,
|
||||
//PC26 / 2: pinPadMapSERCOM1Pad1 | 0, // note: PC26 doesn't support SERCOM, but PC27 does
|
||||
//PC28 / 2: pinPadMapSERCOM1Pad1 | 0, // note: PC29 doesn't exist in the datasheet?
|
||||
PA30 / 2: 0 | pinPadMapSERCOM1AltPad2,
|
||||
@@ -268,6 +352,162 @@ func findPinPadMapping(sercom uint8, pin Pin) (pinMode PinMode, pad uint32, ok b
|
||||
return
|
||||
}
|
||||
|
||||
// SetInterrupt sets an interrupt to be executed when a particular pin changes
|
||||
// state.
|
||||
//
|
||||
// This call will replace a previously set callback on this pin. You can pass a
|
||||
// nil func to unset the pin change interrupt. If you do so, the change
|
||||
// parameter is ignored and can be set to any value (such as 0).
|
||||
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
|
||||
// Most pins follow a common pattern where the EXTINT value is the pin
|
||||
// number modulo 16. However, there are a few exceptions, as you can see
|
||||
// below.
|
||||
extint := uint8(0)
|
||||
|
||||
switch p {
|
||||
case PA08:
|
||||
// Connected to NMI. This is not currently supported.
|
||||
return ErrInvalidInputPin
|
||||
case PB26:
|
||||
extint = 12
|
||||
case PB27:
|
||||
extint = 13
|
||||
case PB28:
|
||||
extint = 14
|
||||
case PB29:
|
||||
extint = 15
|
||||
case PC07:
|
||||
extint = 9
|
||||
case PD08:
|
||||
extint = 3
|
||||
case PD09:
|
||||
extint = 4
|
||||
case PD10:
|
||||
extint = 5
|
||||
case PD11:
|
||||
extint = 6
|
||||
case PD12:
|
||||
extint = 7
|
||||
case PD20:
|
||||
extint = 10
|
||||
case PD21:
|
||||
extint = 11
|
||||
default:
|
||||
// All other pins follow a normal pattern.
|
||||
extint = uint8(p) % 16
|
||||
}
|
||||
|
||||
if callback == nil {
|
||||
// Disable this pin interrupt (if it was enabled).
|
||||
sam.EIC.INTENCLR.Set(1 << extint)
|
||||
if pinCallbacks[extint] != nil {
|
||||
pinCallbacks[extint] = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if pinCallbacks[extint] != nil {
|
||||
// The pin was already configured.
|
||||
// To properly re-configure a pin, unset it first and set a new
|
||||
// configuration.
|
||||
return ErrNoPinChangeChannel
|
||||
}
|
||||
pinCallbacks[extint] = callback
|
||||
interruptPins[extint] = p
|
||||
|
||||
if !sam.EIC.CTRLA.HasBits(sam.EIC_CTRLA_ENABLE) {
|
||||
// EIC peripheral has not yet been initialized. Initialize it now.
|
||||
|
||||
// The EIC needs two clocks: CLK_EIC_APB and GCLK_EIC. CLK_EIC_APB is
|
||||
// enabled by default, so doesn't have to be re-enabled. The other is
|
||||
// required for detecting edges and must be enabled manually.
|
||||
sam.GCLK.PCHCTRL[4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// should not be necessary (CLKCTRL is not synchronized)
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK0 << sam.GCLK_SYNCBUSY_GENCTRL_Pos) {
|
||||
}
|
||||
}
|
||||
|
||||
// CONFIG register is enable-protected, so disable EIC.
|
||||
sam.EIC.CTRLA.ClearBits(sam.EIC_CTRLA_ENABLE)
|
||||
|
||||
// Configure this pin. Set the 4 bits of the EIC.CONFIGx register to the
|
||||
// sense value (filter bit set to 0, sense bits set to the change value).
|
||||
addr := &sam.EIC.CONFIG[0]
|
||||
if extint >= 8 {
|
||||
addr = &sam.EIC.CONFIG[1]
|
||||
}
|
||||
pos := (extint % 8) * 4 // bit position in register
|
||||
addr.Set((addr.Get() &^ (0xf << pos)) | uint32(change)<<pos)
|
||||
|
||||
// Enable external interrupt for this pin.
|
||||
sam.EIC.INTENSET.Set(1 << extint)
|
||||
|
||||
sam.EIC.CTRLA.Set(sam.EIC_CTRLA_ENABLE)
|
||||
for sam.EIC.SYNCBUSY.HasBits(sam.EIC_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
// Set the PMUXEN flag, while keeping the INEN and PULLEN flags (if they
|
||||
// were set before). This avoids clearing the pin pull mode while
|
||||
// configuring the pin interrupt.
|
||||
p.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN | (p.getPinCfg() & (sam.PORT_GROUP_PINCFG_INEN | sam.PORT_GROUP_PINCFG_PULLEN)))
|
||||
if p&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
|
||||
p.setPMux(val | (0 << sam.PORT_GROUP_PMUX_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
|
||||
p.setPMux(val | (0 << sam.PORT_GROUP_PMUX_PMUXE_Pos))
|
||||
}
|
||||
|
||||
handleEICInterrupt := func(interrupt.Interrupt) {
|
||||
flags := sam.EIC.INTFLAG.Get()
|
||||
sam.EIC.INTFLAG.Set(flags) // clear interrupt
|
||||
for i := uint(0); i < 16; i++ { // there are 16 channels
|
||||
if flags&(1<<i) != 0 {
|
||||
pinCallbacks[i](interruptPins[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
switch extint {
|
||||
case 0:
|
||||
interrupt.New(sam.IRQ_EIC_EXTINT_0, handleEICInterrupt).Enable()
|
||||
case 1:
|
||||
interrupt.New(sam.IRQ_EIC_EXTINT_1, handleEICInterrupt).Enable()
|
||||
case 2:
|
||||
interrupt.New(sam.IRQ_EIC_EXTINT_2, handleEICInterrupt).Enable()
|
||||
case 3:
|
||||
interrupt.New(sam.IRQ_EIC_EXTINT_3, handleEICInterrupt).Enable()
|
||||
case 4:
|
||||
interrupt.New(sam.IRQ_EIC_EXTINT_4, handleEICInterrupt).Enable()
|
||||
case 5:
|
||||
interrupt.New(sam.IRQ_EIC_EXTINT_5, handleEICInterrupt).Enable()
|
||||
case 6:
|
||||
interrupt.New(sam.IRQ_EIC_EXTINT_6, handleEICInterrupt).Enable()
|
||||
case 7:
|
||||
interrupt.New(sam.IRQ_EIC_EXTINT_7, handleEICInterrupt).Enable()
|
||||
case 8:
|
||||
interrupt.New(sam.IRQ_EIC_EXTINT_8, handleEICInterrupt).Enable()
|
||||
case 9:
|
||||
interrupt.New(sam.IRQ_EIC_EXTINT_9, handleEICInterrupt).Enable()
|
||||
case 10:
|
||||
interrupt.New(sam.IRQ_EIC_EXTINT_10, handleEICInterrupt).Enable()
|
||||
case 11:
|
||||
interrupt.New(sam.IRQ_EIC_EXTINT_11, handleEICInterrupt).Enable()
|
||||
case 12:
|
||||
interrupt.New(sam.IRQ_EIC_EXTINT_12, handleEICInterrupt).Enable()
|
||||
case 13:
|
||||
interrupt.New(sam.IRQ_EIC_EXTINT_13, handleEICInterrupt).Enable()
|
||||
case 14:
|
||||
interrupt.New(sam.IRQ_EIC_EXTINT_14, handleEICInterrupt).Enable()
|
||||
case 15:
|
||||
interrupt.New(sam.IRQ_EIC_EXTINT_15, handleEICInterrupt).Enable()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return the register and mask to enable a given GPIO pin. This can be used to
|
||||
// implement bit-banged drivers.
|
||||
func (p Pin) PortMaskSet() (*uint32, uint32) {
|
||||
@@ -586,7 +826,7 @@ func (a ADC) Get() uint16 {
|
||||
}
|
||||
|
||||
func (a ADC) getADCBus() *sam.ADC_Type {
|
||||
if a.Pin >= PB04 && a.Pin <= PB07 {
|
||||
if (a.Pin >= PB04 && a.Pin <= PB07) || (a.Pin >= PC00) {
|
||||
return sam.ADC1
|
||||
}
|
||||
return sam.ADC0
|
||||
@@ -629,6 +869,24 @@ func (a ADC) getADCChannel() uint8 {
|
||||
return 8
|
||||
case PB07:
|
||||
return 9
|
||||
|
||||
case PC00:
|
||||
return 10
|
||||
case PC01:
|
||||
return 11
|
||||
case PC02:
|
||||
return 4
|
||||
case PC03:
|
||||
return 5
|
||||
case PC30:
|
||||
return 12
|
||||
case PC31:
|
||||
return 13
|
||||
|
||||
case PD00:
|
||||
return 14
|
||||
case PD01:
|
||||
return 15
|
||||
default:
|
||||
panic("Invalid ADC pin")
|
||||
}
|
||||
@@ -645,28 +903,8 @@ type UART struct {
|
||||
var (
|
||||
// UART0 is actually a USB CDC interface.
|
||||
UART0 = USBCDC{Buffer: NewRingBuffer()}
|
||||
|
||||
// The first hardware serial port on the SAMD51. Uses the SERCOM3 interface.
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM3_USART_INT,
|
||||
SERCOM: 3,
|
||||
}
|
||||
|
||||
// The second hardware serial port on the SAMD51. Uses the SERCOM0 interface.
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART_INT,
|
||||
SERCOM: 0,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Register RXC interrupts.
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM3_2, UART1.handleInterrupt)
|
||||
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART2.handleInterrupt)
|
||||
}
|
||||
|
||||
const (
|
||||
sampleRate16X = 16
|
||||
lsbFirst = 1
|
||||
@@ -1216,7 +1454,7 @@ const (
|
||||
const period = 0xFFFF
|
||||
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() {
|
||||
func (pwm PWM) Configure() error {
|
||||
// Set pin as output
|
||||
sam.PORT.GROUP[0].DIRSET.Set(1 << uint8(pwm.Pin))
|
||||
// Set pin to low
|
||||
@@ -1240,6 +1478,9 @@ func (pwm PWM) Configure() {
|
||||
|
||||
// figure out which TCCX timer for this pin
|
||||
timer := pwm.getTimer()
|
||||
if timer == nil {
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
|
||||
// disable timer
|
||||
timer.CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
|
||||
@@ -1264,7 +1505,7 @@ func (pwm PWM) Configure() {
|
||||
|
||||
// Set the initial value
|
||||
// TCCx->CC[tcChannel].reg = (uint32_t) value;
|
||||
pwm.setChannel(0)
|
||||
pwm.setChannel(timer, 0)
|
||||
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
|
||||
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
|
||||
@@ -1282,12 +1523,19 @@ func (pwm PWM) Configure() {
|
||||
// Wait for synchronization
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set turns on the duty cycle for a PWM pin using the provided value.
|
||||
func (pwm PWM) Set(value uint16) {
|
||||
// figure out which TCCX timer for this pin
|
||||
timer := pwm.getTimer()
|
||||
if timer == nil {
|
||||
// The Configure call above cannot have succeeded, so simply ignore this
|
||||
// error.
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for synchronization
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CTRLB) {
|
||||
@@ -1297,7 +1545,7 @@ func (pwm PWM) Set(value uint16) {
|
||||
}
|
||||
|
||||
// TCCx->CCBUF[tcChannel].reg = (uint32_t) value;
|
||||
pwm.setChannelBuffer(uint32(value))
|
||||
pwm.setChannelBuffer(timer, uint32(value))
|
||||
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
|
||||
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
|
||||
@@ -1329,61 +1577,61 @@ func (pwm PWM) setPinCfg(val uint8) {
|
||||
pwm.Pin.setPinCfg(val)
|
||||
}
|
||||
|
||||
// setChannel sets the value for the correct channel for PWM on this pin
|
||||
func (pwm PWM) setChannel(val uint32) {
|
||||
// setChannel sets the value for the correct channel for PWM on this pin.
|
||||
func (pwm PWM) setChannel(timer *sam.TCC_Type, val uint32) {
|
||||
switch pwm.Pin {
|
||||
case PA16:
|
||||
pwm.getTimer().CC[0].Set(val)
|
||||
timer.CC[0].Set(val)
|
||||
case PA17:
|
||||
pwm.getTimer().CC[1].Set(val)
|
||||
timer.CC[1].Set(val)
|
||||
case PA14:
|
||||
pwm.getTimer().CC[0].Set(val)
|
||||
timer.CC[0].Set(val)
|
||||
case PA15:
|
||||
pwm.getTimer().CC[1].Set(val)
|
||||
timer.CC[1].Set(val)
|
||||
case PA18:
|
||||
pwm.getTimer().CC[2].Set(val)
|
||||
timer.CC[2].Set(val)
|
||||
case PA19:
|
||||
pwm.getTimer().CC[3].Set(val)
|
||||
timer.CC[3].Set(val)
|
||||
case PA20:
|
||||
pwm.getTimer().CC[0].Set(val)
|
||||
timer.CC[0].Set(val)
|
||||
case PA21:
|
||||
pwm.getTimer().CC[1].Set(val)
|
||||
timer.CC[1].Set(val)
|
||||
case PA23:
|
||||
pwm.getTimer().CC[3].Set(val)
|
||||
timer.CC[3].Set(val)
|
||||
case PA22:
|
||||
pwm.getTimer().CC[2].Set(val)
|
||||
timer.CC[2].Set(val)
|
||||
case PB31:
|
||||
pwm.getTimer().CC[1].Set(val)
|
||||
timer.CC[1].Set(val)
|
||||
default:
|
||||
return // not supported on this pin
|
||||
}
|
||||
}
|
||||
|
||||
// setChannelBuffer sets the value for the correct channel buffer for PWM on this pin
|
||||
func (pwm PWM) setChannelBuffer(val uint32) {
|
||||
func (pwm PWM) setChannelBuffer(timer *sam.TCC_Type, val uint32) {
|
||||
switch pwm.Pin {
|
||||
case PA16:
|
||||
pwm.getTimer().CCBUF[0].Set(val)
|
||||
timer.CCBUF[0].Set(val)
|
||||
case PA17:
|
||||
pwm.getTimer().CCBUF[1].Set(val)
|
||||
timer.CCBUF[1].Set(val)
|
||||
case PA14:
|
||||
pwm.getTimer().CCBUF[0].Set(val)
|
||||
timer.CCBUF[0].Set(val)
|
||||
case PA15:
|
||||
pwm.getTimer().CCBUF[1].Set(val)
|
||||
timer.CCBUF[1].Set(val)
|
||||
case PA18:
|
||||
pwm.getTimer().CCBUF[2].Set(val)
|
||||
timer.CCBUF[2].Set(val)
|
||||
case PA19:
|
||||
pwm.getTimer().CCBUF[3].Set(val)
|
||||
timer.CCBUF[3].Set(val)
|
||||
case PA20:
|
||||
pwm.getTimer().CCBUF[0].Set(val)
|
||||
timer.CCBUF[0].Set(val)
|
||||
case PA21:
|
||||
pwm.getTimer().CCBUF[1].Set(val)
|
||||
timer.CCBUF[1].Set(val)
|
||||
case PA23:
|
||||
pwm.getTimer().CCBUF[3].Set(val)
|
||||
timer.CCBUF[3].Set(val)
|
||||
case PA22:
|
||||
pwm.getTimer().CCBUF[2].Set(val)
|
||||
timer.CCBUF[2].Set(val)
|
||||
case PB31:
|
||||
pwm.getTimer().CCBUF[1].Set(val)
|
||||
timer.CCBUF[1].Set(val)
|
||||
default:
|
||||
return // not supported on this pin
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// +build sam,atsamd51,atsamd51p19
|
||||
|
||||
// Peripheral abstraction layer for the atsamd51.
|
||||
//
|
||||
// Datasheet:
|
||||
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
|
||||
//
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
const HSRAM_SIZE = 0x00030000
|
||||
|
||||
// InitPWM initializes the PWM interface.
|
||||
func InitPWM() {
|
||||
// turn on timer clocks used for PWM
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||
|
||||
//use clock generator 0
|
||||
sam.GCLK.PCHCTRL[25].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.GCLK.PCHCTRL[29].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.GCLK.PCHCTRL[38].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
|
||||
// getTimer returns the timer to be used for PWM on this pin
|
||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||
switch pwm.Pin {
|
||||
case PA16:
|
||||
return sam.TCC1
|
||||
case PA17:
|
||||
return sam.TCC1
|
||||
case PA14:
|
||||
return sam.TCC2
|
||||
case PA15:
|
||||
return sam.TCC2
|
||||
case PA18:
|
||||
return sam.TCC1
|
||||
case PA19:
|
||||
return sam.TCC1
|
||||
case PA20:
|
||||
return sam.TCC0
|
||||
case PA21:
|
||||
return sam.TCC0
|
||||
case PA23:
|
||||
return sam.TCC0
|
||||
case PA22:
|
||||
return sam.TCC0
|
||||
case PB31:
|
||||
return sam.TCC4
|
||||
default:
|
||||
return nil // not supported on this pin
|
||||
}
|
||||
}
|
||||
@@ -16,21 +16,8 @@ const (
|
||||
PB5
|
||||
)
|
||||
|
||||
// Configure sets the pin to input or output.
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
if config.Mode == PinOutput { // set output bit
|
||||
avr.DDRB.SetBits(1 << uint8(p))
|
||||
} else { // configure input: clear output bit
|
||||
avr.DDRB.ClearBits(1 << uint8(p))
|
||||
}
|
||||
}
|
||||
|
||||
// getPortMask returns the PORTx register and mask for the pin.
|
||||
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||
// Very simple for the attiny85, which only has a single port.
|
||||
return avr.PORTB, 1 << uint8(p)
|
||||
}
|
||||
|
||||
// Get returns the current value of a GPIO pin.
|
||||
func (p Pin) Get() bool {
|
||||
val := avr.PINB.Get() & (1 << uint8(p))
|
||||
return (val > 0)
|
||||
}
|
||||
|
||||
@@ -5,15 +5,76 @@ package machine
|
||||
import (
|
||||
"device/avr"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinInput PinMode = iota
|
||||
PinInputPullup
|
||||
PinOutput
|
||||
)
|
||||
|
||||
// In all the AVRs I've looked at, the PIN/DDR/PORT registers followed a regular
|
||||
// pattern: PINx, DDRx, PORTx in this order without registers in between.
|
||||
// Therefore, if you know any of them, you can calculate the other two.
|
||||
//
|
||||
// For now, I've chosen to let the PORTx register be the one that is returned
|
||||
// for each specific chip and to calculate the others from that one. Setting an
|
||||
// output port (done using PORTx) is likely the most common operation and the
|
||||
// one that is the most time critical. For others, the PINx and DDRx register
|
||||
// can trivially be calculated using a subtraction.
|
||||
|
||||
// Configure sets the pin to input or output.
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
port, mask := p.getPortMask()
|
||||
// The DDRx register can be found by subtracting one from the PORTx
|
||||
// register, as this appears to be the case for many (most? all?) AVR chips.
|
||||
ddr := (*volatile.Register8)(unsafe.Pointer(uintptr(unsafe.Pointer(port)) - 1))
|
||||
if config.Mode == PinOutput {
|
||||
// set output bit
|
||||
ddr.SetBits(mask)
|
||||
|
||||
// Note: if the pin was PinInputPullup before, it'll now be high.
|
||||
// Otherwise it will be low.
|
||||
} else {
|
||||
// configure input: clear output bit
|
||||
ddr.ClearBits(mask)
|
||||
|
||||
if config.Mode == PinInput {
|
||||
// No pullup (floating).
|
||||
// The transition may be one of the following:
|
||||
// output high -> input pullup -> input (safe: output high and input pullup are similar)
|
||||
// output low -> input -> input (safe: no extra transition)
|
||||
port.ClearBits(mask)
|
||||
} else {
|
||||
// Pullup.
|
||||
// The transition may be one of the following:
|
||||
// output high -> input pullup -> input pullup (safe: no extra transition)
|
||||
// output low -> input -> input pullup (possibly problematic)
|
||||
// For the last transition (output low -> input -> input pullup),
|
||||
// the transition may be problematic in some cases because there is
|
||||
// an intermediate floating state (which may cause irratic
|
||||
// interrupts, for example). If this is a problem, the application
|
||||
// should set the pin high before configuring it as PinInputPullup.
|
||||
// We can't do that here because setting it to high as an
|
||||
// intermediate state may have other problems.
|
||||
port.SetBits(mask)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the current value of a GPIO pin.
|
||||
func (p Pin) Get() bool {
|
||||
port, mask := p.getPortMask()
|
||||
// As noted above, the PINx register is always two registers below the PORTx
|
||||
// register, so we can find it simply by subtracting two from the PORTx
|
||||
// register address.
|
||||
pin := (*volatile.Register8)(unsafe.Pointer(uintptr(unsafe.Pointer(port)) - 2)) // PINA, PINB, etc
|
||||
return (pin.Get() & mask) > 0
|
||||
}
|
||||
|
||||
// Set changes the value of the GPIO pin. The pin must be configured as output.
|
||||
func (p Pin) Set(value bool) {
|
||||
if value { // set bits
|
||||
|
||||
@@ -48,7 +48,6 @@ func (p Pin) Set(high bool) {
|
||||
// Get returns the current value of a GPIO pin.
|
||||
func (p Pin) Get() bool {
|
||||
val := sifive.GPIO0.VALUE.Get() & (1 << uint8(p))
|
||||
println(sifive.GPIO0.VALUE.Get())
|
||||
return (val > 0)
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,8 @@ func InitPWM() {
|
||||
}
|
||||
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() {
|
||||
func (pwm PWM) Configure() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set turns on the duty cycle for a PWM pin using the provided value.
|
||||
|
||||
@@ -21,6 +21,18 @@ const (
|
||||
PinOutput PinMode = (nrf.GPIO_PIN_CNF_DIR_Output << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Disconnect << nrf.GPIO_PIN_CNF_INPUT_Pos)
|
||||
)
|
||||
|
||||
type PinChange uint8
|
||||
|
||||
// Pin change interrupt constants for SetInterrupt.
|
||||
const (
|
||||
PinRising PinChange = nrf.GPIOTE_CONFIG_POLARITY_LoToHi
|
||||
PinFalling PinChange = nrf.GPIOTE_CONFIG_POLARITY_HiToLo
|
||||
PinToggle PinChange = nrf.GPIOTE_CONFIG_POLARITY_Toggle
|
||||
)
|
||||
|
||||
// Callbacks to be called for pins configured with SetInterrupt.
|
||||
var pinCallbacks [len(nrf.GPIOTE.CONFIG)]func(Pin)
|
||||
|
||||
// Configure this pin with the given configuration.
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
cfg := config.Mode | nrf.GPIO_PIN_CNF_DRIVE_S0S1 | nrf.GPIO_PIN_CNF_SENSE_Disabled
|
||||
@@ -59,6 +71,65 @@ func (p Pin) Get() bool {
|
||||
return (port.IN.Get()>>pin)&1 != 0
|
||||
}
|
||||
|
||||
// SetInterrupt sets an interrupt to be executed when a particular pin changes
|
||||
// state.
|
||||
//
|
||||
// This call will replace a previously set callback on this pin. You can pass a
|
||||
// nil func to unset the pin change interrupt. If you do so, the change
|
||||
// parameter is ignored and can be set to any value (such as 0).
|
||||
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
|
||||
// Some variables to easily check whether a channel was already configured
|
||||
// as an event channel for the given pin.
|
||||
// This is not just an optimization, this is requred: the datasheet says
|
||||
// that configuring more than one channel for a given pin results in
|
||||
// unpredictable behavior.
|
||||
expectedConfigMask := uint32(nrf.GPIOTE_CONFIG_MODE_Msk | nrf.GPIOTE_CONFIG_PSEL_Msk)
|
||||
expectedConfig := nrf.GPIOTE_CONFIG_MODE_Event<<nrf.GPIOTE_CONFIG_MODE_Pos | uint32(p)<<nrf.GPIOTE_CONFIG_PSEL_Pos
|
||||
|
||||
foundChannel := false
|
||||
for i := range nrf.GPIOTE.CONFIG {
|
||||
config := nrf.GPIOTE.CONFIG[i].Get()
|
||||
if config == 0 || config&expectedConfigMask == expectedConfig {
|
||||
// Found an empty GPIOTE channel or one that was already configured
|
||||
// for this pin.
|
||||
if callback == nil {
|
||||
// Disable this channel.
|
||||
nrf.GPIOTE.INTENCLR.Set(uint32(1 << uint(i)))
|
||||
pinCallbacks[i] = nil
|
||||
return nil
|
||||
}
|
||||
// Enable this channel with the given callback.
|
||||
nrf.GPIOTE.INTENCLR.Set(uint32(1 << uint(i)))
|
||||
nrf.GPIOTE.CONFIG[i].Set(nrf.GPIOTE_CONFIG_MODE_Event<<nrf.GPIOTE_CONFIG_MODE_Pos |
|
||||
uint32(p)<<nrf.GPIOTE_CONFIG_PSEL_Pos |
|
||||
uint32(change)<<nrf.GPIOTE_CONFIG_POLARITY_Pos)
|
||||
pinCallbacks[i] = callback
|
||||
nrf.GPIOTE.INTENSET.Set(uint32(1 << uint(i)))
|
||||
foundChannel = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !foundChannel {
|
||||
return ErrNoPinChangeChannel
|
||||
}
|
||||
|
||||
// Set and enable the GPIOTE interrupt. It's not a problem if this happens
|
||||
// more than once.
|
||||
interrupt.New(nrf.IRQ_GPIOTE, func(interrupt.Interrupt) {
|
||||
for i := range nrf.GPIOTE.EVENTS_IN {
|
||||
if nrf.GPIOTE.EVENTS_IN[i].Get() != 0 {
|
||||
nrf.GPIOTE.EVENTS_IN[i].Set(0)
|
||||
pin := Pin((nrf.GPIOTE.CONFIG[i].Get() & nrf.GPIOTE_CONFIG_PSEL_Msk) >> nrf.GPIOTE_CONFIG_PSEL_Pos)
|
||||
pinCallbacks[i](pin)
|
||||
}
|
||||
}
|
||||
}).Enable()
|
||||
|
||||
// Everything was configured correctly.
|
||||
return nil
|
||||
}
|
||||
|
||||
// UART on the NRF.
|
||||
type UART struct {
|
||||
Buffer *RingBuffer
|
||||
@@ -372,13 +443,13 @@ func (spi SPI) Tx(w, r []byte) error {
|
||||
spi.Bus.TXD.Set(uint32(b))
|
||||
for spi.Bus.EVENTS_READY.Get() == 0 {
|
||||
}
|
||||
_ = spi.Bus.RXD.Get()
|
||||
spi.Bus.EVENTS_READY.Set(0)
|
||||
_ = spi.Bus.RXD.Get()
|
||||
}
|
||||
for spi.Bus.EVENTS_READY.Get() == 0 {
|
||||
}
|
||||
_ = spi.Bus.RXD.Get()
|
||||
spi.Bus.EVENTS_READY.Set(0)
|
||||
_ = spi.Bus.RXD.Get()
|
||||
|
||||
default:
|
||||
// write/read
|
||||
|
||||
@@ -159,7 +159,8 @@ func InitPWM() {
|
||||
}
|
||||
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() {
|
||||
func (pwm PWM) Configure() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set turns on the duty cycle for a PWM pin using the provided value.
|
||||
|
||||
@@ -104,8 +104,8 @@ func InitADC() {
|
||||
}
|
||||
|
||||
// Configure configures an ADC pin to be able to read analog data.
|
||||
func (a ADC) Configure() {
|
||||
return // no pin specific setup on nrf52840 machine.
|
||||
func (a ADC) Configure() error {
|
||||
return nil // no pin specific setup on nrf52840 machine.
|
||||
}
|
||||
|
||||
// Get returns the current value of a ADC pin in the range 0..0xffff.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package os
|
||||
|
||||
func Getenv(key string) string {
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package os
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalid = errors.New("invalid argument")
|
||||
ErrPermission = errors.New("permission denied")
|
||||
ErrClosed = errors.New("file already closed")
|
||||
|
||||
// Portable analogs of some common system call errors.
|
||||
// Note that these are exported for use in the Filesystem interface.
|
||||
ErrUnsupported = errors.New("operation not supported")
|
||||
ErrNotImplemented = errors.New("operation not implemented")
|
||||
ErrNotExist = errors.New("file not found")
|
||||
ErrExist = errors.New("file exists")
|
||||
)
|
||||
|
||||
func IsPermission(err error) bool {
|
||||
return err == ErrPermission
|
||||
}
|
||||
|
||||
func NewSyscallError(syscall string, err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &SyscallError{syscall, err}
|
||||
}
|
||||
|
||||
// SyscallError records an error from a specific system call.
|
||||
type SyscallError struct {
|
||||
Syscall string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err.Error() }
|
||||
|
||||
func (e *SyscallError) Unwrap() error { return e.Err }
|
||||
@@ -0,0 +1,6 @@
|
||||
package os
|
||||
|
||||
type Signal interface {
|
||||
String() string
|
||||
Signal() // to distinguish from other Stringers
|
||||
}
|
||||
+108
-58
@@ -6,53 +6,132 @@
|
||||
package os
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Portable analogs of some common system call errors.
|
||||
var (
|
||||
errUnsupported = errors.New("operation not supported")
|
||||
notImplemented = errors.New("os: not implemented")
|
||||
)
|
||||
// Mkdir creates a directory. If the operation fails, it will return an error of
|
||||
// type *PathError.
|
||||
func Mkdir(path string, perm FileMode) error {
|
||||
fs, suffix := findMount(path)
|
||||
if fs == nil {
|
||||
return &PathError{"mkdir", path, ErrNotExist}
|
||||
}
|
||||
err := fs.Mkdir(suffix, perm)
|
||||
if err != nil {
|
||||
return &PathError{"mkdir", path, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
|
||||
// standard output, and standard error file descriptors.
|
||||
var (
|
||||
Stdin = &File{0, "/dev/stdin"}
|
||||
Stdout = &File{1, "/dev/stdout"}
|
||||
Stderr = &File{2, "/dev/stderr"}
|
||||
)
|
||||
// Remove removes a file or (empty) directory. If the operation fails, it will
|
||||
// return an error of type *PathError.
|
||||
func Remove(path string) error {
|
||||
fs, suffix := findMount(path)
|
||||
if fs == nil {
|
||||
return &PathError{"remove", path, ErrNotExist}
|
||||
}
|
||||
err := fs.Remove(suffix)
|
||||
if err != nil {
|
||||
return &PathError{"remove", path, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// File represents an open file descriptor.
|
||||
type File struct {
|
||||
fd uintptr
|
||||
name string
|
||||
handle FileHandle
|
||||
name string
|
||||
}
|
||||
|
||||
// Name returns the name of the file with which it was opened.
|
||||
func (f *File) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
// OpenFile opens the named file. If the operation fails, the returned error
|
||||
// will be of type *PathError.
|
||||
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
|
||||
fs, suffix := findMount(name)
|
||||
if fs == nil {
|
||||
return nil, &PathError{"open", name, ErrNotExist}
|
||||
}
|
||||
handle, err := fs.OpenFile(suffix, flag, perm)
|
||||
if err != nil {
|
||||
return nil, &PathError{"open", name, err}
|
||||
}
|
||||
return &File{name: name, handle: handle}, nil
|
||||
}
|
||||
|
||||
// Open opens the file named for reading.
|
||||
func Open(name string) (*File, error) {
|
||||
return OpenFile(name, O_RDONLY, 0)
|
||||
}
|
||||
|
||||
// Create creates the named file, overwriting it if it already exists.
|
||||
func Create(name string) (*File, error) {
|
||||
return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
|
||||
}
|
||||
|
||||
// Read reads up to len(b) bytes from the File. It returns the number of bytes
|
||||
// read and any error encountered. At end of file, Read returns 0, io.EOF.
|
||||
func (f *File) Read(b []byte) (n int, err error) {
|
||||
n, err = f.handle.Read(b)
|
||||
if err != nil {
|
||||
err = &PathError{"read", f.name, err}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
|
||||
return 0, ErrNotImplemented
|
||||
}
|
||||
|
||||
// Write writes len(b) bytes to the File. It returns the number of bytes written
|
||||
// and an error, if any. Write returns a non-nil error when n != len(b).
|
||||
func (f *File) Write(b []byte) (n int, err error) {
|
||||
n, err = f.handle.Write(b)
|
||||
if err != nil {
|
||||
err = &PathError{"write", f.name, err}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Close closes the File, rendering it unusable for I/O.
|
||||
func (f *File) Close() (err error) {
|
||||
err = f.handle.Close()
|
||||
if err != nil {
|
||||
err = &PathError{"close", f.name, err}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Readdir is a stub, not yet implemented
|
||||
func (f *File) Readdir(n int) ([]FileInfo, error) {
|
||||
return nil, notImplemented
|
||||
return nil, &PathError{"readdir", f.name, ErrNotImplemented}
|
||||
}
|
||||
|
||||
// Readdirnames is a stub, not yet implemented
|
||||
func (f *File) Readdirnames(n int) (names []string, err error) {
|
||||
return nil, notImplemented
|
||||
return nil, &PathError{"readdirnames", f.name, ErrNotImplemented}
|
||||
}
|
||||
|
||||
// Stat is a stub, not yet implemented
|
||||
func (f *File) Stat() (FileInfo, error) {
|
||||
return nil, notImplemented
|
||||
return nil, &PathError{"stat", f.name, ErrNotImplemented}
|
||||
}
|
||||
|
||||
// NewFile returns a new File with the given file descriptor and name.
|
||||
func NewFile(fd uintptr, name string) *File {
|
||||
return &File{fd, name}
|
||||
// Sync is a stub, not yet implemented
|
||||
func (f *File) Sync() error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
// Fd returns the integer Unix file descriptor referencing the open file. The
|
||||
// file descriptor is valid only until f.Close is called.
|
||||
func (f *File) SyscallConn() (syscall.RawConn, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
// Fd returns the file handle referencing the open file.
|
||||
func (f *File) Fd() uintptr {
|
||||
return f.fd
|
||||
panic("unimplemented: os.file.Fd()")
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -72,32 +151,8 @@ type PathError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
|
||||
|
||||
// Open is a super simple stub function (for now), only capable of opening stdin, stdout, and stderr
|
||||
func Open(name string) (*File, error) {
|
||||
fd := uintptr(999)
|
||||
switch name {
|
||||
case "/dev/stdin":
|
||||
fd = 0
|
||||
case "/dev/stdout":
|
||||
fd = 1
|
||||
case "/dev/stderr":
|
||||
fd = 2
|
||||
default:
|
||||
return nil, &PathError{"open", name, notImplemented}
|
||||
}
|
||||
return &File{fd, name}, nil
|
||||
}
|
||||
|
||||
// OpenFile is a stub, passing through to the stub Open() call
|
||||
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
|
||||
return Open(name)
|
||||
}
|
||||
|
||||
// Create is a stub, passing through to the stub Open() call
|
||||
func Create(name string) (*File, error) {
|
||||
return Open(name)
|
||||
func (e *PathError) Error() string {
|
||||
return e.Op + " " + e.Path + ": " + e.Err.Error()
|
||||
}
|
||||
|
||||
type FileMode uint32
|
||||
@@ -148,19 +203,19 @@ type FileInfo interface {
|
||||
Name() string // base name of the file
|
||||
Size() int64 // length in bytes for regular files; system-dependent for others
|
||||
Mode() FileMode // file mode bits
|
||||
// ModTime() time.Time // modification time
|
||||
// TODO ModTime() time.Time // modification time
|
||||
IsDir() bool // abbreviation for Mode().IsDir()
|
||||
Sys() interface{} // underlying data source (can return nil)
|
||||
}
|
||||
|
||||
// Stat is a stub, not yet implemented
|
||||
func Stat(name string) (FileInfo, error) {
|
||||
return nil, notImplemented
|
||||
return nil, &PathError{"stat", name, ErrNotImplemented}
|
||||
}
|
||||
|
||||
// Lstat is a stub, not yet implemented
|
||||
func Lstat(name string) (FileInfo, error) {
|
||||
return nil, notImplemented
|
||||
return nil, &PathError{"lstat", name, ErrNotImplemented}
|
||||
}
|
||||
|
||||
// Getwd is a stub (for now), always returning an empty string
|
||||
@@ -178,11 +233,6 @@ func TempDir() string {
|
||||
return "/tmp"
|
||||
}
|
||||
|
||||
// Mkdir is a stub, not yet implemented
|
||||
func Mkdir(name string, perm FileMode) error {
|
||||
return notImplemented
|
||||
}
|
||||
|
||||
// IsExist is a stub (for now), always returning false
|
||||
func IsExist(err error) bool {
|
||||
return false
|
||||
|
||||
+24
-8
@@ -6,28 +6,44 @@ import (
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
|
||||
// standard output, and standard error file descriptors.
|
||||
var (
|
||||
Stdin = &File{stdioFileHandle(0), "/dev/stdin"}
|
||||
Stdout = &File{stdioFileHandle(1), "/dev/stdout"}
|
||||
Stderr = &File{stdioFileHandle(2), "/dev/stderr"}
|
||||
)
|
||||
|
||||
// isOS indicates whether we're running on a real operating system with
|
||||
// filesystem support.
|
||||
const isOS = false
|
||||
|
||||
// stdioFileHandle represents one of stdin, stdout, or stderr depending on the
|
||||
// number. It implements the FileHandle interface.
|
||||
type stdioFileHandle uint8
|
||||
|
||||
// Read is unsupported on this system.
|
||||
func (f *File) Read(b []byte) (n int, err error) {
|
||||
return 0, errUnsupported
|
||||
func (f stdioFileHandle) Read(b []byte) (n int, err error) {
|
||||
return 0, ErrUnsupported
|
||||
}
|
||||
|
||||
// Write writes len(b) bytes to the output. It returns the number of bytes
|
||||
// written or an error if this file is not stdout or stderr.
|
||||
func (f *File) Write(b []byte) (n int, err error) {
|
||||
switch f.fd {
|
||||
case Stdout.fd, Stderr.fd:
|
||||
func (f stdioFileHandle) Write(b []byte) (n int, err error) {
|
||||
switch f {
|
||||
case 1, 2: // stdout, stderr
|
||||
for _, c := range b {
|
||||
putchar(c)
|
||||
}
|
||||
return len(b), nil
|
||||
default:
|
||||
return 0, errUnsupported
|
||||
return 0, ErrUnsupported
|
||||
}
|
||||
}
|
||||
|
||||
// Close is unsupported on this system.
|
||||
func (f *File) Close() error {
|
||||
return errUnsupported
|
||||
func (f stdioFileHandle) Close() error {
|
||||
return ErrUnsupported
|
||||
}
|
||||
|
||||
//go:linkname putchar runtime.putchar
|
||||
|
||||
+92
-6
@@ -6,19 +6,105 @@ import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Mount the host filesystem at the root directory. This is what most
|
||||
// programs will be expecting.
|
||||
Mount("/", unixFilesystem{})
|
||||
}
|
||||
|
||||
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
|
||||
// standard output, and standard error file descriptors.
|
||||
var (
|
||||
Stdin = &File{unixFileHandle(0), "/dev/stdin"}
|
||||
Stdout = &File{unixFileHandle(1), "/dev/stdout"}
|
||||
Stderr = &File{unixFileHandle(2), "/dev/stderr"}
|
||||
)
|
||||
|
||||
// isOS indicates whether we're running on a real operating system with
|
||||
// filesystem support.
|
||||
const isOS = true
|
||||
|
||||
// unixFilesystem is an empty handle for a Unix/Linux filesystem. All operations
|
||||
// are relative to the current working directory.
|
||||
type unixFilesystem struct {
|
||||
}
|
||||
|
||||
func (fs unixFilesystem) Mkdir(path string, perm FileMode) error {
|
||||
return handleSyscallError(syscall.Mkdir(path, uint32(perm)))
|
||||
}
|
||||
|
||||
func (fs unixFilesystem) Remove(path string) error {
|
||||
return handleSyscallError(syscall.Unlink(path))
|
||||
}
|
||||
|
||||
func (fs unixFilesystem) OpenFile(path string, flag int, perm FileMode) (FileHandle, error) {
|
||||
// Map os package flags to syscall flags.
|
||||
syscallFlag := 0
|
||||
if flag&O_RDONLY != 0 {
|
||||
syscallFlag |= syscall.O_RDONLY
|
||||
}
|
||||
if flag&O_WRONLY != 0 {
|
||||
syscallFlag |= syscall.O_WRONLY
|
||||
}
|
||||
if flag&O_RDWR != 0 {
|
||||
syscallFlag |= syscall.O_RDWR
|
||||
}
|
||||
if flag&O_APPEND != 0 {
|
||||
syscallFlag |= syscall.O_APPEND
|
||||
}
|
||||
if flag&O_CREATE != 0 {
|
||||
syscallFlag |= syscall.O_CREAT
|
||||
}
|
||||
if flag&O_EXCL != 0 {
|
||||
syscallFlag |= syscall.O_EXCL
|
||||
}
|
||||
if flag&O_SYNC != 0 {
|
||||
syscallFlag |= syscall.O_SYNC
|
||||
}
|
||||
if flag&O_TRUNC != 0 {
|
||||
syscallFlag |= syscall.O_TRUNC
|
||||
}
|
||||
fp, err := syscall.Open(path, syscallFlag, uint32(perm))
|
||||
return unixFileHandle(fp), handleSyscallError(err)
|
||||
}
|
||||
|
||||
// unixFileHandle is a Unix file pointer with associated methods that implement
|
||||
// the FileHandle interface.
|
||||
type unixFileHandle uintptr
|
||||
|
||||
// Read reads up to len(b) bytes from the File. It returns the number of bytes
|
||||
// read and any error encountered. At end of file, Read returns 0, io.EOF.
|
||||
func (f *File) Read(b []byte) (n int, err error) {
|
||||
return syscall.Read(int(f.fd), b)
|
||||
func (f unixFileHandle) Read(b []byte) (n int, err error) {
|
||||
n, err = syscall.Read(int(f), b)
|
||||
err = handleSyscallError(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Write writes len(b) bytes to the File. It returns the number of bytes written
|
||||
// and an error, if any. Write returns a non-nil error when n != len(b).
|
||||
func (f *File) Write(b []byte) (n int, err error) {
|
||||
return syscall.Write(int(f.fd), b)
|
||||
func (f unixFileHandle) Write(b []byte) (n int, err error) {
|
||||
n, err = syscall.Write(int(f), b)
|
||||
err = handleSyscallError(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Close closes the File, rendering it unusable for I/O.
|
||||
func (f *File) Close() error {
|
||||
return syscall.Close(int(f.fd))
|
||||
func (f unixFileHandle) Close() error {
|
||||
return handleSyscallError(syscall.Close(int(f)))
|
||||
}
|
||||
|
||||
// handleSyscallError converts syscall errors into regular os package errors.
|
||||
// The err parameter must be either nil or of type syscall.Errno.
|
||||
func handleSyscallError(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
switch err.(syscall.Errno) {
|
||||
case syscall.EEXIST:
|
||||
return ErrExist
|
||||
case syscall.ENOENT:
|
||||
return ErrNotExist
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package os
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// mounts lists the mount points currently mounted in the filesystem provided by
|
||||
// the os package. To resolve a path to a mount point, it is scanned from top to
|
||||
// bottom looking for the first prefix match.
|
||||
var mounts []mountPoint
|
||||
|
||||
type mountPoint struct {
|
||||
// prefix is a filesystem prefix, that always starts and ends with a forward
|
||||
// slash. To denote the root filesystem, use a single slash: "/".
|
||||
// This allows fast checking whether a path lies within a mount point.
|
||||
prefix string
|
||||
|
||||
// filesystem is the Filesystem implementation that is mounted at this mount
|
||||
// point.
|
||||
filesystem Filesystem
|
||||
}
|
||||
|
||||
// Filesystem provides an interface for generic filesystem drivers mounted in
|
||||
// the os package. The errors returned must be one of the os.Err* errors, or a
|
||||
// custom error if one doesn't exist. It should not be a *PathError because
|
||||
// errors will be wrapped with a *PathError by the filesystem abstraction.
|
||||
//
|
||||
// WARNING: this interface is not finalized and may change in a future version.
|
||||
type Filesystem interface {
|
||||
// OpenFile opens the named file.
|
||||
OpenFile(name string, flag int, perm FileMode) (FileHandle, error)
|
||||
|
||||
// Mkdir creates a new directoy with the specified permission (before
|
||||
// umask). Some filesystems may not support directories or permissions.
|
||||
Mkdir(name string, perm FileMode) error
|
||||
|
||||
// Remove removes the named file or (empty) directory.
|
||||
Remove(name string) error
|
||||
}
|
||||
|
||||
// FileHandle is an interface that should be implemented by filesystems
|
||||
// implementing the Filesystem interface.
|
||||
//
|
||||
// WARNING: this interface is not finalized and may change in a future version.
|
||||
type FileHandle interface {
|
||||
// Read reads up to len(b) bytes from the file.
|
||||
Read(b []byte) (n int, err error)
|
||||
|
||||
// Write writes up to len(b) bytes to the file.
|
||||
Write(b []byte) (n int, err error)
|
||||
|
||||
// Close closes the file, making it unusable for further writes.
|
||||
Close() (err error)
|
||||
}
|
||||
|
||||
// findMount returns the appropriate (mounted) filesystem to use for a given
|
||||
// filename plus the path relative to that filesystem.
|
||||
func findMount(path string) (Filesystem, string) {
|
||||
for i := len(mounts) - 1; i >= 0; i-- {
|
||||
mount := mounts[i]
|
||||
if strings.HasPrefix(path, mount.prefix) {
|
||||
return mount.filesystem, path[len(mount.prefix)-1:]
|
||||
}
|
||||
}
|
||||
if isOS {
|
||||
// Assume that the first entry in the mounts slice is the OS filesystem
|
||||
// at the root of the directory tree. Use it as-is, to support relative
|
||||
// paths.
|
||||
return mounts[0].filesystem, path
|
||||
}
|
||||
return nil, path
|
||||
}
|
||||
|
||||
// Mount mounts the given filesystem in the filesystem abstraction layer of the
|
||||
// os package. It is not possible to unmount filesystems. Filesystems added
|
||||
// later will override earlier filesystems.
|
||||
//
|
||||
// The provided prefix must start and end with a forward slash. This is true for
|
||||
// the root directory ("/") for example.
|
||||
func Mount(prefix string, filesystem Filesystem) {
|
||||
if prefix[0] != '/' || prefix[len(prefix)-1] != '/' {
|
||||
panic("os.Mount: invalid prefix")
|
||||
}
|
||||
mounts = append(mounts, mountPoint{prefix, filesystem})
|
||||
}
|
||||
@@ -9,6 +9,15 @@ import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Args hold the command-line arguments, starting with the program name.
|
||||
var Args []string
|
||||
|
||||
func init() {
|
||||
Args = runtime_args()
|
||||
}
|
||||
|
||||
func runtime_args() []string // in package runtime
|
||||
|
||||
// Exit causes the current program to exit with the given status code.
|
||||
// Conventionally, code zero indicates success, non-zero an error.
|
||||
// The program terminates immediately; deferred functions are not run.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package os
|
||||
|
||||
func Hostname() (name string, err error) {
|
||||
return "", ErrNotImplemented
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package reflect
|
||||
|
||||
func DeepEqual(x, y interface{}) bool {
|
||||
if x == nil || y == nil {
|
||||
return x == y
|
||||
}
|
||||
|
||||
panic("unimplemented: reflect.DeepEqual()")
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package reflect
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// errSyntax indicates that a value does not have the right syntax for the target type.
|
||||
var errSyntax = badSyntax{}
|
||||
|
||||
type badSyntax struct{}
|
||||
|
||||
func (badSyntax) Error() string {
|
||||
return "invalid syntax"
|
||||
}
|
||||
|
||||
func unhex(b byte) (v rune, ok bool) {
|
||||
c := rune(b)
|
||||
switch {
|
||||
case '0' <= c && c <= '9':
|
||||
return c - '0', true
|
||||
case 'a' <= c && c <= 'f':
|
||||
return c - 'a' + 10, true
|
||||
case 'A' <= c && c <= 'F':
|
||||
return c - 'A' + 10, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// unquoteChar decodes the first character or byte in the escaped string
|
||||
// or character literal represented by the string s.
|
||||
// It returns four values:
|
||||
//
|
||||
// 1) value, the decoded Unicode code point or byte value;
|
||||
// 2) multibyte, a boolean indicating whether the decoded character requires a multibyte UTF-8 representation;
|
||||
// 3) tail, the remainder of the string after the character; and
|
||||
// 4) an error that will be nil if the character is syntactically valid.
|
||||
//
|
||||
// The second argument, quote, specifies the type of literal being parsed
|
||||
// and therefore which escaped quote character is permitted.
|
||||
// If set to a single quote, it permits the sequence \' and disallows unescaped '.
|
||||
// If set to a double quote, it permits \" and disallows unescaped ".
|
||||
// If set to zero, it does not permit either escape and allows both quote characters to appear unescaped.
|
||||
func unquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, err error) {
|
||||
// easy cases
|
||||
if len(s) == 0 {
|
||||
err = errSyntax
|
||||
return
|
||||
}
|
||||
switch c := s[0]; {
|
||||
case c == quote && (quote == '\'' || quote == '"'):
|
||||
err = errSyntax
|
||||
return
|
||||
case c >= utf8.RuneSelf:
|
||||
r, size := utf8.DecodeRuneInString(s)
|
||||
return r, true, s[size:], nil
|
||||
case c != '\\':
|
||||
return rune(s[0]), false, s[1:], nil
|
||||
}
|
||||
|
||||
// hard case: c is backslash
|
||||
if len(s) <= 1 {
|
||||
err = errSyntax
|
||||
return
|
||||
}
|
||||
c := s[1]
|
||||
s = s[2:]
|
||||
|
||||
switch c {
|
||||
case 'a':
|
||||
value = '\a'
|
||||
case 'b':
|
||||
value = '\b'
|
||||
case 'f':
|
||||
value = '\f'
|
||||
case 'n':
|
||||
value = '\n'
|
||||
case 'r':
|
||||
value = '\r'
|
||||
case 't':
|
||||
value = '\t'
|
||||
case 'v':
|
||||
value = '\v'
|
||||
case 'x', 'u', 'U':
|
||||
n := 0
|
||||
switch c {
|
||||
case 'x':
|
||||
n = 2
|
||||
case 'u':
|
||||
n = 4
|
||||
case 'U':
|
||||
n = 8
|
||||
}
|
||||
var v rune
|
||||
if len(s) < n {
|
||||
err = errSyntax
|
||||
return
|
||||
}
|
||||
for j := 0; j < n; j++ {
|
||||
x, ok := unhex(s[j])
|
||||
if !ok {
|
||||
err = errSyntax
|
||||
return
|
||||
}
|
||||
v = v<<4 | x
|
||||
}
|
||||
s = s[n:]
|
||||
if c == 'x' {
|
||||
// single-byte string, possibly not UTF-8
|
||||
value = v
|
||||
break
|
||||
}
|
||||
if v > utf8.MaxRune {
|
||||
err = errSyntax
|
||||
return
|
||||
}
|
||||
value = v
|
||||
multibyte = true
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7':
|
||||
v := rune(c) - '0'
|
||||
if len(s) < 2 {
|
||||
err = errSyntax
|
||||
return
|
||||
}
|
||||
for j := 0; j < 2; j++ { // one digit already; two more
|
||||
x := rune(s[j]) - '0'
|
||||
if x < 0 || x > 7 {
|
||||
err = errSyntax
|
||||
return
|
||||
}
|
||||
v = (v << 3) | x
|
||||
}
|
||||
s = s[2:]
|
||||
if v > 255 {
|
||||
err = errSyntax
|
||||
return
|
||||
}
|
||||
value = v
|
||||
case '\\':
|
||||
value = '\\'
|
||||
case '\'', '"':
|
||||
if c != quote {
|
||||
err = errSyntax
|
||||
return
|
||||
}
|
||||
value = rune(c)
|
||||
default:
|
||||
err = errSyntax
|
||||
return
|
||||
}
|
||||
tail = s
|
||||
return
|
||||
}
|
||||
|
||||
// unquote interprets s as a single-quoted, double-quoted,
|
||||
// or backquoted Go string literal, returning the string value
|
||||
// that s quotes. (If s is single-quoted, it would be a Go
|
||||
// character literal; unquote returns the corresponding
|
||||
// one-character string.)
|
||||
func unquote(s string) (string, error) {
|
||||
n := len(s)
|
||||
if n < 2 {
|
||||
return "", errSyntax
|
||||
}
|
||||
quote := s[0]
|
||||
if quote != s[n-1] {
|
||||
return "", errSyntax
|
||||
}
|
||||
s = s[1 : n-1]
|
||||
|
||||
if quote == '`' {
|
||||
if contains(s, '`') {
|
||||
return "", errSyntax
|
||||
}
|
||||
if contains(s, '\r') {
|
||||
// -1 because we know there is at least one \r to remove.
|
||||
buf := make([]byte, 0, len(s)-1)
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] != '\r' {
|
||||
buf = append(buf, s[i])
|
||||
}
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
if quote != '"' && quote != '\'' {
|
||||
return "", errSyntax
|
||||
}
|
||||
if contains(s, '\n') {
|
||||
return "", errSyntax
|
||||
}
|
||||
|
||||
// Is it trivial? Avoid allocation.
|
||||
if !contains(s, '\\') && !contains(s, quote) {
|
||||
switch quote {
|
||||
case '"':
|
||||
if utf8.ValidString(s) {
|
||||
return s, nil
|
||||
}
|
||||
case '\'':
|
||||
r, size := utf8.DecodeRuneInString(s)
|
||||
if size == len(s) && (r != utf8.RuneError || size != 1) {
|
||||
return s, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var runeTmp [utf8.UTFMax]byte
|
||||
buf := make([]byte, 0, 3*len(s)/2) // Try to avoid more allocations.
|
||||
for len(s) > 0 {
|
||||
c, multibyte, ss, err := unquoteChar(s, quote)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
s = ss
|
||||
if c < utf8.RuneSelf || !multibyte {
|
||||
buf = append(buf, byte(c))
|
||||
} else {
|
||||
n := utf8.EncodeRune(runeTmp[:], c)
|
||||
buf = append(buf, runeTmp[:n]...)
|
||||
}
|
||||
if quote == '\'' && len(s) != 0 {
|
||||
// single-quoted must be single character
|
||||
return "", errSyntax
|
||||
}
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
// contains reports whether the string contains the byte c.
|
||||
func contains(s string, c byte) bool {
|
||||
return indexByteString(s, c) != -1
|
||||
}
|
||||
|
||||
// Index finds the index of the first instance of the specified byte in the string.
|
||||
// If the byte is not found, this returns -1.
|
||||
func indexByteString(s string, c byte) int {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == c {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
+86
-2
@@ -1,3 +1,7 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package reflect
|
||||
|
||||
import (
|
||||
@@ -227,7 +231,7 @@ func (t Type) Field(i int) StructField {
|
||||
// There is a tag.
|
||||
var tagNum uintptr
|
||||
tagNum, p = readVarint(p)
|
||||
field.Tag = readStringSidetable(unsafe.Pointer(&structNamesSidetable), tagNum)
|
||||
field.Tag = StructTag(readStringSidetable(unsafe.Pointer(&structNamesSidetable), tagNum))
|
||||
} else {
|
||||
// There is no tag.
|
||||
field.Tag = ""
|
||||
@@ -445,6 +449,22 @@ func (t Type) Comparable() bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (t Type) ConvertibleTo(u Type) bool {
|
||||
panic("unimplemented: (reflect.Type).ConvertibleTo()")
|
||||
}
|
||||
|
||||
func (t Type) NumMethod() int {
|
||||
panic("unimplemented: (reflect.Type).NumMethod()")
|
||||
}
|
||||
|
||||
func (t Type) Name() string {
|
||||
panic("unimplemented: (reflect.Type).Name()")
|
||||
}
|
||||
|
||||
func (t Type) Key() Type {
|
||||
panic("unimplemented: (reflect.Type).Key()")
|
||||
}
|
||||
|
||||
// A StructField describes a single field in a struct.
|
||||
type StructField struct {
|
||||
// Name indicates the field name.
|
||||
@@ -455,11 +475,75 @@ type StructField struct {
|
||||
PkgPath string
|
||||
|
||||
Type Type
|
||||
Tag string
|
||||
Tag StructTag // field tag string
|
||||
Anonymous bool
|
||||
Offset uintptr
|
||||
}
|
||||
|
||||
// A StructTag is the tag string in a struct field.
|
||||
type StructTag string
|
||||
|
||||
// TODO: it would be feasible to do the key/value splitting at compile time,
|
||||
// avoiding the code size cost of doing it at runtime
|
||||
|
||||
// Get returns the value associated with key in the tag string.
|
||||
func (tag StructTag) Get(key string) string {
|
||||
v, _ := tag.Lookup(key)
|
||||
return v
|
||||
}
|
||||
|
||||
// Lookup returns the value associated with key in the tag string.
|
||||
func (tag StructTag) Lookup(key string) (value string, ok bool) {
|
||||
for tag != "" {
|
||||
// Skip leading space.
|
||||
i := 0
|
||||
for i < len(tag) && tag[i] == ' ' {
|
||||
i++
|
||||
}
|
||||
tag = tag[i:]
|
||||
if tag == "" {
|
||||
break
|
||||
}
|
||||
|
||||
// Scan to colon. A space, a quote or a control character is a syntax error.
|
||||
// Strictly speaking, control chars include the range [0x7f, 0x9f], not just
|
||||
// [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
|
||||
// as it is simpler to inspect the tag's bytes than the tag's runes.
|
||||
i = 0
|
||||
for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
|
||||
i++
|
||||
}
|
||||
if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
|
||||
break
|
||||
}
|
||||
name := string(tag[:i])
|
||||
tag = tag[i+1:]
|
||||
|
||||
// Scan quoted string to find value.
|
||||
i = 1
|
||||
for i < len(tag) && tag[i] != '"' {
|
||||
if tag[i] == '\\' {
|
||||
i++
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i >= len(tag) {
|
||||
break
|
||||
}
|
||||
qvalue := string(tag[:i+1])
|
||||
tag = tag[i+1:]
|
||||
|
||||
if key == name {
|
||||
value, err := unquote(qvalue)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
return value, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// TypeError is the error that is used in a panic when invoking a method on a
|
||||
// type that is not applicable to that type.
|
||||
type TypeError struct {
|
||||
|
||||
+68
-7
@@ -304,29 +304,48 @@ func (v Value) Slice(i, j int) Value {
|
||||
panic("unimplemented: (reflect.Value).Slice()")
|
||||
}
|
||||
|
||||
//go:linkname maplen runtime.hashmapLenUnsafePointer
|
||||
func maplen(p unsafe.Pointer) int
|
||||
|
||||
//go:linkname chanlen runtime.chanLenUnsafePointer
|
||||
func chanlen(p unsafe.Pointer) int
|
||||
|
||||
// Len returns the length of this value for slices, strings, arrays, channels,
|
||||
// and maps. For oter types, it panics.
|
||||
// and maps. For other types, it panics.
|
||||
func (v Value) Len() int {
|
||||
t := v.Type()
|
||||
switch t.Kind() {
|
||||
case Array:
|
||||
return v.Type().Len()
|
||||
case Chan:
|
||||
return chanlen(v.value)
|
||||
case Map:
|
||||
return maplen(v.value)
|
||||
case Slice:
|
||||
return int((*SliceHeader)(v.value).Len)
|
||||
case String:
|
||||
return int((*StringHeader)(v.value).Len)
|
||||
case Array:
|
||||
return v.Type().Len()
|
||||
default: // Chan, Map
|
||||
panic("unimplemented: (reflect.Value).Len()")
|
||||
default:
|
||||
panic(&ValueError{"Len"})
|
||||
}
|
||||
}
|
||||
|
||||
//go:linkname chancap runtime.chanCapUnsafePointer
|
||||
func chancap(p unsafe.Pointer) int
|
||||
|
||||
// Cap returns the capacity of this value for arrays, channels and slices.
|
||||
// For other types, it panics.
|
||||
func (v Value) Cap() int {
|
||||
t := v.Type()
|
||||
switch t.Kind() {
|
||||
case Array:
|
||||
return v.Type().Len()
|
||||
case Chan:
|
||||
return chancap(v.value)
|
||||
case Slice:
|
||||
return int((*SliceHeader)(v.value).Cap)
|
||||
default: // Array, Chan
|
||||
panic("unimplemented: (reflect.Value).Cap()")
|
||||
default:
|
||||
panic(&ValueError{"Cap"})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -642,6 +661,18 @@ func (v Value) checkAddressable() {
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) OverflowInt(x int64) bool {
|
||||
panic("unimplemented: reflect.OverflowInt()")
|
||||
}
|
||||
|
||||
func (v Value) OverflowUint(x uint64) bool {
|
||||
panic("unimplemented: reflect.OverflowUint()")
|
||||
}
|
||||
|
||||
func (v Value) Convert(t Type) Value {
|
||||
panic("unimplemented: (reflect.Value).Convert()")
|
||||
}
|
||||
|
||||
func MakeSlice(typ Type, len, cap int) Value {
|
||||
panic("unimplemented: reflect.MakeSlice()")
|
||||
}
|
||||
@@ -681,3 +712,33 @@ func (e *ValueError) Error() string {
|
||||
// Calls to this function are converted to LLVM intrinsic calls such as
|
||||
// llvm.memcpy.p0i8.p0i8.i32().
|
||||
func memcpy(dst, src unsafe.Pointer, size uintptr)
|
||||
|
||||
// Copy copies the contents of src into dst until either
|
||||
// dst has been filled or src has been exhausted.
|
||||
func Copy(dst, src Value) int {
|
||||
panic("unimplemented: reflect.Copy()")
|
||||
}
|
||||
|
||||
// Append appends the values x to a slice s and returns the resulting slice.
|
||||
// As in Go, each x's value must be assignable to the slice's element type.
|
||||
func Append(s Value, x ...Value) Value {
|
||||
panic("unimplemented: reflect.Append()")
|
||||
}
|
||||
|
||||
func (v Value) SetMapIndex(key, elem Value) {
|
||||
panic("unimplemented: (reflect.Value).SetMapIndex()")
|
||||
}
|
||||
|
||||
// FieldByIndex returns the nested field corresponding to index.
|
||||
func (v Value) FieldByIndex(index []int) Value {
|
||||
panic("unimplemented: (reflect.Value).FieldByIndex()")
|
||||
}
|
||||
|
||||
func (v Value) FieldByName(name string) Value {
|
||||
panic("unimplemented: (reflect.Value).FieldByName()")
|
||||
}
|
||||
|
||||
// MakeMap creates a new map with the specified type.
|
||||
func MakeMap(typ Type) Value {
|
||||
panic("unimplemented: reflect.MakeMap()")
|
||||
}
|
||||
|
||||
@@ -15,5 +15,5 @@ func align(ptr uintptr) uintptr {
|
||||
}
|
||||
|
||||
func getCurrentStackPointer() uintptr {
|
||||
return arm.ReadRegister("sp")
|
||||
return arm.AsmFull("mov {}, sp", nil)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user