mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 22:58:41 +00:00
Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 008dd7dfe6 | |||
| 9f5066aa6f | |||
| 3b24fedf92 | |||
| 8cd2a462b9 | |||
| 25b045d0a7 | |||
| 78acbdf0d9 | |||
| 617e2791ef | |||
| a0908ff55b | |||
| 2f1f8fb075 | |||
| 6f61b83ad5 | |||
| 8dfefb46d1 | |||
| 959442dc82 | |||
| c1aa152a63 | |||
| dd3d8a363a | |||
| bb509ec91d | |||
| 944f022060 | |||
| cd517a30af | |||
| 52d8655eec | |||
| f5786941e5 | |||
| 1f73941c43 | |||
| abeab51d00 | |||
| 9ef75f17bf | |||
| c3992bd77b | |||
| f79e66ac2e | |||
| 4eac212695 | |||
| b661882391 | |||
| 321488dcfe | |||
| 80caf2dab2 | |||
| c466465c32 | |||
| 404b65941a | |||
| 25f3adb47e | |||
| b9043b649d | |||
| 7b761fac78 | |||
| c47cdfa66f | |||
| 768a15c1dd | |||
| c1c3be1aa7 | |||
| b2e72c96f4 | |||
| f706219996 | |||
| 6152a661e8 | |||
| f1a5743f77 | |||
| 9f52fe4e4a | |||
| cb886a35c9 | |||
| 5707022951 | |||
| bd212cc000 | |||
| 6dd5666ed1 | |||
| f145663464 | |||
| 0d66475e10 | |||
| 22b143eba8 | |||
| 3fe13a72bd | |||
| d919905c96 | |||
| 96b1b76483 | |||
| f234df7a50 | |||
| e6d5c26df5 | |||
| ae59e7703e | |||
| e587b1d1b4 | |||
| 57271d7eaa | |||
| 8383552552 | |||
| aa8e12c464 | |||
| e7a05b6e74 | |||
| 2fd8f103ab | |||
| 33f76d1c2e | |||
| ea8f7ba1f9 | |||
| 0ffe5ac2fa | |||
| 7bac93aab3 | |||
| b61751e429 | |||
| 25dac32a88 | |||
| 56cf69a66b | |||
| 04d12bf2ba | |||
| 0b7957d612 | |||
| 61243f6c57 | |||
| 49ec3eb58e | |||
| fa6c1b69ce | |||
| 1e9a41dc94 | |||
| a30671751f | |||
| c246978dd7 | |||
| 72acda22b0 |
+42
-23
@@ -68,14 +68,17 @@ commands:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-11-v1
|
||||
- llvm-source-11-v2
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-11-v1
|
||||
key: llvm-source-11-v2
|
||||
paths:
|
||||
- llvm-project
|
||||
- llvm-project/clang/lib/Headers
|
||||
- llvm-project/clang/include
|
||||
- llvm-project/lld/include
|
||||
- llvm-project/llvm/include
|
||||
build-wasi-libc:
|
||||
steps:
|
||||
- restore_cache:
|
||||
@@ -154,12 +157,15 @@ commands:
|
||||
- llvm-source-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-linux-v2-assert
|
||||
- llvm-build-11-linux-v3-assert
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
||||
then
|
||||
# fetch LLVM source
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# install dependencies
|
||||
sudo apt-get install cmake ninja-build
|
||||
# hack ninja to use less jobs
|
||||
@@ -167,20 +173,22 @@ commands:
|
||||
chmod +x /go/bin/ninja
|
||||
# build!
|
||||
make ASSERT=1 llvm-build
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-11-linux-v2-assert
|
||||
key: llvm-build-11-linux-v3-assert
|
||||
paths:
|
||||
llvm-build
|
||||
- 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
|
||||
- run: make ASSERT=1
|
||||
- build-wasi-libc
|
||||
- run:
|
||||
name: "Test TinyGo"
|
||||
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:
|
||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
@@ -217,12 +225,15 @@ commands:
|
||||
- llvm-source-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-linux-v2-noassert
|
||||
- llvm-build-11-linux-v3-noassert
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
||||
then
|
||||
# fetch LLVM source
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# install dependencies
|
||||
sudo apt-get install cmake ninja-build
|
||||
# hack ninja to use less jobs
|
||||
@@ -230,9 +241,10 @@ commands:
|
||||
chmod +x /go/bin/ninja
|
||||
# build!
|
||||
make llvm-build
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-11-linux-v2-noassert
|
||||
key: llvm-build-11-linux-v3-noassert
|
||||
paths:
|
||||
llvm-build
|
||||
- build-wasi-libc
|
||||
@@ -282,43 +294,50 @@ commands:
|
||||
variant: "macos"
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-macos-v2-{{ checksum "go.mod" }}
|
||||
- go-cache-macos-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-macos-v3-{{ checksum "go.mod" }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-11-macos-v1
|
||||
- llvm-source-11-macos-v3
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-11-macos-v1
|
||||
key: llvm-source-11-macos-v3
|
||||
paths:
|
||||
- llvm-project
|
||||
- llvm-project/clang/lib/Headers
|
||||
- llvm-project/clang/include
|
||||
- llvm-project/lld/include
|
||||
- llvm-project/llvm/include
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-macos-v2
|
||||
- llvm-build-11-macos-v4
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
||||
then
|
||||
# fetch LLVM source
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# install dependencies
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake ninja
|
||||
# build!
|
||||
make llvm-build
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-11-macos-v2
|
||||
key: llvm-build-11-macos-v4
|
||||
paths:
|
||||
llvm-build
|
||||
- restore_cache:
|
||||
keys:
|
||||
- wasi-libc-sysroot-macos-v3
|
||||
- wasi-libc-sysroot-macos-v4
|
||||
- run:
|
||||
name: "Build wasi-libc"
|
||||
command: make wasi-libc
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-macos-v3
|
||||
key: wasi-libc-sysroot-macos-v4
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
- run:
|
||||
@@ -340,7 +359,7 @@ commands:
|
||||
tinygo version
|
||||
- run: make smoketest AVR=0
|
||||
- save_cache:
|
||||
key: go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
key: go-cache-macos-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
- ~/.cache/go-build
|
||||
- /go/pkg/mod
|
||||
@@ -382,7 +401,7 @@ jobs:
|
||||
- build-linux
|
||||
build-macos:
|
||||
macos:
|
||||
xcode: "10.1.0"
|
||||
xcode: "11.1.0" # macOS 10.14
|
||||
steps:
|
||||
- build-macos
|
||||
|
||||
|
||||
@@ -152,9 +152,9 @@ gen-device-stm32: build/gen-device-svd
|
||||
|
||||
|
||||
# Get LLVM sources.
|
||||
$(LLVM_PROJECTDIR)/README.md:
|
||||
$(LLVM_PROJECTDIR)/llvm:
|
||||
git clone -b xtensa_release_11.0.0 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
||||
llvm-source: $(LLVM_PROJECTDIR)/README.md
|
||||
llvm-source: $(LLVM_PROJECTDIR)/llvm
|
||||
|
||||
# Configure LLVM.
|
||||
TINYGO_SOURCE_DIR=$(shell pwd)
|
||||
@@ -182,25 +182,28 @@ tinygo:
|
||||
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 .
|
||||
|
||||
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.
|
||||
# TODO: do this in one command, parallelize, and only show failing tests (no
|
||||
# implied -v flag).
|
||||
# TODO: parallelize, and only show failing tests (no implied -v flag).
|
||||
.PHONY: tinygo-test
|
||||
tinygo-test:
|
||||
$(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
|
||||
$(TINYGO) test $(TEST_PACKAGES)
|
||||
|
||||
.PHONY: smoketest
|
||||
smoketest:
|
||||
@@ -226,8 +229,6 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit examples/microbit-blink
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit-v2 examples/microbit-blink
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
|
||||
@@ -256,6 +257,10 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit-s110v8 examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=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
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10031 examples/blinky1
|
||||
@@ -288,6 +293,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.gba -target=gameboy-advance examples/gba-display
|
||||
@$(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
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1
|
||||
@@ -334,6 +341,10 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=p1am-100 examples/blinky1
|
||||
@$(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
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -341,8 +352,6 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pyportal examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(STM32), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -354,6 +363,8 @@ ifneq ($(STM32), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l031k6 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l432kc examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l552ze examples/blinky1
|
||||
@@ -374,6 +385,10 @@ ifneq ($(AVR), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-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
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1
|
||||
|
||||
@@ -43,13 +43,14 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
||||
|
||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||
|
||||
The following 55 microcontroller boards are currently supported:
|
||||
The following 57 microcontroller boards are currently supported:
|
||||
|
||||
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
|
||||
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
||||
* [Adafruit CLUE](https://www.adafruit.com/product/4500)
|
||||
* [Adafruit Feather M0](https://www.adafruit.com/product/2772)
|
||||
* [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 STM32F405 Express](https://www.adafruit.com/product/4382)
|
||||
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
||||
@@ -77,6 +78,7 @@ The following 55 microcontroller boards are currently supported:
|
||||
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
|
||||
* [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/)
|
||||
* [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/)
|
||||
* [Nintendo Switch](https://www.nintendo.com/switch/)
|
||||
* [Nordic Semiconductor PCA10031](https://www.nordicsemi.com/eng/Products/nRF51-Dongle)
|
||||
|
||||
+164
-35
@@ -39,6 +39,11 @@ type BuildResult struct {
|
||||
// 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.
|
||||
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
|
||||
@@ -52,14 +57,17 @@ type BuildResult struct {
|
||||
// key, avoiding the need for recompiling all dependencies when only the
|
||||
// implementation of an imported package changes.
|
||||
type packageAction struct {
|
||||
ImportPath string
|
||||
CompilerVersion int // compiler.Version
|
||||
InterpVersion int // interp.Version
|
||||
LLVMVersion string
|
||||
Config *compiler.Config
|
||||
CFlags []string
|
||||
FileHashes map[string]string // hash of every file that's part of the package
|
||||
Imports map[string]string // map from imported package to action ID hash
|
||||
ImportPath string
|
||||
CompilerVersion int // compiler.Version
|
||||
InterpVersion int // interp.Version
|
||||
LLVMVersion string
|
||||
Config *compiler.Config
|
||||
CFlags []string
|
||||
FileHashes map[string]string // hash of every file that's part of the package
|
||||
Imports map[string]string // map from imported package to action ID hash
|
||||
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
|
||||
@@ -91,6 +99,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
DefaultStackSize: config.Target.DefaultStackSize,
|
||||
NeedsStackObjects: config.NeedsStackObjects(),
|
||||
Debug: config.Debug(),
|
||||
LLVMFeatures: config.LLVMFeatures(),
|
||||
}
|
||||
|
||||
// Load the target machine, which is the LLVM object that contains all
|
||||
@@ -127,20 +136,30 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
var packageJobs []*compileJob
|
||||
packageBitcodePaths := make(map[string]string)
|
||||
packageActionIDs := make(map[string]string)
|
||||
optLevel, sizeLevel, _ := config.OptLevels()
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
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
|
||||
// the parameters for the build.
|
||||
actionID := packageAction{
|
||||
ImportPath: pkg.ImportPath,
|
||||
CompilerVersion: compiler.Version,
|
||||
InterpVersion: interp.Version,
|
||||
LLVMVersion: llvm.Version,
|
||||
Config: compilerConfig,
|
||||
CFlags: pkg.CFlags,
|
||||
FileHashes: make(map[string]string, len(pkg.FileHashes)),
|
||||
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
||||
ImportPath: pkg.ImportPath,
|
||||
CompilerVersion: compiler.Version,
|
||||
InterpVersion: interp.Version,
|
||||
LLVMVersion: llvm.Version,
|
||||
Config: compilerConfig,
|
||||
CFlags: pkg.CFlags,
|
||||
FileHashes: make(map[string]string, len(pkg.FileHashes)),
|
||||
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
||||
OptLevel: optLevel,
|
||||
SizeLevel: sizeLevel,
|
||||
UndefinedGlobals: undefinedGlobals,
|
||||
}
|
||||
for filePath, hash := range pkg.FileHashes {
|
||||
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
|
||||
@@ -191,6 +210,25 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
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.
|
||||
// It may only be possible to do this partially, in which case
|
||||
// it is completed after all IR files are linked.
|
||||
@@ -206,6 +244,38 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
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.
|
||||
// Write to a temporary path that is renamed to the destination
|
||||
// file to avoid race conditions with other TinyGo invocatiosn
|
||||
@@ -430,7 +500,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
job := &compileJob{
|
||||
description: "compile extra file " + path,
|
||||
run: func(job *compileJob) error {
|
||||
result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config)
|
||||
result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config.Options.PrintCommands)
|
||||
job.result = result
|
||||
return err
|
||||
},
|
||||
@@ -449,7 +519,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
job := &compileJob{
|
||||
description: "compile CGo file " + abspath,
|
||||
run: func(job *compileJob) error {
|
||||
result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config)
|
||||
result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config.Options.PrintCommands)
|
||||
job.result = result
|
||||
return err
|
||||
},
|
||||
@@ -573,8 +643,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return fmt.Errorf("unknown output binary format: %s", outputBinaryFormat)
|
||||
}
|
||||
return action(BuildResult{
|
||||
Binary: tmppath,
|
||||
MainDir: lprogram.MainPkg().Dir,
|
||||
Binary: tmppath,
|
||||
MainDir: lprogram.MainPkg().Dir,
|
||||
ImportPath: lprogram.MainPkg().ImportPath,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -603,6 +674,12 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||
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
|
||||
// cannot be represented exactly in JavaScript (JS only has doubles). To
|
||||
// keep functions interoperable, pass int64 types as pointers to
|
||||
@@ -617,21 +694,8 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||
|
||||
// Optimization levels here are roughly the same as Clang, but probably not
|
||||
// exactly.
|
||||
var errs []error
|
||||
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)
|
||||
}
|
||||
optLevel, sizeLevel, inlinerThreshold := config.OptLevels()
|
||||
errs := transform.Optimize(mod, config, optLevel, sizeLevel, inlinerThreshold)
|
||||
if len(errs) > 0 {
|
||||
return newMultiError(errs)
|
||||
}
|
||||
@@ -653,6 +717,71 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||
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
|
||||
// (usually a goroutine).
|
||||
type functionStackSize struct {
|
||||
|
||||
+12
-8
@@ -17,7 +17,6 @@ import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
@@ -57,7 +56,7 @@ import (
|
||||
// depfile but without invalidating its name. For this reason, the depfile is
|
||||
// written on each new compilation (even when it seems unnecessary). However, it
|
||||
// could in rare cases lead to a stale file fetched from the cache.
|
||||
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, config *compileopts.Config) (string, error) {
|
||||
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands bool) (string, error) {
|
||||
// Hash input file.
|
||||
fileHash, err := hashFile(abspath)
|
||||
if err != nil {
|
||||
@@ -68,14 +67,12 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, config *compi
|
||||
buf, err := json.Marshal(struct {
|
||||
Path string
|
||||
Hash string
|
||||
Compiler string
|
||||
Flags []string
|
||||
LLVMVersion string
|
||||
}{
|
||||
Path: abspath,
|
||||
Hash: fileHash,
|
||||
Compiler: config.Target.Compiler,
|
||||
Flags: config.CFlags(),
|
||||
Flags: cflags,
|
||||
LLVMVersion: llvm.Version,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -124,10 +121,17 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, config *compi
|
||||
flags := append([]string{}, cflags...) // copy cflags
|
||||
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name()) // autogenerate dependencies
|
||||
flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath)
|
||||
if config.Options.PrintCommands {
|
||||
fmt.Printf("%s %s\n", config.Target.Compiler, strings.Join(flags, " "))
|
||||
if strings.ToLower(filepath.Ext(abspath)) == ".s" {
|
||||
// If this is an assembly file (.s or .S, lowercase or uppercase), then
|
||||
// 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")
|
||||
}
|
||||
err = runCCompiler(config.Target.Compiler, flags...)
|
||||
if printCommands {
|
||||
fmt.Printf("clang %s\n", strings.Join(flags, " "))
|
||||
}
|
||||
err = runCCompiler(flags...)
|
||||
if err != nil {
|
||||
return "", &commandError{"failed to build", abspath, err}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,18 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
if err != nil {
|
||||
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")
|
||||
if goroot == "" {
|
||||
return nil, errors.New("cannot locate $GOROOT, please set it manually")
|
||||
}
|
||||
|
||||
major, minor, err := goenv.GetGorootVersion(goroot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||
@@ -28,7 +36,9 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
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)
|
||||
}
|
||||
|
||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
|
||||
return &compileopts.Config{
|
||||
Options: options,
|
||||
Target: spec,
|
||||
|
||||
+1
-1
@@ -136,7 +136,7 @@ func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error)
|
||||
var compileArgs []string
|
||||
compileArgs = append(compileArgs, args...)
|
||||
compileArgs = append(compileArgs, "-o", objpath, srcpath)
|
||||
err := runCCompiler("clang", compileArgs...)
|
||||
err := runCCompiler(compileArgs...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", srcpath, err}
|
||||
}
|
||||
|
||||
+4
-13
@@ -9,8 +9,8 @@ import (
|
||||
)
|
||||
|
||||
// runCCompiler invokes a C compiler with the given arguments.
|
||||
func runCCompiler(command string, flags ...string) error {
|
||||
if hasBuiltinTools && command == "clang" {
|
||||
func runCCompiler(flags ...string) error {
|
||||
if hasBuiltinTools {
|
||||
// Compile this with the internal Clang compiler.
|
||||
headerPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
if headerPath == "" {
|
||||
@@ -23,17 +23,8 @@ func runCCompiler(command string, flags ...string) error {
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// Running some other compiler. Maybe it has been defined in the
|
||||
// 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()
|
||||
// Compile this with an external invocation of the Clang compiler.
|
||||
return execCommand(commands["clang"], flags...)
|
||||
}
|
||||
|
||||
// link invokes a linker with the given name and flags.
|
||||
|
||||
+28
-8
@@ -80,12 +80,7 @@ func (c *Config) GC() string {
|
||||
if c.Target.GC != "" {
|
||||
return c.Target.GC
|
||||
}
|
||||
for _, tag := range c.Target.BuildTags {
|
||||
if tag == "baremetal" || tag == "wasm" {
|
||||
return "conservative"
|
||||
}
|
||||
}
|
||||
return "extalloc"
|
||||
return "conservative"
|
||||
}
|
||||
|
||||
// NeedsStackObjects returns true if the compiler should insert stack objects
|
||||
@@ -118,6 +113,27 @@ func (c *Config) Scheduler() string {
|
||||
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
|
||||
// target.
|
||||
func (c *Config) FuncImplementation() string {
|
||||
@@ -163,7 +179,7 @@ func (c *Config) AutomaticStackSize() bool {
|
||||
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo
|
||||
// preprocessing.
|
||||
func (c *Config) CFlags() []string {
|
||||
cflags := append([]string{}, c.Options.CFlags...)
|
||||
var cflags []string
|
||||
for _, flag := range c.Target.CFlags {
|
||||
cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT")))
|
||||
}
|
||||
@@ -184,7 +200,7 @@ func (c *Config) CFlags() []string {
|
||||
func (c *Config) LDFlags() []string {
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
// Merge and adjust LDFlags.
|
||||
ldflags := append([]string{}, c.Options.LDFlags...)
|
||||
var ldflags []string
|
||||
for _, flag := range c.Target.LDFlags {
|
||||
ldflags = append(ldflags, strings.ReplaceAll(flag, "{root}", root))
|
||||
}
|
||||
@@ -321,6 +337,10 @@ func (c *Config) WasmAbi() string {
|
||||
return c.Target.WasmAbi
|
||||
}
|
||||
|
||||
func (c *Config) LLVMFeatures() string {
|
||||
return c.Options.LLVMFeatures
|
||||
}
|
||||
|
||||
type TestConfig struct {
|
||||
CompileTestBinary bool
|
||||
// TODO: Filter the test functions to run, include verbose flag, etc
|
||||
|
||||
+28
-18
@@ -2,6 +2,7 @@ package compileopts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -10,29 +11,32 @@ var (
|
||||
validSchedulerOptions = []string{"none", "tasks", "coroutines"}
|
||||
validPrintSizeOptions = []string{"none", "short", "full"}
|
||||
validPanicStrategyOptions = []string{"print", "trap"}
|
||||
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
|
||||
)
|
||||
|
||||
// Options contains extra options to give to the compiler. These options are
|
||||
// usually passed from the command line.
|
||||
type Options struct {
|
||||
Target string
|
||||
Opt string
|
||||
GC string
|
||||
PanicStrategy string
|
||||
Scheduler string
|
||||
PrintIR bool
|
||||
DumpSSA bool
|
||||
VerifyIR bool
|
||||
PrintCommands bool
|
||||
Debug bool
|
||||
PrintSizes string
|
||||
PrintStacks bool
|
||||
CFlags []string
|
||||
LDFlags []string
|
||||
Tags string
|
||||
WasmAbi string
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
Target string
|
||||
Opt string
|
||||
GC string
|
||||
PanicStrategy string
|
||||
Scheduler string
|
||||
PrintIR bool
|
||||
DumpSSA bool
|
||||
VerifyIR bool
|
||||
PrintCommands bool
|
||||
Debug bool
|
||||
PrintSizes string
|
||||
PrintAllocs *regexp.Regexp // regexp string
|
||||
PrintStacks bool
|
||||
Tags string
|
||||
WasmAbi string
|
||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
OpenOCDCommands []string
|
||||
LLVMFeatures string
|
||||
}
|
||||
|
||||
// Verify performs a validation on the given options, raising an error if options are not valid.
|
||||
@@ -73,6 +77,12 @@ 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
|
||||
}
|
||||
|
||||
|
||||
+12
-10
@@ -31,7 +31,6 @@ type TargetSpec struct {
|
||||
BuildTags []string `json:"build-tags"`
|
||||
GC string `json:"gc"`
|
||||
Scheduler string `json:"scheduler"`
|
||||
Compiler string `json:"compiler"`
|
||||
Linker string `json:"linker"`
|
||||
RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt)
|
||||
Libc string `json:"libc"`
|
||||
@@ -240,23 +239,26 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
// No target spec available. Use the default one, useful on most systems
|
||||
// with a regular OS.
|
||||
spec := TargetSpec{
|
||||
Triple: triple,
|
||||
GOOS: goos,
|
||||
GOARCH: goarch,
|
||||
BuildTags: []string{goos, goarch},
|
||||
Compiler: "clang",
|
||||
Linker: "cc",
|
||||
CFlags: []string{"--target=" + triple},
|
||||
GDB: []string{"gdb"},
|
||||
PortReset: "false",
|
||||
Triple: triple,
|
||||
GOOS: goos,
|
||||
GOARCH: goarch,
|
||||
BuildTags: []string{goos, goarch},
|
||||
Scheduler: "tasks",
|
||||
Linker: "cc",
|
||||
DefaultStackSize: 1024 * 64, // 64kB
|
||||
CFlags: []string{"--target=" + triple},
|
||||
GDB: []string{"gdb"},
|
||||
PortReset: "false",
|
||||
}
|
||||
if goos == "darwin" {
|
||||
spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
|
||||
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
|
||||
} else {
|
||||
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
||||
}
|
||||
if goarch != "wasm" {
|
||||
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 {
|
||||
// Some educated guesses as to how to invoke helper programs.
|
||||
|
||||
+72
-12
@@ -23,7 +23,7 @@ import (
|
||||
// Version of the compiler pacakge. Must be incremented each time the compiler
|
||||
// package changes in a way that affects the generated LLVM module.
|
||||
// This version is independent of the TinyGo version number.
|
||||
const Version = 6 // last change: fix issue 1304
|
||||
const Version = 9 // last change: implement reflect.New()
|
||||
|
||||
func init() {
|
||||
llvm.InitializeAllTargets()
|
||||
@@ -59,6 +59,7 @@ type Config struct {
|
||||
DefaultStackSize uint64
|
||||
NeedsStackObjects bool
|
||||
Debug bool // Whether to emit debug information in the LLVM module.
|
||||
LLVMFeatures string
|
||||
}
|
||||
|
||||
// compilerContext contains function-independent data that should still be
|
||||
@@ -185,7 +186,12 @@ func NewTargetMachine(config *Config) (llvm.TargetMachine, error) {
|
||||
if err != nil {
|
||||
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 relocationModel llvm.RelocMode
|
||||
@@ -298,7 +304,7 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
||||
c.dibuilder.Finalize()
|
||||
}
|
||||
|
||||
return c.mod, nil
|
||||
return c.mod, c.diagnostics
|
||||
}
|
||||
|
||||
// getLLVMRuntimeType obtains a named type from the runtime package and returns
|
||||
@@ -964,11 +970,59 @@ 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
|
||||
// particular Go SSA instruction.
|
||||
func (b *builder) createInstruction(instr ssa.Instruction) {
|
||||
if b.Debug {
|
||||
pos := b.program.Fset.Position(instr.Pos())
|
||||
pos := b.program.Fset.Position(getPos(instr))
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||
}
|
||||
|
||||
@@ -2258,14 +2312,20 @@ func (b *builder) createConst(prefix string, expr *ssa.Const) llvm.Value {
|
||||
} else if typ.Info()&types.IsString != 0 {
|
||||
str := constant.StringVal(expr.Value)
|
||||
strLen := llvm.ConstInt(b.uintptrType, uint64(len(str)), false)
|
||||
objname := prefix + "$string"
|
||||
global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
|
||||
global.SetInitializer(b.ctx.ConstString(str, false))
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
strPtr := b.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
|
||||
var strPtr llvm.Value
|
||||
if str != "" {
|
||||
objname := prefix + "$string"
|
||||
global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
|
||||
global.SetInitializer(b.ctx.ConstString(str, false))
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
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})
|
||||
return strObj
|
||||
} else if typ.Kind() == types.UnsafePointer {
|
||||
|
||||
@@ -32,7 +32,7 @@ func TestCompiler(t *testing.T) {
|
||||
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
|
||||
}
|
||||
|
||||
target, err := compileopts.LoadTarget("i686--linux")
|
||||
target, err := compileopts.LoadTarget("wasm")
|
||||
if err != nil {
|
||||
t.Fatal("failed to load target:", err)
|
||||
}
|
||||
@@ -62,6 +62,7 @@ func TestCompiler(t *testing.T) {
|
||||
"string.go",
|
||||
"float.go",
|
||||
"interface.go",
|
||||
"func.go",
|
||||
}
|
||||
|
||||
for _, testCase := range tests {
|
||||
@@ -84,11 +85,16 @@ func TestCompiler(t *testing.T) {
|
||||
mod, errs := CompilePackage(testCase, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false)
|
||||
if errs != nil {
|
||||
for _, err := range errs {
|
||||
t.Log("error:", err)
|
||||
t.Error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
err = llvm.VerifyModule(mod, llvm.PrintMessageAction)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Optimize IR a little.
|
||||
funcPasses := llvm.NewFunctionPassManagerForModule(mod)
|
||||
defer funcPasses.Dispose()
|
||||
|
||||
+15
-3
@@ -25,14 +25,13 @@ func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context
|
||||
// Closure is: {context, function pointer}
|
||||
funcValueScalar = funcPtr
|
||||
case "switch":
|
||||
sigGlobal := c.getTypeCode(sig)
|
||||
funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
|
||||
funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
|
||||
if funcValueWithSignatureGlobal.IsNil() {
|
||||
funcValueWithSignatureType := c.getLLVMRuntimeType("funcValueWithSignature")
|
||||
funcValueWithSignature := llvm.ConstNamedStruct(funcValueWithSignatureType, []llvm.Value{
|
||||
llvm.ConstPtrToInt(funcPtr, c.uintptrType),
|
||||
sigGlobal,
|
||||
c.getFuncSignatureID(sig),
|
||||
})
|
||||
funcValueWithSignatureGlobal = llvm.AddGlobal(c.mod, funcValueWithSignatureType, funcValueWithSignatureGlobalName)
|
||||
funcValueWithSignatureGlobal.SetInitializer(funcValueWithSignature)
|
||||
@@ -50,6 +49,19 @@ func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context
|
||||
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
|
||||
// a cheap operation.
|
||||
func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value {
|
||||
@@ -71,7 +83,7 @@ func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (f
|
||||
funcPtr = b.CreateExtractValue(funcValue, 1, "")
|
||||
case "switch":
|
||||
llvmSig := b.getRawFuncType(sig)
|
||||
sigGlobal := b.getTypeCode(sig)
|
||||
sigGlobal := b.getFuncSignatureID(sig)
|
||||
funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
|
||||
funcPtr = b.CreateIntToPtr(funcPtr, llvmSig, "")
|
||||
default:
|
||||
|
||||
@@ -34,6 +34,9 @@ func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, p
|
||||
} else {
|
||||
// The stack size is fixed at compile time. By emitting it here as a
|
||||
// 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)
|
||||
}
|
||||
case "coroutines":
|
||||
|
||||
+20
-16
@@ -46,6 +46,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
var references llvm.Value
|
||||
var length int64
|
||||
var methodSet llvm.Value
|
||||
var ptrTo llvm.Value
|
||||
switch typ := typ.(type) {
|
||||
case *types.Named:
|
||||
references = c.getTypeCode(typ.Underlying())
|
||||
@@ -69,22 +70,25 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
if _, ok := typ.Underlying().(*types.Interface); !ok {
|
||||
methodSet = c.getTypeMethodSet(typ)
|
||||
}
|
||||
if !references.IsNil() || length != 0 || !methodSet.IsNil() {
|
||||
// Set the 'references' field of the runtime.typecodeID struct.
|
||||
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)
|
||||
if _, ok := typ.Underlying().(*types.Pointer); !ok {
|
||||
ptrTo = c.getTypeCode(types.NewPointer(typ))
|
||||
}
|
||||
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)
|
||||
}
|
||||
return global
|
||||
@@ -341,7 +345,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
commaOk = b.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, methodSet}, "")
|
||||
|
||||
} else {
|
||||
globalName := "reflect/types.type:" + getTypeCodeName(expr.AssertedType) + "$id"
|
||||
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
|
||||
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
|
||||
if assertedTypeCodeGlobal.IsNil() {
|
||||
// Create a new typecode global.
|
||||
|
||||
+18
-5
@@ -139,6 +139,17 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
||||
for _, attrName := range []string{"noalias", "nonnull"} {
|
||||
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":
|
||||
// This function is necessary for tracking pointers on the stack in a
|
||||
// portable way (see gc_stack_portable.go). Indicate to the optimizer
|
||||
@@ -355,16 +366,18 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
|
||||
|
||||
// Set alignment from the //go:align comment.
|
||||
var alignInBits uint32
|
||||
if info.align < 0 || info.align&(info.align-1) != 0 {
|
||||
alignment := c.targetData.ABITypeAlignment(llvmType)
|
||||
if info.align > alignment {
|
||||
alignment = info.align
|
||||
}
|
||||
if alignment <= 0 || alignment&(alignment-1) != 0 {
|
||||
// Check for power-of-two (or 0).
|
||||
// See: https://stackoverflow.com/a/108360
|
||||
c.addError(g.Pos(), "global variable alignment must be a positive power of two")
|
||||
} else {
|
||||
// Set the alignment only when it is a power of two.
|
||||
alignInBits = uint32(info.align) ^ uint32(info.align-1)
|
||||
if info.align > c.targetData.ABITypeAlignment(llvmType) {
|
||||
llvmGlobal.SetAlignment(info.align)
|
||||
}
|
||||
alignInBits = uint32(alignment) ^ uint32(alignment-1)
|
||||
llvmGlobal.SetAlignment(alignment)
|
||||
}
|
||||
|
||||
if c.Debug && !info.extern {
|
||||
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
; ModuleID = 'basic.go'
|
||||
source_filename = "basic.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
; ModuleID = 'float.go'
|
||||
source_filename = "float.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func foo(callback func(int)) {
|
||||
callback(3)
|
||||
}
|
||||
|
||||
func bar() {
|
||||
foo(someFunc)
|
||||
}
|
||||
|
||||
func someFunc(int) {
|
||||
}
|
||||
Vendored
+47
@@ -0,0 +1,47 @@
|
||||
; 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
|
||||
}
|
||||
Vendored
+13
-12
@@ -1,25 +1,26 @@
|
||||
; ModuleID = 'interface.go'
|
||||
source_filename = "interface.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo* }
|
||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID* }
|
||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||
%runtime._interface = type { i32, i8* }
|
||||
%runtime._string = type { i8*, i32 }
|
||||
|
||||
@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID zeroinitializer
|
||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"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: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: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: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: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}}" }
|
||||
@"func Error() string" = external constant i8
|
||||
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
|
||||
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"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, %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, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }
|
||||
@"func String() string" = external constant i8
|
||||
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func String() string"]
|
||||
@"reflect/types.type:basic:int$id" = external constant i8
|
||||
@"reflect/types.typeid:basic:int" = external constant i8
|
||||
@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
@@ -51,7 +52,7 @@ entry:
|
||||
|
||||
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.type:basic:int$id", i8* undef, i8* null)
|
||||
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.typeid:basic:int", i8* undef, i8* null)
|
||||
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.ok: ; preds = %entry
|
||||
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
; ModuleID = 'pointer.go'
|
||||
source_filename = "pointer.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
|
||||
Vendored
+4
-4
@@ -1,7 +1,7 @@
|
||||
; ModuleID = 'slice.go'
|
||||
source_filename = "slice.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
@@ -60,7 +60,7 @@ entry:
|
||||
ret { i32*, i32, i32 } %7
|
||||
}
|
||||
|
||||
declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8*, i32, i32, i32, i32, i8*, i8*)
|
||||
declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8* nocapture readonly, i32, i32, i32, i32, i8*, i8*)
|
||||
|
||||
define hidden { i32*, i32, i32 } @main.sliceAppendSlice(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32* %added.data, i32 %added.len, i32 %added.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
@@ -85,4 +85,4 @@ entry:
|
||||
ret i32 %copy.n
|
||||
}
|
||||
|
||||
declare i32 @runtime.sliceCopy(i8*, i8*, i32, i32, i32, i8*, i8*)
|
||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
||||
|
||||
Vendored
+8
@@ -1,5 +1,13 @@
|
||||
package main
|
||||
|
||||
func someString() string {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
func zeroLengthString() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func stringLen(s string) int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
Vendored
+16
-2
@@ -1,7 +1,11 @@
|
||||
; ModuleID = 'string.go'
|
||||
source_filename = "string.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
%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*)
|
||||
|
||||
@@ -10,6 +14,16 @@ entry:
|
||||
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 {
|
||||
entry:
|
||||
ret i32 %s.len
|
||||
|
||||
@@ -12,5 +12,5 @@ require (
|
||||
go.bug.st/serial v1.1.2
|
||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
|
||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
|
||||
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4
|
||||
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c
|
||||
)
|
||||
|
||||
@@ -59,3 +59,5 @@ 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=
|
||||
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-20210325115028-e7b85195e81c h1:vn9IPshzYmzZis10UEVrsIBRv9FpykADw6M3/tHHROg=
|
||||
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
|
||||
+3
-2
@@ -171,8 +171,9 @@ func getGoroot() string {
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
candidates = []string{
|
||||
"/usr/local/go", // manually installed
|
||||
"/usr/lib/go", // from the distribution
|
||||
"/usr/local/go", // manually installed
|
||||
"/usr/lib/go", // from the distribution
|
||||
"/snap/go/current/", // installed using snap
|
||||
}
|
||||
case "darwin":
|
||||
candidates = []string{
|
||||
|
||||
+17
-1
@@ -25,6 +25,7 @@ type function struct {
|
||||
// basicBlock represents a LLVM basic block and contains a slice of
|
||||
// instructions. The last instruction must be a terminator instruction.
|
||||
type basicBlock struct {
|
||||
phiNodes []instruction
|
||||
instructions []instruction
|
||||
}
|
||||
|
||||
@@ -135,6 +136,15 @@ func (r *runner) compileFunction(llvmFn llvm.Value) *function {
|
||||
default:
|
||||
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:
|
||||
inst.name = llvmInst.Name()
|
||||
incomingCount := inst.llvmInst.IncomingCount()
|
||||
@@ -337,7 +347,13 @@ func (r *runner) compileFunction(llvmFn llvm.Value) *function {
|
||||
// This error is handled when actually trying to interpret this
|
||||
// instruction (to not trigger on code that won't be executed).
|
||||
}
|
||||
bb.instructions = append(bb.instructions, inst)
|
||||
if inst.opcode == llvm.PHI {
|
||||
// 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
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
func TestInterp(t *testing.T) {
|
||||
for _, name := range []string{
|
||||
"basic",
|
||||
"phi",
|
||||
"slice-copy",
|
||||
"consteval",
|
||||
"map",
|
||||
"interface",
|
||||
} {
|
||||
name := name // make tc local to this closure
|
||||
|
||||
+84
-92
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -33,6 +34,50 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
lastBB := -1 // last basic block is undefined, only defined after a branch
|
||||
var operands []value
|
||||
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]
|
||||
operands = operands[:0]
|
||||
isRuntimeInst := false
|
||||
@@ -103,27 +148,24 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
default:
|
||||
panic("unknown operands length")
|
||||
}
|
||||
break // continue with next block
|
||||
case llvm.PHI:
|
||||
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
|
||||
}
|
||||
case llvm.Switch:
|
||||
// Switch statement: [value, defaultLabel, case0, label0, case1, label1, ...]
|
||||
value := operands[0].Uint()
|
||||
targetLabel := operands[1].Uint() // default label
|
||||
// Do a lazy switch by iterating over all cases.
|
||||
for i := 2; i < len(operands); i += 2 {
|
||||
if value == operands[i].Uint() {
|
||||
targetLabel = operands[i+1].Uint()
|
||||
break
|
||||
}
|
||||
}
|
||||
lastBB = currentBB
|
||||
currentBB = int(targetLabel)
|
||||
bb = fn.blocks[currentBB]
|
||||
instIndex = -1 // start at 0 the next cycle
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"phi", inst.operands, "->", result)
|
||||
fmt.Fprintln(os.Stderr, indent+"switch", operands, "->", currentBB)
|
||||
}
|
||||
if result == nil {
|
||||
panic("could not find PHI input")
|
||||
}
|
||||
locals[inst.localIndex] = result
|
||||
case llvm.Select:
|
||||
// Select is much like a ternary operator: it picks a result from
|
||||
// the second and third operand based on the boolean first operand.
|
||||
@@ -155,7 +197,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
|
||||
// already be emitted in initAll.
|
||||
continue
|
||||
case strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet":
|
||||
case strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet" || callFn.name == "os.runtime_args":
|
||||
// These functions should be run at runtime. Specifically:
|
||||
// * Print and panic functions are best emitted directly without
|
||||
// interpreting them, otherwise we get a ton of putchar (etc.)
|
||||
@@ -163,6 +205,9 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
// * runtime.hashmapGet tries to access the map value directly.
|
||||
// This is not possible as the map value is treated as a special
|
||||
// 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)
|
||||
if err != nil {
|
||||
return nil, mem, err
|
||||
@@ -328,7 +373,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
actualType := actualTypePtrToInt.Operand(0)
|
||||
if actualType.Name()+"$id" == assertedType.Name() {
|
||||
if strings.TrimPrefix(actualType.Name(), "reflect/types.type:") == strings.TrimPrefix(assertedType.Name(), "reflect/types.typeid:") {
|
||||
locals[inst.localIndex] = literalValue{uint8(1)}
|
||||
} else {
|
||||
locals[inst.localIndex] = literalValue{uint8(0)}
|
||||
@@ -415,74 +460,6 @@ 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()))
|
||||
}
|
||||
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:
|
||||
if len(callFn.blocks) == 0 {
|
||||
// Call to a function declaration without a definition
|
||||
@@ -954,16 +931,31 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
|
||||
result = r.builder.CreateBitCast(operands[0], inst.llvmInst.Type(), inst.name)
|
||||
case llvm.ExtractValue:
|
||||
indices := inst.llvmInst.Indices()
|
||||
if len(indices) != 1 {
|
||||
panic("expected exactly one index")
|
||||
// Note: the Go LLVM API doesn't support multiple indices, so simulate
|
||||
// this operation with some extra extractvalue instructions. Hopefully
|
||||
// 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(operands[0], int(indices[0]), inst.name)
|
||||
result = r.builder.CreateExtractValue(agg, int(indices[len(indices)-1]), inst.name)
|
||||
case llvm.InsertValue:
|
||||
indices := inst.llvmInst.Indices()
|
||||
if len(indices) != 1 {
|
||||
panic("expected exactly one index")
|
||||
// Similar to extractvalue, we're working around a limitation in the Go
|
||||
// LLVM API here by splitting the insertvalue into multiple instructions
|
||||
// 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 = r.builder.CreateInsertValue(operands[0], operands[1], int(indices[0]), inst.name)
|
||||
result = operands[1]
|
||||
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:
|
||||
result = r.builder.CreateAdd(operands[0], operands[1], inst.name)
|
||||
case llvm.Sub:
|
||||
|
||||
@@ -640,288 +640,6 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
|
||||
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
|
||||
// data. This is the fallback data for everything that isn't clearly a
|
||||
// literalValue or pointerValue.
|
||||
|
||||
Vendored
+35
@@ -8,6 +8,7 @@ target triple = "x86_64--linux"
|
||||
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
|
||||
@main.exposedValue1 = global i16 0
|
||||
@main.exposedValue2 = global i16 0
|
||||
@main.insertedValue = global {i8, i32, {float, {i64, i16}}} zeroinitializer
|
||||
|
||||
declare void @runtime.printint64(i64) unnamed_addr
|
||||
|
||||
@@ -65,6 +66,19 @@ entry:
|
||||
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
|
||||
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
|
||||
}
|
||||
|
||||
@@ -87,3 +101,24 @@ entry:
|
||||
store i16 8, i16* @main.exposedValue2
|
||||
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()
|
||||
|
||||
Vendored
+37
@@ -7,6 +7,7 @@ target triple = "x86_64--linux"
|
||||
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
|
||||
@main.exposedValue1 = 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
|
||||
|
||||
@@ -25,6 +26,19 @@ entry:
|
||||
store i16 5, i16* @main.exposedValue1
|
||||
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
|
||||
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
|
||||
}
|
||||
|
||||
@@ -44,3 +58,26 @@ entry:
|
||||
store i16 8, i16* @main.exposedValue2
|
||||
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
|
||||
|
||||
Vendored
+2
-2
@@ -6,7 +6,7 @@ target triple = "x86_64--linux"
|
||||
|
||||
@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$id" = external constant i8
|
||||
@"reflect/types.typeid:named:main.foo" = external constant i8
|
||||
@"reflect/types.type:basic:int" = external constant %runtime.typecodeID
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ entry:
|
||||
define internal void @main.init() unnamed_addr {
|
||||
entry:
|
||||
; Test type asserts.
|
||||
%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)
|
||||
%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)
|
||||
store i1 %typecode, i1* @main.v1
|
||||
ret void
|
||||
}
|
||||
|
||||
Vendored
-74
@@ -1,74 +0,0 @@
|
||||
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
|
||||
}
|
||||
Vendored
-20
@@ -1,20 +0,0 @@
|
||||
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
|
||||
}
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
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
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
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
|
||||
}
|
||||
+1
-1
Submodule lib/cmsis-svd updated: d9b58694ce...2fb66c5735
+1
-1
Submodule lib/stm32-svd updated: c6b5be976f...3a0b0829b7
@@ -23,3 +23,13 @@ type Error struct {
|
||||
func (e Error) Error() string {
|
||||
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"
|
||||
}
|
||||
|
||||
@@ -210,6 +210,12 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,13 @@ import (
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/google/shlex"
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/tinygo-org/tinygo/builder"
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
@@ -28,6 +30,7 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
|
||||
"go.bug.st/serial"
|
||||
"go.bug.st/serial/enumerator"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -133,15 +136,17 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
|
||||
})
|
||||
}
|
||||
|
||||
// Test runs the tests in the given package.
|
||||
func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, outpath string) error {
|
||||
// Test runs the tests in the given package. Returns whether the test passed and
|
||||
// possibly an error if the test failed to run.
|
||||
func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, outpath string) (bool, error) {
|
||||
options.TestConfig.CompileTestBinary = true
|
||||
config, err := builder.NewConfig(options)
|
||||
if err != nil {
|
||||
return err
|
||||
return false, err
|
||||
}
|
||||
|
||||
return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
|
||||
var passed bool
|
||||
err = builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
|
||||
if testCompileOnly || outpath != "" {
|
||||
// Write test binary to the specified file name.
|
||||
if outpath == "" {
|
||||
@@ -155,48 +160,78 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
|
||||
// Do not run the test.
|
||||
return nil
|
||||
}
|
||||
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 {
|
||||
// Propagate the exit code
|
||||
if err, ok := err.(*exec.ExitError); ok {
|
||||
os.Exit(err.ExitCode())
|
||||
}
|
||||
return &commandError{"failed to run compiled binary", result.Binary, err}
|
||||
}
|
||||
return nil
|
||||
|
||||
// Run the test.
|
||||
start := time.Now()
|
||||
var err error
|
||||
passed, err = runPackageTest(config, result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
duration := time.Since(start)
|
||||
|
||||
// Print the result.
|
||||
importPath := strings.TrimSuffix(result.ImportPath, ".test")
|
||||
if passed {
|
||||
fmt.Printf("ok \t%s\t%.3fs\n", importPath, duration.Seconds())
|
||||
} 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 &commandError{"failed to run emulator with", result.Binary, err}
|
||||
}
|
||||
fmt.Printf("FAIL\t%s\t%.3fs\n", importPath, duration.Seconds())
|
||||
}
|
||||
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
|
||||
}
|
||||
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 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.
|
||||
@@ -240,15 +275,12 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
|
||||
// do we need port reset to put MCU into bootloader mode?
|
||||
if config.Target.PortReset == "true" && flashMethod != "openocd" {
|
||||
if port == "" {
|
||||
var err error
|
||||
port, err = getDefaultPort()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
port, err := getDefaultPort(strings.FieldsFunc(port, func(c rune) bool { return c == ',' }))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := touchSerialPortAt1200bps(port)
|
||||
err = touchSerialPortAt1200bps(port)
|
||||
if err != nil {
|
||||
return &commandError{"failed to reset port", result.Binary, err}
|
||||
}
|
||||
@@ -264,9 +296,9 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
fileToken := "{" + fileExt[1:] + "}"
|
||||
flashCmd = strings.ReplaceAll(flashCmd, fileToken, result.Binary)
|
||||
|
||||
if port == "" && strings.Contains(flashCmd, "{port}") {
|
||||
if strings.Contains(flashCmd, "{port}") {
|
||||
var err error
|
||||
port, err = getDefaultPort()
|
||||
port, err = getDefaultPort(strings.FieldsFunc(port, func(c rune) bool { return c == ',' }))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -665,41 +697,66 @@ func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, e
|
||||
}
|
||||
|
||||
// getDefaultPort returns the default serial port depending on the operating system.
|
||||
func getDefaultPort() (port string, err error) {
|
||||
var portPath string
|
||||
func getDefaultPort(portCandidates []string) (port string, err error) {
|
||||
if len(portCandidates) == 1 {
|
||||
return portCandidates[0], nil
|
||||
}
|
||||
|
||||
var ports []string
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
portPath = "/dev/cu.usb*"
|
||||
case "linux":
|
||||
portPath = "/dev/ttyACM*"
|
||||
case "freebsd":
|
||||
portPath = "/dev/cuaU*"
|
||||
case "windows":
|
||||
ports, err := serial.GetPortsList()
|
||||
ports, err = filepath.Glob("/dev/cuaU*")
|
||||
case "darwin", "linux", "windows":
|
||||
var portsList []*enumerator.PortDetails
|
||||
portsList, err = enumerator.GetDetailedPortsList()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(ports) == 0 {
|
||||
return "", errors.New("no serial ports available")
|
||||
} else if len(ports) > 1 {
|
||||
return "", errors.New("multiple serial ports available - use -port flag")
|
||||
for _, p := range portsList {
|
||||
ports = append(ports, p.Name)
|
||||
}
|
||||
|
||||
return ports[0], nil
|
||||
if ports == nil || len(ports) == 0 {
|
||||
// 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:
|
||||
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 {
|
||||
return "", err
|
||||
}
|
||||
if d == nil {
|
||||
} else if ports == nil {
|
||||
return "", errors.New("unable to locate a serial port")
|
||||
} else if len(ports) == 0 {
|
||||
return "", errors.New("no serial ports available")
|
||||
}
|
||||
|
||||
return d[0], nil
|
||||
if len(portCandidates) == 0 {
|
||||
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() {
|
||||
@@ -812,6 +869,52 @@ 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() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
|
||||
@@ -831,14 +934,16 @@ func main() {
|
||||
target := flag.String("target", "", "LLVM target | .json file with TargetSpec")
|
||||
printSize := flag.String("size", "", "print sizes (none, short, full)")
|
||||
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")
|
||||
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")
|
||||
port := flag.String("port", "", "flash port")
|
||||
port := flag.String("port", "", "flash port (can specify multiple candidates separated by commas)")
|
||||
programmer := flag.String("programmer", "", "which hardware programmer to use")
|
||||
cFlags := flag.String("cflags", "", "additional cflags for compiler")
|
||||
ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
|
||||
ldflags := flag.String("ldflags", "", "Go link tool compatible ldflags")
|
||||
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
|
||||
if command == "help" || command == "list" {
|
||||
@@ -867,35 +972,51 @@ func main() {
|
||||
}
|
||||
|
||||
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{
|
||||
Target: *target,
|
||||
Opt: *opt,
|
||||
GC: *gc,
|
||||
PanicStrategy: *panicStrategy,
|
||||
Scheduler: *scheduler,
|
||||
PrintIR: *printIR,
|
||||
DumpSSA: *dumpSSA,
|
||||
VerifyIR: *verifyIR,
|
||||
Debug: !*nodebug,
|
||||
PrintSizes: *printSize,
|
||||
PrintStacks: *printStacks,
|
||||
PrintCommands: *printCommands,
|
||||
Tags: *tags,
|
||||
WasmAbi: *wasmAbi,
|
||||
Programmer: *programmer,
|
||||
}
|
||||
|
||||
if *cFlags != "" {
|
||||
options.CFlags = strings.Split(*cFlags, " ")
|
||||
}
|
||||
|
||||
if *ldFlags != "" {
|
||||
options.LDFlags = strings.Split(*ldFlags, " ")
|
||||
Target: *target,
|
||||
Opt: *opt,
|
||||
GC: *gc,
|
||||
PanicStrategy: *panicStrategy,
|
||||
Scheduler: *scheduler,
|
||||
PrintIR: *printIR,
|
||||
DumpSSA: *dumpSSA,
|
||||
VerifyIR: *verifyIR,
|
||||
Debug: !*nodebug,
|
||||
PrintSizes: *printSize,
|
||||
PrintStacks: *printStacks,
|
||||
PrintAllocs: printAllocs,
|
||||
PrintCommands: *printCommands,
|
||||
Tags: *tags,
|
||||
GlobalValues: globalVarValues,
|
||||
WasmAbi: *wasmAbi,
|
||||
Programmer: *programmer,
|
||||
OpenOCDCommands: ocdCommands,
|
||||
LLVMFeatures: *llvmFeatures,
|
||||
}
|
||||
|
||||
os.Setenv("CC", "clang -target="+*target)
|
||||
|
||||
err := options.Verify()
|
||||
err = options.Verify()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
usage()
|
||||
@@ -983,16 +1104,26 @@ func main() {
|
||||
err := Run(pkgName, options)
|
||||
handleCompilerError(err)
|
||||
case "test":
|
||||
pkgName := "."
|
||||
if flag.NArg() == 1 {
|
||||
pkgName = filepath.ToSlash(flag.Arg(0))
|
||||
} else if flag.NArg() > 1 {
|
||||
fmt.Fprintln(os.Stderr, "test only accepts a single positional argument: package name, but multiple were specified")
|
||||
usage()
|
||||
var pkgNames []string
|
||||
for i := 0; i < flag.NArg(); i++ {
|
||||
pkgNames = append(pkgNames, filepath.ToSlash(flag.Arg(i)))
|
||||
}
|
||||
if len(pkgNames) == 0 {
|
||||
pkgNames = []string{"."}
|
||||
}
|
||||
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)
|
||||
}
|
||||
err := Test(pkgName, options, *testCompileOnlyFlag, outpath)
|
||||
handleCompilerError(err)
|
||||
case "targets":
|
||||
dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets")
|
||||
entries, err := ioutil.ReadDir(dir)
|
||||
|
||||
+125
-55
@@ -13,8 +13,8 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -28,41 +28,43 @@ const TESTDATA = "testdata"
|
||||
var testTarget = flag.String("target", "", "override test target")
|
||||
|
||||
func TestCompiler(t *testing.T) {
|
||||
matches, err := filepath.Glob(filepath.Join(TESTDATA, "*.go"))
|
||||
if err != nil {
|
||||
t.Fatal("could not read test files:", err)
|
||||
tests := []string{
|
||||
"alias.go",
|
||||
"atomic.go",
|
||||
"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 != "" {
|
||||
// This makes it possible to run one specific test (instead of all),
|
||||
// which is especially useful to quickly check whether some changes
|
||||
// affect a particular target architecture.
|
||||
runPlatTests(*testTarget, matches, t)
|
||||
runPlatTests(*testTarget, tests, t)
|
||||
return
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
t.Run("Host", func(t *testing.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)
|
||||
}
|
||||
runPlatTests("", tests, t)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -71,26 +73,26 @@ func TestCompiler(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("EmulatedCortexM3", func(t *testing.T) {
|
||||
runPlatTests("cortex-m-qemu", matches, t)
|
||||
runPlatTests("cortex-m-qemu", tests, t)
|
||||
})
|
||||
|
||||
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
|
||||
// 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.
|
||||
t.Run("EmulatedRISCV", func(t *testing.T) {
|
||||
runPlatTests("riscv-qemu", matches, t)
|
||||
runPlatTests("riscv-qemu", tests, t)
|
||||
})
|
||||
}
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
t.Run("X86Linux", func(t *testing.T) {
|
||||
runPlatTests("i386--linux-gnu", matches, t)
|
||||
runPlatTests("i386--linux-gnu", tests, t)
|
||||
})
|
||||
t.Run("ARMLinux", func(t *testing.T) {
|
||||
runPlatTests("arm--linux-gnueabihf", matches, t)
|
||||
runPlatTests("arm--linux-gnueabihf", tests, t)
|
||||
})
|
||||
t.Run("ARM64Linux", func(t *testing.T) {
|
||||
runPlatTests("aarch64--linux-gnu", matches, t)
|
||||
runPlatTests("aarch64--linux-gnu", tests, t)
|
||||
})
|
||||
goVersion, err := goenv.GorootVersionString(goenv.Get("GOROOT"))
|
||||
if err != nil {
|
||||
@@ -103,35 +105,110 @@ func TestCompiler(t *testing.T) {
|
||||
// below that are also not supported but still seem to pass, so
|
||||
// include them in the tests for now.
|
||||
t.Run("WebAssembly", func(t *testing.T) {
|
||||
runPlatTests("wasm", matches, t)
|
||||
runPlatTests("wasm", tests, t)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("WASI", func(t *testing.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=."})
|
||||
runPlatTests("wasi", tests, t)
|
||||
})
|
||||
}
|
||||
|
||||
// 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, matches []string, t *testing.T) {
|
||||
func runPlatTests(target string, tests []string, t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, path := range matches {
|
||||
path := path // redefine to avoid race condition
|
||||
t.Run(filepath.Base(path), func(t *testing.T) {
|
||||
for _, name := range tests {
|
||||
name := name // redefine to avoid race condition
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest(path, target, t, nil, nil)
|
||||
runTest(name, 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"})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func runTest(path, target string, t *testing.T, environmentVars []string, additionalArgs []string) {
|
||||
// Due to some problems with LLD, we cannot run links in parallel, or in parallel with compiles.
|
||||
// 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.
|
||||
txtpath := path[:len(path)-3] + ".txt"
|
||||
if path[len(path)-1] == os.PathSeparator {
|
||||
if path[len(path)-1] == '/' {
|
||||
txtpath = path + "out.txt"
|
||||
}
|
||||
expected, err := ioutil.ReadFile(txtpath)
|
||||
@@ -152,19 +229,8 @@ func runTest(path, target string, t *testing.T, environmentVars []string, additi
|
||||
}()
|
||||
|
||||
// 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")
|
||||
err = Build("./"+path, binary, config)
|
||||
err = runBuild("./"+path, binary, options)
|
||||
if err != nil {
|
||||
printCompilerError(t.Log, err)
|
||||
t.Fail()
|
||||
@@ -178,6 +244,7 @@ func runTest(path, target string, t *testing.T, environmentVars []string, additi
|
||||
if target == "" {
|
||||
cmd = exec.Command(binary)
|
||||
cmd.Env = append(cmd.Env, environmentVars...)
|
||||
cmd.Args = append(cmd.Args, cmdArgs...)
|
||||
} else {
|
||||
spec, err := compileopts.LoadTarget(target)
|
||||
if err != nil {
|
||||
@@ -187,13 +254,16 @@ func runTest(path, target string, t *testing.T, environmentVars []string, additi
|
||||
cmd = exec.Command(binary)
|
||||
} else {
|
||||
args := append(spec.Emulator[1:], binary)
|
||||
cmd = exec.Command(spec.Emulator[0], append(args, additionalArgs...)...)
|
||||
cmd = exec.Command(spec.Emulator[0], args...)
|
||||
}
|
||||
|
||||
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 {
|
||||
cmd.Args = append(cmd.Args, "--env", v)
|
||||
}
|
||||
cmd.Args = append(cmd.Args, cmdArgs...)
|
||||
} else {
|
||||
cmd.Env = append(cmd.Env, environmentVars...)
|
||||
}
|
||||
|
||||
+15
-6
@@ -26,12 +26,21 @@ type SCB_Type struct {
|
||||
SHPR2 volatile.Register32 // 0xD1C: System Handler Priority Register 2
|
||||
SHPR3 volatile.Register32 // 0xD20: System Handler Priority Register 3
|
||||
// the following are only applicable for Cortex-M3/M33/M4/M7
|
||||
SHCSR volatile.Register32 // 0xD24: System Handler Control and State Register
|
||||
CFSR volatile.Register32 // 0xD28: Configurable Fault Status Register
|
||||
HFSR volatile.Register32 // 0xD2C: HardFault Status Register
|
||||
DFSR volatile.Register32 // 0xD30: Debug Fault Status Register
|
||||
MMFAR volatile.Register32 // 0xD34: MemManage Fault Address Register
|
||||
BFAR volatile.Register32 // 0xD38: BusFault Address Register
|
||||
SHCSR volatile.Register32 // 0xD24: System Handler Control and State Register
|
||||
CFSR volatile.Register32 // 0xD28: Configurable Fault Status Register
|
||||
HFSR volatile.Register32 // 0xD2C: HardFault Status Register
|
||||
DFSR volatile.Register32 // 0xD30: Debug Fault Status Register
|
||||
MMFAR volatile.Register32 // 0xD34: MemManage Fault 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)))
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// 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
|
||||
)
|
||||
@@ -14,7 +14,7 @@ func main() {
|
||||
led := machine.LED
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
sensor := machine.ADC{machine.ADC2}
|
||||
sensor := machine.ADC{Pin: machine.ADC2}
|
||||
sensor.Configure(machine.ADCConfig{})
|
||||
|
||||
for {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// +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
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
// +build arduino
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
// Configuration on an Arduino Uno.
|
||||
pwm = machine.Timer2
|
||||
pinA = machine.PB3 // pin 11 on the Uno
|
||||
pinB = machine.PD3 // pin 3 on the Uno
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
// +build feather_m4
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = machine.TCC0
|
||||
pinA = machine.D12
|
||||
pinB = machine.D13
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
// +build itsybitsy_m0
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = machine.TCC0
|
||||
pinA = machine.D3
|
||||
pinB = machine.D4
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
// +build itsybitsy_m4
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = machine.TCC0
|
||||
pinA = machine.D12
|
||||
pinB = machine.D13
|
||||
)
|
||||
+57
-47
@@ -1,64 +1,74 @@
|
||||
package main
|
||||
|
||||
// This example demonstrates some features of the PWM support.
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 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
|
||||
)
|
||||
|
||||
// cycleColor is just a placeholder until math/rand or some equivalent is working.
|
||||
func cycleColor(color uint8) uint8 {
|
||||
if color < 10 {
|
||||
return color + 1
|
||||
} else if color < 200 {
|
||||
return color + 10
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
const delayBetweenPeriods = time.Second * 5
|
||||
|
||||
func main() {
|
||||
machine.InitPWM()
|
||||
// Delay a bit on startup to easily catch the first messages.
|
||||
time.Sleep(time.Second * 2)
|
||||
|
||||
red := machine.PWM{redPin}
|
||||
err := red.Configure()
|
||||
checkError(err, "failed to configure red pin")
|
||||
// Configure the PWM with the given period.
|
||||
err := pwm.Configure(machine.PWMConfig{
|
||||
Period: 16384e3, // 16.384ms
|
||||
})
|
||||
if err != nil {
|
||||
println("failed to configure PWM")
|
||||
return
|
||||
}
|
||||
|
||||
green := machine.PWM{greenPin}
|
||||
err = green.Configure()
|
||||
checkError(err, "failed to configure green pin")
|
||||
// The top value is the highest value that can be passed to PWMChannel.Set.
|
||||
// It is usually an even number.
|
||||
println("top:", pwm.Top())
|
||||
|
||||
blue := machine.PWM{bluePin}
|
||||
err = blue.Configure()
|
||||
checkError(err, "failed to configure blue pin")
|
||||
// Configure the two channels we'll use as outputs.
|
||||
channelA, err := pwm.Channel(pinA)
|
||||
if err != nil {
|
||||
println("failed to configure channel A")
|
||||
return
|
||||
}
|
||||
channelB, err := pwm.Channel(pinB)
|
||||
if err != nil {
|
||||
println("failed to configure channel B")
|
||||
return
|
||||
}
|
||||
|
||||
var rc uint8
|
||||
var gc uint8 = 20
|
||||
var bc uint8 = 30
|
||||
// Invert one of the channels to demonstrate output polarity.
|
||||
pwm.SetInverting(channelB, true)
|
||||
|
||||
// Test out various frequencies below, including some edge cases.
|
||||
|
||||
println("running at 0% duty cycle")
|
||||
pwm.Set(channelA, 0)
|
||||
pwm.Set(channelB, 0)
|
||||
time.Sleep(delayBetweenPeriods)
|
||||
|
||||
println("running at 1")
|
||||
pwm.Set(channelA, 1)
|
||||
pwm.Set(channelB, 1)
|
||||
time.Sleep(delayBetweenPeriods)
|
||||
|
||||
println("running at 25% duty cycle")
|
||||
pwm.Set(channelA, pwm.Top()/4)
|
||||
pwm.Set(channelB, pwm.Top()/4)
|
||||
time.Sleep(delayBetweenPeriods)
|
||||
|
||||
println("running at top-1")
|
||||
pwm.Set(channelA, pwm.Top()-1)
|
||||
pwm.Set(channelB, pwm.Top()-1)
|
||||
time.Sleep(delayBetweenPeriods)
|
||||
|
||||
println("running at 100% duty cycle")
|
||||
pwm.Set(channelA, pwm.Top())
|
||||
pwm.Set(channelB, pwm.Top())
|
||||
time.Sleep(delayBetweenPeriods)
|
||||
|
||||
for {
|
||||
rc = cycleColor(rc)
|
||||
gc = cycleColor(gc)
|
||||
bc = cycleColor(bc)
|
||||
|
||||
red.Set(uint16(rc) << 8)
|
||||
green.Set(uint16(gc) << 8)
|
||||
blue.Set(uint16(bc) << 8)
|
||||
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
}
|
||||
}
|
||||
|
||||
func checkError(err error, msg string) {
|
||||
if err != nil {
|
||||
print(msg, ": ", err.Error())
|
||||
println()
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
.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
|
||||
@@ -0,0 +1,59 @@
|
||||
// +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
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#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
|
||||
@@ -0,0 +1,61 @@
|
||||
// +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
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// 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}
|
||||
@@ -0,0 +1,61 @@
|
||||
// +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
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
.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
|
||||
@@ -0,0 +1,64 @@
|
||||
// +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
|
||||
}
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
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 (
|
||||
"device/arm"
|
||||
"unsafe"
|
||||
|
||||
@@ -9,4 +9,11 @@ type ADCConfig struct {
|
||||
Reference uint32 // analog reference voltage (AREF) in millivolts
|
||||
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)
|
||||
Bus uint8 // bus Number of ADC
|
||||
}
|
||||
|
||||
const (
|
||||
AdcBusAuto = 0
|
||||
AdcBus0 = 1
|
||||
AdcBus1 = 2
|
||||
)
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
// +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
|
||||
)
|
||||
@@ -0,0 +1,330 @@
|
||||
// +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,
|
||||
}
|
||||
)
|
||||
@@ -25,8 +25,8 @@ const (
|
||||
|
||||
// Analog Pins
|
||||
const (
|
||||
A0 = PA02 // PWM available, also ADC/AIN[0]
|
||||
A1 = PA05 // ADC/AIN[5]
|
||||
A0 = PA02 // ADC/AIN[0]
|
||||
A1 = PA05 // PWM available, also ADC/AIN[5]
|
||||
A2 = PA06 // PWM available, also ADC/AIN[6]
|
||||
A3 = PA07 // PWM available, also ADC/AIN[7]
|
||||
A4 = PB03 // PORTB
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
// +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,
|
||||
}
|
||||
)
|
||||
@@ -41,13 +41,17 @@ const (
|
||||
// LORA RFM95 Radio
|
||||
RFM95_DIO0_PIN = PC13
|
||||
|
||||
//TinyGo UART is MCU LPUSART1
|
||||
// TinyGo UART is MCU LPUSART1
|
||||
UART_RX_PIN = PA13
|
||||
UART_TX_PIN = PA14
|
||||
|
||||
//TinyGo UART1 is MCU USART1
|
||||
// TinyGo UART1 is MCU USART1
|
||||
UART1_RX_PIN = PB6
|
||||
UART1_TX_PIN = PB7
|
||||
|
||||
// MPU9250 Nine-Axis (Gyro + Accelerometer + Compass)
|
||||
I2C0_SCL_PIN = PA9
|
||||
I2C0_SDA_PIN = PA10
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -68,6 +72,13 @@ var (
|
||||
RxAltFuncSelector: 0,
|
||||
}
|
||||
|
||||
// MPU9250 Nine-Axis (Gyro + Accelerometer + Compass)
|
||||
I2C1 = &I2C{
|
||||
Bus: stm32.I2C1,
|
||||
AltFuncSelector: 6,
|
||||
}
|
||||
I2C0 = I2C1
|
||||
|
||||
// SPI
|
||||
SPI0 = SPI{
|
||||
Bus: stm32.SPI1,
|
||||
|
||||
@@ -5,11 +5,6 @@ package machine
|
||||
// The micro:bit does not have a 32kHz crystal on board.
|
||||
const HasLowFrequencyCrystal = false
|
||||
|
||||
const (
|
||||
LED = P13
|
||||
LED1 = LED
|
||||
)
|
||||
|
||||
// Buttons on the micro:bit v2 (A and B)
|
||||
const (
|
||||
BUTTON Pin = BUTTONA
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
// +build nucleol031k6
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
const (
|
||||
LED = LED_BUILTIN
|
||||
LED_BUILTIN = LED_GREEN
|
||||
LED_GREEN = PB3
|
||||
)
|
||||
|
||||
const (
|
||||
// Arduino Pins
|
||||
A0 = PA0 // ADC_IN0
|
||||
A1 = PA1 // ADC_IN1
|
||||
A2 = PA3 // ADC_IN3
|
||||
A3 = PA4 // ADC_IN4
|
||||
A4 = PA5 // ADC_IN5 || I2C1_SDA
|
||||
A5 = PA6 // ADC_IN6 || I2C1_SCL
|
||||
A6 = PA7 // ADC_IN7
|
||||
A7 = PA2 // ADC_IN2
|
||||
|
||||
D0 = PA10 // USART1_TX
|
||||
D1 = PA9 // USART1_RX
|
||||
D2 = PA12
|
||||
D3 = PB0 // TIM2_CH3
|
||||
D4 = PB7
|
||||
D5 = PB6 // TIM16_CH1N
|
||||
D6 = PB1 // TIM14_CH1
|
||||
D9 = PA8 // TIM1_CH1
|
||||
D10 = PA11 // SPI_CS || TIM1_CH4
|
||||
D11 = PB5 // SPI1_MOSI || TIM3_CH2
|
||||
D12 = PB4 // SPI1_MISO
|
||||
D13 = PB3 // SPI1_SCK
|
||||
)
|
||||
|
||||
const (
|
||||
// UART pins
|
||||
// PA2 and PA15 are connected to the ST-Link Virtual Com Port (VCP)
|
||||
UART_TX_PIN = PA2
|
||||
UART_RX_PIN = PA15
|
||||
|
||||
// SPI
|
||||
SPI1_SCK_PIN = PB3
|
||||
SPI1_SDI_PIN = PB5
|
||||
SPI1_SDO_PIN = PB4
|
||||
SPI0_SCK_PIN = SPI1_SCK_PIN
|
||||
SPI0_SDI_PIN = SPI1_SDI_PIN
|
||||
SPI0_SDO_PIN = SPI1_SDO_PIN
|
||||
|
||||
// I2C pins
|
||||
// PB6 and PB7 are mapped to CN4 pin 7 and CN4 pin 8 respectively with the
|
||||
// default solder bridge settings
|
||||
I2C0_SCL_PIN = PB7
|
||||
I2C0_SDA_PIN = PB6
|
||||
I2C0_ALT_FUNC = 1
|
||||
)
|
||||
|
||||
var (
|
||||
// USART2 is the hardware serial port connected to the onboard ST-LINK
|
||||
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
|
||||
// Both UART0 and UART1 refer to USART2.
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART2,
|
||||
TxAltFuncSelector: 4,
|
||||
RxAltFuncSelector: 4,
|
||||
}
|
||||
UART1 = &UART0
|
||||
|
||||
// I2C1 is documented, alias to I2C0 as well
|
||||
I2C1 = &I2C{
|
||||
Bus: stm32.I2C1,
|
||||
AltFuncSelector: 1,
|
||||
}
|
||||
I2C0 = I2C1
|
||||
|
||||
// SPI
|
||||
SPI0 = SPI{
|
||||
Bus: stm32.SPI1,
|
||||
AltFuncSelector: 0,
|
||||
}
|
||||
SPI1 = &SPI0
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART0.Interrupt = interrupt.New(stm32.IRQ_USART2, UART0.handleInterrupt)
|
||||
}
|
||||
@@ -13,20 +13,53 @@ const (
|
||||
LED_GREEN = PB3
|
||||
)
|
||||
|
||||
// UART pins
|
||||
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)
|
||||
UART_TX_PIN = PA2
|
||||
UART_RX_PIN = PA15
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
// I2C pins
|
||||
// With default solder bridge settings:
|
||||
// PB6 / Arduino D5 / CN3 Pin 8 is SCL
|
||||
// PB7 / Arduino D4 / CN3 Pin 7 is SDA
|
||||
I2C0_SCL_PIN = PB6
|
||||
I2C0_SDA_PIN = PB7
|
||||
|
||||
// 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 (
|
||||
@@ -40,15 +73,20 @@ var (
|
||||
RxAltFuncSelector: 3,
|
||||
}
|
||||
UART1 = &UART0
|
||||
)
|
||||
|
||||
var (
|
||||
// I2C1 is documented, alias to I2C0 as well
|
||||
I2C1 = &I2C{
|
||||
Bus: stm32.I2C1,
|
||||
AltFuncSelector: 4,
|
||||
}
|
||||
I2C0 = I2C1
|
||||
|
||||
// SPI1 is documented, alias to SPI0 as well
|
||||
SPI1 = &SPI{
|
||||
Bus: stm32.SPI1,
|
||||
AltFuncSelector: 5,
|
||||
}
|
||||
SPI0 = SPI1
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -94,8 +94,8 @@ const (
|
||||
// I2C on the QT Py M0.
|
||||
var (
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
Bus: sam.SERCOM1_I2CM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build atmega nrf sam stm32,!stm32l0 fe310 k210
|
||||
// +build atmega nrf sam stm32 fe310 k210
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -10,6 +10,11 @@ var (
|
||||
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 {
|
||||
Mode PinMode
|
||||
}
|
||||
@@ -37,10 +42,7 @@ func (p Pin) Low() {
|
||||
p.Set(false)
|
||||
}
|
||||
|
||||
type PWM struct {
|
||||
Pin Pin
|
||||
}
|
||||
|
||||
type ADC struct {
|
||||
Pin Pin
|
||||
Bus uint8
|
||||
}
|
||||
|
||||
@@ -0,0 +1,931 @@
|
||||
// +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}
|
||||
@@ -4,6 +4,7 @@ package machine
|
||||
|
||||
import (
|
||||
"device/avr"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
@@ -21,71 +22,432 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||
}
|
||||
}
|
||||
|
||||
// InitPWM initializes the registers needed for PWM.
|
||||
func InitPWM() {
|
||||
// use waveform generation
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_WGM00)
|
||||
|
||||
// set timer 0 prescale factor to 64
|
||||
avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00)
|
||||
|
||||
// set timer 1 prescale factor to 64
|
||||
avr.TCCR1B.SetBits(avr.TCCR1B_CS11)
|
||||
|
||||
// put timer 1 in 8-bit phase correct pwm mode
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_WGM10)
|
||||
|
||||
// set timer 2 prescale factor to 64
|
||||
avr.TCCR2B.SetBits(avr.TCCR2B_CS22)
|
||||
|
||||
// configure timer 2 for phase correct pwm (8-bit)
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_WGM20)
|
||||
// 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
|
||||
}
|
||||
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() error {
|
||||
switch pwm.Pin / 8 {
|
||||
case 0: // port B
|
||||
avr.DDRB.SetBits(1 << uint8(pwm.Pin))
|
||||
case 2: // port D
|
||||
avr.DDRD.SetBits(1 << uint8(pwm.Pin-16))
|
||||
var (
|
||||
Timer0 = PWM{0} // 8 bit timer for PD5 and PD6
|
||||
Timer1 = PWM{1} // 16 bit timer for PB1 and PB2
|
||||
Timer2 = PWM{2} // 8 bit timer for PB3 and PD3
|
||||
)
|
||||
|
||||
// Configure enables and configures this PWM.
|
||||
//
|
||||
// For the two 8 bit timers, there is only a limited number of periods
|
||||
// available, namely the CPU frequency divided by 256 and again divided by 1, 8,
|
||||
// 64, 256, or 1024. For a MCU running at 16MHz, this would be a period of 16µs,
|
||||
// 128µs, 1024µs, 4096µs, or 16384µs.
|
||||
func (pwm PWM) Configure(config PWMConfig) error {
|
||||
switch pwm.num {
|
||||
case 0, 2: // 8-bit timers (Timer/counter 0 and Timer/counter 2)
|
||||
// Calculate the timer prescaler.
|
||||
// While we could configure a flexible top, that would sacrifice one of
|
||||
// the PWM output compare registers and thus a PWM channel. I've chosen
|
||||
// to instead limit this timer to a fixed number of frequencies.
|
||||
var prescaler uint8
|
||||
switch config.Period {
|
||||
case 0, (uint64(1e9) * 256 * 1) / uint64(CPUFrequency()):
|
||||
prescaler = 1
|
||||
case (uint64(1e9) * 256 * 8) / uint64(CPUFrequency()):
|
||||
prescaler = 2
|
||||
case (uint64(1e9) * 256 * 64) / uint64(CPUFrequency()):
|
||||
prescaler = 3
|
||||
case (uint64(1e9) * 256 * 256) / uint64(CPUFrequency()):
|
||||
prescaler = 4
|
||||
case (uint64(1e9) * 256 * 1024) / uint64(CPUFrequency()):
|
||||
prescaler = 5
|
||||
default:
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
|
||||
if pwm.num == 0 {
|
||||
avr.TCCR0B.Set(prescaler)
|
||||
// Set the PWM mode to fast PWM (mode = 3).
|
||||
avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01)
|
||||
} else {
|
||||
avr.TCCR2B.Set(prescaler)
|
||||
// Set the PWM mode to fast PWM (mode = 3).
|
||||
avr.TCCR2A.Set(avr.TCCR2A_WGM20 | avr.TCCR2A_WGM21)
|
||||
}
|
||||
case 1: // Timer/counter 1
|
||||
// The top value is the number of PWM ticks a PWM period takes. It is
|
||||
// initially picked assuming an unlimited counter top and no PWM
|
||||
// prescaler.
|
||||
var top uint64
|
||||
if config.Period == 0 {
|
||||
// Use a top appropriate for LEDs. Picking a relatively low period
|
||||
// here (0xff) for consistency with the other timers.
|
||||
top = 0xff
|
||||
} else {
|
||||
// The formula below calculates the following formula, optimized:
|
||||
// top = period * (CPUFrequency() / 1e9)
|
||||
// By dividing the CPU frequency first (an operation that is easily
|
||||
// optimized away) the period has less chance of overflowing.
|
||||
top = config.Period * (uint64(CPUFrequency()) / 1000000) / 1000
|
||||
}
|
||||
|
||||
avr.TCCR1A.Set(avr.TCCR1A_WGM11)
|
||||
|
||||
// The ideal PWM period may be larger than would fit in the PWM counter,
|
||||
// which is 16 bits (see maxTop). Therefore, try to make the PWM clock
|
||||
// speed lower with a prescaler to make the top value fit the maximum
|
||||
// top value.
|
||||
const maxTop = 0x10000
|
||||
switch {
|
||||
case top <= maxTop:
|
||||
avr.TCCR1B.Set(3<<3 | 1) // no prescaling
|
||||
case top/8 <= maxTop:
|
||||
avr.TCCR1B.Set(3<<3 | 2) // divide by 8
|
||||
top /= 8
|
||||
case top/64 <= maxTop:
|
||||
avr.TCCR1B.Set(3<<3 | 3) // divide by 64
|
||||
top /= 64
|
||||
case top/256 <= maxTop:
|
||||
avr.TCCR1B.Set(3<<3 | 4) // divide by 256
|
||||
top /= 256
|
||||
case top/1024 <= maxTop:
|
||||
avr.TCCR1B.Set(3<<3 | 5) // divide by 1024
|
||||
top /= 1024
|
||||
default:
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
|
||||
// A top of 0x10000 is at 100% duty cycle. Subtract one because the
|
||||
// counter counts from 0, not 1 (avoiding an off-by-one).
|
||||
top -= 1
|
||||
|
||||
avr.ICR1H.Set(uint8(top >> 8))
|
||||
avr.ICR1L.Set(uint8(top))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
|
||||
// 8-bit value ranging from 0 to 255.
|
||||
func (pwm PWM) Set(value uint16) {
|
||||
value8 := uint8(value >> 8)
|
||||
switch pwm.Pin {
|
||||
case PD3:
|
||||
// connect pwm to pin on timer 2, channel B
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||
avr.OCR2B.Set(value8) // set pwm duty
|
||||
case PD5:
|
||||
// connect pwm to pin on timer 0, channel B
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||
avr.OCR0B.Set(value8) // set pwm duty
|
||||
case PD6:
|
||||
// connect pwm to pin on timer 0, channel A
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||
avr.OCR0A.Set(value8) // set pwm duty
|
||||
case PB1:
|
||||
// connect pwm to pin on timer 1, channel A
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||
avr.OCR1AL.Set(value8) // set pwm duty
|
||||
case PB2:
|
||||
// connect pwm to pin on timer 1, channel B
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||
avr.OCR1BL.Set(value8) // set pwm duty
|
||||
case PB3:
|
||||
// connect pwm to pin on timer 2, channel A
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||
avr.OCR2A.Set(value8) // set pwm duty
|
||||
default:
|
||||
panic("Invalid PWM pin")
|
||||
// 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 != 1 {
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+383
-258
@@ -8,7 +8,6 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device"
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"errors"
|
||||
@@ -17,8 +16,6 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinAnalog PinMode = 1
|
||||
PinSERCOM PinMode = 2
|
||||
@@ -31,8 +28,8 @@ const (
|
||||
PinInput PinMode = 9
|
||||
PinInputPullup PinMode = 10
|
||||
PinOutput PinMode = 11
|
||||
PinPWM PinMode = PinTimer
|
||||
PinPWMAlt PinMode = PinTimerAlt
|
||||
PinTCC PinMode = PinTimer
|
||||
PinTCCAlt PinMode = PinTimerAlt
|
||||
PinInputPulldown PinMode = 12
|
||||
)
|
||||
|
||||
@@ -433,7 +430,18 @@ func (a ADC) Get() uint16 {
|
||||
sam.ADC.CTRLA.ClearBits(sam.ADC_CTRLA_ENABLE)
|
||||
waitADCSync()
|
||||
|
||||
return uint16(val) << 4 // scales from 12 to 16-bit result
|
||||
// scales 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 {
|
||||
@@ -1290,43 +1298,21 @@ var (
|
||||
// spi.Tx(nil, rx)
|
||||
//
|
||||
func (spi SPI) Tx(w, r []byte) error {
|
||||
if spi.Bus.BAUD.Get() == 0x00 {
|
||||
// When the SPI Freq is 24MHz, special processing is performed to improve the speed.
|
||||
switch {
|
||||
case w == nil:
|
||||
// read only, so write zero and read a result.
|
||||
spi.rx(r)
|
||||
case r == nil:
|
||||
// write only
|
||||
spi.tx(w)
|
||||
|
||||
switch {
|
||||
case w == nil:
|
||||
// read only, so write zero and read a result.
|
||||
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)
|
||||
default:
|
||||
// write/read
|
||||
if len(w) != len(r) {
|
||||
return ErrTxInvalidSliceSize
|
||||
}
|
||||
|
||||
} 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)
|
||||
}
|
||||
spi.txrx(w, r)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -1379,243 +1365,373 @@ func (spi SPI) txrx(tx, rx []byte) {
|
||||
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
|
||||
// tx24mhz is a special tx/rx function for CPU Clock 48 Mhz and SPI Freq 24 Mhz
|
||||
func (spi SPI) tx24mhz(tx []byte) {
|
||||
spi.Bus.DATA.Set(uint32(tx[0]))
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
// TCC is one timer/counter peripheral, which consists of a counter and multiple
|
||||
// output channels (that can be connected to actual pins). You can set the
|
||||
// frequency using SetPeriod, but only for all the channels in this TCC
|
||||
// peripheral at once.
|
||||
type TCC sam.TCC_Type
|
||||
|
||||
for i := 1; i < len(tx); i++ {
|
||||
spi.Bus.DATA.Set(uint32(tx[i]))
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
spi.Bus.DATA.Get()
|
||||
}
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||
}
|
||||
spi.Bus.DATA.Get()
|
||||
// The SAM D21 has three TCC peripherals, which have PWM as one feature.
|
||||
var (
|
||||
TCC0 = (*TCC)(sam.TCC0)
|
||||
TCC1 = (*TCC)(sam.TCC1)
|
||||
TCC2 = (*TCC)(sam.TCC2)
|
||||
)
|
||||
|
||||
//go:inline
|
||||
func (tcc *TCC) timer() *sam.TCC_Type {
|
||||
return (*sam.TCC_Type)(tcc)
|
||||
}
|
||||
|
||||
// txrx24mhz is a special tx/rx function for CPU Clock 48 Mhz and SPI Freq 24 Mhz
|
||||
func (spi SPI) txrx24mhz(tx, rx []byte) {
|
||||
spi.Bus.DATA.Set(uint32(tx[0]))
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
|
||||
for i := 1; i < len(rx); i++ {
|
||||
spi.Bus.DATA.Set(uint32(tx[i]))
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
rx[i-1] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||
}
|
||||
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
|
||||
// PWM
|
||||
const period = 0xFFFF
|
||||
|
||||
// InitPWM initializes the PWM interface.
|
||||
func InitPWM() {
|
||||
// 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) {
|
||||
// Configure enables and configures this TCC.
|
||||
func (tcc *TCC) Configure(config PWMConfig) error {
|
||||
// Enable the clock source for this timer.
|
||||
switch tcc.timer() {
|
||||
case sam.TCC0:
|
||||
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC0_)
|
||||
// Use GCLK0 for TCC0/TCC1
|
||||
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_TCC0_TCC1 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
||||
}
|
||||
case sam.TCC1:
|
||||
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC1_)
|
||||
// Use GCLK0 for TCC0/TCC1
|
||||
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_TCC0_TCC1 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
||||
}
|
||||
case sam.TCC2:
|
||||
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC2_)
|
||||
// Use GCLK0 for TCC2/TC3
|
||||
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_TCC2_TC3 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
||||
}
|
||||
}
|
||||
|
||||
// Use GCLK0 for TCC2/TC3
|
||||
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_TCC2_TC3 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
}
|
||||
// Disable timer (if it was enabled). This is necessary because
|
||||
// tcc.setPeriod may want to change the prescaler bits in CTRLA, which is
|
||||
// only allowed when the TCC is disabled.
|
||||
tcc.timer().CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
|
||||
|
||||
// Use "Normal PWM" (single-slope PWM)
|
||||
timer.WAVE.SetBits(sam.TCC_WAVE_WAVEGEN_NPWM)
|
||||
// Wait for synchronization
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_WAVE) {
|
||||
tcc.timer().WAVE.Set(sam.TCC_WAVE_WAVEGEN_NPWM)
|
||||
|
||||
// Wait for synchronization of all changed registers.
|
||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||
}
|
||||
|
||||
// Set the period (the number to count to (TOP) before resetting timer)
|
||||
//TCC0->PER.reg = period;
|
||||
timer.PER.Set(period)
|
||||
// Wait for synchronization
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_PER) {
|
||||
// Set the period and prescaler.
|
||||
err := tcc.setPeriod(config.Period, true)
|
||||
|
||||
// Enable the timer.
|
||||
tcc.timer().CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
|
||||
|
||||
// Wait for synchronization of all changed registers.
|
||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||
}
|
||||
|
||||
// Set pin as output
|
||||
sam.PORT.DIRSET0.Set(1 << uint8(pwm.Pin))
|
||||
// Set pin to low
|
||||
sam.PORT.OUTCLR0.Set(1 << uint8(pwm.Pin))
|
||||
// Return any error that might have occured in the tcc.setPeriod call.
|
||||
return err
|
||||
}
|
||||
|
||||
// Enable the port multiplexer for pin
|
||||
pwm.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
|
||||
|
||||
// Connect TCCX timer to pin.
|
||||
// we normally use the F channel aka ALT
|
||||
pwmConfig := PinPWMAlt
|
||||
|
||||
// in the case of PA6 or PA7 we have to use E channel
|
||||
if pwm.Pin == 6 || pwm.Pin == 7 {
|
||||
pwmConfig = PinPWM
|
||||
// SetPeriod updates the period of this TCC peripheral.
|
||||
// To set a particular frequency, use the following formula:
|
||||
//
|
||||
// period = 1e9 / frequency
|
||||
//
|
||||
// If you use a period of 0, a period that works well for LEDs will be picked.
|
||||
//
|
||||
// SetPeriod will not change the prescaler, but also won't change the current
|
||||
// value in any of the channels. This means that you may need to update the
|
||||
// value for the particular channel.
|
||||
//
|
||||
// Note that you cannot pick any arbitrary period after the TCC peripheral has
|
||||
// been configured. If you want to switch between frequencies, pick the lowest
|
||||
// frequency (longest period) once when calling Configure and adjust the
|
||||
// frequency here as needed.
|
||||
func (tcc *TCC) SetPeriod(period uint64) error {
|
||||
err := tcc.setPeriod(period, false)
|
||||
if err == nil {
|
||||
if tcc.Counter() >= tcc.Top() {
|
||||
// When setting the timer to a shorter period, there is a chance
|
||||
// that it passes the counter value and thus goes all the way to MAX
|
||||
// before wrapping back to zero.
|
||||
// To avoid this, reset the counter back to 0.
|
||||
tcc.timer().COUNT.Set(0)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if pwm.Pin&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := pwm.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_PMUX0_PMUXO_Pos))
|
||||
// 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 {
|
||||
// even pin, so save the odd pins
|
||||
val := pwm.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_PMUX0_PMUXE_Pos))
|
||||
// The formula below calculates the following formula, optimized:
|
||||
// period * (48e6 / 1e9)
|
||||
// This assumes that the chip is running at the (default) 48MHz speed.
|
||||
top = period * 6 / 125
|
||||
}
|
||||
|
||||
maxTop := uint64(0xffffff)
|
||||
if tcc.timer() == sam.TCC2 {
|
||||
// TCC2 is a 16-bit timer, not a 24-bit timer.
|
||||
maxTop = 0xffff
|
||||
}
|
||||
|
||||
if updatePrescaler {
|
||||
// This function was called during Configure(), with the timer disabled.
|
||||
// Note that updating the prescaler can only happen while the peripheral
|
||||
// is disabled.
|
||||
var prescaler uint32
|
||||
switch {
|
||||
case top <= maxTop:
|
||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV1
|
||||
case top/2 <= maxTop:
|
||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV2
|
||||
top = top / 2
|
||||
case top/4 <= maxTop:
|
||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV4
|
||||
top = top / 4
|
||||
case top/8 <= maxTop:
|
||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV8
|
||||
top = top / 8
|
||||
case top/16 <= maxTop:
|
||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV16
|
||||
top = top / 16
|
||||
case top/64 <= maxTop:
|
||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV64
|
||||
top = top / 64
|
||||
case top/256 <= maxTop:
|
||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV256
|
||||
top = top / 256
|
||||
case top/1024 <= maxTop:
|
||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV1024
|
||||
top = top / 1024
|
||||
default:
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
tcc.timer().CTRLA.Set((tcc.timer().CTRLA.Get() &^ sam.TCC_CTRLA_PRESCALER_Msk) | (prescaler << sam.TCC_CTRLA_PRESCALER_Pos))
|
||||
} else {
|
||||
// Do not update the prescaler, but use the already-configured
|
||||
// prescaler. This is the normal SetPeriod case, where the prescaler
|
||||
// must not be changed.
|
||||
prescaler := (tcc.timer().CTRLA.Get() & sam.TCC_CTRLA_PRESCALER_Msk) >> sam.TCC_CTRLA_PRESCALER_Pos
|
||||
switch prescaler {
|
||||
case sam.TCC_CTRLA_PRESCALER_DIV1:
|
||||
top /= 1 // no-op
|
||||
case sam.TCC_CTRLA_PRESCALER_DIV2:
|
||||
top /= 2
|
||||
case sam.TCC_CTRLA_PRESCALER_DIV4:
|
||||
top /= 4
|
||||
case sam.TCC_CTRLA_PRESCALER_DIV8:
|
||||
top /= 8
|
||||
case sam.TCC_CTRLA_PRESCALER_DIV16:
|
||||
top /= 16
|
||||
case sam.TCC_CTRLA_PRESCALER_DIV64:
|
||||
top /= 64
|
||||
case sam.TCC_CTRLA_PRESCALER_DIV256:
|
||||
top /= 256
|
||||
case sam.TCC_CTRLA_PRESCALER_DIV1024:
|
||||
top /= 1024
|
||||
default:
|
||||
// unreachable
|
||||
}
|
||||
if top > maxTop {
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
}
|
||||
|
||||
// Set the period (the counter top).
|
||||
tcc.timer().PER.Set(uint32(top) - 1)
|
||||
|
||||
// Wait for synchronization of CTRLA.PRESCALER and PER registers.
|
||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set turns on the duty cycle for a PWM pin using the provided value.
|
||||
func (pwm PWM) Set(value uint16) {
|
||||
// figure out which TCCX timer for this pin
|
||||
timer := pwm.getTimer()
|
||||
if timer == nil {
|
||||
// The Configure call above cannot have succeeded, so simply ignore this
|
||||
// error.
|
||||
return
|
||||
// Top returns the current counter top, for use in duty cycle calculation. It
|
||||
// will only change with a call to Configure or SetPeriod, otherwise it is
|
||||
// constant.
|
||||
//
|
||||
// The value returned here is hardware dependent. In general, it's best to treat
|
||||
// it as an opaque value that can be divided by some number and passed to Set
|
||||
// (see Set documentation for more information).
|
||||
func (tcc *TCC) Top() uint32 {
|
||||
return tcc.timer().PER.Get() + 1
|
||||
}
|
||||
|
||||
// Counter returns the current counter value of the timer in this TCC
|
||||
// peripheral. It may be useful for debugging.
|
||||
func (tcc *TCC) Counter() uint32 {
|
||||
tcc.timer().CTRLBSET.Set(sam.TCC_CTRLBSET_CMD_READSYNC << sam.TCC_CTRLBSET_CMD_Pos)
|
||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||
}
|
||||
return tcc.timer().COUNT.Get()
|
||||
}
|
||||
|
||||
// Some constans to make pinTimerMapping below easier to read.
|
||||
const (
|
||||
pinTCC0 = 1
|
||||
pinTCC1 = 2
|
||||
pinTCC2 = 3
|
||||
pinTimerCh0 = 0 << 3
|
||||
pinTimerCh2 = 1 << 3
|
||||
pinTCC0Ch0 = pinTCC0 | pinTimerCh0
|
||||
pinTCC0Ch2 = pinTCC0 | pinTimerCh2
|
||||
pinTCC1Ch0 = pinTCC1 | pinTimerCh0
|
||||
pinTCC1Ch2 = pinTCC1 | pinTimerCh2
|
||||
pinTCC2Ch0 = pinTCC2 | pinTimerCh0
|
||||
)
|
||||
|
||||
// Mapping from pin number to TCC peripheral and channel using a special
|
||||
// encoding. Note that only TCC0-TCC2 are included, not TC3 and up.
|
||||
// Every byte is split in two nibbles where the low nibble describes PinTCC and
|
||||
// the high nibble describes PinTCCAlt. Within a nibble, there is one bit that
|
||||
// indicates Ch0/Ch1 or Ch2/Ch3, and three other bits that contain the TCC
|
||||
// peripheral number plus one (to distinguish between TCC0Ch0 and 0).
|
||||
//
|
||||
// The encoding can be so compact because all pins are configured in pairs, so
|
||||
// if you know PA00 you can infer the configuration of PA01. And only channel 0
|
||||
// or 2 need to be included (taking up just one bit), because channel 0 and 2
|
||||
// are only ever used on odd pins and channel 1 and 3 on even pins, again using
|
||||
// the pin pair pattern to reduce the amount of information needed to be stored.
|
||||
//
|
||||
// Datasheet: https://cdn.sparkfun.com/datasheets/Dev/Arduino/Boards/Atmel-42181-SAM-D21_Datasheet.pdf
|
||||
var pinTimerMapping = [...]uint8{
|
||||
// page 21
|
||||
PA00 / 2: pinTCC2Ch0 | 0,
|
||||
PA04 / 2: pinTCC0Ch0 | 0,
|
||||
PA06 / 2: pinTCC1Ch0 | 0,
|
||||
PA08 / 2: pinTCC0Ch0 | pinTCC1Ch2<<4,
|
||||
PA10 / 2: pinTCC1Ch0 | pinTCC0Ch2<<4,
|
||||
// page 22
|
||||
PB10 / 2: 0 | pinTCC0Ch0<<4,
|
||||
PB12 / 2: 0 | pinTCC0Ch2<<4,
|
||||
PA12 / 2: pinTCC2Ch0 | pinTCC0Ch2<<4,
|
||||
PA14 / 2: 0 | pinTCC0Ch0<<4,
|
||||
PA16 / 2: pinTCC2Ch0 | pinTCC0Ch2<<4,
|
||||
PA18 / 2: 0 | pinTCC0Ch2<<4,
|
||||
PB16 / 2: 0 | pinTCC0Ch0<<4,
|
||||
PA20 / 2: 0 | pinTCC0Ch2<<4,
|
||||
PA22 / 2: 0 | pinTCC0Ch0<<4,
|
||||
PA24 / 2: 0 | pinTCC1Ch2<<4,
|
||||
// page 23
|
||||
PA30 / 2: 0 | pinTCC1Ch0<<4,
|
||||
PB30 / 2: pinTCC0Ch0 | pinTCC1Ch2<<4,
|
||||
}
|
||||
|
||||
// findPinPadMapping returns the pin mode (PinTCC or PinTCCAlt) and the channel
|
||||
// number for a given timer and pin. A zero PinMode is returned if no mapping
|
||||
// could be found.
|
||||
func findPinTimerMapping(timer uint8, pin Pin) (PinMode, uint8) {
|
||||
mapping := pinTimerMapping[pin/2]
|
||||
// evenChannel below indicates the channel 0 or 2, for the even part of the
|
||||
// pin pair. The next pin will also have the next channel (1 or 3).
|
||||
if mapping&0x07 == timer+1 {
|
||||
// PWM output is on peripheral function E.
|
||||
evenChannel := ((mapping >> 3) & 1) * 2
|
||||
return PinTCC, evenChannel + uint8(pin&1)
|
||||
}
|
||||
if (mapping&0x70)>>4 == timer+1 {
|
||||
// PWM output is on peripheral function F.
|
||||
evenChannel := ((mapping >> 7) & 1) * 2
|
||||
return PinTCCAlt, evenChannel + uint8(pin&1)
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// Channel returns a PWM channel for the given pin. Note that one channel may be
|
||||
// shared between multiple pins, and so will have the same duty cycle. If this
|
||||
// is not desirable, look for a different TCC peripheral or consider using a
|
||||
// different pin.
|
||||
func (tcc *TCC) Channel(pin Pin) (uint8, error) {
|
||||
var pinMode PinMode
|
||||
var channel uint8
|
||||
switch tcc.timer() {
|
||||
case sam.TCC0:
|
||||
pinMode, channel = findPinTimerMapping(0, pin)
|
||||
case sam.TCC1:
|
||||
pinMode, channel = findPinTimerMapping(1, pin)
|
||||
case sam.TCC2:
|
||||
pinMode, channel = findPinTimerMapping(2, pin)
|
||||
}
|
||||
|
||||
// disable output
|
||||
timer.CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
|
||||
|
||||
// Wait for synchronization
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
|
||||
if pinMode == 0 {
|
||||
// No pin could be found.
|
||||
return 0, ErrInvalidOutputPin
|
||||
}
|
||||
|
||||
// Enable the port multiplexer for pin
|
||||
pin.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
|
||||
|
||||
if pin&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := pin.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
pin.setPMux(val | uint8(pinMode<<sam.PORT_PMUX0_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := pin.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
pin.setPMux(val | uint8(pinMode<<sam.PORT_PMUX0_PMUXE_Pos))
|
||||
}
|
||||
return channel, nil
|
||||
}
|
||||
|
||||
// SetInverting sets whether to invert the output of this channel.
|
||||
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
|
||||
// the time and low for the rest. Inverting flips the output as if a NOT gate
|
||||
// was placed at the output, meaning that the output would be 25% low and 75%
|
||||
// high with a duty cycle of 25%.
|
||||
func (tcc *TCC) SetInverting(channel uint8, inverting bool) {
|
||||
if inverting {
|
||||
tcc.timer().WAVE.SetBits(1 << (sam.TCC_WAVE_POL0_Pos + channel))
|
||||
} else {
|
||||
tcc.timer().WAVE.ClearBits(1 << (sam.TCC_WAVE_POL0_Pos + channel))
|
||||
}
|
||||
|
||||
// Wait for synchronization of the WAVE register.
|
||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||
}
|
||||
}
|
||||
|
||||
// Set updates the channel value. This is used to control the channel duty
|
||||
// cycle, in other words the fraction of time the channel output is high (or low
|
||||
// when inverted). For example, to set it to a 25% duty cycle, use:
|
||||
//
|
||||
// tcc.Set(channel, tcc.Top() / 4)
|
||||
//
|
||||
// tcc.Set(channel, 0) will set the output to low and tcc.Set(channel,
|
||||
// tcc.Top()) will set the output to high, assuming the output isn't inverted.
|
||||
func (tcc *TCC) Set(channel uint8, value uint32) {
|
||||
// Set PWM signal to output duty cycle
|
||||
pwm.setChannel(timer, uint32(value))
|
||||
|
||||
// Wait for synchronization on all channels
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0 |
|
||||
sam.TCC_SYNCBUSY_CC1 |
|
||||
sam.TCC_SYNCBUSY_CC2 |
|
||||
sam.TCC_SYNCBUSY_CC3) {
|
||||
}
|
||||
|
||||
// enable
|
||||
timer.CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
|
||||
// Wait for synchronization
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
}
|
||||
|
||||
// getPMux returns the value for the correct PMUX register for this pin.
|
||||
func (pwm PWM) getPMux() uint8 {
|
||||
return pwm.Pin.getPMux()
|
||||
}
|
||||
|
||||
// setPMux sets the value for the correct PMUX register for this pin.
|
||||
func (pwm PWM) setPMux(val uint8) {
|
||||
pwm.Pin.setPMux(val)
|
||||
}
|
||||
|
||||
// getPinCfg returns the value for the correct PINCFG register for this pin.
|
||||
func (pwm PWM) getPinCfg() uint8 {
|
||||
return pwm.Pin.getPinCfg()
|
||||
}
|
||||
|
||||
// setPinCfg sets the value for the correct PINCFG register for this pin.
|
||||
func (pwm PWM) setPinCfg(val uint8) {
|
||||
pwm.Pin.setPinCfg(val)
|
||||
}
|
||||
|
||||
// getTimer returns the timer to be used for PWM on this pin
|
||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||
switch pwm.Pin {
|
||||
case 6:
|
||||
return sam.TCC1
|
||||
case 7:
|
||||
return sam.TCC1
|
||||
case 8:
|
||||
return sam.TCC1
|
||||
case 9:
|
||||
return sam.TCC1
|
||||
case 14:
|
||||
return sam.TCC0
|
||||
case 15:
|
||||
return sam.TCC0
|
||||
case 16:
|
||||
return sam.TCC0
|
||||
case 17:
|
||||
return sam.TCC0
|
||||
case 18:
|
||||
return sam.TCC0
|
||||
case 19:
|
||||
return sam.TCC0
|
||||
case 20:
|
||||
return sam.TCC0
|
||||
case 21:
|
||||
return sam.TCC0
|
||||
switch channel {
|
||||
case 0:
|
||||
tcc.timer().CC0.Set(value)
|
||||
case 1:
|
||||
tcc.timer().CC1.Set(value)
|
||||
case 2:
|
||||
tcc.timer().CC2.Set(value)
|
||||
case 3:
|
||||
tcc.timer().CC3.Set(value)
|
||||
default:
|
||||
return nil // not supported on this pin
|
||||
// invalid PWM channel, ignore.
|
||||
}
|
||||
}
|
||||
|
||||
// setChannel sets the value for the correct channel for PWM on this pin
|
||||
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
|
||||
// Wait for synchronization on all channels (or anything in this peripheral,
|
||||
// really).
|
||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2126,7 +2242,7 @@ func handleStandardSetup(setup usbSetup) bool {
|
||||
func cdcSetup(setup usbSetup) bool {
|
||||
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_GET_LINE_CODING {
|
||||
b := make([]byte, 7)
|
||||
var b [cdcLineInfoSize]byte
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
@@ -2135,14 +2251,18 @@ func cdcSetup(setup usbSetup) bool {
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
sendUSBPacket(0, b)
|
||||
sendUSBPacket(0, b[:])
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING {
|
||||
b := receiveUSBControlPacket()
|
||||
b, err := receiveUSBControlPacket()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
usbLineInfo.bCharFormat = b[4]
|
||||
usbLineInfo.bParityType = b[5]
|
||||
@@ -2189,7 +2309,9 @@ 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))
|
||||
}
|
||||
|
||||
func receiveUSBControlPacket() []byte {
|
||||
func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
|
||||
// address
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
|
||||
@@ -2204,7 +2326,7 @@ func receiveUSBControlPacket() []byte {
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return []byte{}
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2213,7 +2335,7 @@ func receiveUSBControlPacket() []byte {
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT0) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return []byte{}
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2221,10 +2343,13 @@ func receiveUSBControlPacket() []byte {
|
||||
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
data := make([]byte, bytesread)
|
||||
copy(data, udd_ep_out_cache_buffer[0][:])
|
||||
if bytesread != cdcLineInfoSize {
|
||||
return b, errUSBCDCBytesRead
|
||||
}
|
||||
|
||||
return data
|
||||
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func handleEndpoint(ep uint32) {
|
||||
|
||||
+393
-240
@@ -1,4 +1,4 @@
|
||||
// +build sam,atsamd51
|
||||
// +build sam,atsamd51 sam,atsame5x
|
||||
|
||||
// Peripheral abstraction layer for the atsamd51.
|
||||
//
|
||||
@@ -20,8 +20,6 @@ func CPUFrequency() uint32 {
|
||||
return 120000000
|
||||
}
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinAnalog PinMode = 1
|
||||
PinSERCOM PinMode = 2
|
||||
@@ -40,10 +38,13 @@ const (
|
||||
PinInput PinMode = 15
|
||||
PinInputPullup PinMode = 16
|
||||
PinOutput PinMode = 17
|
||||
PinPWME PinMode = PinTimer
|
||||
PinPWMF PinMode = PinTimerAlt
|
||||
PinPWMG PinMode = PinTCCPDEC
|
||||
PinTCCE PinMode = PinTimer
|
||||
PinTCCF PinMode = PinTimerAlt
|
||||
PinTCCG PinMode = PinTCCPDEC
|
||||
PinInputPulldown PinMode = 18
|
||||
PinCAN PinMode = 19
|
||||
PinCAN0 PinMode = PinSDHC
|
||||
PinCAN1 PinMode = PinCom
|
||||
)
|
||||
|
||||
type PinChange uint8
|
||||
@@ -627,6 +628,18 @@ func (p Pin) Configure(config PinConfig) {
|
||||
}
|
||||
// enable port config
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -800,6 +813,18 @@ func (a ADC) Configure(config ADCConfig) {
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -848,10 +873,27 @@ func (a ADC) Get() uint16 {
|
||||
for bus.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
return uint16(val) << 4 // scales from 12 to 16-bit result
|
||||
// scales 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 {
|
||||
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) {
|
||||
return sam.ADC1
|
||||
}
|
||||
@@ -863,9 +905,17 @@ func (a ADC) getADCChannel() uint8 {
|
||||
case PA02:
|
||||
return 0
|
||||
case PB08:
|
||||
return 2
|
||||
if a.Bus == AdcBus1 {
|
||||
return 0
|
||||
} else {
|
||||
return 2
|
||||
}
|
||||
case PB09:
|
||||
return 3
|
||||
if a.Bus == AdcBus1 {
|
||||
return 1
|
||||
} else {
|
||||
return 3
|
||||
}
|
||||
case PA04:
|
||||
return 4
|
||||
case PA05:
|
||||
@@ -1570,256 +1620,350 @@ const (
|
||||
QSPI_DATA3 = PA11
|
||||
)
|
||||
|
||||
// PWM
|
||||
const period = 0xFFFF
|
||||
// TCC is one timer peripheral, which consists of a counter and multiple output
|
||||
// channels (that can be connected to actual pins). You can set the frequency
|
||||
// using SetPeriod, but only for all the channels in this timer peripheral at
|
||||
// once.
|
||||
type TCC sam.TCC_Type
|
||||
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() error {
|
||||
// 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))
|
||||
//go:inline
|
||||
func (tcc *TCC) timer() *sam.TCC_Type {
|
||||
return (*sam.TCC_Type)(tcc)
|
||||
}
|
||||
|
||||
// Enable the port multiplexer for pin
|
||||
pwm.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN)
|
||||
// Configure enables and configures this TCC.
|
||||
func (tcc *TCC) Configure(config PWMConfig) error {
|
||||
// Enable the TCC clock to be able to use the TCC.
|
||||
tcc.configureClock()
|
||||
|
||||
// Connect timer/mux to pin.
|
||||
pwmConfig := pwm.getMux()
|
||||
|
||||
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)
|
||||
// Disable timer (if it was enabled). This is necessary because
|
||||
// tcc.setPeriod may want to change the prescaler bits in CTRLA, which is
|
||||
// only allowed when the TCC is disabled.
|
||||
tcc.timer().CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
|
||||
|
||||
// Use "Normal PWM" (single-slope PWM)
|
||||
timer.WAVE.SetBits(sam.TCC_WAVE_WAVEGEN_NPWM)
|
||||
// Wait for synchronization
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_WAVE) {
|
||||
tcc.timer().WAVE.Set(sam.TCC_WAVE_WAVEGEN_NPWM)
|
||||
|
||||
// Wait for synchronization of all changed registers.
|
||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||
}
|
||||
|
||||
// while (TCCx->SYNCBUSY.bit.CC0 || TCCx->SYNCBUSY.bit.CC1);
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
|
||||
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
|
||||
// Set the period and prescaler.
|
||||
err := tcc.setPeriod(config.Period, true)
|
||||
|
||||
// Enable the timer.
|
||||
tcc.timer().CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
|
||||
|
||||
// Wait for synchronization of all changed registers.
|
||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||
}
|
||||
|
||||
// Set the initial value
|
||||
// TCCx->CC[tcChannel].reg = (uint32_t) value;
|
||||
pwm.setChannel(timer, 0)
|
||||
// Return any error that might have occured in the tcc.setPeriod call.
|
||||
return err
|
||||
}
|
||||
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
|
||||
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
|
||||
// SetPeriod updates the period of this TCC peripheral.
|
||||
// To set a particular frequency, use the following formula:
|
||||
//
|
||||
// period = 1e9 / frequency
|
||||
//
|
||||
// If you use a period of 0, a period that works well for LEDs will be picked.
|
||||
//
|
||||
// SetPeriod will not change the prescaler, but also won't change the current
|
||||
// value in any of the channels. This means that you may need to update the
|
||||
// value for the particular channel.
|
||||
//
|
||||
// Note that you cannot pick any arbitrary period after the TCC peripheral has
|
||||
// been configured. If you want to switch between frequencies, pick the lowest
|
||||
// frequency (longest period) once when calling Configure and adjust the
|
||||
// frequency here as needed.
|
||||
func (tcc *TCC) SetPeriod(period uint64) error {
|
||||
return tcc.setPeriod(period, false)
|
||||
}
|
||||
|
||||
// setPeriod sets the period of this TCC, possibly updating the prescaler as
|
||||
// well. The prescaler can only modified when the TCC is disabled, that is, in
|
||||
// the Configure function.
|
||||
func (tcc *TCC) setPeriod(period uint64, updatePrescaler bool) error {
|
||||
var top uint64
|
||||
if period == 0 {
|
||||
// Make sure the TOP value is at 0xffff (enough for a 16-bit timer).
|
||||
top = 0xffff
|
||||
} else {
|
||||
// The formula below calculates the following formula, optimized:
|
||||
// period * (120e6 / 1e9)
|
||||
// This assumes that the chip is running from generic clock generator 0
|
||||
// at 120MHz.
|
||||
top = period * 3 / 25
|
||||
}
|
||||
|
||||
// Set the period (the number to count to (TOP) before resetting timer)
|
||||
//TCC0->PER.reg = period;
|
||||
timer.PER.Set(period)
|
||||
// Wait for synchronization
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_PER) {
|
||||
maxTop := uint64(0xffff)
|
||||
if tcc.timer() == sam.TCC0 || tcc.timer() == sam.TCC1 {
|
||||
// Only TCC0 and TCC1 are 24-bit timers, the rest are 16-bit.
|
||||
maxTop = 0xffffff
|
||||
}
|
||||
|
||||
// enable timer
|
||||
timer.CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
|
||||
// Wait for synchronization
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
|
||||
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
|
||||
}
|
||||
|
||||
// Set turns on the duty cycle for a PWM pin using the provided value.
|
||||
func (pwm PWM) Set(value uint16) {
|
||||
// figure out which TCCX timer for this pin
|
||||
timer := pwm.getTimer()
|
||||
if timer == nil {
|
||||
// The Configure call above cannot have succeeded, so simply ignore this
|
||||
// error.
|
||||
return
|
||||
// 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
|
||||
// tcc.Set (see tcc.Set for more information).
|
||||
func (tcc *TCC) Top() uint32 {
|
||||
return tcc.timer().PER.Get() + 1
|
||||
}
|
||||
|
||||
// Counter returns the current counter value of the timer in this TCC
|
||||
// peripheral. It may be useful for debugging.
|
||||
func (tcc *TCC) Counter() uint32 {
|
||||
tcc.timer().CTRLBSET.Set(sam.TCC_CTRLBSET_CMD_READSYNC << sam.TCC_CTRLBSET_CMD_Pos)
|
||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||
}
|
||||
return tcc.timer().COUNT.Get()
|
||||
}
|
||||
|
||||
// Constants that encode a TCC number and WO number together in a single byte.
|
||||
const (
|
||||
pinTCC0 = 1 << 4 // keep the value 0 usable as "no value"
|
||||
pinTCC1 = 2 << 4
|
||||
pinTCC2 = 3 << 4
|
||||
pinTCC3 = 4 << 4
|
||||
pinTCC4 = 5 << 4
|
||||
pinTCC0_0 = pinTCC0 | 0
|
||||
pinTCC0_1 = pinTCC0 | 1
|
||||
pinTCC0_2 = pinTCC0 | 2
|
||||
pinTCC0_3 = pinTCC0 | 3
|
||||
pinTCC0_4 = pinTCC0 | 4
|
||||
pinTCC0_5 = pinTCC0 | 5
|
||||
pinTCC0_6 = pinTCC0 | 6
|
||||
pinTCC1_0 = pinTCC1 | 0
|
||||
pinTCC1_2 = pinTCC1 | 2
|
||||
pinTCC1_4 = pinTCC1 | 4
|
||||
pinTCC1_6 = pinTCC1 | 6
|
||||
pinTCC2_0 = pinTCC2 | 0
|
||||
pinTCC2_2 = pinTCC2 | 2
|
||||
pinTCC3_0 = pinTCC3 | 0
|
||||
pinTCC4_0 = pinTCC4 | 0
|
||||
)
|
||||
|
||||
// This is a copy of columns F and G (the TCC columns) of table 6-1 in the
|
||||
// datasheet:
|
||||
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507E.pdf
|
||||
// For example, "TCC0/WO[2]" is converted to pinTCC0_2.
|
||||
// Only the even pin numbers are stored here. The odd pin numbers are left out,
|
||||
// because their PWM output can be determined from the even number: just add one
|
||||
// to the wave output (WO) number.
|
||||
var pinTimerMapping = [...]struct{ F, G uint8 }{
|
||||
// page 33
|
||||
PC04 / 2: {pinTCC0_0, 0},
|
||||
PA08 / 2: {pinTCC0_0, pinTCC1_4},
|
||||
PA10 / 2: {pinTCC0_2, pinTCC1_6},
|
||||
PB10 / 2: {pinTCC0_4, pinTCC1_0},
|
||||
PB12 / 2: {pinTCC3_0, pinTCC0_0},
|
||||
PB14 / 2: {pinTCC4_0, pinTCC0_2},
|
||||
PD08 / 2: {pinTCC0_1, 0},
|
||||
PD10 / 2: {pinTCC0_3, 0},
|
||||
PD12 / 2: {pinTCC0_5, 0},
|
||||
PC10 / 2: {pinTCC0_0, pinTCC1_4},
|
||||
// page 34
|
||||
PC12 / 2: {pinTCC0_2, pinTCC1_6},
|
||||
PC14 / 2: {pinTCC0_4, pinTCC1_0},
|
||||
PA12 / 2: {pinTCC0_6, pinTCC1_2},
|
||||
PA14 / 2: {pinTCC2_0, pinTCC1_2},
|
||||
PA16 / 2: {pinTCC1_0, pinTCC0_4},
|
||||
PA18 / 2: {pinTCC1_2, pinTCC0_6},
|
||||
PC16 / 2: {pinTCC0_0, 0},
|
||||
PC18 / 2: {pinTCC0_2, 0},
|
||||
PC20 / 2: {pinTCC0_4, 0},
|
||||
PC22 / 2: {pinTCC0_6, 0},
|
||||
PD20 / 2: {pinTCC1_0, 0},
|
||||
PB16 / 2: {pinTCC3_0, pinTCC0_4},
|
||||
PB18 / 2: {pinTCC1_0, 0},
|
||||
// page 35
|
||||
PB20 / 2: {pinTCC1_2, 0},
|
||||
PA20 / 2: {pinTCC1_4, pinTCC0_0},
|
||||
PA22 / 2: {pinTCC1_6, pinTCC0_2},
|
||||
PA24 / 2: {pinTCC2_2, 0},
|
||||
PB26 / 2: {pinTCC1_2, 0},
|
||||
PB28 / 2: {pinTCC1_4, 0},
|
||||
PA30 / 2: {pinTCC2_0, 0},
|
||||
// page 36
|
||||
PB30 / 2: {pinTCC4_0, pinTCC0_6},
|
||||
PB02 / 2: {pinTCC2_2, 0},
|
||||
}
|
||||
|
||||
// findPinPadMapping returns the pin mode (PinTCCF or PinTCCG) and the channel
|
||||
// number for a given timer and pin. A zero PinMode is returned if no mapping
|
||||
// could be found.
|
||||
func findPinTimerMapping(timer uint8, pin Pin) (PinMode, uint8) {
|
||||
if int(pin/2) >= len(pinTimerMapping) {
|
||||
return 0, 0 // invalid pin number
|
||||
}
|
||||
|
||||
// Wait for synchronization
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CTRLB) {
|
||||
}
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
|
||||
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
|
||||
mapping := pinTimerMapping[pin/2]
|
||||
|
||||
// Check for column F in the datasheet.
|
||||
if mapping.F>>4-1 == timer {
|
||||
return PinTCCF, mapping.F&0x0f + uint8(pin)&1
|
||||
}
|
||||
|
||||
// TCCx->CCBUF[tcChannel].reg = (uint32_t) value;
|
||||
pwm.setChannelBuffer(timer, uint32(value))
|
||||
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
|
||||
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
|
||||
// Check for column G in the datasheet.
|
||||
if mapping.G>>4-1 == timer {
|
||||
return PinTCCG, mapping.G&0x0f + uint8(pin)&1
|
||||
}
|
||||
|
||||
// TCCx->CTRLBCLR.bit.LUPD = 1;
|
||||
timer.CTRLBCLR.SetBits(sam.TCC_CTRLBCLR_LUPD)
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CTRLB) {
|
||||
// Nothing found.
|
||||
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 or consider using a different pin.
|
||||
func (tcc *TCC) Channel(pin Pin) (uint8, error) {
|
||||
pinMode, woOutput := findPinTimerMapping(tcc.timerNum(), pin)
|
||||
|
||||
if pinMode == 0 {
|
||||
// No pin could be found.
|
||||
return 0, ErrInvalidOutputPin
|
||||
}
|
||||
|
||||
// Convert from waveform output to channel, assuming WEXCTRL.OTMX equals 0.
|
||||
// See table 49-4 "Output Matrix Channel Pin Routing Configuration" on page
|
||||
// 1829 of the datasheet.
|
||||
// The number of channels varies by TCC instance, hence the need to switch
|
||||
// over them. For TCC2-4 the number of channels is equal to the number of
|
||||
// waveform outputs, so the WO number maps directly to the channel number.
|
||||
// For TCC0 and TCC1 this is not the case so they will need some special
|
||||
// handling.
|
||||
channel := woOutput
|
||||
switch tcc.timer() {
|
||||
case sam.TCC0:
|
||||
channel = woOutput % 6
|
||||
case sam.TCC1:
|
||||
channel = woOutput % 4
|
||||
}
|
||||
|
||||
// Enable the port multiplexer for pin
|
||||
pin.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN)
|
||||
|
||||
// Connect timer/mux to pin.
|
||||
if pin&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := pin.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
|
||||
pin.setPMux(val | uint8(pinMode<<sam.PORT_GROUP_PMUX_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := pin.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
|
||||
pin.setPMux(val | uint8(pinMode<<sam.PORT_GROUP_PMUX_PMUXE_Pos))
|
||||
}
|
||||
|
||||
return channel, nil
|
||||
}
|
||||
|
||||
// SetInverting sets whether to invert the output of this channel.
|
||||
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
|
||||
// the time and low for the rest. Inverting flips the output as if a NOT gate
|
||||
// was placed at the output, meaning that the output would be 25% low and 75%
|
||||
// high with a duty cycle of 25%.
|
||||
func (tcc *TCC) SetInverting(channel uint8, inverting bool) {
|
||||
if inverting {
|
||||
tcc.timer().WAVE.SetBits(1 << (sam.TCC_WAVE_POL0_Pos + channel))
|
||||
} else {
|
||||
tcc.timer().WAVE.ClearBits(1 << (sam.TCC_WAVE_POL0_Pos + channel))
|
||||
}
|
||||
|
||||
// Wait for synchronization of the WAVE register.
|
||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||
}
|
||||
}
|
||||
|
||||
// getPMux returns the value for the correct PMUX register for this pin.
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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) {
|
||||
// Update CCBUF, which provides double buffering. The update is applied on
|
||||
// the next cycle.
|
||||
tcc.timer().CCBUF[channel].Set(value)
|
||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2332,7 +2476,7 @@ func handleStandardSetup(setup usbSetup) bool {
|
||||
func cdcSetup(setup usbSetup) bool {
|
||||
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_GET_LINE_CODING {
|
||||
b := make([]byte, 7)
|
||||
var b [cdcLineInfoSize]byte
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
@@ -2341,14 +2485,18 @@ func cdcSetup(setup usbSetup) bool {
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
sendUSBPacket(0, b)
|
||||
sendUSBPacket(0, b[:])
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING {
|
||||
b := receiveUSBControlPacket()
|
||||
b, err := receiveUSBControlPacket()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
usbLineInfo.bCharFormat = b[4]
|
||||
usbLineInfo.bParityType = b[5]
|
||||
@@ -2395,7 +2543,9 @@ 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))
|
||||
}
|
||||
|
||||
func receiveUSBControlPacket() []byte {
|
||||
func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
|
||||
// address
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
|
||||
@@ -2410,7 +2560,7 @@ func receiveUSBControlPacket() []byte {
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_ENDPOINT_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return []byte{}
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2419,7 +2569,7 @@ func receiveUSBControlPacket() []byte {
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return []byte{}
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2427,10 +2577,13 @@ func receiveUSBControlPacket() []byte {
|
||||
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
data := make([]byte, bytesread)
|
||||
copy(data, udd_ep_out_cache_buffer[0][:])
|
||||
if bytesread != cdcLineInfoSize {
|
||||
return b, errUSBCDCBytesRead
|
||||
}
|
||||
|
||||
return data
|
||||
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func handleEndpoint(ep uint32) {
|
||||
|
||||
@@ -11,43 +11,37 @@ import "device/sam"
|
||||
|
||||
const HSRAM_SIZE = 0x00030000
|
||||
|
||||
// InitPWM initializes the PWM interface.
|
||||
func InitPWM() {
|
||||
// turn on timer clocks used for PWM
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||
// This chip has three TCC peripherals, which have PWM as one feature.
|
||||
var (
|
||||
TCC0 = (*TCC)(sam.TCC0)
|
||||
TCC1 = (*TCC)(sam.TCC1)
|
||||
TCC2 = (*TCC)(sam.TCC2)
|
||||
)
|
||||
|
||||
//use clock generator 0
|
||||
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_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
|
||||
// getTimer returns the timer to be used for PWM on this pin
|
||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||
switch pwm.Pin {
|
||||
case PA14:
|
||||
return sam.TCC2
|
||||
case PA15:
|
||||
return sam.TCC2
|
||||
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:
|
||||
return nil // not supported on this pin
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func (tcc *TCC) timerNum() uint8 {
|
||||
switch tcc.timer() {
|
||||
case sam.TCC0:
|
||||
return 0
|
||||
case sam.TCC1:
|
||||
return 1
|
||||
case sam.TCC2:
|
||||
return 2
|
||||
default:
|
||||
return 0x0f // should not happen
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,60 +11,49 @@ import "device/sam"
|
||||
|
||||
const HSRAM_SIZE = 0x00030000
|
||||
|
||||
// InitPWM initializes the PWM interface.
|
||||
func InitPWM() {
|
||||
// turn on timer clocks used for PWM
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||
// 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)
|
||||
)
|
||||
|
||||
//use clock generator 0
|
||||
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_TCC2].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) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
|
||||
// getTimer returns the timer to be used for PWM on this pin
|
||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||
switch pwm.Pin {
|
||||
case PA14:
|
||||
return sam.TCC2
|
||||
case PA15:
|
||||
return sam.TCC2
|
||||
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
|
||||
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:
|
||||
return nil // not supported on this pin
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,60 +11,49 @@ import "device/sam"
|
||||
|
||||
const HSRAM_SIZE = 0x00040000
|
||||
|
||||
// InitPWM initializes the PWM interface.
|
||||
func InitPWM() {
|
||||
// turn on timer clocks used for PWM
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||
// 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)
|
||||
)
|
||||
|
||||
//use clock generator 0
|
||||
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_TCC2].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) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
|
||||
// getTimer returns the timer to be used for PWM on this pin
|
||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||
switch pwm.Pin {
|
||||
case PA14:
|
||||
return sam.TCC2
|
||||
case PA15:
|
||||
return sam.TCC2
|
||||
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
|
||||
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:
|
||||
return nil // not supported on this pin
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,60 +11,49 @@ import "device/sam"
|
||||
|
||||
const HSRAM_SIZE = 0x00030000
|
||||
|
||||
// InitPWM initializes the PWM interface.
|
||||
func InitPWM() {
|
||||
// turn on timer clocks used for PWM
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||
// 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)
|
||||
)
|
||||
|
||||
//use clock generator 0
|
||||
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_TCC2].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) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
|
||||
// getTimer returns the timer to be used for PWM on this pin
|
||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||
switch pwm.Pin {
|
||||
case PA14:
|
||||
return sam.TCC2
|
||||
case PA15:
|
||||
return sam.TCC2
|
||||
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
|
||||
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:
|
||||
return nil // not supported on this pin
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,60 +11,49 @@ import "device/sam"
|
||||
|
||||
const HSRAM_SIZE = 0x00040000
|
||||
|
||||
// InitPWM initializes the PWM interface.
|
||||
func InitPWM() {
|
||||
// turn on timer clocks used for PWM
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||
// 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)
|
||||
)
|
||||
|
||||
//use clock generator 0
|
||||
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_TCC2].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) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
|
||||
// getTimer returns the timer to be used for PWM on this pin
|
||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||
switch pwm.Pin {
|
||||
case PC18:
|
||||
return sam.TCC0
|
||||
case PC19:
|
||||
return sam.TCC0
|
||||
case PC20:
|
||||
return sam.TCC0
|
||||
case PC21:
|
||||
return sam.TCC0
|
||||
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:
|
||||
return nil // not supported on this pin
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// +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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// +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
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinInput PinMode = iota
|
||||
PinInputPullup
|
||||
|
||||
@@ -21,8 +21,6 @@ var (
|
||||
ErrInvalidSPIBus = errors.New("machine: invalid SPI bus")
|
||||
)
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinOutput PinMode = iota
|
||||
PinInput
|
||||
|
||||
@@ -11,8 +11,6 @@ func CPUFrequency() uint32 {
|
||||
return 80000000 // 80MHz
|
||||
}
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinOutput PinMode = iota
|
||||
PinInput
|
||||
|
||||
@@ -12,8 +12,6 @@ func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinInput PinMode = iota
|
||||
PinOutput
|
||||
|
||||
@@ -30,8 +30,6 @@ const (
|
||||
// Make it easier to directly write to I/O RAM.
|
||||
var ioram = (*[0x400]volatile.Register8)(unsafe.Pointer(uintptr(0x04000000)))
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
// Set has not been implemented.
|
||||
func (p Pin) Set(value bool) {
|
||||
// do nothing
|
||||
@@ -54,7 +52,7 @@ func (d FramebufDisplay) Size() (x, y int16) {
|
||||
}
|
||||
|
||||
func (d FramebufDisplay) SetPixel(x, y int16, c color.RGBA) {
|
||||
d.port[y][x].Set(uint16(c.R)&0x1f | uint16(c.G)&0x1f<<5 | uint16(c.B)&0x1f<<10)
|
||||
d.port[y][x].Set((uint16(c.R) >> 3) | ((uint16(c.G) >> 3) << 5) | ((uint16(c.B) >> 3) << 10))
|
||||
}
|
||||
|
||||
func (d FramebufDisplay) Display() error {
|
||||
|
||||
@@ -10,8 +10,6 @@ var (
|
||||
UART0 = UART{0}
|
||||
)
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinInput PinMode = iota
|
||||
PinOutput
|
||||
@@ -84,24 +82,6 @@ func (adc ADC) Get() uint16 {
|
||||
//export __tinygo_adc_read
|
||||
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.
|
||||
type I2C struct {
|
||||
Bus uint8
|
||||
|
||||
@@ -14,7 +14,6 @@ func CPUFrequency() uint32 {
|
||||
return 390000000
|
||||
}
|
||||
|
||||
type PinMode uint8
|
||||
type fpioaPullMode uint8
|
||||
type PinChange uint8
|
||||
|
||||
|
||||
@@ -15,8 +15,6 @@ func CPUFrequency() uint32 {
|
||||
return 600000000
|
||||
}
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
// GPIO
|
||||
PinInput PinMode = iota
|
||||
|
||||
@@ -13,8 +13,6 @@ var (
|
||||
ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size")
|
||||
)
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
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)
|
||||
PinInputPullup PinMode = PinInput | (nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos)
|
||||
|
||||
@@ -63,7 +63,7 @@ func (i2c *I2C) setPins(scl, sda Pin) {
|
||||
|
||||
// PWM
|
||||
var (
|
||||
pwmChannelPins = [3]uint32{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}
|
||||
pwms = [3]*nrf.PWM_Type{nrf.PWM0, nrf.PWM1, nrf.PWM2}
|
||||
pwmChannelSequence [3]uint16
|
||||
PWM0 = &PWM{PWM: nrf.PWM0}
|
||||
PWM1 = &PWM{PWM: nrf.PWM1}
|
||||
PWM2 = &PWM{PWM: nrf.PWM2}
|
||||
)
|
||||
|
||||
@@ -83,7 +83,8 @@ func (i2c *I2C) setPins(scl, sda Pin) {
|
||||
|
||||
// PWM
|
||||
var (
|
||||
pwmChannelPins = [4]uint32{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}
|
||||
pwms = [4]*nrf.PWM_Type{nrf.PWM0, nrf.PWM1, nrf.PWM2, nrf.PWM3}
|
||||
pwmChannelSequence [4]uint16
|
||||
PWM0 = &PWM{PWM: nrf.PWM0}
|
||||
PWM1 = &PWM{PWM: nrf.PWM1}
|
||||
PWM2 = &PWM{PWM: nrf.PWM2}
|
||||
PWM3 = &PWM{PWM: nrf.PWM3}
|
||||
)
|
||||
|
||||
@@ -79,7 +79,8 @@ func (i2c *I2C) setPins(scl, sda Pin) {
|
||||
|
||||
// PWM
|
||||
var (
|
||||
pwmChannelPins = [4]uint32{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}
|
||||
pwms = [4]*nrf.PWM_Type{nrf.PWM0, nrf.PWM1, nrf.PWM2, nrf.PWM3}
|
||||
pwmChannelSequence [4]uint16
|
||||
PWM0 = &PWM{PWM: nrf.PWM0}
|
||||
PWM1 = &PWM{PWM: nrf.PWM1}
|
||||
PWM2 = &PWM{PWM: nrf.PWM2}
|
||||
PWM3 = &PWM{PWM: nrf.PWM3}
|
||||
)
|
||||
|
||||
@@ -445,7 +445,7 @@ func handleStandardSetup(setup usbSetup) bool {
|
||||
func cdcSetup(setup usbSetup) bool {
|
||||
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_GET_LINE_CODING {
|
||||
b := make([]byte, 7)
|
||||
var b [cdcLineInfoSize]byte
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
@@ -454,7 +454,7 @@ func cdcSetup(setup usbSetup) bool {
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
sendUSBPacket(0, b)
|
||||
sendUSBPacket(0, b[:])
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user