mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-06 12:03:41 +00:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fa79f561ac | |||
| 1e9a41dc94 | |||
| a30671751f | |||
| c246978dd7 | |||
| 72acda22b0 | |||
| f880950c3e | |||
| 1bed192de0 | |||
| 896a848001 | |||
| fb03787b73 | |||
| 83a949647f | |||
| 99a41bec4e | |||
| 312f5d3833 | |||
| 35bf0746a1 | |||
| 8d93b9e545 | |||
| b44d41d9ec | |||
| 4be9802d26 | |||
| 90b42799a2 | |||
| 71bbe93ab2 | |||
| 0535c1bbad | |||
| 4f6d598ea8 | |||
| 16e7dd83a3 | |||
| f23ba3b023 | |||
| 2aa2e750b9 | |||
| bcce296ca3 | |||
| c5ec955081 | |||
| 5d334922d7 | |||
| e9f9a4b750 | |||
| 1406453350 | |||
| 6d3c11627c | |||
| f800f7507c | |||
| 46a7993fb8 | |||
| 9f3dcf3733 | |||
| c849bccb83 | |||
| cffe424849 | |||
| 51938e9d1c | |||
| 19dec048b0 | |||
| bbb2909283 | |||
| aa7c7b7bd9 | |||
| c7bd5405c3 | |||
| dc981ce509 | |||
| c522569378 | |||
| 0db4b13e37 | |||
| 2709d38d63 | |||
| 71d1b70ab7 | |||
| 24676d4366 | |||
| e2f532709f | |||
| dc1ff80e10 | |||
| b5205cc3ca | |||
| a075cbedf5 | |||
| ef613a5db7 | |||
| 1571b8fd34 | |||
| a41b72578b | |||
| 9f3f9d05b8 | |||
| f9865a08bc | |||
| 13db2c13e5 | |||
| ce8ad3650a | |||
| b0b84c48ec | |||
| 5a4dcfb367 |
@@ -177,6 +177,11 @@ commands:
|
|||||||
- run:
|
- run:
|
||||||
name: "Test TinyGo"
|
name: "Test TinyGo"
|
||||||
command: make ASSERT=1 test
|
command: make ASSERT=1 test
|
||||||
|
environment:
|
||||||
|
# Note: -p=2 limits parallelism to two jobs at a time, which is
|
||||||
|
# necessary to keep memory consumption down and avoid OOM (for a
|
||||||
|
# 2CPU/4GB executor).
|
||||||
|
GOFLAGS: -p=2
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||||
paths:
|
paths:
|
||||||
|
|||||||
@@ -19,3 +19,10 @@ src/device/kendryte/*.s
|
|||||||
vendor
|
vendor
|
||||||
llvm-build
|
llvm-build
|
||||||
llvm-project
|
llvm-project
|
||||||
|
|
||||||
|
# Ignore files generated by smoketest
|
||||||
|
test.gba
|
||||||
|
test.hex
|
||||||
|
test.nro
|
||||||
|
test.wasm
|
||||||
|
wasm.wasm
|
||||||
@@ -107,7 +107,10 @@ fmt-check:
|
|||||||
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
|
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
|
||||||
|
|
||||||
|
|
||||||
gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32 gen-device-kendryte gen-device-nxp
|
gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-kendryte gen-device-nxp
|
||||||
|
ifneq ($(STM32), 0)
|
||||||
|
gen-device: gen-device-stm32
|
||||||
|
endif
|
||||||
|
|
||||||
gen-device-avr:
|
gen-device-avr:
|
||||||
@if [ ! -e lib/avr/README.md ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
@if [ ! -e lib/avr/README.md ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
||||||
@@ -117,22 +120,22 @@ gen-device-avr:
|
|||||||
@GO111MODULE=off $(GO) fmt ./src/device/avr
|
@GO111MODULE=off $(GO) fmt ./src/device/avr
|
||||||
|
|
||||||
build/gen-device-svd: ./tools/gen-device-svd/*.go
|
build/gen-device-svd: ./tools/gen-device-svd/*.go
|
||||||
$(GO) build -o $@ ./tools/gen-device-svd/
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -o $@ ./tools/gen-device-svd/
|
||||||
|
|
||||||
gen-device-esp: build/gen-device-svd
|
gen-device-esp: build/gen-device-svd
|
||||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Espressif-Community -interrupts=software lib/cmsis-svd/data/Espressif-Community/ src/device/esp/
|
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Espressif-Community -only-used -interrupts=software lib/cmsis-svd/data/Espressif-Community/ src/device/esp/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/esp
|
GO111MODULE=off $(GO) fmt ./src/device/esp
|
||||||
|
|
||||||
gen-device-nrf: build/gen-device-svd
|
gen-device-nrf: build/gen-device-svd
|
||||||
./build/gen-device-svd -source=https://github.com/NordicSemiconductor/nrfx/tree/master/mdk lib/nrfx/mdk/ src/device/nrf/
|
./build/gen-device-svd -source=https://github.com/NordicSemiconductor/nrfx/tree/master/mdk -only-used lib/nrfx/mdk/ src/device/nrf/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/nrf
|
GO111MODULE=off $(GO) fmt ./src/device/nrf
|
||||||
|
|
||||||
gen-device-nxp: build/gen-device-svd
|
gen-device-nxp: build/gen-device-svd
|
||||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/NXP lib/cmsis-svd/data/NXP/ src/device/nxp/
|
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/NXP -only-used lib/cmsis-svd/data/NXP/ src/device/nxp/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/nxp
|
GO111MODULE=off $(GO) fmt ./src/device/nxp
|
||||||
|
|
||||||
gen-device-sam: build/gen-device-svd
|
gen-device-sam: build/gen-device-svd
|
||||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Atmel lib/cmsis-svd/data/Atmel/ src/device/sam/
|
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Atmel -only-used lib/cmsis-svd/data/Atmel/ src/device/sam/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/sam
|
GO111MODULE=off $(GO) fmt ./src/device/sam
|
||||||
|
|
||||||
gen-device-sifive: build/gen-device-svd
|
gen-device-sifive: build/gen-device-svd
|
||||||
@@ -140,11 +143,11 @@ gen-device-sifive: build/gen-device-svd
|
|||||||
GO111MODULE=off $(GO) fmt ./src/device/sifive
|
GO111MODULE=off $(GO) fmt ./src/device/sifive
|
||||||
|
|
||||||
gen-device-kendryte: build/gen-device-svd
|
gen-device-kendryte: build/gen-device-svd
|
||||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Kendryte-Community -interrupts=software lib/cmsis-svd/data/Kendryte-Community/ src/device/kendryte/
|
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Kendryte-Community -only-used -interrupts=software lib/cmsis-svd/data/Kendryte-Community/ src/device/kendryte/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/kendryte
|
GO111MODULE=off $(GO) fmt ./src/device/kendryte
|
||||||
|
|
||||||
gen-device-stm32: build/gen-device-svd
|
gen-device-stm32: build/gen-device-svd
|
||||||
./build/gen-device-svd -source=https://github.com/tinygo-org/stm32-svd lib/stm32-svd/svd src/device/stm32/
|
./build/gen-device-svd -source=https://github.com/tinygo-org/stm32-svd -only-used lib/stm32-svd/svd src/device/stm32/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/stm32
|
GO111MODULE=off $(GO) fmt ./src/device/stm32
|
||||||
|
|
||||||
|
|
||||||
@@ -177,7 +180,7 @@ tinygo:
|
|||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
|
||||||
|
|
||||||
test: wasi-libc
|
test: wasi-libc
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./cgo ./compileopts ./compiler ./interp ./transform .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||||
|
|
||||||
# Test known-working standard library packages.
|
# Test known-working standard library packages.
|
||||||
# TODO: do this in one command, parallelize, and only show failing tests (no
|
# TODO: do this in one command, parallelize, and only show failing tests (no
|
||||||
@@ -257,8 +260,6 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10031 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pca10031 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
|
||||||
@$(MD5SUM) test.hex
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=reelboard examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=reelboard examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=reelboard examples/blinky2
|
$(TINYGO) build -size short -o test.hex -target=reelboard examples/blinky2
|
||||||
@@ -267,6 +268,10 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10056 examples/blinky2
|
$(TINYGO) build -size short -o test.hex -target=pca10056 examples/blinky2
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=pca10059 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=pca10059 examples/blinky2
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=feather-m0 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=feather-m0 examples/blinky1
|
||||||
@@ -275,14 +280,6 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky1
|
|
||||||
@$(MD5SUM) test.hex
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky2
|
|
||||||
@$(MD5SUM) test.hex
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco-1 examples/blinky1
|
|
||||||
@$(MD5SUM) test.hex
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=feather-stm32f405 examples/blinky1
|
|
||||||
@$(MD5SUM) test.hex
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-bluefruit examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=circuitplay-bluefruit examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
|
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
|
||||||
@@ -307,12 +304,8 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=particle-xenon examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=particle-xenon examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=nucleo-f103rb examples/blinky1
|
|
||||||
@$(MD5SUM) test.hex
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=pinetime-devkit0 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pinetime-devkit0 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=lgt92 examples/blinky1
|
|
||||||
@$(MD5SUM) test.hex
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=x9pro examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=x9pro examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10056-s140v7 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pca10056-s140v7 examples/blinky1
|
||||||
@@ -339,10 +332,6 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=teensy36 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=teensy36 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1
|
|
||||||
@$(MD5SUM) test.hex
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l552ze examples/blinky1
|
|
||||||
@$(MD5SUM) test.hex
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=p1am-100 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=p1am-100 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
# test pwm
|
# test pwm
|
||||||
@@ -352,8 +341,30 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
|
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pyportal examples/pwm
|
ifneq ($(STM32), 0)
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=feather-stm32f405 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=lgt92 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=nucleo-f103rb examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=nucleo-l031k6 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=nucleo-l432kc examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=nucleo-l552ze examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky2
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=stm32f4disco-1 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
endif
|
||||||
ifneq ($(AVR), 0)
|
ifneq ($(AVR), 0)
|
||||||
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
|
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
@@ -391,6 +402,9 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
|
$(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
|
||||||
@$(MD5SUM) test.nro
|
@$(MD5SUM) test.nro
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
|
||||||
|
|
||||||
wasmtest:
|
wasmtest:
|
||||||
$(GO) test ./tests/wasm
|
$(GO) test ./tests/wasm
|
||||||
|
|||||||
+292
-99
@@ -4,16 +4,19 @@
|
|||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha512"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/types"
|
"go/types"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
@@ -38,13 +41,41 @@ type BuildResult struct {
|
|||||||
MainDir string
|
MainDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// packageAction is the struct that is serialized to JSON and hashed, to work as
|
||||||
|
// a cache key of compiled packages. It should contain all the information that
|
||||||
|
// goes into a compiled package to avoid using stale data.
|
||||||
|
//
|
||||||
|
// Right now it's still important to include a hash of every import, because a
|
||||||
|
// dependency might have a public constant that this package uses and thus this
|
||||||
|
// package will need to be recompiled if that constant changes. In the future,
|
||||||
|
// the type data should be serialized to disk which can then be used as cache
|
||||||
|
// key, avoiding the need for recompiling all dependencies when only the
|
||||||
|
// implementation of an imported package changes.
|
||||||
|
type packageAction struct {
|
||||||
|
ImportPath string
|
||||||
|
CompilerVersion int // compiler.Version
|
||||||
|
InterpVersion int // interp.Version
|
||||||
|
LLVMVersion string
|
||||||
|
Config *compiler.Config
|
||||||
|
CFlags []string
|
||||||
|
FileHashes map[string]string // hash of every file that's part of the package
|
||||||
|
Imports map[string]string // map from imported package to action ID hash
|
||||||
|
}
|
||||||
|
|
||||||
// Build performs a single package to executable Go build. It takes in a package
|
// Build performs a single package to executable Go build. It takes in a package
|
||||||
// name, an output path, and set of compile options and from that it manages the
|
// name, an output path, and set of compile options and from that it manages the
|
||||||
// whole compilation process.
|
// whole compilation process.
|
||||||
//
|
//
|
||||||
// The error value may be of type *MultiError. Callers will likely want to check
|
// The error value may be of type *MultiError. Callers will likely want to check
|
||||||
// for this case and print such errors individually.
|
// for this case and print such errors individually.
|
||||||
func Build(pkgName, outpath string, config *compileopts.Config, preAction func() error, action func(BuildResult) error) error {
|
func Build(pkgName, outpath string, config *compileopts.Config, action func(BuildResult) error) error {
|
||||||
|
// Create a temporary directory for intermediary files.
|
||||||
|
dir, err := ioutil.TempDir("", "tinygo")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
compilerConfig := &compiler.Config{
|
compilerConfig := &compiler.Config{
|
||||||
Triple: config.Triple(),
|
Triple: config.Triple(),
|
||||||
CPU: config.CPU(),
|
CPU: config.CPU(),
|
||||||
@@ -87,31 +118,216 @@ func Build(pkgName, outpath string, config *compileopts.Config, preAction func()
|
|||||||
// Makefile target.
|
// Makefile target.
|
||||||
var jobs []*compileJob
|
var jobs []*compileJob
|
||||||
|
|
||||||
if preAction != nil {
|
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
||||||
// Add job to preAction.
|
// program so it's pretty fast and doesn't need to be parallelized.
|
||||||
jobs = append(jobs, &compileJob{
|
program := lprogram.LoadSSA()
|
||||||
description: "preAction",
|
|
||||||
run: preAction,
|
// Add jobs to compile each package.
|
||||||
})
|
// Packages that have a cache hit will not be compiled again.
|
||||||
|
var packageJobs []*compileJob
|
||||||
|
packageBitcodePaths := make(map[string]string)
|
||||||
|
packageActionIDs := make(map[string]string)
|
||||||
|
for _, pkg := range lprogram.Sorted() {
|
||||||
|
pkg := pkg // necessary to avoid a race condition
|
||||||
|
|
||||||
|
// Create a cache key: a hash from the action ID below that contains all
|
||||||
|
// the parameters for the build.
|
||||||
|
actionID := packageAction{
|
||||||
|
ImportPath: pkg.ImportPath,
|
||||||
|
CompilerVersion: compiler.Version,
|
||||||
|
InterpVersion: interp.Version,
|
||||||
|
LLVMVersion: llvm.Version,
|
||||||
|
Config: compilerConfig,
|
||||||
|
CFlags: pkg.CFlags,
|
||||||
|
FileHashes: make(map[string]string, len(pkg.FileHashes)),
|
||||||
|
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
||||||
|
}
|
||||||
|
for filePath, hash := range pkg.FileHashes {
|
||||||
|
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
|
||||||
|
}
|
||||||
|
for _, imported := range pkg.Pkg.Imports() {
|
||||||
|
hash, ok := packageActionIDs[imported.Path()]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
|
||||||
|
}
|
||||||
|
actionID.Imports[imported.Path()] = hash
|
||||||
|
}
|
||||||
|
buf, err := json.Marshal(actionID)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // shouldn't happen
|
||||||
|
}
|
||||||
|
hash := sha512.Sum512_224(buf)
|
||||||
|
packageActionIDs[pkg.ImportPath] = hex.EncodeToString(hash[:])
|
||||||
|
|
||||||
|
// Determine the path of the bitcode file (which is a serialized version
|
||||||
|
// of a LLVM module).
|
||||||
|
cacheDir := goenv.Get("GOCACHE")
|
||||||
|
if cacheDir == "off" {
|
||||||
|
// Use temporary build directory instead, effectively disabling the
|
||||||
|
// build cache.
|
||||||
|
cacheDir = dir
|
||||||
|
}
|
||||||
|
bitcodePath := filepath.Join(cacheDir, "pkg-"+hex.EncodeToString(hash[:])+".bc")
|
||||||
|
packageBitcodePaths[pkg.ImportPath] = bitcodePath
|
||||||
|
|
||||||
|
// Check whether this package has been compiled before, and if so don't
|
||||||
|
// compile it again.
|
||||||
|
if _, err := os.Stat(bitcodePath); err == nil {
|
||||||
|
// Already cached, don't recreate this package.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// The package has not yet been compiled, so create a job to do so.
|
||||||
|
job := &compileJob{
|
||||||
|
description: "compile package " + pkg.ImportPath,
|
||||||
|
run: func(*compileJob) error {
|
||||||
|
// Compile AST to IR. The compiler.CompilePackage function will
|
||||||
|
// build the SSA as needed.
|
||||||
|
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
|
||||||
|
if errs != nil {
|
||||||
|
return newMultiError(errs)
|
||||||
|
}
|
||||||
|
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||||
|
return errors.New("verification error after compiling package " + pkg.ImportPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to interpret package initializers at compile time.
|
||||||
|
// It may only be possible to do this partially, in which case
|
||||||
|
// it is completed after all IR files are linked.
|
||||||
|
pkgInit := mod.NamedFunction(pkg.Pkg.Path() + ".init")
|
||||||
|
if pkgInit.IsNil() {
|
||||||
|
panic("init not found for " + pkg.Pkg.Path())
|
||||||
|
}
|
||||||
|
err := interp.RunFunc(pkgInit, config.DumpSSA())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||||
|
return errors.New("verification error after interpreting " + pkgInit.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize the LLVM module as a bitcode file.
|
||||||
|
// Write to a temporary path that is renamed to the destination
|
||||||
|
// file to avoid race conditions with other TinyGo invocatiosn
|
||||||
|
// that might also be compiling this package at the same time.
|
||||||
|
f, err := ioutil.TempFile(filepath.Dir(bitcodePath), filepath.Base(bitcodePath))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
// Work around a problem on Windows.
|
||||||
|
// For some reason, WriteBitcodeToFile causes TinyGo to
|
||||||
|
// exit with the following message:
|
||||||
|
// LLVM ERROR: IO failure on output stream: Bad file descriptor
|
||||||
|
buf := llvm.WriteBitcodeToMemoryBuffer(mod)
|
||||||
|
defer buf.Dispose()
|
||||||
|
_, err = f.Write(buf.Bytes())
|
||||||
|
} else {
|
||||||
|
// Otherwise, write bitcode directly to the file (probably
|
||||||
|
// faster).
|
||||||
|
err = llvm.WriteBitcodeToFile(mod, f)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
// WriteBitcodeToFile doesn't produce a useful error on its
|
||||||
|
// own, so create a somewhat useful error message here.
|
||||||
|
return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, bitcodePath)
|
||||||
|
}
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Rename(f.Name(), bitcodePath)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
jobs = append(jobs, job)
|
||||||
|
packageJobs = append(packageJobs, job)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add job to compile and optimize all Go files at once.
|
// Add job that links and optimizes all packages together.
|
||||||
// TODO: parallelize this.
|
|
||||||
var mod llvm.Module
|
var mod llvm.Module
|
||||||
var stackSizeLoads []string
|
var stackSizeLoads []string
|
||||||
programJob := &compileJob{
|
programJob := &compileJob{
|
||||||
description: "compile Go files",
|
description: "link+optimize packages (LTO)",
|
||||||
run: func() (err error) {
|
dependencies: packageJobs,
|
||||||
mod, err = compileWholeProgram(pkgName, config, compilerConfig, lprogram, machine)
|
run: func(*compileJob) error {
|
||||||
if err != nil {
|
// Load and link all the bitcode files. This does not yet optimize
|
||||||
return
|
// anything, it only links the bitcode files together.
|
||||||
|
ctx := llvm.NewContext()
|
||||||
|
mod = ctx.NewModule("")
|
||||||
|
for _, pkg := range lprogram.Sorted() {
|
||||||
|
pkgMod, err := ctx.ParseBitcodeFile(packageBitcodePaths[pkg.ImportPath])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to load bitcode file: %w", err)
|
||||||
|
}
|
||||||
|
err = llvm.LinkModules(mod, pkgMod)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to link module: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create runtime.initAll function that calls the runtime
|
||||||
|
// initializer of each package.
|
||||||
|
llvmInitFn := mod.NamedFunction("runtime.initAll")
|
||||||
|
llvmInitFn.SetLinkage(llvm.InternalLinkage)
|
||||||
|
llvmInitFn.SetUnnamedAddr(true)
|
||||||
|
llvmInitFn.Param(0).SetName("context")
|
||||||
|
llvmInitFn.Param(1).SetName("parentHandle")
|
||||||
|
block := mod.Context().AddBasicBlock(llvmInitFn, "entry")
|
||||||
|
irbuilder := mod.Context().NewBuilder()
|
||||||
|
defer irbuilder.Dispose()
|
||||||
|
irbuilder.SetInsertPointAtEnd(block)
|
||||||
|
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||||
|
for _, pkg := range lprogram.Sorted() {
|
||||||
|
pkgInit := mod.NamedFunction(pkg.Pkg.Path() + ".init")
|
||||||
|
if pkgInit.IsNil() {
|
||||||
|
panic("init not found for " + pkg.Pkg.Path())
|
||||||
|
}
|
||||||
|
irbuilder.CreateCall(pkgInit, []llvm.Value{llvm.Undef(i8ptrType), llvm.Undef(i8ptrType)}, "")
|
||||||
|
}
|
||||||
|
irbuilder.CreateRetVoid()
|
||||||
|
|
||||||
|
// After linking, functions should (as far as possible) be set to
|
||||||
|
// private linkage or internal linkage. The compiler package marks
|
||||||
|
// non-exported functions by setting the visibility to hidden or
|
||||||
|
// (for thunks) to linkonce_odr linkage. Change the linkage here to
|
||||||
|
// internal to benefit much more from interprocedural optimizations.
|
||||||
|
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||||
|
if fn.Visibility() == llvm.HiddenVisibility {
|
||||||
|
fn.SetVisibility(llvm.DefaultVisibility)
|
||||||
|
fn.SetLinkage(llvm.InternalLinkage)
|
||||||
|
} else if fn.Linkage() == llvm.LinkOnceODRLinkage {
|
||||||
|
fn.SetLinkage(llvm.InternalLinkage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the same for globals.
|
||||||
|
for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
||||||
|
if global.Visibility() == llvm.HiddenVisibility {
|
||||||
|
global.SetVisibility(llvm.DefaultVisibility)
|
||||||
|
global.SetLinkage(llvm.InternalLinkage)
|
||||||
|
} else if global.Linkage() == llvm.LinkOnceODRLinkage {
|
||||||
|
global.SetLinkage(llvm.InternalLinkage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Options.PrintIR {
|
||||||
|
fmt.Println("; Generated LLVM IR:")
|
||||||
|
fmt.Println(mod.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run all optimization passes, which are much more effective now
|
||||||
|
// that the optimizer can see the whole program at once.
|
||||||
|
err := optimizeProgram(mod, config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure stack sizes are loaded from a separate section so they can be
|
// Make sure stack sizes are loaded from a separate section so they can be
|
||||||
// modified after linking.
|
// modified after linking.
|
||||||
if config.AutomaticStackSize() {
|
if config.AutomaticStackSize() {
|
||||||
stackSizeLoads = transform.CreateStackSizeLoads(mod, config)
|
stackSizeLoads = transform.CreateStackSizeLoads(mod, config)
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
jobs = append(jobs, programJob)
|
jobs = append(jobs, programJob)
|
||||||
@@ -148,19 +364,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, preAction func()
|
|||||||
// First add all jobs necessary to build this object file, then afterwards
|
// First add all jobs necessary to build this object file, then afterwards
|
||||||
// run all jobs in parallel as far as possible.
|
// run all jobs in parallel as far as possible.
|
||||||
|
|
||||||
// Create a temporary directory for intermediary files.
|
|
||||||
dir, err := ioutil.TempDir("", "tinygo")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
// Add job to write the output object file.
|
// Add job to write the output object file.
|
||||||
objfile := filepath.Join(dir, "main.o")
|
objfile := filepath.Join(dir, "main.o")
|
||||||
outputObjectFileJob := &compileJob{
|
outputObjectFileJob := &compileJob{
|
||||||
description: "generate output file",
|
description: "generate output file",
|
||||||
dependencies: []*compileJob{programJob},
|
dependencies: []*compileJob{programJob},
|
||||||
run: func() error {
|
result: objfile,
|
||||||
|
run: func(*compileJob) error {
|
||||||
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
|
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -174,40 +384,32 @@ func Build(pkgName, outpath string, config *compileopts.Config, preAction func()
|
|||||||
linkerDependencies := []*compileJob{outputObjectFileJob}
|
linkerDependencies := []*compileJob{outputObjectFileJob}
|
||||||
executable := filepath.Join(dir, "main")
|
executable := filepath.Join(dir, "main")
|
||||||
tmppath := executable // final file
|
tmppath := executable // final file
|
||||||
ldflags := append(config.LDFlags(), "-o", executable, objfile)
|
ldflags := append(config.LDFlags(), "-o", executable)
|
||||||
|
|
||||||
// Add compiler-rt dependency if needed. Usually this is a simple load from
|
// Add compiler-rt dependency if needed. Usually this is a simple load from
|
||||||
// a cache.
|
// a cache.
|
||||||
if config.Target.RTLib == "compiler-rt" {
|
if config.Target.RTLib == "compiler-rt" {
|
||||||
path, job, err := CompilerRT.load(config.Triple(), config.CPU(), dir)
|
job, err := CompilerRT.load(config.Triple(), config.CPU(), dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if job != nil {
|
jobs = append(jobs, job.dependencies...)
|
||||||
// The library was not loaded from cache so needs to be compiled
|
jobs = append(jobs, job)
|
||||||
// (and then stored in the cache).
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
jobs = append(jobs, job.dependencies...)
|
|
||||||
jobs = append(jobs, job)
|
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
|
||||||
}
|
|
||||||
ldflags = append(ldflags, path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add libc dependency if needed.
|
// Add libc dependency if needed.
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
switch config.Target.Libc {
|
switch config.Target.Libc {
|
||||||
case "picolibc":
|
case "picolibc":
|
||||||
path, job, err := Picolibc.load(config.Triple(), config.CPU(), dir)
|
job, err := Picolibc.load(config.Triple(), config.CPU(), dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if job != nil {
|
// The library needs to be compiled (cache miss).
|
||||||
// The library needs to be compiled (cache miss).
|
jobs = append(jobs, job.dependencies...)
|
||||||
jobs = append(jobs, job.dependencies...)
|
jobs = append(jobs, job)
|
||||||
jobs = append(jobs, job)
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
|
||||||
}
|
|
||||||
ldflags = append(ldflags, path)
|
|
||||||
case "wasi-libc":
|
case "wasi-libc":
|
||||||
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
@@ -223,44 +425,37 @@ func Build(pkgName, outpath string, config *compileopts.Config, preAction func()
|
|||||||
// Add jobs to compile extra files. These files are in C or assembly and
|
// Add jobs to compile extra files. These files are in C or assembly and
|
||||||
// contain things like the interrupt vector table and low level operations
|
// contain things like the interrupt vector table and low level operations
|
||||||
// such as stack switching.
|
// such as stack switching.
|
||||||
for i, path := range config.ExtraFiles() {
|
for _, path := range config.ExtraFiles() {
|
||||||
abspath := filepath.Join(root, path)
|
abspath := filepath.Join(root, path)
|
||||||
outpath := filepath.Join(dir, "extra-"+strconv.Itoa(i)+"-"+filepath.Base(path)+".o")
|
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
description: "compile extra file " + path,
|
description: "compile extra file " + path,
|
||||||
run: func() error {
|
run: func(job *compileJob) error {
|
||||||
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, abspath)...)
|
result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config)
|
||||||
if err != nil {
|
job.result = result
|
||||||
return &commandError{"failed to build", path, err}
|
return err
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
jobs = append(jobs, job)
|
jobs = append(jobs, job)
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
ldflags = append(ldflags, outpath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add jobs to compile C files in all packages. This is part of CGo.
|
// Add jobs to compile C files in all packages. This is part of CGo.
|
||||||
// TODO: do this as part of building the package to be able to link the
|
// TODO: do this as part of building the package to be able to link the
|
||||||
// bitcode files together.
|
// bitcode files together.
|
||||||
for i, pkg := range lprogram.Sorted() {
|
for _, pkg := range lprogram.Sorted() {
|
||||||
for j, filename := range pkg.CFiles {
|
pkg := pkg
|
||||||
file := filepath.Join(pkg.Dir, filename)
|
for _, filename := range pkg.CFiles {
|
||||||
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"."+strconv.Itoa(j)+"-"+filepath.Base(file)+".o")
|
abspath := filepath.Join(pkg.Dir, filename)
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
description: "compile CGo file " + file,
|
description: "compile CGo file " + abspath,
|
||||||
run: func() error {
|
run: func(job *compileJob) error {
|
||||||
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...)
|
result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config)
|
||||||
if err != nil {
|
job.result = result
|
||||||
return &commandError{"failed to build", file, err}
|
return err
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
jobs = append(jobs, job)
|
jobs = append(jobs, job)
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
ldflags = append(ldflags, outpath)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,7 +470,16 @@ func Build(pkgName, outpath string, config *compileopts.Config, preAction func()
|
|||||||
jobs = append(jobs, &compileJob{
|
jobs = append(jobs, &compileJob{
|
||||||
description: "link",
|
description: "link",
|
||||||
dependencies: linkerDependencies,
|
dependencies: linkerDependencies,
|
||||||
run: func() error {
|
run: func(job *compileJob) error {
|
||||||
|
for _, dependency := range job.dependencies {
|
||||||
|
if dependency.result == "" {
|
||||||
|
return errors.New("dependency without result: " + dependency.description)
|
||||||
|
}
|
||||||
|
ldflags = append(ldflags, dependency.result)
|
||||||
|
}
|
||||||
|
if config.Options.PrintCommands {
|
||||||
|
fmt.Printf("%s %s\n", config.Target.Linker, strings.Join(ldflags, " "))
|
||||||
|
}
|
||||||
err = link(config.Target.Linker, ldflags...)
|
err = link(config.Target.Linker, ldflags...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to link", executable, err}
|
return &commandError{"failed to link", executable, err}
|
||||||
@@ -374,29 +578,25 @@ func Build(pkgName, outpath string, config *compileopts.Config, preAction func()
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// compileWholeProgram compiles the entire *loader.Program to a LLVM module and
|
// optimizeProgram runs a series of optimizations and transformations that are
|
||||||
// applies most necessary optimizations and transformations.
|
// needed to convert a program to its final form. Some transformations are not
|
||||||
func compileWholeProgram(pkgName string, config *compileopts.Config, compilerConfig *compiler.Config, lprogram *loader.Program, machine llvm.TargetMachine) (llvm.Module, error) {
|
// optional and must be run as the compiler expects them to run.
|
||||||
// Compile AST to IR.
|
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||||
mod, errs := compiler.CompileProgram(lprogram, machine, compilerConfig, config.DumpSSA())
|
|
||||||
if errs != nil {
|
|
||||||
return mod, newMultiError(errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.Options.PrintIR {
|
|
||||||
fmt.Println("; Generated LLVM IR:")
|
|
||||||
fmt.Println(mod.String())
|
|
||||||
}
|
|
||||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
|
||||||
return mod, errors.New("verification error after IR construction")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := interp.Run(mod, config.DumpSSA())
|
err := interp.Run(mod, config.DumpSSA())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mod, err
|
return err
|
||||||
}
|
}
|
||||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
if config.VerifyIR() {
|
||||||
return mod, errors.New("verification error after interpreting runtime.initAll")
|
// Only verify if we really need it.
|
||||||
|
// The IR has already been verified before writing the bitcode to disk
|
||||||
|
// and the interp function above doesn't need to do a lot as most of the
|
||||||
|
// package initializers have already run. Additionally, verifying this
|
||||||
|
// linked IR is _expensive_ because dead code hasn't been removed yet,
|
||||||
|
// easily costing a few hundred milliseconds. Therefore, only do it when
|
||||||
|
// specifically requested.
|
||||||
|
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||||
|
return errors.New("verification error after interpreting runtime.initAll")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.GOOS() != "darwin" {
|
if config.GOOS() != "darwin" {
|
||||||
@@ -411,23 +611,16 @@ func compileWholeProgram(pkgName string, config *compileopts.Config, compilerCon
|
|||||||
if config.WasmAbi() == "js" {
|
if config.WasmAbi() == "js" {
|
||||||
err := transform.ExternalInt64AsPtr(mod)
|
err := transform.ExternalInt64AsPtr(mod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mod, err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimization levels here are roughly the same as Clang, but probably not
|
// Optimization levels here are roughly the same as Clang, but probably not
|
||||||
// exactly.
|
// exactly.
|
||||||
errs = nil
|
var errs []error
|
||||||
switch config.Options.Opt {
|
switch config.Options.Opt {
|
||||||
/*
|
case "none", "0":
|
||||||
Currently, turning optimizations off causes compile failures.
|
errs = transform.Optimize(mod, config, 0, 0, 0) // -O0
|
||||||
We rely on the optimizer removing some dead symbols.
|
|
||||||
Avoid providing an option that does not work right now.
|
|
||||||
In the future once everything has been fixed we can re-enable this.
|
|
||||||
|
|
||||||
case "none", "0":
|
|
||||||
errs = transform.Optimize(mod, config, 0, 0, 0) // -O0
|
|
||||||
*/
|
|
||||||
case "1":
|
case "1":
|
||||||
errs = transform.Optimize(mod, config, 1, 0, 0) // -O1
|
errs = transform.Optimize(mod, config, 1, 0, 0) // -O1
|
||||||
case "2":
|
case "2":
|
||||||
@@ -437,13 +630,13 @@ func compileWholeProgram(pkgName string, config *compileopts.Config, compilerCon
|
|||||||
case "z":
|
case "z":
|
||||||
errs = transform.Optimize(mod, config, 2, 2, 5) // -Oz, default
|
errs = transform.Optimize(mod, config, 2, 2, 5) // -Oz, default
|
||||||
default:
|
default:
|
||||||
errs = []error{errors.New("unknown optimization level: -opt=" + config.Options.Opt)}
|
return errors.New("unknown optimization level: -opt=" + config.Options.Opt)
|
||||||
}
|
}
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return mod, newMultiError(errs)
|
return newMultiError(errs)
|
||||||
}
|
}
|
||||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||||
return mod, errors.New("verification failure after LLVM optimization passes")
|
return errors.New("verification failure after LLVM optimization passes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// LLVM 11 by default tries to emit tail calls (even with the target feature
|
// LLVM 11 by default tries to emit tail calls (even with the target feature
|
||||||
@@ -457,7 +650,7 @@ func compileWholeProgram(pkgName string, config *compileopts.Config, compilerCon
|
|||||||
transform.DisableTailCalls(mod)
|
transform.DisableTailCalls(mod)
|
||||||
}
|
}
|
||||||
|
|
||||||
return mod, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// functionStackSizes keeps stack size information about a single function
|
// functionStackSizes keeps stack size information about a single function
|
||||||
|
|||||||
+305
@@ -0,0 +1,305 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
// This file implements a wrapper around the C compiler (Clang) which uses a
|
||||||
|
// build cache.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha512"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
|
"tinygo.org/x/go-llvm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// compileAndCacheCFile compiles a C or assembly file using a build cache.
|
||||||
|
// Compiling the same file again (if nothing changed, including included header
|
||||||
|
// files) the output is loaded from the build cache instead.
|
||||||
|
//
|
||||||
|
// Its operation is a bit complex (more complex than Go package build caching)
|
||||||
|
// because the list of file dependencies is only known after the file is
|
||||||
|
// compiled. However, luckily compilers have a flag to write a list of file
|
||||||
|
// dependencies in Makefile syntax which can be used for caching.
|
||||||
|
//
|
||||||
|
// Because of this complexity, every file has in fact two cached build outputs:
|
||||||
|
// the file itself, and the list of dependencies. Its operation is as follows:
|
||||||
|
//
|
||||||
|
// depfile = hash(path, compiler, cflags, ...)
|
||||||
|
// if depfile exists:
|
||||||
|
// outfile = hash of all files and depfile name
|
||||||
|
// if outfile exists:
|
||||||
|
// # cache hit
|
||||||
|
// return outfile
|
||||||
|
// # cache miss
|
||||||
|
// tmpfile = compile file
|
||||||
|
// read dependencies (side effect of compile)
|
||||||
|
// write depfile
|
||||||
|
// outfile = hash of all files and depfile name
|
||||||
|
// rename tmpfile to outfile
|
||||||
|
//
|
||||||
|
// There are a few edge cases that are not handled:
|
||||||
|
// - If a file is added to an include path, that file may be included instead of
|
||||||
|
// some other file. This would be fixed by also including lookup failures in the
|
||||||
|
// dependencies file, but I'm not aware of a compiler which does that.
|
||||||
|
// - The Makefile syntax that compilers output has issues, see readDepFile for
|
||||||
|
// details.
|
||||||
|
// - A header file may be changed to add/remove an include. This invalidates the
|
||||||
|
// depfile but without invalidating its name. For this reason, the depfile is
|
||||||
|
// written on each new compilation (even when it seems unnecessary). However, it
|
||||||
|
// could in rare cases lead to a stale file fetched from the cache.
|
||||||
|
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, config *compileopts.Config) (string, error) {
|
||||||
|
// Hash input file.
|
||||||
|
fileHash, err := hashFile(abspath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create cache key for the dependencies file.
|
||||||
|
buf, err := json.Marshal(struct {
|
||||||
|
Path string
|
||||||
|
Hash string
|
||||||
|
Compiler string
|
||||||
|
Flags []string
|
||||||
|
LLVMVersion string
|
||||||
|
}{
|
||||||
|
Path: abspath,
|
||||||
|
Hash: fileHash,
|
||||||
|
Compiler: config.Target.Compiler,
|
||||||
|
Flags: config.CFlags(),
|
||||||
|
LLVMVersion: llvm.Version,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // shouldn't happen
|
||||||
|
}
|
||||||
|
depfileNameHashBuf := sha512.Sum512_224(buf)
|
||||||
|
depfileNameHash := hex.EncodeToString(depfileNameHashBuf[:])
|
||||||
|
|
||||||
|
// Load dependencies file, if possible.
|
||||||
|
depfileName := "dep-" + depfileNameHash + ".json"
|
||||||
|
depfileCachePath := filepath.Join(goenv.Get("GOCACHE"), depfileName)
|
||||||
|
depfileBuf, err := ioutil.ReadFile(depfileCachePath)
|
||||||
|
var dependencies []string // sorted list of dependency paths
|
||||||
|
if err == nil {
|
||||||
|
// There is a dependency file, that's great!
|
||||||
|
// Parse it first.
|
||||||
|
err := json.Unmarshal(depfileBuf, &dependencies)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not parse dependencies JSON: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtain hashes of all the files listed as a dependency.
|
||||||
|
outpath, err := makeCFileCachePath(dependencies, depfileNameHash)
|
||||||
|
if err == nil {
|
||||||
|
if _, err := os.Stat(outpath); err == nil {
|
||||||
|
return outpath, nil
|
||||||
|
} else if !os.IsNotExist(err) {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if !os.IsNotExist(err) {
|
||||||
|
// expected either nil or IsNotExist
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
objTmpFile, err := ioutil.TempFile(goenv.Get("GOCACHE"), "tmp-*.o")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
objTmpFile.Close()
|
||||||
|
depTmpFile, err := ioutil.TempFile(tmpdir, "dep-*.d")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
depTmpFile.Close()
|
||||||
|
flags := append([]string{}, cflags...) // copy cflags
|
||||||
|
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name()) // autogenerate dependencies
|
||||||
|
flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath)
|
||||||
|
if config.Options.PrintCommands {
|
||||||
|
fmt.Printf("%s %s\n", config.Target.Compiler, strings.Join(flags, " "))
|
||||||
|
}
|
||||||
|
err = runCCompiler(config.Target.Compiler, flags...)
|
||||||
|
if err != nil {
|
||||||
|
return "", &commandError{"failed to build", abspath, err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create sorted and uniqued slice of dependencies.
|
||||||
|
dependencyPaths, err := readDepFile(depTmpFile.Name())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
dependencyPaths = append(dependencyPaths, abspath) // necessary for .s files
|
||||||
|
dependencySet := make(map[string]struct{}, len(dependencyPaths))
|
||||||
|
var dependencySlice []string
|
||||||
|
for _, path := range dependencyPaths {
|
||||||
|
if _, ok := dependencySet[path]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dependencySet[path] = struct{}{}
|
||||||
|
dependencySlice = append(dependencySlice, path)
|
||||||
|
}
|
||||||
|
sort.Strings(dependencySlice)
|
||||||
|
|
||||||
|
// Write dependencies file.
|
||||||
|
f, err := ioutil.TempFile(filepath.Dir(depfileCachePath), depfileName)
|
||||||
|
buf, err = json.MarshalIndent(dependencySlice, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // shouldn't happen
|
||||||
|
}
|
||||||
|
_, err = f.Write(buf)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
err = os.Rename(f.Name(), depfileCachePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move temporary object file to final location.
|
||||||
|
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
err = os.Rename(objTmpFile.Name(), outpath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return outpath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a cache path (a path in GOCACHE) to store the output of a compiler
|
||||||
|
// job. This path is based on the dep file name (which is a hash of metadata
|
||||||
|
// including compiler flags) and the hash of all input files in the paths slice.
|
||||||
|
func makeCFileCachePath(paths []string, depfileNameHash string) (string, error) {
|
||||||
|
// Hash all input files.
|
||||||
|
fileHashes := make(map[string]string, len(paths))
|
||||||
|
for _, path := range paths {
|
||||||
|
hash, err := hashFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
fileHashes[path] = hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate a cache key based on the above hashes.
|
||||||
|
buf, err := json.Marshal(struct {
|
||||||
|
DepfileHash string
|
||||||
|
FileHashes map[string]string
|
||||||
|
}{
|
||||||
|
DepfileHash: depfileNameHash,
|
||||||
|
FileHashes: fileHashes,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // shouldn't happen
|
||||||
|
}
|
||||||
|
outFileNameBuf := sha512.Sum512_224(buf)
|
||||||
|
cacheKey := hex.EncodeToString(outFileNameBuf[:])
|
||||||
|
|
||||||
|
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+".o")
|
||||||
|
return outpath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashFile hashes the given file path and returns the hash as a hex string.
|
||||||
|
func hashFile(path string) (string, error) {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to hash file: %w", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
fileHasher := sha512.New512_224()
|
||||||
|
_, err = io.Copy(fileHasher, f)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to hash file: %w", err)
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(fileHasher.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readDepFile reads a dependency file in NMake (Visual Studio make) format. The
|
||||||
|
// file is assumed to have a single target named deps.
|
||||||
|
//
|
||||||
|
// There are roughly three make syntax variants:
|
||||||
|
// - BSD make, which doesn't support any escaping. This means that many special
|
||||||
|
// characters are not supported in file names.
|
||||||
|
// - GNU make, which supports escaping using a backslash but when it fails to
|
||||||
|
// find a file it tries to fall back with the literal path name (to match BSD
|
||||||
|
// make).
|
||||||
|
// - NMake (Visual Studio) and Jom, which simply quote the string if there are
|
||||||
|
// any weird characters.
|
||||||
|
// Clang supports two variants: a format that's a compromise between BSD and GNU
|
||||||
|
// make (and is buggy to match GCC which is equally buggy), and NMake/Jom, which
|
||||||
|
// is at least somewhat sane. This last format isn't perfect either: it does not
|
||||||
|
// correctly handle filenames with quote marks in them. Those are generally not
|
||||||
|
// allowed on Windows, but of course can be used on POSIX like systems. Still,
|
||||||
|
// it's the most sane of any of the formats so readDepFile will use that format.
|
||||||
|
func readDepFile(filename string) ([]string, error) {
|
||||||
|
buf, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return parseDepFile(string(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDepFile(s string) ([]string, error) {
|
||||||
|
// This function makes no attempt at parsing anything other than Clang -MD
|
||||||
|
// -MV output.
|
||||||
|
|
||||||
|
// For Windows: replace CRLF with LF to make the logic below simpler.
|
||||||
|
s = strings.ReplaceAll(s, "\r\n", "\n")
|
||||||
|
|
||||||
|
// Collapse all lines ending in a backslash. These backslashes are really
|
||||||
|
// just a way to continue a line without making very long lines.
|
||||||
|
s = strings.ReplaceAll(s, "\\\n", " ")
|
||||||
|
|
||||||
|
// Only use the first line, which is expected to begin with "deps:".
|
||||||
|
line := strings.SplitN(s, "\n", 2)[0]
|
||||||
|
if !strings.HasPrefix(line, "deps:") {
|
||||||
|
return nil, errors.New("readDepFile: expected 'deps:' prefix")
|
||||||
|
}
|
||||||
|
line = strings.TrimSpace(line[len("deps:"):])
|
||||||
|
|
||||||
|
var deps []string
|
||||||
|
for line != "" {
|
||||||
|
if line[0] == '"' {
|
||||||
|
// File path is quoted. Path ends with double quote.
|
||||||
|
// This does not handle double quotes in path names, which is a
|
||||||
|
// problem on non-Windows systems.
|
||||||
|
line = line[1:]
|
||||||
|
end := strings.IndexByte(line, '"')
|
||||||
|
if end < 0 {
|
||||||
|
return nil, errors.New("readDepFile: path is incorrectly quoted")
|
||||||
|
}
|
||||||
|
dep := line[:end]
|
||||||
|
line = strings.TrimSpace(line[end+1:])
|
||||||
|
deps = append(deps, dep)
|
||||||
|
} else {
|
||||||
|
// File path is not quoted. Path ends in space or EOL.
|
||||||
|
end := strings.IndexFunc(line, unicode.IsSpace)
|
||||||
|
if end < 0 {
|
||||||
|
// last dependency
|
||||||
|
deps = append(deps, line)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
dep := line[:end]
|
||||||
|
line = strings.TrimSpace(line[end:])
|
||||||
|
deps = append(deps, dep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deps, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSplitDepFile(t *testing.T) {
|
||||||
|
for i, tc := range []struct {
|
||||||
|
in string
|
||||||
|
out []string
|
||||||
|
}{
|
||||||
|
{`deps: foo bar`, []string{"foo", "bar"}},
|
||||||
|
{`deps: foo "bar"`, []string{"foo", "bar"}},
|
||||||
|
{`deps: "foo" bar`, []string{"foo", "bar"}},
|
||||||
|
{`deps: "foo bar"`, []string{"foo bar"}},
|
||||||
|
{`deps: "foo bar" `, []string{"foo bar"}},
|
||||||
|
{"deps: foo\nbar", []string{"foo"}},
|
||||||
|
{"deps: foo \\\nbar", []string{"foo", "bar"}},
|
||||||
|
{"deps: foo\\bar \\\nbaz", []string{"foo\\bar", "baz"}},
|
||||||
|
{"deps: foo\\bar \\\r\n baz", []string{"foo\\bar", "baz"}}, // Windows uses CRLF line endings
|
||||||
|
} {
|
||||||
|
out, err := parseDepFile(tc.in)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test #%d failed: %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(out, tc.out) {
|
||||||
|
t.Errorf("test #%d failed: expected %#v but got %#v", i, tc.out, out)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
-4
@@ -27,12 +27,23 @@ const (
|
|||||||
type compileJob struct {
|
type compileJob struct {
|
||||||
description string // description, only used for logging
|
description string // description, only used for logging
|
||||||
dependencies []*compileJob
|
dependencies []*compileJob
|
||||||
run func() error
|
result string // result (path)
|
||||||
|
run func(*compileJob) (err error)
|
||||||
state jobState
|
state jobState
|
||||||
err error // error if finished
|
err error // error if finished
|
||||||
duration time.Duration // how long it took to run this job (only set after finishing)
|
duration time.Duration // how long it took to run this job (only set after finishing)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dummyCompileJob returns a new *compileJob that produces an output without
|
||||||
|
// doing anything. This can be useful where a *compileJob producing an output is
|
||||||
|
// expected but nothing needs to be done, for example for a load from a cache.
|
||||||
|
func dummyCompileJob(result string) *compileJob {
|
||||||
|
return &compileJob{
|
||||||
|
description: "<dummy>",
|
||||||
|
result: result,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// readyToRun returns whether this job is ready to run: it is itself not yet
|
// readyToRun returns whether this job is ready to run: it is itself not yet
|
||||||
// started and all dependencies are finished.
|
// started and all dependencies are finished.
|
||||||
func (job *compileJob) readyToRun() bool {
|
func (job *compileJob) readyToRun() bool {
|
||||||
@@ -150,9 +161,11 @@ func nextJob(jobs []*compileJob) *compileJob {
|
|||||||
func jobWorker(workerChan, doneChan chan *compileJob) {
|
func jobWorker(workerChan, doneChan chan *compileJob) {
|
||||||
for job := range workerChan {
|
for job := range workerChan {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
err := job.run()
|
if job.run != nil {
|
||||||
if err != nil {
|
err := job.run(job)
|
||||||
job.err = err
|
if err != nil {
|
||||||
|
job.err = err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
job.duration = time.Since(start)
|
job.duration = time.Since(start)
|
||||||
doneChan <- job
|
doneChan <- job
|
||||||
|
|||||||
+16
-17
@@ -42,30 +42,28 @@ func (l *Library) sourcePaths(target string) []string {
|
|||||||
// The resulting file is stored in the provided tmpdir, which is expected to be
|
// The resulting file is stored in the provided tmpdir, which is expected to be
|
||||||
// removed after the Load call.
|
// removed after the Load call.
|
||||||
func (l *Library) Load(target, tmpdir string) (path string, err error) {
|
func (l *Library) Load(target, tmpdir string) (path string, err error) {
|
||||||
path, job, err := l.load(target, "", tmpdir)
|
job, err := l.load(target, "", tmpdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if job != nil {
|
jobs := append([]*compileJob{job}, job.dependencies...)
|
||||||
jobs := append([]*compileJob{job}, job.dependencies...)
|
err = runJobs(jobs)
|
||||||
err = runJobs(jobs)
|
return job.result, err
|
||||||
}
|
|
||||||
return path, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// load returns a path to the library file for the given target, loading it from
|
// load returns a compile job to build this library file for the given target
|
||||||
// cache if possible. It will return a non-zero compiler job if the library
|
// and CPU. It may return a dummy compileJob if the library build is already
|
||||||
// wasn't cached, this job (and its dependencies) must be run before the library
|
// cached. The path is stored as job.result but is only valid if the job and
|
||||||
// path is valid.
|
// job.dependencies have been run.
|
||||||
// The provided tmpdir will be used to store intermediary files and possibly the
|
// The provided tmpdir will be used to store intermediary files and possibly the
|
||||||
// output archive file, it is expected to be removed after use.
|
// output archive file, it is expected to be removed after use.
|
||||||
func (l *Library) load(target, cpu, tmpdir string) (path string, job *compileJob, err error) {
|
func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error) {
|
||||||
// Try to load a precompiled library.
|
// Try to load a precompiled library.
|
||||||
precompiledPath := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", target, l.name+".a")
|
precompiledPath := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", target, l.name+".a")
|
||||||
if _, err := os.Stat(precompiledPath); err == nil {
|
if _, err := os.Stat(precompiledPath); err == nil {
|
||||||
// Found a precompiled library for this OS/architecture. Return the path
|
// Found a precompiled library for this OS/architecture. Return the path
|
||||||
// directly.
|
// directly.
|
||||||
return precompiledPath, nil, nil
|
return dummyCompileJob(precompiledPath), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var outfile string
|
var outfile string
|
||||||
@@ -78,7 +76,7 @@ func (l *Library) load(target, cpu, tmpdir string) (path string, job *compileJob
|
|||||||
// Try to fetch this library from the cache.
|
// Try to fetch this library from the cache.
|
||||||
if path, err := cacheLoad(outfile, l.sourcePaths(target)); path != "" || err != nil {
|
if path, err := cacheLoad(outfile, l.sourcePaths(target)); path != "" || err != nil {
|
||||||
// Cache hit.
|
// Cache hit.
|
||||||
return path, nil, err
|
return dummyCompileJob(path), nil
|
||||||
}
|
}
|
||||||
// Cache miss, build it now.
|
// Cache miss, build it now.
|
||||||
|
|
||||||
@@ -86,7 +84,7 @@ func (l *Library) load(target, cpu, tmpdir string) (path string, job *compileJob
|
|||||||
dir := filepath.Join(tmpdir, "build-lib-"+l.name)
|
dir := filepath.Join(tmpdir, "build-lib-"+l.name)
|
||||||
err = os.Mkdir(dir, 0777)
|
err = os.Mkdir(dir, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Precalculate the flags to the compiler invocation.
|
// Precalculate the flags to the compiler invocation.
|
||||||
@@ -113,7 +111,8 @@ func (l *Library) load(target, cpu, tmpdir string) (path string, job *compileJob
|
|||||||
arpath := filepath.Join(dir, l.name+".a")
|
arpath := filepath.Join(dir, l.name+".a")
|
||||||
job = &compileJob{
|
job = &compileJob{
|
||||||
description: "ar " + l.name + ".a",
|
description: "ar " + l.name + ".a",
|
||||||
run: func() error {
|
result: arpath,
|
||||||
|
run: func(*compileJob) error {
|
||||||
// Create an archive of all object files.
|
// Create an archive of all object files.
|
||||||
err := makeArchive(arpath, objs)
|
err := makeArchive(arpath, objs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -133,7 +132,7 @@ func (l *Library) load(target, cpu, tmpdir string) (path string, job *compileJob
|
|||||||
objs = append(objs, objpath)
|
objs = append(objs, objpath)
|
||||||
job.dependencies = append(job.dependencies, &compileJob{
|
job.dependencies = append(job.dependencies, &compileJob{
|
||||||
description: "compile " + srcpath,
|
description: "compile " + srcpath,
|
||||||
run: func() error {
|
run: func(*compileJob) error {
|
||||||
var compileArgs []string
|
var compileArgs []string
|
||||||
compileArgs = append(compileArgs, args...)
|
compileArgs = append(compileArgs, args...)
|
||||||
compileArgs = append(compileArgs, "-o", objpath, srcpath)
|
compileArgs = append(compileArgs, "-o", objpath, srcpath)
|
||||||
@@ -146,5 +145,5 @@ func (l *Library) load(target, cpu, tmpdir string) (path string, job *compileJob
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return arpath, job, nil
|
return job, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-13
@@ -41,7 +41,9 @@ type cgoPackage struct {
|
|||||||
elaboratedTypes map[string]*elaboratedTypeInfo
|
elaboratedTypes map[string]*elaboratedTypeInfo
|
||||||
enums map[string]enumInfo
|
enums map[string]enumInfo
|
||||||
anonStructNum int
|
anonStructNum int
|
||||||
ldflags []string
|
cflags []string // CFlags from #cgo lines
|
||||||
|
ldflags []string // LDFlags from #cgo lines
|
||||||
|
visitedFiles map[string][]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// constantInfo stores some information about a CGo constant found by libclang
|
// constantInfo stores some information about a CGo constant found by libclang
|
||||||
@@ -156,9 +158,10 @@ typedef unsigned long long _Cgo_ulonglong;
|
|||||||
// Process extracts `import "C"` statements from the AST, parses the comment
|
// Process extracts `import "C"` statements from the AST, parses the comment
|
||||||
// with libclang, and modifies the AST to use this information. It returns a
|
// with libclang, and modifies the AST to use this information. It returns a
|
||||||
// newly created *ast.File that should be added to the list of to-be-parsed
|
// 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
|
// files, the CFLAGS and LDFLAGS found in #cgo lines, and a map of file hashes
|
||||||
// but still modifies the AST.
|
// of the accessed C header files. If there is one or more error, it returns
|
||||||
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, []error) {
|
// these in the []error slice but still modifies the AST.
|
||||||
|
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, []string, map[string][]byte, []error) {
|
||||||
p := &cgoPackage{
|
p := &cgoPackage{
|
||||||
dir: dir,
|
dir: dir,
|
||||||
fset: fset,
|
fset: fset,
|
||||||
@@ -170,13 +173,9 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
typedefs: map[string]*typedefInfo{},
|
typedefs: map[string]*typedefInfo{},
|
||||||
elaboratedTypes: map[string]*elaboratedTypeInfo{},
|
elaboratedTypes: map[string]*elaboratedTypeInfo{},
|
||||||
enums: map[string]enumInfo{},
|
enums: map[string]enumInfo{},
|
||||||
|
visitedFiles: map[string][]byte{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable _FORTIFY_SOURCE as it causes problems on macOS.
|
|
||||||
// Note that it is only disabled for memcpy (etc) calls made from Go, which
|
|
||||||
// have better alternatives anyway.
|
|
||||||
cflags = append(cflags, "-D_FORTIFY_SOURCE=0")
|
|
||||||
|
|
||||||
// Add a new location for the following file.
|
// Add a new location for the following file.
|
||||||
generatedTokenPos := p.fset.AddFile(dir+"/!cgo.go", -1, 0)
|
generatedTokenPos := p.fset.AddFile(dir+"/!cgo.go", -1, 0)
|
||||||
generatedTokenPos.SetLines([]int{0})
|
generatedTokenPos.SetLines([]int{0})
|
||||||
@@ -185,7 +184,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
// Find the absolute path for this package.
|
// Find the absolute path for this package.
|
||||||
packagePath, err := filepath.Abs(fset.File(files[0].Pos()).Name())
|
packagePath, err := filepath.Abs(fset.File(files[0].Pos()).Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, []error{
|
return nil, nil, nil, nil, []error{
|
||||||
scanner.Error{
|
scanner.Error{
|
||||||
Pos: fset.Position(files[0].Pos()),
|
Pos: fset.Position(files[0].Pos()),
|
||||||
Msg: "cgo: cannot find absolute path: " + err.Error(), // TODO: wrap this error
|
Msg: "cgo: cannot find absolute path: " + err.Error(), // TODO: wrap this error
|
||||||
@@ -360,7 +359,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
makePathsAbsolute(flags, packagePath)
|
makePathsAbsolute(flags, packagePath)
|
||||||
cflags = append(cflags, flags...)
|
p.cflags = append(p.cflags, flags...)
|
||||||
case "LDFLAGS":
|
case "LDFLAGS":
|
||||||
flags, err := shlex.Split(value)
|
flags, err := shlex.Split(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -383,6 +382,13 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Define CFlags that will be used while parsing the package.
|
||||||
|
// Disable _FORTIFY_SOURCE as it causes problems on macOS.
|
||||||
|
// Note that it is only disabled for memcpy (etc) calls made from Go, which
|
||||||
|
// have better alternatives anyway.
|
||||||
|
cflagsForCGo := append([]string{"-D_FORTIFY_SOURCE=0"}, cflags...)
|
||||||
|
cflagsForCGo = append(cflagsForCGo, p.cflags...)
|
||||||
|
|
||||||
// Process all CGo imports.
|
// Process all CGo imports.
|
||||||
for _, genDecl := range statements {
|
for _, genDecl := range statements {
|
||||||
cgoComment := genDecl.Doc.Text()
|
cgoComment := genDecl.Doc.Text()
|
||||||
@@ -392,7 +398,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
pos = genDecl.Doc.Pos()
|
pos = genDecl.Doc.Pos()
|
||||||
}
|
}
|
||||||
position := fset.PositionFor(pos, true)
|
position := fset.PositionFor(pos, true)
|
||||||
p.parseFragment(cgoComment+cgoTypes, cflags, position.Filename, position.Line)
|
p.parseFragment(cgoComment+cgoTypes, cflagsForCGo, position.Filename, position.Line)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Declare functions found by libclang.
|
// Declare functions found by libclang.
|
||||||
@@ -427,7 +433,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
// Print the newly generated in-memory AST, for debugging.
|
// Print the newly generated in-memory AST, for debugging.
|
||||||
//ast.Print(fset, p.generated)
|
//ast.Print(fset, p.generated)
|
||||||
|
|
||||||
return p.generated, p.ldflags, p.errors
|
return p.generated, p.cflags, p.ldflags, p.visitedFiles, p.errors
|
||||||
}
|
}
|
||||||
|
|
||||||
// makePathsAbsolute converts some common path compiler flags (-I, -L) from
|
// makePathsAbsolute converts some common path compiler flags (-I, -L) from
|
||||||
|
|||||||
+1
-1
@@ -65,7 +65,7 @@ func TestCGo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process the AST with CGo.
|
// 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.
|
// Check the AST for type errors.
|
||||||
var typecheckErrors []error
|
var typecheckErrors []error
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package cgo
|
|||||||
// modification. It does not touch the AST itself.
|
// modification. It does not touch the AST itself.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha512"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/scanner"
|
"go/scanner"
|
||||||
@@ -56,6 +57,7 @@ unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c);
|
|||||||
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
int tinygo_clang_enum_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
int tinygo_clang_enum_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
|
void tinygo_clang_inclusion_visitor(CXFile included_file, CXSourceLocation *inclusion_stack, unsigned include_len, CXClientData client_data);
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
@@ -114,6 +116,7 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
|
|||||||
}
|
}
|
||||||
defer C.clang_disposeTranslationUnit(unit)
|
defer C.clang_disposeTranslationUnit(unit)
|
||||||
|
|
||||||
|
// Report parser and type errors.
|
||||||
if numDiagnostics := int(C.clang_getNumDiagnostics(unit)); numDiagnostics != 0 {
|
if numDiagnostics := int(C.clang_getNumDiagnostics(unit)); numDiagnostics != 0 {
|
||||||
addDiagnostic := func(diagnostic C.CXDiagnostic) {
|
addDiagnostic := func(diagnostic C.CXDiagnostic) {
|
||||||
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
||||||
@@ -134,10 +137,36 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract information required by CGo.
|
||||||
ref := storedRefs.Put(p)
|
ref := storedRefs.Put(p)
|
||||||
defer storedRefs.Remove(ref)
|
defer storedRefs.Remove(ref)
|
||||||
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
||||||
|
|
||||||
|
// Determine files read during CGo processing, for caching.
|
||||||
|
inclusionCallback := func(includedFile C.CXFile) {
|
||||||
|
// Get full file path.
|
||||||
|
path := getString(C.clang_getFileName(includedFile))
|
||||||
|
|
||||||
|
// Get contents of file (that should be in-memory).
|
||||||
|
size := C.size_t(0)
|
||||||
|
rawData := C.clang_getFileContents(unit, includedFile, &size)
|
||||||
|
if rawData == nil {
|
||||||
|
// Sanity check. This should (hopefully) never trigger.
|
||||||
|
panic("libclang: file contents was not loaded")
|
||||||
|
}
|
||||||
|
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
|
||||||
|
|
||||||
|
// Hash the contents if it isn't hashed yet.
|
||||||
|
if _, ok := p.visitedFiles[path]; !ok {
|
||||||
|
// already stored
|
||||||
|
sum := sha512.Sum512_224(data)
|
||||||
|
p.visitedFiles[path] = sum[:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inclusionCallbackRef := storedRefs.Put(inclusionCallback)
|
||||||
|
defer storedRefs.Remove(inclusionCallbackRef)
|
||||||
|
C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef))
|
||||||
}
|
}
|
||||||
|
|
||||||
//export tinygo_clang_globals_visitor
|
//export tinygo_clang_globals_visitor
|
||||||
@@ -772,3 +801,9 @@ func tinygo_clang_enum_visitor(c, parent C.GoCXCursor, client_data C.CXClientDat
|
|||||||
}
|
}
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//export tinygo_clang_inclusion_visitor
|
||||||
|
func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) {
|
||||||
|
callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile))
|
||||||
|
callback(includedFile)
|
||||||
|
}
|
||||||
|
|||||||
+24
-3
@@ -7,6 +7,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -42,7 +43,7 @@ type TargetSpec struct {
|
|||||||
ExtraFiles []string `json:"extra-files"`
|
ExtraFiles []string `json:"extra-files"`
|
||||||
Emulator []string `json:"emulator" override:"copy"` // inherited Emulator must not be append
|
Emulator []string `json:"emulator" override:"copy"` // inherited Emulator must not be append
|
||||||
FlashCommand string `json:"flash-command"`
|
FlashCommand string `json:"flash-command"`
|
||||||
GDB string `json:"gdb"`
|
GDB []string `json:"gdb"`
|
||||||
PortReset string `json:"flash-1200-bps-reset"`
|
PortReset string `json:"flash-1200-bps-reset"`
|
||||||
FlashMethod string `json:"flash-method"`
|
FlashMethod string `json:"flash-method"`
|
||||||
FlashVolume string `json:"msd-volume-name"`
|
FlashVolume string `json:"msd-volume-name"`
|
||||||
@@ -229,7 +230,13 @@ func LoadTarget(target string) (*TargetSpec, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WindowsBuildNotSupportedErr is being thrown, when goos is windows and no target has been specified.
|
||||||
|
var WindowsBuildNotSupportedErr = errors.New("Building Windows binaries is currently not supported. Try specifying a different target")
|
||||||
|
|
||||||
func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||||
|
if goos == "windows" {
|
||||||
|
return nil, WindowsBuildNotSupportedErr
|
||||||
|
}
|
||||||
// No target spec available. Use the default one, useful on most systems
|
// No target spec available. Use the default one, useful on most systems
|
||||||
// with a regular OS.
|
// with a regular OS.
|
||||||
spec := TargetSpec{
|
spec := TargetSpec{
|
||||||
@@ -240,7 +247,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
Compiler: "clang",
|
Compiler: "clang",
|
||||||
Linker: "cc",
|
Linker: "cc",
|
||||||
CFlags: []string{"--target=" + triple},
|
CFlags: []string{"--target=" + triple},
|
||||||
GDB: "gdb",
|
GDB: []string{"gdb"},
|
||||||
PortReset: "false",
|
PortReset: "false",
|
||||||
}
|
}
|
||||||
if goos == "darwin" {
|
if goos == "darwin" {
|
||||||
@@ -253,7 +260,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
}
|
}
|
||||||
if goarch != runtime.GOARCH {
|
if goarch != runtime.GOARCH {
|
||||||
// Some educated guesses as to how to invoke helper programs.
|
// Some educated guesses as to how to invoke helper programs.
|
||||||
spec.GDB = "gdb-multiarch"
|
spec.GDB = []string{"gdb-multiarch"}
|
||||||
if goarch == "arm" && goos == "linux" {
|
if goarch == "arm" && goos == "linux" {
|
||||||
spec.CFlags = append(spec.CFlags, "--sysroot=/usr/arm-linux-gnueabihf")
|
spec.CFlags = append(spec.CFlags, "--sysroot=/usr/arm-linux-gnueabihf")
|
||||||
spec.Linker = "arm-linux-gnueabihf-gcc"
|
spec.Linker = "arm-linux-gnueabihf-gcc"
|
||||||
@@ -271,3 +278,17 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
}
|
}
|
||||||
return &spec, nil
|
return &spec, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LookupGDB looks up a gdb executable.
|
||||||
|
func (spec *TargetSpec) LookupGDB() (string, error) {
|
||||||
|
if len(spec.GDB) == 0 {
|
||||||
|
return "", errors.New("gdb not configured in the target specification")
|
||||||
|
}
|
||||||
|
for _, d := range spec.GDB {
|
||||||
|
_, err := exec.LookPath(d)
|
||||||
|
if err == nil {
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", errors.New("no gdb found configured in the target specification (" + strings.Join(spec.GDB, ", ") + ")")
|
||||||
|
}
|
||||||
|
|||||||
@@ -159,6 +159,9 @@ func (b *builder) createNilCheck(inst ssa.Value, ptr llvm.Value, blockPrefix str
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch inst := inst.(type) {
|
switch inst := inst.(type) {
|
||||||
|
case *ssa.Alloc:
|
||||||
|
// An alloc is never nil.
|
||||||
|
return
|
||||||
case *ssa.IndexAddr:
|
case *ssa.IndexAddr:
|
||||||
// This pointer is the result of an index operation into a slice or
|
// This pointer is the result of an index operation into a slice or
|
||||||
// array. Such slices/arrays are already bounds checked so the pointer
|
// array. Such slices/arrays are already bounds checked so the pointer
|
||||||
|
|||||||
+22
-10
@@ -58,10 +58,10 @@ 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
|
// Expand an argument type to a list that can be used in a function call
|
||||||
// parameter list.
|
// parameter list.
|
||||||
func expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
||||||
switch t.TypeKind() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fieldInfos := flattenAggregateType(t, name, goType)
|
fieldInfos := c.flattenAggregateType(t, name, goType)
|
||||||
if len(fieldInfos) <= maxFieldsPerParam {
|
if len(fieldInfos) <= maxFieldsPerParam {
|
||||||
return fieldInfos
|
return fieldInfos
|
||||||
} else {
|
} else {
|
||||||
@@ -105,7 +105,7 @@ func (b *builder) expandFormalParamOffsets(t llvm.Type) []uint64 {
|
|||||||
func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
|
func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
|
||||||
switch v.Type().TypeKind() {
|
switch v.Type().TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fieldInfos := flattenAggregateType(v.Type(), "", nil)
|
fieldInfos := b.flattenAggregateType(v.Type(), "", nil)
|
||||||
if len(fieldInfos) <= maxFieldsPerParam {
|
if len(fieldInfos) <= maxFieldsPerParam {
|
||||||
fields := b.flattenAggregate(v)
|
fields := b.flattenAggregate(v)
|
||||||
if len(fields) != len(fieldInfos) {
|
if len(fields) != len(fieldInfos) {
|
||||||
@@ -124,12 +124,15 @@ 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
|
// 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.
|
// with the passed in type if this is not possible.
|
||||||
func flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
||||||
typeFlags := getTypeFlags(goType)
|
typeFlags := getTypeFlags(goType)
|
||||||
switch t.TypeKind() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
paramInfos := make([]paramInfo, 0, t.StructElementTypesCount())
|
var paramInfos []paramInfo
|
||||||
for i, subfield := range t.StructElementTypes() {
|
for i, subfield := range t.StructElementTypes() {
|
||||||
|
if c.targetData.TypeAllocSize(subfield) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
suffix := strconv.Itoa(i)
|
suffix := strconv.Itoa(i)
|
||||||
if goType != nil {
|
if goType != nil {
|
||||||
// Try to come up with a good suffix for this struct field,
|
// Try to come up with a good suffix for this struct field,
|
||||||
@@ -152,7 +155,7 @@ func flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramIn
|
|||||||
suffix = []string{"context", "funcptr"}[i]
|
suffix = []string{"context", "funcptr"}[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
subInfos := flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
||||||
for i := range subInfos {
|
for i := range subInfos {
|
||||||
subInfos[i].flags |= typeFlags
|
subInfos[i].flags |= typeFlags
|
||||||
}
|
}
|
||||||
@@ -218,8 +221,11 @@ func extractSubfield(t types.Type, field int) types.Type {
|
|||||||
func (c *compilerContext) flattenAggregateTypeOffsets(t llvm.Type) []uint64 {
|
func (c *compilerContext) flattenAggregateTypeOffsets(t llvm.Type) []uint64 {
|
||||||
switch t.TypeKind() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fields := make([]uint64, 0, t.StructElementTypesCount())
|
var fields []uint64
|
||||||
for fieldIndex, field := range t.StructElementTypes() {
|
for fieldIndex, field := range t.StructElementTypes() {
|
||||||
|
if c.targetData.TypeAllocSize(field) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
suboffsets := c.flattenAggregateTypeOffsets(field)
|
suboffsets := c.flattenAggregateTypeOffsets(field)
|
||||||
offset := c.targetData.ElementOffset(t, fieldIndex)
|
offset := c.targetData.ElementOffset(t, fieldIndex)
|
||||||
for i := range suboffsets {
|
for i := range suboffsets {
|
||||||
@@ -238,8 +244,11 @@ func (c *compilerContext) flattenAggregateTypeOffsets(t llvm.Type) []uint64 {
|
|||||||
func (b *builder) flattenAggregate(v llvm.Value) []llvm.Value {
|
func (b *builder) flattenAggregate(v llvm.Value) []llvm.Value {
|
||||||
switch v.Type().TypeKind() {
|
switch v.Type().TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fields := make([]llvm.Value, 0, v.Type().StructElementTypesCount())
|
var fields []llvm.Value
|
||||||
for i := range v.Type().StructElementTypes() {
|
for i, field := range v.Type().StructElementTypes() {
|
||||||
|
if b.targetData.TypeAllocSize(field) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
subfield := b.CreateExtractValue(v, i, "")
|
subfield := b.CreateExtractValue(v, i, "")
|
||||||
subfields := b.flattenAggregate(subfield)
|
subfields := b.flattenAggregate(subfield)
|
||||||
fields = append(fields, subfields...)
|
fields = append(fields, subfields...)
|
||||||
@@ -266,10 +275,13 @@ 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) {
|
func (b *builder) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) {
|
||||||
switch t.TypeKind() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
flattened := flattenAggregateType(t, "", nil)
|
flattened := b.flattenAggregateType(t, "", nil)
|
||||||
if len(flattened) <= maxFieldsPerParam {
|
if len(flattened) <= maxFieldsPerParam {
|
||||||
value := llvm.ConstNull(t)
|
value := llvm.ConstNull(t)
|
||||||
for i, subtyp := range t.StructElementTypes() {
|
for i, subtyp := range t.StructElementTypes() {
|
||||||
|
if b.targetData.TypeAllocSize(subtyp) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
structField, remaining := b.collapseFormalParamInternal(subtyp, fields)
|
structField, remaining := b.collapseFormalParamInternal(subtyp, fields)
|
||||||
fields = remaining
|
fields = remaining
|
||||||
value = b.CreateInsertValue(value, structField, i, "")
|
value = b.CreateInsertValue(value, structField, i, "")
|
||||||
|
|||||||
+120
-106
@@ -20,6 +20,11 @@ import (
|
|||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Version of the compiler pacakge. Must be incremented each time the compiler
|
||||||
|
// package changes in a way that affects the generated LLVM module.
|
||||||
|
// This version is independent of the TinyGo version number.
|
||||||
|
const Version = 6 // last change: fix issue 1304
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
llvm.InitializeAllTargets()
|
llvm.InitializeAllTargets()
|
||||||
llvm.InitializeAllTargetMCs()
|
llvm.InitializeAllTargetMCs()
|
||||||
@@ -84,12 +89,13 @@ type compilerContext struct {
|
|||||||
// importantly with a newly created LLVM context and module.
|
// importantly with a newly created LLVM context and module.
|
||||||
func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *Config, dumpSSA bool) *compilerContext {
|
func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *Config, dumpSSA bool) *compilerContext {
|
||||||
c := &compilerContext{
|
c := &compilerContext{
|
||||||
Config: config,
|
Config: config,
|
||||||
DumpSSA: dumpSSA,
|
DumpSSA: dumpSSA,
|
||||||
difiles: make(map[string]llvm.Metadata),
|
difiles: make(map[string]llvm.Metadata),
|
||||||
ditypes: make(map[types.Type]llvm.Metadata),
|
ditypes: make(map[types.Type]llvm.Metadata),
|
||||||
machine: machine,
|
machine: machine,
|
||||||
targetData: machine.CreateTargetData(),
|
targetData: machine.CreateTargetData(),
|
||||||
|
astComments: map[string]*ast.CommentGroup{},
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ctx = llvm.NewContext()
|
c.ctx = llvm.NewContext()
|
||||||
@@ -241,21 +247,14 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompileProgram compiles the given package path or .go file path. Return an
|
// CompilePackage compiles a single package to a LLVM module.
|
||||||
// error when this fails (in any stage). If successful it returns the LLVM
|
func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
||||||
// module. If not, one or more errors will be returned.
|
c := newCompilerContext(moduleName, machine, config, dumpSSA)
|
||||||
func CompileProgram(lprogram *loader.Program, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
|
||||||
c := newCompilerContext("", machine, config, dumpSSA)
|
c.program = ssaPkg.Prog
|
||||||
|
|
||||||
c.program = lprogram.LoadSSA()
|
// Convert AST to SSA.
|
||||||
c.program.Build()
|
ssaPkg.Build()
|
||||||
c.runtimePkg = c.program.ImportedPackage("runtime").Pkg
|
|
||||||
|
|
||||||
// Run a simple dead code elimination pass.
|
|
||||||
functions, err := c.simpleDCE(lprogram)
|
|
||||||
if err != nil {
|
|
||||||
return llvm.Module{}, []error{err}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize debug information.
|
// Initialize debug information.
|
||||||
if c.Debug {
|
if c.Debug {
|
||||||
@@ -268,112 +267,37 @@ func CompileProgram(lprogram *loader.Program, machine llvm.TargetMachine, config
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
c.loadASTComments(lprogram)
|
// Load comments such as //go:extern on globals.
|
||||||
|
c.loadASTComments(pkg)
|
||||||
|
|
||||||
// Predeclare the runtime.alloc function, which is used by the wordpack
|
// Predeclare the runtime.alloc function, which is used by the wordpack
|
||||||
// functionality.
|
// functionality.
|
||||||
c.getFunction(c.program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function))
|
c.getFunction(c.program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function))
|
||||||
|
|
||||||
// Add definitions to declarations.
|
// Compile all functions, methods, and global variables in this package.
|
||||||
var initFuncs []llvm.Value
|
|
||||||
irbuilder := c.ctx.NewBuilder()
|
irbuilder := c.ctx.NewBuilder()
|
||||||
defer irbuilder.Dispose()
|
defer irbuilder.Dispose()
|
||||||
for _, f := range functions {
|
c.createPackage(irbuilder, ssaPkg)
|
||||||
if f.Synthetic == "package initializer" {
|
|
||||||
initFuncs = append(initFuncs, c.getFunction(f))
|
|
||||||
}
|
|
||||||
if f.Blocks == nil {
|
|
||||||
continue // external function
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the function definition.
|
|
||||||
b := newBuilder(c, irbuilder, f)
|
|
||||||
b.createFunction()
|
|
||||||
}
|
|
||||||
|
|
||||||
// After all packages are imported, add a synthetic initializer function
|
|
||||||
// that calls the initializer of each package.
|
|
||||||
initFn := c.program.ImportedPackage("runtime").Members["initAll"].(*ssa.Function)
|
|
||||||
llvmInitFn := c.getFunction(initFn)
|
|
||||||
llvmInitFn.SetLinkage(llvm.InternalLinkage)
|
|
||||||
llvmInitFn.SetUnnamedAddr(true)
|
|
||||||
if c.Debug {
|
|
||||||
difunc := c.attachDebugInfo(initFn)
|
|
||||||
pos := c.program.Fset.Position(initFn.Pos())
|
|
||||||
irbuilder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
|
||||||
}
|
|
||||||
llvmInitFn.Param(0).SetName("context")
|
|
||||||
llvmInitFn.Param(1).SetName("parentHandle")
|
|
||||||
block := c.ctx.AddBasicBlock(llvmInitFn, "entry")
|
|
||||||
irbuilder.SetInsertPointAtEnd(block)
|
|
||||||
for _, fn := range initFuncs {
|
|
||||||
irbuilder.CreateCall(fn, []llvm.Value{llvm.Undef(c.i8ptrType), llvm.Undef(c.i8ptrType)}, "")
|
|
||||||
}
|
|
||||||
irbuilder.CreateRetVoid()
|
|
||||||
|
|
||||||
// see: https://reviews.llvm.org/D18355
|
// see: https://reviews.llvm.org/D18355
|
||||||
if c.Debug {
|
if c.Debug {
|
||||||
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||||
c.ctx.MDNode([]llvm.Metadata{
|
c.ctx.MDNode([]llvm.Metadata{
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch
|
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch
|
||||||
llvm.GlobalContext().MDString("Debug Info Version"),
|
c.ctx.MDString("Debug Info Version"),
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 3, false).ConstantAsMetadata(), // DWARF version
|
llvm.ConstInt(c.ctx.Int32Type(), 3, false).ConstantAsMetadata(), // DWARF version
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||||
c.ctx.MDNode([]llvm.Metadata{
|
c.ctx.MDNode([]llvm.Metadata{
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(),
|
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(),
|
||||||
llvm.GlobalContext().MDString("Dwarf Version"),
|
c.ctx.MDString("Dwarf Version"),
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(),
|
llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
c.dibuilder.Finalize()
|
c.dibuilder.Finalize()
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.mod, c.diagnostics
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompilePackage compiles a single package to a LLVM module.
|
|
||||||
func CompilePackage(moduleName string, pkg *loader.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
|
||||||
c := newCompilerContext(moduleName, machine, config, dumpSSA)
|
|
||||||
|
|
||||||
// Build SSA from AST.
|
|
||||||
ssaPkg := pkg.LoadSSA()
|
|
||||||
ssaPkg.Build()
|
|
||||||
|
|
||||||
// Sort by position, so that the order of the functions in the IR matches
|
|
||||||
// the order of functions in the source file. This is useful for testing,
|
|
||||||
// for example.
|
|
||||||
var members []string
|
|
||||||
for name := range ssaPkg.Members {
|
|
||||||
members = append(members, name)
|
|
||||||
}
|
|
||||||
sort.Slice(members, func(i, j int) bool {
|
|
||||||
iPos := ssaPkg.Members[members[i]].Pos()
|
|
||||||
jPos := ssaPkg.Members[members[j]].Pos()
|
|
||||||
if i == j {
|
|
||||||
// Cannot sort by pos, so do it by name.
|
|
||||||
return members[i] < members[j]
|
|
||||||
}
|
|
||||||
return iPos < jPos
|
|
||||||
})
|
|
||||||
|
|
||||||
// Define all functions.
|
|
||||||
irbuilder := c.ctx.NewBuilder()
|
|
||||||
defer irbuilder.Dispose()
|
|
||||||
for _, name := range members {
|
|
||||||
member := ssaPkg.Members[name]
|
|
||||||
switch member := member.(type) {
|
|
||||||
case *ssa.Function:
|
|
||||||
if member.Blocks == nil {
|
|
||||||
continue // external function
|
|
||||||
}
|
|
||||||
// Create the function definition.
|
|
||||||
b := newBuilder(c, irbuilder, member)
|
|
||||||
b.createFunction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.mod, nil
|
return c.mod, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -720,8 +644,9 @@ func (c *compilerContext) attachDebugInfo(f *ssa.Function) llvm.Metadata {
|
|||||||
// debug info is added to the function.
|
// debug info is added to the function.
|
||||||
func (c *compilerContext) attachDebugInfoRaw(f *ssa.Function, llvmFn llvm.Value, suffix, filename string, line int) llvm.Metadata {
|
func (c *compilerContext) attachDebugInfoRaw(f *ssa.Function, llvmFn llvm.Value, suffix, filename string, line int) llvm.Metadata {
|
||||||
// Debug info for this function.
|
// Debug info for this function.
|
||||||
diparams := make([]llvm.Metadata, 0, len(f.Params))
|
params := getParams(f.Signature)
|
||||||
for _, param := range f.Params {
|
diparams := make([]llvm.Metadata, 0, len(params))
|
||||||
|
for _, param := range params {
|
||||||
diparams = append(diparams, c.getDIType(param.Type()))
|
diparams = append(diparams, c.getDIType(param.Type()))
|
||||||
}
|
}
|
||||||
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
||||||
@@ -759,6 +684,83 @@ func (c *compilerContext) getDIFile(filename string) llvm.Metadata {
|
|||||||
return c.difiles[filename]
|
return c.difiles[filename]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createPackage builds the LLVM IR for all types, methods, and global variables
|
||||||
|
// in the given package.
|
||||||
|
func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package) {
|
||||||
|
// Sort by position, so that the order of the functions in the IR matches
|
||||||
|
// the order of functions in the source file. This is useful for testing,
|
||||||
|
// for example.
|
||||||
|
var members []string
|
||||||
|
for name := range pkg.Members {
|
||||||
|
members = append(members, name)
|
||||||
|
}
|
||||||
|
sort.Slice(members, func(i, j int) bool {
|
||||||
|
iPos := pkg.Members[members[i]].Pos()
|
||||||
|
jPos := pkg.Members[members[j]].Pos()
|
||||||
|
if i == j {
|
||||||
|
// Cannot sort by pos, so do it by name.
|
||||||
|
return members[i] < members[j]
|
||||||
|
}
|
||||||
|
return iPos < jPos
|
||||||
|
})
|
||||||
|
|
||||||
|
// Define all functions.
|
||||||
|
for _, name := range members {
|
||||||
|
member := pkg.Members[name]
|
||||||
|
switch member := member.(type) {
|
||||||
|
case *ssa.Function:
|
||||||
|
if member.Blocks == nil {
|
||||||
|
continue // external function
|
||||||
|
}
|
||||||
|
// Create the function definition.
|
||||||
|
b := newBuilder(c, irbuilder, member)
|
||||||
|
b.createFunction()
|
||||||
|
case *ssa.Type:
|
||||||
|
if types.IsInterface(member.Type()) {
|
||||||
|
// Interfaces don't have concrete methods.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Named type. We should make sure all methods are created.
|
||||||
|
// This includes both functions with pointer receivers and those
|
||||||
|
// without.
|
||||||
|
methods := getAllMethods(pkg.Prog, member.Type())
|
||||||
|
methods = append(methods, getAllMethods(pkg.Prog, types.NewPointer(member.Type()))...)
|
||||||
|
for _, method := range methods {
|
||||||
|
// Parse this method.
|
||||||
|
fn := pkg.Prog.MethodValue(method)
|
||||||
|
if fn.Blocks == nil {
|
||||||
|
continue // external function
|
||||||
|
}
|
||||||
|
if member.Type().String() != member.String() {
|
||||||
|
// This is a member on a type alias. Do not build such a
|
||||||
|
// function.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fn.Synthetic != "" && fn.Synthetic != "package initializer" {
|
||||||
|
// This function is a kind of wrapper function (created by
|
||||||
|
// the ssa package, not appearing in the source code) that
|
||||||
|
// is created by the getFunction method as needed.
|
||||||
|
// Therefore, don't build it here to avoid "function
|
||||||
|
// redeclared" errors.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Create the function definition.
|
||||||
|
b := newBuilder(c, irbuilder, fn)
|
||||||
|
b.createFunction()
|
||||||
|
}
|
||||||
|
case *ssa.Global:
|
||||||
|
// Global variable.
|
||||||
|
info := c.getGlobalInfo(member)
|
||||||
|
if !info.extern {
|
||||||
|
global := c.getGlobal(member)
|
||||||
|
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
|
||||||
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// createFunction builds the LLVM IR implementation for this function. The
|
// createFunction builds the LLVM IR implementation for this function. The
|
||||||
// function must not yet be defined, otherwise this function will create a
|
// function must not yet be defined, otherwise this function will create a
|
||||||
// diagnostic.
|
// diagnostic.
|
||||||
@@ -767,7 +769,7 @@ func (b *builder) createFunction() {
|
|||||||
fmt.Printf("\nfunc %s:\n", b.fn)
|
fmt.Printf("\nfunc %s:\n", b.fn)
|
||||||
}
|
}
|
||||||
if !b.llvmFn.IsDeclaration() {
|
if !b.llvmFn.IsDeclaration() {
|
||||||
errValue := b.fn.Name() + " redeclared in this program"
|
errValue := b.llvmFn.Name() + " redeclared in this program"
|
||||||
fnPos := getPosition(b.llvmFn)
|
fnPos := getPosition(b.llvmFn)
|
||||||
if fnPos.IsValid() {
|
if fnPos.IsValid() {
|
||||||
errValue += "\n\tprevious declaration at " + fnPos.String()
|
errValue += "\n\tprevious declaration at " + fnPos.String()
|
||||||
@@ -776,9 +778,15 @@ func (b *builder) createFunction() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !b.info.exported {
|
if !b.info.exported {
|
||||||
b.llvmFn.SetLinkage(llvm.InternalLinkage)
|
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
||||||
b.llvmFn.SetUnnamedAddr(true)
|
b.llvmFn.SetUnnamedAddr(true)
|
||||||
}
|
}
|
||||||
|
if b.info.exported && strings.HasPrefix(b.Triple, "wasm") {
|
||||||
|
// Set the exported name. This is necessary for WebAssembly because
|
||||||
|
// otherwise the function is not exported.
|
||||||
|
functionAttr := b.ctx.CreateStringAttribute("wasm-export-name", b.info.linkName)
|
||||||
|
b.llvmFn.AddFunctionAttr(functionAttr)
|
||||||
|
}
|
||||||
|
|
||||||
// Some functions have a pragma controlling the inlining level.
|
// Some functions have a pragma controlling the inlining level.
|
||||||
switch b.info.inline {
|
switch b.info.inline {
|
||||||
@@ -821,7 +829,7 @@ func (b *builder) createFunction() {
|
|||||||
for _, param := range b.fn.Params {
|
for _, param := range b.fn.Params {
|
||||||
llvmType := b.getLLVMType(param.Type())
|
llvmType := b.getLLVMType(param.Type())
|
||||||
fields := make([]llvm.Value, 0, 1)
|
fields := make([]llvm.Value, 0, 1)
|
||||||
for _, info := range expandFormalParamType(llvmType, param.Name(), param.Type()) {
|
for _, info := range b.expandFormalParamType(llvmType, param.Name(), param.Type()) {
|
||||||
param := b.llvmFn.Param(llvmParamIndex)
|
param := b.llvmFn.Param(llvmParamIndex)
|
||||||
param.SetName(info.name)
|
param.SetName(info.name)
|
||||||
fields = append(fields, param)
|
fields = append(fields, param)
|
||||||
@@ -948,6 +956,12 @@ func (b *builder) createFunction() {
|
|||||||
b.trackValue(phi.llvm)
|
b.trackValue(phi.llvm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create anonymous functions (closures etc.).
|
||||||
|
for _, sub := range b.fn.AnonFuncs {
|
||||||
|
b := newBuilder(b.compilerContext, b.Builder, sub)
|
||||||
|
b.createFunction()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// createInstruction builds the LLVM IR equivalent instructions for the
|
// createInstruction builds the LLVM IR equivalent instructions for the
|
||||||
|
|||||||
+17
-28
@@ -4,7 +4,6 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"go/types"
|
"go/types"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -20,6 +19,19 @@ var flagUpdate = flag.Bool("update", false, "update tests based on test output")
|
|||||||
// Basic tests for the compiler. Build some Go files and compare the output with
|
// Basic tests for the compiler. Build some Go files and compare the output with
|
||||||
// the expected LLVM IR for regression testing.
|
// the expected LLVM IR for regression testing.
|
||||||
func TestCompiler(t *testing.T) {
|
func TestCompiler(t *testing.T) {
|
||||||
|
// Check LLVM version.
|
||||||
|
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("could not parse LLVM version:", llvm.Version)
|
||||||
|
}
|
||||||
|
if llvmMajor < 11 {
|
||||||
|
// It is likely this version needs to be bumped in the future.
|
||||||
|
// The goal is to at least test the LLVM version that's used by default
|
||||||
|
// in TinyGo and (if possible without too many workarounds) also some
|
||||||
|
// previous versions.
|
||||||
|
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
|
||||||
|
}
|
||||||
|
|
||||||
target, err := compileopts.LoadTarget("i686--linux")
|
target, err := compileopts.LoadTarget("i686--linux")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("failed to load target:", err)
|
t.Fatal("failed to load target:", err)
|
||||||
@@ -47,7 +59,9 @@ func TestCompiler(t *testing.T) {
|
|||||||
"basic.go",
|
"basic.go",
|
||||||
"pointer.go",
|
"pointer.go",
|
||||||
"slice.go",
|
"slice.go",
|
||||||
|
"string.go",
|
||||||
"float.go",
|
"float.go",
|
||||||
|
"interface.go",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range tests {
|
for _, testCase := range tests {
|
||||||
@@ -65,8 +79,9 @@ func TestCompiler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compile AST to IR.
|
// Compile AST to IR.
|
||||||
|
program := lprogram.LoadSSA()
|
||||||
pkg := lprogram.MainPkg()
|
pkg := lprogram.MainPkg()
|
||||||
mod, errs := CompilePackage(testCase, pkg, machine, compilerConfig, false)
|
mod, errs := CompilePackage(testCase, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false)
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
t.Log("error:", err)
|
t.Log("error:", err)
|
||||||
@@ -107,8 +122,6 @@ func TestCompiler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var alignRegexp = regexp.MustCompile(", align [0-9]+$")
|
|
||||||
|
|
||||||
// fuzzyEqualIR returns true if the two LLVM IR strings passed in are roughly
|
// fuzzyEqualIR returns true if the two LLVM IR strings passed in are roughly
|
||||||
// equal. That means, only relevant lines are compared (excluding comments
|
// equal. That means, only relevant lines are compared (excluding comments
|
||||||
// etc.).
|
// etc.).
|
||||||
@@ -120,15 +133,6 @@ func fuzzyEqualIR(s1, s2 string) bool {
|
|||||||
}
|
}
|
||||||
for i, line1 := range lines1 {
|
for i, line1 := range lines1 {
|
||||||
line2 := lines2[i]
|
line2 := lines2[i]
|
||||||
match1 := alignRegexp.MatchString(line1)
|
|
||||||
match2 := alignRegexp.MatchString(line2)
|
|
||||||
if match1 != match2 {
|
|
||||||
// Only one of the lines has the align keyword. Remove it.
|
|
||||||
// This is a change to make the test work in both LLVM 10 and LLVM
|
|
||||||
// 11 (LLVM 11 appears to automatically add alignment everywhere).
|
|
||||||
line1 = alignRegexp.ReplaceAllString(line1, "")
|
|
||||||
line2 = alignRegexp.ReplaceAllString(line2, "")
|
|
||||||
}
|
|
||||||
if line1 != line2 {
|
if line1 != line2 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -142,12 +146,6 @@ func fuzzyEqualIR(s1, s2 string) bool {
|
|||||||
// stripped out.
|
// stripped out.
|
||||||
func filterIrrelevantIRLines(lines []string) []string {
|
func filterIrrelevantIRLines(lines []string) []string {
|
||||||
var out []string
|
var out []string
|
||||||
llvmVersion, err := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
|
|
||||||
if err != nil {
|
|
||||||
// Note: this should never happen and if it does, it will always happen
|
|
||||||
// for a particular build because llvm.Version is a constant.
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
line = strings.Split(line, ";")[0] // strip out comments/info
|
line = strings.Split(line, ";")[0] // strip out comments/info
|
||||||
line = strings.TrimRight(line, "\r ") // drop '\r' on Windows and remove trailing spaces from comments
|
line = strings.TrimRight(line, "\r ") // drop '\r' on Windows and remove trailing spaces from comments
|
||||||
@@ -157,15 +155,6 @@ func filterIrrelevantIRLines(lines []string) []string {
|
|||||||
if strings.HasPrefix(line, "source_filename = ") {
|
if strings.HasPrefix(line, "source_filename = ") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if llvmVersion < 10 && strings.HasPrefix(line, "attributes ") {
|
|
||||||
// Ignore attribute groups. These may change between LLVM versions.
|
|
||||||
// Right now test outputs are for LLVM 10.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if llvmVersion < 10 && strings.HasPrefix(line, "target datalayout ") {
|
|
||||||
// Ignore the target layout. This may change between LLVM versions.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
out = append(out, line)
|
out = append(out, line)
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
|
|||||||
+2
-2
@@ -352,7 +352,7 @@ func (b *builder) createRunDefers() {
|
|||||||
|
|
||||||
// Get the real defer struct type and cast to it.
|
// Get the real defer struct type and cast to it.
|
||||||
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
||||||
for _, param := range callback.Params {
|
for _, param := range getParams(callback.Signature) {
|
||||||
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
|
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
|
||||||
}
|
}
|
||||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||||
@@ -361,7 +361,7 @@ func (b *builder) createRunDefers() {
|
|||||||
// Extract the params from the struct.
|
// Extract the params from the struct.
|
||||||
forwardParams := []llvm.Value{}
|
forwardParams := []llvm.Value{}
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
for i := range callback.Params {
|
for i := range getParams(callback.Signature) {
|
||||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||||
forwardParam := b.CreateLoad(gep, "param")
|
forwardParam := b.CreateLoad(gep, "param")
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
|
|||||||
+3
-3
@@ -37,7 +37,7 @@ func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context
|
|||||||
funcValueWithSignatureGlobal = llvm.AddGlobal(c.mod, funcValueWithSignatureType, funcValueWithSignatureGlobalName)
|
funcValueWithSignatureGlobal = llvm.AddGlobal(c.mod, funcValueWithSignatureType, funcValueWithSignatureGlobalName)
|
||||||
funcValueWithSignatureGlobal.SetInitializer(funcValueWithSignature)
|
funcValueWithSignatureGlobal.SetInitializer(funcValueWithSignature)
|
||||||
funcValueWithSignatureGlobal.SetGlobalConstant(true)
|
funcValueWithSignatureGlobal.SetGlobalConstant(true)
|
||||||
funcValueWithSignatureGlobal.SetLinkage(llvm.InternalLinkage)
|
funcValueWithSignatureGlobal.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
}
|
}
|
||||||
funcValueScalar = llvm.ConstPtrToInt(funcValueWithSignatureGlobal, c.uintptrType)
|
funcValueScalar = llvm.ConstPtrToInt(funcValueWithSignatureGlobal, c.uintptrType)
|
||||||
default:
|
default:
|
||||||
@@ -124,13 +124,13 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
|
|||||||
// The receiver is not an interface, but a i8* type.
|
// The receiver is not an interface, but a i8* type.
|
||||||
recv = c.i8ptrType
|
recv = c.i8ptrType
|
||||||
}
|
}
|
||||||
for _, info := range expandFormalParamType(recv, "", nil) {
|
for _, info := range c.expandFormalParamType(recv, "", nil) {
|
||||||
paramTypes = append(paramTypes, info.llvmType)
|
paramTypes = append(paramTypes, info.llvmType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < typ.Params().Len(); i++ {
|
for i := 0; i < typ.Params().Len(); i++ {
|
||||||
subType := c.getLLVMType(typ.Params().At(i).Type())
|
subType := c.getLLVMType(typ.Params().At(i).Type())
|
||||||
for _, info := range expandFormalParamType(subType, "", nil) {
|
for _, info := range c.expandFormalParamType(subType, "", nil) {
|
||||||
paramTypes = append(paramTypes, info.llvmType)
|
paramTypes = append(paramTypes, info.llvmType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
|||||||
// Create the wrapper.
|
// Create the wrapper.
|
||||||
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
||||||
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
|
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
|
||||||
wrapper.SetLinkage(llvm.InternalLinkage)
|
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
wrapper.SetUnnamedAddr(true)
|
wrapper.SetUnnamedAddr(true)
|
||||||
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", name))
|
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", name))
|
||||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||||
@@ -141,7 +141,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
|||||||
// Create the wrapper.
|
// Create the wrapper.
|
||||||
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
||||||
wrapper = llvm.AddFunction(c.mod, prefix+".gowrapper", wrapperType)
|
wrapper = llvm.AddFunction(c.mod, prefix+".gowrapper", wrapperType)
|
||||||
wrapper.SetLinkage(llvm.InternalLinkage)
|
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
wrapper.SetUnnamedAddr(true)
|
wrapper.SetUnnamedAddr(true)
|
||||||
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", ""))
|
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", ""))
|
||||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||||
|
|||||||
+33
-24
@@ -24,16 +24,7 @@ import (
|
|||||||
func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value {
|
func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value {
|
||||||
itfValue := b.emitPointerPack([]llvm.Value{val})
|
itfValue := b.emitPointerPack([]llvm.Value{val})
|
||||||
itfTypeCodeGlobal := b.getTypeCode(typ)
|
itfTypeCodeGlobal := b.getTypeCode(typ)
|
||||||
itfMethodSetGlobal := b.getTypeMethodSet(typ)
|
itfTypeCode := b.CreatePtrToInt(itfTypeCodeGlobal, b.uintptrType, "")
|
||||||
itfConcreteTypeGlobal := b.mod.NamedGlobal("typeInInterface:" + itfTypeCodeGlobal.Name())
|
|
||||||
if itfConcreteTypeGlobal.IsNil() {
|
|
||||||
typeInInterface := b.getLLVMRuntimeType("typeInInterface")
|
|
||||||
itfConcreteTypeGlobal = llvm.AddGlobal(b.mod, typeInInterface, "typeInInterface:"+itfTypeCodeGlobal.Name())
|
|
||||||
itfConcreteTypeGlobal.SetInitializer(llvm.ConstNamedStruct(typeInInterface, []llvm.Value{itfTypeCodeGlobal, itfMethodSetGlobal}))
|
|
||||||
itfConcreteTypeGlobal.SetGlobalConstant(true)
|
|
||||||
itfConcreteTypeGlobal.SetLinkage(llvm.PrivateLinkage)
|
|
||||||
}
|
|
||||||
itfTypeCode := b.CreatePtrToInt(itfConcreteTypeGlobal, b.uintptrType, "")
|
|
||||||
itf := llvm.Undef(b.getLLVMRuntimeType("_interface"))
|
itf := llvm.Undef(b.getLLVMRuntimeType("_interface"))
|
||||||
itf = b.CreateInsertValue(itf, itfTypeCode, 0, "")
|
itf = b.CreateInsertValue(itf, itfTypeCode, 0, "")
|
||||||
itf = b.CreateInsertValue(itf, itfValue, 1, "")
|
itf = b.CreateInsertValue(itf, itfValue, 1, "")
|
||||||
@@ -54,6 +45,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
// reflect lowering simpler.
|
// reflect lowering simpler.
|
||||||
var references llvm.Value
|
var references llvm.Value
|
||||||
var length int64
|
var length int64
|
||||||
|
var methodSet llvm.Value
|
||||||
switch typ := typ.(type) {
|
switch typ := typ.(type) {
|
||||||
case *types.Named:
|
case *types.Named:
|
||||||
references = c.getTypeCode(typ.Underlying())
|
references = c.getTypeCode(typ.Underlying())
|
||||||
@@ -70,17 +62,28 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
// Take a pointer to the typecodeID of the first field (if it exists).
|
// Take a pointer to the typecodeID of the first field (if it exists).
|
||||||
structGlobal := c.makeStructTypeFields(typ)
|
structGlobal := c.makeStructTypeFields(typ)
|
||||||
references = llvm.ConstBitCast(structGlobal, global.Type())
|
references = llvm.ConstBitCast(structGlobal, global.Type())
|
||||||
|
case *types.Interface:
|
||||||
|
methodSetGlobal := c.getInterfaceMethodSet(typ)
|
||||||
|
references = llvm.ConstBitCast(methodSetGlobal, global.Type())
|
||||||
}
|
}
|
||||||
if !references.IsNil() {
|
if _, ok := typ.Underlying().(*types.Interface); !ok {
|
||||||
|
methodSet = c.getTypeMethodSet(typ)
|
||||||
|
}
|
||||||
|
if !references.IsNil() || length != 0 || !methodSet.IsNil() {
|
||||||
// Set the 'references' field of the runtime.typecodeID struct.
|
// Set the 'references' field of the runtime.typecodeID struct.
|
||||||
globalValue := llvm.ConstNull(global.Type().ElementType())
|
globalValue := llvm.ConstNull(global.Type().ElementType())
|
||||||
globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
|
if !references.IsNil() {
|
||||||
|
globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
|
||||||
|
}
|
||||||
if length != 0 {
|
if length != 0 {
|
||||||
lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false)
|
lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false)
|
||||||
globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
|
globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
|
||||||
}
|
}
|
||||||
|
if !methodSet.IsNil() {
|
||||||
|
globalValue = llvm.ConstInsertValue(globalValue, methodSet, []uint32{2})
|
||||||
|
}
|
||||||
global.SetInitializer(globalValue)
|
global.SetInitializer(globalValue)
|
||||||
global.SetLinkage(llvm.PrivateLinkage)
|
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
}
|
}
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
}
|
}
|
||||||
@@ -103,8 +106,8 @@ func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
|
|||||||
fieldName.SetLinkage(llvm.PrivateLinkage)
|
fieldName.SetLinkage(llvm.PrivateLinkage)
|
||||||
fieldName.SetUnnamedAddr(true)
|
fieldName.SetUnnamedAddr(true)
|
||||||
fieldName = llvm.ConstGEP(fieldName, []llvm.Value{
|
fieldName = llvm.ConstGEP(fieldName, []llvm.Value{
|
||||||
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||||
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||||
})
|
})
|
||||||
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldName, []uint32{1})
|
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldName, []uint32{1})
|
||||||
if typ.Tag(i) != "" {
|
if typ.Tag(i) != "" {
|
||||||
@@ -112,8 +115,8 @@ func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
|
|||||||
fieldTag.SetLinkage(llvm.PrivateLinkage)
|
fieldTag.SetLinkage(llvm.PrivateLinkage)
|
||||||
fieldTag.SetUnnamedAddr(true)
|
fieldTag.SetUnnamedAddr(true)
|
||||||
fieldTag = llvm.ConstGEP(fieldTag, []llvm.Value{
|
fieldTag = llvm.ConstGEP(fieldTag, []llvm.Value{
|
||||||
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||||
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||||
})
|
})
|
||||||
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldTag, []uint32{2})
|
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldTag, []uint32{2})
|
||||||
}
|
}
|
||||||
@@ -186,7 +189,7 @@ func getTypeCodeName(t types.Type) string {
|
|||||||
case *types.Interface:
|
case *types.Interface:
|
||||||
methods := make([]string, t.NumMethods())
|
methods := make([]string, t.NumMethods())
|
||||||
for i := 0; i < t.NumMethods(); i++ {
|
for i := 0; i < t.NumMethods(); i++ {
|
||||||
methods[i] = getTypeCodeName(t.Method(i).Type())
|
methods[i] = t.Method(i).Name() + ":" + getTypeCodeName(t.Method(i).Type())
|
||||||
}
|
}
|
||||||
return "interface:" + "{" + strings.Join(methods, ",") + "}"
|
return "interface:" + "{" + strings.Join(methods, ",") + "}"
|
||||||
case *types.Map:
|
case *types.Map:
|
||||||
@@ -264,7 +267,7 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
|
|||||||
global = llvm.AddGlobal(c.mod, arrayType, typ.String()+"$methodset")
|
global = llvm.AddGlobal(c.mod, arrayType, typ.String()+"$methodset")
|
||||||
global.SetInitializer(value)
|
global.SetInitializer(value)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetLinkage(llvm.PrivateLinkage)
|
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,7 +298,7 @@ func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
|
|||||||
global = llvm.AddGlobal(c.mod, value.Type(), name+"$interface")
|
global = llvm.AddGlobal(c.mod, value.Type(), name+"$interface")
|
||||||
global.SetInitializer(value)
|
global.SetInitializer(value)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetLinkage(llvm.PrivateLinkage)
|
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,10 +341,16 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
|||||||
commaOk = b.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, methodSet}, "")
|
commaOk = b.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, methodSet}, "")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
globalName := "reflect/types.type:" + getTypeCodeName(expr.AssertedType) + "$id"
|
||||||
|
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
|
||||||
|
if assertedTypeCodeGlobal.IsNil() {
|
||||||
|
// Create a new typecode global.
|
||||||
|
assertedTypeCodeGlobal = llvm.AddGlobal(b.mod, b.ctx.Int8Type(), globalName)
|
||||||
|
assertedTypeCodeGlobal.SetGlobalConstant(true)
|
||||||
|
}
|
||||||
// Type assert on concrete type.
|
// Type assert on concrete type.
|
||||||
// Call runtime.typeAssert, which will be lowered to a simple icmp or
|
// Call runtime.typeAssert, which will be lowered to a simple icmp or
|
||||||
// const false in the interface lowering pass.
|
// const false in the interface lowering pass.
|
||||||
assertedTypeCodeGlobal := b.getTypeCode(expr.AssertedType)
|
|
||||||
commaOk = b.createRuntimeCall("typeAssert", []llvm.Value{actualTypeNum, assertedTypeCodeGlobal}, "typecode")
|
commaOk = b.createRuntimeCall("typeAssert", []llvm.Value{actualTypeNum, assertedTypeCodeGlobal}, "typecode")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,9 +454,9 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the expanded receiver type.
|
// Get the expanded receiver type.
|
||||||
receiverType := c.getLLVMType(fn.Params[0].Type())
|
receiverType := c.getLLVMType(fn.Signature.Recv().Type())
|
||||||
var expandedReceiverType []llvm.Type
|
var expandedReceiverType []llvm.Type
|
||||||
for _, info := range expandFormalParamType(receiverType, "", nil) {
|
for _, info := range c.expandFormalParamType(receiverType, "", nil) {
|
||||||
expandedReceiverType = append(expandedReceiverType, info.llvmType)
|
expandedReceiverType = append(expandedReceiverType, info.llvmType)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,7 +476,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
|
|||||||
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
|
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
|
||||||
wrapper.LastParam().SetName("parentHandle")
|
wrapper.LastParam().SetName("parentHandle")
|
||||||
|
|
||||||
wrapper.SetLinkage(llvm.InternalLinkage)
|
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
wrapper.SetUnnamedAddr(true)
|
wrapper.SetUnnamedAddr(true)
|
||||||
|
|
||||||
// Create a new builder just to create this wrapper.
|
// Create a new builder just to create this wrapper.
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
|||||||
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt redeclared in this program")
|
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt redeclared in this program")
|
||||||
}
|
}
|
||||||
global := llvm.AddGlobal(b.mod, globalLLVMType, globalName)
|
global := llvm.AddGlobal(b.mod, globalLLVMType, globalName)
|
||||||
global.SetLinkage(llvm.PrivateLinkage)
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetUnnamedAddr(true)
|
global.SetUnnamedAddr(true)
|
||||||
initializer := llvm.ConstNull(globalLLVMType)
|
initializer := llvm.ConstNull(globalLLVMType)
|
||||||
|
|||||||
@@ -1,164 +0,0 @@
|
|||||||
package compiler
|
|
||||||
|
|
||||||
// This file implements a simple reachability analysis, to reduce compile time.
|
|
||||||
// This DCE pass used to be necessary for improving other passes but now it
|
|
||||||
// isn't necessary anymore.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"go/types"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/loader"
|
|
||||||
"golang.org/x/tools/go/ssa"
|
|
||||||
)
|
|
||||||
|
|
||||||
type dceState struct {
|
|
||||||
*compilerContext
|
|
||||||
functions []*dceFunction
|
|
||||||
functionMap map[*ssa.Function]*dceFunction
|
|
||||||
}
|
|
||||||
|
|
||||||
type dceFunction struct {
|
|
||||||
*ssa.Function
|
|
||||||
functionInfo
|
|
||||||
flag bool // used by dead code elimination
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *dceState) addFunction(ssaFn *ssa.Function) {
|
|
||||||
if _, ok := p.functionMap[ssaFn]; ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
f := &dceFunction{Function: ssaFn}
|
|
||||||
f.functionInfo = p.getFunctionInfo(ssaFn)
|
|
||||||
p.functions = append(p.functions, f)
|
|
||||||
p.functionMap[ssaFn] = f
|
|
||||||
|
|
||||||
for _, anon := range ssaFn.AnonFuncs {
|
|
||||||
p.addFunction(anon)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// simpleDCE returns a list of alive functions in the program. Compiling only
|
|
||||||
// these functions makes the compiler faster.
|
|
||||||
//
|
|
||||||
// This functionality will likely be replaced in the future with build caching.
|
|
||||||
func (c *compilerContext) simpleDCE(lprogram *loader.Program) ([]*ssa.Function, error) {
|
|
||||||
mainPkg := c.program.Package(lprogram.MainPkg().Pkg)
|
|
||||||
if mainPkg == nil {
|
|
||||||
panic("could not find main package")
|
|
||||||
}
|
|
||||||
p := &dceState{
|
|
||||||
compilerContext: c,
|
|
||||||
functionMap: make(map[*ssa.Function]*dceFunction),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, pkg := range lprogram.Sorted() {
|
|
||||||
pkg := c.program.Package(pkg.Pkg)
|
|
||||||
memberNames := make([]string, 0)
|
|
||||||
for name := range pkg.Members {
|
|
||||||
memberNames = append(memberNames, name)
|
|
||||||
}
|
|
||||||
sort.Strings(memberNames)
|
|
||||||
|
|
||||||
for _, name := range memberNames {
|
|
||||||
member := pkg.Members[name]
|
|
||||||
switch member := member.(type) {
|
|
||||||
case *ssa.Function:
|
|
||||||
p.addFunction(member)
|
|
||||||
case *ssa.Type:
|
|
||||||
methods := getAllMethods(pkg.Prog, member.Type())
|
|
||||||
if !types.IsInterface(member.Type()) {
|
|
||||||
// named type
|
|
||||||
for _, method := range methods {
|
|
||||||
p.addFunction(pkg.Prog.MethodValue(method))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *ssa.Global:
|
|
||||||
// Ignore. Globals are not handled here.
|
|
||||||
case *ssa.NamedConst:
|
|
||||||
// Ignore: these are already resolved.
|
|
||||||
default:
|
|
||||||
panic("unknown member type: " + member.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initial set of live functions. Include main.main, *.init and runtime.*
|
|
||||||
// functions.
|
|
||||||
main, ok := mainPkg.Members["main"].(*ssa.Function)
|
|
||||||
if !ok {
|
|
||||||
if mainPkg.Members["main"] == nil {
|
|
||||||
return nil, errors.New("function main is undeclared in the main package")
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("cannot declare main - must be func")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runtimePkg := c.program.ImportedPackage("runtime")
|
|
||||||
mathPkg := c.program.ImportedPackage("math")
|
|
||||||
taskPkg := c.program.ImportedPackage("internal/task")
|
|
||||||
p.functionMap[main].flag = true
|
|
||||||
worklist := []*ssa.Function{main}
|
|
||||||
for _, f := range p.functions {
|
|
||||||
if f.exported || f.Synthetic == "package initializer" || f.Pkg == runtimePkg || f.Pkg == taskPkg || (f.Pkg == mathPkg && f.Pkg != nil) {
|
|
||||||
if f.flag {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
f.flag = true
|
|
||||||
worklist = append(worklist, f.Function)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark all called functions recursively.
|
|
||||||
for len(worklist) != 0 {
|
|
||||||
f := worklist[len(worklist)-1]
|
|
||||||
worklist = worklist[:len(worklist)-1]
|
|
||||||
for _, block := range f.Blocks {
|
|
||||||
for _, instr := range block.Instrs {
|
|
||||||
if instr, ok := instr.(*ssa.MakeInterface); ok {
|
|
||||||
for _, sel := range getAllMethods(c.program, instr.X.Type()) {
|
|
||||||
fn := c.program.MethodValue(sel)
|
|
||||||
callee := p.functionMap[fn]
|
|
||||||
if callee == nil {
|
|
||||||
// TODO: why is this necessary?
|
|
||||||
p.addFunction(fn)
|
|
||||||
callee = p.functionMap[fn]
|
|
||||||
}
|
|
||||||
if !callee.flag {
|
|
||||||
callee.flag = true
|
|
||||||
worklist = append(worklist, callee.Function)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, operand := range instr.Operands(nil) {
|
|
||||||
if operand == nil || *operand == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch operand := (*operand).(type) {
|
|
||||||
case *ssa.Function:
|
|
||||||
f := p.functionMap[operand]
|
|
||||||
if f == nil {
|
|
||||||
// FIXME HACK: this function should have been
|
|
||||||
// discovered already. It is not for bound methods.
|
|
||||||
p.addFunction(operand)
|
|
||||||
f = p.functionMap[operand]
|
|
||||||
}
|
|
||||||
if !f.flag {
|
|
||||||
f.flag = true
|
|
||||||
worklist = append(worklist, operand)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return all live functions.
|
|
||||||
liveFunctions := []*ssa.Function{}
|
|
||||||
for _, f := range p.functions {
|
|
||||||
if f.flag {
|
|
||||||
liveFunctions = append(liveFunctions, f.Function)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return liveFunctions, nil
|
|
||||||
}
|
|
||||||
@@ -150,6 +150,9 @@ func (s *stdSizes) Sizeof(T types.Type) int64 {
|
|||||||
return s.PtrSize * 2
|
return s.PtrSize * 2
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
return s.PtrSize
|
return s.PtrSize
|
||||||
|
case *types.Signature:
|
||||||
|
// Func values in TinyGo are two words in size.
|
||||||
|
return s.PtrSize * 2
|
||||||
default:
|
default:
|
||||||
panic("unknown type: " + t.String())
|
panic("unknown type: " + t.String())
|
||||||
}
|
}
|
||||||
|
|||||||
+47
-27
@@ -72,9 +72,9 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var paramInfos []paramInfo
|
var paramInfos []paramInfo
|
||||||
for _, param := range fn.Params {
|
for _, param := range getParams(fn.Signature) {
|
||||||
paramType := c.getLLVMType(param.Type())
|
paramType := c.getLLVMType(param.Type())
|
||||||
paramFragmentInfos := expandFormalParamType(paramType, param.Name(), param.Type())
|
paramFragmentInfos := c.expandFormalParamType(paramType, param.Name(), param.Type())
|
||||||
paramInfos = append(paramInfos, paramFragmentInfos...)
|
paramInfos = append(paramInfos, paramFragmentInfos...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,6 +172,21 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Synthetic functions are functions that do not appear in the source code,
|
||||||
|
// they are artificially constructed. Usually they are wrapper functions
|
||||||
|
// that are not referenced anywhere except in a SSA call instruction so
|
||||||
|
// should be created right away.
|
||||||
|
// The exception is the package initializer, which does appear in the
|
||||||
|
// *ssa.Package members and so shouldn't be created here.
|
||||||
|
if fn.Synthetic != "" && fn.Synthetic != "package initializer" {
|
||||||
|
irbuilder := c.ctx.NewBuilder()
|
||||||
|
b := newBuilder(c, irbuilder, fn)
|
||||||
|
b.createFunction()
|
||||||
|
irbuilder.Dispose()
|
||||||
|
llvmFn.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
|
llvmFn.SetUnnamedAddr(true)
|
||||||
|
}
|
||||||
|
|
||||||
return llvmFn
|
return llvmFn
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,6 +293,20 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getParams returns the function parameters, including the receiver at the
|
||||||
|
// start. This is an alternative to the Params member of *ssa.Function, which is
|
||||||
|
// not yet populated when the package has not yet been built.
|
||||||
|
func getParams(sig *types.Signature) []*types.Var {
|
||||||
|
params := []*types.Var{}
|
||||||
|
if sig.Recv() != nil {
|
||||||
|
params = append(params, sig.Recv())
|
||||||
|
}
|
||||||
|
for i := 0; i < sig.Params().Len(); i++ {
|
||||||
|
params = append(params, sig.Params().At(i))
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
// globalInfo contains some information about a specific global. By default,
|
// globalInfo contains some information about a specific global. By default,
|
||||||
// linkName is equal to .RelString(nil) on a global and extern is false, but for
|
// linkName is equal to .RelString(nil) on a global and extern is false, but for
|
||||||
// some symbols this is different (due to //go:extern for example).
|
// some symbols this is different (due to //go:extern for example).
|
||||||
@@ -289,25 +318,22 @@ type globalInfo struct {
|
|||||||
|
|
||||||
// loadASTComments loads comments on globals from the AST, for use later in the
|
// loadASTComments loads comments on globals from the AST, for use later in the
|
||||||
// program. In particular, they are required for //go:extern pragmas on globals.
|
// program. In particular, they are required for //go:extern pragmas on globals.
|
||||||
func (c *compilerContext) loadASTComments(lprogram *loader.Program) {
|
func (c *compilerContext) loadASTComments(pkg *loader.Package) {
|
||||||
c.astComments = map[string]*ast.CommentGroup{}
|
for _, file := range pkg.Files {
|
||||||
for _, pkgInfo := range lprogram.Sorted() {
|
for _, decl := range file.Decls {
|
||||||
for _, file := range pkgInfo.Files {
|
switch decl := decl.(type) {
|
||||||
for _, decl := range file.Decls {
|
case *ast.GenDecl:
|
||||||
switch decl := decl.(type) {
|
switch decl.Tok {
|
||||||
case *ast.GenDecl:
|
case token.VAR:
|
||||||
switch decl.Tok {
|
if len(decl.Specs) != 1 {
|
||||||
case token.VAR:
|
continue
|
||||||
if len(decl.Specs) != 1 {
|
}
|
||||||
continue
|
for _, spec := range decl.Specs {
|
||||||
}
|
switch spec := spec.(type) {
|
||||||
for _, spec := range decl.Specs {
|
case *ast.ValueSpec: // decl.Tok == token.VAR
|
||||||
switch spec := spec.(type) {
|
for _, name := range spec.Names {
|
||||||
case *ast.ValueSpec: // decl.Tok == token.VAR
|
id := pkg.Pkg.Path() + "." + name.Name
|
||||||
for _, name := range spec.Names {
|
c.astComments[id] = decl.Doc
|
||||||
id := pkgInfo.Pkg.Path() + "." + name.Name
|
|
||||||
c.astComments[id] = decl.Doc
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -326,10 +352,6 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
|
|||||||
typ := g.Type().(*types.Pointer).Elem()
|
typ := g.Type().(*types.Pointer).Elem()
|
||||||
llvmType := c.getLLVMType(typ)
|
llvmType := c.getLLVMType(typ)
|
||||||
llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName)
|
llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName)
|
||||||
if !info.extern {
|
|
||||||
llvmGlobal.SetInitializer(llvm.ConstNull(llvmType))
|
|
||||||
llvmGlobal.SetLinkage(llvm.InternalLinkage)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set alignment from the //go:align comment.
|
// Set alignment from the //go:align comment.
|
||||||
var alignInBits uint32
|
var alignInBits uint32
|
||||||
@@ -347,8 +369,6 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
|
|||||||
|
|
||||||
if c.Debug && !info.extern {
|
if c.Debug && !info.extern {
|
||||||
// Add debug info.
|
// Add debug info.
|
||||||
// TODO: this should be done for every global in the program, not just
|
|
||||||
// the ones that are referenced from some code.
|
|
||||||
pos := c.program.Fset.Position(g.Pos())
|
pos := c.program.Fset.Position(g.Pos())
|
||||||
diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{
|
diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{
|
||||||
Name: g.RelString(nil),
|
Name: g.RelString(nil),
|
||||||
|
|||||||
Vendored
+16
-14
@@ -3,70 +3,72 @@ source_filename = "basic.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||||
target triple = "i686--linux"
|
target triple = "i686--linux"
|
||||||
|
|
||||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i32 @main.addInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i32 @main.addInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = add i32 %x, %y
|
%0 = add i32 %x, %y
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i1 @main.equalInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i1 @main.equalInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %x, %y
|
%0 = icmp eq i32 %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i1 @main.floatEQ(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i1 @main.floatEQ(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp oeq float %x, %y
|
%0 = fcmp oeq float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i1 @main.floatNE(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i1 @main.floatNE(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp une float %x, %y
|
%0 = fcmp une float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i1 @main.floatLower(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i1 @main.floatLower(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp olt float %x, %y
|
%0 = fcmp olt float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i1 @main.floatLowerEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i1 @main.floatLowerEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp ole float %x, %y
|
%0 = fcmp ole float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i1 @main.floatGreater(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i1 @main.floatGreater(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp ogt float %x, %y
|
%0 = fcmp ogt float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i1 @main.floatGreaterEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i1 @main.floatGreaterEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp oge float %x, %y
|
%0 = fcmp oge float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal float @main.complexReal(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden float @main.complexReal(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret float %x.r
|
ret float %x.r
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal float @main.complexImag(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden float @main.complexImag(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret float %x.i
|
ret float %x.i
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fadd float %x.r, %y.r
|
%0 = fadd float %x.r, %y.r
|
||||||
%1 = fadd float %x.i, %y.i
|
%1 = fadd float %x.i, %y.i
|
||||||
@@ -75,7 +77,7 @@ entry:
|
|||||||
ret { float, float } %3
|
ret { float, float } %3
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fsub float %x.r, %y.r
|
%0 = fsub float %x.r, %y.r
|
||||||
%1 = fsub float %x.i, %y.i
|
%1 = fsub float %x.i, %y.i
|
||||||
@@ -84,7 +86,7 @@ entry:
|
|||||||
ret { float, float } %3
|
ret { float, float } %3
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = fmul float %x.r, %y.r
|
%0 = fmul float %x.r, %y.r
|
||||||
%1 = fmul float %x.i, %y.i
|
%1 = fmul float %x.i, %y.i
|
||||||
|
|||||||
Vendored
+11
-9
@@ -3,12 +3,14 @@ source_filename = "float.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||||
target triple = "i686--linux"
|
target triple = "i686--linux"
|
||||||
|
|
||||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i32 @main.f32tou32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i32 @main.f32tou32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%positive = fcmp oge float %v, 0.000000e+00
|
%positive = fcmp oge float %v, 0.000000e+00
|
||||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||||
@@ -19,22 +21,22 @@ entry:
|
|||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal float @main.maxu32f(i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden float @main.maxu32f(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret float 0x41F0000000000000
|
ret float 0x41F0000000000000
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i32 @main.maxu32tof32(i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i32 @main.maxu32tof32(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret i32 -1
|
ret i32 -1
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal { i32, i32, i32, i32 } @main.inftoi32(i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden { i32, i32, i32, i32 } @main.inftoi32(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
|
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i32 @main.u32tof32tou32(i32 %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i32 @main.u32tof32tou32(i32 %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = uitofp i32 %v to float
|
%0 = uitofp i32 %v to float
|
||||||
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
|
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
|
||||||
@@ -43,7 +45,7 @@ entry:
|
|||||||
ret i32 %1
|
ret i32 %1
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal float @main.f32tou32tof32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden float @main.f32tou32tof32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%positive = fcmp oge float %v, 0.000000e+00
|
%positive = fcmp oge float %v, 0.000000e+00
|
||||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||||
@@ -55,7 +57,7 @@ entry:
|
|||||||
ret float %1
|
ret float %1
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i8 @main.f32tou8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i8 @main.f32tou8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%positive = fcmp oge float %v, 0.000000e+00
|
%positive = fcmp oge float %v, 0.000000e+00
|
||||||
%withinmax = fcmp ole float %v, 2.550000e+02
|
%withinmax = fcmp ole float %v, 2.550000e+02
|
||||||
@@ -66,7 +68,7 @@ entry:
|
|||||||
ret i8 %0
|
ret i8 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i8 @main.f32toi8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i8 @main.f32toi8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%abovemin = fcmp oge float %v, -1.280000e+02
|
%abovemin = fcmp oge float %v, -1.280000e+02
|
||||||
%belowmax = fcmp ole float %v, 1.270000e+02
|
%belowmax = fcmp ole float %v, 1.270000e+02
|
||||||
|
|||||||
Vendored
+54
@@ -0,0 +1,54 @@
|
|||||||
|
// This file tests interface types and interface builtins.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// Test interface construction.
|
||||||
|
|
||||||
|
func simpleType() interface{} {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func pointerType() interface{} {
|
||||||
|
// Pointers have an element type, in this case int.
|
||||||
|
var v *int
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func interfaceType() interface{} {
|
||||||
|
// Interfaces can exist in interfaces, but only indirectly (through
|
||||||
|
// pointers).
|
||||||
|
var v *error
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func anonymousInterfaceType() interface{} {
|
||||||
|
var v *interface {
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test interface builtins.
|
||||||
|
|
||||||
|
func isInt(itf interface{}) bool {
|
||||||
|
_, ok := itf.(int)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func isError(itf interface{}) bool {
|
||||||
|
// Interface assert on (builtin) named interface type.
|
||||||
|
_, ok := itf.(error)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func isStringer(itf interface{}) bool {
|
||||||
|
// Interface assert on anonymous interface type.
|
||||||
|
_, ok := itf.(interface {
|
||||||
|
String() string
|
||||||
|
})
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func callErrorMethod(itf error) string {
|
||||||
|
return itf.Error()
|
||||||
|
}
|
||||||
Vendored
+100
@@ -0,0 +1,100 @@
|
|||||||
|
; ModuleID = 'interface.go'
|
||||||
|
source_filename = "interface.go"
|
||||||
|
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||||
|
target triple = "i686--linux"
|
||||||
|
|
||||||
|
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo* }
|
||||||
|
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||||
|
%runtime._interface = type { i32, i8* }
|
||||||
|
%runtime._string = type { i8*, i32 }
|
||||||
|
|
||||||
|
@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID zeroinitializer
|
||||||
|
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null }
|
||||||
|
@"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null }
|
||||||
|
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null }
|
||||||
|
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null }
|
||||||
|
@"func Error() string" = external constant i8
|
||||||
|
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
|
||||||
|
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null }
|
||||||
|
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null }
|
||||||
|
@"func String() string" = external constant i8
|
||||||
|
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func String() string"]
|
||||||
|
@"reflect/types.type:basic:int$id" = external constant i8
|
||||||
|
@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
|
||||||
|
|
||||||
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden %runtime._interface @main.simpleType(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:int" to i32), i8* null }
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden %runtime._interface @main.pointerType(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:basic:int" to i32), i8* null }
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden %runtime._interface @main.interfaceType(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:named:error" to i32), i8* null }
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden %runtime._interface @main.anonymousInterfaceType(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" to i32), i8* null }
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.type:basic:int$id", i8* undef, i8* null)
|
||||||
|
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
|
typeassert.ok: ; preds = %entry
|
||||||
|
br label %typeassert.next
|
||||||
|
|
||||||
|
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||||
|
ret i1 %typecode
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i1 @runtime.typeAssert(i32, i8* dereferenceable_or_null(1), i8*, i8*)
|
||||||
|
|
||||||
|
define hidden i1 @main.isError(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = call i1 @runtime.interfaceImplements(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"error$interface", i32 0, i32 0), i8* undef, i8* null)
|
||||||
|
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
|
typeassert.ok: ; preds = %entry
|
||||||
|
br label %typeassert.next
|
||||||
|
|
||||||
|
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||||
|
ret i1 %0
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i1 @runtime.interfaceImplements(i32, i8** dereferenceable_or_null(4), i8*, i8*)
|
||||||
|
|
||||||
|
define hidden i1 @main.isStringer(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = call i1 @runtime.interfaceImplements(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"reflect/types.interface:interface{String() string}$interface", i32 0, i32 0), i8* undef, i8* null)
|
||||||
|
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
|
typeassert.ok: ; preds = %entry
|
||||||
|
br label %typeassert.next
|
||||||
|
|
||||||
|
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||||
|
ret i1 %0
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%invoke.func = call i32 @runtime.interfaceMethod(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"error$interface", i32 0, i32 0), i8* nonnull @"func Error() string", i8* undef, i8* null)
|
||||||
|
%invoke.func.cast = inttoptr i32 %invoke.func to %runtime._string (i8*, i8*, i8*)*
|
||||||
|
%0 = call %runtime._string %invoke.func.cast(i8* %itf.value, i8* undef, i8* undef)
|
||||||
|
ret %runtime._string %0
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i32 @runtime.interfaceMethod(i32, i8** dereferenceable_or_null(4), i8* dereferenceable_or_null(1), i8*, i8*)
|
||||||
Vendored
+10
-8
@@ -3,46 +3,48 @@ source_filename = "pointer.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||||
target triple = "i686--linux"
|
target triple = "i686--linux"
|
||||||
|
|
||||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret [0 x i32] zeroinitializer
|
ret [0 x i32] zeroinitializer
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = bitcast i8* %x to i32*
|
%0 = bitcast i8* %x to i32*
|
||||||
ret i32* %0
|
ret i32* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = bitcast i32* %x to i8*
|
%0 = bitcast i32* %x to i8*
|
||||||
ret i8* %0
|
ret i8* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret i8* %x
|
ret i8* %x
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 10
|
%0 = getelementptr inbounds i8, i8* %ptr, i32 10
|
||||||
ret i8* %0
|
ret i8* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
|
%0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
|
||||||
ret i8* %0
|
ret i8* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%0 = getelementptr i32, i32* %ptr, i32 %offset
|
%0 = getelementptr i32, i32* %ptr, i32 %offset
|
||||||
ret i32* %0
|
ret i32* %0
|
||||||
|
|||||||
Vendored
+16
@@ -7,3 +7,19 @@ func sliceLen(ints []int) int {
|
|||||||
func sliceCap(ints []int) int {
|
func sliceCap(ints []int) int {
|
||||||
return cap(ints)
|
return cap(ints)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sliceElement(ints []int, index int) int {
|
||||||
|
return ints[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
func sliceAppendValues(ints []int) []int {
|
||||||
|
return append(ints, 1, 2, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sliceAppendSlice(ints, added []int) []int {
|
||||||
|
return append(ints, added...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sliceCopy(dst, src []int) int {
|
||||||
|
return copy(dst, src)
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+72
-3
@@ -3,17 +3,86 @@ source_filename = "slice.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||||
target triple = "i686--linux"
|
target triple = "i686--linux"
|
||||||
|
|
||||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %ints.len
|
ret i32 %ints.len
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %ints.cap
|
ret i32 %ints.cap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define hidden i32 @main.sliceElement(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%.not = icmp ult i32 %index, %ints.len
|
||||||
|
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||||
|
|
||||||
|
lookup.throw: ; preds = %entry
|
||||||
|
call void @runtime.lookupPanic(i8* undef, i8* null)
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
lookup.next: ; preds = %entry
|
||||||
|
%0 = getelementptr inbounds i32, i32* %ints.data, i32 %index
|
||||||
|
%1 = load i32, i32* %0, align 4
|
||||||
|
ret i32 %1
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.lookupPanic(i8*, i8*)
|
||||||
|
|
||||||
|
define hidden { i32*, i32, i32 } @main.sliceAppendValues(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%varargs = call i8* @runtime.alloc(i32 12, i8* undef, i8* null)
|
||||||
|
%0 = bitcast i8* %varargs to i32*
|
||||||
|
store i32 1, i32* %0, align 4
|
||||||
|
%1 = getelementptr inbounds i8, i8* %varargs, i32 4
|
||||||
|
%2 = bitcast i8* %1 to i32*
|
||||||
|
store i32 2, i32* %2, align 4
|
||||||
|
%3 = getelementptr inbounds i8, i8* %varargs, i32 8
|
||||||
|
%4 = bitcast i8* %3 to i32*
|
||||||
|
store i32 3, i32* %4, align 4
|
||||||
|
%append.srcPtr = bitcast i32* %ints.data to i8*
|
||||||
|
%append.new = call { i8*, i32, i32 } @runtime.sliceAppend(i8* %append.srcPtr, i8* nonnull %varargs, i32 %ints.len, i32 %ints.cap, i32 3, i32 4, i8* undef, i8* null)
|
||||||
|
%append.newPtr = extractvalue { i8*, i32, i32 } %append.new, 0
|
||||||
|
%append.newBuf = bitcast i8* %append.newPtr to i32*
|
||||||
|
%append.newLen = extractvalue { i8*, i32, i32 } %append.new, 1
|
||||||
|
%append.newCap = extractvalue { i8*, i32, i32 } %append.new, 2
|
||||||
|
%5 = insertvalue { i32*, i32, i32 } undef, i32* %append.newBuf, 0
|
||||||
|
%6 = insertvalue { i32*, i32, i32 } %5, i32 %append.newLen, 1
|
||||||
|
%7 = insertvalue { i32*, i32, i32 } %6, i32 %append.newCap, 2
|
||||||
|
ret { i32*, i32, i32 } %7
|
||||||
|
}
|
||||||
|
|
||||||
|
declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8*, i32, i32, i32, i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden { i32*, i32, i32 } @main.sliceAppendSlice(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32* %added.data, i32 %added.len, i32 %added.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%append.srcPtr = bitcast i32* %ints.data to i8*
|
||||||
|
%append.srcPtr1 = bitcast i32* %added.data to i8*
|
||||||
|
%append.new = call { i8*, i32, i32 } @runtime.sliceAppend(i8* %append.srcPtr, i8* %append.srcPtr1, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, i8* undef, i8* null)
|
||||||
|
%append.newPtr = extractvalue { i8*, i32, i32 } %append.new, 0
|
||||||
|
%append.newBuf = bitcast i8* %append.newPtr to i32*
|
||||||
|
%append.newLen = extractvalue { i8*, i32, i32 } %append.new, 1
|
||||||
|
%append.newCap = extractvalue { i8*, i32, i32 } %append.new, 2
|
||||||
|
%0 = insertvalue { i32*, i32, i32 } undef, i32* %append.newBuf, 0
|
||||||
|
%1 = insertvalue { i32*, i32, i32 } %0, i32 %append.newLen, 1
|
||||||
|
%2 = insertvalue { i32*, i32, i32 } %1, i32 %append.newCap, 2
|
||||||
|
ret { i32*, i32, i32 } %2
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden i32 @main.sliceCopy(i32* %dst.data, i32 %dst.len, i32 %dst.cap, i32* %src.data, i32 %src.len, i32 %src.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%copy.dstPtr = bitcast i32* %dst.data to i8*
|
||||||
|
%copy.srcPtr = bitcast i32* %src.data to i8*
|
||||||
|
%copy.n = call i32 @runtime.sliceCopy(i8* %copy.dstPtr, i8* %copy.srcPtr, i32 %dst.len, i32 %src.len, i32 4, i8* undef, i8* null)
|
||||||
|
ret i32 %copy.n
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i32 @runtime.sliceCopy(i8*, i8*, i32, i32, i32, i8*, i8*)
|
||||||
|
|||||||
Vendored
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func stringLen(s string) int {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringIndex(s string, index int) byte {
|
||||||
|
return s[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringCompareEqual(s1, s2 string) bool {
|
||||||
|
return s1 == s2
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringCompareUnequal(s1, s2 string) bool {
|
||||||
|
return s1 != s2
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringCompareLarger(s1, s2 string) bool {
|
||||||
|
return s1 > s2
|
||||||
|
}
|
||||||
Vendored
+57
@@ -0,0 +1,57 @@
|
|||||||
|
; ModuleID = 'string.go'
|
||||||
|
source_filename = "string.go"
|
||||||
|
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||||
|
target triple = "i686--linux"
|
||||||
|
|
||||||
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden i32 @main.stringLen(i8* %s.data, i32 %s.len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret i32 %s.len
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden i8 @main.stringIndex(i8* %s.data, i32 %s.len, i32 %index, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%.not = icmp ult i32 %index, %s.len
|
||||||
|
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||||
|
|
||||||
|
lookup.throw: ; preds = %entry
|
||||||
|
call void @runtime.lookupPanic(i8* undef, i8* null)
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
lookup.next: ; preds = %entry
|
||||||
|
%0 = getelementptr inbounds i8, i8* %s.data, i32 %index
|
||||||
|
%1 = load i8, i8* %0, align 1
|
||||||
|
ret i8 %1
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.lookupPanic(i8*, i8*)
|
||||||
|
|
||||||
|
define hidden i1 @main.stringCompareEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef, i8* null)
|
||||||
|
ret i1 %0
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i1 @runtime.stringEqual(i8*, i32, i8*, i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden i1 @main.stringCompareUnequal(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef, i8* null)
|
||||||
|
%1 = xor i1 %0, true
|
||||||
|
ret i1 %1
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden i1 @main.stringCompareLarger(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = call i1 @runtime.stringLess(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef, i8* null)
|
||||||
|
%1 = xor i1 %0, true
|
||||||
|
ret i1 %1
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i1 @runtime.stringLess(i8*, i32, i8*, i32, i8*, i8*)
|
||||||
@@ -12,5 +12,5 @@ require (
|
|||||||
go.bug.st/serial v1.1.2
|
go.bug.st/serial v1.1.2
|
||||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
|
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
|
||||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
|
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
|
||||||
tinygo.org/x/go-llvm v0.0.0-20210206225315-7fe719483a0f
|
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -57,5 +57,7 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbO
|
|||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20210206225315-7fe719483a0f h1:FP5Do5omlQ/dLQ3Hfy7oyJo69VS5Hn46rZw004r0lGU=
|
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20210206225315-7fe719483a0f/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||||
|
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c h1:vn9IPshzYmzZis10UEVrsIBRv9FpykADw6M3/tHHROg=
|
||||||
|
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ var (
|
|||||||
errMapAlreadyCreated = errors.New("interp: map already created")
|
errMapAlreadyCreated = errors.New("interp: map already created")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is one of the errors that can be returned from toLLVMValue when the
|
||||||
|
// passed type does not fit the data to serialize. It is recoverable by
|
||||||
|
// serializing without a type (using rawValue.rawLLVMValue).
|
||||||
|
var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not equal pointer size")
|
||||||
|
|
||||||
func isRecoverableError(err error) bool {
|
func isRecoverableError(err error) bool {
|
||||||
return err == errIntegerAsPointer || err == errUnsupportedInst || err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated
|
return err == errIntegerAsPointer || err == errUnsupportedInst || err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated
|
||||||
}
|
}
|
||||||
|
|||||||
+124
-5
@@ -11,6 +11,12 @@ import (
|
|||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Version of the interp package. It must be incremented whenever the interp
|
||||||
|
// package is changed in a way that affects the output so that cached package
|
||||||
|
// builds will be invalidated.
|
||||||
|
// This version is independent of the TinyGo version number.
|
||||||
|
const Version = 1
|
||||||
|
|
||||||
// Enable extra checks, which should be disabled by default.
|
// Enable extra checks, which should be disabled by default.
|
||||||
// This may help track down bugs by adding a few more sanity checks.
|
// This may help track down bugs by adding a few more sanity checks.
|
||||||
const checks = true
|
const checks = true
|
||||||
@@ -32,9 +38,7 @@ type runner struct {
|
|||||||
callsExecuted uint64
|
callsExecuted uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run evaluates runtime.initAll function as much as possible at compile time.
|
func newRunner(mod llvm.Module, debug bool) *runner {
|
||||||
// Set debug to true if it should print output while running.
|
|
||||||
func Run(mod llvm.Module, debug bool) error {
|
|
||||||
r := runner{
|
r := runner{
|
||||||
mod: mod,
|
mod: mod,
|
||||||
targetData: llvm.NewTargetData(mod.DataLayout()),
|
targetData: llvm.NewTargetData(mod.DataLayout()),
|
||||||
@@ -47,6 +51,13 @@ func Run(mod llvm.Module, debug bool) error {
|
|||||||
r.pointerSize = uint32(r.targetData.PointerSize())
|
r.pointerSize = uint32(r.targetData.PointerSize())
|
||||||
r.i8ptrType = llvm.PointerType(mod.Context().Int8Type(), 0)
|
r.i8ptrType = llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||||
r.maxAlign = r.targetData.PrefTypeAlignment(r.i8ptrType) // assume pointers are maximally aligned (this is not always the case)
|
r.maxAlign = r.targetData.PrefTypeAlignment(r.i8ptrType) // assume pointers are maximally aligned (this is not always the case)
|
||||||
|
return &r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run evaluates runtime.initAll function as much as possible at compile time.
|
||||||
|
// Set debug to true if it should print output while running.
|
||||||
|
func Run(mod llvm.Module, debug bool) error {
|
||||||
|
r := newRunner(mod, debug)
|
||||||
|
|
||||||
initAll := mod.NamedFunction("runtime.initAll")
|
initAll := mod.NamedFunction("runtime.initAll")
|
||||||
bb := initAll.EntryBasicBlock()
|
bb := initAll.EntryBasicBlock()
|
||||||
@@ -117,7 +128,7 @@ func Run(mod llvm.Module, debug bool) error {
|
|||||||
r.pkgName = ""
|
r.pkgName = ""
|
||||||
|
|
||||||
// Update all global variables in the LLVM module.
|
// Update all global variables in the LLVM module.
|
||||||
mem := memoryView{r: &r}
|
mem := memoryView{r: r}
|
||||||
for _, obj := range r.objects {
|
for _, obj := range r.objects {
|
||||||
if obj.llvmGlobal.IsNil() {
|
if obj.llvmGlobal.IsNil() {
|
||||||
continue
|
continue
|
||||||
@@ -125,7 +136,34 @@ func Run(mod llvm.Module, debug bool) error {
|
|||||||
if obj.buffer == nil {
|
if obj.buffer == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
initializer := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
|
initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
|
||||||
|
if err == errInvalidPtrToIntSize {
|
||||||
|
// This can happen when a previous interp run did not have the
|
||||||
|
// correct LLVM type for a global and made something up. In that
|
||||||
|
// case, some fields could be written out as a series of (null)
|
||||||
|
// bytes even though they actually contain a pointer value.
|
||||||
|
// As a fallback, use asRawValue to get something of the correct
|
||||||
|
// memory layout.
|
||||||
|
initializer, err := obj.buffer.asRawValue(r).rawLLVMValue(&mem)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
initializerType := initializer.Type()
|
||||||
|
newGlobal := llvm.AddGlobal(mod, initializerType, obj.llvmGlobal.Name()+".tmp")
|
||||||
|
newGlobal.SetInitializer(initializer)
|
||||||
|
newGlobal.SetLinkage(obj.llvmGlobal.Linkage())
|
||||||
|
newGlobal.SetAlignment(obj.llvmGlobal.Alignment())
|
||||||
|
// TODO: copy debug info, unnamed_addr, ...
|
||||||
|
bitcast := llvm.ConstBitCast(newGlobal, obj.llvmGlobal.Type())
|
||||||
|
obj.llvmGlobal.ReplaceAllUsesWith(bitcast)
|
||||||
|
name := obj.llvmGlobal.Name()
|
||||||
|
obj.llvmGlobal.EraseFromParentAsGlobal()
|
||||||
|
newGlobal.SetName(name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if checks && initializer.Type() != obj.llvmGlobal.Type().ElementType() {
|
if checks && initializer.Type() != obj.llvmGlobal.Type().ElementType() {
|
||||||
panic("initializer type mismatch")
|
panic("initializer type mismatch")
|
||||||
}
|
}
|
||||||
@@ -135,6 +173,87 @@ func Run(mod llvm.Module, debug bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RunFunc evaluates a single package initializer at compile time.
|
||||||
|
// Set debug to true if it should print output while running.
|
||||||
|
func RunFunc(fn llvm.Value, debug bool) error {
|
||||||
|
// Create and initialize *runner object.
|
||||||
|
mod := fn.GlobalParent()
|
||||||
|
r := newRunner(mod, debug)
|
||||||
|
initName := fn.Name()
|
||||||
|
if !strings.HasSuffix(initName, ".init") {
|
||||||
|
return errorAt(fn, "interp: unexpected function name (expected *.init)")
|
||||||
|
}
|
||||||
|
r.pkgName = initName[:len(initName)-len(".init")]
|
||||||
|
|
||||||
|
// Create new function with the interp result.
|
||||||
|
newFn := llvm.AddFunction(mod, fn.Name()+".tmp", fn.Type().ElementType())
|
||||||
|
newFn.SetLinkage(fn.Linkage())
|
||||||
|
newFn.SetVisibility(fn.Visibility())
|
||||||
|
entry := mod.Context().AddBasicBlock(newFn, "entry")
|
||||||
|
|
||||||
|
// Create a builder, to insert instructions that could not be evaluated at
|
||||||
|
// compile time.
|
||||||
|
r.builder = mod.Context().NewBuilder()
|
||||||
|
defer r.builder.Dispose()
|
||||||
|
r.builder.SetInsertPointAtEnd(entry)
|
||||||
|
|
||||||
|
// Copy debug information.
|
||||||
|
subprogram := fn.Subprogram()
|
||||||
|
if !subprogram.IsNil() {
|
||||||
|
newFn.SetSubprogram(subprogram)
|
||||||
|
r.builder.SetCurrentDebugLocation(subprogram.SubprogramLine(), 0, subprogram, llvm.Metadata{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the initializer, filling the .init.tmp function.
|
||||||
|
if r.debug {
|
||||||
|
fmt.Fprintln(os.Stderr, "interp:", fn.Name())
|
||||||
|
}
|
||||||
|
_, pkgMem, callErr := r.run(r.getFunction(fn), nil, nil, " ")
|
||||||
|
if callErr != nil {
|
||||||
|
if isRecoverableError(callErr.Err) {
|
||||||
|
// Could not finish, but could recover from it.
|
||||||
|
if r.debug {
|
||||||
|
fmt.Fprintln(os.Stderr, "not interpreting", r.pkgName, "because of error:", callErr.Error())
|
||||||
|
}
|
||||||
|
newFn.EraseFromParentAsFunction()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return callErr
|
||||||
|
}
|
||||||
|
for index, obj := range pkgMem.objects {
|
||||||
|
r.objects[index] = obj
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update globals with values determined while running the initializer above.
|
||||||
|
mem := memoryView{r: r}
|
||||||
|
for _, obj := range r.objects {
|
||||||
|
if obj.llvmGlobal.IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if obj.buffer == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if checks && initializer.Type() != obj.llvmGlobal.Type().ElementType() {
|
||||||
|
panic("initializer type mismatch")
|
||||||
|
}
|
||||||
|
obj.llvmGlobal.SetInitializer(initializer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalize: remove the old init function and replace it with the new
|
||||||
|
// (.init.tmp) function.
|
||||||
|
r.builder.CreateRetVoid()
|
||||||
|
fnName := fn.Name()
|
||||||
|
fn.ReplaceAllUsesWith(newFn)
|
||||||
|
fn.EraseFromParentAsFunction()
|
||||||
|
newFn.SetName(fnName)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// getFunction returns the compiled version of the given LLVM function. It
|
// getFunction returns the compiled version of the given LLVM function. It
|
||||||
// compiles the function if necessary and caches the result.
|
// compiles the function if necessary and caches the result.
|
||||||
func (r *runner) getFunction(llvmFn llvm.Value) *function {
|
func (r *runner) getFunction(llvmFn llvm.Value) *function {
|
||||||
|
|||||||
+100
-16
@@ -155,11 +155,8 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
// which case this call won't even get to this point but will
|
// which case this call won't even get to this point but will
|
||||||
// already be emitted in initAll.
|
// already be emitted in initAll.
|
||||||
continue
|
continue
|
||||||
case callFn.name == "(reflect.Type).Elem" || strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet":
|
case strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet":
|
||||||
// These functions should be run at runtime. Specifically:
|
// These functions should be run at runtime. Specifically:
|
||||||
// * (reflect.Type).Elem is a special function. It should
|
|
||||||
// eventually be interpreted, but fall back to a runtime call
|
|
||||||
// for now.
|
|
||||||
// * Print and panic functions are best emitted directly without
|
// * Print and panic functions are best emitted directly without
|
||||||
// interpreting them, otherwise we get a ton of putchar (etc.)
|
// interpreting them, otherwise we get a ton of putchar (etc.)
|
||||||
// calls.
|
// calls.
|
||||||
@@ -280,26 +277,58 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
|
copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
|
||||||
dstObj.buffer = dstBuf
|
dstObj.buffer = dstBuf
|
||||||
mem.put(dst.index(), dstObj)
|
mem.put(dst.index(), dstObj)
|
||||||
|
case callFn.name == "(reflect.rawType).elem":
|
||||||
|
if r.debug {
|
||||||
|
fmt.Fprintln(os.Stderr, indent+"call (reflect.rawType).elem:", operands[1:])
|
||||||
|
}
|
||||||
|
// Extract the type code global from the first parameter.
|
||||||
|
typecodeIDPtrToInt, err := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
|
||||||
|
if err != nil {
|
||||||
|
return nil, mem, r.errorAt(inst, err)
|
||||||
|
}
|
||||||
|
typecodeID := typecodeIDPtrToInt.Operand(0)
|
||||||
|
|
||||||
|
// Get the type class.
|
||||||
|
// See also: getClassAndValueFromTypeCode in transform/reflect.go.
|
||||||
|
typecodeName := typecodeID.Name()
|
||||||
|
const prefix = "reflect/types.type:"
|
||||||
|
if !strings.HasPrefix(typecodeName, prefix) {
|
||||||
|
panic("unexpected typecode name: " + typecodeName)
|
||||||
|
}
|
||||||
|
id := typecodeName[len(prefix):]
|
||||||
|
class := id[:strings.IndexByte(id, ':')]
|
||||||
|
value := id[len(class)+1:]
|
||||||
|
if class == "named" {
|
||||||
|
// Get the underlying type.
|
||||||
|
class = value[:strings.IndexByte(value, ':')]
|
||||||
|
value = value[len(class)+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elem() is only valid for certain type classes.
|
||||||
|
switch class {
|
||||||
|
case "chan", "pointer", "slice", "array":
|
||||||
|
elementType := llvm.ConstExtractValue(typecodeID.Initializer(), []uint32{0})
|
||||||
|
uintptrType := r.mod.Context().IntType(int(mem.r.pointerSize) * 8)
|
||||||
|
locals[inst.localIndex] = r.getValue(llvm.ConstPtrToInt(elementType, uintptrType))
|
||||||
|
default:
|
||||||
|
return nil, mem, r.errorAt(inst, fmt.Errorf("(reflect.Type).Elem() called on %s type", class))
|
||||||
|
}
|
||||||
case callFn.name == "runtime.typeAssert":
|
case callFn.name == "runtime.typeAssert":
|
||||||
// This function must be implemented manually as it is normally
|
// This function must be implemented manually as it is normally
|
||||||
// implemented by the interface lowering pass.
|
// implemented by the interface lowering pass.
|
||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, indent+"typeassert:", operands[1:])
|
fmt.Fprintln(os.Stderr, indent+"typeassert:", operands[1:])
|
||||||
}
|
}
|
||||||
typeInInterfacePtr, err := operands[1].asPointer(r)
|
assertedType, err := operands[2].toLLVMValue(inst.llvmInst.Operand(1).Type(), &mem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, mem, r.errorAt(inst, err)
|
return nil, mem, r.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
actualType, err := mem.load(typeInInterfacePtr, r.pointerSize).asPointer(r)
|
actualTypePtrToInt, err := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, mem, r.errorAt(inst, err)
|
return nil, mem, r.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
assertedType, err := operands[2].asPointer(r)
|
actualType := actualTypePtrToInt.Operand(0)
|
||||||
if err != nil {
|
if actualType.Name()+"$id" == assertedType.Name() {
|
||||||
return nil, mem, r.errorAt(inst, err)
|
|
||||||
}
|
|
||||||
result := assertedType.asRawValue(r).equal(actualType.asRawValue(r))
|
|
||||||
if result {
|
|
||||||
locals[inst.localIndex] = literalValue{uint8(1)}
|
locals[inst.localIndex] = literalValue{uint8(1)}
|
||||||
} else {
|
} else {
|
||||||
locals[inst.localIndex] = literalValue{uint8(0)}
|
locals[inst.localIndex] = literalValue{uint8(0)}
|
||||||
@@ -310,11 +339,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load various values for the interface implements check below.
|
// Load various values for the interface implements check below.
|
||||||
typeInInterfacePtr, err := operands[1].asPointer(r)
|
typecodePtr, err := operands[1].asPointer(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, mem, r.errorAt(inst, err)
|
return nil, mem, r.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
methodSetPtr, err := mem.load(typeInInterfacePtr.addOffset(r.pointerSize), r.pointerSize).asPointer(r)
|
methodSetPtr, err := mem.load(typecodePtr.addOffset(r.pointerSize*2), r.pointerSize).asPointer(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, mem, r.errorAt(inst, err)
|
return nil, mem, r.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
@@ -349,6 +378,43 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
}
|
}
|
||||||
// If assertOk is still 1, the assertion succeeded.
|
// If assertOk is still 1, the assertion succeeded.
|
||||||
locals[inst.localIndex] = literalValue{assertOk}
|
locals[inst.localIndex] = literalValue{assertOk}
|
||||||
|
case callFn.name == "runtime.interfaceMethod":
|
||||||
|
// This builtin returns the function (which may be a thunk) to
|
||||||
|
// invoke a method on an interface. It does not call the method.
|
||||||
|
if r.debug {
|
||||||
|
fmt.Fprintln(os.Stderr, indent+"interface method:", operands[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the first param, which is the type code (ptrtoint of the
|
||||||
|
// type code global).
|
||||||
|
typecodeIDPtrToInt, err := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
|
||||||
|
if err != nil {
|
||||||
|
return nil, mem, r.errorAt(inst, err)
|
||||||
|
}
|
||||||
|
typecodeID := typecodeIDPtrToInt.Operand(0).Initializer()
|
||||||
|
|
||||||
|
// Load the method set, which is part of the typecodeID object.
|
||||||
|
methodSet := llvm.ConstExtractValue(typecodeID, []uint32{2}).Operand(0).Initializer()
|
||||||
|
|
||||||
|
// We don't need to load the interface method set.
|
||||||
|
|
||||||
|
// Load the signature of the to-be-called function.
|
||||||
|
signature := inst.llvmInst.Operand(2)
|
||||||
|
|
||||||
|
// Iterate through all methods, looking for the one method that
|
||||||
|
// should be returned.
|
||||||
|
numMethods := methodSet.Type().ArrayLength()
|
||||||
|
var method llvm.Value
|
||||||
|
for i := 0; i < numMethods; i++ {
|
||||||
|
methodSignature := llvm.ConstExtractValue(methodSet, []uint32{uint32(i), 0})
|
||||||
|
if methodSignature == signature {
|
||||||
|
method = llvm.ConstExtractValue(methodSet, []uint32{uint32(i), 1}).Operand(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if method.IsNil() {
|
||||||
|
return nil, mem, r.errorAt(inst, errors.New("could not find method: "+signature.Name()))
|
||||||
|
}
|
||||||
|
locals[inst.localIndex] = r.getValue(method)
|
||||||
case callFn.name == "runtime.hashmapMake":
|
case callFn.name == "runtime.hashmapMake":
|
||||||
// Create a new map.
|
// Create a new map.
|
||||||
hashmapPointerType := inst.llvmInst.Type()
|
hashmapPointerType := inst.llvmInst.Type()
|
||||||
@@ -482,6 +548,13 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
result := mem.load(ptr, uint32(size))
|
result := mem.load(ptr, uint32(size))
|
||||||
|
if result == nil {
|
||||||
|
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, mem, err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, indent+"load:", ptr, "->", result)
|
fmt.Fprintln(os.Stderr, indent+"load:", ptr, "->", result)
|
||||||
}
|
}
|
||||||
@@ -504,7 +577,14 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, indent+"store:", val, ptr)
|
fmt.Fprintln(os.Stderr, indent+"store:", val, ptr)
|
||||||
}
|
}
|
||||||
mem.store(val, ptr)
|
ok := mem.store(val, ptr)
|
||||||
|
if !ok {
|
||||||
|
// Could not store the value, do it at runtime.
|
||||||
|
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, mem, err
|
||||||
|
}
|
||||||
|
}
|
||||||
case llvm.Alloca:
|
case llvm.Alloca:
|
||||||
// Alloca normally allocates some stack memory. In the interpreter,
|
// Alloca normally allocates some stack memory. In the interpreter,
|
||||||
// it allocates a global instead.
|
// it allocates a global instead.
|
||||||
@@ -836,7 +916,11 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
|
|||||||
for i := 0; i < numOperands; i++ {
|
for i := 0; i < numOperands; i++ {
|
||||||
operand := inst.llvmInst.Operand(i)
|
operand := inst.llvmInst.Operand(i)
|
||||||
if !operand.IsAInstruction().IsNil() || !operand.IsAArgument().IsNil() {
|
if !operand.IsAInstruction().IsNil() || !operand.IsAArgument().IsNil() {
|
||||||
operand = locals[fn.locals[operand]].toLLVMValue(operand.Type(), mem)
|
var err error
|
||||||
|
operand, err = locals[fn.locals[operand]].toLLVMValue(operand.Type(), mem)
|
||||||
|
if err != nil {
|
||||||
|
return r.errorAt(inst, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
operands[i] = operand
|
operands[i] = operand
|
||||||
}
|
}
|
||||||
|
|||||||
+105
-56
@@ -259,12 +259,17 @@ func (mv *memoryView) put(index uint32, obj object) {
|
|||||||
mv.objects[index] = obj
|
mv.objects[index] = obj
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the value behind the given pointer.
|
// Load the value behind the given pointer. Returns nil if the pointer points to
|
||||||
|
// an external global.
|
||||||
func (mv *memoryView) load(p pointerValue, size uint32) value {
|
func (mv *memoryView) load(p pointerValue, size uint32) value {
|
||||||
if checks && mv.hasExternalStore(p) {
|
if checks && mv.hasExternalStore(p) {
|
||||||
panic("interp: load from object with external store")
|
panic("interp: load from object with external store")
|
||||||
}
|
}
|
||||||
obj := mv.get(p.index())
|
obj := mv.get(p.index())
|
||||||
|
if obj.buffer == nil {
|
||||||
|
// External global, return nil.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if p.offset() == 0 && size == obj.size {
|
if p.offset() == 0 && size == obj.size {
|
||||||
return obj.buffer.clone()
|
return obj.buffer.clone()
|
||||||
}
|
}
|
||||||
@@ -280,12 +285,17 @@ func (mv *memoryView) load(p pointerValue, size uint32) value {
|
|||||||
|
|
||||||
// Store to the value behind the given pointer. This overwrites the value in the
|
// Store to the value behind the given pointer. This overwrites the value in the
|
||||||
// memory view, so that the changed value is discarded when the memory view is
|
// memory view, so that the changed value is discarded when the memory view is
|
||||||
// reverted.
|
// reverted. Returns true on success, false if the object to store to is
|
||||||
func (mv *memoryView) store(v value, p pointerValue) {
|
// external.
|
||||||
|
func (mv *memoryView) store(v value, p pointerValue) bool {
|
||||||
if checks && mv.hasExternalLoadOrStore(p) {
|
if checks && mv.hasExternalLoadOrStore(p) {
|
||||||
panic("interp: store to object with external load/store")
|
panic("interp: store to object with external load/store")
|
||||||
}
|
}
|
||||||
obj := mv.get(p.index())
|
obj := mv.get(p.index())
|
||||||
|
if obj.buffer == nil {
|
||||||
|
// External global, return false (for a failure).
|
||||||
|
return false
|
||||||
|
}
|
||||||
if checks && p.offset()+v.len(mv.r) > obj.size {
|
if checks && p.offset()+v.len(mv.r) > obj.size {
|
||||||
panic("interp: store out of bounds")
|
panic("interp: store out of bounds")
|
||||||
}
|
}
|
||||||
@@ -301,6 +311,7 @@ func (mv *memoryView) store(v value, p pointerValue) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mv.put(p.index(), obj)
|
mv.put(p.index(), obj)
|
||||||
|
return true // success
|
||||||
}
|
}
|
||||||
|
|
||||||
// value is some sort of value, comparable to a LLVM constant. It can be
|
// value is some sort of value, comparable to a LLVM constant. It can be
|
||||||
@@ -314,7 +325,7 @@ type value interface {
|
|||||||
asRawValue(*runner) rawValue
|
asRawValue(*runner) rawValue
|
||||||
Uint() uint64
|
Uint() uint64
|
||||||
Int() int64
|
Int() int64
|
||||||
toLLVMValue(llvm.Type, *memoryView) llvm.Value
|
toLLVMValue(llvm.Type, *memoryView) (llvm.Value, error)
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,25 +416,25 @@ func (v literalValue) Int() int64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v literalValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
func (v literalValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
|
||||||
switch llvmType.TypeKind() {
|
switch llvmType.TypeKind() {
|
||||||
case llvm.IntegerTypeKind:
|
case llvm.IntegerTypeKind:
|
||||||
switch value := v.value.(type) {
|
switch value := v.value.(type) {
|
||||||
case uint64:
|
case uint64:
|
||||||
return llvm.ConstInt(llvmType, value, false)
|
return llvm.ConstInt(llvmType, value, false), nil
|
||||||
case uint32:
|
case uint32:
|
||||||
return llvm.ConstInt(llvmType, uint64(value), false)
|
return llvm.ConstInt(llvmType, uint64(value), false), nil
|
||||||
case uint16:
|
case uint16:
|
||||||
return llvm.ConstInt(llvmType, uint64(value), false)
|
return llvm.ConstInt(llvmType, uint64(value), false), nil
|
||||||
case uint8:
|
case uint8:
|
||||||
return llvm.ConstInt(llvmType, uint64(value), false)
|
return llvm.ConstInt(llvmType, uint64(value), false), nil
|
||||||
default:
|
default:
|
||||||
panic("inpterp: unknown literal type")
|
return llvm.Value{}, errors.New("interp: unknown literal type")
|
||||||
}
|
}
|
||||||
case llvm.DoubleTypeKind:
|
case llvm.DoubleTypeKind:
|
||||||
return llvm.ConstFloat(llvmType, math.Float64frombits(v.value.(uint64)))
|
return llvm.ConstFloat(llvmType, math.Float64frombits(v.value.(uint64))), nil
|
||||||
case llvm.FloatTypeKind:
|
case llvm.FloatTypeKind:
|
||||||
return llvm.ConstFloat(llvmType, float64(math.Float32frombits(v.value.(uint32))))
|
return llvm.ConstFloat(llvmType, float64(math.Float32frombits(v.value.(uint32)))), nil
|
||||||
default:
|
default:
|
||||||
return v.asRawValue(mem.r).toLLVMValue(llvmType, mem)
|
return v.asRawValue(mem.r).toLLVMValue(llvmType, mem)
|
||||||
}
|
}
|
||||||
@@ -507,7 +518,7 @@ func (v pointerValue) llvmValue(mem *memoryView) llvm.Value {
|
|||||||
// toLLVMValue returns the LLVM value for this pointer, which may be a GEP or
|
// toLLVMValue returns the LLVM value for this pointer, which may be a GEP or
|
||||||
// bitcast. The llvm.Type parameter is optional, if omitted the pointer type may
|
// bitcast. The llvm.Type parameter is optional, if omitted the pointer type may
|
||||||
// be different than expected.
|
// be different than expected.
|
||||||
func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
|
||||||
// Obtain the llvmValue, creating it if it doesn't exist yet.
|
// Obtain the llvmValue, creating it if it doesn't exist yet.
|
||||||
llvmValue := v.llvmValue(mem)
|
llvmValue := v.llvmValue(mem)
|
||||||
if llvmValue.IsNil() {
|
if llvmValue.IsNil() {
|
||||||
@@ -518,7 +529,10 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
if obj.llvmType.IsNil() {
|
if obj.llvmType.IsNil() {
|
||||||
// Create an initializer without knowing the global type.
|
// Create an initializer without knowing the global type.
|
||||||
// This is probably the result of a runtime.alloc call.
|
// This is probably the result of a runtime.alloc call.
|
||||||
initializer := obj.buffer.asRawValue(mem.r).rawLLVMValue(mem)
|
initializer, err := obj.buffer.asRawValue(mem.r).rawLLVMValue(mem)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
globalType := initializer.Type()
|
globalType := initializer.Type()
|
||||||
llvmValue = llvm.AddGlobal(mem.r.mod, globalType, obj.globalName)
|
llvmValue = llvm.AddGlobal(mem.r.mod, globalType, obj.globalName)
|
||||||
llvmValue.SetInitializer(initializer)
|
llvmValue.SetInitializer(initializer)
|
||||||
@@ -537,9 +551,12 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
// Set the initializer for the global. Do this after creation to avoid
|
// Set the initializer for the global. Do this after creation to avoid
|
||||||
// infinite recursion between creating the global and creating the
|
// infinite recursion between creating the global and creating the
|
||||||
// contents of the global (if the global contains itself).
|
// contents of the global (if the global contains itself).
|
||||||
initializer := obj.buffer.toLLVMValue(globalType, mem)
|
initializer, err := obj.buffer.toLLVMValue(globalType, mem)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
if checks && initializer.Type() != globalType {
|
if checks && initializer.Type() != globalType {
|
||||||
panic("allocated value does not match allocated type")
|
return llvm.Value{}, errors.New("interp: allocated value does not match allocated type")
|
||||||
}
|
}
|
||||||
llvmValue.SetInitializer(initializer)
|
llvmValue.SetInitializer(initializer)
|
||||||
}
|
}
|
||||||
@@ -552,7 +569,7 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
}
|
}
|
||||||
|
|
||||||
if llvmType.IsNil() {
|
if llvmType.IsNil() {
|
||||||
return llvmValue
|
return llvmValue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if llvmType.TypeKind() != llvm.PointerTypeKind {
|
if llvmType.TypeKind() != llvm.PointerTypeKind {
|
||||||
@@ -564,7 +581,7 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
// This can be worked around by simply converting to a raw value,
|
// This can be worked around by simply converting to a raw value,
|
||||||
// rawValue knows how to create such structs.
|
// rawValue knows how to create such structs.
|
||||||
if v.offset() != 0 {
|
if v.offset() != 0 {
|
||||||
panic("offset set without known pointer type")
|
return llvm.Value{}, errors.New("interp: offset set without known pointer type")
|
||||||
}
|
}
|
||||||
return v.asRawValue(mem.r).toLLVMValue(llvmType, mem)
|
return v.asRawValue(mem.r).toLLVMValue(llvmType, mem)
|
||||||
}
|
}
|
||||||
@@ -575,14 +592,14 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
if v.offset() != 0 {
|
if v.offset() != 0 {
|
||||||
// This should never happen, if offset is non-zero, the types
|
// This should never happen, if offset is non-zero, the types
|
||||||
// shouldn't match.
|
// shouldn't match.
|
||||||
panic("offset set while there is no way to convert the type")
|
return llvm.Value{}, errors.New("interp: offset set while there is no way to convert the type")
|
||||||
}
|
}
|
||||||
return llvmValue
|
return llvmValue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.offset() == 0 {
|
if v.offset() == 0 {
|
||||||
// Offset is zero, so we can just bitcast to get a correct pointer.
|
// Offset is zero, so we can just bitcast to get a correct pointer.
|
||||||
return llvm.ConstBitCast(llvmValue, llvmType)
|
return llvm.ConstBitCast(llvmValue, llvmType), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to make a constant GEP for pointer arithmetic.
|
// We need to make a constant GEP for pointer arithmetic.
|
||||||
@@ -606,11 +623,11 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
offset -= int64(mem.r.targetData.ElementOffset(objectElementType, element))
|
offset -= int64(mem.r.targetData.ElementOffset(objectElementType, element))
|
||||||
objectElementType = objectElementType.StructElementTypes()[element]
|
objectElementType = objectElementType.StructElementTypes()[element]
|
||||||
default:
|
default:
|
||||||
panic("pointer index with something other than a struct or array?")
|
return llvm.Value{}, errors.New("interp: pointer index with something other than a struct or array?")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if offset < 0 {
|
if offset < 0 {
|
||||||
panic("offset has somehow gone negative, this should be impossible")
|
return llvm.Value{}, errors.New("interp: offset has somehow gone negative, this should be impossible")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally do the gep, using the above computed indices.
|
// Finally do the gep, using the above computed indices.
|
||||||
@@ -618,9 +635,9 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
// the bits of the pointer are now correct, just not the type).
|
// the bits of the pointer are now correct, just not the type).
|
||||||
gep := llvm.ConstInBoundsGEP(llvmValue, indices)
|
gep := llvm.ConstInBoundsGEP(llvmValue, indices)
|
||||||
if gep.Type() != llvmType {
|
if gep.Type() != llvmType {
|
||||||
return llvm.ConstBitCast(gep, llvmType)
|
return llvm.ConstBitCast(gep, llvmType), nil
|
||||||
}
|
}
|
||||||
return gep
|
return gep, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// mapValue implements a Go map which is created at compile time and stored as a
|
// mapValue implements a Go map which is created at compile time and stored as a
|
||||||
@@ -713,7 +730,11 @@ func (b *mapBucket) create(ctx llvm.Context, nextBucket llvm.Value, mem *memoryV
|
|||||||
// Create data for keys.
|
// Create data for keys.
|
||||||
var keyValues []llvm.Value
|
var keyValues []llvm.Value
|
||||||
for _, key := range b.keys {
|
for _, key := range b.keys {
|
||||||
keyValues = append(keyValues, key.rawLLVMValue(mem))
|
keyValue, err := key.rawLLVMValue(mem)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
keyValues = append(keyValues, keyValue)
|
||||||
}
|
}
|
||||||
if len(b.keys) < 8 {
|
if len(b.keys) < 8 {
|
||||||
keyValues = append(keyValues, llvm.ConstNull(llvm.ArrayType(int8Type, int(b.m.keySize)*(8-len(b.keys)))))
|
keyValues = append(keyValues, llvm.ConstNull(llvm.ArrayType(int8Type, int(b.m.keySize)*(8-len(b.keys)))))
|
||||||
@@ -726,7 +747,11 @@ func (b *mapBucket) create(ctx llvm.Context, nextBucket llvm.Value, mem *memoryV
|
|||||||
// Create data for values.
|
// Create data for values.
|
||||||
var valueValues []llvm.Value
|
var valueValues []llvm.Value
|
||||||
for _, value := range b.values {
|
for _, value := range b.values {
|
||||||
valueValues = append(valueValues, value.rawLLVMValue(mem))
|
v, err := value.rawLLVMValue(mem)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
valueValues = append(valueValues, v)
|
||||||
}
|
}
|
||||||
if len(b.values) < 8 {
|
if len(b.values) < 8 {
|
||||||
valueValues = append(valueValues, llvm.ConstNull(llvm.ArrayType(int8Type, int(b.m.valueSize)*(8-len(b.values)))))
|
valueValues = append(valueValues, llvm.ConstNull(llvm.ArrayType(int8Type, int(b.m.valueSize)*(8-len(b.values)))))
|
||||||
@@ -750,9 +775,9 @@ func (b *mapBucket) create(ctx llvm.Context, nextBucket llvm.Value, mem *memoryV
|
|||||||
return bucket
|
return bucket
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *mapValue) toLLVMValue(hashmapType llvm.Type, mem *memoryView) llvm.Value {
|
func (v *mapValue) toLLVMValue(hashmapType llvm.Type, mem *memoryView) (llvm.Value, error) {
|
||||||
if !v.hashmap.IsNil() {
|
if !v.hashmap.IsNil() {
|
||||||
return v.hashmap
|
return v.hashmap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a slice of buckets with all the keys and values in the hashmap.
|
// Create a slice of buckets with all the keys and values in the hashmap.
|
||||||
@@ -772,12 +797,12 @@ func (v *mapValue) toLLVMValue(hashmapType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
copy(keyValue.buf[v.keySize/2:], literalValue{key.size}.asRawValue(v.r).buf)
|
copy(keyValue.buf[v.keySize/2:], literalValue{key.size}.asRawValue(v.r).buf)
|
||||||
case rawValue:
|
case rawValue:
|
||||||
if key.hasPointer() {
|
if key.hasPointer() {
|
||||||
panic("todo: map key with pointer")
|
return llvm.Value{}, errors.New("interp: todo: map key with pointer")
|
||||||
}
|
}
|
||||||
data = key.buf
|
data = key.buf
|
||||||
keyValue = key
|
keyValue = key
|
||||||
default:
|
default:
|
||||||
panic("unknown map key type")
|
return llvm.Value{}, errors.New("interp: unknown map key type")
|
||||||
}
|
}
|
||||||
buf := make([]byte, len(data))
|
buf := make([]byte, len(data))
|
||||||
for i, p := range data {
|
for i, p := range data {
|
||||||
@@ -821,7 +846,7 @@ func (v *mapValue) toLLVMValue(hashmapType llvm.Type, mem *memoryView) llvm.Valu
|
|||||||
})
|
})
|
||||||
|
|
||||||
v.hashmap = hashmap
|
v.hashmap = hashmap
|
||||||
return v.hashmap
|
return v.hashmap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// putString does a map assign operation, assuming that the map is of type
|
// putString does a map assign operation, assuming that the map is of type
|
||||||
@@ -1018,7 +1043,7 @@ func (v rawValue) equal(rhs rawValue) bool {
|
|||||||
// goes. The resulting value does not have a specified type, but it will be the
|
// goes. The resulting value does not have a specified type, but it will be the
|
||||||
// same size and have the same bytes if it was created with a provided LLVM type
|
// same size and have the same bytes if it was created with a provided LLVM type
|
||||||
// (through toLLVMValue).
|
// (through toLLVMValue).
|
||||||
func (v rawValue) rawLLVMValue(mem *memoryView) llvm.Value {
|
func (v rawValue) rawLLVMValue(mem *memoryView) (llvm.Value, error) {
|
||||||
var structFields []llvm.Value
|
var structFields []llvm.Value
|
||||||
ctx := mem.r.mod.Context()
|
ctx := mem.r.mod.Context()
|
||||||
int8Type := ctx.Int8Type()
|
int8Type := ctx.Int8Type()
|
||||||
@@ -1042,13 +1067,16 @@ func (v rawValue) rawLLVMValue(mem *memoryView) llvm.Value {
|
|||||||
for i := uint32(0); i < uint32(len(v.buf)); {
|
for i := uint32(0); i < uint32(len(v.buf)); {
|
||||||
if v.buf[i] > 255 {
|
if v.buf[i] > 255 {
|
||||||
addBytes()
|
addBytes()
|
||||||
field := pointerValue{v.buf[i]}.toLLVMValue(llvm.Type{}, mem)
|
field, err := pointerValue{v.buf[i]}.toLLVMValue(llvm.Type{}, mem)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
elementType := field.Type().ElementType()
|
elementType := field.Type().ElementType()
|
||||||
if elementType.TypeKind() == llvm.StructTypeKind {
|
if elementType.TypeKind() == llvm.StructTypeKind {
|
||||||
// There are some special pointer types that should be used as a
|
// There are some special pointer types that should be used as a
|
||||||
// ptrtoint, so that they can be used in certain optimizations.
|
// ptrtoint, so that they can be used in certain optimizations.
|
||||||
name := elementType.StructName()
|
name := elementType.StructName()
|
||||||
if name == "runtime.typeInInterface" || name == "runtime.funcValueWithSignature" {
|
if name == "runtime.typecodeID" || name == "runtime.funcValueWithSignature" {
|
||||||
uintptrType := ctx.IntType(int(mem.r.pointerSize) * 8)
|
uintptrType := ctx.IntType(int(mem.r.pointerSize) * 8)
|
||||||
field = llvm.ConstPtrToInt(field, uintptrType)
|
field = llvm.ConstPtrToInt(field, uintptrType)
|
||||||
}
|
}
|
||||||
@@ -1065,12 +1093,12 @@ func (v rawValue) rawLLVMValue(mem *memoryView) llvm.Value {
|
|||||||
|
|
||||||
// Return the created data.
|
// Return the created data.
|
||||||
if len(structFields) == 1 {
|
if len(structFields) == 1 {
|
||||||
return structFields[0]
|
return structFields[0], nil
|
||||||
}
|
}
|
||||||
return ctx.ConstStruct(structFields, false)
|
return ctx.ConstStruct(structFields, false), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
|
||||||
isZero := true
|
isZero := true
|
||||||
for _, p := range v.buf {
|
for _, p := range v.buf {
|
||||||
if p != 0 {
|
if p != 0 {
|
||||||
@@ -1079,7 +1107,7 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if isZero {
|
if isZero {
|
||||||
return llvm.ConstNull(llvmType)
|
return llvm.ConstNull(llvmType), nil
|
||||||
}
|
}
|
||||||
switch llvmType.TypeKind() {
|
switch llvmType.TypeKind() {
|
||||||
case llvm.IntegerTypeKind:
|
case llvm.IntegerTypeKind:
|
||||||
@@ -1088,7 +1116,17 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return llvm.ConstPtrToInt(ptr.toLLVMValue(llvm.Type{}, mem), llvmType)
|
if checks && mem.r.targetData.TypeAllocSize(llvmType) != mem.r.targetData.TypeAllocSize(mem.r.i8ptrType) {
|
||||||
|
// Probably trying to serialize a pointer to a byte array,
|
||||||
|
// perhaps as a result of rawLLVMValue() in a previous interp
|
||||||
|
// run.
|
||||||
|
return llvm.Value{}, errInvalidPtrToIntSize
|
||||||
|
}
|
||||||
|
v, err := ptr.toLLVMValue(llvm.Type{}, mem)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
|
return llvm.ConstPtrToInt(v, llvmType), nil
|
||||||
}
|
}
|
||||||
var n uint64
|
var n uint64
|
||||||
switch llvmType.IntTypeWidth() {
|
switch llvmType.IntTypeWidth() {
|
||||||
@@ -1108,7 +1146,7 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
|||||||
default:
|
default:
|
||||||
panic("unknown integer size")
|
panic("unknown integer size")
|
||||||
}
|
}
|
||||||
return llvm.ConstInt(llvmType, n, false)
|
return llvm.ConstInt(llvmType, n, false), nil
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fieldTypes := llvmType.StructElementTypes()
|
fieldTypes := llvmType.StructElementTypes()
|
||||||
fields := make([]llvm.Value, len(fieldTypes))
|
fields := make([]llvm.Value, len(fieldTypes))
|
||||||
@@ -1117,12 +1155,16 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
|||||||
field := rawValue{
|
field := rawValue{
|
||||||
buf: v.buf[offset:],
|
buf: v.buf[offset:],
|
||||||
}
|
}
|
||||||
fields[i] = field.toLLVMValue(fieldType, mem)
|
var err error
|
||||||
|
fields[i], err = field.toLLVMValue(fieldType, mem)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if llvmType.StructName() != "" {
|
if llvmType.StructName() != "" {
|
||||||
return llvm.ConstNamedStruct(llvmType, fields)
|
return llvm.ConstNamedStruct(llvmType, fields), nil
|
||||||
}
|
}
|
||||||
return llvmType.Context().ConstStruct(fields, false)
|
return llvmType.Context().ConstStruct(fields, false), nil
|
||||||
case llvm.ArrayTypeKind:
|
case llvm.ArrayTypeKind:
|
||||||
numElements := llvmType.ArrayLength()
|
numElements := llvmType.ArrayLength()
|
||||||
childType := llvmType.ElementType()
|
childType := llvmType.ElementType()
|
||||||
@@ -1133,27 +1175,34 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
|||||||
field := rawValue{
|
field := rawValue{
|
||||||
buf: v.buf[offset:],
|
buf: v.buf[offset:],
|
||||||
}
|
}
|
||||||
fields[i] = field.toLLVMValue(childType, mem)
|
var err error
|
||||||
|
fields[i], err = field.toLLVMValue(childType, mem)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
if checks && fields[i].Type() != childType {
|
if checks && fields[i].Type() != childType {
|
||||||
panic("child type doesn't match")
|
panic("child type doesn't match")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return llvm.ConstArray(childType, fields)
|
return llvm.ConstArray(childType, fields), nil
|
||||||
case llvm.PointerTypeKind:
|
case llvm.PointerTypeKind:
|
||||||
if v.buf[0] > 255 {
|
if v.buf[0] > 255 {
|
||||||
// This is a regular pointer.
|
// This is a regular pointer.
|
||||||
llvmValue := pointerValue{v.buf[0]}.toLLVMValue(llvm.Type{}, mem)
|
llvmValue, err := pointerValue{v.buf[0]}.toLLVMValue(llvm.Type{}, mem)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
if llvmValue.Type() != llvmType {
|
if llvmValue.Type() != llvmType {
|
||||||
llvmValue = llvm.ConstBitCast(llvmValue, llvmType)
|
llvmValue = llvm.ConstBitCast(llvmValue, llvmType)
|
||||||
}
|
}
|
||||||
return llvmValue
|
return llvmValue, nil
|
||||||
}
|
}
|
||||||
// This is either a null pointer or a raw pointer for memory-mapped I/O
|
// This is either a null pointer or a raw pointer for memory-mapped I/O
|
||||||
// (such as 0xe000ed00).
|
// (such as 0xe000ed00).
|
||||||
ptr := rawValue{v.buf[:mem.r.pointerSize]}.Uint()
|
ptr := rawValue{v.buf[:mem.r.pointerSize]}.Uint()
|
||||||
if ptr == 0 {
|
if ptr == 0 {
|
||||||
// Null pointer.
|
// Null pointer.
|
||||||
return llvm.ConstNull(llvmType)
|
return llvm.ConstNull(llvmType), nil
|
||||||
}
|
}
|
||||||
var ptrValue llvm.Value // the underlying int
|
var ptrValue llvm.Value // the underlying int
|
||||||
switch mem.r.pointerSize {
|
switch mem.r.pointerSize {
|
||||||
@@ -1164,19 +1213,19 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
|||||||
case 2:
|
case 2:
|
||||||
ptrValue = llvm.ConstInt(llvmType.Context().Int16Type(), ptr, false)
|
ptrValue = llvm.ConstInt(llvmType.Context().Int16Type(), ptr, false)
|
||||||
default:
|
default:
|
||||||
panic("unknown pointer size")
|
return llvm.Value{}, errors.New("interp: unknown pointer size")
|
||||||
}
|
}
|
||||||
return llvm.ConstIntToPtr(ptrValue, llvmType)
|
return llvm.ConstIntToPtr(ptrValue, llvmType), nil
|
||||||
case llvm.DoubleTypeKind:
|
case llvm.DoubleTypeKind:
|
||||||
b := rawValue{v.buf[:8]}.Uint()
|
b := rawValue{v.buf[:8]}.Uint()
|
||||||
f := math.Float64frombits(b)
|
f := math.Float64frombits(b)
|
||||||
return llvm.ConstFloat(llvmType, f)
|
return llvm.ConstFloat(llvmType, f), nil
|
||||||
case llvm.FloatTypeKind:
|
case llvm.FloatTypeKind:
|
||||||
b := uint32(rawValue{v.buf[:4]}.Uint())
|
b := uint32(rawValue{v.buf[:4]}.Uint())
|
||||||
f := math.Float32frombits(b)
|
f := math.Float32frombits(b)
|
||||||
return llvm.ConstFloat(llvmType, float64(f))
|
return llvm.ConstFloat(llvmType, float64(f)), nil
|
||||||
default:
|
default:
|
||||||
panic("todo: raw value to LLVM value: " + llvmType.String())
|
return llvm.Value{}, errors.New("interp: todo: raw value to LLVM value: " + llvmType.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1365,8 +1414,8 @@ func (v localValue) Int() int64 {
|
|||||||
panic("interp: localValue.Int")
|
panic("interp: localValue.Int")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v localValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
|
func (v localValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
|
||||||
return v.value
|
return v.value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *runner) getValue(llvmValue llvm.Value) value {
|
func (r *runner) getValue(llvmValue llvm.Value) value {
|
||||||
|
|||||||
Vendored
+5
-6
@@ -1,17 +1,16 @@
|
|||||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
target triple = "x86_64--linux"
|
target triple = "x86_64--linux"
|
||||||
|
|
||||||
%runtime.typecodeID = type { %runtime.typecodeID*, i64 }
|
%runtime.typecodeID = type { %runtime.typecodeID*, i64, %runtime.interfaceMethodInfo* }
|
||||||
%runtime.interfaceMethodInfo = type { i8*, i64 }
|
%runtime.interfaceMethodInfo = type { i8*, i64 }
|
||||||
%runtime.typeInInterface = type { %runtime.typecodeID*, %runtime.interfaceMethodInfo* }
|
|
||||||
|
|
||||||
@main.v1 = global i1 0
|
@main.v1 = global i1 0
|
||||||
@"reflect/types.type:named:main.foo" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i64 0 }
|
@"reflect/types.type:named:main.foo" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i64 0, %runtime.interfaceMethodInfo* null }
|
||||||
|
@"reflect/types.type:named:main.foo$id" = external constant i8
|
||||||
@"reflect/types.type:basic:int" = external constant %runtime.typecodeID
|
@"reflect/types.type:basic:int" = external constant %runtime.typecodeID
|
||||||
@"typeInInterface:reflect/types.type:named:main.foo" = private constant %runtime.typeInInterface { %runtime.typecodeID* @"reflect/types.type:named:main.foo", %runtime.interfaceMethodInfo* null }
|
|
||||||
|
|
||||||
|
|
||||||
declare i1 @runtime.typeAssert(i64, %runtime.typecodeID*, i8*, i8*)
|
declare i1 @runtime.typeAssert(i64, i8*, i8*, i8*)
|
||||||
|
|
||||||
define void @runtime.initAll() unnamed_addr {
|
define void @runtime.initAll() unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
@@ -22,7 +21,7 @@ entry:
|
|||||||
define internal void @main.init() unnamed_addr {
|
define internal void @main.init() unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
; Test type asserts.
|
; Test type asserts.
|
||||||
%typecode = call i1 @runtime.typeAssert(i64 ptrtoint (%runtime.typeInInterface* @"typeInInterface:reflect/types.type:named:main.foo" to i64), %runtime.typecodeID* @"reflect/types.type:named:main.foo", i8* undef, i8* null)
|
%typecode = call i1 @runtime.typeAssert(i64 ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:main.foo" to i64), i8* @"reflect/types.type:named:main.foo$id", i8* undef, i8* null)
|
||||||
store i1 %typecode, i1* @main.v1
|
store i1 %typecode, i1* @main.v1
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-19
@@ -2,6 +2,7 @@ package loader
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/sha512"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -11,6 +12,7 @@ import (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -66,10 +68,12 @@ type PackageJSON struct {
|
|||||||
type Package struct {
|
type Package struct {
|
||||||
PackageJSON
|
PackageJSON
|
||||||
|
|
||||||
program *Program
|
program *Program
|
||||||
Files []*ast.File
|
Files []*ast.File
|
||||||
Pkg *types.Package
|
FileHashes map[string][]byte
|
||||||
info types.Info
|
CFlags []string // CFlags used during CGo preprocessing (only set if CGo is used)
|
||||||
|
Pkg *types.Package
|
||||||
|
info types.Info
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load loads the given package with all dependencies (including the runtime
|
// Load loads the given package with all dependencies (including the runtime
|
||||||
@@ -118,7 +122,8 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
|||||||
decoder := json.NewDecoder(buf)
|
decoder := json.NewDecoder(buf)
|
||||||
for {
|
for {
|
||||||
pkg := &Package{
|
pkg := &Package{
|
||||||
program: p,
|
program: p,
|
||||||
|
FileHashes: make(map[string][]byte),
|
||||||
info: types.Info{
|
info: types.Info{
|
||||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||||
Defs: make(map[*ast.Ident]types.Object),
|
Defs: make(map[*ast.Ident]types.Object),
|
||||||
@@ -277,17 +282,15 @@ func (p *Program) Parse() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseFile is a wrapper around parser.ParseFile.
|
// parseFile is a wrapper around parser.ParseFile.
|
||||||
func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
func (p *Package) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
||||||
if p.fset == nil {
|
originalPath := p.program.getOriginalPath(path)
|
||||||
p.fset = token.NewFileSet()
|
data, err := ioutil.ReadFile(path)
|
||||||
}
|
|
||||||
|
|
||||||
rd, err := os.Open(path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rd.Close()
|
sum := sha512.Sum512_224(data)
|
||||||
return parser.ParseFile(p.fset, p.getOriginalPath(path), rd, mode)
|
p.FileHashes[originalPath] = sum[:]
|
||||||
|
return parser.ParseFile(p.program.fset, originalPath, data, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse parses and typechecks this package.
|
// Parse parses and typechecks this package.
|
||||||
@@ -363,7 +366,7 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
if !filepath.IsAbs(file) {
|
if !filepath.IsAbs(file) {
|
||||||
file = filepath.Join(p.Dir, file)
|
file = filepath.Join(p.Dir, file)
|
||||||
}
|
}
|
||||||
f, err := p.program.parseFile(file, parser.ParseComments)
|
f, err := p.parseFile(file, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fileErrs = append(fileErrs, err)
|
fileErrs = append(fileErrs, err)
|
||||||
return
|
return
|
||||||
@@ -379,13 +382,17 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
|
|
||||||
// Do CGo processing.
|
// Do CGo processing.
|
||||||
if len(p.CgoFiles) != 0 {
|
if len(p.CgoFiles) != 0 {
|
||||||
var cflags []string
|
var initialCFlags []string
|
||||||
cflags = append(cflags, p.program.config.CFlags()...)
|
initialCFlags = append(initialCFlags, p.program.config.CFlags()...)
|
||||||
cflags = append(cflags, "-I"+p.Dir)
|
initialCFlags = append(initialCFlags, "-I"+p.Dir)
|
||||||
if p.program.clangHeaders != "" {
|
if p.program.clangHeaders != "" {
|
||||||
cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.program.clangHeaders)
|
initialCFlags = append(initialCFlags, "-Xclang", "-internal-isystem", "-Xclang", p.program.clangHeaders)
|
||||||
|
}
|
||||||
|
generated, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.program.fset, initialCFlags)
|
||||||
|
p.CFlags = append(initialCFlags, cflags...)
|
||||||
|
for path, hash := range accessedFiles {
|
||||||
|
p.FileHashes[path] = hash
|
||||||
}
|
}
|
||||||
generated, ldflags, errs := cgo.Process(files, p.program.workingDir, p.program.fset, cflags)
|
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
fileErrs = append(fileErrs, errs...)
|
fileErrs = append(fileErrs, errs...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ func copyFile(src, dst string) error {
|
|||||||
// executeCommand is a simple wrapper to exec.Cmd
|
// executeCommand is a simple wrapper to exec.Cmd
|
||||||
func executeCommand(options *compileopts.Options, name string, arg ...string) *exec.Cmd {
|
func executeCommand(options *compileopts.Options, name string, arg ...string) *exec.Cmd {
|
||||||
if options.PrintCommands {
|
if options.PrintCommands {
|
||||||
fmt.Printf("%s %s\n ", name, strings.Join(arg, " "))
|
fmt.Printf("%s %s\n", name, strings.Join(arg, " "))
|
||||||
}
|
}
|
||||||
return exec.Command(name, arg...)
|
return exec.Command(name, arg...)
|
||||||
}
|
}
|
||||||
@@ -105,7 +105,7 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.Build(pkgName, outpath, config, nil, func(result builder.BuildResult) error {
|
return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
|
||||||
if err := os.Rename(result.Binary, outpath); err != nil {
|
if err := os.Rename(result.Binary, outpath); err != nil {
|
||||||
// Moving failed. Do a file copy.
|
// Moving failed. Do a file copy.
|
||||||
inf, err := os.Open(result.Binary)
|
inf, err := os.Open(result.Binary)
|
||||||
@@ -141,7 +141,7 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.Build(pkgName, outpath, config, nil, func(result builder.BuildResult) error {
|
return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
|
||||||
if testCompileOnly || outpath != "" {
|
if testCompileOnly || outpath != "" {
|
||||||
// Write test binary to the specified file name.
|
// Write test binary to the specified file name.
|
||||||
if outpath == "" {
|
if outpath == "" {
|
||||||
@@ -237,7 +237,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
|||||||
return errors.New("unknown flash method: " + flashMethod)
|
return errors.New("unknown flash method: " + flashMethod)
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.Build(pkgName, fileExt, config, func() error {
|
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
|
||||||
// do we need port reset to put MCU into bootloader mode?
|
// do we need port reset to put MCU into bootloader mode?
|
||||||
if config.Target.PortReset == "true" && flashMethod != "openocd" {
|
if config.Target.PortReset == "true" && flashMethod != "openocd" {
|
||||||
if port == "" {
|
if port == "" {
|
||||||
@@ -250,14 +250,12 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
|||||||
|
|
||||||
err := touchSerialPortAt1200bps(port)
|
err := touchSerialPortAt1200bps(port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to reset port", "", err}
|
return &commandError{"failed to reset port", result.Binary, err}
|
||||||
}
|
}
|
||||||
// give the target MCU a chance to restart into bootloader
|
// give the target MCU a chance to restart into bootloader
|
||||||
time.Sleep(3 * time.Second)
|
time.Sleep(3 * time.Second)
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
|
||||||
}, func(result builder.BuildResult) error {
|
|
||||||
// this flashing method copies the binary data to a Mass Storage Device (msd)
|
// this flashing method copies the binary data to a Mass Storage Device (msd)
|
||||||
switch flashMethod {
|
switch flashMethod {
|
||||||
case "", "command":
|
case "", "command":
|
||||||
@@ -346,11 +344,12 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if config.Target.GDB == "" {
|
gdb, err := config.Target.LookupGDB()
|
||||||
return errors.New("gdb not configured in the target specification")
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.Build(pkgName, "", config, nil, func(result builder.BuildResult) error {
|
return builder.Build(pkgName, "", config, func(result builder.BuildResult) error {
|
||||||
// Find a good way to run GDB.
|
// Find a good way to run GDB.
|
||||||
gdbInterface, openocdInterface := config.Programmer()
|
gdbInterface, openocdInterface := config.Programmer()
|
||||||
switch gdbInterface {
|
switch gdbInterface {
|
||||||
@@ -492,7 +491,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
|||||||
for _, cmd := range gdbCommands {
|
for _, cmd := range gdbCommands {
|
||||||
params = append(params, "-ex", cmd)
|
params = append(params, "-ex", cmd)
|
||||||
}
|
}
|
||||||
cmd := executeCommand(config.Options, config.Target.GDB, params...)
|
cmd := executeCommand(config.Options, gdb, params...)
|
||||||
cmd.Stdin = os.Stdin
|
cmd.Stdin = os.Stdin
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
@@ -514,7 +513,7 @@ func Run(pkgName string, options *compileopts.Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.Build(pkgName, ".elf", config, nil, func(result builder.BuildResult) error {
|
return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error {
|
||||||
if len(config.Target.Emulator) == 0 {
|
if len(config.Target.Emulator) == 0 {
|
||||||
// Run directly.
|
// Run directly.
|
||||||
cmd := executeCommand(config.Options, result.Binary)
|
cmd := executeCommand(config.Options, result.Binary)
|
||||||
|
|||||||
+10
-5
@@ -59,7 +59,10 @@ func TestCompiler(t *testing.T) {
|
|||||||
t.Run("Host", func(t *testing.T) {
|
t.Run("Host", func(t *testing.T) {
|
||||||
runPlatTests("", matches, t)
|
runPlatTests("", matches, t)
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
runTest("testdata/libc/env.go", "", t, []string{"ENV1=VALUE1", "ENV2=VALUE2"}...)
|
runTest("testdata/libc/filesystem.go", "", t,
|
||||||
|
nil, nil)
|
||||||
|
runTest("testdata/libc/env.go", "", t,
|
||||||
|
[]string{"ENV1=VALUE1", "ENV2=VALUE2"}, nil)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -107,7 +110,9 @@ func TestCompiler(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("WASI", func(t *testing.T) {
|
t.Run("WASI", func(t *testing.T) {
|
||||||
runPlatTests("wasi", matches, t)
|
runPlatTests("wasi", matches, t)
|
||||||
runTest("testdata/libc/env.go", "wasi", t, []string{"ENV1=VALUE1", "ENV2=VALUE2"}...)
|
runTest("testdata/libc/env.go", "wasi", t,
|
||||||
|
[]string{"--env", "ENV1=VALUE1", "--env", "ENV2=VALUE2"}, nil)
|
||||||
|
runTest("testdata/libc/filesystem.go", "wasi", t, nil, []string{"--dir=."})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,7 +124,7 @@ func runPlatTests(target string, matches []string, t *testing.T) {
|
|||||||
path := path // redefine to avoid race condition
|
path := path // redefine to avoid race condition
|
||||||
t.Run(filepath.Base(path), func(t *testing.T) {
|
t.Run(filepath.Base(path), func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runTest(path, target, t)
|
runTest(path, target, t, nil, nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,7 +141,7 @@ func runBuild(src, out string, opts *compileopts.Options) error {
|
|||||||
return Build(src, out, opts)
|
return Build(src, out, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTest(path, target string, t *testing.T, environmentVars ...string) {
|
func runTest(path, target string, t *testing.T, environmentVars []string, additionalArgs []string) {
|
||||||
// Get the expected output for this test.
|
// Get the expected output for this test.
|
||||||
txtpath := path[:len(path)-3] + ".txt"
|
txtpath := path[:len(path)-3] + ".txt"
|
||||||
if path[len(path)-1] == os.PathSeparator {
|
if path[len(path)-1] == os.PathSeparator {
|
||||||
@@ -195,7 +200,7 @@ func runTest(path, target string, t *testing.T, environmentVars ...string) {
|
|||||||
cmd = exec.Command(binary)
|
cmd = exec.Command(binary)
|
||||||
} else {
|
} else {
|
||||||
args := append(spec.Emulator[1:], binary)
|
args := append(spec.Emulator[1:], binary)
|
||||||
cmd = exec.Command(spec.Emulator[0], args...)
|
cmd = exec.Command(spec.Emulator[0], append(args, additionalArgs...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
|
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// +build arduino
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "machine"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Configuration on an Arduino Uno.
|
||||||
|
pwm = machine.Timer2
|
||||||
|
pinA = machine.PB3 // pin 11 on the Uno
|
||||||
|
pinB = machine.PD3 // pin 3 on the Uno
|
||||||
|
)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// +build feather_m4
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "machine"
|
||||||
|
|
||||||
|
var (
|
||||||
|
pwm = machine.TCC0
|
||||||
|
pinA = machine.D12
|
||||||
|
pinB = machine.D13
|
||||||
|
)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// +build itsybitsy_m0
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "machine"
|
||||||
|
|
||||||
|
var (
|
||||||
|
pwm = machine.TCC0
|
||||||
|
pinA = machine.D3
|
||||||
|
pinB = machine.D4
|
||||||
|
)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// +build itsybitsy_m4
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "machine"
|
||||||
|
|
||||||
|
var (
|
||||||
|
pwm = machine.TCC0
|
||||||
|
pinA = machine.D12
|
||||||
|
pinB = machine.D13
|
||||||
|
)
|
||||||
+57
-47
@@ -1,64 +1,74 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
// This example demonstrates some features of the PWM support.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"machine"
|
"machine"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This example assumes that an RGB LED is connected to pins 3, 5 and 6 on an Arduino.
|
const delayBetweenPeriods = time.Second * 5
|
||||||
// Change the values below to use different pins.
|
|
||||||
const (
|
|
||||||
redPin = machine.D4
|
|
||||||
greenPin = machine.D5
|
|
||||||
bluePin = machine.D6
|
|
||||||
)
|
|
||||||
|
|
||||||
// cycleColor is just a placeholder until math/rand or some equivalent is working.
|
|
||||||
func cycleColor(color uint8) uint8 {
|
|
||||||
if color < 10 {
|
|
||||||
return color + 1
|
|
||||||
} else if color < 200 {
|
|
||||||
return color + 10
|
|
||||||
} else {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
machine.InitPWM()
|
// Delay a bit on startup to easily catch the first messages.
|
||||||
|
time.Sleep(time.Second * 2)
|
||||||
|
|
||||||
red := machine.PWM{redPin}
|
// Configure the PWM with the given period.
|
||||||
err := red.Configure()
|
err := pwm.Configure(machine.PWMConfig{
|
||||||
checkError(err, "failed to configure red pin")
|
Period: 16384e3, // 16.384ms
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
println("failed to configure PWM")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
green := machine.PWM{greenPin}
|
// The top value is the highest value that can be passed to PWMChannel.Set.
|
||||||
err = green.Configure()
|
// It is usually an even number.
|
||||||
checkError(err, "failed to configure green pin")
|
println("top:", pwm.Top())
|
||||||
|
|
||||||
blue := machine.PWM{bluePin}
|
// Configure the two channels we'll use as outputs.
|
||||||
err = blue.Configure()
|
channelA, err := pwm.Channel(pinA)
|
||||||
checkError(err, "failed to configure blue pin")
|
if err != nil {
|
||||||
|
println("failed to configure channel A")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
channelB, err := pwm.Channel(pinB)
|
||||||
|
if err != nil {
|
||||||
|
println("failed to configure channel B")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var rc uint8
|
// Invert one of the channels to demonstrate output polarity.
|
||||||
var gc uint8 = 20
|
pwm.SetInverting(channelB, true)
|
||||||
var bc uint8 = 30
|
|
||||||
|
// Test out various frequencies below, including some edge cases.
|
||||||
|
|
||||||
|
println("running at 0% duty cycle")
|
||||||
|
pwm.Set(channelA, 0)
|
||||||
|
pwm.Set(channelB, 0)
|
||||||
|
time.Sleep(delayBetweenPeriods)
|
||||||
|
|
||||||
|
println("running at 1")
|
||||||
|
pwm.Set(channelA, 1)
|
||||||
|
pwm.Set(channelB, 1)
|
||||||
|
time.Sleep(delayBetweenPeriods)
|
||||||
|
|
||||||
|
println("running at 25% duty cycle")
|
||||||
|
pwm.Set(channelA, pwm.Top()/4)
|
||||||
|
pwm.Set(channelB, pwm.Top()/4)
|
||||||
|
time.Sleep(delayBetweenPeriods)
|
||||||
|
|
||||||
|
println("running at top-1")
|
||||||
|
pwm.Set(channelA, pwm.Top()-1)
|
||||||
|
pwm.Set(channelB, pwm.Top()-1)
|
||||||
|
time.Sleep(delayBetweenPeriods)
|
||||||
|
|
||||||
|
println("running at 100% duty cycle")
|
||||||
|
pwm.Set(channelA, pwm.Top())
|
||||||
|
pwm.Set(channelB, pwm.Top())
|
||||||
|
time.Sleep(delayBetweenPeriods)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
rc = cycleColor(rc)
|
time.Sleep(time.Second)
|
||||||
gc = cycleColor(gc)
|
|
||||||
bc = cycleColor(bc)
|
|
||||||
|
|
||||||
red.Set(uint16(rc) << 8)
|
|
||||||
green.Set(uint16(gc) << 8)
|
|
||||||
blue.Set(uint16(bc) << 8)
|
|
||||||
|
|
||||||
time.Sleep(time.Millisecond * 500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkError(err error, msg string) {
|
|
||||||
if err != nil {
|
|
||||||
print(msg, ": ", err.Error())
|
|
||||||
println()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
type rawState uint8
|
type rawState uint8
|
||||||
|
|
||||||
//export llvm.coro.resume
|
//export llvm.coro.resume
|
||||||
func (s *rawState) resume()
|
func coroResume(*rawState)
|
||||||
|
|
||||||
type state struct{ *rawState }
|
type state struct{ *rawState }
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ func noopState() *rawState
|
|||||||
|
|
||||||
// Resume the task until it pauses or completes.
|
// Resume the task until it pauses or completes.
|
||||||
func (t *Task) Resume() {
|
func (t *Task) Resume() {
|
||||||
t.state.resume()
|
coroResume(t.state.rawState)
|
||||||
}
|
}
|
||||||
|
|
||||||
// setState is used by the compiler to set the state of the function at the beginning of a function call.
|
// setState is used by the compiler to set the state of the function at the beginning of a function call.
|
||||||
@@ -77,22 +77,9 @@ func Current() *Task
|
|||||||
// This is implemented inside the compiler.
|
// This is implemented inside the compiler.
|
||||||
func Pause()
|
func Pause()
|
||||||
|
|
||||||
type taskHolder interface {
|
|
||||||
setState(*rawState) *rawState
|
|
||||||
returnTo(*rawState)
|
|
||||||
returnCurrent()
|
|
||||||
setReturnPtr(unsafe.Pointer)
|
|
||||||
getReturnPtr() unsafe.Pointer
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are no direct references to the task methods, they will not be discovered by the compiler, and this will trigger a compiler error.
|
|
||||||
// Instantiating this interface forces discovery of these methods.
|
|
||||||
var _ = taskHolder((*Task)(nil))
|
|
||||||
|
|
||||||
func fake() {
|
func fake() {
|
||||||
// Hack to ensure intrinsics are discovered.
|
// Hack to ensure intrinsics are discovered.
|
||||||
Current()
|
Current()
|
||||||
go func() {}()
|
|
||||||
Pause()
|
Pause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func init() {
|
|||||||
|
|
||||||
// I2C on the Arduino Nano 33.
|
// I2C on the Arduino Nano 33.
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM4_I2CM,
|
Bus: sam.SERCOM4_I2CM,
|
||||||
SERCOM: 4,
|
SERCOM: 4,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,6 @@ const (
|
|||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SDA_PIN = PB7
|
I2C0_SDA_PIN = PB7
|
||||||
SCL_PIN = PB6
|
I2C0_SCL_PIN = PB6
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ const (
|
|||||||
|
|
||||||
// Analog Pins
|
// Analog Pins
|
||||||
const (
|
const (
|
||||||
A0 = PA02 // PWM available, also ADC/AIN[0]
|
A0 = PA02 // ADC/AIN[0]
|
||||||
A1 = PA05 // ADC/AIN[5]
|
A1 = PA05 // PWM available, also ADC/AIN[5]
|
||||||
A2 = PA06 // PWM available, also ADC/AIN[6]
|
A2 = PA06 // PWM available, also ADC/AIN[6]
|
||||||
A3 = PA07 // PWM available, also ADC/AIN[7]
|
A3 = PA07 // PWM available, also ADC/AIN[7]
|
||||||
A4 = PB03 // PORTB
|
A4 = PB03 // PORTB
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ func init() {
|
|||||||
// I2C on the Circuit Playground Express.
|
// I2C on the Circuit Playground Express.
|
||||||
var (
|
var (
|
||||||
// external device
|
// external device
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM5_I2CM,
|
Bus: sam.SERCOM5_I2CM,
|
||||||
SERCOM: 5,
|
SERCOM: 5,
|
||||||
}
|
}
|
||||||
// internal device
|
// internal device
|
||||||
I2C1 = I2C{
|
I2C1 = &I2C{
|
||||||
Bus: sam.SERCOM1_I2CM,
|
Bus: sam.SERCOM1_I2CM,
|
||||||
SERCOM: 1,
|
SERCOM: 1,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ const (
|
|||||||
|
|
||||||
// I2C on the Feather M0.
|
// I2C on the Feather M0.
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM3_I2CM,
|
Bus: sam.SERCOM3_I2CM,
|
||||||
SERCOM: 3,
|
SERCOM: 3,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func init() {
|
|||||||
|
|
||||||
// I2C on the Feather M4.
|
// I2C on the Feather M4.
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM2_I2CM,
|
Bus: sam.SERCOM2_I2CM,
|
||||||
SERCOM: 2,
|
SERCOM: 2,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,19 +120,22 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
UART1 = UART{
|
UART1 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.USART3,
|
Bus: stm32.USART3,
|
||||||
AltFuncSelector: AF7_USART1_2_3,
|
TxAltFuncSelector: AF7_USART1_2_3,
|
||||||
|
RxAltFuncSelector: AF7_USART1_2_3,
|
||||||
}
|
}
|
||||||
UART2 = UART{
|
UART2 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.USART6,
|
Bus: stm32.USART6,
|
||||||
AltFuncSelector: AF8_USART4_5_6,
|
TxAltFuncSelector: AF8_USART4_5_6,
|
||||||
|
RxAltFuncSelector: AF8_USART4_5_6,
|
||||||
}
|
}
|
||||||
UART3 = UART{
|
UART3 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.USART1,
|
Bus: stm32.USART1,
|
||||||
AltFuncSelector: AF7_USART1_2_3,
|
TxAltFuncSelector: AF7_USART1_2_3,
|
||||||
|
RxAltFuncSelector: AF7_USART1_2_3,
|
||||||
}
|
}
|
||||||
UART0 = UART1
|
UART0 = UART1
|
||||||
)
|
)
|
||||||
@@ -227,15 +230,15 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
I2C1 = I2C{
|
I2C1 = &I2C{
|
||||||
Bus: stm32.I2C1,
|
Bus: stm32.I2C1,
|
||||||
AltFuncSelector: AF4_I2C1_2_3,
|
AltFuncSelector: AF4_I2C1_2_3,
|
||||||
}
|
}
|
||||||
I2C2 = I2C{
|
I2C2 = &I2C{
|
||||||
Bus: stm32.I2C2,
|
Bus: stm32.I2C2,
|
||||||
AltFuncSelector: AF4_I2C1_2_3,
|
AltFuncSelector: AF4_I2C1_2_3,
|
||||||
}
|
}
|
||||||
I2C3 = I2C{
|
I2C3 = &I2C{
|
||||||
Bus: stm32.I2C1,
|
Bus: stm32.I2C1,
|
||||||
AltFuncSelector: AF4_I2C1_2_3,
|
AltFuncSelector: AF4_I2C1_2_3,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,11 +40,11 @@ var (
|
|||||||
|
|
||||||
// I2C on the Grand Central M4
|
// I2C on the Grand Central M4
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM3_I2CM,
|
Bus: sam.SERCOM3_I2CM,
|
||||||
SERCOM: 3,
|
SERCOM: 3,
|
||||||
}
|
}
|
||||||
I2C1 = I2C{
|
I2C1 = &I2C{
|
||||||
Bus: sam.SERCOM6_I2CM,
|
Bus: sam.SERCOM6_I2CM,
|
||||||
SERCOM: 6,
|
SERCOM: 6,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,10 +10,3 @@ var (
|
|||||||
Bus: sifive.QSPI1,
|
Bus: sifive.QSPI1,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2C on the HiFive1 rev B.
|
|
||||||
var (
|
|
||||||
I2C0 = I2C{
|
|
||||||
Bus: sifive.I2C0,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ const (
|
|||||||
|
|
||||||
// I2C on the ItsyBitsy M0.
|
// I2C on the ItsyBitsy M0.
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM3_I2CM,
|
Bus: sam.SERCOM3_I2CM,
|
||||||
SERCOM: 3,
|
SERCOM: 3,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func init() {
|
|||||||
|
|
||||||
// I2C on the ItsyBitsy M4.
|
// I2C on the ItsyBitsy M4.
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM2_I2CM,
|
Bus: sam.SERCOM2_I2CM,
|
||||||
SERCOM: 2,
|
SERCOM: 2,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,31 +41,44 @@ const (
|
|||||||
// LORA RFM95 Radio
|
// LORA RFM95 Radio
|
||||||
RFM95_DIO0_PIN = PC13
|
RFM95_DIO0_PIN = PC13
|
||||||
|
|
||||||
//TinyGo UART is MCU LPUSART1
|
// TinyGo UART is MCU LPUSART1
|
||||||
UART_RX_PIN = PA13
|
UART_RX_PIN = PA13
|
||||||
UART_TX_PIN = PA14
|
UART_TX_PIN = PA14
|
||||||
|
|
||||||
//TinyGo UART1 is MCU USART1
|
// TinyGo UART1 is MCU USART1
|
||||||
UART1_RX_PIN = PB6
|
UART1_RX_PIN = PB6
|
||||||
UART1_TX_PIN = PB7
|
UART1_TX_PIN = PB7
|
||||||
|
|
||||||
|
// MPU9250 Nine-Axis (Gyro + Accelerometer + Compass)
|
||||||
|
I2C0_SCL_PIN = PA9
|
||||||
|
I2C0_SDA_PIN = PA10
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
||||||
// Console UART (LPUSART1)
|
// Console UART (LPUSART1)
|
||||||
UART0 = UART{
|
UART0 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.LPUART1,
|
Bus: stm32.LPUART1,
|
||||||
AltFuncSelector: 6,
|
TxAltFuncSelector: 6,
|
||||||
|
RxAltFuncSelector: 6,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gps UART
|
// Gps UART
|
||||||
UART1 = UART{
|
UART1 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.USART1,
|
Bus: stm32.USART1,
|
||||||
AltFuncSelector: 0,
|
TxAltFuncSelector: 0,
|
||||||
|
RxAltFuncSelector: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MPU9250 Nine-Axis (Gyro + Accelerometer + Compass)
|
||||||
|
I2C1 = &I2C{
|
||||||
|
Bus: stm32.I2C1,
|
||||||
|
AltFuncSelector: 6,
|
||||||
|
}
|
||||||
|
I2C0 = I2C1
|
||||||
|
|
||||||
// SPI
|
// SPI
|
||||||
SPI0 = SPI{
|
SPI0 = SPI{
|
||||||
Bus: stm32.SPI1,
|
Bus: stm32.SPI1,
|
||||||
|
|||||||
@@ -13,16 +13,3 @@ var (
|
|||||||
Bus: kendryte.SPI1,
|
Bus: kendryte.SPI1,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2C on the MAix Bit.
|
|
||||||
var (
|
|
||||||
I2C0 = I2C{
|
|
||||||
Bus: kendryte.I2C0,
|
|
||||||
}
|
|
||||||
I2C1 = I2C{
|
|
||||||
Bus: kendryte.I2C1,
|
|
||||||
}
|
|
||||||
I2C2 = I2C{
|
|
||||||
Bus: kendryte.I2C2,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func init() {
|
|||||||
|
|
||||||
// I2C on the MatrixPortal M4
|
// I2C on the MatrixPortal M4
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM5_I2CM,
|
Bus: sam.SERCOM5_I2CM,
|
||||||
SERCOM: 5,
|
SERCOM: 5,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func init() {
|
|||||||
|
|
||||||
// I2C on the Metro M4.
|
// I2C on the Metro M4.
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM5_I2CM,
|
Bus: sam.SERCOM5_I2CM,
|
||||||
SERCOM: 5,
|
SERCOM: 5,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const (
|
|||||||
// USB CDC identifiers
|
// USB CDC identifiers
|
||||||
const (
|
const (
|
||||||
usb_STRING_PRODUCT = "Makerdiary nRF52840 MDK USB Dongle"
|
usb_STRING_PRODUCT = "Makerdiary nRF52840 MDK USB Dongle"
|
||||||
usb_STRING_MANUFACTURER = "Makerdiary"
|
usb_STRING_MANUFACTURER = "Nordic Semiconductor ASA"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const (
|
|||||||
// USB CDC identifiers
|
// USB CDC identifiers
|
||||||
const (
|
const (
|
||||||
usb_STRING_PRODUCT = "Makerdiary nRF52840 MDK"
|
usb_STRING_PRODUCT = "Makerdiary nRF52840 MDK"
|
||||||
usb_STRING_MANUFACTURER = "Makerdiary"
|
usb_STRING_MANUFACTURER = "Nordic Semiconductor ASA"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -120,6 +120,6 @@ const (
|
|||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SCL_PIN = PB6
|
I2C0_SCL_PIN = PB6
|
||||||
SDA_PIN = PB7
|
I2C0_SDA_PIN = PB7
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -33,9 +33,10 @@ var (
|
|||||||
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
|
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
|
||||||
// Both UART0 and UART1 refer to USART2.
|
// Both UART0 and UART1 refer to USART2.
|
||||||
UART0 = UART{
|
UART0 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.USART3,
|
Bus: stm32.USART3,
|
||||||
AltFuncSelector: UART_ALT_FN,
|
TxAltFuncSelector: UART_ALT_FN,
|
||||||
|
RxAltFuncSelector: UART_ALT_FN,
|
||||||
}
|
}
|
||||||
UART1 = &UART0
|
UART1 = &UART0
|
||||||
)
|
)
|
||||||
@@ -53,6 +54,15 @@ const (
|
|||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SCL_PIN = PB6
|
I2C0_SCL_PIN = PB8
|
||||||
SDA_PIN = PB7
|
I2C0_SDA_PIN = PB9
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// I2C1 is documented, alias to I2C0 as well
|
||||||
|
I2C1 = &I2C{
|
||||||
|
Bus: stm32.I2C1,
|
||||||
|
AltFuncSelector: 4,
|
||||||
|
}
|
||||||
|
I2C0 = I2C1
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
// +build nucleol031k6
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/stm32"
|
||||||
|
"runtime/interrupt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LED = LED_BUILTIN
|
||||||
|
LED_BUILTIN = LED_GREEN
|
||||||
|
LED_GREEN = PB3
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Arduino Pins
|
||||||
|
A0 = PA0 // ADC_IN0
|
||||||
|
A1 = PA1 // ADC_IN1
|
||||||
|
A2 = PA3 // ADC_IN3
|
||||||
|
A3 = PA4 // ADC_IN4
|
||||||
|
A4 = PA5 // ADC_IN5 || I2C1_SDA
|
||||||
|
A5 = PA6 // ADC_IN6 || I2C1_SCL
|
||||||
|
A6 = PA7 // ADC_IN7
|
||||||
|
A7 = PA2 // ADC_IN2
|
||||||
|
|
||||||
|
D0 = PA10 // USART1_TX
|
||||||
|
D1 = PA9 // USART1_RX
|
||||||
|
D2 = PA12
|
||||||
|
D3 = PB0 // TIM2_CH3
|
||||||
|
D4 = PB7
|
||||||
|
D5 = PB6 // TIM16_CH1N
|
||||||
|
D6 = PB1 // TIM14_CH1
|
||||||
|
D9 = PA8 // TIM1_CH1
|
||||||
|
D10 = PA11 // SPI_CS || TIM1_CH4
|
||||||
|
D11 = PB5 // SPI1_MOSI || TIM3_CH2
|
||||||
|
D12 = PB4 // SPI1_MISO
|
||||||
|
D13 = PB3 // SPI1_SCK
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// UART pins
|
||||||
|
// PA2 and PA15 are connected to the ST-Link Virtual Com Port (VCP)
|
||||||
|
UART_TX_PIN = PA2
|
||||||
|
UART_RX_PIN = PA15
|
||||||
|
|
||||||
|
// SPI
|
||||||
|
SPI1_SCK_PIN = PB3
|
||||||
|
SPI1_SDI_PIN = PB5
|
||||||
|
SPI1_SDO_PIN = PB4
|
||||||
|
SPI0_SCK_PIN = SPI1_SCK_PIN
|
||||||
|
SPI0_SDI_PIN = SPI1_SDI_PIN
|
||||||
|
SPI0_SDO_PIN = SPI1_SDO_PIN
|
||||||
|
|
||||||
|
// I2C pins
|
||||||
|
// PB6 and PB7 are mapped to CN4 pin 7 and CN4 pin 8 respectively with the
|
||||||
|
// default solder bridge settings
|
||||||
|
I2C0_SCL_PIN = PB7
|
||||||
|
I2C0_SDA_PIN = PB6
|
||||||
|
I2C0_ALT_FUNC = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// USART2 is the hardware serial port connected to the onboard ST-LINK
|
||||||
|
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
|
||||||
|
// Both UART0 and UART1 refer to USART2.
|
||||||
|
UART0 = UART{
|
||||||
|
Buffer: NewRingBuffer(),
|
||||||
|
Bus: stm32.USART2,
|
||||||
|
TxAltFuncSelector: 4,
|
||||||
|
RxAltFuncSelector: 4,
|
||||||
|
}
|
||||||
|
UART1 = &UART0
|
||||||
|
|
||||||
|
// I2C1 is documented, alias to I2C0 as well
|
||||||
|
I2C1 = &I2C{
|
||||||
|
Bus: stm32.I2C1,
|
||||||
|
AltFuncSelector: 1,
|
||||||
|
}
|
||||||
|
I2C0 = I2C1
|
||||||
|
|
||||||
|
// SPI
|
||||||
|
SPI0 = SPI{
|
||||||
|
Bus: stm32.SPI1,
|
||||||
|
AltFuncSelector: 0,
|
||||||
|
}
|
||||||
|
SPI1 = &SPI0
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
UART0.Interrupt = interrupt.New(stm32.IRQ_USART2, UART0.handleInterrupt)
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// +build nucleol432kc
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/stm32"
|
||||||
|
"runtime/interrupt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LED = LED_BUILTIN
|
||||||
|
LED_BUILTIN = LED_GREEN
|
||||||
|
LED_GREEN = PB3
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART pins
|
||||||
|
const (
|
||||||
|
// PA2 and PA15 are connected to the ST-Link Virtual Com Port (VCP)
|
||||||
|
UART_TX_PIN = PA2
|
||||||
|
UART_RX_PIN = PA15
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C pins
|
||||||
|
const (
|
||||||
|
// With default solder bridge settings:
|
||||||
|
// PB6 / Arduino D5 / CN3 Pin 8 is SCL
|
||||||
|
// PB7 / Arduino D4 / CN3 Pin 7 is SDA
|
||||||
|
I2C0_SCL_PIN = PB6
|
||||||
|
I2C0_SDA_PIN = PB7
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// USART2 is the hardware serial port connected to the onboard ST-LINK
|
||||||
|
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
|
||||||
|
// Both UART0 and UART1 refer to USART2.
|
||||||
|
UART0 = UART{
|
||||||
|
Buffer: NewRingBuffer(),
|
||||||
|
Bus: stm32.USART2,
|
||||||
|
TxAltFuncSelector: 7,
|
||||||
|
RxAltFuncSelector: 3,
|
||||||
|
}
|
||||||
|
UART1 = &UART0
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// I2C1 is documented, alias to I2C0 as well
|
||||||
|
I2C1 = &I2C{
|
||||||
|
Bus: stm32.I2C1,
|
||||||
|
AltFuncSelector: 4,
|
||||||
|
}
|
||||||
|
I2C0 = I2C1
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
UART0.Interrupt = interrupt.New(stm32.IRQ_USART2, UART0.handleInterrupt)
|
||||||
|
}
|
||||||
@@ -33,13 +33,28 @@ var (
|
|||||||
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
|
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
|
||||||
// Both UART0 and UART1 refer to LPUART1.
|
// Both UART0 and UART1 refer to LPUART1.
|
||||||
UART0 = UART{
|
UART0 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.LPUART1,
|
Bus: stm32.LPUART1,
|
||||||
AltFuncSelector: UART_ALT_FN,
|
TxAltFuncSelector: UART_ALT_FN,
|
||||||
|
RxAltFuncSelector: UART_ALT_FN,
|
||||||
}
|
}
|
||||||
UART1 = &UART0
|
UART1 = &UART0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
I2C0_SCL_PIN = PB8
|
||||||
|
I2C0_SDA_PIN = PB9
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// I2C1 is documented, alias to I2C0 as well
|
||||||
|
I2C1 = &I2C{
|
||||||
|
Bus: stm32.I2C1,
|
||||||
|
AltFuncSelector: 4,
|
||||||
|
}
|
||||||
|
I2C0 = I2C1
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
UART0.Interrupt = interrupt.New(stm32.IRQ_LPUART1, UART0.handleInterrupt)
|
UART0.Interrupt = interrupt.New(stm32.IRQ_LPUART1, UART0.handleInterrupt)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ func init() {
|
|||||||
|
|
||||||
// I2C on the P1AM-100.
|
// I2C on the P1AM-100.
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM0_I2CM,
|
Bus: sam.SERCOM0_I2CM,
|
||||||
SERCOM: 0,
|
SERCOM: 0,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
// +build pca10059
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
// The PCA10040 has a low-frequency (32kHz) crystal oscillator on board.
|
||||||
|
const HasLowFrequencyCrystal = true
|
||||||
|
|
||||||
|
// LEDs on the PCA10059 (nRF52840 dongle)
|
||||||
|
const (
|
||||||
|
LED Pin = LED1
|
||||||
|
LED1 Pin = 6
|
||||||
|
LED2 Pin = 8
|
||||||
|
LED3 Pin = (1 << 5) | 9
|
||||||
|
LED4 Pin = 12
|
||||||
|
)
|
||||||
|
|
||||||
|
// Buttons on the PCA10059 (nRF52840 dongle)
|
||||||
|
const (
|
||||||
|
BUTTON Pin = BUTTON1
|
||||||
|
BUTTON1 Pin = (1 << 5) | 6
|
||||||
|
)
|
||||||
|
|
||||||
|
// ADC pins
|
||||||
|
const (
|
||||||
|
ADC1 Pin = 2
|
||||||
|
ADC2 Pin = 4
|
||||||
|
ADC3 Pin = 29
|
||||||
|
ADC4 Pin = 31
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART pins
|
||||||
|
const (
|
||||||
|
UART_TX_PIN Pin = NoPin
|
||||||
|
UART_RX_PIN Pin = NoPin
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART0 is the USB device
|
||||||
|
var (
|
||||||
|
UART0 = USB
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C pins (unused)
|
||||||
|
const (
|
||||||
|
SDA_PIN = NoPin
|
||||||
|
SCL_PIN = NoPin
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI pins (unused)
|
||||||
|
const (
|
||||||
|
SPI0_SCK_PIN = NoPin
|
||||||
|
SPI0_SDO_PIN = NoPin
|
||||||
|
SPI0_SDI_PIN = NoPin
|
||||||
|
)
|
||||||
|
|
||||||
|
// USB CDC identifiers
|
||||||
|
const (
|
||||||
|
usb_STRING_PRODUCT = "nRF52840 Dongle"
|
||||||
|
usb_STRING_MANUFACTURER = "Nordic Semiconductor ASA"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
usb_VID uint16 = 0x1915
|
||||||
|
usb_PID uint16 = 0xCAFE
|
||||||
|
)
|
||||||
@@ -28,7 +28,7 @@ func init() {
|
|||||||
|
|
||||||
// I2C on the ItsyBitsy M4.
|
// I2C on the ItsyBitsy M4.
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM2_I2CM,
|
Bus: sam.SERCOM2_I2CM,
|
||||||
SERCOM: 2,
|
SERCOM: 2,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ const (
|
|||||||
|
|
||||||
// I2C on the PyGamer.
|
// I2C on the PyGamer.
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM2_I2CM,
|
Bus: sam.SERCOM2_I2CM,
|
||||||
SERCOM: 2,
|
SERCOM: 2,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func init() {
|
|||||||
|
|
||||||
// I2C on the PyPortal.
|
// I2C on the PyPortal.
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM5_I2CM,
|
Bus: sam.SERCOM5_I2CM,
|
||||||
SERCOM: 5,
|
SERCOM: 5,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ const (
|
|||||||
|
|
||||||
// I2C on the QT Py M0.
|
// I2C on the QT Py M0.
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM2_I2CM,
|
Bus: sam.SERCOM2_I2CM,
|
||||||
SERCOM: 2,
|
SERCOM: 2,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
// +build bluepill nucleof103rb stm32f4
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
// Peripheral abstraction layer for the stm32.
|
|
||||||
@@ -28,9 +28,10 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
UART0 = UART{
|
UART0 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.USART2,
|
Bus: stm32.USART2,
|
||||||
AltFuncSelector: AF7_USART1_2_3,
|
TxAltFuncSelector: AF7_USART1_2_3,
|
||||||
|
RxAltFuncSelector: AF7_USART1_2_3,
|
||||||
}
|
}
|
||||||
UART1 = &UART0
|
UART1 = &UART0
|
||||||
)
|
)
|
||||||
@@ -73,7 +74,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: stm32.I2C1,
|
Bus: stm32.I2C1,
|
||||||
AltFuncSelector: AF4_I2C1_2_3,
|
AltFuncSelector: AF4_I2C1_2_3,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ const (
|
|||||||
|
|
||||||
// I2C on the Trinket M0.
|
// I2C on the Trinket M0.
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM2_I2CM,
|
Bus: sam.SERCOM2_I2CM,
|
||||||
SERCOM: 2,
|
SERCOM: 2,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,12 +29,12 @@ func init() {
|
|||||||
|
|
||||||
// I2C on the Wio Terminal
|
// I2C on the Wio Terminal
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM4_I2CM,
|
Bus: sam.SERCOM4_I2CM,
|
||||||
SERCOM: 4,
|
SERCOM: 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
I2C1 = I2C{
|
I2C1 = &I2C{
|
||||||
Bus: sam.SERCOM4_I2CM,
|
Bus: sam.SERCOM4_I2CM,
|
||||||
SERCOM: 4,
|
SERCOM: 4,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ const (
|
|||||||
|
|
||||||
// I2C on the Xiao
|
// I2C on the Xiao
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM2_I2CM,
|
Bus: sam.SERCOM2_I2CM,
|
||||||
SERCOM: 2,
|
SERCOM: 2,
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
// +build avr nrf sam stm32,!stm32f7x2,!stm32l5x2,!stm32l0 fe310 k210
|
// +build atmega nrf sam stm32 fe310 k210
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ var (
|
|||||||
// Many I2C-compatible devices are organized in terms of registers. This method
|
// Many I2C-compatible devices are organized in terms of registers. This method
|
||||||
// is a shortcut to easily write to such registers. Also, it only works for
|
// is a shortcut to easily write to such registers. Also, it only works for
|
||||||
// devices with 7-bit addresses, which is the vast majority.
|
// devices with 7-bit addresses, which is the vast majority.
|
||||||
func (i2c I2C) WriteRegister(address uint8, register uint8, data []byte) error {
|
func (i2c *I2C) WriteRegister(address uint8, register uint8, data []byte) error {
|
||||||
buf := make([]uint8, len(data)+1)
|
buf := make([]uint8, len(data)+1)
|
||||||
buf[0] = register
|
buf[0] = register
|
||||||
copy(buf[1:], data)
|
copy(buf[1:], data)
|
||||||
@@ -42,6 +42,6 @@ func (i2c I2C) WriteRegister(address uint8, register uint8, data []byte) error {
|
|||||||
// Many I2C-compatible devices are organized in terms of registers. This method
|
// Many I2C-compatible devices are organized in terms of registers. This method
|
||||||
// is a shortcut to easily read such registers. Also, it only works for devices
|
// is a shortcut to easily read such registers. Also, it only works for devices
|
||||||
// with 7-bit addresses, which is the vast majority.
|
// with 7-bit addresses, which is the vast majority.
|
||||||
func (i2c I2C) ReadRegister(address uint8, register uint8, data []byte) error {
|
func (i2c *I2C) ReadRegister(address uint8, register uint8, data []byte) error {
|
||||||
return i2c.Tx(uint16(address), []byte{register}, data)
|
return i2c.Tx(uint16(address), []byte{register}, data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,10 +37,6 @@ func (p Pin) Low() {
|
|||||||
p.Set(false)
|
p.Set(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
type PWM struct {
|
|
||||||
Pin Pin
|
|
||||||
}
|
|
||||||
|
|
||||||
type ADC struct {
|
type ADC struct {
|
||||||
Pin Pin
|
Pin Pin
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,20 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// I2C on AVR.
|
||||||
|
type I2C struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// I2C0 is the only I2C interface on most AVRs.
|
||||||
|
var I2C0 *I2C = nil
|
||||||
|
|
||||||
// I2CConfig is used to store config info for I2C.
|
// I2CConfig is used to store config info for I2C.
|
||||||
type I2CConfig struct {
|
type I2CConfig struct {
|
||||||
Frequency uint32
|
Frequency uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure is intended to setup the I2C interface.
|
// Configure is intended to setup the I2C interface.
|
||||||
func (i2c I2C) Configure(config I2CConfig) error {
|
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||||
// Default I2C bus speed is 100 kHz.
|
// Default I2C bus speed is 100 kHz.
|
||||||
if config.Frequency == 0 {
|
if config.Frequency == 0 {
|
||||||
config.Frequency = TWI_FREQ_100KHZ
|
config.Frequency = TWI_FREQ_100KHZ
|
||||||
@@ -42,7 +49,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
|||||||
// Tx does a single I2C transaction at the specified address.
|
// Tx does a single I2C transaction at the specified address.
|
||||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||||
if len(w) != 0 {
|
if len(w) != 0 {
|
||||||
i2c.start(uint8(addr), true) // start transmission for writing
|
i2c.start(uint8(addr), true) // start transmission for writing
|
||||||
for _, b := range w {
|
for _, b := range w {
|
||||||
@@ -63,7 +70,7 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// start starts an I2C communication session.
|
// start starts an I2C communication session.
|
||||||
func (i2c I2C) start(address uint8, write bool) {
|
func (i2c *I2C) start(address uint8, write bool) {
|
||||||
// Clear TWI interrupt flag, put start condition on SDA, and enable TWI.
|
// Clear TWI interrupt flag, put start condition on SDA, and enable TWI.
|
||||||
avr.TWCR.Set((avr.TWCR_TWINT | avr.TWCR_TWSTA | avr.TWCR_TWEN))
|
avr.TWCR.Set((avr.TWCR_TWINT | avr.TWCR_TWSTA | avr.TWCR_TWEN))
|
||||||
|
|
||||||
@@ -80,7 +87,7 @@ func (i2c I2C) start(address uint8, write bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// stop ends an I2C communication session.
|
// stop ends an I2C communication session.
|
||||||
func (i2c I2C) stop() {
|
func (i2c *I2C) stop() {
|
||||||
// Send stop condition.
|
// Send stop condition.
|
||||||
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWSTO)
|
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWSTO)
|
||||||
|
|
||||||
@@ -90,7 +97,7 @@ func (i2c I2C) stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// writeByte writes a single byte to the I2C bus.
|
// writeByte writes a single byte to the I2C bus.
|
||||||
func (i2c I2C) writeByte(data byte) {
|
func (i2c *I2C) writeByte(data byte) {
|
||||||
// Write data to register.
|
// Write data to register.
|
||||||
avr.TWDR.Set(data)
|
avr.TWDR.Set(data)
|
||||||
|
|
||||||
@@ -103,7 +110,7 @@ func (i2c I2C) writeByte(data byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// readByte reads a single byte from the I2C bus.
|
// readByte reads a single byte from the I2C bus.
|
||||||
func (i2c I2C) readByte() byte {
|
func (i2c *I2C) readByte() byte {
|
||||||
// Clear TWI interrupt flag and enable TWI.
|
// Clear TWI interrupt flag and enable TWI.
|
||||||
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWEA)
|
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWEA)
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/avr"
|
"device/avr"
|
||||||
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -21,71 +22,432 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitPWM initializes the registers needed for PWM.
|
// PWM is one PWM peripheral, which consists of a counter and two output
|
||||||
func InitPWM() {
|
// channels (that can be connected to two fixed pins). You can set the frequency
|
||||||
// use waveform generation
|
// using SetPeriod, but only for all the channels in this PWM peripheral at
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_WGM00)
|
// once.
|
||||||
|
type PWM struct {
|
||||||
// set timer 0 prescale factor to 64
|
num uint8
|
||||||
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.
|
var (
|
||||||
func (pwm PWM) Configure() error {
|
Timer0 = PWM{0} // 8 bit timer for PD5 and PD6
|
||||||
switch pwm.Pin / 8 {
|
Timer1 = PWM{1} // 16 bit timer for PB1 and PB2
|
||||||
case 0: // port B
|
Timer2 = PWM{2} // 8 bit timer for PB3 and PD3
|
||||||
avr.DDRB.SetBits(1 << uint8(pwm.Pin))
|
)
|
||||||
case 2: // port D
|
|
||||||
avr.DDRD.SetBits(1 << uint8(pwm.Pin-16))
|
// Configure enables and configures this PWM.
|
||||||
|
//
|
||||||
|
// For the two 8 bit timers, there is only a limited number of periods
|
||||||
|
// available, namely the CPU frequency divided by 256 and again divided by 1, 8,
|
||||||
|
// 64, 256, or 1024. For a MCU running at 16MHz, this would be a period of 16µs,
|
||||||
|
// 128µs, 1024µs, 4096µs, or 16384µs.
|
||||||
|
func (pwm PWM) Configure(config PWMConfig) error {
|
||||||
|
switch pwm.num {
|
||||||
|
case 0, 2: // 8-bit timers (Timer/counter 0 and Timer/counter 2)
|
||||||
|
// Calculate the timer prescaler.
|
||||||
|
// While we could configure a flexible top, that would sacrifice one of
|
||||||
|
// the PWM output compare registers and thus a PWM channel. I've chosen
|
||||||
|
// to instead limit this timer to a fixed number of frequencies.
|
||||||
|
var prescaler uint8
|
||||||
|
switch config.Period {
|
||||||
|
case 0, (uint64(1e9) * 256 * 1) / uint64(CPUFrequency()):
|
||||||
|
prescaler = 1
|
||||||
|
case (uint64(1e9) * 256 * 8) / uint64(CPUFrequency()):
|
||||||
|
prescaler = 2
|
||||||
|
case (uint64(1e9) * 256 * 64) / uint64(CPUFrequency()):
|
||||||
|
prescaler = 3
|
||||||
|
case (uint64(1e9) * 256 * 256) / uint64(CPUFrequency()):
|
||||||
|
prescaler = 4
|
||||||
|
case (uint64(1e9) * 256 * 1024) / uint64(CPUFrequency()):
|
||||||
|
prescaler = 5
|
||||||
|
default:
|
||||||
|
return ErrPWMPeriodTooLong
|
||||||
|
}
|
||||||
|
|
||||||
|
if pwm.num == 0 {
|
||||||
|
avr.TCCR0B.Set(prescaler)
|
||||||
|
// Set the PWM mode to fast PWM (mode = 3).
|
||||||
|
avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01)
|
||||||
|
} else {
|
||||||
|
avr.TCCR2B.Set(prescaler)
|
||||||
|
// Set the PWM mode to fast PWM (mode = 3).
|
||||||
|
avr.TCCR2A.Set(avr.TCCR2A_WGM20 | avr.TCCR2A_WGM21)
|
||||||
|
}
|
||||||
|
case 1: // Timer/counter 1
|
||||||
|
// The top value is the number of PWM ticks a PWM period takes. It is
|
||||||
|
// initially picked assuming an unlimited counter top and no PWM
|
||||||
|
// prescaler.
|
||||||
|
var top uint64
|
||||||
|
if config.Period == 0 {
|
||||||
|
// Use a top appropriate for LEDs. Picking a relatively low period
|
||||||
|
// here (0xff) for consistency with the other timers.
|
||||||
|
top = 0xff
|
||||||
|
} else {
|
||||||
|
// The formula below calculates the following formula, optimized:
|
||||||
|
// top = period * (CPUFrequency() / 1e9)
|
||||||
|
// By dividing the CPU frequency first (an operation that is easily
|
||||||
|
// optimized away) the period has less chance of overflowing.
|
||||||
|
top = config.Period * (uint64(CPUFrequency()) / 1000000) / 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
avr.TCCR1A.Set(avr.TCCR1A_WGM11)
|
||||||
|
|
||||||
|
// The ideal PWM period may be larger than would fit in the PWM counter,
|
||||||
|
// which is 16 bits (see maxTop). Therefore, try to make the PWM clock
|
||||||
|
// speed lower with a prescaler to make the top value fit the maximum
|
||||||
|
// top value.
|
||||||
|
const maxTop = 0x10000
|
||||||
|
switch {
|
||||||
|
case top <= maxTop:
|
||||||
|
avr.TCCR1B.Set(3<<3 | 1) // no prescaling
|
||||||
|
case top/8 <= maxTop:
|
||||||
|
avr.TCCR1B.Set(3<<3 | 2) // divide by 8
|
||||||
|
top /= 8
|
||||||
|
case top/64 <= maxTop:
|
||||||
|
avr.TCCR1B.Set(3<<3 | 3) // divide by 64
|
||||||
|
top /= 64
|
||||||
|
case top/256 <= maxTop:
|
||||||
|
avr.TCCR1B.Set(3<<3 | 4) // divide by 256
|
||||||
|
top /= 256
|
||||||
|
case top/1024 <= maxTop:
|
||||||
|
avr.TCCR1B.Set(3<<3 | 5) // divide by 1024
|
||||||
|
top /= 1024
|
||||||
|
default:
|
||||||
|
return ErrPWMPeriodTooLong
|
||||||
|
}
|
||||||
|
|
||||||
|
// A top of 0x10000 is at 100% duty cycle. Subtract one because the
|
||||||
|
// counter counts from 0, not 1 (avoiding an off-by-one).
|
||||||
|
top -= 1
|
||||||
|
|
||||||
|
avr.ICR1H.Set(uint8(top >> 8))
|
||||||
|
avr.ICR1L.Set(uint8(top))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
|
// SetPeriod updates the period of this PWM peripheral.
|
||||||
// 8-bit value ranging from 0 to 255.
|
// To set a particular frequency, use the following formula:
|
||||||
func (pwm PWM) Set(value uint16) {
|
//
|
||||||
value8 := uint8(value >> 8)
|
// period = 1e9 / frequency
|
||||||
switch pwm.Pin {
|
//
|
||||||
case PD3:
|
// If you use a period of 0, a period that works well for LEDs will be picked.
|
||||||
// connect pwm to pin on timer 2, channel B
|
//
|
||||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
// SetPeriod will not change the prescaler, but also won't change the current
|
||||||
avr.OCR2B.Set(value8) // set pwm duty
|
// value in any of the channels. This means that you may need to update the
|
||||||
case PD5:
|
// value for the particular channel.
|
||||||
// connect pwm to pin on timer 0, channel B
|
//
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
// Note that you cannot pick any arbitrary period after the PWM peripheral has
|
||||||
avr.OCR0B.Set(value8) // set pwm duty
|
// been configured. If you want to switch between frequencies, pick the lowest
|
||||||
case PD6:
|
// frequency (longest period) once when calling Configure and adjust the
|
||||||
// connect pwm to pin on timer 0, channel A
|
// frequency here as needed.
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
func (pwm PWM) SetPeriod(period uint64) error {
|
||||||
avr.OCR0A.Set(value8) // set pwm duty
|
if pwm.num != 1 {
|
||||||
case PB1:
|
return ErrPWMPeriodTooLong // TODO better error message
|
||||||
// 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
|
// The top value is the number of PWM ticks a PWM period takes. It is
|
||||||
avr.OCR1AL.Set(value8) // set pwm duty
|
// initially picked assuming an unlimited counter top and no PWM
|
||||||
case PB2:
|
// prescaler.
|
||||||
// connect pwm to pin on timer 1, channel B
|
var top uint64
|
||||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
if period == 0 {
|
||||||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
// Use a top appropriate for LEDs. Picking a relatively low period
|
||||||
avr.OCR1BL.Set(value8) // set pwm duty
|
// here (0xff) for consistency with the other timers.
|
||||||
case PB3:
|
top = 0xff
|
||||||
// connect pwm to pin on timer 2, channel A
|
} else {
|
||||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
// The formula below calculates the following formula, optimized:
|
||||||
avr.OCR2A.Set(value8) // set pwm duty
|
// top = period * (CPUFrequency() / 1e9)
|
||||||
default:
|
// By dividing the CPU frequency first (an operation that is easily
|
||||||
panic("Invalid PWM pin")
|
// optimized away) the period has less chance of overflowing.
|
||||||
|
top = period * (uint64(CPUFrequency()) / 1000000) / 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
prescaler := avr.TCCR1B.Get() & 0x7
|
||||||
|
switch prescaler {
|
||||||
|
case 1:
|
||||||
|
top /= 1
|
||||||
|
case 2:
|
||||||
|
top /= 8
|
||||||
|
case 3:
|
||||||
|
top /= 64
|
||||||
|
case 4:
|
||||||
|
top /= 256
|
||||||
|
case 5:
|
||||||
|
top /= 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
// A top of 0x10000 is at 100% duty cycle. Subtract one because the counter
|
||||||
|
// counts from 0, not 1 (avoiding an off-by-one).
|
||||||
|
top -= 1
|
||||||
|
|
||||||
|
if top > 0xffff {
|
||||||
|
return ErrPWMPeriodTooLong
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning: this change is not atomic!
|
||||||
|
avr.ICR1H.Set(uint8(top >> 8))
|
||||||
|
avr.ICR1L.Set(uint8(top))
|
||||||
|
|
||||||
|
// ... and because of that, set the counter back to zero to avoid most of
|
||||||
|
// the effects of this non-atomicity.
|
||||||
|
avr.TCNT1H.Set(0)
|
||||||
|
avr.TCNT1L.Set(0)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top returns the current counter top, for use in duty cycle calculation. It
|
||||||
|
// will only change with a call to Configure or SetPeriod, otherwise it is
|
||||||
|
// constant.
|
||||||
|
//
|
||||||
|
// The value returned here is hardware dependent. In general, it's best to treat
|
||||||
|
// it as an opaque value that can be divided by some number and passed to Set
|
||||||
|
// (see Set documentation for more information).
|
||||||
|
func (pwm PWM) Top() uint32 {
|
||||||
|
if pwm.num == 1 {
|
||||||
|
// Timer 1 has a configurable top value.
|
||||||
|
low := avr.ICR1L.Get()
|
||||||
|
high := avr.ICR1H.Get()
|
||||||
|
return uint32(high)<<8 | uint32(low) + 1
|
||||||
|
}
|
||||||
|
// Other timers go from 0 to 0xff (0x100 or 256 in total).
|
||||||
|
return 256
|
||||||
|
}
|
||||||
|
|
||||||
|
// Counter returns the current counter value of the timer in this PWM
|
||||||
|
// peripheral. It may be useful for debugging.
|
||||||
|
func (pwm PWM) Counter() uint32 {
|
||||||
|
switch pwm.num {
|
||||||
|
case 0:
|
||||||
|
return uint32(avr.TCNT0.Get())
|
||||||
|
case 1:
|
||||||
|
mask := interrupt.Disable()
|
||||||
|
low := avr.TCNT1L.Get()
|
||||||
|
high := avr.TCNT1H.Get()
|
||||||
|
interrupt.Restore(mask)
|
||||||
|
return uint32(high)<<8 | uint32(low)
|
||||||
|
case 2:
|
||||||
|
return uint32(avr.TCNT2.Get())
|
||||||
|
}
|
||||||
|
// Unknown PWM.
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Period returns the used PWM period in nanoseconds. It might deviate slightly
|
||||||
|
// from the configured period due to rounding.
|
||||||
|
func (pwm PWM) Period() uint64 {
|
||||||
|
var prescaler uint8
|
||||||
|
switch pwm.num {
|
||||||
|
case 0:
|
||||||
|
prescaler = avr.TCCR0B.Get() & 0x7
|
||||||
|
case 1:
|
||||||
|
prescaler = avr.TCCR1B.Get() & 0x7
|
||||||
|
case 2:
|
||||||
|
prescaler = avr.TCCR2B.Get() & 0x7
|
||||||
|
}
|
||||||
|
top := uint64(pwm.Top())
|
||||||
|
switch prescaler {
|
||||||
|
case 1: // prescaler 1
|
||||||
|
return 1 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||||
|
case 2: // prescaler 8
|
||||||
|
return 8 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||||
|
case 3: // prescaler 64
|
||||||
|
return 64 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||||
|
case 4: // prescaler 256
|
||||||
|
return 256 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||||
|
case 5: // prescaler 1024
|
||||||
|
return 1024 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||||
|
default: // unknown clock source
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel returns a PWM channel for the given pin.
|
||||||
|
func (pwm PWM) Channel(pin Pin) (uint8, error) {
|
||||||
|
pin.Configure(PinConfig{Mode: PinOutput})
|
||||||
|
pin.Low()
|
||||||
|
switch pwm.num {
|
||||||
|
case 0:
|
||||||
|
switch pin {
|
||||||
|
case PD6: // channel A
|
||||||
|
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||||
|
return 0, nil
|
||||||
|
case PD5: // channel B
|
||||||
|
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
switch pin {
|
||||||
|
case PB1: // channel A
|
||||||
|
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||||
|
return 0, nil
|
||||||
|
case PB2: // channel B
|
||||||
|
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
switch pin {
|
||||||
|
case PB3: // channel A
|
||||||
|
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||||
|
return 0, nil
|
||||||
|
case PD3: // channel B
|
||||||
|
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, ErrInvalidOutputPin
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInverting sets whether to invert the output of this channel.
|
||||||
|
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
|
||||||
|
// the time and low for the rest. Inverting flips the output as if a NOT gate
|
||||||
|
// was placed at the output, meaning that the output would be 25% low and 75%
|
||||||
|
// high with a duty cycle of 25%.
|
||||||
|
//
|
||||||
|
// Note: the invert state may not be applied on the AVR until the next call to
|
||||||
|
// ch.Set().
|
||||||
|
func (pwm PWM) SetInverting(channel uint8, inverting bool) {
|
||||||
|
switch pwm.num {
|
||||||
|
case 0:
|
||||||
|
switch channel {
|
||||||
|
case 0: // channel A
|
||||||
|
if inverting {
|
||||||
|
avr.PORTB.SetBits(1 << 6) // PB6 high
|
||||||
|
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A0)
|
||||||
|
} else {
|
||||||
|
avr.PORTB.ClearBits(1 << 6) // PB6 low
|
||||||
|
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A0)
|
||||||
|
}
|
||||||
|
case 1: // channel B
|
||||||
|
if inverting {
|
||||||
|
avr.PORTB.SetBits(1 << 5) // PB5 high
|
||||||
|
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B0)
|
||||||
|
} else {
|
||||||
|
avr.PORTB.ClearBits(1 << 5) // PB5 low
|
||||||
|
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
// Note: the COM1A0/COM1B0 bit is not set with the configuration below.
|
||||||
|
// It will be set the following call to Set(), however.
|
||||||
|
switch channel {
|
||||||
|
case 0: // channel A, PB1
|
||||||
|
if inverting {
|
||||||
|
avr.PORTB.SetBits(1 << 1) // PB1 high
|
||||||
|
} else {
|
||||||
|
avr.PORTB.ClearBits(1 << 1) // PB1 low
|
||||||
|
}
|
||||||
|
case 1: // channel B, PB2
|
||||||
|
if inverting {
|
||||||
|
avr.PORTB.SetBits(1 << 2) // PB2 high
|
||||||
|
} else {
|
||||||
|
avr.PORTB.ClearBits(1 << 2) // PB2 low
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
switch channel {
|
||||||
|
case 0: // channel A
|
||||||
|
if inverting {
|
||||||
|
avr.PORTB.SetBits(1 << 3) // PB3 high
|
||||||
|
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A0)
|
||||||
|
} else {
|
||||||
|
avr.PORTB.ClearBits(1 << 3) // PB3 low
|
||||||
|
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A0)
|
||||||
|
}
|
||||||
|
case 1: // channel B
|
||||||
|
if inverting {
|
||||||
|
avr.PORTD.SetBits(1 << 3) // PD3 high
|
||||||
|
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B0)
|
||||||
|
} else {
|
||||||
|
avr.PORTD.ClearBits(1 << 3) // PD3 low
|
||||||
|
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set updates the channel value. This is used to control the channel duty
|
||||||
|
// cycle, in other words the fraction of time the channel output is high (or low
|
||||||
|
// when inverted). For example, to set it to a 25% duty cycle, use:
|
||||||
|
//
|
||||||
|
// pwm.Set(channel, pwm.Top() / 4)
|
||||||
|
//
|
||||||
|
// pwm.Set(channel, 0) will set the output to low and pwm.Set(channel,
|
||||||
|
// pwm.Top()) will set the output to high, assuming the output isn't inverted.
|
||||||
|
func (pwm PWM) Set(channel uint8, value uint32) {
|
||||||
|
switch pwm.num {
|
||||||
|
case 0:
|
||||||
|
value := uint16(value)
|
||||||
|
switch channel {
|
||||||
|
case 0: // channel A
|
||||||
|
if value == 0 {
|
||||||
|
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A1)
|
||||||
|
} else {
|
||||||
|
avr.OCR0A.Set(uint8(value - 1))
|
||||||
|
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||||
|
}
|
||||||
|
case 1: // channel B
|
||||||
|
if value == 0 {
|
||||||
|
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B1)
|
||||||
|
} else {
|
||||||
|
avr.OCR0B.Set(uint8(value) - 1)
|
||||||
|
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
mask := interrupt.Disable()
|
||||||
|
switch channel {
|
||||||
|
case 0: // channel A, PB1
|
||||||
|
if value == 0 {
|
||||||
|
avr.TCCR1A.ClearBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
|
||||||
|
} else {
|
||||||
|
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||||
|
avr.OCR1AH.Set(uint8(value >> 8))
|
||||||
|
avr.OCR1AL.Set(uint8(value))
|
||||||
|
if avr.PORTB.HasBits(1 << 1) { // is PB1 high?
|
||||||
|
// Yes, set the inverting bit.
|
||||||
|
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
|
||||||
|
} else {
|
||||||
|
// No, output is non-inverting.
|
||||||
|
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 1: // channel B, PB2
|
||||||
|
if value == 0 {
|
||||||
|
avr.TCCR1A.ClearBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
|
||||||
|
} else {
|
||||||
|
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||||
|
avr.OCR1BH.Set(uint8(value >> 8))
|
||||||
|
avr.OCR1BL.Set(uint8(value))
|
||||||
|
if avr.PORTB.HasBits(1 << 2) { // is PB2 high?
|
||||||
|
// Yes, set the inverting bit.
|
||||||
|
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
|
||||||
|
} else {
|
||||||
|
// No, output is non-inverting.
|
||||||
|
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interrupt.Restore(mask)
|
||||||
|
case 2:
|
||||||
|
value := uint16(value)
|
||||||
|
switch channel {
|
||||||
|
case 0: // channel A
|
||||||
|
if value == 0 {
|
||||||
|
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A1)
|
||||||
|
} else {
|
||||||
|
avr.OCR2A.Set(uint8(value - 1))
|
||||||
|
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||||
|
}
|
||||||
|
case 1: // channel B
|
||||||
|
if value == 0 {
|
||||||
|
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B1)
|
||||||
|
} else {
|
||||||
|
avr.OCR2B.Set(uint8(value - 1))
|
||||||
|
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+516
-192
@@ -8,8 +8,10 @@
|
|||||||
package machine
|
package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"device"
|
||||||
"device/arm"
|
"device/arm"
|
||||||
"device/sam"
|
"device/sam"
|
||||||
|
"errors"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@@ -29,8 +31,8 @@ const (
|
|||||||
PinInput PinMode = 9
|
PinInput PinMode = 9
|
||||||
PinInputPullup PinMode = 10
|
PinInputPullup PinMode = 10
|
||||||
PinOutput PinMode = 11
|
PinOutput PinMode = 11
|
||||||
PinPWM PinMode = PinTimer
|
PinTCC PinMode = PinTimer
|
||||||
PinPWMAlt PinMode = PinTimerAlt
|
PinTCCAlt PinMode = PinTimerAlt
|
||||||
PinInputPulldown PinMode = 12
|
PinInputPulldown PinMode = 12
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -667,7 +669,7 @@ const (
|
|||||||
const i2cTimeout = 1000
|
const i2cTimeout = 1000
|
||||||
|
|
||||||
// Configure is intended to setup the I2C interface.
|
// Configure is intended to setup the I2C interface.
|
||||||
func (i2c I2C) Configure(config I2CConfig) error {
|
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||||
// Default I2C bus speed is 100 kHz.
|
// Default I2C bus speed is 100 kHz.
|
||||||
if config.Frequency == 0 {
|
if config.Frequency == 0 {
|
||||||
config.Frequency = TWI_FREQ_100KHZ
|
config.Frequency = TWI_FREQ_100KHZ
|
||||||
@@ -723,7 +725,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetBaudRate sets the communication speed for the I2C.
|
// SetBaudRate sets the communication speed for the I2C.
|
||||||
func (i2c I2C) SetBaudRate(br uint32) {
|
func (i2c *I2C) SetBaudRate(br uint32) {
|
||||||
// Synchronous arithmetic baudrate, via Arduino SAMD implementation:
|
// Synchronous arithmetic baudrate, via Arduino SAMD implementation:
|
||||||
// SystemCoreClock / ( 2 * baudrate) - 5 - (((SystemCoreClock / 1000000) * WIRE_RISE_TIME_NANOSECONDS) / (2 * 1000));
|
// SystemCoreClock / ( 2 * baudrate) - 5 - (((SystemCoreClock / 1000000) * WIRE_RISE_TIME_NANOSECONDS) / (2 * 1000));
|
||||||
baud := CPUFrequency()/(2*br) - 5 - (((CPUFrequency() / 1000000) * riseTimeNanoseconds) / (2 * 1000))
|
baud := CPUFrequency()/(2*br) - 5 - (((CPUFrequency() / 1000000) * riseTimeNanoseconds) / (2 * 1000))
|
||||||
@@ -733,7 +735,7 @@ func (i2c I2C) SetBaudRate(br uint32) {
|
|||||||
// Tx does a single I2C transaction at the specified address.
|
// Tx does a single I2C transaction at the specified address.
|
||||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||||
var err error
|
var err error
|
||||||
if len(w) != 0 {
|
if len(w) != 0 {
|
||||||
// send start/address for write
|
// send start/address for write
|
||||||
@@ -810,7 +812,7 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteByte writes a single byte to the I2C bus.
|
// WriteByte writes a single byte to the I2C bus.
|
||||||
func (i2c I2C) WriteByte(data byte) error {
|
func (i2c *I2C) WriteByte(data byte) error {
|
||||||
// Send data byte
|
// Send data byte
|
||||||
i2c.Bus.DATA.Set(data)
|
i2c.Bus.DATA.Set(data)
|
||||||
|
|
||||||
@@ -835,7 +837,7 @@ func (i2c I2C) WriteByte(data byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sendAddress sends the address and start signal
|
// sendAddress sends the address and start signal
|
||||||
func (i2c I2C) sendAddress(address uint16, write bool) error {
|
func (i2c *I2C) sendAddress(address uint16, write bool) error {
|
||||||
data := (address << 1)
|
data := (address << 1)
|
||||||
if !write {
|
if !write {
|
||||||
data |= 1 // set read flag
|
data |= 1 // set read flag
|
||||||
@@ -855,7 +857,7 @@ func (i2c I2C) sendAddress(address uint16, write bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i2c I2C) signalStop() error {
|
func (i2c *I2C) signalStop() error {
|
||||||
i2c.Bus.CTRLB.SetBits(wireCmdStop << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Stop command
|
i2c.Bus.CTRLB.SetBits(wireCmdStop << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Stop command
|
||||||
timeout := i2cTimeout
|
timeout := i2cTimeout
|
||||||
for i2c.Bus.SYNCBUSY.HasBits(sam.SERCOM_I2CM_SYNCBUSY_SYSOP) {
|
for i2c.Bus.SYNCBUSY.HasBits(sam.SERCOM_I2CM_SYNCBUSY_SYSOP) {
|
||||||
@@ -867,7 +869,7 @@ func (i2c I2C) signalStop() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i2c I2C) signalRead() error {
|
func (i2c *I2C) signalRead() error {
|
||||||
i2c.Bus.CTRLB.SetBits(wireCmdRead << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Read command
|
i2c.Bus.CTRLB.SetBits(wireCmdRead << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Read command
|
||||||
timeout := i2cTimeout
|
timeout := i2cTimeout
|
||||||
for i2c.Bus.SYNCBUSY.HasBits(sam.SERCOM_I2CM_SYNCBUSY_SYSOP) {
|
for i2c.Bus.SYNCBUSY.HasBits(sam.SERCOM_I2CM_SYNCBUSY_SYSOP) {
|
||||||
@@ -879,7 +881,7 @@ func (i2c I2C) signalRead() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i2c I2C) readByte() byte {
|
func (i2c *I2C) readByte() byte {
|
||||||
for !i2c.Bus.INTFLAG.HasBits(sam.SERCOM_I2CM_INTFLAG_SB) {
|
for !i2c.Bus.INTFLAG.HasBits(sam.SERCOM_I2CM_INTFLAG_SB) {
|
||||||
}
|
}
|
||||||
return byte(i2c.Bus.DATA.Get())
|
return byte(i2c.Bus.DATA.Get())
|
||||||
@@ -1265,201 +1267,527 @@ func (spi SPI) Transfer(w byte) (byte, error) {
|
|||||||
return byte(spi.Bus.DATA.Get()), nil
|
return byte(spi.Bus.DATA.Get()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PWM
|
var (
|
||||||
const period = 0xFFFF
|
ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size")
|
||||||
|
)
|
||||||
|
|
||||||
// InitPWM initializes the PWM interface.
|
// Tx handles read/write operation for SPI interface. Since SPI is a syncronous write/read
|
||||||
func InitPWM() {
|
// interface, there must always be the same number of bytes written as bytes read.
|
||||||
// turn on timer clocks used for PWM
|
// The Tx method knows about this, and offers a few different ways of calling it.
|
||||||
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC0_ | sam.PM_APBCMASK_TCC1_ | sam.PM_APBCMASK_TCC2_)
|
//
|
||||||
|
// This form sends the bytes in tx buffer, putting the resulting bytes read into the rx buffer.
|
||||||
|
// Note that the tx and rx buffers must be the same size:
|
||||||
|
//
|
||||||
|
// spi.Tx(tx, rx)
|
||||||
|
//
|
||||||
|
// This form sends the tx buffer, ignoring the result. Useful for sending "commands" that return zeros
|
||||||
|
// until all the bytes in the command packet have been received:
|
||||||
|
//
|
||||||
|
// spi.Tx(tx, nil)
|
||||||
|
//
|
||||||
|
// This form sends zeros, putting the result into the rx buffer. Good for reading a "result packet":
|
||||||
|
//
|
||||||
|
// spi.Tx(nil, rx)
|
||||||
|
//
|
||||||
|
func (spi SPI) Tx(w, r []byte) error {
|
||||||
|
if spi.Bus.BAUD.Get() == 0x00 {
|
||||||
|
// When the SPI Freq is 24MHz, special processing is performed to improve the speed.
|
||||||
|
|
||||||
// Use GCLK0 for TCC0/TCC1
|
switch {
|
||||||
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_TCC0_TCC1 << sam.GCLK_CLKCTRL_ID_Pos) |
|
case w == nil:
|
||||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
// read only, so write zero and read a result.
|
||||||
sam.GCLK_CLKCTRL_CLKEN)
|
spi.rx(r)
|
||||||
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
case r == nil:
|
||||||
}
|
// write only
|
||||||
|
spi.tx24mhz(w)
|
||||||
|
|
||||||
// Use GCLK0 for TCC2/TC3
|
default:
|
||||||
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_TCC2_TC3 << sam.GCLK_CLKCTRL_ID_Pos) |
|
// write/read
|
||||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
if len(w) != len(r) {
|
||||||
sam.GCLK_CLKCTRL_CLKEN)
|
return ErrTxInvalidSliceSize
|
||||||
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure configures a PWM pin for output.
|
spi.txrx24mhz(w, r)
|
||||||
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)
|
|
||||||
// Wait for synchronization
|
|
||||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use "Normal PWM" (single-slope PWM)
|
|
||||||
timer.WAVE.SetBits(sam.TCC_WAVE_WAVEGEN_NPWM)
|
|
||||||
// Wait for synchronization
|
|
||||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_WAVE) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the period (the number to count to (TOP) before resetting timer)
|
|
||||||
//TCC0->PER.reg = period;
|
|
||||||
timer.PER.Set(period)
|
|
||||||
// Wait for synchronization
|
|
||||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_PER) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set pin as output
|
|
||||||
sam.PORT.DIRSET0.Set(1 << uint8(pwm.Pin))
|
|
||||||
// Set pin to low
|
|
||||||
sam.PORT.OUTCLR0.Set(1 << uint8(pwm.Pin))
|
|
||||||
|
|
||||||
// Enable the port multiplexer for pin
|
|
||||||
pwm.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
|
|
||||||
|
|
||||||
// Connect TCCX timer to pin.
|
|
||||||
// we normally use the F channel aka ALT
|
|
||||||
pwmConfig := PinPWMAlt
|
|
||||||
|
|
||||||
// in the case of PA6 or PA7 we have to use E channel
|
|
||||||
if pwm.Pin == 6 || pwm.Pin == 7 {
|
|
||||||
pwmConfig = PinPWM
|
|
||||||
}
|
|
||||||
|
|
||||||
if pwm.Pin&1 > 0 {
|
|
||||||
// odd pin, so save the even pins
|
|
||||||
val := pwm.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
|
||||||
pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_PMUX0_PMUXO_Pos))
|
|
||||||
} else {
|
} else {
|
||||||
// even pin, so save the odd pins
|
switch {
|
||||||
val := pwm.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
case w == nil:
|
||||||
pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_PMUX0_PMUXE_Pos))
|
// read only, so write zero and read a result.
|
||||||
|
spi.rx(r)
|
||||||
|
case r == nil:
|
||||||
|
// write only
|
||||||
|
spi.tx(w)
|
||||||
|
|
||||||
|
default:
|
||||||
|
// write/read
|
||||||
|
if len(w) != len(r) {
|
||||||
|
return ErrTxInvalidSliceSize
|
||||||
|
}
|
||||||
|
|
||||||
|
spi.txrx(w, r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set turns on the duty cycle for a PWM pin using the provided value.
|
func (spi SPI) tx(tx []byte) {
|
||||||
func (pwm PWM) Set(value uint16) {
|
for i := 0; i < len(tx); i++ {
|
||||||
// figure out which TCCX timer for this pin
|
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
|
||||||
timer := pwm.getTimer()
|
}
|
||||||
if timer == nil {
|
spi.Bus.DATA.Set(uint32(tx[i]))
|
||||||
// The Configure call above cannot have succeeded, so simply ignore this
|
}
|
||||||
// error.
|
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_TXC) {
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable output
|
// read to clear RXC register
|
||||||
timer.CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
|
for spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||||
|
spi.Bus.DATA.Get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for synchronization
|
func (spi SPI) rx(rx []byte) {
|
||||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
|
spi.Bus.DATA.Set(0)
|
||||||
|
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i := 1; i < len(rx); i++ {
|
||||||
|
spi.Bus.DATA.Set(0)
|
||||||
|
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||||
|
}
|
||||||
|
rx[i-1] = byte(spi.Bus.DATA.Get())
|
||||||
|
}
|
||||||
|
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||||
|
}
|
||||||
|
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi SPI) txrx(tx, rx []byte) {
|
||||||
|
spi.Bus.DATA.Set(uint32(tx[0]))
|
||||||
|
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 1; i < len(rx); i++ {
|
||||||
|
spi.Bus.DATA.Set(uint32(tx[i]))
|
||||||
|
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||||
|
}
|
||||||
|
rx[i-1] = byte(spi.Bus.DATA.Get())
|
||||||
|
}
|
||||||
|
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||||
|
}
|
||||||
|
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
||||||
|
}
|
||||||
|
|
||||||
|
// tx24mhz is a special tx/rx function for CPU Clock 48 Mhz and SPI Freq 24 Mhz
|
||||||
|
func (spi SPI) tx24mhz(tx []byte) {
|
||||||
|
spi.Bus.DATA.Set(uint32(tx[0]))
|
||||||
|
device.Asm("nop")
|
||||||
|
device.Asm("nop")
|
||||||
|
device.Asm("nop")
|
||||||
|
device.Asm("nop")
|
||||||
|
device.Asm("nop")
|
||||||
|
device.Asm("nop")
|
||||||
|
|
||||||
|
for i := 1; i < len(tx); i++ {
|
||||||
|
spi.Bus.DATA.Set(uint32(tx[i]))
|
||||||
|
device.Asm("nop")
|
||||||
|
device.Asm("nop")
|
||||||
|
spi.Bus.DATA.Get()
|
||||||
|
}
|
||||||
|
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||||
|
}
|
||||||
|
spi.Bus.DATA.Get()
|
||||||
|
}
|
||||||
|
|
||||||
|
// txrx24mhz is a special tx/rx function for CPU Clock 48 Mhz and SPI Freq 24 Mhz
|
||||||
|
func (spi SPI) txrx24mhz(tx, rx []byte) {
|
||||||
|
spi.Bus.DATA.Set(uint32(tx[0]))
|
||||||
|
device.Asm("nop")
|
||||||
|
device.Asm("nop")
|
||||||
|
device.Asm("nop")
|
||||||
|
device.Asm("nop")
|
||||||
|
device.Asm("nop")
|
||||||
|
device.Asm("nop")
|
||||||
|
|
||||||
|
for i := 1; i < len(rx); i++ {
|
||||||
|
spi.Bus.DATA.Set(uint32(tx[i]))
|
||||||
|
device.Asm("nop")
|
||||||
|
device.Asm("nop")
|
||||||
|
rx[i-1] = byte(spi.Bus.DATA.Get())
|
||||||
|
}
|
||||||
|
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||||
|
}
|
||||||
|
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCC is one timer/counter peripheral, which consists of a counter and multiple
|
||||||
|
// output channels (that can be connected to actual pins). You can set the
|
||||||
|
// frequency using SetPeriod, but only for all the channels in this TCC
|
||||||
|
// peripheral at once.
|
||||||
|
type TCC sam.TCC_Type
|
||||||
|
|
||||||
|
// The SAM D21 has three TCC peripherals, which have PWM as one feature.
|
||||||
|
var (
|
||||||
|
TCC0 = (*TCC)(sam.TCC0)
|
||||||
|
TCC1 = (*TCC)(sam.TCC1)
|
||||||
|
TCC2 = (*TCC)(sam.TCC2)
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func (tcc *TCC) timer() *sam.TCC_Type {
|
||||||
|
return (*sam.TCC_Type)(tcc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure enables and configures this TCC.
|
||||||
|
func (tcc *TCC) Configure(config PWMConfig) error {
|
||||||
|
// Enable the clock source for this timer.
|
||||||
|
switch tcc.timer() {
|
||||||
|
case sam.TCC0:
|
||||||
|
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC0_)
|
||||||
|
// Use GCLK0 for TCC0/TCC1
|
||||||
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_TCC0_TCC1 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
|
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
|
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
||||||
|
}
|
||||||
|
case sam.TCC1:
|
||||||
|
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC1_)
|
||||||
|
// Use GCLK0 for TCC0/TCC1
|
||||||
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_TCC0_TCC1 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
|
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
|
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
||||||
|
}
|
||||||
|
case sam.TCC2:
|
||||||
|
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC2_)
|
||||||
|
// Use GCLK0 for TCC2/TC3
|
||||||
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_TCC2_TC3 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
|
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
|
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable timer (if it was enabled). This is necessary because
|
||||||
|
// tcc.setPeriod may want to change the prescaler bits in CTRLA, which is
|
||||||
|
// only allowed when the TCC is disabled.
|
||||||
|
tcc.timer().CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
|
||||||
|
|
||||||
|
// Use "Normal PWM" (single-slope PWM)
|
||||||
|
tcc.timer().WAVE.Set(sam.TCC_WAVE_WAVEGEN_NPWM)
|
||||||
|
|
||||||
|
// Wait for synchronization of all changed registers.
|
||||||
|
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the period and prescaler.
|
||||||
|
err := tcc.setPeriod(config.Period, true)
|
||||||
|
|
||||||
|
// Enable the timer.
|
||||||
|
tcc.timer().CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
|
||||||
|
|
||||||
|
// Wait for synchronization of all changed registers.
|
||||||
|
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return any error that might have occured in the tcc.setPeriod call.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPeriod updates the period of this TCC peripheral.
|
||||||
|
// To set a particular frequency, use the following formula:
|
||||||
|
//
|
||||||
|
// period = 1e9 / frequency
|
||||||
|
//
|
||||||
|
// If you use a period of 0, a period that works well for LEDs will be picked.
|
||||||
|
//
|
||||||
|
// SetPeriod will not change the prescaler, but also won't change the current
|
||||||
|
// value in any of the channels. This means that you may need to update the
|
||||||
|
// value for the particular channel.
|
||||||
|
//
|
||||||
|
// Note that you cannot pick any arbitrary period after the TCC peripheral has
|
||||||
|
// been configured. If you want to switch between frequencies, pick the lowest
|
||||||
|
// frequency (longest period) once when calling Configure and adjust the
|
||||||
|
// frequency here as needed.
|
||||||
|
func (tcc *TCC) SetPeriod(period uint64) error {
|
||||||
|
err := tcc.setPeriod(period, false)
|
||||||
|
if err == nil {
|
||||||
|
if tcc.Counter() >= tcc.Top() {
|
||||||
|
// When setting the timer to a shorter period, there is a chance
|
||||||
|
// that it passes the counter value and thus goes all the way to MAX
|
||||||
|
// before wrapping back to zero.
|
||||||
|
// To avoid this, reset the counter back to 0.
|
||||||
|
tcc.timer().COUNT.Set(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPeriod sets the period of this TCC, possibly updating the prescaler as
|
||||||
|
// well. The prescaler can only modified when the TCC is disabled, that is, in
|
||||||
|
// the Configure function.
|
||||||
|
func (tcc *TCC) setPeriod(period uint64, updatePrescaler bool) error {
|
||||||
|
var top uint64
|
||||||
|
if period == 0 {
|
||||||
|
// Make sure the TOP value is at 0xffff (enough for a 16-bit timer).
|
||||||
|
top = 0xffff
|
||||||
|
} else {
|
||||||
|
// The formula below calculates the following formula, optimized:
|
||||||
|
// period * (48e6 / 1e9)
|
||||||
|
// This assumes that the chip is running at the (default) 48MHz speed.
|
||||||
|
top = period * 6 / 125
|
||||||
|
}
|
||||||
|
|
||||||
|
maxTop := uint64(0xffffff)
|
||||||
|
if tcc.timer() == sam.TCC2 {
|
||||||
|
// TCC2 is a 16-bit timer, not a 24-bit timer.
|
||||||
|
maxTop = 0xffff
|
||||||
|
}
|
||||||
|
|
||||||
|
if updatePrescaler {
|
||||||
|
// This function was called during Configure(), with the timer disabled.
|
||||||
|
// Note that updating the prescaler can only happen while the peripheral
|
||||||
|
// is disabled.
|
||||||
|
var prescaler uint32
|
||||||
|
switch {
|
||||||
|
case top <= maxTop:
|
||||||
|
prescaler = sam.TCC_CTRLA_PRESCALER_DIV1
|
||||||
|
case top/2 <= maxTop:
|
||||||
|
prescaler = sam.TCC_CTRLA_PRESCALER_DIV2
|
||||||
|
top = top / 2
|
||||||
|
case top/4 <= maxTop:
|
||||||
|
prescaler = sam.TCC_CTRLA_PRESCALER_DIV4
|
||||||
|
top = top / 4
|
||||||
|
case top/8 <= maxTop:
|
||||||
|
prescaler = sam.TCC_CTRLA_PRESCALER_DIV8
|
||||||
|
top = top / 8
|
||||||
|
case top/16 <= maxTop:
|
||||||
|
prescaler = sam.TCC_CTRLA_PRESCALER_DIV16
|
||||||
|
top = top / 16
|
||||||
|
case top/64 <= maxTop:
|
||||||
|
prescaler = sam.TCC_CTRLA_PRESCALER_DIV64
|
||||||
|
top = top / 64
|
||||||
|
case top/256 <= maxTop:
|
||||||
|
prescaler = sam.TCC_CTRLA_PRESCALER_DIV256
|
||||||
|
top = top / 256
|
||||||
|
case top/1024 <= maxTop:
|
||||||
|
prescaler = sam.TCC_CTRLA_PRESCALER_DIV1024
|
||||||
|
top = top / 1024
|
||||||
|
default:
|
||||||
|
return ErrPWMPeriodTooLong
|
||||||
|
}
|
||||||
|
tcc.timer().CTRLA.Set((tcc.timer().CTRLA.Get() &^ sam.TCC_CTRLA_PRESCALER_Msk) | (prescaler << sam.TCC_CTRLA_PRESCALER_Pos))
|
||||||
|
} else {
|
||||||
|
// Do not update the prescaler, but use the already-configured
|
||||||
|
// prescaler. This is the normal SetPeriod case, where the prescaler
|
||||||
|
// must not be changed.
|
||||||
|
prescaler := (tcc.timer().CTRLA.Get() & sam.TCC_CTRLA_PRESCALER_Msk) >> sam.TCC_CTRLA_PRESCALER_Pos
|
||||||
|
switch prescaler {
|
||||||
|
case sam.TCC_CTRLA_PRESCALER_DIV1:
|
||||||
|
top /= 1 // no-op
|
||||||
|
case sam.TCC_CTRLA_PRESCALER_DIV2:
|
||||||
|
top /= 2
|
||||||
|
case sam.TCC_CTRLA_PRESCALER_DIV4:
|
||||||
|
top /= 4
|
||||||
|
case sam.TCC_CTRLA_PRESCALER_DIV8:
|
||||||
|
top /= 8
|
||||||
|
case sam.TCC_CTRLA_PRESCALER_DIV16:
|
||||||
|
top /= 16
|
||||||
|
case sam.TCC_CTRLA_PRESCALER_DIV64:
|
||||||
|
top /= 64
|
||||||
|
case sam.TCC_CTRLA_PRESCALER_DIV256:
|
||||||
|
top /= 256
|
||||||
|
case sam.TCC_CTRLA_PRESCALER_DIV1024:
|
||||||
|
top /= 1024
|
||||||
|
default:
|
||||||
|
// unreachable
|
||||||
|
}
|
||||||
|
if top > maxTop {
|
||||||
|
return ErrPWMPeriodTooLong
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the period (the counter top).
|
||||||
|
tcc.timer().PER.Set(uint32(top) - 1)
|
||||||
|
|
||||||
|
// Wait for synchronization of CTRLA.PRESCALER and PER registers.
|
||||||
|
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top returns the current counter top, for use in duty cycle calculation. It
|
||||||
|
// will only change with a call to Configure or SetPeriod, otherwise it is
|
||||||
|
// constant.
|
||||||
|
//
|
||||||
|
// The value returned here is hardware dependent. In general, it's best to treat
|
||||||
|
// it as an opaque value that can be divided by some number and passed to Set
|
||||||
|
// (see Set documentation for more information).
|
||||||
|
func (tcc *TCC) Top() uint32 {
|
||||||
|
return tcc.timer().PER.Get() + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Counter returns the current counter value of the timer in this TCC
|
||||||
|
// peripheral. It may be useful for debugging.
|
||||||
|
func (tcc *TCC) Counter() uint32 {
|
||||||
|
tcc.timer().CTRLBSET.Set(sam.TCC_CTRLBSET_CMD_READSYNC << sam.TCC_CTRLBSET_CMD_Pos)
|
||||||
|
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||||
|
}
|
||||||
|
return tcc.timer().COUNT.Get()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some constans to make pinTimerMapping below easier to read.
|
||||||
|
const (
|
||||||
|
pinTCC0 = 1
|
||||||
|
pinTCC1 = 2
|
||||||
|
pinTCC2 = 3
|
||||||
|
pinTimerCh0 = 0 << 3
|
||||||
|
pinTimerCh2 = 1 << 3
|
||||||
|
pinTCC0Ch0 = pinTCC0 | pinTimerCh0
|
||||||
|
pinTCC0Ch2 = pinTCC0 | pinTimerCh2
|
||||||
|
pinTCC1Ch0 = pinTCC1 | pinTimerCh0
|
||||||
|
pinTCC1Ch2 = pinTCC1 | pinTimerCh2
|
||||||
|
pinTCC2Ch0 = pinTCC2 | pinTimerCh0
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mapping from pin number to TCC peripheral and channel using a special
|
||||||
|
// encoding. Note that only TCC0-TCC2 are included, not TC3 and up.
|
||||||
|
// Every byte is split in two nibbles where the low nibble describes PinTCC and
|
||||||
|
// the high nibble describes PinTCCAlt. Within a nibble, there is one bit that
|
||||||
|
// indicates Ch0/Ch1 or Ch2/Ch3, and three other bits that contain the TCC
|
||||||
|
// peripheral number plus one (to distinguish between TCC0Ch0 and 0).
|
||||||
|
//
|
||||||
|
// The encoding can be so compact because all pins are configured in pairs, so
|
||||||
|
// if you know PA00 you can infer the configuration of PA01. And only channel 0
|
||||||
|
// or 2 need to be included (taking up just one bit), because channel 0 and 2
|
||||||
|
// are only ever used on odd pins and channel 1 and 3 on even pins, again using
|
||||||
|
// the pin pair pattern to reduce the amount of information needed to be stored.
|
||||||
|
//
|
||||||
|
// Datasheet: https://cdn.sparkfun.com/datasheets/Dev/Arduino/Boards/Atmel-42181-SAM-D21_Datasheet.pdf
|
||||||
|
var pinTimerMapping = [...]uint8{
|
||||||
|
// page 21
|
||||||
|
PA00 / 2: pinTCC2Ch0 | 0,
|
||||||
|
PA04 / 2: pinTCC0Ch0 | 0,
|
||||||
|
PA06 / 2: pinTCC1Ch0 | 0,
|
||||||
|
PA08 / 2: pinTCC0Ch0 | pinTCC1Ch2<<4,
|
||||||
|
PA10 / 2: pinTCC1Ch0 | pinTCC0Ch2<<4,
|
||||||
|
// page 22
|
||||||
|
PB10 / 2: 0 | pinTCC0Ch0<<4,
|
||||||
|
PB12 / 2: 0 | pinTCC0Ch2<<4,
|
||||||
|
PA12 / 2: pinTCC2Ch0 | pinTCC0Ch2<<4,
|
||||||
|
PA14 / 2: 0 | pinTCC0Ch0<<4,
|
||||||
|
PA16 / 2: pinTCC2Ch0 | pinTCC0Ch2<<4,
|
||||||
|
PA18 / 2: 0 | pinTCC0Ch2<<4,
|
||||||
|
PB16 / 2: 0 | pinTCC0Ch0<<4,
|
||||||
|
PA20 / 2: 0 | pinTCC0Ch2<<4,
|
||||||
|
PA22 / 2: 0 | pinTCC0Ch0<<4,
|
||||||
|
PA24 / 2: 0 | pinTCC1Ch2<<4,
|
||||||
|
// page 23
|
||||||
|
PA30 / 2: 0 | pinTCC1Ch0<<4,
|
||||||
|
PB30 / 2: pinTCC0Ch0 | pinTCC1Ch2<<4,
|
||||||
|
}
|
||||||
|
|
||||||
|
// findPinPadMapping returns the pin mode (PinTCC or PinTCCAlt) and the channel
|
||||||
|
// number for a given timer and pin. A zero PinMode is returned if no mapping
|
||||||
|
// could be found.
|
||||||
|
func findPinTimerMapping(timer uint8, pin Pin) (PinMode, uint8) {
|
||||||
|
mapping := pinTimerMapping[pin/2]
|
||||||
|
// evenChannel below indicates the channel 0 or 2, for the even part of the
|
||||||
|
// pin pair. The next pin will also have the next channel (1 or 3).
|
||||||
|
if mapping&0x07 == timer+1 {
|
||||||
|
// PWM output is on peripheral function E.
|
||||||
|
evenChannel := ((mapping >> 3) & 1) * 2
|
||||||
|
return PinTCC, evenChannel + uint8(pin&1)
|
||||||
|
}
|
||||||
|
if (mapping&0x70)>>4 == timer+1 {
|
||||||
|
// PWM output is on peripheral function F.
|
||||||
|
evenChannel := ((mapping >> 7) & 1) * 2
|
||||||
|
return PinTCCAlt, evenChannel + uint8(pin&1)
|
||||||
|
}
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel returns a PWM channel for the given pin. Note that one channel may be
|
||||||
|
// shared between multiple pins, and so will have the same duty cycle. If this
|
||||||
|
// is not desirable, look for a different TCC peripheral or consider using a
|
||||||
|
// different pin.
|
||||||
|
func (tcc *TCC) Channel(pin Pin) (uint8, error) {
|
||||||
|
var pinMode PinMode
|
||||||
|
var channel uint8
|
||||||
|
switch tcc.timer() {
|
||||||
|
case sam.TCC0:
|
||||||
|
pinMode, channel = findPinTimerMapping(0, pin)
|
||||||
|
case sam.TCC1:
|
||||||
|
pinMode, channel = findPinTimerMapping(1, pin)
|
||||||
|
case sam.TCC2:
|
||||||
|
pinMode, channel = findPinTimerMapping(2, pin)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pinMode == 0 {
|
||||||
|
// No pin could be found.
|
||||||
|
return 0, ErrInvalidOutputPin
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the port multiplexer for pin
|
||||||
|
pin.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
|
||||||
|
|
||||||
|
if pin&1 > 0 {
|
||||||
|
// odd pin, so save the even pins
|
||||||
|
val := pin.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||||
|
pin.setPMux(val | uint8(pinMode<<sam.PORT_PMUX0_PMUXO_Pos))
|
||||||
|
} else {
|
||||||
|
// even pin, so save the odd pins
|
||||||
|
val := pin.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||||
|
pin.setPMux(val | uint8(pinMode<<sam.PORT_PMUX0_PMUXE_Pos))
|
||||||
|
}
|
||||||
|
return channel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInverting sets whether to invert the output of this channel.
|
||||||
|
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
|
||||||
|
// the time and low for the rest. Inverting flips the output as if a NOT gate
|
||||||
|
// was placed at the output, meaning that the output would be 25% low and 75%
|
||||||
|
// high with a duty cycle of 25%.
|
||||||
|
func (tcc *TCC) SetInverting(channel uint8, inverting bool) {
|
||||||
|
if inverting {
|
||||||
|
tcc.timer().WAVE.SetBits(1 << (sam.TCC_WAVE_POL0_Pos + channel))
|
||||||
|
} else {
|
||||||
|
tcc.timer().WAVE.ClearBits(1 << (sam.TCC_WAVE_POL0_Pos + channel))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for synchronization of the WAVE register.
|
||||||
|
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set updates the channel value. This is used to control the channel duty
|
||||||
|
// cycle, in other words the fraction of time the channel output is high (or low
|
||||||
|
// when inverted). For example, to set it to a 25% duty cycle, use:
|
||||||
|
//
|
||||||
|
// tcc.Set(channel, tcc.Top() / 4)
|
||||||
|
//
|
||||||
|
// tcc.Set(channel, 0) will set the output to low and tcc.Set(channel,
|
||||||
|
// tcc.Top()) will set the output to high, assuming the output isn't inverted.
|
||||||
|
func (tcc *TCC) Set(channel uint8, value uint32) {
|
||||||
// Set PWM signal to output duty cycle
|
// Set PWM signal to output duty cycle
|
||||||
pwm.setChannel(timer, uint32(value))
|
switch channel {
|
||||||
|
case 0:
|
||||||
// Wait for synchronization on all channels
|
tcc.timer().CC0.Set(value)
|
||||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0 |
|
case 1:
|
||||||
sam.TCC_SYNCBUSY_CC1 |
|
tcc.timer().CC1.Set(value)
|
||||||
sam.TCC_SYNCBUSY_CC2 |
|
case 2:
|
||||||
sam.TCC_SYNCBUSY_CC3) {
|
tcc.timer().CC2.Set(value)
|
||||||
}
|
case 3:
|
||||||
|
tcc.timer().CC3.Set(value)
|
||||||
// enable
|
|
||||||
timer.CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
|
|
||||||
// Wait for synchronization
|
|
||||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPMux returns the value for the correct PMUX register for this pin.
|
|
||||||
func (pwm PWM) getPMux() uint8 {
|
|
||||||
return pwm.Pin.getPMux()
|
|
||||||
}
|
|
||||||
|
|
||||||
// setPMux sets the value for the correct PMUX register for this pin.
|
|
||||||
func (pwm PWM) setPMux(val uint8) {
|
|
||||||
pwm.Pin.setPMux(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPinCfg returns the value for the correct PINCFG register for this pin.
|
|
||||||
func (pwm PWM) getPinCfg() uint8 {
|
|
||||||
return pwm.Pin.getPinCfg()
|
|
||||||
}
|
|
||||||
|
|
||||||
// setPinCfg sets the value for the correct PINCFG register for this pin.
|
|
||||||
func (pwm PWM) setPinCfg(val uint8) {
|
|
||||||
pwm.Pin.setPinCfg(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getTimer returns the timer to be used for PWM on this pin
|
|
||||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
|
||||||
switch pwm.Pin {
|
|
||||||
case 6:
|
|
||||||
return sam.TCC1
|
|
||||||
case 7:
|
|
||||||
return sam.TCC1
|
|
||||||
case 8:
|
|
||||||
return sam.TCC1
|
|
||||||
case 9:
|
|
||||||
return sam.TCC1
|
|
||||||
case 14:
|
|
||||||
return sam.TCC0
|
|
||||||
case 15:
|
|
||||||
return sam.TCC0
|
|
||||||
case 16:
|
|
||||||
return sam.TCC0
|
|
||||||
case 17:
|
|
||||||
return sam.TCC0
|
|
||||||
case 18:
|
|
||||||
return sam.TCC0
|
|
||||||
case 19:
|
|
||||||
return sam.TCC0
|
|
||||||
case 20:
|
|
||||||
return sam.TCC0
|
|
||||||
case 21:
|
|
||||||
return sam.TCC0
|
|
||||||
default:
|
default:
|
||||||
return nil // not supported on this pin
|
// invalid PWM channel, ignore.
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// setChannel sets the value for the correct channel for PWM on this pin
|
// Wait for synchronization on all channels (or anything in this peripheral,
|
||||||
func (pwm PWM) setChannel(timer *sam.TCC_Type, val uint32) {
|
// really).
|
||||||
switch pwm.Pin {
|
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||||
case 6:
|
|
||||||
timer.CC0.Set(val)
|
|
||||||
case 7:
|
|
||||||
timer.CC1.Set(val)
|
|
||||||
case 8:
|
|
||||||
timer.CC0.Set(val)
|
|
||||||
case 9:
|
|
||||||
timer.CC1.Set(val)
|
|
||||||
case 14:
|
|
||||||
timer.CC0.Set(val)
|
|
||||||
case 15:
|
|
||||||
timer.CC1.Set(val)
|
|
||||||
case 16:
|
|
||||||
timer.CC2.Set(val)
|
|
||||||
case 17:
|
|
||||||
timer.CC3.Set(val)
|
|
||||||
case 18:
|
|
||||||
timer.CC2.Set(val)
|
|
||||||
case 19:
|
|
||||||
timer.CC3.Set(val)
|
|
||||||
case 20:
|
|
||||||
timer.CC2.Set(val)
|
|
||||||
case 21:
|
|
||||||
timer.CC3.Set(val)
|
|
||||||
default:
|
|
||||||
return // not supported on this pin
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1490,6 +1818,7 @@ func (usbcdc *USBCDC) Flush() error {
|
|||||||
|
|
||||||
if usbcdc.waitTxc {
|
if usbcdc.waitTxc {
|
||||||
// waiting for the next flush(), because the transmission is not complete
|
// waiting for the next flush(), because the transmission is not complete
|
||||||
|
usbcdc.waitTxcRetryCount++
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
usbcdc.waitTxc = true
|
usbcdc.waitTxc = true
|
||||||
@@ -1544,7 +1873,7 @@ func (usbcdc *USBCDC) WriteByte(c byte) error {
|
|||||||
mask := interrupt.Disable()
|
mask := interrupt.Disable()
|
||||||
UART0.waitTxc = false
|
UART0.waitTxc = false
|
||||||
UART0.waitTxcRetryCount = 0
|
UART0.waitTxcRetryCount = 0
|
||||||
usbcdc.TxIdx.Set(0)
|
UART0.TxIdx.Set(0)
|
||||||
usbLineInfo.lineState = 0
|
usbLineInfo.lineState = 0
|
||||||
interrupt.Restore(mask)
|
interrupt.Restore(mask)
|
||||||
break
|
break
|
||||||
@@ -1712,6 +2041,7 @@ func handleUSB(intr interrupt.Interrupt) {
|
|||||||
|
|
||||||
// Start of frame
|
// Start of frame
|
||||||
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
||||||
|
UART0.Flush()
|
||||||
// if you want to blink LED showing traffic, this would be the place...
|
// if you want to blink LED showing traffic, this would be the place...
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1775,13 +2105,7 @@ func handleUSB(intr interrupt.Interrupt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if i == usb_CDC_ENDPOINT_IN && UART0.waitTxc {
|
|
||||||
UART0.waitTxcRetryCount++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UART0.Flush()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initEndpoint(ep, config uint32) {
|
func initEndpoint(ep, config uint32) {
|
||||||
|
|||||||
+330
-240
@@ -40,9 +40,9 @@ const (
|
|||||||
PinInput PinMode = 15
|
PinInput PinMode = 15
|
||||||
PinInputPullup PinMode = 16
|
PinInputPullup PinMode = 16
|
||||||
PinOutput PinMode = 17
|
PinOutput PinMode = 17
|
||||||
PinPWME PinMode = PinTimer
|
PinTCCE PinMode = PinTimer
|
||||||
PinPWMF PinMode = PinTimerAlt
|
PinTCCF PinMode = PinTimerAlt
|
||||||
PinPWMG PinMode = PinTCCPDEC
|
PinTCCG PinMode = PinTCCPDEC
|
||||||
PinInputPulldown PinMode = 18
|
PinInputPulldown PinMode = 18
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1104,7 +1104,7 @@ const (
|
|||||||
const i2cTimeout = 1000
|
const i2cTimeout = 1000
|
||||||
|
|
||||||
// Configure is intended to setup the I2C interface.
|
// Configure is intended to setup the I2C interface.
|
||||||
func (i2c I2C) Configure(config I2CConfig) error {
|
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||||
// Default I2C bus speed is 100 kHz.
|
// Default I2C bus speed is 100 kHz.
|
||||||
if config.Frequency == 0 {
|
if config.Frequency == 0 {
|
||||||
config.Frequency = TWI_FREQ_100KHZ
|
config.Frequency = TWI_FREQ_100KHZ
|
||||||
@@ -1163,7 +1163,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetBaudRate sets the communication speed for the I2C.
|
// SetBaudRate sets the communication speed for the I2C.
|
||||||
func (i2c I2C) SetBaudRate(br uint32) {
|
func (i2c *I2C) SetBaudRate(br uint32) {
|
||||||
// Synchronous arithmetic baudrate, via Adafruit SAMD51 implementation:
|
// Synchronous arithmetic baudrate, via Adafruit SAMD51 implementation:
|
||||||
// sercom->I2CM.BAUD.bit.BAUD = SERCOM_FREQ_REF / ( 2 * baudrate) - 1 ;
|
// sercom->I2CM.BAUD.bit.BAUD = SERCOM_FREQ_REF / ( 2 * baudrate) - 1 ;
|
||||||
baud := SERCOM_FREQ_REF/(2*br) - 1
|
baud := SERCOM_FREQ_REF/(2*br) - 1
|
||||||
@@ -1173,7 +1173,7 @@ func (i2c I2C) SetBaudRate(br uint32) {
|
|||||||
// Tx does a single I2C transaction at the specified address.
|
// Tx does a single I2C transaction at the specified address.
|
||||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||||
var err error
|
var err error
|
||||||
if len(w) != 0 {
|
if len(w) != 0 {
|
||||||
// send start/address for write
|
// send start/address for write
|
||||||
@@ -1250,7 +1250,7 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteByte writes a single byte to the I2C bus.
|
// WriteByte writes a single byte to the I2C bus.
|
||||||
func (i2c I2C) WriteByte(data byte) error {
|
func (i2c *I2C) WriteByte(data byte) error {
|
||||||
// Send data byte
|
// Send data byte
|
||||||
i2c.Bus.DATA.Set(data)
|
i2c.Bus.DATA.Set(data)
|
||||||
|
|
||||||
@@ -1275,7 +1275,7 @@ func (i2c I2C) WriteByte(data byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sendAddress sends the address and start signal
|
// sendAddress sends the address and start signal
|
||||||
func (i2c I2C) sendAddress(address uint16, write bool) error {
|
func (i2c *I2C) sendAddress(address uint16, write bool) error {
|
||||||
data := (address << 1)
|
data := (address << 1)
|
||||||
if !write {
|
if !write {
|
||||||
data |= 1 // set read flag
|
data |= 1 // set read flag
|
||||||
@@ -1295,7 +1295,7 @@ func (i2c I2C) sendAddress(address uint16, write bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i2c I2C) signalStop() error {
|
func (i2c *I2C) signalStop() error {
|
||||||
i2c.Bus.CTRLB.SetBits(wireCmdStop << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Stop command
|
i2c.Bus.CTRLB.SetBits(wireCmdStop << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Stop command
|
||||||
timeout := i2cTimeout
|
timeout := i2cTimeout
|
||||||
for i2c.Bus.SYNCBUSY.HasBits(sam.SERCOM_I2CM_SYNCBUSY_SYSOP) {
|
for i2c.Bus.SYNCBUSY.HasBits(sam.SERCOM_I2CM_SYNCBUSY_SYSOP) {
|
||||||
@@ -1307,7 +1307,7 @@ func (i2c I2C) signalStop() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i2c I2C) signalRead() error {
|
func (i2c *I2C) signalRead() error {
|
||||||
i2c.Bus.CTRLB.SetBits(wireCmdRead << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Read command
|
i2c.Bus.CTRLB.SetBits(wireCmdRead << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Read command
|
||||||
timeout := i2cTimeout
|
timeout := i2cTimeout
|
||||||
for i2c.Bus.SYNCBUSY.HasBits(sam.SERCOM_I2CM_SYNCBUSY_SYSOP) {
|
for i2c.Bus.SYNCBUSY.HasBits(sam.SERCOM_I2CM_SYNCBUSY_SYSOP) {
|
||||||
@@ -1319,7 +1319,7 @@ func (i2c I2C) signalRead() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i2c I2C) readByte() byte {
|
func (i2c *I2C) readByte() byte {
|
||||||
for !i2c.Bus.INTFLAG.HasBits(sam.SERCOM_I2CM_INTFLAG_SB) {
|
for !i2c.Bus.INTFLAG.HasBits(sam.SERCOM_I2CM_INTFLAG_SB) {
|
||||||
}
|
}
|
||||||
return byte(i2c.Bus.DATA.Get())
|
return byte(i2c.Bus.DATA.Get())
|
||||||
@@ -1570,256 +1570,350 @@ const (
|
|||||||
QSPI_DATA3 = PA11
|
QSPI_DATA3 = PA11
|
||||||
)
|
)
|
||||||
|
|
||||||
// PWM
|
// TCC is one timer peripheral, which consists of a counter and multiple output
|
||||||
const period = 0xFFFF
|
// channels (that can be connected to actual pins). You can set the frequency
|
||||||
|
// using SetPeriod, but only for all the channels in this timer peripheral at
|
||||||
|
// once.
|
||||||
|
type TCC sam.TCC_Type
|
||||||
|
|
||||||
// Configure configures a PWM pin for output.
|
//go:inline
|
||||||
func (pwm PWM) Configure() error {
|
func (tcc *TCC) timer() *sam.TCC_Type {
|
||||||
// Set pin as output
|
return (*sam.TCC_Type)(tcc)
|
||||||
sam.PORT.GROUP[0].DIRSET.Set(1 << uint8(pwm.Pin))
|
}
|
||||||
// Set pin to low
|
|
||||||
sam.PORT.GROUP[0].OUTCLR.Set(1 << uint8(pwm.Pin))
|
|
||||||
|
|
||||||
// Enable the port multiplexer for pin
|
// Configure enables and configures this TCC.
|
||||||
pwm.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN)
|
func (tcc *TCC) Configure(config PWMConfig) error {
|
||||||
|
// Enable the TCC clock to be able to use the TCC.
|
||||||
|
tcc.configureClock()
|
||||||
|
|
||||||
// Connect timer/mux to pin.
|
// Disable timer (if it was enabled). This is necessary because
|
||||||
pwmConfig := pwm.getMux()
|
// tcc.setPeriod may want to change the prescaler bits in CTRLA, which is
|
||||||
|
// only allowed when the TCC is disabled.
|
||||||
if pwm.Pin&1 > 0 {
|
tcc.timer().CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
|
||||||
// odd pin, so save the even pins
|
|
||||||
val := pwm.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
|
|
||||||
pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_GROUP_PMUX_PMUXO_Pos))
|
|
||||||
} else {
|
|
||||||
// even pin, so save the odd pins
|
|
||||||
val := pwm.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
|
|
||||||
pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_GROUP_PMUX_PMUXE_Pos))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
// Wait for synchronization
|
|
||||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set prescaler to 1/256
|
|
||||||
// TCCx->CTRLA.reg = TCC_CTRLA_PRESCALER_DIV256 | TCC_CTRLA_PRESCSYNC_GCLK;
|
|
||||||
timer.CTRLA.SetBits(sam.TCC_CTRLA_PRESCALER_DIV256 | sam.TCC_CTRLA_PRESCSYNC_GCLK)
|
|
||||||
|
|
||||||
// Use "Normal PWM" (single-slope PWM)
|
// Use "Normal PWM" (single-slope PWM)
|
||||||
timer.WAVE.SetBits(sam.TCC_WAVE_WAVEGEN_NPWM)
|
tcc.timer().WAVE.Set(sam.TCC_WAVE_WAVEGEN_NPWM)
|
||||||
// Wait for synchronization
|
|
||||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_WAVE) {
|
// Wait for synchronization of all changed registers.
|
||||||
|
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// while (TCCx->SYNCBUSY.bit.CC0 || TCCx->SYNCBUSY.bit.CC1);
|
// Set the period and prescaler.
|
||||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
|
err := tcc.setPeriod(config.Period, true)
|
||||||
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
|
|
||||||
|
// Enable the timer.
|
||||||
|
tcc.timer().CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
|
||||||
|
|
||||||
|
// Wait for synchronization of all changed registers.
|
||||||
|
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the initial value
|
// Return any error that might have occured in the tcc.setPeriod call.
|
||||||
// TCCx->CC[tcChannel].reg = (uint32_t) value;
|
return err
|
||||||
pwm.setChannel(timer, 0)
|
}
|
||||||
|
|
||||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
|
// SetPeriod updates the period of this TCC peripheral.
|
||||||
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
|
// To set a particular frequency, use the following formula:
|
||||||
|
//
|
||||||
|
// period = 1e9 / frequency
|
||||||
|
//
|
||||||
|
// If you use a period of 0, a period that works well for LEDs will be picked.
|
||||||
|
//
|
||||||
|
// SetPeriod will not change the prescaler, but also won't change the current
|
||||||
|
// value in any of the channels. This means that you may need to update the
|
||||||
|
// value for the particular channel.
|
||||||
|
//
|
||||||
|
// Note that you cannot pick any arbitrary period after the TCC peripheral has
|
||||||
|
// been configured. If you want to switch between frequencies, pick the lowest
|
||||||
|
// frequency (longest period) once when calling Configure and adjust the
|
||||||
|
// frequency here as needed.
|
||||||
|
func (tcc *TCC) SetPeriod(period uint64) error {
|
||||||
|
return tcc.setPeriod(period, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPeriod sets the period of this TCC, possibly updating the prescaler as
|
||||||
|
// well. The prescaler can only modified when the TCC is disabled, that is, in
|
||||||
|
// the Configure function.
|
||||||
|
func (tcc *TCC) setPeriod(period uint64, updatePrescaler bool) error {
|
||||||
|
var top uint64
|
||||||
|
if period == 0 {
|
||||||
|
// Make sure the TOP value is at 0xffff (enough for a 16-bit timer).
|
||||||
|
top = 0xffff
|
||||||
|
} else {
|
||||||
|
// The formula below calculates the following formula, optimized:
|
||||||
|
// period * (120e6 / 1e9)
|
||||||
|
// This assumes that the chip is running from generic clock generator 0
|
||||||
|
// at 120MHz.
|
||||||
|
top = period * 3 / 25
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the period (the number to count to (TOP) before resetting timer)
|
maxTop := uint64(0xffff)
|
||||||
//TCC0->PER.reg = period;
|
if tcc.timer() == sam.TCC0 || tcc.timer() == sam.TCC1 {
|
||||||
timer.PER.Set(period)
|
// Only TCC0 and TCC1 are 24-bit timers, the rest are 16-bit.
|
||||||
// Wait for synchronization
|
maxTop = 0xffffff
|
||||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_PER) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable timer
|
if updatePrescaler {
|
||||||
timer.CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
|
// This function was called during Configure(), with the timer disabled.
|
||||||
// Wait for synchronization
|
// Note that updating the prescaler can only happen while the peripheral
|
||||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
|
// is disabled.
|
||||||
|
var prescaler uint32
|
||||||
|
switch {
|
||||||
|
case top <= maxTop:
|
||||||
|
prescaler = sam.TCC_CTRLA_PRESCALER_DIV1
|
||||||
|
case top/2 <= maxTop:
|
||||||
|
prescaler = sam.TCC_CTRLA_PRESCALER_DIV2
|
||||||
|
top = top / 2
|
||||||
|
case top/4 <= maxTop:
|
||||||
|
prescaler = sam.TCC_CTRLA_PRESCALER_DIV4
|
||||||
|
top = top / 4
|
||||||
|
case top/8 <= maxTop:
|
||||||
|
prescaler = sam.TCC_CTRLA_PRESCALER_DIV8
|
||||||
|
top = top / 8
|
||||||
|
case top/16 <= maxTop:
|
||||||
|
prescaler = sam.TCC_CTRLA_PRESCALER_DIV16
|
||||||
|
top = top / 16
|
||||||
|
case top/64 <= maxTop:
|
||||||
|
prescaler = sam.TCC_CTRLA_PRESCALER_DIV64
|
||||||
|
top = top / 64
|
||||||
|
case top/256 <= maxTop:
|
||||||
|
prescaler = sam.TCC_CTRLA_PRESCALER_DIV256
|
||||||
|
top = top / 256
|
||||||
|
case top/1024 <= maxTop:
|
||||||
|
prescaler = sam.TCC_CTRLA_PRESCALER_DIV1024
|
||||||
|
top = top / 1024
|
||||||
|
default:
|
||||||
|
return ErrPWMPeriodTooLong
|
||||||
|
}
|
||||||
|
tcc.timer().CTRLA.Set((tcc.timer().CTRLA.Get() &^ sam.TCC_CTRLA_PRESCALER_Msk) | (prescaler << sam.TCC_CTRLA_PRESCALER_Pos))
|
||||||
|
} else {
|
||||||
|
// Do not update the prescaler, but use the already-configured
|
||||||
|
// prescaler. This is the normal SetPeriod case, where the prescaler
|
||||||
|
// must not be changed.
|
||||||
|
prescaler := (tcc.timer().CTRLA.Get() & sam.TCC_CTRLA_PRESCALER_Msk) >> sam.TCC_CTRLA_PRESCALER_Pos
|
||||||
|
switch prescaler {
|
||||||
|
case sam.TCC_CTRLA_PRESCALER_DIV1:
|
||||||
|
top /= 1 // no-op
|
||||||
|
case sam.TCC_CTRLA_PRESCALER_DIV2:
|
||||||
|
top /= 2
|
||||||
|
case sam.TCC_CTRLA_PRESCALER_DIV4:
|
||||||
|
top /= 4
|
||||||
|
case sam.TCC_CTRLA_PRESCALER_DIV8:
|
||||||
|
top /= 8
|
||||||
|
case sam.TCC_CTRLA_PRESCALER_DIV16:
|
||||||
|
top /= 16
|
||||||
|
case sam.TCC_CTRLA_PRESCALER_DIV64:
|
||||||
|
top /= 64
|
||||||
|
case sam.TCC_CTRLA_PRESCALER_DIV256:
|
||||||
|
top /= 256
|
||||||
|
case sam.TCC_CTRLA_PRESCALER_DIV1024:
|
||||||
|
top /= 1024
|
||||||
|
default:
|
||||||
|
// unreachable
|
||||||
|
}
|
||||||
|
if top > maxTop {
|
||||||
|
return ErrPWMPeriodTooLong
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the period (the counter top).
|
||||||
|
tcc.timer().PER.Set(uint32(top) - 1)
|
||||||
|
|
||||||
|
// Wait for synchronization of CTRLA.PRESCALER and PER registers.
|
||||||
|
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set turns on the duty cycle for a PWM pin using the provided value.
|
// Top returns the current counter top, for use in duty cycle calculation. It
|
||||||
func (pwm PWM) Set(value uint16) {
|
// will only change with a call to Configure or SetPeriod, otherwise it is
|
||||||
// figure out which TCCX timer for this pin
|
// constant.
|
||||||
timer := pwm.getTimer()
|
//
|
||||||
if timer == nil {
|
// The value returned here is hardware dependent. In general, it's best to treat
|
||||||
// The Configure call above cannot have succeeded, so simply ignore this
|
// it as an opaque value that can be divided by some number and passed to
|
||||||
// error.
|
// tcc.Set (see tcc.Set for more information).
|
||||||
return
|
func (tcc *TCC) Top() uint32 {
|
||||||
|
return tcc.timer().PER.Get() + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Counter returns the current counter value of the timer in this TCC
|
||||||
|
// peripheral. It may be useful for debugging.
|
||||||
|
func (tcc *TCC) Counter() uint32 {
|
||||||
|
tcc.timer().CTRLBSET.Set(sam.TCC_CTRLBSET_CMD_READSYNC << sam.TCC_CTRLBSET_CMD_Pos)
|
||||||
|
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||||
|
}
|
||||||
|
return tcc.timer().COUNT.Get()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constants that encode a TCC number and WO number together in a single byte.
|
||||||
|
const (
|
||||||
|
pinTCC0 = 1 << 4 // keep the value 0 usable as "no value"
|
||||||
|
pinTCC1 = 2 << 4
|
||||||
|
pinTCC2 = 3 << 4
|
||||||
|
pinTCC3 = 4 << 4
|
||||||
|
pinTCC4 = 5 << 4
|
||||||
|
pinTCC0_0 = pinTCC0 | 0
|
||||||
|
pinTCC0_1 = pinTCC0 | 1
|
||||||
|
pinTCC0_2 = pinTCC0 | 2
|
||||||
|
pinTCC0_3 = pinTCC0 | 3
|
||||||
|
pinTCC0_4 = pinTCC0 | 4
|
||||||
|
pinTCC0_5 = pinTCC0 | 5
|
||||||
|
pinTCC0_6 = pinTCC0 | 6
|
||||||
|
pinTCC1_0 = pinTCC1 | 0
|
||||||
|
pinTCC1_2 = pinTCC1 | 2
|
||||||
|
pinTCC1_4 = pinTCC1 | 4
|
||||||
|
pinTCC1_6 = pinTCC1 | 6
|
||||||
|
pinTCC2_0 = pinTCC2 | 0
|
||||||
|
pinTCC2_2 = pinTCC2 | 2
|
||||||
|
pinTCC3_0 = pinTCC3 | 0
|
||||||
|
pinTCC4_0 = pinTCC4 | 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a copy of columns F and G (the TCC columns) of table 6-1 in the
|
||||||
|
// datasheet:
|
||||||
|
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507E.pdf
|
||||||
|
// For example, "TCC0/WO[2]" is converted to pinTCC0_2.
|
||||||
|
// Only the even pin numbers are stored here. The odd pin numbers are left out,
|
||||||
|
// because their PWM output can be determined from the even number: just add one
|
||||||
|
// to the wave output (WO) number.
|
||||||
|
var pinTimerMapping = [...]struct{ F, G uint8 }{
|
||||||
|
// page 33
|
||||||
|
PC04 / 2: {pinTCC0_0, 0},
|
||||||
|
PA08 / 2: {pinTCC0_0, pinTCC1_4},
|
||||||
|
PA10 / 2: {pinTCC0_2, pinTCC1_6},
|
||||||
|
PB10 / 2: {pinTCC0_4, pinTCC1_0},
|
||||||
|
PB12 / 2: {pinTCC3_0, pinTCC0_0},
|
||||||
|
PB14 / 2: {pinTCC4_0, pinTCC0_2},
|
||||||
|
PD08 / 2: {pinTCC0_1, 0},
|
||||||
|
PD10 / 2: {pinTCC0_3, 0},
|
||||||
|
PD12 / 2: {pinTCC0_5, 0},
|
||||||
|
PC10 / 2: {pinTCC0_0, pinTCC1_4},
|
||||||
|
// page 34
|
||||||
|
PC12 / 2: {pinTCC0_2, pinTCC1_6},
|
||||||
|
PC14 / 2: {pinTCC0_4, pinTCC1_0},
|
||||||
|
PA12 / 2: {pinTCC0_6, pinTCC1_2},
|
||||||
|
PA14 / 2: {pinTCC2_0, pinTCC1_2},
|
||||||
|
PA16 / 2: {pinTCC1_0, pinTCC0_4},
|
||||||
|
PA18 / 2: {pinTCC1_2, pinTCC0_6},
|
||||||
|
PC16 / 2: {pinTCC0_0, 0},
|
||||||
|
PC18 / 2: {pinTCC0_2, 0},
|
||||||
|
PC20 / 2: {pinTCC0_4, 0},
|
||||||
|
PC22 / 2: {pinTCC0_6, 0},
|
||||||
|
PD20 / 2: {pinTCC1_0, 0},
|
||||||
|
PB16 / 2: {pinTCC3_0, pinTCC0_4},
|
||||||
|
PB18 / 2: {pinTCC1_0, 0},
|
||||||
|
// page 35
|
||||||
|
PB20 / 2: {pinTCC1_2, 0},
|
||||||
|
PA20 / 2: {pinTCC1_4, pinTCC0_0},
|
||||||
|
PA22 / 2: {pinTCC1_6, pinTCC0_2},
|
||||||
|
PA24 / 2: {pinTCC2_2, 0},
|
||||||
|
PB26 / 2: {pinTCC1_2, 0},
|
||||||
|
PB28 / 2: {pinTCC1_4, 0},
|
||||||
|
PA30 / 2: {pinTCC2_0, 0},
|
||||||
|
// page 36
|
||||||
|
PB30 / 2: {pinTCC4_0, pinTCC0_6},
|
||||||
|
PB02 / 2: {pinTCC2_2, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
// findPinPadMapping returns the pin mode (PinTCCF or PinTCCG) and the channel
|
||||||
|
// number for a given timer and pin. A zero PinMode is returned if no mapping
|
||||||
|
// could be found.
|
||||||
|
func findPinTimerMapping(timer uint8, pin Pin) (PinMode, uint8) {
|
||||||
|
if int(pin/2) >= len(pinTimerMapping) {
|
||||||
|
return 0, 0 // invalid pin number
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for synchronization
|
mapping := pinTimerMapping[pin/2]
|
||||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CTRLB) {
|
|
||||||
}
|
// Check for column F in the datasheet.
|
||||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
|
if mapping.F>>4-1 == timer {
|
||||||
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
|
return PinTCCF, mapping.F&0x0f + uint8(pin)&1
|
||||||
}
|
}
|
||||||
|
|
||||||
// TCCx->CCBUF[tcChannel].reg = (uint32_t) value;
|
// Check for column G in the datasheet.
|
||||||
pwm.setChannelBuffer(timer, uint32(value))
|
if mapping.G>>4-1 == timer {
|
||||||
|
return PinTCCG, mapping.G&0x0f + uint8(pin)&1
|
||||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
|
|
||||||
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TCCx->CTRLBCLR.bit.LUPD = 1;
|
// Nothing found.
|
||||||
timer.CTRLBCLR.SetBits(sam.TCC_CTRLBCLR_LUPD)
|
return 0, 0
|
||||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CTRLB) {
|
}
|
||||||
|
|
||||||
|
// Channel returns a PWM channel for the given pin. Note that one channel may be
|
||||||
|
// shared between multiple pins, and so will have the same duty cycle. If this
|
||||||
|
// is not desirable, look for a different TCC or consider using a different pin.
|
||||||
|
func (tcc *TCC) Channel(pin Pin) (uint8, error) {
|
||||||
|
pinMode, woOutput := findPinTimerMapping(tcc.timerNum(), pin)
|
||||||
|
|
||||||
|
if pinMode == 0 {
|
||||||
|
// No pin could be found.
|
||||||
|
return 0, ErrInvalidOutputPin
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from waveform output to channel, assuming WEXCTRL.OTMX equals 0.
|
||||||
|
// See table 49-4 "Output Matrix Channel Pin Routing Configuration" on page
|
||||||
|
// 1829 of the datasheet.
|
||||||
|
// The number of channels varies by TCC instance, hence the need to switch
|
||||||
|
// over them. For TCC2-4 the number of channels is equal to the number of
|
||||||
|
// waveform outputs, so the WO number maps directly to the channel number.
|
||||||
|
// For TCC0 and TCC1 this is not the case so they will need some special
|
||||||
|
// handling.
|
||||||
|
channel := woOutput
|
||||||
|
switch tcc.timer() {
|
||||||
|
case sam.TCC0:
|
||||||
|
channel = woOutput % 6
|
||||||
|
case sam.TCC1:
|
||||||
|
channel = woOutput % 4
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the port multiplexer for pin
|
||||||
|
pin.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN)
|
||||||
|
|
||||||
|
// Connect timer/mux to pin.
|
||||||
|
if pin&1 > 0 {
|
||||||
|
// odd pin, so save the even pins
|
||||||
|
val := pin.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
|
||||||
|
pin.setPMux(val | uint8(pinMode<<sam.PORT_GROUP_PMUX_PMUXO_Pos))
|
||||||
|
} else {
|
||||||
|
// even pin, so save the odd pins
|
||||||
|
val := pin.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
|
||||||
|
pin.setPMux(val | uint8(pinMode<<sam.PORT_GROUP_PMUX_PMUXE_Pos))
|
||||||
|
}
|
||||||
|
|
||||||
|
return channel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInverting sets whether to invert the output of this channel.
|
||||||
|
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
|
||||||
|
// the time and low for the rest. Inverting flips the output as if a NOT gate
|
||||||
|
// was placed at the output, meaning that the output would be 25% low and 75%
|
||||||
|
// high with a duty cycle of 25%.
|
||||||
|
func (tcc *TCC) SetInverting(channel uint8, inverting bool) {
|
||||||
|
if inverting {
|
||||||
|
tcc.timer().WAVE.SetBits(1 << (sam.TCC_WAVE_POL0_Pos + channel))
|
||||||
|
} else {
|
||||||
|
tcc.timer().WAVE.ClearBits(1 << (sam.TCC_WAVE_POL0_Pos + channel))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for synchronization of the WAVE register.
|
||||||
|
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPMux returns the value for the correct PMUX register for this pin.
|
// Set updates the channel value. This is used to control the channel duty
|
||||||
func (pwm PWM) getPMux() uint8 {
|
// cycle, in other words the fraction of time the channel output is high (or low
|
||||||
return pwm.Pin.getPMux()
|
// when inverted). For example, to set it to a 25% duty cycle, use:
|
||||||
}
|
//
|
||||||
|
// tcc.Set(channel, tcc.Top() / 4)
|
||||||
// setPMux sets the value for the correct PMUX register for this pin.
|
//
|
||||||
func (pwm PWM) setPMux(val uint8) {
|
// tcc.Set(channel, 0) will set the output to low and tcc.Set(channel,
|
||||||
pwm.Pin.setPMux(val)
|
// tcc.Top()) will set the output to high, assuming the output isn't inverted.
|
||||||
}
|
func (tcc *TCC) Set(channel uint8, value uint32) {
|
||||||
|
// Update CCBUF, which provides double buffering. The update is applied on
|
||||||
// getPinCfg returns the value for the correct PINCFG register for this pin.
|
// the next cycle.
|
||||||
func (pwm PWM) getPinCfg() uint8 {
|
tcc.timer().CCBUF[channel].Set(value)
|
||||||
return pwm.Pin.getPinCfg()
|
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||||
}
|
|
||||||
|
|
||||||
// setPinCfg sets the value for the correct PINCFG register for this pin.
|
|
||||||
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(timer *sam.TCC_Type, val uint32) {
|
|
||||||
switch pwm.Pin {
|
|
||||||
case PA14:
|
|
||||||
timer.CC[0].Set(val)
|
|
||||||
case PA15:
|
|
||||||
timer.CC[1].Set(val)
|
|
||||||
case PA16:
|
|
||||||
timer.CC[0].Set(val)
|
|
||||||
case PA17:
|
|
||||||
timer.CC[1].Set(val)
|
|
||||||
case PA18:
|
|
||||||
timer.CC[2].Set(val)
|
|
||||||
case PA19:
|
|
||||||
timer.CC[3].Set(val)
|
|
||||||
case PA20:
|
|
||||||
timer.CC[0].Set(val)
|
|
||||||
case PA21:
|
|
||||||
timer.CC[1].Set(val)
|
|
||||||
case PA22:
|
|
||||||
timer.CC[2].Set(val)
|
|
||||||
case PA23:
|
|
||||||
timer.CC[3].Set(val)
|
|
||||||
case PB12:
|
|
||||||
timer.CC[0].Set(val)
|
|
||||||
case PB13:
|
|
||||||
timer.CC[1].Set(val)
|
|
||||||
case PB14:
|
|
||||||
timer.CC[0].Set(val)
|
|
||||||
case PB15:
|
|
||||||
timer.CC[1].Set(val)
|
|
||||||
case PB16:
|
|
||||||
timer.CC[4].Set(val)
|
|
||||||
case PB17:
|
|
||||||
timer.CC[5].Set(val)
|
|
||||||
case PB31:
|
|
||||||
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(timer *sam.TCC_Type, val uint32) {
|
|
||||||
switch pwm.Pin {
|
|
||||||
case PA14:
|
|
||||||
timer.CCBUF[0].Set(val)
|
|
||||||
case PA15:
|
|
||||||
timer.CCBUF[1].Set(val)
|
|
||||||
case PA16:
|
|
||||||
timer.CCBUF[0].Set(val)
|
|
||||||
case PA17:
|
|
||||||
timer.CCBUF[1].Set(val)
|
|
||||||
case PA18:
|
|
||||||
timer.CCBUF[2].Set(val)
|
|
||||||
case PA19:
|
|
||||||
timer.CCBUF[3].Set(val)
|
|
||||||
case PA20:
|
|
||||||
timer.CCBUF[0].Set(val)
|
|
||||||
case PA21:
|
|
||||||
timer.CCBUF[1].Set(val)
|
|
||||||
case PA22:
|
|
||||||
timer.CCBUF[2].Set(val)
|
|
||||||
case PA23:
|
|
||||||
timer.CCBUF[3].Set(val)
|
|
||||||
case PB12:
|
|
||||||
timer.CCBUF[0].Set(val)
|
|
||||||
case PB13:
|
|
||||||
timer.CCBUF[1].Set(val)
|
|
||||||
case PB14:
|
|
||||||
timer.CCBUF[0].Set(val)
|
|
||||||
case PB15:
|
|
||||||
timer.CCBUF[1].Set(val)
|
|
||||||
case PB16:
|
|
||||||
timer.CCBUF[4].Set(val)
|
|
||||||
case PB17:
|
|
||||||
timer.CCBUF[5].Set(val)
|
|
||||||
case PB31:
|
|
||||||
timer.CCBUF[1].Set(val)
|
|
||||||
default:
|
|
||||||
return // not supported on this pin
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getMux returns the pin mode mux to be used for PWM on this pin.
|
|
||||||
func (pwm PWM) getMux() PinMode {
|
|
||||||
switch pwm.Pin {
|
|
||||||
case PA14:
|
|
||||||
return PinPWMF
|
|
||||||
case PA15:
|
|
||||||
return PinPWMF
|
|
||||||
case PA16:
|
|
||||||
return PinPWMF
|
|
||||||
case PA17:
|
|
||||||
return PinPWMF
|
|
||||||
case PA18:
|
|
||||||
return PinPWMF
|
|
||||||
case PA19:
|
|
||||||
return PinPWMF
|
|
||||||
case PA20:
|
|
||||||
return PinPWMG
|
|
||||||
case PA21:
|
|
||||||
return PinPWMG
|
|
||||||
case PA22:
|
|
||||||
return PinPWMG
|
|
||||||
case PA23:
|
|
||||||
return PinPWMG
|
|
||||||
case PB12:
|
|
||||||
return PinPWMF
|
|
||||||
case PB13:
|
|
||||||
return PinPWMF
|
|
||||||
case PB14:
|
|
||||||
return PinPWMF
|
|
||||||
case PB15:
|
|
||||||
return PinPWMF
|
|
||||||
case PB16:
|
|
||||||
return PinPWMG
|
|
||||||
case PB17:
|
|
||||||
return PinPWMG
|
|
||||||
case PB31:
|
|
||||||
return PinPWMF
|
|
||||||
default:
|
|
||||||
return 0 // not supported on this pin
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1850,6 +1944,7 @@ func (usbcdc *USBCDC) Flush() error {
|
|||||||
|
|
||||||
if usbcdc.waitTxc {
|
if usbcdc.waitTxc {
|
||||||
// waiting for the next flush(), because the transmission is not complete
|
// waiting for the next flush(), because the transmission is not complete
|
||||||
|
usbcdc.waitTxcRetryCount++
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
usbcdc.waitTxc = true
|
usbcdc.waitTxc = true
|
||||||
@@ -1904,7 +1999,7 @@ func (usbcdc *USBCDC) WriteByte(c byte) error {
|
|||||||
mask := interrupt.Disable()
|
mask := interrupt.Disable()
|
||||||
UART0.waitTxc = false
|
UART0.waitTxc = false
|
||||||
UART0.waitTxcRetryCount = 0
|
UART0.waitTxcRetryCount = 0
|
||||||
usbcdc.TxIdx.Set(0)
|
UART0.TxIdx.Set(0)
|
||||||
usbLineInfo.lineState = 0
|
usbLineInfo.lineState = 0
|
||||||
interrupt.Restore(mask)
|
interrupt.Restore(mask)
|
||||||
break
|
break
|
||||||
@@ -2074,6 +2169,7 @@ func handleUSBIRQ(interrupt.Interrupt) {
|
|||||||
|
|
||||||
// Start of frame
|
// Start of frame
|
||||||
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
||||||
|
UART0.Flush()
|
||||||
// if you want to blink LED showing traffic, this would be the place...
|
// if you want to blink LED showing traffic, this would be the place...
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2137,13 +2233,7 @@ func handleUSBIRQ(interrupt.Interrupt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if i == usb_CDC_ENDPOINT_IN && UART0.waitTxc {
|
|
||||||
UART0.waitTxcRetryCount++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UART0.Flush()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initEndpoint(ep, config uint32) {
|
func initEndpoint(ep, config uint32) {
|
||||||
|
|||||||
@@ -11,43 +11,37 @@ import "device/sam"
|
|||||||
|
|
||||||
const HSRAM_SIZE = 0x00030000
|
const HSRAM_SIZE = 0x00030000
|
||||||
|
|
||||||
// InitPWM initializes the PWM interface.
|
// This chip has three TCC peripherals, which have PWM as one feature.
|
||||||
func InitPWM() {
|
var (
|
||||||
// turn on timer clocks used for PWM
|
TCC0 = (*TCC)(sam.TCC0)
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
TCC1 = (*TCC)(sam.TCC1)
|
||||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
TCC2 = (*TCC)(sam.TCC2)
|
||||||
|
)
|
||||||
|
|
||||||
//use clock generator 0
|
func (tcc *TCC) configureClock() {
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
// Turn on timer clocks used for TCC and use generic clock generator 0.
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
switch tcc.timer() {
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
case sam.TCC0:
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_)
|
||||||
}
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
case sam.TCC1:
|
||||||
// getTimer returns the timer to be used for PWM on this pin
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC1_)
|
||||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||||
switch pwm.Pin {
|
case sam.TCC2:
|
||||||
case PA14:
|
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||||
return sam.TCC2
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||||
case PA15:
|
}
|
||||||
return sam.TCC2
|
}
|
||||||
case PA16:
|
|
||||||
return sam.TCC1
|
func (tcc *TCC) timerNum() uint8 {
|
||||||
case PA17:
|
switch tcc.timer() {
|
||||||
return sam.TCC1
|
case sam.TCC0:
|
||||||
case PA18:
|
return 0
|
||||||
return sam.TCC1
|
case sam.TCC1:
|
||||||
case PA19:
|
return 1
|
||||||
return sam.TCC1
|
case sam.TCC2:
|
||||||
case PA20:
|
return 2
|
||||||
return sam.TCC0
|
default:
|
||||||
case PA21:
|
return 0x0f // should not happen
|
||||||
return sam.TCC0
|
|
||||||
case PA22:
|
|
||||||
return sam.TCC0
|
|
||||||
case PA23:
|
|
||||||
return sam.TCC0
|
|
||||||
default:
|
|
||||||
return nil // not supported on this pin
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,60 +11,49 @@ import "device/sam"
|
|||||||
|
|
||||||
const HSRAM_SIZE = 0x00030000
|
const HSRAM_SIZE = 0x00030000
|
||||||
|
|
||||||
// InitPWM initializes the PWM interface.
|
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||||
func InitPWM() {
|
var (
|
||||||
// turn on timer clocks used for PWM
|
TCC0 = (*TCC)(sam.TCC0)
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
TCC1 = (*TCC)(sam.TCC1)
|
||||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
|
TCC2 = (*TCC)(sam.TCC2)
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
TCC3 = (*TCC)(sam.TCC3)
|
||||||
|
TCC4 = (*TCC)(sam.TCC4)
|
||||||
|
)
|
||||||
|
|
||||||
//use clock generator 0
|
func (tcc *TCC) configureClock() {
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
// Turn on timer clocks used for the TCC and use generic clock generator 0.
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
switch tcc.timer() {
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
case sam.TCC0:
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_)
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
case sam.TCC1:
|
||||||
}
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC1_)
|
||||||
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC1].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
|
case sam.TCC2:
|
||||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||||
switch pwm.Pin {
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||||
case PA14:
|
case sam.TCC3:
|
||||||
return sam.TCC2
|
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
|
||||||
case PA15:
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||||
return sam.TCC2
|
case sam.TCC4:
|
||||||
case PA16:
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||||
return sam.TCC1
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||||
case PA17:
|
}
|
||||||
return sam.TCC1
|
}
|
||||||
case PA18:
|
|
||||||
return sam.TCC1
|
func (tcc *TCC) timerNum() uint8 {
|
||||||
case PA19:
|
switch tcc.timer() {
|
||||||
return sam.TCC1
|
case sam.TCC0:
|
||||||
case PA20:
|
return 0
|
||||||
return sam.TCC0
|
case sam.TCC1:
|
||||||
case PA21:
|
return 1
|
||||||
return sam.TCC0
|
case sam.TCC2:
|
||||||
case PA22:
|
return 2
|
||||||
return sam.TCC0
|
case sam.TCC3:
|
||||||
case PA23:
|
return 3
|
||||||
return sam.TCC0
|
case sam.TCC4:
|
||||||
case PB12:
|
return 4
|
||||||
return sam.TCC3
|
default:
|
||||||
case PB13:
|
return 0x0f // should not happen
|
||||||
return sam.TCC3
|
|
||||||
case PB14:
|
|
||||||
return sam.TCC4
|
|
||||||
case PB15:
|
|
||||||
return sam.TCC4
|
|
||||||
case PB16:
|
|
||||||
return sam.TCC0
|
|
||||||
case PB17:
|
|
||||||
return sam.TCC0
|
|
||||||
case PB31:
|
|
||||||
return sam.TCC4
|
|
||||||
default:
|
|
||||||
return nil // not supported on this pin
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,60 +11,49 @@ import "device/sam"
|
|||||||
|
|
||||||
const HSRAM_SIZE = 0x00040000
|
const HSRAM_SIZE = 0x00040000
|
||||||
|
|
||||||
// InitPWM initializes the PWM interface.
|
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||||
func InitPWM() {
|
var (
|
||||||
// turn on timer clocks used for PWM
|
TCC0 = (*TCC)(sam.TCC0)
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
TCC1 = (*TCC)(sam.TCC1)
|
||||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
|
TCC2 = (*TCC)(sam.TCC2)
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
TCC3 = (*TCC)(sam.TCC3)
|
||||||
|
TCC4 = (*TCC)(sam.TCC4)
|
||||||
|
)
|
||||||
|
|
||||||
//use clock generator 0
|
func (tcc *TCC) configureClock() {
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
// Turn on timer clocks used for TCC and use generic clock generator 0.
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
switch tcc.timer() {
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
case sam.TCC0:
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_)
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
case sam.TCC1:
|
||||||
}
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC1_)
|
||||||
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC1].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
|
case sam.TCC2:
|
||||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||||
switch pwm.Pin {
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||||
case PA14:
|
case sam.TCC3:
|
||||||
return sam.TCC2
|
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
|
||||||
case PA15:
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||||
return sam.TCC2
|
case sam.TCC4:
|
||||||
case PA16:
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||||
return sam.TCC1
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||||
case PA17:
|
}
|
||||||
return sam.TCC1
|
}
|
||||||
case PA18:
|
|
||||||
return sam.TCC1
|
func (tcc *TCC) timerNum() uint8 {
|
||||||
case PA19:
|
switch tcc.timer() {
|
||||||
return sam.TCC1
|
case sam.TCC0:
|
||||||
case PA20:
|
return 0
|
||||||
return sam.TCC0
|
case sam.TCC1:
|
||||||
case PA21:
|
return 1
|
||||||
return sam.TCC0
|
case sam.TCC2:
|
||||||
case PA22:
|
return 2
|
||||||
return sam.TCC0
|
case sam.TCC3:
|
||||||
case PA23:
|
return 3
|
||||||
return sam.TCC0
|
case sam.TCC4:
|
||||||
case PB12:
|
return 4
|
||||||
return sam.TCC3
|
default:
|
||||||
case PB13:
|
return 0x0f // should not happen
|
||||||
return sam.TCC3
|
|
||||||
case PB14:
|
|
||||||
return sam.TCC4
|
|
||||||
case PB15:
|
|
||||||
return sam.TCC4
|
|
||||||
case PB16:
|
|
||||||
return sam.TCC0
|
|
||||||
case PB17:
|
|
||||||
return sam.TCC0
|
|
||||||
case PB31:
|
|
||||||
return sam.TCC4
|
|
||||||
default:
|
|
||||||
return nil // not supported on this pin
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,60 +11,49 @@ import "device/sam"
|
|||||||
|
|
||||||
const HSRAM_SIZE = 0x00030000
|
const HSRAM_SIZE = 0x00030000
|
||||||
|
|
||||||
// InitPWM initializes the PWM interface.
|
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||||
func InitPWM() {
|
var (
|
||||||
// turn on timer clocks used for PWM
|
TCC0 = (*TCC)(sam.TCC0)
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
TCC1 = (*TCC)(sam.TCC1)
|
||||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
|
TCC2 = (*TCC)(sam.TCC2)
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
TCC3 = (*TCC)(sam.TCC3)
|
||||||
|
TCC4 = (*TCC)(sam.TCC4)
|
||||||
|
)
|
||||||
|
|
||||||
//use clock generator 0
|
func (tcc *TCC) configureClock() {
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
// Turn on timer clocks used for TCC and use generic clock generator 0.
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
switch tcc.timer() {
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
case sam.TCC0:
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_)
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
case sam.TCC1:
|
||||||
}
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC1_)
|
||||||
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC1].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
|
case sam.TCC2:
|
||||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||||
switch pwm.Pin {
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||||
case PA14:
|
case sam.TCC3:
|
||||||
return sam.TCC2
|
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
|
||||||
case PA15:
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||||
return sam.TCC2
|
case sam.TCC4:
|
||||||
case PA16:
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||||
return sam.TCC1
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||||
case PA17:
|
}
|
||||||
return sam.TCC1
|
}
|
||||||
case PA18:
|
|
||||||
return sam.TCC1
|
func (tcc *TCC) timerNum() uint8 {
|
||||||
case PA19:
|
switch tcc.timer() {
|
||||||
return sam.TCC1
|
case sam.TCC0:
|
||||||
case PA20:
|
return 0
|
||||||
return sam.TCC0
|
case sam.TCC1:
|
||||||
case PA21:
|
return 1
|
||||||
return sam.TCC0
|
case sam.TCC2:
|
||||||
case PA22:
|
return 2
|
||||||
return sam.TCC0
|
case sam.TCC3:
|
||||||
case PA23:
|
return 3
|
||||||
return sam.TCC0
|
case sam.TCC4:
|
||||||
case PB12:
|
return 4
|
||||||
return sam.TCC3
|
default:
|
||||||
case PB13:
|
return 0x0f // should not happen
|
||||||
return sam.TCC3
|
|
||||||
case PB14:
|
|
||||||
return sam.TCC4
|
|
||||||
case PB15:
|
|
||||||
return sam.TCC4
|
|
||||||
case PB16:
|
|
||||||
return sam.TCC0
|
|
||||||
case PB17:
|
|
||||||
return sam.TCC0
|
|
||||||
case PB31:
|
|
||||||
return sam.TCC4
|
|
||||||
default:
|
|
||||||
return nil // not supported on this pin
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
// +build avr,attiny
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
// Tx is a dummy implementation. I2C has not been implemented for ATtiny
|
|
||||||
// devices.
|
|
||||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -141,10 +141,3 @@ func (a ADC) Get() uint16 {
|
|||||||
|
|
||||||
return uint16(avr.ADCL.Get()) | uint16(avr.ADCH.Get())<<8
|
return uint16(avr.ADCL.Get()) | uint16(avr.ADCH.Get())<<8
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2C on AVR.
|
|
||||||
type I2C struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// I2C0 is the only I2C interface on most AVRs.
|
|
||||||
var I2C0 = I2C{}
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package machine
|
|||||||
import (
|
import (
|
||||||
"device/sifive"
|
"device/sifive"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CPUFrequency() uint32 {
|
func CPUFrequency() uint32 {
|
||||||
@@ -185,9 +186,13 @@ func (spi SPI) Transfer(w byte) (byte, error) {
|
|||||||
|
|
||||||
// I2C on the FE310-G002.
|
// I2C on the FE310-G002.
|
||||||
type I2C struct {
|
type I2C struct {
|
||||||
Bus *sifive.I2C_Type
|
Bus sifive.I2C_Type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
I2C0 = (*I2C)(unsafe.Pointer(sifive.I2C0))
|
||||||
|
)
|
||||||
|
|
||||||
// I2CConfig is used to store config info for I2C.
|
// I2CConfig is used to store config info for I2C.
|
||||||
type I2CConfig struct {
|
type I2CConfig struct {
|
||||||
Frequency uint32
|
Frequency uint32
|
||||||
@@ -196,7 +201,7 @@ type I2CConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Configure is intended to setup the I2C interface.
|
// Configure is intended to setup the I2C interface.
|
||||||
func (i2c I2C) Configure(config I2CConfig) error {
|
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||||
var i2cClockFrequency uint32 = 32000000
|
var i2cClockFrequency uint32 = 32000000
|
||||||
if config.Frequency == 0 {
|
if config.Frequency == 0 {
|
||||||
config.Frequency = TWI_FREQ_100KHZ
|
config.Frequency = TWI_FREQ_100KHZ
|
||||||
@@ -228,7 +233,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
|||||||
// Tx does a single I2C transaction at the specified address.
|
// Tx does a single I2C transaction at the specified address.
|
||||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||||
var err error
|
var err error
|
||||||
if len(w) != 0 {
|
if len(w) != 0 {
|
||||||
// send start/address for write
|
// send start/address for write
|
||||||
@@ -276,7 +281,7 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Writes a single byte to the I2C bus.
|
// Writes a single byte to the I2C bus.
|
||||||
func (i2c I2C) writeByte(data byte) error {
|
func (i2c *I2C) writeByte(data byte) error {
|
||||||
// Send data byte
|
// Send data byte
|
||||||
i2c.Bus.TXR_RXR.Set(uint32(data))
|
i2c.Bus.TXR_RXR.Set(uint32(data))
|
||||||
|
|
||||||
@@ -295,7 +300,7 @@ func (i2c I2C) writeByte(data byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reads a single byte from the I2C bus.
|
// Reads a single byte from the I2C bus.
|
||||||
func (i2c I2C) readByte() byte {
|
func (i2c *I2C) readByte() byte {
|
||||||
i2c.Bus.CR_SR.Set(sifive.I2C_CR_RD)
|
i2c.Bus.CR_SR.Set(sifive.I2C_CR_RD)
|
||||||
|
|
||||||
// wait until transmission complete
|
// wait until transmission complete
|
||||||
@@ -306,7 +311,7 @@ func (i2c I2C) readByte() byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sends the address and start signal.
|
// Sends the address and start signal.
|
||||||
func (i2c I2C) sendAddress(address uint16, write bool) error {
|
func (i2c *I2C) sendAddress(address uint16, write bool) error {
|
||||||
data := (address << 1)
|
data := (address << 1)
|
||||||
if !write {
|
if !write {
|
||||||
data |= 1 // set read flag in transmit register
|
data |= 1 // set read flag in transmit register
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ package machine
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
SPI0 = SPI{0}
|
SPI0 = SPI{0}
|
||||||
I2C0 = I2C{0}
|
I2C0 = &I2C{0}
|
||||||
UART0 = UART{0}
|
UART0 = UART{0}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -84,24 +84,6 @@ func (adc ADC) Get() uint16 {
|
|||||||
//export __tinygo_adc_read
|
//export __tinygo_adc_read
|
||||||
func adcRead(pin Pin) uint16
|
func adcRead(pin Pin) uint16
|
||||||
|
|
||||||
// InitPWM enables support for PWM peripherals.
|
|
||||||
func InitPWM() {
|
|
||||||
// Nothing to do here.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure configures a PWM pin for output.
|
|
||||||
func (pwm PWM) Configure() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set turns on the duty cycle for a PWM pin using the provided value.
|
|
||||||
func (pwm PWM) Set(value uint16) {
|
|
||||||
pwmSet(pwm.Pin, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
//export __tinygo_pwm_set
|
|
||||||
func pwmSet(pin Pin, value uint16)
|
|
||||||
|
|
||||||
// I2C is a generic implementation of the Inter-IC communication protocol.
|
// I2C is a generic implementation of the Inter-IC communication protocol.
|
||||||
type I2C struct {
|
type I2C struct {
|
||||||
Bus uint8
|
Bus uint8
|
||||||
@@ -115,13 +97,13 @@ type I2CConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Configure is intended to setup the I2C interface.
|
// Configure is intended to setup the I2C interface.
|
||||||
func (i2c I2C) Configure(config I2CConfig) error {
|
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||||
i2cConfigure(i2c.Bus, config.SCL, config.SDA)
|
i2cConfigure(i2c.Bus, config.SCL, config.SDA)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tx does a single I2C transaction at the specified address.
|
// Tx does a single I2C transaction at the specified address.
|
||||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||||
i2cTransfer(i2c.Bus, &w[0], len(w), &r[0], len(r))
|
i2cTransfer(i2c.Bus, &w[0], len(w), &r[0], len(r))
|
||||||
// TODO: do something with the returned error code.
|
// TODO: do something with the returned error code.
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"device/riscv"
|
"device/riscv"
|
||||||
"errors"
|
"errors"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CPUFrequency() uint32 {
|
func CPUFrequency() uint32 {
|
||||||
@@ -493,9 +494,15 @@ func (spi SPI) Transfer(w byte) (byte, error) {
|
|||||||
|
|
||||||
// I2C on the K210.
|
// I2C on the K210.
|
||||||
type I2C struct {
|
type I2C struct {
|
||||||
Bus *kendryte.I2C_Type
|
Bus kendryte.I2C_Type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
I2C0 = (*I2C)(unsafe.Pointer(kendryte.I2C0))
|
||||||
|
I2C1 = (*I2C)(unsafe.Pointer(kendryte.I2C1))
|
||||||
|
I2C2 = (*I2C)(unsafe.Pointer(kendryte.I2C2))
|
||||||
|
)
|
||||||
|
|
||||||
// I2CConfig is used to store config info for I2C.
|
// I2CConfig is used to store config info for I2C.
|
||||||
type I2CConfig struct {
|
type I2CConfig struct {
|
||||||
Frequency uint32
|
Frequency uint32
|
||||||
@@ -504,7 +511,7 @@ type I2CConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Configure is intended to setup the I2C interface.
|
// Configure is intended to setup the I2C interface.
|
||||||
func (i2c I2C) Configure(config I2CConfig) error {
|
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||||
|
|
||||||
if config.Frequency == 0 {
|
if config.Frequency == 0 {
|
||||||
config.Frequency = TWI_FREQ_100KHZ
|
config.Frequency = TWI_FREQ_100KHZ
|
||||||
@@ -518,7 +525,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
|||||||
// Enable APB0 clock.
|
// Enable APB0 clock.
|
||||||
kendryte.SYSCTL.CLK_EN_CENT.SetBits(kendryte.SYSCTL_CLK_EN_CENT_APB0_CLK_EN)
|
kendryte.SYSCTL.CLK_EN_CENT.SetBits(kendryte.SYSCTL_CLK_EN_CENT_APB0_CLK_EN)
|
||||||
|
|
||||||
switch i2c.Bus {
|
switch &i2c.Bus {
|
||||||
case kendryte.I2C0:
|
case kendryte.I2C0:
|
||||||
// Initialize I2C0 clock.
|
// Initialize I2C0 clock.
|
||||||
kendryte.SYSCTL.CLK_EN_PERI.SetBits(kendryte.SYSCTL_CLK_EN_PERI_I2C0_CLK_EN)
|
kendryte.SYSCTL.CLK_EN_PERI.SetBits(kendryte.SYSCTL_CLK_EN_PERI_I2C0_CLK_EN)
|
||||||
@@ -567,7 +574,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
|||||||
// Tx does a single I2C transaction at the specified address.
|
// Tx does a single I2C transaction at the specified address.
|
||||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||||
// Set peripheral address.
|
// Set peripheral address.
|
||||||
i2c.Bus.TAR.Set(uint32(addr))
|
i2c.Bus.TAR.Set(uint32(addr))
|
||||||
// Enable controller.
|
// Enable controller.
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"device/nrf"
|
"device/nrf"
|
||||||
"errors"
|
"errors"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -203,13 +204,13 @@ func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
|||||||
|
|
||||||
// I2C on the NRF.
|
// I2C on the NRF.
|
||||||
type I2C struct {
|
type I2C struct {
|
||||||
Bus *nrf.TWI_Type
|
Bus nrf.TWI_Type
|
||||||
}
|
}
|
||||||
|
|
||||||
// There are 2 I2C interfaces on the NRF.
|
// There are 2 I2C interfaces on the NRF.
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{Bus: nrf.TWI0}
|
I2C0 = (*I2C)(unsafe.Pointer(nrf.TWI0))
|
||||||
I2C1 = I2C{Bus: nrf.TWI1}
|
I2C1 = (*I2C)(unsafe.Pointer(nrf.TWI1))
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2CConfig is used to store config info for I2C.
|
// I2CConfig is used to store config info for I2C.
|
||||||
@@ -220,7 +221,7 @@ type I2CConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Configure is intended to setup the I2C interface.
|
// Configure is intended to setup the I2C interface.
|
||||||
func (i2c I2C) Configure(config I2CConfig) error {
|
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||||
// Default I2C bus speed is 100 kHz.
|
// Default I2C bus speed is 100 kHz.
|
||||||
if config.Frequency == 0 {
|
if config.Frequency == 0 {
|
||||||
config.Frequency = TWI_FREQ_100KHZ
|
config.Frequency = TWI_FREQ_100KHZ
|
||||||
@@ -261,7 +262,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
|||||||
// Tx does a single I2C transaction at the specified address.
|
// Tx does a single I2C transaction at the specified address.
|
||||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||||
func (i2c I2C) Tx(addr uint16, w, r []byte) (err error) {
|
func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) {
|
||||||
i2c.Bus.ADDRESS.Set(uint32(addr))
|
i2c.Bus.ADDRESS.Set(uint32(addr))
|
||||||
|
|
||||||
if len(w) != 0 {
|
if len(w) != 0 {
|
||||||
@@ -299,7 +300,7 @@ cleanUp:
|
|||||||
// signalStop sends a stop signal when writing or tells the I2C peripheral that
|
// signalStop sends a stop signal when writing or tells the I2C peripheral that
|
||||||
// it must generate a stop condition after the next character is retrieved when
|
// it must generate a stop condition after the next character is retrieved when
|
||||||
// reading.
|
// reading.
|
||||||
func (i2c I2C) signalStop() {
|
func (i2c *I2C) signalStop() {
|
||||||
i2c.Bus.TASKS_STOP.Set(1)
|
i2c.Bus.TASKS_STOP.Set(1)
|
||||||
for i2c.Bus.EVENTS_STOPPED.Get() == 0 {
|
for i2c.Bus.EVENTS_STOPPED.Get() == 0 {
|
||||||
}
|
}
|
||||||
@@ -307,7 +308,7 @@ func (i2c I2C) signalStop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// writeByte writes a single byte to the I2C bus.
|
// writeByte writes a single byte to the I2C bus.
|
||||||
func (i2c I2C) writeByte(data byte) error {
|
func (i2c *I2C) writeByte(data byte) error {
|
||||||
i2c.Bus.TXD.Set(uint32(data))
|
i2c.Bus.TXD.Set(uint32(data))
|
||||||
for i2c.Bus.EVENTS_TXDSENT.Get() == 0 {
|
for i2c.Bus.EVENTS_TXDSENT.Get() == 0 {
|
||||||
if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 {
|
if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 {
|
||||||
@@ -320,7 +321,7 @@ func (i2c I2C) writeByte(data byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// readByte reads a single byte from the I2C bus.
|
// readByte reads a single byte from the I2C bus.
|
||||||
func (i2c I2C) readByte() (byte, error) {
|
func (i2c *I2C) readByte() (byte, error) {
|
||||||
for i2c.Bus.EVENTS_RXDREADY.Get() == 0 {
|
for i2c.Bus.EVENTS_RXDREADY.Get() == 0 {
|
||||||
if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 {
|
if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 {
|
||||||
i2c.Bus.EVENTS_ERROR.Set(0)
|
i2c.Bus.EVENTS_ERROR.Set(0)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func (uart UART) setPins(tx, rx Pin) {
|
|||||||
nrf.UART0.PSELRXD.Set(uint32(rx))
|
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i2c I2C) setPins(scl, sda Pin) {
|
func (i2c *I2C) setPins(scl, sda Pin) {
|
||||||
i2c.Bus.PSELSCL.Set(uint32(scl))
|
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||||
i2c.Bus.PSELSDA.Set(uint32(sda))
|
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,14 +56,14 @@ func (uart UART) setPins(tx, rx Pin) {
|
|||||||
nrf.UART0.PSELRXD.Set(uint32(rx))
|
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i2c I2C) setPins(scl, sda Pin) {
|
func (i2c *I2C) setPins(scl, sda Pin) {
|
||||||
i2c.Bus.PSELSCL.Set(uint32(scl))
|
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||||
i2c.Bus.PSELSDA.Set(uint32(sda))
|
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||||
}
|
}
|
||||||
|
|
||||||
// PWM
|
// PWM
|
||||||
var (
|
var (
|
||||||
pwmChannelPins = [3]uint32{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}
|
PWM0 = &PWM{PWM: nrf.PWM0}
|
||||||
pwms = [3]*nrf.PWM_Type{nrf.PWM0, nrf.PWM1, nrf.PWM2}
|
PWM1 = &PWM{PWM: nrf.PWM1}
|
||||||
pwmChannelSequence [3]uint16
|
PWM2 = &PWM{PWM: nrf.PWM2}
|
||||||
)
|
)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user