Compare commits

..

1 Commits

Author SHA1 Message Date
sago35 13c0714fda test: remove thread-safe wrapper 2021-04-06 21:46:13 +09:00
248 changed files with 2586 additions and 7189 deletions
+23 -42
View File
@@ -68,17 +68,14 @@ commands:
steps: steps:
- restore_cache: - restore_cache:
keys: keys:
- llvm-source-11-v2 - llvm-source-11-v1
- run: - run:
name: "Fetch LLVM source" name: "Fetch LLVM source"
command: make llvm-source command: make llvm-source
- save_cache: - save_cache:
key: llvm-source-11-v2 key: llvm-source-11-v1
paths: paths:
- llvm-project/clang/lib/Headers - llvm-project
- llvm-project/clang/include
- llvm-project/lld/include
- llvm-project/llvm/include
build-wasi-libc: build-wasi-libc:
steps: steps:
- restore_cache: - restore_cache:
@@ -157,15 +154,12 @@ commands:
- llvm-source-linux - llvm-source-linux
- restore_cache: - restore_cache:
keys: keys:
- llvm-build-11-linux-v3-assert - llvm-build-11-linux-v2-assert
- run: - run:
name: "Build LLVM" name: "Build LLVM"
command: | command: |
if [ ! -f llvm-build/lib/liblldELF.a ] if [ ! -f llvm-build/lib/liblldELF.a ]
then then
# fetch LLVM source
rm -rf llvm-project
make llvm-source
# install dependencies # install dependencies
sudo apt-get install cmake ninja-build sudo apt-get install cmake ninja-build
# hack ninja to use less jobs # hack ninja to use less jobs
@@ -173,22 +167,20 @@ commands:
chmod +x /go/bin/ninja chmod +x /go/bin/ninja
# build! # build!
make ASSERT=1 llvm-build make ASSERT=1 llvm-build
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
fi fi
- save_cache: - save_cache:
key: llvm-build-11-linux-v3-assert key: llvm-build-11-linux-v2-assert
paths: paths:
llvm-build llvm-build
- run: make ASSERT=1 - run: |
# 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" make ASSERT=1
- build-wasi-libc - build-wasi-libc
- 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:
@@ -225,15 +217,12 @@ commands:
- llvm-source-linux - llvm-source-linux
- restore_cache: - restore_cache:
keys: keys:
- llvm-build-11-linux-v3-noassert - llvm-build-11-linux-v2-noassert
- run: - run:
name: "Build LLVM" name: "Build LLVM"
command: | command: |
if [ ! -f llvm-build/lib/liblldELF.a ] if [ ! -f llvm-build/lib/liblldELF.a ]
then then
# fetch LLVM source
rm -rf llvm-project
make llvm-source
# install dependencies # install dependencies
sudo apt-get install cmake ninja-build sudo apt-get install cmake ninja-build
# hack ninja to use less jobs # hack ninja to use less jobs
@@ -241,10 +230,9 @@ commands:
chmod +x /go/bin/ninja chmod +x /go/bin/ninja
# build! # build!
make llvm-build make llvm-build
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
fi fi
- save_cache: - save_cache:
key: llvm-build-11-linux-v3-noassert key: llvm-build-11-linux-v2-noassert
paths: paths:
llvm-build llvm-build
- build-wasi-libc - build-wasi-libc
@@ -294,50 +282,43 @@ commands:
variant: "macos" variant: "macos"
- restore_cache: - restore_cache:
keys: keys:
- go-cache-macos-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }} - go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-macos-v3-{{ checksum "go.mod" }} - go-cache-macos-v2-{{ checksum "go.mod" }}
- restore_cache: - restore_cache:
keys: keys:
- llvm-source-11-macos-v3 - llvm-source-11-macos-v1
- run: - run:
name: "Fetch LLVM source" name: "Fetch LLVM source"
command: make llvm-source command: make llvm-source
- save_cache: - save_cache:
key: llvm-source-11-macos-v3 key: llvm-source-11-macos-v1
paths: paths:
- llvm-project/clang/lib/Headers - llvm-project
- llvm-project/clang/include
- llvm-project/lld/include
- llvm-project/llvm/include
- restore_cache: - restore_cache:
keys: keys:
- llvm-build-11-macos-v4 - llvm-build-11-macos-v2
- run: - run:
name: "Build LLVM" name: "Build LLVM"
command: | command: |
if [ ! -f llvm-build/lib/liblldELF.a ] if [ ! -f llvm-build/lib/liblldELF.a ]
then then
# fetch LLVM source
rm -rf llvm-project
make llvm-source
# install dependencies # install dependencies
HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake ninja HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake ninja
# build! # build!
make llvm-build make llvm-build
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
fi fi
- save_cache: - save_cache:
key: llvm-build-11-macos-v4 key: llvm-build-11-macos-v2
paths: paths:
llvm-build llvm-build
- restore_cache: - restore_cache:
keys: keys:
- wasi-libc-sysroot-macos-v4 - wasi-libc-sysroot-macos-v3
- run: - run:
name: "Build wasi-libc" name: "Build wasi-libc"
command: make wasi-libc command: make wasi-libc
- save_cache: - save_cache:
key: wasi-libc-sysroot-macos-v4 key: wasi-libc-sysroot-macos-v3
paths: paths:
- lib/wasi-libc/sysroot - lib/wasi-libc/sysroot
- run: - run:
@@ -359,7 +340,7 @@ commands:
tinygo version tinygo version
- run: make smoketest AVR=0 - run: make smoketest AVR=0
- save_cache: - save_cache:
key: go-cache-macos-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }} key: go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
paths: paths:
- ~/.cache/go-build - ~/.cache/go-build
- /go/pkg/mod - /go/pkg/mod
@@ -401,7 +382,7 @@ jobs:
- build-linux - build-linux
build-macos: build-macos:
macos: macos:
xcode: "11.1.0" # macOS 10.14 xcode: "10.1.0"
steps: steps:
- build-macos - build-macos
+22 -37
View File
@@ -152,9 +152,9 @@ gen-device-stm32: build/gen-device-svd
# Get LLVM sources. # Get LLVM sources.
$(LLVM_PROJECTDIR)/llvm: $(LLVM_PROJECTDIR)/README.md:
git clone -b xtensa_release_11.0.0 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR) git clone -b xtensa_release_11.0.0 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
llvm-source: $(LLVM_PROJECTDIR)/llvm llvm-source: $(LLVM_PROJECTDIR)/README.md
# Configure LLVM. # Configure LLVM.
TINYGO_SOURCE_DIR=$(shell pwd) TINYGO_SOURCE_DIR=$(shell pwd)
@@ -182,28 +182,25 @@ tinygo:
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 ./builder ./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_PACKAGES = \
container/heap \
container/list \
container/ring \
crypto/des \
encoding \
encoding/ascii85 \
encoding/base32 \
encoding/hex \
hash/adler32 \
hash/fnv \
hash/crc64 \
math \
math/cmplx \
text/scanner \
unicode/utf8 \
# Test known-working standard library packages. # Test known-working standard library packages.
# TODO: parallelize, and only show failing tests (no implied -v flag). # TODO: do this in one command, parallelize, and only show failing tests (no
# implied -v flag).
.PHONY: tinygo-test .PHONY: tinygo-test
tinygo-test: tinygo-test:
$(TINYGO) test $(TEST_PACKAGES) $(TINYGO) test container/heap
$(TINYGO) test container/list
$(TINYGO) test container/ring
$(TINYGO) test crypto/des
$(TINYGO) test encoding/ascii85
$(TINYGO) test encoding/base32
$(TINYGO) test encoding/hex
$(TINYGO) test hash/adler32
$(TINYGO) test hash/fnv
$(TINYGO) test hash/crc64
$(TINYGO) test math
$(TINYGO) test math/cmplx
$(TINYGO) test text/scanner
$(TINYGO) test unicode/utf8
.PHONY: smoketest .PHONY: smoketest
smoketest: smoketest:
@@ -229,6 +226,8 @@ smoketest:
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit examples/microbit-blink $(TINYGO) build -size short -o test.hex -target=microbit examples/microbit-blink
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit-v2 examples/microbit-blink
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt $(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial $(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
@@ -257,10 +256,6 @@ smoketest:
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit-s110v8 examples/echo $(TINYGO) build -size short -o test.hex -target=microbit-s110v8 examples/echo
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit-v2 examples/microbit-blink
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit-v2-s113v7 examples/microbit-blink
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nrf52840-mdk examples/blinky1 $(TINYGO) build -size short -o test.hex -target=nrf52840-mdk examples/blinky1
@$(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
@@ -293,8 +288,6 @@ smoketest:
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.gba -target=gameboy-advance examples/gba-display $(TINYGO) build -size short -o test.gba -target=gameboy-advance examples/gba-display
@$(MD5SUM) test.gba @$(MD5SUM) test.gba
$(TINYGO) build -size short -o test.hex -target=grandcentral-m4 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m4 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=itsybitsy-m4 examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1
@@ -341,10 +334,6 @@ smoketest:
@$(MD5SUM) test.hex @$(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
$(TINYGO) build -size short -o test.hex -target=atsame54-xpro examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-m4-can examples/blinky1
@$(MD5SUM) test.hex
# test pwm # test pwm
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm $(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
@@ -352,6 +341,8 @@ 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
@$(MD5SUM) test.hex
ifneq ($(STM32), 0) ifneq ($(STM32), 0)
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1 $(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
@@ -363,8 +354,6 @@ ifneq ($(STM32), 0)
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1 $(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1
@$(MD5SUM) test.hex @$(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 $(TINYGO) build -size short -o test.hex -target=nucleo-l432kc examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nucleo-l552ze examples/blinky1 $(TINYGO) build -size short -o test.hex -target=nucleo-l552ze examples/blinky1
@@ -385,10 +374,6 @@ ifneq ($(AVR), 0)
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1 $(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino-mega1280 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino-mega1280 examples/pwm
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino-nano examples/blinky1 $(TINYGO) build -size short -o test.hex -target=arduino-nano examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1 $(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1
+1 -3
View File
@@ -43,14 +43,13 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux. You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
The following 57 microcontroller boards are currently supported: The following 55 microcontroller boards are currently supported:
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333) * [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333) * [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
* [Adafruit CLUE](https://www.adafruit.com/product/4500) * [Adafruit CLUE](https://www.adafruit.com/product/4500)
* [Adafruit Feather M0](https://www.adafruit.com/product/2772) * [Adafruit Feather M0](https://www.adafruit.com/product/2772)
* [Adafruit Feather M4](https://www.adafruit.com/product/3857) * [Adafruit Feather M4](https://www.adafruit.com/product/3857)
* [Adafruit Feather M4 CAN](https://www.adafruit.com/product/4759)
* [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062) * [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)
* [Adafruit Feather STM32F405 Express](https://www.adafruit.com/product/4382) * [Adafruit Feather STM32F405 Express](https://www.adafruit.com/product/4382)
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727) * [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
@@ -78,7 +77,6 @@ The following 57 microcontroller boards are currently supported:
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance) * [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
* [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/) * [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/)
* [Makerdiary nRF52840-MDK USB Dongle](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/) * [Makerdiary nRF52840-MDK USB Dongle](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/)
* [Microchip SAM E54 Xplained Pro](https://www.microchip.com/developmenttools/productdetails/atsame54-xpro)
* [nice!nano](https://docs.nicekeyboards.com/#/nice!nano/) * [nice!nano](https://docs.nicekeyboards.com/#/nice!nano/)
* [Nintendo Switch](https://www.nintendo.com/switch/) * [Nintendo Switch](https://www.nintendo.com/switch/)
* [Nordic Semiconductor PCA10031](https://www.nordicsemi.com/eng/Products/nRF51-Dongle) * [Nordic Semiconductor PCA10031](https://www.nordicsemi.com/eng/Products/nRF51-Dongle)
+35 -164
View File
@@ -39,11 +39,6 @@ type BuildResult struct {
// The directory of the main package. This is useful for testing as the test // The directory of the main package. This is useful for testing as the test
// binary must be run in the directory of the tested package. // binary must be run in the directory of the tested package.
MainDir string MainDir string
// ImportPath is the import path of the main package. This is useful for
// correctly printing test results: the import path isn't always the same as
// the path listed on the command line.
ImportPath string
} }
// packageAction is the struct that is serialized to JSON and hashed, to work as // packageAction is the struct that is serialized to JSON and hashed, to work as
@@ -57,17 +52,14 @@ type BuildResult struct {
// key, avoiding the need for recompiling all dependencies when only the // key, avoiding the need for recompiling all dependencies when only the
// implementation of an imported package changes. // implementation of an imported package changes.
type packageAction struct { type packageAction struct {
ImportPath string ImportPath string
CompilerVersion int // compiler.Version CompilerVersion int // compiler.Version
InterpVersion int // interp.Version InterpVersion int // interp.Version
LLVMVersion string LLVMVersion string
Config *compiler.Config Config *compiler.Config
CFlags []string CFlags []string
FileHashes map[string]string // hash of every file that's part of the package 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 Imports map[string]string // map from imported package to action ID hash
OptLevel int // LLVM optimization level (0-3)
SizeLevel int // LLVM optimization for size level (0-2)
UndefinedGlobals []string // globals that are left as external globals (no initializer)
} }
// 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
@@ -99,7 +91,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
DefaultStackSize: config.Target.DefaultStackSize, DefaultStackSize: config.Target.DefaultStackSize,
NeedsStackObjects: config.NeedsStackObjects(), NeedsStackObjects: config.NeedsStackObjects(),
Debug: config.Debug(), Debug: config.Debug(),
LLVMFeatures: config.LLVMFeatures(),
} }
// Load the target machine, which is the LLVM object that contains all // Load the target machine, which is the LLVM object that contains all
@@ -136,30 +127,20 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
var packageJobs []*compileJob var packageJobs []*compileJob
packageBitcodePaths := make(map[string]string) packageBitcodePaths := make(map[string]string)
packageActionIDs := make(map[string]string) packageActionIDs := make(map[string]string)
optLevel, sizeLevel, _ := config.OptLevels()
for _, pkg := range lprogram.Sorted() { for _, pkg := range lprogram.Sorted() {
pkg := pkg // necessary to avoid a race condition pkg := pkg // necessary to avoid a race condition
var undefinedGlobals []string
for name := range config.Options.GlobalValues[pkg.Pkg.Path()] {
undefinedGlobals = append(undefinedGlobals, name)
}
sort.Strings(undefinedGlobals)
// Create a cache key: a hash from the action ID below that contains all // Create a cache key: a hash from the action ID below that contains all
// the parameters for the build. // the parameters for the build.
actionID := packageAction{ actionID := packageAction{
ImportPath: pkg.ImportPath, ImportPath: pkg.ImportPath,
CompilerVersion: compiler.Version, CompilerVersion: compiler.Version,
InterpVersion: interp.Version, InterpVersion: interp.Version,
LLVMVersion: llvm.Version, LLVMVersion: llvm.Version,
Config: compilerConfig, Config: compilerConfig,
CFlags: pkg.CFlags, CFlags: pkg.CFlags,
FileHashes: make(map[string]string, len(pkg.FileHashes)), FileHashes: make(map[string]string, len(pkg.FileHashes)),
Imports: make(map[string]string, len(pkg.Pkg.Imports())), Imports: make(map[string]string, len(pkg.Pkg.Imports())),
OptLevel: optLevel,
SizeLevel: sizeLevel,
UndefinedGlobals: undefinedGlobals,
} }
for filePath, hash := range pkg.FileHashes { for filePath, hash := range pkg.FileHashes {
actionID.FileHashes[filePath] = hex.EncodeToString(hash) actionID.FileHashes[filePath] = hex.EncodeToString(hash)
@@ -210,25 +191,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return errors.New("verification error after compiling package " + pkg.ImportPath) return errors.New("verification error after compiling package " + pkg.ImportPath)
} }
// Erase all globals that are part of the undefinedGlobals list.
// This list comes from the -ldflags="-X pkg.foo=val" option.
// Instead of setting the value directly in the AST (which would
// mean the value, which may be a secret, is stored in the build
// cache), the global itself is left external (undefined) and is
// only set at the end of the compilation.
for _, name := range undefinedGlobals {
globalName := pkg.Pkg.Path() + "." + name
global := mod.NamedGlobal(globalName)
if global.IsNil() {
return errors.New("global not found: " + globalName)
}
name := global.Name()
newGlobal := llvm.AddGlobal(mod, global.Type().ElementType(), name+".tmp")
global.ReplaceAllUsesWith(newGlobal)
global.EraseFromParentAsGlobal()
newGlobal.SetName(name)
}
// Try to interpret package initializers at compile time. // Try to interpret package initializers at compile time.
// It may only be possible to do this partially, in which case // It may only be possible to do this partially, in which case
// it is completed after all IR files are linked. // it is completed after all IR files are linked.
@@ -244,38 +206,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return errors.New("verification error after interpreting " + pkgInit.Name()) return errors.New("verification error after interpreting " + pkgInit.Name())
} }
if sizeLevel >= 2 {
// Set the "optsize" attribute to make slightly smaller
// binaries at the cost of some performance.
kind := llvm.AttributeKindID("optsize")
attr := mod.Context().CreateEnumAttribute(kind, 0)
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
fn.AddFunctionAttr(attr)
}
}
// Run function passes for each function in the module.
// These passes are intended to be run on each function right
// after they're created to reduce IR size (and maybe also for
// cache locality to improve performance), but for now they're
// run here for each function in turn. Maybe this can be
// improved in the future.
builder := llvm.NewPassManagerBuilder()
defer builder.Dispose()
builder.SetOptLevel(optLevel)
builder.SetSizeLevel(sizeLevel)
funcPasses := llvm.NewFunctionPassManagerForModule(mod)
defer funcPasses.Dispose()
builder.PopulateFunc(funcPasses)
funcPasses.InitializeFunc()
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
if fn.IsDeclaration() {
continue
}
funcPasses.RunFunc(fn)
}
funcPasses.FinalizeFunc()
// Serialize the LLVM module as a bitcode file. // Serialize the LLVM module as a bitcode file.
// Write to a temporary path that is renamed to the destination // Write to a temporary path that is renamed to the destination
// file to avoid race conditions with other TinyGo invocatiosn // file to avoid race conditions with other TinyGo invocatiosn
@@ -500,7 +430,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
job := &compileJob{ job := &compileJob{
description: "compile extra file " + path, description: "compile extra file " + path,
run: func(job *compileJob) error { run: func(job *compileJob) error {
result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config.Options.PrintCommands) result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config)
job.result = result job.result = result
return err return err
}, },
@@ -519,7 +449,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
job := &compileJob{ job := &compileJob{
description: "compile CGo file " + abspath, description: "compile CGo file " + abspath,
run: func(job *compileJob) error { run: func(job *compileJob) error {
result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config.Options.PrintCommands) result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config)
job.result = result job.result = result
return err return err
}, },
@@ -643,9 +573,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return fmt.Errorf("unknown output binary format: %s", outputBinaryFormat) return fmt.Errorf("unknown output binary format: %s", outputBinaryFormat)
} }
return action(BuildResult{ return action(BuildResult{
Binary: tmppath, Binary: tmppath,
MainDir: lprogram.MainPkg().Dir, MainDir: lprogram.MainPkg().Dir,
ImportPath: lprogram.MainPkg().ImportPath,
}) })
} }
@@ -674,12 +603,6 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
transform.ApplyFunctionSections(mod) // -ffunction-sections transform.ApplyFunctionSections(mod) // -ffunction-sections
} }
// Insert values from -ldflags="-X ..." into the IR.
err = setGlobalValues(mod, config.Options.GlobalValues)
if err != nil {
return err
}
// Browsers cannot handle external functions that have type i64 because it // Browsers cannot handle external functions that have type i64 because it
// cannot be represented exactly in JavaScript (JS only has doubles). To // cannot be represented exactly in JavaScript (JS only has doubles). To
// keep functions interoperable, pass int64 types as pointers to // keep functions interoperable, pass int64 types as pointers to
@@ -694,8 +617,21 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
// Optimization levels here are roughly the same as Clang, but probably not // Optimization levels here are roughly the same as Clang, but probably not
// exactly. // exactly.
optLevel, sizeLevel, inlinerThreshold := config.OptLevels() var errs []error
errs := transform.Optimize(mod, config, optLevel, sizeLevel, inlinerThreshold) switch config.Options.Opt {
case "none", "0":
errs = transform.Optimize(mod, config, 0, 0, 0) // -O0
case "1":
errs = transform.Optimize(mod, config, 1, 0, 0) // -O1
case "2":
errs = transform.Optimize(mod, config, 2, 0, 225) // -O2
case "s":
errs = transform.Optimize(mod, config, 2, 1, 225) // -Os
case "z":
errs = transform.Optimize(mod, config, 2, 2, 5) // -Oz, default
default:
return errors.New("unknown optimization level: -opt=" + config.Options.Opt)
}
if len(errs) > 0 { if len(errs) > 0 {
return newMultiError(errs) return newMultiError(errs)
} }
@@ -717,71 +653,6 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
return nil return nil
} }
// setGlobalValues sets the global values from the -ldflags="-X ..." compiler
// option in the given module. An error may be returned if the global is not of
// the expected type.
func setGlobalValues(mod llvm.Module, globals map[string]map[string]string) error {
var pkgPaths []string
for pkgPath := range globals {
pkgPaths = append(pkgPaths, pkgPath)
}
sort.Strings(pkgPaths)
for _, pkgPath := range pkgPaths {
pkg := globals[pkgPath]
var names []string
for name := range pkg {
names = append(names, name)
}
sort.Strings(names)
for _, name := range names {
value := pkg[name]
globalName := pkgPath + "." + name
global := mod.NamedGlobal(globalName)
if global.IsNil() || !global.Initializer().IsNil() {
// The global either does not exist (optimized away?) or has
// some value, in which case it has already been initialized at
// package init time.
continue
}
// A strin is a {ptr, len} pair. We need these types to build the
// initializer.
initializerType := global.Type().ElementType()
if initializerType.TypeKind() != llvm.StructTypeKind || initializerType.StructName() == "" {
return fmt.Errorf("%s: not a string", globalName)
}
elementTypes := initializerType.StructElementTypes()
if len(elementTypes) != 2 {
return fmt.Errorf("%s: not a string", globalName)
}
// Create a buffer for the string contents.
bufInitializer := mod.Context().ConstString(value, false)
buf := llvm.AddGlobal(mod, bufInitializer.Type(), ".string")
buf.SetInitializer(bufInitializer)
buf.SetAlignment(1)
buf.SetUnnamedAddr(true)
buf.SetLinkage(llvm.PrivateLinkage)
// Create the string value, which is a {ptr, len} pair.
zero := llvm.ConstInt(mod.Context().Int32Type(), 0, false)
ptr := llvm.ConstGEP(buf, []llvm.Value{zero, zero})
if ptr.Type() != elementTypes[0] {
return fmt.Errorf("%s: not a string", globalName)
}
length := llvm.ConstInt(elementTypes[1], uint64(len(value)), false)
initializer := llvm.ConstNamedStruct(initializerType, []llvm.Value{
ptr,
length,
})
// Set the initializer. No initializer should be set at this point.
global.SetInitializer(initializer)
}
}
return nil
}
// functionStackSizes keeps stack size information about a single function // functionStackSizes keeps stack size information about a single function
// (usually a goroutine). // (usually a goroutine).
type functionStackSize struct { type functionStackSize struct {
+8 -12
View File
@@ -17,6 +17,7 @@ import (
"strings" "strings"
"unicode" "unicode"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/goenv"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@@ -56,7 +57,7 @@ import (
// depfile but without invalidating its name. For this reason, the depfile is // depfile but without invalidating its name. For this reason, the depfile is
// written on each new compilation (even when it seems unnecessary). However, it // 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. // could in rare cases lead to a stale file fetched from the cache.
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands bool) (string, error) { func compileAndCacheCFile(abspath, tmpdir string, cflags []string, config *compileopts.Config) (string, error) {
// Hash input file. // Hash input file.
fileHash, err := hashFile(abspath) fileHash, err := hashFile(abspath)
if err != nil { if err != nil {
@@ -67,12 +68,14 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
buf, err := json.Marshal(struct { buf, err := json.Marshal(struct {
Path string Path string
Hash string Hash string
Compiler string
Flags []string Flags []string
LLVMVersion string LLVMVersion string
}{ }{
Path: abspath, Path: abspath,
Hash: fileHash, Hash: fileHash,
Flags: cflags, Compiler: config.Target.Compiler,
Flags: config.CFlags(),
LLVMVersion: llvm.Version, LLVMVersion: llvm.Version,
}) })
if err != nil { if err != nil {
@@ -121,17 +124,10 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
flags := append([]string{}, cflags...) // copy cflags flags := append([]string{}, cflags...) // copy cflags
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name()) // autogenerate dependencies flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name()) // autogenerate dependencies
flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath) flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath)
if strings.ToLower(filepath.Ext(abspath)) == ".s" { if config.Options.PrintCommands {
// If this is an assembly file (.s or .S, lowercase or uppercase), then fmt.Printf("%s %s\n", config.Target.Compiler, strings.Join(flags, " "))
// we'll need to add -Qunused-arguments because many parameters are
// relevant to C, not assembly. And with -Werror, having meaningless
// flags (for the assembler) is a compiler error.
flags = append(flags, "-Qunused-arguments")
} }
if printCommands { err = runCCompiler(config.Target.Compiler, flags...)
fmt.Printf("clang %s\n", strings.Join(flags, " "))
}
err = runCCompiler(flags...)
if err != nil { if err != nil {
return "", &commandError{"failed to build", abspath, err} return "", &commandError{"failed to build", abspath, err}
} }
-10
View File
@@ -17,18 +17,10 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if options.OpenOCDCommands != nil {
// Override the OpenOCDCommands from the target spec if specified on
// the command-line
spec.OpenOCDCommands = options.OpenOCDCommands
}
goroot := goenv.Get("GOROOT") goroot := goenv.Get("GOROOT")
if goroot == "" { if goroot == "" {
return nil, errors.New("cannot locate $GOROOT, please set it manually") return nil, errors.New("cannot locate $GOROOT, please set it manually")
} }
major, minor, err := goenv.GetGorootVersion(goroot) major, minor, err := goenv.GetGorootVersion(goroot)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err) return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
@@ -36,9 +28,7 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
if major != 1 || minor < 13 || minor > 16 { if major != 1 || minor < 13 || minor > 16 {
return nil, fmt.Errorf("requires go version 1.13 through 1.16, got go%d.%d", major, minor) return nil, fmt.Errorf("requires go version 1.13 through 1.16, got go%d.%d", major, minor)
} }
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT")) clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
return &compileopts.Config{ return &compileopts.Config{
Options: options, Options: options,
Target: spec, Target: spec,
+1 -1
View File
@@ -136,7 +136,7 @@ func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err 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)
err := runCCompiler(compileArgs...) err := runCCompiler("clang", compileArgs...)
if err != nil { if err != nil {
return &commandError{"failed to build", srcpath, err} return &commandError{"failed to build", srcpath, err}
} }
+13 -4
View File
@@ -9,8 +9,8 @@ import (
) )
// runCCompiler invokes a C compiler with the given arguments. // runCCompiler invokes a C compiler with the given arguments.
func runCCompiler(flags ...string) error { func runCCompiler(command string, flags ...string) error {
if hasBuiltinTools { if hasBuiltinTools && command == "clang" {
// Compile this with the internal Clang compiler. // Compile this with the internal Clang compiler.
headerPath := getClangHeaderPath(goenv.Get("TINYGOROOT")) headerPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
if headerPath == "" { if headerPath == "" {
@@ -23,8 +23,17 @@ func runCCompiler(flags ...string) error {
return cmd.Run() return cmd.Run()
} }
// Compile this with an external invocation of the Clang compiler. // Running some other compiler. Maybe it has been defined in the
return execCommand(commands["clang"], flags...) // commands map (unlikely).
if cmdNames, ok := commands[command]; ok {
return execCommand(cmdNames, flags...)
}
// Alternatively, run the compiler directly.
cmd := exec.Command(command, flags...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
} }
// link invokes a linker with the given name and flags. // link invokes a linker with the given name and flags.
+8 -28
View File
@@ -80,7 +80,12 @@ func (c *Config) GC() string {
if c.Target.GC != "" { if c.Target.GC != "" {
return c.Target.GC return c.Target.GC
} }
return "conservative" for _, tag := range c.Target.BuildTags {
if tag == "baremetal" || tag == "wasm" {
return "conservative"
}
}
return "extalloc"
} }
// NeedsStackObjects returns true if the compiler should insert stack objects // NeedsStackObjects returns true if the compiler should insert stack objects
@@ -113,27 +118,6 @@ func (c *Config) Scheduler() string {
return "coroutines" return "coroutines"
} }
// OptLevels returns the optimization level (0-2), size level (0-2), and inliner
// threshold as used in the LLVM optimization pipeline.
func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) {
switch c.Options.Opt {
case "none", "0":
return 0, 0, 0 // -O0
case "1":
return 1, 0, 0 // -O1
case "2":
return 2, 0, 225 // -O2
case "s":
return 2, 1, 225 // -Os
case "z":
return 2, 2, 5 // -Oz, default
default:
// This is not shown to the user: valid choices are already checked as
// part of Options.Verify(). It is here as a sanity check.
panic("unknown optimization level: -opt=" + c.Options.Opt)
}
}
// FuncImplementation picks an appropriate func value implementation for the // FuncImplementation picks an appropriate func value implementation for the
// target. // target.
func (c *Config) FuncImplementation() string { func (c *Config) FuncImplementation() string {
@@ -179,7 +163,7 @@ func (c *Config) AutomaticStackSize() bool {
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo // CFlags returns the flags to pass to the C compiler. This is necessary for CGo
// preprocessing. // preprocessing.
func (c *Config) CFlags() []string { func (c *Config) CFlags() []string {
var cflags []string cflags := append([]string{}, c.Options.CFlags...)
for _, flag := range c.Target.CFlags { for _, flag := range c.Target.CFlags {
cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT"))) cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT")))
} }
@@ -200,7 +184,7 @@ func (c *Config) CFlags() []string {
func (c *Config) LDFlags() []string { func (c *Config) LDFlags() []string {
root := goenv.Get("TINYGOROOT") root := goenv.Get("TINYGOROOT")
// Merge and adjust LDFlags. // Merge and adjust LDFlags.
var ldflags []string ldflags := append([]string{}, c.Options.LDFlags...)
for _, flag := range c.Target.LDFlags { for _, flag := range c.Target.LDFlags {
ldflags = append(ldflags, strings.ReplaceAll(flag, "{root}", root)) ldflags = append(ldflags, strings.ReplaceAll(flag, "{root}", root))
} }
@@ -337,10 +321,6 @@ func (c *Config) WasmAbi() string {
return c.Target.WasmAbi return c.Target.WasmAbi
} }
func (c *Config) LLVMFeatures() string {
return c.Options.LLVMFeatures
}
type TestConfig struct { type TestConfig struct {
CompileTestBinary bool CompileTestBinary bool
// TODO: Filter the test functions to run, include verbose flag, etc // TODO: Filter the test functions to run, include verbose flag, etc
+18 -28
View File
@@ -2,7 +2,6 @@ package compileopts
import ( import (
"fmt" "fmt"
"regexp"
"strings" "strings"
) )
@@ -11,32 +10,29 @@ var (
validSchedulerOptions = []string{"none", "tasks", "coroutines"} validSchedulerOptions = []string{"none", "tasks", "coroutines"}
validPrintSizeOptions = []string{"none", "short", "full"} validPrintSizeOptions = []string{"none", "short", "full"}
validPanicStrategyOptions = []string{"print", "trap"} validPanicStrategyOptions = []string{"print", "trap"}
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
) )
// Options contains extra options to give to the compiler. These options are // Options contains extra options to give to the compiler. These options are
// usually passed from the command line. // usually passed from the command line.
type Options struct { type Options struct {
Target string Target string
Opt string Opt string
GC string GC string
PanicStrategy string PanicStrategy string
Scheduler string Scheduler string
PrintIR bool PrintIR bool
DumpSSA bool DumpSSA bool
VerifyIR bool VerifyIR bool
PrintCommands bool PrintCommands bool
Debug bool Debug bool
PrintSizes string PrintSizes string
PrintAllocs *regexp.Regexp // regexp string PrintStacks bool
PrintStacks bool CFlags []string
Tags string LDFlags []string
WasmAbi string Tags string
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value WasmAbi string
TestConfig TestConfig TestConfig TestConfig
Programmer string Programmer string
OpenOCDCommands []string
LLVMFeatures string
} }
// Verify performs a validation on the given options, raising an error if options are not valid. // Verify performs a validation on the given options, raising an error if options are not valid.
@@ -77,12 +73,6 @@ func (o *Options) Verify() error {
} }
} }
if o.Opt != "" {
if !isInArray(validOptOptions, o.Opt) {
return fmt.Errorf("invalid -opt=%s: valid values are %s", o.Opt, strings.Join(validOptOptions, ", "))
}
}
return nil return nil
} }
+10 -12
View File
@@ -31,6 +31,7 @@ type TargetSpec struct {
BuildTags []string `json:"build-tags"` BuildTags []string `json:"build-tags"`
GC string `json:"gc"` GC string `json:"gc"`
Scheduler string `json:"scheduler"` Scheduler string `json:"scheduler"`
Compiler string `json:"compiler"`
Linker string `json:"linker"` Linker string `json:"linker"`
RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt) RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt)
Libc string `json:"libc"` Libc string `json:"libc"`
@@ -239,26 +240,23 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
// 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{
Triple: triple, Triple: triple,
GOOS: goos, GOOS: goos,
GOARCH: goarch, GOARCH: goarch,
BuildTags: []string{goos, goarch}, BuildTags: []string{goos, goarch},
Scheduler: "tasks", Compiler: "clang",
Linker: "cc", Linker: "cc",
DefaultStackSize: 1024 * 64, // 64kB CFlags: []string{"--target=" + triple},
CFlags: []string{"--target=" + triple}, GDB: []string{"gdb"},
GDB: []string{"gdb"}, PortReset: "false",
PortReset: "false",
} }
if goos == "darwin" { if goos == "darwin" {
spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip") spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
} else { } else {
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
} }
if goarch != "wasm" { if goarch != "wasm" {
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+".S") spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+".S")
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+".S")
} }
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.
+12 -72
View File
@@ -23,7 +23,7 @@ import (
// Version of the compiler pacakge. Must be incremented each time the compiler // Version of the compiler pacakge. Must be incremented each time the compiler
// package changes in a way that affects the generated LLVM module. // package changes in a way that affects the generated LLVM module.
// This version is independent of the TinyGo version number. // This version is independent of the TinyGo version number.
const Version = 9 // last change: implement reflect.New() const Version = 6 // last change: fix issue 1304
func init() { func init() {
llvm.InitializeAllTargets() llvm.InitializeAllTargets()
@@ -59,7 +59,6 @@ type Config struct {
DefaultStackSize uint64 DefaultStackSize uint64
NeedsStackObjects bool NeedsStackObjects bool
Debug bool // Whether to emit debug information in the LLVM module. Debug bool // Whether to emit debug information in the LLVM module.
LLVMFeatures string
} }
// compilerContext contains function-independent data that should still be // compilerContext contains function-independent data that should still be
@@ -186,12 +185,7 @@ func NewTargetMachine(config *Config) (llvm.TargetMachine, error) {
if err != nil { if err != nil {
return llvm.TargetMachine{}, err return llvm.TargetMachine{}, err
} }
features := strings.Join(config.Features, ",")
feat := config.Features
if len(config.LLVMFeatures) > 0 {
feat = append(feat, config.LLVMFeatures)
}
features := strings.Join(feat, ",")
var codeModel llvm.CodeModel var codeModel llvm.CodeModel
var relocationModel llvm.RelocMode var relocationModel llvm.RelocMode
@@ -304,7 +298,7 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
c.dibuilder.Finalize() c.dibuilder.Finalize()
} }
return c.mod, c.diagnostics return c.mod, nil
} }
// getLLVMRuntimeType obtains a named type from the runtime package and returns // getLLVMRuntimeType obtains a named type from the runtime package and returns
@@ -970,59 +964,11 @@ func (b *builder) createFunction() {
} }
} }
// posser is an interface that's implemented by both ssa.Value and
// ssa.Instruction. It is implemented by everything that has a Pos() method,
// which is all that getPos() needs.
type posser interface {
Pos() token.Pos
}
// getPos returns position information for a ssa.Value or ssa.Instruction.
//
// Not all instructions have position information, especially when they're
// implicit (such as implicit casts or implicit returns at the end of a
// function). In these cases, it makes sense to try a bit harder to guess what
// the position really should be.
func getPos(val posser) token.Pos {
pos := val.Pos()
if pos != token.NoPos {
// Easy: position is known.
return pos
}
// No position information is known.
switch val := val.(type) {
case *ssa.MakeInterface:
return getPos(val.X)
case *ssa.Return:
syntax := val.Parent().Syntax()
if syntax != nil {
// non-synthetic
return syntax.End()
}
return token.NoPos
case *ssa.FieldAddr:
return getPos(val.X)
case *ssa.IndexAddr:
return getPos(val.X)
case *ssa.Slice:
return getPos(val.X)
case *ssa.Store:
return getPos(val.Addr)
case *ssa.Extract:
return getPos(val.Tuple)
default:
// This is reachable, for example with *ssa.Const, *ssa.If, and
// *ssa.Jump. They might be implemented in some way in the future.
return token.NoPos
}
}
// createInstruction builds the LLVM IR equivalent instructions for the // createInstruction builds the LLVM IR equivalent instructions for the
// particular Go SSA instruction. // particular Go SSA instruction.
func (b *builder) createInstruction(instr ssa.Instruction) { func (b *builder) createInstruction(instr ssa.Instruction) {
if b.Debug { if b.Debug {
pos := b.program.Fset.Position(getPos(instr)) pos := b.program.Fset.Position(instr.Pos())
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{}) b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
} }
@@ -2312,20 +2258,14 @@ func (b *builder) createConst(prefix string, expr *ssa.Const) llvm.Value {
} else if typ.Info()&types.IsString != 0 { } else if typ.Info()&types.IsString != 0 {
str := constant.StringVal(expr.Value) str := constant.StringVal(expr.Value)
strLen := llvm.ConstInt(b.uintptrType, uint64(len(str)), false) strLen := llvm.ConstInt(b.uintptrType, uint64(len(str)), false)
var strPtr llvm.Value objname := prefix + "$string"
if str != "" { global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
objname := prefix + "$string" global.SetInitializer(b.ctx.ConstString(str, false))
global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname) global.SetLinkage(llvm.InternalLinkage)
global.SetInitializer(b.ctx.ConstString(str, false)) global.SetGlobalConstant(true)
global.SetLinkage(llvm.InternalLinkage) global.SetUnnamedAddr(true)
global.SetGlobalConstant(true) zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
global.SetUnnamedAddr(true) strPtr := b.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
global.SetAlignment(1)
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
strPtr = b.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
} else {
strPtr = llvm.ConstNull(b.i8ptrType)
}
strObj := llvm.ConstNamedStruct(b.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen}) strObj := llvm.ConstNamedStruct(b.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
return strObj return strObj
} else if typ.Kind() == types.UnsafePointer { } else if typ.Kind() == types.UnsafePointer {
+2 -8
View File
@@ -32,7 +32,7 @@ func TestCompiler(t *testing.T) {
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version) t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
} }
target, err := compileopts.LoadTarget("wasm") 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)
} }
@@ -62,7 +62,6 @@ func TestCompiler(t *testing.T) {
"string.go", "string.go",
"float.go", "float.go",
"interface.go", "interface.go",
"func.go",
} }
for _, testCase := range tests { for _, testCase := range tests {
@@ -85,16 +84,11 @@ func TestCompiler(t *testing.T) {
mod, errs := CompilePackage(testCase, pkg, program.Package(pkg.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.Error(err) t.Log("error:", err)
} }
return return
} }
err = llvm.VerifyModule(mod, llvm.PrintMessageAction)
if err != nil {
t.Error(err)
}
// Optimize IR a little. // Optimize IR a little.
funcPasses := llvm.NewFunctionPassManagerForModule(mod) funcPasses := llvm.NewFunctionPassManagerForModule(mod)
defer funcPasses.Dispose() defer funcPasses.Dispose()
+3 -15
View File
@@ -25,13 +25,14 @@ func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context
// Closure is: {context, function pointer} // Closure is: {context, function pointer}
funcValueScalar = funcPtr funcValueScalar = funcPtr
case "switch": case "switch":
sigGlobal := c.getTypeCode(sig)
funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature" funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName) funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
if funcValueWithSignatureGlobal.IsNil() { if funcValueWithSignatureGlobal.IsNil() {
funcValueWithSignatureType := c.getLLVMRuntimeType("funcValueWithSignature") funcValueWithSignatureType := c.getLLVMRuntimeType("funcValueWithSignature")
funcValueWithSignature := llvm.ConstNamedStruct(funcValueWithSignatureType, []llvm.Value{ funcValueWithSignature := llvm.ConstNamedStruct(funcValueWithSignatureType, []llvm.Value{
llvm.ConstPtrToInt(funcPtr, c.uintptrType), llvm.ConstPtrToInt(funcPtr, c.uintptrType),
c.getFuncSignatureID(sig), sigGlobal,
}) })
funcValueWithSignatureGlobal = llvm.AddGlobal(c.mod, funcValueWithSignatureType, funcValueWithSignatureGlobalName) funcValueWithSignatureGlobal = llvm.AddGlobal(c.mod, funcValueWithSignatureType, funcValueWithSignatureGlobalName)
funcValueWithSignatureGlobal.SetInitializer(funcValueWithSignature) funcValueWithSignatureGlobal.SetInitializer(funcValueWithSignature)
@@ -49,19 +50,6 @@ func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context
return funcValue return funcValue
} }
// getFuncSignatureID returns a new external global for a given signature. This
// global reference is not real, it is only used during func lowering to assign
// signature types to functions and will then be removed.
func (c *compilerContext) getFuncSignatureID(sig *types.Signature) llvm.Value {
sigGlobalName := "reflect/types.funcid:" + getTypeCodeName(sig)
sigGlobal := c.mod.NamedGlobal(sigGlobalName)
if sigGlobal.IsNil() {
sigGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), sigGlobalName)
sigGlobal.SetGlobalConstant(true)
}
return sigGlobal
}
// extractFuncScalar returns some scalar that can be used in comparisons. It is // extractFuncScalar returns some scalar that can be used in comparisons. It is
// a cheap operation. // a cheap operation.
func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value { func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value {
@@ -83,7 +71,7 @@ func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (f
funcPtr = b.CreateExtractValue(funcValue, 1, "") funcPtr = b.CreateExtractValue(funcValue, 1, "")
case "switch": case "switch":
llvmSig := b.getRawFuncType(sig) llvmSig := b.getRawFuncType(sig)
sigGlobal := b.getFuncSignatureID(sig) sigGlobal := b.getTypeCode(sig)
funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "") funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
funcPtr = b.CreateIntToPtr(funcPtr, llvmSig, "") funcPtr = b.CreateIntToPtr(funcPtr, llvmSig, "")
default: default:
-3
View File
@@ -34,9 +34,6 @@ func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, p
} else { } else {
// The stack size is fixed at compile time. By emitting it here as a // The stack size is fixed at compile time. By emitting it here as a
// constant, it can be optimized. // constant, it can be optimized.
if b.DefaultStackSize == 0 {
b.addError(pos, "default stack size for goroutines is not set")
}
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false) stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
} }
case "coroutines": case "coroutines":
+16 -20
View File
@@ -46,7 +46,6 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
var references llvm.Value var references llvm.Value
var length int64 var length int64
var methodSet llvm.Value var methodSet llvm.Value
var ptrTo 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,25 +69,22 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
if _, ok := typ.Underlying().(*types.Interface); !ok { if _, ok := typ.Underlying().(*types.Interface); !ok {
methodSet = c.getTypeMethodSet(typ) methodSet = c.getTypeMethodSet(typ)
} }
if _, ok := typ.Underlying().(*types.Pointer); !ok { if !references.IsNil() || length != 0 || !methodSet.IsNil() {
ptrTo = c.getTypeCode(types.NewPointer(typ)) // Set the 'references' field of the runtime.typecodeID struct.
globalValue := llvm.ConstNull(global.Type().ElementType())
if !references.IsNil() {
globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
}
if length != 0 {
lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false)
globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
}
if !methodSet.IsNil() {
globalValue = llvm.ConstInsertValue(globalValue, methodSet, []uint32{2})
}
global.SetInitializer(globalValue)
global.SetLinkage(llvm.LinkOnceODRLinkage)
} }
globalValue := llvm.ConstNull(global.Type().ElementType())
if !references.IsNil() {
globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
}
if length != 0 {
lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false)
globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
}
if !methodSet.IsNil() {
globalValue = llvm.ConstInsertValue(globalValue, methodSet, []uint32{2})
}
if !ptrTo.IsNil() {
globalValue = llvm.ConstInsertValue(globalValue, ptrTo, []uint32{3})
}
global.SetInitializer(globalValue)
global.SetLinkage(llvm.LinkOnceODRLinkage)
global.SetGlobalConstant(true) global.SetGlobalConstant(true)
} }
return global return global
@@ -345,7 +341,7 @@ 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.typeid:" + getTypeCodeName(expr.AssertedType) globalName := "reflect/types.type:" + getTypeCodeName(expr.AssertedType) + "$id"
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName) assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
if assertedTypeCodeGlobal.IsNil() { if assertedTypeCodeGlobal.IsNil() {
// Create a new typecode global. // Create a new typecode global.
+5 -18
View File
@@ -139,17 +139,6 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
for _, attrName := range []string{"noalias", "nonnull"} { for _, attrName := range []string{"noalias", "nonnull"} {
llvmFn.AddAttributeAtIndex(0, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(attrName), 0)) llvmFn.AddAttributeAtIndex(0, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(attrName), 0))
} }
case "runtime.sliceAppend":
// Appending a slice will only read the to-be-appended slice, it won't
// be modified.
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
case "runtime.sliceCopy":
// Copying a slice won't capture any of the parameters.
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("writeonly"), 0))
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
case "runtime.trackPointer": case "runtime.trackPointer":
// This function is necessary for tracking pointers on the stack in a // This function is necessary for tracking pointers on the stack in a
// portable way (see gc_stack_portable.go). Indicate to the optimizer // portable way (see gc_stack_portable.go). Indicate to the optimizer
@@ -366,18 +355,16 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
// Set alignment from the //go:align comment. // Set alignment from the //go:align comment.
var alignInBits uint32 var alignInBits uint32
alignment := c.targetData.ABITypeAlignment(llvmType) if info.align < 0 || info.align&(info.align-1) != 0 {
if info.align > alignment {
alignment = info.align
}
if alignment <= 0 || alignment&(alignment-1) != 0 {
// Check for power-of-two (or 0). // Check for power-of-two (or 0).
// See: https://stackoverflow.com/a/108360 // See: https://stackoverflow.com/a/108360
c.addError(g.Pos(), "global variable alignment must be a positive power of two") c.addError(g.Pos(), "global variable alignment must be a positive power of two")
} else { } else {
// Set the alignment only when it is a power of two. // Set the alignment only when it is a power of two.
alignInBits = uint32(alignment) ^ uint32(alignment-1) alignInBits = uint32(info.align) ^ uint32(info.align-1)
llvmGlobal.SetAlignment(alignment) if info.align > c.targetData.ABITypeAlignment(llvmType) {
llvmGlobal.SetAlignment(info.align)
}
} }
if c.Debug && !info.extern { if c.Debug && !info.extern {
+2 -2
View File
@@ -1,7 +1,7 @@
; ModuleID = 'basic.go' ; ModuleID = 'basic.go'
source_filename = "basic.go" source_filename = "basic.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-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 = "wasm32--wasi" target triple = "i686--linux"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+2 -2
View File
@@ -1,7 +1,7 @@
; ModuleID = 'float.go' ; ModuleID = 'float.go'
source_filename = "float.go" source_filename = "float.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-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 = "wasm32--wasi" target triple = "i686--linux"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
-12
View File
@@ -1,12 +0,0 @@
package main
func foo(callback func(int)) {
callback(3)
}
func bar() {
foo(someFunc)
}
func someFunc(int) {
}
-47
View File
@@ -1,47 +0,0 @@
; ModuleID = 'func.go'
source_filename = "func.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32--wasi"
%runtime.funcValueWithSignature = type { i32, i8* }
@"reflect/types.funcid:func:{basic:int}{}" = external constant i8
@"main.someFunc$withSignature" = linkonce_odr constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @main.someFunc to i32), i8* @"reflect/types.funcid:func:{basic:int}{}" }
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 void @main.foo(i8* %callback.context, i32 %callback.funcptr, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = call i32 @runtime.getFuncPtr(i8* %callback.context, i32 %callback.funcptr, i8* nonnull @"reflect/types.funcid:func:{basic:int}{}", i8* undef, i8* null)
%1 = icmp eq i32 %0, 0
br i1 %1, label %fpcall.throw, label %fpcall.next
fpcall.throw: ; preds = %entry
call void @runtime.nilPanic(i8* undef, i8* null)
unreachable
fpcall.next: ; preds = %entry
%2 = inttoptr i32 %0 to void (i32, i8*, i8*)*
call void %2(i32 3, i8* %callback.context, i8* undef)
ret void
}
declare i32 @runtime.getFuncPtr(i8*, i32, i8* dereferenceable_or_null(1), i8*, i8*)
declare void @runtime.nilPanic(i8*, i8*)
define hidden void @main.bar(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
call void @main.foo(i8* undef, i32 ptrtoint (%runtime.funcValueWithSignature* @"main.someFunc$withSignature" to i32), i8* undef, i8* undef)
ret void
}
define hidden void @main.someFunc(i32 %arg0, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret void
}
+12 -13
View File
@@ -1,26 +1,25 @@
; ModuleID = 'interface.go' ; ModuleID = 'interface.go'
source_filename = "interface.go" source_filename = "interface.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-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 = "wasm32--wasi" target triple = "i686--linux"
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID* } %runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo* }
%runtime.interfaceMethodInfo = type { i8*, i32 } %runtime.interfaceMethodInfo = type { i8*, i32 }
%runtime._interface = type { i32, i8* } %runtime._interface = type { i32, i8* }
%runtime._string = type { i8*, i32 } %runtime._string = type { i8*, i32 }
@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* null, i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:basic:int" } @"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, %runtime.typecodeID* null } @"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, %runtime.typecodeID* 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, %runtime.typecodeID* @"reflect/types.type:pointer:named:error" } @"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, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" } @"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 @"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.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null } @"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: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, %runtime.typecodeID* 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 }
@"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, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }
@"func String() string" = external constant i8 @"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.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func String() string"]
@"reflect/types.typeid:basic:int" = external constant i8 @"reflect/types.type:basic:int$id" = external constant i8
@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"] @"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
@@ -52,7 +51,7 @@ entry:
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr { define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
entry: entry:
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.typeid:basic:int", i8* undef, i8* null) %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 br i1 %typecode, label %typeassert.ok, label %typeassert.next
typeassert.ok: ; preds = %entry typeassert.ok: ; preds = %entry
+2 -2
View File
@@ -1,7 +1,7 @@
; ModuleID = 'pointer.go' ; ModuleID = 'pointer.go'
source_filename = "pointer.go" source_filename = "pointer.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-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 = "wasm32--wasi" target triple = "i686--linux"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+4 -4
View File
@@ -1,7 +1,7 @@
; ModuleID = 'slice.go' ; ModuleID = 'slice.go'
source_filename = "slice.go" source_filename = "slice.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-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 = "wasm32--wasi" target triple = "i686--linux"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
@@ -60,7 +60,7 @@ entry:
ret { i32*, i32, i32 } %7 ret { i32*, i32, i32 } %7
} }
declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8* nocapture readonly, i32, i32, i32, i32, i8*, i8*) 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 { 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: entry:
@@ -85,4 +85,4 @@ entry:
ret i32 %copy.n ret i32 %copy.n
} }
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*) declare i32 @runtime.sliceCopy(i8*, i8*, i32, i32, i32, i8*, i8*)
-8
View File
@@ -1,13 +1,5 @@
package main package main
func someString() string {
return "foo"
}
func zeroLengthString() string {
return ""
}
func stringLen(s string) int { func stringLen(s string) int {
return len(s) return len(s)
} }
+2 -16
View File
@@ -1,11 +1,7 @@
; ModuleID = 'string.go' ; ModuleID = 'string.go'
source_filename = "string.go" source_filename = "string.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-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 = "wasm32--wasi" target triple = "i686--linux"
%runtime._string = type { i8*, i32 }
@"main.someString$string" = internal unnamed_addr constant [3 x i8] c"foo", align 1
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
@@ -14,16 +10,6 @@ entry:
ret void ret void
} }
define hidden %runtime._string @main.someString(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret %runtime._string { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"main.someString$string", i32 0, i32 0), i32 3 }
}
define hidden %runtime._string @main.zeroLengthString(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret %runtime._string zeroinitializer
}
define hidden i32 @main.stringLen(i8* %s.data, i32 %s.len, i8* %context, i8* %parentHandle) unnamed_addr { define hidden i32 @main.stringLen(i8* %s.data, i32 %s.len, i8* %context, i8* %parentHandle) unnamed_addr {
entry: entry:
ret i32 %s.len ret i32 %s.len
+1 -1
View File
@@ -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-20210325115028-e7b85195e81c tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4
) )
-2
View File
@@ -59,5 +59,3 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
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-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs= tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs=
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4/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=
+2 -3
View File
@@ -171,9 +171,8 @@ func getGoroot() string {
switch runtime.GOOS { switch runtime.GOOS {
case "linux": case "linux":
candidates = []string{ candidates = []string{
"/usr/local/go", // manually installed "/usr/local/go", // manually installed
"/usr/lib/go", // from the distribution "/usr/lib/go", // from the distribution
"/snap/go/current/", // installed using snap
} }
case "darwin": case "darwin":
candidates = []string{ candidates = []string{
+1 -17
View File
@@ -25,7 +25,6 @@ type function struct {
// basicBlock represents a LLVM basic block and contains a slice of // basicBlock represents a LLVM basic block and contains a slice of
// instructions. The last instruction must be a terminator instruction. // instructions. The last instruction must be a terminator instruction.
type basicBlock struct { type basicBlock struct {
phiNodes []instruction
instructions []instruction instructions []instruction
} }
@@ -136,15 +135,6 @@ func (r *runner) compileFunction(llvmFn llvm.Value) *function {
default: default:
panic("unknown number of operands") panic("unknown number of operands")
} }
case llvm.Switch:
// A switch is an array of (value, label) pairs, of which the
// first one indicates the to-switch value and the default
// label.
numOperands := llvmInst.OperandsCount()
for i := 0; i < numOperands; i += 2 {
inst.operands = append(inst.operands, r.getValue(llvmInst.Operand(i)))
inst.operands = append(inst.operands, literalValue{uint32(blockIndices[llvmInst.Operand(i+1)])})
}
case llvm.PHI: case llvm.PHI:
inst.name = llvmInst.Name() inst.name = llvmInst.Name()
incomingCount := inst.llvmInst.IncomingCount() incomingCount := inst.llvmInst.IncomingCount()
@@ -347,13 +337,7 @@ func (r *runner) compileFunction(llvmFn llvm.Value) *function {
// This error is handled when actually trying to interpret this // This error is handled when actually trying to interpret this
// instruction (to not trigger on code that won't be executed). // instruction (to not trigger on code that won't be executed).
} }
if inst.opcode == llvm.PHI { bb.instructions = append(bb.instructions, inst)
// PHI nodes need to be treated specially, see the comment in
// interpreter.go for an explanation.
bb.phiNodes = append(bb.phiNodes, inst)
} else {
bb.instructions = append(bb.instructions, inst)
}
} }
} }
return fn return fn
+1 -1
View File
@@ -13,9 +13,9 @@ import (
func TestInterp(t *testing.T) { func TestInterp(t *testing.T) {
for _, name := range []string{ for _, name := range []string{
"basic", "basic",
"phi",
"slice-copy", "slice-copy",
"consteval", "consteval",
"map",
"interface", "interface",
} { } {
name := name // make tc local to this closure name := name // make tc local to this closure
+92 -84
View File
@@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"math" "math"
"os" "os"
"strconv"
"strings" "strings"
"time" "time"
@@ -34,50 +33,6 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
lastBB := -1 // last basic block is undefined, only defined after a branch lastBB := -1 // last basic block is undefined, only defined after a branch
var operands []value var operands []value
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ { for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
if instIndex == 0 {
// This is the start of a new basic block.
// There may be PHI nodes that need to be resolved. Resolve all PHI
// nodes before continuing with regular instructions.
// PHI nodes need to be treated specially because they can have a
// mutual dependency:
// for.loop:
// %a = phi i8 [ 1, %entry ], [ %b, %for.loop ]
// %b = phi i8 [ 3, %entry ], [ %a, %for.loop ]
// If these PHI nodes are processed like a regular instruction, %a
// and %b are both 3 on the second iteration of the loop because %b
// loads the value of %a from the second iteration, while it should
// load the value from the previous iteration. The correct behavior
// is that these two values swap each others place on each
// iteration.
var phiValues []value
var phiIndices []int
for _, inst := range bb.phiNodes {
var result value
for i := 0; i < len(inst.operands); i += 2 {
if int(inst.operands[i].(literalValue).value.(uint32)) == lastBB {
incoming := inst.operands[i+1]
if local, ok := incoming.(localValue); ok {
result = locals[fn.locals[local.value]]
} else {
result = incoming
}
break
}
}
if r.debug {
fmt.Fprintln(os.Stderr, indent+"phi", inst.operands, "->", result)
}
if result == nil {
panic("could not find PHI input")
}
phiValues = append(phiValues, result)
phiIndices = append(phiIndices, inst.localIndex)
}
for i, value := range phiValues {
locals[phiIndices[i]] = value
}
}
inst := bb.instructions[instIndex] inst := bb.instructions[instIndex]
operands = operands[:0] operands = operands[:0]
isRuntimeInst := false isRuntimeInst := false
@@ -148,24 +103,27 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
default: default:
panic("unknown operands length") panic("unknown operands length")
} }
case llvm.Switch: break // continue with next block
// Switch statement: [value, defaultLabel, case0, label0, case1, label1, ...] case llvm.PHI:
value := operands[0].Uint() var result value
targetLabel := operands[1].Uint() // default label for i := 0; i < len(inst.operands); i += 2 {
// Do a lazy switch by iterating over all cases. if int(inst.operands[i].(literalValue).value.(uint32)) == lastBB {
for i := 2; i < len(operands); i += 2 { incoming := inst.operands[i+1]
if value == operands[i].Uint() { if local, ok := incoming.(localValue); ok {
targetLabel = operands[i+1].Uint() result = locals[fn.locals[local.value]]
} else {
result = incoming
}
break break
} }
} }
lastBB = currentBB
currentBB = int(targetLabel)
bb = fn.blocks[currentBB]
instIndex = -1 // start at 0 the next cycle
if r.debug { if r.debug {
fmt.Fprintln(os.Stderr, indent+"switch", operands, "->", currentBB) fmt.Fprintln(os.Stderr, indent+"phi", inst.operands, "->", result)
} }
if result == nil {
panic("could not find PHI input")
}
locals[inst.localIndex] = result
case llvm.Select: case llvm.Select:
// Select is much like a ternary operator: it picks a result from // Select is much like a ternary operator: it picks a result from
// the second and third operand based on the boolean first operand. // the second and third operand based on the boolean first operand.
@@ -197,7 +155,7 @@ 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 strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet" || callFn.name == "os.runtime_args": 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:
// * 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.)
@@ -205,9 +163,6 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// * runtime.hashmapGet tries to access the map value directly. // * runtime.hashmapGet tries to access the map value directly.
// This is not possible as the map value is treated as a special // This is not possible as the map value is treated as a special
// kind of object in this package. // kind of object in this package.
// * os.runtime_args reads globals that are initialized outside
// the view of the interp package so it always needs to be run
// at runtime.
err := r.runAtRuntime(fn, inst, locals, &mem, indent) err := r.runAtRuntime(fn, inst, locals, &mem, indent)
if err != nil { if err != nil {
return nil, mem, err return nil, mem, err
@@ -373,7 +328,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
return nil, mem, r.errorAt(inst, err) return nil, mem, r.errorAt(inst, err)
} }
actualType := actualTypePtrToInt.Operand(0) actualType := actualTypePtrToInt.Operand(0)
if strings.TrimPrefix(actualType.Name(), "reflect/types.type:") == strings.TrimPrefix(assertedType.Name(), "reflect/types.typeid:") { if actualType.Name()+"$id" == assertedType.Name() {
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)}
@@ -460,6 +415,74 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
return nil, mem, r.errorAt(inst, errors.New("could not find method: "+signature.Name())) return nil, mem, r.errorAt(inst, errors.New("could not find method: "+signature.Name()))
} }
locals[inst.localIndex] = r.getValue(method) locals[inst.localIndex] = r.getValue(method)
case callFn.name == "runtime.hashmapMake":
// Create a new map.
hashmapPointerType := inst.llvmInst.Type()
keySize := uint32(operands[1].Uint())
valueSize := uint32(operands[2].Uint())
m := newMapValue(r, hashmapPointerType, keySize, valueSize)
alloc := object{
llvmType: hashmapPointerType,
globalName: r.pkgName + "$map",
buffer: m,
size: m.len(r),
}
index := len(r.objects)
r.objects = append(r.objects, alloc)
// Create a pointer to this map. Maps are reference types, so
// are implemented as pointers.
ptr := newPointerValue(r, index, 0)
if r.debug {
fmt.Fprintln(os.Stderr, indent+"runtime.hashmapMake:", keySize, valueSize, "->", ptr)
}
locals[inst.localIndex] = ptr
case callFn.name == "runtime.hashmapBinarySet":
// Do a mapassign operation with a binary key (that is, without
// a string key).
if r.debug {
fmt.Fprintln(os.Stderr, indent+"runtime.hashmapBinarySet:", operands[1:])
}
mapPtr, err := operands[1].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
m := mem.getWritable(mapPtr.index()).buffer.(*mapValue)
keyPtr, err := operands[2].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
valuePtr, err := operands[3].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
err = m.putBinary(&mem, keyPtr, valuePtr)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
case callFn.name == "runtime.hashmapStringSet":
// Do a mapassign operation with a string key.
if r.debug {
fmt.Fprintln(os.Stderr, indent+"runtime.hashmapBinarySet:", operands[1:])
}
mapPtr, err := operands[1].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
m := mem.getWritable(mapPtr.index()).buffer.(*mapValue)
stringPtr, err := operands[2].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
stringLen := operands[3].Uint()
valuePtr, err := operands[4].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
err = m.putString(&mem, stringPtr, stringLen, valuePtr)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
default: default:
if len(callFn.blocks) == 0 { if len(callFn.blocks) == 0 {
// Call to a function declaration without a definition // Call to a function declaration without a definition
@@ -931,31 +954,16 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
result = r.builder.CreateBitCast(operands[0], inst.llvmInst.Type(), inst.name) result = r.builder.CreateBitCast(operands[0], inst.llvmInst.Type(), inst.name)
case llvm.ExtractValue: case llvm.ExtractValue:
indices := inst.llvmInst.Indices() indices := inst.llvmInst.Indices()
// Note: the Go LLVM API doesn't support multiple indices, so simulate if len(indices) != 1 {
// this operation with some extra extractvalue instructions. Hopefully panic("expected exactly one index")
// this is optimized to a single instruction.
agg := operands[0]
for i := 0; i < len(indices)-1; i++ {
agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg")
} }
result = r.builder.CreateExtractValue(agg, int(indices[len(indices)-1]), inst.name) result = r.builder.CreateExtractValue(operands[0], int(indices[0]), inst.name)
case llvm.InsertValue: case llvm.InsertValue:
indices := inst.llvmInst.Indices() indices := inst.llvmInst.Indices()
// Similar to extractvalue, we're working around a limitation in the Go if len(indices) != 1 {
// LLVM API here by splitting the insertvalue into multiple instructions panic("expected exactly one index")
// if there is more than one operand.
agg := operands[0]
aggregates := []llvm.Value{agg}
for i := 0; i < len(indices)-1; i++ {
agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg"+strconv.Itoa(i))
aggregates = append(aggregates, agg)
} }
result = operands[1] result = r.builder.CreateInsertValue(operands[0], operands[1], int(indices[0]), inst.name)
for i := len(indices) - 1; i >= 0; i-- {
agg := aggregates[i]
result = r.builder.CreateInsertValue(agg, result, int(indices[i]), inst.name+".insertvalue"+strconv.Itoa(i))
}
case llvm.Add: case llvm.Add:
result = r.builder.CreateAdd(operands[0], operands[1], inst.name) result = r.builder.CreateAdd(operands[0], operands[1], inst.name)
case llvm.Sub: case llvm.Sub:
+282
View File
@@ -640,6 +640,288 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
return gep, nil return gep, nil
} }
// mapValue implements a Go map which is created at compile time and stored as a
// global variable.
// The value itself is only used as part of an object (object.buffer). Maps are
// reference types aka pointers, so it can only be used as a pointerValue, not
// directly.
type mapValue struct {
r *runner
pkgName string
size uint32 // byte size of runtime.hashmap
hashmap llvm.Value
keyIsString bool
keys []interface{} // either rawValue (for binary key) or mapStringKey (for string key)
values []rawValue
keySize uint32
valueSize uint32
}
type mapStringKey struct {
buf pointerValue
size uint64
data []uint64
}
func newMapValue(r *runner, hashmapPointerType llvm.Type, keySize, valueSize uint32) *mapValue {
size := uint32(r.targetData.TypeAllocSize(hashmapPointerType.ElementType()))
return &mapValue{
r: r,
pkgName: r.pkgName,
size: size,
keySize: keySize,
valueSize: valueSize,
}
}
func (v *mapValue) len(r *runner) uint32 {
return v.size
}
func (v *mapValue) clone() value {
// Return a copy of mapValue.
clone := *v
clone.keys = append([]interface{}{}, clone.keys...)
clone.values = append([]rawValue{}, clone.values...)
return &clone
}
func (v *mapValue) asPointer(r *runner) (pointerValue, error) {
panic("interp: mapValue.asPointer")
}
func (v *mapValue) asRawValue(r *runner) rawValue {
panic("interp: mapValue.asRawValue")
}
func (v *mapValue) Uint() uint64 {
panic("interp: mapValue.Uint")
}
func (v *mapValue) Int() int64 {
panic("interp: mapValue.Int")
}
// Temporary struct to collect data before turning this into a hashmap bucket
// LLVM value.
type mapBucket struct {
m *mapValue
tophash [8]uint8
keys []rawValue // can have up to 8 keys
values []rawValue // can have up to 8 values, len(keys) == len(values)
}
// create returns a (pointer to a) buffer structurally equivalent to
// runtime.hashmapBucket.
func (b *mapBucket) create(ctx llvm.Context, nextBucket llvm.Value, mem *memoryView) llvm.Value {
// Create tophash array.
int8Type := ctx.Int8Type()
tophashValues := make([]llvm.Value, 8)
for i := range tophashValues {
tophashValues[i] = llvm.ConstInt(int8Type, uint64(b.tophash[i]), false)
}
tophash := llvm.ConstArray(int8Type, tophashValues)
// Create next pointer (if not set).
if nextBucket.IsNil() {
nextBucket = llvm.ConstNull(llvm.PointerType(int8Type, 0))
}
// Create data for keys.
var keyValues []llvm.Value
for _, key := range b.keys {
keyValue, err := key.rawLLVMValue(mem)
if err != nil {
panic(err)
}
keyValues = append(keyValues, keyValue)
}
if len(b.keys) < 8 {
keyValues = append(keyValues, llvm.ConstNull(llvm.ArrayType(int8Type, int(b.m.keySize)*(8-len(b.keys)))))
}
keyValue := ctx.ConstStruct(keyValues, false)
if checks && uint32(b.m.r.targetData.TypeAllocSize(keyValue.Type())) != b.m.keySize*8 {
panic("key size invalid")
}
// Create data for values.
var valueValues []llvm.Value
for _, value := range b.values {
v, err := value.rawLLVMValue(mem)
if err != nil {
panic(err)
}
valueValues = append(valueValues, v)
}
if len(b.values) < 8 {
valueValues = append(valueValues, llvm.ConstNull(llvm.ArrayType(int8Type, int(b.m.valueSize)*(8-len(b.values)))))
}
valueValue := ctx.ConstStruct(valueValues, false)
if checks && uint32(b.m.r.targetData.TypeAllocSize(valueValue.Type())) != b.m.valueSize*8 {
panic("value size invalid")
}
// Create the bucket.
bucketInitializer := ctx.ConstStruct([]llvm.Value{
tophash,
nextBucket,
keyValue,
valueValue,
}, false)
bucket := llvm.AddGlobal(b.m.r.mod, bucketInitializer.Type(), b.m.pkgName+"$mapbucket")
bucket.SetInitializer(bucketInitializer)
bucket.SetLinkage(llvm.InternalLinkage)
bucket.SetUnnamedAddr(true)
return bucket
}
func (v *mapValue) toLLVMValue(hashmapType llvm.Type, mem *memoryView) (llvm.Value, error) {
if !v.hashmap.IsNil() {
return v.hashmap, nil
}
// Create a slice of buckets with all the keys and values in the hashmap.
var buckets []*mapBucket
var bucket *mapBucket
for i, key := range v.keys {
var data []uint64
var keyValue rawValue
switch key := key.(type) {
case mapStringKey:
data = key.data
keyValue = newRawValue(v.keySize)
// runtime._string is {ptr, length}
for i := uint32(0); i < v.keySize/2; i++ {
keyValue.buf[i] = key.buf.pointer
}
copy(keyValue.buf[v.keySize/2:], literalValue{key.size}.asRawValue(v.r).buf)
case rawValue:
if key.hasPointer() {
return llvm.Value{}, errors.New("interp: todo: map key with pointer")
}
data = key.buf
keyValue = key
default:
return llvm.Value{}, errors.New("interp: unknown map key type")
}
buf := make([]byte, len(data))
for i, p := range data {
buf[i] = byte(p)
}
hash := v.hash(buf)
if i%8 == 0 {
bucket = &mapBucket{m: v}
buckets = append(buckets, bucket)
}
bucket.tophash[i%8] = v.topHash(hash)
bucket.keys = append(bucket.keys, keyValue)
bucket.values = append(bucket.values, v.values[i])
}
// Convert these buckets into LLVM global variables.
ctx := v.r.mod.Context()
var nextBucket llvm.Value
for i := len(buckets) - 1; i >= 0; i-- {
bucket = buckets[i]
bucketValue := bucket.create(ctx, nextBucket, mem)
nextBucket = bucketValue
}
firstBucket := nextBucket
if firstBucket.IsNil() {
firstBucket = llvm.ConstNull(mem.r.i8ptrType)
} else {
firstBucket = llvm.ConstBitCast(firstBucket, mem.r.i8ptrType)
}
// Create the hashmap itself, pointing to these buckets.
hashmapPointerType := llvm.PointerType(hashmapType, 0)
hashmap := llvm.ConstNamedStruct(hashmapType, []llvm.Value{
llvm.ConstPointerNull(hashmapPointerType), // next
firstBucket, // buckets
llvm.ConstInt(hashmapType.StructElementTypes()[2], uint64(len(v.keys)), false), // count
llvm.ConstInt(ctx.Int8Type(), uint64(v.keySize), false), // keySize
llvm.ConstInt(ctx.Int8Type(), uint64(v.valueSize), false), // valueSize
llvm.ConstInt(ctx.Int8Type(), 0, false), // bucketBits
})
v.hashmap = hashmap
return v.hashmap, nil
}
// putString does a map assign operation, assuming that the map is of type
// map[string]T.
func (v *mapValue) putString(mem *memoryView, stringBuf pointerValue, stringLen uint64, valuePtr pointerValue) error {
if !v.hashmap.IsNil() {
return errMapAlreadyCreated
}
value := mem.load(valuePtr, v.valueSize)
stringValue := mem.load(stringBuf, uint32(stringLen)).asRawValue(v.r)
if stringValue.hasPointer() {
panic("interp: string contains pointer")
}
// TODO: avoid duplicate keys
v.keys = append(v.keys, mapStringKey{stringBuf, stringLen, stringValue.buf})
v.values = append(v.values, value.asRawValue(v.r))
v.keyIsString = true
return nil
}
// putBinary does a map assign operation for binary data (e.g. [3]int etc). The
// key must not contain pointer values.
func (v *mapValue) putBinary(mem *memoryView, keyPtr, valuePtr pointerValue) error {
if !v.hashmap.IsNil() {
return errMapAlreadyCreated
}
key := mem.load(keyPtr, v.keySize)
value := mem.load(valuePtr, v.valueSize)
// Sanity checks.
if v.keySize != key.len(mem.r) || v.valueSize != value.len(mem.r) {
// This is a bug (not unhandled input), so panic.
panic("interp: key or value size mismatch")
}
if v.keyIsString {
panic("cannot put binary keys in string map")
}
// TODO: avoid duplicate keys
v.keys = append(v.keys, key.asRawValue(v.r))
v.values = append(v.values, value.asRawValue(v.r))
return nil
}
// Get FNV-1a hash of this string.
//
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash
func (v *mapValue) hash(data []byte) uint32 {
var result uint32 = 2166136261 // FNV offset basis
for _, c := range data {
result ^= uint32(c)
result *= 16777619 // FNV prime
}
return result
}
// Get the topmost 8 bits of the hash, without using a special value (like 0).
func (v *mapValue) topHash(hash uint32) uint8 {
tophash := uint8(hash >> 24)
if tophash < 1 {
// 0 means empty slot, so make it bigger.
tophash++
}
return tophash
}
func (v *mapValue) String() string {
return "<map keySize=" + strconv.Itoa(int(v.keySize)) + " valueSize=" + strconv.Itoa(int(v.valueSize)) + ">"
}
// rawValue is a raw memory buffer that can store either pointers or regular // rawValue is a raw memory buffer that can store either pointers or regular
// data. This is the fallback data for everything that isn't clearly a // data. This is the fallback data for everything that isn't clearly a
// literalValue or pointerValue. // literalValue or pointerValue.
-35
View File
@@ -8,7 +8,6 @@ target triple = "x86_64--linux"
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1] @main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
@main.exposedValue1 = global i16 0 @main.exposedValue1 = global i16 0
@main.exposedValue2 = global i16 0 @main.exposedValue2 = global i16 0
@main.insertedValue = global {i8, i32, {float, {i64, i16}}} zeroinitializer
declare void @runtime.printint64(i64) unnamed_addr declare void @runtime.printint64(i64) unnamed_addr
@@ -66,19 +65,6 @@ entry:
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*)) call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
store i16 7, i16* @main.exposedValue2 store i16 7, i16* @main.exposedValue2
; Test switch statement.
%switch1 = call i64 @testSwitch(i64 1) ; 1 returns 6
%switch2 = call i64 @testSwitch(i64 9) ; 9 returns the default value -1
call void @runtime.printint64(i64 %switch1)
call void @runtime.printint64(i64 %switch2)
; Test extractvalue/insertvalue with multiple operands.
%agg = call {i8, i32, {float, {i64, i16}}} @nestedStruct()
%elt = extractvalue {i8, i32, {float, {i64, i16}}} %agg, 2, 1, 0
call void @runtime.printint64(i64 %elt)
%agg2 = insertvalue {i8, i32, {float, {i64, i16}}} %agg, i64 5, 2, 1, 0
store {i8, i32, {float, {i64, i16}}} %agg2, {i8, i32, {float, {i64, i16}}}* @main.insertedValue
ret void ret void
} }
@@ -101,24 +87,3 @@ entry:
store i16 8, i16* @main.exposedValue2 store i16 8, i16* @main.exposedValue2
ret void ret void
} }
define i64 @testSwitch(i64 %val) {
entry:
; Test switch statement.
switch i64 %val, label %otherwise [ i64 0, label %zero
i64 1, label %one
i64 2, label %two ]
zero:
ret i64 5
one:
ret i64 6
two:
ret i64 7
otherwise:
ret i64 -1
}
declare {i8, i32, {float, {i64, i16}}} @nestedStruct()
-37
View File
@@ -7,7 +7,6 @@ target triple = "x86_64--linux"
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1] @main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
@main.exposedValue1 = global i16 0 @main.exposedValue1 = global i16 0
@main.exposedValue2 = local_unnamed_addr global i16 0 @main.exposedValue2 = local_unnamed_addr global i16 0
@main.insertedValue = local_unnamed_addr global { i8, i32, { float, { i64, i16 } } } zeroinitializer
declare void @runtime.printint64(i64) unnamed_addr declare void @runtime.printint64(i64) unnamed_addr
@@ -26,19 +25,6 @@ entry:
store i16 5, i16* @main.exposedValue1 store i16 5, i16* @main.exposedValue1
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*)) call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
store i16 7, i16* @main.exposedValue2 store i16 7, i16* @main.exposedValue2
call void @runtime.printint64(i64 6)
call void @runtime.printint64(i64 -1)
%agg = call { i8, i32, { float, { i64, i16 } } } @nestedStruct()
%elt.agg = extractvalue { i8, i32, { float, { i64, i16 } } } %agg, 2
%elt.agg1 = extractvalue { float, { i64, i16 } } %elt.agg, 1
%elt = extractvalue { i64, i16 } %elt.agg1, 0
call void @runtime.printint64(i64 %elt)
%agg2.agg0 = extractvalue { i8, i32, { float, { i64, i16 } } } %agg, 2
%agg2.agg1 = extractvalue { float, { i64, i16 } } %agg2.agg0, 1
%agg2.insertvalue2 = insertvalue { i64, i16 } %agg2.agg1, i64 5, 0
%agg2.insertvalue1 = insertvalue { float, { i64, i16 } } %agg2.agg0, { i64, i16 } %agg2.insertvalue2, 1
%agg2.insertvalue0 = insertvalue { i8, i32, { float, { i64, i16 } } } %agg, { float, { i64, i16 } } %agg2.insertvalue1, 2
store { i8, i32, { float, { i64, i16 } } } %agg2.insertvalue0, { i8, i32, { float, { i64, i16 } } }* @main.insertedValue
ret void ret void
} }
@@ -58,26 +44,3 @@ entry:
store i16 8, i16* @main.exposedValue2 store i16 8, i16* @main.exposedValue2
ret void ret void
} }
define i64 @testSwitch(i64 %val) local_unnamed_addr {
entry:
switch i64 %val, label %otherwise [
i64 0, label %zero
i64 1, label %one
i64 2, label %two
]
zero: ; preds = %entry
ret i64 5
one: ; preds = %entry
ret i64 6
two: ; preds = %entry
ret i64 7
otherwise: ; preds = %entry
ret i64 -1
}
declare { i8, i32, { float, { i64, i16 } } } @nestedStruct() local_unnamed_addr
+2 -2
View File
@@ -6,7 +6,7 @@ target triple = "x86_64--linux"
@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, %runtime.interfaceMethodInfo* null } @"reflect/types.type:named:main.foo" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i64 0, %runtime.interfaceMethodInfo* null }
@"reflect/types.typeid:named:main.foo" = external constant i8 @"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
@@ -21,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.typecodeID* @"reflect/types.type:named:main.foo" to i64), i8* @"reflect/types.typeid: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
} }
+74
View File
@@ -0,0 +1,74 @@
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "armv6m-none-eabi"
%runtime._string = type { i8*, i32 }
%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 }
@main.m = global %runtime.hashmap* null
@main.binaryMap = global %runtime.hashmap* null
@main.stringMap = global %runtime.hashmap* null
@main.init.string = internal unnamed_addr constant [7 x i8] c"CONNECT"
declare %runtime.hashmap* @runtime.hashmapMake(i8, i8, i32, i8* %context, i8* %parentHandle)
declare void @runtime.hashmapBinarySet(%runtime.hashmap*, i8*, i8*, i8* %context, i8* %parentHandle)
declare void @runtime.hashmapStringSet(%runtime.hashmap*, i8*, i32, i8*, i8* %context, i8* %parentHandle)
declare void @llvm.lifetime.end.p0i8(i64, i8*)
declare void @llvm.lifetime.start.p0i8(i64, i8*)
define void @runtime.initAll() unnamed_addr {
entry:
call void @main.init(i8* undef, i8* null)
ret void
}
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
; Test that hashmap optimizations generally work (even with lifetimes).
%hashmap.key = alloca i8
%hashmap.value = alloca %runtime._string
%0 = call %runtime.hashmap* @runtime.hashmapMake(i8 1, i8 8, i32 1, i8* undef, i8* null)
%hashmap.value.bitcast = bitcast %runtime._string* %hashmap.value to i8*
call void @llvm.lifetime.start.p0i8(i64 8, i8* %hashmap.value.bitcast)
store %runtime._string { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7 }, %runtime._string* %hashmap.value
call void @llvm.lifetime.start.p0i8(i64 1, i8* %hashmap.key)
store i8 1, i8* %hashmap.key
call void @runtime.hashmapBinarySet(%runtime.hashmap* %0, i8* %hashmap.key, i8* %hashmap.value.bitcast, i8* undef, i8* null)
call void @llvm.lifetime.end.p0i8(i64 1, i8* %hashmap.key)
call void @llvm.lifetime.end.p0i8(i64 8, i8* %hashmap.value.bitcast)
store %runtime.hashmap* %0, %runtime.hashmap** @main.m
; Other tests, that can be done in a separate function.
call void @main.testNonConstantBinarySet()
call void @main.testNonConstantStringSet()
ret void
}
; Test that a map loaded from a global can still be used for mapassign
; operations (with binary keys).
define internal void @main.testNonConstantBinarySet() {
%hashmap.key = alloca i8
%hashmap.value = alloca i8
; Create hashmap from global.
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 1, i8 1, i32 1, i8* undef, i8* null)
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.binaryMap
%map = load %runtime.hashmap*, %runtime.hashmap** @main.binaryMap
; Do the binary set to the newly loaded map.
store i8 1, i8* %hashmap.key
store i8 2, i8* %hashmap.value
call void @runtime.hashmapBinarySet(%runtime.hashmap* %map, i8* %hashmap.key, i8* %hashmap.value, i8* undef, i8* null)
ret void
}
; Test that a map loaded from a global can still be used for mapassign
; operations (with string keys).
define internal void @main.testNonConstantStringSet() {
%hashmap.value = alloca i8
; Create hashmap from global.
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 8, i8 1, i32 1, i8* undef, i8* null)
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.stringMap
%map = load %runtime.hashmap*, %runtime.hashmap** @main.stringMap
; Do the string set to the newly loaded map.
store i8 2, i8* %hashmap.value
call void @runtime.hashmapStringSet(%runtime.hashmap* %map, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7, i8* %hashmap.value, i8* undef, i8* null)
ret void
}
+20
View File
@@ -0,0 +1,20 @@
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "armv6m-none-eabi"
%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 }
@main.m = local_unnamed_addr global %runtime.hashmap* @"main$map"
@main.binaryMap = local_unnamed_addr global %runtime.hashmap* @"main$map.1"
@main.stringMap = local_unnamed_addr global %runtime.hashmap* @"main$map.3"
@main.init.string = internal unnamed_addr constant [7 x i8] c"CONNECT"
@"main$map" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } }, { [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } }* @"main$mapbucket", i32 0, i32 0, i32 0), i32 1, i8 1, i8 8, i8 0 }
@"main$mapbucket" = internal unnamed_addr global { [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, { i8, [7 x i8] } { i8 1, [7 x i8] zeroinitializer }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } { { [7 x i8]*, [4 x i8] } { [7 x i8]* @main.init.string, [4 x i8] c"\07\00\00\00" }, [56 x i8] zeroinitializer } }
@"main$map.1" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } }, { [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } }* @"main$mapbucket.2", i32 0, i32 0, i32 0), i32 1, i8 1, i8 1, i8 0 }
@"main$mapbucket.2" = internal unnamed_addr global { [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, { i8, [7 x i8] } { i8 1, [7 x i8] zeroinitializer }, { i8, [7 x i8] } { i8 2, [7 x i8] zeroinitializer } }
@"main$map.3" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } }, { [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } }* @"main$mapbucket.4", i32 0, i32 0, i32 0), i32 1, i8 8, i8 1, i8 0 }
@"main$mapbucket.4" = internal unnamed_addr global { [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } } { [8 x i8] c"x\00\00\00\00\00\00\00", i8* null, { { [7 x i8]*, [4 x i8] }, [56 x i8] } { { [7 x i8]*, [4 x i8] } { [7 x i8]* @main.init.string, [4 x i8] c"\07\00\00\00" }, [56 x i8] zeroinitializer }, { i8, [7 x i8] } { i8 2, [7 x i8] zeroinitializer } }
define void @runtime.initAll() unnamed_addr {
entry:
ret void
}
-31
View File
@@ -1,31 +0,0 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux"
@main.phiNodesResultA = global i8 0
@main.phiNodesResultB = global i8 0
define void @runtime.initAll() {
call void @main.init()
ret void
}
; PHI nodes always use the value from the previous block, even in a loop. This
; means that the loop below should swap the values %a and %b on each iteration.
; Previously there was a bug which resulted in %b getting the value 3 on the
; second iteration while it should have gotten 1 (from the first iteration of
; %for.loop).
define internal void @main.init() {
entry:
br label %for.loop
for.loop:
%a = phi i8 [ 1, %entry ], [ %b, %for.loop ]
%b = phi i8 [ 3, %entry ], [ %a, %for.loop ]
%icmp = icmp eq i8 %a, 3
br i1 %icmp, label %for.done, label %for.loop
for.done:
store i8 %a, i8* @main.phiNodesResultA
store i8 %b, i8* @main.phiNodesResultB
ret void
}
-9
View File
@@ -1,9 +0,0 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux"
@main.phiNodesResultA = local_unnamed_addr global i8 3
@main.phiNodesResultB = local_unnamed_addr global i8 1
define void @runtime.initAll() local_unnamed_addr {
ret void
}
-10
View File
@@ -23,13 +23,3 @@ type Error struct {
func (e Error) Error() string { func (e Error) Error() string {
return e.Err.Error() return e.Err.Error()
} }
// Error returned when loading a *Program for a test binary but no test files
// are present.
type NoTestFilesError struct {
ImportPath string
}
func (e NoTestFilesError) Error() string {
return "no test files"
}
-6
View File
@@ -210,12 +210,6 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
p.Packages[pkg.ImportPath] = pkg p.Packages[pkg.ImportPath] = pkg
} }
if config.TestConfig.CompileTestBinary && !strings.HasSuffix(p.sorted[len(p.sorted)-1].ImportPath, ".test") {
// Trying to compile a test binary but there are no test files in this
// package.
return p, NoTestFilesError{p.sorted[len(p.sorted)-1].ImportPath}
}
return p, nil return p, nil
} }
+105 -236
View File
@@ -13,13 +13,11 @@ import (
"os/exec" "os/exec"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"regexp"
"runtime" "runtime"
"strings" "strings"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/google/shlex"
"github.com/mattn/go-colorable" "github.com/mattn/go-colorable"
"github.com/tinygo-org/tinygo/builder" "github.com/tinygo-org/tinygo/builder"
"github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/compileopts"
@@ -30,7 +28,6 @@ import (
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
"go.bug.st/serial" "go.bug.st/serial"
"go.bug.st/serial/enumerator"
) )
var ( var (
@@ -136,17 +133,15 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
}) })
} }
// Test runs the tests in the given package. Returns whether the test passed and // Test runs the tests in the given package.
// possibly an error if the test failed to run. func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, outpath string) error {
func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, outpath string) (bool, error) {
options.TestConfig.CompileTestBinary = true options.TestConfig.CompileTestBinary = true
config, err := builder.NewConfig(options) config, err := builder.NewConfig(options)
if err != nil { if err != nil {
return false, err return err
} }
var passed bool return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
err = 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 == "" {
@@ -160,78 +155,48 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
// Do not run the test. // Do not run the test.
return nil return nil
} }
if len(config.Target.Emulator) == 0 {
// Run the test. // Run directly.
start := time.Now() cmd := executeCommand(config.Options, result.Binary)
var err error cmd.Stdout = os.Stdout
passed, err = runPackageTest(config, result) cmd.Stderr = os.Stderr
if err != nil { cmd.Dir = result.MainDir
return err err := cmd.Run()
} if err != nil {
duration := time.Since(start) // Propagate the exit code
if err, ok := err.(*exec.ExitError); ok {
// Print the result. os.Exit(err.ExitCode())
importPath := strings.TrimSuffix(result.ImportPath, ".test") }
if passed { return &commandError{"failed to run compiled binary", result.Binary, err}
fmt.Printf("ok \t%s\t%.3fs\n", importPath, duration.Seconds()) }
return nil
} else { } else {
fmt.Printf("FAIL\t%s\t%.3fs\n", importPath, duration.Seconds()) // Run in an emulator.
args := append(config.Target.Emulator[1:], result.Binary)
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...)
buf := &bytes.Buffer{}
w := io.MultiWriter(os.Stdout, buf)
cmd.Stdout = w
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
if err, ok := err.(*exec.ExitError); !ok || !err.Exited() {
// Workaround for QEMU which always exits with an error.
return &commandError{"failed to run emulator with", result.Binary, err}
}
}
testOutput := string(buf.Bytes())
if testOutput == "PASS\n" || strings.HasSuffix(testOutput, "\nPASS\n") {
// Test passed.
return nil
} else {
// Test failed, either by ending with the word "FAIL" or with a
// panic of some sort.
os.Exit(1)
return nil // unreachable
}
} }
return nil
}) })
if err, ok := err.(loader.NoTestFilesError); ok {
fmt.Printf("? \t%s\t[no test files]\n", err.ImportPath)
// Pretend the test passed - it at least didn't fail.
return true, nil
}
return passed, err
}
// runPackageTest runs a test binary that was previously built. The return
// values are whether the test passed and any errors encountered while trying to
// run the binary.
func runPackageTest(config *compileopts.Config, result builder.BuildResult) (bool, error) {
if len(config.Target.Emulator) == 0 {
// Run directly.
cmd := executeCommand(config.Options, result.Binary)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = result.MainDir
err := cmd.Run()
if err != nil {
if _, ok := err.(*exec.ExitError); ok {
// Binary exited with a non-zero exit code, which means the test
// failed.
return false, nil
}
return false, &commandError{"failed to run compiled binary", result.Binary, err}
}
return true, nil
} else {
// Run in an emulator.
args := append(config.Target.Emulator[1:], result.Binary)
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...)
buf := &bytes.Buffer{}
w := io.MultiWriter(os.Stdout, buf)
cmd.Stdout = w
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
if err, ok := err.(*exec.ExitError); !ok || !err.Exited() {
// Workaround for QEMU which always exits with an error.
return false, &commandError{"failed to run emulator with", result.Binary, err}
}
}
testOutput := string(buf.Bytes())
if testOutput == "PASS\n" || strings.HasSuffix(testOutput, "\nPASS\n") {
// Test passed.
return true, nil
} else {
// Test failed, either by ending with the word "FAIL" or with a
// panic of some sort.
return false, nil
}
}
} }
// Flash builds and flashes the built binary to the given serial port. // Flash builds and flashes the built binary to the given serial port.
@@ -275,12 +240,15 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) 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" {
port, err := getDefaultPort(strings.FieldsFunc(port, func(c rune) bool { return c == ',' })) if port == "" {
if err != nil { var err error
return err port, err = getDefaultPort()
if err != nil {
return err
}
} }
err = touchSerialPortAt1200bps(port) err := touchSerialPortAt1200bps(port)
if err != nil { if err != nil {
return &commandError{"failed to reset port", result.Binary, err} return &commandError{"failed to reset port", result.Binary, err}
} }
@@ -296,9 +264,9 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
fileToken := "{" + fileExt[1:] + "}" fileToken := "{" + fileExt[1:] + "}"
flashCmd = strings.ReplaceAll(flashCmd, fileToken, result.Binary) flashCmd = strings.ReplaceAll(flashCmd, fileToken, result.Binary)
if strings.Contains(flashCmd, "{port}") { if port == "" && strings.Contains(flashCmd, "{port}") {
var err error var err error
port, err = getDefaultPort(strings.FieldsFunc(port, func(c rune) bool { return c == ',' })) port, err = getDefaultPort()
if err != nil { if err != nil {
return err return err
} }
@@ -697,66 +665,41 @@ func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, e
} }
// getDefaultPort returns the default serial port depending on the operating system. // getDefaultPort returns the default serial port depending on the operating system.
func getDefaultPort(portCandidates []string) (port string, err error) { func getDefaultPort() (port string, err error) {
if len(portCandidates) == 1 { var portPath string
return portCandidates[0], nil
}
var ports []string
switch runtime.GOOS { switch runtime.GOOS {
case "darwin":
portPath = "/dev/cu.usb*"
case "linux":
portPath = "/dev/ttyACM*"
case "freebsd": case "freebsd":
ports, err = filepath.Glob("/dev/cuaU*") portPath = "/dev/cuaU*"
case "darwin", "linux", "windows": case "windows":
var portsList []*enumerator.PortDetails ports, err := serial.GetPortsList()
portsList, err = enumerator.GetDetailedPortsList()
if err != nil { if err != nil {
return "", err return "", err
} }
for _, p := range portsList { if len(ports) == 0 {
ports = append(ports, p.Name) return "", errors.New("no serial ports available")
} else if len(ports) > 1 {
return "", errors.New("multiple serial ports available - use -port flag")
} }
if ports == nil || len(ports) == 0 { return ports[0], nil
// fallback
switch runtime.GOOS {
case "darwin":
ports, err = filepath.Glob("/dev/cu.usb*")
case "linux":
ports, err = filepath.Glob("/dev/ttyACM*")
case "windows":
ports, err = serial.GetPortsList()
}
}
default: default:
return "", errors.New("unable to search for a default USB device to be flashed on this OS") return "", errors.New("unable to search for a default USB device to be flashed on this OS")
} }
d, err := filepath.Glob(portPath)
if err != nil { if err != nil {
return "", err return "", err
} else if ports == nil { }
if d == nil {
return "", errors.New("unable to locate a serial port") return "", errors.New("unable to locate a serial port")
} else if len(ports) == 0 {
return "", errors.New("no serial ports available")
} }
if len(portCandidates) == 0 { return d[0], nil
if len(ports) == 1 {
return ports[0], nil
} else {
return "", errors.New("multiple serial ports available - use -port flag, available ports are " + strings.Join(ports, ", "))
}
}
for _, ps := range portCandidates {
for _, p := range ports {
if p == ps {
return p, nil
}
}
}
return "", errors.New("port you specified '" + strings.Join(portCandidates, ",") + "' does not exist, available ports are " + strings.Join(ports, ", "))
} }
func usage() { func usage() {
@@ -869,52 +812,6 @@ func handleCompilerError(err error) {
} }
} }
// This is a special type for the -X flag to parse the pkgpath.Var=stringVal
// format. It has to be a special type to allow multiple variables to be defined
// this way.
type globalValuesFlag map[string]map[string]string
func (m globalValuesFlag) String() string {
return "pkgpath.Var=value"
}
func (m globalValuesFlag) Set(value string) error {
equalsIndex := strings.IndexByte(value, '=')
if equalsIndex < 0 {
return errors.New("expected format pkgpath.Var=value")
}
pathAndName := value[:equalsIndex]
pointIndex := strings.LastIndexByte(pathAndName, '.')
if pointIndex < 0 {
return errors.New("expected format pkgpath.Var=value")
}
path := pathAndName[:pointIndex]
name := pathAndName[pointIndex+1:]
stringValue := value[equalsIndex+1:]
if m[path] == nil {
m[path] = make(map[string]string)
}
m[path][name] = stringValue
return nil
}
// parseGoLinkFlag parses the -ldflags parameter. Its primary purpose right now
// is the -X flag, for setting the value of global string variables.
func parseGoLinkFlag(flagsString string) (map[string]map[string]string, error) {
set := flag.NewFlagSet("link", flag.ExitOnError)
globalVarValues := make(globalValuesFlag)
set.Var(globalVarValues, "X", "Set the value of the string variable to the given value.")
flags, err := shlex.Split(flagsString)
if err != nil {
return nil, err
}
err = set.Parse(flags)
if err != nil {
return nil, err
}
return map[string]map[string]string(globalVarValues), nil
}
func main() { func main() {
if len(os.Args) < 2 { if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.") fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
@@ -934,16 +831,14 @@ func main() {
target := flag.String("target", "", "LLVM target | .json file with TargetSpec") target := flag.String("target", "", "LLVM target | .json file with TargetSpec")
printSize := flag.String("size", "", "print sizes (none, short, full)") printSize := flag.String("size", "", "print sizes (none, short, full)")
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines") printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed")
printCommands := flag.Bool("x", false, "Print commands") printCommands := flag.Bool("x", false, "Print commands")
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation") nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
ocdCommandsString := flag.String("ocd-commands", "", "OpenOCD commands, overriding target spec (can specify multiple separated by commas)")
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug") ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
port := flag.String("port", "", "flash port (can specify multiple candidates separated by commas)") port := flag.String("port", "", "flash port")
programmer := flag.String("programmer", "", "which hardware programmer to use") programmer := flag.String("programmer", "", "which hardware programmer to use")
ldflags := flag.String("ldflags", "", "Go link tool compatible ldflags") cFlags := flag.String("cflags", "", "additional cflags for compiler")
ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
wasmAbi := flag.String("wasm-abi", "", "WebAssembly ABI conventions: js (no i64 params) or generic") wasmAbi := flag.String("wasm-abi", "", "WebAssembly ABI conventions: js (no i64 params) or generic")
llvmFeatures := flag.String("llvm-features", "", "comma separated LLVM features to enable")
var flagJSON, flagDeps *bool var flagJSON, flagDeps *bool
if command == "help" || command == "list" { if command == "help" || command == "list" {
@@ -972,51 +867,35 @@ func main() {
} }
flag.CommandLine.Parse(os.Args[2:]) flag.CommandLine.Parse(os.Args[2:])
globalVarValues, err := parseGoLinkFlag(*ldflags)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
var printAllocs *regexp.Regexp
if *printAllocsString != "" {
printAllocs, err = regexp.Compile(*printAllocsString)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
var ocdCommands []string
if *ocdCommandsString != "" {
ocdCommands = strings.Split(*ocdCommandsString, ",")
}
options := &compileopts.Options{ options := &compileopts.Options{
Target: *target, Target: *target,
Opt: *opt, Opt: *opt,
GC: *gc, GC: *gc,
PanicStrategy: *panicStrategy, PanicStrategy: *panicStrategy,
Scheduler: *scheduler, Scheduler: *scheduler,
PrintIR: *printIR, PrintIR: *printIR,
DumpSSA: *dumpSSA, DumpSSA: *dumpSSA,
VerifyIR: *verifyIR, VerifyIR: *verifyIR,
Debug: !*nodebug, Debug: !*nodebug,
PrintSizes: *printSize, PrintSizes: *printSize,
PrintStacks: *printStacks, PrintStacks: *printStacks,
PrintAllocs: printAllocs, PrintCommands: *printCommands,
PrintCommands: *printCommands, Tags: *tags,
Tags: *tags, WasmAbi: *wasmAbi,
GlobalValues: globalVarValues, Programmer: *programmer,
WasmAbi: *wasmAbi, }
Programmer: *programmer,
OpenOCDCommands: ocdCommands, if *cFlags != "" {
LLVMFeatures: *llvmFeatures, options.CFlags = strings.Split(*cFlags, " ")
}
if *ldFlags != "" {
options.LDFlags = strings.Split(*ldFlags, " ")
} }
os.Setenv("CC", "clang -target="+*target) os.Setenv("CC", "clang -target="+*target)
err = options.Verify() err := options.Verify()
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err.Error()) fmt.Fprintln(os.Stderr, err.Error())
usage() usage()
@@ -1104,26 +983,16 @@ func main() {
err := Run(pkgName, options) err := Run(pkgName, options)
handleCompilerError(err) handleCompilerError(err)
case "test": case "test":
var pkgNames []string pkgName := "."
for i := 0; i < flag.NArg(); i++ { if flag.NArg() == 1 {
pkgNames = append(pkgNames, filepath.ToSlash(flag.Arg(i))) pkgName = filepath.ToSlash(flag.Arg(0))
} } else if flag.NArg() > 1 {
if len(pkgNames) == 0 { fmt.Fprintln(os.Stderr, "test only accepts a single positional argument: package name, but multiple were specified")
pkgNames = []string{"."} usage()
}
allTestsPassed := true
for _, pkgName := range pkgNames {
// TODO: parallelize building the test binaries
passed, err := Test(pkgName, options, *testCompileOnlyFlag, outpath)
handleCompilerError(err)
if !passed {
allTestsPassed = false
}
}
if !allTestsPassed {
fmt.Println("FAIL")
os.Exit(1) os.Exit(1)
} }
err := Test(pkgName, options, *testCompileOnlyFlag, outpath)
handleCompilerError(err)
case "targets": case "targets":
dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets") dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets")
entries, err := ioutil.ReadDir(dir) entries, err := ioutil.ReadDir(dir)
+55 -125
View File
@@ -13,8 +13,8 @@ import (
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime" "runtime"
"sort"
"strings" "strings"
"sync"
"testing" "testing"
"time" "time"
@@ -28,43 +28,41 @@ const TESTDATA = "testdata"
var testTarget = flag.String("target", "", "override test target") var testTarget = flag.String("target", "", "override test target")
func TestCompiler(t *testing.T) { func TestCompiler(t *testing.T) {
tests := []string{ matches, err := filepath.Glob(filepath.Join(TESTDATA, "*.go"))
"alias.go", if err != nil {
"atomic.go", t.Fatal("could not read test files:", err)
"binop.go",
"calls.go",
"cgo/",
"channel.go",
"coroutines.go",
"float.go",
"gc.go",
"init.go",
"init_multi.go",
"interface.go",
"json.go",
"map.go",
"math.go",
"print.go",
"reflect.go",
"slice.go",
"sort.go",
"stdlib.go",
"string.go",
"structs.go",
"zeroalloc.go",
} }
dirMatches, err := filepath.Glob(filepath.Join(TESTDATA, "*", "main.go"))
if err != nil {
t.Fatal("could not read test packages:", err)
}
if len(matches) == 0 || len(dirMatches) == 0 {
t.Fatal("no test files found")
}
for _, m := range dirMatches {
matches = append(matches, filepath.Dir(m)+string(filepath.Separator))
}
sort.Strings(matches)
if *testTarget != "" { if *testTarget != "" {
// This makes it possible to run one specific test (instead of all), // This makes it possible to run one specific test (instead of all),
// which is especially useful to quickly check whether some changes // which is especially useful to quickly check whether some changes
// affect a particular target architecture. // affect a particular target architecture.
runPlatTests(*testTarget, tests, t) runPlatTests(*testTarget, matches, t)
return return
} }
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
t.Run("Host", func(t *testing.T) { t.Run("Host", func(t *testing.T) {
runPlatTests("", tests, t) runPlatTests("", matches, t)
if runtime.GOOS == "darwin" {
runTest("testdata/libc/filesystem.go", "", t,
nil, nil)
runTest("testdata/libc/env.go", "", t,
[]string{"ENV1=VALUE1", "ENV2=VALUE2"}, nil)
}
}) })
} }
@@ -73,26 +71,26 @@ func TestCompiler(t *testing.T) {
} }
t.Run("EmulatedCortexM3", func(t *testing.T) { t.Run("EmulatedCortexM3", func(t *testing.T) {
runPlatTests("cortex-m-qemu", tests, t) runPlatTests("cortex-m-qemu", matches, t)
}) })
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
// Note: running only on Windows and macOS because Linux (as of 2020) // Note: running only on Windows and macOS because Linux (as of 2020)
// usually has an outdated QEMU version that doesn't support RISC-V yet. // usually has an outdated QEMU version that doesn't support RISC-V yet.
t.Run("EmulatedRISCV", func(t *testing.T) { t.Run("EmulatedRISCV", func(t *testing.T) {
runPlatTests("riscv-qemu", tests, t) runPlatTests("riscv-qemu", matches, t)
}) })
} }
if runtime.GOOS == "linux" { if runtime.GOOS == "linux" {
t.Run("X86Linux", func(t *testing.T) { t.Run("X86Linux", func(t *testing.T) {
runPlatTests("i386--linux-gnu", tests, t) runPlatTests("i386--linux-gnu", matches, t)
}) })
t.Run("ARMLinux", func(t *testing.T) { t.Run("ARMLinux", func(t *testing.T) {
runPlatTests("arm--linux-gnueabihf", tests, t) runPlatTests("arm--linux-gnueabihf", matches, t)
}) })
t.Run("ARM64Linux", func(t *testing.T) { t.Run("ARM64Linux", func(t *testing.T) {
runPlatTests("aarch64--linux-gnu", tests, t) runPlatTests("aarch64--linux-gnu", matches, t)
}) })
goVersion, err := goenv.GorootVersionString(goenv.Get("GOROOT")) goVersion, err := goenv.GorootVersionString(goenv.Get("GOROOT"))
if err != nil { if err != nil {
@@ -105,110 +103,35 @@ func TestCompiler(t *testing.T) {
// below that are also not supported but still seem to pass, so // below that are also not supported but still seem to pass, so
// include them in the tests for now. // include them in the tests for now.
t.Run("WebAssembly", func(t *testing.T) { t.Run("WebAssembly", func(t *testing.T) {
runPlatTests("wasm", tests, t) runPlatTests("wasm", matches, t)
}) })
} }
t.Run("WASI", func(t *testing.T) { t.Run("WASI", func(t *testing.T) {
runPlatTests("wasi", tests, t) runPlatTests("wasi", matches, t)
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=."})
}) })
} }
// Test a few build options.
t.Run("build-options", func(t *testing.T) {
if runtime.GOOS == "windows" {
// These tests assume a host that is supported by TinyGo.
t.Skip("can't test build options on Windows")
}
t.Parallel()
// Test with few optimizations enabled (no inlining, etc).
t.Run("opt=1", func(t *testing.T) {
t.Parallel()
runTestWithConfig("stdlib.go", "", t, &compileopts.Options{
Opt: "1",
}, nil, nil)
})
// Test with only the bare minimum of optimizations enabled.
// TODO: fix this for stdlib.go, which currently fails.
t.Run("opt=0", func(t *testing.T) {
t.Parallel()
runTestWithConfig("print.go", "", t, &compileopts.Options{
Opt: "0",
}, nil, nil)
})
t.Run("ldflags", func(t *testing.T) {
t.Parallel()
runTestWithConfig("ldflags.go", "", t, &compileopts.Options{
Opt: "z",
GlobalValues: map[string]map[string]string{
"main": {
"someGlobal": "foobar",
},
},
}, nil, nil)
})
})
} }
func runPlatTests(target string, tests []string, t *testing.T) { func runPlatTests(target string, matches []string, t *testing.T) {
t.Parallel() t.Parallel()
for _, name := range tests { for _, path := range matches {
name := name // redefine to avoid race condition path := path // redefine to avoid race condition
t.Run(name, func(t *testing.T) { t.Run(filepath.Base(path), func(t *testing.T) {
t.Parallel() t.Parallel()
runTest(name, target, t, nil, nil) runTest(path, target, t, nil, nil)
})
}
if target == "wasi" || target == "" {
t.Run("filesystem.go", func(t *testing.T) {
t.Parallel()
runTest("filesystem.go", target, t, nil, nil)
})
t.Run("env.go", func(t *testing.T) {
t.Parallel()
runTest("env.go", target, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
}) })
} }
} }
// Due to some problems with LLD, we cannot run links in parallel, or in parallel with compiles. func runTest(path, target string, t *testing.T, environmentVars []string, additionalArgs []string) {
// Therefore, we put a lock around builds and run everything else in parallel.
var buildLock sync.Mutex
// runBuild is a thread-safe wrapper around Build.
func runBuild(src, out string, opts *compileopts.Options) error {
buildLock.Lock()
defer buildLock.Unlock()
return Build(src, out, opts)
}
func runTest(name, target string, t *testing.T, cmdArgs, environmentVars []string) {
options := &compileopts.Options{
Target: target,
Opt: "z",
PrintIR: false,
DumpSSA: false,
VerifyIR: true,
Debug: true,
PrintSizes: "",
WasmAbi: "",
}
runTestWithConfig(name, target, t, options, cmdArgs, environmentVars)
}
func runTestWithConfig(name, target string, t *testing.T, options *compileopts.Options, cmdArgs, environmentVars []string) {
// Get the expected output for this test.
// Note: not using filepath.Join as it strips the path separator at the end
// of the path.
path := TESTDATA + "/" + name
// 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] == '/' { if path[len(path)-1] == os.PathSeparator {
txtpath = path + "out.txt" txtpath = path + "out.txt"
} }
expected, err := ioutil.ReadFile(txtpath) expected, err := ioutil.ReadFile(txtpath)
@@ -229,8 +152,19 @@ func runTestWithConfig(name, target string, t *testing.T, options *compileopts.O
}() }()
// Build the test binary. // Build the test binary.
config := &compileopts.Options{
Target: target,
Opt: "z",
PrintIR: false,
DumpSSA: false,
VerifyIR: true,
Debug: true,
PrintSizes: "",
WasmAbi: "",
}
binary := filepath.Join(tmpdir, "test") binary := filepath.Join(tmpdir, "test")
err = runBuild("./"+path, binary, options) err = Build("./"+path, binary, config)
if err != nil { if err != nil {
printCompilerError(t.Log, err) printCompilerError(t.Log, err)
t.Fail() t.Fail()
@@ -244,7 +178,6 @@ func runTestWithConfig(name, target string, t *testing.T, options *compileopts.O
if target == "" { if target == "" {
cmd = exec.Command(binary) cmd = exec.Command(binary)
cmd.Env = append(cmd.Env, environmentVars...) cmd.Env = append(cmd.Env, environmentVars...)
cmd.Args = append(cmd.Args, cmdArgs...)
} else { } else {
spec, err := compileopts.LoadTarget(target) spec, err := compileopts.LoadTarget(target)
if err != nil { if err != nil {
@@ -254,16 +187,13 @@ func runTestWithConfig(name, target string, t *testing.T, options *compileopts.O
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" {
// Allow reading from the current directory.
cmd.Args = append(cmd.Args, "--dir=.")
for _, v := range environmentVars { for _, v := range environmentVars {
cmd.Args = append(cmd.Args, "--env", v) cmd.Args = append(cmd.Args, "--env", v)
} }
cmd.Args = append(cmd.Args, cmdArgs...)
} else { } else {
cmd.Env = append(cmd.Env, environmentVars...) cmd.Env = append(cmd.Env, environmentVars...)
} }
+6 -15
View File
@@ -26,21 +26,12 @@ type SCB_Type struct {
SHPR2 volatile.Register32 // 0xD1C: System Handler Priority Register 2 SHPR2 volatile.Register32 // 0xD1C: System Handler Priority Register 2
SHPR3 volatile.Register32 // 0xD20: System Handler Priority Register 3 SHPR3 volatile.Register32 // 0xD20: System Handler Priority Register 3
// the following are only applicable for Cortex-M3/M33/M4/M7 // the following are only applicable for Cortex-M3/M33/M4/M7
SHCSR volatile.Register32 // 0xD24: System Handler Control and State Register SHCSR volatile.Register32 // 0xD24: System Handler Control and State Register
CFSR volatile.Register32 // 0xD28: Configurable Fault Status Register CFSR volatile.Register32 // 0xD28: Configurable Fault Status Register
HFSR volatile.Register32 // 0xD2C: HardFault Status Register HFSR volatile.Register32 // 0xD2C: HardFault Status Register
DFSR volatile.Register32 // 0xD30: Debug Fault Status Register DFSR volatile.Register32 // 0xD30: Debug Fault Status Register
MMFAR volatile.Register32 // 0xD34: MemManage Fault Address Register MMFAR volatile.Register32 // 0xD34: MemManage Fault Address Register
BFAR volatile.Register32 // 0xD38: BusFault Address Register BFAR volatile.Register32 // 0xD38: BusFault Address Register
AFSR volatile.Register32 // 0xD3C: Auxiliary Fault Status Register
PFR [2]volatile.Register32 // 0xD40: Processor Feature Register
DFR volatile.Register32 // 0xD48: Debug Feature Register
ADR volatile.Register32 // 0xD4C: Auxiliary Feature Register
MMFR [4]volatile.Register32 // 0xD50: Memory Model Feature Register
ISAR [5]volatile.Register32 // 0xD60: Instruction Set Attributes Register
_ [5]uint32 // reserved
CPACR volatile.Register32 // 0xD88: Coprocessor Access Control Register
} }
var SCB = (*SCB_Type)(unsafe.Pointer(uintptr(SCB_BASE))) var SCB = (*SCB_Type)(unsafe.Pointer(uintptr(SCB_BASE)))
-71
View File
@@ -1,71 +0,0 @@
// Hand created file. DO NOT DELETE.
// atsamd51x bitfield definitions that are not auto-generated by gen-device-svd.go
// +build sam,atsame5x
// These are the supported pchctrl function numberings on the atsamd51x
// See http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D5xE5x_Family_Data_Sheet_DS60001507F.pdf
// table 14-9
package sam
const (
PCHCTRL_GCLK_OSCCTRL_DFLL48 = 0 // DFLL48 input clock source
PCHCTRL_GCLK_OSCCTRL_FDPLL0 = 1 // Reference clock for FDPLL0
PCHCTRL_GCLK_OSCCTRL_FDPLL1 = 2 // Reference clock for FDPLL1
PCHCTRL_GCLK_OSCCTRL_FDPLL0_32K = 3 // FDPLL0 = 3 // 32KHz clock for internal lock timer
PCHCTRL_GCLK_OSCCTRL_FDPLL1_32K = 3 // FDPLL1 = 3 // 32KHz clock for internal lock timer
PCHCTRL_GCLK_SDHC0_SLOW = 3 // SDHC0 = 3 // Slow
PCHCTRL_GCLK_SDHC1_SLOW = 3 // SDHC1 = 3 // Slow
PCHCTRL_GCLK_SERCOMX_SLOW = 3 // GCLK_SERCOM[0..7]_SLOW = 3
PCHCTRL_GCLK_EIC = 4
PCHCTRL_GCLK_FREQM_MSR = 5 // FREQM Measure
PCHCTRL_GCLK_FREQM_REF = 6 // FREQM Reference
PCHCTRL_GCLK_SERCOM0_CORE = 7 // SERCOM0 Core
PCHCTRL_GCLK_SERCOM1_CORE = 8 // SERCOM1 Core
PCHCTRL_GCLK_TC0 = 9
PCHCTRL_GCLK_TC1 = 9 // TC0, TC1
PCHCTRL_GCLK_USB = 10 // USB
PCHCTRL_GCLK_EVSYS0 = 11
PCHCTRL_GCLK_EVSYS1 = 12
PCHCTRL_GCLK_EVSYS2 = 13
PCHCTRL_GCLK_EVSYS3 = 14
PCHCTRL_GCLK_EVSYS4 = 15
PCHCTRL_GCLK_EVSYS5 = 16
PCHCTRL_GCLK_EVSYS6 = 17
PCHCTRL_GCLK_EVSYS7 = 18
PCHCTRL_GCLK_EVSYS8 = 19
PCHCTRL_GCLK_EVSYS9 = 20
PCHCTRL_GCLK_EVSYS10 = 21
PCHCTRL_GCLK_EVSYS11 = 22
PCHCTRL_GCLK_SERCOM2_CORE = 23 // SERCOM2 Core
PCHCTRL_GCLK_SERCOM3_CORE = 24 // SERCOM3 Core
PCHCTRL_GCLK_TCC0 = 25
PCHCTRL_GCLK_TCC1 = 25 // TCC0, TCC1
PCHCTRL_GCLK_TC2 = 26
PCHCTRL_GCLK_TC3 = 26 // TC2, TC3
PCHCTRL_GCLK_CAN0 = 27 // CAN0
PCHCTRL_GCLK_CAN1 = 28 // CAN1
PCHCTRL_GCLK_TCC2 = 29
PCHCTRL_GCLK_TCC3 = 29 // TCC2, TCC3
PCHCTRL_GCLK_TC4 = 30
PCHCTRL_GCLK_TC5 = 30 // TC4, TC5
PCHCTRL_GCLK_PDEC = 31 // PDEC
PCHCTRL_GCLK_AC = 32 // AC
PCHCTRL_GCLK_CCL = 33 // CCL
PCHCTRL_GCLK_SERCOM4_CORE = 34 // SERCOM4 Core
PCHCTRL_GCLK_SERCOM5_CORE = 35 // SERCOM5 Core
PCHCTRL_GCLK_SERCOM6_CORE = 36 // SERCOM6 Core
PCHCTRL_GCLK_SERCOM7_CORE = 37 // SERCOM7 Core
PCHCTRL_GCLK_TCC4 = 38 // TCC4
PCHCTRL_GCLK_TC6 = 39
PCHCTRL_GCLK_TC7 = 39 // TC6, TC7
PCHCTRL_GCLK_ADC0 = 40 // ADC0
PCHCTRL_GCLK_ADC1 = 41 // ADC1
PCHCTRL_GCLK_DAC = 42 // DAC
PCHCTRL_GCLK_I2S0 = 43
PCHCTRL_GCLK_I2S1 = 44
PCHCTRL_GCLK_SDHC0 = 45 // SDHC0
PCHCTRL_GCLK_SDHC1 = 46 // SDHC1
PCHCTRL_GCLK_CM4_TRACE = 47 // CM4 Trace
)
+1 -1
View File
@@ -14,7 +14,7 @@ func main() {
led := machine.LED led := machine.LED
led.Configure(machine.PinConfig{Mode: machine.PinOutput}) led.Configure(machine.PinConfig{Mode: machine.PinOutput})
sensor := machine.ADC{Pin: machine.ADC2} sensor := machine.ADC{machine.ADC2}
sensor.Configure(machine.ADCConfig{}) sensor.Configure(machine.ADCConfig{})
for { for {
-12
View File
@@ -1,12 +0,0 @@
// +build arduino_mega1280
package main
import "machine"
var (
// Configuration on an Arduino Uno.
pwm = machine.Timer3
pinA = machine.PH3 // pin 6 on the Mega
pinB = machine.PH4 // pin 7 on the Mega
)
-12
View File
@@ -1,12 +0,0 @@
// +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
)
-11
View File
@@ -1,11 +0,0 @@
// +build feather_m4
package main
import "machine"
var (
pwm = machine.TCC0
pinA = machine.D12
pinB = machine.D13
)
-11
View File
@@ -1,11 +0,0 @@
// +build itsybitsy_m0
package main
import "machine"
var (
pwm = machine.TCC0
pinA = machine.D3
pinB = machine.D4
)
-11
View File
@@ -1,11 +0,0 @@
// +build itsybitsy_m4
package main
import "machine"
var (
pwm = machine.TCC0
pinA = machine.D12
pinB = machine.D13
)
+54 -64
View File
@@ -1,74 +1,64 @@
package main package main
// This example demonstrates some features of the PWM support.
import ( import (
"machine" "machine"
"time" "time"
) )
const delayBetweenPeriods = time.Second * 5 // This example assumes that an RGB LED is connected to pins 3, 5 and 6 on an Arduino.
// Change the values below to use different pins.
const (
redPin = machine.D4
greenPin = machine.D5
bluePin = machine.D6
)
func main() { // cycleColor is just a placeholder until math/rand or some equivalent is working.
// Delay a bit on startup to easily catch the first messages. func cycleColor(color uint8) uint8 {
time.Sleep(time.Second * 2) if color < 10 {
return color + 1
// Configure the PWM with the given period. } else if color < 200 {
err := pwm.Configure(machine.PWMConfig{ return color + 10
Period: 16384e3, // 16.384ms } else {
}) return 0
if err != nil { }
println("failed to configure PWM") }
return
} func main() {
machine.InitPWM()
// The top value is the highest value that can be passed to PWMChannel.Set.
// It is usually an even number. red := machine.PWM{redPin}
println("top:", pwm.Top()) err := red.Configure()
checkError(err, "failed to configure red pin")
// Configure the two channels we'll use as outputs.
channelA, err := pwm.Channel(pinA) green := machine.PWM{greenPin}
if err != nil { err = green.Configure()
println("failed to configure channel A") checkError(err, "failed to configure green pin")
return
} blue := machine.PWM{bluePin}
channelB, err := pwm.Channel(pinB) err = blue.Configure()
if err != nil { checkError(err, "failed to configure blue pin")
println("failed to configure channel B")
return var rc uint8
} var gc uint8 = 20
var bc uint8 = 30
// Invert one of the channels to demonstrate output polarity.
pwm.SetInverting(channelB, true) for {
rc = cycleColor(rc)
// Test out various frequencies below, including some edge cases. gc = cycleColor(gc)
bc = cycleColor(bc)
println("running at 0% duty cycle")
pwm.Set(channelA, 0) red.Set(uint16(rc) << 8)
pwm.Set(channelB, 0) green.Set(uint16(gc) << 8)
time.Sleep(delayBetweenPeriods) blue.Set(uint16(bc) << 8)
println("running at 1") time.Sleep(time.Millisecond * 500)
pwm.Set(channelA, 1) }
pwm.Set(channelB, 1) }
time.Sleep(delayBetweenPeriods)
func checkError(err error, msg string) {
println("running at 25% duty cycle") if err != nil {
pwm.Set(channelA, pwm.Top()/4) print(msg, ": ", err.Error())
pwm.Set(channelB, pwm.Top()/4) println()
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 {
time.Sleep(time.Second)
} }
} }
-58
View File
@@ -1,58 +0,0 @@
.section .text.tinygo_startTask
.global tinygo_startTask
.type tinygo_startTask, %function
tinygo_startTask:
.cfi_startproc
// Small assembly stub for starting a goroutine. This is already run on the
// new stack, with the callee-saved registers already loaded.
// Most importantly, EBX contain the pc of the to-be-started function and
// ESI contain the only argument it is given. Multiple arguments are packed
// into one by storing them in a new allocation.
// Indicate to the unwinder that there is nothing to unwind, this is the
// root frame. It avoids bogus extra frames in GDB.
.cfi_undefined eip
// Set the first argument of the goroutine start wrapper, which contains all
// the arguments.
pushl %esi
// Branch to the "goroutine start" function.
calll *%ebx
// Rebalance the stack (to undo the above push).
addl $4, %esp
// After return, exit this goroutine. This is a tail call.
jmp tinygo_pause
.cfi_endproc
.global tinygo_swapTask
.type tinygo_swapTask, %function
tinygo_swapTask:
// This function gets the following parameters:
movl 4(%esp), %eax // newStack uintptr
movl 8(%esp), %ecx // oldStack *uintptr
// More information on the calling convention:
// https://wiki.osdev.org/System_V_ABI#i386
// Save all callee-saved registers:
pushl %ebp
pushl %edi
pushl %esi
pushl %ebx
// Save the current stack pointer in oldStack.
movl %esp, (%ecx)
// Switch to the new stack pointer.
movl %eax, %esp
// Load saved register from the new stack.
popl %ebx
popl %esi
popl %edi
popl %ebp
// Return into the new task, as if tinygo_swapTask was a regular call.
ret
-59
View File
@@ -1,59 +0,0 @@
// +build scheduler.tasks,386
package task
import "unsafe"
var systemStack uintptr
// calleeSavedRegs is the list of registers that must be saved and restored when
// switching between tasks. Also see task_stack_386.S that relies on the exact
// layout of this struct.
type calleeSavedRegs struct {
ebx uintptr
esi uintptr
edi uintptr
ebp uintptr
pc uintptr
}
// archInit runs architecture-specific setup for the goroutine startup.
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
// Store the initial sp for the startTask function (implemented in assembly).
s.sp = uintptr(unsafe.Pointer(r))
// Initialize the registers.
// These will be popped off of the stack on the first resume of the goroutine.
// Start the function at tinygo_startTask (defined in
// src/internal/task/task_stack_386.S). This assembly code calls a function
// (passed in EBX) with a single argument (passed in ESI). After the
// function returns, it calls Pause().
r.pc = uintptr(unsafe.Pointer(&startTask))
// Pass the function to call in EBX.
// This function is a compiler-generated wrapper which loads arguments out
// of a struct pointer. See createGoroutineStartWrapper (defined in
// compiler/goroutine.go) for more information.
r.ebx = fn
// Pass the pointer to the arguments struct in ESI.
r.esi = uintptr(args)
}
func (s *state) resume() {
swapTask(s.sp, &systemStack)
}
func (s *state) pause() {
newStack := systemStack
systemStack = 0
swapTask(newStack, &s.sp)
}
// SystemStack returns the system stack pointer when called from a task stack.
// When called from the system stack, it returns 0.
func SystemStack() uintptr {
return systemStack
}
-74
View File
@@ -1,74 +0,0 @@
#ifdef __MACH__ // Darwin
.global _tinygo_startTask
_tinygo_startTask:
#else // Linux etc
.section .text.tinygo_startTask
.global tinygo_startTask
tinygo_startTask:
#endif
.cfi_startproc
// Small assembly stub for starting a goroutine. This is already run on the
// new stack, with the callee-saved registers already loaded.
// Most importantly, r12 contain the pc of the to-be-started function and
// r13 contain the only argument it is given. Multiple arguments are packed
// into one by storing them in a new allocation.
// Indicate to the unwinder that there is nothing to unwind, this is the
// root frame. It avoids bogus extra frames in GDB like here:
// #10 0x00000000004277b6 in <goroutine wrapper> () at [...]
// #11 0x00000000004278f3 in tinygo_startTask () at [...]
// #12 0x0000000000002030 in ?? ()
// #13 0x0000000000000071 in ?? ()
.cfi_undefined rip
// Set the first argument of the goroutine start wrapper, which contains all
// the arguments.
movq %r13, %rdi
// Branch to the "goroutine start" function.
callq *%r12
// After return, exit this goroutine. This is a tail call.
#ifdef __MACH__
jmp _tinygo_pause
#else
jmp tinygo_pause
#endif
.cfi_endproc
#ifdef __MACH__ // Darwin
.global _tinygo_swapTask
_tinygo_swapTask:
#else // Linux etc
.global tinygo_swapTask
.section .text.tinygo_swapTask
tinygo_swapTask:
#endif
// This function gets the following parameters:
// %rdi = newStack uintptr
// %rsi = oldStack *uintptr
// Save all callee-saved registers:
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbp
pushq %rbx
// Save the current stack pointer in oldStack.
movq %rsp, (%rsi)
// Switch to the new stack pointer.
movq %rdi, %rsp
// Load saved register from the new stack.
popq %rbx
popq %rbp
popq %r12
popq %r13
popq %r14
popq %r15
// Return into the new task, as if tinygo_swapTask was a regular call.
ret
-61
View File
@@ -1,61 +0,0 @@
// +build scheduler.tasks,amd64
package task
import "unsafe"
var systemStack uintptr
// calleeSavedRegs is the list of registers that must be saved and restored when
// switching between tasks. Also see task_stack_amd64.S that relies on the exact
// layout of this struct.
type calleeSavedRegs struct {
rbx uintptr
rbp uintptr
r12 uintptr
r13 uintptr
r14 uintptr
r15 uintptr
pc uintptr
}
// archInit runs architecture-specific setup for the goroutine startup.
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
// Store the initial sp for the startTask function (implemented in assembly).
s.sp = uintptr(unsafe.Pointer(r))
// Initialize the registers.
// These will be popped off of the stack on the first resume of the goroutine.
// Start the function at tinygo_startTask (defined in
// src/internal/task/task_stack_amd64.S). This assembly code calls a
// function (passed in r12) with a single argument (passed in r13). After
// the function returns, it calls Pause().
r.pc = uintptr(unsafe.Pointer(&startTask))
// Pass the function to call in r12.
// This function is a compiler-generated wrapper which loads arguments out
// of a struct pointer. See createGoroutineStartWrapper (defined in
// compiler/goroutine.go) for more information.
r.r12 = fn
// Pass the pointer to the arguments struct in r13.
r.r13 = uintptr(args)
}
func (s *state) resume() {
swapTask(s.sp, &systemStack)
}
func (s *state) pause() {
newStack := systemStack
systemStack = 0
swapTask(newStack, &s.sp)
}
// SystemStack returns the system stack pointer when called from a task stack.
// When called from the system stack, it returns 0.
func SystemStack() uintptr {
return systemStack
}
-51
View File
@@ -1,51 +0,0 @@
// Only generate .debug_frame, don't generate .eh_frame.
.cfi_sections .debug_frame
.section .text.tinygo_startTask
.global tinygo_startTask
.type tinygo_startTask, %function
tinygo_startTask:
.cfi_startproc
// Small assembly stub for starting a goroutine. This is already run on the
// new stack, with the callee-saved registers already loaded.
// Most importantly, r4 contains the pc of the to-be-started function and r5
// contains the only argument it is given. Multiple arguments are packed
// into one by storing them in a new allocation.
// Indicate to the unwinder that there is nothing to unwind, this is the
// root frame. It avoids the following (bogus) error message in GDB:
// Backtrace stopped: previous frame identical to this frame (corrupt stack?)
.cfi_undefined lr
// Set the first argument of the goroutine start wrapper, which contains all
// the arguments.
mov r0, r5
// Branch to the "goroutine start" function. By using blx instead of bx,
// we'll return here instead of tail calling.
blx r4
// After return, exit this goroutine. This is a tail call.
bl tinygo_pause
.cfi_endproc
.size tinygo_startTask, .-tinygo_startTask
.global tinygo_swapTask
.type tinygo_swapTask, %function
tinygo_swapTask:
// This function gets the following parameters:
// r0 = newStack uintptr
// r1 = oldStack *uintptr
// Save all callee-saved registers:
push {r4-r11, lr}
// Save the current stack pointer in oldStack.
str sp, [r1]
// Switch to the new stack pointer.
mov sp, r0
// Load state from new task and branch to the previous position in the
// program.
pop {r4-r11, pc}
-61
View File
@@ -1,61 +0,0 @@
// +build scheduler.tasks,arm,!cortexm,!avr,!xtensa
package task
import "unsafe"
var systemStack uintptr
// calleeSavedRegs is the list of registers that must be saved and restored when
// switching between tasks. Also see task_stack_arm.S that relies on the exact
// layout of this struct.
type calleeSavedRegs struct {
r4 uintptr
r5 uintptr
r6 uintptr
r7 uintptr
r8 uintptr
r9 uintptr
r10 uintptr
r11 uintptr
pc uintptr
}
// archInit runs architecture-specific setup for the goroutine startup.
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
// Store the initial sp for the startTask function (implemented in assembly).
s.sp = uintptr(unsafe.Pointer(r))
// Initialize the registers.
// These will be popped off of the stack on the first resume of the goroutine.
// Start the function at tinygo_startTask (defined in src/internal/task/task_stack_arm.S).
// This assembly code calls a function (passed in r4) with a single argument
// (passed in r5). After the function returns, it calls Pause().
r.pc = uintptr(unsafe.Pointer(&startTask))
// Pass the function to call in r4.
// This function is a compiler-generated wrapper which loads arguments out of a struct pointer.
// See createGoroutineStartWrapper (defined in compiler/goroutine.go) for more information.
r.r4 = fn
// Pass the pointer to the arguments struct in r5.
r.r5 = uintptr(args)
}
func (s *state) resume() {
swapTask(s.sp, &systemStack)
}
func (s *state) pause() {
newStack := systemStack
systemStack = 0
swapTask(newStack, &s.sp)
}
// SystemStack returns the system stack pointer when called from a task stack.
// When called from the system stack, it returns 0.
func SystemStack() uintptr {
return systemStack
}
-59
View File
@@ -1,59 +0,0 @@
.section .text.tinygo_startTask
.global tinygo_startTask
.type tinygo_startTask, %function
tinygo_startTask:
.cfi_startproc
// Small assembly stub for starting a goroutine. This is already run on the
// new stack, with the callee-saved registers already loaded.
// Most importantly, x19 contains the pc of the to-be-started function and
// x20 contains the only argument it is given. Multiple arguments are packed
// into one by storing them in a new allocation.
// Indicate to the unwinder that there is nothing to unwind, this is the
// root frame. It avoids the following (bogus) error message in GDB:
// Backtrace stopped: previous frame identical to this frame (corrupt stack?)
.cfi_undefined lr
// Set the first argument of the goroutine start wrapper, which contains all
// the arguments.
mov x0, x20
// Branch to the "goroutine start" function. By using blx instead of bx,
// we'll return here instead of tail calling.
blr x19
// After return, exit this goroutine. This is a tail call.
b tinygo_pause
.cfi_endproc
.size tinygo_startTask, .-tinygo_startTask
.global tinygo_swapTask
.type tinygo_swapTask, %function
tinygo_swapTask:
// This function gets the following parameters:
// x0 = newStack uintptr
// x1 = oldStack *uintptr
// Save all callee-saved registers:
stp x19, x20, [sp, #-96]!
stp x21, x22, [sp, #16]
stp x23, x24, [sp, #32]
stp x25, x26, [sp, #48]
stp x27, x28, [sp, #64]
stp x29, x30, [sp, #80]
// Save the current stack pointer in oldStack.
mov x8, sp
str x8, [x1]
// Switch to the new stack pointer.
mov sp, x0
// Restore stack state and return.
ldp x29, x30, [sp, #80]
ldp x27, x28, [sp, #64]
ldp x25, x26, [sp, #48]
ldp x23, x24, [sp, #32]
ldp x21, x22, [sp, #16]
ldp x19, x20, [sp], #96
ret
-64
View File
@@ -1,64 +0,0 @@
// +build scheduler.tasks,arm64
package task
import "unsafe"
var systemStack uintptr
// calleeSavedRegs is the list of registers that must be saved and restored when
// switching between tasks. Also see task_stack_arm64.S that relies on the exact
// layout of this struct.
type calleeSavedRegs struct {
x19 uintptr
x20 uintptr
x21 uintptr
x22 uintptr
x23 uintptr
x24 uintptr
x25 uintptr
x26 uintptr
x27 uintptr
x28 uintptr
x29 uintptr
pc uintptr // aka x30 aka LR
}
// archInit runs architecture-specific setup for the goroutine startup.
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
// Store the initial sp for the startTask function (implemented in assembly).
s.sp = uintptr(unsafe.Pointer(r))
// Initialize the registers.
// These will be popped off of the stack on the first resume of the goroutine.
// Start the function at tinygo_startTask (defined in src/internal/task/task_stack_arm64.S).
// This assembly code calls a function (passed in x19) with a single argument
// (passed in x20). After the function returns, it calls Pause().
r.pc = uintptr(unsafe.Pointer(&startTask))
// Pass the function to call in x19.
// This function is a compiler-generated wrapper which loads arguments out of a struct pointer.
// See createGoroutineStartWrapper (defined in compiler/goroutine.go) for more information.
r.x19 = fn
// Pass the pointer to the arguments struct in x20.
r.x20 = uintptr(args)
}
func (s *state) resume() {
swapTask(s.sp, &systemStack)
}
func (s *state) pause() {
newStack := systemStack
systemStack = 0
swapTask(newStack, &s.sp)
}
// SystemStack returns the system stack pointer when called from a task stack.
// When called from the system stack, it returns 0.
func SystemStack() uintptr {
return systemStack
}
-6
View File
@@ -2,12 +2,6 @@
package task package task
// Note that this is almost the same as task_stack_arm.go, but it uses the MSP
// register to store the system stack pointer instead of a global variable. The
// big advantage of this is that interrupts always execute with MSP (and not
// PSP, which is used for goroutines) so that goroutines do not need extra stack
// space for interrupts.
import ( import (
"device/arm" "device/arm"
"unsafe" "unsafe"
-7
View File
@@ -9,11 +9,4 @@ type ADCConfig struct {
Reference uint32 // analog reference voltage (AREF) in millivolts Reference uint32 // analog reference voltage (AREF) in millivolts
Resolution uint32 // number of bits for a single conversion (e.g., 8, 10, 12) Resolution uint32 // number of bits for a single conversion (e.g., 8, 10, 12)
Samples uint32 // number of samples for a single conversion (e.g., 4, 8, 16, 32) Samples uint32 // number of samples for a single conversion (e.g., 4, 8, 16, 32)
Bus uint8 // bus Number of ADC
} }
const (
AdcBusAuto = 0
AdcBus0 = 1
AdcBus1 = 2
)
-104
View File
@@ -1,104 +0,0 @@
// +build arduino_mega1280
package machine
// Return the current CPU frequency in hertz.
func CPUFrequency() uint32 {
return 16000000
}
const (
AREF Pin = NoPin
LED Pin = PB7
A0 Pin = PF0
A1 Pin = PF1
A2 Pin = PF2
A3 Pin = PF3
A4 Pin = PF4
A5 Pin = PF5
A6 Pin = PF6
A7 Pin = PF7
A8 Pin = PK0
A9 Pin = PK1
A10 Pin = PK2
A11 Pin = PK3
A12 Pin = PK4
A13 Pin = PK5
A14 Pin = PK6
A15 Pin = PK7
// Analog Input
ADC0 Pin = PF0
ADC1 Pin = PF1
ADC2 Pin = PF2
ADC3 Pin = PF3
ADC4 Pin = PF4
ADC5 Pin = PF5
ADC6 Pin = PF6
ADC7 Pin = PF7
ADC8 Pin = PK0
ADC9 Pin = PK1
ADC10 Pin = PK2
ADC11 Pin = PK3
ADC12 Pin = PK4
ADC13 Pin = PK5
ADC14 Pin = PK6
ADC15 Pin = PK7
// Digital pins
D0 Pin = PE0
D1 Pin = PE1
D2 Pin = PE4
D3 Pin = PE5
D4 Pin = PG5
D5 Pin = PE3
D6 Pin = PH3
D7 Pin = PH4
D8 Pin = PH5
D9 Pin = PH6
D10 Pin = PB4
D11 Pin = PB5
D12 Pin = PB6
D13 Pin = PB7
D14 Pin = PJ1
D15 Pin = PJ0
D16 Pin = PH1
D17 Pin = PH0
D18 Pin = PD3
D19 Pin = PD2
D20 Pin = PD1
D21 Pin = PD0
D22 Pin = PA0
D23 Pin = PA1
D24 Pin = PA2
D25 Pin = PA3
D26 Pin = PA4
D27 Pin = PA5
D28 Pin = PA6
D29 Pin = PA7
D30 Pin = PC7
D31 Pin = PC6
D32 Pin = PC5
D33 Pin = PC4
D34 Pin = PC3
D35 Pin = PC2
D36 Pin = PC1
D37 Pin = PC0
D38 Pin = PD7
D39 Pin = PG2
D40 Pin = PG1
D41 Pin = PG0
D42 Pin = PL7
D43 Pin = PL6
D44 Pin = PL5
D45 Pin = PL4
D46 Pin = PL3
D47 Pin = PL2
D48 Pin = PL1
D49 Pin = PL0
D50 Pin = PB3
D51 Pin = PB2
D52 Pin = PB1
D53 Pin = PB0
)
-330
View File
@@ -1,330 +0,0 @@
// +build atsame54_xpro
package machine
import (
"device/sam"
"runtime/interrupt"
)
// Definition for compatibility, but not used
const RESET_MAGIC_VALUE = 0x00000000
const (
LED = PC18
BUTTON = PB31
)
const (
// https://ww1.microchip.com/downloads/en/DeviceDoc/70005321A.pdf
// Extension Header EXT1
EXT1_PIN3_ADC_P = PB04
EXT1_PIN4_ADC_N = PB05
EXT1_PIN5_GPIO1 = PA06
EXT1_PIN6_GPIO2 = PA07
EXT1_PIN7_PWM_P = PB08
EXT1_PIN8_PWM_N = PB09
EXT1_PIN9_IRQ = PB07
EXT1_PIN9_GPIO = PB07
EXT1_PIN10_SPI_SS_B = PA27
EXT1_PIN10_GPIO = PA27
EXT1_PIN11_TWI_SDA = PA22
EXT1_PIN12_TWI_SCL = PA23
EXT1_PIN13_UART_RX = PA05
EXT1_PIN14_UART_TX = PA04
EXT1_PIN15_SPI_SS_A = PB28
EXT1_PIN16_SPI_SDO = PB27
EXT1_PIN17_SPI_SDI = PB29
EXT1_PIN18_SPI_SCK = PB26
// Extension Header EXT2
EXT2_PIN3_ADC_P = PB00
EXT2_PIN4_ADC_N = PA03
EXT2_PIN5_GPIO1 = PB01
EXT2_PIN6_GPIO2 = PB06
EXT2_PIN7_PWM_P = PB14
EXT2_PIN8_PWM_N = PB15
EXT2_PIN9_IRQ = PD00
EXT2_PIN9_GPIO = PD00
EXT2_PIN10_SPI_SS_B = PB02
EXT2_PIN10_GPIO = PB02
EXT2_PIN11_TWI_SDA = PD08
EXT2_PIN12_TWI_SCL = PD09
EXT2_PIN13_UART_RX = PB17
EXT2_PIN14_UART_TX = PB16
EXT2_PIN15_SPI_SS_A = PC06
EXT2_PIN16_SPI_SDO = PC04
EXT2_PIN17_SPI_SDI = PC07
EXT2_PIN18_SPI_SCK = PC05
// Extension Header EXT3
EXT3_PIN3_ADC_P = PC02
EXT3_PIN4_ADC_N = PC03
EXT3_PIN5_GPIO1 = PC01
EXT3_PIN6_GPIO2 = PC10
EXT3_PIN7_PWM_P = PD10
EXT3_PIN8_PWM_N = PD11
EXT3_PIN9_IRQ = PC30
EXT3_PIN9_GPIO = PC30
EXT3_PIN10_SPI_SS_B = PC31
EXT3_PIN10_GPIO = PC31
EXT3_PIN11_TWI_SDA = PD08
EXT3_PIN12_TWI_SCL = PD09
EXT3_PIN13_UART_RX = PC23
EXT3_PIN14_UART_TX = PC22
EXT3_PIN15_SPI_SS_A = PC14
EXT3_PIN16_SPI_SDO = PC04
EXT3_PIN17_SPI_SDI = PC07
EXT3_PIN18_SPI_SCK = PC05
// SD_CARD
SD_CARD_MCDA0 = PB18
SD_CARD_MCDA1 = PB19
SD_CARD_MCDA2 = PB20
SD_CARD_MCDA3 = PB21
SD_CARD_MCCK = PA21
SD_CARD_MCCDA = PA20
SD_CARD_DETECT = PD20
SD_CARD_PROTECT = PD21
// I2C
I2C_SDA = PD08
I2C_SCL = PD09
// CAN
CAN0_TX = PA22
CAN0_RX = PA23
CAN1_STANDBY = PC13
CAN1_TX = PB12
CAN1_RX = PB13
CAN_STANDBY = CAN1_STANDBY
CAN_TX = CAN1_TX
CAN_RX = CAN1_RX
// PDEC
PDEC_PHASE_A = PC16
PDEC_PHASE_B = PC17
PDEC_INDEX = PC18
// PCC
PCC_I2C_SDA = PD08
PCC_I2C_SCL = PD09
PCC_VSYNC_DEN1 = PA12
PCC_HSYNC_DEN2 = PA13
PCC_CLK = PA14
PCC_XCLK = PA15
PCC_DATA00 = PA16
PCC_DATA01 = PA17
PCC_DATA02 = PA18
PCC_DATA03 = PA19
PCC_DATA04 = PA20
PCC_DATA05 = PA21
PCC_DATA06 = PA22
PCC_DATA07 = PA23
PCC_DATA08 = PB14
PCC_DATA09 = PB15
PCC_RESET = PC12
PCC_PWDN = PC11
// Ethernet
ETHERNET_TXCK = PA14
ETHERNET_TXEN = PA17
ETHERNET_TX0 = PA18
ETHERNET_TX1 = PA19
ETHERNET_RXER = PA15
ETHERNET_RX0 = PA13
ETHERNET_RX1 = PA12
ETHERNET_RXDV = PC20
ETHERNET_MDIO = PC12
ETHERNET_MDC = PC11
ETHERNET_INT = PD12
ETHERNET_RESET = PC21
PIN_QT_BUTTON = PA16
PIN_BTN0 = PB31
PIN_ETH_LED = PC15
PIN_LED0 = PC18
PIN_ADC_DAC = PA02
PIN_VBUS_DETECT = PC00
PIN_USB_ID = PC19
)
// UART0 aka USBCDC pins
const (
USBCDC_DM_PIN = PA24
USBCDC_DP_PIN = PA25
)
// UART pins
const (
// Extension Header EXT1
UART_TX_PIN = PA04 // TX : SERCOM0/PAD[0]
UART_RX_PIN = PA05 // RX : SERCOM0/PAD[1]
// Extension Header EXT2
UART2_TX_PIN = PB16 // TX : SERCOM5/PAD[0]
UART2_RX_PIN = PB17 // RX : SERCOM5/PAD[1]
// Extension Header EXT3
UART3_TX_PIN = PC22 // TX : SERCOM1/PAD[0]
UART3_RX_PIN = PC23 // RX : SERCOM1/PAD[1]
// Virtual COM Port
UART4_TX_PIN = PB25 // TX : SERCOM2/PAD[0]
UART4_RX_PIN = PB24 // RX : SERCOM2/PAD[1]
)
// I2C pins
const (
// Extension Header EXT1
SDA0_PIN = PA22 // SDA: SERCOM3/PAD[0]
SCL0_PIN = PA23 // SCL: SERCOM3/PAD[1]
// Extension Header EXT2
SDA1_PIN = PD08 // SDA: SERCOM7/PAD[0]
SCL1_PIN = PD09 // SCL: SERCOM7/PAD[1]
// Extension Header EXT3
SDA2_PIN = PD08 // SDA: SERCOM7/PAD[0]
SCL2_PIN = PD09 // SCL: SERCOM7/PAD[1]
// Data Gateway Interface
SDA_DGI_PIN = PD08 // SDA: SERCOM7/PAD[0]
SCL_DGI_PIN = PD09 // SCL: SERCOM7/PAD[1]
SDA_PIN = SDA0_PIN
SCL_PIN = SCL0_PIN
)
// SPI pins
const (
// Extension Header EXT1
SPI0_SCK_PIN = PB26 // SCK: SERCOM4/PAD[1]
SPI0_SDO_PIN = PB27 // SDO: SERCOM4/PAD[0]
SPI0_SDI_PIN = PB29 // SDI: SERCOM4/PAD[3]
SPI0_SS_PIN = PB28 // SS : SERCOM4/PAD[2]
// Extension Header EXT2
SPI1_SCK_PIN = PC05 // SCK: SERCOM6/PAD[1]
SPI1_SDO_PIN = PC04 // SDO: SERCOM6/PAD[0]
SPI1_SDI_PIN = PC07 // SDI: SERCOM6/PAD[3]
SPI1_SS_PIN = PC06 // SS : SERCOM6/PAD[2]
// Extension Header EXT3
SPI2_SCK_PIN = PC05 // SCK: SERCOM6/PAD[1]
SPI2_SDO_PIN = PC04 // SDO: SERCOM6/PAD[0]
SPI2_SDI_PIN = PC07 // SDI: SERCOM6/PAD[3]
SPI2_SS_PIN = PC14 // SS : GPIO
// Data Gateway Interface
SPI_DGI_SCK_PIN = PC05 // SCK: SERCOM6/PAD[1]
SPI_DGI_SDO_PIN = PC04 // SDO: SERCOM6/PAD[0]
SPI_DGI_SDI_PIN = PC07 // SDI: SERCOM6/PAD[3]
SPI_DGI_SS_PIN = PD01 // SS : GPIO
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "SAM E54 Xplained Pro"
usb_STRING_MANUFACTURER = "Atmel"
)
var (
usb_VID uint16 = 0x03EB
usb_PID uint16 = 0x2404
)
// UART on the SAM E54 Xplained Pro
var (
// Extension Header EXT1
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM0_USART_INT,
SERCOM: 0,
}
// Extension Header EXT2
UART2 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM5_USART_INT,
SERCOM: 5,
}
// Extension Header EXT3
UART3 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM1_USART_INT,
SERCOM: 1,
}
// EDBG Virtual COM Port
UART4 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM2_USART_INT,
SERCOM: 2,
}
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, UART2.handleInterrupt)
UART3.Interrupt = interrupt.New(sam.IRQ_SERCOM1_2, UART3.handleInterrupt)
UART4.Interrupt = interrupt.New(sam.IRQ_SERCOM2_2, UART4.handleInterrupt)
}
// I2C on the SAM E54 Xplained Pro
var (
// Extension Header EXT1
I2C0 = I2C{
Bus: sam.SERCOM3_I2CM,
SERCOM: 3,
}
// Extension Header EXT2
I2C1 = I2C{
Bus: sam.SERCOM7_I2CM,
SERCOM: 7,
}
// Extension Header EXT3
I2C2 = I2C{
Bus: sam.SERCOM7_I2CM,
SERCOM: 7,
}
// Data Gateway Interface
I2C3 = I2C{
Bus: sam.SERCOM7_I2CM,
SERCOM: 7,
}
)
// SPI on the SAM E54 Xplained Pro
var (
// Extension Header EXT1
SPI0 = SPI{
Bus: sam.SERCOM4_SPIM,
SERCOM: 4,
}
// Extension Header EXT2
SPI1 = SPI{
Bus: sam.SERCOM6_SPIM,
SERCOM: 6,
}
// Extension Header EXT3
SPI2 = SPI{
Bus: sam.SERCOM6_SPIM,
SERCOM: 6,
}
// Data Gateway Interface
SPI3 = SPI{
Bus: sam.SERCOM6_SPIM,
SERCOM: 6,
}
)
+2 -2
View File
@@ -25,8 +25,8 @@ const (
// Analog Pins // Analog Pins
const ( const (
A0 = PA02 // ADC/AIN[0] A0 = PA02 // PWM available, also ADC/AIN[0]
A1 = PA05 // PWM available, also ADC/AIN[5] A1 = PA05 // 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
-142
View File
@@ -1,142 +0,0 @@
// +build feather_m4_can
package machine
import (
"device/sam"
"runtime/interrupt"
)
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0xf01669ef
// GPIO Pins
const (
D0 = PB17 // UART0 RX/PWM available
D1 = PB16 // UART0 TX/PWM available
D4 = PA14 // PWM available
D5 = PA16 // PWM available
D6 = PA18 // PWM available
D7 = PB03 // neopixel power
D8 = PB02 // built-in neopixel
D9 = PA19 // PWM available
D10 = PA20 // can be used for PWM or UART1 TX
D11 = PA21 // can be used for PWM or UART1 RX
D12 = PA22 // PWM available
D13 = PA23 // PWM available
D21 = PA13 // PWM available
D22 = PA12 // PWM available
D23 = PB22 // PWM available
D24 = PB23 // PWM available
D25 = PA17 // PWM available
)
// Analog pins
const (
A0 = PA02 // ADC/AIN[0]
A1 = PA05 // ADC/AIN[2]
A2 = PB08 // ADC/AIN[3]
A3 = PB09 // ADC/AIN[4]
A4 = PA04 // ADC/AIN[5]
A5 = PA06 // ADC/AIN[10]
)
const (
LED = D13
NEOPIXELS = D8
)
// UART0 aka USBCDC pins
const (
USBCDC_DM_PIN = PA24
USBCDC_DP_PIN = PA25
)
const (
UART_TX_PIN = D1
UART_RX_PIN = D0
)
const (
UART2_TX_PIN = A4
UART2_RX_PIN = A5
)
// I2C pins
const (
SDA_PIN = D22 // SDA: SERCOM2/PAD[0]
SCL_PIN = D21 // SCL: SERCOM2/PAD[1]
)
// SPI pins
const (
SPI0_SCK_PIN = D25 // SCK: SERCOM1/PAD[1]
SPI0_SDO_PIN = D24 // SDO: SERCOM1/PAD[3]
SPI0_SDI_PIN = D23 // SDI: SERCOM1/PAD[2]
)
// CAN pins
const (
CAN0_TX = PA22
CAN0_RX = PA23
CAN1_STANDBY = PB12
CAN1_TX = PB14
CAN1_RX = PB15
BOOST_EN = PB13 // power control of CAN1's TCAN1051HGV (H: enable)
CAN_STANDBY = CAN1_STANDBY
CAN_S = CAN1_STANDBY
CAN_TX = CAN1_TX
CAN_RX = CAN1_RX
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Adafruit Feather M4 CAN"
usb_STRING_MANUFACTURER = "Adafruit"
)
var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x80CD
)
var (
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM5_USART_INT,
SERCOM: 5,
}
UART2 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM0_USART_INT,
SERCOM: 0,
}
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART2.handleInterrupt)
// turn on neopixel
D7.Configure(PinConfig{Mode: PinOutput})
D7.High()
}
// I2C on the Feather M4.
var (
I2C0 = &I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
)
// SPI on the Feather M4.
var (
SPI0 = SPI{
Bus: sam.SERCOM1_SPIM,
SERCOM: 1,
}
)
+2 -13
View File
@@ -41,17 +41,13 @@ 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 (
@@ -72,13 +68,6 @@ var (
RxAltFuncSelector: 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,
+5
View File
@@ -5,6 +5,11 @@ package machine
// The micro:bit does not have a 32kHz crystal on board. // The micro:bit does not have a 32kHz crystal on board.
const HasLowFrequencyCrystal = false const HasLowFrequencyCrystal = false
const (
LED = P13
LED1 = LED
)
// Buttons on the micro:bit v2 (A and B) // Buttons on the micro:bit v2 (A and B)
const ( const (
BUTTON Pin = BUTTONA BUTTON Pin = BUTTONA
-92
View File
@@ -1,92 +0,0 @@
// +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)
}
+6 -44
View File
@@ -13,53 +13,20 @@ const (
LED_GREEN = PB3 LED_GREEN = PB3
) )
// UART pins
const ( const (
// Arduino Pins
A0 = PA0
A1 = PA1
A2 = PA3
A3 = PA4
A4 = PA5
A5 = PA6
A6 = PA7
A7 = PA2
D0 = PA10
D1 = PA9
D2 = PA12
D3 = PB0
D4 = PB7
D5 = PB6
D6 = PB1
D7 = PC14
D8 = PC15
D9 = PA8
D10 = PA11
D11 = PB5
D12 = PB4
D13 = PB3
)
const (
// UART pins
// PA2 and PA15 are connected to the ST-Link Virtual Com Port (VCP) // PA2 and PA15 are connected to the ST-Link Virtual Com Port (VCP)
UART_TX_PIN = PA2 UART_TX_PIN = PA2
UART_RX_PIN = PA15 UART_RX_PIN = PA15
)
// I2C pins // I2C pins
const (
// With default solder bridge settings: // With default solder bridge settings:
// PB6 / Arduino D5 / CN3 Pin 8 is SCL // PB6 / Arduino D5 / CN3 Pin 8 is SCL
// PB7 / Arduino D4 / CN3 Pin 7 is SDA // PB7 / Arduino D4 / CN3 Pin 7 is SDA
I2C0_SCL_PIN = PB6 I2C0_SCL_PIN = PB6
I2C0_SDA_PIN = PB7 I2C0_SDA_PIN = PB7
// SPI pins
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
) )
var ( var (
@@ -73,20 +40,15 @@ var (
RxAltFuncSelector: 3, RxAltFuncSelector: 3,
} }
UART1 = &UART0 UART1 = &UART0
)
var (
// I2C1 is documented, alias to I2C0 as well // I2C1 is documented, alias to I2C0 as well
I2C1 = &I2C{ I2C1 = &I2C{
Bus: stm32.I2C1, Bus: stm32.I2C1,
AltFuncSelector: 4, AltFuncSelector: 4,
} }
I2C0 = I2C1 I2C0 = I2C1
// SPI1 is documented, alias to SPI0 as well
SPI1 = &SPI{
Bus: stm32.SPI1,
AltFuncSelector: 5,
}
SPI0 = SPI1
) )
func init() { func init() {
+2 -2
View File
@@ -94,8 +94,8 @@ const (
// I2C on the QT Py M0. // I2C on the QT Py M0.
var ( var (
I2C0 = &I2C{ I2C0 = &I2C{
Bus: sam.SERCOM1_I2CM, Bus: sam.SERCOM2_I2CM,
SERCOM: 1, SERCOM: 2,
} }
) )
+1 -1
View File
@@ -1,4 +1,4 @@
// +build atmega nrf sam stm32 fe310 k210 // +build atmega nrf sam stm32,!stm32l0 fe310 k210
package machine package machine
+4 -6
View File
@@ -10,11 +10,6 @@ var (
ErrNoPinChangeChannel = errors.New("machine: no channel available for pin interrupt") ErrNoPinChangeChannel = errors.New("machine: no channel available for pin interrupt")
) )
// PinMode sets the direction and pull mode of the pin. For example, PinOutput
// sets the pin as an output and PinInputPullup sets the pin as an input with a
// pull-up.
type PinMode uint8
type PinConfig struct { type PinConfig struct {
Mode PinMode Mode PinMode
} }
@@ -42,7 +37,10 @@ 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
Bus uint8
} }
-931
View File
@@ -1,931 +0,0 @@
// +build avr,atmega1280
package machine
import (
"device/avr"
"runtime/interrupt"
"runtime/volatile"
)
const irq_USART0_RX = avr.IRQ_USART0_RX
const (
portA Pin = iota * 8
portB
portC
portD
portE
portF
portG
portH
portJ
portK
portL
)
const (
PA0 = portA + 0
PA1 = portA + 1
PA2 = portA + 2
PA3 = portA + 3
PA4 = portA + 4
PA5 = portA + 5
PA6 = portA + 6
PA7 = portA + 7
PB0 = portB + 0
PB1 = portB + 1
PB2 = portB + 2
PB3 = portB + 3
PB4 = portB + 4
PB5 = portB + 5
PB6 = portB + 6
PB7 = portB + 7
PC0 = portC + 0
PC1 = portC + 1
PC2 = portC + 2
PC3 = portC + 3
PC4 = portC + 4
PC5 = portC + 5
PC6 = portC + 6
PC7 = portC + 7
PD0 = portD + 0
PD1 = portD + 1
PD2 = portD + 2
PD3 = portD + 3
PD7 = portD + 7
PE0 = portE + 0
PE1 = portE + 1
PE3 = portE + 3
PE4 = portE + 4
PE5 = portE + 5
PE6 = portE + 6
PF0 = portF + 0
PF1 = portF + 1
PF2 = portF + 2
PF3 = portF + 3
PF4 = portF + 4
PF5 = portF + 5
PF6 = portF + 6
PF7 = portF + 7
PG0 = portG + 0
PG1 = portG + 1
PG2 = portG + 2
PG5 = portG + 5
PH0 = portH + 0
PH1 = portH + 1
PH3 = portH + 3
PH4 = portH + 4
PH5 = portH + 5
PH6 = portH + 6
PJ0 = portJ + 0
PJ1 = portJ + 1
PK0 = portK + 0
PK1 = portK + 1
PK2 = portK + 2
PK3 = portK + 3
PK4 = portH + 4
PK5 = portH + 5
PK6 = portH + 6
PK7 = portH + 7
PL0 = portL + 0
PL1 = portL + 1
PL2 = portL + 2
PL3 = portL + 3
PL4 = portL + 4
PL5 = portL + 5
PL6 = portL + 6
PL7 = portL + 7
)
// getPortMask returns the PORTx register and mask for the pin.
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
switch {
case p >= PA0 && p <= PA7:
return avr.PORTA, 1 << uint8(p-portA)
case p >= PB0 && p <= PB7:
return avr.PORTB, 1 << uint8(p-portB)
case p >= PC0 && p <= PC7:
return avr.PORTC, 1 << uint8(p-portC)
case p >= PD0 && p <= PD7:
return avr.PORTD, 1 << uint8(p-portD)
case p >= PE0 && p <= PE6:
return avr.PORTE, 1 << uint8(p-portE)
case p >= PF0 && p <= PF7:
return avr.PORTF, 1 << uint8(p-portF)
case p >= PG0 && p <= PG5:
return avr.PORTG, 1 << uint8(p-portG)
case p >= PH0 && p <= PH6:
return avr.PORTH, 1 << uint8(p-portH)
case p >= PJ0 && p <= PJ1:
return avr.PORTJ, 1 << uint8(p-portJ)
case p >= PK0 && p <= PK7:
return avr.PORTK, 1 << uint8(p-portK)
case p >= PL0 && p <= PL7:
return avr.PORTL, 1 << uint8(p-portL)
default:
return avr.PORTA, 255
}
}
// PWM is one PWM peripheral, which consists of a counter and two output
// channels (that can be connected to two fixed pins). You can set the frequency
// using SetPeriod, but only for all the channels in this PWM peripheral at
// once.
type PWM struct {
num uint8
}
var (
Timer0 = PWM{0} // 8 bit timer for PB7 and PG5
Timer1 = PWM{1} // 16 bit timer for PB5 and PB6
Timer2 = PWM{2} // 8 bit timer for PB4 and PH6
Timer3 = PWM{3} // 16 bit timer for PE3, PE4 and PE5
Timer4 = PWM{4} // 16 bit timer for PH3, PH4 and PH5
Timer5 = PWM{5} // 16 bit timer for PL3, PL4 and PL5
)
// 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, 3, 4, 5:
// 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
}
// 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
var prescalingTop uint8
switch {
case top <= maxTop:
prescalingTop = 3<<3 | 1 // no prescaling
case top/8 <= maxTop:
prescalingTop = 3<<3 | 2 // divide by 8
top /= 8
case top/64 <= maxTop:
prescalingTop = 3<<3 | 3 // divide by 64
top /= 64
case top/256 <= maxTop:
prescalingTop = 3<<3 | 4 // divide by 256
top /= 256
case top/1024 <= maxTop:
prescalingTop = 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
switch pwm.num {
case 1:
avr.TCCR1A.Set(avr.TCCR1A_WGM11)
avr.TCCR1B.Set(prescalingTop)
avr.ICR1H.Set(uint8(top >> 8))
avr.ICR1L.Set(uint8(top))
case 3:
avr.TCCR3A.Set(avr.TCCR3A_WGM31)
avr.TCCR3B.Set(prescalingTop)
avr.ICR3H.Set(uint8(top >> 8))
avr.ICR3L.Set(uint8(top))
case 4:
avr.TCCR4A.Set(avr.TCCR4A_WGM41)
avr.TCCR4B.Set(prescalingTop)
avr.ICR4H.Set(uint8(top >> 8))
avr.ICR4L.Set(uint8(top))
case 5:
avr.TCCR5A.Set(avr.TCCR5A_WGM51)
avr.TCCR5B.Set(prescalingTop)
avr.ICR5H.Set(uint8(top >> 8))
avr.ICR5L.Set(uint8(top))
}
}
return nil
}
// SetPeriod updates the period of this PWM 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 PWM 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 (pwm PWM) SetPeriod(period uint64) error {
if pwm.num == 0 || pwm.num == 2 {
return ErrPWMPeriodTooLong // TODO better error message
}
// 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 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 = period * (uint64(CPUFrequency()) / 1000000) / 1000
}
var prescaler uint8
switch pwm.num {
case 1:
prescaler = avr.TCCR1B.Get() & 0x7
case 3:
prescaler = avr.TCCR3B.Get() & 0x7
case 4:
prescaler = avr.TCCR4B.Get() & 0x7
case 5:
prescaler = avr.TCCR5B.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
}
switch pwm.num {
case 1:
// 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)
case 3:
// Warning: this change is not atomic!
avr.ICR3H.Set(uint8(top >> 8))
avr.ICR3L.Set(uint8(top))
// ... and because of that, set the counter back to zero to avoid most of
// the effects of this non-atomicity.
avr.TCNT3H.Set(0)
avr.TCNT3L.Set(0)
case 4:
// Warning: this change is not atomic!
avr.ICR4H.Set(uint8(top >> 8))
avr.ICR4L.Set(uint8(top))
// ... and because of that, set the counter back to zero to avoid most of
// the effects of this non-atomicity.
avr.TCNT4H.Set(0)
avr.TCNT4L.Set(0)
case 5:
// Warning: this change is not atomic!
avr.ICR5H.Set(uint8(top >> 8))
avr.ICR5L.Set(uint8(top))
// ... and because of that, set the counter back to zero to avoid most of
// the effects of this non-atomicity.
avr.TCNT5H.Set(0)
avr.TCNT5L.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 {
switch pwm.num {
case 1:
// Timer 1 has a configurable top value.
low := avr.ICR1L.Get()
high := avr.ICR1H.Get()
return uint32(high)<<8 | uint32(low) + 1
case 3:
// Timer 3 has a configurable top value.
low := avr.ICR3L.Get()
high := avr.ICR3H.Get()
return uint32(high)<<8 | uint32(low) + 1
case 4:
// Timer 4 has a configurable top value.
low := avr.ICR4L.Get()
high := avr.ICR4H.Get()
return uint32(high)<<8 | uint32(low) + 1
case 5:
// Timer 5 has a configurable top value.
low := avr.ICR5L.Get()
high := avr.ICR5H.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())
case 3:
mask := interrupt.Disable()
low := avr.TCNT3L.Get()
high := avr.TCNT3H.Get()
interrupt.Restore(mask)
return uint32(high)<<8 | uint32(low)
case 4:
mask := interrupt.Disable()
low := avr.TCNT4L.Get()
high := avr.TCNT4H.Get()
interrupt.Restore(mask)
return uint32(high)<<8 | uint32(low)
case 5:
mask := interrupt.Disable()
low := avr.TCNT5L.Get()
high := avr.TCNT5H.Get()
interrupt.Restore(mask)
return uint32(high)<<8 | uint32(low)
}
// 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
case 3:
prescaler = avr.TCCR3B.Get() & 0x7
case 4:
prescaler = avr.TCCR4B.Get() & 0x7
case 5:
prescaler = avr.TCCR5B.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 PB7: // channel A
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
return 0, nil
case PG5: // channel B
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
return 1, nil
}
case 1:
switch pin {
case PB5: // channel A
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
return 0, nil
case PB6: // channel B
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
return 1, nil
}
case 2:
switch pin {
case PB4: // channel A
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
return 0, nil
case PH6: // channel B
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
return 1, nil
}
case 3:
switch pin {
case PE3: // channel A
avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1)
return 0, nil
case PE4: //channel B
avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1)
return 1, nil
case PE5: //channel C
avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1)
return 2, nil
}
case 4:
switch pin {
case PH3: // channel A
avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1)
return 0, nil
case PH4: //channel B
avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1)
return 1, nil
case PH5: //channel C
avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1)
return 2, nil
}
case 5:
switch pin {
case PL3: // channel A
avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1)
return 0, nil
case PL4: //channel B
avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1)
return 1, nil
case PL5: //channel C
avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1)
return 2, 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, PB7
if inverting {
avr.PORTB.SetBits(1 << 7) // PB7 high
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A0)
} else {
avr.PORTB.ClearBits(1 << 7) // PB7 low
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A0)
}
case 1: // channel B, PG5
if inverting {
avr.PORTG.SetBits(1 << 5) // PG5 high
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B0)
} else {
avr.PORTG.ClearBits(1 << 5) // PG5 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, PB5
if inverting {
avr.PORTB.SetBits(1 << 5) // PB5 high
} else {
avr.PORTB.ClearBits(1 << 5) // PB5 low
}
case 1: // channel B, PB6
if inverting {
avr.PORTB.SetBits(1 << 6) // PB6 high
} else {
avr.PORTB.ClearBits(1 << 6) // PB6 low
}
}
case 2:
switch channel {
case 0: // channel A, PB4
if inverting {
avr.PORTB.SetBits(1 << 4) // PB4 high
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A0)
} else {
avr.PORTB.ClearBits(1 << 4) // PB4 low
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A0)
}
case 1: // channel B, PH6
if inverting {
avr.PORTH.SetBits(1 << 6) // PH6 high
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B0)
} else {
avr.PORTH.ClearBits(1 << 6) // PH6 low
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B0)
}
}
case 3:
// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
// It will be set the following call to Set(), however.
switch channel {
case 0: // channel A, PE3
if inverting {
avr.PORTE.SetBits(1 << 3) // PE3 high
} else {
avr.PORTE.ClearBits(1 << 3) // PE3 low
}
case 1: // channel B, PE4
if inverting {
avr.PORTE.SetBits(1 << 4) // PE4 high
} else {
avr.PORTE.ClearBits(1 << 4) // PE4 low
}
case 2: // channel C, PE5
if inverting {
avr.PORTE.SetBits(1 << 5) // PE4 high
} else {
avr.PORTE.ClearBits(1 << 5) // PE4 low
}
}
case 4:
// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
// It will be set the following call to Set(), however.
switch channel {
case 0: // channel A, PH3
if inverting {
avr.PORTH.SetBits(1 << 3) // PH3 high
} else {
avr.PORTH.ClearBits(1 << 3) // PH3 low
}
case 1: // channel B, PH4
if inverting {
avr.PORTH.SetBits(1 << 4) // PH4 high
} else {
avr.PORTH.ClearBits(1 << 4) // PH4 low
}
case 2: // channel C, PH5
if inverting {
avr.PORTH.SetBits(1 << 5) // PH4 high
} else {
avr.PORTH.ClearBits(1 << 5) // PH4 low
}
}
case 5:
// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
// It will be set the following call to Set(), however.
switch channel {
case 0: // channel A, PL3
if inverting {
avr.PORTL.SetBits(1 << 3) // PL3 high
} else {
avr.PORTL.ClearBits(1 << 3) // PL3 low
}
case 1: // channel B, PL4
if inverting {
avr.PORTL.SetBits(1 << 4) // PL4 high
} else {
avr.PORTL.ClearBits(1 << 4) // PL4 low
}
case 2: // channel C, PH5
if inverting {
avr.PORTL.SetBits(1 << 5) // PL4 high
} else {
avr.PORTL.ClearBits(1 << 5) // PL4 low
}
}
}
}
// 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, PB5
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 << 5) { // 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, PB6
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 << 6) { // is PB6 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)
}
}
case 3:
mask := interrupt.Disable()
switch channel {
case 0: // channel A, PE3
if value == 0 {
avr.TCCR3A.ClearBits(avr.TCCR3A_COM3A1 | avr.TCCR3A_COM3A0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR3AH.Set(uint8(value >> 8))
avr.OCR3AL.Set(uint8(value))
if avr.PORTE.HasBits(1 << 3) { // is PE3 high?
// Yes, set the inverting bit.
avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1 | avr.TCCR3A_COM3A0)
} else {
// No, output is non-inverting.
avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1)
}
}
case 1: // channel B, PE4
if value == 0 {
avr.TCCR3A.ClearBits(avr.TCCR3A_COM3B1 | avr.TCCR3A_COM3B0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR3BH.Set(uint8(value >> 8))
avr.OCR3BL.Set(uint8(value))
if avr.PORTE.HasBits(1 << 4) { // is PE4 high?
// Yes, set the inverting bit.
avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1 | avr.TCCR3A_COM3B0)
} else {
// No, output is non-inverting.
avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1)
}
}
case 2: // channel C, PE5
if value == 0 {
avr.TCCR3A.ClearBits(avr.TCCR3A_COM3C1 | avr.TCCR3A_COM3C0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR3CH.Set(uint8(value >> 8))
avr.OCR3CL.Set(uint8(value))
if avr.PORTE.HasBits(1 << 5) { // is PE5 high?
// Yes, set the inverting bit.
avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1 | avr.TCCR3A_COM3C0)
} else {
// No, output is non-inverting.
avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1)
}
}
}
interrupt.Restore(mask)
case 4:
mask := interrupt.Disable()
switch channel {
case 0: // channel A, PH3
if value == 0 {
avr.TCCR4A.ClearBits(avr.TCCR4A_COM4A1 | avr.TCCR4A_COM4A0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR4AH.Set(uint8(value >> 8))
avr.OCR4AL.Set(uint8(value))
if avr.PORTH.HasBits(1 << 3) { // is PH3 high?
// Yes, set the inverting bit.
avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1 | avr.TCCR4A_COM4A0)
} else {
// No, output is non-inverting.
avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1)
}
}
case 1: // channel B, PH4
if value == 0 {
avr.TCCR4A.ClearBits(avr.TCCR4A_COM4B1 | avr.TCCR4A_COM4B0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR4BH.Set(uint8(value >> 8))
avr.OCR4BL.Set(uint8(value))
if avr.PORTH.HasBits(1 << 4) { // is PH4 high?
// Yes, set the inverting bit.
avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1 | avr.TCCR4A_COM4B0)
} else {
// No, output is non-inverting.
avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1)
}
}
case 2: // channel C, PH5
if value == 0 {
avr.TCCR4A.ClearBits(avr.TCCR4A_COM4C1 | avr.TCCR4A_COM4C0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR4CH.Set(uint8(value >> 8))
avr.OCR4CL.Set(uint8(value))
if avr.PORTH.HasBits(1 << 5) { // is PH5 high?
// Yes, set the inverting bit.
avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1 | avr.TCCR4A_COM4C0)
} else {
// No, output is non-inverting.
avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1)
}
}
}
interrupt.Restore(mask)
case 5:
mask := interrupt.Disable()
switch channel {
case 0: // channel A, PL3
if value == 0 {
avr.TCCR5A.ClearBits(avr.TCCR5A_COM5A1 | avr.TCCR5A_COM5A0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR5AH.Set(uint8(value >> 8))
avr.OCR5AL.Set(uint8(value))
if avr.PORTL.HasBits(1 << 3) { // is PL3 high?
// Yes, set the inverting bit.
avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1 | avr.TCCR5A_COM5A0)
} else {
// No, output is non-inverting.
avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1)
}
}
case 1: // channel B, PL4
if value == 0 {
avr.TCCR5A.ClearBits(avr.TCCR5A_COM5B1 | avr.TCCR5A_COM5B0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR5BH.Set(uint8(value >> 8))
avr.OCR5BL.Set(uint8(value))
if avr.PORTL.HasBits(1 << 4) { // is PL4 high?
// Yes, set the inverting bit.
avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1 | avr.TCCR5A_COM5B0)
} else {
// No, output is non-inverting.
avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1)
}
}
case 2: // channel C, PL5
if value == 0 {
avr.TCCR5A.ClearBits(avr.TCCR5A_COM5C1 | avr.TCCR5A_COM5C0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR5CH.Set(uint8(value >> 8))
avr.OCR5CL.Set(uint8(value))
if avr.PORTL.HasBits(1 << 5) { // is PL5 high?
// Yes, set the inverting bit.
avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1 | avr.TCCR5A_COM5C0)
} else {
// No, output is non-inverting.
avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1)
}
}
}
interrupt.Restore(mask)
}
}
// SPI configuration
var SPI0 = SPI{
spcr: avr.SPCR,
spdr: avr.SPDR,
spsr: avr.SPSR,
sck: PB1,
sdo: PB2,
sdi: PB3,
cs: PB0}
+59 -421
View File
@@ -4,7 +4,6 @@ package machine
import ( import (
"device/avr" "device/avr"
"runtime/interrupt"
"runtime/volatile" "runtime/volatile"
) )
@@ -22,432 +21,71 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
} }
} }
// PWM is one PWM peripheral, which consists of a counter and two output // InitPWM initializes the registers needed for PWM.
// channels (that can be connected to two fixed pins). You can set the frequency func InitPWM() {
// using SetPeriod, but only for all the channels in this PWM peripheral at // use waveform generation
// once. avr.TCCR0A.SetBits(avr.TCCR0A_WGM00)
type PWM struct {
num uint8 // set timer 0 prescale factor to 64
avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00)
// set timer 1 prescale factor to 64
avr.TCCR1B.SetBits(avr.TCCR1B_CS11)
// put timer 1 in 8-bit phase correct pwm mode
avr.TCCR1A.SetBits(avr.TCCR1A_WGM10)
// set timer 2 prescale factor to 64
avr.TCCR2B.SetBits(avr.TCCR2B_CS22)
// configure timer 2 for phase correct pwm (8-bit)
avr.TCCR2A.SetBits(avr.TCCR2A_WGM20)
} }
var ( // Configure configures a PWM pin for output.
Timer0 = PWM{0} // 8 bit timer for PD5 and PD6 func (pwm PWM) Configure() error {
Timer1 = PWM{1} // 16 bit timer for PB1 and PB2 switch pwm.Pin / 8 {
Timer2 = PWM{2} // 8 bit timer for PB3 and PD3 case 0: // port B
) avr.DDRB.SetBits(1 << uint8(pwm.Pin))
case 2: // port D
// Configure enables and configures this PWM. avr.DDRD.SetBits(1 << uint8(pwm.Pin-16))
//
// 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
} }
// SetPeriod updates the period of this PWM peripheral. // Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
// To set a particular frequency, use the following formula: // 8-bit value ranging from 0 to 255.
// func (pwm PWM) Set(value uint16) {
// period = 1e9 / frequency value8 := uint8(value >> 8)
// switch pwm.Pin {
// If you use a period of 0, a period that works well for LEDs will be picked. case PD3:
// // connect pwm to pin on timer 2, channel B
// SetPeriod will not change the prescaler, but also won't change the current avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
// value in any of the channels. This means that you may need to update the avr.OCR2B.Set(value8) // set pwm duty
// value for the particular channel. case PD5:
// // connect pwm to pin on timer 0, channel B
// Note that you cannot pick any arbitrary period after the PWM peripheral has avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
// been configured. If you want to switch between frequencies, pick the lowest avr.OCR0B.Set(value8) // set pwm duty
// frequency (longest period) once when calling Configure and adjust the case PD6:
// frequency here as needed. // connect pwm to pin on timer 0, channel A
func (pwm PWM) SetPeriod(period uint64) error { avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
if pwm.num != 1 { avr.OCR0A.Set(value8) // set pwm duty
return ErrPWMPeriodTooLong // TODO better error message case PB1:
} // connect pwm to pin on timer 1, channel A
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
// The top value is the number of PWM ticks a PWM period takes. It is // this is a 16-bit value, but we only currently allow the low order bits to be set
// initially picked assuming an unlimited counter top and no PWM avr.OCR1AL.Set(value8) // set pwm duty
// prescaler. case PB2:
var top uint64 // connect pwm to pin on timer 1, channel B
if period == 0 { avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
// Use a top appropriate for LEDs. Picking a relatively low period // this is a 16-bit value, but we only currently allow the low order bits to be set
// here (0xff) for consistency with the other timers. avr.OCR1BL.Set(value8) // set pwm duty
top = 0xff case PB3:
} else { // connect pwm to pin on timer 2, channel A
// The formula below calculates the following formula, optimized: avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
// top = period * (CPUFrequency() / 1e9) avr.OCR2A.Set(value8) // set pwm duty
// By dividing the CPU frequency first (an operation that is easily default:
// optimized away) the period has less chance of overflowing. panic("Invalid PWM pin")
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)
}
}
} }
} }
+259 -384
View File
@@ -8,6 +8,7 @@
package machine package machine
import ( import (
"device"
"device/arm" "device/arm"
"device/sam" "device/sam"
"errors" "errors"
@@ -16,6 +17,8 @@ import (
"unsafe" "unsafe"
) )
type PinMode uint8
const ( const (
PinAnalog PinMode = 1 PinAnalog PinMode = 1
PinSERCOM PinMode = 2 PinSERCOM PinMode = 2
@@ -28,8 +31,8 @@ const (
PinInput PinMode = 9 PinInput PinMode = 9
PinInputPullup PinMode = 10 PinInputPullup PinMode = 10
PinOutput PinMode = 11 PinOutput PinMode = 11
PinTCC PinMode = PinTimer PinPWM PinMode = PinTimer
PinTCCAlt PinMode = PinTimerAlt PinPWMAlt PinMode = PinTimerAlt
PinInputPulldown PinMode = 12 PinInputPulldown PinMode = 12
) )
@@ -430,18 +433,7 @@ func (a ADC) Get() uint16 {
sam.ADC.CTRLA.ClearBits(sam.ADC_CTRLA_ENABLE) sam.ADC.CTRLA.ClearBits(sam.ADC_CTRLA_ENABLE)
waitADCSync() waitADCSync()
// scales to 16-bit result return uint16(val) << 4 // scales from 12 to 16-bit result
switch (sam.ADC.CTRLB.Get() & sam.ADC_CTRLB_RESSEL_Msk) >> sam.ADC_CTRLB_RESSEL_Pos {
case sam.ADC_CTRLB_RESSEL_8BIT:
val = val << 8
case sam.ADC_CTRLB_RESSEL_10BIT:
val = val << 6
case sam.ADC_CTRLB_RESSEL_16BIT:
val = val << 4
case sam.ADC_CTRLB_RESSEL_12BIT:
val = val << 4
}
return val
} }
func (a ADC) getADCChannel() uint8 { func (a ADC) getADCChannel() uint8 {
@@ -1298,21 +1290,43 @@ var (
// spi.Tx(nil, rx) // spi.Tx(nil, rx)
// //
func (spi SPI) Tx(w, r []byte) error { func (spi SPI) Tx(w, r []byte) error {
switch { if spi.Bus.BAUD.Get() == 0x00 {
case w == nil: // When the SPI Freq is 24MHz, special processing is performed to improve the speed.
// read only, so write zero and read a result.
spi.rx(r)
case r == nil:
// write only
spi.tx(w)
default: switch {
// write/read case w == nil:
if len(w) != len(r) { // read only, so write zero and read a result.
return ErrTxInvalidSliceSize spi.rx(r)
case r == nil:
// write only
spi.tx24mhz(w)
default:
// write/read
if len(w) != len(r) {
return ErrTxInvalidSliceSize
}
spi.txrx24mhz(w, r)
} }
spi.txrx(w, r) } else {
switch {
case w == nil:
// 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
@@ -1365,373 +1379,243 @@ func (spi SPI) txrx(tx, rx []byte) {
rx[len(rx)-1] = byte(spi.Bus.DATA.Get()) rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
} }
// TCC is one timer/counter peripheral, which consists of a counter and multiple // tx24mhz is a special tx/rx function for CPU Clock 48 Mhz and SPI Freq 24 Mhz
// output channels (that can be connected to actual pins). You can set the func (spi SPI) tx24mhz(tx []byte) {
// frequency using SetPeriod, but only for all the channels in this TCC spi.Bus.DATA.Set(uint32(tx[0]))
// peripheral at once. device.Asm("nop")
type TCC sam.TCC_Type device.Asm("nop")
device.Asm("nop")
device.Asm("nop")
device.Asm("nop")
device.Asm("nop")
// The SAM D21 has three TCC peripherals, which have PWM as one feature. for i := 1; i < len(tx); i++ {
var ( spi.Bus.DATA.Set(uint32(tx[i]))
TCC0 = (*TCC)(sam.TCC0) device.Asm("nop")
TCC1 = (*TCC)(sam.TCC1) device.Asm("nop")
TCC2 = (*TCC)(sam.TCC2) spi.Bus.DATA.Get()
) }
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
//go:inline }
func (tcc *TCC) timer() *sam.TCC_Type { spi.Bus.DATA.Get()
return (*sam.TCC_Type)(tcc)
} }
// Configure enables and configures this TCC. // txrx24mhz is a special tx/rx function for CPU Clock 48 Mhz and SPI Freq 24 Mhz
func (tcc *TCC) Configure(config PWMConfig) error { func (spi SPI) txrx24mhz(tx, rx []byte) {
// Enable the clock source for this timer. spi.Bus.DATA.Set(uint32(tx[0]))
switch tcc.timer() { device.Asm("nop")
case sam.TCC0: device.Asm("nop")
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC0_) device.Asm("nop")
// Use GCLK0 for TCC0/TCC1 device.Asm("nop")
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_TCC0_TCC1 << sam.GCLK_CLKCTRL_ID_Pos) | device.Asm("nop")
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) | device.Asm("nop")
sam.GCLK_CLKCTRL_CLKEN)
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) { for i := 1; i < len(rx); i++ {
} spi.Bus.DATA.Set(uint32(tx[i]))
case sam.TCC1: device.Asm("nop")
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC1_) device.Asm("nop")
// Use GCLK0 for TCC0/TCC1 rx[i-1] = byte(spi.Bus.DATA.Get())
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) | for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
sam.GCLK_CLKCTRL_CLKEN) }
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) { rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
} }
case sam.TCC2:
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC2_) // PWM
// Use GCLK0 for TCC2/TC3 const period = 0xFFFF
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) | // InitPWM initializes the PWM interface.
sam.GCLK_CLKCTRL_CLKEN) func InitPWM() {
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) { // turn on timer clocks used for PWM
} sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC0_ | sam.PM_APBCMASK_TCC1_ | sam.PM_APBCMASK_TCC2_)
// 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) {
} }
// Disable timer (if it was enabled). This is necessary because // Use GCLK0 for TCC2/TC3
// tcc.setPeriod may want to change the prescaler bits in CTRLA, which is sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_TCC2_TC3 << sam.GCLK_CLKCTRL_ID_Pos) |
// only allowed when the TCC is disabled. (sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
tcc.timer().CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE) sam.GCLK_CLKCTRL_CLKEN)
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
}
}
// Configure configures a PWM pin for output.
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) // Use "Normal PWM" (single-slope PWM)
tcc.timer().WAVE.Set(sam.TCC_WAVE_WAVEGEN_NPWM) timer.WAVE.SetBits(sam.TCC_WAVE_WAVEGEN_NPWM)
// Wait for synchronization
// Wait for synchronization of all changed registers. for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_WAVE) {
for tcc.timer().SYNCBUSY.Get() != 0 {
} }
// Set the period and prescaler. // Set the period (the number to count to (TOP) before resetting timer)
err := tcc.setPeriod(config.Period, true) //TCC0->PER.reg = period;
timer.PER.Set(period)
// Enable the timer. // Wait for synchronization
tcc.timer().CTRLA.SetBits(sam.TCC_CTRLA_ENABLE) for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_PER) {
// 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. // Set pin as output
return err sam.PORT.DIRSET0.Set(1 << uint8(pwm.Pin))
} // Set pin to low
sam.PORT.OUTCLR0.Set(1 << uint8(pwm.Pin))
// SetPeriod updates the period of this TCC peripheral. // Enable the port multiplexer for pin
// To set a particular frequency, use the following formula: pwm.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
//
// period = 1e9 / frequency // Connect TCCX timer to pin.
// // we normally use the F channel aka ALT
// If you use a period of 0, a period that works well for LEDs will be picked. pwmConfig := PinPWMAlt
//
// SetPeriod will not change the prescaler, but also won't change the current // in the case of PA6 or PA7 we have to use E channel
// value in any of the channels. This means that you may need to update the if pwm.Pin == 6 || pwm.Pin == 7 {
// value for the particular channel. pwmConfig = PinPWM
//
// 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 if pwm.Pin&1 > 0 {
// well. The prescaler can only modified when the TCC is disabled, that is, in // odd pin, so save the even pins
// the Configure function. val := pwm.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
func (tcc *TCC) setPeriod(period uint64, updatePrescaler bool) error { pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_PMUX0_PMUXO_Pos))
var top uint64
if period == 0 {
// Make sure the TOP value is at 0xffff (enough for a 16-bit timer).
top = 0xffff
} else { } else {
// The formula below calculates the following formula, optimized: // even pin, so save the odd pins
// period * (48e6 / 1e9) val := pwm.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
// This assumes that the chip is running at the (default) 48MHz speed. pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_PMUX0_PMUXE_Pos))
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 return nil
} }
// Top returns the current counter top, for use in duty cycle calculation. It // Set turns on the duty cycle for a PWM pin using the provided value.
// will only change with a call to Configure or SetPeriod, otherwise it is func (pwm PWM) Set(value uint16) {
// constant. // figure out which TCCX timer for this pin
// timer := pwm.getTimer()
// The value returned here is hardware dependent. In general, it's best to treat if timer == nil {
// it as an opaque value that can be divided by some number and passed to Set // The Configure call above cannot have succeeded, so simply ignore this
// (see Set documentation for more information). // error.
func (tcc *TCC) Top() uint32 { return
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 { // disable output
// No pin could be found. timer.CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
return 0, ErrInvalidOutputPin
// Wait for synchronization
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
} }
// 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
switch channel { pwm.setChannel(timer, uint32(value))
case 0:
tcc.timer().CC0.Set(value) // Wait for synchronization on all channels
case 1: for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0 |
tcc.timer().CC1.Set(value) sam.TCC_SYNCBUSY_CC1 |
case 2: sam.TCC_SYNCBUSY_CC2 |
tcc.timer().CC2.Set(value) sam.TCC_SYNCBUSY_CC3) {
case 3:
tcc.timer().CC3.Set(value)
default:
// invalid PWM channel, ignore.
} }
// Wait for synchronization on all channels (or anything in this peripheral, // enable
// really). timer.CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
for tcc.timer().SYNCBUSY.Get() != 0 { // 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:
return nil // not supported on this pin
}
}
// 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 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
} }
} }
@@ -2242,7 +2126,7 @@ func handleStandardSetup(setup usbSetup) bool {
func cdcSetup(setup usbSetup) bool { func cdcSetup(setup usbSetup) bool {
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE { if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
if setup.bRequest == usb_CDC_GET_LINE_CODING { if setup.bRequest == usb_CDC_GET_LINE_CODING {
var b [cdcLineInfoSize]byte b := make([]byte, 7)
b[0] = byte(usbLineInfo.dwDTERate) b[0] = byte(usbLineInfo.dwDTERate)
b[1] = byte(usbLineInfo.dwDTERate >> 8) b[1] = byte(usbLineInfo.dwDTERate >> 8)
b[2] = byte(usbLineInfo.dwDTERate >> 16) b[2] = byte(usbLineInfo.dwDTERate >> 16)
@@ -2251,18 +2135,14 @@ func cdcSetup(setup usbSetup) bool {
b[5] = byte(usbLineInfo.bParityType) b[5] = byte(usbLineInfo.bParityType)
b[6] = byte(usbLineInfo.bDataBits) b[6] = byte(usbLineInfo.bDataBits)
sendUSBPacket(0, b[:]) sendUSBPacket(0, b)
return true return true
} }
} }
if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE { if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
if setup.bRequest == usb_CDC_SET_LINE_CODING { if setup.bRequest == usb_CDC_SET_LINE_CODING {
b, err := receiveUSBControlPacket() b := receiveUSBControlPacket()
if err != nil {
return false
}
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
usbLineInfo.bCharFormat = b[4] usbLineInfo.bCharFormat = b[4]
usbLineInfo.bParityType = b[5] usbLineInfo.bParityType = b[5]
@@ -2309,9 +2189,7 @@ func sendUSBPacket(ep uint32, data []byte) {
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(uint32((len(data) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)) usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(uint32((len(data) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos))
} }
func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) { func receiveUSBControlPacket() []byte {
var b [cdcLineInfoSize]byte
// address // address
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0])))) usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
@@ -2326,7 +2204,7 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
for (getEPSTATUS(0) & sam.USB_DEVICE_EPSTATUS_BK0RDY) == 0 { for (getEPSTATUS(0) & sam.USB_DEVICE_EPSTATUS_BK0RDY) == 0 {
timeout-- timeout--
if timeout == 0 { if timeout == 0 {
return b, errUSBCDCReadTimeout return []byte{}
} }
} }
@@ -2335,7 +2213,7 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT0) == 0 { for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT0) == 0 {
timeout-- timeout--
if timeout == 0 { if timeout == 0 {
return b, errUSBCDCReadTimeout return []byte{}
} }
} }
@@ -2343,13 +2221,10 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >> bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
if bytesread != cdcLineInfoSize { data := make([]byte, bytesread)
return b, errUSBCDCBytesRead copy(data, udd_ep_out_cache_buffer[0][:])
}
copy(b[:7], udd_ep_out_cache_buffer[0][:7]) return data
return b, nil
} }
func handleEndpoint(ep uint32) { func handleEndpoint(ep uint32) {
+240 -393
View File
@@ -1,4 +1,4 @@
// +build sam,atsamd51 sam,atsame5x // +build sam,atsamd51
// Peripheral abstraction layer for the atsamd51. // Peripheral abstraction layer for the atsamd51.
// //
@@ -20,6 +20,8 @@ func CPUFrequency() uint32 {
return 120000000 return 120000000
} }
type PinMode uint8
const ( const (
PinAnalog PinMode = 1 PinAnalog PinMode = 1
PinSERCOM PinMode = 2 PinSERCOM PinMode = 2
@@ -38,13 +40,10 @@ const (
PinInput PinMode = 15 PinInput PinMode = 15
PinInputPullup PinMode = 16 PinInputPullup PinMode = 16
PinOutput PinMode = 17 PinOutput PinMode = 17
PinTCCE PinMode = PinTimer PinPWME PinMode = PinTimer
PinTCCF PinMode = PinTimerAlt PinPWMF PinMode = PinTimerAlt
PinTCCG PinMode = PinTCCPDEC PinPWMG PinMode = PinTCCPDEC
PinInputPulldown PinMode = 18 PinInputPulldown PinMode = 18
PinCAN PinMode = 19
PinCAN0 PinMode = PinSDHC
PinCAN1 PinMode = PinCom
) )
type PinChange uint8 type PinChange uint8
@@ -628,18 +627,6 @@ func (p Pin) Configure(config PinConfig) {
} }
// enable port config // enable port config
p.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN | sam.PORT_GROUP_PINCFG_DRVSTR) p.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN | sam.PORT_GROUP_PINCFG_DRVSTR)
case PinSDHC:
if p&1 > 0 {
// odd pin, so save the even pins
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
p.setPMux(val | (uint8(PinSDHC) << sam.PORT_GROUP_PMUX_PMUXO_Pos))
} else {
// even pin, so save the odd pins
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
p.setPMux(val | (uint8(PinSDHC) << sam.PORT_GROUP_PMUX_PMUXE_Pos))
}
// enable port config
p.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN)
} }
} }
@@ -813,18 +800,6 @@ func (a ADC) Configure(config ADCConfig) {
} }
a.Pin.Configure(PinConfig{Mode: PinAnalog}) a.Pin.Configure(PinConfig{Mode: PinAnalog})
switch config.Bus {
case AdcBusAuto:
bus := a.getADCBus()
if bus == sam.ADC0 {
a.Bus = 0
} else {
a.Bus = 1
}
default:
a.Bus = config.Bus
}
} }
// Get returns the current value of a ADC pin, in the range 0..0xffff. // Get returns the current value of a ADC pin, in the range 0..0xffff.
@@ -873,27 +848,10 @@ func (a ADC) Get() uint16 {
for bus.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_ENABLE) { for bus.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_ENABLE) {
} }
// scales to 16-bit result return uint16(val) << 4 // scales from 12 to 16-bit result
switch (bus.CTRLB.Get() & sam.ADC_CTRLB_RESSEL_Msk) >> sam.ADC_CTRLB_RESSEL_Pos {
case sam.ADC_CTRLB_RESSEL_8BIT:
val = val << 8
case sam.ADC_CTRLB_RESSEL_10BIT:
val = val << 6
case sam.ADC_CTRLB_RESSEL_16BIT:
val = val << 4
case sam.ADC_CTRLB_RESSEL_12BIT:
val = val << 4
}
return val
} }
func (a ADC) getADCBus() *sam.ADC_Type { func (a ADC) getADCBus() *sam.ADC_Type {
if a.Bus == AdcBus0 {
return sam.ADC0
} else if a.Bus == AdcBus1 {
return sam.ADC1
}
if (a.Pin >= PB04 && a.Pin <= PB07) || (a.Pin >= PC00) { if (a.Pin >= PB04 && a.Pin <= PB07) || (a.Pin >= PC00) {
return sam.ADC1 return sam.ADC1
} }
@@ -905,17 +863,9 @@ func (a ADC) getADCChannel() uint8 {
case PA02: case PA02:
return 0 return 0
case PB08: case PB08:
if a.Bus == AdcBus1 { return 2
return 0
} else {
return 2
}
case PB09: case PB09:
if a.Bus == AdcBus1 { return 3
return 1
} else {
return 3
}
case PA04: case PA04:
return 4 return 4
case PA05: case PA05:
@@ -1620,350 +1570,256 @@ const (
QSPI_DATA3 = PA11 QSPI_DATA3 = PA11
) )
// TCC is one timer peripheral, which consists of a counter and multiple output // PWM
// channels (that can be connected to actual pins). You can set the frequency const period = 0xFFFF
// using SetPeriod, but only for all the channels in this timer peripheral at
// once.
type TCC sam.TCC_Type
//go:inline // Configure configures a PWM pin for output.
func (tcc *TCC) timer() *sam.TCC_Type { func (pwm PWM) Configure() error {
return (*sam.TCC_Type)(tcc) // Set pin as output
} sam.PORT.GROUP[0].DIRSET.Set(1 << uint8(pwm.Pin))
// Set pin to low
sam.PORT.GROUP[0].OUTCLR.Set(1 << uint8(pwm.Pin))
// Configure enables and configures this TCC. // Enable the port multiplexer for pin
func (tcc *TCC) Configure(config PWMConfig) error { pwm.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN)
// Enable the TCC clock to be able to use the TCC.
tcc.configureClock()
// Disable timer (if it was enabled). This is necessary because // Connect timer/mux to pin.
// tcc.setPeriod may want to change the prescaler bits in CTRLA, which is pwmConfig := pwm.getMux()
// only allowed when the TCC is disabled.
tcc.timer().CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE) if pwm.Pin&1 > 0 {
// 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)
tcc.timer().WAVE.Set(sam.TCC_WAVE_WAVEGEN_NPWM) timer.WAVE.SetBits(sam.TCC_WAVE_WAVEGEN_NPWM)
// Wait for synchronization
// Wait for synchronization of all changed registers. for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_WAVE) {
for tcc.timer().SYNCBUSY.Get() != 0 {
} }
// Set the period and prescaler. // while (TCCx->SYNCBUSY.bit.CC0 || TCCx->SYNCBUSY.bit.CC1);
err := tcc.setPeriod(config.Period, true) for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
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 {
} }
// Return any error that might have occured in the tcc.setPeriod call. // Set the initial value
return err // TCCx->CC[tcChannel].reg = (uint32_t) value;
} pwm.setChannel(timer, 0)
// SetPeriod updates the period of this TCC peripheral. for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
// To set a particular frequency, use the following formula: timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
//
// 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
} }
maxTop := uint64(0xffff) // Set the period (the number to count to (TOP) before resetting timer)
if tcc.timer() == sam.TCC0 || tcc.timer() == sam.TCC1 { //TCC0->PER.reg = period;
// Only TCC0 and TCC1 are 24-bit timers, the rest are 16-bit. timer.PER.Set(period)
maxTop = 0xffffff // Wait for synchronization
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_PER) {
} }
if updatePrescaler { // enable timer
// This function was called during Configure(), with the timer disabled. timer.CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
// Note that updating the prescaler can only happen while the peripheral // Wait for synchronization
// is disabled. for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
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
} }
// Top returns the current counter top, for use in duty cycle calculation. It // Set turns on the duty cycle for a PWM pin using the provided value.
// will only change with a call to Configure or SetPeriod, otherwise it is func (pwm PWM) Set(value uint16) {
// constant. // figure out which TCCX timer for this pin
// timer := pwm.getTimer()
// The value returned here is hardware dependent. In general, it's best to treat if timer == nil {
// it as an opaque value that can be divided by some number and passed to // The Configure call above cannot have succeeded, so simply ignore this
// tcc.Set (see tcc.Set for more information). // error.
func (tcc *TCC) Top() uint32 { return
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
} }
mapping := pinTimerMapping[pin/2] // Wait for synchronization
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CTRLB) {
// Check for column F in the datasheet. }
if mapping.F>>4-1 == timer { for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
return PinTCCF, mapping.F&0x0f + uint8(pin)&1 timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
} }
// Check for column G in the datasheet. // TCCx->CCBUF[tcChannel].reg = (uint32_t) value;
if mapping.G>>4-1 == timer { pwm.setChannelBuffer(timer, uint32(value))
return PinTCCG, mapping.G&0x0f + uint8(pin)&1
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
} }
// Nothing found. // TCCx->CTRLBCLR.bit.LUPD = 1;
return 0, 0 timer.CTRLBCLR.SetBits(sam.TCC_CTRLBCLR_LUPD)
} 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 {
} }
} }
// Set updates the channel value. This is used to control the channel duty // getPMux returns the value for the correct PMUX register for this pin.
// cycle, in other words the fraction of time the channel output is high (or low func (pwm PWM) getPMux() uint8 {
// when inverted). For example, to set it to a 25% duty cycle, use: return pwm.Pin.getPMux()
// }
// tcc.Set(channel, tcc.Top() / 4)
// // setPMux sets the value for the correct PMUX register for this pin.
// tcc.Set(channel, 0) will set the output to low and tcc.Set(channel, func (pwm PWM) setPMux(val uint8) {
// tcc.Top()) will set the output to high, assuming the output isn't inverted. pwm.Pin.setPMux(val)
func (tcc *TCC) Set(channel uint8, value uint32) { }
// Update CCBUF, which provides double buffering. The update is applied on
// the next cycle. // getPinCfg returns the value for the correct PINCFG register for this pin.
tcc.timer().CCBUF[channel].Set(value) func (pwm PWM) getPinCfg() uint8 {
for tcc.timer().SYNCBUSY.Get() != 0 { 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)
}
// 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
} }
} }
@@ -2476,7 +2332,7 @@ func handleStandardSetup(setup usbSetup) bool {
func cdcSetup(setup usbSetup) bool { func cdcSetup(setup usbSetup) bool {
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE { if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
if setup.bRequest == usb_CDC_GET_LINE_CODING { if setup.bRequest == usb_CDC_GET_LINE_CODING {
var b [cdcLineInfoSize]byte b := make([]byte, 7)
b[0] = byte(usbLineInfo.dwDTERate) b[0] = byte(usbLineInfo.dwDTERate)
b[1] = byte(usbLineInfo.dwDTERate >> 8) b[1] = byte(usbLineInfo.dwDTERate >> 8)
b[2] = byte(usbLineInfo.dwDTERate >> 16) b[2] = byte(usbLineInfo.dwDTERate >> 16)
@@ -2485,18 +2341,14 @@ func cdcSetup(setup usbSetup) bool {
b[5] = byte(usbLineInfo.bParityType) b[5] = byte(usbLineInfo.bParityType)
b[6] = byte(usbLineInfo.bDataBits) b[6] = byte(usbLineInfo.bDataBits)
sendUSBPacket(0, b[:]) sendUSBPacket(0, b)
return true return true
} }
} }
if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE { if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
if setup.bRequest == usb_CDC_SET_LINE_CODING { if setup.bRequest == usb_CDC_SET_LINE_CODING {
b, err := receiveUSBControlPacket() b := receiveUSBControlPacket()
if err != nil {
return false
}
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
usbLineInfo.bCharFormat = b[4] usbLineInfo.bCharFormat = b[4]
usbLineInfo.bParityType = b[5] usbLineInfo.bParityType = b[5]
@@ -2543,9 +2395,7 @@ func sendUSBPacket(ep uint32, data []byte) {
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(uint32((len(data) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)) usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(uint32((len(data) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos))
} }
func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) { func receiveUSBControlPacket() []byte {
var b [cdcLineInfoSize]byte
// address // address
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0])))) usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
@@ -2560,7 +2410,7 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
for (getEPSTATUS(0) & sam.USB_DEVICE_ENDPOINT_EPSTATUS_BK0RDY) == 0 { for (getEPSTATUS(0) & sam.USB_DEVICE_ENDPOINT_EPSTATUS_BK0RDY) == 0 {
timeout-- timeout--
if timeout == 0 { if timeout == 0 {
return b, errUSBCDCReadTimeout return []byte{}
} }
} }
@@ -2569,7 +2419,7 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 { for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
timeout-- timeout--
if timeout == 0 { if timeout == 0 {
return b, errUSBCDCReadTimeout return []byte{}
} }
} }
@@ -2577,13 +2427,10 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >> bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
if bytesread != cdcLineInfoSize { data := make([]byte, bytesread)
return b, errUSBCDCBytesRead copy(data, udd_ep_out_cache_buffer[0][:])
}
copy(b[:7], udd_ep_out_cache_buffer[0][:7]) return data
return b, nil
} }
func handleEndpoint(ep uint32) { func handleEndpoint(ep uint32) {
+34 -28
View File
@@ -11,37 +11,43 @@ import "device/sam"
const HSRAM_SIZE = 0x00030000 const HSRAM_SIZE = 0x00030000
// This chip has three TCC peripherals, which have PWM as one feature. // InitPWM initializes the PWM interface.
var ( func InitPWM() {
TCC0 = (*TCC)(sam.TCC0) // turn on timer clocks used for PWM
TCC1 = (*TCC)(sam.TCC1) sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
TCC2 = (*TCC)(sam.TCC2) sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
)
func (tcc *TCC) configureClock() { //use clock generator 0
// Turn on timer clocks used for TCC and use generic clock generator 0. sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
switch tcc.timer() { sam.GCLK_PCHCTRL_CHEN)
case sam.TCC0: sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_) sam.GCLK_PCHCTRL_CHEN)
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:
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)
case sam.TCC2:
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
}
} }
func (tcc *TCC) timerNum() uint8 { // getTimer returns the timer to be used for PWM on this pin
switch tcc.timer() { func (pwm PWM) getTimer() *sam.TCC_Type {
case sam.TCC0: switch pwm.Pin {
return 0 case PA14:
case sam.TCC1: return sam.TCC2
return 1 case PA15:
case sam.TCC2: return sam.TCC2
return 2 case PA16:
return sam.TCC1
case PA17:
return sam.TCC1
case PA18:
return sam.TCC1
case PA19:
return sam.TCC1
case PA20:
return sam.TCC0
case PA21:
return sam.TCC0
case PA22:
return sam.TCC0
case PA23:
return sam.TCC0
default: default:
return 0x0f // should not happen return nil // not supported on this pin
} }
} }
+51 -40
View File
@@ -11,49 +11,60 @@ import "device/sam"
const HSRAM_SIZE = 0x00030000 const HSRAM_SIZE = 0x00030000
// This chip has five TCC peripherals, which have PWM as one feature. // InitPWM initializes the PWM interface.
var ( func InitPWM() {
TCC0 = (*TCC)(sam.TCC0) // turn on timer clocks used for PWM
TCC1 = (*TCC)(sam.TCC1) sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
TCC2 = (*TCC)(sam.TCC2) sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
TCC3 = (*TCC)(sam.TCC3) sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
TCC4 = (*TCC)(sam.TCC4)
)
func (tcc *TCC) configureClock() { //use clock generator 0
// Turn on timer clocks used for the TCC and use generic clock generator 0. sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
switch tcc.timer() { sam.GCLK_PCHCTRL_CHEN)
case sam.TCC0: sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_) sam.GCLK_PCHCTRL_CHEN)
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[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
case sam.TCC1: sam.GCLK_PCHCTRL_CHEN)
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)
case sam.TCC2:
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC3:
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC4:
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
}
} }
func (tcc *TCC) timerNum() uint8 { // getTimer returns the timer to be used for PWM on this pin
switch tcc.timer() { func (pwm PWM) getTimer() *sam.TCC_Type {
case sam.TCC0: switch pwm.Pin {
return 0 case PA14:
case sam.TCC1: return sam.TCC2
return 1 case PA15:
case sam.TCC2: return sam.TCC2
return 2 case PA16:
case sam.TCC3: return sam.TCC1
return 3 case PA17:
case sam.TCC4: return sam.TCC1
return 4 case PA18:
return sam.TCC1
case PA19:
return sam.TCC1
case PA20:
return sam.TCC0
case PA21:
return sam.TCC0
case PA22:
return sam.TCC0
case PA23:
return sam.TCC0
case PB12:
return sam.TCC3
case PB13:
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: default:
return 0x0f // should not happen return nil // not supported on this pin
} }
} }
+51 -40
View File
@@ -11,49 +11,60 @@ import "device/sam"
const HSRAM_SIZE = 0x00040000 const HSRAM_SIZE = 0x00040000
// This chip has five TCC peripherals, which have PWM as one feature. // InitPWM initializes the PWM interface.
var ( func InitPWM() {
TCC0 = (*TCC)(sam.TCC0) // turn on timer clocks used for PWM
TCC1 = (*TCC)(sam.TCC1) sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
TCC2 = (*TCC)(sam.TCC2) sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
TCC3 = (*TCC)(sam.TCC3) sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
TCC4 = (*TCC)(sam.TCC4)
)
func (tcc *TCC) configureClock() { //use clock generator 0
// Turn on timer clocks used for TCC and use generic clock generator 0. sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
switch tcc.timer() { sam.GCLK_PCHCTRL_CHEN)
case sam.TCC0: sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_) sam.GCLK_PCHCTRL_CHEN)
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[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
case sam.TCC1: sam.GCLK_PCHCTRL_CHEN)
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)
case sam.TCC2:
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC3:
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC4:
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
}
} }
func (tcc *TCC) timerNum() uint8 { // getTimer returns the timer to be used for PWM on this pin
switch tcc.timer() { func (pwm PWM) getTimer() *sam.TCC_Type {
case sam.TCC0: switch pwm.Pin {
return 0 case PA14:
case sam.TCC1: return sam.TCC2
return 1 case PA15:
case sam.TCC2: return sam.TCC2
return 2 case PA16:
case sam.TCC3: return sam.TCC1
return 3 case PA17:
case sam.TCC4: return sam.TCC1
return 4 case PA18:
return sam.TCC1
case PA19:
return sam.TCC1
case PA20:
return sam.TCC0
case PA21:
return sam.TCC0
case PA22:
return sam.TCC0
case PA23:
return sam.TCC0
case PB12:
return sam.TCC3
case PB13:
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: default:
return 0x0f // should not happen return nil // not supported on this pin
} }
} }
+51 -40
View File
@@ -11,49 +11,60 @@ import "device/sam"
const HSRAM_SIZE = 0x00030000 const HSRAM_SIZE = 0x00030000
// This chip has five TCC peripherals, which have PWM as one feature. // InitPWM initializes the PWM interface.
var ( func InitPWM() {
TCC0 = (*TCC)(sam.TCC0) // turn on timer clocks used for PWM
TCC1 = (*TCC)(sam.TCC1) sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
TCC2 = (*TCC)(sam.TCC2) sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
TCC3 = (*TCC)(sam.TCC3) sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
TCC4 = (*TCC)(sam.TCC4)
)
func (tcc *TCC) configureClock() { //use clock generator 0
// Turn on timer clocks used for TCC and use generic clock generator 0. sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
switch tcc.timer() { sam.GCLK_PCHCTRL_CHEN)
case sam.TCC0: sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_) sam.GCLK_PCHCTRL_CHEN)
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[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
case sam.TCC1: sam.GCLK_PCHCTRL_CHEN)
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)
case sam.TCC2:
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC3:
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC4:
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
}
} }
func (tcc *TCC) timerNum() uint8 { // getTimer returns the timer to be used for PWM on this pin
switch tcc.timer() { func (pwm PWM) getTimer() *sam.TCC_Type {
case sam.TCC0: switch pwm.Pin {
return 0 case PA14:
case sam.TCC1: return sam.TCC2
return 1 case PA15:
case sam.TCC2: return sam.TCC2
return 2 case PA16:
case sam.TCC3: return sam.TCC1
return 3 case PA17:
case sam.TCC4: return sam.TCC1
return 4 case PA18:
return sam.TCC1
case PA19:
return sam.TCC1
case PA20:
return sam.TCC0
case PA21:
return sam.TCC0
case PA22:
return sam.TCC0
case PA23:
return sam.TCC0
case PB12:
return sam.TCC3
case PB13:
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: default:
return 0x0f // should not happen return nil // not supported on this pin
} }
} }
+51 -40
View File
@@ -11,49 +11,60 @@ import "device/sam"
const HSRAM_SIZE = 0x00040000 const HSRAM_SIZE = 0x00040000
// This chip has five TCC peripherals, which have PWM as one feature. // InitPWM initializes the PWM interface.
var ( func InitPWM() {
TCC0 = (*TCC)(sam.TCC0) // turn on timer clocks used for PWM
TCC1 = (*TCC)(sam.TCC1) sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
TCC2 = (*TCC)(sam.TCC2) sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
TCC3 = (*TCC)(sam.TCC3) sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
TCC4 = (*TCC)(sam.TCC4)
)
func (tcc *TCC) configureClock() { //use clock generator 0
// Turn on timer clocks used for TCC and use generic clock generator 0. sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
switch tcc.timer() { sam.GCLK_PCHCTRL_CHEN)
case sam.TCC0: sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_) sam.GCLK_PCHCTRL_CHEN)
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[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
case sam.TCC1: sam.GCLK_PCHCTRL_CHEN)
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)
case sam.TCC2:
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC3:
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC4:
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
}
} }
func (tcc *TCC) timerNum() uint8 { // getTimer returns the timer to be used for PWM on this pin
switch tcc.timer() { func (pwm PWM) getTimer() *sam.TCC_Type {
case sam.TCC0: switch pwm.Pin {
return 0 case PC18:
case sam.TCC1: return sam.TCC0
return 1 case PC19:
case sam.TCC2: return sam.TCC0
return 2 case PC20:
case sam.TCC3: return sam.TCC0
return 3 case PC21:
case sam.TCC4: return sam.TCC0
return 4 case PD20:
return sam.TCC1
case PD21:
return sam.TCC1
case PB18:
return sam.TCC1
case PB12:
return sam.TCC3
case PB13:
return sam.TCC3
case PA15:
return sam.TCC2
case PC17:
return sam.TCC0
case PC16:
return sam.TCC0
case PA14:
return sam.TCC2
case PB15:
return sam.TCC4
case PB14:
return sam.TCC4
case PB20:
return sam.TCC1
case PB21:
return sam.TCC1
default: default:
return 0x0f // should not happen return nil // not supported on this pin
} }
} }
-59
View File
@@ -1,59 +0,0 @@
// +build sam,atsame51,atsame51j19
// Peripheral abstraction layer for the atsame51.
//
// Datasheet:
// http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D5xE5x_Family_Data_Sheet_DS60001507F.pdf
//
package machine
import "device/sam"
const HSRAM_SIZE = 0x00030000
// This chip has five TCC peripherals, which have PWM as one feature.
var (
TCC0 = (*TCC)(sam.TCC0)
TCC1 = (*TCC)(sam.TCC1)
TCC2 = (*TCC)(sam.TCC2)
TCC3 = (*TCC)(sam.TCC3)
TCC4 = (*TCC)(sam.TCC4)
)
func (tcc *TCC) configureClock() {
// Turn on timer clocks used for the TCC and use generic clock generator 0.
switch tcc.timer() {
case sam.TCC0:
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:
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)
case sam.TCC2:
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC3:
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC4:
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
}
}
func (tcc *TCC) timerNum() uint8 {
switch tcc.timer() {
case sam.TCC0:
return 0
case sam.TCC1:
return 1
case sam.TCC2:
return 2
case sam.TCC3:
return 3
case sam.TCC4:
return 4
default:
return 0x0f // should not happen
}
}
-59
View File
@@ -1,59 +0,0 @@
// +build sam,atsame5x,atsame54p20
// Peripheral abstraction layer for the atsame54.
//
// Datasheet:
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
//
package machine
import "device/sam"
const HSRAM_SIZE = 0x00040000
// This chip has five TCC peripherals, which have PWM as one feature.
var (
TCC0 = (*TCC)(sam.TCC0)
TCC1 = (*TCC)(sam.TCC1)
TCC2 = (*TCC)(sam.TCC2)
TCC3 = (*TCC)(sam.TCC3)
TCC4 = (*TCC)(sam.TCC4)
)
func (tcc *TCC) configureClock() {
// Turn on timer clocks used for TCC and use generic clock generator 0.
switch tcc.timer() {
case sam.TCC0:
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:
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)
case sam.TCC2:
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC3:
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC4:
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
}
}
func (tcc *TCC) timerNum() uint8 {
switch tcc.timer() {
case sam.TCC0:
return 0
case sam.TCC1:
return 1
case sam.TCC2:
return 2
case sam.TCC3:
return 3
case sam.TCC4:
return 4
default:
return 0x0f // should not happen
}
}
+2
View File
@@ -8,6 +8,8 @@ import (
"unsafe" "unsafe"
) )
type PinMode uint8
const ( const (
PinInput PinMode = iota PinInput PinMode = iota
PinInputPullup PinInputPullup
+2
View File
@@ -21,6 +21,8 @@ var (
ErrInvalidSPIBus = errors.New("machine: invalid SPI bus") ErrInvalidSPIBus = errors.New("machine: invalid SPI bus")
) )
type PinMode uint8
const ( const (
PinOutput PinMode = iota PinOutput PinMode = iota
PinInput PinInput
+2
View File
@@ -11,6 +11,8 @@ func CPUFrequency() uint32 {
return 80000000 // 80MHz return 80000000 // 80MHz
} }
type PinMode uint8
const ( const (
PinOutput PinMode = iota PinOutput PinMode = iota
PinInput PinInput
+2
View File
@@ -12,6 +12,8 @@ func CPUFrequency() uint32 {
return 16000000 return 16000000
} }
type PinMode uint8
const ( const (
PinInput PinMode = iota PinInput PinMode = iota
PinOutput PinOutput
+3 -1
View File
@@ -30,6 +30,8 @@ const (
// Make it easier to directly write to I/O RAM. // Make it easier to directly write to I/O RAM.
var ioram = (*[0x400]volatile.Register8)(unsafe.Pointer(uintptr(0x04000000))) var ioram = (*[0x400]volatile.Register8)(unsafe.Pointer(uintptr(0x04000000)))
type PinMode uint8
// Set has not been implemented. // Set has not been implemented.
func (p Pin) Set(value bool) { func (p Pin) Set(value bool) {
// do nothing // do nothing
@@ -52,7 +54,7 @@ func (d FramebufDisplay) Size() (x, y int16) {
} }
func (d FramebufDisplay) SetPixel(x, y int16, c color.RGBA) { func (d FramebufDisplay) SetPixel(x, y int16, c color.RGBA) {
d.port[y][x].Set((uint16(c.R) >> 3) | ((uint16(c.G) >> 3) << 5) | ((uint16(c.B) >> 3) << 10)) d.port[y][x].Set(uint16(c.R)&0x1f | uint16(c.G)&0x1f<<5 | uint16(c.B)&0x1f<<10)
} }
func (d FramebufDisplay) Display() error { func (d FramebufDisplay) Display() error {
+20
View File
@@ -10,6 +10,8 @@ var (
UART0 = UART{0} UART0 = UART{0}
) )
type PinMode uint8
const ( const (
PinInput PinMode = iota PinInput PinMode = iota
PinOutput PinOutput
@@ -82,6 +84,24 @@ 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
+1
View File
@@ -14,6 +14,7 @@ func CPUFrequency() uint32 {
return 390000000 return 390000000
} }
type PinMode uint8
type fpioaPullMode uint8 type fpioaPullMode uint8
type PinChange uint8 type PinChange uint8
+2
View File
@@ -15,6 +15,8 @@ func CPUFrequency() uint32 {
return 600000000 return 600000000
} }
type PinMode uint8
const ( const (
// GPIO // GPIO
PinInput PinMode = iota PinInput PinMode = iota
+2
View File
@@ -13,6 +13,8 @@ var (
ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size") ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size")
) )
type PinMode uint8
const ( const (
PinInput PinMode = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos) PinInput PinMode = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos)
PinInputPullup PinMode = PinInput | (nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos) PinInputPullup PinMode = PinInput | (nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos)
+3 -3
View File
@@ -63,7 +63,7 @@ func (i2c *I2C) setPins(scl, sda Pin) {
// PWM // PWM
var ( var (
PWM0 = &PWM{PWM: nrf.PWM0} pwmChannelPins = [3]uint32{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}
PWM1 = &PWM{PWM: nrf.PWM1} pwms = [3]*nrf.PWM_Type{nrf.PWM0, nrf.PWM1, nrf.PWM2}
PWM2 = &PWM{PWM: nrf.PWM2} pwmChannelSequence [3]uint16
) )
+3 -4
View File
@@ -83,8 +83,7 @@ func (i2c *I2C) setPins(scl, sda Pin) {
// PWM // PWM
var ( var (
PWM0 = &PWM{PWM: nrf.PWM0} pwmChannelPins = [4]uint32{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}
PWM1 = &PWM{PWM: nrf.PWM1} pwms = [4]*nrf.PWM_Type{nrf.PWM0, nrf.PWM1, nrf.PWM2, nrf.PWM3}
PWM2 = &PWM{PWM: nrf.PWM2} pwmChannelSequence [4]uint16
PWM3 = &PWM{PWM: nrf.PWM3}
) )
+3 -4
View File
@@ -79,8 +79,7 @@ func (i2c *I2C) setPins(scl, sda Pin) {
// PWM // PWM
var ( var (
PWM0 = &PWM{PWM: nrf.PWM0} pwmChannelPins = [4]uint32{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}
PWM1 = &PWM{PWM: nrf.PWM1} pwms = [4]*nrf.PWM_Type{nrf.PWM0, nrf.PWM1, nrf.PWM2, nrf.PWM3}
PWM2 = &PWM{PWM: nrf.PWM2} pwmChannelSequence [4]uint16
PWM3 = &PWM{PWM: nrf.PWM3}
) )
+2 -2
View File
@@ -445,7 +445,7 @@ func handleStandardSetup(setup usbSetup) bool {
func cdcSetup(setup usbSetup) bool { func cdcSetup(setup usbSetup) bool {
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE { if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
if setup.bRequest == usb_CDC_GET_LINE_CODING { if setup.bRequest == usb_CDC_GET_LINE_CODING {
var b [cdcLineInfoSize]byte b := make([]byte, 7)
b[0] = byte(usbLineInfo.dwDTERate) b[0] = byte(usbLineInfo.dwDTERate)
b[1] = byte(usbLineInfo.dwDTERate >> 8) b[1] = byte(usbLineInfo.dwDTERate >> 8)
b[2] = byte(usbLineInfo.dwDTERate >> 16) b[2] = byte(usbLineInfo.dwDTERate >> 16)
@@ -454,7 +454,7 @@ func cdcSetup(setup usbSetup) bool {
b[5] = byte(usbLineInfo.bParityType) b[5] = byte(usbLineInfo.bParityType)
b[6] = byte(usbLineInfo.bDataBits) b[6] = byte(usbLineInfo.bDataBits)
sendUSBPacket(0, b[:]) sendUSBPacket(0, b)
return true return true
} }
} }

Some files were not shown because too many files have changed in this diff Show More