mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-27 23:28:42 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 13c0714fda |
+27
-46
@@ -68,17 +68,14 @@ commands:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-11-v2
|
||||
- llvm-source-11-v1
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-11-v2
|
||||
key: llvm-source-11-v1
|
||||
paths:
|
||||
- llvm-project/clang/lib/Headers
|
||||
- llvm-project/clang/include
|
||||
- llvm-project/lld/include
|
||||
- llvm-project/llvm/include
|
||||
- llvm-project
|
||||
build-wasi-libc:
|
||||
steps:
|
||||
- restore_cache:
|
||||
@@ -119,7 +116,7 @@ commands:
|
||||
- lib/wasi-libc/sysroot
|
||||
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||
- run: make gen-device -j4
|
||||
- run: make smoketest XTENSA=0 RUN_SMOKETEST_JOBS=4
|
||||
- run: make smoketest XTENSA=0
|
||||
- run: make tinygo-test
|
||||
- run: make wasmtest
|
||||
- save_cache:
|
||||
@@ -157,15 +154,12 @@ commands:
|
||||
- llvm-source-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-linux-v3-assert
|
||||
- llvm-build-11-linux-v2-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
|
||||
@@ -173,29 +167,27 @@ 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-v3-assert
|
||||
key: llvm-build-11-linux-v2-assert
|
||||
paths:
|
||||
llvm-build
|
||||
- run: make ASSERT=1
|
||||
- run: |
|
||||
# Note: -p=2 limits parallelism to two jobs at a time, which is
|
||||
# necessary to keep memory consumption down and avoid OOM (for a
|
||||
# 2CPU/4GB executor).
|
||||
GOFLAGS="-p=2" make ASSERT=1
|
||||
- build-wasi-libc
|
||||
- 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:
|
||||
- ~/.cache/go-build
|
||||
- /go/pkg/mod
|
||||
- run: make gen-device -j4
|
||||
- run: make smoketest TINYGO=build/tinygo RUN_SMOKETEST_JOBS=4
|
||||
- run: make smoketest TINYGO=build/tinygo
|
||||
build-linux:
|
||||
steps:
|
||||
- checkout
|
||||
@@ -225,15 +217,12 @@ commands:
|
||||
- llvm-source-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-linux-v3-noassert
|
||||
- llvm-build-11-linux-v2-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
|
||||
@@ -241,10 +230,9 @@ 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-v3-noassert
|
||||
key: llvm-build-11-linux-v2-noassert
|
||||
paths:
|
||||
llvm-build
|
||||
- build-wasi-libc
|
||||
@@ -278,7 +266,7 @@ commands:
|
||||
tar -C ~/lib -xf /tmp/tinygo.linux-amd64.tar.gz
|
||||
ln -s ~/lib/tinygo/bin/tinygo /go/bin/tinygo
|
||||
tinygo version
|
||||
- run: make smoketest RUN_SMOKETEST_JOBS=4
|
||||
- run: make smoketest
|
||||
build-macos:
|
||||
steps:
|
||||
- checkout
|
||||
@@ -294,50 +282,43 @@ commands:
|
||||
variant: "macos"
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-macos-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-macos-v3-{{ checksum "go.mod" }}
|
||||
- go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-macos-v2-{{ checksum "go.mod" }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-11-macos-v3
|
||||
- llvm-source-11-macos-v1
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-11-macos-v3
|
||||
key: llvm-source-11-macos-v1
|
||||
paths:
|
||||
- llvm-project/clang/lib/Headers
|
||||
- llvm-project/clang/include
|
||||
- llvm-project/lld/include
|
||||
- llvm-project/llvm/include
|
||||
- llvm-project
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-macos-v4
|
||||
- llvm-build-11-macos-v2
|
||||
- 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-v4
|
||||
key: llvm-build-11-macos-v2
|
||||
paths:
|
||||
llvm-build
|
||||
- restore_cache:
|
||||
keys:
|
||||
- wasi-libc-sysroot-macos-v4
|
||||
- wasi-libc-sysroot-macos-v3
|
||||
- run:
|
||||
name: "Build wasi-libc"
|
||||
command: make wasi-libc
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-macos-v4
|
||||
key: wasi-libc-sysroot-macos-v3
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
- run:
|
||||
@@ -357,9 +338,9 @@ commands:
|
||||
tar -C /usr/local/opt -xf /tmp/tinygo.darwin-amd64.tar.gz
|
||||
ln -s /usr/local/opt/tinygo/bin/tinygo /usr/local/bin/tinygo
|
||||
tinygo version
|
||||
- run: make smoketest AVR=0 RUN_SMOKETEST_JOBS=4
|
||||
- run: make smoketest AVR=0
|
||||
- save_cache:
|
||||
key: go-cache-macos-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
key: go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
- ~/.cache/go-build
|
||||
- /go/pkg/mod
|
||||
@@ -401,7 +382,7 @@ jobs:
|
||||
- build-linux
|
||||
build-macos:
|
||||
macos:
|
||||
xcode: "11.1.0" # macOS 10.14
|
||||
xcode: "10.1.0"
|
||||
steps:
|
||||
- build-macos
|
||||
|
||||
|
||||
@@ -152,9 +152,9 @@ gen-device-stm32: build/gen-device-svd
|
||||
|
||||
|
||||
# Get LLVM sources.
|
||||
$(LLVM_PROJECTDIR)/llvm:
|
||||
$(LLVM_PROJECTDIR)/README.md:
|
||||
git clone -b xtensa_release_11.0.0 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
||||
llvm-source: $(LLVM_PROJECTDIR)/llvm
|
||||
llvm-source: $(LLVM_PROJECTDIR)/README.md
|
||||
|
||||
# Configure LLVM.
|
||||
TINYGO_SOURCE_DIR=$(shell pwd)
|
||||
@@ -202,11 +202,8 @@ tinygo-test:
|
||||
$(TINYGO) test text/scanner
|
||||
$(TINYGO) test unicode/utf8
|
||||
|
||||
.PHONY: smoketest smoketest-commands
|
||||
.PHONY: smoketest
|
||||
smoketest:
|
||||
@go run ./src/cmd/run-smoketest make smoketest-commands
|
||||
|
||||
smoketest-commands:
|
||||
$(TINYGO) version
|
||||
# test all examples (except pwm)
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1
|
||||
@@ -229,6 +226,8 @@ smoketest-commands:
|
||||
@$(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
|
||||
@@ -257,10 +256,6 @@ smoketest-commands:
|
||||
@$(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
|
||||
@@ -293,8 +288,6 @@ smoketest-commands:
|
||||
@$(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
|
||||
@@ -341,10 +334,6 @@ smoketest-commands:
|
||||
@$(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
|
||||
@@ -352,6 +341,8 @@ smoketest-commands:
|
||||
@$(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
|
||||
@@ -363,8 +354,6 @@ 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
|
||||
@@ -385,10 +374,6 @@ 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,14 +43,13 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
||||
|
||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||
|
||||
The following 57 microcontroller boards are currently supported:
|
||||
The following 55 microcontroller boards are currently supported:
|
||||
|
||||
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
|
||||
* [Adafruit Circuit Playground 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)
|
||||
@@ -78,7 +77,6 @@ The following 57 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)
|
||||
|
||||
+34
-162
@@ -52,17 +52,14 @@ 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
|
||||
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)
|
||||
ImportPath string
|
||||
CompilerVersion int // compiler.Version
|
||||
InterpVersion int // interp.Version
|
||||
LLVMVersion string
|
||||
Config *compiler.Config
|
||||
CFlags []string
|
||||
FileHashes map[string]string // hash of every file that's part of the package
|
||||
Imports map[string]string // map from imported package to action ID hash
|
||||
}
|
||||
|
||||
// Build performs a single package to executable Go build. It takes in a package
|
||||
@@ -130,30 +127,20 @@ 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())),
|
||||
OptLevel: optLevel,
|
||||
SizeLevel: sizeLevel,
|
||||
UndefinedGlobals: undefinedGlobals,
|
||||
ImportPath: pkg.ImportPath,
|
||||
CompilerVersion: compiler.Version,
|
||||
InterpVersion: interp.Version,
|
||||
LLVMVersion: llvm.Version,
|
||||
Config: compilerConfig,
|
||||
CFlags: pkg.CFlags,
|
||||
FileHashes: make(map[string]string, len(pkg.FileHashes)),
|
||||
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
||||
}
|
||||
for filePath, hash := range pkg.FileHashes {
|
||||
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
|
||||
@@ -204,25 +191,6 @@ 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.
|
||||
@@ -238,38 +206,6 @@ 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
|
||||
@@ -300,13 +236,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename may fail if another process is trying to write to
|
||||
// the same file. However, in this case, the failure is
|
||||
// acceptable because the result of the other process can be
|
||||
// used.
|
||||
os.Rename(f.Name(), bitcodePath)
|
||||
return nil
|
||||
return os.Rename(f.Name(), bitcodePath)
|
||||
},
|
||||
}
|
||||
jobs = append(jobs, job)
|
||||
@@ -500,7 +430,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.Options.PrintCommands)
|
||||
result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config)
|
||||
job.result = result
|
||||
return err
|
||||
},
|
||||
@@ -519,7 +449,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.Options.PrintCommands)
|
||||
result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config)
|
||||
job.result = result
|
||||
return err
|
||||
},
|
||||
@@ -673,12 +603,6 @@ 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
|
||||
@@ -693,8 +617,21 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||
|
||||
// Optimization levels here are roughly the same as Clang, but probably not
|
||||
// exactly.
|
||||
optLevel, sizeLevel, inlinerThreshold := config.OptLevels()
|
||||
errs := transform.Optimize(mod, config, optLevel, sizeLevel, inlinerThreshold)
|
||||
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)
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return newMultiError(errs)
|
||||
}
|
||||
@@ -716,71 +653,6 @@ 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 {
|
||||
|
||||
@@ -84,7 +84,7 @@ func copyFile(src, dst string) error {
|
||||
return err
|
||||
}
|
||||
defer inf.Close()
|
||||
outpath := src + ".tmp"
|
||||
outpath := dst + ".tmp"
|
||||
outf, err := os.Create(outpath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -101,10 +101,5 @@ func copyFile(src, dst string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename may fail if another process is trying to write to
|
||||
// the same file. However, in this case, the failure is
|
||||
// acceptable because the result of the other process can be
|
||||
// used.
|
||||
os.Rename(outpath, dst)
|
||||
return nil
|
||||
return os.Rename(dst+".tmp", dst)
|
||||
}
|
||||
|
||||
+12
-13
@@ -17,6 +17,7 @@ import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
@@ -56,7 +57,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, printCommands bool) (string, error) {
|
||||
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, config *compileopts.Config) (string, error) {
|
||||
// Hash input file.
|
||||
fileHash, err := hashFile(abspath)
|
||||
if err != nil {
|
||||
@@ -67,12 +68,14 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
||||
buf, err := json.Marshal(struct {
|
||||
Path string
|
||||
Hash string
|
||||
Compiler string
|
||||
Flags []string
|
||||
LLVMVersion string
|
||||
}{
|
||||
Path: abspath,
|
||||
Hash: fileHash,
|
||||
Flags: cflags,
|
||||
Compiler: config.Target.Compiler,
|
||||
Flags: config.CFlags(),
|
||||
LLVMVersion: llvm.Version,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -121,17 +124,10 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
||||
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 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")
|
||||
if config.Options.PrintCommands {
|
||||
fmt.Printf("%s %s\n", config.Target.Compiler, strings.Join(flags, " "))
|
||||
}
|
||||
if printCommands {
|
||||
fmt.Printf("clang %s\n", strings.Join(flags, " "))
|
||||
}
|
||||
err = runCCompiler(flags...)
|
||||
err = runCCompiler(config.Target.Compiler, flags...)
|
||||
if err != nil {
|
||||
return "", &commandError{"failed to build", abspath, err}
|
||||
}
|
||||
@@ -167,7 +163,10 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
os.Rename(f.Name(), depfileCachePath)
|
||||
err = os.Rename(f.Name(), depfileCachePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Move temporary object file to final location.
|
||||
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash)
|
||||
|
||||
+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(compileArgs...)
|
||||
err := runCCompiler("clang", compileArgs...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", srcpath, err}
|
||||
}
|
||||
|
||||
+13
-4
@@ -9,8 +9,8 @@ import (
|
||||
)
|
||||
|
||||
// runCCompiler invokes a C compiler with the given arguments.
|
||||
func runCCompiler(flags ...string) error {
|
||||
if hasBuiltinTools {
|
||||
func runCCompiler(command string, flags ...string) error {
|
||||
if hasBuiltinTools && command == "clang" {
|
||||
// Compile this with the internal Clang compiler.
|
||||
headerPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
if headerPath == "" {
|
||||
@@ -23,8 +23,17 @@ func runCCompiler(flags ...string) error {
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// Compile this with an external invocation of the Clang compiler.
|
||||
return execCommand(commands["clang"], flags...)
|
||||
// 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()
|
||||
}
|
||||
|
||||
// link invokes a linker with the given name and flags.
|
||||
|
||||
+2
-23
@@ -118,27 +118,6 @@ 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 {
|
||||
@@ -184,7 +163,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 {
|
||||
var cflags []string
|
||||
cflags := append([]string{}, c.Options.CFlags...)
|
||||
for _, flag := range c.Target.CFlags {
|
||||
cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT")))
|
||||
}
|
||||
@@ -205,7 +184,7 @@ func (c *Config) CFlags() []string {
|
||||
func (c *Config) LDFlags() []string {
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
// Merge and adjust LDFlags.
|
||||
var ldflags []string
|
||||
ldflags := append([]string{}, c.Options.LDFlags...)
|
||||
for _, flag := range c.Target.LDFlags {
|
||||
ldflags = append(ldflags, strings.ReplaceAll(flag, "{root}", root))
|
||||
}
|
||||
|
||||
+2
-10
@@ -2,7 +2,6 @@ package compileopts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -11,7 +10,6 @@ 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
|
||||
@@ -28,11 +26,11 @@ type Options struct {
|
||||
PrintCommands bool
|
||||
Debug bool
|
||||
PrintSizes string
|
||||
PrintAllocs *regexp.Regexp // regexp string
|
||||
PrintStacks bool
|
||||
CFlags []string
|
||||
LDFlags []string
|
||||
Tags string
|
||||
WasmAbi string
|
||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
}
|
||||
@@ -75,12 +73,6 @@ func (o *Options) Verify() error {
|
||||
}
|
||||
}
|
||||
|
||||
if o.Opt != "" {
|
||||
if !isInArray(validOptOptions, o.Opt) {
|
||||
return fmt.Errorf("invalid -opt=%s: valid values are %s", o.Opt, strings.Join(validOptOptions, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ 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"`
|
||||
@@ -243,13 +244,13 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
GOOS: goos,
|
||||
GOARCH: goarch,
|
||||
BuildTags: []string{goos, goarch},
|
||||
Compiler: "clang",
|
||||
Linker: "cc",
|
||||
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
|
||||
|
||||
+11
-65
@@ -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 = 9 // last change: implement reflect.New()
|
||||
const Version = 6 // last change: fix issue 1304
|
||||
|
||||
func init() {
|
||||
llvm.InitializeAllTargets()
|
||||
@@ -298,7 +298,7 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
||||
c.dibuilder.Finalize()
|
||||
}
|
||||
|
||||
return c.mod, c.diagnostics
|
||||
return c.mod, nil
|
||||
}
|
||||
|
||||
// getLLVMRuntimeType obtains a named type from the runtime package and returns
|
||||
@@ -964,59 +964,11 @@ func (b *builder) createFunction() {
|
||||
}
|
||||
}
|
||||
|
||||
// posser is an interface that's implemented by both ssa.Value and
|
||||
// ssa.Instruction. It is implemented by everything that has a Pos() method,
|
||||
// which is all that getPos() needs.
|
||||
type posser interface {
|
||||
Pos() token.Pos
|
||||
}
|
||||
|
||||
// getPos returns position information for a ssa.Value or ssa.Instruction.
|
||||
//
|
||||
// Not all instructions have position information, especially when they're
|
||||
// implicit (such as implicit casts or implicit returns at the end of a
|
||||
// function). In these cases, it makes sense to try a bit harder to guess what
|
||||
// the position really should be.
|
||||
func getPos(val posser) token.Pos {
|
||||
pos := val.Pos()
|
||||
if pos != token.NoPos {
|
||||
// Easy: position is known.
|
||||
return pos
|
||||
}
|
||||
|
||||
// No position information is known.
|
||||
switch val := val.(type) {
|
||||
case *ssa.MakeInterface:
|
||||
return getPos(val.X)
|
||||
case *ssa.Return:
|
||||
syntax := val.Parent().Syntax()
|
||||
if syntax != nil {
|
||||
// non-synthetic
|
||||
return syntax.End()
|
||||
}
|
||||
return token.NoPos
|
||||
case *ssa.FieldAddr:
|
||||
return getPos(val.X)
|
||||
case *ssa.IndexAddr:
|
||||
return getPos(val.X)
|
||||
case *ssa.Slice:
|
||||
return getPos(val.X)
|
||||
case *ssa.Store:
|
||||
return getPos(val.Addr)
|
||||
case *ssa.Extract:
|
||||
return getPos(val.Tuple)
|
||||
default:
|
||||
// This is reachable, for example with *ssa.Const, *ssa.If, and
|
||||
// *ssa.Jump. They might be implemented in some way in the future.
|
||||
return token.NoPos
|
||||
}
|
||||
}
|
||||
|
||||
// createInstruction builds the LLVM IR equivalent instructions for the
|
||||
// particular Go SSA instruction.
|
||||
func (b *builder) createInstruction(instr ssa.Instruction) {
|
||||
if b.Debug {
|
||||
pos := b.program.Fset.Position(getPos(instr))
|
||||
pos := b.program.Fset.Position(instr.Pos())
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||
}
|
||||
|
||||
@@ -2306,20 +2258,14 @@ 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)
|
||||
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)
|
||||
}
|
||||
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}, "")
|
||||
strObj := llvm.ConstNamedStruct(b.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||
return strObj
|
||||
} else if typ.Kind() == types.UnsafePointer {
|
||||
|
||||
@@ -62,7 +62,6 @@ func TestCompiler(t *testing.T) {
|
||||
"string.go",
|
||||
"float.go",
|
||||
"interface.go",
|
||||
"func.go",
|
||||
}
|
||||
|
||||
for _, testCase := range tests {
|
||||
@@ -85,16 +84,11 @@ func TestCompiler(t *testing.T) {
|
||||
mod, errs := CompilePackage(testCase, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false)
|
||||
if errs != nil {
|
||||
for _, err := range errs {
|
||||
t.Error(err)
|
||||
t.Log("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()
|
||||
|
||||
+3
-15
@@ -25,13 +25,14 @@ 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),
|
||||
c.getFuncSignatureID(sig),
|
||||
sigGlobal,
|
||||
})
|
||||
funcValueWithSignatureGlobal = llvm.AddGlobal(c.mod, funcValueWithSignatureType, funcValueWithSignatureGlobalName)
|
||||
funcValueWithSignatureGlobal.SetInitializer(funcValueWithSignature)
|
||||
@@ -49,19 +50,6 @@ 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 {
|
||||
@@ -83,7 +71,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.getFuncSignatureID(sig)
|
||||
sigGlobal := b.getTypeCode(sig)
|
||||
funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
|
||||
funcPtr = b.CreateIntToPtr(funcPtr, llvmSig, "")
|
||||
default:
|
||||
|
||||
+16
-20
@@ -46,7 +46,6 @@ 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())
|
||||
@@ -70,25 +69,22 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
if _, ok := typ.Underlying().(*types.Interface); !ok {
|
||||
methodSet = c.getTypeMethodSet(typ)
|
||||
}
|
||||
if _, ok := typ.Underlying().(*types.Pointer); !ok {
|
||||
ptrTo = c.getTypeCode(types.NewPointer(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)
|
||||
}
|
||||
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
|
||||
@@ -345,7 +341,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
commaOk = b.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, methodSet}, "")
|
||||
|
||||
} else {
|
||||
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
|
||||
globalName := "reflect/types.type:" + getTypeCodeName(expr.AssertedType) + "$id"
|
||||
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
|
||||
if assertedTypeCodeGlobal.IsNil() {
|
||||
// Create a new typecode global.
|
||||
|
||||
+5
-18
@@ -139,17 +139,6 @@ 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
|
||||
@@ -366,18 +355,16 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
|
||||
|
||||
// Set alignment from the //go:align comment.
|
||||
var alignInBits uint32
|
||||
alignment := c.targetData.ABITypeAlignment(llvmType)
|
||||
if info.align > alignment {
|
||||
alignment = info.align
|
||||
}
|
||||
if alignment <= 0 || alignment&(alignment-1) != 0 {
|
||||
if info.align < 0 || info.align&(info.align-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(alignment) ^ uint32(alignment-1)
|
||||
llvmGlobal.SetAlignment(alignment)
|
||||
alignInBits = uint32(info.align) ^ uint32(info.align-1)
|
||||
if info.align > c.targetData.ABITypeAlignment(llvmType) {
|
||||
llvmGlobal.SetAlignment(info.align)
|
||||
}
|
||||
}
|
||||
|
||||
if c.Debug && !info.extern {
|
||||
|
||||
Vendored
-12
@@ -1,12 +0,0 @@
|
||||
package main
|
||||
|
||||
func foo(callback func(int)) {
|
||||
callback(3)
|
||||
}
|
||||
|
||||
func bar() {
|
||||
foo(someFunc)
|
||||
}
|
||||
|
||||
func someFunc(int) {
|
||||
}
|
||||
Vendored
-47
@@ -1,47 +0,0 @@
|
||||
; ModuleID = 'func.go'
|
||||
source_filename = "func.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
|
||||
%runtime.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
+10
-11
@@ -3,24 +3,23 @@ source_filename = "interface.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
|
||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID* }
|
||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo* }
|
||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||
%runtime._interface = type { i32, i8* }
|
||||
%runtime._string = type { i8*, i32 }
|
||||
|
||||
@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID { %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}}" }
|
||||
@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID zeroinitializer
|
||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"func Error() string" = external constant i8
|
||||
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
|
||||
@"reflect/types.type:pointer:interface:{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}}" }
|
||||
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"func String() string" = external constant i8
|
||||
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func String() string"]
|
||||
@"reflect/types.typeid:basic:int" = external constant i8
|
||||
@"reflect/types.type:basic:int$id" = external constant i8
|
||||
@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
@@ -52,7 +51,7 @@ entry:
|
||||
|
||||
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.typeid:basic:int", i8* undef, i8* null)
|
||||
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.type:basic:int$id", i8* undef, i8* null)
|
||||
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.ok: ; preds = %entry
|
||||
|
||||
Vendored
+2
-2
@@ -60,7 +60,7 @@ entry:
|
||||
ret { i32*, i32, i32 } %7
|
||||
}
|
||||
|
||||
declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8* nocapture readonly, i32, i32, i32, i32, i8*, i8*)
|
||||
declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8*, i32, i32, i32, i32, i8*, i8*)
|
||||
|
||||
define hidden { i32*, i32, i32 } @main.sliceAppendSlice(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32* %added.data, i32 %added.len, i32 %added.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
@@ -85,4 +85,4 @@ entry:
|
||||
ret i32 %copy.n
|
||||
}
|
||||
|
||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
||||
declare i32 @runtime.sliceCopy(i8*, i8*, i32, i32, i32, i8*, i8*)
|
||||
|
||||
Vendored
-8
@@ -1,13 +1,5 @@
|
||||
package main
|
||||
|
||||
func someString() string {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
func zeroLengthString() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func stringLen(s string) int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
Vendored
-14
@@ -3,10 +3,6 @@ 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"
|
||||
|
||||
%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*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
@@ -14,16 +10,6 @@ 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
|
||||
|
||||
@@ -9,10 +9,8 @@ require (
|
||||
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
|
||||
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
|
||||
github.com/mattn/go-colorable v0.1.8
|
||||
github.com/sago35/ochan v0.0.0-20200313013834-e0f46da4579f // indirect
|
||||
go.bug.st/serial v1.1.2
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
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-20210325115028-e7b85195e81c
|
||||
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4
|
||||
)
|
||||
|
||||
@@ -28,8 +28,6 @@ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sago35/ochan v0.0.0-20200313013834-e0f46da4579f h1:bHOcIl3AODQMzleLeSGs3xbIvAzhcgLTUwd8IUKqLMI=
|
||||
github.com/sago35/ochan v0.0.0-20200313013834-e0f46da4579f/go.mod h1:55Pg0jnassdnFxlhS8HIU5pdYhEBYftGaknIP4bBUq8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
go.bug.st/serial v1.0.0 h1:ogEPzrllCsnG00EqKRjeYvPRsO7NJW6DqykzkdD6E/k=
|
||||
@@ -43,8 +41,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
|
||||
@@ -63,5 +59,3 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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=
|
||||
|
||||
+2
-3
@@ -171,9 +171,8 @@ func getGoroot() string {
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
candidates = []string{
|
||||
"/usr/local/go", // manually installed
|
||||
"/usr/lib/go", // from the distribution
|
||||
"/snap/go/current/", // installed using snap
|
||||
"/usr/local/go", // manually installed
|
||||
"/usr/lib/go", // from the distribution
|
||||
}
|
||||
case "darwin":
|
||||
candidates = []string{
|
||||
|
||||
+1
-17
@@ -25,7 +25,6 @@ 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
|
||||
}
|
||||
|
||||
@@ -136,15 +135,6 @@ 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()
|
||||
@@ -347,13 +337,7 @@ 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).
|
||||
}
|
||||
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)
|
||||
}
|
||||
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
|
||||
|
||||
+92
-84
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -34,50 +33,6 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
lastBB := -1 // last basic block is undefined, only defined after a branch
|
||||
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
|
||||
@@ -148,24 +103,27 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
default:
|
||||
panic("unknown operands length")
|
||||
}
|
||||
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 // 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
|
||||
}
|
||||
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+"switch", operands, "->", currentBB)
|
||||
fmt.Fprintln(os.Stderr, indent+"phi", inst.operands, "->", result)
|
||||
}
|
||||
if result == nil {
|
||||
panic("could not find PHI input")
|
||||
}
|
||||
locals[inst.localIndex] = result
|
||||
case llvm.Select:
|
||||
// Select is much like a ternary operator: it picks a result from
|
||||
// the second and third operand based on the boolean first operand.
|
||||
@@ -197,7 +155,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
// which case this call won't even get to this point but will
|
||||
// already be emitted in initAll.
|
||||
continue
|
||||
case strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet" || callFn.name == "os.runtime_args":
|
||||
case strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet":
|
||||
// These functions should be run at runtime. Specifically:
|
||||
// * Print and panic functions are best emitted directly without
|
||||
// interpreting them, otherwise we get a ton of putchar (etc.)
|
||||
@@ -205,9 +163,6 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
// * runtime.hashmapGet tries to access the map value directly.
|
||||
// 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
|
||||
@@ -373,7 +328,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
actualType := actualTypePtrToInt.Operand(0)
|
||||
if strings.TrimPrefix(actualType.Name(), "reflect/types.type:") == strings.TrimPrefix(assertedType.Name(), "reflect/types.typeid:") {
|
||||
if actualType.Name()+"$id" == assertedType.Name() {
|
||||
locals[inst.localIndex] = literalValue{uint8(1)}
|
||||
} else {
|
||||
locals[inst.localIndex] = literalValue{uint8(0)}
|
||||
@@ -460,6 +415,74 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
return nil, mem, r.errorAt(inst, errors.New("could not find method: "+signature.Name()))
|
||||
}
|
||||
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
|
||||
@@ -931,31 +954,16 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
|
||||
result = r.builder.CreateBitCast(operands[0], inst.llvmInst.Type(), inst.name)
|
||||
case llvm.ExtractValue:
|
||||
indices := inst.llvmInst.Indices()
|
||||
// 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")
|
||||
if len(indices) != 1 {
|
||||
panic("expected exactly one index")
|
||||
}
|
||||
result = r.builder.CreateExtractValue(agg, int(indices[len(indices)-1]), inst.name)
|
||||
result = r.builder.CreateExtractValue(operands[0], int(indices[0]), inst.name)
|
||||
case llvm.InsertValue:
|
||||
indices := inst.llvmInst.Indices()
|
||||
// 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)
|
||||
if len(indices) != 1 {
|
||||
panic("expected exactly one index")
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
result = r.builder.CreateInsertValue(operands[0], operands[1], int(indices[0]), inst.name)
|
||||
case llvm.Add:
|
||||
result = r.builder.CreateAdd(operands[0], operands[1], inst.name)
|
||||
case llvm.Sub:
|
||||
|
||||
@@ -640,6 +640,288 @@ 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,7 +8,6 @@ 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
|
||||
|
||||
@@ -66,19 +65,6 @@ 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
|
||||
}
|
||||
|
||||
@@ -101,24 +87,3 @@ 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,7 +7,6 @@ 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
|
||||
|
||||
@@ -26,19 +25,6 @@ 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
|
||||
}
|
||||
|
||||
@@ -58,26 +44,3 @@ 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.typeid:named:main.foo" = external constant i8
|
||||
@"reflect/types.type:named:main.foo$id" = external constant i8
|
||||
@"reflect/types.type:basic:int" = external constant %runtime.typecodeID
|
||||
|
||||
|
||||
@@ -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.typeid:named:main.foo", i8* undef, i8* null)
|
||||
%typecode = call i1 @runtime.typeAssert(i64 ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:main.foo" to i64), i8* @"reflect/types.type:named:main.foo$id", i8* undef, i8* null)
|
||||
store i1 %typecode, i1* @main.v1
|
||||
ret void
|
||||
}
|
||||
|
||||
Vendored
+74
@@ -0,0 +1,74 @@
|
||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "armv6m-none-eabi"
|
||||
|
||||
%runtime._string = type { i8*, i32 }
|
||||
%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 }
|
||||
|
||||
@main.m = global %runtime.hashmap* null
|
||||
@main.binaryMap = global %runtime.hashmap* null
|
||||
@main.stringMap = global %runtime.hashmap* null
|
||||
@main.init.string = internal unnamed_addr constant [7 x i8] c"CONNECT"
|
||||
|
||||
declare %runtime.hashmap* @runtime.hashmapMake(i8, i8, i32, i8* %context, i8* %parentHandle)
|
||||
declare void @runtime.hashmapBinarySet(%runtime.hashmap*, i8*, i8*, i8* %context, i8* %parentHandle)
|
||||
declare void @runtime.hashmapStringSet(%runtime.hashmap*, i8*, i32, i8*, i8* %context, i8* %parentHandle)
|
||||
declare void @llvm.lifetime.end.p0i8(i64, i8*)
|
||||
declare void @llvm.lifetime.start.p0i8(i64, i8*)
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call void @main.init(i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
; Test that hashmap optimizations generally work (even with lifetimes).
|
||||
%hashmap.key = alloca i8
|
||||
%hashmap.value = alloca %runtime._string
|
||||
%0 = call %runtime.hashmap* @runtime.hashmapMake(i8 1, i8 8, i32 1, i8* undef, i8* null)
|
||||
%hashmap.value.bitcast = bitcast %runtime._string* %hashmap.value to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 8, i8* %hashmap.value.bitcast)
|
||||
store %runtime._string { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7 }, %runtime._string* %hashmap.value
|
||||
call void @llvm.lifetime.start.p0i8(i64 1, i8* %hashmap.key)
|
||||
store i8 1, i8* %hashmap.key
|
||||
call void @runtime.hashmapBinarySet(%runtime.hashmap* %0, i8* %hashmap.key, i8* %hashmap.value.bitcast, i8* undef, i8* null)
|
||||
call void @llvm.lifetime.end.p0i8(i64 1, i8* %hashmap.key)
|
||||
call void @llvm.lifetime.end.p0i8(i64 8, i8* %hashmap.value.bitcast)
|
||||
store %runtime.hashmap* %0, %runtime.hashmap** @main.m
|
||||
|
||||
; Other tests, that can be done in a separate function.
|
||||
call void @main.testNonConstantBinarySet()
|
||||
call void @main.testNonConstantStringSet()
|
||||
ret void
|
||||
}
|
||||
|
||||
; Test that a map loaded from a global can still be used for mapassign
|
||||
; operations (with binary keys).
|
||||
define internal void @main.testNonConstantBinarySet() {
|
||||
%hashmap.key = alloca i8
|
||||
%hashmap.value = alloca i8
|
||||
; Create hashmap from global.
|
||||
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 1, i8 1, i32 1, i8* undef, i8* null)
|
||||
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.binaryMap
|
||||
%map = load %runtime.hashmap*, %runtime.hashmap** @main.binaryMap
|
||||
; Do the binary set to the newly loaded map.
|
||||
store i8 1, i8* %hashmap.key
|
||||
store i8 2, i8* %hashmap.value
|
||||
call void @runtime.hashmapBinarySet(%runtime.hashmap* %map, i8* %hashmap.key, i8* %hashmap.value, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Test that a map loaded from a global can still be used for mapassign
|
||||
; operations (with string keys).
|
||||
define internal void @main.testNonConstantStringSet() {
|
||||
%hashmap.value = alloca i8
|
||||
; Create hashmap from global.
|
||||
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 8, i8 1, i32 1, i8* undef, i8* null)
|
||||
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.stringMap
|
||||
%map = load %runtime.hashmap*, %runtime.hashmap** @main.stringMap
|
||||
; Do the string set to the newly loaded map.
|
||||
store i8 2, i8* %hashmap.value
|
||||
call void @runtime.hashmapStringSet(%runtime.hashmap* %map, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7, i8* %hashmap.value, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "armv6m-none-eabi"
|
||||
|
||||
%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 }
|
||||
|
||||
@main.m = local_unnamed_addr global %runtime.hashmap* @"main$map"
|
||||
@main.binaryMap = local_unnamed_addr global %runtime.hashmap* @"main$map.1"
|
||||
@main.stringMap = local_unnamed_addr global %runtime.hashmap* @"main$map.3"
|
||||
@main.init.string = internal unnamed_addr constant [7 x i8] c"CONNECT"
|
||||
@"main$map" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } }, { [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } }* @"main$mapbucket", i32 0, i32 0, i32 0), i32 1, i8 1, i8 8, i8 0 }
|
||||
@"main$mapbucket" = internal unnamed_addr global { [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, { i8, [7 x i8] } { i8 1, [7 x i8] zeroinitializer }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } { { [7 x i8]*, [4 x i8] } { [7 x i8]* @main.init.string, [4 x i8] c"\07\00\00\00" }, [56 x i8] zeroinitializer } }
|
||||
@"main$map.1" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } }, { [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } }* @"main$mapbucket.2", i32 0, i32 0, i32 0), i32 1, i8 1, i8 1, i8 0 }
|
||||
@"main$mapbucket.2" = internal unnamed_addr global { [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, { i8, [7 x i8] } { i8 1, [7 x i8] zeroinitializer }, { i8, [7 x i8] } { i8 2, [7 x i8] zeroinitializer } }
|
||||
@"main$map.3" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } }, { [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } }* @"main$mapbucket.4", i32 0, i32 0, i32 0), i32 1, i8 8, i8 1, i8 0 }
|
||||
@"main$mapbucket.4" = internal unnamed_addr global { [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } } { [8 x i8] c"x\00\00\00\00\00\00\00", i8* null, { { [7 x i8]*, [4 x i8] }, [56 x i8] } { { [7 x i8]*, [4 x i8] } { [7 x i8]* @main.init.string, [4 x i8] c"\07\00\00\00" }, [56 x i8] zeroinitializer }, { i8, [7 x i8] } { i8 2, [7 x i8] zeroinitializer } }
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
Vendored
-31
@@ -1,31 +0,0 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
@main.phiNodesResultA = global i8 0
|
||||
@main.phiNodesResultB = global i8 0
|
||||
|
||||
define void @runtime.initAll() {
|
||||
call void @main.init()
|
||||
ret void
|
||||
}
|
||||
|
||||
; PHI nodes always use the value from the previous block, even in a loop. This
|
||||
; means that the loop below should swap the values %a and %b on each iteration.
|
||||
; Previously there was a bug which resulted in %b getting the value 3 on the
|
||||
; second iteration while it should have gotten 1 (from the first iteration of
|
||||
; %for.loop).
|
||||
define internal void @main.init() {
|
||||
entry:
|
||||
br label %for.loop
|
||||
|
||||
for.loop:
|
||||
%a = phi i8 [ 1, %entry ], [ %b, %for.loop ]
|
||||
%b = phi i8 [ 3, %entry ], [ %a, %for.loop ]
|
||||
%icmp = icmp eq i8 %a, 3
|
||||
br i1 %icmp, label %for.done, label %for.loop
|
||||
|
||||
for.done:
|
||||
store i8 %a, i8* @main.phiNodesResultA
|
||||
store i8 %b, i8* @main.phiNodesResultB
|
||||
ret void
|
||||
}
|
||||
Vendored
-9
@@ -1,9 +0,0 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
@main.phiNodesResultA = local_unnamed_addr global i8 3
|
||||
@main.phiNodesResultB = local_unnamed_addr global i8 1
|
||||
|
||||
define void @runtime.initAll() local_unnamed_addr {
|
||||
ret void
|
||||
}
|
||||
+1
-1
Submodule lib/cmsis-svd updated: 2fb66c5735...d9b58694ce
+1
-1
Submodule lib/stm32-svd updated: 3a0b0829b7...c6b5be976f
@@ -13,13 +13,11 @@ 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"
|
||||
@@ -30,7 +28,6 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
|
||||
"go.bug.st/serial"
|
||||
"go.bug.st/serial/enumerator"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -243,12 +240,15 @@ 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" {
|
||||
port, err := getDefaultPort(strings.FieldsFunc(port, func(c rune) bool { return c == ',' }))
|
||||
if err != nil {
|
||||
return err
|
||||
if port == "" {
|
||||
var err error
|
||||
port, err = getDefaultPort()
|
||||
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 +264,9 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
fileToken := "{" + fileExt[1:] + "}"
|
||||
flashCmd = strings.ReplaceAll(flashCmd, fileToken, result.Binary)
|
||||
|
||||
if strings.Contains(flashCmd, "{port}") {
|
||||
if port == "" && strings.Contains(flashCmd, "{port}") {
|
||||
var err error
|
||||
port, err = getDefaultPort(strings.FieldsFunc(port, func(c rune) bool { return c == ',' }))
|
||||
port, err = getDefaultPort()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -665,66 +665,41 @@ func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, e
|
||||
}
|
||||
|
||||
// getDefaultPort returns the default serial port depending on the operating system.
|
||||
func getDefaultPort(portCandidates []string) (port string, err error) {
|
||||
if len(portCandidates) == 1 {
|
||||
return portCandidates[0], nil
|
||||
}
|
||||
|
||||
var ports []string
|
||||
func getDefaultPort() (port string, err error) {
|
||||
var portPath string
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
portPath = "/dev/cu.usb*"
|
||||
case "linux":
|
||||
portPath = "/dev/ttyACM*"
|
||||
case "freebsd":
|
||||
ports, err = filepath.Glob("/dev/cuaU*")
|
||||
case "darwin", "linux", "windows":
|
||||
var portsList []*enumerator.PortDetails
|
||||
portsList, err = enumerator.GetDetailedPortsList()
|
||||
portPath = "/dev/cuaU*"
|
||||
case "windows":
|
||||
ports, err := serial.GetPortsList()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, p := range portsList {
|
||||
ports = append(ports, p.Name)
|
||||
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")
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
return ports[0], nil
|
||||
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
|
||||
} else if ports == nil {
|
||||
}
|
||||
if d == nil {
|
||||
return "", errors.New("unable to locate a serial port")
|
||||
} else if len(ports) == 0 {
|
||||
return "", errors.New("no serial ports available")
|
||||
}
|
||||
|
||||
if len(portCandidates) == 0 {
|
||||
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, ", "))
|
||||
return d[0], nil
|
||||
}
|
||||
|
||||
func usage() {
|
||||
@@ -837,52 +812,6 @@ func handleCompilerError(err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// This is a special type for the -X flag to parse the pkgpath.Var=stringVal
|
||||
// format. It has to be a special type to allow multiple variables to be defined
|
||||
// this way.
|
||||
type globalValuesFlag map[string]map[string]string
|
||||
|
||||
func (m globalValuesFlag) String() string {
|
||||
return "pkgpath.Var=value"
|
||||
}
|
||||
|
||||
func (m globalValuesFlag) Set(value string) error {
|
||||
equalsIndex := strings.IndexByte(value, '=')
|
||||
if equalsIndex < 0 {
|
||||
return errors.New("expected format pkgpath.Var=value")
|
||||
}
|
||||
pathAndName := value[:equalsIndex]
|
||||
pointIndex := strings.LastIndexByte(pathAndName, '.')
|
||||
if pointIndex < 0 {
|
||||
return errors.New("expected format pkgpath.Var=value")
|
||||
}
|
||||
path := pathAndName[:pointIndex]
|
||||
name := pathAndName[pointIndex+1:]
|
||||
stringValue := value[equalsIndex+1:]
|
||||
if m[path] == nil {
|
||||
m[path] = make(map[string]string)
|
||||
}
|
||||
m[path][name] = stringValue
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseGoLinkFlag parses the -ldflags parameter. Its primary purpose right now
|
||||
// is the -X flag, for setting the value of global string variables.
|
||||
func parseGoLinkFlag(flagsString string) (map[string]map[string]string, error) {
|
||||
set := flag.NewFlagSet("link", flag.ExitOnError)
|
||||
globalVarValues := make(globalValuesFlag)
|
||||
set.Var(globalVarValues, "X", "Set the value of the string variable to the given value.")
|
||||
flags, err := shlex.Split(flagsString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = set.Parse(flags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return map[string]map[string]string(globalVarValues), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
|
||||
@@ -902,13 +831,13 @@ 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")
|
||||
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
||||
port := flag.String("port", "", "flash port (can specify multiple candidates separated by commas)")
|
||||
port := flag.String("port", "", "flash port")
|
||||
programmer := flag.String("programmer", "", "which hardware programmer to use")
|
||||
ldflags := flag.String("ldflags", "", "Go link tool compatible ldflags")
|
||||
cFlags := flag.String("cflags", "", "additional cflags for compiler")
|
||||
ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
|
||||
wasmAbi := flag.String("wasm-abi", "", "WebAssembly ABI conventions: js (no i64 params) or generic")
|
||||
|
||||
var flagJSON, flagDeps *bool
|
||||
@@ -938,19 +867,6 @@ 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)
|
||||
}
|
||||
}
|
||||
options := &compileopts.Options{
|
||||
Target: *target,
|
||||
Opt: *opt,
|
||||
@@ -963,17 +879,23 @@ func main() {
|
||||
Debug: !*nodebug,
|
||||
PrintSizes: *printSize,
|
||||
PrintStacks: *printStacks,
|
||||
PrintAllocs: printAllocs,
|
||||
PrintCommands: *printCommands,
|
||||
Tags: *tags,
|
||||
GlobalValues: globalVarValues,
|
||||
WasmAbi: *wasmAbi,
|
||||
Programmer: *programmer,
|
||||
}
|
||||
|
||||
if *cFlags != "" {
|
||||
options.CFlags = strings.Split(*cFlags, " ")
|
||||
}
|
||||
|
||||
if *ldFlags != "" {
|
||||
options.LDFlags = strings.Split(*ldFlags, " ")
|
||||
}
|
||||
|
||||
os.Setenv("CC", "clang -target="+*target)
|
||||
|
||||
err = options.Verify()
|
||||
err := options.Verify()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
usage()
|
||||
|
||||
+55
-125
@@ -13,8 +13,8 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -28,43 +28,41 @@ const TESTDATA = "testdata"
|
||||
var testTarget = flag.String("target", "", "override test target")
|
||||
|
||||
func TestCompiler(t *testing.T) {
|
||||
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",
|
||||
matches, err := filepath.Glob(filepath.Join(TESTDATA, "*.go"))
|
||||
if err != nil {
|
||||
t.Fatal("could not read test files:", err)
|
||||
}
|
||||
|
||||
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, tests, t)
|
||||
runPlatTests(*testTarget, matches, t)
|
||||
return
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
t.Run("Host", func(t *testing.T) {
|
||||
runPlatTests("", tests, t)
|
||||
runPlatTests("", matches, t)
|
||||
if runtime.GOOS == "darwin" {
|
||||
runTest("testdata/libc/filesystem.go", "", t,
|
||||
nil, nil)
|
||||
runTest("testdata/libc/env.go", "", t,
|
||||
[]string{"ENV1=VALUE1", "ENV2=VALUE2"}, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -73,26 +71,26 @@ func TestCompiler(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("EmulatedCortexM3", func(t *testing.T) {
|
||||
runPlatTests("cortex-m-qemu", tests, t)
|
||||
runPlatTests("cortex-m-qemu", matches, 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", tests, t)
|
||||
runPlatTests("riscv-qemu", matches, t)
|
||||
})
|
||||
}
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
t.Run("X86Linux", func(t *testing.T) {
|
||||
runPlatTests("i386--linux-gnu", tests, t)
|
||||
runPlatTests("i386--linux-gnu", matches, t)
|
||||
})
|
||||
t.Run("ARMLinux", func(t *testing.T) {
|
||||
runPlatTests("arm--linux-gnueabihf", tests, t)
|
||||
runPlatTests("arm--linux-gnueabihf", matches, t)
|
||||
})
|
||||
t.Run("ARM64Linux", func(t *testing.T) {
|
||||
runPlatTests("aarch64--linux-gnu", tests, t)
|
||||
runPlatTests("aarch64--linux-gnu", matches, t)
|
||||
})
|
||||
goVersion, err := goenv.GorootVersionString(goenv.Get("GOROOT"))
|
||||
if err != nil {
|
||||
@@ -105,110 +103,35 @@ 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", tests, t)
|
||||
runPlatTests("wasm", matches, t)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("WASI", func(t *testing.T) {
|
||||
runPlatTests("wasi", tests, t)
|
||||
runPlatTests("wasi", matches, t)
|
||||
runTest("testdata/libc/env.go", "wasi", t,
|
||||
[]string{"--env", "ENV1=VALUE1", "--env", "ENV2=VALUE2"}, nil)
|
||||
runTest("testdata/libc/filesystem.go", "wasi", t, nil, []string{"--dir=."})
|
||||
})
|
||||
}
|
||||
|
||||
// Test a few build options.
|
||||
t.Run("build-options", func(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
// These tests assume a host that is supported by TinyGo.
|
||||
t.Skip("can't test build options on Windows")
|
||||
}
|
||||
t.Parallel()
|
||||
|
||||
// Test with few optimizations enabled (no inlining, etc).
|
||||
t.Run("opt=1", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTestWithConfig("stdlib.go", "", t, &compileopts.Options{
|
||||
Opt: "1",
|
||||
}, nil, nil)
|
||||
})
|
||||
|
||||
// Test with only the bare minimum of optimizations enabled.
|
||||
// TODO: fix this for stdlib.go, which currently fails.
|
||||
t.Run("opt=0", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTestWithConfig("print.go", "", t, &compileopts.Options{
|
||||
Opt: "0",
|
||||
}, nil, nil)
|
||||
})
|
||||
|
||||
t.Run("ldflags", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTestWithConfig("ldflags.go", "", t, &compileopts.Options{
|
||||
Opt: "z",
|
||||
GlobalValues: map[string]map[string]string{
|
||||
"main": {
|
||||
"someGlobal": "foobar",
|
||||
},
|
||||
},
|
||||
}, nil, nil)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func runPlatTests(target string, tests []string, t *testing.T) {
|
||||
func runPlatTests(target string, matches []string, t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, name := range tests {
|
||||
name := name // redefine to avoid race condition
|
||||
t.Run(name, func(t *testing.T) {
|
||||
for _, path := range matches {
|
||||
path := path // redefine to avoid race condition
|
||||
t.Run(filepath.Base(path), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
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"})
|
||||
runTest(path, target, t, nil, nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
func runTest(path, target string, t *testing.T, environmentVars []string, additionalArgs []string) {
|
||||
// Get the expected output for this test.
|
||||
txtpath := path[:len(path)-3] + ".txt"
|
||||
if path[len(path)-1] == '/' {
|
||||
if path[len(path)-1] == os.PathSeparator {
|
||||
txtpath = path + "out.txt"
|
||||
}
|
||||
expected, err := ioutil.ReadFile(txtpath)
|
||||
@@ -229,8 +152,19 @@ func runTestWithConfig(name, target string, t *testing.T, options *compileopts.O
|
||||
}()
|
||||
|
||||
// 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 = runBuild("./"+path, binary, options)
|
||||
err = Build("./"+path, binary, config)
|
||||
if err != nil {
|
||||
printCompilerError(t.Log, err)
|
||||
t.Fail()
|
||||
@@ -244,7 +178,6 @@ func runTestWithConfig(name, target string, t *testing.T, options *compileopts.O
|
||||
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 {
|
||||
@@ -254,16 +187,13 @@ func runTestWithConfig(name, target string, t *testing.T, options *compileopts.O
|
||||
cmd = exec.Command(binary)
|
||||
} else {
|
||||
args := append(spec.Emulator[1:], binary)
|
||||
cmd = exec.Command(spec.Emulator[0], args...)
|
||||
cmd = exec.Command(spec.Emulator[0], append(args, additionalArgs...)...)
|
||||
}
|
||||
|
||||
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
|
||||
// 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...)
|
||||
}
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/sago35/ochan"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func main() {
|
||||
threads := runtime.NumCPU()
|
||||
if j, ok := os.LookupEnv(`RUN_SMOKETEST_JOBS`); ok {
|
||||
n, err := strconv.ParseInt(j, 0, 0)
|
||||
if err == nil {
|
||||
threads = int(n)
|
||||
}
|
||||
}
|
||||
|
||||
flag.IntVar(&threads, "threads", threads, "threads of make smoketest")
|
||||
flag.Parse()
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Printf("usage: %s make smoketest\n", filepath.Base(os.Args[0]))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
commands, err := getMakeCommands(flag.Args())
|
||||
if err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
err = run(commands, threads)
|
||||
if err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func getMakeCommands(args []string) ([]string, error) {
|
||||
var buf bytes.Buffer
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Args = append(cmd.Args, "-n")
|
||||
cmd.Stdout = &buf
|
||||
cmd.Stderr = &buf
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commands := []string{}
|
||||
scanner := bufio.NewScanner(&buf)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
fields := strings.Fields(line)
|
||||
if filepath.Base(fields[0]) == "tinygo" && (fields[1] == "build" || fields[1] == "version") {
|
||||
commands = append(commands, line)
|
||||
} else if strings.HasPrefix(line, "#") {
|
||||
commands = append(commands, line)
|
||||
}
|
||||
}
|
||||
|
||||
return commands, nil
|
||||
}
|
||||
|
||||
func run(commands []string, threads int) error {
|
||||
ch := make(chan string, 1000)
|
||||
o := ochan.NewOchan(ch, 100)
|
||||
|
||||
go func() {
|
||||
limit := make(chan struct{}, threads)
|
||||
var eg errgroup.Group
|
||||
|
||||
for _, command := range commands {
|
||||
command := command
|
||||
select {
|
||||
case limit <- struct{}{}:
|
||||
och := o.GetCh()
|
||||
eg.Go(func() error {
|
||||
var err error
|
||||
|
||||
if runtime.GOOS == `windows` {
|
||||
command = convertToWindowsPath(command)
|
||||
}
|
||||
|
||||
fields := strings.Fields(command)
|
||||
if filepath.Base(fields[0]) == "tinygo" && fields[1] == "build" {
|
||||
err = buildAndmd5sum(command, och)
|
||||
} else if strings.HasPrefix(command, "#") {
|
||||
och <- fmt.Sprintf("%s\n", command)
|
||||
} else {
|
||||
err = runCommand(command, och)
|
||||
}
|
||||
close(och)
|
||||
<-limit
|
||||
return err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
o.Wait()
|
||||
close(ch)
|
||||
}()
|
||||
|
||||
for s := range ch {
|
||||
fmt.Print(s)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runCommand(command string, ch chan string) error {
|
||||
var buf bytes.Buffer
|
||||
|
||||
fmt.Fprintf(&buf, "%s\n", command)
|
||||
|
||||
args := strings.Fields(command)
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Stdout = &buf
|
||||
cmd.Stderr = &buf
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
ch <- buf.String()
|
||||
return err
|
||||
}
|
||||
|
||||
ch <- buf.String()
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildAndmd5sum(command string, ch chan string) error {
|
||||
var buf bytes.Buffer
|
||||
|
||||
fmt.Fprintf(&buf, "%s\n", command)
|
||||
|
||||
args := strings.Fields(command)
|
||||
|
||||
output := ""
|
||||
tmpOutput := ""
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range args {
|
||||
if args[i] == "-o" {
|
||||
output = args[i+1]
|
||||
ext := filepath.Ext(output)
|
||||
tmpOutput = filepath.Join(tmpdir, fmt.Sprintf("%s.%s", filepath.Base(output), ext))
|
||||
args[i+1] = tmpOutput
|
||||
} else if strings.HasPrefix(args[i], "-o=") {
|
||||
output = args[i][3:]
|
||||
ext := filepath.Ext(output)
|
||||
tmpOutput = filepath.Join(tmpdir, fmt.Sprintf("%s.%s", filepath.Base(output), ext))
|
||||
args[i] = fmt.Sprintf("-o=%s", tmpOutput)
|
||||
}
|
||||
}
|
||||
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Stdout = &buf
|
||||
cmd.Stderr = &buf
|
||||
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
ch <- buf.String()
|
||||
return err
|
||||
}
|
||||
|
||||
md5str, err := calcMD5(tmpOutput)
|
||||
if err != nil {
|
||||
ch <- buf.String()
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(&buf, "%s %s\n", md5str, output)
|
||||
|
||||
ch <- buf.String()
|
||||
return nil
|
||||
}
|
||||
|
||||
func calcMD5(f string) (string, error) {
|
||||
r, err := os.Open(f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
h := md5.New()
|
||||
if _, err := io.Copy(h, r); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x", h.Sum(nil)), nil
|
||||
}
|
||||
|
||||
func convertToWindowsPath(command string) string {
|
||||
if !strings.HasPrefix(command, `/`) {
|
||||
return command
|
||||
}
|
||||
|
||||
command = fmt.Sprintf("%c:%s", command[1], command[2:])
|
||||
return command
|
||||
}
|
||||
+6
-15
@@ -26,21 +26,12 @@ 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
|
||||
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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
var SCB = (*SCB_Type)(unsafe.Pointer(uintptr(SCB_BASE)))
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
// Hand created file. DO NOT DELETE.
|
||||
// atsamd51x bitfield definitions that are not auto-generated by gen-device-svd.go
|
||||
|
||||
// +build sam,atsame5x
|
||||
|
||||
// These are the supported pchctrl function numberings on the atsamd51x
|
||||
// See http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D5xE5x_Family_Data_Sheet_DS60001507F.pdf
|
||||
// table 14-9
|
||||
|
||||
package sam
|
||||
|
||||
const (
|
||||
PCHCTRL_GCLK_OSCCTRL_DFLL48 = 0 // DFLL48 input clock source
|
||||
PCHCTRL_GCLK_OSCCTRL_FDPLL0 = 1 // Reference clock for FDPLL0
|
||||
PCHCTRL_GCLK_OSCCTRL_FDPLL1 = 2 // Reference clock for FDPLL1
|
||||
PCHCTRL_GCLK_OSCCTRL_FDPLL0_32K = 3 // FDPLL0 = 3 // 32KHz clock for internal lock timer
|
||||
PCHCTRL_GCLK_OSCCTRL_FDPLL1_32K = 3 // FDPLL1 = 3 // 32KHz clock for internal lock timer
|
||||
PCHCTRL_GCLK_SDHC0_SLOW = 3 // SDHC0 = 3 // Slow
|
||||
PCHCTRL_GCLK_SDHC1_SLOW = 3 // SDHC1 = 3 // Slow
|
||||
PCHCTRL_GCLK_SERCOMX_SLOW = 3 // GCLK_SERCOM[0..7]_SLOW = 3
|
||||
PCHCTRL_GCLK_EIC = 4
|
||||
PCHCTRL_GCLK_FREQM_MSR = 5 // FREQM Measure
|
||||
PCHCTRL_GCLK_FREQM_REF = 6 // FREQM Reference
|
||||
PCHCTRL_GCLK_SERCOM0_CORE = 7 // SERCOM0 Core
|
||||
PCHCTRL_GCLK_SERCOM1_CORE = 8 // SERCOM1 Core
|
||||
PCHCTRL_GCLK_TC0 = 9
|
||||
PCHCTRL_GCLK_TC1 = 9 // TC0, TC1
|
||||
PCHCTRL_GCLK_USB = 10 // USB
|
||||
PCHCTRL_GCLK_EVSYS0 = 11
|
||||
PCHCTRL_GCLK_EVSYS1 = 12
|
||||
PCHCTRL_GCLK_EVSYS2 = 13
|
||||
PCHCTRL_GCLK_EVSYS3 = 14
|
||||
PCHCTRL_GCLK_EVSYS4 = 15
|
||||
PCHCTRL_GCLK_EVSYS5 = 16
|
||||
PCHCTRL_GCLK_EVSYS6 = 17
|
||||
PCHCTRL_GCLK_EVSYS7 = 18
|
||||
PCHCTRL_GCLK_EVSYS8 = 19
|
||||
PCHCTRL_GCLK_EVSYS9 = 20
|
||||
PCHCTRL_GCLK_EVSYS10 = 21
|
||||
PCHCTRL_GCLK_EVSYS11 = 22
|
||||
PCHCTRL_GCLK_SERCOM2_CORE = 23 // SERCOM2 Core
|
||||
PCHCTRL_GCLK_SERCOM3_CORE = 24 // SERCOM3 Core
|
||||
PCHCTRL_GCLK_TCC0 = 25
|
||||
PCHCTRL_GCLK_TCC1 = 25 // TCC0, TCC1
|
||||
PCHCTRL_GCLK_TC2 = 26
|
||||
PCHCTRL_GCLK_TC3 = 26 // TC2, TC3
|
||||
PCHCTRL_GCLK_CAN0 = 27 // CAN0
|
||||
PCHCTRL_GCLK_CAN1 = 28 // CAN1
|
||||
PCHCTRL_GCLK_TCC2 = 29
|
||||
PCHCTRL_GCLK_TCC3 = 29 // TCC2, TCC3
|
||||
PCHCTRL_GCLK_TC4 = 30
|
||||
PCHCTRL_GCLK_TC5 = 30 // TC4, TC5
|
||||
PCHCTRL_GCLK_PDEC = 31 // PDEC
|
||||
PCHCTRL_GCLK_AC = 32 // AC
|
||||
PCHCTRL_GCLK_CCL = 33 // CCL
|
||||
PCHCTRL_GCLK_SERCOM4_CORE = 34 // SERCOM4 Core
|
||||
PCHCTRL_GCLK_SERCOM5_CORE = 35 // SERCOM5 Core
|
||||
PCHCTRL_GCLK_SERCOM6_CORE = 36 // SERCOM6 Core
|
||||
PCHCTRL_GCLK_SERCOM7_CORE = 37 // SERCOM7 Core
|
||||
PCHCTRL_GCLK_TCC4 = 38 // TCC4
|
||||
PCHCTRL_GCLK_TC6 = 39
|
||||
PCHCTRL_GCLK_TC7 = 39 // TC6, TC7
|
||||
PCHCTRL_GCLK_ADC0 = 40 // ADC0
|
||||
PCHCTRL_GCLK_ADC1 = 41 // ADC1
|
||||
PCHCTRL_GCLK_DAC = 42 // DAC
|
||||
PCHCTRL_GCLK_I2S0 = 43
|
||||
PCHCTRL_GCLK_I2S1 = 44
|
||||
PCHCTRL_GCLK_SDHC0 = 45 // SDHC0
|
||||
PCHCTRL_GCLK_SDHC1 = 46 // SDHC1
|
||||
PCHCTRL_GCLK_CM4_TRACE = 47 // CM4 Trace
|
||||
)
|
||||
@@ -1,12 +0,0 @@
|
||||
// +build arduino_mega1280
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
// Configuration on an Arduino Uno.
|
||||
pwm = machine.Timer3
|
||||
pinA = machine.PH3 // pin 6 on the Mega
|
||||
pinB = machine.PH4 // pin 7 on the Mega
|
||||
)
|
||||
@@ -1,12 +0,0 @@
|
||||
// +build arduino
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
// Configuration on an Arduino Uno.
|
||||
pwm = machine.Timer2
|
||||
pinA = machine.PB3 // pin 11 on the Uno
|
||||
pinB = machine.PD3 // pin 3 on the Uno
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
// +build feather_m4
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = machine.TCC0
|
||||
pinA = machine.D12
|
||||
pinB = machine.D13
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
// +build itsybitsy_m0
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = machine.TCC0
|
||||
pinA = machine.D3
|
||||
pinB = machine.D4
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
// +build itsybitsy_m4
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = machine.TCC0
|
||||
pinA = machine.D12
|
||||
pinB = machine.D13
|
||||
)
|
||||
+54
-64
@@ -1,74 +1,64 @@
|
||||
package main
|
||||
|
||||
// This example demonstrates some features of the PWM support.
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
const delayBetweenPeriods = time.Second * 5
|
||||
// This example assumes that an RGB LED is connected to pins 3, 5 and 6 on an Arduino.
|
||||
// Change the values below to use different pins.
|
||||
const (
|
||||
redPin = machine.D4
|
||||
greenPin = machine.D5
|
||||
bluePin = machine.D6
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Delay a bit on startup to easily catch the first messages.
|
||||
time.Sleep(time.Second * 2)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// The top value is the highest value that can be passed to PWMChannel.Set.
|
||||
// It is usually an even number.
|
||||
println("top:", pwm.Top())
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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 {
|
||||
time.Sleep(time.Second)
|
||||
// cycleColor is just a placeholder until math/rand or some equivalent is working.
|
||||
func cycleColor(color uint8) uint8 {
|
||||
if color < 10 {
|
||||
return color + 1
|
||||
} else if color < 200 {
|
||||
return color + 10
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
machine.InitPWM()
|
||||
|
||||
red := machine.PWM{redPin}
|
||||
err := red.Configure()
|
||||
checkError(err, "failed to configure red pin")
|
||||
|
||||
green := machine.PWM{greenPin}
|
||||
err = green.Configure()
|
||||
checkError(err, "failed to configure green pin")
|
||||
|
||||
blue := machine.PWM{bluePin}
|
||||
err = blue.Configure()
|
||||
checkError(err, "failed to configure blue pin")
|
||||
|
||||
var rc uint8
|
||||
var gc uint8 = 20
|
||||
var bc uint8 = 30
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
// +build arduino_mega1280
|
||||
|
||||
package machine
|
||||
|
||||
// Return the current CPU frequency in hertz.
|
||||
func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
const (
|
||||
AREF Pin = NoPin
|
||||
LED Pin = PB7
|
||||
|
||||
A0 Pin = PF0
|
||||
A1 Pin = PF1
|
||||
A2 Pin = PF2
|
||||
A3 Pin = PF3
|
||||
A4 Pin = PF4
|
||||
A5 Pin = PF5
|
||||
A6 Pin = PF6
|
||||
A7 Pin = PF7
|
||||
A8 Pin = PK0
|
||||
A9 Pin = PK1
|
||||
A10 Pin = PK2
|
||||
A11 Pin = PK3
|
||||
A12 Pin = PK4
|
||||
A13 Pin = PK5
|
||||
A14 Pin = PK6
|
||||
A15 Pin = PK7
|
||||
|
||||
// Analog Input
|
||||
ADC0 Pin = PF0
|
||||
ADC1 Pin = PF1
|
||||
ADC2 Pin = PF2
|
||||
ADC3 Pin = PF3
|
||||
ADC4 Pin = PF4
|
||||
ADC5 Pin = PF5
|
||||
ADC6 Pin = PF6
|
||||
ADC7 Pin = PF7
|
||||
ADC8 Pin = PK0
|
||||
ADC9 Pin = PK1
|
||||
ADC10 Pin = PK2
|
||||
ADC11 Pin = PK3
|
||||
ADC12 Pin = PK4
|
||||
ADC13 Pin = PK5
|
||||
ADC14 Pin = PK6
|
||||
ADC15 Pin = PK7
|
||||
|
||||
// Digital pins
|
||||
D0 Pin = PE0
|
||||
D1 Pin = PE1
|
||||
D2 Pin = PE4
|
||||
D3 Pin = PE5
|
||||
D4 Pin = PG5
|
||||
D5 Pin = PE3
|
||||
D6 Pin = PH3
|
||||
D7 Pin = PH4
|
||||
D8 Pin = PH5
|
||||
D9 Pin = PH6
|
||||
D10 Pin = PB4
|
||||
D11 Pin = PB5
|
||||
D12 Pin = PB6
|
||||
D13 Pin = PB7
|
||||
D14 Pin = PJ1
|
||||
D15 Pin = PJ0
|
||||
D16 Pin = PH1
|
||||
D17 Pin = PH0
|
||||
D18 Pin = PD3
|
||||
D19 Pin = PD2
|
||||
D20 Pin = PD1
|
||||
D21 Pin = PD0
|
||||
D22 Pin = PA0
|
||||
D23 Pin = PA1
|
||||
D24 Pin = PA2
|
||||
D25 Pin = PA3
|
||||
D26 Pin = PA4
|
||||
D27 Pin = PA5
|
||||
D28 Pin = PA6
|
||||
D29 Pin = PA7
|
||||
D30 Pin = PC7
|
||||
D31 Pin = PC6
|
||||
D32 Pin = PC5
|
||||
D33 Pin = PC4
|
||||
D34 Pin = PC3
|
||||
D35 Pin = PC2
|
||||
D36 Pin = PC1
|
||||
D37 Pin = PC0
|
||||
D38 Pin = PD7
|
||||
D39 Pin = PG2
|
||||
D40 Pin = PG1
|
||||
D41 Pin = PG0
|
||||
D42 Pin = PL7
|
||||
D43 Pin = PL6
|
||||
D44 Pin = PL5
|
||||
D45 Pin = PL4
|
||||
D46 Pin = PL3
|
||||
D47 Pin = PL2
|
||||
D48 Pin = PL1
|
||||
D49 Pin = PL0
|
||||
D50 Pin = PB3
|
||||
D51 Pin = PB2
|
||||
D52 Pin = PB1
|
||||
D53 Pin = PB0
|
||||
)
|
||||
@@ -1,330 +0,0 @@
|
||||
// +build atsame54_xpro
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// Definition for compatibility, but not used
|
||||
const RESET_MAGIC_VALUE = 0x00000000
|
||||
|
||||
const (
|
||||
LED = PC18
|
||||
BUTTON = PB31
|
||||
)
|
||||
|
||||
const (
|
||||
// https://ww1.microchip.com/downloads/en/DeviceDoc/70005321A.pdf
|
||||
|
||||
// Extension Header EXT1
|
||||
EXT1_PIN3_ADC_P = PB04
|
||||
EXT1_PIN4_ADC_N = PB05
|
||||
EXT1_PIN5_GPIO1 = PA06
|
||||
EXT1_PIN6_GPIO2 = PA07
|
||||
EXT1_PIN7_PWM_P = PB08
|
||||
EXT1_PIN8_PWM_N = PB09
|
||||
EXT1_PIN9_IRQ = PB07
|
||||
EXT1_PIN9_GPIO = PB07
|
||||
EXT1_PIN10_SPI_SS_B = PA27
|
||||
EXT1_PIN10_GPIO = PA27
|
||||
EXT1_PIN11_TWI_SDA = PA22
|
||||
EXT1_PIN12_TWI_SCL = PA23
|
||||
EXT1_PIN13_UART_RX = PA05
|
||||
EXT1_PIN14_UART_TX = PA04
|
||||
EXT1_PIN15_SPI_SS_A = PB28
|
||||
EXT1_PIN16_SPI_SDO = PB27
|
||||
EXT1_PIN17_SPI_SDI = PB29
|
||||
EXT1_PIN18_SPI_SCK = PB26
|
||||
|
||||
// Extension Header EXT2
|
||||
EXT2_PIN3_ADC_P = PB00
|
||||
EXT2_PIN4_ADC_N = PA03
|
||||
EXT2_PIN5_GPIO1 = PB01
|
||||
EXT2_PIN6_GPIO2 = PB06
|
||||
EXT2_PIN7_PWM_P = PB14
|
||||
EXT2_PIN8_PWM_N = PB15
|
||||
EXT2_PIN9_IRQ = PD00
|
||||
EXT2_PIN9_GPIO = PD00
|
||||
EXT2_PIN10_SPI_SS_B = PB02
|
||||
EXT2_PIN10_GPIO = PB02
|
||||
EXT2_PIN11_TWI_SDA = PD08
|
||||
EXT2_PIN12_TWI_SCL = PD09
|
||||
EXT2_PIN13_UART_RX = PB17
|
||||
EXT2_PIN14_UART_TX = PB16
|
||||
EXT2_PIN15_SPI_SS_A = PC06
|
||||
EXT2_PIN16_SPI_SDO = PC04
|
||||
EXT2_PIN17_SPI_SDI = PC07
|
||||
EXT2_PIN18_SPI_SCK = PC05
|
||||
|
||||
// Extension Header EXT3
|
||||
EXT3_PIN3_ADC_P = PC02
|
||||
EXT3_PIN4_ADC_N = PC03
|
||||
EXT3_PIN5_GPIO1 = PC01
|
||||
EXT3_PIN6_GPIO2 = PC10
|
||||
EXT3_PIN7_PWM_P = PD10
|
||||
EXT3_PIN8_PWM_N = PD11
|
||||
EXT3_PIN9_IRQ = PC30
|
||||
EXT3_PIN9_GPIO = PC30
|
||||
EXT3_PIN10_SPI_SS_B = PC31
|
||||
EXT3_PIN10_GPIO = PC31
|
||||
EXT3_PIN11_TWI_SDA = PD08
|
||||
EXT3_PIN12_TWI_SCL = PD09
|
||||
EXT3_PIN13_UART_RX = PC23
|
||||
EXT3_PIN14_UART_TX = PC22
|
||||
EXT3_PIN15_SPI_SS_A = PC14
|
||||
EXT3_PIN16_SPI_SDO = PC04
|
||||
EXT3_PIN17_SPI_SDI = PC07
|
||||
EXT3_PIN18_SPI_SCK = PC05
|
||||
|
||||
// SD_CARD
|
||||
SD_CARD_MCDA0 = PB18
|
||||
SD_CARD_MCDA1 = PB19
|
||||
SD_CARD_MCDA2 = PB20
|
||||
SD_CARD_MCDA3 = PB21
|
||||
SD_CARD_MCCK = PA21
|
||||
SD_CARD_MCCDA = PA20
|
||||
SD_CARD_DETECT = PD20
|
||||
SD_CARD_PROTECT = PD21
|
||||
|
||||
// I2C
|
||||
I2C_SDA = PD08
|
||||
I2C_SCL = PD09
|
||||
|
||||
// CAN
|
||||
CAN0_TX = PA22
|
||||
CAN0_RX = PA23
|
||||
|
||||
CAN1_STANDBY = PC13
|
||||
CAN1_TX = PB12
|
||||
CAN1_RX = PB13
|
||||
|
||||
CAN_STANDBY = CAN1_STANDBY
|
||||
CAN_TX = CAN1_TX
|
||||
CAN_RX = CAN1_RX
|
||||
|
||||
// PDEC
|
||||
PDEC_PHASE_A = PC16
|
||||
PDEC_PHASE_B = PC17
|
||||
PDEC_INDEX = PC18
|
||||
|
||||
// PCC
|
||||
PCC_I2C_SDA = PD08
|
||||
PCC_I2C_SCL = PD09
|
||||
PCC_VSYNC_DEN1 = PA12
|
||||
PCC_HSYNC_DEN2 = PA13
|
||||
PCC_CLK = PA14
|
||||
PCC_XCLK = PA15
|
||||
PCC_DATA00 = PA16
|
||||
PCC_DATA01 = PA17
|
||||
PCC_DATA02 = PA18
|
||||
PCC_DATA03 = PA19
|
||||
PCC_DATA04 = PA20
|
||||
PCC_DATA05 = PA21
|
||||
PCC_DATA06 = PA22
|
||||
PCC_DATA07 = PA23
|
||||
PCC_DATA08 = PB14
|
||||
PCC_DATA09 = PB15
|
||||
PCC_RESET = PC12
|
||||
PCC_PWDN = PC11
|
||||
|
||||
// Ethernet
|
||||
ETHERNET_TXCK = PA14
|
||||
ETHERNET_TXEN = PA17
|
||||
ETHERNET_TX0 = PA18
|
||||
ETHERNET_TX1 = PA19
|
||||
ETHERNET_RXER = PA15
|
||||
ETHERNET_RX0 = PA13
|
||||
ETHERNET_RX1 = PA12
|
||||
ETHERNET_RXDV = PC20
|
||||
ETHERNET_MDIO = PC12
|
||||
ETHERNET_MDC = PC11
|
||||
ETHERNET_INT = PD12
|
||||
ETHERNET_RESET = PC21
|
||||
|
||||
PIN_QT_BUTTON = PA16
|
||||
PIN_BTN0 = PB31
|
||||
PIN_ETH_LED = PC15
|
||||
PIN_LED0 = PC18
|
||||
PIN_ADC_DAC = PA02
|
||||
PIN_VBUS_DETECT = PC00
|
||||
PIN_USB_ID = PC19
|
||||
)
|
||||
|
||||
// UART0 aka USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
USBCDC_DP_PIN = PA25
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
// Extension Header EXT1
|
||||
UART_TX_PIN = PA04 // TX : SERCOM0/PAD[0]
|
||||
UART_RX_PIN = PA05 // RX : SERCOM0/PAD[1]
|
||||
|
||||
// Extension Header EXT2
|
||||
UART2_TX_PIN = PB16 // TX : SERCOM5/PAD[0]
|
||||
UART2_RX_PIN = PB17 // RX : SERCOM5/PAD[1]
|
||||
|
||||
// Extension Header EXT3
|
||||
UART3_TX_PIN = PC22 // TX : SERCOM1/PAD[0]
|
||||
UART3_RX_PIN = PC23 // RX : SERCOM1/PAD[1]
|
||||
|
||||
// Virtual COM Port
|
||||
UART4_TX_PIN = PB25 // TX : SERCOM2/PAD[0]
|
||||
UART4_RX_PIN = PB24 // RX : SERCOM2/PAD[1]
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
// Extension Header EXT1
|
||||
SDA0_PIN = PA22 // SDA: SERCOM3/PAD[0]
|
||||
SCL0_PIN = PA23 // SCL: SERCOM3/PAD[1]
|
||||
|
||||
// Extension Header EXT2
|
||||
SDA1_PIN = PD08 // SDA: SERCOM7/PAD[0]
|
||||
SCL1_PIN = PD09 // SCL: SERCOM7/PAD[1]
|
||||
|
||||
// Extension Header EXT3
|
||||
SDA2_PIN = PD08 // SDA: SERCOM7/PAD[0]
|
||||
SCL2_PIN = PD09 // SCL: SERCOM7/PAD[1]
|
||||
|
||||
// Data Gateway Interface
|
||||
SDA_DGI_PIN = PD08 // SDA: SERCOM7/PAD[0]
|
||||
SCL_DGI_PIN = PD09 // SCL: SERCOM7/PAD[1]
|
||||
|
||||
SDA_PIN = SDA0_PIN
|
||||
SCL_PIN = SCL0_PIN
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
// Extension Header EXT1
|
||||
SPI0_SCK_PIN = PB26 // SCK: SERCOM4/PAD[1]
|
||||
SPI0_SDO_PIN = PB27 // SDO: SERCOM4/PAD[0]
|
||||
SPI0_SDI_PIN = PB29 // SDI: SERCOM4/PAD[3]
|
||||
SPI0_SS_PIN = PB28 // SS : SERCOM4/PAD[2]
|
||||
|
||||
// Extension Header EXT2
|
||||
SPI1_SCK_PIN = PC05 // SCK: SERCOM6/PAD[1]
|
||||
SPI1_SDO_PIN = PC04 // SDO: SERCOM6/PAD[0]
|
||||
SPI1_SDI_PIN = PC07 // SDI: SERCOM6/PAD[3]
|
||||
SPI1_SS_PIN = PC06 // SS : SERCOM6/PAD[2]
|
||||
|
||||
// Extension Header EXT3
|
||||
SPI2_SCK_PIN = PC05 // SCK: SERCOM6/PAD[1]
|
||||
SPI2_SDO_PIN = PC04 // SDO: SERCOM6/PAD[0]
|
||||
SPI2_SDI_PIN = PC07 // SDI: SERCOM6/PAD[3]
|
||||
SPI2_SS_PIN = PC14 // SS : GPIO
|
||||
|
||||
// Data Gateway Interface
|
||||
SPI_DGI_SCK_PIN = PC05 // SCK: SERCOM6/PAD[1]
|
||||
SPI_DGI_SDO_PIN = PC04 // SDO: SERCOM6/PAD[0]
|
||||
SPI_DGI_SDI_PIN = PC07 // SDI: SERCOM6/PAD[3]
|
||||
SPI_DGI_SS_PIN = PD01 // SS : GPIO
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "SAM E54 Xplained Pro"
|
||||
usb_STRING_MANUFACTURER = "Atmel"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x03EB
|
||||
usb_PID uint16 = 0x2404
|
||||
)
|
||||
|
||||
// UART on the SAM E54 Xplained Pro
|
||||
var (
|
||||
// Extension Header EXT1
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART_INT,
|
||||
SERCOM: 0,
|
||||
}
|
||||
|
||||
// Extension Header EXT2
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM5_USART_INT,
|
||||
SERCOM: 5,
|
||||
}
|
||||
|
||||
// Extension Header EXT3
|
||||
UART3 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM1_USART_INT,
|
||||
SERCOM: 1,
|
||||
}
|
||||
|
||||
// EDBG Virtual COM Port
|
||||
UART4 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM2_USART_INT,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART1.handleInterrupt)
|
||||
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, UART2.handleInterrupt)
|
||||
UART3.Interrupt = interrupt.New(sam.IRQ_SERCOM1_2, UART3.handleInterrupt)
|
||||
UART4.Interrupt = interrupt.New(sam.IRQ_SERCOM2_2, UART4.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the SAM E54 Xplained Pro
|
||||
var (
|
||||
// Extension Header EXT1
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM3_I2CM,
|
||||
SERCOM: 3,
|
||||
}
|
||||
|
||||
// Extension Header EXT2
|
||||
I2C1 = I2C{
|
||||
Bus: sam.SERCOM7_I2CM,
|
||||
SERCOM: 7,
|
||||
}
|
||||
|
||||
// Extension Header EXT3
|
||||
I2C2 = I2C{
|
||||
Bus: sam.SERCOM7_I2CM,
|
||||
SERCOM: 7,
|
||||
}
|
||||
|
||||
// Data Gateway Interface
|
||||
I2C3 = I2C{
|
||||
Bus: sam.SERCOM7_I2CM,
|
||||
SERCOM: 7,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the SAM E54 Xplained Pro
|
||||
var (
|
||||
// Extension Header EXT1
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM4_SPIM,
|
||||
SERCOM: 4,
|
||||
}
|
||||
|
||||
// Extension Header EXT2
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM6_SPIM,
|
||||
SERCOM: 6,
|
||||
}
|
||||
|
||||
// Extension Header EXT3
|
||||
SPI2 = SPI{
|
||||
Bus: sam.SERCOM6_SPIM,
|
||||
SERCOM: 6,
|
||||
}
|
||||
|
||||
// Data Gateway Interface
|
||||
SPI3 = SPI{
|
||||
Bus: sam.SERCOM6_SPIM,
|
||||
SERCOM: 6,
|
||||
}
|
||||
)
|
||||
@@ -25,8 +25,8 @@ const (
|
||||
|
||||
// Analog Pins
|
||||
const (
|
||||
A0 = PA02 // ADC/AIN[0]
|
||||
A1 = PA05 // PWM available, also ADC/AIN[5]
|
||||
A0 = PA02 // PWM available, also ADC/AIN[0]
|
||||
A1 = PA05 // ADC/AIN[5]
|
||||
A2 = PA06 // PWM available, also ADC/AIN[6]
|
||||
A3 = PA07 // PWM available, also ADC/AIN[7]
|
||||
A4 = PB03 // PORTB
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
// +build feather_m4_can
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 = PB17 // UART0 RX/PWM available
|
||||
D1 = PB16 // UART0 TX/PWM available
|
||||
D4 = PA14 // PWM available
|
||||
D5 = PA16 // PWM available
|
||||
D6 = PA18 // PWM available
|
||||
D7 = PB03 // neopixel power
|
||||
D8 = PB02 // built-in neopixel
|
||||
D9 = PA19 // PWM available
|
||||
D10 = PA20 // can be used for PWM or UART1 TX
|
||||
D11 = PA21 // can be used for PWM or UART1 RX
|
||||
D12 = PA22 // PWM available
|
||||
D13 = PA23 // PWM available
|
||||
D21 = PA13 // PWM available
|
||||
D22 = PA12 // PWM available
|
||||
D23 = PB22 // PWM available
|
||||
D24 = PB23 // PWM available
|
||||
D25 = PA17 // PWM available
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 = PA02 // ADC/AIN[0]
|
||||
A1 = PA05 // ADC/AIN[2]
|
||||
A2 = PB08 // ADC/AIN[3]
|
||||
A3 = PB09 // ADC/AIN[4]
|
||||
A4 = PA04 // ADC/AIN[5]
|
||||
A5 = PA06 // ADC/AIN[10]
|
||||
)
|
||||
|
||||
const (
|
||||
LED = D13
|
||||
NEOPIXELS = D8
|
||||
)
|
||||
|
||||
// UART0 aka USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
USBCDC_DP_PIN = PA25
|
||||
)
|
||||
|
||||
const (
|
||||
UART_TX_PIN = D1
|
||||
UART_RX_PIN = D0
|
||||
)
|
||||
|
||||
const (
|
||||
UART2_TX_PIN = A4
|
||||
UART2_RX_PIN = A5
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = D22 // SDA: SERCOM2/PAD[0]
|
||||
SCL_PIN = D21 // SCL: SERCOM2/PAD[1]
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = D25 // SCK: SERCOM1/PAD[1]
|
||||
SPI0_SDO_PIN = D24 // SDO: SERCOM1/PAD[3]
|
||||
SPI0_SDI_PIN = D23 // SDI: SERCOM1/PAD[2]
|
||||
)
|
||||
|
||||
// CAN pins
|
||||
const (
|
||||
CAN0_TX = PA22
|
||||
CAN0_RX = PA23
|
||||
|
||||
CAN1_STANDBY = PB12
|
||||
CAN1_TX = PB14
|
||||
CAN1_RX = PB15
|
||||
BOOST_EN = PB13 // power control of CAN1's TCAN1051HGV (H: enable)
|
||||
|
||||
CAN_STANDBY = CAN1_STANDBY
|
||||
CAN_S = CAN1_STANDBY
|
||||
CAN_TX = CAN1_TX
|
||||
CAN_RX = CAN1_RX
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Adafruit Feather M4 CAN"
|
||||
usb_STRING_MANUFACTURER = "Adafruit"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x239A
|
||||
usb_PID uint16 = 0x80CD
|
||||
)
|
||||
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM5_USART_INT,
|
||||
SERCOM: 5,
|
||||
}
|
||||
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART_INT,
|
||||
SERCOM: 0,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, UART1.handleInterrupt)
|
||||
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART2.handleInterrupt)
|
||||
|
||||
// turn on neopixel
|
||||
D7.Configure(PinConfig{Mode: PinOutput})
|
||||
D7.High()
|
||||
}
|
||||
|
||||
// I2C on the Feather M4.
|
||||
var (
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the Feather M4.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
@@ -41,17 +41,13 @@ 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 (
|
||||
@@ -72,13 +68,6 @@ 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,6 +5,11 @@ 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
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
// +build nucleol031k6
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
const (
|
||||
LED = LED_BUILTIN
|
||||
LED_BUILTIN = LED_GREEN
|
||||
LED_GREEN = PB3
|
||||
)
|
||||
|
||||
const (
|
||||
// Arduino Pins
|
||||
A0 = PA0 // ADC_IN0
|
||||
A1 = PA1 // ADC_IN1
|
||||
A2 = PA3 // ADC_IN3
|
||||
A3 = PA4 // ADC_IN4
|
||||
A4 = PA5 // ADC_IN5 || I2C1_SDA
|
||||
A5 = PA6 // ADC_IN6 || I2C1_SCL
|
||||
A6 = PA7 // ADC_IN7
|
||||
A7 = PA2 // ADC_IN2
|
||||
|
||||
D0 = PA10 // USART1_TX
|
||||
D1 = PA9 // USART1_RX
|
||||
D2 = PA12
|
||||
D3 = PB0 // TIM2_CH3
|
||||
D4 = PB7
|
||||
D5 = PB6 // TIM16_CH1N
|
||||
D6 = PB1 // TIM14_CH1
|
||||
D9 = PA8 // TIM1_CH1
|
||||
D10 = PA11 // SPI_CS || TIM1_CH4
|
||||
D11 = PB5 // SPI1_MOSI || TIM3_CH2
|
||||
D12 = PB4 // SPI1_MISO
|
||||
D13 = PB3 // SPI1_SCK
|
||||
)
|
||||
|
||||
const (
|
||||
// UART pins
|
||||
// PA2 and PA15 are connected to the ST-Link Virtual Com Port (VCP)
|
||||
UART_TX_PIN = PA2
|
||||
UART_RX_PIN = PA15
|
||||
|
||||
// SPI
|
||||
SPI1_SCK_PIN = PB3
|
||||
SPI1_SDI_PIN = PB5
|
||||
SPI1_SDO_PIN = PB4
|
||||
SPI0_SCK_PIN = SPI1_SCK_PIN
|
||||
SPI0_SDI_PIN = SPI1_SDI_PIN
|
||||
SPI0_SDO_PIN = SPI1_SDO_PIN
|
||||
|
||||
// I2C pins
|
||||
// PB6 and PB7 are mapped to CN4 pin 7 and CN4 pin 8 respectively with the
|
||||
// default solder bridge settings
|
||||
I2C0_SCL_PIN = PB7
|
||||
I2C0_SDA_PIN = PB6
|
||||
I2C0_ALT_FUNC = 1
|
||||
)
|
||||
|
||||
var (
|
||||
// USART2 is the hardware serial port connected to the onboard ST-LINK
|
||||
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
|
||||
// Both UART0 and UART1 refer to USART2.
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART2,
|
||||
TxAltFuncSelector: 4,
|
||||
RxAltFuncSelector: 4,
|
||||
}
|
||||
UART1 = &UART0
|
||||
|
||||
// I2C1 is documented, alias to I2C0 as well
|
||||
I2C1 = &I2C{
|
||||
Bus: stm32.I2C1,
|
||||
AltFuncSelector: 1,
|
||||
}
|
||||
I2C0 = I2C1
|
||||
|
||||
// SPI
|
||||
SPI0 = SPI{
|
||||
Bus: stm32.SPI1,
|
||||
AltFuncSelector: 0,
|
||||
}
|
||||
SPI1 = &SPI0
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART0.Interrupt = interrupt.New(stm32.IRQ_USART2, UART0.handleInterrupt)
|
||||
}
|
||||
@@ -13,53 +13,20 @@ 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
|
||||
// I2C pins
|
||||
const (
|
||||
// With default solder bridge settings:
|
||||
// PB6 / Arduino D5 / CN3 Pin 8 is SCL
|
||||
// PB7 / Arduino D4 / CN3 Pin 7 is SDA
|
||||
I2C0_SCL_PIN = PB6
|
||||
I2C0_SDA_PIN = PB7
|
||||
|
||||
// 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 (
|
||||
@@ -73,20 +40,15 @@ 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() {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build atmega nrf sam stm32 fe310 k210
|
||||
// +build atmega nrf sam stm32,!stm32l0 fe310 k210
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -37,6 +37,10 @@ func (p Pin) Low() {
|
||||
p.Set(false)
|
||||
}
|
||||
|
||||
type PWM struct {
|
||||
Pin Pin
|
||||
}
|
||||
|
||||
type ADC struct {
|
||||
Pin Pin
|
||||
}
|
||||
|
||||
@@ -1,931 +0,0 @@
|
||||
// +build avr,atmega1280
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/avr"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
const irq_USART0_RX = avr.IRQ_USART0_RX
|
||||
|
||||
const (
|
||||
portA Pin = iota * 8
|
||||
portB
|
||||
portC
|
||||
portD
|
||||
portE
|
||||
portF
|
||||
portG
|
||||
portH
|
||||
portJ
|
||||
portK
|
||||
portL
|
||||
)
|
||||
|
||||
const (
|
||||
PA0 = portA + 0
|
||||
PA1 = portA + 1
|
||||
PA2 = portA + 2
|
||||
PA3 = portA + 3
|
||||
PA4 = portA + 4
|
||||
PA5 = portA + 5
|
||||
PA6 = portA + 6
|
||||
PA7 = portA + 7
|
||||
PB0 = portB + 0
|
||||
PB1 = portB + 1
|
||||
PB2 = portB + 2
|
||||
PB3 = portB + 3
|
||||
PB4 = portB + 4
|
||||
PB5 = portB + 5
|
||||
PB6 = portB + 6
|
||||
PB7 = portB + 7
|
||||
PC0 = portC + 0
|
||||
PC1 = portC + 1
|
||||
PC2 = portC + 2
|
||||
PC3 = portC + 3
|
||||
PC4 = portC + 4
|
||||
PC5 = portC + 5
|
||||
PC6 = portC + 6
|
||||
PC7 = portC + 7
|
||||
PD0 = portD + 0
|
||||
PD1 = portD + 1
|
||||
PD2 = portD + 2
|
||||
PD3 = portD + 3
|
||||
PD7 = portD + 7
|
||||
PE0 = portE + 0
|
||||
PE1 = portE + 1
|
||||
PE3 = portE + 3
|
||||
PE4 = portE + 4
|
||||
PE5 = portE + 5
|
||||
PE6 = portE + 6
|
||||
PF0 = portF + 0
|
||||
PF1 = portF + 1
|
||||
PF2 = portF + 2
|
||||
PF3 = portF + 3
|
||||
PF4 = portF + 4
|
||||
PF5 = portF + 5
|
||||
PF6 = portF + 6
|
||||
PF7 = portF + 7
|
||||
PG0 = portG + 0
|
||||
PG1 = portG + 1
|
||||
PG2 = portG + 2
|
||||
PG5 = portG + 5
|
||||
PH0 = portH + 0
|
||||
PH1 = portH + 1
|
||||
PH3 = portH + 3
|
||||
PH4 = portH + 4
|
||||
PH5 = portH + 5
|
||||
PH6 = portH + 6
|
||||
PJ0 = portJ + 0
|
||||
PJ1 = portJ + 1
|
||||
PK0 = portK + 0
|
||||
PK1 = portK + 1
|
||||
PK2 = portK + 2
|
||||
PK3 = portK + 3
|
||||
PK4 = portH + 4
|
||||
PK5 = portH + 5
|
||||
PK6 = portH + 6
|
||||
PK7 = portH + 7
|
||||
PL0 = portL + 0
|
||||
PL1 = portL + 1
|
||||
PL2 = portL + 2
|
||||
PL3 = portL + 3
|
||||
PL4 = portL + 4
|
||||
PL5 = portL + 5
|
||||
PL6 = portL + 6
|
||||
PL7 = portL + 7
|
||||
)
|
||||
|
||||
// getPortMask returns the PORTx register and mask for the pin.
|
||||
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||
switch {
|
||||
case p >= PA0 && p <= PA7:
|
||||
return avr.PORTA, 1 << uint8(p-portA)
|
||||
case p >= PB0 && p <= PB7:
|
||||
return avr.PORTB, 1 << uint8(p-portB)
|
||||
case p >= PC0 && p <= PC7:
|
||||
return avr.PORTC, 1 << uint8(p-portC)
|
||||
case p >= PD0 && p <= PD7:
|
||||
return avr.PORTD, 1 << uint8(p-portD)
|
||||
case p >= PE0 && p <= PE6:
|
||||
return avr.PORTE, 1 << uint8(p-portE)
|
||||
case p >= PF0 && p <= PF7:
|
||||
return avr.PORTF, 1 << uint8(p-portF)
|
||||
case p >= PG0 && p <= PG5:
|
||||
return avr.PORTG, 1 << uint8(p-portG)
|
||||
case p >= PH0 && p <= PH6:
|
||||
return avr.PORTH, 1 << uint8(p-portH)
|
||||
case p >= PJ0 && p <= PJ1:
|
||||
return avr.PORTJ, 1 << uint8(p-portJ)
|
||||
case p >= PK0 && p <= PK7:
|
||||
return avr.PORTK, 1 << uint8(p-portK)
|
||||
case p >= PL0 && p <= PL7:
|
||||
return avr.PORTL, 1 << uint8(p-portL)
|
||||
default:
|
||||
return avr.PORTA, 255
|
||||
}
|
||||
}
|
||||
|
||||
// PWM is one PWM peripheral, which consists of a counter and two output
|
||||
// channels (that can be connected to two fixed pins). You can set the frequency
|
||||
// using SetPeriod, but only for all the channels in this PWM peripheral at
|
||||
// once.
|
||||
type PWM struct {
|
||||
num uint8
|
||||
}
|
||||
|
||||
var (
|
||||
Timer0 = PWM{0} // 8 bit timer for PB7 and PG5
|
||||
Timer1 = PWM{1} // 16 bit timer for PB5 and PB6
|
||||
Timer2 = PWM{2} // 8 bit timer for PB4 and PH6
|
||||
Timer3 = PWM{3} // 16 bit timer for PE3, PE4 and PE5
|
||||
Timer4 = PWM{4} // 16 bit timer for PH3, PH4 and PH5
|
||||
Timer5 = PWM{5} // 16 bit timer for PL3, PL4 and PL5
|
||||
)
|
||||
|
||||
// Configure enables and configures this PWM.
|
||||
//
|
||||
// For the two 8 bit timers, there is only a limited number of periods
|
||||
// available, namely the CPU frequency divided by 256 and again divided by 1, 8,
|
||||
// 64, 256, or 1024. For a MCU running at 16MHz, this would be a period of 16µs,
|
||||
// 128µs, 1024µs, 4096µs, or 16384µs.
|
||||
func (pwm PWM) Configure(config PWMConfig) error {
|
||||
|
||||
switch pwm.num {
|
||||
case 0, 2: // 8-bit timers (Timer/counter 0 and Timer/counter 2)
|
||||
// Calculate the timer prescaler.
|
||||
// While we could configure a flexible top, that would sacrifice one of
|
||||
// the PWM output compare registers and thus a PWM channel. I've chosen
|
||||
// to instead limit this timer to a fixed number of frequencies.
|
||||
var prescaler uint8
|
||||
switch config.Period {
|
||||
case 0, (uint64(1e9) * 256 * 1) / uint64(CPUFrequency()):
|
||||
prescaler = 1
|
||||
case (uint64(1e9) * 256 * 8) / uint64(CPUFrequency()):
|
||||
prescaler = 2
|
||||
case (uint64(1e9) * 256 * 64) / uint64(CPUFrequency()):
|
||||
prescaler = 3
|
||||
case (uint64(1e9) * 256 * 256) / uint64(CPUFrequency()):
|
||||
prescaler = 4
|
||||
case (uint64(1e9) * 256 * 1024) / uint64(CPUFrequency()):
|
||||
prescaler = 5
|
||||
default:
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
|
||||
if pwm.num == 0 {
|
||||
avr.TCCR0B.Set(prescaler)
|
||||
// Set the PWM mode to fast PWM (mode = 3).
|
||||
avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01)
|
||||
} else {
|
||||
avr.TCCR2B.Set(prescaler)
|
||||
// Set the PWM mode to fast PWM (mode = 3).
|
||||
avr.TCCR2A.Set(avr.TCCR2A_WGM20 | avr.TCCR2A_WGM21)
|
||||
}
|
||||
case 1, 3, 4, 5:
|
||||
// The top value is the number of PWM ticks a PWM period takes. It is
|
||||
// initially picked assuming an unlimited counter top and no PWM
|
||||
// prescaler.
|
||||
var top uint64
|
||||
if config.Period == 0 {
|
||||
// Use a top appropriate for LEDs. Picking a relatively low period
|
||||
// here (0xff) for consistency with the other timers.
|
||||
top = 0xff
|
||||
} else {
|
||||
// The formula below calculates the following formula, optimized:
|
||||
// top = period * (CPUFrequency() / 1e9)
|
||||
// By dividing the CPU frequency first (an operation that is easily
|
||||
// optimized away) the period has less chance of overflowing.
|
||||
top = config.Period * (uint64(CPUFrequency()) / 1000000) / 1000
|
||||
}
|
||||
|
||||
// The ideal PWM period may be larger than would fit in the PWM counter,
|
||||
// which is 16 bits (see maxTop). Therefore, try to make the PWM clock
|
||||
// speed lower with a prescaler to make the top value fit the maximum
|
||||
// top value.
|
||||
|
||||
const maxTop = 0x10000
|
||||
var prescalingTop uint8
|
||||
switch {
|
||||
case top <= maxTop:
|
||||
prescalingTop = 3<<3 | 1 // no prescaling
|
||||
case top/8 <= maxTop:
|
||||
prescalingTop = 3<<3 | 2 // divide by 8
|
||||
top /= 8
|
||||
case top/64 <= maxTop:
|
||||
prescalingTop = 3<<3 | 3 // divide by 64
|
||||
top /= 64
|
||||
case top/256 <= maxTop:
|
||||
prescalingTop = 3<<3 | 4 // divide by 256
|
||||
top /= 256
|
||||
case top/1024 <= maxTop:
|
||||
prescalingTop = 3<<3 | 5 // divide by 1024
|
||||
top /= 1024
|
||||
default:
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
|
||||
// A top of 0x10000 is at 100% duty cycle. Subtract one because the
|
||||
// counter counts from 0, not 1 (avoiding an off-by-one).
|
||||
top -= 1
|
||||
|
||||
switch pwm.num {
|
||||
case 1:
|
||||
avr.TCCR1A.Set(avr.TCCR1A_WGM11)
|
||||
avr.TCCR1B.Set(prescalingTop)
|
||||
avr.ICR1H.Set(uint8(top >> 8))
|
||||
avr.ICR1L.Set(uint8(top))
|
||||
case 3:
|
||||
avr.TCCR3A.Set(avr.TCCR3A_WGM31)
|
||||
avr.TCCR3B.Set(prescalingTop)
|
||||
avr.ICR3H.Set(uint8(top >> 8))
|
||||
avr.ICR3L.Set(uint8(top))
|
||||
case 4:
|
||||
avr.TCCR4A.Set(avr.TCCR4A_WGM41)
|
||||
avr.TCCR4B.Set(prescalingTop)
|
||||
avr.ICR4H.Set(uint8(top >> 8))
|
||||
avr.ICR4L.Set(uint8(top))
|
||||
case 5:
|
||||
avr.TCCR5A.Set(avr.TCCR5A_WGM51)
|
||||
avr.TCCR5B.Set(prescalingTop)
|
||||
avr.ICR5H.Set(uint8(top >> 8))
|
||||
avr.ICR5L.Set(uint8(top))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetPeriod updates the period of this PWM peripheral.
|
||||
// To set a particular frequency, use the following formula:
|
||||
//
|
||||
// period = 1e9 / frequency
|
||||
//
|
||||
// If you use a period of 0, a period that works well for LEDs will be picked.
|
||||
//
|
||||
// SetPeriod will not change the prescaler, but also won't change the current
|
||||
// value in any of the channels. This means that you may need to update the
|
||||
// value for the particular channel.
|
||||
//
|
||||
// Note that you cannot pick any arbitrary period after the PWM peripheral has
|
||||
// been configured. If you want to switch between frequencies, pick the lowest
|
||||
// frequency (longest period) once when calling Configure and adjust the
|
||||
// frequency here as needed.
|
||||
func (pwm PWM) SetPeriod(period uint64) error {
|
||||
if pwm.num == 0 || pwm.num == 2 {
|
||||
return ErrPWMPeriodTooLong // TODO better error message
|
||||
}
|
||||
|
||||
// The top value is the number of PWM ticks a PWM period takes. It is
|
||||
// initially picked assuming an unlimited counter top and no PWM
|
||||
// prescaler.
|
||||
var top uint64
|
||||
if period == 0 {
|
||||
// Use a top appropriate for LEDs. Picking a relatively low period
|
||||
// here (0xff) for consistency with the other timers.
|
||||
top = 0xff
|
||||
} else {
|
||||
// The formula below calculates the following formula, optimized:
|
||||
// top = period * (CPUFrequency() / 1e9)
|
||||
// By dividing the CPU frequency first (an operation that is easily
|
||||
// optimized away) the period has less chance of overflowing.
|
||||
top = period * (uint64(CPUFrequency()) / 1000000) / 1000
|
||||
}
|
||||
|
||||
var prescaler uint8
|
||||
|
||||
switch pwm.num {
|
||||
case 1:
|
||||
prescaler = avr.TCCR1B.Get() & 0x7
|
||||
case 3:
|
||||
prescaler = avr.TCCR3B.Get() & 0x7
|
||||
case 4:
|
||||
prescaler = avr.TCCR4B.Get() & 0x7
|
||||
case 5:
|
||||
prescaler = avr.TCCR5B.Get() & 0x7
|
||||
}
|
||||
|
||||
switch prescaler {
|
||||
case 1:
|
||||
top /= 1
|
||||
case 2:
|
||||
top /= 8
|
||||
case 3:
|
||||
top /= 64
|
||||
case 4:
|
||||
top /= 256
|
||||
case 5:
|
||||
top /= 1024
|
||||
}
|
||||
|
||||
// A top of 0x10000 is at 100% duty cycle. Subtract one because the counter
|
||||
// counts from 0, not 1 (avoiding an off-by-one).
|
||||
top -= 1
|
||||
|
||||
if top > 0xffff {
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
|
||||
switch pwm.num {
|
||||
case 1:
|
||||
// Warning: this change is not atomic!
|
||||
avr.ICR1H.Set(uint8(top >> 8))
|
||||
avr.ICR1L.Set(uint8(top))
|
||||
|
||||
// ... and because of that, set the counter back to zero to avoid most of
|
||||
// the effects of this non-atomicity.
|
||||
avr.TCNT1H.Set(0)
|
||||
avr.TCNT1L.Set(0)
|
||||
case 3:
|
||||
// Warning: this change is not atomic!
|
||||
avr.ICR3H.Set(uint8(top >> 8))
|
||||
avr.ICR3L.Set(uint8(top))
|
||||
|
||||
// ... and because of that, set the counter back to zero to avoid most of
|
||||
// the effects of this non-atomicity.
|
||||
avr.TCNT3H.Set(0)
|
||||
avr.TCNT3L.Set(0)
|
||||
case 4:
|
||||
// Warning: this change is not atomic!
|
||||
avr.ICR4H.Set(uint8(top >> 8))
|
||||
avr.ICR4L.Set(uint8(top))
|
||||
|
||||
// ... and because of that, set the counter back to zero to avoid most of
|
||||
// the effects of this non-atomicity.
|
||||
avr.TCNT4H.Set(0)
|
||||
avr.TCNT4L.Set(0)
|
||||
case 5:
|
||||
// Warning: this change is not atomic!
|
||||
avr.ICR5H.Set(uint8(top >> 8))
|
||||
avr.ICR5L.Set(uint8(top))
|
||||
|
||||
// ... and because of that, set the counter back to zero to avoid most of
|
||||
// the effects of this non-atomicity.
|
||||
avr.TCNT5H.Set(0)
|
||||
avr.TCNT5L.Set(0)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Top returns the current counter top, for use in duty cycle calculation. It
|
||||
// will only change with a call to Configure or SetPeriod, otherwise it is
|
||||
// constant.
|
||||
//
|
||||
// The value returned here is hardware dependent. In general, it's best to treat
|
||||
// it as an opaque value that can be divided by some number and passed to Set
|
||||
// (see Set documentation for more information).
|
||||
func (pwm PWM) Top() uint32 {
|
||||
switch pwm.num {
|
||||
case 1:
|
||||
// Timer 1 has a configurable top value.
|
||||
low := avr.ICR1L.Get()
|
||||
high := avr.ICR1H.Get()
|
||||
return uint32(high)<<8 | uint32(low) + 1
|
||||
case 3:
|
||||
// Timer 3 has a configurable top value.
|
||||
low := avr.ICR3L.Get()
|
||||
high := avr.ICR3H.Get()
|
||||
return uint32(high)<<8 | uint32(low) + 1
|
||||
case 4:
|
||||
// Timer 4 has a configurable top value.
|
||||
low := avr.ICR4L.Get()
|
||||
high := avr.ICR4H.Get()
|
||||
return uint32(high)<<8 | uint32(low) + 1
|
||||
case 5:
|
||||
// Timer 5 has a configurable top value.
|
||||
low := avr.ICR5L.Get()
|
||||
high := avr.ICR5H.Get()
|
||||
return uint32(high)<<8 | uint32(low) + 1
|
||||
}
|
||||
|
||||
// Other timers go from 0 to 0xff (0x100 or 256 in total).
|
||||
return 256
|
||||
}
|
||||
|
||||
// Counter returns the current counter value of the timer in this PWM
|
||||
// peripheral. It may be useful for debugging.
|
||||
func (pwm PWM) Counter() uint32 {
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
return uint32(avr.TCNT0.Get())
|
||||
case 1:
|
||||
mask := interrupt.Disable()
|
||||
low := avr.TCNT1L.Get()
|
||||
high := avr.TCNT1H.Get()
|
||||
interrupt.Restore(mask)
|
||||
return uint32(high)<<8 | uint32(low)
|
||||
case 2:
|
||||
return uint32(avr.TCNT2.Get())
|
||||
case 3:
|
||||
mask := interrupt.Disable()
|
||||
low := avr.TCNT3L.Get()
|
||||
high := avr.TCNT3H.Get()
|
||||
interrupt.Restore(mask)
|
||||
return uint32(high)<<8 | uint32(low)
|
||||
case 4:
|
||||
mask := interrupt.Disable()
|
||||
low := avr.TCNT4L.Get()
|
||||
high := avr.TCNT4H.Get()
|
||||
interrupt.Restore(mask)
|
||||
return uint32(high)<<8 | uint32(low)
|
||||
case 5:
|
||||
mask := interrupt.Disable()
|
||||
low := avr.TCNT5L.Get()
|
||||
high := avr.TCNT5H.Get()
|
||||
interrupt.Restore(mask)
|
||||
return uint32(high)<<8 | uint32(low)
|
||||
}
|
||||
|
||||
// Unknown PWM.
|
||||
return 0
|
||||
}
|
||||
|
||||
// Period returns the used PWM period in nanoseconds. It might deviate slightly
|
||||
// from the configured period due to rounding.
|
||||
func (pwm PWM) Period() uint64 {
|
||||
var prescaler uint8
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
prescaler = avr.TCCR0B.Get() & 0x7
|
||||
case 1:
|
||||
prescaler = avr.TCCR1B.Get() & 0x7
|
||||
case 2:
|
||||
prescaler = avr.TCCR2B.Get() & 0x7
|
||||
case 3:
|
||||
prescaler = avr.TCCR3B.Get() & 0x7
|
||||
case 4:
|
||||
prescaler = avr.TCCR4B.Get() & 0x7
|
||||
case 5:
|
||||
prescaler = avr.TCCR5B.Get() & 0x7
|
||||
}
|
||||
top := uint64(pwm.Top())
|
||||
switch prescaler {
|
||||
case 1: // prescaler 1
|
||||
return 1 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
case 2: // prescaler 8
|
||||
return 8 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
case 3: // prescaler 64
|
||||
return 64 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
case 4: // prescaler 256
|
||||
return 256 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
case 5: // prescaler 1024
|
||||
return 1024 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
default: // unknown clock source
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Channel returns a PWM channel for the given pin.
|
||||
func (pwm PWM) Channel(pin Pin) (uint8, error) {
|
||||
pin.Configure(PinConfig{Mode: PinOutput})
|
||||
pin.Low()
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
switch pin {
|
||||
case PB7: // channel A
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||
return 0, nil
|
||||
case PG5: // channel B
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||
return 1, nil
|
||||
}
|
||||
case 1:
|
||||
switch pin {
|
||||
case PB5: // channel A
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||
return 0, nil
|
||||
case PB6: // channel B
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||
return 1, nil
|
||||
}
|
||||
case 2:
|
||||
switch pin {
|
||||
case PB4: // channel A
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||
return 0, nil
|
||||
case PH6: // channel B
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||
return 1, nil
|
||||
}
|
||||
case 3:
|
||||
switch pin {
|
||||
case PE3: // channel A
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1)
|
||||
return 0, nil
|
||||
case PE4: //channel B
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1)
|
||||
return 1, nil
|
||||
case PE5: //channel C
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1)
|
||||
return 2, nil
|
||||
}
|
||||
case 4:
|
||||
switch pin {
|
||||
case PH3: // channel A
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1)
|
||||
return 0, nil
|
||||
case PH4: //channel B
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1)
|
||||
return 1, nil
|
||||
case PH5: //channel C
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1)
|
||||
return 2, nil
|
||||
}
|
||||
case 5:
|
||||
switch pin {
|
||||
case PL3: // channel A
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1)
|
||||
return 0, nil
|
||||
case PL4: //channel B
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1)
|
||||
return 1, nil
|
||||
case PL5: //channel C
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1)
|
||||
return 2, nil
|
||||
}
|
||||
}
|
||||
return 0, ErrInvalidOutputPin
|
||||
}
|
||||
|
||||
// SetInverting sets whether to invert the output of this channel.
|
||||
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
|
||||
// the time and low for the rest. Inverting flips the output as if a NOT gate
|
||||
// was placed at the output, meaning that the output would be 25% low and 75%
|
||||
// high with a duty cycle of 25%.
|
||||
//
|
||||
// Note: the invert state may not be applied on the AVR until the next call to
|
||||
// ch.Set().
|
||||
func (pwm PWM) SetInverting(channel uint8, inverting bool) {
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
switch channel {
|
||||
case 0: // channel A, PB7
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 7) // PB7 high
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A0)
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 7) // PB7 low
|
||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A0)
|
||||
}
|
||||
case 1: // channel B, PG5
|
||||
if inverting {
|
||||
avr.PORTG.SetBits(1 << 5) // PG5 high
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B0)
|
||||
} else {
|
||||
avr.PORTG.ClearBits(1 << 5) // PG5 low
|
||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B0)
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
// Note: the COM1A0/COM1B0 bit is not set with the configuration below.
|
||||
// It will be set the following call to Set(), however.
|
||||
switch channel {
|
||||
case 0: // channel A, PB5
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 5) // PB5 high
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 5) // PB5 low
|
||||
}
|
||||
case 1: // channel B, PB6
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 6) // PB6 high
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 6) // PB6 low
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
switch channel {
|
||||
case 0: // channel A, PB4
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 4) // PB4 high
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A0)
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 4) // PB4 low
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A0)
|
||||
}
|
||||
case 1: // channel B, PH6
|
||||
if inverting {
|
||||
avr.PORTH.SetBits(1 << 6) // PH6 high
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B0)
|
||||
} else {
|
||||
avr.PORTH.ClearBits(1 << 6) // PH6 low
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B0)
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
|
||||
// It will be set the following call to Set(), however.
|
||||
switch channel {
|
||||
case 0: // channel A, PE3
|
||||
if inverting {
|
||||
avr.PORTE.SetBits(1 << 3) // PE3 high
|
||||
} else {
|
||||
avr.PORTE.ClearBits(1 << 3) // PE3 low
|
||||
}
|
||||
case 1: // channel B, PE4
|
||||
if inverting {
|
||||
avr.PORTE.SetBits(1 << 4) // PE4 high
|
||||
} else {
|
||||
avr.PORTE.ClearBits(1 << 4) // PE4 low
|
||||
}
|
||||
case 2: // channel C, PE5
|
||||
if inverting {
|
||||
avr.PORTE.SetBits(1 << 5) // PE4 high
|
||||
} else {
|
||||
avr.PORTE.ClearBits(1 << 5) // PE4 low
|
||||
}
|
||||
}
|
||||
case 4:
|
||||
// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
|
||||
// It will be set the following call to Set(), however.
|
||||
switch channel {
|
||||
case 0: // channel A, PH3
|
||||
if inverting {
|
||||
avr.PORTH.SetBits(1 << 3) // PH3 high
|
||||
} else {
|
||||
avr.PORTH.ClearBits(1 << 3) // PH3 low
|
||||
}
|
||||
case 1: // channel B, PH4
|
||||
if inverting {
|
||||
avr.PORTH.SetBits(1 << 4) // PH4 high
|
||||
} else {
|
||||
avr.PORTH.ClearBits(1 << 4) // PH4 low
|
||||
}
|
||||
case 2: // channel C, PH5
|
||||
if inverting {
|
||||
avr.PORTH.SetBits(1 << 5) // PH4 high
|
||||
} else {
|
||||
avr.PORTH.ClearBits(1 << 5) // PH4 low
|
||||
}
|
||||
}
|
||||
case 5:
|
||||
// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
|
||||
// It will be set the following call to Set(), however.
|
||||
switch channel {
|
||||
case 0: // channel A, PL3
|
||||
if inverting {
|
||||
avr.PORTL.SetBits(1 << 3) // PL3 high
|
||||
} else {
|
||||
avr.PORTL.ClearBits(1 << 3) // PL3 low
|
||||
}
|
||||
case 1: // channel B, PL4
|
||||
if inverting {
|
||||
avr.PORTL.SetBits(1 << 4) // PL4 high
|
||||
} else {
|
||||
avr.PORTL.ClearBits(1 << 4) // PL4 low
|
||||
}
|
||||
case 2: // channel C, PH5
|
||||
if inverting {
|
||||
avr.PORTL.SetBits(1 << 5) // PL4 high
|
||||
} else {
|
||||
avr.PORTL.ClearBits(1 << 5) // PL4 low
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set updates the channel value. This is used to control the channel duty
|
||||
// cycle, in other words the fraction of time the channel output is high (or low
|
||||
// when inverted). For example, to set it to a 25% duty cycle, use:
|
||||
//
|
||||
// pwm.Set(channel, pwm.Top() / 4)
|
||||
//
|
||||
// pwm.Set(channel, 0) will set the output to low and pwm.Set(channel,
|
||||
// pwm.Top()) will set the output to high, assuming the output isn't inverted.
|
||||
func (pwm PWM) Set(channel uint8, value uint32) {
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
value := uint16(value)
|
||||
switch channel {
|
||||
case 0: // channel A
|
||||
if value == 0 {
|
||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A1)
|
||||
} else {
|
||||
avr.OCR0A.Set(uint8(value - 1))
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||
}
|
||||
case 1: // channel B
|
||||
if value == 0 {
|
||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B1)
|
||||
} else {
|
||||
avr.OCR0B.Set(uint8(value) - 1)
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
mask := interrupt.Disable()
|
||||
switch channel {
|
||||
case 0: // channel A, PB5
|
||||
if value == 0 {
|
||||
avr.TCCR1A.ClearBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR1AH.Set(uint8(value >> 8))
|
||||
avr.OCR1AL.Set(uint8(value))
|
||||
if avr.PORTB.HasBits(1 << 5) { // is PB1 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||
}
|
||||
}
|
||||
case 1: // channel B, PB6
|
||||
if value == 0 {
|
||||
avr.TCCR1A.ClearBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR1BH.Set(uint8(value >> 8))
|
||||
avr.OCR1BL.Set(uint8(value))
|
||||
if avr.PORTB.HasBits(1 << 6) { // is PB6 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||
}
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
case 2:
|
||||
value := uint16(value)
|
||||
switch channel {
|
||||
case 0: // channel A
|
||||
if value == 0 {
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A1)
|
||||
} else {
|
||||
avr.OCR2A.Set(uint8(value - 1))
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||
}
|
||||
case 1: // channel B
|
||||
if value == 0 {
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B1)
|
||||
} else {
|
||||
avr.OCR2B.Set(uint8(value - 1))
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
mask := interrupt.Disable()
|
||||
switch channel {
|
||||
case 0: // channel A, PE3
|
||||
if value == 0 {
|
||||
avr.TCCR3A.ClearBits(avr.TCCR3A_COM3A1 | avr.TCCR3A_COM3A0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR3AH.Set(uint8(value >> 8))
|
||||
avr.OCR3AL.Set(uint8(value))
|
||||
if avr.PORTE.HasBits(1 << 3) { // is PE3 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1 | avr.TCCR3A_COM3A0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1)
|
||||
}
|
||||
}
|
||||
case 1: // channel B, PE4
|
||||
if value == 0 {
|
||||
avr.TCCR3A.ClearBits(avr.TCCR3A_COM3B1 | avr.TCCR3A_COM3B0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR3BH.Set(uint8(value >> 8))
|
||||
avr.OCR3BL.Set(uint8(value))
|
||||
if avr.PORTE.HasBits(1 << 4) { // is PE4 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1 | avr.TCCR3A_COM3B0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1)
|
||||
}
|
||||
}
|
||||
case 2: // channel C, PE5
|
||||
if value == 0 {
|
||||
avr.TCCR3A.ClearBits(avr.TCCR3A_COM3C1 | avr.TCCR3A_COM3C0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR3CH.Set(uint8(value >> 8))
|
||||
avr.OCR3CL.Set(uint8(value))
|
||||
if avr.PORTE.HasBits(1 << 5) { // is PE5 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1 | avr.TCCR3A_COM3C0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1)
|
||||
}
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
case 4:
|
||||
mask := interrupt.Disable()
|
||||
switch channel {
|
||||
case 0: // channel A, PH3
|
||||
if value == 0 {
|
||||
avr.TCCR4A.ClearBits(avr.TCCR4A_COM4A1 | avr.TCCR4A_COM4A0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR4AH.Set(uint8(value >> 8))
|
||||
avr.OCR4AL.Set(uint8(value))
|
||||
if avr.PORTH.HasBits(1 << 3) { // is PH3 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1 | avr.TCCR4A_COM4A0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1)
|
||||
}
|
||||
}
|
||||
case 1: // channel B, PH4
|
||||
if value == 0 {
|
||||
avr.TCCR4A.ClearBits(avr.TCCR4A_COM4B1 | avr.TCCR4A_COM4B0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR4BH.Set(uint8(value >> 8))
|
||||
avr.OCR4BL.Set(uint8(value))
|
||||
if avr.PORTH.HasBits(1 << 4) { // is PH4 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1 | avr.TCCR4A_COM4B0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1)
|
||||
}
|
||||
}
|
||||
case 2: // channel C, PH5
|
||||
if value == 0 {
|
||||
avr.TCCR4A.ClearBits(avr.TCCR4A_COM4C1 | avr.TCCR4A_COM4C0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR4CH.Set(uint8(value >> 8))
|
||||
avr.OCR4CL.Set(uint8(value))
|
||||
if avr.PORTH.HasBits(1 << 5) { // is PH5 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1 | avr.TCCR4A_COM4C0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1)
|
||||
}
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
case 5:
|
||||
mask := interrupt.Disable()
|
||||
switch channel {
|
||||
case 0: // channel A, PL3
|
||||
if value == 0 {
|
||||
avr.TCCR5A.ClearBits(avr.TCCR5A_COM5A1 | avr.TCCR5A_COM5A0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR5AH.Set(uint8(value >> 8))
|
||||
avr.OCR5AL.Set(uint8(value))
|
||||
if avr.PORTL.HasBits(1 << 3) { // is PL3 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1 | avr.TCCR5A_COM5A0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1)
|
||||
}
|
||||
}
|
||||
case 1: // channel B, PL4
|
||||
if value == 0 {
|
||||
avr.TCCR5A.ClearBits(avr.TCCR5A_COM5B1 | avr.TCCR5A_COM5B0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR5BH.Set(uint8(value >> 8))
|
||||
avr.OCR5BL.Set(uint8(value))
|
||||
if avr.PORTL.HasBits(1 << 4) { // is PL4 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1 | avr.TCCR5A_COM5B0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1)
|
||||
}
|
||||
}
|
||||
case 2: // channel C, PL5
|
||||
if value == 0 {
|
||||
avr.TCCR5A.ClearBits(avr.TCCR5A_COM5C1 | avr.TCCR5A_COM5C0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR5CH.Set(uint8(value >> 8))
|
||||
avr.OCR5CL.Set(uint8(value))
|
||||
if avr.PORTL.HasBits(1 << 5) { // is PL5 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1 | avr.TCCR5A_COM5C0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1)
|
||||
}
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
}
|
||||
|
||||
// SPI configuration
|
||||
var SPI0 = SPI{
|
||||
spcr: avr.SPCR,
|
||||
spdr: avr.SPDR,
|
||||
spsr: avr.SPSR,
|
||||
sck: PB1,
|
||||
sdo: PB2,
|
||||
sdi: PB3,
|
||||
cs: PB0}
|
||||
@@ -4,7 +4,6 @@ package machine
|
||||
|
||||
import (
|
||||
"device/avr"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
@@ -22,432 +21,71 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||
}
|
||||
}
|
||||
|
||||
// PWM is one PWM peripheral, which consists of a counter and two output
|
||||
// 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
|
||||
// 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)
|
||||
}
|
||||
|
||||
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))
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() error {
|
||||
switch pwm.Pin / 8 {
|
||||
case 0: // port B
|
||||
avr.DDRB.SetBits(1 << uint8(pwm.Pin))
|
||||
case 2: // port D
|
||||
avr.DDRD.SetBits(1 << uint8(pwm.Pin-16))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
// 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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+181
-373
@@ -31,8 +31,8 @@ const (
|
||||
PinInput PinMode = 9
|
||||
PinInputPullup PinMode = 10
|
||||
PinOutput PinMode = 11
|
||||
PinTCC PinMode = PinTimer
|
||||
PinTCCAlt PinMode = PinTimerAlt
|
||||
PinPWM PinMode = PinTimer
|
||||
PinPWMAlt PinMode = PinTimerAlt
|
||||
PinInputPulldown PinMode = 12
|
||||
)
|
||||
|
||||
@@ -433,18 +433,7 @@ func (a ADC) Get() uint16 {
|
||||
sam.ADC.CTRLA.ClearBits(sam.ADC_CTRLA_ENABLE)
|
||||
waitADCSync()
|
||||
|
||||
// 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
|
||||
return uint16(val) << 4 // scales from 12 to 16-bit result
|
||||
}
|
||||
|
||||
func (a ADC) getADCChannel() uint8 {
|
||||
@@ -1432,373 +1421,201 @@ func (spi SPI) txrx24mhz(tx, rx []byte) {
|
||||
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
|
||||
// TCC is one timer/counter peripheral, which consists of a counter and multiple
|
||||
// output channels (that can be connected to actual pins). You can set the
|
||||
// frequency using SetPeriod, but only for all the channels in this TCC
|
||||
// peripheral at once.
|
||||
type TCC sam.TCC_Type
|
||||
// PWM
|
||||
const period = 0xFFFF
|
||||
|
||||
// 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)
|
||||
)
|
||||
// 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_)
|
||||
|
||||
//go:inline
|
||||
func (tcc *TCC) timer() *sam.TCC_Type {
|
||||
return (*sam.TCC_Type)(tcc)
|
||||
}
|
||||
|
||||
// Configure enables and configures this TCC.
|
||||
func (tcc *TCC) Configure(config PWMConfig) error {
|
||||
// Enable the clock source for this timer.
|
||||
switch tcc.timer() {
|
||||
case sam.TCC0:
|
||||
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC0_)
|
||||
// Use GCLK0 for TCC0/TCC1
|
||||
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_TCC0_TCC1 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
||||
}
|
||||
case sam.TCC1:
|
||||
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC1_)
|
||||
// Use GCLK0 for TCC0/TCC1
|
||||
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_TCC0_TCC1 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
||||
}
|
||||
case sam.TCC2:
|
||||
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC2_)
|
||||
// Use GCLK0 for TCC2/TC3
|
||||
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_TCC2_TC3 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
||||
}
|
||||
// Use GCLK0 for TCC0/TCC1
|
||||
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_TCC0_TCC1 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
||||
}
|
||||
|
||||
// Disable timer (if it was enabled). This is necessary because
|
||||
// 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 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) {
|
||||
}
|
||||
|
||||
// Use "Normal PWM" (single-slope PWM)
|
||||
tcc.timer().WAVE.Set(sam.TCC_WAVE_WAVEGEN_NPWM)
|
||||
|
||||
// Wait for synchronization of all changed registers.
|
||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||
timer.WAVE.SetBits(sam.TCC_WAVE_WAVEGEN_NPWM)
|
||||
// Wait for synchronization
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_WAVE) {
|
||||
}
|
||||
|
||||
// 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 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) {
|
||||
}
|
||||
|
||||
// Return any error that might have occured in the tcc.setPeriod call.
|
||||
return err
|
||||
}
|
||||
// Set pin as output
|
||||
sam.PORT.DIRSET0.Set(1 << uint8(pwm.Pin))
|
||||
// Set pin to low
|
||||
sam.PORT.OUTCLR0.Set(1 << uint8(pwm.Pin))
|
||||
|
||||
// SetPeriod updates the period of this TCC peripheral.
|
||||
// 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)
|
||||
}
|
||||
// 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
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// setPeriod sets the period of this TCC, possibly updating the prescaler as
|
||||
// well. The prescaler can only modified when the TCC is disabled, that is, in
|
||||
// the Configure function.
|
||||
func (tcc *TCC) setPeriod(period uint64, updatePrescaler bool) error {
|
||||
var top uint64
|
||||
if period == 0 {
|
||||
// Make sure the TOP value is at 0xffff (enough for a 16-bit timer).
|
||||
top = 0xffff
|
||||
if pwm.Pin&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := pwm.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_PMUX0_PMUXO_Pos))
|
||||
} else {
|
||||
// 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 {
|
||||
// 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))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Top returns the current counter top, for use in duty cycle calculation. It
|
||||
// will only change with a call to Configure or SetPeriod, otherwise it is
|
||||
// constant.
|
||||
//
|
||||
// The value returned here is hardware dependent. In general, it's best to treat
|
||||
// it as an opaque value that can be divided by some number and passed to Set
|
||||
// (see Set documentation for more information).
|
||||
func (tcc *TCC) Top() uint32 {
|
||||
return tcc.timer().PER.Get() + 1
|
||||
}
|
||||
|
||||
// Counter returns the current counter value of the timer in this TCC
|
||||
// peripheral. It may be useful for debugging.
|
||||
func (tcc *TCC) Counter() uint32 {
|
||||
tcc.timer().CTRLBSET.Set(sam.TCC_CTRLBSET_CMD_READSYNC << sam.TCC_CTRLBSET_CMD_Pos)
|
||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||
}
|
||||
return tcc.timer().COUNT.Get()
|
||||
}
|
||||
|
||||
// Some constans to make pinTimerMapping below easier to read.
|
||||
const (
|
||||
pinTCC0 = 1
|
||||
pinTCC1 = 2
|
||||
pinTCC2 = 3
|
||||
pinTimerCh0 = 0 << 3
|
||||
pinTimerCh2 = 1 << 3
|
||||
pinTCC0Ch0 = pinTCC0 | pinTimerCh0
|
||||
pinTCC0Ch2 = pinTCC0 | pinTimerCh2
|
||||
pinTCC1Ch0 = pinTCC1 | pinTimerCh0
|
||||
pinTCC1Ch2 = pinTCC1 | pinTimerCh2
|
||||
pinTCC2Ch0 = pinTCC2 | pinTimerCh0
|
||||
)
|
||||
|
||||
// Mapping from pin number to TCC peripheral and channel using a special
|
||||
// encoding. Note that only TCC0-TCC2 are included, not TC3 and up.
|
||||
// Every byte is split in two nibbles where the low nibble describes PinTCC and
|
||||
// the high nibble describes PinTCCAlt. Within a nibble, there is one bit that
|
||||
// indicates Ch0/Ch1 or Ch2/Ch3, and three other bits that contain the TCC
|
||||
// peripheral number plus one (to distinguish between TCC0Ch0 and 0).
|
||||
//
|
||||
// The encoding can be so compact because all pins are configured in pairs, so
|
||||
// if you know PA00 you can infer the configuration of PA01. And only channel 0
|
||||
// or 2 need to be included (taking up just one bit), because channel 0 and 2
|
||||
// are only ever used on odd pins and channel 1 and 3 on even pins, again using
|
||||
// the pin pair pattern to reduce the amount of information needed to be stored.
|
||||
//
|
||||
// Datasheet: https://cdn.sparkfun.com/datasheets/Dev/Arduino/Boards/Atmel-42181-SAM-D21_Datasheet.pdf
|
||||
var pinTimerMapping = [...]uint8{
|
||||
// page 21
|
||||
PA00 / 2: pinTCC2Ch0 | 0,
|
||||
PA04 / 2: pinTCC0Ch0 | 0,
|
||||
PA06 / 2: pinTCC1Ch0 | 0,
|
||||
PA08 / 2: pinTCC0Ch0 | pinTCC1Ch2<<4,
|
||||
PA10 / 2: pinTCC1Ch0 | pinTCC0Ch2<<4,
|
||||
// page 22
|
||||
PB10 / 2: 0 | pinTCC0Ch0<<4,
|
||||
PB12 / 2: 0 | pinTCC0Ch2<<4,
|
||||
PA12 / 2: pinTCC2Ch0 | pinTCC0Ch2<<4,
|
||||
PA14 / 2: 0 | pinTCC0Ch0<<4,
|
||||
PA16 / 2: pinTCC2Ch0 | pinTCC0Ch2<<4,
|
||||
PA18 / 2: 0 | pinTCC0Ch2<<4,
|
||||
PB16 / 2: 0 | pinTCC0Ch0<<4,
|
||||
PA20 / 2: 0 | pinTCC0Ch2<<4,
|
||||
PA22 / 2: 0 | pinTCC0Ch0<<4,
|
||||
PA24 / 2: 0 | pinTCC1Ch2<<4,
|
||||
// page 23
|
||||
PA30 / 2: 0 | pinTCC1Ch0<<4,
|
||||
PB30 / 2: pinTCC0Ch0 | pinTCC1Ch2<<4,
|
||||
}
|
||||
|
||||
// findPinPadMapping returns the pin mode (PinTCC or PinTCCAlt) and the channel
|
||||
// number for a given timer and pin. A zero PinMode is returned if no mapping
|
||||
// could be found.
|
||||
func findPinTimerMapping(timer uint8, pin Pin) (PinMode, uint8) {
|
||||
mapping := pinTimerMapping[pin/2]
|
||||
// evenChannel below indicates the channel 0 or 2, for the even part of the
|
||||
// pin pair. The next pin will also have the next channel (1 or 3).
|
||||
if mapping&0x07 == timer+1 {
|
||||
// PWM output is on peripheral function E.
|
||||
evenChannel := ((mapping >> 3) & 1) * 2
|
||||
return PinTCC, evenChannel + uint8(pin&1)
|
||||
}
|
||||
if (mapping&0x70)>>4 == timer+1 {
|
||||
// PWM output is on peripheral function F.
|
||||
evenChannel := ((mapping >> 7) & 1) * 2
|
||||
return PinTCCAlt, evenChannel + uint8(pin&1)
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// Channel returns a PWM channel for the given pin. Note that one channel may be
|
||||
// shared between multiple pins, and so will have the same duty cycle. If this
|
||||
// is not desirable, look for a different TCC peripheral or consider using a
|
||||
// different pin.
|
||||
func (tcc *TCC) Channel(pin Pin) (uint8, error) {
|
||||
var pinMode PinMode
|
||||
var channel uint8
|
||||
switch tcc.timer() {
|
||||
case sam.TCC0:
|
||||
pinMode, channel = findPinTimerMapping(0, pin)
|
||||
case sam.TCC1:
|
||||
pinMode, channel = findPinTimerMapping(1, pin)
|
||||
case sam.TCC2:
|
||||
pinMode, channel = findPinTimerMapping(2, pin)
|
||||
// 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
|
||||
}
|
||||
|
||||
if pinMode == 0 {
|
||||
// No pin could be found.
|
||||
return 0, ErrInvalidOutputPin
|
||||
// disable output
|
||||
timer.CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
|
||||
|
||||
// Wait for synchronization
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
// Enable the port multiplexer for pin
|
||||
pin.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
|
||||
|
||||
if pin&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := pin.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
pin.setPMux(val | uint8(pinMode<<sam.PORT_PMUX0_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := pin.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
pin.setPMux(val | uint8(pinMode<<sam.PORT_PMUX0_PMUXE_Pos))
|
||||
}
|
||||
return channel, nil
|
||||
}
|
||||
|
||||
// SetInverting sets whether to invert the output of this channel.
|
||||
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
|
||||
// the time and low for the rest. Inverting flips the output as if a NOT gate
|
||||
// was placed at the output, meaning that the output would be 25% low and 75%
|
||||
// high with a duty cycle of 25%.
|
||||
func (tcc *TCC) SetInverting(channel uint8, inverting bool) {
|
||||
if inverting {
|
||||
tcc.timer().WAVE.SetBits(1 << (sam.TCC_WAVE_POL0_Pos + channel))
|
||||
} else {
|
||||
tcc.timer().WAVE.ClearBits(1 << (sam.TCC_WAVE_POL0_Pos + channel))
|
||||
}
|
||||
|
||||
// Wait for synchronization of the WAVE register.
|
||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||
}
|
||||
}
|
||||
|
||||
// Set updates the channel value. This is used to control the channel duty
|
||||
// cycle, in other words the fraction of time the channel output is high (or low
|
||||
// when inverted). For example, to set it to a 25% duty cycle, use:
|
||||
//
|
||||
// tcc.Set(channel, tcc.Top() / 4)
|
||||
//
|
||||
// tcc.Set(channel, 0) will set the output to low and tcc.Set(channel,
|
||||
// tcc.Top()) will set the output to high, assuming the output isn't inverted.
|
||||
func (tcc *TCC) Set(channel uint8, value uint32) {
|
||||
// Set PWM signal to output duty cycle
|
||||
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:
|
||||
// invalid PWM channel, ignore.
|
||||
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) {
|
||||
}
|
||||
|
||||
// Wait for synchronization on all channels (or anything in this peripheral,
|
||||
// really).
|
||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||
// enable
|
||||
timer.CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
|
||||
// Wait for synchronization
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
}
|
||||
|
||||
// getPMux returns the value for the correct PMUX register for this pin.
|
||||
func (pwm PWM) getPMux() uint8 {
|
||||
return pwm.Pin.getPMux()
|
||||
}
|
||||
|
||||
// setPMux sets the value for the correct PMUX register for this pin.
|
||||
func (pwm PWM) setPMux(val uint8) {
|
||||
pwm.Pin.setPMux(val)
|
||||
}
|
||||
|
||||
// getPinCfg returns the value for the correct PINCFG register for this pin.
|
||||
func (pwm PWM) getPinCfg() uint8 {
|
||||
return pwm.Pin.getPinCfg()
|
||||
}
|
||||
|
||||
// setPinCfg sets the value for the correct PINCFG register for this pin.
|
||||
func (pwm PWM) setPinCfg(val uint8) {
|
||||
pwm.Pin.setPinCfg(val)
|
||||
}
|
||||
|
||||
// getTimer returns the timer to be used for PWM on this pin
|
||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||
switch pwm.Pin {
|
||||
case 6:
|
||||
return sam.TCC1
|
||||
case 7:
|
||||
return sam.TCC1
|
||||
case 8:
|
||||
return sam.TCC1
|
||||
case 9:
|
||||
return sam.TCC1
|
||||
case 14:
|
||||
return sam.TCC0
|
||||
case 15:
|
||||
return sam.TCC0
|
||||
case 16:
|
||||
return sam.TCC0
|
||||
case 17:
|
||||
return sam.TCC0
|
||||
case 18:
|
||||
return sam.TCC0
|
||||
case 19:
|
||||
return sam.TCC0
|
||||
case 20:
|
||||
return sam.TCC0
|
||||
case 21:
|
||||
return sam.TCC0
|
||||
default:
|
||||
return nil // not supported on this pin
|
||||
}
|
||||
}
|
||||
|
||||
// setChannel sets the value for the correct channel for PWM on this pin
|
||||
func (pwm PWM) setChannel(timer *sam.TCC_Type, val uint32) {
|
||||
switch pwm.Pin {
|
||||
case 6:
|
||||
timer.CC0.Set(val)
|
||||
case 7:
|
||||
timer.CC1.Set(val)
|
||||
case 8:
|
||||
timer.CC0.Set(val)
|
||||
case 9:
|
||||
timer.CC1.Set(val)
|
||||
case 14:
|
||||
timer.CC0.Set(val)
|
||||
case 15:
|
||||
timer.CC1.Set(val)
|
||||
case 16:
|
||||
timer.CC2.Set(val)
|
||||
case 17:
|
||||
timer.CC3.Set(val)
|
||||
case 18:
|
||||
timer.CC2.Set(val)
|
||||
case 19:
|
||||
timer.CC3.Set(val)
|
||||
case 20:
|
||||
timer.CC2.Set(val)
|
||||
case 21:
|
||||
timer.CC3.Set(val)
|
||||
default:
|
||||
return // not supported on this pin
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2309,7 +2126,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 {
|
||||
var b [cdcLineInfoSize]byte
|
||||
b := make([]byte, 7)
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
@@ -2318,18 +2135,14 @@ 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, err := receiveUSBControlPacket()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
b := receiveUSBControlPacket()
|
||||
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]
|
||||
@@ -2376,9 +2189,7 @@ func sendUSBPacket(ep uint32, data []byte) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(uint32((len(data) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos))
|
||||
}
|
||||
|
||||
func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
|
||||
func receiveUSBControlPacket() []byte {
|
||||
// address
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
|
||||
@@ -2393,7 +2204,7 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
return []byte{}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2402,7 +2213,7 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT0) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
return []byte{}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2410,13 +2221,10 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
if bytesread != cdcLineInfoSize {
|
||||
return b, errUSBCDCBytesRead
|
||||
}
|
||||
data := make([]byte, bytesread)
|
||||
copy(data, udd_ep_out_cache_buffer[0][:])
|
||||
|
||||
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
|
||||
|
||||
return b, nil
|
||||
return data
|
||||
}
|
||||
|
||||
func handleEndpoint(ep uint32) {
|
||||
|
||||
+236
-365
@@ -1,4 +1,4 @@
|
||||
// +build sam,atsamd51 sam,atsame5x
|
||||
// +build sam,atsamd51
|
||||
|
||||
// Peripheral abstraction layer for the atsamd51.
|
||||
//
|
||||
@@ -40,13 +40,10 @@ const (
|
||||
PinInput PinMode = 15
|
||||
PinInputPullup PinMode = 16
|
||||
PinOutput PinMode = 17
|
||||
PinTCCE PinMode = PinTimer
|
||||
PinTCCF PinMode = PinTimerAlt
|
||||
PinTCCG PinMode = PinTCCPDEC
|
||||
PinPWME PinMode = PinTimer
|
||||
PinPWMF PinMode = PinTimerAlt
|
||||
PinPWMG PinMode = PinTCCPDEC
|
||||
PinInputPulldown PinMode = 18
|
||||
PinCAN PinMode = 19
|
||||
PinCAN0 PinMode = PinSDHC
|
||||
PinCAN1 PinMode = PinCom
|
||||
)
|
||||
|
||||
type PinChange uint8
|
||||
@@ -630,18 +627,6 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -863,18 +848,7 @@ func (a ADC) Get() uint16 {
|
||||
for bus.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
// 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
|
||||
return uint16(val) << 4 // scales from 12 to 16-bit result
|
||||
}
|
||||
|
||||
func (a ADC) getADCBus() *sam.ADC_Type {
|
||||
@@ -1596,350 +1570,256 @@ const (
|
||||
QSPI_DATA3 = PA11
|
||||
)
|
||||
|
||||
// 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
|
||||
// PWM
|
||||
const period = 0xFFFF
|
||||
|
||||
//go:inline
|
||||
func (tcc *TCC) timer() *sam.TCC_Type {
|
||||
return (*sam.TCC_Type)(tcc)
|
||||
}
|
||||
// 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))
|
||||
|
||||
// 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()
|
||||
// Enable the port multiplexer for pin
|
||||
pwm.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN)
|
||||
|
||||
// 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)
|
||||
// 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)
|
||||
|
||||
// Use "Normal PWM" (single-slope PWM)
|
||||
tcc.timer().WAVE.Set(sam.TCC_WAVE_WAVEGEN_NPWM)
|
||||
|
||||
// Wait for synchronization of all changed registers.
|
||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
||||
timer.WAVE.SetBits(sam.TCC_WAVE_WAVEGEN_NPWM)
|
||||
// Wait for synchronization
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_WAVE) {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// while (TCCx->SYNCBUSY.bit.CC0 || TCCx->SYNCBUSY.bit.CC1);
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
|
||||
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
|
||||
}
|
||||
|
||||
// Return any error that might have occured in the tcc.setPeriod call.
|
||||
return err
|
||||
}
|
||||
// Set the initial value
|
||||
// TCCx->CC[tcChannel].reg = (uint32_t) value;
|
||||
pwm.setChannel(timer, 0)
|
||||
|
||||
// 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
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
|
||||
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
|
||||
}
|
||||
|
||||
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
|
||||
// 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) {
|
||||
}
|
||||
|
||||
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 {
|
||||
// enable timer
|
||||
timer.CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
|
||||
// Wait for synchronization
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
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
|
||||
// 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
|
||||
// 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
|
||||
}
|
||||
|
||||
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
|
||||
// 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) {
|
||||
}
|
||||
|
||||
// Check for column G in the datasheet.
|
||||
if mapping.G>>4-1 == timer {
|
||||
return PinTCCG, mapping.G&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) {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// TCCx->CTRLBCLR.bit.LUPD = 1;
|
||||
timer.CTRLBCLR.SetBits(sam.TCC_CTRLBCLR_LUPD)
|
||||
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CTRLB) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2452,7 +2332,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 {
|
||||
var b [cdcLineInfoSize]byte
|
||||
b := make([]byte, 7)
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
@@ -2461,18 +2341,14 @@ 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, err := receiveUSBControlPacket()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
b := receiveUSBControlPacket()
|
||||
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]
|
||||
@@ -2519,9 +2395,7 @@ func sendUSBPacket(ep uint32, data []byte) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(uint32((len(data) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos))
|
||||
}
|
||||
|
||||
func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
|
||||
func receiveUSBControlPacket() []byte {
|
||||
// address
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
|
||||
@@ -2536,7 +2410,7 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_ENDPOINT_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
return []byte{}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2545,7 +2419,7 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
return []byte{}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2553,13 +2427,10 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
if bytesread != cdcLineInfoSize {
|
||||
return b, errUSBCDCBytesRead
|
||||
}
|
||||
data := make([]byte, bytesread)
|
||||
copy(data, udd_ep_out_cache_buffer[0][:])
|
||||
|
||||
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
|
||||
|
||||
return b, nil
|
||||
return data
|
||||
}
|
||||
|
||||
func handleEndpoint(ep uint32) {
|
||||
|
||||
@@ -11,37 +11,43 @@ import "device/sam"
|
||||
|
||||
const HSRAM_SIZE = 0x00030000
|
||||
|
||||
// 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)
|
||||
)
|
||||
// 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_)
|
||||
|
||||
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)
|
||||
}
|
||||
//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)
|
||||
}
|
||||
|
||||
func (tcc *TCC) timerNum() uint8 {
|
||||
switch tcc.timer() {
|
||||
case sam.TCC0:
|
||||
return 0
|
||||
case sam.TCC1:
|
||||
return 1
|
||||
case sam.TCC2:
|
||||
return 2
|
||||
// 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 0x0f // should not happen
|
||||
return nil // not supported on this pin
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,49 +11,60 @@ 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)
|
||||
)
|
||||
// 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_)
|
||||
|
||||
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)
|
||||
}
|
||||
//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)
|
||||
}
|
||||
|
||||
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
|
||||
// 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 0x0f // should not happen
|
||||
return nil // not supported on this pin
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,49 +11,60 @@ 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)
|
||||
)
|
||||
// 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_)
|
||||
|
||||
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)
|
||||
}
|
||||
//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)
|
||||
}
|
||||
|
||||
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
|
||||
// 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 0x0f // should not happen
|
||||
return nil // not supported on this pin
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,49 +11,60 @@ 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)
|
||||
)
|
||||
// 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_)
|
||||
|
||||
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)
|
||||
}
|
||||
//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)
|
||||
}
|
||||
|
||||
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
|
||||
// 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 0x0f // should not happen
|
||||
return nil // not supported on this pin
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,49 +11,60 @@ 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)
|
||||
)
|
||||
// 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_)
|
||||
|
||||
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)
|
||||
}
|
||||
//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)
|
||||
}
|
||||
|
||||
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
|
||||
// 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 0x0f // should not happen
|
||||
return nil // not supported on this pin
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
// +build sam,atsame51,atsame51j19
|
||||
|
||||
// Peripheral abstraction layer for the atsame51.
|
||||
//
|
||||
// Datasheet:
|
||||
// http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D5xE5x_Family_Data_Sheet_DS60001507F.pdf
|
||||
//
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
const HSRAM_SIZE = 0x00030000
|
||||
|
||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||
var (
|
||||
TCC0 = (*TCC)(sam.TCC0)
|
||||
TCC1 = (*TCC)(sam.TCC1)
|
||||
TCC2 = (*TCC)(sam.TCC2)
|
||||
TCC3 = (*TCC)(sam.TCC3)
|
||||
TCC4 = (*TCC)(sam.TCC4)
|
||||
)
|
||||
|
||||
func (tcc *TCC) configureClock() {
|
||||
// Turn on timer clocks used for the TCC and use generic clock generator 0.
|
||||
switch tcc.timer() {
|
||||
case sam.TCC0:
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC1:
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC1_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC2:
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC3:
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC4:
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
}
|
||||
|
||||
func (tcc *TCC) timerNum() uint8 {
|
||||
switch tcc.timer() {
|
||||
case sam.TCC0:
|
||||
return 0
|
||||
case sam.TCC1:
|
||||
return 1
|
||||
case sam.TCC2:
|
||||
return 2
|
||||
case sam.TCC3:
|
||||
return 3
|
||||
case sam.TCC4:
|
||||
return 4
|
||||
default:
|
||||
return 0x0f // should not happen
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// +build sam,atsame5x,atsame54p20
|
||||
|
||||
// Peripheral abstraction layer for the atsame54.
|
||||
//
|
||||
// Datasheet:
|
||||
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
|
||||
//
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
const HSRAM_SIZE = 0x00040000
|
||||
|
||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||
var (
|
||||
TCC0 = (*TCC)(sam.TCC0)
|
||||
TCC1 = (*TCC)(sam.TCC1)
|
||||
TCC2 = (*TCC)(sam.TCC2)
|
||||
TCC3 = (*TCC)(sam.TCC3)
|
||||
TCC4 = (*TCC)(sam.TCC4)
|
||||
)
|
||||
|
||||
func (tcc *TCC) configureClock() {
|
||||
// Turn on timer clocks used for TCC and use generic clock generator 0.
|
||||
switch tcc.timer() {
|
||||
case sam.TCC0:
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC1:
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC1_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC2:
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC3:
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC4:
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
}
|
||||
|
||||
func (tcc *TCC) timerNum() uint8 {
|
||||
switch tcc.timer() {
|
||||
case sam.TCC0:
|
||||
return 0
|
||||
case sam.TCC1:
|
||||
return 1
|
||||
case sam.TCC2:
|
||||
return 2
|
||||
case sam.TCC3:
|
||||
return 3
|
||||
case sam.TCC4:
|
||||
return 4
|
||||
default:
|
||||
return 0x0f // should not happen
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,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) >> 3) | ((uint16(c.G) >> 3) << 5) | ((uint16(c.B) >> 3) << 10))
|
||||
d.port[y][x].Set(uint16(c.R)&0x1f | uint16(c.G)&0x1f<<5 | uint16(c.B)&0x1f<<10)
|
||||
}
|
||||
|
||||
func (d FramebufDisplay) Display() error {
|
||||
|
||||
@@ -84,6 +84,24 @@ 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
|
||||
|
||||
@@ -63,7 +63,7 @@ func (i2c *I2C) setPins(scl, sda Pin) {
|
||||
|
||||
// PWM
|
||||
var (
|
||||
PWM0 = &PWM{PWM: nrf.PWM0}
|
||||
PWM1 = &PWM{PWM: nrf.PWM1}
|
||||
PWM2 = &PWM{PWM: nrf.PWM2}
|
||||
pwmChannelPins = [3]uint32{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}
|
||||
pwms = [3]*nrf.PWM_Type{nrf.PWM0, nrf.PWM1, nrf.PWM2}
|
||||
pwmChannelSequence [3]uint16
|
||||
)
|
||||
|
||||
@@ -83,8 +83,7 @@ func (i2c *I2C) setPins(scl, sda Pin) {
|
||||
|
||||
// PWM
|
||||
var (
|
||||
PWM0 = &PWM{PWM: nrf.PWM0}
|
||||
PWM1 = &PWM{PWM: nrf.PWM1}
|
||||
PWM2 = &PWM{PWM: nrf.PWM2}
|
||||
PWM3 = &PWM{PWM: nrf.PWM3}
|
||||
pwmChannelPins = [4]uint32{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}
|
||||
pwms = [4]*nrf.PWM_Type{nrf.PWM0, nrf.PWM1, nrf.PWM2, nrf.PWM3}
|
||||
pwmChannelSequence [4]uint16
|
||||
)
|
||||
|
||||
@@ -79,8 +79,7 @@ func (i2c *I2C) setPins(scl, sda Pin) {
|
||||
|
||||
// PWM
|
||||
var (
|
||||
PWM0 = &PWM{PWM: nrf.PWM0}
|
||||
PWM1 = &PWM{PWM: nrf.PWM1}
|
||||
PWM2 = &PWM{PWM: nrf.PWM2}
|
||||
PWM3 = &PWM{PWM: nrf.PWM3}
|
||||
pwmChannelPins = [4]uint32{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}
|
||||
pwms = [4]*nrf.PWM_Type{nrf.PWM0, nrf.PWM1, nrf.PWM2, nrf.PWM3}
|
||||
pwmChannelSequence [4]uint16
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
var b [cdcLineInfoSize]byte
|
||||
b := make([]byte, 7)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
+28
-193
@@ -4,7 +4,6 @@ package machine
|
||||
|
||||
import (
|
||||
"device/nrf"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -257,205 +256,41 @@ func (spi SPI) Tx(w, r []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PWM is one PWM 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 PWM peripheral at
|
||||
// once.
|
||||
type PWM struct {
|
||||
PWM *nrf.PWM_Type
|
||||
|
||||
channelValues [4]volatile.Register16
|
||||
// InitPWM initializes the registers needed for PWM.
|
||||
func InitPWM() {
|
||||
return
|
||||
}
|
||||
|
||||
// Configure enables and configures this PWM.
|
||||
// On the nRF52 series, the maximum period is around 0.26s.
|
||||
func (pwm *PWM) Configure(config PWMConfig) error {
|
||||
// Enable the peripheral.
|
||||
pwm.PWM.ENABLE.Set(nrf.PWM_ENABLE_ENABLE_Enabled << nrf.PWM_ENABLE_ENABLE_Pos)
|
||||
|
||||
// Use up counting only. TODO: allow configuring as up-and-down.
|
||||
pwm.PWM.MODE.Set(nrf.PWM_MODE_UPDOWN_Up << nrf.PWM_MODE_UPDOWN_Pos)
|
||||
|
||||
// Indicate there are four channels that each have a different value.
|
||||
pwm.PWM.DECODER.Set(nrf.PWM_DECODER_LOAD_Individual<<nrf.PWM_DECODER_LOAD_Pos | nrf.PWM_DECODER_MODE_RefreshCount<<nrf.PWM_DECODER_MODE_Pos)
|
||||
|
||||
err := pwm.setPeriod(config.Period, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the EasyDMA buffer, which has 4 values (one for each channel).
|
||||
pwm.PWM.SEQ[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&pwm.channelValues[0]))))
|
||||
pwm.PWM.SEQ[0].CNT.Set(4)
|
||||
|
||||
// SEQ[0] is not yet started, it will be started on the first
|
||||
// PWMChannel.Set() call.
|
||||
|
||||
return nil
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return pwm.setPeriod(period, false)
|
||||
}
|
||||
// Set turns on the duty cycle for a PWM pin using the provided value.
|
||||
func (pwm PWM) Set(value uint16) {
|
||||
for i := 0; i < len(pwmChannelPins); i++ {
|
||||
if pwmChannelPins[i] == 0xFFFFFFFF || pwmChannelPins[i] == uint32(pwm.Pin) {
|
||||
pwmChannelPins[i] = uint32(pwm.Pin)
|
||||
pwmChannelSequence[i] = (value >> 2) | 0x8000 // set bit 15 to invert polarity
|
||||
|
||||
func (pwm *PWM) setPeriod(period uint64, updatePrescaler bool) error {
|
||||
const maxTop = 0x7fff // 15 bits counter
|
||||
p := pwms[i]
|
||||
|
||||
// The top value is the number of PWM ticks a PWM period takes. It is
|
||||
// initially picked assuming an unlimited COUNTERTOP and no PWM prescaler.
|
||||
var top uint64
|
||||
if period == 0 {
|
||||
// The period is 0, which means "pick something reasonable for LEDs".
|
||||
top = maxTop
|
||||
} else {
|
||||
// The formula below calculates the following formula, optimized:
|
||||
// period * (16e6 / 1e9)
|
||||
// The max frequency (16e6 or 16MHz) is set by the hardware.
|
||||
top = period * 2 / 125
|
||||
}
|
||||
p.PSEL.OUT[0].Set(uint32(pwm.Pin))
|
||||
p.PSEL.OUT[1].Set(uint32(pwm.Pin))
|
||||
p.PSEL.OUT[2].Set(uint32(pwm.Pin))
|
||||
p.PSEL.OUT[3].Set(uint32(pwm.Pin))
|
||||
p.ENABLE.Set(nrf.PWM_ENABLE_ENABLE_Enabled << nrf.PWM_ENABLE_ENABLE_Pos)
|
||||
p.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_2)
|
||||
p.MODE.Set(nrf.PWM_MODE_UPDOWN_Up)
|
||||
p.COUNTERTOP.Set(16384) // frequency
|
||||
p.LOOP.Set(0)
|
||||
p.DECODER.Set((nrf.PWM_DECODER_LOAD_Common << nrf.PWM_DECODER_LOAD_Pos) | (nrf.PWM_DECODER_MODE_RefreshCount << nrf.PWM_DECODER_MODE_Pos))
|
||||
p.SEQ[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&pwmChannelSequence[i]))))
|
||||
p.SEQ[0].CNT.Set(1)
|
||||
p.SEQ[0].REFRESH.Set(1)
|
||||
p.SEQ[0].ENDDELAY.Set(0)
|
||||
p.TASKS_SEQSTART[0].Set(1)
|
||||
|
||||
// The ideal PWM period may be larger than would fit in the PWM counter,
|
||||
// which is only 15 bits (see maxTop). Therefore, try to make the PWM clock
|
||||
// speed lower with a prescaler to make the top value fit the COUNTERTOP.
|
||||
if updatePrescaler {
|
||||
// This function was called during Configure().
|
||||
switch {
|
||||
case top <= maxTop:
|
||||
pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_1)
|
||||
case top/2 <= maxTop:
|
||||
pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_2)
|
||||
top /= 2
|
||||
case top/4 <= maxTop:
|
||||
pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_4)
|
||||
top /= 4
|
||||
case top/8 <= maxTop:
|
||||
pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_8)
|
||||
top /= 8
|
||||
case top/16 <= maxTop:
|
||||
pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_16)
|
||||
top /= 16
|
||||
case top/32 <= maxTop:
|
||||
pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_32)
|
||||
top /= 32
|
||||
case top/64 <= maxTop:
|
||||
pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_64)
|
||||
top /= 64
|
||||
case top/128 <= maxTop:
|
||||
pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_128)
|
||||
top /= 128
|
||||
default:
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
} 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 := pwm.PWM.PRESCALER.Get()
|
||||
switch prescaler {
|
||||
case nrf.PWM_PRESCALER_PRESCALER_DIV_1:
|
||||
top /= 1
|
||||
case nrf.PWM_PRESCALER_PRESCALER_DIV_2:
|
||||
top /= 2
|
||||
case nrf.PWM_PRESCALER_PRESCALER_DIV_4:
|
||||
top /= 4
|
||||
case nrf.PWM_PRESCALER_PRESCALER_DIV_8:
|
||||
top /= 8
|
||||
case nrf.PWM_PRESCALER_PRESCALER_DIV_16:
|
||||
top /= 16
|
||||
case nrf.PWM_PRESCALER_PRESCALER_DIV_32:
|
||||
top /= 32
|
||||
case nrf.PWM_PRESCALER_PRESCALER_DIV_64:
|
||||
top /= 64
|
||||
case nrf.PWM_PRESCALER_PRESCALER_DIV_128:
|
||||
top /= 128
|
||||
}
|
||||
if top > maxTop {
|
||||
return ErrPWMPeriodTooLong
|
||||
break
|
||||
}
|
||||
}
|
||||
pwm.PWM.COUNTERTOP.Set(uint32(top))
|
||||
|
||||
// Apparently this is needed to apply the new COUNTERTOP.
|
||||
pwm.PWM.TASKS_SEQSTART[0].Set(1)
|
||||
|
||||
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
|
||||
// pwm.Set (see pwm.Set for more information).
|
||||
func (pwm *PWM) Top() uint32 {
|
||||
return pwm.PWM.COUNTERTOP.Get()
|
||||
}
|
||||
|
||||
// Channel returns a PWM channel for the given pin.
|
||||
func (pwm *PWM) Channel(pin Pin) (uint8, error) {
|
||||
config := uint32(pin)
|
||||
for ch := uint8(0); ch < 4; ch++ {
|
||||
channelConfig := pwm.PWM.PSEL.OUT[ch].Get()
|
||||
if channelConfig == 0xffffffff {
|
||||
// Unused channel. Configure it.
|
||||
pwm.PWM.PSEL.OUT[ch].Set(config)
|
||||
// Configure the pin (required by the reference manual).
|
||||
pin.Configure(PinConfig{Mode: PinOutput})
|
||||
// Set channel to zero and non-inverting.
|
||||
pwm.channelValues[ch].Set(0x8000)
|
||||
return ch, nil
|
||||
} else if channelConfig == config {
|
||||
// This channel is already configured for this pin.
|
||||
return ch, nil
|
||||
}
|
||||
}
|
||||
|
||||
// All four pins are already in use with other pins.
|
||||
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%.
|
||||
func (pwm *PWM) SetInverting(channel uint8, inverting bool) {
|
||||
ptr := &pwm.channelValues[channel]
|
||||
if inverting {
|
||||
ptr.Set(ptr.Get() &^ 0x8000)
|
||||
} else {
|
||||
ptr.Set(ptr.Get() | 0x8000)
|
||||
}
|
||||
}
|
||||
|
||||
// Set updates the channel value. This is used to control the channel duty
|
||||
// cycle. For example, to set it to a 25% duty cycle, use:
|
||||
//
|
||||
// ch.Set(ch.Top() / 4)
|
||||
//
|
||||
// ch.Set(0) will set the output to low and ch.Set(ch.Top()) will set the output
|
||||
// to high, assuming the output isn't inverted.
|
||||
func (pwm *PWM) Set(channel uint8, value uint32) {
|
||||
// Update the channel value while retaining the polarity bit.
|
||||
ptr := &pwm.channelValues[channel]
|
||||
ptr.Set(ptr.Get()&0x8000 | uint16(value)&0x7fff)
|
||||
|
||||
// Start the PWM, if it isn't already running.
|
||||
pwm.PWM.TASKS_SEQSTART[0].Set(1)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build stm32l5 stm32f7 stm32l4 stm32l0
|
||||
// +build stm32l5 stm32f7 stm32l4
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -55,11 +55,9 @@ const (
|
||||
gpioPullMask = 0x3
|
||||
|
||||
// OSPEED bitfields.
|
||||
gpioOutputSpeedVeryHigh = 3
|
||||
gpioOutputSpeedHigh = 2
|
||||
gpioOutputSpeedMedium = 1
|
||||
gpioOutputSpeedLow = 0
|
||||
gpioOutputSpeedMask = 0x3
|
||||
gpioOutputSpeedHigh = 2
|
||||
gpioOutputSpeedLow = 0
|
||||
gpioOutputSpeedMask = 0x3
|
||||
)
|
||||
|
||||
// Configure this pin with the given configuration
|
||||
@@ -122,7 +120,7 @@ func (p Pin) ConfigureAltFunc(config PinConfig, altFunc uint8) {
|
||||
// SPI
|
||||
case PinModeSPICLK:
|
||||
port.MODER.ReplaceBits(gpioModeAlternate, gpioModeMask, pos)
|
||||
port.OSPEEDR.ReplaceBits(gpioOutputSpeedHigh, gpioOutputSpeedMask, pos)
|
||||
port.OSPEEDR.ReplaceBits(gpioOutputSpeedLow, gpioOutputSpeedMask, pos)
|
||||
port.PUPDR.ReplaceBits(gpioPullFloating, gpioPullMask, pos)
|
||||
p.SetAltFunc(altFunc)
|
||||
case PinModeSPISDO:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build stm32,!stm32f7x2,!stm32l5x2
|
||||
// +build stm32,!stm32f7x2,!stm32l5x2,!stm32l4x2
|
||||
|
||||
package machine
|
||||
|
||||
@@ -6,7 +6,6 @@ package machine
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -87,12 +86,7 @@ func (spi SPI) Configure(config SPIConfig) {
|
||||
|
||||
// now set the configuration
|
||||
spi.Bus.CR1.Set(conf)
|
||||
|
||||
// Series-specific configuration to set 8-bit transfer mode
|
||||
spi.config8Bits()
|
||||
|
||||
// enable SPI
|
||||
spi.Bus.CR1.SetBits(stm32.SPI_CR1_SPE)
|
||||
spi.Bus.CR2.SetBits((conf & stm32.SPI_CR1_SSM_Msk) >> 16)
|
||||
}
|
||||
|
||||
// Transfer writes/reads a single byte using the SPI interface.
|
||||
@@ -109,10 +103,8 @@ func (spi SPI) Transfer(w byte) (byte, error) {
|
||||
// 5. Wait until TXE=1 and then wait until BSY=0 before disabling the SPI.
|
||||
|
||||
// put output word (8-bit) in data register (DR), which is parallel-loaded
|
||||
// into shift register, and shifted out on MOSI. Some series have 16-bit
|
||||
// register but writes must be strictly 8-bit to output a byte. Writing
|
||||
// 16-bits indicates a packed transfer (2 bytes).
|
||||
(*volatile.Register8)(unsafe.Pointer(&spi.Bus.DR.Reg)).Set(w)
|
||||
// into shift register, and shifted out on MOSI.
|
||||
spi.Bus.DR.Set(uint32(w))
|
||||
|
||||
// wait for SPI bus receive buffer not empty bit (RXNE) to be set.
|
||||
// warning: blocks forever until this condition is met.
|
||||
|
||||
@@ -161,10 +161,6 @@ var (
|
||||
SPI0 = SPI1
|
||||
)
|
||||
|
||||
func (spi SPI) config8Bits() {
|
||||
// no-op on this series
|
||||
}
|
||||
|
||||
// Set baud rate for SPI
|
||||
func (spi SPI) getBaudRate(config SPIConfig) uint32 {
|
||||
var conf uint32
|
||||
@@ -204,16 +200,15 @@ func (spi SPI) configurePins(config SPIConfig) {
|
||||
// There are 2 I2C interfaces on the STM32F103xx.
|
||||
// Since the first interface is named I2C1, both I2C0 and I2C1 refer to I2C1.
|
||||
// TODO: implement I2C2.
|
||||
var (
|
||||
I2C1 = (*I2C)(unsafe.Pointer(stm32.I2C1))
|
||||
I2C0 = I2C1
|
||||
)
|
||||
|
||||
type I2C struct {
|
||||
Bus *stm32.I2C_Type
|
||||
}
|
||||
|
||||
var (
|
||||
I2C1 = &I2C{Bus: stm32.I2C1}
|
||||
I2C0 = I2C1
|
||||
)
|
||||
|
||||
func (i2c *I2C) configurePins(config I2CConfig) {
|
||||
if config.SDA == PB9 {
|
||||
// use alternate I2C1 pins PB8/PB9 via AFIO mapping
|
||||
|
||||
@@ -67,10 +67,6 @@ type SPI struct {
|
||||
AltFuncSelector uint8
|
||||
}
|
||||
|
||||
func (spi SPI) config8Bits() {
|
||||
// no-op on this series
|
||||
}
|
||||
|
||||
func (spi SPI) configurePins(config SPIConfig) {
|
||||
config.SCK.ConfigureAltFunc(PinConfig{Mode: PinModeSPICLK}, spi.AltFuncSelector)
|
||||
config.SDO.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDO}, spi.AltFuncSelector)
|
||||
|
||||
@@ -70,10 +70,6 @@ type SPI struct {
|
||||
AltFuncSelector uint8
|
||||
}
|
||||
|
||||
func (spi SPI) config8Bits() {
|
||||
// no-op on this series
|
||||
}
|
||||
|
||||
// Set baud rate for SPI
|
||||
func (spi SPI) getBaudRate(config SPIConfig) uint32 {
|
||||
var conf uint32
|
||||
|
||||
@@ -6,6 +6,7 @@ package machine
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func CPUFrequency() uint32 {
|
||||
@@ -141,6 +142,50 @@ func (p Pin) enableClock() {
|
||||
}
|
||||
}
|
||||
|
||||
// Enable peripheral clock
|
||||
func enableAltFuncClock(bus unsafe.Pointer) {
|
||||
switch bus {
|
||||
case unsafe.Pointer(stm32.DAC): // DAC interface clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_DACEN)
|
||||
case unsafe.Pointer(stm32.PWR): // Power interface clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN)
|
||||
case unsafe.Pointer(stm32.I2C3): // I2C3 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C3EN)
|
||||
case unsafe.Pointer(stm32.I2C2): // I2C2 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C2EN)
|
||||
case unsafe.Pointer(stm32.I2C1): // I2C1 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN)
|
||||
case unsafe.Pointer(stm32.USART5): // UART5 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART5EN)
|
||||
case unsafe.Pointer(stm32.USART4): // UART4 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART4EN)
|
||||
case unsafe.Pointer(stm32.USART2): // USART2 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN)
|
||||
case unsafe.Pointer(stm32.SPI2): // SPI2 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_SPI2EN)
|
||||
case unsafe.Pointer(stm32.LPUART1): // LPUART1 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_LPUART1EN)
|
||||
case unsafe.Pointer(stm32.WWDG): // Window watchdog clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_WWDGEN)
|
||||
case unsafe.Pointer(stm32.TIM7): // TIM7 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM7EN)
|
||||
case unsafe.Pointer(stm32.TIM6): // TIM6 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM6EN)
|
||||
case unsafe.Pointer(stm32.TIM3): // TIM3 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
||||
case unsafe.Pointer(stm32.TIM2): // TIM2 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM2EN)
|
||||
case unsafe.Pointer(stm32.SYSCFG): // System configuration controller clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SYSCFGEN)
|
||||
case unsafe.Pointer(stm32.SPI1): // SPI1 clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
|
||||
case unsafe.Pointer(stm32.ADC): // ADC clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADCEN)
|
||||
case unsafe.Pointer(stm32.USART1): // USART1 clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
|
||||
}
|
||||
}
|
||||
|
||||
//---------- UART related types and code
|
||||
|
||||
// Configure the UART.
|
||||
@@ -184,10 +229,6 @@ type SPI struct {
|
||||
AltFuncSelector uint8
|
||||
}
|
||||
|
||||
func (spi SPI) config8Bits() {
|
||||
// no-op on this series
|
||||
}
|
||||
|
||||
// Set baud rate for SPI
|
||||
func (spi SPI) getBaudRate(config SPIConfig) uint32 {
|
||||
var conf uint32
|
||||
@@ -244,13 +285,3 @@ func (spi SPI) configurePins(config SPIConfig) {
|
||||
config.SDO.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDO}, spi.AltFuncSelector)
|
||||
config.SDI.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDI}, spi.AltFuncSelector)
|
||||
}
|
||||
|
||||
//---------- I2C related types and code
|
||||
|
||||
// Gets the value for TIMINGR register
|
||||
func (i2c I2C) getFreqRange() uint32 {
|
||||
// This is a 'magic' value calculated by STM32CubeMX
|
||||
// for 80MHz PCLK1.
|
||||
// TODO: Do calculations based on PCLK1
|
||||
return 0x00303D5B
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
// +build stm32l0x1
|
||||
|
||||
package machine
|
||||
|
||||
// Peripheral abstraction layer for the stm32l0
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Enable peripheral clock
|
||||
func enableAltFuncClock(bus unsafe.Pointer) {
|
||||
switch bus {
|
||||
case unsafe.Pointer(stm32.PWR): // Power interface clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN)
|
||||
case unsafe.Pointer(stm32.I2C3): // I2C3 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C3EN)
|
||||
case unsafe.Pointer(stm32.I2C2): // I2C2 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C2EN)
|
||||
case unsafe.Pointer(stm32.I2C1): // I2C1 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN)
|
||||
case unsafe.Pointer(stm32.USART5): // UART5 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART5EN)
|
||||
case unsafe.Pointer(stm32.USART4): // UART4 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART4EN)
|
||||
case unsafe.Pointer(stm32.USART2): // USART2 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN)
|
||||
case unsafe.Pointer(stm32.SPI2): // SPI2 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_SPI2EN)
|
||||
case unsafe.Pointer(stm32.LPUART1): // LPUART1 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_LPUART1EN)
|
||||
case unsafe.Pointer(stm32.WWDG): // Window watchdog clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_WWDGEN)
|
||||
case unsafe.Pointer(stm32.TIM7): // TIM7 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM7EN)
|
||||
case unsafe.Pointer(stm32.TIM6): // TIM6 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM6EN)
|
||||
case unsafe.Pointer(stm32.TIM3): // TIM3 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
||||
case unsafe.Pointer(stm32.TIM2): // TIM2 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM2EN)
|
||||
case unsafe.Pointer(stm32.SYSCFG): // System configuration controller clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SYSCFGEN)
|
||||
case unsafe.Pointer(stm32.SPI1): // SPI1 clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
|
||||
case unsafe.Pointer(stm32.ADC): // ADC clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADCEN)
|
||||
case unsafe.Pointer(stm32.USART1): // USART1 clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// +build stm32l0x2
|
||||
|
||||
package machine
|
||||
|
||||
// Peripheral abstraction layer for the stm32l0
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Enable peripheral clock
|
||||
func enableAltFuncClock(bus unsafe.Pointer) {
|
||||
switch bus {
|
||||
case unsafe.Pointer(stm32.DAC): // DAC interface clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_DACEN)
|
||||
case unsafe.Pointer(stm32.PWR): // Power interface clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN)
|
||||
case unsafe.Pointer(stm32.I2C3): // I2C3 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C3EN)
|
||||
case unsafe.Pointer(stm32.I2C2): // I2C2 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C2EN)
|
||||
case unsafe.Pointer(stm32.I2C1): // I2C1 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN)
|
||||
case unsafe.Pointer(stm32.USART5): // UART5 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART5EN)
|
||||
case unsafe.Pointer(stm32.USART4): // UART4 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART4EN)
|
||||
case unsafe.Pointer(stm32.USART2): // USART2 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN)
|
||||
case unsafe.Pointer(stm32.SPI2): // SPI2 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_SPI2EN)
|
||||
case unsafe.Pointer(stm32.LPUART1): // LPUART1 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_LPUART1EN)
|
||||
case unsafe.Pointer(stm32.WWDG): // Window watchdog clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_WWDGEN)
|
||||
case unsafe.Pointer(stm32.TIM7): // TIM7 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM7EN)
|
||||
case unsafe.Pointer(stm32.TIM6): // TIM6 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM6EN)
|
||||
case unsafe.Pointer(stm32.TIM3): // TIM3 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
||||
case unsafe.Pointer(stm32.TIM2): // TIM2 clock enable
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM2EN)
|
||||
case unsafe.Pointer(stm32.SYSCFG): // System configuration controller clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SYSCFGEN)
|
||||
case unsafe.Pointer(stm32.SPI1): // SPI1 clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
|
||||
case unsafe.Pointer(stm32.ADC): // ADC clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADCEN)
|
||||
case unsafe.Pointer(stm32.USART1): // USART1 clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
|
||||
}
|
||||
}
|
||||
@@ -182,67 +182,3 @@ func enableAltFuncClock(bus unsafe.Pointer) {
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM1EN)
|
||||
}
|
||||
}
|
||||
|
||||
//---------- SPI related types and code
|
||||
|
||||
// SPI on the STM32Fxxx using MODER / alternate function pins
|
||||
type SPI struct {
|
||||
Bus *stm32.SPI_Type
|
||||
AltFuncSelector uint8
|
||||
}
|
||||
|
||||
func (spi SPI) config8Bits() {
|
||||
// Set rx threshold to 8-bits, so RXNE flag is set for 1 byte
|
||||
// (common STM32 SPI implementation does 8-bit transfers only)
|
||||
spi.Bus.CR2.SetBits(stm32.SPI_CR2_FRXTH)
|
||||
}
|
||||
|
||||
// Set baud rate for SPI
|
||||
func (spi SPI) getBaudRate(config SPIConfig) uint32 {
|
||||
var conf uint32
|
||||
|
||||
// Default
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = 4e6
|
||||
}
|
||||
|
||||
localFrequency := config.Frequency
|
||||
|
||||
// set frequency dependent on PCLK prescaler. Since these are rather weird
|
||||
// speeds due to the CPU freqency, pick a range up to that frquency for
|
||||
// clients to use more human-understandable numbers, e.g. nearest 100KHz
|
||||
|
||||
// These are based on 80MHz peripheral clock frquency
|
||||
switch {
|
||||
case localFrequency < 312500:
|
||||
conf = stm32.SPI_CR1_BR_Div256
|
||||
case localFrequency < 625000:
|
||||
conf = stm32.SPI_CR1_BR_Div128
|
||||
case localFrequency < 1250000:
|
||||
conf = stm32.SPI_CR1_BR_Div64
|
||||
case localFrequency < 2500000:
|
||||
conf = stm32.SPI_CR1_BR_Div32
|
||||
case localFrequency < 5000000:
|
||||
conf = stm32.SPI_CR1_BR_Div16
|
||||
case localFrequency < 10000000:
|
||||
conf = stm32.SPI_CR1_BR_Div8
|
||||
// NOTE: many SPI components won't operate reliably (or at all) above 10MHz
|
||||
// Check the datasheet of the part
|
||||
case localFrequency < 20000000:
|
||||
conf = stm32.SPI_CR1_BR_Div4
|
||||
case localFrequency < 40000000:
|
||||
conf = stm32.SPI_CR1_BR_Div2
|
||||
default:
|
||||
// None of the specific baudrates were selected; choose the lowest speed
|
||||
conf = stm32.SPI_CR1_BR_Div256
|
||||
}
|
||||
|
||||
return conf << stm32.SPI_CR1_BR_Pos
|
||||
}
|
||||
|
||||
// Configure SPI pins for input output and clock
|
||||
func (spi SPI) configurePins(config SPIConfig) {
|
||||
config.SCK.ConfigureAltFunc(PinConfig{Mode: PinModeSPICLK}, spi.AltFuncSelector)
|
||||
config.SDO.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDO}, spi.AltFuncSelector)
|
||||
config.SDI.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDI}, spi.AltFuncSelector)
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package machine
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrPWMPeriodTooLong = errors.New("pwm: period too long")
|
||||
)
|
||||
|
||||
// PWMConfig allows setting some configuration while configuring a PWM
|
||||
// peripheral. A zero PWMConfig is ready to use for simple applications such as
|
||||
// dimming LEDs.
|
||||
type PWMConfig struct {
|
||||
// PWM period in nanosecond. Leaving this zero will pick a reasonable period
|
||||
// value for use with LEDs.
|
||||
// If you want to configure a frequency instead of a period, you can use the
|
||||
// following formula to calculate a period from a frequency:
|
||||
//
|
||||
// period = 1e9 / frequency
|
||||
//
|
||||
Period uint64
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build !baremetal stm32,!stm32f7x2,!stm32l5x2 fe310 k210 atmega
|
||||
// +build !baremetal stm32,!stm32f7x2,!stm32l5x2,!stm32l4x2 fe310 k210 atmega
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
+35
-75
@@ -12,8 +12,6 @@ const deviceDescriptorSize = 18
|
||||
var (
|
||||
errUSBCDCBufferEmpty = errors.New("USB-CDC buffer empty")
|
||||
errUSBCDCWriteByteTimeout = errors.New("USB-CDC write byte timeout")
|
||||
errUSBCDCReadTimeout = errors.New("USB-CDC read timeout")
|
||||
errUSBCDCBytesRead = errors.New("USB-CDC invalid number of bytes read")
|
||||
)
|
||||
|
||||
// DeviceDescriptor implements the USB standard device descriptor.
|
||||
@@ -45,8 +43,8 @@ func NewDeviceDescriptor(class, subClass, proto, packetSize0 uint8, vid, pid, ve
|
||||
}
|
||||
|
||||
// Bytes returns DeviceDescriptor data
|
||||
func (d DeviceDescriptor) Bytes() [deviceDescriptorSize]byte {
|
||||
var b [deviceDescriptorSize]byte
|
||||
func (d DeviceDescriptor) Bytes() []byte {
|
||||
b := make([]byte, deviceDescriptorSize)
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bcdUSB)
|
||||
@@ -93,8 +91,8 @@ func NewConfigDescriptor(totalLength uint16, interfaces uint8) ConfigDescriptor
|
||||
}
|
||||
|
||||
// Bytes returns ConfigDescriptor data.
|
||||
func (d ConfigDescriptor) Bytes() [configDescriptorSize]byte {
|
||||
var b [configDescriptorSize]byte
|
||||
func (d ConfigDescriptor) Bytes() []byte {
|
||||
b := make([]byte, configDescriptorSize)
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.wTotalLength)
|
||||
@@ -133,8 +131,8 @@ func NewInterfaceDescriptor(n, numEndpoints, class, subClass, protocol uint8) In
|
||||
}
|
||||
|
||||
// Bytes returns InterfaceDescriptor data.
|
||||
func (d InterfaceDescriptor) Bytes() [interfaceDescriptorSize]byte {
|
||||
var b [interfaceDescriptorSize]byte
|
||||
func (d InterfaceDescriptor) Bytes() []byte {
|
||||
b := make([]byte, interfaceDescriptorSize)
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bInterfaceNumber)
|
||||
@@ -169,8 +167,8 @@ func NewEndpointDescriptor(addr, attr uint8, packetSize uint16, interval uint8)
|
||||
}
|
||||
|
||||
// Bytes returns EndpointDescriptor data.
|
||||
func (d EndpointDescriptor) Bytes() [endpointDescriptorSize]byte {
|
||||
var b [endpointDescriptorSize]byte
|
||||
func (d EndpointDescriptor) Bytes() []byte {
|
||||
b := make([]byte, endpointDescriptorSize)
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bEndpointAddress)
|
||||
@@ -207,8 +205,8 @@ func NewIADDescriptor(firstInterface, count, class, subClass, protocol uint8) IA
|
||||
}
|
||||
|
||||
// Bytes returns IADDescriptor data.
|
||||
func (d IADDescriptor) Bytes() [iadDescriptorSize]byte {
|
||||
var b [iadDescriptorSize]byte
|
||||
func (d IADDescriptor) Bytes() []byte {
|
||||
b := make([]byte, iadDescriptorSize)
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bFirstInterface)
|
||||
@@ -237,8 +235,8 @@ func NewCDCCSInterfaceDescriptor(subtype, d0, d1 uint8) CDCCSInterfaceDescriptor
|
||||
}
|
||||
|
||||
// Bytes returns CDCCSInterfaceDescriptor data.
|
||||
func (d CDCCSInterfaceDescriptor) Bytes() [cdcCSInterfaceDescriptorSize]byte {
|
||||
var b [cdcCSInterfaceDescriptorSize]byte
|
||||
func (d CDCCSInterfaceDescriptor) Bytes() []byte {
|
||||
b := make([]byte, cdcCSInterfaceDescriptorSize)
|
||||
b[0] = byte(d.len)
|
||||
b[1] = byte(d.dtype)
|
||||
b[2] = byte(d.subtype)
|
||||
@@ -264,8 +262,8 @@ func NewCMFunctionalDescriptor(subtype, d0, d1 uint8) CMFunctionalDescriptor {
|
||||
}
|
||||
|
||||
// Bytes returns the CMFunctionalDescriptor data.
|
||||
func (d CMFunctionalDescriptor) Bytes() [cmFunctionalDescriptorSize]byte {
|
||||
var b [cmFunctionalDescriptorSize]byte
|
||||
func (d CMFunctionalDescriptor) Bytes() []byte {
|
||||
b := make([]byte, cmFunctionalDescriptorSize)
|
||||
b[0] = byte(d.bFunctionLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bDescriptorSubtype)
|
||||
@@ -290,8 +288,8 @@ func NewACMFunctionalDescriptor(subtype, d0 uint8) ACMFunctionalDescriptor {
|
||||
}
|
||||
|
||||
// Bytes returns the ACMFunctionalDescriptor data.
|
||||
func (d ACMFunctionalDescriptor) Bytes() [acmFunctionalDescriptorSize]byte {
|
||||
var b [acmFunctionalDescriptorSize]byte
|
||||
func (d ACMFunctionalDescriptor) Bytes() []byte {
|
||||
b := make([]byte, acmFunctionalDescriptorSize)
|
||||
b[0] = byte(d.len)
|
||||
b[1] = byte(d.dtype)
|
||||
b[2] = byte(d.subtype)
|
||||
@@ -353,51 +351,19 @@ const cdcSize = iadDescriptorSize +
|
||||
endpointDescriptorSize
|
||||
|
||||
// Bytes returns CDCDescriptor data.
|
||||
func (d CDCDescriptor) Bytes() [cdcSize]byte {
|
||||
var b [cdcSize]byte
|
||||
offset := 0
|
||||
|
||||
iad := d.iad.Bytes()
|
||||
copy(b[offset:], iad[:])
|
||||
offset += len(iad)
|
||||
|
||||
cif := d.cif.Bytes()
|
||||
copy(b[offset:], cif[:])
|
||||
offset += len(cif)
|
||||
|
||||
header := d.header.Bytes()
|
||||
copy(b[offset:], header[:])
|
||||
offset += len(header)
|
||||
|
||||
controlManagement := d.controlManagement.Bytes()
|
||||
copy(b[offset:], controlManagement[:])
|
||||
offset += len(controlManagement)
|
||||
|
||||
functionalDescriptor := d.functionalDescriptor.Bytes()
|
||||
copy(b[offset:], functionalDescriptor[:])
|
||||
offset += len(functionalDescriptor)
|
||||
|
||||
callManagement := d.callManagement.Bytes()
|
||||
copy(b[offset:], callManagement[:])
|
||||
offset += len(callManagement)
|
||||
|
||||
cifin := d.cifin.Bytes()
|
||||
copy(b[offset:], cifin[:])
|
||||
offset += len(cifin)
|
||||
|
||||
dif := d.dif.Bytes()
|
||||
copy(b[offset:], dif[:])
|
||||
offset += len(dif)
|
||||
|
||||
out := d.out.Bytes()
|
||||
copy(b[offset:], out[:])
|
||||
offset += len(out)
|
||||
|
||||
in := d.in.Bytes()
|
||||
copy(b[offset:], in[:])
|
||||
offset += len(in)
|
||||
|
||||
return b
|
||||
func (d CDCDescriptor) Bytes() []byte {
|
||||
var buf []byte
|
||||
buf = append(buf, d.iad.Bytes()...)
|
||||
buf = append(buf, d.cif.Bytes()...)
|
||||
buf = append(buf, d.header.Bytes()...)
|
||||
buf = append(buf, d.controlManagement.Bytes()...)
|
||||
buf = append(buf, d.functionalDescriptor.Bytes()...)
|
||||
buf = append(buf, d.callManagement.Bytes()...)
|
||||
buf = append(buf, d.cifin.Bytes()...)
|
||||
buf = append(buf, d.dif.Bytes()...)
|
||||
buf = append(buf, d.out.Bytes()...)
|
||||
buf = append(buf, d.in.Bytes()...)
|
||||
return buf
|
||||
}
|
||||
|
||||
// MSCDescriptor is not used yet.
|
||||
@@ -407,8 +373,6 @@ type MSCDescriptor struct {
|
||||
out EndpointDescriptor
|
||||
}
|
||||
|
||||
const cdcLineInfoSize = 7
|
||||
|
||||
type cdcLineInfo struct {
|
||||
dwDTERate uint32
|
||||
bCharFormat uint8
|
||||
@@ -669,8 +633,7 @@ func sendDescriptor(setup usbSetup) {
|
||||
if setup.wLength < deviceDescriptorSize {
|
||||
l = int(setup.wLength)
|
||||
}
|
||||
buf := dd.Bytes()
|
||||
sendUSBPacket(0, buf[:l])
|
||||
sendUSBPacket(0, dd.Bytes()[:l])
|
||||
return
|
||||
|
||||
case usb_STRING_DESCRIPTOR_TYPE:
|
||||
@@ -706,8 +669,7 @@ func sendConfiguration(setup usbSetup) {
|
||||
if setup.wLength == 9 {
|
||||
sz := uint16(configDescriptorSize + cdcSize)
|
||||
config := NewConfigDescriptor(sz, 2)
|
||||
configBuf := config.Bytes()
|
||||
sendUSBPacket(0, configBuf[:])
|
||||
sendUSBPacket(0, config.Bytes())
|
||||
} else {
|
||||
iad := NewIADDescriptor(0, 2, usb_CDC_COMMUNICATION_INTERFACE_CLASS, usb_CDC_ABSTRACT_CONTROL_MODEL, 0)
|
||||
|
||||
@@ -743,12 +705,10 @@ func sendConfiguration(setup usbSetup) {
|
||||
sz := uint16(configDescriptorSize + cdcSize)
|
||||
config := NewConfigDescriptor(sz, 2)
|
||||
|
||||
configBuf := config.Bytes()
|
||||
cdcBuf := cdc.Bytes()
|
||||
var buf [configDescriptorSize + cdcSize]byte
|
||||
copy(buf[0:], configBuf[:])
|
||||
copy(buf[configDescriptorSize:], cdcBuf[:])
|
||||
buf := make([]byte, 0, sz)
|
||||
buf = append(buf, config.Bytes()...)
|
||||
buf = append(buf, cdc.Bytes()...)
|
||||
|
||||
sendUSBPacket(0, buf[:])
|
||||
sendUSBPacket(0, buf)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,9 +322,6 @@ func TypeOf(i interface{}) Type {
|
||||
}
|
||||
|
||||
func PtrTo(t Type) Type {
|
||||
if t.Kind() == Ptr {
|
||||
panic("reflect: cannot make **T type")
|
||||
}
|
||||
ptrType := t.(rawType)<<5 | 5 // 0b0101 == 5
|
||||
if ptrType>>5 != t {
|
||||
panic("reflect: PtrTo type does not fit")
|
||||
|
||||
+1
-10
@@ -724,14 +724,8 @@ func Zero(typ Type) Value {
|
||||
panic("unimplemented: reflect.Zero()")
|
||||
}
|
||||
|
||||
// New is the reflect equivalent of the new(T) keyword, returning a pointer to a
|
||||
// new value of the given type.
|
||||
func New(typ Type) Value {
|
||||
return Value{
|
||||
typecode: PtrTo(typ).(rawType),
|
||||
value: alloc(typ.Size()),
|
||||
flags: valueFlagExported,
|
||||
}
|
||||
panic("unimplemented: reflect.New()")
|
||||
}
|
||||
|
||||
type funcHeader struct {
|
||||
@@ -762,9 +756,6 @@ func (e *ValueError) Error() string {
|
||||
// llvm.memcpy.p0i8.p0i8.i32().
|
||||
func memcpy(dst, src unsafe.Pointer, size uintptr)
|
||||
|
||||
//go:linkname alloc runtime.alloc
|
||||
func alloc(size uintptr) unsafe.Pointer
|
||||
|
||||
// Copy copies the contents of src into dst until either
|
||||
// dst has been filled or src has been exhausted.
|
||||
func Copy(dst, src Value) int {
|
||||
|
||||
+3
-3
@@ -16,13 +16,13 @@ type funcValue struct {
|
||||
|
||||
// funcValueWithSignature is used before the func lowering pass.
|
||||
type funcValueWithSignature struct {
|
||||
funcPtr uintptr // ptrtoint of the actual function pointer
|
||||
signature *uint8 // external *i8 with a name identifying the function signature
|
||||
funcPtr uintptr // ptrtoint of the actual function pointer
|
||||
signature *typecodeID // pointer to identify this signature (the value is undef)
|
||||
}
|
||||
|
||||
// getFuncPtr is a dummy function that may be used if the func lowering pass is
|
||||
// not used. It is generally too slow but may be a useful fallback to debug the
|
||||
// func lowering pass.
|
||||
func getFuncPtr(val funcValue, signature *uint8) uintptr {
|
||||
func getFuncPtr(val funcValue, signature *typecodeID) uintptr {
|
||||
return (*funcValueWithSignature)(unsafe.Pointer(val.id)).funcPtr
|
||||
}
|
||||
|
||||
@@ -112,11 +112,6 @@ type typecodeID struct {
|
||||
length uintptr
|
||||
|
||||
methodSet *interfaceMethodInfo // nil or a GEP of an array
|
||||
|
||||
// The type that's a pointer to this type, nil if it is already a pointer.
|
||||
// Keeping the type struct alive here is important so that values from
|
||||
// reflect.New (which uses reflect.PtrTo) can be used in type asserts etc.
|
||||
ptrTo *typecodeID
|
||||
}
|
||||
|
||||
// structField is used by the compiler to pass information to the interface
|
||||
|
||||
@@ -23,9 +23,7 @@ func GOROOT() string {
|
||||
return "/usr/local/go"
|
||||
}
|
||||
|
||||
// This is the default set of arguments, if nothing else has been set.
|
||||
// This may be overriden by modifying this global at runtime init (for example,
|
||||
// on Linux where there are real command line arguments).
|
||||
// TODO: fill with real args.
|
||||
var args = []string{"/proc/self/exe"}
|
||||
|
||||
//go:linkname os_runtime_args os.runtime_args
|
||||
@@ -49,9 +47,6 @@ func memmove(dst, src unsafe.Pointer, size uintptr)
|
||||
// llvm.memset.p0i8.i32(ptr, 0, size, false).
|
||||
func memzero(ptr unsafe.Pointer, size uintptr)
|
||||
|
||||
//export strlen
|
||||
func strlen(ptr unsafe.Pointer) uintptr
|
||||
|
||||
// Compare two same-size buffers for equality.
|
||||
func memequal(x, y unsafe.Pointer, n uintptr) bool {
|
||||
for i := uintptr(0); i < n; i++ {
|
||||
@@ -94,3 +89,8 @@ func AdjustTimeOffset(offset int64) {
|
||||
func os_sigpipe() {
|
||||
runtimePanic("too many writes on closed pipe")
|
||||
}
|
||||
|
||||
//go:linkname syscall_runtime_envs syscall.runtime_envs
|
||||
func syscall_runtime_envs() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build sam,atsamd51 sam,atsame5x
|
||||
// +build sam,atsamd51
|
||||
|
||||
package runtime
|
||||
|
||||
@@ -16,7 +16,6 @@ func postinit() {}
|
||||
|
||||
//export Reset_Handler
|
||||
func main() {
|
||||
arm.SCB.CPACR.Set(0) // disable FPU if it is enabled
|
||||
preinit()
|
||||
run()
|
||||
abort()
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
// +build sam,atsame51,atsame51j19
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
)
|
||||
|
||||
func initSERCOMClocks() {
|
||||
// Turn on clock to SERCOM0 for UART0
|
||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM0_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// sets the "slow" clock shared by all SERCOM
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOMX_SLOW].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM1
|
||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM1_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM2
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM2_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM3
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM3_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM4
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM4_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM5
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM5_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
// +build sam,atsame5x,atsame54p20
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
)
|
||||
|
||||
func initSERCOMClocks() {
|
||||
// Turn on clock to SERCOM0 for UART0
|
||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM0_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// sets the "slow" clock shared by all SERCOM
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOMX_SLOW].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM1
|
||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM1_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM2
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM2_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM3
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM3_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM4
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM4_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM5
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM5_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM6
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM6_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM6_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM7
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM7_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM7_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
@@ -102,6 +102,11 @@ func initSystem() {
|
||||
|
||||
func initPeripherals() {
|
||||
|
||||
// enable FPU - set CP10, CP11 full access
|
||||
nxp.SystemControl.CPACR.SetBits(
|
||||
((nxp.SCB_CPACR_CP10_CP10_3 << nxp.SCB_CPACR_CP10_Pos) & nxp.SCB_CPACR_CP10_Msk) |
|
||||
((nxp.SCB_CPACR_CP11_CP11_3 << nxp.SCB_CPACR_CP11_Pos) & nxp.SCB_CPACR_CP11_Msk))
|
||||
|
||||
enableTimerClocks() // activate GPT/PIT clock gates
|
||||
initSysTick() // enable SysTick
|
||||
initRTC() // enable real-time clock
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/nrf"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
@@ -19,9 +18,6 @@ func postinit() {}
|
||||
|
||||
//export Reset_Handler
|
||||
func main() {
|
||||
if nrf.FPUPresent {
|
||||
arm.SCB.CPACR.Set(0) // disable FPU if it is enabled
|
||||
}
|
||||
systemInit()
|
||||
preinit()
|
||||
run()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user